diff --git a/CHANGELOG.md b/CHANGELOG.md index 36e572c..e148ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Update to Wasmer 2.0.0 - All WASM modules and isntances use a singleton store, to enable sharing of memory and functions. +- Add options to setup.dart for configuring the build. ## 0.1.0+1 diff --git a/bin/setup.dart b/bin/setup.dart index f29140d..81c05aa 100644 --- a/bin/setup.dart +++ b/bin/setup.dart @@ -3,27 +3,75 @@ // BSD-style license that can be found in the LICENSE file. // Builds the wasmer runtime library, to by used by package:wasm. Requires -// rustc, cargo, clang, and clang++. If a target triple is not specified, it -// will default to the host target. -// Usage: dart run wasm:setup [optional target-triple] +// Rust (rustc, rustup, cargo), and Clang (clang, clang++, ar). +// Usage: dart run wasm:setup +// For more details use the --help option. import 'dart:convert'; import 'dart:io' hide exit; +import 'package:args/args.dart'; import 'package:package_config/package_config.dart' show findPackageConfig; import 'package:wasm/src/shared.dart'; -Future main(List args) async { - if (args.length > 1) { - print('Usage: $invocationString [target-triple]'); - exitCode = 64; // bad usage +Future main(List arguments) async { + final parser = ArgParser() + ..addOption( + 'target', + abbr: 't', + help: 'Target triple. Defaults to host target.', + ) + ..addOption( + 'out-dir', + abbr: 'o', + help: 'Output directory. Defaults to the directory that package:wasm ' + 'searches.', + ) + ..addOption( + 'rustc', + help: "Path of rustc. Defaults to assuming it's in PATH variable.", + ) + ..addOption( + 'rustup', + help: "Path of rustup. Defaults to assuming it's in PATH variable.", + ) + ..addOption( + 'cargo', + help: "Path of cargo. Defaults to assuming it's in PATH variable.", + ) + ..addOption( + 'clang', + help: "Path of clang. Defaults to assuming it's in PATH variable.", + ) + ..addOption( + 'clangpp', + help: "Path of clang++. Defaults to assuming it's in PATH variable.", + ) + ..addOption( + 'ar', + help: "Path of ar. Defaults to assuming it's in PATH variable.", + ) + ..addOption( + 'sysroot', + help: 'Sysroot argument passed to linker.', + ) + ..addFlag( + 'help', + abbr: 'h', + negatable: false, + help: 'Show this help.', + ); + final args = parser.parse(arguments); + + if (args['help'] as bool) { + print('Usage: $invocationString [OPTION...]\n'); + print(parser.usage); + exitCode = 0; // ok return; } - final target = args.isNotEmpty ? args[0] : await _getTargetTriple(); - try { - await _main(target); + await _main(args); } on ProcessException catch (e) { final invocation = [e.executable, ...e.arguments].join(' '); print('FAILED with exit code ${e.errorCode} `$invocation`'); @@ -144,9 +192,9 @@ String _getWasmerLib(String os) { return 'libwasmer.a'; } -Future _getTargetTriple() async { +Future _getTargetTriple(String rustc) async { final _regexp = RegExp(r'^([^=]+)="(.*)"$'); - final process = await Process.start('rustc', ['--print', 'cfg']); + final process = await Process.start(rustc, ['--print', 'cfg']); final sub = process.stderr .transform(utf8.decoder) .transform(const LineSplitter()) @@ -170,21 +218,42 @@ Future _getTargetTriple() async { .join('-'); } -Future _run(String exe, List args) async { +Future _run( + String exe, + List args, { + Map? environment, +}) async { print('\n$exe ${args.join(' ')}\n'); - final process = - await Process.start(exe, args, mode: ProcessStartMode.inheritStdio); + final process = await Process.start( + exe, + args, + mode: ProcessStartMode.inheritStdio, + environment: environment, + ); final result = await process.exitCode; if (result != 0) { throw ProcessException(exe, args, '', result); } } -Future _main(String target) async { +String _toUpperUnderscore(String string) { + return string.toUpperCase().replaceAll('-', '_'); +} + +Future _main(ArgResults args) async { + final rustc = args['rustc'] as String? ?? 'rustc'; + final rustup = args['rustup'] as String? ?? 'rustup'; + final cargo = args['cargo'] as String? ?? 'cargo'; + final clang = args['clang'] as String? ?? 'clang'; + final clangpp = args['clangpp'] as String? ?? 'clang++'; + + final target = args['target'] as String? ?? await _getTargetTriple(rustc); final sdkDir = _getSdkDir(); final sdkIncDir = _getSdkIncDir(sdkDir); final srcDir = await _getSrcDir(); - final outDir = _getOutDir(Directory.current.uri); + final outDir = args['out-dir'] != null + ? Uri.directory(args['out-dir'] as String) + : _getOutDir(Directory.current.uri); final os = _getOsFromTarget(target); final outLib = outDir.resolve(_getOutLib(os)).toFilePath(); @@ -196,17 +265,32 @@ Future _main(String target) async { print('OS: $os'); print('Output library: $outLib'); + // Make sure rust libs are installed for the target. + await _run(rustup, ['target', 'add', target]); + // Build wasmer crate. - await _run('cargo', [ - 'build', - '--target', - target, - '--target-dir', - outDir.toFilePath(), - '--manifest-path', - srcDir.resolve('Cargo.toml').toFilePath(), - '--release' - ]); + await _run( + cargo, + [ + 'build', + '--target', + target, + '--target-dir', + outDir.toFilePath(), + '--manifest-path', + srcDir.resolve('Cargo.toml').toFilePath(), + '--release' + ], + environment: { + if (args['clangpp'] != null) ...{ + 'CC': clangpp, + 'CXX': clangpp, + 'LINKER': clangpp, + 'CARGO_TARGET_${_toUpperUnderscore(target)}_LINKER': clangpp, + }, + if (args['ar'] != null) 'AR': args['ar'] as String, + }, + ); // Hack around a bug with dart_api_dl_impl.h include path in dart_api_dl.c. const dartApiDlImplPath = 'include/internal/dart_api_dl_impl.h'; @@ -218,7 +302,7 @@ Future _main(String target) async { } // Build dart_api_dl.o. - await _run('clang', [ + await _run(clang, [ '-DDART_SHARED_LIB', '-DNDEBUG', '-fno-exceptions', @@ -237,7 +321,7 @@ Future _main(String target) async { ]); // Build finalizers.o. - await _run('clang++', [ + await _run(clangpp, [ '-DDART_SHARED_LIB', '-DNDEBUG', '-fno-exceptions', @@ -258,8 +342,9 @@ Future _main(String target) async { ]); // Link wasmer, dart_api_dl, and finalizers to create the output library. - await _run('clang++', [ + await _run(clang, [ '-shared', + if (args['sysroot'] != null) '--sysroot=${args['sysroot']}', if (os != 'windows') '-fPIC', if (os == 'windows') ...[ '-lws2_32', @@ -271,6 +356,7 @@ Future _main(String target) async { '-z', '/NODEFAULTLIB:MSVCRT', ], + '-lm', '-target', target, outDir.resolve('dart_api_dl.o').toFilePath(), diff --git a/lib/src/runtime.dart b/lib/src/runtime.dart index 38f0576..5a1c3e3 100644 --- a/lib/src/runtime.dart +++ b/lib/src/runtime.dart @@ -99,7 +99,7 @@ class _WasiStreamIterable extends Iterable> { String _getLibName() { if (Platform.isMacOS) return appleLib; - if (Platform.isLinux) return linuxLib; + if (Platform.isLinux || Platform.isAndroid) return linuxLib; if (Platform.isWindows) return windowsLib; // TODO(dartbug.com/37882): Support more platforms. throw WasmError('Wasm not currently supported on this platform'); @@ -107,11 +107,11 @@ String _getLibName() { String? _getLibPathFrom(Uri root) { final pkgRoot = packageRootUri(root); - return pkgRoot?.resolve('$wasmToolDir${_getLibName()}').toFilePath(); } String _getLibPath() { + if (Platform.isAndroid) return _getLibName(); var path = _getLibPathFrom(Platform.script.resolve('./')); if (path != null) return path; path = _getLibPathFrom(Directory.current.uri); @@ -119,6 +119,12 @@ String _getLibPath() { throw WasmError('Wasm library not found. Did you `$invocationString`?'); } +DynamicLibrary _loadDynamicLib() { + return Platform.isIOS + ? DynamicLibrary.process() + : DynamicLibrary.open(_getLibPath()); +} + String getSignatureString( String name, List argTypes, diff --git a/lib/src/runtime.g.dart b/lib/src/runtime.g.dart index c1cbb50..c4771bc 100644 --- a/lib/src/runtime.g.dart +++ b/lib/src/runtime.g.dart @@ -116,7 +116,7 @@ class WasmRuntime { late final WasmerWasmerLastErrorLengthFn _wasmer_last_error_length; late final WasmerWasmerLastErrorMessageFn _wasmer_last_error_message; - WasmRuntime._init() : _lib = _load_dynamic_lib() { + WasmRuntime._init() : _lib = _loadDynamicLib() { _Dart_InitializeApiDL = _lib.lookupFunction< NativeWasmerDartInitializeApiDLFn, WasmerDartInitializeApiDLFn>( 'Dart_InitializeApiDL', @@ -511,17 +511,6 @@ class WasmRuntime { _set_finalizer_for_store(this, _store); } - static DynamicLibrary _load_dynamic_lib() { - try { - return DynamicLibrary.open(_getLibPath()); - } catch (e) { - throw WasmError( - 'Failed to load Wasm dynamic library. ' - 'Have you run `dart run wasm:setup`?\n $e', - ); - } - } - Pointer compile( Object owner, Uint8List data, diff --git a/pubspec.yaml b/pubspec.yaml index a597a1d..0e29a60 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,6 +7,7 @@ environment: sdk: '>=2.12.0 <3.0.0' dependencies: + args: ^2.3.0 ffi: ^1.0.0 package_config: ^2.0.0 diff --git a/tool/runtime_template.dart.t b/tool/runtime_template.dart.t index f9b5fc6..b8f8651 100644 --- a/tool/runtime_template.dart.t +++ b/tool/runtime_template.dart.t @@ -18,7 +18,7 @@ class WasmRuntime { /* */ - WasmRuntime._init() : _lib = _load_dynamic_lib() { + WasmRuntime._init() : _lib = _loadDynamicLib() { /* */ if (_Dart_InitializeApiDL(NativeApi.initializeApiDLData) != 0) { @@ -32,17 +32,6 @@ class WasmRuntime { _set_finalizer_for_store(this, _store); } - static DynamicLibrary _load_dynamic_lib() { - try { - return DynamicLibrary.open(_getLibPath()); - } catch (e) { - throw WasmError( - 'Failed to load Wasm dynamic library. ' - 'Have you run `dart run wasm:setup`?\n $e', - ); - } - } - Pointer compile( Object owner, Uint8List data,