[c_compiler] Support setting the install name of dylibs (#55)
diff --git a/pkgs/c_compiler/lib/src/cbuilder/cbuilder.dart b/pkgs/c_compiler/lib/src/cbuilder/cbuilder.dart
index 00006b0..e8ec280 100644
--- a/pkgs/c_compiler/lib/src/cbuilder/cbuilder.dart
+++ b/pkgs/c_compiler/lib/src/cbuilder/cbuilder.dart
@@ -5,6 +5,7 @@
import 'dart:io';
import 'package:logging/logging.dart';
+import 'package:meta/meta.dart';
import 'package:native_assets_cli/native_assets_cli.dart';
import 'run_cbuilder.dart';
@@ -51,11 +52,17 @@
/// Used to output the [BuildOutput.dependencies].
final List<String> dartBuildFiles;
+ /// TODO(https://github.com/dart-lang/native/issues/54): Move to [BuildConfig]
+ /// or hide in public API.
+ @visibleForTesting
+ final Uri? installName;
+
CBuilder.library({
required this.name,
required this.assetName,
this.sources = const [],
this.dartBuildFiles = const ['build.dart'],
+ @visibleForTesting this.installName,
}) : _type = _CBuilderType.library;
CBuilder.executable({
@@ -63,7 +70,8 @@
this.sources = const [],
this.dartBuildFiles = const ['build.dart'],
}) : _type = _CBuilderType.executable,
- assetName = null;
+ assetName = null,
+ installName = null;
/// Runs the C Compiler with on this C build spec.
///
@@ -103,6 +111,7 @@
? libUri
: null,
executable: _type == _CBuilderType.executable ? exeUri : null,
+ installName: installName,
);
await task.run();
diff --git a/pkgs/c_compiler/lib/src/cbuilder/run_cbuilder.dart b/pkgs/c_compiler/lib/src/cbuilder/run_cbuilder.dart
index 3fe0725..7c8eb08 100644
--- a/pkgs/c_compiler/lib/src/cbuilder/run_cbuilder.dart
+++ b/pkgs/c_compiler/lib/src/cbuilder/run_cbuilder.dart
@@ -25,6 +25,13 @@
final Uri outDir;
final Target target;
+ /// The install of the [dynamicLibrary].
+ ///
+ /// Can be inspected with `otool -D <path-to-dylib>`.
+ ///
+ /// Can be modified with `install_name_tool`.
+ final Uri? installName;
+
RunCBuilder({
required this.buildConfig,
this.logger,
@@ -32,6 +39,7 @@
this.executable,
this.dynamicLibrary,
this.staticLibrary,
+ this.installName,
}) : outDir = buildConfig.outDir,
target = buildConfig.target,
assert([executable, dynamicLibrary, staticLibrary]
@@ -110,6 +118,10 @@
'-isysroot',
(await macosSdk(logger: logger)).toFilePath(),
],
+ if (installName != null) ...[
+ '-install_name',
+ installName!.toFilePath(),
+ ],
...sources.map((e) => e.toFilePath()),
if (executable != null) ...[
'-o',
diff --git a/pkgs/c_compiler/lib/src/native_toolchain/apple_clang.dart b/pkgs/c_compiler/lib/src/native_toolchain/apple_clang.dart
index 7d90283..9c7eb4c 100644
--- a/pkgs/c_compiler/lib/src/native_toolchain/apple_clang.dart
+++ b/pkgs/c_compiler/lib/src/native_toolchain/apple_clang.dart
@@ -45,3 +45,16 @@
),
]),
);
+
+/// The Mach-O dumping tool.
+///
+/// https://llvm.org/docs/CommandGuide/llvm-otool.html
+final Tool otool = Tool(
+ name: 'otool',
+ defaultResolver: CliVersionResolver(
+ wrappedResolver: PathToolResolver(
+ toolName: 'otool',
+ executableName: 'otool',
+ ),
+ ),
+);
diff --git a/pkgs/c_compiler/pubspec.yaml b/pkgs/c_compiler/pubspec.yaml
index abb5769..90d6ea4 100644
--- a/pkgs/c_compiler/pubspec.yaml
+++ b/pkgs/c_compiler/pubspec.yaml
@@ -12,6 +12,7 @@
cli_config: ^0.1.1
glob: ^2.1.1
logging: ^1.1.1
+ meta: ^1.9.1
# TODO(dacoharkes): Publish native_assets_cli first.
native_assets_cli:
path: ../native_assets_cli/
diff --git a/pkgs/c_compiler/test/cbuilder/cbuilder_cross_ios_test.dart b/pkgs/c_compiler/test/cbuilder/cbuilder_cross_ios_test.dart
index 415740e..2c05b9f 100644
--- a/pkgs/c_compiler/test/cbuilder/cbuilder_cross_ios_test.dart
+++ b/pkgs/c_compiler/test/cbuilder/cbuilder_cross_ios_test.dart
@@ -34,54 +34,81 @@
Target.iOSX64: '64-bit x86-64',
};
+ const name = 'add';
+
for (final linkMode in LinkMode.values) {
for (final targetIOSSdk in IOSSdk.values) {
for (final target in targets) {
if (target == Target.iOSX64 && targetIOSSdk == IOSSdk.iPhoneOs) {
continue;
}
- test('Cbuilder $linkMode library $targetIOSSdk $target', () async {
- await inTempDir((tempUri) async {
- final addCUri =
- packageUri.resolve('test/cbuilder/testfiles/add/src/add.c');
- const name = 'add';
- final buildConfig = BuildConfig(
- outDir: tempUri,
- packageRoot: tempUri,
- target: target,
- linkModePreference: linkMode == LinkMode.dynamic
- ? LinkModePreference.dynamic
- : LinkModePreference.static,
- targetIOSSdk: targetIOSSdk,
- );
- final buildOutput = BuildOutput();
+ final libName = target.os.libraryFileName(name, linkMode);
+ for (final installName in [
+ null,
+ if (linkMode == LinkMode.dynamic)
+ Uri.file('@executable_path/Frameworks/$libName'),
+ ]) {
+ test(
+ 'Cbuilder $linkMode library $targetIOSSdk $target'
+ ' ${installName ?? ''}'
+ .trim(), () async {
+ await inTempDir((tempUri) async {
+ final addCUri =
+ packageUri.resolve('test/cbuilder/testfiles/add/src/add.c');
+ final buildConfig = BuildConfig(
+ outDir: tempUri,
+ packageRoot: tempUri,
+ target: target,
+ linkModePreference: linkMode == LinkMode.dynamic
+ ? LinkModePreference.dynamic
+ : LinkModePreference.static,
+ targetIOSSdk: targetIOSSdk,
+ );
+ final buildOutput = BuildOutput();
- final cbuilder = CBuilder.library(
- name: name,
- assetName: name,
- sources: [addCUri.toFilePath()],
- );
- await cbuilder.run(
- buildConfig: buildConfig,
- buildOutput: buildOutput,
- logger: logger,
- );
+ final cbuilder = CBuilder.library(
+ name: name,
+ assetName: name,
+ sources: [addCUri.toFilePath()],
+ installName: installName,
+ );
+ await cbuilder.run(
+ buildConfig: buildConfig,
+ buildOutput: buildOutput,
+ logger: logger,
+ );
- final libUri =
- tempUri.resolve(target.os.libraryFileName(name, linkMode));
- final result = await runProcess(
- executable: Uri.file('objdump'),
- arguments: ['-t', libUri.path],
- logger: logger,
- );
- expect(result.exitCode, 0);
- final machine = result.stdout
- .split('\n')
- .firstWhere((e) => e.contains('file format'));
- expect(machine, contains(objdumpFileFormat[target]));
+ final libUri = tempUri.resolve(libName);
+ final objdumpResult = await runProcess(
+ executable: Uri.file('objdump'),
+ arguments: ['-t', libUri.path],
+ logger: logger,
+ );
+ expect(objdumpResult.exitCode, 0);
+ final machine = objdumpResult.stdout
+ .split('\n')
+ .firstWhere((e) => e.contains('file format'));
+ expect(machine, contains(objdumpFileFormat[target]));
+
+ if (linkMode == LinkMode.dynamic) {
+ final libInstallName =
+ await runOtoolInstallName(libUri, libName);
+ if (installName == null) {
+ // If no install path is passed, we have an absolute path.
+ final tempName =
+ tempUri.pathSegments.lastWhere((e) => e != '');
+ final pathEnding =
+ Uri.directory(tempName).resolve(libName).toFilePath();
+ expect(Uri.file(libInstallName).isAbsolute, true);
+ expect(libInstallName, contains(pathEnding));
+ } else {
+ expect(libInstallName, installName.toFilePath());
+ }
+ }
+ });
});
- });
+ }
}
}
}
diff --git a/pkgs/c_compiler/test/helpers.dart b/pkgs/c_compiler/test/helpers.dart
index 082e102..4c6be25 100644
--- a/pkgs/c_compiler/test/helpers.dart
+++ b/pkgs/c_compiler/test/helpers.dart
@@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:io';
+import 'package:c_compiler/src/native_toolchain/apple_clang.dart';
+import 'package:c_compiler/src/utils/run_process.dart';
import 'package:logging/logging.dart';
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:test/test.dart';
@@ -120,3 +122,25 @@
extension on String {
Uri asFileUri() => Uri.file(this);
}
+
+/// Looks up the install name of a dynamic library at [libraryUri].
+///
+/// Because `otool` output multiple names, [libraryName] as search parameter.
+Future<String> runOtoolInstallName(Uri libraryUri, String libraryName) async {
+ final otoolUri =
+ (await otool.defaultResolver!.resolve(logger: logger)).first.uri;
+ final otoolResult = await runProcess(
+ executable: otoolUri,
+ arguments: ['-l', libraryUri.path],
+ logger: logger,
+ );
+ expect(otoolResult.exitCode, 0);
+ // Leading space on purpose to differentiate from other types of names.
+ const installNameName = ' name ';
+ final installName = otoolResult.stdout
+ .split('\n')
+ .firstWhere((e) => e.contains(installNameName) && e.contains(libraryName))
+ .trim()
+ .split(' ')[1];
+ return installName;
+}
diff --git a/pkgs/c_compiler/test/native_toolchain/apple_clang_test.dart b/pkgs/c_compiler/test/native_toolchain/apple_clang_test.dart
index 998922c..77a7061 100644
--- a/pkgs/c_compiler/test/native_toolchain/apple_clang_test.dart
+++ b/pkgs/c_compiler/test/native_toolchain/apple_clang_test.dart
@@ -47,4 +47,12 @@
final satisfied = requirement.satisfy(resolved);
expect(satisfied?.length, 1);
});
+
+ test('otool test', () async {
+ final requirement = ToolRequirement(otool);
+ final resolved = await otool.defaultResolver!.resolve(logger: logger);
+ expect(resolved.isNotEmpty, true);
+ final satisfied = requirement.satisfy(resolved);
+ expect(satisfied?.length, 1);
+ });
}