Skip to content

[native_toolchain_c] Add linking for Android #2347

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

goderbauer
Copy link

@goderbauer goderbauer commented Jun 6, 2025

Towards #1376

This enables linking for Android on Windows, Linux, and MacOS hosts.

With this PR the linker is no longer invoked directly. Instead, it is invoked via the compiler driver (clang, etc.), which has the advantage that we don't have to hand-translate the arguments to the format the linker expects them to be. The compiler driver is doing that for us and we can mostly reuse the compiler driver invocation.


  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
Contribution guidelines:

Note that many Dart repos have a weekly cadence for reviewing PRs - please allow for some latency before initial review feedback.

Copy link

github-actions bot commented Jun 6, 2025

PR Health

Changelog Entry ✔️
Package Changed Files

Changes to files need to be accounted for in their respective changelogs.

API leaks ✔️

The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.

Package Leaked API symbols
License Headers ✔️
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
Files
no missing headers

All source files should start with a license header.

Unrelated files missing license headers
Files
pkgs/jni/lib/src/third_party/generated_bindings.dart
pkgs/native_doc_dartifier/example/native_doc_dartifier_example.dart
pkgs/native_doc_dartifier/lib/native_doc_dartifier.dart
pkgs/native_doc_dartifier/lib/src/native_doc_dartifier_base.dart
pkgs/objective_c/lib/src/ns_input_stream.dart

Copy link

github-actions bot commented Jun 6, 2025

Package publishing

Package Version Status Publish tag (post-merge)
package:code_assets 0.19.3 already published at pub.dev
package:data_assets 0.19.1 already published at pub.dev
package:ffi 2.1.4 already published at pub.dev
package:ffigen 19.1.0-wip WIP (no publish necessary)
package:hooks 0.19.3 already published at pub.dev
package:hooks_runner 0.21.0 already published at pub.dev
package:jni 0.14.2 already published at pub.dev
package:jnigen 0.15.0-wip WIP (no publish necessary)
package:native_doc_dartifier 0.0.1-pre already published at pub.dev
package:native_toolchain_c 0.16.3 ready to publish native_toolchain_c-v0.16.3
package:objective_c 8.1.0-wip WIP (no publish necessary)
package:swift2objc 0.0.1-wip WIP (no publish necessary)
package:swiftgen 0.0.1-wip WIP (no publish necessary)

Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.

@goderbauer goderbauer force-pushed the android-linker branch 3 times, most recently from 84b1b48 to ca6c274 Compare June 6, 2025 13:22
@goderbauer goderbauer requested review from dcharkes and mosuem June 6, 2025 13:36
@goderbauer
Copy link
Author

Hm, it's not clear to me why the bot wants the version to be bumped to 0.17.0

@dcharkes
Copy link
Collaborator

dcharkes commented Jun 6, 2025

Hm, it's not clear to me why the bot wants the version to be bumped to 0.17.0

You should be able to repro it with the dart_apitool yourself with https://github.com/dart-lang/ecosystem/blob/64aac3a9c4606950bcf6c8729f01ed8548b9ed87/pkgs/firehose/lib/src/health/health.dart#L152


//TODO(mosuem): Enable for windows and mac.
// See https://github.com/dart-lang/native/issues/1376.
@TestOn('linux')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be removed now?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is still correct. This test checks whether we can link on the current host OS for different architectures running the same (host) OS. For that, we still only support Linux for now. The new functionality (cross linking for Android from Linux/Windows/MaxOS) I added the new *_cross_android_test.dart tests, which do not have a @TestOn limitation.

(Technically, this test doesn't belong to this change as it is unrelated to the Android work and testing existing functionality. I just saw this as a whole in the existing testing story and added it for completeness - and to make sure that my Android work doesn't break this scenario).

@goderbauer
Copy link
Author

goderbauer commented Jun 10, 2025

You should be able to repro it with the dart_apitool yourself

