diff --git a/pkgs/native_toolchain_c/CHANGELOG.md b/pkgs/native_toolchain_c/CHANGELOG.md index 83e4eacb9..4e713d4d4 100644 --- a/pkgs/native_toolchain_c/CHANGELOG.md +++ b/pkgs/native_toolchain_c/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.16.4 + +* Support linking for MacOS. + ## 0.16.3 * Support linking for Android. diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart index 32ff08df6..116255da8 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart @@ -53,7 +53,7 @@ class CLinker extends CTool implements Linker { required LinkOutputBuilder output, required Logger? logger, }) async { - const supportedTargetOSs = [OS.linux, OS.android]; + const supportedTargetOSs = [OS.linux, OS.android, OS.macOS]; if (!supportedTargetOSs.contains(input.config.code.targetOS)) { throw UnsupportedError( 'This feature is only supported when targeting ' diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/linker_options.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/linker_options.dart index 0f692f3ef..b0072b8f2 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/linker_options.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/linker_options.dart @@ -4,6 +4,8 @@ import 'dart:io'; +import 'package:code_assets/code_assets.dart'; + import '../native_toolchain/tool_likeness.dart'; import '../tool/tool.dart'; @@ -14,7 +16,7 @@ import '../tool/tool.dart'; /// the [LinkerOptions.treeshake] constructor can be used. 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. + /// invoked, the actual usage is via the [sourceFilesToFlags] method. final List _linkerFlags; /// Enable garbage collection of unused input sections. @@ -27,20 +29,24 @@ class LinkerOptions { /// See also the `ld` man page at https://linux.die.net/man/1/ld. final Uri? linkerScript; - /// Whether to include all symbols from the sources. + /// Whether to strip debugging symbols from the binary. + final bool stripDebug; + + /// The symbols to keep in the resulting binaries. /// - /// This is achieved by setting the `whole-archive` flag before passing the - /// sources, and the `no-whole-archive` flag after. - final bool _wholeArchiveSandwich; + /// If null all symbols will be kept. + final List? _symbolsToKeep; /// Create linking options manually for fine-grained control. LinkerOptions.manual({ List? flags, bool? gcSections, this.linkerScript, + this.stripDebug = true, + Iterable? symbolsToKeep, }) : _linkerFlags = flags ?? [], gcSections = gcSections ?? true, - _wholeArchiveSandwich = false; + _symbolsToKeep = symbolsToKeep?.toList(growable: false); /// Create linking options to tree-shake symbols from the input files. /// @@ -48,16 +54,13 @@ class LinkerOptions { LinkerOptions.treeshake({ Iterable? flags, required Iterable? symbols, - }) : _linkerFlags = [ - ...flags ?? [], - '--strip-debug', - if (symbols != null) ...symbols.map((e) => '-u,$e'), - ].toList(), + this.stripDebug = true, + }) : _linkerFlags = flags?.toList(growable: false) ?? [], + _symbolsToKeep = symbols?.toList(growable: false), gcSections = true, - _wholeArchiveSandwich = symbols == null, linkerScript = _createLinkerScript(symbols); - Iterable _toLinkerSyntax(Tool linker, List flagList) { + Iterable _toLinkerSyntax(Tool linker, Iterable flagList) { if (linker.isClangLike) { return flagList.map((e) => '-Wl,$e'); } else if (linker.isLdLike) { @@ -85,38 +88,48 @@ class LinkerOptions { } 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 preSourcesFlags(Tool linker, Iterable 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 postSourcesFlags( - Tool linker, + /// Takes [sourceFiles] and turns it into flags for the compiler driver while + /// considering the current [LinkerOptions]. + Iterable sourceFilesToFlags( + Tool tool, Iterable 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', - ]); + OS targetOS, + ) { + final includeAllSymbols = _symbolsToKeep == null; + + switch (targetOS) { + case OS.macOS: + return [ + if (!includeAllSymbols) ...sourceFiles, + ..._toLinkerSyntax(tool, [ + if (includeAllSymbols) ...sourceFiles.map((e) => '-force_load,$e'), + ..._linkerFlags, + ..._symbolsToKeep?.map((symbol) => '-u,_$symbol') ?? [], + if (stripDebug) '-S', + if (gcSections) '-dead_strip', + ]), + ]; + + case OS.android || OS.linux: + final wholeArchiveSandwich = + sourceFiles.any((source) => source.endsWith('.a')) || + includeAllSymbols; + return [ + if (wholeArchiveSandwich) + ..._toLinkerSyntax(tool, ['--whole-archive']), + ...sourceFiles, + ..._toLinkerSyntax(tool, [ + ..._linkerFlags, + ..._symbolsToKeep?.map((symbol) => '-u,$symbol') ?? [], + if (stripDebug) '--strip-debug', + if (gcSections) '--gc-sections', + if (linkerScript != null) + '--version-script=${linkerScript!.toFilePath()}', + if (wholeArchiveSandwich) '--no-whole-archive', + ]), + ]; + case OS(): + throw UnimplementedError(); + } + } } diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart index fa29d211e..30a39c3c0 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart @@ -262,9 +262,9 @@ class RunCBuilder { if (dynamicLibrary != null) '-fPIC', // Using PIC for static libraries allows them to be linked into // any executable, but it is not necessarily the best option in - // terms of overhead. We would have to know wether the target into - // which the static library is linked is PIC, PIE or neither. Then - // we could use the same option for the static library. + // terms of overhead. We would have to know whether the target + // into which the static library is linked is PIC, PIE or neither. + // Then we could use the same option for the static library. if (staticLibrary != null) '-fPIC', if (executable != null) ...[ // Generate position-independent code for executables. @@ -296,7 +296,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', @@ -306,7 +305,14 @@ class RunCBuilder { for (final include in includes) '-I${include.toFilePath()}', for (final forcedInclude in forcedIncludes) '-include${forcedInclude.toFilePath()}', - ...sourceFiles, + if (linkerOptions != null) + ...linkerOptions!.sourceFilesToFlags( + toolInstance.tool, + sourceFiles, + codeConfig.targetOS, + ) + else + ...sourceFiles, if (language == Language.objectiveC) ...[ for (final framework in frameworks) ...['-framework', framework], ], @@ -322,8 +328,6 @@ 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. diff --git a/pkgs/native_toolchain_c/pubspec.yaml b/pkgs/native_toolchain_c/pubspec.yaml index e7784090d..e10c3c6e9 100644 --- a/pkgs/native_toolchain_c/pubspec.yaml +++ b/pkgs/native_toolchain_c/pubspec.yaml @@ -1,7 +1,7 @@ name: native_toolchain_c description: >- A library to invoke the native C compiler installed on the host machine. -version: 0.16.3 +version: 0.16.4 repository: https://github.com/dart-lang/native/tree/main/pkgs/native_toolchain_c topics: diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_android_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_android_test.dart index 8dbf67608..f29ca1393 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_android_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_android_test.dart @@ -43,7 +43,7 @@ void main() { linkMode, optimizationLevel: optimizationLevel, ); - await expectMachineArchitecture(libUri, target); + await expectMachineArchitecture(libUri, target, OS.android); if (linkMode == DynamicLoadingBundled()) { await expectPageSize(libUri, 16 * 1024); } diff --git a/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart b/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart index 75d8c4926..217f2c4f0 100644 --- a/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart +++ b/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart @@ -16,8 +16,15 @@ Future buildTestArchive( OS targetOS, Architecture architecture, { int? androidTargetNdkApi, // Must be specified iff targetOS is OS.android. + int? macOSTargetVersion, // Must be specified iff targetOS is OS.macos. }) async { - assert((targetOS != OS.android) == (androidTargetNdkApi == null)); + if (targetOS == OS.android) { + ArgumentError.checkNotNull(androidTargetNdkApi, 'androidTargetNdkApi'); + } + if (targetOS == OS.macOS) { + ArgumentError.checkNotNull(macOSTargetVersion, 'macOSTargetVersion'); + } + final test1Uri = packageUri.resolve('test/clinker/testfiles/linker/test1.c'); final test2Uri = packageUri.resolve('test/clinker/testfiles/linker/test2.c'); if (!await File.fromUri(test1Uri).exists() || @@ -46,6 +53,9 @@ Future buildTestArchive( android: androidTargetNdkApi != null ? AndroidCodeConfig(targetNdkApi: androidTargetNdkApi) : null, + macOS: macOSTargetVersion != null + ? MacOSCodeConfig(targetVersion: macOSTargetVersion) + : null, ), ); diff --git a/pkgs/native_toolchain_c/test/clinker/objects_cross_android_test.dart b/pkgs/native_toolchain_c/test/clinker/objects_cross_android_test.dart index 114cd31f6..530ff4abf 100644 --- a/pkgs/native_toolchain_c/test/clinker/objects_cross_android_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/objects_cross_android_test.dart @@ -9,15 +9,8 @@ import '../helpers.dart'; import 'objects_helper.dart'; void main() { - final architectures = [ - Architecture.arm, - Architecture.arm64, - Architecture.ia32, - Architecture.x64, - Architecture.riscv64, - ]; - const targetOS = OS.android; + final architectures = supportedArchitecturesFor(targetOS); for (final apiLevel in [ flutterAndroidNdkVersionLowestSupported, diff --git a/pkgs/native_toolchain_c/test/clinker/objects_cross_test.dart b/pkgs/native_toolchain_c/test/clinker/objects_cross_test.dart index f7d44454e..e32de5353 100644 --- a/pkgs/native_toolchain_c/test/clinker/objects_cross_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/objects_cross_test.dart @@ -2,9 +2,9 @@ // 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. -//TODO(mosuem): Enable for windows and mac. +// TODO(mosuem): Enable for windows. // See https://github.com/dart-lang/native/issues/1376. -@TestOn('linux') +@TestOn('linux || mac-os') library; import 'dart:io'; @@ -12,21 +12,21 @@ import 'dart:io'; import 'package:code_assets/code_assets.dart'; import 'package:test/test.dart'; +import '../helpers.dart'; import 'objects_helper.dart'; void main() { - if (!Platform.isLinux) { + if (!Platform.isLinux && !Platform.isMacOS) { // Avoid needing status files on Dart SDK CI. return; } - final architectures = [ - Architecture.arm, - Architecture.arm64, - Architecture.ia32, - Architecture.x64, - Architecture.riscv64, - ]..remove(Architecture.current); + final architectures = supportedArchitecturesFor(OS.current) + ..remove(Architecture.current); // See objects_test.dart for current arch. - runObjectsTests(OS.current, architectures); + runObjectsTests( + OS.current, + architectures, + macOSTargetVersion: OS.current == OS.macOS ? defaultMacOSVersion : null, + ); } diff --git a/pkgs/native_toolchain_c/test/clinker/objects_helper.dart b/pkgs/native_toolchain_c/test/clinker/objects_helper.dart index 17036f5d1..5d5fe78a0 100644 --- a/pkgs/native_toolchain_c/test/clinker/objects_helper.dart +++ b/pkgs/native_toolchain_c/test/clinker/objects_helper.dart @@ -16,8 +16,15 @@ void runObjectsTests( OS targetOS, List architectures, { int? androidTargetNdkApi, // Must be specified iff targetOS is OS.android. + int? macOSTargetVersion, // Must be specified iff targetOS is OS.macos. }) { - assert((targetOS != OS.android) == (androidTargetNdkApi == null)); + if (targetOS == OS.android) { + ArgumentError.checkNotNull(androidTargetNdkApi, 'androidTargetNdkApi'); + } + if (targetOS == OS.macOS) { + ArgumentError.checkNotNull(macOSTargetVersion, 'macOSTargetVersion'); + } + const name = 'mylibname'; for (final architecture in architectures) { @@ -31,6 +38,7 @@ void runObjectsTests( targetOS, architecture, androidTargetNdkApi: androidTargetNdkApi, + macOSTargetVersion: macOSTargetVersion, ); final linkInputBuilder = LinkInputBuilder() @@ -50,6 +58,9 @@ void runObjectsTests( android: androidTargetNdkApi != null ? AndroidCodeConfig(targetNdkApi: androidTargetNdkApi) : null, + macOS: macOSTargetVersion != null + ? MacOSCodeConfig(targetVersion: macOSTargetVersion) + : null, ), ); @@ -70,7 +81,7 @@ void runObjectsTests( final asset = codeAssets.first; expect(asset, isA()); expect( - await nmReadSymbols(asset), + await nmReadSymbols(asset, targetOS), stringContainsInOrder(['my_func', 'my_other_func']), ); }); diff --git a/pkgs/native_toolchain_c/test/clinker/objects_test.dart b/pkgs/native_toolchain_c/test/clinker/objects_test.dart index 7c4c318bf..93556524f 100644 --- a/pkgs/native_toolchain_c/test/clinker/objects_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/objects_test.dart @@ -2,9 +2,9 @@ // 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. -//TODO(mosuem): Enable for windows and mac. +// TODO(mosuem): Enable for windows. // See https://github.com/dart-lang/native/issues/1376. -@TestOn('linux') +@TestOn('linux || mac-os') library; import 'dart:io'; @@ -12,13 +12,18 @@ import 'dart:io'; import 'package:code_assets/code_assets.dart'; import 'package:test/test.dart'; +import '../helpers.dart'; import 'objects_helper.dart'; void main() { - if (!Platform.isLinux) { + if (!Platform.isLinux && !Platform.isMacOS) { // Avoid needing status files on Dart SDK CI. return; } - runObjectsTests(OS.current, [Architecture.current]); + runObjectsTests( + OS.current, + [Architecture.current], + macOSTargetVersion: OS.current == OS.macOS ? defaultMacOSVersion : null, + ); } diff --git a/pkgs/native_toolchain_c/test/clinker/throws_test.dart b/pkgs/native_toolchain_c/test/clinker/throws_test.dart index 0b2e15a5c..8fb28dc7c 100644 --- a/pkgs/native_toolchain_c/test/clinker/throws_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/throws_test.dart @@ -11,7 +11,9 @@ import '../helpers.dart'; void main() { for (final targetOS in OS.values) { - if (targetOS == OS.linux || targetOS == OS.android) { + if (targetOS == OS.linux || + targetOS == OS.android || + targetOS == OS.macOS) { // Is implemented. continue; } diff --git a/pkgs/native_toolchain_c/test/clinker/treeshake_cross_android_test.dart b/pkgs/native_toolchain_c/test/clinker/treeshake_cross_android_test.dart index b4d05fa48..bafb94e22 100644 --- a/pkgs/native_toolchain_c/test/clinker/treeshake_cross_android_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/treeshake_cross_android_test.dart @@ -9,15 +9,8 @@ import '../helpers.dart'; import 'treeshake_helper.dart'; void main() { - final architectures = [ - Architecture.arm, - Architecture.arm64, - Architecture.ia32, - Architecture.x64, - Architecture.riscv64, - ]; - const targetOS = OS.android; + final architectures = supportedArchitecturesFor(targetOS); for (final apiLevel in [ flutterAndroidNdkVersionLowestSupported, diff --git a/pkgs/native_toolchain_c/test/clinker/treeshake_cross_test.dart b/pkgs/native_toolchain_c/test/clinker/treeshake_cross_test.dart index ea0218b6d..5851921bc 100644 --- a/pkgs/native_toolchain_c/test/clinker/treeshake_cross_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/treeshake_cross_test.dart @@ -2,9 +2,9 @@ // 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. -//TODO(mosuem): Enable for windows and mac. +// TODO(mosuem): Enable for windows. // See https://github.com/dart-lang/native/issues/1376. -@TestOn('linux') +@TestOn('linux || mac-os') library; import 'dart:io'; @@ -12,21 +12,21 @@ import 'dart:io'; import 'package:code_assets/code_assets.dart'; import 'package:test/test.dart'; +import '../helpers.dart'; import 'treeshake_helper.dart'; void main() { - if (!Platform.isLinux) { + if (!Platform.isLinux && !Platform.isMacOS) { // Avoid needing status files on Dart SDK CI. return; } - final architectures = [ - Architecture.arm, - Architecture.arm64, - Architecture.ia32, - Architecture.x64, - Architecture.riscv64, - ]..remove(Architecture.current); + final architectures = supportedArchitecturesFor(OS.current) + ..remove(Architecture.current); // See treeshake_test.dart for current arch. - runTreeshakeTests(OS.current, architectures); + runTreeshakeTests( + OS.current, + architectures, + macOSTargetVersion: OS.current == OS.macOS ? defaultMacOSVersion : null, + ); } diff --git a/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart b/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart index 214deeb69..751916d37 100644 --- a/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart +++ b/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart @@ -16,14 +16,22 @@ void runTreeshakeTests( OS targetOS, List architectures, { int? androidTargetNdkApi, // Must be specified iff targetOS is OS.android. + int? macOSTargetVersion, // Must be specified iff targetOS is OS.macos. }) { - assert((targetOS != OS.android) == (androidTargetNdkApi == null)); + if (targetOS == OS.android) { + ArgumentError.checkNotNull(androidTargetNdkApi, 'androidTargetNdkApi'); + } + if (targetOS == OS.macOS) { + ArgumentError.checkNotNull(macOSTargetVersion, 'macOSTargetVersion'); + } + CLinker linkerManual(List sources) => CLinker.library( name: 'mylibname', assetName: '', sources: sources, linkerOptions: LinkerOptions.manual( - flags: ['--strip-debug', '-u,my_other_func'], + symbolsToKeep: ['my_other_func'], + stripDebug: true, gcSections: true, linkerScript: packageUri.resolve( 'test/clinker/testfiles/linker/symbols.lds', @@ -61,6 +69,7 @@ void runTreeshakeTests( targetOS, architecture, androidTargetNdkApi: androidTargetNdkApi, + macOSTargetVersion: macOSTargetVersion, ); final linkInputBuilder = LinkInputBuilder() @@ -80,6 +89,9 @@ void runTreeshakeTests( android: androidTargetNdkApi != null ? AndroidCodeConfig(targetNdkApi: androidTargetNdkApi) : null, + macOS: macOSTargetVersion != null + ? MacOSCodeConfig(targetVersion: macOSTargetVersion) + : null, ), ); @@ -95,9 +107,9 @@ void runTreeshakeTests( final linkOutput = linkOutputBuilder.build(); final asset = linkOutput.assets.code.first; - await expectMachineArchitecture(asset.file!, architecture); + await expectMachineArchitecture(asset.file!, architecture, targetOS); - final symbols = await nmReadSymbols(asset); + final symbols = await nmReadSymbols(asset, targetOS); if (clinker.linker != linkerAutoEmpty) { expect(symbols, contains('my_other_func')); expect(symbols, isNot(contains('my_func'))); diff --git a/pkgs/native_toolchain_c/test/clinker/treeshake_test.dart b/pkgs/native_toolchain_c/test/clinker/treeshake_test.dart index 86543b14c..9938f01a2 100644 --- a/pkgs/native_toolchain_c/test/clinker/treeshake_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/treeshake_test.dart @@ -2,9 +2,9 @@ // 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. -//TODO(mosuem): Enable for windows and mac. +// TODO(mosuem): Enable for windows. // See https://github.com/dart-lang/native/issues/1376. -@TestOn('linux') +@TestOn('linux || mac-os') library; import 'dart:io'; @@ -12,13 +12,18 @@ import 'dart:io'; import 'package:code_assets/code_assets.dart'; import 'package:test/test.dart'; +import '../helpers.dart'; import 'treeshake_helper.dart'; void main() { - if (!Platform.isLinux) { + if (!Platform.isLinux && !Platform.isMacOS) { // Avoid needing status files on Dart SDK CI. return; } - runTreeshakeTests(OS.current, [Architecture.current]); + runTreeshakeTests( + OS.current, + [Architecture.current], + macOSTargetVersion: OS.current == OS.macOS ? defaultMacOSVersion : null, + ); } diff --git a/pkgs/native_toolchain_c/test/helpers.dart b/pkgs/native_toolchain_c/test/helpers.dart index 3b951b7f2..f6a95ded6 100644 --- a/pkgs/native_toolchain_c/test/helpers.dart +++ b/pkgs/native_toolchain_c/test/helpers.dart @@ -245,11 +245,11 @@ Future readelf(String filePath, String flags) async { return result.stdout; } -Future nmReadSymbols(CodeAsset asset) async { +Future nmReadSymbols(CodeAsset asset, OS targetOS) async { final assetUri = asset.file!; final result = await runProcess( executable: Uri(path: 'nm'), - arguments: ['-D', assetUri.toFilePath()], + arguments: [if (targetOS != OS.macOS) '-D', assetUri.toFilePath()], logger: logger, ); @@ -309,9 +309,9 @@ const flutterAndroidNdkVersionLowestSupported = 21; /// From https://docs.flutter.dev/reference/supported-platforms. const flutterAndroidNdkVersionHighestSupported = 34; -/// File-format strings used by the `objdump` tool for binaries that run on a -/// given architecture. -const objdumpFileFormat = { +/// File-format strings used by the `objdump` tool for Android binaries that +/// run on a given architecture. +const objdumpFileFormatAndroid = { Architecture.arm: 'elf32-littlearm', Architecture.arm64: 'elf64-littleaarch64', Architecture.ia32: 'elf32-i386', @@ -319,15 +319,29 @@ const objdumpFileFormat = { Architecture.riscv64: 'elf64-littleriscv', }; +const objdumpFileFormatMacOS = { + Architecture.arm64: 'mach-o arm64', + Architecture.x64: 'mach-o 64-bit x86-64', +}; + +const targetOSToObjdumpFileFormat = { + OS.android: objdumpFileFormatAndroid, + OS.macOS: objdumpFileFormatMacOS, +}; + /// Checks that the provided [libUri] binary has the correct format to be -/// executed on the provided [target] architecture. +/// executed on the provided [targetArch] architecture. /// /// On Linux, the format of the binary is determined by `readelf`. On MacOS, /// the `objsdump` tool is used. -Future expectMachineArchitecture(Uri libUri, Architecture target) async { +Future expectMachineArchitecture( + Uri libUri, + Architecture targetArch, + OS targetOS, +) async { if (Platform.isLinux) { final machine = await readelfMachine(libUri.path); - expect(machine, contains(readElfMachine[target])); + expect(machine, contains(readElfMachine[targetArch])); } else if (Platform.isMacOS) { final result = await runProcess( executable: Uri.file('objdump'), @@ -338,6 +352,20 @@ Future expectMachineArchitecture(Uri libUri, Architecture target) async { final machine = result.stdout .split('\n') .firstWhere((e) => e.contains('file format')); - expect(machine, contains(objdumpFileFormat[target])); + expect( + machine, + contains(targetOSToObjdumpFileFormat[targetOS]![targetArch]), + ); } } + +List supportedArchitecturesFor(OS targetOS) => switch (targetOS) { + OS.macOS => [Architecture.arm64, Architecture.x64], + OS() => [ + Architecture.arm, + Architecture.arm64, + Architecture.ia32, + Architecture.x64, + Architecture.riscv64, + ], +};