diff --git a/.github/workflows/ffigen.yml b/.github/workflows/ffigen.yml index 764cba199..4f38efc22 100644 --- a/.github/workflows/ffigen.yml +++ b/.github/workflows/ffigen.yml @@ -37,7 +37,7 @@ jobs: channel: 'stable' - id: install name: Install dependencies - run: flutter pub get && flutter pub get --directory="example/shared_bindings" + run: flutter pub get && flutter pub get --directory="example/shared_bindings" && flutter pub get --directory="../objective_c" - name: Check formatting run: dart format --output=none --set-exit-if-changed . if: always() && steps.install.outcome == 'success' @@ -79,7 +79,7 @@ jobs: with: channel: 'stable' - name: Install dependencies - run: flutter pub get + run: flutter pub get && flutter pub get --directory="../objective_c" - name: Build test dylib and bindings run: dart test/setup.dart - name: Run VM tests and collect coverage @@ -110,9 +110,9 @@ jobs: with: channel: 'stable' - name: Install dependencies - run: flutter pub get + run: flutter pub get && flutter pub get --directory="../objective_c" - name: Build test dylib and bindings - run: dart test/setup.dart + run: dart test/setup.dart --main-thread-dispatcher - name: Run Flutter tests run: flutter test diff --git a/.github/workflows/ffigen_weekly.yml b/.github/workflows/ffigen_weekly.yml index 9dfb93ccb..abf752453 100644 --- a/.github/workflows/ffigen_weekly.yml +++ b/.github/workflows/ffigen_weekly.yml @@ -25,7 +25,7 @@ jobs: flutter-version: 3.19.0 channel: 'stable' - name: Install dependencies - run: flutter pub get + run: flutter pub get && flutter pub get --directory="../objective_c" - name: Build test dylib and bindings run: dart test/setup.dart - name: Run VM tests diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 32b22e01c..7a8fef9cd 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -52,7 +52,7 @@ jobs: - uses: nttld/setup-ndk@afb4c9964b521afb97c864b7d40b11e6911bd410 with: - ndk-version: r26b + ndk-version: r27 if: ${{ matrix.os != 'macos' }} - run: dart pub get diff --git a/.github/workflows/native_toolchain_c.yaml b/.github/workflows/native_toolchain_c.yaml index fe6ef7000..51d2c503a 100644 --- a/.github/workflows/native_toolchain_c.yaml +++ b/.github/workflows/native_toolchain_c.yaml @@ -40,7 +40,7 @@ jobs: - uses: nttld/setup-ndk@afb4c9964b521afb97c864b7d40b11e6911bd410 with: - ndk-version: r26b + ndk-version: r27 if: ${{ matrix.sdk == 'stable' }} - run: dart pub get diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index b27719370..4f3aa0f78 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -15,6 +15,8 @@ ObjC but before the invocation was received by Dart: https://github.com/dart-lang/native/issues/1571 - `sort:` config option now affects ObjC interface/protocol methods. +- Fix a bug where `NSRange` was not being imported from package:objective_c: + https://github.com/dart-lang/native/issues/1180 - __Breaking change__: Return structs from ObjC methods by value instead of taking a struct return pointer. diff --git a/pkgs/ffigen/lib/src/code_generator/compound.dart b/pkgs/ffigen/lib/src/code_generator/compound.dart index 910287bbb..837575a71 100644 --- a/pkgs/ffigen/lib/src/code_generator/compound.dart +++ b/pkgs/ffigen/lib/src/code_generator/compound.dart @@ -105,7 +105,7 @@ abstract class Compound extends BindingType { } bool get _isBuiltIn => - objCBuiltInFunctions?.isBuiltInCompound(originalName) ?? false; + objCBuiltInFunctions?.getBuiltInCompoundName(originalName) != null; @override BindingString toBindingString(Writer w) { @@ -188,7 +188,11 @@ abstract class Compound extends BindingType { bool get isIncompleteCompound => isIncomplete; @override - String getCType(Writer w) => _isBuiltIn ? '${w.objcPkgPrefix}.$name' : name; + String getCType(Writer w) { + final builtInName = + objCBuiltInFunctions?.getBuiltInCompoundName(originalName); + return builtInName != null ? '${w.objcPkgPrefix}.$builtInName' : name; + } @override String getNativeType({String varName = ''}) => '$nativeType $varName'; diff --git a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart index 445e666bd..a40c5bf7a 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart @@ -43,7 +43,7 @@ class ObjCBuiltInFunctions { ObjCImport('UnimplementedOptionalMethodException'); // Keep in sync with pkgs/objective_c/ffigen_objc.yaml. - static const builtInInterfaces = { + static const _builtInInterfaces = { 'DartProxy', 'DartProxyBuilder', 'NSArray', @@ -78,11 +78,11 @@ class ObjCBuiltInFunctions { 'NSValue', 'Protocol', }; - static const builtInCompounds = { - 'NSFastEnumerationState', - 'NSRange', + static const _builtInCompounds = { + 'NSFastEnumerationState': 'NSFastEnumerationState', + '_NSRange': 'NSRange', }; - static const builtInEnums = { + static const _builtInEnums = { 'NSBinarySearchingOptions', 'NSComparisonResult', 'NSDataBase64DecodingOptions', @@ -110,11 +110,11 @@ class ObjCBuiltInFunctions { // TODO(https://github.com/dart-lang/native/issues/1173): Ideally this check // would be based on more than just the name. bool isBuiltInInterface(String name) => - !generateForPackageObjectiveC && builtInInterfaces.contains(name); - bool isBuiltInCompound(String name) => - !generateForPackageObjectiveC && builtInCompounds.contains(name); + !generateForPackageObjectiveC && _builtInInterfaces.contains(name); + String? getBuiltInCompoundName(String name) => + generateForPackageObjectiveC ? null : _builtInCompounds[name]; bool isBuiltInEnum(String name) => - !generateForPackageObjectiveC && builtInEnums.contains(name); + !generateForPackageObjectiveC && _builtInEnums.contains(name); bool isNSObject(String name) => name == 'NSObject'; // We need to load a separate instance of objc_msgSend for each signature. If diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart index 3eaa3e2ca..ffb7ad729 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart @@ -208,10 +208,10 @@ class ObjCInterface extends BindingType with ObjCMethods { .invoke(w, target, sel, msgSendParams, structRetPtr: '_ptr'); s.write(''' final _ptr = $calloc<$returnTypeStr>(); - final _data = _ptr.cast<$uint8Type>().asTypedList( - $sizeOf<$returnTypeStr>(), finalizer: $calloc.nativeFree); $invoke; - return ${w.ffiLibraryPrefix}.Struct.create<$returnTypeStr>(_data); + final _finalizable = _ptr.cast<$uint8Type>().asTypedList( + $sizeOf<$returnTypeStr>(), finalizer: $calloc.nativeFree); + return ${w.ffiLibraryPrefix}.Struct.create<$returnTypeStr>(_finalizable); '''); } else { if (returnType != voidType) { diff --git a/pkgs/ffigen/test/native_objc_test/arc_config.yaml b/pkgs/ffigen/test/native_objc_test/arc_config.yaml index 175315d51..62183576d 100644 --- a/pkgs/ffigen/test/native_objc_test/arc_config.yaml +++ b/pkgs/ffigen/test/native_objc_test/arc_config.yaml @@ -1,7 +1,9 @@ name: ArcTestObjCLibrary description: 'Tests ARC' language: objc -output: 'arc_bindings.dart' +output: + bindings: 'arc_bindings.dart' + objc-bindings: 'arc_bindings.m' exclude-all-by-default: true functions: include: @@ -10,8 +12,9 @@ functions: objc-interfaces: include: - ArcTestObject + - ArcDtorTestObject headers: entry-points: - - 'arc_test.m' + - 'arc_test.h' preamble: | // ignore_for_file: camel_case_types, non_constant_identifier_names, unnecessary_non_null_assertion, unused_element, unused_field diff --git a/pkgs/ffigen/test/native_objc_test/arc_test.dart b/pkgs/ffigen/test/native_objc_test/arc_test.dart index c996d9c13..8c4f6a89f 100644 --- a/pkgs/ffigen/test/native_objc_test/arc_test.dart +++ b/pkgs/ffigen/test/native_objc_test/arc_test.dart @@ -20,7 +20,7 @@ import 'util.dart'; void main() { late ArcTestObjCLibrary lib; - group('Reference counting', () { + group('ARC', () { setUpAll(() { // TODO(https://github.com/dart-lang/native/issues/1068): Remove this. DynamicLibrary.open('../objective_c/test/objective_c.dylib'); @@ -474,5 +474,23 @@ void main() { expect(counter.value, 0); calloc.free(counter); }, skip: !canDoGC); + + test('Destroy on main thread', () async { + const numTestObjects = 1000; + + final dtorCounter = calloc(); + final dtorOnMainThreadCounter = calloc(); + final objects = []; + for (var i = 0; i < numTestObjects; ++i) { + objects.add(ArcDtorTestObject.alloc().initWithCounters_onMainThread_( + dtorCounter, dtorOnMainThreadCounter)); + } + objects.clear(); + + while (dtorCounter.value < numTestObjects) { + await flutterDoGC(); + } + expect(dtorOnMainThreadCounter.value, numTestObjects); + }, skip: !isFlutterTester); }); } diff --git a/pkgs/ffigen/test/native_objc_test/arc_test.h b/pkgs/ffigen/test/native_objc_test/arc_test.h new file mode 100644 index 000000000..e6a84e0c2 --- /dev/null +++ b/pkgs/ffigen/test/native_objc_test/arc_test.h @@ -0,0 +1,41 @@ +// Copyright (c) 2024, 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. + +#import + +void objc_autoreleasePoolPop(void *pool); +void *objc_autoreleasePoolPush(); + +@interface ArcTestObject : NSObject { + int32_t* counter; +} + ++ (instancetype)allocTheThing; ++ (instancetype)newWithCounter:(int32_t*) _counter; +- (instancetype)initWithCounter:(int32_t*) _counter; ++ (ArcTestObject*)makeAndAutorelease:(int32_t*) _counter; +- (void)setCounter:(int32_t*) _counter; +- (void)dealloc; +- (ArcTestObject*)copyMe; +- (ArcTestObject*)mutableCopyMe; +- (id)copyWithZone:(NSZone*) zone; +- (ArcTestObject*)returnsRetained NS_RETURNS_RETAINED; +- (ArcTestObject*)copyMeNoRetain __attribute__((ns_returns_not_retained)); +- (ArcTestObject*)copyMeAutorelease __attribute__((ns_returns_autoreleased)); +- (ArcTestObject*)copyMeConsumeSelf __attribute__((ns_consumes_self)); ++ (void)consumeArg:(ArcTestObject*) __attribute((ns_consumed)) arg; + +@property (assign) ArcTestObject* assignedProperty; +@property (retain) ArcTestObject* retainedProperty; +@property (copy) ArcTestObject* copiedProperty; + +@end + +@interface ArcDtorTestObject : NSObject { + int32_t* dtorCounter; + int32_t* dtorOnMainThreadCounter; +} +- (instancetype)initWithCounters:(int32_t*) _dtorCounter + onMainThread: (int32_t*) _dtorOnMainThreadCounter; +@end diff --git a/pkgs/ffigen/test/native_objc_test/arc_test.m b/pkgs/ffigen/test/native_objc_test/arc_test.m index dfda41242..291c5e396 100644 --- a/pkgs/ffigen/test/native_objc_test/arc_test.m +++ b/pkgs/ffigen/test/native_objc_test/arc_test.m @@ -2,42 +2,15 @@ // 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. -#import +#import +#include "arc_test.h" #include "util.h" #if !__has_feature(objc_arc) #error "This file must be compiled with ARC enabled" #endif -void objc_autoreleasePoolPop(void *pool); -void *objc_autoreleasePoolPush(); - -@interface ArcTestObject : NSObject { - int32_t* counter; -} - -+ (instancetype)allocTheThing; -+ (instancetype)newWithCounter:(int32_t*) _counter; -- (instancetype)initWithCounter:(int32_t*) _counter; -+ (ArcTestObject*)makeAndAutorelease:(int32_t*) _counter; -- (void)setCounter:(int32_t*) _counter; -- (void)dealloc; -- (ArcTestObject*)copyMe; -- (ArcTestObject*)mutableCopyMe; -- (id)copyWithZone:(NSZone*) zone; -- (ArcTestObject*)returnsRetained NS_RETURNS_RETAINED; -- (ArcTestObject*)copyMeNoRetain __attribute__((ns_returns_not_retained)); -- (ArcTestObject*)copyMeAutorelease __attribute__((ns_returns_autoreleased)); -- (ArcTestObject*)copyMeConsumeSelf __attribute__((ns_consumes_self)); -+ (void)consumeArg:(ArcTestObject*) __attribute((ns_consumed)) arg; - -@property (assign) ArcTestObject* assignedProperty; -@property (retain) ArcTestObject* retainedProperty; -@property (copy) ArcTestObject* copiedProperty; - -@end - @implementation ArcTestObject + (instancetype)allocTheThing { @@ -98,3 +71,20 @@ - (ArcTestObject*)copyMeConsumeSelf __attribute__((ns_consumes_self)) { + (void)consumeArg:(ArcTestObject*) __attribute((ns_consumed)) arg {} @end + +@implementation ArcDtorTestObject + +- (instancetype)initWithCounters:(int32_t*) _dtorCounter + onMainThread: (int32_t*) _dtorOnMainThreadCounter { + dtorCounter = _dtorCounter; + dtorOnMainThreadCounter = _dtorOnMainThreadCounter; + return [super init]; +} + +- (void)dealloc { + ++*dtorCounter; + if ([NSThread isMainThread]) { + ++*dtorOnMainThreadCounter; + } +} +@end diff --git a/pkgs/ffigen/test/native_objc_test/ns_range_test.dart b/pkgs/ffigen/test/native_objc_test/ns_range_test.dart new file mode 100644 index 000000000..64dd0af19 --- /dev/null +++ b/pkgs/ffigen/test/native_objc_test/ns_range_test.dart @@ -0,0 +1,44 @@ +// Copyright (c) 2024, 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. + +// Objective C support is only available on mac. +@TestOn('mac-os') + +import 'dart:ffi'; +import 'dart:io'; + +import 'package:ffi/ffi.dart'; +import 'package:ffigen/ffigen.dart'; +import 'package:ffigen/src/config_provider/config.dart'; +import 'package:ffigen/src/config_provider/config_types.dart'; +import 'package:logging/logging.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:test/test.dart'; +import '../test_utils.dart'; +import 'util.dart'; + +void main() { + group('NSRange', () { + late final String bindings; + setUpAll(() { + final config = Config( + wrapperName: 'NSRangeTestObjCLibrary', + language: Language.objc, + output: Uri.file('test/native_objc_test/ns_range_bindings.dart'), + entryPoints: [Uri.file('test/native_objc_test/ns_range_test.m')], + formatOutput: false, + objcInterfaces: DeclarationFilters.include({'SFTranscriptionSegment'}), + ); + FfiGen(logLevel: Level.SEVERE).run(config); + bindings = File('test/native_objc_test/ns_range_bindings.dart') + .readAsStringSync(); + }); + + test('interfaces', () { + // Regression test for https://github.com/dart-lang/native/issues/1180. + expect(bindings.split('\n'), + isNot(contains(matches(RegExp(r'class.*NSRange.*Struct'))))); + }); + }); +} diff --git a/pkgs/ffigen/test/native_objc_test/ns_range_test.m b/pkgs/ffigen/test/native_objc_test/ns_range_test.m new file mode 100644 index 000000000..766f58268 --- /dev/null +++ b/pkgs/ffigen/test/native_objc_test/ns_range_test.m @@ -0,0 +1,5 @@ +// Copyright (c) 2024, 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. + +#import diff --git a/pkgs/ffigen/test/native_objc_test/setup.dart b/pkgs/ffigen/test/native_objc_test/setup.dart index eccf1ddf7..36c93260b 100644 --- a/pkgs/ffigen/test/native_objc_test/setup.dart +++ b/pkgs/ffigen/test/native_objc_test/setup.dart @@ -5,6 +5,8 @@ import 'dart:async'; import 'dart:io'; +import 'package:args/args.dart'; + // All ObjC source files are compiled with ARC enabled except these. const arcDisabledFiles = { 'ref_count_test.m', @@ -152,16 +154,24 @@ Future clean(List testNames) async { } Future main(List arguments) async { + final parser = ArgParser(); + parser.addFlag('clean'); + parser.addFlag('main-thread-dispatcher'); + final args = parser.parse(arguments); + // Allow running this script directly from any path (or an IDE). Directory.current = Platform.script.resolve('.').toFilePath(); if (!Platform.isMacOS) { throw OSError('Objective C tests are only supported on MacOS'); } - if (arguments.isNotEmpty && arguments[0] == 'clean') { + if (args.flag('clean')) { return await clean(_getTestNames()); } - await _runDart(['../objective_c/test/setup.dart']); - return await build(arguments.isNotEmpty ? arguments : _getTestNames()); + await _runDart([ + '../objective_c/test/setup.dart', + if (args.flag('main-thread-dispatcher')) '--main-thread-dispatcher', + ]); + return await build(args.rest.isNotEmpty ? args.rest : _getTestNames()); } diff --git a/pkgs/ffigen/test/setup.dart b/pkgs/ffigen/test/setup.dart index 3439901f2..56d99bc6d 100644 --- a/pkgs/ffigen/test/setup.dart +++ b/pkgs/ffigen/test/setup.dart @@ -8,10 +8,12 @@ import 'dart:async'; import 'dart:io'; -Future _run(String subdir, String script) async { +import 'package:args/args.dart'; + +Future _run(String subdir, String script, List flags) async { final dir = Platform.script.resolve('$subdir/'); print('\nRunning $script in ${dir.toFilePath()}'); - final args = ['run', dir.resolve(script).toFilePath()]; + final args = ['run', dir.resolve(script).toFilePath(), ...flags]; final process = await Process.start( Platform.executable, args, @@ -25,10 +27,16 @@ Future _run(String subdir, String script) async { } } -Future main() async { - await _run('native_test', 'build_test_dylib.dart'); +Future main(List arguments) async { + final parser = ArgParser(); + parser.addFlag('main-thread-dispatcher'); + final args = parser.parse(arguments); + + await _run('native_test', 'build_test_dylib.dart', []); if (Platform.isMacOS) { - await _run('native_objc_test', 'setup.dart'); + await _run('native_objc_test', 'setup.dart', [ + if (args.flag('main-thread-dispatcher')) '--main-thread-dispatcher', + ]); } print('\nSuccess :)\n'); } diff --git a/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/PortProxyBuilder.java b/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/PortProxyBuilder.java index f4026189e..2f70b2b5d 100644 --- a/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/PortProxyBuilder.java +++ b/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/PortProxyBuilder.java @@ -5,8 +5,7 @@ package com.github.dart_lang.jni; import java.lang.reflect.*; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.*; public class PortProxyBuilder implements InvocationHandler { private static final PortCleaner cleaner = new PortCleaner(); @@ -28,6 +27,7 @@ private static final class DartImplementation { private boolean built = false; private final long isolateId; private final HashMap implementations = new HashMap<>(); + private final HashSet asyncMethods = new HashSet<>(); public PortProxyBuilder(long isolateId) { this.isolateId = isolateId; @@ -72,8 +72,10 @@ private static void appendType(StringBuilder descriptor, Class type) { } } - public void addImplementation(String binaryName, long port, long functionPointer) { + public void addImplementation( + String binaryName, long port, long functionPointer, List asyncMethods) { implementations.put(binaryName, new DartImplementation(port, functionPointer)); + this.asyncMethods.addAll(asyncMethods); } public Object build() throws ClassNotFoundException { @@ -106,21 +108,28 @@ private static native Object[] _invoke( long functionPtr, Object proxy, String methodDescriptor, - Object[] args); + Object[] args, + boolean isBlocking); private static native void _cleanUp(long resultPtr); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { DartImplementation implementation = implementations.get(method.getDeclaringClass().getName()); + String descriptor = getDescriptor(method); + boolean isBlocking = !asyncMethods.contains(descriptor); Object[] result = _invoke( implementation.port, isolateId, implementation.pointer, proxy, - getDescriptor(method), - args); + descriptor, + args, + isBlocking); + if (!isBlocking) { + return null; + } _cleanUp((Long) result[0]); if (result[1] instanceof DartException) { Throwable cause = ((DartException) result[1]).cause; diff --git a/pkgs/jni/lib/src/jimplementer.dart b/pkgs/jni/lib/src/jimplementer.dart index ea6784b7b..8b66b8a8f 100644 --- a/pkgs/jni/lib/src/jimplementer.dart +++ b/pkgs/jni/lib/src/jimplementer.dart @@ -5,15 +5,11 @@ import 'dart:ffi'; import 'dart:isolate'; -import 'package:ffi/ffi.dart'; import 'package:meta/meta.dart' show internal; +import '../jni.dart'; import 'accessors.dart'; import 'jni.dart'; -import 'jobject.dart'; -import 'jreference.dart'; -import 'lang/jstring.dart'; -import 'third_party/generated_bindings.dart'; import 'types.dart'; /// A builder that builds proxy objects that implement one or more interfaces. @@ -50,17 +46,17 @@ class JImplementer extends JObject { static final _addImplementationId = _class.instanceMethodId( r'addImplementation', - r'(Ljava/lang/String;JJ)V', + r'(Ljava/lang/String;JJLjava/util/List;)V', ); static final _addImplementation = ProtectedJniExtensions.lookup< NativeFunction< JThrowablePtr Function(Pointer, JMethodIDPtr, - VarArgs<(Pointer, Int64, Int64)>)>>( + VarArgs<(Pointer, Int64, Int64, Pointer)>)>>( 'globalEnv_CallVoidMethod') .asFunction< - JThrowablePtr Function( - Pointer, JMethodIDPtr, Pointer, int, int)>(); + JThrowablePtr Function(Pointer, JMethodIDPtr, Pointer, + int, int, Pointer)>(); /// Should not be used directly. /// @@ -71,15 +67,22 @@ class JImplementer extends JObject { RawReceivePort port, Pointer> pointer, + List asyncMethods, ) { using((arena) { _addImplementation( - reference.pointer, - _addImplementationId as JMethodIDPtr, - (binaryName.toJString()..releasedBy(arena)).reference.pointer, - port.sendPort.nativePort, - pointer.address) - .check(); + reference.pointer, + _addImplementationId as JMethodIDPtr, + (binaryName.toJString()..releasedBy(arena)).reference.pointer, + port.sendPort.nativePort, + pointer.address, + (asyncMethods + .map((m) => m.toJString()..releasedBy(arena)) + .toJList(JString.type) + ..releasedBy(arena)) + .reference + .pointer, + ).check(); }); } diff --git a/pkgs/jni/lib/src/jni.dart b/pkgs/jni/lib/src/jni.dart index 71967cd00..6d19e844d 100644 --- a/pkgs/jni/lib/src/jni.dart +++ b/pkgs/jni/lib/src/jni.dart @@ -273,7 +273,10 @@ extension ProtectedJniExtensions on Jni { /// Returns the result of a callback. static void returnResult( Pointer result, JObjectPtr object) async { - Jni._bindings.resultFor(result, object); + // The result is `nullptr` when the callback is a listener. + if (result != nullptr) { + Jni._bindings.resultFor(result, object); + } } static Dart_FinalizableHandle newJObjectFinalizableHandle( diff --git a/pkgs/jni/src/dartjni.c b/pkgs/jni/src/dartjni.c index 0ae465ef6..26b9cc351 100644 --- a/pkgs/jni/src/dartjni.c +++ b/pkgs/jni/src/dartjni.c @@ -418,14 +418,20 @@ Java_com_github_dart_1lang_jni_PortProxyBuilder__1invoke( jlong functionPtr, jobject proxy, jstring methodDescriptor, - jobjectArray args) { - CallbackResult* result = (CallbackResult*)malloc(sizeof(CallbackResult)); - if (isolateId != (jlong)Dart_CurrentIsolate_DL()) { - init_lock(&result->lock); - init_cond(&result->cond); - acquire_lock(&result->lock); - result->ready = 0; - result->object = NULL; + jobjectArray args, + jboolean isBlocking) { + CallbackResult* result = NULL; + if (isBlocking) { + result = (CallbackResult*)malloc(sizeof(CallbackResult)); + } + if (isolateId != (jlong)Dart_CurrentIsolate_DL() || !isBlocking) { + if (isBlocking) { + init_lock(&result->lock); + init_cond(&result->cond); + acquire_lock(&result->lock); + result->ready = 0; + result->object = NULL; + } Dart_CObject c_result; c_result.type = Dart_CObject_kInt64; @@ -448,18 +454,24 @@ Java_com_github_dart_1lang_jni_PortProxyBuilder__1invoke( Dart_PostCObject_DL(port, &c_post); - while (!result->ready) { - wait_for(&result->cond, &result->lock); - } + if (isBlocking) { + while (!result->ready) { + wait_for(&result->cond, &result->lock); + } - release_lock(&result->lock); - destroy_lock(&result->lock); - destroy_cond(&result->cond); + release_lock(&result->lock); + destroy_lock(&result->lock); + destroy_cond(&result->cond); + } } else { result->object = ((jobject(*)(uint64_t, jobject, jobject))functionPtr)( port, (*env)->NewGlobalRef(env, methodDescriptor), (*env)->NewGlobalRef(env, args)); } + if (!isBlocking) { + // No result is created in this case, there is nothing to clean up either. + return NULL; + } // Returning an array of length 2. // [0]: The result pointer, used for cleaning up the global reference, and // freeing the memory since we passed the ownership to Java. diff --git a/pkgs/jnigen/CHANGELOG.md b/pkgs/jnigen/CHANGELOG.md index 12b2f6120..dc71ab96f 100644 --- a/pkgs/jnigen/CHANGELOG.md +++ b/pkgs/jnigen/CHANGELOG.md @@ -19,6 +19,7 @@ - Added `JImplementer` which enables building an object that implements multiple Java interfaces. Each interface now has a static `implementIn` method that takes a `JImplementer` and the implementation object. +- Added the ability to implement void-returning interface methods as listeners. - Generating identifiers that start with an underscore (`_`) and making them public by prepending a dollar sign. - Fixed an issue where inheriting a generic class could generate incorrect code. diff --git a/pkgs/jnigen/docs/interface_implementation.md b/pkgs/jnigen/docs/interface_implementation.md index 01a25efce..f96c10642 100644 --- a/pkgs/jnigen/docs/interface_implementation.md +++ b/pkgs/jnigen/docs/interface_implementation.md @@ -36,20 +36,24 @@ class Runnable extends JObject { ) { /* ... */ } } -abstract interface class $Runnable { +abstract mixin class $Runnable { factory $Runnable({ required void Function() run, + bool run$async, }) = _$Runnable; + bool get run$async => false; void run(); } class _$Runnable implements $Runnable { _$Runnable({ required void Function() run, + this.run$async = false; }) : _run = run; final void Function() _run; + final bool run$async; void run() { return _run(); @@ -83,7 +87,7 @@ implementing the interface in Java instead of using the lambdas: ```java // Java -public class Printer implements Runnable { +public class Printer with Runnable { private final String text; public Printer(String text) { @@ -104,7 +108,7 @@ You can do the same in Dart by creating a subclass that implements `$Runnable`: ```dart // Dart -class Printer implements $Runnable { +class Printer with $Runnable { final String text; Printer(this.text); @@ -120,6 +124,42 @@ And similarly write `Runnable.implement(Printer('hello'))` and `Runnable.implement(Printer('world'))`, to create multiple Runnables and share common logic. +### Implement as a listener + +By default, when any of methods of the implemented interface gets called, the +caller will block until the callee returns a result. + +Void-returning functions don't have to return a result, so we can choose to not +block the caller when the method is just a listener. To signal this, pass `true` +to `$async` argument when implementing the interface inline: + +```dart +// Dart +final runnable = Runnable.implement($Runnable( + run: () => print('hello'), + run$async: true, // This makes the run method non-blocking. +)); +``` + +Similarly, when subclassing + +```dart +// Dart +class Printer with $Runnable { + final String text; + + Printer(this.text); + + @override + void run() { + print(text); + } + + @override + bool get run$async => true; // This makes the run method non-blocking. +``` + + ### Implement multiple interfaces To implement more than one interface, use a `JImplementer` from `package:jni`. @@ -141,5 +181,5 @@ possible to make it a `Closable` by passing in `Closable.type` to ```dart // Dart -final closable = object.castTo(Closable.type); +final closable = object.as(Closable.type); ``` diff --git a/pkgs/jnigen/lib/src/bindings/dart_generator.dart b/pkgs/jnigen/lib/src/bindings/dart_generator.dart index 1c7420c43..1fe8186d2 100644 --- a/pkgs/jnigen/lib/src/bindings/dart_generator.dart +++ b/pkgs/jnigen/lib/src/bindings/dart_generator.dart @@ -469,6 +469,18 @@ class $name$typeParamsDef extends $superName { r'${node.binaryName}', \$p, _\$invokePointer, + [ +'''); + final interfaceAsyncMethod = _InterfaceIfAsyncMethod( + resolver, + s, + implClassName: implClassName, + ); + for (final method in node.methods) { + method.accept(interfaceAsyncMethod); + } + s.write(''' + ], ); final \$a = \$p.sendPort.nativePort; _\$impls[\$a] = \$impl; @@ -516,12 +528,12 @@ class $name$typeParamsDef extends $superName { [ ...typeParams .map((typeParam) => 'required $_jType<\$$typeParam> $typeParam,'), - ...node.methods.accept(_ConcreteImplClosureCtorArg(resolver)), + ...node.methods.accept(_AbstractImplFactoryArg(resolver)), ].join(_newLine(depth: 2)), '}', ); s.write(''' -abstract interface class $implClassName$typeParamsDef { +abstract mixin class $implClassName$typeParamsDef { factory $implClassName( $abstractFactoryArgs ) = _$implClassName; @@ -745,22 +757,11 @@ class _TypeClass { /// Generates the type class. class _TypeClassGenerator extends TypeVisitor<_TypeClass> { final bool isConst; - - /// Whether or not to return the equivalent boxed type class for primitives. - /// Only for interface implemetation. - final bool boxPrimitives; - - /// Whether or not to find the correct type variable from the static map. - /// Only for interface implemetation. - final bool typeVarFromMap; - final Resolver resolver; _TypeClassGenerator( this.resolver, { this.isConst = true, - this.boxPrimitives = false, - this.typeVarFromMap = false, }); @override @@ -768,8 +769,6 @@ class _TypeClassGenerator extends TypeVisitor<_TypeClass> { final innerTypeClass = node.type.accept(_TypeClassGenerator( resolver, isConst: false, - boxPrimitives: false, - typeVarFromMap: typeVarFromMap, )); final ifConst = innerTypeClass.canBeConst && isConst ? 'const ' : ''; return _TypeClass( @@ -788,8 +787,6 @@ class _TypeClassGenerator extends TypeVisitor<_TypeClass> { final definedTypeClasses = node.params.accept(_TypeClassGenerator( resolver, isConst: false, - boxPrimitives: false, - typeVarFromMap: typeVarFromMap, )); // Can be const if all the type parameters are defined and each of them are @@ -831,15 +828,12 @@ class _TypeClassGenerator extends TypeVisitor<_TypeClass> { @override _TypeClass visitPrimitiveType(PrimitiveType node) { final ifConst = isConst ? 'const ' : ''; - final name = boxPrimitives ? 'J${node.boxedName}' : 'j${node.name}'; + final name = 'j${node.name}'; return _TypeClass('$ifConst$_jni.${name}Type()', true); } @override _TypeClass visitTypeVar(TypeVar node) { - if (typeVarFromMap) { - return _TypeClass('_\$impls[\$p]!.${node.name}', false); - } return _TypeClass(node.name, false); } @@ -856,6 +850,23 @@ class _TypeClassGenerator extends TypeVisitor<_TypeClass> { } } +class _ImplTypeClass extends _TypeClassGenerator { + _ImplTypeClass(super.resolver); + + @override + _TypeClass visitTypeVar(TypeVar node) { + // Get the concrete type variable from the static map. + return _TypeClass('_\$impls[\$p]!.${node.name}', false); + } + + @override + _TypeClass visitPrimitiveType(PrimitiveType node) { + final ifConst = isConst ? 'const ' : ''; + final name = 'J${node.boxedName}'; + return _TypeClass('$ifConst$_jni.${name}Type()', true); + } +} + class _TypeParamGenerator extends Visitor { final bool withExtends; @@ -1484,6 +1495,9 @@ class _AbstractImplMethod extends Visitor { final name = node.finalName; final args = node.params.accept(_ParamDef(resolver)).join(', '); s.writeln(' $returnType $name($args);'); + if (returnType == 'void') { + s.writeln(' bool get $name\$async => false;'); + } } } @@ -1500,6 +1514,29 @@ class _ConcreteImplClosureDef extends Visitor { final name = node.finalName; final args = node.params.accept(_ParamDef(resolver)).join(', '); s.writeln(' final $returnType Function($args) _$name;'); + if (returnType == 'void') { + s.writeln(' final bool $name\$async;'); + } + } +} + +/// Closure argument for the factory of the implementation's abstract class. +/// Used for interface implementation. +class _AbstractImplFactoryArg extends Visitor { + final Resolver resolver; + + _AbstractImplFactoryArg(this.resolver); + + @override + String visit(Method node) { + final returnType = node.returnType.accept(_TypeGenerator(resolver)); + final name = node.finalName; + final args = node.params.accept(_ParamDef(resolver)).join(', '); + final functionArg = 'required $returnType Function($args) $name,'; + if (node.returnType.name == 'void') { + return '$functionArg bool $name\$async,'; + } + return functionArg; } } @@ -1515,7 +1552,11 @@ class _ConcreteImplClosureCtorArg extends Visitor { final returnType = node.returnType.accept(_TypeGenerator(resolver)); final name = node.finalName; final args = node.params.accept(_ParamDef(resolver)).join(', '); - return 'required $returnType Function($args) $name,'; + final functionArg = 'required $returnType Function($args) $name,'; + if (node.returnType.name == 'void') { + return '$functionArg this.$name\$async = false,'; + } + return functionArg; } } @@ -1568,6 +1609,27 @@ class _InterfaceMethodIf extends Visitor { } } +/// The if statement within the async methods list to conditionally add methods. +class _InterfaceIfAsyncMethod extends Visitor { + final Resolver resolver; + final StringSink s; + final String implClassName; + + _InterfaceIfAsyncMethod(this.resolver, this.s, {required this.implClassName}); + + @override + void visit(Method node) { + if (node.returnType.name != 'void') { + return; + } + final signature = node.javaSig; + final name = node.finalName; + s.write(''' + if (\$impl.$name\$async) r'$signature', +'''); + } +} + /// Generates casting to the correct parameter type from the list of JObject /// arguments received from the call to the proxy class. class _InterfaceParamCast extends Visitor { @@ -1583,13 +1645,7 @@ class _InterfaceParamCast extends Visitor { @override void visit(Param node) { - final typeClass = node.type - .accept(_TypeClassGenerator( - resolver, - boxPrimitives: true, - typeVarFromMap: true, - )) - .name; + final typeClass = node.type.accept(_ImplTypeClass(resolver)).name; s.write('\$a[$paramIndex].as($typeClass, releaseOriginal: true)'); if (node.type.kind == Kind.primitive) { // Convert to Dart type. @@ -1606,7 +1662,7 @@ class _InterfaceParamCast extends Visitor { /// /// Since Dart doesn't know that this global reference is still used, it might /// garbage collect it via `NativeFinalizer` thus making it invalid. -/// This passes the ownership to Java using `setAsReleased`. +/// This passes the ownership to Java using `toPointer()`. /// /// `toPointer` detaches the object from the `NativeFinalizer` and Java /// will clean up the global reference afterwards. diff --git a/pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart b/pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart index 08303ee91..0707eb370 100644 --- a/pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart +++ b/pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart @@ -4865,6 +4865,9 @@ class MyInterface<$T extends _$jni.JObject> extends _$jni.JObject { r'com.github.dart_lang.jnigen.interfaces.MyInterface', $p, _$invokePointer, + [ + if ($impl.voidCallback$async) r'voidCallback(Ljava/lang/String;)V', + ], ); final $a = $p.sendPort.nativePort; _$impls[$a] = $impl; @@ -4883,10 +4886,11 @@ class MyInterface<$T extends _$jni.JObject> extends _$jni.JObject { static _$core.Map get $impls => _$impls; } -abstract interface class $MyInterface<$T extends _$jni.JObject> { +abstract mixin class $MyInterface<$T extends _$jni.JObject> { factory $MyInterface({ required _$jni.JObjType<$T> T, required void Function(_$jni.JString s) voidCallback, + bool voidCallback$async, required _$jni.JString Function(_$jni.JString s) stringCallback, required $T Function($T t) varCallback, required int Function(int a, bool b, int c, double d) manyPrimitives, @@ -4895,6 +4899,7 @@ abstract interface class $MyInterface<$T extends _$jni.JObject> { _$jni.JObjType<$T> get T; void voidCallback(_$jni.JString s); + bool get voidCallback$async => false; _$jni.JString stringCallback(_$jni.JString s); $T varCallback($T t); int manyPrimitives(int a, bool b, int c, double d); @@ -4904,6 +4909,7 @@ class _$MyInterface<$T extends _$jni.JObject> implements $MyInterface<$T> { _$MyInterface({ required this.T, required void Function(_$jni.JString s) voidCallback, + this.voidCallback$async = false, required _$jni.JString Function(_$jni.JString s) stringCallback, required $T Function($T t) varCallback, required int Function(int a, bool b, int c, double d) manyPrimitives, @@ -4916,6 +4922,7 @@ class _$MyInterface<$T extends _$jni.JObject> implements $MyInterface<$T> { final _$jni.JObjType<$T> T; final void Function(_$jni.JString s) _voidCallback; + final bool voidCallback$async; final _$jni.JString Function(_$jni.JString s) _stringCallback; final $T Function($T t) _varCallback; final int Function(int a, bool b, int c, double d) _manyPrimitives; @@ -5269,6 +5276,9 @@ class MyRunnable extends _$jni.JObject { r'com.github.dart_lang.jnigen.interfaces.MyRunnable', $p, _$invokePointer, + [ + if ($impl.run$async) r'run()V', + ], ); final $a = $p.sendPort.nativePort; _$impls[$a] = $impl; @@ -5286,20 +5296,24 @@ class MyRunnable extends _$jni.JObject { static _$core.Map get $impls => _$impls; } -abstract interface class $MyRunnable { +abstract mixin class $MyRunnable { factory $MyRunnable({ required void Function() run, + bool run$async, }) = _$MyRunnable; void run(); + bool get run$async => false; } class _$MyRunnable implements $MyRunnable { _$MyRunnable({ required void Function() run, + this.run$async = false, }) : _run = run; final void Function() _run; + final bool run$async; void run() { return _run(); @@ -5440,6 +5454,30 @@ class MyRunnableRunner extends _$jni.JObject { reference.pointer, _id_runOnAnotherThread as _$jni.JMethodIDPtr) .check(); } + + static final _id_runOnAnotherThreadAndJoin = _class.instanceMethodId( + r'runOnAnotherThreadAndJoin', + r'()V', + ); + + static final _runOnAnotherThreadAndJoin = _$jni.ProtectedJniExtensions.lookup< + _$jni.NativeFunction< + _$jni.JThrowablePtr Function( + _$jni.Pointer<_$jni.Void>, + _$jni.JMethodIDPtr, + )>>('globalEnv_CallVoidMethod') + .asFunction< + _$jni.JThrowablePtr Function( + _$jni.Pointer<_$jni.Void>, + _$jni.JMethodIDPtr, + )>(); + + /// from: `public void runOnAnotherThreadAndJoin()` + void runOnAnotherThreadAndJoin() { + _runOnAnotherThreadAndJoin(reference.pointer, + _id_runOnAnotherThreadAndJoin as _$jni.JMethodIDPtr) + .check(); + } } final class $MyRunnableRunner$Type extends _$jni.JObjType { @@ -5655,6 +5693,7 @@ class StringConverter extends _$jni.JObject { r'com.github.dart_lang.jnigen.interfaces.StringConverter', $p, _$invokePointer, + [], ); final $a = $p.sendPort.nativePort; _$impls[$a] = $impl; @@ -5671,7 +5710,7 @@ class StringConverter extends _$jni.JObject { } } -abstract interface class $StringConverter { +abstract mixin class $StringConverter { factory $StringConverter({ required int Function(_$jni.JString s) parseToInt, }) = _$StringConverter; @@ -6340,6 +6379,7 @@ class JsonSerializable extends _$jni.JObject { r'com.github.dart_lang.jnigen.annotations.JsonSerializable', $p, _$invokePointer, + [], ); final $a = $p.sendPort.nativePort; _$impls[$a] = $impl; @@ -6356,7 +6396,7 @@ class JsonSerializable extends _$jni.JObject { } } -abstract interface class $JsonSerializable { +abstract mixin class $JsonSerializable { factory $JsonSerializable({ required JsonSerializable_Case Function() value, }) = _$JsonSerializable; diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyRunnableRunner.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyRunnableRunner.java index 2903f37e0..7aeb91a3b 100644 --- a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyRunnableRunner.java +++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyRunnableRunner.java @@ -25,4 +25,10 @@ public void runOnAnotherThread() { var thread = new Thread(this::runOnSameThread); thread.start(); } + + public void runOnAnotherThreadAndJoin() throws InterruptedException { + var thread = new Thread(this::runOnSameThread); + thread.start(); + thread.join(); + } } diff --git a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart index 642ba29a3..e9afc9a0d 100644 --- a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart +++ b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart @@ -702,6 +702,44 @@ void registerTests(String groupName, TestRunnerCallback test) { expect(fortyTwo.intValue(releaseOriginal: true), 42); }); }); + for (final style in ['callback', 'implemented class']) { + test('Listener callbacks - $style style', () async { + final completer = Completer(); + + final MyRunnable runnable; + if (style == 'callback') { + runnable = MyRunnable.implement($MyRunnable( + run: completer.complete, + run$async: true, + )); + } else { + runnable = MyRunnable.implement(AsyncRunnable(completer)); + } + final runner = MyRunnableRunner(runnable); + // Normally this would cause a deadlock, but as the callback is a + // listener, it will work. + runner.runOnAnotherThreadAndJoin(); + await completer.future; + expect(MyRunnable.$impls, hasLength(1)); + runnable.release(); + runner.release(); + if (!Platform.isAndroid) { + // Running garbage collection does not work on Android. Skipping + // this test for android. + _runJavaGC(); + for (var i = 0; i < 8; ++i) { + await Future.delayed( + Duration(milliseconds: (1 << i) * 100)); + if (MyInterface.$impls.isEmpty) { + break; + } + } + // Since the interface is now deleted, the cleaner must signal to + // Dart to clean up. + expect(MyRunnable.$impls, isEmpty); + } + }); + } } group('Dart exceptions are handled', () { for (final exception in [UnimplementedError(), 'Hello!']) { @@ -906,3 +944,17 @@ class DartStringToIntParser implements $StringConverter { return int.parse(s.toDartString(releaseOriginal: true), radix: radix); } } + +class AsyncRunnable with $MyRunnable { + final Completer completer; + + AsyncRunnable(this.completer); + + @override + Future run() async { + completer.complete(); + } + + @override + bool get run$async => true; +} diff --git a/pkgs/native_assets_builder/CHANGELOG.md b/pkgs/native_assets_builder/CHANGELOG.md index ed1a89875..b5184798e 100644 --- a/pkgs/native_assets_builder/CHANGELOG.md +++ b/pkgs/native_assets_builder/CHANGELOG.md @@ -9,6 +9,8 @@ Dart CI test runner to the `package:native_assets_builder` for testing the dart-lang/native repository to make it clear those are not intended to be used by end-users. +- Remove link-dry-run concept as it's unused by Flutter Tools & Dart SDK +- Use unified classes instead of two `{OS,...}` and `{OS,,...}Impl` ## 0.8.3 diff --git a/pkgs/native_assets_builder/lib/native_assets_builder.dart b/pkgs/native_assets_builder/lib/native_assets_builder.dart index 7eb67361d..ec69de6cd 100644 --- a/pkgs/native_assets_builder/lib/native_assets_builder.dart +++ b/pkgs/native_assets_builder/lib/native_assets_builder.dart @@ -6,6 +6,5 @@ export 'package:native_assets_builder/src/build_runner/build_runner.dart'; export 'package:native_assets_builder/src/model/build_dry_run_result.dart'; export 'package:native_assets_builder/src/model/build_result.dart'; export 'package:native_assets_builder/src/model/kernel_assets.dart'; -export 'package:native_assets_builder/src/model/link_dry_run_result.dart'; export 'package:native_assets_builder/src/model/link_result.dart'; export 'package:native_assets_builder/src/package_layout/package_layout.dart'; diff --git a/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart b/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart index 5c0a2a0cb..faa9a8422 100644 --- a/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart +++ b/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart @@ -14,7 +14,6 @@ import '../locking/locking.dart'; import '../model/build_dry_run_result.dart'; import '../model/build_result.dart'; import '../model/hook_result.dart'; -import '../model/link_dry_run_result.dart'; import '../model/link_result.dart'; import '../package_layout/package_layout.dart'; import '../utils/file.dart'; @@ -55,12 +54,12 @@ class NativeAssetsBuildRunner { /// [api.BuildConfig] and [api.LinkConfig]! For more info see: /// https://github.com/dart-lang/native/issues/1319 Future build({ - required LinkModePreferenceImpl linkModePreference, + required LinkModePreference linkModePreference, required Target target, required Uri workingDirectory, - required BuildModeImpl buildMode, - CCompilerConfigImpl? cCompilerConfig, - IOSSdkImpl? targetIOSSdk, + required BuildMode buildMode, + CCompilerConfig? cCompilerConfig, + IOSSdk? targetIOSSdk, int? targetIOSVersion, int? targetMacOSVersion, int? targetAndroidNdkApi, @@ -100,12 +99,12 @@ class NativeAssetsBuildRunner { /// [api.BuildConfig] and [api.LinkConfig]! For more info see: /// https://github.com/dart-lang/native/issues/1319 Future link({ - required LinkModePreferenceImpl linkModePreference, + required LinkModePreference linkModePreference, required Target target, required Uri workingDirectory, - required BuildModeImpl buildMode, - CCompilerConfigImpl? cCompilerConfig, - IOSSdkImpl? targetIOSSdk, + required BuildMode buildMode, + CCompilerConfig? cCompilerConfig, + IOSSdk? targetIOSSdk, int? targetIOSVersion, int? targetMacOSVersion, int? targetAndroidNdkApi, @@ -138,12 +137,12 @@ class NativeAssetsBuildRunner { /// The common method for running building or linking of assets. Future _run({ required Hook hook, - required LinkModePreferenceImpl linkModePreference, + required LinkModePreference linkModePreference, required Target target, required Uri workingDirectory, - required BuildModeImpl buildMode, - CCompilerConfigImpl? cCompilerConfig, - IOSSdkImpl? targetIOSSdk, + required BuildMode buildMode, + CCompilerConfig? cCompilerConfig, + IOSSdk? targetIOSSdk, int? targetIOSVersion, int? targetMacOSVersion, int? targetAndroidNdkApi, @@ -161,18 +160,13 @@ class NativeAssetsBuildRunner { // Specifically for running our tests on Dart CI with the test runner, we // recognize specific variables to setup the C Compiler configuration. if (cCompilerConfig == null) { - String? unparseKey(String key) => - 'DART_HOOK_TESTING_${key.replaceAll('.', '__').toUpperCase()}'; - final env = Platform.environment; - String? lookup(String key) => env[unparseKey(key)]; - - final cc = lookup(CCompilerConfigImpl.ccConfigKeyFull); - final ar = lookup(CCompilerConfigImpl.arConfigKeyFull); - final ld = lookup(CCompilerConfigImpl.ldConfigKeyFull); - final envScript = lookup(CCompilerConfigImpl.envScriptConfigKeyFull); + final cc = env['DART_HOOK_TESTING_C_COMPILER__CC']; + final ar = env['DART_HOOK_TESTING_C_COMPILER__AR']; + final ld = env['DART_HOOK_TESTING_C_COMPILER__LD']; + final envScript = env['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT']; final envScriptArgs = - lookup(CCompilerConfigImpl.envScriptArgsConfigKeyFull) + env['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT_ARGUMENTS'] ?.split(' ') .map((arg) => arg.trim()) .where((arg) => arg.isNotEmpty) @@ -185,7 +179,7 @@ class NativeAssetsBuildRunner { ld != null || envScript != null || hasEnvScriptArgs) { - cCompilerConfig = CCompilerConfigImpl( + cCompilerConfig = CCompilerConfig( archiver: ar != null ? Uri.file(ar) : null, compiler: cc != null ? Uri.file(cc) : null, envScript: envScript != null ? Uri.file(envScript) : null, @@ -269,12 +263,12 @@ class NativeAssetsBuildRunner { Package package, PackageLayout packageLayout, Target target, - BuildModeImpl buildMode, - LinkModePreferenceImpl linkModePreference, + BuildMode buildMode, + LinkModePreference linkModePreference, DependencyMetadata? dependencyMetadata, bool? linkingEnabled, - CCompilerConfigImpl? cCompilerConfig, - IOSSdkImpl? targetIOSSdk, + CCompilerConfig? cCompilerConfig, + IOSSdk? targetIOSSdk, int? targetAndroidNdkApi, int? targetIOSVersion, int? targetMacOSVersion, @@ -371,74 +365,20 @@ class NativeAssetsBuildRunner { /// If provided, only native assets of all transitive dependencies of /// [runPackageName] are built. Future buildDryRun({ - required LinkModePreferenceImpl linkModePreference, - required OSImpl targetOS, + required LinkModePreference linkModePreference, + required OS targetOS, required Uri workingDirectory, required bool includeParentEnvironment, required bool linkingEnabled, PackageLayout? packageLayout, String? runPackageName, Iterable? supportedAssetTypes, - }) => - _runDryRun( - hook: Hook.build, - linkModePreference: linkModePreference, - targetOS: targetOS, - workingDirectory: workingDirectory, - includeParentEnvironment: includeParentEnvironment, - packageLayout: packageLayout, - runPackageName: runPackageName, - supportedAssetTypes: supportedAssetTypes, - linkingEnabled: linkingEnabled, - ); - - /// [workingDirectory] is expected to contain `.dart_tool`. - /// - /// This method is invoked by launchers such as dartdev (for `dart run`) and - /// flutter_tools (for `flutter run` and `flutter build`). - /// - /// If provided, only native assets of all transitive dependencies of - /// [runPackageName] are built. - Future linkDryRun({ - required LinkModePreferenceImpl linkModePreference, - required OSImpl targetOS, - required Uri workingDirectory, - required bool includeParentEnvironment, - PackageLayout? packageLayout, - String? runPackageName, - Iterable? supportedAssetTypes, - required BuildDryRunResult buildDryRunResult, - }) => - _runDryRun( - hook: Hook.link, - linkModePreference: linkModePreference, - targetOS: targetOS, - workingDirectory: workingDirectory, - includeParentEnvironment: includeParentEnvironment, - packageLayout: packageLayout, - runPackageName: runPackageName, - supportedAssetTypes: supportedAssetTypes, - buildDryRunResult: buildDryRunResult, - linkingEnabled: null, - ); - - Future _runDryRun({ - required LinkModePreferenceImpl linkModePreference, - required OSImpl targetOS, - required Uri workingDirectory, - required bool includeParentEnvironment, - PackageLayout? packageLayout, - String? runPackageName, - Iterable? supportedAssetTypes, - required Hook hook, - BuildDryRunResult? buildDryRunResult, - required bool? linkingEnabled, }) async { + const hook = Hook.build; packageLayout ??= await PackageLayout.fromRootPackageRoot(workingDirectory); final (buildPlan, _, planSuccess) = await _makePlan( hook: hook, packageLayout: packageLayout, - buildDryRunResult: buildDryRunResult, runPackageName: runPackageName, ); if (!planSuccess) { @@ -455,8 +395,6 @@ class NativeAssetsBuildRunner { linkMode: linkModePreference, buildParentDir: packageLayout.dartToolNativeAssetsBuilder, supportedAssetTypes: supportedAssetTypes, - hook: hook, - buildDryRunResult: buildDryRunResult, linkingEnabled: linkingEnabled, ); final packageConfigUri = packageLayout.packageConfigUri; @@ -817,14 +755,13 @@ ${compileResult.stdout} required Package package, required String packageName, required Uri packageRoot, - required OSImpl targetOS, - required LinkModePreferenceImpl linkMode, + required OS targetOS, + required LinkModePreference linkMode, required Uri buildParentDir, - required Hook hook, - BuildDryRunResult? buildDryRunResult, Iterable? supportedAssetTypes, required bool? linkingEnabled, }) async { + const hook = Hook.build; final buildDirName = HookConfigImpl.checksumDryRun( packageName: package.name, packageRoot: package.root, @@ -850,30 +787,16 @@ ${compileResult.stdout} await outDirShared.create(recursive: true); } - switch (hook) { - case Hook.build: - return BuildConfigImpl.dryRun( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRoot, - targetOS: targetOS, - linkModePreference: linkMode, - supportedAssetTypes: supportedAssetTypes, - linkingEnabled: linkingEnabled, - ); - case Hook.link: - return LinkConfigImpl.dryRun( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRoot, - targetOS: targetOS, - assets: buildDryRunResult!.assetsForLinking[packageName] ?? [], - supportedAssetTypes: supportedAssetTypes, - linkModePreference: linkMode, - ); - } + return BuildConfigImpl.dryRun( + outputDirectory: outDirUri, + outputDirectoryShared: outputDirectoryShared, + packageName: packageName, + packageRoot: packageRoot, + targetOS: targetOS, + linkModePreference: linkMode, + supportedAssetTypes: supportedAssetTypes, + linkingEnabled: linkingEnabled, + ); } DependencyMetadata? _metadataForPackage({ @@ -926,7 +849,6 @@ ${compileResult.stdout} required Hook hook, // TODO(dacoharkes): How to share these two? Make them extend each other? BuildResult? buildResult, - BuildDryRunResult? buildDryRunResult, }) async { final packagesWithHook = await packageLayout.packagesWithAssets(hook); final List buildPlan; @@ -956,8 +878,7 @@ ${compileResult.stdout} // Link hooks are skipped if no assets for linking are provided. buildPlan = []; final skipped = []; - final assetsForLinking = buildResult?.assetsForLinking ?? - buildDryRunResult?.assetsForLinking; + final assetsForLinking = buildResult?.assetsForLinking; for (final package in packagesWithHook) { if (assetsForLinking![package.name]?.isNotEmpty ?? false) { buildPlan.add(package); @@ -988,3 +909,43 @@ extension on DateTime { extension on Uri { Uri get parent => File(toFilePath()).parent.uri; } + +extension OSArchitectures on OS { + Set get architectures => _osTargets[this]!; +} + +const _osTargets = { + OS.android: { + Architecture.arm, + Architecture.arm64, + Architecture.ia32, + Architecture.x64, + Architecture.riscv64, + }, + OS.fuchsia: { + Architecture.arm64, + Architecture.x64, + }, + OS.iOS: { + Architecture.arm, + Architecture.arm64, + Architecture.x64, + }, + OS.linux: { + Architecture.arm, + Architecture.arm64, + Architecture.ia32, + Architecture.riscv32, + Architecture.riscv64, + Architecture.x64, + }, + OS.macOS: { + Architecture.arm64, + Architecture.x64, + }, + OS.windows: { + Architecture.arm64, + Architecture.ia32, + Architecture.x64, + }, +}; diff --git a/pkgs/native_assets_builder/lib/src/model/hook_result.dart b/pkgs/native_assets_builder/lib/src/model/hook_result.dart index f0c23c9eb..cc7656c0c 100644 --- a/pkgs/native_assets_builder/lib/src/model/hook_result.dart +++ b/pkgs/native_assets_builder/lib/src/model/hook_result.dart @@ -9,8 +9,7 @@ import '../../native_assets_builder.dart'; /// The result from a [NativeAssetsBuildRunner.build] or /// [NativeAssetsBuildRunner.link]. -final class HookResult - implements BuildResult, BuildDryRunResult, LinkResult, LinkDryRunResult { +final class HookResult implements BuildResult, BuildDryRunResult, LinkResult { /// The native assets produced by the hooks, which should be bundled. @override final List assets; diff --git a/pkgs/native_assets_builder/lib/src/model/link_dry_run_result.dart b/pkgs/native_assets_builder/lib/src/model/link_dry_run_result.dart deleted file mode 100644 index 65342ef86..000000000 --- a/pkgs/native_assets_builder/lib/src/model/link_dry_run_result.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2024, 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. - -import 'package:native_assets_cli/native_assets_cli_internal.dart'; - -import '../../native_assets_builder.dart'; - -/// The result of executing the link hooks in dry run mode from all packages in -/// the dependency tree of the entry point application. -abstract interface class LinkDryRunResult { - /// The native assets produced by the hooks, which should be bundled. - List get assets; - - /// Whether all hooks completed without errors. - /// - /// All error messages are streamed to [NativeAssetsBuildRunner.logger]. - bool get success; -} diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_reusability_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_reusability_test.dart index 2472cf3d6..6568332ab 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_reusability_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_reusability_test.dart @@ -30,29 +30,29 @@ void main() async { await buildRunner.buildDryRun( targetOS: Target.current.os, - linkModePreference: LinkModePreferenceImpl.dynamic, + linkModePreference: LinkModePreference.dynamic, workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, ); await buildRunner.buildDryRun( targetOS: Target.current.os, - linkModePreference: LinkModePreferenceImpl.dynamic, + linkModePreference: LinkModePreference.dynamic, workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, ); await buildRunner.build( - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, target: Target.current, workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, ); await buildRunner.build( - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, target: Target.current, workingDirectory: packageUri, includeParentEnvironment: true, diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_run_in_isolation_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_run_in_isolation_test.dart index ecfa8b28b..bfb9b738f 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_run_in_isolation_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_run_in_isolation_test.dart @@ -13,17 +13,17 @@ import 'helpers.dart'; const Timeout longTimeout = Timeout(Duration(minutes: 5)); void main() async { - String unparseKey(String key) => - 'DART_HOOK_TESTING_${key.replaceAll('.', '__').toUpperCase()}'; - - final arKey = unparseKey(CCompilerConfigImpl.arConfigKeyFull); - final ccKey = unparseKey(CCompilerConfigImpl.ccConfigKeyFull); - final ldKey = unparseKey(CCompilerConfigImpl.ldConfigKeyFull); - final envScriptKey = unparseKey(CCompilerConfigImpl.envScriptConfigKeyFull); - final envScriptArgsKey = - unparseKey(CCompilerConfigImpl.envScriptArgsConfigKeyFull); - - final cc = Platform.environment[ccKey]?.fileUri; + final env = Platform.environment; + final cc = env['DART_HOOK_TESTING_C_COMPILER__CC']; + final ar = env['DART_HOOK_TESTING_C_COMPILER__AR']; + final ld = env['DART_HOOK_TESTING_C_COMPILER__LD']; + final envScript = env['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT']; + final envScriptArgs = + env['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT_ARGUMENTS'] + ?.split(' ') + .map((arg) => arg.trim()) + .where((arg) => arg.isNotEmpty) + .toList(); if (cc == null) { // We don't set any compiler paths on the GitHub CI. @@ -45,8 +45,6 @@ void main() async { await runPubGet(workingDirectory: packageUri, logger: logger); - printOnFailure( - 'Platform.environment[ccKey]: ${Platform.environment[ccKey]}'); printOnFailure('cc: $cc'); final result = await build( @@ -54,12 +52,12 @@ void main() async { logger, dartExecutable, // Manually pass in a compiler. - cCompilerConfig: CCompilerConfigImpl( - archiver: Platform.environment[arKey]?.fileUri, - compiler: cc, - envScript: Platform.environment[envScriptKey]?.fileUri, - envScriptArgs: Platform.environment[envScriptArgsKey]?.split(' '), - linker: Platform.environment[ldKey]?.fileUri, + cCompilerConfig: CCompilerConfig( + archiver: ar?.fileUri, + compiler: cc.fileUri, + envScript: envScript?.fileUri, + envScriptArgs: envScriptArgs, + linker: ld?.fileUri, ), // Prevent any other environment variables. includeParentEnvironment: false, diff --git a/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test_helper.dart b/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test_helper.dart index ebec9d0d5..1e127a21b 100644 --- a/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test_helper.dart +++ b/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test_helper.dart @@ -4,7 +4,6 @@ import 'package:logging/logging.dart'; import 'package:native_assets_builder/native_assets_builder.dart'; -import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import '../helpers.dart'; @@ -22,8 +21,8 @@ void main(List args) async { logger: logger, dartExecutable: dartExecutable, ).build( - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, target: target, workingDirectory: packageUri, includeParentEnvironment: true, diff --git a/pkgs/native_assets_builder/test/build_runner/concurrency_test_helper.dart b/pkgs/native_assets_builder/test/build_runner/concurrency_test_helper.dart index 1bcd8cf9a..fe37397bb 100644 --- a/pkgs/native_assets_builder/test/build_runner/concurrency_test_helper.dart +++ b/pkgs/native_assets_builder/test/build_runner/concurrency_test_helper.dart @@ -25,8 +25,8 @@ void main(List args) async { dartExecutable: dartExecutable, singleHookTimeout: timeout, ).build( - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, target: Target.current, workingDirectory: packageUri, includeParentEnvironment: true, diff --git a/pkgs/native_assets_builder/test/build_runner/fail_on_os_sdk_version_test.dart b/pkgs/native_assets_builder/test/build_runner/fail_on_os_sdk_version_test.dart index 674548934..0e2055130 100644 --- a/pkgs/native_assets_builder/test/build_runner/fail_on_os_sdk_version_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/fail_on_os_sdk_version_test.dart @@ -5,7 +5,7 @@ import 'package:logging/logging.dart'; import 'package:native_assets_cli/native_assets_cli.dart' show OS; import 'package:native_assets_cli/native_assets_cli_internal.dart' - show IOSSdkImpl, Target; + show IOSSdk, Target; import 'package:test/test.dart'; import '../helpers.dart'; @@ -69,8 +69,7 @@ void main() async { final logMessages = []; final (buildResult, linkResult) = await buildAndLink( target: target, - targetIOSSdk: - (target.os == OS.iOS) ? IOSSdkImpl.iPhoneOS : null, + targetIOSSdk: (target.os == OS.iOS) ? IOSSdk.iPhoneOS : null, targetIOSVersion: (target.os == OS.iOS) ? version : null, targetMacOSVersion: (target.os == OS.macOS) ? version : null, targetAndroidNdkApi: (target.os == OS.android) ? version : null, diff --git a/pkgs/native_assets_builder/test/build_runner/helpers.dart b/pkgs/native_assets_builder/test/build_runner/helpers.dart index c5aa75d04..8f644a3da 100644 --- a/pkgs/native_assets_builder/test/build_runner/helpers.dart +++ b/pkgs/native_assets_builder/test/build_runner/helpers.dart @@ -35,13 +35,13 @@ Future build( Uri packageUri, Logger logger, Uri dartExecutable, { - LinkModePreferenceImpl linkModePreference = LinkModePreferenceImpl.dynamic, - CCompilerConfigImpl? cCompilerConfig, + LinkModePreference linkModePreference = LinkModePreference.dynamic, + CCompilerConfig? cCompilerConfig, bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, String? runPackageName, - IOSSdkImpl? targetIOSSdk, + IOSSdk? targetIOSSdk, int? targetIOSVersion, int? targetMacOSVersion, int? targetAndroidNdkApi, @@ -54,7 +54,7 @@ Future build( logger: logger, dartExecutable: dartExecutable, ).build( - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, linkModePreference: linkModePreference, target: target ?? Target.current, workingDirectory: packageUri, @@ -84,14 +84,14 @@ Future link( Uri packageUri, Logger logger, Uri dartExecutable, { - LinkModePreferenceImpl linkModePreference = LinkModePreferenceImpl.dynamic, - CCompilerConfigImpl? cCompilerConfig, + LinkModePreference linkModePreference = LinkModePreference.dynamic, + CCompilerConfig? cCompilerConfig, bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, required BuildResult buildResult, Uri? resourceIdentifiers, - IOSSdkImpl? targetIOSSdk, + IOSSdk? targetIOSSdk, int? targetIOSVersion, int? targetMacOSVersion, int? targetAndroidNdkApi, @@ -104,7 +104,7 @@ Future link( dartExecutable: dartExecutable, ).link( linkModePreference: linkModePreference, - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, target: target ?? Target.current, workingDirectory: packageUri, cCompilerConfig: cCompilerConfig, @@ -130,13 +130,13 @@ Future<(BuildResult, LinkResult)> buildAndLink( Uri packageUri, Logger logger, Uri dartExecutable, { - LinkModePreferenceImpl linkModePreference = LinkModePreferenceImpl.dynamic, - CCompilerConfigImpl? cCompilerConfig, + LinkModePreference linkModePreference = LinkModePreference.dynamic, + CCompilerConfig? cCompilerConfig, bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, String? runPackageName, - IOSSdkImpl? targetIOSSdk, + IOSSdk? targetIOSSdk, int? targetIOSVersion, int? targetMacOSVersion, int? targetAndroidNdkApi, @@ -150,7 +150,7 @@ Future<(BuildResult, LinkResult)> buildAndLink( dartExecutable: dartExecutable, ); final buildResult = await buildRunner.build( - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, linkModePreference: linkModePreference, target: target ?? Target.current, workingDirectory: packageUri, @@ -177,7 +177,7 @@ Future<(BuildResult, LinkResult)> buildAndLink( final linkResult = await buildRunner.link( linkModePreference: linkModePreference, - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, target: target ?? Target.current, workingDirectory: packageUri, cCompilerConfig: cCompilerConfig, @@ -222,8 +222,8 @@ Future buildDryRun( Uri packageUri, Logger logger, Uri dartExecutable, { - LinkModePreferenceImpl linkModePreference = LinkModePreferenceImpl.dynamic, - CCompilerConfigImpl? cCompilerConfig, + LinkModePreference linkModePreference = LinkModePreference.dynamic, + CCompilerConfig? cCompilerConfig, bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, @@ -246,34 +246,6 @@ Future buildDryRun( return result; }); -Future linkDryRun( - Uri packageUri, - Logger logger, - Uri dartExecutable, { - LinkModePreferenceImpl linkModePreference = LinkModePreferenceImpl.dynamic, - CCompilerConfigImpl? cCompilerConfig, - bool includeParentEnvironment = true, - List? capturedLogs, - PackageLayout? packageLayout, - required BuildDryRunResult buildDryRunResult, - Iterable? supportedAssetTypes, -}) async => - runWithLog(capturedLogs, () async { - final result = await NativeAssetsBuildRunner( - logger: logger, - dartExecutable: dartExecutable, - ).linkDryRun( - linkModePreference: linkModePreference, - targetOS: Target.current.os, - workingDirectory: packageUri, - includeParentEnvironment: includeParentEnvironment, - packageLayout: packageLayout, - buildDryRunResult: buildDryRunResult, - supportedAssetTypes: supportedAssetTypes, - ); - return result; - }); - Future expectAssetsExist(List assets) async { for (final asset in assets) { expect(File.fromUri(asset.file!), exists); diff --git a/pkgs/native_assets_builder/test/build_runner/link_dry_run_test.dart b/pkgs/native_assets_builder/test/build_runner/link_dry_run_test.dart deleted file mode 100644 index c18011cf7..000000000 --- a/pkgs/native_assets_builder/test/build_runner/link_dry_run_test.dart +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2024, 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. - -import 'package:native_assets_cli/native_assets_cli.dart' as cli; -import 'package:native_assets_cli/src/api/asset.dart'; -import 'package:test/test.dart'; - -import '../helpers.dart'; -import 'helpers.dart'; - -const Timeout longTimeout = Timeout(Duration(minutes: 5)); - -void main() async { - const supportedAssetTypes = [DataAsset.type]; - - test( - 'simple_link linking', - timeout: longTimeout, - () async { - await inTempDir((tempUri) async { - await copyTestProjects(targetUri: tempUri); - final packageUri = tempUri.resolve('simple_link/'); - - // First, run `pub get`, we need pub to resolve our dependencies. - await runPubGet( - workingDirectory: packageUri, - logger: logger, - ); - - final buildResult = await buildDryRun( - packageUri, - logger, - dartExecutable, - linkingEnabled: true, - supportedAssetTypes: supportedAssetTypes, - ); - expect(buildResult.assets.length, 0); - - final linkResult = await linkDryRun( - packageUri, - logger, - dartExecutable, - buildDryRunResult: buildResult, - supportedAssetTypes: supportedAssetTypes, - ); - expect(linkResult.assets.length, 2); - }); - }, - ); - - test( - 'complex_link linking', - timeout: longTimeout, - () async { - await inTempDir((tempUri) async { - // From package:complex_link_helper. - const builtHelperAssets = [ - 'assets/data_helper_0.json', - 'assets/data_helper_1.json', - ]; - const helperAssetsForLinking = [ - 'assets/data_helper_2.json', - 'assets/data_helper_3.json', - ]; - // From package:complex_link. - const mainAssetsForLinking = [ - 'assets/data_0.json', - 'assets/data_1.json', - ]; - final assetsForLinking = [ - ...helperAssetsForLinking, - ...mainAssetsForLinking, - ]; - final linkedAssets = assetsForLinking.skip(1); - - await copyTestProjects(targetUri: tempUri); - final packageUri = tempUri.resolve('complex_link/'); - - // First, run `pub get`, we need pub to resolve our dependencies. - await runPubGet(workingDirectory: packageUri, logger: logger); - - final buildResult = await buildDryRun( - packageUri, - logger, - dartExecutable, - linkingEnabled: true, - supportedAssetTypes: supportedAssetTypes, - ); - expect(buildResult.success, true); - expect( - _getNames(buildResult.assets), unorderedEquals(builtHelperAssets)); - expect( - _getNames(buildResult.assetsForLinking['complex_link']!), - unorderedEquals(assetsForLinking), - ); - - final linkResult = await linkDryRun( - packageUri, - logger, - dartExecutable, - buildDryRunResult: buildResult, - supportedAssetTypes: supportedAssetTypes, - ); - expect(linkResult.success, true); - - expect(_getNames(linkResult.assets), unorderedEquals(linkedAssets)); - }); - }, - ); -} - -Iterable _getNames(List assets) => - assets.whereType().map((asset) => asset.name); diff --git a/pkgs/native_assets_builder/test/build_runner/packaging_preference_test.dart b/pkgs/native_assets_builder/test/build_runner/packaging_preference_test.dart index b9be4a481..02510ff47 100644 --- a/pkgs/native_assets_builder/test/build_runner/packaging_preference_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/packaging_preference_test.dart @@ -26,46 +26,46 @@ void main() async { packageUri, logger, dartExecutable, - linkModePreference: LinkModePreferenceImpl.dynamic, + linkModePreference: LinkModePreference.dynamic, ); final resultPreferDynamic = await build( packageUri, logger, dartExecutable, - linkModePreference: LinkModePreferenceImpl.preferDynamic, + linkModePreference: LinkModePreference.preferDynamic, ); final resultStatic = await build( packageUri, logger, dartExecutable, - linkModePreference: LinkModePreferenceImpl.static, + linkModePreference: LinkModePreference.static, ); final resultPreferStatic = await build( packageUri, logger, dartExecutable, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); // This package honors preferences. expect( (resultDynamic.assets.single as NativeCodeAssetImpl).linkMode, - DynamicLoadingBundledImpl(), + DynamicLoadingBundled(), ); expect( (resultPreferDynamic.assets.single as NativeCodeAssetImpl).linkMode, - DynamicLoadingBundledImpl(), + DynamicLoadingBundled(), ); expect( (resultStatic.assets.single as NativeCodeAssetImpl).linkMode, - StaticLinkingImpl(), + StaticLinking(), ); expect( (resultPreferStatic.assets.single as NativeCodeAssetImpl).linkMode, - StaticLinkingImpl(), + StaticLinking(), ); }); }); diff --git a/pkgs/native_assets_builder/test/helpers.dart b/pkgs/native_assets_builder/test/helpers.dart index fa6fc9638..21d22a538 100644 --- a/pkgs/native_assets_builder/test/helpers.dart +++ b/pkgs/native_assets_builder/test/helpers.dart @@ -123,48 +123,42 @@ final pkgNativeAssetsBuilderUri = findPackageRoot('native_assets_builder'); final testDataUri = pkgNativeAssetsBuilderUri.resolve('test_data/'); -String unparseKey(String key) => - 'DART_HOOK_TESTING_${key.replaceAll('.', '__').toUpperCase()}'; - /// Archiver provided by the environment. /// /// Provided on Dart CI. -final Uri? _ar = Platform - .environment[unparseKey(internal.CCompilerConfigImpl.arConfigKeyFull)] - ?.asFileUri(); +final Uri? _ar = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__AR']?.asFileUri(); /// Compiler provided by the environment. /// /// Provided on Dart CI. -final Uri? _cc = Platform - .environment[unparseKey(internal.CCompilerConfigImpl.ccConfigKeyFull)] - ?.asFileUri(); +final Uri? _cc = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__CC']?.asFileUri(); /// Linker provided by the environment. /// /// Provided on Dart CI. -final Uri? _ld = Platform - .environment[unparseKey(internal.CCompilerConfigImpl.ldConfigKeyFull)] - ?.asFileUri(); +final Uri? _ld = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__LD']?.asFileUri(); /// Path to script that sets environment variables for [_cc], [_ld], and [_ar]. /// /// Provided on Dart CI. -final Uri? _envScript = Platform.environment[ - unparseKey(internal.CCompilerConfigImpl.envScriptConfigKeyFull)] +final Uri? _envScript = Platform + .environment['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT'] ?.asFileUri(); /// Arguments for [_envScript] provided by environment. /// /// Provided on Dart CI. -final List? _envScriptArgs = Platform.environment[ - unparseKey(internal.CCompilerConfigImpl.envScriptArgsConfigKeyFull)] +final List? _envScriptArgs = Platform + .environment['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT_ARGUMENTS'] ?.split(' '); /// Configuration for the native toolchain. /// /// Provided on Dart CI. -final cCompiler = internal.CCompilerConfigImpl( +final cCompiler = internal.CCompilerConfig( compiler: _cc, archiver: _ar, linker: _ld, diff --git a/pkgs/native_assets_builder/test/test_data/native_dynamic_linking_test.dart b/pkgs/native_assets_builder/test/test_data/native_dynamic_linking_test.dart index 15352eae7..165a76e44 100644 --- a/pkgs/native_assets_builder/test/test_data/native_dynamic_linking_test.dart +++ b/pkgs/native_assets_builder/test/test_data/native_dynamic_linking_test.dart @@ -38,13 +38,13 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: name, packageRoot: testPackageUri, - targetOS: OSImpl.current, + targetOS: OS.current, version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreferenceImpl.dynamic, + linkModePreference: LinkModePreference.dynamic, dryRun: dryRun, linkingEnabled: false, - targetArchitecture: dryRun ? null : ArchitectureImpl.current, - buildMode: dryRun ? null : BuildModeImpl.debug, + targetArchitecture: dryRun ? null : Architecture.current, + buildMode: dryRun ? null : BuildMode.debug, cCompiler: dryRun ? null : cCompiler, ); diff --git a/pkgs/native_assets_builder/test/test_data/transformer_test.dart b/pkgs/native_assets_builder/test/test_data/transformer_test.dart index eae5cfc7b..e26c8be5e 100644 --- a/pkgs/native_assets_builder/test/test_data/transformer_test.dart +++ b/pkgs/native_assets_builder/test/test_data/transformer_test.dart @@ -11,7 +11,6 @@ library; import 'dart:convert'; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; @@ -52,13 +51,13 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageUri, - targetOS: OSImpl.current, + targetOS: OS.current, version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreferenceImpl.dynamic, + linkModePreference: LinkModePreference.dynamic, dryRun: false, linkingEnabled: false, - targetArchitecture: architecture as ArchitectureImpl, - buildMode: BuildModeImpl.debug, + targetArchitecture: architecture, + buildMode: BuildMode.debug, supportedAssetTypes: [DataAsset.type], ); diff --git a/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart b/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart index 37793ae57..c91b70d43 100644 --- a/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart @@ -4,7 +4,6 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; void main(List arguments) async { diff --git a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart index 7c99be55e..1c0c8a664 100644 --- a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart @@ -4,7 +4,6 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; void main(List arguments) async { diff --git a/pkgs/native_assets_cli/CHANGELOG.md b/pkgs/native_assets_cli/CHANGELOG.md index f04086cd1..91c891905 100644 --- a/pkgs/native_assets_cli/CHANGELOG.md +++ b/pkgs/native_assets_cli/CHANGELOG.md @@ -17,6 +17,8 @@ - No longer try to resolve uris encoded in `config.json` against any base uri. The `hook/{build,link}.dart` invoker has to ensure the uris it encodes can be opened as-is (i.e. without resolving against any base uri) +- **Breaking change** Use unified classes instead of two `{OS,...}` and + move methods (e.g. from `OS`) to extensions (e.g. `OSLibraryNaming`) ## 0.8.0 diff --git a/pkgs/native_assets_cli/lib/native_assets_cli.dart b/pkgs/native_assets_cli/lib/native_assets_cli.dart index ba5118b3d..610159c08 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli.dart @@ -6,27 +6,27 @@ /// build hook (`hook/build.dart`). library native_assets_cli; -export 'src/api/architecture.dart' show Architecture; export 'src/api/asset.dart' - show - Asset, - DataAsset, - DynamicLoadingBundled, - DynamicLoadingSystem, - LinkMode, - LookupInExecutable, - LookupInProcess, - NativeCodeAsset, - StaticLinking; + show Asset, DataAsset, NativeCodeAsset, OSLibraryNaming; export 'src/api/build.dart' show build; -export 'src/api/build_config.dart' show BuildConfig, CCompilerConfig; -export 'src/api/build_mode.dart' show BuildMode; +export 'src/api/build_config.dart' show BuildConfig; export 'src/api/build_output.dart' show BuildOutput, LinkOutput; export 'src/api/builder.dart' show Builder; export 'src/api/hook_config.dart' show HookConfig; -export 'src/api/ios_sdk.dart' show IOSSdk; export 'src/api/link.dart' show link; export 'src/api/link_config.dart' show LinkConfig; -export 'src/api/link_mode_preference.dart' show LinkModePreference; export 'src/api/linker.dart' show Linker; -export 'src/api/os.dart' show OS; +export 'src/architecture.dart' show Architecture; +export 'src/build_mode.dart' show BuildMode; +export 'src/c_compiler_config.dart' show CCompilerConfig; +export 'src/ios_sdk.dart' show IOSSdk; +export 'src/link_mode.dart' + show + DynamicLoadingBundled, + DynamicLoadingSystem, + LinkMode, + LookupInExecutable, + LookupInProcess, + StaticLinking; +export 'src/link_mode_preference.dart' show LinkModePreference; +export 'src/os.dart' show OS; diff --git a/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart b/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart index 5599f5a29..fbb4ad9a3 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart @@ -16,30 +16,16 @@ /// @nodoc library native_assets_cli_internal; -export 'src/api/architecture.dart' show ArchitectureImpl; -export 'src/api/asset.dart' - show - AssetImpl, - DataAssetImpl, - DynamicLoadingBundledImpl, - DynamicLoadingSystemImpl, - LinkModeImpl, - LookupInExecutableImpl, - LookupInProcessImpl, - NativeCodeAssetImpl, - StaticLinkingImpl; -export 'src/api/build_config.dart' show BuildConfigImpl, CCompilerConfigImpl; -export 'src/api/build_mode.dart' show BuildModeImpl; +export 'native_assets_cli.dart' hide build, link; +export 'src/api/asset.dart' show AssetImpl, DataAssetImpl, NativeCodeAssetImpl; +export 'src/api/build_config.dart' show BuildConfigImpl; export 'src/api/build_output.dart' show HookOutputImpl; export 'src/api/hook_config.dart' show HookConfigImpl; -export 'src/api/ios_sdk.dart' show IOSSdkImpl; export 'src/api/link_config.dart' show LinkConfigImpl; -export 'src/api/link_mode_preference.dart' show LinkModePreferenceImpl; -export 'src/api/os.dart' show OSImpl; export 'src/model/dependencies.dart'; export 'src/model/hook.dart'; export 'src/model/metadata.dart'; export 'src/model/resource_identifiers.dart'; -export 'src/model/target.dart'; +export 'src/model/target.dart' show Target; export 'src/validator/validator.dart' show ValidateResult, validateBuild, validateLink, validateNoDuplicateDylibs; diff --git a/pkgs/native_assets_cli/lib/src/api/architecture.dart b/pkgs/native_assets_cli/lib/src/api/architecture.dart deleted file mode 100644 index 8c78531fe..000000000 --- a/pkgs/native_assets_cli/lib/src/api/architecture.dart +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2024, 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. - -import 'dart:ffi' show Abi; -import 'dart:io'; - -import '../model/target.dart'; - -part '../model/architecture.dart'; - -/// A hardware architecture which the Dart VM can run on. -abstract final class Architecture { - /// The [arm](https://en.wikipedia.org/wiki/ARM_architecture_family) - /// architecture. - static const Architecture arm = ArchitectureImpl.arm; - - /// The [AArch64](https://en.wikipedia.org/wiki/AArch64) architecture. - static const Architecture arm64 = ArchitectureImpl.arm64; - - /// The [IA-32](https://en.wikipedia.org/wiki/IA-32) architecture. - static const Architecture ia32 = ArchitectureImpl.ia32; - - /// The [RISC-V](https://en.wikipedia.org/wiki/RISC-V) 32 bit architecture. - static const Architecture riscv32 = ArchitectureImpl.riscv32; - - /// The [RISC-V](https://en.wikipedia.org/wiki/RISC-V) 64 bit architecture. - static const Architecture riscv64 = ArchitectureImpl.riscv64; - - /// The [x86-64](https://en.wikipedia.org/wiki/X86-64) architecture. - static const Architecture x64 = ArchitectureImpl.x64; - - /// Known values for [Architecture]. - static const List values = ArchitectureImpl.values; - - /// The current [Architecture]. - /// - /// Read from the [Platform.version] string. - static Architecture get current => ArchitectureImpl.current; -} diff --git a/pkgs/native_assets_cli/lib/src/api/asset.dart b/pkgs/native_assets_cli/lib/src/api/asset.dart index 79dbf3041..95de679b0 100644 --- a/pkgs/native_assets_cli/lib/src/api/asset.dart +++ b/pkgs/native_assets_cli/lib/src/api/asset.dart @@ -4,12 +4,13 @@ import 'package:pub_semver/pub_semver.dart'; +import '../architecture.dart'; +import '../link_mode.dart'; +import '../os.dart'; import '../utils/json.dart'; import '../utils/map.dart'; -import 'architecture.dart'; import 'build_config.dart'; import 'build_output.dart'; -import 'os.dart'; part '../model/asset.dart'; part '../model/data_asset.dart'; diff --git a/pkgs/native_assets_cli/lib/src/api/build_config.dart b/pkgs/native_assets_cli/lib/src/api/build_config.dart index ff0401eb6..557651ca7 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/build_config.dart @@ -8,26 +8,25 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; +import '../architecture.dart'; import '../args_parser.dart'; +import '../build_mode.dart'; +import '../c_compiler_config.dart'; +import '../ios_sdk.dart'; import '../json_utils.dart'; +import '../link_mode_preference.dart'; import '../model/hook.dart'; import '../model/metadata.dart'; +import '../os.dart'; import '../utils/json.dart'; import '../utils/map.dart'; -import 'architecture.dart'; import 'asset.dart'; import 'build.dart'; -import 'build_mode.dart'; import 'build_output.dart'; import 'deprecation_messages.dart'; import 'hook_config.dart'; -import 'ios_sdk.dart'; -import 'link_mode_preference.dart'; -import 'os.dart'; part '../model/build_config.dart'; -part '../model/c_compiler_config.dart'; -part 'c_compiler_config.dart'; /// The configuration for a build hook (`hook/build.dart`) invocation. /// @@ -136,15 +135,15 @@ abstract final class BuildConfig implements HookConfig { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRoot, - buildMode: buildMode as BuildModeImpl, - targetArchitecture: targetArchitecture as ArchitectureImpl, - targetOS: targetOS as OSImpl, - targetIOSSdk: targetIOSSdk as IOSSdkImpl?, + buildMode: buildMode, + targetArchitecture: targetArchitecture, + targetOS: targetOS, + targetIOSSdk: targetIOSSdk, targetIOSVersion: targetIOSVersion, targetMacOSVersion: targetMacOSVersion, targetAndroidNdkApi: targetAndroidNdkApi, - cCompiler: cCompiler as CCompilerConfigImpl?, - linkModePreference: linkModePreference as LinkModePreferenceImpl, + cCompiler: cCompiler, + linkModePreference: linkModePreference, dependencyMetadata: dependencyMetadata != null ? { for (final entry in dependencyMetadata.entries) @@ -177,8 +176,8 @@ abstract final class BuildConfig implements HookConfig { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRoot, - targetOS: targetOS as OSImpl, - linkModePreference: linkModePreference as LinkModePreferenceImpl, + targetOS: targetOS, + linkModePreference: linkModePreference, supportedAssetTypes: supportedAssetTypes, linkingEnabled: linkingEnabled, ); diff --git a/pkgs/native_assets_cli/lib/src/api/build_output.dart b/pkgs/native_assets_cli/lib/src/api/build_output.dart index 66b97e4e5..821f97fc3 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_output.dart +++ b/pkgs/native_assets_cli/lib/src/api/build_output.dart @@ -8,13 +8,14 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; +import '../architecture.dart'; import '../model/dependencies.dart'; import '../model/metadata.dart'; +import '../os.dart'; import '../utils/datetime.dart'; import '../utils/file.dart'; import '../utils/json.dart'; import '../utils/map.dart'; -import 'architecture.dart'; import 'asset.dart'; import 'build.dart'; import 'build_config.dart'; @@ -23,7 +24,6 @@ import 'deprecation_messages.dart'; import 'hook_config.dart'; import 'link.dart'; import 'linker.dart'; -import 'os.dart'; part '../model/hook_output.dart'; part 'link_output.dart'; diff --git a/pkgs/native_assets_cli/lib/src/api/c_compiler_config.dart b/pkgs/native_assets_cli/lib/src/api/c_compiler_config.dart deleted file mode 100644 index 30dc67ac4..000000000 --- a/pkgs/native_assets_cli/lib/src/api/c_compiler_config.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2024, 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. - -part of 'build_config.dart'; - -/// The configuration for a C toolchain. -abstract final class CCompilerConfig { - /// Path to a C compiler. - Uri? get compiler; - - /// Path to a native linker. - Uri? get linker; - - /// Path to a native archiver. - Uri? get archiver; - - /// Path to script that sets environment variables for [compiler], [linker], - /// and [archiver]. - Uri? get envScript; - - /// Arguments for [envScript]. - List? get envScriptArgs; - - factory CCompilerConfig({ - Uri? archiver, - Uri? compiler, - Uri? linker, - Uri? envScript, - List? envScriptArgs, - }) = CCompilerConfigImpl; -} diff --git a/pkgs/native_assets_cli/lib/src/api/hook_config.dart b/pkgs/native_assets_cli/lib/src/api/hook_config.dart index f05b8d748..64271462b 100644 --- a/pkgs/native_assets_cli/lib/src/api/hook_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/hook_config.dart @@ -9,19 +9,22 @@ import 'package:collection/collection.dart'; import 'package:crypto/crypto.dart'; import 'package:pub_semver/pub_semver.dart'; +import '../architecture.dart'; +import '../build_mode.dart'; +import '../c_compiler_config.dart'; +import '../ios_sdk.dart'; import '../json_utils.dart'; +import '../link_mode.dart'; +import '../link_mode_preference.dart'; import '../model/hook.dart'; import '../model/metadata.dart'; import '../model/target.dart'; +import '../os.dart'; import '../utils/map.dart'; -import 'architecture.dart'; import 'asset.dart'; import 'build_config.dart'; -import 'build_mode.dart'; -import 'ios_sdk.dart'; import 'link_config.dart'; -import 'link_mode_preference.dart'; -import 'os.dart'; + part '../model/hook_config.dart'; /// The shared properties of a [LinkConfig] and a [BuildConfig]. diff --git a/pkgs/native_assets_cli/lib/src/api/link_config.dart b/pkgs/native_assets_cli/lib/src/api/link_config.dart index e3897bd01..318298046 100644 --- a/pkgs/native_assets_cli/lib/src/api/link_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/link_config.dart @@ -8,18 +8,19 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:pub_semver/pub_semver.dart'; +import '../architecture.dart'; import '../args_parser.dart'; +import '../build_mode.dart'; +import '../c_compiler_config.dart'; +import '../ios_sdk.dart'; import '../json_utils.dart'; +import '../link_mode_preference.dart'; import '../model/hook.dart'; +import '../os.dart'; import '../utils/map.dart'; -import 'architecture.dart'; import 'asset.dart'; import 'build_config.dart'; -import 'build_mode.dart'; import 'hook_config.dart'; -import 'ios_sdk.dart'; -import 'link_mode_preference.dart'; -import 'os.dart'; part '../model/link_config.dart'; @@ -67,16 +68,16 @@ abstract class LinkConfig implements HookConfig { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRoot, - buildMode: buildMode as BuildModeImpl, - cCompiler: cCompiler as CCompilerConfigImpl?, + buildMode: buildMode, + cCompiler: cCompiler, targetAndroidNdkApi: targetAndroidNdkApi, - targetArchitecture: targetArchitecture as ArchitectureImpl?, - targetIOSSdk: targetIOSSdk as IOSSdkImpl?, - targetOS: targetOS as OSImpl, + targetArchitecture: targetArchitecture, + targetIOSSdk: targetIOSSdk, + targetOS: targetOS, targetIOSVersion: targetIOSVersion, targetMacOSVersion: targetMacOSVersion, dryRun: dryRun, - linkModePreference: linkModePreference as LinkModePreferenceImpl, + linkModePreference: linkModePreference, supportedAssetTypes: supportedAssetTypes, version: version, ); @@ -98,9 +99,9 @@ abstract class LinkConfig implements HookConfig { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRoot, - targetOS: targetOS as OSImpl, + targetOS: targetOS, supportedAssetTypes: supportedAssetTypes, - linkModePreference: linkModePreference as LinkModePreferenceImpl, + linkModePreference: linkModePreference, version: version, ); diff --git a/pkgs/native_assets_cli/lib/src/api/native_code_asset.dart b/pkgs/native_assets_cli/lib/src/api/native_code_asset.dart index b5f08f79a..49353cd36 100644 --- a/pkgs/native_assets_cli/lib/src/api/native_code_asset.dart +++ b/pkgs/native_assets_cli/lib/src/api/native_code_asset.dart @@ -89,87 +89,86 @@ abstract final class NativeCodeAsset implements Asset { }) => NativeCodeAssetImpl( id: 'package:$package/$name', - linkMode: linkMode as LinkModeImpl, - os: os as OSImpl, - architecture: architecture as ArchitectureImpl?, + linkMode: linkMode, + os: os, + architecture: architecture, file: file, ); static const String type = 'native_code'; } -/// The link mode for a [NativeCodeAsset]. -/// -/// Known linking modes: -/// -/// * [DynamicLoading] -/// * [DynamicLoadingBundled] -/// * [DynamicLoadingSystem] -/// * [LookupInProcess] -/// * [LookupInExecutable] -/// * [StaticLinking] -/// -/// See the documentation on the above classes. -abstract final class LinkMode {} +extension OSLibraryNaming on OS { + /// The default dynamic library file name on this os. + String dylibFileName(String name) { + final prefix = _dylibPrefix[this]!; + final extension = _dylibExtension[this]!; + return '$prefix$name.$extension'; + } -/// The [NativeCodeAsset] will be loaded at runtime. -/// -/// Nothing happens at native code linking time. -/// -/// Supported in the Dart and Flutter SDK. -/// -/// Note: Dynamic loading is not equal to dynamic linking. Dynamic linking -/// would have to run the linker at compile-time, which is currently not -/// supported in the Dart and Flutter SDK. -abstract final class DynamicLoading implements LinkMode {} + /// The default static library file name on this os. + String staticlibFileName(String name) { + final prefix = _staticlibPrefix[this]!; + final extension = _staticlibExtension[this]!; + return '$prefix$name.$extension'; + } -/// The dynamic library is bundled by Dart/Flutter at build time. -/// -/// At runtime, the dynamic library will be loaded and the symbols will be -/// looked up in this dynamic library. -/// -/// An asset with this dynamic loading method must provide a -/// [NativeCodeAsset.file]. The Dart and Flutter SDK will bundle this code in -/// the final application. -/// -/// During a [BuildConfig.dryRun], the [NativeCodeAsset.file] can be a file name -/// instead of a the full path. The file does not have to exist during a dry -/// run. -abstract final class DynamicLoadingBundled implements DynamicLoading { - factory DynamicLoadingBundled() = DynamicLoadingBundledImpl; + /// The default library file name on this os. + String libraryFileName(String name, LinkMode linkMode) { + if (linkMode is DynamicLoading) { + return dylibFileName(name); + } + assert(linkMode is StaticLinking); + return staticlibFileName(name); + } + + /// The default executable file name on this os. + String executableFileName(String name) { + final extension = _executableExtension[this]!; + final dot = extension.isNotEmpty ? '.' : ''; + return '$name$dot$extension'; + } } -/// The dynamic library is avaliable on the target system `PATH`. -/// -/// At buildtime, nothing happens. -/// -/// At runtime, the dynamic library will be loaded and the symbols will be -/// looked up in this dynamic library. -abstract final class DynamicLoadingSystem implements DynamicLoading { - Uri get uri; +/// The default name prefix for dynamic libraries per [OS]. +const _dylibPrefix = { + OS.android: 'lib', + OS.fuchsia: 'lib', + OS.iOS: 'lib', + OS.linux: 'lib', + OS.macOS: 'lib', + OS.windows: '', +}; - factory DynamicLoadingSystem(Uri uri) = DynamicLoadingSystemImpl; -} +/// The default extension for dynamic libraries per [OS]. +const _dylibExtension = { + OS.android: 'so', + OS.fuchsia: 'so', + OS.iOS: 'dylib', + OS.linux: 'so', + OS.macOS: 'dylib', + OS.windows: 'dll', +}; -/// The native code is loaded in the process and symbols are available through -/// `DynamicLibrary.process()`. -abstract final class LookupInProcess implements DynamicLoading { - factory LookupInProcess() = LookupInProcessImpl; -} +/// The default name prefix for static libraries per [OS]. +const _staticlibPrefix = _dylibPrefix; -/// The native code is embedded in executable and symbols are available through -/// `DynamicLibrary.executable()`. -abstract final class LookupInExecutable implements DynamicLoading { - factory LookupInExecutable() = LookupInExecutableImpl; -} +/// The default extension for static libraries per [OS]. +const _staticlibExtension = { + OS.android: 'a', + OS.fuchsia: 'a', + OS.iOS: 'a', + OS.linux: 'a', + OS.macOS: 'a', + OS.windows: 'lib', +}; -/// Static linking. -/// -/// At native linking time, native function names will be resolved to static -/// libraries. -/// -/// Not yet supported in the Dart and Flutter SDK. -// TODO(https://github.com/dart-lang/sdk/issues/49418): Support static linking. -abstract final class StaticLinking implements LinkMode { - factory StaticLinking() = StaticLinkingImpl; -} +/// The default extension for executables per [OS]. +const _executableExtension = { + OS.android: '', + OS.fuchsia: '', + OS.iOS: '', + OS.linux: '', + OS.macOS: '', + OS.windows: 'exe', +}; diff --git a/pkgs/native_assets_cli/lib/src/api/os.dart b/pkgs/native_assets_cli/lib/src/api/os.dart deleted file mode 100644 index b4b301fa2..000000000 --- a/pkgs/native_assets_cli/lib/src/api/os.dart +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2024, 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. - -import 'dart:ffi' show Abi; -import 'dart:io'; - -import '../model/target.dart'; -import 'architecture.dart'; -import 'asset.dart'; - -part '../model/os.dart'; - -/// An operating system the Dart VM runs on. -abstract final class OS { - /// The - /// [Android](https://en.wikipedia.org/wiki/Android_%28operating_system%29) - /// operating system. - static const OS android = OSImpl.android; - - /// The [Fuchsia](https://en.wikipedia.org/wiki/Google_Fuchsia) operating - /// system. - static const OS fuchsia = OSImpl.fuchsia; - - /// The [iOS](https://en.wikipedia.org/wiki/IOS) operating system. - static const OS iOS = OSImpl.iOS; - - /// The [Linux](https://en.wikipedia.org/wiki/Linux) operating system. - static const OS linux = OSImpl.linux; - - /// The [macOS](https://en.wikipedia.org/wiki/MacOS) operating system. - static const OS macOS = OSImpl.macOS; - - /// The - /// [Microsoft Windows](https://en.wikipedia.org/wiki/Microsoft_Windows) - /// operating system. - static const OS windows = OSImpl.windows; - - /// Known values for [OS]. - static const List values = OSImpl.values; - - /// The default dynamic library file name on this os. - String dylibFileName(String name); - - /// The default static library file name on this os. - String staticlibFileName(String name); - - /// The default library file name on this os. - String libraryFileName(String name, LinkMode linkMode); - - /// The default executable file name on this os. - String executableFileName(String name); - - /// The current [OS]. - /// - /// Consisten with the [Platform.version] string. - static OS get current => OSImpl.current; -} diff --git a/pkgs/native_assets_cli/lib/src/api/test.dart b/pkgs/native_assets_cli/lib/src/api/test.dart index e4b25c2d5..167a248fd 100644 --- a/pkgs/native_assets_cli/lib/src/api/test.dart +++ b/pkgs/native_assets_cli/lib/src/api/test.dart @@ -8,14 +8,15 @@ import 'package:meta/meta.dart' show isTest; import 'package:test/test.dart'; import 'package:yaml/yaml.dart'; -import 'architecture.dart'; +import '../architecture.dart'; +import '../build_mode.dart'; +import '../c_compiler_config.dart'; +import '../ios_sdk.dart'; +import '../link_mode_preference.dart'; +import '../os.dart'; import 'asset.dart'; import 'build_config.dart'; -import 'build_mode.dart'; import 'build_output.dart'; -import 'ios_sdk.dart'; -import 'link_mode_preference.dart'; -import 'os.dart'; @isTest Future testBuildHook({ diff --git a/pkgs/native_assets_cli/lib/src/architecture.dart b/pkgs/native_assets_cli/lib/src/architecture.dart new file mode 100644 index 000000000..6e7ce0ca1 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/architecture.dart @@ -0,0 +1,90 @@ +// Copyright (c) 2024, 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. + +import 'dart:ffi' show Abi; + +/// A hardware architecture which the Dart VM can run on. +final class Architecture { + /// The name of this architecture. + final String name; + + const Architecture._(this.name); + + /// The [Architecture] corresponding to the given [abi]. + factory Architecture.fromAbi(Abi abi) => _abiToArch[abi]!; + + /// The [arm](https://en.wikipedia.org/wiki/ARM_architecture_family) + /// architecture. + static const Architecture arm = Architecture._('arm'); + + /// The [AArch64](https://en.wikipedia.org/wiki/AArch64) architecture. + static const Architecture arm64 = Architecture._('arm64'); + + /// The [IA-32](https://en.wikipedia.org/wiki/IA-32) architecture. + static const Architecture ia32 = Architecture._('ia32'); + + /// The [RISC-V](https://en.wikipedia.org/wiki/RISC-V) 32 bit architecture. + static const Architecture riscv32 = Architecture._('riscv32'); + + /// The [RISC-V](https://en.wikipedia.org/wiki/RISC-V) 64 bit architecture. + static const Architecture riscv64 = Architecture._('riscv64'); + + /// The [x86-64](https://en.wikipedia.org/wiki/X86-64) architecture. + static const Architecture x64 = Architecture._('x64'); + + /// Known values for [Architecture]. + static const List values = [ + arm, + arm64, + ia32, + riscv32, + riscv64, + x64, + ]; + + static const _abiToArch = { + Abi.androidArm: Architecture.arm, + Abi.androidArm64: Architecture.arm64, + Abi.androidIA32: Architecture.ia32, + Abi.androidX64: Architecture.x64, + Abi.androidRiscv64: Architecture.riscv64, + Abi.fuchsiaArm64: Architecture.arm64, + Abi.fuchsiaRiscv64: Architecture.riscv64, + Abi.fuchsiaX64: Architecture.x64, + Abi.iosArm: Architecture.arm, + Abi.iosArm64: Architecture.arm64, + Abi.iosX64: Architecture.x64, + Abi.linuxArm: Architecture.arm, + Abi.linuxArm64: Architecture.arm64, + Abi.linuxIA32: Architecture.ia32, + Abi.linuxRiscv32: Architecture.riscv32, + Abi.linuxRiscv64: Architecture.riscv64, + Abi.linuxX64: Architecture.x64, + Abi.macosArm64: Architecture.arm64, + Abi.macosX64: Architecture.x64, + Abi.windowsArm64: Architecture.arm64, + Abi.windowsIA32: Architecture.ia32, + Abi.windowsX64: Architecture.x64, + }; + + /// The name of this [Architecture]. + /// + /// This returns a stable string that can be used to construct an + /// [Architecture] via [Architecture.fromString]. + @override + String toString() => name; + + static final Map _architectureByName = { + for (var architecture in values) architecture.name: architecture + }; + + /// Creates an [Architecture] from the given [name]. + /// + /// The name can be obtained from [Architecture.name] or + /// [Architecture.toString]. + factory Architecture.fromString(String name) => _architectureByName[name]!; + + /// The current [Architecture]. + static final Architecture current = _abiToArch[Abi.current()]!; +} diff --git a/pkgs/native_assets_cli/lib/src/api/build_mode.dart b/pkgs/native_assets_cli/lib/src/build_mode.dart similarity index 59% rename from pkgs/native_assets_cli/lib/src/api/build_mode.dart rename to pkgs/native_assets_cli/lib/src/build_mode.dart index ac973c7a2..7bbcba0d9 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_mode.dart +++ b/pkgs/native_assets_cli/lib/src/build_mode.dart @@ -2,8 +2,6 @@ // 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. -part '../model/build_mode.dart'; - /// The build mode for compiling native code assets. /// /// The Dart SDK does not have build modes. All build hook invocations are @@ -14,25 +12,41 @@ part '../model/build_mode.dart'; /// * Flutter release -> [release]. /// * Flutter profile -> [release]. /// * Flutter jit release -> [release]. -abstract final class BuildMode { +final class BuildMode { /// The name for this build mode. - String get name; + final String name; + + const BuildMode._(this.name); /// The debug build mode. /// /// Used by the Flutter SDK in its debug mode. - static const BuildMode debug = BuildModeImpl.debug; + static const debug = BuildMode._('debug'); /// The release build mode. /// /// Used by the Flutter SDK in its release, profile, and jit release modes. /// /// Used by the Dart SDK for every build. - static const BuildMode release = BuildModeImpl.release; + static const release = BuildMode._('release'); /// All known build modes. - static const values = [ + static const values = [ debug, release, ]; + + /// The name of this [BuildMode]. + /// + /// This returns a stable string that can be used to construct a + /// [BuildMode] via [BuildMode.fromString]. + factory BuildMode.fromString(String target) => + values.firstWhere((e) => e.name == target); + + /// The name of this [BuildMode]. + /// + /// This returns a stable string that can be used to construct a + /// [BuildMode] via [BuildMode.fromString]. + @override + String toString() => name; } diff --git a/pkgs/native_assets_cli/lib/src/c_compiler_config.dart b/pkgs/native_assets_cli/lib/src/c_compiler_config.dart new file mode 100644 index 000000000..b04cc7cbc --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/c_compiler_config.dart @@ -0,0 +1,117 @@ +// Copyright (c) 2023, 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. + +import 'package:collection/collection.dart'; + +import 'json_utils.dart'; +import 'utils/map.dart'; + +/// The configuration for a C toolchain. +final class CCompilerConfig { + /// Path to a C compiler. + late final Uri? compiler; + + /// Path to a native linker. + late final Uri? linker; + + /// Path to a native archiver. + late final Uri? archiver; + + /// Path to script that sets environment variables for [compiler], [linker], + /// and [archiver]. + late final Uri? envScript; + + /// Arguments for [envScript]. + late final List? envScriptArgs; + + /// Constructs a new [CCompilerConfig] based on the given toolchain tools. + CCompilerConfig({ + this.archiver, + this.compiler, + this.linker, + this.envScript, + this.envScriptArgs, + }); + + /// Constructs a [CCompilerConfig] from the given [json]. + /// + /// The json is expected to be valid encoding obtained via + /// [CCompilerConfig.toJson]. + factory CCompilerConfig.fromJson(Map json) { + final compiler = _parseCompiler(json); + return CCompilerConfig( + archiver: _parseArchiver(json), + compiler: compiler, + envScript: _parseEnvScript(json, compiler), + envScriptArgs: _parseEnvScriptArgs(json), + linker: _parseLinker(json), + ); + } + + /// The json representation of this [CCompilerConfig]. + /// + /// The returned json can be used in [CCompilerConfig.fromJson] to + /// obtain a [CCompilerConfig] again. + Map toJson() => { + if (archiver != null) _arConfigKey: archiver!.toFilePath(), + if (compiler != null) _ccConfigKey: compiler!.toFilePath(), + if (linker != null) _ldConfigKey: linker!.toFilePath(), + if (envScript != null) _envScriptConfigKey: envScript!.toFilePath(), + if (envScriptArgs != null) _envScriptArgsConfigKey: envScriptArgs!, + }.sortOnKey(); + + @override + bool operator ==(Object other) { + if (other is! CCompilerConfig) { + return false; + } + if (other.archiver != archiver) return false; + if (other.compiler != compiler) return false; + if (other.linker != linker) return false; + if (other.envScript != envScript) return false; + if (!const ListEquality() + .equals(other.envScriptArgs, envScriptArgs)) { + return false; + } + return true; + } + + @override + int get hashCode => Object.hash( + archiver, + compiler, + linker, + envScript, + const ListEquality().hash(envScriptArgs), + ); +} + +Uri? _parseArchiver(Map config) => config.optionalPath( + _arConfigKey, + mustExist: true, + ); + +Uri? _parseCompiler(Map config) => config.optionalPath( + _ccConfigKey, + mustExist: true, + ); + +Uri? _parseLinker(Map config) => config.optionalPath( + _ldConfigKey, + mustExist: true, + ); + +Uri? _parseEnvScript(Map config, Uri? compiler) => + (compiler != null && compiler.toFilePath().endsWith('cl.exe')) + ? config.path(_envScriptConfigKey, mustExist: true) + : null; + +List? _parseEnvScriptArgs(Map config) => + config.optionalStringList(_envScriptArgsConfigKey); + +const _arConfigKey = 'ar'; +const _ccConfigKey = 'cc'; +const _ldConfigKey = 'ld'; +const _envScriptConfigKey = 'env_script'; +const _envScriptArgsConfigKey = 'env_script_arguments'; diff --git a/pkgs/native_assets_cli/lib/src/api/ios_sdk.dart b/pkgs/native_assets_cli/lib/src/ios_sdk.dart similarity index 51% rename from pkgs/native_assets_cli/lib/src/api/ios_sdk.dart rename to pkgs/native_assets_cli/lib/src/ios_sdk.dart index a949f226f..c15ff7c17 100644 --- a/pkgs/native_assets_cli/lib/src/api/ios_sdk.dart +++ b/pkgs/native_assets_cli/lib/src/ios_sdk.dart @@ -2,25 +2,41 @@ // 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. -part '../model/ios_sdk.dart'; - /// For an iOS target, a build is either done for the device or the simulator. -abstract final class IOSSdk { +final class IOSSdk { + final String type; + + const IOSSdk._(this.type); + /// The iphoneos SDK in Xcode. /// /// The SDK location can be found on the host machine with /// `xcrun --sdk iphoneos --show-sdk-path`. - static const IOSSdk iPhoneOS = IOSSdkImpl.iPhoneOS; + static const iPhoneOS = IOSSdk._('iphoneos'); /// The iphonesimulator SDK in Xcode. /// /// The SDK location can be found on the host machine with /// `xcrun --sdk iphonesimulator --show-sdk-path`. - static const IOSSdk iPhoneSimulator = IOSSdkImpl.iPhoneSimulator; + static const iPhoneSimulator = IOSSdk._('iphonesimulator'); /// All known values for [IOSSdk]. - static const values = [ + static const values = [ iPhoneOS, iPhoneSimulator, ]; + + /// The type of this [IOSSdk]. + /// + /// This returns a stable string that can be used to construct a + /// [IOSSdk] via [IOSSdk.fromString]. + factory IOSSdk.fromString(String type) => + values.firstWhere((e) => e.type == type); + + /// The type of this [IOSSdk]. + /// + /// This returns a stable string that can be used to construct a + /// [IOSSdk] via [IOSSdk.fromString]. + @override + String toString() => type; } diff --git a/pkgs/native_assets_cli/lib/src/link_mode.dart b/pkgs/native_assets_cli/lib/src/link_mode.dart new file mode 100644 index 000000000..f258a4302 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/link_mode.dart @@ -0,0 +1,158 @@ +// Copyright (c) 2024, 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. + +import 'api/asset.dart'; +import 'api/build_config.dart'; + +/// The link mode for a [NativeCodeAsset]. +/// +/// Known linking modes: +/// +/// * [StaticLinking] +/// * [DynamicLoading] +/// * [LookupInProcess] +/// * [LookupInExecutable] +/// * [DynamicLoadingBundled] +/// * [DynamicLoadingSystem] +/// +/// See the documentation on the above classes. +abstract final class LinkMode { + const LinkMode._(); + + /// Constructs a [LinkMode] from the given [json]. + /// + /// The json is expected to be valid encoding obtained via [LinkMode.toJson]. + factory LinkMode.fromJson(Map json) { + final type = json['type']; + return switch (type) { + 'static' => StaticLinking._singleton, + 'dynamic_loading_process' => LookupInProcess._singleton, + 'dynamic_loading_executable' => LookupInExecutable._singleton, + 'dynamic_loading_bundle' => DynamicLoadingBundled._singleton, + 'dynamic_loading_system' => + DynamicLoadingSystem(Uri.parse(json['uri'] as String)), + _ => throw FormatException('The link mode "$type" is not known'), + }; + } + + /// The json representation of this [LinkMode]. + /// + /// The returned json is stable and can be used in [LinkMode.fromJson] to + /// obtain a [LinkMode] again. + Map toJson() => switch (this) { + StaticLinking() => {'type': 'static'}, + LookupInProcess() => {'type': 'dynamic_loading_process'}, + LookupInExecutable() => {'type': 'dynamic_loading_executable'}, + DynamicLoadingBundled() => {'type': 'dynamic_loading_bundle'}, + final DynamicLoadingSystem system => { + 'type': 'dynamic_loading_system', + 'uri': system.uri.toFilePath(), + }, + _ => throw UnimplementedError('The link mode "$this" is not known'), + }; +} + +/// The [NativeCodeAsset] will be loaded at runtime. +/// +/// Nothing happens at native code linking time. +/// +/// Supported in the Dart and Flutter SDK. +/// +/// Note: Dynamic loading is not equal to dynamic linking. Dynamic linking +/// would have to run the linker at compile-time, which is currently not +/// supported in the Dart and Flutter SDK. +abstract final class DynamicLoading extends LinkMode { + DynamicLoading._() : super._(); +} + +/// The dynamic library is bundled by Dart/Flutter at build time. +/// +/// At runtime, the dynamic library will be loaded and the symbols will be +/// looked up in this dynamic library. +/// +/// An asset with this dynamic loading method must provide a +/// [NativeCodeAsset.file]. The Dart and Flutter SDK will bundle this code in +/// the final application. +/// +/// During a [BuildConfig.dryRun], the [NativeCodeAsset.file] can be a file name +/// instead of a the full path. The file does not have to exist during a dry +/// run. +final class DynamicLoadingBundled extends DynamicLoading { + DynamicLoadingBundled._() : super._(); + + static final DynamicLoadingBundled _singleton = DynamicLoadingBundled._(); + + factory DynamicLoadingBundled() => _singleton; + + @override + String toString() => 'bundled'; +} + +/// The dynamic library is avaliable on the target system `PATH`. +/// +/// At buildtime, nothing happens. +/// +/// At runtime, the dynamic library will be loaded and the symbols will be +/// looked up in this dynamic library. +final class DynamicLoadingSystem extends DynamicLoading { + /// The [Uri] of the + final Uri uri; + + DynamicLoadingSystem(this.uri) : super._(); + + static const _typeValue = 'dynamic_loading_system'; + + @override + int get hashCode => uri.hashCode; + + @override + bool operator ==(Object other) => + other is DynamicLoadingSystem && uri == other.uri; + + @override + String toString() => _typeValue; +} + +/// The native code is loaded in the process and symbols are available through +/// `DynamicLibrary.process()`. +final class LookupInProcess extends DynamicLoading { + LookupInProcess._() : super._(); + + static final LookupInProcess _singleton = LookupInProcess._(); + + factory LookupInProcess() => _singleton; + + @override + String toString() => 'process'; +} + +/// The native code is embedded in executable and symbols are available through +/// `DynamicLibrary.executable()`. +final class LookupInExecutable extends DynamicLoading { + LookupInExecutable._() : super._(); + + static final LookupInExecutable _singleton = LookupInExecutable._(); + + factory LookupInExecutable() => _singleton; + + @override + String toString() => 'executable'; +} + +/// Static linking. +/// +/// At native linking time, native function names will be resolved to static +/// libraries. +/// +/// Not yet supported in the Dart and Flutter SDK. +// TODO(https://github.com/dart-lang/sdk/issues/49418): Support static linking. +final class StaticLinking extends LinkMode { + const StaticLinking._() : super._(); + factory StaticLinking() => _singleton; + + static const StaticLinking _singleton = StaticLinking._(); + + @override + String toString() => 'static'; +} diff --git a/pkgs/native_assets_cli/lib/src/api/link_mode_preference.dart b/pkgs/native_assets_cli/lib/src/link_mode_preference.dart similarity index 63% rename from pkgs/native_assets_cli/lib/src/api/link_mode_preference.dart rename to pkgs/native_assets_cli/lib/src/link_mode_preference.dart index f600704a1..8a1eb614f 100644 --- a/pkgs/native_assets_cli/lib/src/api/link_mode_preference.dart +++ b/pkgs/native_assets_cli/lib/src/link_mode_preference.dart @@ -2,39 +2,58 @@ // 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. -import 'asset.dart'; - -part '../model/link_mode_preference.dart'; +import 'api/asset.dart'; /// The preferred linkMode method for [NativeCodeAsset]s. -abstract final class LinkModePreference { +final class LinkModePreference { /// The name for this link mode. - String get name; + final String name; + + const LinkModePreference(this.name); + + factory LinkModePreference.fromString(String name) => + values.firstWhere((element) => element.name == name); /// Provide native assets as dynamic libraries. /// /// Fails if not all native assets can only be provided as static library. /// Required to run Dart in JIT mode. - static const LinkModePreference dynamic = LinkModePreferenceImpl.dynamic; + static const dynamic = LinkModePreference( + 'dynamic', + ); /// Provide native assets as static libraries. /// /// Fails if not all native assets can only be provided as dynamic library. /// Required for potential link-time tree-shaking of native code. /// Therefore, preferred to in Dart AOT mode. - static const LinkModePreference static = LinkModePreferenceImpl.static; + static const static = LinkModePreference( + 'static', + ); /// Provide native assets as dynamic libraries, if possible. /// /// Otherwise, build native assets as static libraries - static const LinkModePreference preferDynamic = - LinkModePreferenceImpl.preferDynamic; + static const preferDynamic = LinkModePreference( + 'prefer-dynamic', + ); /// Provide native assets as static libraries, if possible. /// /// Otherwise, build native assets as dynamic libraries. Preferred for AOT /// compilation, if there are any native assets which can only be provided as /// dynamic libraries. - static const LinkModePreference preferStatic = - LinkModePreferenceImpl.preferStatic; + static const preferStatic = LinkModePreference( + 'prefer-static', + ); + + static const values = [ + dynamic, + static, + preferDynamic, + preferStatic, + ]; + + @override + String toString() => name; } diff --git a/pkgs/native_assets_cli/lib/src/model/architecture.dart b/pkgs/native_assets_cli/lib/src/model/architecture.dart deleted file mode 100644 index 384eb5487..000000000 --- a/pkgs/native_assets_cli/lib/src/model/architecture.dart +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2023, 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. - -part of '../api/architecture.dart'; - -final class ArchitectureImpl implements Architecture { - /// This architecture as used in [Platform.version]. - final String dartPlatform; - - const ArchitectureImpl._(this.dartPlatform); - - factory ArchitectureImpl.fromAbi(Abi abi) => _abiToArch[abi]!; - - static const ArchitectureImpl arm = ArchitectureImpl._('arm'); - static const ArchitectureImpl arm64 = ArchitectureImpl._('arm64'); - static const ArchitectureImpl ia32 = ArchitectureImpl._('ia32'); - static const ArchitectureImpl riscv32 = ArchitectureImpl._('riscv32'); - static const ArchitectureImpl riscv64 = ArchitectureImpl._('riscv64'); - static const ArchitectureImpl x64 = ArchitectureImpl._('x64'); - - static const List values = [ - arm, - arm64, - ia32, - riscv32, - riscv64, - x64, - ]; - - static const _abiToArch = { - Abi.androidArm: ArchitectureImpl.arm, - Abi.androidArm64: ArchitectureImpl.arm64, - Abi.androidIA32: ArchitectureImpl.ia32, - Abi.androidX64: ArchitectureImpl.x64, - Abi.androidRiscv64: ArchitectureImpl.riscv64, - Abi.fuchsiaArm64: ArchitectureImpl.arm64, - Abi.fuchsiaX64: ArchitectureImpl.x64, - Abi.iosArm: ArchitectureImpl.arm, - Abi.iosArm64: ArchitectureImpl.arm64, - Abi.iosX64: ArchitectureImpl.x64, - Abi.linuxArm: ArchitectureImpl.arm, - Abi.linuxArm64: ArchitectureImpl.arm64, - Abi.linuxIA32: ArchitectureImpl.ia32, - Abi.linuxRiscv32: ArchitectureImpl.riscv32, - Abi.linuxRiscv64: ArchitectureImpl.riscv64, - Abi.linuxX64: ArchitectureImpl.x64, - Abi.macosArm64: ArchitectureImpl.arm64, - Abi.macosX64: ArchitectureImpl.x64, - Abi.windowsArm64: ArchitectureImpl.arm64, - Abi.windowsIA32: ArchitectureImpl.ia32, - Abi.windowsX64: ArchitectureImpl.x64, - }; - - static const String configKey = 'target_architecture'; - - @override - String toString() => dartPlatform; - - static final Map _architectureByName = { - for (var architecture in values) architecture.dartPlatform: architecture - }; - - factory ArchitectureImpl.fromString(String target) => - _architectureByName[target]!; - - static final ArchitectureImpl current = Target.current.architecture; -} diff --git a/pkgs/native_assets_cli/lib/src/model/build_mode.dart b/pkgs/native_assets_cli/lib/src/model/build_mode.dart deleted file mode 100644 index 9fc2d8d63..000000000 --- a/pkgs/native_assets_cli/lib/src/model/build_mode.dart +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2023, 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. - -part of '../api/build_mode.dart'; - -final class BuildModeImpl implements BuildMode { - @override - final String name; - - const BuildModeImpl._(this.name); - - static const debug = BuildModeImpl._('debug'); - static const release = BuildModeImpl._('release'); - - static const values = [ - debug, - release, - ]; - - factory BuildModeImpl.fromString(String target) => - values.firstWhere((e) => e.name == target); - - static const String configKey = 'build_mode'; - - @override - String toString() => name; -} diff --git a/pkgs/native_assets_cli/lib/src/model/c_compiler_config.dart b/pkgs/native_assets_cli/lib/src/model/c_compiler_config.dart deleted file mode 100644 index 1dc496c0a..000000000 --- a/pkgs/native_assets_cli/lib/src/model/c_compiler_config.dart +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2023, 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. - -part of '../api/build_config.dart'; - -final class CCompilerConfigImpl implements CCompilerConfig { - /// Path to a C compiler. - @override - Uri? get compiler => _compiler; - late final Uri? _compiler; - - /// Path to a native linker. - @override - Uri? get linker => _linker; - late final Uri? _linker; - - /// Path to a native archiver. - @override - Uri? get archiver => _archiver; - late final Uri? _archiver; - - /// Path to script that sets environment variables for [compiler], [linker], - /// and [archiver]. - @override - Uri? get envScript => _envScript; - late final Uri? _envScript; - - /// Arguments for [envScript]. - @override - List? get envScriptArgs => _envScriptArgs; - late final List? _envScriptArgs; - - factory CCompilerConfigImpl({ - Uri? archiver, - Uri? compiler, - Uri? linker, - Uri? envScript, - List? envScriptArgs, - }) => - CCompilerConfigImpl._() - .._archiver = archiver - .._compiler = compiler - .._linker = linker - .._envScript = envScript - .._envScriptArgs = envScriptArgs; - - CCompilerConfigImpl._(); - - static const configKey = 'c_compiler'; - static const arConfigKey = 'ar'; - static const arConfigKeyFull = '$configKey.$arConfigKey'; - static const ccConfigKey = 'cc'; - static const ccConfigKeyFull = '$configKey.$ccConfigKey'; - static const ldConfigKey = 'ld'; - static const ldConfigKeyFull = '$configKey.$ldConfigKey'; - static const envScriptConfigKey = 'env_script'; - static const envScriptConfigKeyFull = '$configKey.$envScriptConfigKey'; - static const envScriptArgsConfigKey = 'env_script_arguments'; - static const envScriptArgsConfigKeyFull = - '$configKey.$envScriptArgsConfigKey'; - - Map toJson() => { - if (_archiver != null) arConfigKey: _archiver.toFilePath(), - if (_compiler != null) ccConfigKey: _compiler.toFilePath(), - if (_linker != null) ldConfigKey: _linker.toFilePath(), - if (_envScript != null) envScriptConfigKey: _envScript.toFilePath(), - if (_envScriptArgs != null) envScriptArgsConfigKey: _envScriptArgs, - }.sortOnKey(); - - @override - bool operator ==(Object other) { - if (other is! CCompilerConfigImpl) { - return false; - } - if (other.archiver != archiver) return false; - if (other.compiler != compiler) return false; - if (other.linker != linker) return false; - if (other.envScript != envScript) return false; - if (!const ListEquality() - .equals(other.envScriptArgs, envScriptArgs)) { - return false; - } - return true; - } - - @override - int get hashCode => Object.hash( - _archiver, - _compiler, - _linker, - _envScript, - const ListEquality().hash(envScriptArgs), - ); -} diff --git a/pkgs/native_assets_cli/lib/src/model/hook_config.dart b/pkgs/native_assets_cli/lib/src/model/hook_config.dart index f67ce2513..8cba1dbb9 100644 --- a/pkgs/native_assets_cli/lib/src/model/hook_config.dart +++ b/pkgs/native_assets_cli/lib/src/model/hook_config.dart @@ -21,18 +21,18 @@ abstract class HookConfigImpl implements HookConfig { final Version version; - final BuildModeImpl? _buildMode; + final BuildMode? _buildMode; @override - BuildModeImpl get buildMode { + BuildMode get buildMode { ensureNotDryRun(dryRun); return _buildMode!; } - final CCompilerConfigImpl _cCompiler; + final CCompilerConfig _cCompiler; @override - CCompilerConfigImpl get cCompiler { + CCompilerConfig get cCompiler { ensureNotDryRun(dryRun); return _cCompiler; } @@ -46,7 +46,7 @@ abstract class HookConfigImpl implements HookConfig { final int? _targetAndroidNdkApi; @override - final LinkModePreferenceImpl linkModePreference; + final LinkModePreference linkModePreference; @override int? get targetAndroidNdkApi { @@ -55,12 +55,12 @@ abstract class HookConfigImpl implements HookConfig { } @override - final ArchitectureImpl? targetArchitecture; + final Architecture? targetArchitecture; - final IOSSdkImpl? _targetIOSSdk; + final IOSSdk? _targetIOSSdk; @override - IOSSdkImpl? get targetIOSSdk { + IOSSdk? get targetIOSSdk { ensureNotDryRun(dryRun); if (targetOS != OS.iOS) { throw StateError( @@ -97,7 +97,7 @@ abstract class HookConfigImpl implements HookConfig { } @override - final OSImpl targetOS; + final OS targetOS; /// Output file name based on the protocol version. /// @@ -111,12 +111,12 @@ abstract class HookConfigImpl implements HookConfig { required this.packageName, required this.packageRoot, required this.version, - required BuildModeImpl? buildMode, - required CCompilerConfigImpl? cCompiler, + required BuildMode? buildMode, + required CCompilerConfig? cCompiler, required this.supportedAssetTypes, required int? targetAndroidNdkApi, required this.targetArchitecture, - required IOSSdkImpl? targetIOSSdk, + required IOSSdk? targetIOSSdk, required int? targetIOSVersion, required int? targetMacOSVersion, required this.linkModePreference, @@ -127,7 +127,7 @@ abstract class HookConfigImpl implements HookConfig { _targetIOSVersion = targetIOSVersion, _targetMacOSVersion = targetMacOSVersion, _buildMode = buildMode, - _cCompiler = cCompiler ?? CCompilerConfigImpl(), + _cCompiler = cCompiler ?? CCompilerConfig(), dryRun = dryRun ?? false; HookConfigImpl.dryRun({ @@ -140,7 +140,7 @@ abstract class HookConfigImpl implements HookConfig { required this.supportedAssetTypes, required this.linkModePreference, required this.targetOS, - }) : _cCompiler = CCompilerConfigImpl(), + }) : _cCompiler = CCompilerConfig(), dryRun = true, targetArchitecture = null, _buildMode = null, @@ -181,26 +181,25 @@ abstract class HookConfigImpl implements HookConfig { outDirSharedConfigKey: outputDirectoryShared.toFilePath(), packageNameConfigKey: packageName, packageRootConfigKey: packageRoot.toFilePath(), - OSImpl.configKey: targetOS.toString(), + _targetOSConfigKey: targetOS.toString(), if (supportedAssetTypes.isNotEmpty) supportedAssetTypesKey: supportedAssetTypes, _versionKey: version.toString(), if (dryRun) dryRunConfigKey: dryRun, if (!dryRun) ...{ - BuildModeImpl.configKey: buildMode.toString(), - ArchitectureImpl.configKey: targetArchitecture.toString(), + _buildModeConfigKey: buildMode.toString(), + _targetArchitectureKey: targetArchitecture.toString(), if (targetOS == OS.iOS && targetIOSSdk != null) - IOSSdkImpl.configKey: targetIOSSdk.toString(), + _targetIOSSdkConfigKey: targetIOSSdk.toString(), if (targetOS == OS.iOS && targetIOSVersion != null) targetIOSVersionConfigKey: targetIOSVersion!, if (targetOS == OS.macOS && targetMacOSVersion != null) targetMacOSVersionConfigKey: targetMacOSVersion!, if (targetAndroidNdkApi != null) targetAndroidNdkApiConfigKey: targetAndroidNdkApi!, - if (cCompilerJson.isNotEmpty) - CCompilerConfigImpl.configKey: cCompilerJson, + if (cCompilerJson.isNotEmpty) _compilerConfigKey: cCompilerJson, }, - LinkModePreferenceImpl.configKey: linkModePreference.toString(), + _linkModePreferenceConfigKey: linkModePreference.toString(), }.sortOnKey(); } @@ -250,73 +249,72 @@ abstract class HookConfigImpl implements HookConfig { static Uri parsePackageRoot(Map config) => config.path(packageRootConfigKey, mustExist: true); - static BuildModeImpl? parseBuildMode( - Map config, bool dryRun) { + static BuildMode? parseBuildMode(Map config, bool dryRun) { if (dryRun) { - _throwIfNotNullInDryRun(config, BuildModeImpl.configKey); + _throwIfNotNullInDryRun(config, _buildModeConfigKey); return null; } else { - return BuildModeImpl.fromString( + return BuildMode.fromString( config.string( - BuildModeImpl.configKey, - validValues: BuildModeImpl.values.map((e) => '$e'), + _buildModeConfigKey, + validValues: BuildMode.values.map((e) => '$e'), ), ); } } - static LinkModePreferenceImpl parseLinkModePreference( + static LinkModePreference parseLinkModePreference( Map config) => - LinkModePreferenceImpl.fromString( + LinkModePreference.fromString( config.string( - LinkModePreferenceImpl.configKey, - validValues: LinkModePreferenceImpl.values.map((e) => e.toString()), + _linkModePreferenceConfigKey, + validValues: LinkModePreference.values.map((e) => '$e'), ), ); - static OSImpl parseTargetOS(Map config) => OSImpl.fromString( + static OS parseTargetOS(Map config) => OS.fromString( config.string( - OSImpl.configKey, - validValues: OSImpl.values.map((e) => '$e'), + _targetOSConfigKey, + validValues: OS.values.map((e) => '$e'), ), ); - static ArchitectureImpl? parseTargetArchitecture( + static Architecture? parseTargetArchitecture( Map config, bool dryRun, - OSImpl? targetOS, + OS? targetOS, ) { if (dryRun) { - _throwIfNotNullInDryRun(config, ArchitectureImpl.configKey); + _throwIfNotNullInDryRun(config, _targetArchitectureKey); return null; } else { final validArchitectures = [ if (targetOS == null) - ...ArchitectureImpl.values + ...Architecture.values else for (final target in Target.values) if (target.os == targetOS) target.architecture ]; - return ArchitectureImpl.fromString( + return Architecture.fromString( config.string( - ArchitectureImpl.configKey, + _targetArchitectureKey, validValues: validArchitectures.map((e) => '$e'), ), ); } } - static IOSSdkImpl? parseTargetIOSSdk( - Map config, bool dryRun, OSImpl? targetOS) { + static IOSSdk? parseTargetIOSSdk( + Map config, bool dryRun, OS? targetOS) { if (dryRun) { - _throwIfNotNullInDryRun(config, IOSSdkImpl.configKey); + _throwIfNotNullInDryRun(config, _targetIOSSdkConfigKey); return null; } else { - return targetOS == OSImpl.iOS - ? IOSSdkImpl.fromString( + return targetOS == OS.iOS + ? IOSSdk.fromString( config.string( - IOSSdkImpl.configKey, - validValues: IOSSdkImpl.values.map((e) => '$e'), + _targetIOSSdkConfigKey, + validValues: IOSSdk.values.map((e) => '$e'), ), ) : null; @@ -326,13 +324,13 @@ abstract class HookConfigImpl implements HookConfig { static int? parseTargetAndroidNdkApi( Map config, bool dryRun, - OSImpl? targetOS, + OS? targetOS, ) { if (dryRun) { _throwIfNotNullInDryRun(config, targetAndroidNdkApiConfigKey); return null; } else { - return (targetOS == OSImpl.android) + return (targetOS == OS.android) ? config.int(targetAndroidNdkApiConfigKey) : null; } @@ -341,13 +339,13 @@ abstract class HookConfigImpl implements HookConfig { static int? parseTargetIosVersion( Map config, bool dryRun, - OSImpl? targetOS, + OS? targetOS, ) { if (dryRun) { _throwIfNotNullInDryRun(config, targetIOSVersionConfigKey); return null; } else { - return (targetOS == OSImpl.iOS) + return (targetOS == OS.iOS) ? config.optionalInt(targetIOSVersionConfigKey) : null; } @@ -356,65 +354,33 @@ abstract class HookConfigImpl implements HookConfig { static int? parseTargetMacOSVersion( Map config, bool dryRun, - OSImpl? targetOS, + OS? targetOS, ) { if (dryRun) { _throwIfNotNullInDryRun(config, targetMacOSVersionConfigKey); return null; } else { - return (targetOS == OSImpl.macOS) + return (targetOS == OS.macOS) ? config.optionalInt(targetMacOSVersionConfigKey) : null; } } - static Uri? _parseArchiver(Map config) => - config.optionalPath( - CCompilerConfigImpl.arConfigKey, - mustExist: true, - ); - - static Uri? _parseCompiler(Map config) => - config.optionalPath( - CCompilerConfigImpl.ccConfigKey, - mustExist: true, - ); - - static Uri? _parseLinker(Map config) => config.optionalPath( - CCompilerConfigImpl.ldConfigKey, - mustExist: true, - ); - - static Uri? _parseEnvScript(Map config, Uri? compiler) => - (compiler != null && compiler.toFilePath().endsWith('cl.exe')) - ? config.path(CCompilerConfigImpl.envScriptConfigKey, mustExist: true) - : null; - - static List? _parseEnvScriptArgs(Map config) => - config.optionalStringList(CCompilerConfigImpl.envScriptArgsConfigKey); - static List parseSupportedAssetTypes(Map config) => config.optionalStringList(supportedAssetTypesKey) ?? [NativeCodeAsset.type]; - static CCompilerConfigImpl parseCCompiler( + static CCompilerConfig parseCCompiler( Map config, bool dryRun) { if (dryRun) { - _throwIfNotNullInDryRun(config, CCompilerConfigImpl.configKey); + _throwIfNotNullInDryRun(config, _compilerConfigKey); } final cCompilerJson = - config.getOptional>(CCompilerConfigImpl.configKey); - if (cCompilerJson == null) return CCompilerConfigImpl(); - - final compiler = _parseCompiler(cCompilerJson); - return CCompilerConfigImpl( - archiver: _parseArchiver(cCompilerJson), - compiler: compiler, - envScript: _parseEnvScript(cCompilerJson, compiler), - envScriptArgs: _parseEnvScriptArgs(cCompilerJson), - linker: _parseLinker(cCompilerJson), - ); + config.getOptional>(_compilerConfigKey); + if (cCompilerJson == null) return CCompilerConfig(); + + return CCompilerConfig.fromJson(cCompilerJson); } static void _throwIfNotNullInDryRun( @@ -493,13 +459,13 @@ can _only_ depend on OS.'''); static String checksum({ required String packageName, required Uri packageRoot, - required ArchitectureImpl targetArchitecture, - required OSImpl targetOS, - required BuildModeImpl buildMode, - IOSSdkImpl? targetIOSSdk, + required Architecture targetArchitecture, + required OS targetOS, + required BuildMode buildMode, + IOSSdk? targetIOSSdk, int? targetAndroidNdkApi, - CCompilerConfigImpl? cCompiler, - required LinkModePreferenceImpl linkModePreference, + CCompilerConfig? cCompiler, + required LinkModePreference linkModePreference, Map? dependencyMetadata, Iterable? supportedAssetTypes, Version? version, @@ -540,8 +506,8 @@ can _only_ depend on OS.'''); static String checksumDryRun({ required String packageName, required Uri packageRoot, - required OSImpl targetOS, - required LinkModePreferenceImpl linkModePreference, + required OS targetOS, + required LinkModePreference linkModePreference, Version? version, Iterable? supportedAssetTypes, required Hook hook, @@ -574,3 +540,10 @@ can _only_ depend on OS.'''); /// representation in the protocol. static Version latestVersion = Version(1, 5, 0); } + +const String _compilerConfigKey = 'c_compiler'; +const String _buildModeConfigKey = 'build_mode'; +const String _targetOSConfigKey = 'target_os'; +const String _targetArchitectureKey = 'target_architecture'; +const String _targetIOSSdkConfigKey = 'target_ios_sdk'; +const String _linkModePreferenceConfigKey = 'link_mode_preference'; diff --git a/pkgs/native_assets_cli/lib/src/model/ios_sdk.dart b/pkgs/native_assets_cli/lib/src/model/ios_sdk.dart deleted file mode 100644 index 9c20c9e12..000000000 --- a/pkgs/native_assets_cli/lib/src/model/ios_sdk.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2023, 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. - -part of '../api/ios_sdk.dart'; - -final class IOSSdkImpl implements IOSSdk { - final String xcodebuildSdk; - - const IOSSdkImpl._(this.xcodebuildSdk); - - static const iPhoneOS = IOSSdkImpl._('iphoneos'); - static const iPhoneSimulator = IOSSdkImpl._('iphonesimulator'); - - static const values = [ - iPhoneOS, - iPhoneSimulator, - ]; - - factory IOSSdkImpl.fromString(String target) => - values.firstWhere((e) => e.xcodebuildSdk == target); - - static const String configKey = 'target_ios_sdk'; - - @override - String toString() => xcodebuildSdk; -} diff --git a/pkgs/native_assets_cli/lib/src/model/link_mode_preference.dart b/pkgs/native_assets_cli/lib/src/model/link_mode_preference.dart deleted file mode 100644 index 1ee1357a4..000000000 --- a/pkgs/native_assets_cli/lib/src/model/link_mode_preference.dart +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2023, 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. - -part of '../api/link_mode_preference.dart'; - -final class LinkModePreferenceImpl implements LinkModePreference { - @override - final String name; - - const LinkModePreferenceImpl(this.name); - - factory LinkModePreferenceImpl.fromString(String name) => - values.where((element) => element.name == name).first; - - static const dynamic = LinkModePreferenceImpl( - 'dynamic', - ); - - static const static = LinkModePreferenceImpl( - 'static', - ); - - static const preferDynamic = LinkModePreferenceImpl( - 'prefer-dynamic', - ); - - static const preferStatic = LinkModePreferenceImpl( - 'prefer-static', - ); - - static const values = [ - dynamic, - static, - preferDynamic, - preferStatic, - ]; - - static const String configKey = 'link_mode_preference'; - - @override - String toString() => name; -} diff --git a/pkgs/native_assets_cli/lib/src/model/native_code_asset.dart b/pkgs/native_assets_cli/lib/src/model/native_code_asset.dart index b04f278d6..fcf14eef9 100644 --- a/pkgs/native_assets_cli/lib/src/model/native_code_asset.dart +++ b/pkgs/native_assets_cli/lib/src/model/native_code_asset.dart @@ -4,167 +4,21 @@ part of '../api/asset.dart'; -abstract final class LinkModeImpl implements LinkMode { - /// Serialization of a link mode. - /// - /// Does not include the toplevel keys. - /// - /// ``` - /// type: dynamic_loading_system - /// uri: ${foo3Uri.toFilePath()} - /// ``` - Map toJson(); - - factory LinkModeImpl(String type, Uri? uri) { - switch (type) { - case DynamicLoadingBundledImpl._typeValue: - return DynamicLoadingBundledImpl(); - case DynamicLoadingSystemImpl._typeValue: - return DynamicLoadingSystemImpl(uri!); - case LookupInExecutableImpl._typeValue: - return LookupInExecutableImpl(); - case LookupInProcessImpl._typeValue: - return LookupInProcessImpl(); - case StaticLinkingImpl._typeValue: - return StaticLinkingImpl(); - } - throw FormatException('Unknown type: $type.'); - } - - factory LinkModeImpl.fromJson(Map jsonMap) { - final type = get(jsonMap, _typeKey); - final uriString = get(jsonMap, _uriKey); - final uri = uriString != null ? Uri(path: uriString) : null; - return LinkModeImpl(type, uri); - } - - static const _typeKey = 'type'; - static const _uriKey = 'uri'; -} - -abstract final class DynamicLoadingImpl - implements LinkModeImpl, DynamicLoading { - static const _typeKey = 'type'; - static const _uriKey = 'uri'; -} - -final class DynamicLoadingBundledImpl - implements DynamicLoadingImpl, DynamicLoadingBundled { - DynamicLoadingBundledImpl._(); - - static final DynamicLoadingBundledImpl _singleton = - DynamicLoadingBundledImpl._(); - - factory DynamicLoadingBundledImpl() => _singleton; - - static const _typeValue = 'dynamic_loading_bundle'; - - @override - Map toJson() => { - DynamicLoadingImpl._typeKey: _typeValue, - }; - - @override - String toString() => _typeValue; -} - -final class DynamicLoadingSystemImpl - implements DynamicLoadingImpl, DynamicLoadingSystem { - @override - final Uri uri; - - DynamicLoadingSystemImpl(this.uri); - - static const _typeValue = 'dynamic_loading_system'; - - @override - Map toJson() => { - DynamicLoadingImpl._typeKey: _typeValue, - DynamicLoadingImpl._uriKey: uri.toFilePath(), - }; - - @override - int get hashCode => Object.hash(uri, 133723); - - @override - bool operator ==(Object other) { - if (other is! DynamicLoadingSystemImpl) { - return false; - } - return uri == other.uri; - } - - @override - String toString() => _typeValue; -} - -final class LookupInProcessImpl implements DynamicLoadingImpl, LookupInProcess { - LookupInProcessImpl._(); - - static final LookupInProcessImpl _singleton = LookupInProcessImpl._(); - - factory LookupInProcessImpl() => _singleton; - - static const _typeValue = 'dynamic_loading_process'; - - @override - Map toJson() => { - DynamicLoadingImpl._typeKey: _typeValue, - }; - - @override - String toString() => _typeValue; -} - -final class LookupInExecutableImpl - implements DynamicLoadingImpl, LookupInExecutable { - LookupInExecutableImpl._(); - - static final LookupInExecutableImpl _singleton = LookupInExecutableImpl._(); - - factory LookupInExecutableImpl() => _singleton; - - static const _typeValue = 'dynamic_loading_executable'; - - @override - Map toJson() => { - DynamicLoadingImpl._typeKey: _typeValue, - }; -} - -final class StaticLinkingImpl implements LinkModeImpl, StaticLinking { - StaticLinkingImpl._(); - - static final StaticLinkingImpl _singleton = StaticLinkingImpl._(); - - factory StaticLinkingImpl() => _singleton; - - static const _typeValue = 'static'; - - @override - Map toJson() => { - DynamicLoadingImpl._typeKey: _typeValue, - }; - - @override - String toString() => _typeValue; -} - final class NativeCodeAssetImpl implements NativeCodeAsset, AssetImpl { @override final Uri? file; @override - final LinkModeImpl linkMode; + final LinkMode linkMode; @override final String id; @override - final OSImpl os; + final OS os; @override - final ArchitectureImpl? architecture; + final Architecture? architecture; NativeCodeAssetImpl({ this.file, @@ -186,9 +40,7 @@ final class NativeCodeAssetImpl implements NativeCodeAsset, AssetImpl { factory NativeCodeAssetImpl.fromJson(Map jsonMap) { final linkModeJson = jsonMap[_linkModeKey]; - final linkMode = - LinkModeImpl.fromJson(as>(linkModeJson)); - + final linkMode = LinkMode.fromJson(as>(linkModeJson)); final fileString = get(jsonMap, _fileKey); final Uri? file; if (fileString != null) { @@ -196,11 +48,11 @@ final class NativeCodeAssetImpl implements NativeCodeAsset, AssetImpl { } else { file = null; } - final ArchitectureImpl? architecture; - final os = OSImpl.fromString(get(jsonMap, _osKey)); + final Architecture? architecture; + final os = OS.fromString(get(jsonMap, _osKey)); final architectureString = get(jsonMap, _architectureKey); if (architectureString != null) { - architecture = ArchitectureImpl.fromString(architectureString); + architecture = Architecture.fromString(architectureString); } else { architecture = null; } @@ -215,10 +67,10 @@ final class NativeCodeAssetImpl implements NativeCodeAsset, AssetImpl { } NativeCodeAssetImpl copyWith({ - LinkModeImpl? linkMode, + LinkMode? linkMode, String? id, - OSImpl? os, - ArchitectureImpl? architecture, + OS? os, + Architecture? architecture, Uri? file, }) => NativeCodeAssetImpl( diff --git a/pkgs/native_assets_cli/lib/src/model/os.dart b/pkgs/native_assets_cli/lib/src/model/os.dart deleted file mode 100644 index 73fd3ea0c..000000000 --- a/pkgs/native_assets_cli/lib/src/model/os.dart +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) 2023, 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. - -part of '../api/os.dart'; - -final class OSImpl implements OS { - /// This OS as used in [Platform.version] - final String dartPlatform; - - const OSImpl._(this.dartPlatform); - - factory OSImpl.fromAbi(Abi abi) => _abiToOS[abi]!; - - static const OSImpl android = OSImpl._('android'); - static const OSImpl fuchsia = OSImpl._('fuchsia'); - static const OSImpl iOS = OSImpl._('ios'); - static const OSImpl linux = OSImpl._('linux'); - static const OSImpl macOS = OSImpl._('macos'); - static const OSImpl windows = OSImpl._('windows'); - - static const List values = [ - android, - fuchsia, - iOS, - linux, - macOS, - windows, - ]; - - static const _abiToOS = { - Abi.androidArm: OSImpl.android, - Abi.androidArm64: OSImpl.android, - Abi.androidIA32: OSImpl.android, - Abi.androidX64: OSImpl.android, - Abi.androidRiscv64: OSImpl.android, - Abi.fuchsiaArm64: OSImpl.fuchsia, - Abi.fuchsiaX64: OSImpl.fuchsia, - Abi.iosArm: OSImpl.iOS, - Abi.iosArm64: OSImpl.iOS, - Abi.iosX64: OSImpl.iOS, - Abi.linuxArm: OSImpl.linux, - Abi.linuxArm64: OSImpl.linux, - Abi.linuxIA32: OSImpl.linux, - Abi.linuxRiscv32: OSImpl.linux, - Abi.linuxRiscv64: OSImpl.linux, - Abi.linuxX64: OSImpl.linux, - Abi.macosArm64: OSImpl.macOS, - Abi.macosX64: OSImpl.macOS, - Abi.windowsArm64: OSImpl.windows, - Abi.windowsIA32: OSImpl.windows, - Abi.windowsX64: OSImpl.windows, - }; - - static const _osTargets = { - OSImpl.android: { - ArchitectureImpl.arm, - ArchitectureImpl.arm64, - ArchitectureImpl.ia32, - ArchitectureImpl.x64, - ArchitectureImpl.riscv64, - }, - OSImpl.fuchsia: { - ArchitectureImpl.arm64, - ArchitectureImpl.x64, - }, - OSImpl.iOS: { - ArchitectureImpl.arm, - ArchitectureImpl.arm64, - ArchitectureImpl.x64, - }, - OSImpl.linux: { - ArchitectureImpl.arm, - ArchitectureImpl.arm64, - ArchitectureImpl.ia32, - ArchitectureImpl.riscv32, - ArchitectureImpl.riscv64, - ArchitectureImpl.x64, - }, - OSImpl.macOS: { - ArchitectureImpl.arm64, - ArchitectureImpl.x64, - }, - OSImpl.windows: { - ArchitectureImpl.arm64, - ArchitectureImpl.ia32, - ArchitectureImpl.x64, - }, - }; - - Iterable get architectures => _osTargets[this]!; - - /// Typical cross compilation between OSes. - static const osCrossCompilationDefault = { - OSImpl.macOS: [OSImpl.macOS, OSImpl.iOS, OSImpl.android], - OSImpl.linux: [OSImpl.linux, OSImpl.android], - OSImpl.windows: [OSImpl.windows, OSImpl.android], - }; - - @override - String dylibFileName(String name) { - final prefix = _dylibPrefix[this]!; - final extension = _dylibExtension[this]!; - return '$prefix$name.$extension'; - } - - @override - String staticlibFileName(String name) { - final prefix = _staticlibPrefix[this]!; - final extension = _staticlibExtension[this]!; - return '$prefix$name.$extension'; - } - - @override - String libraryFileName(String name, LinkMode linkMode) { - if (linkMode is DynamicLoading) { - return dylibFileName(name); - } - assert(linkMode is StaticLinking); - return staticlibFileName(name); - } - - @override - String executableFileName(String name) { - final extension = _executableExtension[this]!; - final dot = extension.isNotEmpty ? '.' : ''; - return '$name$dot$extension'; - } - - /// The default name prefix for dynamic libraries per [OSImpl]. - static const _dylibPrefix = { - OSImpl.android: 'lib', - OSImpl.fuchsia: 'lib', - OSImpl.iOS: 'lib', - OSImpl.linux: 'lib', - OSImpl.macOS: 'lib', - OSImpl.windows: '', - }; - - /// The default extension for dynamic libraries per [OSImpl]. - static const _dylibExtension = { - OSImpl.android: 'so', - OSImpl.fuchsia: 'so', - OSImpl.iOS: 'dylib', - OSImpl.linux: 'so', - OSImpl.macOS: 'dylib', - OSImpl.windows: 'dll', - }; - - /// The default name prefix for static libraries per [OSImpl]. - static const _staticlibPrefix = _dylibPrefix; - - /// The default extension for static libraries per [OSImpl]. - static const _staticlibExtension = { - OSImpl.android: 'a', - OSImpl.fuchsia: 'a', - OSImpl.iOS: 'a', - OSImpl.linux: 'a', - OSImpl.macOS: 'a', - OSImpl.windows: 'lib', - }; - - /// The default extension for executables per [OSImpl]. - static const _executableExtension = { - OSImpl.android: '', - OSImpl.fuchsia: '', - OSImpl.iOS: '', - OSImpl.linux: '', - OSImpl.macOS: '', - OSImpl.windows: 'exe', - }; - - static const String configKey = 'target_os'; - - @override - String toString() => dartPlatform; - - /// Mapping from strings as used in [OSImpl.toString] to - /// [OSImpl]s. - static final Map _stringToOS = - Map.fromEntries(OSImpl.values.map((os) => MapEntry(os.toString(), os))); - - factory OSImpl.fromString(String target) => _stringToOS[target]!; - - /// The current [OSImpl]. - /// - /// Read from the [Platform.version] string. - static final OSImpl current = Target.current.os; -} diff --git a/pkgs/native_assets_cli/lib/src/model/target.dart b/pkgs/native_assets_cli/lib/src/model/target.dart index 18cdc9775..d80673537 100644 --- a/pkgs/native_assets_cli/lib/src/model/target.dart +++ b/pkgs/native_assets_cli/lib/src/model/target.dart @@ -5,8 +5,8 @@ import 'dart:ffi' show Abi; import 'dart:io'; -import '../api/architecture.dart'; -import '../api/os.dart'; +import '../architecture.dart'; +import '../os.dart'; final class Target implements Comparable { final Abi abi; @@ -36,8 +36,8 @@ final class Target implements Comparable { } factory Target.fromArchitectureAndOS( - ArchitectureImpl architecture, - OSImpl os, + Architecture architecture, + OS os, ) { for (final value in values) { if (value.os == os && value.architecture == architecture) { @@ -108,19 +108,37 @@ final class Target implements Comparable { /// Read from the [Platform.version] string. static final Target current = Target.fromDartPlatform(Platform.version); - ArchitectureImpl get architecture => ArchitectureImpl.fromAbi(abi); - - OSImpl get os => OSImpl.fromAbi(abi); - - String get _architectureString => architecture.dartPlatform; - - String get _osString => os.dartPlatform; + Architecture get architecture => Architecture.fromAbi(abi); + + OS get os => { + Abi.androidArm: OS.android, + Abi.androidArm64: OS.android, + Abi.androidIA32: OS.android, + Abi.androidX64: OS.android, + Abi.androidRiscv64: OS.android, + Abi.fuchsiaArm64: OS.fuchsia, + Abi.fuchsiaX64: OS.fuchsia, + Abi.iosArm: OS.iOS, + Abi.iosArm64: OS.iOS, + Abi.iosX64: OS.iOS, + Abi.linuxArm: OS.linux, + Abi.linuxArm64: OS.linux, + Abi.linuxIA32: OS.linux, + Abi.linuxRiscv32: OS.linux, + Abi.linuxRiscv64: OS.linux, + Abi.linuxX64: OS.linux, + Abi.macosArm64: OS.macOS, + Abi.macosX64: OS.macOS, + Abi.windowsArm64: OS.windows, + Abi.windowsIA32: OS.windows, + Abi.windowsX64: OS.windows, + }[abi]!; @override String toString() => dartVMToString(); /// As used in [Platform.version]. - String dartVMToString() => '${_osString}_$_architectureString'; + String dartVMToString() => '${os.name}_${architecture.name}'; /// Compares `this` to [other]. /// @@ -130,8 +148,8 @@ final class Target implements Comparable { /// A list of supported target [Target]s from this host [os]. List supportedTargetTargets( - {Map> osCrossCompilation = - OSImpl.osCrossCompilationDefault}) => + {Map> osCrossCompilation = + OS.osCrossCompilationDefault}) => Target.values .where((target) => // Only valid cross compilation. diff --git a/pkgs/native_assets_cli/lib/src/os.dart b/pkgs/native_assets_cli/lib/src/os.dart new file mode 100644 index 000000000..63fa5d2cb --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/os.dart @@ -0,0 +1,78 @@ +// Copyright (c) 2024, 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. + +import 'dart:io'; + +/// An operating system the Dart VM runs on. +final class OS { + /// The name of this operating system. + final String name; + + const OS._(this.name); + + /// The + /// [Android](https://en.wikipedia.org/wiki/Android_%28operating_system%29) + /// operating system. + static const OS android = OS._('android'); + + /// The [Fuchsia](https://en.wikipedia.org/wiki/Google_Fuchsia) operating + /// system. + static const OS fuchsia = OS._('fuchsia'); + + /// The [iOS](https://en.wikipedia.org/wiki/IOS) operating system. + static const OS iOS = OS._('ios'); + + /// The [Linux](https://en.wikipedia.org/wiki/Linux) operating system. + static const OS linux = OS._('linux'); + + /// The [macOS](https://en.wikipedia.org/wiki/MacOS) operating system. + static const OS macOS = OS._('macos'); + + /// The + /// [Microsoft Windows](https://en.wikipedia.org/wiki/Microsoft_Windows) + /// operating system. + static const OS windows = OS._('windows'); + + /// Known values for [OS]. + static const List values = [ + android, + fuchsia, + iOS, + linux, + macOS, + windows, + ]; + + /// Typical cross compilation between OSes. + static const osCrossCompilationDefault = { + OS.macOS: [OS.macOS, OS.iOS, OS.android], + OS.linux: [OS.linux, OS.android], + OS.windows: [OS.windows, OS.android], + }; + + static const String configKey = 'target_os'; + + /// The name of this [OS]. + /// + /// This returns a stable string that can be used to construct a + /// [OS] via [OS.fromString]. + @override + String toString() => name; + + /// Mapping from strings as used in [OS.toString] to + /// [OS]s. + static final Map _stringToOS = { + for (var os in OS.values) os.toString(): os + }; + + /// Creates a [OS] from the given [name]. + /// + /// The name can be obtained from [OS.name] or [OS.toString]. + factory OS.fromString(String name) => _stringToOS[name]!; + + /// The current [OS]. + /// + /// Consisten with the [Platform.version] string. + static final OS current = OS.fromString(Platform.operatingSystem); +} diff --git a/pkgs/native_assets_cli/lib/src/validator/validator.dart b/pkgs/native_assets_cli/lib/src/validator/validator.dart index 199d67468..d8cc1b8bb 100644 --- a/pkgs/native_assets_cli/lib/src/validator/validator.dart +++ b/pkgs/native_assets_cli/lib/src/validator/validator.dart @@ -9,7 +9,8 @@ import '../api/build_config.dart'; import '../api/build_output.dart'; import '../api/hook_config.dart'; import '../api/link_config.dart'; -import '../api/link_mode_preference.dart'; +import '../link_mode.dart'; +import '../link_mode_preference.dart'; typedef ValidateResult = ({ bool success, diff --git a/pkgs/native_assets_cli/test/example/local_asset_test.dart b/pkgs/native_assets_cli/test/example/local_asset_test.dart index 000edb57f..b556a402d 100644 --- a/pkgs/native_assets_cli/test/example/local_asset_test.dart +++ b/pkgs/native_assets_cli/test/example/local_asset_test.dart @@ -45,13 +45,13 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: name, packageRoot: testPackageUri, - targetOS: OSImpl.current, + targetOS: OS.current, version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreferenceImpl.dynamic, + linkModePreference: LinkModePreference.dynamic, dryRun: dryRun, linkingEnabled: false, - targetArchitecture: dryRun ? null : ArchitectureImpl.current, - buildMode: dryRun ? null : BuildModeImpl.debug, + targetArchitecture: dryRun ? null : Architecture.current, + buildMode: dryRun ? null : BuildMode.debug, cCompiler: dryRun ? null : cCompiler, ); diff --git a/pkgs/native_assets_cli/test/example/native_add_library_test.dart b/pkgs/native_assets_cli/test/example/native_add_library_test.dart index ccec7203b..8f3fb3cb6 100644 --- a/pkgs/native_assets_cli/test/example/native_add_library_test.dart +++ b/pkgs/native_assets_cli/test/example/native_add_library_test.dart @@ -45,13 +45,13 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: name, packageRoot: testPackageUri, - targetOS: OSImpl.current, + targetOS: OS.current, version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreferenceImpl.dynamic, + linkModePreference: LinkModePreference.dynamic, dryRun: dryRun, linkingEnabled: false, - targetArchitecture: dryRun ? null : ArchitectureImpl.current, - buildMode: dryRun ? null : BuildModeImpl.debug, + targetArchitecture: dryRun ? null : Architecture.current, + buildMode: dryRun ? null : BuildMode.debug, cCompiler: dryRun ? null : cCompiler, ); diff --git a/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart b/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart index 193dd159b..00b1f706b 100644 --- a/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart +++ b/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart @@ -54,13 +54,13 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: name, packageRoot: testPackageUri, - targetOS: OSImpl.current, + targetOS: OS.current, version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreferenceImpl.dynamic, + linkModePreference: LinkModePreference.dynamic, dryRun: dryRun, linkingEnabled: false, - targetArchitecture: dryRun ? null : ArchitectureImpl.current, - buildMode: dryRun ? null : BuildModeImpl.debug, + targetArchitecture: dryRun ? null : Architecture.current, + buildMode: dryRun ? null : BuildMode.debug, cCompiler: dryRun ? null : cCompiler, ); diff --git a/pkgs/native_assets_cli/test/helpers.dart b/pkgs/native_assets_cli/test/helpers.dart index d3771ed9e..36a45c744 100644 --- a/pkgs/native_assets_cli/test/helpers.dart +++ b/pkgs/native_assets_cli/test/helpers.dart @@ -80,48 +80,42 @@ extension on Uri { String get name => pathSegments.where((e) => e != '').last; } -String unparseKey(String key) => - 'DART_HOOK_TESTING_${key.replaceAll('.', '__').toUpperCase()}'; - /// Archiver provided by the environment. /// /// Provided on Dart CI. -final Uri? _ar = Platform - .environment[unparseKey(internal.CCompilerConfigImpl.arConfigKeyFull)] - ?.asFileUri(); +final Uri? _ar = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__AR']?.asFileUri(); /// Compiler provided by the environment. /// /// Provided on Dart CI. -final Uri? _cc = Platform - .environment[unparseKey(internal.CCompilerConfigImpl.ccConfigKeyFull)] - ?.asFileUri(); +final Uri? _cc = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__CC']?.asFileUri(); /// Linker provided by the environment. /// /// Provided on Dart CI. -final Uri? _ld = Platform - .environment[unparseKey(internal.CCompilerConfigImpl.ldConfigKeyFull)] - ?.asFileUri(); +final Uri? _ld = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__LD']?.asFileUri(); /// Path to script that sets environment variables for [_cc], [_ld], and [_ar]. /// /// Provided on Dart CI. -final Uri? _envScript = Platform.environment[ - unparseKey(internal.CCompilerConfigImpl.envScriptConfigKeyFull)] +final Uri? _envScript = Platform + .environment['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT'] ?.asFileUri(); /// Arguments for [_envScript] provided by environment. /// /// Provided on Dart CI. -final List? _envScriptArgs = Platform.environment[ - unparseKey(internal.CCompilerConfigImpl.envScriptArgsConfigKeyFull)] +final List? _envScriptArgs = Platform + .environment['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT_ARGUMENTS'] ?.split(' '); /// Configuration for the native toolchain. /// /// Provided on Dart CI. -final cCompiler = internal.CCompilerConfigImpl( +final cCompiler = internal.CCompilerConfig( compiler: _cc, archiver: _ar, linker: _ld, diff --git a/pkgs/native_assets_cli/test/model/asset_test.dart b/pkgs/native_assets_cli/test/model/asset_test.dart index 61b33baf6..774c7471f 100644 --- a/pkgs/native_assets_cli/test/model/asset_test.dart +++ b/pkgs/native_assets_cli/test/model/asset_test.dart @@ -17,41 +17,41 @@ void main() { NativeCodeAssetImpl( id: 'package:my_package/foo', file: fooUri, - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingBundled(), + os: OS.android, + architecture: Architecture.x64, ), NativeCodeAssetImpl( id: 'package:my_package/foo3', - linkMode: DynamicLoadingSystemImpl(foo3Uri), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingSystem(foo3Uri), + os: OS.android, + architecture: Architecture.x64, ), NativeCodeAssetImpl( id: 'package:my_package/foo4', - linkMode: LookupInExecutableImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: LookupInExecutable(), + os: OS.android, + architecture: Architecture.x64, ), NativeCodeAssetImpl( id: 'package:my_package/foo5', - linkMode: LookupInProcessImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: LookupInProcess(), + os: OS.android, + architecture: Architecture.x64, ), NativeCodeAssetImpl( id: 'package:my_package/bar', file: barUri, - os: OSImpl.linux, - architecture: ArchitectureImpl.arm64, - linkMode: StaticLinkingImpl(), + os: OS.linux, + architecture: Architecture.arm64, + linkMode: StaticLinking(), ), NativeCodeAssetImpl( id: 'package:my_package/bla', file: blaUri, - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.windows, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingBundled(), + os: OS.windows, + architecture: Architecture.x64, ), ]; final dataAssets = [ @@ -145,9 +145,11 @@ void main() { test('AssetPath factory', () async { expect( - () => LinkModeImpl('wrong', null), + () => LinkMode.fromJson({'type': 'wrong'}), throwsA(predicate( - (e) => e is FormatException && e.message.contains('Unknown type'), + (e) => + e is FormatException && + e.message.contains('The link mode "wrong" is not known'), )), ); }); diff --git a/pkgs/native_assets_cli/test/model/build_config_test.dart b/pkgs/native_assets_cli/test/model/build_config_test.dart index 9d5e90751..c41d6cce6 100644 --- a/pkgs/native_assets_cli/test/model/build_config_test.dart +++ b/pkgs/native_assets_cli/test/model/build_config_test.dart @@ -5,7 +5,6 @@ import 'dart:io'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import 'package:native_assets_cli/src/api/asset.dart'; import 'package:test/test.dart'; void main() async { @@ -56,16 +55,16 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.iOS, - targetIOSSdk: IOSSdkImpl.iPhoneOS, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( compiler: fakeClang, linker: fakeLd, archiver: fakeAr, ), - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, ); @@ -74,11 +73,11 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.android, + targetArchitecture: Architecture.arm64, + targetOS: OS.android, targetAndroidNdkApi: 30, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, ); @@ -88,7 +87,7 @@ void main() async { expect(config1.packageRoot, config2.packageRoot); expect(config1.targetArchitecture == config2.targetArchitecture, true); expect(config1.targetOS != config2.targetOS, true); - expect(config1.targetIOSSdk, IOSSdkImpl.iPhoneOS); + expect(config1.targetIOSSdk, IOSSdk.iPhoneOS); expect(() => config2.targetIOSSdk, throwsStateError); expect(config1.cCompiler.compiler != config2.cCompiler.compiler, true); expect(config1.cCompiler.linker != config2.cCompiler.linker, true); @@ -107,11 +106,11 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRootUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.android, + targetArchitecture: Architecture.arm64, + targetOS: OS.android, targetAndroidNdkApi: 30, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, ); @@ -140,8 +139,8 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRootUri, - targetOS: OSImpl.android, - linkModePreference: LinkModePreferenceImpl.preferStatic, + targetOS: OS.android, + linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, ); @@ -167,15 +166,15 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRootUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.iOS, - targetIOSSdk: IOSSdkImpl.iPhoneOS, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( compiler: fakeClang, linker: fakeLd, ), - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, ); @@ -190,11 +189,11 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.android, + targetArchitecture: Architecture.arm64, + targetOS: OS.android, targetAndroidNdkApi: 30, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, dependencyMetadata: { 'bar': const Metadata({ 'key': 'value', @@ -212,11 +211,11 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.android, + targetArchitecture: Architecture.arm64, + targetOS: OS.android, targetAndroidNdkApi: 30, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, dependencyMetadata: { 'bar': const Metadata({ 'key': 'value', @@ -240,15 +239,15 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.iOS, - targetIOSSdk: IOSSdkImpl.iPhoneOS, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( compiler: fakeClang, linker: fakeLd, ), - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, // This map should be sorted on key for two layers. dependencyMetadata: { 'foo': const Metadata({ @@ -334,7 +333,7 @@ void main() async { 'target_os': 'android', 'target_android_ndk_api': 30, 'link_mode_preference': 'prefer-static', - 'build_mode': BuildModeImpl.release.name, + 'build_mode': BuildMode.release.name, 'dependency_metadata': { 'bar': {'key': 'value'}, 'foo': [], @@ -359,7 +358,7 @@ void main() async { 'target_architecture': 'arm64', 'target_os': 'android', 'link_mode_preference': 'prefer-static', - 'build_mode': BuildModeImpl.release.name, + 'build_mode': BuildMode.release.name, }), throwsA(predicate( (e) => @@ -377,15 +376,15 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.iOS, - targetIOSSdk: IOSSdkImpl.iPhoneOS, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( compiler: fakeClang, linker: fakeLd, ), - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, ); config.toString(); @@ -397,11 +396,11 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.android, + targetArchitecture: Architecture.arm64, + targetOS: OS.android, targetAndroidNdkApi: 30, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.preferStatic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, ); final configFileContents = buildConfig.toJsonString(); @@ -421,15 +420,15 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRootUri, - targetArchitecture: ArchitectureImpl.x64, - targetOS: OSImpl.windows, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.x64, + targetOS: OS.windows, + cCompiler: CCompilerConfig( compiler: fakeCl, envScript: fakeVcVars, envScriptArgs: ['x64'], ), - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, linkingEnabled: false, ); @@ -552,8 +551,8 @@ void main() async { outputDirectoryShared: outputDirectoryShared, outputDirectory: outDirUri, packageRoot: tempUri, - targetOS: OSImpl.windows, - linkModePreference: LinkModePreferenceImpl.dynamic, + targetOS: OS.windows, + linkModePreference: LinkModePreference.dynamic, linkingEnabled: false, ); buildConfig.toJsonString(); diff --git a/pkgs/native_assets_cli/test/model/build_output_test.dart b/pkgs/native_assets_cli/test/model/build_output_test.dart index 80449dede..b05c99238 100644 --- a/pkgs/native_assets_cli/test/model/build_output_test.dart +++ b/pkgs/native_assets_cli/test/model/build_output_test.dart @@ -115,10 +115,10 @@ void main() { outputDirectoryShared: outDirShared, packageName: 'dontcare', packageRoot: packageRoot, - buildMode: BuildModeImpl.debug, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.macOS, - linkModePreference: LinkModePreferenceImpl.dynamic, + buildMode: BuildMode.debug, + targetArchitecture: Architecture.arm64, + targetOS: OS.macOS, + linkModePreference: LinkModePreference.dynamic, linkingEnabled: false, ); final buildOutput = getBuildOutput(); @@ -139,10 +139,10 @@ void main() { outputDirectoryShared: outDirShared, packageName: 'dontcare', packageRoot: packageRoot, - buildMode: BuildModeImpl.debug, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.macOS, - linkModePreference: LinkModePreferenceImpl.dynamic, + buildMode: BuildMode.debug, + targetArchitecture: Architecture.arm64, + targetOS: OS.macOS, + linkModePreference: LinkModePreference.dynamic, version: Version(1, 1, 0), linkingEnabled: null, // version < 1.4.0 ); @@ -188,15 +188,15 @@ void main() { NativeCodeAssetImpl( id: 'package:my_package/foo', file: Uri(path: 'path/to/libfoo.so'), - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingBundled(), + os: OS.android, + architecture: Architecture.x64, ), NativeCodeAssetImpl( id: 'package:my_package/foo2', - linkMode: DynamicLoadingSystemImpl(Uri(path: 'path/to/libfoo2.so')), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingSystem(Uri(path: 'path/to/libfoo2.so')), + os: OS.android, + architecture: Architecture.x64, ), ], dependencies: Dependencies([ @@ -216,17 +216,17 @@ void main() { NativeCodeAssetImpl( id: 'package:my_package/foo', file: Uri(path: 'path/to/libfoo.so'), - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingBundled(), + os: OS.android, + architecture: Architecture.x64, ), ); buildOutput2.addAssets([ NativeCodeAssetImpl( id: 'package:my_package/foo2', - linkMode: DynamicLoadingSystemImpl(Uri(path: 'path/to/libfoo2.so')), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingSystem(Uri(path: 'path/to/libfoo2.so')), + os: OS.android, + architecture: Architecture.x64, ), ]); buildOutput2.addDependency( @@ -253,27 +253,27 @@ HookOutputImpl getBuildOutput({bool withLinkedAssets = true}) => HookOutputImpl( NativeCodeAssetImpl( id: 'package:my_package/foo', file: Uri(path: 'path/to/libfoo.so'), - linkMode: DynamicLoadingBundledImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingBundled(), + os: OS.android, + architecture: Architecture.x64, ), NativeCodeAssetImpl( id: 'package:my_package/foo2', - linkMode: DynamicLoadingSystemImpl(Uri(path: 'path/to/libfoo2.so')), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: DynamicLoadingSystem(Uri(path: 'path/to/libfoo2.so')), + os: OS.android, + architecture: Architecture.x64, ), NativeCodeAssetImpl( id: 'package:my_package/foo3', - linkMode: LookupInProcessImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: LookupInProcess(), + os: OS.android, + architecture: Architecture.x64, ), NativeCodeAssetImpl( id: 'package:my_package/foo4', - linkMode: LookupInExecutableImpl(), - os: OSImpl.android, - architecture: ArchitectureImpl.x64, + linkMode: LookupInExecutable(), + os: OS.android, + architecture: Architecture.x64, ), ], assetsForLinking: withLinkedAssets diff --git a/pkgs/native_assets_cli/test/model/checksum_test.dart b/pkgs/native_assets_cli/test/model/checksum_test.dart index 35e3c7b58..140689cf4 100644 --- a/pkgs/native_assets_cli/test/model/checksum_test.dart +++ b/pkgs/native_assets_cli/test/model/checksum_test.dart @@ -5,7 +5,6 @@ import 'dart:io'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import 'package:native_assets_cli/src/api/asset.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -21,10 +20,10 @@ void main() { final name1 = HookConfigImpl.checksum( packageName: packageName, packageRoot: nativeAddUri, - targetArchitecture: ArchitectureImpl.x64, - targetOS: OSImpl.linux, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, + targetArchitecture: Architecture.x64, + targetOS: OS.linux, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, supportedAssetTypes: [NativeCodeAsset.type], hook: Hook.build, version: HookConfigImpl.latestVersion, @@ -38,10 +37,10 @@ void main() { final name2 = HookConfigImpl.checksum( packageName: packageName, packageRoot: nativeAddUri, - targetArchitecture: ArchitectureImpl.x64, - targetOS: OSImpl.linux, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, + targetArchitecture: Architecture.x64, + targetOS: OS.linux, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, dependencyMetadata: { 'foo': const Metadata({'key': 'value'}) }, @@ -56,11 +55,11 @@ void main() { final name3 = HookConfigImpl.checksum( packageName: packageName, packageRoot: nativeAddUri, - targetArchitecture: ArchitectureImpl.x64, - targetOS: OSImpl.linux, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.x64, + targetOS: OS.linux, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, + cCompiler: CCompilerConfig( compiler: fakeClangUri, ), hook: Hook.build, @@ -74,11 +73,11 @@ void main() { final name4 = HookConfigImpl.checksum( packageName: packageName, packageRoot: nativeAddUri, - targetArchitecture: ArchitectureImpl.x64, - targetOS: OSImpl.linux, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.x64, + targetOS: OS.linux, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, + cCompiler: CCompilerConfig( compiler: fakeClangUri, ), hook: Hook.link, @@ -92,10 +91,10 @@ void main() { final name5 = HookConfigImpl.checksum( packageName: packageName, packageRoot: nativeAddUri, - targetArchitecture: ArchitectureImpl.x64, - targetOS: OSImpl.linux, - buildMode: BuildModeImpl.release, - linkModePreference: LinkModePreferenceImpl.dynamic, + targetArchitecture: Architecture.x64, + targetOS: OS.linux, + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.dynamic, supportedAssetTypes: [NativeCodeAsset.type], hook: Hook.build, version: HookConfigImpl.latestVersion, diff --git a/pkgs/native_assets_cli/test/model/link_config_test.dart b/pkgs/native_assets_cli/test/model/link_config_test.dart index ab09936c2..8339f2ffe 100644 --- a/pkgs/native_assets_cli/test/model/link_config_test.dart +++ b/pkgs/native_assets_cli/test/model/link_config_test.dart @@ -4,9 +4,7 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import 'package:native_assets_cli/src/api/asset.dart'; import 'package:test/test.dart'; void main() async { @@ -75,18 +73,18 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.iOS, - targetIOSSdk: IOSSdkImpl.iPhoneOS, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( compiler: fakeClang, linker: fakeLd, archiver: fakeAr, ), - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, assets: assets, recordedUsagesFile: recordedUsagesFile, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); final config2 = LinkConfigImpl( @@ -94,13 +92,13 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.android, + targetArchitecture: Architecture.arm64, + targetOS: OS.android, targetAndroidNdkApi: 30, - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, assets: [], recordedUsagesFile: null, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); expect(config1, equals(config1)); @@ -109,7 +107,7 @@ void main() async { expect(config1.packageRoot, config2.packageRoot); expect(config1.targetArchitecture == config2.targetArchitecture, true); expect(config1.targetOS != config2.targetOS, true); - expect(config1.targetIOSSdk, IOSSdkImpl.iPhoneOS); + expect(config1.targetIOSSdk, IOSSdk.iPhoneOS); expect(() => config2.targetIOSSdk, throwsStateError); expect(config1.cCompiler.compiler != config2.cCompiler.compiler, true); expect(config1.cCompiler.linker != config2.cCompiler.linker, true); @@ -127,12 +125,12 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRootUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.android, + targetArchitecture: Architecture.arm64, + targetOS: OS.android, targetAndroidNdkApi: 30, - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, assets: assets, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); final config = { @@ -160,9 +158,9 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRootUri, - targetOS: OSImpl.android, + targetOS: OS.android, assets: [], - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); final config = { @@ -187,16 +185,16 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRootUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.iOS, - targetIOSSdk: IOSSdkImpl.iPhoneOS, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( compiler: fakeClang, linker: fakeLd, ), - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, assets: assets, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); final configFile = buildConfig1.toJson(); @@ -211,16 +209,16 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.iOS, - targetIOSSdk: IOSSdkImpl.iPhoneOS, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( compiler: fakeClang, linker: fakeLd, ), - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, assets: assets, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); final jsonObject = buildConfig1.toJson(); @@ -283,9 +281,9 @@ void main() async { 'target_architecture': 'arm64', 'target_os': 'android', 'target_android_ndk_api': 30, - 'build_mode': BuildModeImpl.release.name, + 'build_mode': BuildMode.release.name, 'assets': 'astring', - 'link_mode_preference': LinkModePreferenceImpl.preferStatic.name, + 'link_mode_preference': LinkModePreference.preferStatic.name, }), throwsA(predicate( (e) => @@ -305,8 +303,8 @@ void main() async { 'package_root': packageRootUri.toFilePath(), 'target_architecture': 'arm64', 'target_os': 'android', - 'build_mode': BuildModeImpl.release.name, - 'link_mode_preference': LinkModePreferenceImpl.preferStatic.name, + 'build_mode': BuildMode.release.name, + 'link_mode_preference': LinkModePreference.preferStatic.name, }), throwsA(predicate( (e) => @@ -324,16 +322,16 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.iOS, - targetIOSSdk: IOSSdkImpl.iPhoneOS, - cCompiler: CCompilerConfigImpl( + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( compiler: fakeClang, linker: fakeLd, ), - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, assets: assets, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); expect(config.toString(), isNotEmpty); }); @@ -344,13 +342,13 @@ void main() async { outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: tempUri, - targetArchitecture: ArchitectureImpl.arm64, - targetOS: OSImpl.android, + targetArchitecture: Architecture.arm64, + targetOS: OS.android, targetAndroidNdkApi: 30, - buildMode: BuildModeImpl.release, + buildMode: BuildMode.release, assets: assets, recordedUsagesFile: recordedUsagesFile, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); final configFileContents = buildConfig.toJsonString(); final configUri = tempUri.resolve('config.json'); @@ -473,9 +471,9 @@ void main() async { outputDirectory: outDirUri, outputDirectoryShared: outputDirectoryShared, packageRoot: tempUri, - targetOS: OSImpl.windows, + targetOS: OS.windows, assets: assets, - linkModePreference: LinkModePreferenceImpl.preferStatic, + linkModePreference: LinkModePreference.preferStatic, ); expect(buildConfig.toJsonString(), isNotEmpty); }); diff --git a/pkgs/native_assets_cli/test/model/target_test.dart b/pkgs/native_assets_cli/test/model/target_test.dart index ac6a34b76..f63f16856 100644 --- a/pkgs/native_assets_cli/test/model/target_test.dart +++ b/pkgs/native_assets_cli/test/model/target_test.dart @@ -10,15 +10,14 @@ import 'package:test/test.dart'; void main() { test('OS naming conventions', () async { - expect(OSImpl.android.dylibFileName('foo'), 'libfoo.so'); - expect(OSImpl.android.staticlibFileName('foo'), 'libfoo.a'); - expect(OSImpl.windows.dylibFileName('foo'), 'foo.dll'); - expect(OSImpl.windows.libraryFileName('foo', DynamicLoadingBundledImpl()), - 'foo.dll'); - expect(OSImpl.windows.staticlibFileName('foo'), 'foo.lib'); + expect(OS.android.dylibFileName('foo'), 'libfoo.so'); + expect(OS.android.staticlibFileName('foo'), 'libfoo.a'); + expect(OS.windows.dylibFileName('foo'), 'foo.dll'); expect( - OSImpl.windows.libraryFileName('foo', StaticLinkingImpl()), 'foo.lib'); - expect(OSImpl.windows.executableFileName('foo'), 'foo.exe'); + OS.windows.libraryFileName('foo', DynamicLoadingBundled()), 'foo.dll'); + expect(OS.windows.staticlibFileName('foo'), 'foo.lib'); + expect(OS.windows.libraryFileName('foo', StaticLinking()), 'foo.lib'); + expect(OS.windows.executableFileName('foo'), 'foo.exe'); }); test('Target current', () async { @@ -61,11 +60,11 @@ void main() { test('Target fromArchitectureAndOS', () async { final current = - Target.fromArchitectureAndOS(ArchitectureImpl.current, OSImpl.current); + Target.fromArchitectureAndOS(Architecture.current, OS.current); expect(current.toString(), Abi.current().toString()); expect( - () => Target.fromArchitectureAndOS(ArchitectureImpl.arm, OSImpl.windows), + () => Target.fromArchitectureAndOS(Architecture.arm, OS.windows), throwsA(predicate( (e) => e is ArgumentError && @@ -74,14 +73,4 @@ void main() { )), ); }); - - test('OS.architectures', () { - expect(OSImpl.android.architectures, [ - ArchitectureImpl.arm, - ArchitectureImpl.arm64, - ArchitectureImpl.ia32, - ArchitectureImpl.x64, - ArchitectureImpl.riscv64, - ]); - }); } diff --git a/pkgs/native_toolchain_c/CHANGELOG.md b/pkgs/native_toolchain_c/CHANGELOG.md index 7543f7510..3715b49c8 100644 --- a/pkgs/native_toolchain_c/CHANGELOG.md +++ b/pkgs/native_toolchain_c/CHANGELOG.md @@ -1,6 +1,7 @@ ## 0.5.5-wip - Address analyzer info diagnostic about multi-line if requiring a block body. +- Bump `package:native_assets_cli` to `0.9.0`. ## 0.5.4 diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart index c128846dd..e38bdbf7d 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart @@ -16,7 +16,8 @@ abstract class CTool { /// Name of the library or executable to linkg. /// /// The filename will be decided by [LinkConfig.targetOS] and - /// [OS.libraryFileName] or [OS.executableFileName]. + /// [OSLibraryNaming.libraryFileName] or + /// [OSLibraryNaming.executableFileName]. /// /// File will be placed in [LinkConfig.outputDirectory]. final String name; 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 021238663..b00696455 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 @@ -17,8 +17,7 @@ void main() { Architecture.arm64, Architecture.ia32, Architecture.x64, - // TODO(rmacnak): Enable when stable NDK 27 is available. - // Architecture.riscv64, + Architecture.riscv64, ]; const objdumpFileFormat = { diff --git a/pkgs/native_toolchain_c/test/helpers.dart b/pkgs/native_toolchain_c/test/helpers.dart index 0756ba198..9c4981464 100644 --- a/pkgs/native_toolchain_c/test/helpers.dart +++ b/pkgs/native_toolchain_c/test/helpers.dart @@ -118,35 +118,36 @@ extension on Uri { String get name => pathSegments.where((e) => e != '').last; } -String unparseKey(String key) => - 'DART_HOOK_TESTING_${key.replaceAll('.', '__').toUpperCase()}'; - /// Archiver provided by the environment. /// /// Provided on Dart CI. -final Uri? _ar = Platform.environment[unparseKey('c_compiler.ar')]?.asFileUri(); +final Uri? _ar = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__AR']?.asFileUri(); /// Compiler provided by the environment. /// /// Provided on Dart CI. -final Uri? _cc = Platform.environment[unparseKey('c_compiler.cc')]?.asFileUri(); +final Uri? _cc = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__CC']?.asFileUri(); /// Linker provided by the environment. /// /// Provided on Dart CI. -final Uri? _ld = Platform.environment[unparseKey('c_compiler.ld')]?.asFileUri(); +final Uri? _ld = + Platform.environment['DART_HOOK_TESTING_C_COMPILER__LD']?.asFileUri(); /// Path to script that sets environment variables for [_cc], [_ld], and [_ar]. /// /// Provided on Dart CI. -final Uri? _envScript = - Platform.environment[unparseKey('c_compiler.env_script')]?.asFileUri(); +final Uri? _envScript = Platform + .environment['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT'] + ?.asFileUri(); /// Arguments for [_envScript] provided by environment. /// /// Provided on Dart CI. final List? _envScriptArgs = Platform - .environment[unparseKey('c_compiler.env_script_arguments')] + .environment['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT_ARGUMENTS'] ?.split(' '); /// Configuration for the native toolchain. diff --git a/pkgs/objective_c/CHANGELOG.md b/pkgs/objective_c/CHANGELOG.md index 12e7e44ad..41c265239 100644 --- a/pkgs/objective_c/CHANGELOG.md +++ b/pkgs/objective_c/CHANGELOG.md @@ -3,6 +3,7 @@ - Add `UnimplementedOptionalMethodException`, which is thrown by the ObjC bindings if an optional method is invoked, and the instance doesn't implement the method. +- Dispatch all object/block releases to the main thread. ## 2.0.0 diff --git a/pkgs/objective_c/ios/Classes/objective_c.m b/pkgs/objective_c/ios/Classes/objective_c.m index 545adba1f..25d76dca3 100644 --- a/pkgs/objective_c/ios/Classes/objective_c.m +++ b/pkgs/objective_c/ios/Classes/objective_c.m @@ -4,5 +4,6 @@ // Relative import to be able to reuse the ObjC sources. // See the comment in ../objective_c.podspec for more information. +#include "../../src/objective_c.m" #include "../../src/objective_c_bindings_generated.m" #include "../../src/proxy.m" diff --git a/pkgs/objective_c/lib/src/objective_c_bindings_generated.dart.m b/pkgs/objective_c/lib/src/objective_c_bindings_generated.dart.m new file mode 100644 index 000000000..28a98f63b --- /dev/null +++ b/pkgs/objective_c/lib/src/objective_c_bindings_generated.dart.m @@ -0,0 +1,26 @@ +#include +#import "../../src/foundation.h" +#import "../../src/proxy.h" + +#if !__has_feature(objc_arc) +#error "This file must be compiled with ARC enabled" +#endif + +id objc_retain(id); +id objc_retainBlock(id); + +typedef void (^_ListenerTrampoline)(void * arg0, id arg1); +_ListenerTrampoline _wrapListenerBlock_3f15836a(_ListenerTrampoline block) NS_RETURNS_RETAINED { + return ^void(void * arg0, id arg1) { + objc_retainBlock(block); + block(arg0, objc_retain(arg1)); + }; +} + +typedef void (^_ListenerTrampoline1)(void * arg0); +_ListenerTrampoline1 _wrapListenerBlock_162449db(_ListenerTrampoline1 block) NS_RETURNS_RETAINED { + return ^void(void * arg0) { + objc_retainBlock(block); + block(arg0); + }; +} diff --git a/pkgs/objective_c/macos/Classes/objective_c.m b/pkgs/objective_c/macos/Classes/objective_c.m index 545adba1f..25d76dca3 100644 --- a/pkgs/objective_c/macos/Classes/objective_c.m +++ b/pkgs/objective_c/macos/Classes/objective_c.m @@ -4,5 +4,6 @@ // Relative import to be able to reuse the ObjC sources. // See the comment in ../objective_c.podspec for more information. +#include "../../src/objective_c.m" #include "../../src/objective_c_bindings_generated.m" #include "../../src/proxy.m" diff --git a/pkgs/objective_c/pubspec.yaml b/pkgs/objective_c/pubspec.yaml index c64c11f43..56bdc8101 100644 --- a/pkgs/objective_c/pubspec.yaml +++ b/pkgs/objective_c/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: yaml: ^3.1.0 dev_dependencies: + args: ^2.0.0 coverage: ^1.8.0 dart_flutter_team_lints: ^2.0.0 ffigen: ^14.0.0 diff --git a/pkgs/objective_c/src/CMakeLists.txt b/pkgs/objective_c/src/CMakeLists.txt index 5988f69f1..58d0c454e 100644 --- a/pkgs/objective_c/src/CMakeLists.txt +++ b/pkgs/objective_c/src/CMakeLists.txt @@ -7,6 +7,8 @@ project(objective_c_library VERSION 0.0.1 LANGUAGES C) add_library(objective_c SHARED "objective_c.c" + "objective_c.m" + "proxy.m" "include/dart_api_dl.c" ) diff --git a/pkgs/objective_c/src/objective_c.c b/pkgs/objective_c/src/objective_c.c index 8457548ef..f46e823bf 100644 --- a/pkgs/objective_c/src/objective_c.c +++ b/pkgs/objective_c/src/objective_c.c @@ -25,11 +25,11 @@ bool isValidBlock(ObjCBlockImpl* block) { void finalizeObject(void* isolate_callback_data, void* peer) { // objc_release works for Objects and Blocks. - objc_release(peer); + runOnMainThread((void (*)(void*))objc_release, peer); } Dart_FinalizableHandle newFinalizableHandle(Dart_Handle owner, - ObjCObject* object) { + ObjCObject* object) { return Dart_NewFinalizableHandle_DL(owner, object, 0, finalizeObject); } @@ -37,9 +37,7 @@ void deleteFinalizableHandle(Dart_FinalizableHandle handle, Dart_Handle owner) { Dart_DeleteFinalizableHandle_DL(handle, owner); } -void finalizeMalloc(void* isolate_callback_data, void* peer) { - free(peer); -} +void finalizeMalloc(void* isolate_callback_data, void* peer) { free(peer); } bool* newFinalizableBool(Dart_Handle owner) { bool* pointer = (bool*)malloc(1); diff --git a/pkgs/objective_c/src/objective_c.h b/pkgs/objective_c/src/objective_c.h index 17e39c654..8f66066b0 100644 --- a/pkgs/objective_c/src/objective_c.h +++ b/pkgs/objective_c/src/objective_c.h @@ -28,4 +28,13 @@ void deleteFinalizableHandle(Dart_FinalizableHandle handle, Dart_Handle owner); // by a Dart_FinalizableHandle when the owner is garbage collected. bool* newFinalizableBool(Dart_Handle owner); +// Runs fn(arg) on the main thread. If runOnMainThread is already running on the +// main thread, fn(arg) is invoked synchronously. Otherwise it is dispatched to +// the main thread (ie dispatch_async(dispatch_get_main_queue(), ...)). +// +// This assumes that the main thread is executing its queue. If not, #define +// NO_MAIN_THREAD_DISPATCH to disable this, and run fn(arg) synchronously. The +// flutter runner does execute the main dispatch queue, but the Dart VM doesn't. +void runOnMainThread(void (*fn)(void*), void* arg); + #endif // OBJECTIVE_C_SRC_OBJECTIVE_C_H_ diff --git a/pkgs/objective_c/src/objective_c.m b/pkgs/objective_c/src/objective_c.m new file mode 100644 index 000000000..5cf87fe48 --- /dev/null +++ b/pkgs/objective_c/src/objective_c.m @@ -0,0 +1,20 @@ +// Copyright (c) 2024, 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. + +#import +#import + +void runOnMainThread(void (*fn)(void*), void* arg) { +#ifdef NO_MAIN_THREAD_DISPATCH + fn(arg); +#else + if ([NSThread isMainThread]) { + fn(arg); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + fn(arg); + }); + } +#endif +} diff --git a/pkgs/objective_c/test/setup.dart b/pkgs/objective_c/test/setup.dart index 66cb15b7f..667af5aa6 100644 --- a/pkgs/objective_c/test/setup.dart +++ b/pkgs/objective_c/test/setup.dart @@ -12,8 +12,14 @@ import 'dart:ffi'; import 'dart:io'; +import 'package:args/args.dart'; + const cFiles = ['src/objective_c.c', 'src/include/dart_api_dl.c']; -const objCFiles = ['src/proxy.m', 'src/objective_c_bindings_generated.m']; +const objCFiles = [ + 'src/objective_c.m', + 'src/objective_c_bindings_generated.m', + 'src/proxy.m', +]; const objCFlags = [ '-x', 'objective-c', @@ -50,17 +56,25 @@ String _buildObject(String input, List flags) { void _linkLib(List inputs, String output) => _runClang(['-shared', '-undefined', 'dynamic_lookup', ...inputs], output); -void main() { +void main(List arguments) { + final parser = ArgParser(); + parser.addFlag('main-thread-dispatcher'); + final args = parser.parse(arguments); + + final flags = [ + if (!args.flag('main-thread-dispatcher')) '-DNO_MAIN_THREAD_DISPATCH', + ]; Directory.current = Platform.script.resolve('..').path; final objFiles = [ - for (final src in cFiles) _buildObject(src, []), - for (final src in objCFiles) _buildObject(src, objCFlags), + for (final src in cFiles) _buildObject(src, flags), + for (final src in objCFiles) _buildObject(src, [...objCFlags, ...flags]), ]; _linkLib(objFiles, outputFile); // Sanity check that the dylib was created correctly. final lib = DynamicLibrary.open(outputFile); lib.lookup('disposeObjCBlockWithClosure'); // objective_c.c + lib.lookup('runOnMainThread'); // objective_c.m lib.lookup('Dart_InitializeApiDL'); // dart_api_dl.c lib.lookup('OBJC_CLASS_\$_DartProxy'); // proxy.m lib.lookup('_wrapListenerBlock_hepzs'); // objective_c_bindings_generated.m