Thank you! I am pretty sure that the tool is wrong here based on what it has identified as a breaking change in a local run:

BREAKING CHANGES
├── Function "environmentFromBatchFile" removed (CE10)
├─┬ Class CBuilder
│ ├─┬ Constructor library
│ │ └── Type of parameter "linkModePreference" changed. InvalidType -> LinkModePreference? (CE08)
│ └─┬ Field linkModePreference
│   └── Type of field changed. InvalidType -> LinkModePreference? (CF04)
└─┬ Class CLinker
  ├─┬ Constructor library
  │ └── Type of parameter "linkModePreference" changed. InvalidType -> LinkModePreference? (CE08)
  └─┬ Field linkModePreference
    └── Type of field changed. InvalidType -> LinkModePreference? (CF04)

The environmentFromBtachFile wasn't touched in this PR, it is still there and properly exported. The tool also seems to be unable to correctly infer the type of the linkModePreference parameter in the old version of this package and now that it can determine the correct type it thinks the type has changed. That's not correct, the type remains unchanged. I am going to overwrite the check for now.

Edit: It looks like the dart_apitool may have trouble with native_toolchain_c being part of a pub workspace. It seems to be checking out the previous version of the package from pub and analyzing that code outside of its workspace, which seems to result in some problems. If I actually check out the whole repository at the old version's revision and have the tool compare that with the current revision, it correctly detects no breaking change: No breaking changes!

@coveralls
Copy link

Coverage Status

coverage: 81.518%. first build
when pulling 330b8db on goderbauer:android-linker
into fd9415b on dart-lang:main.

@dcharkes
Copy link
Collaborator

Edit: It looks like the dart_apitool may have trouble with native_toolchain_c being part of a pub workspace. It seems to be checking out the previous version of the package from pub and analyzing that code outside of its workspace, which seems to result in some problems. If I actually check out the whole repository at the old version's revision and have the tool compare that with the current revision, it correctly detects no breaking change: No breaking changes!

It does indeed check out the package from pub. See the discussion here: bmw-tech/dart_apitool#216

The packages on pub.dev should work, they are used in the flutter template and flutter integration tests. It's fishy that it fails. (Yeah indeed just apply the label for now.)

@@ -53,10 +53,12 @@ class CLinker extends CTool implements Linker {
required LinkOutputBuilder output,
required Logger? logger,
}) async {
if (OS.current != OS.linux || input.config.code.targetOS != OS.linux) {
const supportedTargetOSs = [OS.linux, OS.android];
if (!supportedTargetOSs.contains(input.config.code.targetOS)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This no longer checks if the hostOS is Linux if the targetOS is Linux.

@@ -0,0 +1,78 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new file 2025

@@ -0,0 +1,30 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2025

Copy link
Member

@mosuem mosuem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that users need to have clang or similar installed? Linux will have ld preinstalled, but not clang.

@@ -51,7 +51,7 @@ class LinkerOptions {
}) : _linkerFlags = <String>[
...flags ?? [],
'--strip-debug',
if (symbols != null) ...symbols.expand((e) => ['-u', e]),
if (symbols != null) ...symbols.expand((e) => ['-u,$e']),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (symbols != null) ...symbols.expand((e) => ['-u,$e']),
if (symbols != null) ...symbols.map((e) => '-u,$e'),

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(in case you want to go with interpolation instead of expand)

Architecture.riscv64: 'elf64-littleriscv',
};

Future<void> expectMachineArchitecture(Uri libUri, Architecture target) async {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use some documentation

/// From https://docs.flutter.dev/reference/supported-platforms.
const flutterAndroidNdkVersionHighestSupported = 34;

const objdumpFileFormat = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also could use some documentation- where are the strings taken from?

final toolInstance_ = linkerOptions != null
? await linker()
: await compiler();
final toolInstance_ = await compiler();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we now delete linker?

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants