From 16918aef9da60646e5ae0ad0307fe0e1a64355b2 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Fri, 27 Sep 2024 04:20:19 +0200 Subject: [PATCH 1/6] [jnigen] Implement void interface methods as listeners (#1596) --- .../dart_lang/jni/PortProxyBuilder.java | 21 +++- pkgs/jni/lib/src/jimplementer.dart | 33 ++--- pkgs/jni/lib/src/jni.dart | 5 +- pkgs/jni/src/dartjni.c | 40 +++--- pkgs/jnigen/CHANGELOG.md | 1 + pkgs/jnigen/docs/interface_implementation.md | 48 +++++++- .../lib/src/bindings/dart_generator.dart | 116 +++++++++++++----- .../bindings/simple_package.dart | 48 +++++++- .../jnigen/interfaces/MyRunnableRunner.java | 6 + .../runtime_test_registrant.dart | 52 ++++++++ 10 files changed, 296 insertions(+), 74 deletions(-) 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; +} From 540502829152112b4f689e0b87311f7ec20319ca Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Fri, 27 Sep 2024 16:27:11 +0200 Subject: [PATCH 2/6] [native_toolchain_c] Enable Android RISC-V testing (#1612) --- .github/workflows/native.yaml | 2 +- .github/workflows/native_toolchain_c.yaml | 2 +- .../test/cbuilder/cbuilder_cross_android_test.dart | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) 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/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 = { From 5d7976144484dad68f6107b2059ae0425d629915 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 27 Sep 2024 16:27:32 +0200 Subject: [PATCH 3/6] Remove link-dry-run related code (which seems not used anywhere in flutter & dart) (#1613) It seems neither Flutter Tools nor Dart SDK uses or needs the link-dry-run functionality. So I think we can simplify the code by removing this. --- pkgs/native_assets_builder/CHANGELOG.md | 1 + .../lib/native_assets_builder.dart | 1 - .../lib/src/build_runner/build_runner.dart | 100 ++------------- .../lib/src/model/hook_result.dart | 3 +- .../lib/src/model/link_dry_run_result.dart | 19 --- .../test/build_runner/helpers.dart | 28 ----- .../test/build_runner/link_dry_run_test.dart | 114 ------------------ 7 files changed, 15 insertions(+), 251 deletions(-) delete mode 100644 pkgs/native_assets_builder/lib/src/model/link_dry_run_result.dart delete mode 100644 pkgs/native_assets_builder/test/build_runner/link_dry_run_test.dart diff --git a/pkgs/native_assets_builder/CHANGELOG.md b/pkgs/native_assets_builder/CHANGELOG.md index ed1a89875..8e40e14a8 100644 --- a/pkgs/native_assets_builder/CHANGELOG.md +++ b/pkgs/native_assets_builder/CHANGELOG.md @@ -9,6 +9,7 @@ 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 ## 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..30df250f3 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'; @@ -379,66 +378,12 @@ class NativeAssetsBuildRunner { 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 +400,6 @@ class NativeAssetsBuildRunner { linkMode: linkModePreference, buildParentDir: packageLayout.dartToolNativeAssetsBuilder, supportedAssetTypes: supportedAssetTypes, - hook: hook, - buildDryRunResult: buildDryRunResult, linkingEnabled: linkingEnabled, ); final packageConfigUri = packageLayout.packageConfigUri; @@ -820,11 +763,10 @@ ${compileResult.stdout} required OSImpl targetOS, required LinkModePreferenceImpl 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 +792,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 +854,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 +883,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); 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/helpers.dart b/pkgs/native_assets_builder/test/build_runner/helpers.dart index c5aa75d04..399fa9812 100644 --- a/pkgs/native_assets_builder/test/build_runner/helpers.dart +++ b/pkgs/native_assets_builder/test/build_runner/helpers.dart @@ -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); From 1aae9dab0d5aaa81af1579d3f7dc4394a19bf9c5 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 27 Sep 2024 20:18:13 +0200 Subject: [PATCH 4/6] Unify {OS,Architecture,...} and {OS,Architecture,...}Impl classes that are effectively simple enums or data classes (#1604) The API of `package:native_assets_cli` uses currently a one-size-fits-all approach: * almost every API class has two versions a `` and a `Impl` * `package:native_assets_cli/native_assets_cli.dart` exposes `` * `package:native_assets_cli/native_assets_cli_internal.dart` exposes `Impl` This has a number of issues * The `_internal.dart` is actually a public API: It's used e.g. by flutter tools and breaking changes to it need major version bumps. * A user of the `` API doesn't know that such objects are for example * hashable (e.g. go-to-definition to `` there's no indication of that), e.g. `DynamicLoadingSystem` makes no mention of it, no `get hashCode` override, ... only in `DynamicLoadingSystemImpl` there is. * have a `toString()` that code can rely on the returned string not changing * ... * It causes code to have to choose between e.g. `OS` and `OSImpl` - which is very confusing as it's just one concept. * It causes code to have to downcast e.g. `os as OSImpl`. => This is a very bad smell, as generally it's an unsafe operation to downcast. * It's code duplication across the api/ and model/ folders, make it harder to navigate in codebase (as one may have to constantly switch between the files), ... One original intention for this separation may have been to hide all traces of the json protocol in the non-internal version, but I think this is not needed at all, because the protocol itself will be *more* stable than the APIs offered by the package itself (much easier to break the Dart API and publish and new version of the package than to break the CLI protocol). => So this PR starts to merge some `` and `Impl` classes into one ``. => This simplifies the code, removes `as ...Impl` downcasts, etc One extra thing this CL does is remove some members from e.g. `OS`. This is part of disentangling the concepts. The build protocol may always expose the target operating system, but it may only expose the target ABI if the building code supports code assets. => So one should be able to use `OS` without `OS.dylibFileName` / `OS.architectures` / ... => We may even decide to move code asset related code into it's own package, making the core build protocol package not need the concept of `Architecture` at all. --- pkgs/native_assets_builder/CHANGELOG.md | 1 + .../lib/src/build_runner/build_runner.dart | 97 ++++++--- .../build_runner_reusability_test.dart | 12 +- .../build_runner_run_in_isolation_test.dart | 36 ++-- .../concurrency_shared_test_helper.dart | 5 +- .../build_runner/concurrency_test_helper.dart | 4 +- .../fail_on_os_sdk_version_test.dart | 5 +- .../test/build_runner/helpers.dart | 30 +-- .../packaging_preference_test.dart | 16 +- pkgs/native_assets_builder/test/helpers.dart | 28 +-- .../native_dynamic_linking_test.dart | 8 +- .../test/test_data/transformer_test.dart | 9 +- .../test_data/wrong_linker/hook/build.dart | 1 - .../wrong_namespace_asset/hook/build.dart | 1 - pkgs/native_assets_cli/CHANGELOG.md | 2 + .../lib/native_assets_cli.dart | 32 +-- .../lib/native_assets_cli_internal.dart | 22 +- .../lib/src/api/architecture.dart | 40 ---- pkgs/native_assets_cli/lib/src/api/asset.dart | 5 +- .../lib/src/api/build_config.dart | 29 ++- .../lib/src/api/build_output.dart | 4 +- .../lib/src/api/c_compiler_config.dart | 32 --- .../lib/src/api/hook_config.dart | 13 +- .../lib/src/api/link_config.dart | 27 +-- .../lib/src/api/native_code_asset.dart | 139 +++++++------ pkgs/native_assets_cli/lib/src/api/os.dart | 58 ------ pkgs/native_assets_cli/lib/src/api/test.dart | 11 +- .../lib/src/architecture.dart | 90 +++++++++ .../lib/src/{api => }/build_mode.dart | 28 ++- .../lib/src/c_compiler_config.dart | 117 +++++++++++ .../lib/src/{api => }/ios_sdk.dart | 28 ++- pkgs/native_assets_cli/lib/src/link_mode.dart | 158 +++++++++++++++ .../src/{api => }/link_mode_preference.dart | 41 +++- .../lib/src/model/architecture.dart | 68 ------- .../lib/src/model/build_mode.dart | 28 --- .../lib/src/model/c_compiler_config.dart | 95 --------- .../lib/src/model/hook_config.dart | 171 +++++++--------- .../lib/src/model/ios_sdk.dart | 27 --- .../lib/src/model/link_mode_preference.dart | 43 ---- .../lib/src/model/native_code_asset.dart | 168 +--------------- pkgs/native_assets_cli/lib/src/model/os.dart | 189 ------------------ .../lib/src/model/target.dart | 46 +++-- pkgs/native_assets_cli/lib/src/os.dart | 78 ++++++++ .../lib/src/validator/validator.dart | 3 +- .../test/example/local_asset_test.dart | 8 +- .../test/example/native_add_library_test.dart | 8 +- .../example/native_dynamic_linking_test.dart | 8 +- pkgs/native_assets_cli/test/helpers.dart | 28 +-- .../test/model/asset_test.dart | 42 ++-- .../test/model/build_config_test.dart | 113 ++++++----- .../test/model/build_output_test.dart | 64 +++--- .../test/model/checksum_test.dart | 45 ++--- .../test/model/link_config_test.dart | 92 +++++---- .../test/model/target_test.dart | 29 +-- pkgs/native_toolchain_c/CHANGELOG.md | 1 + .../lib/src/cbuilder/ctool.dart | 3 +- pkgs/native_toolchain_c/test/helpers.dart | 19 +- 57 files changed, 1130 insertions(+), 1375 deletions(-) delete mode 100644 pkgs/native_assets_cli/lib/src/api/architecture.dart delete mode 100644 pkgs/native_assets_cli/lib/src/api/c_compiler_config.dart delete mode 100644 pkgs/native_assets_cli/lib/src/api/os.dart create mode 100644 pkgs/native_assets_cli/lib/src/architecture.dart rename pkgs/native_assets_cli/lib/src/{api => }/build_mode.dart (59%) create mode 100644 pkgs/native_assets_cli/lib/src/c_compiler_config.dart rename pkgs/native_assets_cli/lib/src/{api => }/ios_sdk.dart (51%) create mode 100644 pkgs/native_assets_cli/lib/src/link_mode.dart rename pkgs/native_assets_cli/lib/src/{api => }/link_mode_preference.dart (63%) delete mode 100644 pkgs/native_assets_cli/lib/src/model/architecture.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/build_mode.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/c_compiler_config.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/ios_sdk.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/link_mode_preference.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/os.dart create mode 100644 pkgs/native_assets_cli/lib/src/os.dart diff --git a/pkgs/native_assets_builder/CHANGELOG.md b/pkgs/native_assets_builder/CHANGELOG.md index 8e40e14a8..b5184798e 100644 --- a/pkgs/native_assets_builder/CHANGELOG.md +++ b/pkgs/native_assets_builder/CHANGELOG.md @@ -10,6 +10,7 @@ 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/src/build_runner/build_runner.dart b/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart index 30df250f3..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 @@ -54,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, @@ -99,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, @@ -137,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, @@ -160,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) @@ -184,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, @@ -268,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, @@ -370,8 +365,8 @@ 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, @@ -760,8 +755,8 @@ ${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, Iterable? supportedAssetTypes, required bool? linkingEnabled, @@ -914,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/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 399fa9812..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, 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/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. From 963155deaa501cc62a19fcc7e6a7a0fe9ce5bf20 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Mon, 30 Sep 2024 12:30:54 +1300 Subject: [PATCH 5/6] [ffigen] Fix bug in NSRange import (#1602) --- pkgs/ffigen/CHANGELOG.md | 2 + .../lib/src/code_generator/compound.dart | 8 +++- .../objc_built_in_functions.dart | 18 ++++---- .../test/native_objc_test/ns_range_test.dart | 44 +++++++++++++++++++ .../test/native_objc_test/ns_range_test.m | 5 +++ 5 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 pkgs/ffigen/test/native_objc_test/ns_range_test.dart create mode 100644 pkgs/ffigen/test/native_objc_test/ns_range_test.m diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 7e4443604..ceb33ba38 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -14,6 +14,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 ## 14.0.1 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/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 From f9a388863ea610e22bf71e721c03f402fde397ab Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Mon, 30 Sep 2024 12:31:10 +1300 Subject: [PATCH 6/6] [ffigen] Release all ObjC references on the main thread (#1603) --- .github/workflows/ffigen.yml | 8 ++-- .github/workflows/ffigen_weekly.yml | 2 +- .../test/native_objc_test/arc_config.yaml | 7 ++- .../test/native_objc_test/arc_test.dart | 20 +++++++- pkgs/ffigen/test/native_objc_test/arc_test.h | 41 ++++++++++++++++ pkgs/ffigen/test/native_objc_test/arc_test.m | 48 ++++++++----------- pkgs/ffigen/test/native_objc_test/setup.dart | 16 +++++-- pkgs/ffigen/test/setup.dart | 18 +++++-- pkgs/objective_c/CHANGELOG.md | 1 + pkgs/objective_c/ios/Classes/objective_c.m | 1 + .../src/objective_c_bindings_generated.dart.m | 26 ++++++++++ pkgs/objective_c/macos/Classes/objective_c.m | 1 + pkgs/objective_c/pubspec.yaml | 1 + pkgs/objective_c/src/CMakeLists.txt | 2 + pkgs/objective_c/src/objective_c.c | 8 ++-- pkgs/objective_c/src/objective_c.h | 9 ++++ pkgs/objective_c/src/objective_c.m | 20 ++++++++ pkgs/objective_c/test/setup.dart | 22 +++++++-- 18 files changed, 197 insertions(+), 54 deletions(-) create mode 100644 pkgs/ffigen/test/native_objc_test/arc_test.h create mode 100644 pkgs/objective_c/lib/src/objective_c_bindings_generated.dart.m create mode 100644 pkgs/objective_c/src/objective_c.m 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/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/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/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