Skip to content

Add more link OSs #2177

New issue

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

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

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class CBuilder extends CTool implements Builder {
cppLinkStdLib: cppLinkStdLib,
optimizationLevel: optimizationLevel,
);
await task.run();
await task.runCompiler();

if (assetName != null) {
for (final route in routing) {
Expand Down
15 changes: 10 additions & 5 deletions pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'linker_options.dart';
import 'linkmode.dart';
import 'optimization_level.dart';
import 'output_type.dart';
import 'run_cbuilder.dart';
import 'run_clinker.dart';

/// Specification for linking an artifact with a C linker.
//TODO(mosuem): This is currently only implemented for linux.
Expand Down Expand Up @@ -43,6 +43,10 @@ class CLinker extends CTool implements Linker {
super.optimizationLevel = OptimizationLevel.o3,
}) : super(type: OutputType.library);

//TODO(mosuem): Remove this field once all OSs are supported.
@visibleForTesting
static const supportedLinkingOSs = [OS.linux, OS.macOS];

/// Runs the C Linker with on this C build spec.
///
/// Completes with an error if the linking fails.
Expand All @@ -52,9 +56,10 @@ class CLinker extends CTool implements Linker {
required LinkOutputBuilder output,
required Logger? logger,
}) async {
if (OS.current != OS.linux || input.config.code.targetOS != OS.linux) {
if (!supportedLinkingOSs.contains(OS.current) ||
!supportedLinkingOSs.contains(input.config.code.targetOS)) {
throw UnsupportedError(
'Currently, only linux is supported for this '
'Currently, only $supportedLinkingOSs are supported for this '
'feature. See also https://github.com/dart-lang/native/issues/1376',
);
}
Expand All @@ -79,7 +84,7 @@ class CLinker extends CTool implements Linker {
for (final directory in this.libraryDirectories)
outDir.resolveUri(Uri.file(directory)),
];
final task = RunCBuilder(
final task = RunCLinker(
input: input,
codeConfig: input.config.code,
linkerOptions: linkerOptions,
Expand All @@ -101,7 +106,7 @@ class CLinker extends CTool implements Linker {
cppLinkStdLib: cppLinkStdLib,
optimizationLevel: optimizationLevel,
);
await task.run();
await task.runLinker();

if (assetName != null) {
output.assets.code.add(
Expand Down
69 changes: 22 additions & 47 deletions pkgs/native_toolchain_c/lib/src/cbuilder/linker_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import 'dart:io';

import 'package:native_assets_cli/code_assets.dart';

import '../native_toolchain/tool_likeness.dart';
import '../tool/tool.dart';

Expand All @@ -15,7 +17,7 @@ import '../tool/tool.dart';
class LinkerOptions {
/// The flags to be passed to the linker. As they depend on the linker being
/// invoked, the actual usage is via the [postSourcesFlags] method.
final List<String> _linkerFlags;
final List<String> linkerFlags;

/// Enable garbage collection of unused input sections.
///
Expand All @@ -31,34 +33,44 @@ class LinkerOptions {
///
/// This is achieved by setting the `whole-archive` flag before passing the
/// sources, and the `no-whole-archive` flag after.
final bool _wholeArchiveSandwich;
final bool includeAllSymbols;

final bool linkStandardLibraries;

final bool stripDebug;

/// Create linking options manually for fine-grained control.
LinkerOptions.manual({
List<String>? flags,
bool? gcSections,
this.linkerScript,
}) : _linkerFlags = flags ?? [],
gcSections = gcSections ?? true,
_wholeArchiveSandwich = false;
this.linkStandardLibraries = true,
this.includeAllSymbols = true,
this.stripDebug = true,
}) : linkerFlags = flags ?? [],
gcSections = gcSections ?? true;

/// Create linking options to tree-shake symbols from the input files.
///
/// The [symbols] specify the symbols which should be kept.
LinkerOptions.treeshake({
Iterable<String>? flags,
required Iterable<String>? symbols,
}) : _linkerFlags =
this.linkStandardLibraries = true,
this.stripDebug = true,
}) : linkerFlags =
<String>[
...flags ?? [],
'--strip-debug',
if (symbols != null) ...symbols.expand((e) => ['-u', e]),
if (symbols != null)
...symbols.expand(
(e) => ['-u', (OS.current == OS.macOS ? '_' : '') + e],
),
].toList(),
gcSections = true,
_wholeArchiveSandwich = symbols == null,
includeAllSymbols = symbols == null,
linkerScript = _createLinkerScript(symbols);

Iterable<String> _toLinkerSyntax(Tool linker, List<String> flagList) {
Iterable<String> toLinkerSyntax(Tool linker, List<String> flagList) {
if (linker.isClangLike) {
return flagList.map((e) => '-Wl,$e');
} else if (linker.isLdLike) {
Expand All @@ -84,40 +96,3 @@ class LinkerOptions {
return symbolsFileUri;
}
}

extension LinkerOptionsExt on LinkerOptions {
/// The flags for the specified [linker], which are inserted _before_ the
/// sources.
///
/// This is mainly used for the whole-archive ... no-whole-archive
/// trick, which includes all symbols when linking object files.
///
/// Throws if the [linker] is not supported.
Iterable<String> preSourcesFlags(Tool linker, Iterable<String> sourceFiles) =>
_toLinkerSyntax(
linker,
sourceFiles.any((source) => source.endsWith('.a')) ||
_wholeArchiveSandwich
? ['--whole-archive']
: [],
);

/// The flags for the specified [linker], which are inserted _after_ the
/// sources.
///
/// This is mainly used for the whole-archive ... no-whole-archive
/// trick, which includes all symbols when linking object files.
///
/// Throws if the [linker] is not supported.
Iterable<String> postSourcesFlags(
Tool linker,
Iterable<String> sourceFiles,
) => _toLinkerSyntax(linker, [
..._linkerFlags,
if (gcSections) '--gc-sections',
if (linkerScript != null) '--version-script=${linkerScript!.toFilePath()}',
if (sourceFiles.any((source) => source.endsWith('.a')) ||
_wholeArchiveSandwich)
'--no-whole-archive',
]);
}
26 changes: 11 additions & 15 deletions pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,12 @@ class RunCBuilder {
Uri androidSysroot(ToolInstance compiler) =>
compiler.uri.resolve('../sysroot/');

Future<void> run() async {
Future<void> runCompiler() async {
assert(linkerOptions == null);
final toolInstance_ =
linkerOptions != null ? await linker() : await compiler();
final tool = toolInstance_.tool;
if (tool.isClangLike || tool.isLdLike) {
if (tool.isClangLike) {
await runClangLike(tool: toolInstance_);
return;
} else if (tool == cl) {
Expand All @@ -141,12 +142,8 @@ class RunCBuilder {
archiver_ = await archiver();
}

final IOSSdk? targetIosSdk;
if (codeConfig.targetOS == OS.iOS) {
targetIosSdk = codeConfig.iOS.targetSdk;
} else {
targetIosSdk = null;
}
final targetIosSdk =
codeConfig.targetOS == OS.iOS ? codeConfig.iOS.targetSdk : null;

// The Android Gradle plugin does not honor API level 19 and 20 when
// invoking clang. Mimic that behavior here.
Expand Down Expand Up @@ -295,7 +292,6 @@ class RunCBuilder {
],
if (optimizationLevel != OptimizationLevel.unspecified)
optimizationLevel.clangFlag(),
...linkerOptions?.preSourcesFlags(toolInstance.tool, sourceFiles) ?? [],
// Support Android 15 page size by default, can be overridden by
// passing [flags].
if (codeConfig.targetOS == OS.android) '-Wl,-z,max-page-size=16384',
Expand All @@ -321,17 +317,12 @@ class RunCBuilder {
'-o',
outFile!.toFilePath(),
],
...linkerOptions?.postSourcesFlags(toolInstance.tool, sourceFiles) ??
[],
if (executable != null || dynamicLibrary != null) ...[
if (codeConfig.targetOS case OS.android || OS.linux)
// During bundling code assets are all placed in the same directory.
// Setting this rpath allows the binary to find other code assets
// it is linked against.
if (linkerOptions != null)
'-rpath=\$ORIGIN'
else
'-Wl,-rpath=\$ORIGIN',
'-Wl,-rpath=\$ORIGIN',
for (final directory in libraryDirectories)
'-L${directory.toFilePath()}',
for (final library in libraries) '-l$library',
Expand Down Expand Up @@ -419,6 +410,11 @@ class RunCBuilder {
Architecture.x64: 'x86_64-apple-darwin',
};

static const appleLdArchs = {
Architecture.arm64: 'arm64',
Architecture.x64: 'x86_64',
};

static const appleClangIosTargetFlags = {
Architecture.arm64: {
IOSSdk.iPhoneOS: 'arm64-apple-ios',
Expand Down
Loading
Loading