diff --git a/pkgs/native_assets_builder/CHANGELOG.md b/pkgs/native_assets_builder/CHANGELOG.md index 900457b42..ca1125660 100644 --- a/pkgs/native_assets_builder/CHANGELOG.md +++ b/pkgs/native_assets_builder/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0-wip + +- **Breaking change** Completely rewritten API in `native_assets_cli`. + ## 0.5.0 - **Breaking change**: Hide implementation of `KernelAssets`. diff --git a/pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart b/pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart index 492f8a533..0df0cbff3 100644 --- a/pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart +++ b/pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart @@ -88,11 +88,11 @@ class PackageGraph { PackageGraph(this.map); - /// Construct a graph from the JSON produced by `dart pub deps --json`. + /// Constructs a graph from the JSON produced by `dart pub deps --json`. factory PackageGraph.fromPubDepsJsonString(String json) => PackageGraph.fromPubDepsJson(jsonDecode(json) as Map); - /// Construct a graph from the JSON produced by `dart pub deps --json`. + /// Constructs a graph from the JSON produced by `dart pub deps --json`. factory PackageGraph.fromPubDepsJson(Map map) { final result = >{}; final packages = map['packages'] as List; 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 5a35967fd..53e083ca3 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 @@ -36,16 +36,17 @@ class NativeAssetsBuildRunner { /// If provided, only native assets of all transitive dependencies of /// [runPackageName] are built. Future build({ - required LinkModePreference linkModePreference, + required LinkModePreferenceImpl linkModePreference, required Target target, required Uri workingDirectory, - required BuildMode buildMode, - CCompilerConfig? cCompilerConfig, - IOSSdk? targetIOSSdk, + required BuildModeImpl buildMode, + CCompilerConfigImpl? cCompilerConfig, + IOSSdkImpl? targetIOSSdk, int? targetAndroidNdkApi, required bool includeParentEnvironment, PackageLayout? packageLayout, String? runPackageName, + Iterable? supportedAssetTypes, }) async { packageLayout ??= await PackageLayout.fromRootPackageRoot(workingDirectory); final packagesWithNativeAssets = @@ -77,7 +78,7 @@ class NativeAssetsBuildRunner { buildPlan = plan; packageGraph = planner.packageGraph; } - final assets = []; + final assets = []; final dependencies = []; final metadata = {}; var success = true; @@ -98,6 +99,7 @@ class NativeAssetsBuildRunner { cCompilerConfig: cCompilerConfig, targetIOSSdk: targetIOSSdk, targetAndroidNdkApi: targetAndroidNdkApi, + supportedAssetTypes: supportedAssetTypes, ); final ( packageAssets, @@ -132,12 +134,13 @@ class NativeAssetsBuildRunner { /// If provided, only native assets of all transitive dependencies of /// [runPackageName] are built. Future dryRun({ - required LinkModePreference linkModePreference, - required OS targetOs, + required LinkModePreferenceImpl linkModePreference, + required OSImpl targetOS, required Uri workingDirectory, required bool includeParentEnvironment, PackageLayout? packageLayout, String? runPackageName, + Iterable? supportedAssetTypes, }) async { packageLayout ??= await PackageLayout.fromRootPackageRoot(workingDirectory); final packagesWithNativeAssets = @@ -163,15 +166,16 @@ class NativeAssetsBuildRunner { } buildPlan = plan; } - final assets = []; + final assets = []; var success = true; for (final package in buildPlan) { final config = await _cliConfigDryRun( packageName: package.name, packageRoot: packageLayout.packageRoot(package.name), - targetOs: targetOs, + targetOS: targetOS, linkMode: linkModePreference, buildParentDir: packageLayout.dartToolNativeAssetsBuilder, + supportedAssetTypes: supportedAssetTypes, ); final (packageAssets, _, _, packageSuccess) = await _buildPackage( config, @@ -180,7 +184,26 @@ class NativeAssetsBuildRunner { includeParentEnvironment, dryRun: true, ); - assets.addAll(packageAssets); + for (final asset in packageAssets) { + switch (asset) { + case NativeCodeAssetImpl _: + if (asset.architecture != null) { + // Backwards compatibility, if an architecture is provided use it. + assets.add(asset); + } else { + // Dry run does not report architecture. Dart VM branches on OS + // and Target when looking up assets, so populate assets for all + // architectures. + for (final architecture in asset.os.architectures) { + assets.add(asset.copyWith( + architecture: architecture, + )); + } + } + case DataAssetImpl _: + assets.add(asset); + } + } success &= packageSuccess; } return _DryRunResultImpl( @@ -190,21 +213,21 @@ class NativeAssetsBuildRunner { } Future<_PackageBuildRecord> _buildPackageCached( - BuildConfig config, + BuildConfigImpl config, Uri packageConfigUri, Uri workingDirectory, bool includeParentEnvironment, ) async { final packageName = config.packageName; - final outDir = config.outDir; + final outDir = config.outputDirectory; if (!await Directory.fromUri(outDir).exists()) { await Directory.fromUri(outDir).create(recursive: true); } - final buildOutput = await BuildOutput.readFromFile(outDir: outDir); + final buildOutput = await BuildOutputImpl.readFromFile(outDir: outDir); final lastBuilt = buildOutput?.timestamp.roundDownToSeconds() ?? DateTime.fromMillisecondsSinceEpoch(0); - final dependencies = buildOutput?.dependencies; + final dependencies = buildOutput?.dependenciesModel; final lastChange = await dependencies?.lastModified() ?? DateTime.now(); if (lastBuilt.isAfter(lastChange)) { @@ -213,8 +236,8 @@ class NativeAssetsBuildRunner { // All build flags go into [outDir]. Therefore we do not have to check // here whether the config is equal. final assets = buildOutput!.assets; - final dependencies = buildOutput.dependencies.dependencies; - final metadata = buildOutput.metadata; + final dependencies = buildOutput.dependencies; + final metadata = buildOutput.metadataModel; return (assets, dependencies, metadata, true); } @@ -228,19 +251,20 @@ class NativeAssetsBuildRunner { } Future<_PackageBuildRecord> _buildPackage( - BuildConfig config, + BuildConfigImpl config, Uri packageConfigUri, Uri workingDirectory, bool includeParentEnvironment, { required bool dryRun, }) async { - final outDir = config.outDir; + final outDir = config.outputDirectory; final configFile = outDir.resolve('../config.yaml'); final buildDotDart = config.packageRoot.resolve('build.dart'); final configFileContents = config.toYamlString(); logger.info('config.yaml contents: $configFileContents'); await File.fromUri(configFile).writeAsString(configFileContents); - final buildOutputFile = File.fromUri(outDir.resolve(BuildOutput.fileName)); + final buildOutputFile = + File.fromUri(outDir.resolve(BuildOutputImpl.fileName)); if (await buildOutputFile.exists()) { // Ensure we'll never read outdated build results. await buildOutputFile.delete(); @@ -282,11 +306,11 @@ ${result.stdout} } try { - final buildOutput = await BuildOutput.readFromFile(outDir: outDir); + final buildOutput = await BuildOutputImpl.readFromFile(outDir: outDir); final assets = buildOutput?.assets ?? []; success &= validateAssetsPackage(assets, config.packageName); - final dependencies = buildOutput?.dependencies.dependencies ?? []; - final metadata = dryRun ? null : buildOutput?.metadata; + final dependencies = buildOutput?.dependencies ?? []; + final metadata = dryRun ? null : buildOutput?.metadataModel; return (assets, dependencies, metadata, success); } on FormatException catch (e) { logger.severe(''' @@ -295,7 +319,7 @@ build_output.yaml contained a format error. ${e.message} '''); success = false; - return ([], [], const Metadata({}), false); + return ([], [], const Metadata({}), false); // TODO(https://github.com/dart-lang/native/issues/109): Stop throwing // type errors in native_assets_cli, release a new version of that package // and then remove this. @@ -306,11 +330,11 @@ Building native assets for package:${config.packageName} failed. build_output.yaml contained a format error. '''); success = false; - return ([], [], const Metadata({}), false); + return ([], [], const Metadata({}), false); } finally { if (!success) { final buildOutputFile = - File.fromUri(outDir.resolve(BuildOutput.fileName)); + File.fromUri(outDir.resolve(BuildOutputImpl.fileName)); if (await buildOutputFile.exists()) { await buildOutputFile.delete(); } @@ -318,22 +342,23 @@ build_output.yaml contained a format error. } } - static Future _cliConfig({ + static Future _cliConfig({ required String packageName, required Uri packageRoot, required Target target, - IOSSdk? targetIOSSdk, + IOSSdkImpl? targetIOSSdk, int? targetAndroidNdkApi, - required BuildMode buildMode, - required LinkModePreference linkMode, + required BuildModeImpl buildMode, + required LinkModePreferenceImpl linkMode, required Uri buildParentDir, - CCompilerConfig? cCompilerConfig, + CCompilerConfigImpl? cCompilerConfig, DependencyMetadata? dependencyMetadata, + Iterable? supportedAssetTypes, }) async { - final buildDirName = BuildConfig.checksum( + final buildDirName = BuildConfigImpl.checksum( packageName: packageName, packageRoot: packageRoot, - targetOs: target.os, + targetOS: target.os, targetArchitecture: target.architecture, buildMode: buildMode, linkModePreference: linkMode, @@ -341,6 +366,7 @@ build_output.yaml contained a format error. cCompiler: cCompilerConfig, dependencyMetadata: dependencyMetadata, targetAndroidNdkApi: targetAndroidNdkApi, + supportedAssetTypes: supportedAssetTypes, ); final outDirUri = buildParentDir.resolve('$buildDirName/out/'); final outDir = Directory.fromUri(outDirUri); @@ -348,11 +374,11 @@ build_output.yaml contained a format error. // TODO(https://dartbug.com/50565): Purge old or unused folders. await outDir.create(recursive: true); } - return BuildConfig( + return BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: packageRoot, - targetOs: target.os, + targetOS: target.os, targetArchitecture: target.architecture, buildMode: buildMode, linkModePreference: linkMode, @@ -363,25 +389,27 @@ build_output.yaml contained a format error. ); } - static Future _cliConfigDryRun({ + static Future _cliConfigDryRun({ required String packageName, required Uri packageRoot, - required OS targetOs, - required LinkModePreference linkMode, + required OSImpl targetOS, + required LinkModePreferenceImpl linkMode, required Uri buildParentDir, + Iterable? supportedAssetTypes, }) async { - final buildDirName = 'dry_run_${targetOs}_$linkMode'; + final buildDirName = 'dry_run_${targetOS}_$linkMode'; final outDirUri = buildParentDir.resolve('$buildDirName/out/'); final outDir = Directory.fromUri(outDirUri); if (!await outDir.exists()) { await outDir.create(recursive: true); } - return BuildConfig.dryRun( + return BuildConfigImpl.dryRun( outDir: outDirUri, packageName: packageName, packageRoot: packageRoot, - targetOs: targetOs, + targetOS: targetOS, linkModePreference: linkMode, + supportedAssetTypes: supportedAssetTypes, ); } @@ -400,7 +428,7 @@ build_output.yaml contained a format error. }; } - bool validateAssetsPackage(List assets, String packageName) { + bool validateAssetsPackage(Iterable assets, String packageName) { final invalidAssetIds = assets .map((a) => a.id) .where((n) => !n.startsWith('package:$packageName/')) @@ -419,8 +447,8 @@ build_output.yaml contained a format error. } typedef _PackageBuildRecord = ( - List, - List dependencies, + Iterable, + Iterable dependencies, Metadata?, bool success, ); @@ -428,7 +456,7 @@ typedef _PackageBuildRecord = ( /// The result from a [NativeAssetsBuildRunner.dryRun]. abstract interface class DryRunResult { /// The native assets for all [Target]s for the build or dry run. - List get assets; + List get assets; /// Whether all builds completed without errors. /// @@ -438,7 +466,7 @@ abstract interface class DryRunResult { final class _DryRunResultImpl implements DryRunResult { @override - final List assets; + final List assets; @override final bool success; @@ -462,7 +490,7 @@ abstract class BuildResult implements DryRunResult { final class _BuildResultImpl implements BuildResult { @override - final List assets; + final List assets; @override final List dependencies; diff --git a/pkgs/native_assets_builder/pubspec.yaml b/pkgs/native_assets_builder/pubspec.yaml index 37199f6a1..bed088c25 100644 --- a/pkgs/native_assets_builder/pubspec.yaml +++ b/pkgs/native_assets_builder/pubspec.yaml @@ -1,18 +1,20 @@ name: native_assets_builder description: >- This package is the backend that invokes top-level `build.dart` scripts. -version: 0.5.0 +version: 0.6.0-wip repository: https://github.com/dart-lang/native/tree/main/pkgs/native_assets_builder environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' + +publish_to: none dependencies: graphs: ^2.3.1 logging: ^1.2.0 - native_assets_cli: ^0.4.2 - # native_assets_cli: - # path: ../native_assets_cli/ + # native_assets_cli: ^0.4.2 + native_assets_cli: + path: ../native_assets_cli/ package_config: ^2.1.0 yaml: ^3.1.2 yaml_edit: ^2.1.0 diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_caching_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_caching_test.dart index 2749e53a5..d5774cd10 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_caching_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_caching_test.dart @@ -4,6 +4,7 @@ import 'dart:io'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -74,7 +75,9 @@ void main() async { { final result = await build(packageUri, logger, dartExecutable); - await expectSymbols(asset: result.assets.single, symbols: ['add']); + await expectSymbols( + asset: result.assets.single as NativeCodeAssetImpl, + symbols: ['add']); } await copyTestProjects( @@ -85,7 +88,7 @@ void main() async { { final result = await build(packageUri, logger, dartExecutable); await expectSymbols( - asset: result.assets.single, + asset: result.assets.single as NativeCodeAssetImpl, symbols: ['add', 'subtract'], ); } @@ -101,7 +104,9 @@ void main() async { { final result = await build(packageUri, logger, dartExecutable); - await expectSymbols(asset: result.assets.single, symbols: ['add']); + await expectSymbols( + asset: result.assets.single as NativeCodeAssetImpl, + symbols: ['add']); } await copyTestProjects( @@ -111,7 +116,8 @@ void main() async { { final result = await build(packageUri, logger, dartExecutable); await expectSymbols( - asset: result.assets.single, symbols: ['add', 'multiply']); + asset: result.assets.single as NativeCodeAssetImpl, + symbols: ['add', 'multiply']); } }); }); diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_dry_run_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_dry_run_test.dart index 057751469..a7b4614b9 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_dry_run_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_dry_run_test.dart @@ -24,20 +24,34 @@ void main() async { logger: logger, ); - final dryRunAssets = (await dryRun(packageUri, logger, dartExecutable)) - .assets - .where((element) => element.target == Target.current) - .toList(); - final result = await build(packageUri, logger, dartExecutable); + final dryRunResult = await dryRun( + packageUri, + logger, + dartExecutable, + ); + final dryRunAssets = dryRunResult.assets.toList(); + final result = await build( + packageUri, + logger, + dartExecutable, + ); - expect(dryRunAssets.length, result.assets.length); + // Every OS has more than one architecture. + expect(dryRunAssets.length, greaterThan(result.assets.length)); for (var i = 0; i < dryRunAssets.length; i++) { - final dryRunAsset = dryRunAssets[0]; + final dryRunAsset = dryRunAssets[i]; final buildAsset = result.assets[0]; - expect(dryRunAsset.linkMode, buildAsset.linkMode); expect(dryRunAsset.id, buildAsset.id); - expect(dryRunAsset.target, buildAsset.target); - // The target folders are different, so the paths are different. + // The build runner expands NativeCodeAssets to all architectures. + expect(buildAsset.file, isNotNull); + expect(dryRunAsset.file, isNull); + if (dryRunAsset is NativeCodeAssetImpl && + buildAsset is NativeCodeAssetImpl) { + expect(dryRunAsset.architecture, isNotNull); + expect(buildAsset.architecture, isNotNull); + expect(dryRunAsset.os, buildAsset.os); + expect(dryRunAsset.linkMode, buildAsset.linkMode); + } } final dryRunDir = packageUri.resolve( diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_failure_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_failure_test.dart index c844be2bf..2355353ed 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_failure_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_failure_test.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:logging/logging.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -26,7 +27,9 @@ void main() async { { final result = await build(packageUri, logger, dartExecutable); expect(result.assets.length, 1); - await expectSymbols(asset: result.assets.single, symbols: ['add']); + await expectSymbols( + asset: result.assets.single as NativeCodeAssetImpl, + symbols: ['add']); expect( result.dependencies, [ @@ -69,7 +72,9 @@ void main() async { { final result = await build(packageUri, logger, dartExecutable); expect(result.assets.length, 1); - await expectSymbols(asset: result.assets.single, symbols: ['add']); + await expectSymbols( + asset: result.assets.single as NativeCodeAssetImpl, + symbols: ['add']); expect( result.dependencies, [ 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 0ee48ab31..c7e84e926 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 @@ -29,27 +29,27 @@ void main() async { ); await buildRunner.dryRun( - targetOs: Target.current.os, - linkModePreference: LinkModePreference.dynamic, + targetOS: Target.current.os, + linkModePreference: LinkModePreferenceImpl.dynamic, workingDirectory: packageUri, includeParentEnvironment: true, ); await buildRunner.dryRun( - targetOs: Target.current.os, - linkModePreference: LinkModePreference.dynamic, + targetOS: Target.current.os, + linkModePreference: LinkModePreferenceImpl.dynamic, workingDirectory: packageUri, includeParentEnvironment: true, ); await buildRunner.build( - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.dynamic, target: Target.current, workingDirectory: packageUri, includeParentEnvironment: true, ); await buildRunner.build( - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.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 ea7b05e3f..a7f02557f 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 @@ -14,12 +14,12 @@ const Timeout longTimeout = Timeout(Duration(minutes: 5)); void main() async { String unparseKey(String key) => key.replaceAll('.', '__').toUpperCase(); - final arKey = unparseKey(CCompilerConfig.arConfigKeyFull); - final ccKey = unparseKey(CCompilerConfig.ccConfigKeyFull); - final ldKey = unparseKey(CCompilerConfig.ldConfigKeyFull); - final envScriptKey = unparseKey(CCompilerConfig.envScriptConfigKeyFull); + final arKey = unparseKey(CCompilerConfigImpl.arConfigKeyFull); + final ccKey = unparseKey(CCompilerConfigImpl.ccConfigKeyFull); + final ldKey = unparseKey(CCompilerConfigImpl.ldConfigKeyFull); + final envScriptKey = unparseKey(CCompilerConfigImpl.envScriptConfigKeyFull); final envScriptArgsKey = - unparseKey(CCompilerConfig.envScriptArgsConfigKeyFull); + unparseKey(CCompilerConfigImpl.envScriptArgsConfigKeyFull); final cc = Platform.environment[ccKey]?.fileUri; @@ -52,12 +52,12 @@ void main() async { logger, dartExecutable, // Manually pass in a compiler. - cCompilerConfig: CCompilerConfig( - ar: Platform.environment[arKey]?.fileUri, - cc: cc, + cCompilerConfig: CCompilerConfigImpl( + archiver: Platform.environment[arKey]?.fileUri, + compiler: cc, envScript: Platform.environment[envScriptKey]?.fileUri, envScriptArgs: Platform.environment[envScriptArgsKey]?.split(' '), - ld: Platform.environment[ldKey]?.fileUri, + linker: Platform.environment[ldKey]?.fileUri, ), // Prevent any other environment variables. includeParentEnvironment: false, diff --git a/pkgs/native_assets_builder/test/build_runner/helpers.dart b/pkgs/native_assets_builder/test/build_runner/helpers.dart index c21ec033e..6dab3951e 100644 --- a/pkgs/native_assets_builder/test/build_runner/helpers.dart +++ b/pkgs/native_assets_builder/test/build_runner/helpers.dart @@ -34,8 +34,8 @@ Future build( Uri packageUri, Logger logger, Uri dartExecutable, { - LinkModePreference linkModePreference = LinkModePreference.dynamic, - CCompilerConfig? cCompilerConfig, + LinkModePreferenceImpl linkModePreference = LinkModePreferenceImpl.dynamic, + CCompilerConfigImpl? cCompilerConfig, bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, @@ -51,7 +51,7 @@ Future build( logger: logger, dartExecutable: dartExecutable, ).build( - buildMode: BuildMode.release, + buildMode: BuildModeImpl.release, linkModePreference: linkModePreference, target: Target.current, workingDirectory: packageUri, @@ -61,7 +61,7 @@ Future build( runPackageName: runPackageName, ); if (result.success) { - await expectAssetsExist(result.assets); + await expectAssetsExist(result.assets.cast()); } if (subscription != null) { @@ -75,8 +75,8 @@ Future dryRun( Uri packageUri, Logger logger, Uri dartExecutable, { - LinkModePreference linkModePreference = LinkModePreference.dynamic, - CCompilerConfig? cCompilerConfig, + LinkModePreferenceImpl linkModePreference = LinkModePreferenceImpl.dynamic, + CCompilerConfigImpl? cCompilerConfig, bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, @@ -92,7 +92,7 @@ Future dryRun( dartExecutable: dartExecutable, ).dryRun( linkModePreference: linkModePreference, - targetOs: Target.current.os, + targetOS: Target.current.os, workingDirectory: packageUri, includeParentEnvironment: includeParentEnvironment, packageLayout: packageLayout, @@ -105,9 +105,9 @@ Future dryRun( return result; } -Future expectAssetsExist(List assets) async { +Future expectAssetsExist(List assets) async { for (final asset in assets) { - final uri = (asset.path as AssetAbsolutePath).uri; + final uri = asset.file!; expect( uri.toFilePath(), contains('${Platform.pathSeparator}.dart_tool${Platform.pathSeparator}' @@ -118,11 +118,11 @@ Future expectAssetsExist(List assets) async { } Future expectSymbols({ - required Asset asset, + required NativeCodeAssetImpl asset, required List symbols, }) async { if (Platform.isLinux) { - final assetUri = (asset.path as AssetAbsolutePath).uri; + final assetUri = asset.file!; final nmResult = await runProcess( executable: Uri(path: 'nm'), arguments: [ 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 7db076c08..b9be4a481 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,35 +26,47 @@ void main() async { packageUri, logger, dartExecutable, - linkModePreference: LinkModePreference.dynamic, + linkModePreference: LinkModePreferenceImpl.dynamic, ); final resultPreferDynamic = await build( packageUri, logger, dartExecutable, - linkModePreference: LinkModePreference.preferDynamic, + linkModePreference: LinkModePreferenceImpl.preferDynamic, ); final resultStatic = await build( packageUri, logger, dartExecutable, - linkModePreference: LinkModePreference.static, + linkModePreference: LinkModePreferenceImpl.static, ); final resultPreferStatic = await build( packageUri, logger, dartExecutable, - linkModePreference: LinkModePreference.preferStatic, + linkModePreference: LinkModePreferenceImpl.preferStatic, ); // This package honors preferences. - expect(resultDynamic.assets.single.linkMode, LinkMode.dynamic); - expect(resultPreferDynamic.assets.single.linkMode, LinkMode.dynamic); - expect(resultStatic.assets.single.linkMode, LinkMode.static); - expect(resultPreferStatic.assets.single.linkMode, LinkMode.static); + expect( + (resultDynamic.assets.single as NativeCodeAssetImpl).linkMode, + DynamicLoadingBundledImpl(), + ); + expect( + (resultPreferDynamic.assets.single as NativeCodeAssetImpl).linkMode, + DynamicLoadingBundledImpl(), + ); + expect( + (resultStatic.assets.single as NativeCodeAssetImpl).linkMode, + StaticLinkingImpl(), + ); + expect( + (resultPreferStatic.assets.single as NativeCodeAssetImpl).linkMode, + StaticLinkingImpl(), + ); }); }); } diff --git a/pkgs/native_assets_builder/test_data/cyclic_package_1/build.dart b/pkgs/native_assets_builder/test_data/cyclic_package_1/build.dart index 2479e4ea2..291cc50bd 100644 --- a/pkgs/native_assets_builder/test_data/cyclic_package_1/build.dart +++ b/pkgs/native_assets_builder/test_data/cyclic_package_1/build.dart @@ -4,8 +4,6 @@ import 'package:native_assets_cli/native_assets_cli.dart'; -void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput(); - await buildOutput.writeToFile(outDir: buildConfig.outDir); +void main(List arguments) async { + await build(arguments, (config, output) async {}); } diff --git a/pkgs/native_assets_builder/test_data/cyclic_package_1/pubspec.yaml b/pkgs/native_assets_builder/test_data/cyclic_package_1/pubspec.yaml index 87f9dfdf3..266a06602 100644 --- a/pkgs/native_assets_builder/test_data/cyclic_package_1/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/cyclic_package_1/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 cyclic_package_2: path: ../cyclic_package_2 native_assets_cli: diff --git a/pkgs/native_assets_builder/test_data/cyclic_package_2/build.dart b/pkgs/native_assets_builder/test_data/cyclic_package_2/build.dart index 2479e4ea2..291cc50bd 100644 --- a/pkgs/native_assets_builder/test_data/cyclic_package_2/build.dart +++ b/pkgs/native_assets_builder/test_data/cyclic_package_2/build.dart @@ -4,8 +4,6 @@ import 'package:native_assets_cli/native_assets_cli.dart'; -void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput(); - await buildOutput.writeToFile(outDir: buildConfig.outDir); +void main(List arguments) async { + await build(arguments, (config, output) async {}); } diff --git a/pkgs/native_assets_builder/test_data/cyclic_package_2/pubspec.yaml b/pkgs/native_assets_builder/test_data/cyclic_package_2/pubspec.yaml index 9b49072e4..fa798a453 100644 --- a/pkgs/native_assets_builder/test_data/cyclic_package_2/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/cyclic_package_2/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 cyclic_package_1: path: ../cyclic_package_1 native_assets_cli: diff --git a/pkgs/native_assets_builder/test_data/dart_app/pubspec.yaml b/pkgs/native_assets_builder/test_data/dart_app/pubspec.yaml index 1ead5cb85..b1a44c2a3 100644 --- a/pkgs/native_assets_builder/test_data/dart_app/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/dart_app/pubspec.yaml @@ -3,7 +3,7 @@ name: dart_app publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: native_add: diff --git a/pkgs/native_assets_builder/test_data/native_add/build.dart b/pkgs/native_assets_builder/test_data/native_add/build.dart index 69bbb9ede..07118b3c8 100644 --- a/pkgs/native_assets_builder/test_data/native_add/build.dart +++ b/pkgs/native_assets_builder/test_data/native_add/build.dart @@ -8,24 +8,23 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; const packageName = 'native_add'; -void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput(); - final cbuilder = CBuilder.library( - name: packageName, - assetId: 'package:$packageName/src/${packageName}_bindings_generated.dart', - sources: [ - 'src/$packageName.c', - ], - ); - await cbuilder.run( - buildConfig: buildConfig, - buildOutput: buildOutput, - logger: Logger('') - ..level = Level.ALL - ..onRecord.listen((record) { - print('${record.level.name}: ${record.time}: ${record.message}'); - }), - ); - await buildOutput.writeToFile(outDir: buildConfig.outDir); +void main(List arguments) async { + await build(arguments, (config, output) async { + final cbuilder = CBuilder.library( + name: packageName, + assetName: 'src/${packageName}_bindings_generated.dart', + sources: [ + 'src/$packageName.c', + ], + ); + await cbuilder.run( + buildConfig: config, + buildOutput: output, + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((record) { + print('${record.level.name}: ${record.time}: ${record.message}'); + }), + ); + }); } diff --git a/pkgs/native_assets_builder/test_data/native_add/pubspec.yaml b/pkgs/native_assets_builder/test_data/native_add/pubspec.yaml index d99730406..461a6e9ec 100644 --- a/pkgs/native_assets_builder/test_data/native_add/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/native_add/pubspec.yaml @@ -5,14 +5,12 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 logging: ^1.1.1 native_assets_cli: path: ../../../native_assets_cli/ - # native_toolchain_c: ^0.3.4+1 native_toolchain_c: path: ../../../native_toolchain_c/ diff --git a/pkgs/native_assets_builder/test_data/native_add_add_source/build.dart b/pkgs/native_assets_builder/test_data/native_add_add_source/build.dart index 3fcb30e4b..aba869dee 100644 --- a/pkgs/native_assets_builder/test_data/native_add_add_source/build.dart +++ b/pkgs/native_assets_builder/test_data/native_add_add_source/build.dart @@ -8,25 +8,24 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; const packageName = 'native_add'; -void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput(); - final cbuilder = CBuilder.library( - name: packageName, - assetId: 'package:$packageName/src/${packageName}_bindings_generated.dart', - sources: [ - 'src/$packageName.c', - 'src/native_multiply.c', - ], - ); - await cbuilder.run( - buildConfig: buildConfig, - buildOutput: buildOutput, - logger: Logger('') - ..level = Level.ALL - ..onRecord.listen((record) { - print('${record.level.name}: ${record.time}: ${record.message}'); - }), - ); - await buildOutput.writeToFile(outDir: buildConfig.outDir); +void main(List arguments) async { + await build(arguments, (config, output) async { + final cbuilder = CBuilder.library( + name: packageName, + assetName: '${packageName}_bindings_generated.dart', + sources: [ + 'src/$packageName.c', + 'src/native_multiply.c', + ], + ); + await cbuilder.run( + buildConfig: config, + buildOutput: output, + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((record) { + print('${record.level.name}: ${record.time}: ${record.message}'); + }), + ); + }); } diff --git a/pkgs/native_assets_builder/test_data/native_add_add_source/pubspec.yaml b/pkgs/native_assets_builder/test_data/native_add_add_source/pubspec.yaml index eadbcb078..94a7c0e61 100644 --- a/pkgs/native_assets_builder/test_data/native_add_add_source/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/native_add_add_source/pubspec.yaml @@ -5,14 +5,12 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 logging: ^1.1.1 native_assets_cli: path: ../../../native_assets_cli/ - # native_toolchain_c: ^0.3.4+1 native_toolchain_c: path: ../../../native_toolchain_c/ diff --git a/pkgs/native_assets_builder/test_data/native_subtract/build.dart b/pkgs/native_assets_builder/test_data/native_subtract/build.dart index 66b62ea90..d1b1f6f07 100644 --- a/pkgs/native_assets_builder/test_data/native_subtract/build.dart +++ b/pkgs/native_assets_builder/test_data/native_subtract/build.dart @@ -8,24 +8,23 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; const packageName = 'native_subtract'; -void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput(); - final cbuilder = CBuilder.library( - name: packageName, - assetId: 'package:$packageName/src/${packageName}_bindings_generated.dart', - sources: [ - 'src/$packageName.c', - ], - ); - await cbuilder.run( - buildConfig: buildConfig, - buildOutput: buildOutput, - logger: Logger('') - ..level = Level.ALL - ..onRecord.listen((record) { - print('${record.level.name}: ${record.time}: ${record.message}'); - }), - ); - await buildOutput.writeToFile(outDir: buildConfig.outDir); +void main(List arguments) async { + await build(arguments, (config, output) async { + final cbuilder = CBuilder.library( + name: packageName, + assetName: 'src/${packageName}_bindings_generated.dart', + sources: [ + 'src/$packageName.c', + ], + ); + await cbuilder.run( + buildConfig: config, + buildOutput: output, + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((record) { + print('${record.level.name}: ${record.time}: ${record.message}'); + }), + ); + }); } diff --git a/pkgs/native_assets_builder/test_data/native_subtract/pubspec.yaml b/pkgs/native_assets_builder/test_data/native_subtract/pubspec.yaml index a4f5ee5e3..78a51acc9 100644 --- a/pkgs/native_assets_builder/test_data/native_subtract/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/native_subtract/pubspec.yaml @@ -5,14 +5,12 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 logging: ^1.1.1 native_assets_cli: path: ../../../native_assets_cli/ - # native_toolchain_c: ^0.3.4+1 native_toolchain_c: path: ../../../native_toolchain_c/ diff --git a/pkgs/native_assets_builder/test_data/package_reading_metadata/build.dart b/pkgs/native_assets_builder/test_data/package_reading_metadata/build.dart index e8009dbd9..e5278254b 100644 --- a/pkgs/native_assets_builder/test_data/package_reading_metadata/build.dart +++ b/pkgs/native_assets_builder/test_data/package_reading_metadata/build.dart @@ -2,20 +2,20 @@ // 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:cli_config/cli_config.dart'; import 'package:native_assets_cli/native_assets_cli.dart'; void main(List args) async { - final config = await Config.fromArgs(args: args); - final buildConfig = BuildConfig.fromConfig(config); + final buildConfig = BuildConfig(args); if (!buildConfig.dryRun) { - final metadata = - buildConfig.dependencyMetadata!['package_with_metadata']!.metadata; - final someValue = metadata['some_key']; + final someValue = + buildConfig.metadatum('package_with_metadata', 'some_key'); assert(someValue != null); - final someInt = metadata['some_int']; + final someInt = buildConfig.metadatum('package_with_metadata', 'some_int'); assert(someInt != null); - print(metadata); + print({ + 'some_int': someInt, + 'some_key': someValue, + }); } else { print('meta data not available in dry run'); } diff --git a/pkgs/native_assets_builder/test_data/package_reading_metadata/pubspec.yaml b/pkgs/native_assets_builder/test_data/package_reading_metadata/pubspec.yaml index 81c554892..679d9c119 100644 --- a/pkgs/native_assets_builder/test_data/package_reading_metadata/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/package_reading_metadata/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 native_assets_cli: path: ../../../native_assets_cli/ package_with_metadata: diff --git a/pkgs/native_assets_builder/test_data/package_with_metadata/build.dart b/pkgs/native_assets_builder/test_data/package_with_metadata/build.dart index a5e908ac7..87be9a0da 100644 --- a/pkgs/native_assets_builder/test_data/package_with_metadata/build.dart +++ b/pkgs/native_assets_builder/test_data/package_with_metadata/build.dart @@ -4,13 +4,11 @@ import 'package:native_assets_cli/native_assets_cli.dart'; -void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput( - metadata: Metadata({ +void main(List arguments) async { + await build(arguments, (config, output) async { + output.addMetadata({ 'some_key': 'some_value', 'some_int': 3, - }), - ); - await buildOutput.writeToFile(outDir: buildConfig.outDir); + }); + }); } diff --git a/pkgs/native_assets_builder/test_data/package_with_metadata/pubspec.yaml b/pkgs/native_assets_builder/test_data/package_with_metadata/pubspec.yaml index 84a7437dc..577487967 100644 --- a/pkgs/native_assets_builder/test_data/package_with_metadata/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/package_with_metadata/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 native_assets_cli: path: ../../../native_assets_cli/ yaml: ^3.1.1 diff --git a/pkgs/native_assets_builder/test_data/some_dev_dep/pubspec.yaml b/pkgs/native_assets_builder/test_data/some_dev_dep/pubspec.yaml index 4d22d95cd..f3fe7dc84 100644 --- a/pkgs/native_assets_builder/test_data/some_dev_dep/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/some_dev_dep/pubspec.yaml @@ -5,4 +5,4 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output/build.dart b/pkgs/native_assets_builder/test_data/wrong_build_output/build.dart index 8e19fd899..f36522b9d 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_build_output/build.dart @@ -7,8 +7,9 @@ import 'dart:io'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - await File.fromUri(buildConfig.outDir.resolve(BuildOutput.fileName)) + final buildConfig = BuildConfigImpl.fromArguments(args); + await File.fromUri( + buildConfig.outputDirectory.resolve(BuildOutputImpl.fileName)) .writeAsString(_wrongContents); } diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output/pubspec.yaml b/pkgs/native_assets_builder/test_data/wrong_build_output/pubspec.yaml index 1aa7e38ee..e752f2ab2 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/wrong_build_output/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 native_assets_cli: path: ../../../native_assets_cli/ yaml: ^3.1.1 diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output_2/build.dart b/pkgs/native_assets_builder/test_data/wrong_build_output_2/build.dart index de6e65daf..ae90eac44 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output_2/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_build_output_2/build.dart @@ -7,8 +7,9 @@ import 'dart:io'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - await File.fromUri(buildConfig.outDir.resolve(BuildOutput.fileName)) + final buildConfig = BuildConfigImpl.fromArguments(args); + await File.fromUri( + buildConfig.outputDirectory.resolve(BuildOutputImpl.fileName)) .writeAsString(_wrongContents); } diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output_2/pubspec.yaml b/pkgs/native_assets_builder/test_data/wrong_build_output_2/pubspec.yaml index 82cc12d17..bb3708d01 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output_2/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/wrong_build_output_2/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 native_assets_cli: path: ../../../native_assets_cli/ yaml: ^3.1.1 diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output_3/build.dart b/pkgs/native_assets_builder/test_data/wrong_build_output_3/build.dart index 33f9dfc01..1a660dc60 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output_3/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_build_output_3/build.dart @@ -7,8 +7,9 @@ import 'dart:io'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - await File.fromUri(buildConfig.outDir.resolve(BuildOutput.fileName)) + final buildConfig = BuildConfigImpl.fromArguments(args); + await File.fromUri( + buildConfig.outputDirectory.resolve(BuildOutputImpl.fileName)) .writeAsString(_rightContents); exit(1); } diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output_3/pubspec.yaml b/pkgs/native_assets_builder/test_data/wrong_build_output_3/pubspec.yaml index 630439123..e56d04670 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output_3/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/wrong_build_output_3/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 native_assets_cli: path: ../../../native_assets_cli/ yaml: ^3.1.1 diff --git a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/build.dart b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/build.dart index 457aa902b..17c7e0b43 100644 --- a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/build.dart @@ -4,21 +4,19 @@ import 'package:native_assets_cli/native_assets_cli.dart'; -void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput( - assets: [ - Asset( - id: 'package:other_package/foo', - linkMode: LinkMode.dynamic, - target: Target.current, - path: AssetAbsolutePath( - buildConfig.outDir.resolve( - Target.current.os.dylibFileName('foo'), - ), +void main(List arguments) async { + await build(arguments, (config, output) async { + output.addAsset( + NativeCodeAsset( + package: 'other_package', + name: 'foo', + file: config.outputDirectory.resolve( + OS.current.dylibFileName('foo'), ), + linkMode: DynamicLoadingBundled(), + os: OS.current, + architecture: Architecture.current, ), - ], - ); - await buildOutput.writeToFile(outDir: buildConfig.outDir); + ); + }); } diff --git a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/pubspec.yaml b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/pubspec.yaml index b422479d1..f48f8a63c 100644 --- a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/pubspec.yaml +++ b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 native_assets_cli: path: ../../../native_assets_cli/ yaml: ^3.1.1 diff --git a/pkgs/native_assets_cli/CHANGELOG.md b/pkgs/native_assets_cli/CHANGELOG.md index 0f77bcb16..858b731cf 100644 --- a/pkgs/native_assets_cli/CHANGELOG.md +++ b/pkgs/native_assets_cli/CHANGELOG.md @@ -1,6 +1,8 @@ -## 0.4.3-wip +## 0.5.0-wip -- Bump examples dependencies to path dependencies. (2x) +- **Breaking change** Completely rewritten API. + https://github.com/dart-lang/native/pull/946 +- Bump examples dependencies to path dependencies. ## 0.4.2 diff --git a/pkgs/native_assets_cli/example/local_asset/README.md b/pkgs/native_assets_cli/example/local_asset/README.md new file mode 100644 index 000000000..3ffe34caf --- /dev/null +++ b/pkgs/native_assets_cli/example/local_asset/README.md @@ -0,0 +1,5 @@ +This example shows two use cases: + +* Data assets that are part of the package source code. +* Code assets which are downloaded from the cloud. + (It doesn't show how to download, but shows how to implement the protocol correctly.) diff --git a/pkgs/native_assets_cli/example/local_asset/build.dart b/pkgs/native_assets_cli/example/local_asset/build.dart new file mode 100644 index 000000000..4dab00e6c --- /dev/null +++ b/pkgs/native_assets_cli/example/local_asset/build.dart @@ -0,0 +1,46 @@ +// 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'; + +import 'package:native_assets_cli/native_assets_cli.dart'; + +const assetName = 'asset.txt'; +final packageAssetPath = Uri.file('data/$assetName'); + +void main(List args) async { + await build(args, (config, output) async { + if (config.linkModePreference == LinkModePreference.static) { + // Simulate that this script only supports dynamic libraries. + throw UnsupportedError( + 'LinkModePreference.static is not supported.', + ); + } + + final packageName = config.packageName; + final assetPath = config.outputDirectory.resolve(assetName); + final assetSourcePath = config.packageRoot.resolveUri(packageAssetPath); + if (!config.dryRun) { + // Insert code that downloads or builds the asset to `assetPath`. + await File.fromUri(assetSourcePath).copy(assetPath.toFilePath()); + + output.addDependencies([ + assetSourcePath, + config.packageRoot.resolve('build.dart'), + ]); + } + + output.addAsset( + // TODO: Change to DataAsset once the Dart/Flutter SDK can consume it. + NativeCodeAsset( + package: packageName, + name: 'asset.txt', + file: assetPath, + linkMode: DynamicLoadingBundled(), + os: config.targetOS, + architecture: config.targetArchitecture, + ), + ); + }); +} diff --git a/pkgs/native_assets_cli/example/local_asset/data/asset.txt b/pkgs/native_assets_cli/example/local_asset/data/asset.txt new file mode 100644 index 000000000..0cdca5885 --- /dev/null +++ b/pkgs/native_assets_cli/example/local_asset/data/asset.txt @@ -0,0 +1 @@ +Some arbitrary contents diff --git a/pkgs/native_assets_cli/example/local_asset/pubspec.yaml b/pkgs/native_assets_cli/example/local_asset/pubspec.yaml new file mode 100644 index 000000000..392fe0021 --- /dev/null +++ b/pkgs/native_assets_cli/example/local_asset/pubspec.yaml @@ -0,0 +1,19 @@ +publish_to: none + +name: local_asset +description: Uses an asset local to the package. +version: 0.1.0 +repository: https://github.com/dart-lang/native/tree/main/pkgs/native_assets_cli/example/native_add_library + +environment: + sdk: '>=3.3.0 <4.0.0' + +dependencies: + logging: ^1.1.1 + native_assets_cli: + path: ../../../native_assets_cli/ + +dev_dependencies: + ffigen: ^8.0.2 + lints: ^3.0.0 + test: ^1.21.0 diff --git a/pkgs/native_assets_cli/example/native_add_app/pubspec.yaml b/pkgs/native_assets_cli/example/native_add_app/pubspec.yaml index 68f3d8e64..f6b2adc52 100644 --- a/pkgs/native_assets_cli/example/native_add_app/pubspec.yaml +++ b/pkgs/native_assets_cli/example/native_add_app/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0 repository: https://github.com/dart-lang/native/tree/main/pkgs/native_assets_cli/example/native_add_app environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: native_add_library: diff --git a/pkgs/native_assets_cli/example/native_add_library/build.dart b/pkgs/native_assets_cli/example/native_add_library/build.dart index ae39cdc1a..86ea4c888 100644 --- a/pkgs/native_assets_cli/example/native_add_library/build.dart +++ b/pkgs/native_assets_cli/example/native_add_library/build.dart @@ -6,34 +6,22 @@ import 'package:logging/logging.dart'; import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; -const packageName = 'native_add_library'; - -/// Implements the protocol from `package:native_assets_cli` by building -/// the C code in `src/` and reporting what native assets it built. void main(List args) async { - // Parse the build configuration passed to this CLI from Dart or Flutter. - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput(); - - // Configure `package:native_toolchain_c` to build the C code for us. - final cbuilder = CBuilder.library( - name: packageName, - assetId: 'package:$packageName/${packageName}.dart', - sources: [ - 'src/$packageName.c', - ], - ); - await cbuilder.run( - buildConfig: buildConfig, - // `package:native_toolchain_c` will output the dynamic or static libraries it built, - // what files it accessed (for caching the build), etc. - buildOutput: buildOutput, - logger: Logger('') - ..level = Level.ALL - ..onRecord.listen((record) => print(record.message)), - ); - - // Write the output according to the native assets protocol so that Dart or - // Flutter can find the native assets produced by this script. - await buildOutput.writeToFile(outDir: buildConfig.outDir); + await build(args, (config, output) async { + final packageName = config.packageName; + final cbuilder = CBuilder.library( + name: packageName, + assetName: '$packageName.dart', + sources: [ + 'src/$packageName.c', + ], + ); + await cbuilder.run( + buildConfig: config, + buildOutput: output, + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((record) => print(record.message)), + ); + }); } diff --git a/pkgs/native_assets_cli/example/native_add_library/pubspec.yaml b/pkgs/native_assets_cli/example/native_add_library/pubspec.yaml index cbf53ffc2..f80bf153b 100644 --- a/pkgs/native_assets_cli/example/native_add_library/pubspec.yaml +++ b/pkgs/native_assets_cli/example/native_add_library/pubspec.yaml @@ -6,10 +6,9 @@ version: 0.1.0 repository: https://github.com/dart-lang/native/tree/main/pkgs/native_assets_cli/example/native_add_library environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 logging: ^1.1.1 native_assets_cli: path: ../../../native_assets_cli/ diff --git a/pkgs/native_assets_cli/example/use_dart_api/build.dart b/pkgs/native_assets_cli/example/use_dart_api/build.dart index 10380ec58..a776b5e6d 100644 --- a/pkgs/native_assets_cli/example/use_dart_api/build.dart +++ b/pkgs/native_assets_cli/example/use_dart_api/build.dart @@ -8,25 +8,24 @@ import 'package:native_toolchain_c/native_toolchain_c.dart'; const packageName = 'use_dart_api'; -void main(List args) async { - final buildConfig = await BuildConfig.fromArgs(args); - final buildOutput = BuildOutput(); - final cbuilder = CBuilder.library( - name: packageName, - assetId: 'package:$packageName/src/${packageName}_bindings_generated.dart', - sources: [ - 'src/$packageName.c', - 'src/dart_api_dl.c', - ], - ); - await cbuilder.run( - buildConfig: buildConfig, - buildOutput: buildOutput, - logger: Logger('') - ..level = Level.ALL - ..onRecord.listen((record) { - print('${record.level.name}: ${record.time}: ${record.message}'); - }), - ); - await buildOutput.writeToFile(outDir: buildConfig.outDir); +void main(List arguments) async { + await build(arguments, (config, output) async { + final cbuilder = CBuilder.library( + name: packageName, + assetName: 'src/${packageName}_bindings_generated.dart', + sources: [ + 'src/$packageName.c', + 'src/dart_api_dl.c', + ], + ); + await cbuilder.run( + buildConfig: config, + buildOutput: output, + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((record) { + print('${record.level.name}: ${record.time}: ${record.message}'); + }), + ); + }); } diff --git a/pkgs/native_assets_cli/example/use_dart_api/pubspec.yaml b/pkgs/native_assets_cli/example/use_dart_api/pubspec.yaml index 31c94cb6c..6701efa43 100644 --- a/pkgs/native_assets_cli/example/use_dart_api/pubspec.yaml +++ b/pkgs/native_assets_cli/example/use_dart_api/pubspec.yaml @@ -5,10 +5,9 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 logging: ^1.1.1 native_assets_cli: path: ../../../native_assets_cli/ diff --git a/pkgs/native_assets_cli/lib/native_assets_cli.dart b/pkgs/native_assets_cli/lib/native_assets_cli.dart index b46aa7031..13e43029a 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli.dart @@ -6,13 +6,21 @@ /// native assets CLI, e.g. a `build.dart` script. library native_assets_cli; -export 'src/api/asset.dart'; -export 'src/api/build_config.dart'; -export 'src/api/build_mode.dart'; -export 'src/api/build_output.dart'; -export 'src/api/dependencies.dart'; -export 'src/api/ios_sdk.dart'; -export 'src/api/link_mode.dart'; -export 'src/api/link_mode_preference.dart'; -export 'src/api/metadata.dart'; -export 'src/api/target.dart'; +export 'src/api/architecture.dart' show Architecture; +export 'src/api/asset.dart' + show + Asset, + DynamicLoadingBundled, + DynamicLoadingSystem, + LinkMode, + LookupInExecutable, + LookupInProcess, + NativeCodeAsset, + StaticLinking; +export 'src/api/build.dart'; +export 'src/api/build_config.dart' show BuildConfig, CCompilerConfig; +export 'src/api/build_mode.dart' show BuildMode; +export 'src/api/build_output.dart' show BuildOutput; +export 'src/api/ios_sdk.dart' show IOSSdk; +export 'src/api/link_mode_preference.dart' show LinkModePreference; +export 'src/api/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 9bf58c114..a76b12210 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart @@ -2,19 +2,38 @@ // 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. -/// This library should not be used by `build.dart` scripts. +/// Internal API for invoking `build.dart` hooks. +/// +/// This library is intended for use by: +/// * `package:native_assets_builder`, +/// * `dartdev` (in the Dart SDK), and, +/// * `flutter_tools` (in the Flutter SDK). +/// +/// > [!CAUTION] +/// > Unless you are building a custom SDK that embeds Dart you should +/// > not be importing this library! /// -/// Only `package:native_assets_builder` should use this. /// @nodoc library native_assets_cli_internal; -export 'src/model/asset.dart'; -export 'src/model/build_config.dart'; -export 'src/model/build_mode.dart'; -export 'src/model/build_output.dart'; +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 'src/api/build_output.dart' show BuildOutputImpl; +export 'src/api/ios_sdk.dart' show IOSSdkImpl; +export 'src/api/link_mode_preference.dart' show LinkModePreferenceImpl; +export 'src/api/os.dart' show OSImpl; export 'src/model/dependencies.dart'; -export 'src/model/ios_sdk.dart'; -export 'src/model/link_mode.dart'; -export 'src/model/link_mode_preference.dart'; export 'src/model/metadata.dart'; export 'src/model/target.dart'; diff --git a/pkgs/native_assets_cli/lib/src/api/architecture.dart b/pkgs/native_assets_cli/lib/src/api/architecture.dart new file mode 100644 index 000000000..8c78531fe --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/api/architecture.dart @@ -0,0 +1,40 @@ +// 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 1d10d644c..00fcc9c4a 100644 --- a/pkgs/native_assets_cli/lib/src/api/asset.dart +++ b/pkgs/native_assets_cli/lib/src/api/asset.dart @@ -2,58 +2,66 @@ // 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 '../model/asset.dart' as model; -import '../model/link_mode.dart' as model; -import '../model/target.dart' as model; -import 'link_mode.dart'; -import 'target.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:yaml/yaml.dart'; -abstract class AssetPath {} +import '../model/target.dart'; +import '../utils/map.dart'; +import '../utils/yaml.dart'; +import 'architecture.dart'; +import 'build_config.dart'; +import 'build_output.dart'; +import 'os.dart'; -/// Asset at absolute path [uri]. -abstract class AssetAbsolutePath implements AssetPath { - Uri get uri; +part 'native_code_asset.dart'; +part 'data_asset.dart'; +part '../model/asset.dart'; +part '../model/native_code_asset.dart'; +part '../model/data_asset.dart'; - factory AssetAbsolutePath(Uri uri) = model.AssetAbsolutePath; -} - -/// Asset is avaliable on the system `PATH`. +/// Data or code bundled with a Dart or Flutter application. /// -/// [uri] only contains a file name. -abstract class AssetSystemPath implements AssetPath { - Uri get uri; - - factory AssetSystemPath(Uri uri) = model.AssetSystemPath; -} - -/// Asset is loaded in the process and symbols are available through -/// `DynamicLibrary.process()`. -abstract class AssetInProcess implements AssetPath { - factory AssetInProcess() = model.AssetInProcess; -} - -/// Asset is embedded in executable and symbols are available through -/// `DynamicLibrary.executable()`. -abstract class AssetInExecutable implements AssetPath { - factory AssetInExecutable() = model.AssetInExecutable; -} - -abstract class Asset { - LinkMode get linkMode; +/// An asset is data or code which is accessible from a Dart or Flutter +/// application. To access an asset at runtime, the asset [id] is used. +abstract final class Asset { + /// The identifier for this asset. + /// + /// An [Asset] has a string identifier called "asset id". Dart code that uses + /// an asset references the asset using this asset id. + /// + /// An asset identifier consists of two elements, the `package` and `name`, + /// which together make a library uri `package:/`. The package + /// being part of the identifer prevents name collisions between assets of + /// different packages. + /// + /// The default asset id for an asset reference from `lib/src/foo.dart` is + /// `'package:foo/src/foo.dart'`. For example a [NativeCodeAsset] can be accessed + /// via `@Native` with the `assetId` argument omitted: + /// + /// ```dart + /// // file package:foo/src/foo.dart + /// @Native() + /// external int add(int a, int b); + /// ``` + /// + /// This will be then automatically expanded to + /// + /// ```dart + /// // file package:foo/src/foo.dart + /// @Native(assetId: 'package:foo/src/foo.dart') + /// external int add(int a, int b); + /// ``` String get id; - Target get target; - AssetPath get path; - factory Asset({ - required String id, - required LinkMode linkMode, - required Target target, - required AssetPath path, - }) => - model.Asset( - id: id, - linkMode: linkMode as model.LinkMode, - target: target as model.Target, - path: path as model.AssetPath, - ); + /// The file to be bundled with the Dart or Flutter application. + /// + /// How this file is bundled depends on the kind of asset, represented by a + /// concrete subtype of [Asset], and the SDK (Dart or Flutter). + /// + /// The file can be omitted in the [BuildOutput] for [BuildConfig.dryRun]. + /// + /// The file can also be omitted for asset types which refer to an asset + /// already present on the target system or an asset already present in Dart + /// or Flutter. + Uri? get file; } diff --git a/pkgs/native_assets_cli/lib/src/api/build.dart b/pkgs/native_assets_cli/lib/src/api/build.dart new file mode 100644 index 000000000..b6e805b5f --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/api/build.dart @@ -0,0 +1,95 @@ +// 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 'build_config.dart'; +import 'build_output.dart'; + +/// Runs a native assets build. +/// +/// Can build native assets which are not already available, or expose existing +/// files. Each individual asset is assigned a unique asset ID. +/// +/// Example using `package:native_toolchain_c`: +/// +/// ```dart +/// import 'package:logging/logging.dart'; +/// import 'package:native_assets_cli/native_assets_cli.dart'; +/// import 'package:native_toolchain_c/native_toolchain_c.dart'; +/// +/// void main(List args) async { +/// await build(args, (config, output) async { +/// final packageName = config.packageName; +/// final cbuilder = CBuilder.library( +/// name: packageName, +/// assetName: '$packageName.dart', +/// sources: [ +/// 'src/$packageName.c', +/// ], +/// ); +/// await cbuilder.run( +/// buildConfig: config, +/// buildOutput: output, +/// logger: Logger('') +/// ..level = Level.ALL +/// ..onRecord.listen((record) => print(record.message)), +/// ); +/// }); +/// } +/// ``` +/// +/// Example outputting assets manually: +/// +/// ```dart +/// import 'dart:io'; +/// +/// import 'package:native_assets_cli/native_assets_cli.dart'; +/// +/// const assetName = 'asset.txt'; +/// final packageAssetPath = Uri.file('data/$assetName'); +/// +/// void main(List args) async { +/// await build(args, (config, output) async { +/// if (config.linkModePreference == LinkModePreference.static) { +/// // Simulate that this script only supports dynamic libraries. +/// throw UnsupportedError( +/// 'LinkModePreference.static is not supported.', +/// ); +/// } +/// +/// final packageName = config.packageName; +/// final assetPath = config.outputDirectory.resolve(assetName); +/// final assetSourcePath = config.packageRoot.resolveUri(packageAssetPath); +/// if (!config.dryRun) { +/// // Insert code that downloads or builds the asset to `assetPath`. +/// await File.fromUri(assetSourcePath).copy(assetPath.toFilePath()); +/// +/// output.addDependencies([ +/// assetSourcePath, +/// config.packageRoot.resolve('build.dart'), +/// ]); +/// } +/// +/// output.addAsset( +/// // TODO: Change to DataAsset once the Dart/Flutter SDK can consume it. +/// NativeCodeAsset( +/// package: packageName, +/// name: 'asset.txt', +/// file: assetPath, +/// linkMode: DynamicLoadingBundled(), +/// os: config.targetOS, +/// architecture: config.targetArchitecture, +/// ), +/// ); +/// }); +/// } +/// ``` +Future build( + List arguments, + Future Function(BuildConfig config, BuildOutput output) builder, +) async { + final config = BuildConfig(arguments); + final output = BuildOutputImpl(); + await builder(config, output); + await output.writeToFile(config: config); +} 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 b9de44e64..715439cd9 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/build_config.dart @@ -2,26 +2,40 @@ // 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:convert'; import 'dart:io'; import 'package:cli_config/cli_config.dart'; +import 'package:collection/collection.dart'; +import 'package:crypto/crypto.dart'; import 'package:pub_semver/pub_semver.dart'; -import '../model/build_config.dart' as model; -import '../model/build_mode.dart' as model; -import '../model/ios_sdk.dart' as model; -import '../model/link_mode_preference.dart' as model; -import '../model/target.dart' as model; +import '../model/metadata.dart'; +import '../model/target.dart'; +import '../utils/map.dart'; +import '../utils/yaml.dart'; +import 'architecture.dart'; +import 'asset.dart'; +import 'build.dart'; import 'build_mode.dart'; import 'ios_sdk.dart'; import 'link_mode_preference.dart'; -import 'metadata.dart'; -import 'target.dart'; - -abstract class BuildConfig { - /// The folder in which all output and intermediate artifacts should be +import 'os.dart'; + +part 'c_compiler_config.dart'; +part '../model/build_config.dart'; +part '../model/c_compiler_config.dart'; + +/// The configuration for a `build.dart` invocation. +/// +/// A package can choose to have a toplevel `build.dart` script. If such a +/// script exists, it will be automatically run, by the Flutter and Dart SDK +/// tools. The script will then be run with specific commandline arguments, +/// which [BuildConfig] can parse and provide more convenient access to. +abstract final class BuildConfig { + /// The directory in which all output and intermediate artifacts should be /// placed. - Uri get outDir; + Uri get outputDirectory; /// The name of the package the native assets are built for. String get packageName; @@ -32,168 +46,178 @@ abstract class BuildConfig { /// dependency of another. For this it is convenient to know the packageRoot. Uri get packageRoot; - /// The target being compiled for. - /// - /// Not available in [dryRun]. - Target get target; - /// The architecture being compiled for. /// - /// Not available in [dryRun]. - Architecture get targetArchitecture; + /// Not specified (`null`) during a [dryRun]. + Architecture? get targetArchitecture; /// The operating system being compiled for. - OS get targetOs; + OS get targetOS; /// When compiling for iOS, whether to target device or simulator. /// - /// Required when [targetOs] equals [OS.iOS]. + /// Not available if [targetOS] is [OS.iOS]. Will throw a [StateError] if + /// accessed during a [dryRun]. /// - /// Not available in [dryRun].s - IOSSdk? get targetIOSSdk; + /// Not available during a [dryRun]. Will throw a [StateError] if accessed + /// during a [dryRun]. + IOSSdk get targetIOSSdk; /// When compiling for Android, the minimum Android SDK API version to that /// the compiled code will be compatible with. /// - /// Required when [targetOs] equals [OS.android]. + /// Required when [targetOS] equals [OS.android]. /// - /// Not available in [dryRun]. + /// Not available during a [dryRun]. Will throw a [StateError] if accessed + /// during a [dryRun]. /// /// For more information about the Android API version, refer to /// [`minSdkVersion`](https://developer.android.com/ndk/guides/sdk-versions#minsdkversion) /// in the Android documentation. int? get targetAndroidNdkApi; - /// Preferred linkMode method for library. + /// The preferred [LinkMode] method for [NativeCodeAsset]s. LinkModePreference get linkModePreference; - /// Metadata from direct dependencies. + /// Metadata from a direct dependency. /// - /// The key in the map is the package name of the dependency. + /// The [packageName] of is the package name of the direct dependency. /// - /// The key in the nested map is the key for the metadata from the dependency. + /// Returns `null` if metadata was not provided. /// - /// Not available in [dryRun]. - Map? get dependencyMetadata; + /// Not available during a [dryRun]. Will throw a [StateError] if accessed + /// during a [dryRun]. + Object? metadatum(String packageName, String key); /// The configuration for invoking the C compiler. /// - /// Not available in [dryRun]. + /// Not available during a [dryRun]. Will throw a [StateError] if accessed + /// during a [dryRun]. CCompilerConfig get cCompiler; - /// Don't run the build, only report the native assets produced. + /// Whether this run is a dry-run, which doesn't build anything. + /// + /// A dry-run only reports information about which assets a build would + /// create, but doesn't actually create files. bool get dryRun; - /// The build mode that the code should be compiled in. + /// The [BuildMode] that the code should be compiled in. /// - /// Not available in [dryRun]. + /// Currently [BuildMode.debug] and [BuildMode.release] are the only modes. + /// + /// Not available during a [dryRun]. Will throw a [StateError] if accessed + /// during a [dryRun]. BuildMode get buildMode; - /// The underlying config. + /// The asset types that the invoker of this build supports. /// - /// Can be used for easier access to values on [dependencyMetadata]. - Config get config; + /// Currently known values: + /// * [NativeCodeAsset.type] + /// * [DataAsset.type] + Iterable get supportedAssetTypes; /// The version of [BuildConfig]. /// - /// This class is used in the protocol between the Dart and Flutter SDKs + /// The build config is used in the protocol between the Dart and Flutter SDKs /// and packages through `build.dart` invocations. /// - /// If we ever were to make breaking changes, it would be useful to give - /// proper error messages rather than just fail to parse the YAML - /// representation in the protocol. - static Version get version => model.BuildConfig.version; + /// We're trying to avoid breaking changes. However, in the case that we have + /// to, the major version mismatch between the Dart or Flutter SDK and a + /// `build.dart` script will lead to a nice error message. + static Version get latestVersion => BuildConfigImpl.latestVersion; - factory BuildConfig({ - required Uri outDir, + /// Constructs a config by parsing CLI arguments and loading the config file. + /// + /// `build.dart` hooks will most likely use [build] instead of this + /// constructor. + /// + /// The [arguments] must be commandline arguments. + /// + /// If provided, [environment] must be a map containing environment variables. + /// If not provided, [environment] defaults to [Platform.environment]. + /// + /// If provided, [workingDirectory] is used to resolves paths inside + /// [environment]. + /// If not provided, [workingDirectory] defaults to [Directory.current]. + /// + /// This async constructor is intended to be used directly in CLI files. + factory BuildConfig( + List arguments, { + Map? environment, + Uri? workingDirectory, + }) => + BuildConfigImpl.fromArguments( + arguments, + environment: environment, + workingDirectory: workingDirectory, + ); + + /// Constructs a config for a non-dry run by providing values for each field. + /// + /// `build.dart` hooks will most likely use [build] instead of this + /// constructor. However, for unit testing code which consumes a + /// [BuildConfig], this constructor facilitates easy construction. + /// + /// For the documentation of the parameters, see the equally named fields. + /// + /// Parameter [dependencyMetadata] must be a nested map `{'packageName' : + /// {'key' : 'value'}}` where `packageName` and `key` correspond to the + /// parameters in [metadatum]. + factory BuildConfig.build({ + required Uri outputDirectory, required String packageName, required Uri packageRoot, required BuildMode buildMode, required Architecture targetArchitecture, - required OS targetOs, + required OS targetOS, IOSSdk? targetIOSSdk, int? targetAndroidNdkApi, CCompilerConfig? cCompiler, required LinkModePreference linkModePreference, - Map? dependencyMetadata, + Map>? dependencyMetadata, + Iterable? supportedAssetTypes, }) => - model.BuildConfig( - outDir: outDir, + BuildConfigImpl( + outDir: outputDirectory, packageName: packageName, packageRoot: packageRoot, - buildMode: buildMode as model.BuildMode, - targetArchitecture: targetArchitecture as model.Architecture, - targetOs: targetOs as model.OS, - targetIOSSdk: targetIOSSdk as model.IOSSdk?, + buildMode: buildMode as BuildModeImpl, + targetArchitecture: targetArchitecture as ArchitectureImpl, + targetOS: targetOS as OSImpl, + targetIOSSdk: targetIOSSdk as IOSSdkImpl?, targetAndroidNdkApi: targetAndroidNdkApi, - cCompiler: cCompiler as model.CCompilerConfig?, - linkModePreference: linkModePreference as model.LinkModePreference, - dependencyMetadata: dependencyMetadata?.cast(), + cCompiler: cCompiler as CCompilerConfigImpl?, + linkModePreference: linkModePreference as LinkModePreferenceImpl, + dependencyMetadata: dependencyMetadata != null + ? { + for (final entry in dependencyMetadata.entries) + entry.key: Metadata(entry.value.cast()) + } + : {}, + supportedAssetTypes: supportedAssetTypes, ); + /// Constructs a config for a dry run by providing values for each field. + /// + /// `build.dart` hooks will most likely use [build] instead of this + /// constructor. However, for unit testing code which consumes a + /// [BuildConfig], this constructor facilitates easy construction. + /// + /// For the documentation of the parameters, see the equally named fields. factory BuildConfig.dryRun({ required Uri outDir, required String packageName, required Uri packageRoot, - required OS targetOs, + required OS targetOS, required LinkModePreference linkModePreference, + Iterable? supportedAssetTypes, }) => - model.BuildConfig.dryRun( + BuildConfigImpl.dryRun( outDir: outDir, packageName: packageName, packageRoot: packageRoot, - targetOs: targetOs as model.OS, - linkModePreference: linkModePreference as model.LinkModePreference, - ); - - factory BuildConfig.fromConfig(Config config) => - model.BuildConfig.fromConfig(config); - - /// Constructs a config by parsing CLI arguments and loading the config file. - /// - /// The [args] must be commandline arguments. - /// - /// If provided, [environment] must be a map containing environment variables. - /// If not provided, [environment] defaults to [Platform.environment]. - /// - /// If provided, [workingDirectory] is used to resolves paths inside - /// [environment]. - /// If not provided, [workingDirectory] defaults to [Directory.current]. - /// - /// This async constructor is intended to be used directly in CLI files. - static Future fromArgs( - List args, { - Map? environment, - Uri? workingDirectory, - }) => - model.BuildConfig.fromArgs( - args, - environment: environment, - workingDirectory: workingDirectory, + targetOS: targetOS as OSImpl, + linkModePreference: linkModePreference as LinkModePreferenceImpl, + supportedAssetTypes: supportedAssetTypes, ); } - -abstract class CCompilerConfig { - /// Path to a C compiler. - Uri? get cc; - - /// Path to a native linker. - Uri? get ld; - - /// Path to a native archiver. - Uri? get ar; - - /// Path to script that sets environment variables for [cc], [ld], and [ar]. - Uri? get envScript; - - /// Arguments for [envScript]. - List? get envScriptArgs; - - factory CCompilerConfig({ - Uri? ar, - Uri? cc, - Uri? ld, - Uri? envScript, - List? envScriptArgs, - }) = model.CCompilerConfig; -} diff --git a/pkgs/native_assets_cli/lib/src/api/build_mode.dart b/pkgs/native_assets_cli/lib/src/api/build_mode.dart index f3efc3efb..1e8e636f6 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_mode.dart +++ b/pkgs/native_assets_cli/lib/src/api/build_mode.dart @@ -2,14 +2,36 @@ // 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 '../model/build_mode.dart' as model; +part '../model/build_mode.dart'; -abstract class BuildMode { +/// The build mode for compiling native code assets. +/// +/// The Dart SDK does not have build modes. All `build.dart` invocations +/// are invoked with [release]. +/// +/// The Flutter SDK build modes map as the following to `build.dart` build +/// modes: +/// * Flutter debug -> [debug]. +/// * Flutter release -> [release]. +/// * Flutter profile -> [release]. +/// * Flutter jit release -> [release]. +abstract final class BuildMode { + /// The name for this build mode. String get name; - static const BuildMode debug = model.BuildMode.debug; - static const BuildMode release = model.BuildMode.release; + /// The debug build mode. + /// + /// Used by the Flutter SDK in its debug mode. + static const BuildMode debug = BuildModeImpl.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; + + /// All known build modes. static const values = [ debug, release, 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 8c93ffb11..e7004a413 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_output.dart +++ b/pkgs/native_assets_cli/lib/src/api/build_output.dart @@ -4,48 +4,113 @@ import 'dart:io'; +import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; +import 'package:yaml/yaml.dart'; -import '../model/asset.dart' as model; -import '../model/build_output.dart' as model; -import '../model/dependencies.dart' as model; -import '../model/metadata.dart' as model; +import '../model/dependencies.dart'; +import '../model/metadata.dart'; +import '../utils/datetime.dart'; +import '../utils/file.dart'; +import '../utils/map.dart'; +import '../utils/yaml.dart'; +import 'architecture.dart'; import 'asset.dart'; -import 'dependencies.dart'; -import 'metadata.dart'; +import 'build_config.dart'; +import 'os.dart'; -abstract class BuildOutput { - /// Time the build this output belongs to started. +part '../model/build_output.dart'; + +/// The output of a `build.dart` invocation. +/// +/// A package can have a toplevel `build.dart` script. If such a script exists, +/// it will be automatically run, by the Flutter and Dart SDK tools. The script +/// is expect to produce a specific output which [BuildOutput] can produce. +abstract final class BuildOutput { + /// Start time for the build of this output. /// - /// Rounded down to whole seconds, because [File.lastModified] is rounded - /// to whole seconds and caching logic compares these timestamps. + /// The [timestamp] is rounded down to whole seconds, because + /// [File.lastModified] is rounded to whole seconds and caching logic compares + /// these timestamps. DateTime get timestamp; - List get assets; - Dependencies get dependencies; - Metadata get metadata; + /// The assets produced by this build. + /// + /// In dry runs, the assets for all [Architecture]s for the [OS] specified in + /// the dry run must be provided. + Iterable get assets; + + /// The files used by this build. + /// + /// If any of the files in [dependencies] are modified after [timestamp], the + /// build will be re-run. + Iterable get dependencies; + + /// Create a build output. + /// + /// The [timestamp] must be before any [dependencies] are read by the build + /// this output belongs to. If the [BuildOutput] object is created at the + /// beginning of the `build.dart` script, [timestamp] can be omitted and will + /// default to [DateTime.now]. The [timestamp] is rounded down to whole + /// seconds, because [File.lastModified] is rounded to whole seconds and + /// caching logic compares these timestamps. + /// + /// The [Asset]s produced by this build or dry-run can be provided to the + /// constructor as [assets], or can be added later using [addAsset] and + /// [addAssets]. In dry runs, the [Architecture] for [NativeCodeAsset]s can be + /// omitted. + /// + /// The files used by this build must be provided to the constructor as + /// [dependencies], or can be added later with [addDependency] and + /// [addDependencies]. If any of these files are modified after [timestamp], + /// the build will be re-run. Typically these dependencies contain the + /// `build.dart` script itself, and the source files used in the build. + /// + /// Metadata can be passed to `build.dart` invocations of dependent packages. + /// It must be provided to the constructor as [metadata], or added later with + /// [addMetadatum] and [addMetadata]. factory BuildOutput({ DateTime? timestamp, - List? assets, - Dependencies? dependencies, - Metadata? metadata, + Iterable? assets, + Iterable? dependencies, + Map? metadata, }) => - model.BuildOutput( + BuildOutputImpl( timestamp: timestamp, - assets: assets?.map((e) => e as model.Asset).toList(), - dependencies: dependencies as model.Dependencies?, - metadata: metadata as model.Metadata?, + assets: assets?.cast().toList(), + dependencies: Dependencies([...?dependencies]), + metadata: Metadata({...?metadata}), ); - /// The version of [BuildOutput]. + /// Adds [Asset]s produced by this build or dry run. + void addAsset(Asset asset); + + /// Adds [Asset]s produced by this build or dry run. + void addAssets(Iterable assets); + + /// Adds file used by this build. /// - /// This class is used in the protocol between the Dart and Flutter SDKs - /// and packages through `build.dart` invocations. + /// If any of the files are modified after [timestamp], the build will be + /// re-run. + void addDependency(Uri dependency); + + /// Adds files used by this build. /// - /// If we ever were to make breaking changes, it would be useful to give - /// proper error messages rather than just fail to parse the YAML - /// representation in the protocol. - static Version get version => model.BuildOutput.version; + /// If any of the files are modified after [timestamp], the build will be + /// re-run. + void addDependencies(Iterable dependencies); + + /// Adds metadata to be passed to `build.dart` invocations of dependent + /// packages. + void addMetadatum(String key, Object value); - Future writeToFile({required Uri outDir}); + /// Adds metadata to be passed to `build.dart` invocations of dependent + /// packages. + void addMetadata(Map metadata); + + /// The version of [BuildOutput]. + /// + /// The build output is used in the protocol between the Dart and Flutter SDKs + /// and packages through `build.dart` invocations. + static Version get latestVersion => BuildOutputImpl.latestVersion; } 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 new file mode 100644 index 000000000..30dc67ac4 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/api/c_compiler_config.dart @@ -0,0 +1,32 @@ +// 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/data_asset.dart b/pkgs/native_assets_cli/lib/src/api/data_asset.dart new file mode 100644 index 000000000..6726cf583 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/api/data_asset.dart @@ -0,0 +1,31 @@ +// 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 'asset.dart'; + +/// Data bundled with a Dart or Flutter application. +/// +/// A data asset is accessible in a Dart or Flutter application. To retrieve an +/// asset at runtime, the [id] is used. This enables access to the asset +/// irrespective of how and where the application is run. +/// +/// An data asset must provide a [Asset.file]. The Dart and Flutter SDK will +/// bundle this code in the final application. +abstract final class DataAsset implements Asset { + /// Constructs a data asset. + /// + /// The [id] of this asset is a uri `package:/` from [package] + /// and [name]. + factory DataAsset({ + required String package, + required String name, + required Uri file, + }) => + DataAssetImpl( + id: 'package:$package/$name', + file: file, + ); + + static const String type = 'data'; +} diff --git a/pkgs/native_assets_cli/lib/src/api/dependencies.dart b/pkgs/native_assets_cli/lib/src/api/dependencies.dart deleted file mode 100644 index 5c8763b91..000000000 --- a/pkgs/native_assets_cli/lib/src/api/dependencies.dart +++ /dev/null @@ -1,12 +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 '../model/dependencies.dart' as model; - -abstract class Dependencies { - /// The dependencies a build relied on. - List get dependencies; - - const factory Dependencies(List dependencies) = model.Dependencies; -} diff --git a/pkgs/native_assets_cli/lib/src/api/ios_sdk.dart b/pkgs/native_assets_cli/lib/src/api/ios_sdk.dart index b76afbd09..a949f226f 100644 --- a/pkgs/native_assets_cli/lib/src/api/ios_sdk.dart +++ b/pkgs/native_assets_cli/lib/src/api/ios_sdk.dart @@ -2,17 +2,25 @@ // 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 '../model/ios_sdk.dart' as model; +part '../model/ios_sdk.dart'; /// For an iOS target, a build is either done for the device or the simulator. -/// -/// Only fat binaries or xcframeworks can contain both targets. -abstract class IOSSdk { - static const IOSSdk iPhoneOs = model.IOSSdk.iPhoneOs; - static const IOSSdk iPhoneSimulator = model.IOSSdk.iPhoneSimulator; +abstract final class IOSSdk { + /// 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; + /// 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; + + /// All known values for [IOSSdk]. static const values = [ - iPhoneOs, + iPhoneOS, iPhoneSimulator, ]; } diff --git a/pkgs/native_assets_cli/lib/src/api/link_mode.dart b/pkgs/native_assets_cli/lib/src/api/link_mode.dart deleted file mode 100644 index d389cf751..000000000 --- a/pkgs/native_assets_cli/lib/src/api/link_mode.dart +++ /dev/null @@ -1,16 +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 '../model/link_mode.dart' as model; - -abstract class LinkMode { - static const LinkMode dynamic = model.LinkMode.dynamic; - static const LinkMode static = model.LinkMode.static; - - /// Known values for [LinkMode]. - static const List values = [ - dynamic, - static, - ]; -} diff --git a/pkgs/native_assets_cli/lib/src/api/link_mode_preference.dart b/pkgs/native_assets_cli/lib/src/api/link_mode_preference.dart index 8224e4d40..f600704a1 100644 --- a/pkgs/native_assets_cli/lib/src/api/link_mode_preference.dart +++ b/pkgs/native_assets_cli/lib/src/api/link_mode_preference.dart @@ -2,34 +2,33 @@ // 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 '../model/link_mode_preference.dart' as model; -import 'link_mode.dart'; +import 'asset.dart'; -abstract class LinkModePreference { - String get name; - - LinkMode get preferredLinkMode; +part '../model/link_mode_preference.dart'; - List get potentialLinkMode; +/// The preferred linkMode method for [NativeCodeAsset]s. +abstract final class LinkModePreference { + /// The name for this link mode. + String get 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 = model.LinkModePreference.dynamic; + static const LinkModePreference dynamic = LinkModePreferenceImpl.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 = model.LinkModePreference.static; + static const LinkModePreference static = LinkModePreferenceImpl.static; /// Provide native assets as dynamic libraries, if possible. /// /// Otherwise, build native assets as static libraries static const LinkModePreference preferDynamic = - model.LinkModePreference.preferDynamic; + LinkModePreferenceImpl.preferDynamic; /// Provide native assets as static libraries, if possible. /// @@ -37,12 +36,5 @@ abstract class LinkModePreference { /// compilation, if there are any native assets which can only be provided as /// dynamic libraries. static const LinkModePreference preferStatic = - model.LinkModePreference.preferStatic; - - static const values = [ - dynamic, - static, - preferDynamic, - preferStatic, - ]; + LinkModePreferenceImpl.preferStatic; } diff --git a/pkgs/native_assets_cli/lib/src/api/metadata.dart b/pkgs/native_assets_cli/lib/src/api/metadata.dart deleted file mode 100644 index 234e653a0..000000000 --- a/pkgs/native_assets_cli/lib/src/api/metadata.dart +++ /dev/null @@ -1,11 +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 '../model/metadata.dart' as model; - -abstract class Metadata { - Map get metadata; - - const factory Metadata(Map metadata) = model.Metadata; -} 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 new file mode 100644 index 000000000..171af2fa0 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/api/native_code_asset.dart @@ -0,0 +1,154 @@ +// 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 'asset.dart'; + +/// A code [Asset] which respects the native application binary interface (ABI). +/// +/// Typical languages which produce code assets that respect the native ABI +/// include C, C++ (with `extern "C"`), Rust (with `extern "C"`), and a subset +/// of language features of Objective-C. +/// +/// Native code assets can be accessed at runtime through native external +/// functions via their asset [id]: +/// +/// ```dart +/// import 'dart:ffi'; +/// +/// void main() { +/// final result = add(14, 28); +/// print(result); +/// } +/// +/// @Native(assetId: 'package:my_package/add.dart') +/// external int add(int a, int b); +/// ``` +/// +/// There are several types of native code assets: +/// * Assets which designate symbols present in the target system +/// ([DynamicLoadingSystem]), process ([LookupInProcess]), or executable +/// ([LookupInExecutable]). These assets do not have a [file]. +/// * Dynamic libraries bundled into the application +/// ([DynamicLoadingBundled]). These assets must provide a [file] to be +/// bundled. +/// +/// An application is compiled to run on a specific target [os] and +/// [architecture]. Different targets require different assets, so the package +/// developer must specify which asset to bundle for which target. +/// +/// An asset has different ways of being accessible in the final application. It +/// is either brought in "manually" by having the package developer specify a +/// [file] path of the asset on the current system, it can be part of the Dart +/// or Flutter SDK ([LookupInProcess]), or it can be already present in the +/// target system ([DynamicLoadingSystem]). If the asset is bundled +/// "manually", the Dart or Flutter SDK will take care of copying the asset +/// [file] from its specified location on the current system into the +/// application bundle. +abstract final class NativeCodeAsset implements Asset { + /// The operating system this asset can run on. + OS get os; + + /// The architecture this asset can run on. + /// + /// Not available during a [BuildConfig.dryRun]. + Architecture? get architecture; + + /// The link mode for this native code. + /// + /// Either dynamic loading or static linking. + LinkMode get linkMode; + + /// Constructs a native code asset. + /// + /// The [id] of this asset is a uri `package:/` from [package] + /// and [name]. + factory NativeCodeAsset({ + required String package, + required String name, + required LinkMode linkMode, + required OS os, + Uri? file, + Architecture? architecture, + }) => + NativeCodeAssetImpl( + id: 'package:$package/$name', + linkMode: linkMode as LinkModeImpl, + os: os as OSImpl, + architecture: architecture as ArchitectureImpl?, + 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 {} + +/// 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 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 [Asset.file]. The +/// Dart and Flutter SDK will bundle this code in the final application. +abstract final class DynamicLoadingBundled implements DynamicLoading { + factory DynamicLoadingBundled() = DynamicLoadingBundledImpl; +} + +/// 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; + + factory DynamicLoadingSystem(Uri uri) = DynamicLoadingSystemImpl; +} + +/// 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 native code is embedded in executable and symbols are available through +/// `DynamicLibrary.executable()`. +abstract final class LookupInExecutable implements DynamicLoading { + factory LookupInExecutable() = LookupInExecutableImpl; +} + +/// 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; +} diff --git a/pkgs/native_assets_cli/lib/src/api/os.dart b/pkgs/native_assets_cli/lib/src/api/os.dart new file mode 100644 index 000000000..b4b301fa2 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/api/os.dart @@ -0,0 +1,58 @@ +// 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/target.dart b/pkgs/native_assets_cli/lib/src/api/target.dart deleted file mode 100644 index 721a52fb3..000000000 --- a/pkgs/native_assets_cli/lib/src/api/target.dart +++ /dev/null @@ -1,134 +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:io'; - -import '../model/target.dart' as model; -import 'link_mode.dart'; - -/// The hardware architectures the Dart VM runs on. -abstract class Architecture { - static const Architecture arm = model.Architecture.arm; - static const Architecture arm64 = model.Architecture.arm64; - static const Architecture ia32 = model.Architecture.ia32; - static const Architecture riscv32 = model.Architecture.riscv32; - static const Architecture riscv64 = model.Architecture.riscv64; - static const Architecture x64 = model.Architecture.x64; - - /// Known values for [Architecture]. - static const List values = [ - arm, - arm64, - ia32, - riscv32, - riscv64, - x64, - ]; - - /// The current [Architecture]. - /// - /// Read from the [Platform.version] string. - static Architecture get current => model.Architecture.current; -} - -/// The operating systems the Dart VM runs on. -abstract class OS { - static const OS android = model.OS.android; - static const OS fuchsia = model.OS.fuchsia; - static const OS iOS = model.OS.iOS; - static const OS linux = model.OS.linux; - static const OS macOS = model.OS.macOS; - static const OS windows = model.OS.windows; - - /// Known values for [OS]. - static const List values = [ - android, - fuchsia, - iOS, - linux, - macOS, - windows, - ]; - - /// 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); - - String libraryFileName(String name, LinkMode linkMode); - - /// The default executable file name on this [OS]. - String executableFileName(String name); - - /// The current [OS]. - /// - /// Read from the [Platform.version] string. - static OS get current => model.OS.current; -} - -/// Application binary interface. -/// -/// The Dart VM can run on a variety of [Target]s, see [Target.values]. -abstract class Target implements Comparable { - static const Target androidArm = model.Target.androidArm; - static const Target androidArm64 = model.Target.androidArm64; - static const Target androidIA32 = model.Target.androidIA32; - static const Target androidX64 = model.Target.androidX64; - static const Target androidRiscv64 = model.Target.androidRiscv64; - static const Target fuchsiaArm64 = model.Target.fuchsiaArm64; - static const Target fuchsiaX64 = model.Target.fuchsiaX64; - static const Target iOSArm = model.Target.iOSArm; - static const Target iOSArm64 = model.Target.iOSArm64; - static const Target iOSX64 = model.Target.iOSX64; - static const Target linuxArm = model.Target.linuxArm; - static const Target linuxArm64 = model.Target.linuxArm64; - static const Target linuxIA32 = model.Target.linuxIA32; - static const Target linuxRiscv32 = model.Target.linuxRiscv32; - static const Target linuxRiscv64 = model.Target.linuxRiscv64; - static const Target linuxX64 = model.Target.linuxX64; - static const Target macOSArm64 = model.Target.macOSArm64; - static const Target macOSX64 = model.Target.macOSX64; - static const Target windowsArm64 = model.Target.windowsArm64; - static const Target windowsIA32 = model.Target.windowsIA32; - static const Target windowsX64 = model.Target.windowsX64; - - /// All Targets that native assets can be built for. - /// - /// Note that for some of these a Dart SDK is not available and they are only - /// used as target architectures for Flutter apps. - static const values = { - androidArm, - androidArm64, - androidIA32, - androidX64, - androidRiscv64, - fuchsiaArm64, - fuchsiaX64, - iOSArm, - iOSArm64, - iOSX64, - linuxArm, - linuxArm64, - linuxIA32, - linuxRiscv32, - linuxRiscv64, - linuxX64, - macOSArm64, - macOSX64, - windowsArm64, - windowsIA32, - windowsX64, - // TODO(dacoharkes): Add support for `wasm`. - }; - - /// The current [Target]. - /// - /// Read from the [Platform.version] string. - static Target get current => model.Target.current; - - Architecture get architecture; - - OS get os; -} diff --git a/pkgs/native_assets_cli/lib/src/model/architecture.dart b/pkgs/native_assets_cli/lib/src/model/architecture.dart new file mode 100644 index 000000000..384eb5487 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/model/architecture.dart @@ -0,0 +1,68 @@ +// 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/asset.dart b/pkgs/native_assets_cli/lib/src/model/asset.dart index babbed31a..d8f8ea19f 100644 --- a/pkgs/native_assets_cli/lib/src/model/asset.dart +++ b/pkgs/native_assets_cli/lib/src/model/asset.dart @@ -1,219 +1,27 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// 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:yaml/yaml.dart'; - -import '../api/asset.dart' as api; -import '../utils/yaml.dart'; -import 'link_mode.dart'; -import 'target.dart'; - -abstract class AssetPath implements api.AssetPath { - factory AssetPath(String pathType, Uri? uri) { - switch (pathType) { - case AssetAbsolutePath._pathTypeValue: - return AssetAbsolutePath(uri!); - case AssetSystemPath._pathTypeValue: - return AssetSystemPath(uri!); - case AssetInExecutable._pathTypeValue: - return AssetInExecutable(); - case AssetInProcess._pathTypeValue: - return AssetInProcess(); - } - throw FormatException('Unknown pathType: $pathType.'); - } - - factory AssetPath.fromYaml(YamlMap yamlMap) { - final pathType = as(yamlMap[_pathTypeKey]); - final uriString = as(yamlMap[_uriKey]); - final uri = uriString != null ? Uri(path: uriString) : null; - return AssetPath(pathType, uri); - } - - Map toYaml(); - - static const _pathTypeKey = 'path_type'; - static const _uriKey = 'uri'; -} - -/// Asset at absolute path [uri]. -class AssetAbsolutePath implements AssetPath, api.AssetAbsolutePath { - @override - final Uri uri; - - AssetAbsolutePath(this.uri); - - static const _pathTypeValue = 'absolute'; - - @override - Map toYaml() => { - AssetPath._pathTypeKey: _pathTypeValue, - AssetPath._uriKey: uri.toFilePath(), - }; - - @override - int get hashCode => Object.hash(uri, 133711); - - @override - bool operator ==(Object other) { - if (other is! AssetAbsolutePath) { - return false; - } - return uri == other.uri; - } -} - -/// Asset is avaliable on the system `PATH`. -/// -/// [uri] only contains a file name. -class AssetSystemPath implements AssetPath, api.AssetSystemPath { - @override - final Uri uri; - - AssetSystemPath(this.uri); - - static const _pathTypeValue = 'system'; - - @override - Map toYaml() => { - AssetPath._pathTypeKey: _pathTypeValue, - AssetPath._uriKey: uri.toFilePath(), - }; - - @override - int get hashCode => Object.hash(uri, 133723); - - @override - bool operator ==(Object other) { - if (other is! AssetSystemPath) { - return false; - } - return uri == other.uri; - } -} - -/// Asset is loaded in the process and symbols are available through -/// `DynamicLibrary.process()`. -class AssetInProcess implements AssetPath, api.AssetInProcess { - AssetInProcess._(); - - static final AssetInProcess _singleton = AssetInProcess._(); - - factory AssetInProcess() => _singleton; - - static const _pathTypeValue = 'process'; - - @override - Map toYaml() => { - AssetPath._pathTypeKey: _pathTypeValue, - }; -} - -/// Asset is embedded in executable and symbols are available through -/// `DynamicLibrary.executable()`. -class AssetInExecutable implements AssetPath, api.AssetInExecutable { - AssetInExecutable._(); - - static final AssetInExecutable _singleton = AssetInExecutable._(); - - factory AssetInExecutable() => _singleton; - - static const _pathTypeValue = 'executable'; - - @override - Map toYaml() => { - AssetPath._pathTypeKey: _pathTypeValue, - }; -} - -class Asset implements api.Asset { - @override - final LinkMode linkMode; - @override - final String id; - @override - final Target target; - @override - final AssetPath path; - - Asset({ - required this.id, - required this.linkMode, - required this.target, - required this.path, - }); - - factory Asset.fromYaml(YamlMap yamlMap) => Asset( - id: as(yamlMap[_idKey]), - path: AssetPath.fromYaml(as(yamlMap[_pathKey])), - target: Target.fromString(as(yamlMap[_targetKey])), - linkMode: LinkMode.fromName(as(yamlMap[_linkModeKey])), - ); - - static List listFromYamlString(String yaml) { - final yamlObject = loadYaml(yaml); - if (yamlObject == null) { - return []; +part of '../api/asset.dart'; + +abstract final class AssetImpl implements Asset { + Map toYaml(Version version); + + static List listFromYamlList(YamlList list) { + final assets = []; + for (final yamlElement in list) { + final yamlMap = as(yamlElement); + final type = yamlMap[NativeCodeAssetImpl.typeKey]; + switch (type) { + case NativeCodeAsset.type: + case null: // Backwards compatibility with v1.0.0. + assets.add(NativeCodeAssetImpl.fromYaml(yamlMap)); + case DataAsset.type: + assets.add(DataAssetImpl.fromYaml(yamlMap)); + default: + // Do nothing, some other launcher might define it's own asset types. + } } - return [ - for (final yamlElement in as(yamlObject)) - Asset.fromYaml(as(yamlElement)), - ]; + return assets; } - - static List listFromYamlList(YamlList yamlList) => [ - for (final yamlElement in yamlList) - Asset.fromYaml(as(yamlElement)), - ]; - - Asset copyWith({ - LinkMode? linkMode, - String? id, - Target? target, - AssetPath? path, - }) => - Asset( - id: id ?? this.id, - linkMode: linkMode ?? this.linkMode, - target: target ?? this.target, - path: path ?? this.path, - ); - - @override - bool operator ==(Object other) { - if (other is! Asset) { - return false; - } - return other.id == id && - other.linkMode == linkMode && - other.target == target && - other.path == path; - } - - @override - int get hashCode => Object.hash(id, linkMode, target, path); - - Map toYaml() => { - _idKey: id, - _linkModeKey: linkMode.name, - _pathKey: path.toYaml(), - _targetKey: target.toString(), - }; - - static const _idKey = 'id'; - static const _linkModeKey = 'link_mode'; - static const _pathKey = 'path'; - static const _targetKey = 'target'; - - // Future exists() => path.exists(); - - @override - String toString() => 'Asset(${toYaml()})'; -} - -extension AssetIterable on Iterable { - List toYaml() => [for (final item in this) item.toYaml()]; - - String toYamlString() => yamlEncode(toYaml()); } diff --git a/pkgs/native_assets_cli/lib/src/model/build_config.dart b/pkgs/native_assets_cli/lib/src/model/build_config.dart index a5019d24f..dc807060a 100644 --- a/pkgs/native_assets_cli/lib/src/model/build_config.dart +++ b/pkgs/native_assets_cli/lib/src/model/build_config.dart @@ -2,89 +2,42 @@ // 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:convert'; -import 'dart:io'; - -import 'package:cli_config/cli_config.dart'; -import 'package:collection/collection.dart'; -import 'package:crypto/crypto.dart'; -import 'package:pub_semver/pub_semver.dart'; - -import '../api/build_config.dart' as api; -import '../utils/map.dart'; -import '../utils/yaml.dart'; -import 'build_mode.dart'; -import 'ios_sdk.dart'; -import 'link_mode_preference.dart'; -import 'metadata.dart'; -import 'target.dart'; - -class BuildConfig implements api.BuildConfig { - /// The folder in which all output and intermediate artifacts should be - /// placed. +part of '../api/build_config.dart'; + +final class BuildConfigImpl implements BuildConfig { @override - Uri get outDir => _outDir; + Uri get outputDirectory => _outDir; late final Uri _outDir; - /// The name of the package the native assets are built for. @override String get packageName => _packageName; late final String _packageName; - /// The root of the package the native assets are built for. - /// - /// Often a package's native assets are built because a package is a - /// dependency of another. For this it is convenient to know the packageRoot. @override Uri get packageRoot => _packageRoot; late final Uri _packageRoot; - /// The target being compiled for. - /// - /// Not available in [dryRun]. @override - late final Target target = - Target.fromArchitectureAndOs(targetArchitecture, targetOs); + ArchitectureImpl? get targetArchitecture => _targetArchitecture; + late final ArchitectureImpl? _targetArchitecture; - /// The architecture being compiled for. - /// - /// Not available in [dryRun]. @override - Architecture get targetArchitecture { - _ensureNotDryRun(); - return _targetArchitecture; - } + OSImpl get targetOS => _targetOS; + late final OSImpl _targetOS; - late final Architecture _targetArchitecture; - - /// The operating system being compiled for. @override - OS get targetOs => _targetOs; - late final OS _targetOs; - - /// When compiling for iOS, whether to target device or simulator. - /// - /// Required when [targetOs] equals [OS.iOS]. - /// - /// Not available in [dryRun].s - @override - IOSSdk? get targetIOSSdk { + IOSSdkImpl get targetIOSSdk { _ensureNotDryRun(); - return _targetIOSSdk; + if (_targetOS != OS.iOS) { + throw StateError( + 'This field is not available in if targetOS is not OS.iOS.', + ); + } + return _targetIOSSdk!; } - late final IOSSdk? _targetIOSSdk; + late final IOSSdkImpl? _targetIOSSdk; - /// When compiling for Android, the minimum Android SDK API version to that - /// the compiled code will be compatible with. - /// - /// Required when [targetOs] equals [OS.android]. - /// - /// Not available in [dryRun]. - /// - /// For more information about the Android API version, refer to - /// [`minSdkVersion`](https://developer.android.com/ndk/guides/sdk-versions#minsdkversion) - /// in the Android documentation. @override int? get targetAndroidNdkApi { _ensureNotDryRun(); @@ -93,148 +46,152 @@ class BuildConfig implements api.BuildConfig { late final int? _targetAndroidNdkApi; - /// Preferred linkMode method for library. @override - LinkModePreference get linkModePreference => _linkModePreference; - late final LinkModePreference _linkModePreference; + LinkModePreferenceImpl get linkModePreference => _linkModePreference; + late final LinkModePreferenceImpl _linkModePreference; - /// Metadata from direct dependencies. - /// - /// The key in the map is the package name of the dependency. - /// - /// The key in the nested map is the key for the metadata from the dependency. - /// - /// Not available in [dryRun]. @override - Map? get dependencyMetadata { + Object? metadatum(String packageName, String key) { _ensureNotDryRun(); - return _dependencyMetadata; + return _dependencyMetadata?[packageName]?.metadata[key]; } late final Map? _dependencyMetadata; - /// The configuration for invoking the C compiler. - /// - /// Not available in [dryRun]. @override - CCompilerConfig get cCompiler { + CCompilerConfigImpl get cCompiler { _ensureNotDryRun(); return _cCompiler; } - late final CCompilerConfig _cCompiler; + late final CCompilerConfigImpl _cCompiler; - /// Don't run the build, only report the native assets produced. @override bool get dryRun => _dryRun ?? false; late final bool? _dryRun; - /// The build mode that the code should be compiled in. - /// - /// Not available in [dryRun]. @override - BuildMode get buildMode { + BuildModeImpl get buildMode { _ensureNotDryRun(); return _buildMode; } - late final BuildMode _buildMode; + late final BuildModeImpl _buildMode; - /// The underlying config. - /// - /// Can be used for easier access to values on [dependencyMetadata]. @override + Iterable get supportedAssetTypes => _supportedAssetTypes; + + late final List _supportedAssetTypes; + + Version get version => _version; + + late final Version _version; + Config get config => _config; late final Config _config; - factory BuildConfig({ + factory BuildConfigImpl({ required Uri outDir, required String packageName, required Uri packageRoot, - required BuildMode buildMode, - required Architecture targetArchitecture, - required OS targetOs, - IOSSdk? targetIOSSdk, + required BuildModeImpl buildMode, + required ArchitectureImpl targetArchitecture, + required OSImpl targetOS, + IOSSdkImpl? targetIOSSdk, int? targetAndroidNdkApi, - CCompilerConfig? cCompiler, - required LinkModePreference linkModePreference, + CCompilerConfigImpl? cCompiler, + required LinkModePreferenceImpl linkModePreference, Map? dependencyMetadata, + Iterable? supportedAssetTypes, }) { - final nonValidated = BuildConfig._() + final nonValidated = BuildConfigImpl._() + .._version = latestVersion .._outDir = outDir .._packageName = packageName .._packageRoot = packageRoot .._buildMode = buildMode .._targetArchitecture = targetArchitecture - .._targetOs = targetOs + .._targetOS = targetOS .._targetIOSSdk = targetIOSSdk .._targetAndroidNdkApi = targetAndroidNdkApi - .._cCompiler = cCompiler ?? CCompilerConfig() + .._cCompiler = cCompiler ?? CCompilerConfigImpl() .._linkModePreference = linkModePreference .._dependencyMetadata = dependencyMetadata - .._dryRun = false; + .._dryRun = false + .._supportedAssetTypes = + _supportedAssetTypesBackwardsCompatibility(supportedAssetTypes); final parsedConfigFile = nonValidated.toYaml(); final config = Config(fileParsed: parsedConfigFile); - return BuildConfig.fromConfig(config); + return BuildConfigImpl.fromConfig(config); } - factory BuildConfig.dryRun({ + factory BuildConfigImpl.dryRun({ required Uri outDir, required String packageName, required Uri packageRoot, - required OS targetOs, - required LinkModePreference linkModePreference, + required OSImpl targetOS, + required LinkModePreferenceImpl linkModePreference, + Iterable? supportedAssetTypes, }) { - final nonValidated = BuildConfig._() + final nonValidated = BuildConfigImpl._() + .._version = latestVersion .._outDir = outDir .._packageName = packageName .._packageRoot = packageRoot - .._targetOs = targetOs + .._targetOS = targetOS + .._targetArchitecture = null .._linkModePreference = linkModePreference - .._cCompiler = CCompilerConfig() - .._dryRun = true; + .._cCompiler = CCompilerConfigImpl() + .._dryRun = true + .._supportedAssetTypes = + _supportedAssetTypesBackwardsCompatibility(supportedAssetTypes); final parsedConfigFile = nonValidated.toYaml(); final config = Config(fileParsed: parsedConfigFile); - return BuildConfig.fromConfig(config); + return BuildConfigImpl.fromConfig(config); } - /// Constructs a checksum for a [BuildConfig] based on the fields - /// of a buildconfig that influence the build. + /// Constructs a checksum for a [BuildConfigImpl] based on the fields of a + /// buildconfig that influence the build. /// - /// This can be used for an [outDir], but should not be used for dry-runs. + /// This can be used for an [outputDirectory], but should not be used for + /// dry-runs. /// - /// In particular, it only takes the package name from [packageRoot], - /// so that the hash is equal across checkouts and ignores [outDir] itself. + /// In particular, it only takes the package name from [packageRoot], so that + /// the hash is equal across checkouts and ignores [outputDirectory] itself. static String checksum({ required String packageName, required Uri packageRoot, - required Architecture targetArchitecture, - required OS targetOs, - required BuildMode buildMode, - IOSSdk? targetIOSSdk, + required ArchitectureImpl targetArchitecture, + required OSImpl targetOS, + required BuildModeImpl buildMode, + IOSSdkImpl? targetIOSSdk, int? targetAndroidNdkApi, - CCompilerConfig? cCompiler, - required LinkModePreference linkModePreference, + CCompilerConfigImpl? cCompiler, + required LinkModePreferenceImpl linkModePreference, Map? dependencyMetadata, + Iterable? supportedAssetTypes, + Version? version, }) { final input = [ + version ?? latestVersion, packageName, targetArchitecture.toString(), - targetOs.toString(), + targetOS.toString(), targetIOSSdk.toString(), targetAndroidNdkApi.toString(), buildMode.toString(), linkModePreference.toString(), - cCompiler?.ar.toString(), - cCompiler?.cc.toString(), + cCompiler?.archiver.toString(), + cCompiler?.compiler.toString(), cCompiler?.envScript.toString(), cCompiler?.envScriptArgs.toString(), - cCompiler?.ld.toString(), + cCompiler?.linker.toString(), if (dependencyMetadata != null) for (final entry in dependencyMetadata.entries) ...[ entry.key, json.encode(entry.value.toYaml()), - ] + ], + ..._supportedAssetTypesBackwardsCompatibility(supportedAssetTypes), ].join('###'); final sha256String = sha256.convert(utf8.encode(input)).toString(); // 256 bit hashes lead to 64 hex character strings. @@ -244,9 +201,17 @@ class BuildConfig implements api.BuildConfig { return sha256String.substring(0, nameLength); } - BuildConfig._(); + static List _supportedAssetTypesBackwardsCompatibility( + Iterable? supportedAssetTypes, + ) => + [ + ...?supportedAssetTypes, + if (supportedAssetTypes == null) NativeCodeAsset.type, + ]; + + BuildConfigImpl._(); - /// The version of [BuildConfig]. + /// The version of [BuildConfigImpl]. /// /// This class is used in the protocol between the Dart and Flutter SDKs /// and packages through `build.dart` invocations. @@ -254,10 +219,10 @@ class BuildConfig implements api.BuildConfig { /// If we ever were to make breaking changes, it would be useful to give /// proper error messages rather than just fail to parse the YAML /// representation in the protocol. - static Version version = Version(1, 0, 0); + static Version latestVersion = Version(1, 1, 0); - factory BuildConfig.fromConfig(Config config) { - final result = BuildConfig._().._cCompiler = CCompilerConfig._(); + factory BuildConfigImpl.fromConfig(Config config) { + final result = BuildConfigImpl._().._cCompiler = CCompilerConfigImpl._(); final configExceptions = []; for (final f in result._readFieldsFromConfig()) { try { @@ -276,29 +241,17 @@ class BuildConfig implements api.BuildConfig { return result; } - /// Constructs a config by parsing CLI arguments and loading the config file. - /// - /// The [args] must be commandline arguments. - /// - /// If provided, [environment] must be a map containing environment variables. - /// If not provided, [environment] defaults to [Platform.environment]. - /// - /// If provided, [workingDirectory] is used to resolves paths inside - /// [environment]. - /// If not provided, [workingDirectory] defaults to [Directory.current]. - /// - /// This async constructor is intended to be used directly in CLI files. - static Future fromArgs( + static BuildConfigImpl fromArguments( List args, { Map? environment, Uri? workingDirectory, - }) async { - final config = await Config.fromArgs( - args: args, + }) { + final config = Config.fromArgumentsSync( + arguments: args, environment: environment, workingDirectory: workingDirectory, ); - return BuildConfig.fromConfig(config); + return BuildConfigImpl.fromConfig(config); } static const outDirConfigKey = 'out_dir'; @@ -308,27 +261,29 @@ class BuildConfig implements api.BuildConfig { static const _versionKey = 'version'; static const targetAndroidNdkApiConfigKey = 'target_android_ndk_api'; static const dryRunConfigKey = 'dry_run'; + static const supportedAssetTypesKey = 'supported_asset_types'; List _readFieldsFromConfig() { var osSet = false; var ccSet = false; return [ (config) { - final configVersion = Version.parse(config.string('version')); - if (configVersion.major > version.major) { + final version = Version.parse(config.string('version')); + if (version.major > latestVersion.major) { throw FormatException( - 'The config version $configVersion is newer than this ' - 'package:native_assets_cli config version $version, ' + 'The config version $version is newer than this ' + 'package:native_assets_cli config version $latestVersion, ' 'please update native_assets_cli.', ); } - if (configVersion.major < version.major) { + if (version.major < latestVersion.major) { throw FormatException( - 'The config version $configVersion is newer than this ' - 'package:native_assets_cli config version $version, ' + 'The config version $version is newer than this ' + 'package:native_assets_cli config version $latestVersion, ' 'please update the Dart or Flutter SDK.', ); } + _version = version; }, (config) => _config = config, (config) => _dryRun = config.optionalBool(dryRunConfigKey), @@ -338,39 +293,40 @@ class BuildConfig implements api.BuildConfig { _packageRoot = config.path(packageRootConfigKey, mustExist: true), (config) { if (dryRun) { - _throwIfNotNullInDryRun(BuildMode.configKey); + _throwIfNotNullInDryRun(BuildModeImpl.configKey); } else { - _buildMode = BuildMode.fromString( + _buildMode = BuildModeImpl.fromString( config.string( - BuildMode.configKey, - validValues: BuildMode.values.map((e) => '$e'), + BuildModeImpl.configKey, + validValues: BuildModeImpl.values.map((e) => '$e'), ), ); } }, (config) { - _targetOs = OS.fromString( + _targetOS = OSImpl.fromString( config.string( - OS.configKey, - validValues: OS.values.map((e) => '$e'), + OSImpl.configKey, + validValues: OSImpl.values.map((e) => '$e'), ), ); osSet = true; }, (config) { if (dryRun) { - _throwIfNotNullInDryRun(Architecture.configKey); + _throwIfNotNullInDryRun(ArchitectureImpl.configKey); + _targetArchitecture = null; } else { final validArchitectures = [ if (!osSet) - ...Architecture.values + ...ArchitectureImpl.values else for (final target in Target.values) - if (target.os == _targetOs) target.architecture + if (target.os == _targetOS) target.architecture ]; - _targetArchitecture = Architecture.fromString( + _targetArchitecture = ArchitectureImpl.fromString( config.string( - Architecture.configKey, + ArchitectureImpl.configKey, validValues: validArchitectures.map((e) => '$e'), ), ); @@ -378,13 +334,13 @@ class BuildConfig implements api.BuildConfig { }, (config) { if (dryRun) { - _throwIfNotNullInDryRun(IOSSdk.configKey); + _throwIfNotNullInDryRun(IOSSdkImpl.configKey); } else { - _targetIOSSdk = (osSet && _targetOs == OS.iOS) - ? IOSSdk.fromString( + _targetIOSSdk = (osSet && _targetOS == OSImpl.iOS) + ? IOSSdkImpl.fromString( config.string( - IOSSdk.configKey, - validValues: IOSSdk.values.map((e) => '$e'), + IOSSdkImpl.configKey, + validValues: IOSSdkImpl.values.map((e) => '$e'), ), ) : null; @@ -394,27 +350,27 @@ class BuildConfig implements api.BuildConfig { if (dryRun) { _throwIfNotNullInDryRun(targetAndroidNdkApiConfigKey); } else { - _targetAndroidNdkApi = (osSet && _targetOs == OS.android) + _targetAndroidNdkApi = (osSet && _targetOS == OSImpl.android) ? config.int(targetAndroidNdkApiConfigKey) : null; } }, (config) { if (dryRun) { - _throwIfNotNullInDryRun(CCompilerConfig.arConfigKeyFull); + _throwIfNotNullInDryRun(CCompilerConfigImpl.arConfigKeyFull); } else { - cCompiler._ar = config.optionalPath( - CCompilerConfig.arConfigKeyFull, + cCompiler._archiver = config.optionalPath( + CCompilerConfigImpl.arConfigKeyFull, mustExist: true, ); } }, (config) { if (dryRun) { - _throwIfNotNullInDryRun(CCompilerConfig.ccConfigKeyFull); + _throwIfNotNullInDryRun(CCompilerConfigImpl.ccConfigKeyFull); } else { - cCompiler._cc = config.optionalPath( - CCompilerConfig.ccConfigKeyFull, + cCompiler._compiler = config.optionalPath( + CCompilerConfigImpl.ccConfigKeyFull, mustExist: true, ); ccSet = true; @@ -422,47 +378,50 @@ class BuildConfig implements api.BuildConfig { }, (config) { if (dryRun) { - _throwIfNotNullInDryRun(CCompilerConfig.ccConfigKeyFull); + _throwIfNotNullInDryRun(CCompilerConfigImpl.ccConfigKeyFull); } else { - cCompiler._ld = config.optionalPath( - CCompilerConfig.ldConfigKeyFull, + cCompiler._linker = config.optionalPath( + CCompilerConfigImpl.ldConfigKeyFull, mustExist: true, ); } }, (config) { if (dryRun) { - _throwIfNotNullInDryRun(CCompilerConfig.ccConfigKeyFull); + _throwIfNotNullInDryRun(CCompilerConfigImpl.ccConfigKeyFull); } else { cCompiler._envScript = (ccSet && - cCompiler.cc != null && - cCompiler.cc!.toFilePath().endsWith('cl.exe')) - ? config.path(CCompilerConfig.envScriptConfigKeyFull, + cCompiler.compiler != null && + cCompiler.compiler!.toFilePath().endsWith('cl.exe')) + ? config.path(CCompilerConfigImpl.envScriptConfigKeyFull, mustExist: true) : null; } }, (config) { if (dryRun) { - _throwIfNotNullInDryRun(CCompilerConfig.ccConfigKeyFull); + _throwIfNotNullInDryRun(CCompilerConfigImpl.ccConfigKeyFull); } else { cCompiler._envScriptArgs = config.optionalStringList( - CCompilerConfig.envScriptArgsConfigKeyFull, + CCompilerConfigImpl.envScriptArgsConfigKeyFull, splitEnvironmentPattern: ' ', ); } }, (config) { - _linkModePreference = LinkModePreference.fromString( + _linkModePreference = LinkModePreferenceImpl.fromString( config.string( - LinkModePreference.configKey, - validValues: LinkModePreference.values.map((e) => '$e'), + LinkModePreferenceImpl.configKey, + validValues: LinkModePreferenceImpl.values.map((e) => '$e'), ), ); }, (config) { _dependencyMetadata = _readDependencyMetadataFromConfig(config); }, + (config) => _supportedAssetTypes = + config.optionalStringList(supportedAssetTypesKey) ?? + [NativeCodeAsset.type], ]; } @@ -502,20 +461,23 @@ class BuildConfig implements api.BuildConfig { outDirConfigKey: _outDir.toFilePath(), packageNameConfigKey: _packageName, packageRootConfigKey: _packageRoot.toFilePath(), - OS.configKey: _targetOs.toString(), - LinkModePreference.configKey: _linkModePreference.toString(), - _versionKey: version.toString(), + OSImpl.configKey: _targetOS.toString(), + LinkModePreferenceImpl.configKey: _linkModePreference.toString(), + supportedAssetTypesKey: _supportedAssetTypes, + _versionKey: latestVersion.toString(), if (dryRun) dryRunConfigKey: dryRun, if (!dryRun) ...{ - BuildMode.configKey: _buildMode.toString(), - Architecture.configKey: _targetArchitecture.toString(), - if (_targetIOSSdk != null) IOSSdk.configKey: _targetIOSSdk.toString(), + BuildModeImpl.configKey: _buildMode.toString(), + ArchitectureImpl.configKey: _targetArchitecture.toString(), + if (_targetIOSSdk != null) + IOSSdkImpl.configKey: _targetIOSSdk.toString(), if (_targetAndroidNdkApi != null) - targetAndroidNdkApiConfigKey: _targetAndroidNdkApi!, - if (cCompilerYaml.isNotEmpty) CCompilerConfig.configKey: cCompilerYaml, - if (_dependencyMetadata != null) + targetAndroidNdkApiConfigKey: _targetAndroidNdkApi, + if (cCompilerYaml.isNotEmpty) + CCompilerConfigImpl.configKey: cCompilerYaml, + if (_dependencyMetadata != null && _dependencyMetadata.isNotEmpty) dependencyMetadataConfigKey: { - for (final entry in _dependencyMetadata!.entries) + for (final entry in _dependencyMetadata.entries) entry.key: entry.value.toYaml(), }, }, @@ -526,47 +488,52 @@ class BuildConfig implements api.BuildConfig { @override bool operator ==(Object other) { - if (other is! BuildConfig) { + if (other is! BuildConfigImpl) { return false; } - if (other.outDir != outDir) return false; + if (other.outputDirectory != outputDirectory) return false; if (other.packageName != packageName) return false; if (other.packageRoot != packageRoot) return false; if (other.dryRun != dryRun) return false; - if (other.targetOs != targetOs) return false; + if (other.targetOS != targetOS) return false; if (other.linkModePreference != linkModePreference) return false; + if (!const DeepCollectionEquality() + .equals(other._supportedAssetTypes, _supportedAssetTypes)) return false; if (!dryRun) { if (other.buildMode != buildMode) return false; if (other.targetArchitecture != targetArchitecture) return false; - if (other.targetIOSSdk != targetIOSSdk) return false; + if (targetOS == OS.iOS && other.targetIOSSdk != targetIOSSdk) { + return false; + } if (other.targetAndroidNdkApi != targetAndroidNdkApi) return false; if (other.cCompiler != cCompiler) return false; if (!const DeepCollectionEquality() - .equals(other.dependencyMetadata, _dependencyMetadata)) return false; + .equals(other._dependencyMetadata, _dependencyMetadata)) return false; } return true; } @override int get hashCode => Object.hashAll([ - outDir, + outputDirectory, packageName, packageRoot, - targetOs, + targetOS, linkModePreference, dryRun, + const DeepCollectionEquality().hash(_supportedAssetTypes), if (!dryRun) ...[ buildMode, - const DeepCollectionEquality().hash(dependencyMetadata), + const DeepCollectionEquality().hash(_dependencyMetadata), targetArchitecture, - targetIOSSdk, + if (targetOS == OS.iOS) targetIOSSdk, targetAndroidNdkApi, cCompiler, ], ]); @override - String toString() => 'BuildConfig(${toYaml()})'; + String toString() => 'BuildConfig.build(${toYaml()})'; void _ensureNotDryRun() { if (dryRun) { @@ -587,92 +554,3 @@ can _only_ depend on OS.'''); } } } - -class CCompilerConfig implements api.CCompilerConfig { - /// Path to a C compiler. - @override - Uri? get cc => _cc; - late final Uri? _cc; - - /// Path to a native linker. - @override - Uri? get ld => _ld; - late final Uri? _ld; - - /// Path to a native archiver. - @override - Uri? get ar => _ar; - late final Uri? _ar; - - /// Path to script that sets environment variables for [cc], [ld], and [ar]. - @override - Uri? get envScript => _envScript; - late final Uri? _envScript; - - /// Arguments for [envScript]. - @override - List? get envScriptArgs => _envScriptArgs; - late final List? _envScriptArgs; - - factory CCompilerConfig({ - Uri? ar, - Uri? cc, - Uri? ld, - Uri? envScript, - List? envScriptArgs, - }) => - CCompilerConfig._() - .._ar = ar - .._cc = cc - .._ld = ld - .._envScript = envScript - .._envScriptArgs = envScriptArgs; - - CCompilerConfig._(); - - 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 toYaml() => { - if (_ar != null) arConfigKey: _ar!.toFilePath(), - if (_cc != null) ccConfigKey: _cc!.toFilePath(), - if (_ld != null) ldConfigKey: _ld!.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.ar != ar) return false; - if (other.cc != cc) return false; - if (other.ld != ld) 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( - _ar, - _cc, - _ld, - _envScript, - const ListEquality().hash(envScriptArgs), - ); -} diff --git a/pkgs/native_assets_cli/lib/src/model/build_config_CHANGELOG.md b/pkgs/native_assets_cli/lib/src/model/build_config_CHANGELOG.md new file mode 100644 index 000000000..5975085a5 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/model/build_config_CHANGELOG.md @@ -0,0 +1,8 @@ +## 1.1.0 + +- Added supported asset types. + Backwards compatibility: Defaults to a list with a single element: `native_code`. + +## 1.0.0 + +- Initial version. diff --git a/pkgs/native_assets_cli/lib/src/model/build_mode.dart b/pkgs/native_assets_cli/lib/src/model/build_mode.dart index 472569bce..9fc2d8d63 100644 --- a/pkgs/native_assets_cli/lib/src/model/build_mode.dart +++ b/pkgs/native_assets_cli/lib/src/model/build_mode.dart @@ -2,26 +2,25 @@ // 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/build_mode.dart' as api; +part of '../api/build_mode.dart'; -class BuildMode implements api.BuildMode { +final class BuildModeImpl implements BuildMode { @override final String name; - const BuildMode._(this.name); + const BuildModeImpl._(this.name); - static const debug = BuildMode._('debug'); - static const release = BuildMode._('release'); + static const debug = BuildModeImpl._('debug'); + static const release = BuildModeImpl._('release'); static const values = [ debug, release, ]; - factory BuildMode.fromString(String target) => + factory BuildModeImpl.fromString(String target) => values.firstWhere((e) => e.name == target); - /// The `package:config` key preferably used. static const String configKey = 'build_mode'; @override diff --git a/pkgs/native_assets_cli/lib/src/model/build_output.dart b/pkgs/native_assets_cli/lib/src/model/build_output.dart index ccf3795a2..fd549287a 100644 --- a/pkgs/native_assets_cli/lib/src/model/build_output.dart +++ b/pkgs/native_assets_cli/lib/src/model/build_output.dart @@ -2,46 +2,45 @@ // 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'; - -import 'package:collection/collection.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:yaml/yaml.dart'; - -import '../api/build_output.dart' as api; -import '../utils/datetime.dart'; -import '../utils/file.dart'; -import '../utils/map.dart'; -import '../utils/yaml.dart'; -import 'asset.dart'; -import 'dependencies.dart'; -import 'metadata.dart'; - -class BuildOutput implements api.BuildOutput { - /// Time the build this output belongs to started. - /// - /// Rounded down to whole seconds, because [File.lastModified] is rounded - /// to whole seconds and caching logic compares these timestamps. +part of '../api/build_output.dart'; + +final class BuildOutputImpl implements BuildOutput { @override final DateTime timestamp; + + final List _assets; + @override - final List assets; - @override - final Dependencies dependencies; + Iterable get assets => _assets; + + final Dependencies _dependencies; + + Dependencies get dependenciesModel => _dependencies; + @override - final Metadata metadata; + Iterable get dependencies => _dependencies.dependencies; - BuildOutput({ + final Metadata _metadata; + + BuildOutputImpl({ DateTime? timestamp, - List? assets, + List? assets, Dependencies? dependencies, Metadata? metadata, }) : timestamp = (timestamp ?? DateTime.now()).roundDownToSeconds(), - assets = assets ?? [], + _assets = assets ?? [], // ignore: prefer_const_constructors - dependencies = dependencies ?? Dependencies([]), + _dependencies = dependencies ?? Dependencies([]), // ignore: prefer_const_constructors - metadata = metadata ?? Metadata({}); + _metadata = metadata ?? Metadata({}); + + @override + void addDependency(Uri dependency) => + _dependencies.dependencies.add(dependency); + + @override + void addDependencies(Iterable dependencies) => + _dependencies.dependencies.addAll(dependencies); static const _assetsKey = 'assets'; static const _dependenciesKey = 'dependencies'; @@ -49,96 +48,130 @@ class BuildOutput implements api.BuildOutput { static const _timestampKey = 'timestamp'; static const _versionKey = 'version'; - factory BuildOutput.fromYamlString(String yaml) { + factory BuildOutputImpl.fromYamlString(String yaml) { final yamlObject = loadYaml(yaml); - return BuildOutput.fromYaml(as(yamlObject)); + return BuildOutputImpl.fromYaml(as(yamlObject)); } - factory BuildOutput.fromYaml(YamlMap yamlMap) { + factory BuildOutputImpl.fromYaml(YamlMap yamlMap) { final outputVersion = Version.parse(as(yamlMap['version'])); - if (outputVersion.major > version.major) { + if (outputVersion.major > latestVersion.major) { throw FormatException( 'The output version $outputVersion is newer than the ' - 'package:native_assets_cli config version $version in Dart or Flutter, ' - 'please update the Dart or Flutter SDK.', + 'package:native_assets_cli config version $latestVersion in Dart or ' + 'Flutter, please update the Dart or Flutter SDK.', ); } - if (outputVersion.major < version.major) { + if (outputVersion.major < latestVersion.major) { throw FormatException( 'The output version $outputVersion is newer than this ' - 'package:native_assets_cli config version $version in Dart or Flutter, ' - 'please update native_assets_cli.', + 'package:native_assets_cli config version $latestVersion in Dart or ' + 'Flutter, please update native_assets_cli.', ); } - return BuildOutput( + final assets = + AssetImpl.listFromYamlList(as(yamlMap[_assetsKey])); + + return BuildOutputImpl( timestamp: DateTime.parse(as(yamlMap[_timestampKey])), - assets: Asset.listFromYamlList(as(yamlMap[_assetsKey])), + assets: assets, dependencies: Dependencies.fromYaml(as(yamlMap[_dependenciesKey])), metadata: Metadata.fromYaml(as(yamlMap[_metadataKey])), ); } - Map toYaml() => { + Map toYaml(Version version) => { _timestampKey: timestamp.toString(), - _assetsKey: assets.toYaml(), - _dependenciesKey: dependencies.toYaml(), - _metadataKey: metadata.toYaml(), + _assetsKey: [ + for (final asset in _assets) asset.toYaml(version), + ], + if (_dependencies.dependencies.isNotEmpty) + _dependenciesKey: _dependencies.toYaml(), + _metadataKey: _metadata.toYaml(), _versionKey: version.toString(), }..sortOnKey(); - String toYamlString() => yamlEncode(toYaml()); + String toYamlString(Version version) => yamlEncode(toYaml(version)); - /// The version of [BuildOutput]. + /// The version of [BuildOutputImpl]. /// - /// This class is used in the protocol between the Dart and Flutter SDKs - /// and packages through `build.dart` invocations. + /// This class is used in the protocol between the Dart and Flutter SDKs and + /// packages through `build.dart` invocations. /// /// If we ever were to make breaking changes, it would be useful to give /// proper error messages rather than just fail to parse the YAML /// representation in the protocol. - static Version version = Version(1, 0, 0); + /// + /// [BuildOutput.latestVersion] is tied to [BuildConfig.latestVersion]. This + /// enables making the yaml serialization in `build.dart` dependent on the + /// version of the Dart or Flutter SDK. When there is a need to split the + /// versions of BuildConfig and BuildOutput, the BuildConfig should start + /// passing the highest supported version of BuildOutput. + static Version latestVersion = BuildConfigImpl.latestVersion; static const fileName = 'build_output.yaml'; - /// Writes the YAML file from [outDir]/[fileName]. - static Future readFromFile({required Uri outDir}) async { + /// Reads the YAML file from [outDir]/[fileName]. + static Future readFromFile({required Uri outDir}) async { final buildOutputUri = outDir.resolve(fileName); final buildOutputFile = File.fromUri(buildOutputUri); if (!await buildOutputFile.exists()) { return null; } - return BuildOutput.fromYamlString(await buildOutputFile.readAsString()); + return BuildOutputImpl.fromYamlString(await buildOutputFile.readAsString()); } - /// Writes the [toYamlString] to [outDir]/[fileName]. - @override - Future writeToFile({required Uri outDir}) async { + /// Writes the [toYamlString] to [BuildConfig.outputDirectory]/[fileName]. + Future writeToFile({required BuildConfig config}) async { + final outDir = config.outputDirectory; final buildOutputUri = outDir.resolve(fileName); - await File.fromUri(buildOutputUri) - .writeAsStringCreateDirectory(toYamlString()); + final yamlString = toYamlString((config as BuildConfigImpl).version); + await File.fromUri(buildOutputUri).writeAsStringCreateDirectory(yamlString); } @override - String toString() => toYamlString(); + String toString() => toYamlString(BuildConfigImpl.latestVersion); @override bool operator ==(Object other) { - if (other is! BuildOutput) { + if (other is! BuildOutputImpl) { return false; } return other.timestamp == timestamp && - const ListEquality().equals(other.assets, assets) && - other.dependencies == dependencies && - other.metadata == metadata; + const ListEquality().equals(other._assets, _assets) && + other._dependencies == _dependencies && + other._metadata == _metadata; } @override int get hashCode => Object.hash( timestamp.hashCode, - const ListEquality().hash(assets), - dependencies, - metadata, + const ListEquality().hash(_assets), + _dependencies, + _metadata, ); + + @override + void addMetadatum(String key, Object value) { + _metadata.metadata[key] = value; + } + + @override + void addMetadata(Map metadata) { + _metadata.metadata.addAll(metadata); + } + + Metadata get metadataModel => _metadata; + + @override + void addAsset(Asset asset) { + _assets.add(asset as AssetImpl); + } + + @override + void addAssets(Iterable assets) { + _assets.addAll(assets.cast()); + } } diff --git a/pkgs/native_assets_cli/lib/src/model/build_output_CHANGELOG.md b/pkgs/native_assets_cli/lib/src/model/build_output_CHANGELOG.md new file mode 100644 index 000000000..a807846fe --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/model/build_output_CHANGELOG.md @@ -0,0 +1,17 @@ +## 1.1.0 + +- Assets now have a `type`. + Backwards compatibility: assets without a type are interpreted as `native_code` assets. +- Assets now have an optional `file`. + Backwards compatibility: assets that have an `AssetAbsolutePath` will have that path used as `file`. +- **Breaking change** Assets now have a `dynamic_loading` field instead of `path_type`. + The `absolute` path_type is renamed to `bundled` as dynamic loading type. + Backwards compatibility: `path` is parsed as `dynamic_linking`. + Backwards compatibility: the nested `path_type` is parsed as `type`. + Backwards compatibility older SDKs: emit the old format if an older BuildConfig was passed in. +- Added `DataAsset`s. + Backwards compatibility: These are ignored on older SDKs. + +## 1.0.0 + +- Initial version. 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 new file mode 100644 index 000000000..10416e4e0 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/model/c_compiler_config.dart @@ -0,0 +1,95 @@ +// 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 toYaml() => { + 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/data_asset.dart b/pkgs/native_assets_cli/lib/src/model/data_asset.dart new file mode 100644 index 000000000..16df695e7 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/model/data_asset.dart @@ -0,0 +1,51 @@ +// 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/asset.dart'; + +final class DataAssetImpl implements DataAsset, AssetImpl { + @override + final Uri file; + + @override + final String id; + + DataAssetImpl({ + required this.file, + required this.id, + }); + + factory DataAssetImpl.fromYaml(YamlMap yamlMap) => DataAssetImpl( + id: as(yamlMap[_idKey]), + file: Uri(path: as(yamlMap[_fileKey])), + ); + + @override + bool operator ==(Object other) { + if (other is! DataAssetImpl) { + return false; + } + return other.id == id && other.file == file; + } + + @override + int get hashCode => Object.hash( + id, + file, + ); + + @override + Map toYaml(Version version) => { + _idKey: id, + _fileKey: file.toFilePath(), + typeKey: DataAsset.type, + }..sortOnKey(); + + static const typeKey = 'type'; + static const _idKey = 'id'; + static const _fileKey = 'file'; + + @override + String toString() => 'DataAsset(${toYaml(BuildOutput.latestVersion)})'; +} diff --git a/pkgs/native_assets_cli/lib/src/model/dependencies.dart b/pkgs/native_assets_cli/lib/src/model/dependencies.dart index 8cc801a41..9ecc2cb91 100644 --- a/pkgs/native_assets_cli/lib/src/model/dependencies.dart +++ b/pkgs/native_assets_cli/lib/src/model/dependencies.dart @@ -5,14 +5,12 @@ import 'package:collection/collection.dart'; import 'package:yaml/yaml.dart'; -import '../api/dependencies.dart' as api; import '../utils/file.dart'; import '../utils/uri.dart'; import '../utils/yaml.dart'; -class Dependencies implements api.Dependencies { +class Dependencies { /// The dependencies a build relied on. - @override final List dependencies; const Dependencies(this.dependencies); diff --git a/pkgs/native_assets_cli/lib/src/model/ios_sdk.dart b/pkgs/native_assets_cli/lib/src/model/ios_sdk.dart index 8955b44bf..9c20c9e12 100644 --- a/pkgs/native_assets_cli/lib/src/model/ios_sdk.dart +++ b/pkgs/native_assets_cli/lib/src/model/ios_sdk.dart @@ -2,28 +2,24 @@ // 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/ios_sdk.dart' as api; +part of '../api/ios_sdk.dart'; -/// For an iOS target, a build is either done for the device or the simulator. -/// -/// Only fat binaries or xcframeworks can contain both targets. -class IOSSdk implements api.IOSSdk { +final class IOSSdkImpl implements IOSSdk { final String xcodebuildSdk; - const IOSSdk._(this.xcodebuildSdk); + const IOSSdkImpl._(this.xcodebuildSdk); - static const iPhoneOs = IOSSdk._('iphoneos'); - static const iPhoneSimulator = IOSSdk._('iphonesimulator'); + static const iPhoneOS = IOSSdkImpl._('iphoneos'); + static const iPhoneSimulator = IOSSdkImpl._('iphonesimulator'); static const values = [ - iPhoneOs, + iPhoneOS, iPhoneSimulator, ]; - factory IOSSdk.fromString(String target) => + factory IOSSdkImpl.fromString(String target) => values.firstWhere((e) => e.xcodebuildSdk == target); - /// The `package:config` key preferably used. static const String configKey = 'target_ios_sdk'; @override diff --git a/pkgs/native_assets_cli/lib/src/model/link_mode.dart b/pkgs/native_assets_cli/lib/src/model/link_mode.dart deleted file mode 100644 index 8f87983a2..000000000 --- a/pkgs/native_assets_cli/lib/src/model/link_mode.dart +++ /dev/null @@ -1,26 +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. - -import '../api/link_mode.dart' as api; - -class LinkMode implements api.LinkMode { - final String name; - - const LinkMode._(this.name); - - static const LinkMode dynamic = LinkMode._('dynamic'); - static const LinkMode static = LinkMode._('static'); - - /// Known values for [LinkMode]. - static const List values = [ - dynamic, - static, - ]; - - factory LinkMode.fromName(String name) => - values.where((element) => element.name == name).first; - - @override - String toString() => name; -} 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 index 83d72e92e..1ee1357a4 100644 --- a/pkgs/native_assets_cli/lib/src/model/link_mode_preference.dart +++ b/pkgs/native_assets_cli/lib/src/model/link_mode_preference.dart @@ -2,50 +2,31 @@ // 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/link_mode_preference.dart' as api; -import 'link_mode.dart'; +part of '../api/link_mode_preference.dart'; -class LinkModePreference implements api.LinkModePreference { +final class LinkModePreferenceImpl implements LinkModePreference { @override final String name; - @override - final LinkMode preferredLinkMode; - - @override - final List potentialLinkMode; - - const LinkModePreference( - this.name, { - required this.preferredLinkMode, - required this.potentialLinkMode, - }); + const LinkModePreferenceImpl(this.name); - factory LinkModePreference.fromString(String name) => + factory LinkModePreferenceImpl.fromString(String name) => values.where((element) => element.name == name).first; - static const dynamic = LinkModePreference( + static const dynamic = LinkModePreferenceImpl( 'dynamic', - preferredLinkMode: LinkMode.dynamic, - potentialLinkMode: [LinkMode.dynamic], ); - static const static = LinkModePreference( + static const static = LinkModePreferenceImpl( 'static', - preferredLinkMode: LinkMode.static, - potentialLinkMode: [LinkMode.static], ); - static const preferDynamic = LinkModePreference( + static const preferDynamic = LinkModePreferenceImpl( 'prefer-dynamic', - preferredLinkMode: LinkMode.dynamic, - potentialLinkMode: LinkMode.values, ); - static const preferStatic = LinkModePreference( + static const preferStatic = LinkModePreferenceImpl( 'prefer-static', - preferredLinkMode: LinkMode.static, - potentialLinkMode: LinkMode.values, ); static const values = [ @@ -55,7 +36,6 @@ class LinkModePreference implements api.LinkModePreference { preferStatic, ]; - /// The `package:config` key preferably used. static const String configKey = 'link_mode_preference'; @override diff --git a/pkgs/native_assets_cli/lib/src/model/metadata.dart b/pkgs/native_assets_cli/lib/src/model/metadata.dart index db68330d6..34949b1d4 100644 --- a/pkgs/native_assets_cli/lib/src/model/metadata.dart +++ b/pkgs/native_assets_cli/lib/src/model/metadata.dart @@ -5,12 +5,10 @@ import 'package:collection/collection.dart'; import 'package:yaml/yaml.dart'; -import '../api/metadata.dart' as api; import '../utils/map.dart'; import '../utils/yaml.dart'; -class Metadata implements api.Metadata { - @override +class Metadata { final Map metadata; const Metadata(this.metadata); 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 new file mode 100644 index 000000000..1608c8ed6 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/model/native_code_asset.dart @@ -0,0 +1,383 @@ +// 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/asset.dart'; + +abstract final class LinkModeImpl implements LinkMode { + /// Serialization of the current version. + /// + /// v1.1.0 does not include the toplevel keys. + /// + /// ``` + /// type: dynamic_loading_system + /// uri: ${foo3Uri.toFilePath()} + /// ``` + Map toYaml(); + + /// Backwards compatibility with v1.0.0 of the protocol + /// + /// Includes the parent keys. + /// + /// ``` + /// link_mode: dynamic + /// path: + /// path_type: system + /// uri: ${foo3Uri.toFilePath()} + /// ``` + Map toYamlV1_0_0(Uri? file); + + factory LinkModeImpl(String type, Uri? uri) { + switch (type) { + case DynamicLoadingBundledImpl._typeValueV1_0_0: + case DynamicLoadingBundledImpl._typeValue: + return DynamicLoadingBundledImpl(); + case DynamicLoadingSystemImpl._typeValueV1_0_0: + case DynamicLoadingSystemImpl._typeValue: + return DynamicLoadingSystemImpl(uri!); + case LookupInExecutableImpl._typeValueV1_0_0: + case LookupInExecutableImpl._typeValue: + return LookupInExecutableImpl(); + case LookupInProcessImpl._typeValueV1_0_0: + case LookupInProcessImpl._typeValue: + return LookupInProcessImpl(); + case StaticLinkingImpl._typeValue: + return StaticLinkingImpl(); + } + throw FormatException('Unknown type: $type.'); + } + + /// v1.1.0 only. + factory LinkModeImpl.fromYaml(YamlMap yamlMap) { + final type = as(yamlMap[_typeKey]); + final uriString = as(yamlMap[_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 { + factory DynamicLoadingImpl(String type, Uri? uri) { + switch (type) { + case DynamicLoadingBundledImpl._typeValueV1_0_0: + // For backwards compatibility. + case DynamicLoadingBundledImpl._typeValue: + return DynamicLoadingBundledImpl(); + case DynamicLoadingSystemImpl._typeValue: + return DynamicLoadingSystemImpl(uri!); + case LookupInExecutableImpl._typeValue: + return LookupInExecutableImpl(); + case LookupInProcessImpl._typeValue: + return LookupInProcessImpl(); + } + throw FormatException('Unknown type: $type.'); + } + + static const _pathTypeKeyV1_0_0 = 'path_type'; + static const _typeKey = 'type'; + static const _uriKey = 'uri'; + + static const _typeValueV1_0_0 = 'dynamic'; +} + +final class DynamicLoadingBundledImpl + implements DynamicLoadingImpl, DynamicLoadingBundled { + DynamicLoadingBundledImpl._(); + + static final DynamicLoadingBundledImpl _singleton = + DynamicLoadingBundledImpl._(); + + factory DynamicLoadingBundledImpl() => _singleton; + + static const _typeValueV1_0_0 = 'absolute'; + static const _typeValue = 'dynamic_loading_bundle'; + + @override + Map toYaml() => { + DynamicLoadingImpl._typeKey: _typeValue, + }; + + @override + Map toYamlV1_0_0(Uri? file) => { + NativeCodeAssetImpl._linkModeKey: DynamicLoadingImpl._typeValueV1_0_0, + NativeCodeAssetImpl._pathKey: { + DynamicLoadingImpl._pathTypeKeyV1_0_0: _typeValueV1_0_0, + DynamicLoadingImpl._uriKey: file!.toFilePath(), + } + }; +} + +final class DynamicLoadingSystemImpl + implements DynamicLoadingImpl, DynamicLoadingSystem { + @override + final Uri uri; + + DynamicLoadingSystemImpl(this.uri); + + static const _typeValue = 'dynamic_loading_system'; + static const _typeValueV1_0_0 = 'system'; + + @override + Map toYaml() => { + DynamicLoadingImpl._typeKey: _typeValue, + DynamicLoadingImpl._uriKey: uri.toFilePath(), + }; + + @override + Map toYamlV1_0_0(Uri? file) => { + NativeCodeAssetImpl._linkModeKey: DynamicLoadingImpl._typeValueV1_0_0, + NativeCodeAssetImpl._pathKey: { + DynamicLoadingImpl._pathTypeKeyV1_0_0: _typeValueV1_0_0, + 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; + } +} + +final class LookupInProcessImpl implements DynamicLoadingImpl, LookupInProcess { + LookupInProcessImpl._(); + + static final LookupInProcessImpl _singleton = LookupInProcessImpl._(); + + factory LookupInProcessImpl() => _singleton; + + static const _typeValue = 'dynamic_loading_process'; + static const _typeValueV1_0_0 = 'process'; + + @override + Map toYaml() => { + DynamicLoadingImpl._typeKey: _typeValue, + }; + + @override + Map toYamlV1_0_0(Uri? file) => { + NativeCodeAssetImpl._linkModeKey: DynamicLoadingImpl._typeValueV1_0_0, + NativeCodeAssetImpl._pathKey: { + DynamicLoadingImpl._pathTypeKeyV1_0_0: _typeValueV1_0_0, + } + }; +} + +final class LookupInExecutableImpl + implements DynamicLoadingImpl, LookupInExecutable { + LookupInExecutableImpl._(); + + static final LookupInExecutableImpl _singleton = LookupInExecutableImpl._(); + + factory LookupInExecutableImpl() => _singleton; + + static const _typeValue = 'dynamic_loading_executable'; + static const _typeValueV1_0_0 = 'executable'; + + @override + Map toYaml() => { + DynamicLoadingImpl._typeKey: _typeValue, + }; + + @override + Map toYamlV1_0_0(Uri? file) => { + NativeCodeAssetImpl._linkModeKey: DynamicLoadingImpl._typeValueV1_0_0, + NativeCodeAssetImpl._pathKey: { + DynamicLoadingImpl._pathTypeKeyV1_0_0: _typeValueV1_0_0, + } + }; +} + +final class StaticLinkingImpl implements LinkModeImpl, StaticLinking { + StaticLinkingImpl._(); + + static final StaticLinkingImpl _singleton = StaticLinkingImpl._(); + + factory StaticLinkingImpl() => _singleton; + + static const _typeValue = 'static'; + + @override + Map toYaml() => { + DynamicLoadingImpl._typeKey: _typeValue, + }; + + @override + Map toYamlV1_0_0(Uri? file) => { + NativeCodeAssetImpl._linkModeKey: _typeValue, + }; +} + +final class NativeCodeAssetImpl implements NativeCodeAsset, AssetImpl { + @override + final Uri? file; + + @override + final LinkModeImpl linkMode; + + @override + final String id; + + @override + final OSImpl os; + + @override + final ArchitectureImpl? architecture; + + NativeCodeAssetImpl({ + this.file, + required this.id, + required this.linkMode, + required this.os, + this.architecture, + }) { + if (linkMode is DynamicLoading && + linkMode is! DynamicLoadingBundled && + file != null) { + throw ArgumentError.value( + file, + 'file', + 'Must be null if dynamicLoading is not BundledDylib.', + ); + } + } + + factory NativeCodeAssetImpl.fromYaml(YamlMap yamlMap) { + final LinkModeImpl linkMode; + final linkModeYaml = yamlMap[_linkModeKey]; + if (linkModeYaml is String) { + // v1.0.0 + if (linkModeYaml == StaticLinkingImpl._typeValue) { + linkMode = StaticLinkingImpl(); + } else { + assert(linkModeYaml == DynamicLoadingImpl._typeValueV1_0_0); + final pathYaml = as(yamlMap[_pathKey]); + final type = + as(pathYaml[DynamicLoadingImpl._pathTypeKeyV1_0_0]); + final uriString = as(pathYaml[DynamicLoadingImpl._uriKey]); + final uri = uriString != null ? Uri(path: uriString) : null; + linkMode = LinkModeImpl(type, uri); + } + } else { + // v1.1.0 + linkMode = LinkModeImpl.fromYaml(as(linkModeYaml)); + } + + final fileString = as(yamlMap[_fileKey]); + final Uri? file; + if (fileString != null) { + file = Uri(path: fileString); + } else if ((linkMode is DynamicLoadingBundledImpl || + linkMode is StaticLinkingImpl) && + yamlMap[_pathKey] != null) { + // Compatibility with v1.0.0. + final oldPath = as( + (yamlMap[_pathKey] as YamlMap)[DynamicLoadingImpl._uriKey]); + file = oldPath != null ? Uri(path: oldPath) : null; + } else { + file = null; + } + final targetString = as(yamlMap[_targetKey]); + final ArchitectureImpl? architecture; + final OSImpl os; + if (targetString != null) { + // Compatibility with v1.0.0. + final target = Target.fromString(targetString); + os = target.os; + architecture = target.architecture; + } else { + os = OSImpl.fromString(as(yamlMap[_osKey])); + final architectureString = as(yamlMap[_architectureKey]); + if (architectureString != null) { + architecture = ArchitectureImpl.fromString(architectureString); + } else { + architecture = null; + } + } + + return NativeCodeAssetImpl( + id: as(yamlMap[_idKey]), + os: os, + architecture: architecture, + linkMode: linkMode, + file: file, + ); + } + + NativeCodeAssetImpl copyWith({ + LinkModeImpl? linkMode, + String? id, + OSImpl? os, + ArchitectureImpl? architecture, + Uri? file, + }) => + NativeCodeAssetImpl( + id: id ?? this.id, + linkMode: linkMode ?? this.linkMode, + os: os ?? this.os, + architecture: this.architecture ?? architecture, + file: file ?? this.file, + ); + + @override + bool operator ==(Object other) { + if (other is! NativeCodeAssetImpl) { + return false; + } + return other.id == id && + other.linkMode == linkMode && + other.architecture == architecture && + other.os == os && + other.file == file; + } + + @override + int get hashCode => Object.hash( + id, + linkMode, + architecture, + os, + file, + ); + + @override + Map toYaml(Version version) { + if (version == Version(1, 0, 0)) { + return { + _idKey: id, + ...linkMode.toYamlV1_0_0(file), + _targetKey: Target.fromArchitectureAndOS(architecture!, os).toString(), + }..sortOnKey(); + } + return { + if (architecture != null) _architectureKey: architecture.toString(), + if (file != null) _fileKey: file!.toFilePath(), + _idKey: id, + _linkModeKey: linkMode.toYaml(), + _osKey: os.toString(), + typeKey: NativeCodeAsset.type, + }..sortOnKey(); + } + + static const typeKey = 'type'; + static const _idKey = 'id'; + static const _linkModeKey = 'link_mode'; + static const _pathKey = 'path'; + static const _targetKey = 'target'; + static const _fileKey = 'file'; + static const _osKey = 'os'; + static const _architectureKey = 'architecture'; + + @override + String toString() => + 'NativeCodeAsset(${toYaml(BuildOutputImpl.latestVersion)})'; +} diff --git a/pkgs/native_assets_cli/lib/src/model/os.dart b/pkgs/native_assets_cli/lib/src/model/os.dart new file mode 100644 index 000000000..73fd3ea0c --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/model/os.dart @@ -0,0 +1,189 @@ +// 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 8492e3992..18cdc9775 100644 --- a/pkgs/native_assets_cli/lib/src/model/target.dart +++ b/pkgs/native_assets_cli/lib/src/model/target.dart @@ -5,237 +5,10 @@ import 'dart:ffi' show Abi; import 'dart:io'; -import '../api/link_mode.dart' as api; -import '../api/target.dart' as api; -import 'link_mode.dart'; +import '../api/architecture.dart'; +import '../api/os.dart'; -/// The hardware architectures the Dart VM runs on. -class Architecture implements api.Architecture { - /// This architecture as used in [Platform.version]. - final String dartPlatform; - - const Architecture._(this.dartPlatform); - - factory Architecture.fromAbi(Abi abi) => _abiToArch[abi]!; - - static const Architecture arm = Architecture._('arm'); - static const Architecture arm64 = Architecture._('arm64'); - static const Architecture ia32 = Architecture._('ia32'); - static const Architecture riscv32 = Architecture._('riscv32'); - static const Architecture riscv64 = Architecture._('riscv64'); - 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.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 `package:config` key preferably used. - static const String configKey = 'target_architecture'; - - @override - String toString() => dartPlatform; - - /// Mapping from strings as used in [Architecture.toString] to - /// [Architecture]s. - static final Map _stringToArchitecture = - Map.fromEntries(Architecture.values.map( - (architecture) => MapEntry(architecture.toString(), architecture))); - - factory Architecture.fromString(String target) => - _stringToArchitecture[target]!; - - /// The current [Architecture]. - /// - /// Read from the [Platform.version] string. - static final Architecture current = Target.current.architecture; -} - -/// The operating systems the Dart VM runs on. -class OS implements api.OS { - /// This OS as used in [Platform.version] - final String dartPlatform; - - const OS._(this.dartPlatform); - - factory OS.fromAbi(Abi abi) => _abiToOS[abi]!; - - static const OS android = OS._('android'); - static const OS fuchsia = OS._('fuchsia'); - static const OS iOS = OS._('ios'); - static const OS linux = OS._('linux'); - static const OS macOS = OS._('macos'); - static const OS windows = OS._('windows'); - - /// Known values for [OS]. - static const List values = [ - android, - fuchsia, - iOS, - linux, - macOS, - windows, - ]; - - static const _abiToOS = { - 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, - }; - - /// 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], - }; - - /// The default dynamic library file name on this [OS]. - @override - String dylibFileName(String name) { - final prefix = _dylibPrefix[this]!; - final extension = _dylibExtension[this]!; - return '$prefix$name.$extension'; - } - - /// The default static library file name on this [OS]. - @override - String staticlibFileName(String name) { - final prefix = _staticlibPrefix[this]!; - final extension = _staticlibExtension[this]!; - return '$prefix$name.$extension'; - } - - @override - String libraryFileName(String name, api.LinkMode linkMode) { - if (linkMode == LinkMode.dynamic) { - return dylibFileName(name); - } - assert(linkMode == LinkMode.static); - return staticlibFileName(name); - } - - /// The default executable file name on this [OS]. - @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 [OS]. - static const _dylibPrefix = { - OS.android: 'lib', - OS.fuchsia: 'lib', - OS.iOS: 'lib', - OS.linux: 'lib', - OS.macOS: 'lib', - OS.windows: '', - }; - - /// The default extension for dynamic libraries per [OS]. - static const _dylibExtension = { - OS.android: 'so', - OS.fuchsia: 'so', - OS.iOS: 'dylib', - OS.linux: 'so', - OS.macOS: 'dylib', - OS.windows: 'dll', - }; - - /// The default name prefix for static libraries per [OS]. - static const _staticlibPrefix = _dylibPrefix; - - /// The default extension for static libraries per [OS]. - static const _staticlibExtension = { - OS.android: 'a', - OS.fuchsia: 'a', - OS.iOS: 'a', - OS.linux: 'a', - OS.macOS: 'a', - OS.windows: 'lib', - }; - - /// The default extension for executables per [OS]. - static const _executableExtension = { - OS.android: '', - OS.fuchsia: '', - OS.iOS: '', - OS.linux: '', - OS.macOS: '', - OS.windows: 'exe', - }; - - /// The `package:config` key preferably used. - static const String configKey = 'target_os'; - - @override - String toString() => dartPlatform; - - /// Mapping from strings as used in [OS.toString] to - /// [OS]s. - static final Map _stringToOS = - Map.fromEntries(OS.values.map((os) => MapEntry(os.toString(), os))); - - factory OS.fromString(String target) => _stringToOS[target]!; - - /// The current [OS]. - /// - /// Read from the [Platform.version] string. - static final OS current = Target.current.os; -} - -/// Application binary interface. -/// -/// The Dart VM can run on a variety of [Target]s, see [Target.values]. -class Target implements api.Target { +final class Target implements Comparable { final Abi abi; const Target._(this.abi); @@ -262,7 +35,10 @@ class Target implements api.Target { return target; } - factory Target.fromArchitectureAndOs(Architecture architecture, OS os) { + factory Target.fromArchitectureAndOS( + ArchitectureImpl architecture, + OSImpl os, + ) { for (final value in values) { if (value.os == os && value.architecture == architecture) { return value; @@ -294,11 +70,7 @@ class Target implements api.Target { static const windowsIA32 = Target._(Abi.windowsIA32); static const windowsX64 = Target._(Abi.windowsX64); - /// All Targets that we can build for. - /// - /// Note that for some of these a Dart SDK is not available and they are only - /// used as target architectures for Flutter apps. - static const values = { + static const Set values = { androidArm, androidArm64, androidIA32, @@ -320,7 +92,6 @@ class Target implements api.Target { windowsArm64, windowsIA32, windowsX64, - // TODO(dacoharkes): Add support for `wasm`. }; /// Mapping from strings as used in [Target.toString] to [Target]s. @@ -337,17 +108,14 @@ class Target implements api.Target { /// Read from the [Platform.version] string. static final Target current = Target.fromDartPlatform(Platform.version); - @override - Architecture get architecture => Architecture.fromAbi(abi); + ArchitectureImpl get architecture => ArchitectureImpl.fromAbi(abi); - @override - OS get os => OS.fromAbi(abi); + OSImpl get os => OSImpl.fromAbi(abi); String get _architectureString => architecture.dartPlatform; String get _osString => os.dartPlatform; - /// A string representation of this object. @override String toString() => dartVMToString(); @@ -358,12 +126,12 @@ class Target implements api.Target { /// /// If [other] is also an [Target], consistent with sorting on [toString]. @override - int compareTo(api.Target other) => toString().compareTo(other.toString()); + int compareTo(Target other) => toString().compareTo(other.toString()); /// A list of supported target [Target]s from this host [os]. List supportedTargetTargets( - {Map> osCrossCompilation = - OS._osCrossCompilationDefault}) => + {Map> osCrossCompilation = + OSImpl.osCrossCompilationDefault}) => Target.values .where((target) => // Only valid cross compilation. @@ -374,7 +142,7 @@ class Target implements api.Target { } /// Common methods for manipulating iterables of [Target]s. -extension TargetList on Iterable { +extension on Iterable { /// The [Target]s in `this` sorted by name alphabetically. List get sorted => [for (final target in this) target]..sort(); } diff --git a/pkgs/native_assets_cli/pubspec.yaml b/pkgs/native_assets_cli/pubspec.yaml index 42fe30873..fe02649a9 100644 --- a/pkgs/native_assets_cli/pubspec.yaml +++ b/pkgs/native_assets_cli/pubspec.yaml @@ -4,7 +4,7 @@ description: >- native assets CLI. # Note: Bump BuildConfig.version and BuildOutput.version on breaking changes! -version: 0.4.3-wip +version: 0.5.0-wip repository: https://github.com/dart-lang/native/tree/main/pkgs/native_assets_cli topics: @@ -13,16 +13,18 @@ topics: - native-assets environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: - cli_config: ^0.1.1 + cli_config: ^0.2.0 collection: ^1.17.1 crypto: ^3.0.3 + logging: ^1.2.0 pub_semver: ^2.1.3 yaml: ^3.1.1 yaml_edit: ^2.1.0 dev_dependencies: dart_flutter_team_lints: ^2.1.1 + file_testing: ^3.0.0 test: ^1.21.0 diff --git a/pkgs/native_assets_cli/test/api/asset_test.dart b/pkgs/native_assets_cli/test/api/asset_test.dart index 54f2c61bc..606d775a5 100644 --- a/pkgs/native_assets_cli/test/api/asset_test.dart +++ b/pkgs/native_assets_cli/test/api/asset_test.dart @@ -3,48 +3,77 @@ // BSD-style license that can be found in the LICENSE file. import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/src/api/asset.dart'; import 'package:test/test.dart'; void main() { test('Asset constructors', () async { final assets = [ - Asset( - id: 'foo', - path: AssetAbsolutePath(Uri.file('path/to/libfoo.so')), - target: Target.androidX64, - linkMode: LinkMode.dynamic, - ), - Asset( - id: 'foo3', - path: AssetSystemPath(Uri(path: 'libfoo3.so')), - target: Target.androidX64, - linkMode: LinkMode.dynamic, - ), - Asset( - id: 'foo4', - path: AssetInExecutable(), - target: Target.androidX64, - linkMode: LinkMode.dynamic, - ), - Asset( - id: 'foo5', - path: AssetInProcess(), - target: Target.androidX64, - linkMode: LinkMode.dynamic, - ), - Asset( - id: 'bar', - path: AssetAbsolutePath(Uri(path: 'path/to/libbar.a')), - target: Target.linuxArm64, - linkMode: LinkMode.static, - ), - Asset( - id: 'bla', - path: AssetAbsolutePath(Uri(path: 'path/with spaces/bla.dll')), - target: Target.windowsX64, - linkMode: LinkMode.dynamic, + NativeCodeAsset( + package: 'my_package', + name: 'foo', + file: Uri.file('path/to/libfoo.so'), + linkMode: DynamicLoadingBundled(), + os: OS.android, + architecture: Architecture.x64, + ), + NativeCodeAsset( + package: 'my_package', + name: 'foo3', + linkMode: DynamicLoadingSystem(Uri(path: 'libfoo3.so')), + os: OS.android, + architecture: Architecture.x64, + ), + NativeCodeAsset( + package: 'my_package', + name: 'foo4', + linkMode: LookupInExecutable(), + os: OS.android, + architecture: Architecture.x64, + ), + NativeCodeAsset( + package: 'my_package', + name: 'foo5', + linkMode: LookupInProcess(), + os: OS.android, + architecture: Architecture.x64, + ), + NativeCodeAsset( + package: 'my_package', + name: 'bar', + file: Uri(path: 'path/to/libbar.a'), + os: OS.linux, + architecture: Architecture.arm64, + linkMode: StaticLinking(), + ), + NativeCodeAsset( + package: 'my_package', + name: 'bla', + file: Uri(path: 'path/with spaces/bla.dll'), + linkMode: DynamicLoadingBundled(), + os: OS.windows, + architecture: Architecture.x64, + ), + DataAsset( + package: 'my_package', + name: 'data/some_text.txt', + file: Uri(path: 'data/some_text.txt'), ), ]; assets.toString(); }); + + test('Errors', () { + expect( + () => NativeCodeAsset( + package: 'my_package', + name: 'foo', + file: Uri.file('path/to/libfoo.so'), + linkMode: LookupInExecutable(), + os: OS.android, + architecture: Architecture.x64, + ), + throwsArgumentError, + ); + }); } diff --git a/pkgs/native_assets_cli/test/api/build_config_test.dart b/pkgs/native_assets_cli/test/api/build_config_test.dart index 93ace7cb5..cc0204b35 100644 --- a/pkgs/native_assets_cli/test/api/build_config_test.dart +++ b/pkgs/native_assets_cli/test/api/build_config_test.dart @@ -6,7 +6,7 @@ import 'dart:io'; import 'package:cli_config/cli_config.dart'; import 'package:native_assets_cli/native_assets_cli.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart' as internal; +import 'package:native_assets_cli/src/api/build_config.dart'; import 'package:test/test.dart'; void main() async { @@ -47,58 +47,61 @@ void main() async { }); test('BuildConfig ==', () { - final config1 = BuildConfig( - outDir: outDirUri, + final config1 = BuildConfig.build( + outputDirectory: outDirUri, packageName: packageName, packageRoot: tempUri, targetArchitecture: Architecture.arm64, - targetOs: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOs, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, cCompiler: CCompilerConfig( - cc: fakeClang, - ld: fakeLd, - ar: fakeAr, + compiler: fakeClang, + linker: fakeLd, + archiver: fakeAr, ), buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, + supportedAssetTypes: [NativeCodeAsset.type], ); - final config2 = BuildConfig( - outDir: outDir2Uri, + final config2 = BuildConfig.build( + outputDirectory: outDir2Uri, packageName: packageName, packageRoot: tempUri, targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetOS: OS.android, targetAndroidNdkApi: 30, buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, + supportedAssetTypes: [NativeCodeAsset.type], ); expect(config1, equals(config1)); expect(config1 == config2, false); - expect(config1.outDir != config2.outDir, true); + expect(config1.outputDirectory != config2.outputDirectory, true); expect(config1.packageRoot, config2.packageRoot); expect(config1.targetArchitecture == config2.targetArchitecture, true); - expect(config1.targetOs != config2.targetOs, true); - expect(config1.targetIOSSdk != config2.targetIOSSdk, true); - expect(config1.cCompiler.cc != config2.cCompiler.cc, true); - expect(config1.cCompiler.ld != config2.cCompiler.ld, true); - expect(config1.cCompiler.ar != config2.cCompiler.ar, true); + expect(config1.targetOS != config2.targetOS, true); + 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); + expect(config1.cCompiler.archiver != config2.cCompiler.archiver, true); expect(config1.cCompiler.envScript == config2.cCompiler.envScript, true); expect(config1.cCompiler.envScriptArgs == config2.cCompiler.envScriptArgs, true); expect(config1.cCompiler != config2.cCompiler, true); expect(config1.linkModePreference, config2.linkModePreference); - expect(config1.dependencyMetadata, config2.dependencyMetadata); + expect(config1.supportedAssetTypes, config2.supportedAssetTypes); }); test('BuildConfig fromConfig', () { - final buildConfig2 = BuildConfig( - outDir: outDirUri, + final buildConfig2 = BuildConfig.build( + outputDirectory: outDirUri, packageName: packageName, packageRoot: packageRootUri, targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetOS: OS.android, targetAndroidNdkApi: 30, buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, @@ -114,10 +117,10 @@ void main() async { 'target_android_ndk_api': 30, 'target_architecture': 'arm64', 'target_os': 'android', - 'version': BuildOutput.version.toString(), + 'version': BuildOutput.latestVersion.toString(), }); - final fromConfig = BuildConfig.fromConfig(config); + final fromConfig = BuildConfigImpl.fromConfig(config); expect(fromConfig, equals(buildConfig2)); }); @@ -126,8 +129,9 @@ void main() async { outDir: outDirUri, packageName: packageName, packageRoot: packageRootUri, - targetOs: OS.android, + targetOS: OS.android, linkModePreference: LinkModePreference.preferStatic, + supportedAssetTypes: [NativeCodeAsset.type], ); final config = Config(fileParsed: { @@ -137,75 +141,76 @@ void main() async { 'package_name': packageName, 'package_root': packageRootUri.toFilePath(), 'target_os': 'android', - 'version': BuildOutput.version.toString(), + 'version': BuildOutput.latestVersion.toString(), }); - final fromConfig = BuildConfig.fromConfig(config); + final fromConfig = BuildConfigImpl.fromConfig(config); expect(fromConfig, equals(buildConfig2)); }); test('BuildConfig == dependency metadata', () { - final buildConfig1 = BuildConfig( - outDir: outDirUri, + final buildConfig1 = BuildConfig.build( + outputDirectory: outDirUri, packageName: packageName, packageRoot: tempUri, targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetOS: OS.android, targetAndroidNdkApi: 30, buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, dependencyMetadata: { - 'bar': const Metadata({ + 'bar': { 'key': 'value', 'foo': ['asdf', 'fdsa'], - }), - 'foo': const Metadata({ + }, + 'foo': { 'key': 321, - }), + }, }, ); - final buildConfig2 = BuildConfig( - outDir: outDirUri, + final buildConfig2 = BuildConfig.build( + outputDirectory: outDirUri, packageName: packageName, packageRoot: tempUri, targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetOS: OS.android, targetAndroidNdkApi: 30, buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, dependencyMetadata: { - 'bar': const Metadata({ + 'bar': { 'key': 'value', - }), - 'foo': const Metadata({ + }, + 'foo': { 'key': 123, - }), + }, }, ); expect(buildConfig1, equals(buildConfig1)); expect(buildConfig1 == buildConfig2, false); expect(buildConfig1.hashCode == buildConfig2.hashCode, false); + + expect(buildConfig1.metadatum('bar', 'key'), 'value'); }); test('BuildConfig fromArgs', () async { - final buildConfig = BuildConfig( - outDir: outDirUri, + final buildConfig = BuildConfig.build( + outputDirectory: outDirUri, packageName: packageName, packageRoot: tempUri, targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetOS: OS.android, targetAndroidNdkApi: 30, buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, ); - final configFileContents = - (buildConfig as internal.BuildConfig).toYamlString(); + final configFileContents = (buildConfig as BuildConfigImpl).toYamlString(); final configUri = tempUri.resolve('config.yaml'); final configFile = File.fromUri(configUri); await configFile.writeAsString(configFileContents); - final buildConfigFromArgs = await BuildConfig.fromArgs( + final buildConfigFromArgs = BuildConfig( ['--config', configUri.toFilePath()], environment: {}, // Don't inherit the test environment. ); @@ -213,6 +218,6 @@ void main() async { }); test('BuildConfig.version', () { - BuildConfig.version.toString(); + BuildConfig.latestVersion.toString(); }); } diff --git a/pkgs/native_assets_cli/test/api/build_output_test.dart b/pkgs/native_assets_cli/test/api/build_output_test.dart index 48a2644b7..88993a72c 100644 --- a/pkgs/native_assets_cli/test/api/build_output_test.dart +++ b/pkgs/native_assets_cli/test/api/build_output_test.dart @@ -22,25 +22,28 @@ void main() { BuildOutput( timestamp: DateTime.parse('2022-11-10 13:25:01.000'), assets: [ - Asset( - id: 'foo', - path: AssetAbsolutePath(Uri(path: 'path/to/libfoo.so')), - target: Target.androidX64, - linkMode: LinkMode.dynamic, + NativeCodeAsset( + package: 'my_package', + name: 'foo', + file: Uri(path: 'path/to/libfoo.so'), + linkMode: DynamicLoadingBundled(), + os: OS.android, + architecture: Architecture.x64, ), - Asset( - id: 'foo2', - path: AssetSystemPath(Uri(path: 'path/to/libfoo2.so')), - target: Target.androidX64, - linkMode: LinkMode.dynamic, + NativeCodeAsset( + package: 'my_package', + name: 'foo2', + linkMode: DynamicLoadingSystem(Uri(path: 'path/to/libfoo2.so')), + os: OS.android, + architecture: Architecture.x64, ), ], - dependencies: Dependencies([ + dependencies: [ Uri.file('path/to/file.ext'), - ]), - metadata: const Metadata({ + ], + metadata: { 'key': 'value', - }), + }, ); }); } diff --git a/pkgs/native_assets_cli/test/api/build_test.dart b/pkgs/native_assets_cli/test/api/build_test.dart new file mode 100644 index 000000000..85b74d8ee --- /dev/null +++ b/pkgs/native_assets_cli/test/api/build_test.dart @@ -0,0 +1,71 @@ +// 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 'dart:io'; + +import 'package:file_testing/file_testing.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/src/api/build_config.dart'; +import 'package:native_assets_cli/src/api/build_output.dart'; +import 'package:test/test.dart'; + +void main() async { + late Uri tempUri; + late Uri outDirUri; + late String packageName; + late Uri packageRootUri; + late Uri fakeClang; + late Uri fakeLd; + late Uri fakeAr; + late Uri fakeCl; + late Uri fakeVcVars; + late Uri buildConfigUri; + + setUp(() async { + tempUri = (await Directory.systemTemp.createTemp()).uri; + outDirUri = tempUri.resolve('out1/'); + await Directory.fromUri(outDirUri).create(); + packageName = 'my_package'; + packageRootUri = tempUri.resolve('$packageName/'); + await Directory.fromUri(packageRootUri).create(); + fakeClang = tempUri.resolve('fake_clang'); + await File.fromUri(fakeClang).create(); + fakeLd = tempUri.resolve('fake_ld'); + await File.fromUri(fakeLd).create(); + fakeAr = tempUri.resolve('fake_ar'); + await File.fromUri(fakeAr).create(); + fakeCl = tempUri.resolve('cl.exe'); + await File.fromUri(fakeCl).create(); + fakeVcVars = tempUri.resolve('vcvarsall.bat'); + await File.fromUri(fakeVcVars).create(); + + final config1 = BuildConfig.build( + outputDirectory: outDirUri, + packageName: packageName, + packageRoot: tempUri, + targetArchitecture: Architecture.arm64, + targetOS: OS.iOS, + targetIOSSdk: IOSSdk.iPhoneOS, + cCompiler: CCompilerConfig( + compiler: fakeClang, + linker: fakeLd, + archiver: fakeAr, + ), + buildMode: BuildMode.release, + linkModePreference: LinkModePreference.preferDynamic, + ); + final configYaml = (config1 as BuildConfigImpl).toYamlString(); + buildConfigUri = tempUri.resolve('build_config.yaml'); + await File.fromUri(buildConfigUri).writeAsString(configYaml); + }); + + test('build method', () async { + await build(['--config', buildConfigUri.toFilePath()], + (config, output) async { + output.addDependency(packageRootUri.resolve('foo')); + }); + final buildOutputUri = outDirUri.resolve(BuildOutputImpl.fileName); + expect(File.fromUri(buildOutputUri), exists); + }); +} diff --git a/pkgs/native_assets_cli/test/api/target_test.dart b/pkgs/native_assets_cli/test/api/target_test.dart index 3f9084f18..4d44547d1 100644 --- a/pkgs/native_assets_cli/test/api/target_test.dart +++ b/pkgs/native_assets_cli/test/api/target_test.dart @@ -8,11 +8,6 @@ import 'package:native_assets_cli/native_assets_cli.dart'; import 'package:test/test.dart'; void main() { - test('Target current', () async { - final current = Target.current; - expect(current.toString(), Abi.current().toString()); - }); - test('OS current', () async { final current = OS.current; expect(current.toString(), Abi.current().toString().split('_').first); diff --git a/pkgs/native_assets_cli/test/example/local_asset_test.dart b/pkgs/native_assets_cli/test/example/local_asset_test.dart new file mode 100644 index 000000000..f007d22b6 --- /dev/null +++ b/pkgs/native_assets_cli/test/example/local_asset_test.dart @@ -0,0 +1,92 @@ +// 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. + +@OnPlatform({ + 'mac-os': Timeout.factor(2), + 'windows': Timeout.factor(10), +}) +library; + +import 'dart:io'; + +import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:test/test.dart'; + +import '../helpers.dart'; + +void main() async { + late Uri tempUri; + const name = 'local_asset'; + + setUp(() async { + tempUri = (await Directory.systemTemp.createTemp()).uri; + }); + + tearDown(() async { + await Directory.fromUri(tempUri).delete(recursive: true); + }); + + for (final dryRun in [true, false]) { + final testSuffix = dryRun ? ' dry_run' : ''; + test('local_asset build$testSuffix', () async { + final testTempUri = tempUri.resolve('test1/'); + await Directory.fromUri(testTempUri).create(); + final testPackageUri = packageUri.resolve('example/$name/'); + final dartUri = Uri.file(Platform.resolvedExecutable); + + final processResult = await Process.run( + dartUri.toFilePath(), + [ + 'build.dart', + '-Dout_dir=${tempUri.toFilePath()}', + '-Dpackage_name=$name', + '-Dpackage_root=${testPackageUri.toFilePath()}', + '-Dtarget_os=${OSImpl.current}', + '-Dversion=${BuildConfigImpl.latestVersion}', + '-Dlink_mode_preference=dynamic', + '-Ddry_run=$dryRun', + if (!dryRun) ...[ + '-Dtarget_architecture=${ArchitectureImpl.current}', + '-Dbuild_mode=debug', + if (cc != null) '-Dcc=${cc!.toFilePath()}', + if (envScript != null) + '-D${CCompilerConfigImpl.envScriptConfigKeyFull}=' + '${envScript!.toFilePath()}', + if (envScriptArgs != null) + '-D${CCompilerConfigImpl.envScriptArgsConfigKeyFull}=' + '${envScriptArgs!.join(' ')}', + ], + ], + workingDirectory: testPackageUri.toFilePath(), + ); + if (processResult.exitCode != 0) { + print(processResult.stdout); + print(processResult.stderr); + print(processResult.exitCode); + } + expect(processResult.exitCode, 0); + + final buildOutputUri = tempUri.resolve('build_output.yaml'); + final buildOutput = BuildOutputImpl.fromYamlString( + await File.fromUri(buildOutputUri).readAsString()); + final assets = buildOutput.assets; + final dependencies = buildOutput.dependencies; + if (dryRun) { + expect(assets.length, greaterThanOrEqualTo(1)); + expect(await File.fromUri(assets.first.file!).exists(), false); + expect(dependencies, []); + } else { + expect(assets.length, 1); + expect(await assets.allExist(), true); + expect( + dependencies, + [ + testPackageUri.resolve('data/asset.txt'), + testPackageUri.resolve('build.dart'), + ], + ); + } + }); + } +} 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 d7a0b3f84..0de664ef9 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 @@ -42,19 +42,19 @@ void main() async { '-Dout_dir=${tempUri.toFilePath()}', '-Dpackage_name=$name', '-Dpackage_root=${testPackageUri.toFilePath()}', - '-Dtarget_os=${OS.current}', - '-Dversion=${BuildConfig.version}', + '-Dtarget_os=${OSImpl.current}', + '-Dversion=${BuildConfigImpl.latestVersion}', '-Dlink_mode_preference=dynamic', '-Ddry_run=$dryRun', if (!dryRun) ...[ - '-Dtarget_architecture=${Architecture.current}', + '-Dtarget_architecture=${ArchitectureImpl.current}', '-Dbuild_mode=debug', if (cc != null) '-Dcc=${cc!.toFilePath()}', if (envScript != null) - '-D${CCompilerConfig.envScriptConfigKeyFull}=' + '-D${CCompilerConfigImpl.envScriptConfigKeyFull}=' '${envScript!.toFilePath()}', if (envScriptArgs != null) - '-D${CCompilerConfig.envScriptArgsConfigKeyFull}=' + '-D${CCompilerConfigImpl.envScriptArgsConfigKeyFull}=' '${envScriptArgs!.join(' ')}', ], ], @@ -68,22 +68,19 @@ void main() async { expect(processResult.exitCode, 0); final buildOutputUri = tempUri.resolve('build_output.yaml'); - final buildOutput = BuildOutput.fromYamlString( + final buildOutput = BuildOutputImpl.fromYamlString( await File.fromUri(buildOutputUri).readAsString()); final assets = buildOutput.assets; final dependencies = buildOutput.dependencies; if (dryRun) { expect(assets.length, greaterThanOrEqualTo(1)); - expect( - await File.fromUri((assets.first.path as AssetAbsolutePath).uri) - .exists(), - false); - expect(dependencies.dependencies, []); + expect(assets.first.file, isNull); + expect(dependencies, []); } else { expect(assets.length, 1); expect(await assets.allExist(), true); expect( - dependencies.dependencies, + dependencies, [ testPackageUri.resolve('src/$name.c'), testPackageUri.resolve('build.dart'), diff --git a/pkgs/native_assets_cli/test/helpers.dart b/pkgs/native_assets_cli/test/helpers.dart index e4c633c86..1b70aa739 100644 --- a/pkgs/native_assets_cli/test/helpers.dart +++ b/pkgs/native_assets_cli/test/helpers.dart @@ -78,35 +78,35 @@ String unparseKey(String key) => key.replaceAll('.', '__').toUpperCase(); /// /// Provided on Dart CI. final Uri? ar = Platform - .environment[unparseKey(internal.CCompilerConfig.arConfigKeyFull)] + .environment[unparseKey(internal.CCompilerConfigImpl.arConfigKeyFull)] ?.asFileUri(); /// Compiler provided by the environment. /// /// Provided on Dart CI. final Uri? cc = Platform - .environment[unparseKey(internal.CCompilerConfig.ccConfigKeyFull)] + .environment[unparseKey(internal.CCompilerConfigImpl.ccConfigKeyFull)] ?.asFileUri(); /// Linker provided by the environment. /// /// Provided on Dart CI. final Uri? ld = Platform - .environment[unparseKey(internal.CCompilerConfig.ldConfigKeyFull)] + .environment[unparseKey(internal.CCompilerConfigImpl.ldConfigKeyFull)] ?.asFileUri(); /// Path to script that sets environment variables for [cc], [ld], and [ar]. /// /// Provided on Dart CI. -final Uri? envScript = Platform - .environment[unparseKey(internal.CCompilerConfig.envScriptConfigKeyFull)] +final Uri? envScript = Platform.environment[ + unparseKey(internal.CCompilerConfigImpl.envScriptConfigKeyFull)] ?.asFileUri(); /// Arguments for [envScript] provided by environment. /// /// Provided on Dart CI. final List? envScriptArgs = Platform.environment[ - unparseKey(internal.CCompilerConfig.envScriptArgsConfigKeyFull)] + unparseKey(internal.CCompilerConfigImpl.envScriptArgsConfigKeyFull)] ?.split(' '); extension on String { @@ -123,10 +123,10 @@ extension AssetIterable on Iterable { extension on Asset { Future exists() async { - final path_ = path; + final path_ = file; return switch (path_) { - AssetAbsolutePath _ => await path_.uri.fileSystemEntity.exists(), - _ => true, + null => true, + _ => await path_.fileSystemEntity.exists(), }; } } diff --git a/pkgs/native_assets_cli/test/model/asset_test.dart b/pkgs/native_assets_cli/test/model/asset_test.dart index a7dd3bd0c..901932cc2 100644 --- a/pkgs/native_assets_cli/test/model/asset_test.dart +++ b/pkgs/native_assets_cli/test/model/asset_test.dart @@ -4,124 +4,198 @@ import 'package:collection/collection.dart'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/src/api/asset.dart'; +import 'package:native_assets_cli/src/utils/yaml.dart'; import 'package:test/test.dart'; +import 'package:yaml/yaml.dart'; void main() { final fooUri = Uri.file('path/to/libfoo.so'); final foo3Uri = Uri(path: 'libfoo3.so'); final barUri = Uri(path: 'path/to/libbar.a'); final blaUri = Uri(path: 'path/with spaces/bla.dll'); - final assets = [ - Asset( - id: 'foo', - path: AssetAbsolutePath(fooUri), - target: Target.androidX64, - linkMode: LinkMode.dynamic, + final dataUri = Uri.file('path/to/data.txt'); + final data2Uri = Uri.file('path/to/data.json'); + final nativeCodeAssets = [ + NativeCodeAssetImpl( + id: 'package:my_package/foo', + file: fooUri, + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, + ), + NativeCodeAssetImpl( + id: 'package:my_package/foo3', + linkMode: DynamicLoadingSystemImpl(foo3Uri), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, ), - Asset( - id: 'foo3', - path: AssetSystemPath(foo3Uri), - target: Target.androidX64, - linkMode: LinkMode.dynamic, + NativeCodeAssetImpl( + id: 'package:my_package/foo4', + linkMode: LookupInExecutableImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, ), - Asset( - id: 'foo4', - path: AssetInExecutable(), - target: Target.androidX64, - linkMode: LinkMode.dynamic, + NativeCodeAssetImpl( + id: 'package:my_package/foo5', + linkMode: LookupInProcessImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, ), - Asset( - id: 'foo5', - path: AssetInProcess(), - target: Target.androidX64, - linkMode: LinkMode.dynamic, + NativeCodeAssetImpl( + id: 'package:my_package/bar', + file: barUri, + os: OSImpl.linux, + architecture: ArchitectureImpl.arm64, + linkMode: StaticLinkingImpl(), ), - Asset( - id: 'bar', - path: AssetAbsolutePath(barUri), - target: Target.linuxArm64, - linkMode: LinkMode.static, + NativeCodeAssetImpl( + id: 'package:my_package/bla', + file: blaUri, + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.windows, + architecture: ArchitectureImpl.x64, + ), + ]; + final dataAssets = [ + DataAssetImpl( + id: 'package:my_package/my_data_asset', + file: dataUri, ), - Asset( - id: 'bla', - path: AssetAbsolutePath(blaUri), - target: Target.windowsX64, - linkMode: LinkMode.dynamic, + DataAssetImpl( + id: 'package:my_package/my_data_asset2', + file: data2Uri, ), ]; + final assets = [ + ...nativeCodeAssets, + ...dataAssets, + ]; - final assetsYamlEncoding = '''- id: foo + final assetsYamlEncodingV1_0_0 = '''- id: package:my_package/foo link_mode: dynamic path: path_type: absolute uri: ${fooUri.toFilePath()} target: android_x64 -- id: foo3 +- id: package:my_package/foo3 link_mode: dynamic path: path_type: system uri: ${foo3Uri.toFilePath()} target: android_x64 -- id: foo4 +- id: package:my_package/foo4 link_mode: dynamic path: path_type: executable target: android_x64 -- id: foo5 +- id: package:my_package/foo5 link_mode: dynamic path: path_type: process target: android_x64 -- id: bar +- id: package:my_package/bar link_mode: static path: path_type: absolute uri: ${barUri.toFilePath()} target: linux_arm64 -- id: bla +- id: package:my_package/bla link_mode: dynamic path: path_type: absolute uri: ${blaUri.toFilePath()} target: windows_x64'''; + final assetsYamlEncoding = '''- architecture: x64 + file: ${fooUri.toFilePath()} + id: package:my_package/foo + link_mode: + type: dynamic_loading_bundle + os: android + type: native_code +- architecture: x64 + id: package:my_package/foo3 + link_mode: + type: dynamic_loading_system + uri: ${foo3Uri.toFilePath()} + os: android + type: native_code +- architecture: x64 + id: package:my_package/foo4 + link_mode: + type: dynamic_loading_executable + os: android + type: native_code +- architecture: x64 + id: package:my_package/foo5 + link_mode: + type: dynamic_loading_process + os: android + type: native_code +- architecture: arm64 + file: ${barUri.toFilePath()} + id: package:my_package/bar + link_mode: + type: static + os: linux + type: native_code +- architecture: x64 + file: ${blaUri.toFilePath()} + id: package:my_package/bla + link_mode: + type: dynamic_loading_bundle + os: windows + type: native_code +- id: package:my_package/my_data_asset + file: ${dataUri.toFilePath()} + type: data +- id: package:my_package/my_data_asset2 + file: ${data2Uri.toFilePath()} + type: data'''; + test('asset yaml', () { - final yaml = assets.toYamlString(); + final yaml = yamlEncode([ + for (final item in assets) item.toYaml(BuildOutputImpl.latestVersion) + ]); expect(yaml, assetsYamlEncoding); - final assets2 = Asset.listFromYamlString(yaml); + final assets2 = AssetImpl.listFromYamlList(loadYaml(yaml) as YamlList); expect(assets, assets2); }); + test('build_output protocol v1.0.0 keeps working', () { + final assets2 = AssetImpl.listFromYamlList( + loadYaml(assetsYamlEncodingV1_0_0) as YamlList); + expect(nativeCodeAssets, assets2); + }); + test('AssetPath factory', () async { expect( - () => AssetPath('wrong', null), + () => DynamicLoadingImpl('wrong', null), throwsA(predicate( - (e) => e is FormatException && e.message.contains('Unknown pathType'), + (e) => e is FormatException && e.message.contains('Unknown type'), )), ); }); test('Asset hashCode copyWith', () async { - final asset = assets.first; + final asset = nativeCodeAssets.first; final asset2 = asset.copyWith(id: 'foo321'); expect(asset.hashCode != asset2.hashCode, true); final asset3 = asset.copyWith(); expect(asset.hashCode, asset3.hashCode); + + expect(dataAssets[0].hashCode, isNot(dataAssets[1].hashCode)); }); test('List hashCode', () async { - final assets2 = assets.take(3).toList(); - const equality = ListEquality(); - expect(equality.hash(assets) != equality.hash(assets2), true); + final assets2 = nativeCodeAssets.take(3).toList(); + const equality = ListEquality(); + expect(equality.hash(nativeCodeAssets) != equality.hash(assets2), true); }); test('Asset toString', () async { assets.toString(); }); - - test('Asset listFromYamlString', () async { - final assets = Asset.listFromYamlString(''); - expect(assets, []); - }); } 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 df9df7c6a..6eeb63535 100644 --- a/pkgs/native_assets_cli/test/model/build_config_test.dart +++ b/pkgs/native_assets_cli/test/model/build_config_test.dart @@ -6,6 +6,7 @@ import 'dart:io'; import 'package:cli_config/cli_config.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'; import '../helpers.dart'; @@ -48,61 +49,61 @@ void main() async { }); test('BuildConfig ==', () { - final config1 = BuildConfig( + final config1 = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOs, - cCompiler: CCompilerConfig( - cc: fakeClang, - ld: fakeLd, - ar: fakeAr, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.iOS, + targetIOSSdk: IOSSdkImpl.iPhoneOS, + cCompiler: CCompilerConfigImpl( + compiler: fakeClang, + linker: fakeLd, + archiver: fakeAr, ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, ); - final config2 = BuildConfig( + final config2 = BuildConfigImpl( outDir: outDir2Uri, packageName: packageName, packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.android, targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, ); expect(config1, equals(config1)); expect(config1 == config2, false); - expect(config1.outDir != config2.outDir, true); + expect(config1.outputDirectory != config2.outputDirectory, true); expect(config1.packageRoot, config2.packageRoot); expect(config1.targetArchitecture == config2.targetArchitecture, true); - expect(config1.targetOs != config2.targetOs, true); - expect(config1.targetIOSSdk != config2.targetIOSSdk, true); - expect(config1.cCompiler.cc != config2.cCompiler.cc, true); - expect(config1.cCompiler.ld != config2.cCompiler.ld, true); - expect(config1.cCompiler.ar != config2.cCompiler.ar, true); + expect(config1.targetOS != config2.targetOS, true); + expect(config1.targetIOSSdk, IOSSdkImpl.iPhoneOS); + expect(() => config2.targetIOSSdk, throwsStateError); + expect(config1.cCompiler.compiler != config2.cCompiler.compiler, true); + expect(config1.cCompiler.linker != config2.cCompiler.linker, true); + expect(config1.cCompiler.archiver != config2.cCompiler.archiver, true); expect(config1.cCompiler.envScript == config2.cCompiler.envScript, true); expect(config1.cCompiler.envScriptArgs == config2.cCompiler.envScriptArgs, true); expect(config1.cCompiler != config2.cCompiler, true); expect(config1.linkModePreference, config2.linkModePreference); - expect(config1.dependencyMetadata, config2.dependencyMetadata); }); test('BuildConfig fromConfig', () { - final buildConfig2 = BuildConfig( + final buildConfig2 = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: packageRootUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.android, targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, ); final config = Config(fileParsed: { @@ -115,20 +116,20 @@ void main() async { 'target_android_ndk_api': 30, 'target_architecture': 'arm64', 'target_os': 'android', - 'version': BuildOutput.version.toString(), + 'version': BuildOutputImpl.latestVersion.toString(), }); - final fromConfig = BuildConfig.fromConfig(config); + final fromConfig = BuildConfigImpl.fromConfig(config); expect(fromConfig, equals(buildConfig2)); }); test('BuildConfig.dryRun', () { - final buildConfig2 = BuildConfig.dryRun( + final buildConfig2 = BuildConfigImpl.dryRun( outDir: outDirUri, packageName: packageName, packageRoot: packageRootUri, - targetOs: OS.android, - linkModePreference: LinkModePreference.preferStatic, + targetOS: OSImpl.android, + linkModePreference: LinkModePreferenceImpl.preferStatic, ); final config = Config(fileParsed: { @@ -138,45 +139,45 @@ void main() async { 'package_name': packageName, 'package_root': packageRootUri.toFilePath(), 'target_os': 'android', - 'version': BuildOutput.version.toString(), + 'version': BuildOutputImpl.latestVersion.toString(), }); - final fromConfig = BuildConfig.fromConfig(config); + final fromConfig = BuildConfigImpl.fromConfig(config); expect(fromConfig, equals(buildConfig2)); }); test('BuildConfig toYaml fromConfig', () { - final buildConfig1 = BuildConfig( + final buildConfig1 = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: packageRootUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOs, - cCompiler: CCompilerConfig( - cc: fakeClang, - ld: fakeLd, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.iOS, + targetIOSSdk: IOSSdkImpl.iPhoneOS, + cCompiler: CCompilerConfigImpl( + compiler: fakeClang, + linker: fakeLd, ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, ); final configFile = buildConfig1.toYaml(); final config = Config(fileParsed: configFile); - final fromConfig = BuildConfig.fromConfig(config); + final fromConfig = BuildConfigImpl.fromConfig(config); expect(fromConfig, equals(buildConfig1)); }); test('BuildConfig == dependency metadata', () { - final buildConfig1 = BuildConfig( + final buildConfig1 = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.android, targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, dependencyMetadata: { 'bar': const Metadata({ 'key': 'value', @@ -188,15 +189,15 @@ void main() async { }, ); - final buildConfig2 = BuildConfig( + final buildConfig2 = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.android, targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, dependencyMetadata: { 'bar': const Metadata({ 'key': 'value', @@ -214,19 +215,19 @@ void main() async { test('BuildConfig toYaml fromYaml', () { final outDir = outDirUri; - final buildConfig1 = BuildConfig( + final buildConfig1 = BuildConfigImpl( outDir: outDir, packageName: packageName, packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOs, - cCompiler: CCompilerConfig( - cc: fakeClang, - ld: fakeLd, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.iOS, + targetIOSSdk: IOSSdkImpl.iPhoneOS, + cCompiler: CCompilerConfigImpl( + compiler: fakeClang, + linker: fakeLd, ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, // This map should be sorted on key for two layers. dependencyMetadata: { 'foo': const Metadata({ @@ -255,13 +256,70 @@ link_mode_preference: prefer-static out_dir: ${outDir.toFilePath()} package_name: $packageName package_root: ${tempUri.toFilePath()} +supported_asset_types: + - ${NativeCodeAsset.type} target_architecture: arm64 target_ios_sdk: iphoneos target_os: ios -version: ${BuildConfig.version}'''; +version: ${BuildConfigImpl.latestVersion}'''; expect(yamlString, equals(expectedYamlString)); - final buildConfig2 = BuildConfig.fromConfig( + final buildConfig2 = BuildConfigImpl.fromConfig( + Config.fromConfigFileContents( + fileContents: yamlString, + ), + ); + expect(buildConfig2, buildConfig1); + }); + + test('BuildConfig fromYaml v1.0.0 keeps working', () { + final outDir = outDirUri; + final yamlString = '''build_mode: release +c_compiler: + cc: ${fakeClang.toFilePath()} + ld: ${fakeLd.toFilePath()} +dependency_metadata: + bar: + key: value + foo: + a: 321 + z: + - z + - a +link_mode_preference: prefer-static +out_dir: ${outDir.toFilePath()} +package_name: $packageName +package_root: ${tempUri.toFilePath()} +target_architecture: arm64 +target_ios_sdk: iphoneos +target_os: ios +version: 1.0.0'''; + final buildConfig1 = BuildConfigImpl( + outDir: outDir, + packageName: packageName, + packageRoot: tempUri, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.iOS, + targetIOSSdk: IOSSdkImpl.iPhoneOS, + cCompiler: CCompilerConfigImpl( + compiler: fakeClang, + linker: fakeLd, + ), + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, + // This map should be sorted on key for two layers. + dependencyMetadata: { + 'foo': const Metadata({ + 'z': ['z', 'a'], + 'a': 321, + }), + 'bar': const Metadata({ + 'key': 'value', + }), + }, + ); + + final buildConfig2 = BuildConfigImpl.fromConfig( Config.fromConfigFileContents( fileContents: yamlString, ), @@ -271,7 +329,7 @@ version: ${BuildConfig.version}'''; test('BuildConfig FormatExceptions', () { expect( - () => BuildConfig.fromConfig(Config(fileParsed: {})), + () => BuildConfigImpl.fromConfig(Config(fileParsed: {})), throwsA(predicate( (e) => e is FormatException && @@ -281,8 +339,8 @@ version: ${BuildConfig.version}'''; )), ); expect( - () => BuildConfig.fromConfig(Config(fileParsed: { - 'version': BuildConfig.version.toString(), + () => BuildConfigImpl.fromConfig(Config(fileParsed: { + 'version': BuildConfigImpl.latestVersion.toString(), 'package_name': packageName, 'package_root': packageRootUri.toFilePath(), 'target_architecture': 'arm64', @@ -299,8 +357,8 @@ version: ${BuildConfig.version}'''; )), ); expect( - () => BuildConfig.fromConfig(Config(fileParsed: { - 'version': BuildConfig.version.toString(), + () => BuildConfigImpl.fromConfig(Config(fileParsed: { + 'version': BuildConfigImpl.latestVersion.toString(), 'out_dir': outDirUri.toFilePath(), 'package_name': packageName, 'package_root': packageRootUri.toFilePath(), @@ -323,9 +381,9 @@ version: ${BuildConfig.version}'''; )), ); expect( - () => BuildConfig.fromConfig(Config(fileParsed: { + () => BuildConfigImpl.fromConfig(Config(fileParsed: { 'out_dir': outDirUri.toFilePath(), - 'version': BuildConfig.version.toString(), + 'version': BuildConfigImpl.latestVersion.toString(), 'package_name': packageName, 'package_root': packageRootUri.toFilePath(), 'target_architecture': 'arm64', @@ -344,7 +402,7 @@ version: ${BuildConfig.version}'''; test('FormatExceptions contain full stack trace of wrapped exception', () { try { - BuildConfig.fromConfig(Config(fileParsed: { + BuildConfigImpl.fromConfig(Config(fileParsed: { 'out_dir': outDirUri.toFilePath(), 'package_root': packageRootUri.toFilePath(), 'target': [1, 2, 3, 4, 5], @@ -356,39 +414,39 @@ version: ${BuildConfig.version}'''; }); test('BuildConfig toString', () { - final config = BuildConfig( + final config = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOs, - cCompiler: CCompilerConfig( - cc: fakeClang, - ld: fakeLd, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.iOS, + targetIOSSdk: IOSSdkImpl.iPhoneOS, + cCompiler: CCompilerConfigImpl( + compiler: fakeClang, + linker: fakeLd, ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, ); config.toString(); }); test('BuildConfig fromArgs', () async { - final buildConfig = BuildConfig( + final buildConfig = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.android, targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, ); final configFileContents = buildConfig.toYamlString(); final configUri = tempUri.resolve('config.yaml'); final configFile = File.fromUri(configUri); await configFile.writeAsString(configFileContents); - final buildConfig2 = await BuildConfig.fromArgs( + final buildConfig2 = BuildConfigImpl.fromArguments( ['--config', configUri.toFilePath()], environment: {}, // Don't inherit the test environment. ); @@ -396,15 +454,15 @@ version: ${BuildConfig.version}'''; }); test('dependency metadata via config accessor', () { - final buildConfig1 = BuildConfig( + final buildConfig1 = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOs: OS.android, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.android, targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.preferStatic, dependencyMetadata: { 'bar': const Metadata({ 'key': {'key2': 'value'}, @@ -414,7 +472,7 @@ version: ${BuildConfig.version}'''; // Useful for doing `path(..., exists: true)`. expect( buildConfig1.config.string([ - BuildConfig.dependencyMetadataConfigKey, + BuildConfigImpl.dependencyMetadataConfigKey, 'bar', 'key', 'key2' @@ -424,24 +482,24 @@ version: ${BuildConfig.version}'''; }); test('envScript', () { - final buildConfig1 = BuildConfig( + final buildConfig1 = BuildConfigImpl( outDir: outDirUri, packageName: packageName, packageRoot: packageRootUri, - targetArchitecture: Architecture.x64, - targetOs: OS.windows, - cCompiler: CCompilerConfig( - cc: fakeCl, + targetArchitecture: ArchitectureImpl.x64, + targetOS: OSImpl.windows, + cCompiler: CCompilerConfigImpl( + compiler: fakeCl, envScript: fakeVcVars, envScriptArgs: ['x64'], ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.dynamic, ); final configFile = buildConfig1.toYaml(); final config = Config(fileParsed: configFile); - final fromConfig = BuildConfig.fromConfig(config); + final fromConfig = BuildConfigImpl.fromConfig(config); expect(fromConfig, equals(buildConfig1)); }); @@ -457,12 +515,12 @@ version: ${BuildConfig.version}'''; 'version': version, }); expect( - () => BuildConfig.fromConfig(config), + () => BuildConfigImpl.fromConfig(config), throwsA(predicate( (e) => e is FormatException && e.message.contains(version) && - e.message.contains(BuildConfig.version.toString()), + e.message.contains(BuildConfigImpl.latestVersion.toString()), )), ); }); @@ -474,26 +532,27 @@ version: ${BuildConfig.version}'''; final fakeClangUri = tempUri.resolve('fake_clang'); await File.fromUri(fakeClangUri).create(); - final name1 = BuildConfig.checksum( + final name1 = BuildConfigImpl.checksum( packageName: packageName, packageRoot: nativeAddUri, - targetArchitecture: Architecture.x64, - targetOs: OS.linux, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, + targetArchitecture: ArchitectureImpl.x64, + targetOS: OSImpl.linux, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.dynamic, + supportedAssetTypes: [NativeCodeAsset.type], ); // Using the checksum for a build folder should be stable. - expect(name1, '037109b9824b2559502fa7bd42e1b6f8'); + expect(name1, 'd95547e3b45e88a475739eb4fe03600a'); // Build folder different due to metadata. - final name2 = BuildConfig.checksum( + final name2 = BuildConfigImpl.checksum( packageName: packageName, packageRoot: nativeAddUri, - targetArchitecture: Architecture.x64, - targetOs: OS.linux, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, + targetArchitecture: ArchitectureImpl.x64, + targetOS: OSImpl.linux, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.dynamic, dependencyMetadata: { 'foo': const Metadata({'key': 'value'}) }, @@ -502,15 +561,15 @@ version: ${BuildConfig.version}'''; expect(name1 != name2, true); // Build folder different due to cc. - final name3 = BuildConfig.checksum( + final name3 = BuildConfigImpl.checksum( packageName: packageName, packageRoot: nativeAddUri, - targetArchitecture: Architecture.x64, - targetOs: OS.linux, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - cCompiler: CCompilerConfig( - cc: fakeClangUri, + targetArchitecture: ArchitectureImpl.x64, + targetOS: OSImpl.linux, + buildMode: BuildModeImpl.release, + linkModePreference: LinkModePreferenceImpl.dynamic, + cCompiler: CCompilerConfigImpl( + compiler: fakeClangUri, )); printOnFailure([name1, name3].toString()); expect(name1 != name3, true); @@ -527,10 +586,10 @@ version: ${BuildConfig.version}'''; 'target_os': 'windows', 'target_architecture': 'arm', 'build_mode': 'debug', - 'version': BuildConfig.version.toString(), + 'version': BuildConfigImpl.latestVersion.toString(), }); expect( - () => BuildConfig.fromConfig(config), + () => BuildConfigImpl.fromConfig(config), throwsA(predicate( (e) => e is FormatException && e.message.contains('arm'), )), @@ -548,10 +607,10 @@ version: ${BuildConfig.version}'''; 'target_architecture': 'arm64', 'build_mode': 'debug', 'dry_run': true, - 'version': BuildConfig.version.toString(), + 'version': BuildConfigImpl.latestVersion.toString(), }); expect( - () => BuildConfig.fromConfig(config), + () => BuildConfigImpl.fromConfig(config), throwsA(predicate( (e) => e is FormatException && e.message.contains('In Flutter projects'), @@ -566,28 +625,62 @@ version: ${BuildConfig.version}'''; 'out_dir': outDir.toFilePath(), 'package_name': packageName, 'package_root': tempUri.toFilePath(), - 'target_os': 'windows', + 'target_os': 'android', 'dry_run': true, - 'version': BuildConfig.version.toString(), + 'version': BuildConfigImpl.latestVersion.toString(), }); - final buildConfig = BuildConfig.fromConfig(config); + final buildConfig = BuildConfigImpl.fromConfig(config); expect( - () => buildConfig.targetArchitecture, + () => buildConfig.targetAndroidNdkApi, throwsA(predicate( (e) => e is StateError && e.message.contains('In Flutter projects'), )), ); }); - test('BuildConfig dry_run access invalid args', () { - final buildConfig = BuildConfig.dryRun( + test('BuildConfig dry_run target arch', () { + final outDir = outDirUri; + final config = Config(fileParsed: { + 'link_mode_preference': 'prefer-static', + 'out_dir': outDir.toFilePath(), + 'package_name': packageName, + 'package_root': tempUri.toFilePath(), + 'target_os': 'windows', + 'dry_run': true, + 'version': BuildConfigImpl.latestVersion.toString(), + }); + final buildConfig = BuildConfigImpl.fromConfig(config); + expect(buildConfig.targetArchitecture, isNull); + }); + + test('BuildConfig dry_run toString', () { + final buildConfig = BuildConfigImpl.dryRun( packageName: packageName, outDir: outDirUri, packageRoot: tempUri, - targetOs: OS.windows, - linkModePreference: LinkModePreference.dynamic, + targetOS: OSImpl.windows, + linkModePreference: LinkModePreferenceImpl.dynamic, ); buildConfig.toYamlString(); // No crash. }); + + test('invalid architecture', () { + final config = Config(fileParsed: { + 'build_mode': 'release', + 'dry_run': false, + 'link_mode_preference': 'prefer-static', + 'out_dir': outDirUri.toFilePath(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_android_ndk_api': 30, + 'target_architecture': 'invalid_architecture', + 'target_os': 'android', + 'version': BuildOutputImpl.latestVersion.toString(), + }); + expect( + () => BuildConfigImpl.fromConfig(config), + throwsFormatException, + ); + }); } 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 8ac830685..3b6ea00d3 100644 --- a/pkgs/native_assets_cli/test/model/build_output_test.dart +++ b/pkgs/native_assets_cli/test/model/build_output_test.dart @@ -5,6 +5,8 @@ import 'dart:io'; import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/src/utils/yaml.dart'; +import 'package:pub_semver/pub_semver.dart'; import 'package:test/test.dart'; void main() { @@ -18,20 +20,33 @@ void main() { await Directory.fromUri(tempUri).delete(recursive: true); }); - final buildOutput = BuildOutput( + final buildOutput = BuildOutputImpl( timestamp: DateTime.parse('2022-11-10 13:25:01.000'), assets: [ - Asset( - id: 'foo', - path: AssetAbsolutePath(Uri(path: 'path/to/libfoo.so')), - target: Target.androidX64, - linkMode: LinkMode.dynamic, + NativeCodeAssetImpl( + id: 'package:my_package/foo', + file: Uri(path: 'path/to/libfoo.so'), + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, ), - Asset( - id: 'foo2', - path: AssetSystemPath(Uri(path: 'path/to/libfoo2.so')), - target: Target.androidX64, - linkMode: LinkMode.dynamic, + NativeCodeAssetImpl( + id: 'package:my_package/foo2', + linkMode: DynamicLoadingSystemImpl(Uri(path: 'path/to/libfoo2.so')), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, + ), + NativeCodeAssetImpl( + id: 'package:my_package/foo3', + linkMode: LookupInProcessImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, + ), + NativeCodeAssetImpl( + id: 'package:my_package/foo4', + linkMode: LookupInExecutableImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, ), ], dependencies: Dependencies([ @@ -42,41 +57,99 @@ void main() { }), ); - final yamlEncoding = '''timestamp: 2022-11-10 13:25:01.000 + const yamlEncodingV1_0_0 = '''timestamp: 2022-11-10 13:25:01.000 assets: - - id: foo + - id: package:my_package/foo link_mode: dynamic path: path_type: absolute uri: path/to/libfoo.so target: android_x64 - - id: foo2 + - id: package:my_package/foo2 link_mode: dynamic path: path_type: system uri: path/to/libfoo2.so target: android_x64 + - id: package:my_package/foo3 + link_mode: dynamic + path: + path_type: process + target: android_x64 + - id: package:my_package/foo4 + link_mode: dynamic + path: + path_type: executable + target: android_x64 +dependencies: + - path/to/file.ext +metadata: + key: value +version: 1.0.0'''; + + final yamlEncoding = '''timestamp: 2022-11-10 13:25:01.000 +assets: + - architecture: x64 + file: path/to/libfoo.so + id: package:my_package/foo + link_mode: + type: dynamic_loading_bundle + os: android + type: native_code + - architecture: x64 + id: package:my_package/foo2 + link_mode: + type: dynamic_loading_system + uri: path/to/libfoo2.so + os: android + type: native_code + - architecture: x64 + id: package:my_package/foo3 + link_mode: + type: dynamic_loading_process + os: android + type: native_code + - architecture: x64 + id: package:my_package/foo4 + link_mode: + type: dynamic_loading_executable + os: android + type: native_code dependencies: - path/to/file.ext metadata: key: value -version: ${BuildOutput.version}'''; +version: ${BuildOutputImpl.latestVersion}'''; test('built info yaml', () { - final yaml = buildOutput.toYamlString().replaceAll('\\', '/'); + final yaml = buildOutput + .toYamlString(BuildOutputImpl.latestVersion) + .replaceAll('\\', '/'); expect(yaml, yamlEncoding); - final buildOutput2 = BuildOutput.fromYamlString(yaml); + final buildOutput2 = BuildOutputImpl.fromYamlString(yaml); + expect(buildOutput.hashCode, buildOutput2.hashCode); + expect(buildOutput, buildOutput2); + }); + + test('built info yaml v1.0.0 parsing keeps working', () { + final buildOutput2 = BuildOutputImpl.fromYamlString(yamlEncodingV1_0_0); expect(buildOutput.hashCode, buildOutput2.hashCode); expect(buildOutput, buildOutput2); }); + test('built info yaml v1.0.0 serialization keeps working', () { + final yamlEncoding = + yamlEncode(buildOutput.toYaml(Version(1, 0, 0))).replaceAll('\\', '/'); + expect(yamlEncoding, yamlEncodingV1_0_0); + }); + test('BuildOutput.toString', buildOutput.toString); test('BuildOutput.hashCode', () { - final buildOutput2 = BuildOutput.fromYamlString(yamlEncoding); + final buildOutput2 = BuildOutputImpl.fromYamlString(yamlEncoding); expect(buildOutput.hashCode, buildOutput2.hashCode); - final buildOutput3 = BuildOutput( + final buildOutput3 = BuildOutputImpl( timestamp: DateTime.parse('2022-11-10 13:25:01.000'), ); expect(buildOutput.hashCode != buildOutput3.hashCode, true); @@ -84,13 +157,25 @@ version: ${BuildOutput.version}'''; test('BuildOutput.readFromFile BuildOutput.writeToFile', () async { final outDir = tempUri.resolve('out_dir/'); - await buildOutput.writeToFile(outDir: outDir); - final buildOutput2 = await BuildOutput.readFromFile(outDir: outDir); + final packageRoot = tempUri.resolve('package_root/'); + await Directory.fromUri(outDir).create(); + await Directory.fromUri(packageRoot).create(); + final config = BuildConfigImpl( + outDir: outDir, + packageName: 'dontcare', + packageRoot: packageRoot, + buildMode: BuildModeImpl.debug, + targetArchitecture: ArchitectureImpl.arm64, + targetOS: OSImpl.macOS, + linkModePreference: LinkModePreferenceImpl.dynamic, + ); + await buildOutput.writeToFile(config: config); + final buildOutput2 = await BuildOutputImpl.readFromFile(outDir: outDir); expect(buildOutput2, buildOutput); }); test('Round timestamp', () { - final buildOutput3 = BuildOutput( + final buildOutput3 = BuildOutputImpl( timestamp: DateTime.parse('2022-11-10 13:25:01.372257'), ); expect(buildOutput3.timestamp, DateTime.parse('2022-11-10 13:25:01.000')); @@ -99,12 +184,12 @@ version: ${BuildOutput.version}'''; for (final version in ['9001.0.0', '0.0.1']) { test('BuildOutput version $version', () { expect( - () => BuildOutput.fromYamlString('version: $version'), + () => BuildOutputImpl.fromYamlString('version: $version'), throwsA(predicate( (e) => e is FormatException && e.message.contains(version) && - e.message.contains(BuildConfig.version.toString()), + e.message.contains(BuildOutputImpl.latestVersion.toString()), )), ); }); @@ -112,7 +197,7 @@ version: ${BuildOutput.version}'''; test('format exception', () { expect( - () => BuildOutput.fromYamlString('''timestamp: 2022-11-10 13:25:01.000 + () => BuildOutputImpl.fromYamlString('''timestamp: 2022-11-10 13:25:01.000 assets: - name: foo link_mode: dynamic @@ -124,11 +209,11 @@ assets: dependencies: [] metadata: key: value -version: ${BuildOutput.version}'''), +version: 1.0.0'''), throwsFormatException, ); expect( - () => BuildOutput.fromYamlString('''timestamp: 2022-11-10 13:25:01.000 + () => BuildOutputImpl.fromYamlString('''timestamp: 2022-11-10 13:25:01.000 assets: - name: foo link_mode: dynamic @@ -140,11 +225,11 @@ dependencies: 1: foo metadata: key: value -version: ${BuildOutput.version}'''), +version: 1.0.0'''), throwsFormatException, ); expect( - () => BuildOutput.fromYamlString('''timestamp: 2022-11-10 13:25:01.000 + () => BuildOutputImpl.fromYamlString('''timestamp: 2022-11-10 13:25:01.000 assets: - name: foo link_mode: dynamic @@ -155,19 +240,81 @@ assets: dependencies: [] metadata: 123: value -version: ${BuildOutput.version}'''), +version: 1.0.0'''), throwsFormatException, ); }); test('BuildOutput dependencies can be modified', () { - // TODO(https://github.com/dart-lang/native/issues/25): - // Remove once dependencies are made immutable. - final buildOutput = BuildOutput(); + final buildOutput = BuildOutputImpl(); expect( - () => buildOutput.dependencies.dependencies - .add(Uri.file('path/to/file.ext')), + () => buildOutput.addDependencies([Uri.file('path/to/file.ext')]), returnsNormally, ); }); + + test('BuildOutput setters', () { + final buildOutput = BuildOutputImpl( + timestamp: DateTime.parse('2022-11-10 13:25:01.000'), + assets: [ + NativeCodeAssetImpl( + id: 'package:my_package/foo', + file: Uri(path: 'path/to/libfoo.so'), + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, + ), + NativeCodeAssetImpl( + id: 'package:my_package/foo2', + linkMode: DynamicLoadingSystemImpl(Uri(path: 'path/to/libfoo2.so')), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, + ), + ], + dependencies: Dependencies([ + Uri.file('path/to/file.ext'), + Uri.file('path/to/file2.ext'), + ]), + metadata: const Metadata({ + 'key': 'value', + 'key2': 'value2', + }), + ); + + final buildOutput2 = BuildOutputImpl( + timestamp: DateTime.parse('2022-11-10 13:25:01.000'), + ); + buildOutput2.addAsset( + NativeCodeAssetImpl( + id: 'package:my_package/foo', + file: Uri(path: 'path/to/libfoo.so'), + linkMode: DynamicLoadingBundledImpl(), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, + ), + ); + buildOutput2.addAssets([ + NativeCodeAssetImpl( + id: 'package:my_package/foo2', + linkMode: DynamicLoadingSystemImpl(Uri(path: 'path/to/libfoo2.so')), + os: OSImpl.android, + architecture: ArchitectureImpl.x64, + ), + ]); + buildOutput2.addDependency( + Uri.file('path/to/file.ext'), + ); + buildOutput2.addDependencies([ + Uri.file('path/to/file2.ext'), + ]); + buildOutput2.addMetadata({ + 'key': 'value', + }); + buildOutput2.addMetadatum('key2', 'value2'); + + expect(buildOutput2, equals(buildOutput)); + expect( + buildOutput2.dependenciesModel, equals(buildOutput.dependenciesModel)); + expect(buildOutput2.metadataModel, equals(buildOutput.metadataModel)); + }); } diff --git a/pkgs/native_assets_cli/test/model/link_mode_test.dart b/pkgs/native_assets_cli/test/model/link_mode_test.dart index ba86d444c..d27fdc129 100644 --- a/pkgs/native_assets_cli/test/model/link_mode_test.dart +++ b/pkgs/native_assets_cli/test/model/link_mode_test.dart @@ -7,6 +7,6 @@ import 'package:test/test.dart'; void main() { test('LinkMode toString', () async { - LinkMode.static.toString(); + StaticLinking().toString(); }); } diff --git a/pkgs/native_assets_cli/test/model/target_test.dart b/pkgs/native_assets_cli/test/model/target_test.dart index 412082b2a..ac6a34b76 100644 --- a/pkgs/native_assets_cli/test/model/target_test.dart +++ b/pkgs/native_assets_cli/test/model/target_test.dart @@ -10,13 +10,15 @@ import 'package:test/test.dart'; void main() { test('OS naming conventions', () async { - expect(OS.android.dylibFileName('foo'), 'libfoo.so'); - expect(OS.android.staticlibFileName('foo'), 'libfoo.a'); - expect(OS.windows.dylibFileName('foo'), 'foo.dll'); - expect(OS.windows.libraryFileName('foo', LinkMode.dynamic), 'foo.dll'); - expect(OS.windows.staticlibFileName('foo'), 'foo.lib'); - expect(OS.windows.libraryFileName('foo', LinkMode.static), 'foo.lib'); - expect(OS.windows.executableFileName('foo'), 'foo.exe'); + 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( + OSImpl.windows.libraryFileName('foo', StaticLinkingImpl()), 'foo.lib'); + expect(OSImpl.windows.executableFileName('foo'), 'foo.exe'); }); test('Target current', () async { @@ -57,13 +59,13 @@ void main() { Target.macOSArm64.supportedTargetTargets(), contains(Target.iOSArm64)); }); - test('Target fromArchitectureAndOs', () async { + test('Target fromArchitectureAndOS', () async { final current = - Target.fromArchitectureAndOs(Architecture.current, OS.current); + Target.fromArchitectureAndOS(ArchitectureImpl.current, OSImpl.current); expect(current.toString(), Abi.current().toString()); expect( - () => Target.fromArchitectureAndOs(Architecture.arm, OS.windows), + () => Target.fromArchitectureAndOS(ArchitectureImpl.arm, OSImpl.windows), throwsA(predicate( (e) => e is ArgumentError && @@ -72,4 +74,14 @@ 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 a04137bde..9e7e56f96 100644 --- a/pkgs/native_toolchain_c/CHANGELOG.md +++ b/pkgs/native_toolchain_c/CHANGELOG.md @@ -1,6 +1,6 @@ -## 0.3.5-wip +## 0.4.0-wip -- Bump `package:native_assets_cli` to 0.4.1. +- **Breaking change** Completely rewritten API in `native_assets_cli`. ## 0.3.4+1 diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart index a0a199b0d..1476c3bb6 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart @@ -44,10 +44,10 @@ class CBuilder implements Builder { /// Name of the library or executable to build. /// - /// The filename will be decided by [BuildConfig.target] and + /// The filename will be decided by [BuildConfig.targetOS] and /// [OS.libraryFileName] or [OS.executableFileName]. /// - /// File will be placed in [BuildConfig.outDir]. + /// File will be placed in [BuildConfig.outputDirectory]. final String name; /// Asset identifier. @@ -55,7 +55,7 @@ class CBuilder implements Builder { /// Used to output the [BuildOutput.assets]. /// /// If omitted, no asset will be added to the build output. - final String? assetId; + final String? assetName; /// Sources to build the library or executable. /// @@ -154,7 +154,7 @@ class CBuilder implements Builder { CBuilder.library({ required this.name, - required this.assetId, + required this.assetName, this.sources = const [], this.includes = const [], this.dartBuildFiles = const ['build.dart'], @@ -183,7 +183,7 @@ class CBuilder implements Builder { this.language = Language.c, this.cppLinkStdLib, }) : _type = _CBuilderType.executable, - assetId = null, + assetName = null, installName = null, pic = pie; @@ -196,14 +196,14 @@ class CBuilder implements Builder { required BuildOutput buildOutput, required Logger? logger, }) async { - final outDir = buildConfig.outDir; + final outDir = buildConfig.outputDirectory; final packageRoot = buildConfig.packageRoot; await Directory.fromUri(outDir).create(recursive: true); - final linkMode = buildConfig.linkModePreference.preferredLinkMode; + final linkMode = _linkMode(buildConfig.linkModePreference); final libUri = - outDir.resolve(buildConfig.targetOs.libraryFileName(name, linkMode)); + outDir.resolve(buildConfig.targetOS.libraryFileName(name, linkMode)); final exeUri = - outDir.resolve(buildConfig.targetOs.executableFileName(name)); + outDir.resolve(buildConfig.targetOS.executableFileName(name)); final sources = [ for (final source in this.sources) packageRoot.resolveUri(Uri.file(source)), @@ -221,12 +221,12 @@ class CBuilder implements Builder { logger: logger, sources: sources, includes: includes, - dynamicLibrary: - _type == _CBuilderType.library && linkMode == LinkMode.dynamic - ? libUri - : null, + dynamicLibrary: _type == _CBuilderType.library && + linkMode == DynamicLoadingBundled() + ? libUri + : null, staticLibrary: - _type == _CBuilderType.library && linkMode == LinkMode.static + _type == _CBuilderType.library && linkMode == StaticLinking() ? libUri : null, executable: _type == _CBuilderType.executable ? exeUri : null, @@ -246,22 +246,18 @@ class CBuilder implements Builder { await task.run(); } - if (assetId != null) { - final targets = [ - if (!buildConfig.dryRun) - buildConfig.target - else - for (final target in Target.values) - if (target.os == buildConfig.targetOs) target - ]; - for (final target in targets) { - buildOutput.assets.add(Asset( - id: assetId!, + if (assetName != null) { + buildOutput.addAssets([ + NativeCodeAsset( + package: buildConfig.packageName, + name: assetName!, + file: buildConfig.dryRun ? null : libUri, linkMode: linkMode, - target: target, - path: AssetAbsolutePath(libUri), - )); - } + os: buildConfig.targetOS, + architecture: + buildConfig.dryRun ? null : buildConfig.targetArchitecture, + ) + ]); } if (!buildConfig.dryRun) { final includeFiles = await Stream.fromIterable(includes) @@ -273,7 +269,7 @@ class CBuilder implements Builder { ) .toList(); - buildOutput.dependencies.dependencies.addAll({ + buildOutput.addDependencies({ // Note: We use a Set here to deduplicate the dependencies. ...sources, ...includeFiles, @@ -287,3 +283,13 @@ enum _CBuilderType { executable, library, } + +LinkMode _linkMode(LinkModePreference preference) { + if (preference == LinkModePreference.dynamic || + preference == LinkModePreference.preferDynamic) { + return DynamicLoadingBundled(); + } + assert(preference == LinkModePreference.static || + preference == LinkModePreference.preferStatic); + return StaticLinking(); +} diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/compiler_resolver.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/compiler_resolver.dart index 243fdd910..cdc8ee0e6 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/compiler_resolver.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/compiler_resolver.dart @@ -22,13 +22,16 @@ import '../tool/tool_instance.dart'; class CompilerResolver { final BuildConfig buildConfig; final Logger? logger; - final Target host; + final OS hostOS; + final Architecture hostArchitecture; CompilerResolver({ required this.buildConfig, required this.logger, - Target? host, // Only visible for testing. - }) : host = host ?? Target.current; + OS? hostOS, // Only visible for testing. + Architecture? hostArchitecture, // Only visible for testing. + }) : hostOS = hostOS ?? OS.current, + hostArchitecture = hostArchitecture ?? Architecture.current; Future resolveCompiler() async { // First, check if the launcher provided a direct path to the compiler. @@ -44,26 +47,27 @@ class CompilerResolver { return result; } - final targetOs = buildConfig.targetOs; + final targetOS = buildConfig.targetOS; final targetArchitecture = buildConfig.targetArchitecture; - final errorMessage = "No tools configured on host '$host' with target " - "'${targetOs}_$targetArchitecture'."; + final errorMessage = + "No tools configured on host '${hostOS}_$hostArchitecture' with target " + "'${targetOS}_$targetArchitecture'."; logger?.severe(errorMessage); throw ToolError(errorMessage); } /// Select the right compiler for cross compiling to the specified target. Tool? _selectCompiler() { - final targetOs = buildConfig.targetOs; + final targetOS = buildConfig.targetOS; final targetArch = buildConfig.targetArchitecture; // TODO(dacoharkes): Support falling back on other tools. - if (targetArch == host.architecture && - targetOs == host.os && - host.os == OS.linux) return clang; - if (targetOs == OS.macOS || targetOs == OS.iOS) return appleClang; - if (targetOs == OS.android) return androidNdkClang; - if (host.os == OS.linux) { + if (targetArch == hostArchitecture && + targetOS == hostOS && + hostOS == OS.linux) return clang; + if (targetOS == OS.macOS || targetOS == OS.iOS) return appleClang; + if (targetOS == OS.android) return androidNdkClang; + if (hostOS == OS.linux) { switch (targetArch) { case Architecture.arm: return armLinuxGnueabihfGcc; @@ -78,7 +82,7 @@ class CompilerResolver { } } - if (host.os == OS.windows) { + if (hostOS == OS.windows) { switch (targetArch) { case Architecture.ia32: return clIA32; @@ -93,7 +97,7 @@ class CompilerResolver { } Future _tryLoadCompilerFromConfig() async { - final configCcUri = buildConfig.cCompiler.cc; + final configCcUri = buildConfig.cCompiler.compiler; if (configCcUri != null) { assert(await File.fromUri(configCcUri).exists()); logger?.finer('Using compiler ${configCcUri.toFilePath()} ' @@ -127,28 +131,29 @@ class CompilerResolver { return result; } - final targetOs = buildConfig.targetOs; + final targetOS = buildConfig.targetOS; final targetArchitecture = buildConfig.targetArchitecture; - final errorMessage = "No tools configured on host '$host' with target " - "'${targetOs}_$targetArchitecture'."; + final errorMessage = + "No tools configured on host '${hostOS}_$hostArchitecture' with target " + "'${targetOS}_$targetArchitecture'."; logger?.severe(errorMessage); throw ToolError(errorMessage); } /// Select the right archiver for cross compiling to the specified target. Tool? _selectArchiver() { - final targetOs = buildConfig.targetOs; + final targetOS = buildConfig.targetOS; final targetArchitecture = buildConfig.targetArchitecture; // TODO(dacoharkes): Support falling back on other tools. - if (targetArchitecture == host.architecture && - targetOs == host.os && - host.os == OS.linux) { + if (targetArchitecture == hostArchitecture && + targetOS == hostOS && + hostOS == OS.linux) { return llvmAr; } - if (targetOs == OS.macOS || targetOs == OS.iOS) return appleAr; - if (targetOs == OS.android) return androidNdkLlvmAr; - if (host.os == OS.linux) { + if (targetOS == OS.macOS || targetOS == OS.iOS) return appleAr; + if (targetOS == OS.android) return androidNdkLlvmAr; + if (hostOS == OS.linux) { switch (targetArchitecture) { case Architecture.arm: return armLinuxGnueabihfGccAr; @@ -162,7 +167,7 @@ class CompilerResolver { return riscv64LinuxGnuGccAr; } } - if (host.os == OS.windows) { + if (hostOS == OS.windows) { switch (targetArchitecture) { case Architecture.ia32: return libIA32; @@ -177,7 +182,7 @@ class CompilerResolver { } Future _tryLoadArchiverFromConfig() async { - final configArUri = buildConfig.cCompiler.ar; + final configArUri = buildConfig.cCompiler.archiver; if (configArUri != null) { assert(await File.fromUri(configArUri).exists()); logger?.finer('Using archiver ${configArUri.toFilePath()} ' diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart index dc2b8fb21..6f5fdbbad 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart @@ -27,7 +27,6 @@ class RunCBuilder { final Uri? dynamicLibrary; final Uri? staticLibrary; final Uri outDir; - final Target target; /// The install name of the [dynamicLibrary]. /// @@ -58,13 +57,12 @@ class RunCBuilder { this.std, this.language = Language.c, this.cppLinkStdLib, - }) : outDir = buildConfig.outDir, - target = buildConfig.target, + }) : outDir = buildConfig.outputDirectory, assert([executable, dynamicLibrary, staticLibrary] .whereType() .length == 1) { - if (target.os == OS.windows && cppLinkStdLib != null) { + if (buildConfig.targetOS == OS.windows && cppLinkStdLib != null) { throw ArgumentError.value( cppLinkStdLib, 'cppLinkStdLib', @@ -81,7 +79,7 @@ class RunCBuilder { Future archiver() async => (await _resolver.resolveArchiver()).uri; Future iosSdk(IOSSdk iosSdk, {required Logger? logger}) async { - if (iosSdk == IOSSdk.iPhoneOs) { + if (iosSdk == IOSSdk.iPhoneOS) { return (await iPhoneOSSdk.defaultResolver!.resolve(logger: logger)) .where((i) => i.tool == iPhoneOSSdk) .first @@ -124,37 +122,40 @@ class RunCBuilder { } late final IOSSdk targetIosSdk; - if (target.os == OS.iOS) { - targetIosSdk = buildConfig.targetIOSSdk!; + if (buildConfig.targetOS == OS.iOS) { + targetIosSdk = buildConfig.targetIOSSdk; } // The Android Gradle plugin does not honor API level 19 and 20 when // invoking clang. Mimic that behavior here. // See https://github.com/dart-lang/native/issues/171. late final int targetAndroidNdkApi; - if (target.os == OS.android) { - final minimumApi = target == Target.androidRiscv64 ? 35 : 21; + if (buildConfig.targetOS == OS.android) { + final minimumApi = + buildConfig.targetArchitecture == Architecture.riscv64 ? 35 : 21; targetAndroidNdkApi = max(buildConfig.targetAndroidNdkApi!, minimumApi); } + final architecture = buildConfig.targetArchitecture; + await runProcess( executable: compiler.uri, arguments: [ - if (target.os == OS.android) ...[ + if (buildConfig.targetOS == OS.android) ...[ '--target=' - '${androidNdkClangTargetFlags[target]!}' + '${androidNdkClangTargetFlags[architecture]!}' '$targetAndroidNdkApi', '--sysroot=${androidSysroot(compiler).toFilePath()}', ], - if (target.os == OS.macOS) - '--target=${appleClangMacosTargetFlags[target]!}', - if (target.os == OS.iOS) - '--target=${appleClangIosTargetFlags[target]![targetIosSdk]!}', - if (target.os == OS.iOS) ...[ + if (buildConfig.targetOS == OS.macOS) + '--target=${appleClangMacosTargetFlags[architecture]!}', + if (buildConfig.targetOS == OS.iOS) + '--target=${appleClangIosTargetFlags[architecture]![targetIosSdk]!}', + if (buildConfig.targetOS == OS.iOS) ...[ '-isysroot', (await iosSdk(targetIosSdk, logger: logger)).toFilePath(), ], - if (target.os == OS.macOS) ...[ + if (buildConfig.targetOS == OS.macOS) ...[ '-isysroot', (await macosSdk(logger: logger)).toFilePath(), ], @@ -189,7 +190,7 @@ class RunCBuilder { '-x', 'c++', '-l', - cppLinkStdLib ?? defaultCppLinkStdLib[target.os]! + cppLinkStdLib ?? defaultCppLinkStdLib[buildConfig.targetOS]! ], ...flags, for (final MapEntry(key: name, :value) in defines.entries) @@ -292,24 +293,24 @@ class RunCBuilder { } static const androidNdkClangTargetFlags = { - Target.androidArm: 'armv7a-linux-androideabi', - Target.androidArm64: 'aarch64-linux-android', - Target.androidIA32: 'i686-linux-android', - Target.androidX64: 'x86_64-linux-android', - Target.androidRiscv64: 'riscv64-linux-android', + Architecture.arm: 'armv7a-linux-androideabi', + Architecture.arm64: 'aarch64-linux-android', + Architecture.ia32: 'i686-linux-android', + Architecture.x64: 'x86_64-linux-android', + Architecture.riscv64: 'riscv64-linux-android', }; static const appleClangMacosTargetFlags = { - Target.macOSArm64: 'arm64-apple-darwin', - Target.macOSX64: 'x86_64-apple-darwin', + Architecture.arm64: 'arm64-apple-darwin', + Architecture.x64: 'x86_64-apple-darwin', }; static const appleClangIosTargetFlags = { - Target.iOSArm64: { - IOSSdk.iPhoneOs: 'arm64-apple-ios', + Architecture.arm64: { + IOSSdk.iPhoneOS: 'arm64-apple-ios', IOSSdk.iPhoneSimulator: 'arm64-apple-ios-simulator', }, - Target.iOSX64: { + Architecture.x64: { IOSSdk.iPhoneSimulator: 'x86_64-apple-ios-simulator', }, }; diff --git a/pkgs/native_toolchain_c/lib/src/native_toolchain/android_ndk.dart b/pkgs/native_toolchain_c/lib/src/native_toolchain/android_ndk.dart index 31edfc54f..964a97a35 100644 --- a/pkgs/native_toolchain_c/lib/src/native_toolchain/android_ndk.dart +++ b/pkgs/native_toolchain_c/lib/src/native_toolchain/android_ndk.dart @@ -92,7 +92,7 @@ class _AndroidNdkResolver implements ToolResolver { for (final hostArchDir in hostArchDirs) { final clangUri = hostArchDir.uri .resolve('bin/') - .resolve(Target.current.os.executableFileName('clang')); + .resolve(OS.current.executableFileName('clang')); if (await File.fromUri(clangUri).exists()) { result.add(await CliVersionResolver.lookupVersion( ToolInstance( @@ -104,7 +104,7 @@ class _AndroidNdkResolver implements ToolResolver { } final arUri = hostArchDir.uri .resolve('bin/') - .resolve(Target.current.os.executableFileName('llvm-ar')); + .resolve(OS.current.executableFileName('llvm-ar')); if (await File.fromUri(arUri).exists()) { result.add(await CliVersionResolver.lookupVersion( ToolInstance( @@ -116,7 +116,7 @@ class _AndroidNdkResolver implements ToolResolver { } final ldUri = hostArchDir.uri .resolve('bin/') - .resolve(Target.current.os.executableFileName('ld.lld')); + .resolve(OS.current.executableFileName('ld.lld')); if (await File.fromUri(arUri).exists()) { result.add(await CliVersionResolver.lookupVersion( ToolInstance( diff --git a/pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart b/pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart index a95356e99..da527f0b0 100644 --- a/pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart +++ b/pkgs/native_toolchain_c/lib/src/native_toolchain/msvc.dart @@ -145,7 +145,7 @@ final Tool cl = _msvcTool( name: 'cl', versionArguments: [], targetArchitecture: Architecture.x64, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, ); /// The C/C++ Optimizing Compiler main executable. @@ -155,7 +155,7 @@ final Tool clIA32 = _msvcTool( name: 'cl', versionArguments: [], targetArchitecture: Architecture.ia32, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, ); /// The C/C++ Optimizing Compiler main executable. @@ -165,13 +165,13 @@ final Tool clArm64 = _msvcTool( name: 'cl', versionArguments: [], targetArchitecture: Architecture.arm64, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, ); final Tool lib = _msvcTool( name: 'lib', targetArchitecture: Architecture.x64, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, // https://github.com/dart-lang/native/issues/18 resolveVersion: false, ); @@ -179,7 +179,7 @@ final Tool lib = _msvcTool( final Tool libIA32 = _msvcTool( name: 'lib', targetArchitecture: Architecture.ia32, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, // https://github.com/dart-lang/native/issues/18 resolveVersion: false, ); @@ -187,7 +187,7 @@ final Tool libIA32 = _msvcTool( final Tool libArm64 = _msvcTool( name: 'lib', targetArchitecture: Architecture.arm64, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, // https://github.com/dart-lang/native/issues/18 resolveVersion: false, ); @@ -197,7 +197,7 @@ final Tool link = _msvcTool( versionArguments: ['/help'], versionExitCode: 1100, targetArchitecture: Architecture.x64, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, ); final Tool linkIA32 = _msvcTool( @@ -205,7 +205,7 @@ final Tool linkIA32 = _msvcTool( versionArguments: ['/help'], versionExitCode: 1100, targetArchitecture: Architecture.ia32, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, ); final Tool linkArm64 = _msvcTool( @@ -213,13 +213,13 @@ final Tool linkArm64 = _msvcTool( versionArguments: ['/help'], versionExitCode: 1100, targetArchitecture: Architecture.arm64, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, ); final Tool dumpbin = _msvcTool( name: 'dumpbin', targetArchitecture: Architecture.x64, - hostArchitecture: Target.current.architecture, + hostArchitecture: Architecture.current, ); const _msvcArchNames = { @@ -237,7 +237,7 @@ Tool _msvcTool({ bool resolveVersion = true, }) { final executableName = OS.windows.executableFileName(name); - if (Target.current.os != OS.windows) { + if (OS.current != OS.windows) { return Tool( name: executableName, defaultResolver: ToolResolvers([]), diff --git a/pkgs/native_toolchain_c/lib/src/native_toolchain/recognizer.dart b/pkgs/native_toolchain_c/lib/src/native_toolchain/recognizer.dart index 0de8418ad..cc5bfaf58 100644 --- a/pkgs/native_toolchain_c/lib/src/native_toolchain/recognizer.dart +++ b/pkgs/native_toolchain_c/lib/src/native_toolchain/recognizer.dart @@ -20,7 +20,7 @@ class CompilerRecognizer implements ToolResolver { @override Future> resolve({required Logger? logger}) async { - final os = Target.current.os; + final os = OS.current; logger?.finer('Trying to recognize $uri.'); final filePath = uri.toFilePath(); Tool? tool; @@ -64,7 +64,7 @@ class LinkerRecognizer implements ToolResolver { @override Future> resolve({required Logger? logger}) async { - final os = Target.current.os; + final os = OS.current; logger?.finer('Trying to recognize $uri.'); final filePath = uri.toFilePath(); Tool? tool; @@ -115,7 +115,7 @@ class ArchiverRecognizer implements ToolResolver { @override Future> resolve({required Logger? logger}) async { logger?.finer('Trying to recognize $uri.'); - final os = Target.current.os; + final os = OS.current; final filePath = uri.toFilePath(); Tool? tool; if (filePath.contains('-gcc-ar')) { diff --git a/pkgs/native_toolchain_c/lib/src/tool/tool_resolver.dart b/pkgs/native_toolchain_c/lib/src/tool/tool_resolver.dart index 257ef8ee1..337254828 100644 --- a/pkgs/native_toolchain_c/lib/src/tool/tool_resolver.dart +++ b/pkgs/native_toolchain_c/lib/src/tool/tool_resolver.dart @@ -35,7 +35,7 @@ class PathToolResolver extends ToolResolver { required this.toolName, String? executableName, }) : executableName = executableName ?? - Target.current.os.executableFileName(toolName.toLowerCase()); + OS.current.executableFileName(toolName.toLowerCase()); @override Future> resolve({required Logger? logger}) async { diff --git a/pkgs/native_toolchain_c/pubspec.yaml b/pkgs/native_toolchain_c/pubspec.yaml index 99b4101f2..4015fc05b 100644 --- a/pkgs/native_toolchain_c/pubspec.yaml +++ b/pkgs/native_toolchain_c/pubspec.yaml @@ -1,7 +1,7 @@ name: native_toolchain_c description: >- A library to invoke the native C compiler installed on the host machine. -version: 0.3.5-wip +version: 0.4.0-wip repository: https://github.com/dart-lang/native/tree/main/pkgs/native_toolchain_c topics: @@ -17,7 +17,6 @@ environment: publish_to: none dependencies: - cli_config: ^0.1.1 glob: ^2.1.1 logging: ^1.1.1 meta: ^1.9.1 diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_build_failure_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_build_failure_test.dart index f2a73c4e1..d7aa14d6b 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_build_failure_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_build_failure_test.dart @@ -29,16 +29,16 @@ void main() { await File.fromUri(addCUri).writeAsString(addCBrokenContents); const name = 'add'; - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, linkModePreference: LinkModePreference.dynamic, buildMode: BuildMode.release, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), @@ -48,7 +48,7 @@ void main() { final cbuilder = CBuilder.library( sources: [addCUri.toFilePath()], name: name, - assetId: name, + assetName: name, ); expect( () => cbuilder.run( 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 6e1800e0b..af22594f4 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 @@ -13,28 +13,28 @@ import '../helpers.dart'; void main() { const targets = [ - Target.androidArm, - Target.androidArm64, - Target.androidIA32, - Target.androidX64, + Architecture.arm, + Architecture.arm64, + Architecture.ia32, + Architecture.x64, // TODO(rmacnak): Enable when stable NDK 27 is available. - // Target.androidRiscv64, + // Architecture.riscv64, ]; const readElfMachine = { - Target.androidArm: 'ARM', - Target.androidArm64: 'AArch64', - Target.androidIA32: 'Intel 80386', - Target.androidX64: 'Advanced Micro Devices X86-64', - Target.androidRiscv64: 'RISC-V', + Architecture.arm: 'ARM', + Architecture.arm64: 'AArch64', + Architecture.ia32: 'Intel 80386', + Architecture.x64: 'Advanced Micro Devices X86-64', + Architecture.riscv64: 'RISC-V', }; const objdumpFileFormat = { - Target.androidArm: 'elf32-littlearm', - Target.androidArm64: 'elf64-littleaarch64', - Target.androidIA32: 'elf32-i386', - Target.androidX64: 'elf64-x86-64', - Target.androidRiscv64: 'elf64-littleriscv', + Architecture.arm: 'elf32-littlearm', + Architecture.arm64: 'elf64-littleaarch64', + Architecture.ia32: 'elf32-i386', + Architecture.x64: 'elf64-x86-64', + Architecture.riscv64: 'elf64-littleriscv', }; /// From https://docs.flutter.dev/reference/supported-platforms. @@ -46,7 +46,7 @@ void main() { /// From https://docs.flutter.dev/reference/supported-platforms. const flutterAndroidNdkVersionHighestSupported = 34; - for (final linkMode in LinkMode.values) { + for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) { for (final target in targets) { for (final apiLevel in [ flutterAndroidNdkVersionLowestBestEffort, @@ -91,8 +91,8 @@ void main() { } test('CBuilder API levels binary difference', () async { - const target = Target.androidArm64; - const linkMode = LinkMode.dynamic; + const target = Architecture.arm64; + final linkMode = DynamicLoadingBundled(); const apiLevel1 = flutterAndroidNdkVersionLowestSupported; const apiLevel2 = flutterAndroidNdkVersionHighestSupported; final tempUri = await tempDirForTest(); @@ -117,22 +117,22 @@ void main() { Future buildLib( Uri tempUri, - Target target, + Architecture targetArchitecture, int androidNdkApi, LinkMode linkMode, ) async { final addCUri = packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); const name = 'add'; - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, - targetArchitecture: target.architecture, - targetOs: target.os, + targetArchitecture: targetArchitecture, + targetOS: OS.android, targetAndroidNdkApi: androidNdkApi, buildMode: BuildMode.release, - linkModePreference: linkMode == LinkMode.dynamic + linkModePreference: linkMode == DynamicLoadingBundled() ? LinkModePreference.dynamic : LinkModePreference.static, ); @@ -140,7 +140,7 @@ Future buildLib( final cbuilder = CBuilder.library( name: name, - assetId: name, + assetName: name, sources: [addCUri.toFilePath()], ); await cbuilder.run( @@ -149,6 +149,6 @@ Future buildLib( logger: logger, ); - final libUri = tempUri.resolve(target.os.libraryFileName(name, linkMode)); + final libUri = tempUri.resolve(OS.android.libraryFileName(name, linkMode)); return libUri; } diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_ios_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_ios_test.dart index 586f7ff5b..7cc4365e4 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_ios_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_ios_test.dart @@ -24,29 +24,29 @@ void main() { } const targets = [ - Target.iOSArm64, - Target.iOSX64, + Architecture.arm64, + Architecture.x64, ]; // Dont include 'mach-o' or 'Mach-O', different spelling is used. const objdumpFileFormat = { - Target.iOSArm64: 'arm64', - Target.iOSX64: '64-bit x86-64', + Architecture.arm64: 'arm64', + Architecture.x64: '64-bit x86-64', }; const name = 'add'; - for (final linkMode in LinkMode.values) { + for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) { for (final targetIOSSdk in IOSSdk.values) { for (final target in targets) { - if (target == Target.iOSX64 && targetIOSSdk == IOSSdk.iPhoneOs) { + if (target == Architecture.x64 && targetIOSSdk == IOSSdk.iPhoneOS) { continue; } - final libName = target.os.libraryFileName(name, linkMode); + final libName = OS.iOS.libraryFileName(name, linkMode); for (final installName in [ null, - if (linkMode == LinkMode.dynamic) + if (linkMode == DynamicLoadingBundled()) Uri.file('@executable_path/Frameworks/$libName'), ]) { test( @@ -56,14 +56,14 @@ void main() { final tempUri = await tempDirForTest(); final addCUri = packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, - targetArchitecture: target.architecture, - targetOs: target.os, + targetArchitecture: target, + targetOS: OS.iOS, buildMode: BuildMode.release, - linkModePreference: linkMode == LinkMode.dynamic + linkModePreference: linkMode == DynamicLoadingBundled() ? LinkModePreference.dynamic : LinkModePreference.static, targetIOSSdk: targetIOSSdk, @@ -72,7 +72,7 @@ void main() { final cbuilder = CBuilder.library( name: name, - assetId: name, + assetName: name, sources: [addCUri.toFilePath()], installName: installName, ); @@ -100,7 +100,7 @@ void main() { logger: logger, ); expect(otoolResult.exitCode, 0); - if (targetIOSSdk == IOSSdk.iPhoneOs || target == Target.iOSX64) { + if (targetIOSSdk == IOSSdk.iPhoneOS || target == Architecture.x64) { // The x64 simulator behaves as device, presumably because the // devices are never x64. expect(otoolResult.stdout, contains('LC_VERSION_MIN_IPHONEOS')); @@ -116,7 +116,7 @@ void main() { expect(platform, contains(platformIosSimulator.toString())); } - if (linkMode == LinkMode.dynamic) { + if (linkMode == DynamicLoadingBundled()) { final libInstallName = await runOtoolInstallName(libUri, libName); if (installName == null) { // If no install path is passed, we have an absolute path. diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_linux_host_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_linux_host_test.dart index 8abe7a492..eab2a77e4 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_linux_host_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_linux_host_test.dart @@ -21,22 +21,22 @@ void main() { } const targets = [ - Target.linuxArm, - Target.linuxArm64, - Target.linuxIA32, - Target.linuxX64, - Target.linuxRiscv64, + Architecture.arm, + Architecture.arm64, + Architecture.ia32, + Architecture.x64, + Architecture.riscv64, ]; const readElfMachine = { - Target.linuxArm: 'ARM', - Target.linuxArm64: 'AArch64', - Target.linuxIA32: 'Intel 80386', - Target.linuxX64: 'Advanced Micro Devices X86-64', - Target.linuxRiscv64: 'RISC-V', + Architecture.arm: 'ARM', + Architecture.arm64: 'AArch64', + Architecture.ia32: 'Intel 80386', + Architecture.x64: 'Advanced Micro Devices X86-64', + Architecture.riscv64: 'RISC-V', }; - for (final linkMode in LinkMode.values) { + for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) { for (final target in targets) { test('CBuilder $linkMode library $target', () async { final tempUri = await tempDirForTest(); @@ -44,14 +44,14 @@ void main() { packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); const name = 'add'; - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, - targetArchitecture: target.architecture, - targetOs: target.os, + targetArchitecture: target, + targetOS: OS.linux, buildMode: BuildMode.release, - linkModePreference: linkMode == LinkMode.dynamic + linkModePreference: linkMode == DynamicLoadingBundled() ? LinkModePreference.dynamic : LinkModePreference.static, ); @@ -59,7 +59,7 @@ void main() { final cbuilder = CBuilder.library( name: name, - assetId: name, + assetName: name, sources: [addCUri.toFilePath()], ); await cbuilder.run( @@ -69,7 +69,7 @@ void main() { ); final libUri = - tempUri.resolve(target.os.libraryFileName(name, linkMode)); + tempUri.resolve(OS.linux.libraryFileName(name, linkMode)); final result = await runProcess( executable: Uri.file('readelf'), arguments: ['-h', libUri.path], diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_macos_host_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_macos_host_test.dart index 655ea253f..971855e2f 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_macos_host_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_macos_host_test.dart @@ -24,17 +24,17 @@ void main() { } const targets = [ - Target.macOSArm64, - Target.macOSX64, + Architecture.arm64, + Architecture.x64, ]; // Dont include 'mach-o' or 'Mach-O', different spelling is used. const objdumpFileFormat = { - Target.macOSArm64: 'arm64', - Target.macOSX64: '64-bit x86-64', + Architecture.arm64: 'arm64', + Architecture.x64: '64-bit x86-64', }; - for (final linkMode in LinkMode.values) { + for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) { for (final target in targets) { test('CBuilder $linkMode library $target', () async { final tempUri = await tempDirForTest(); @@ -42,14 +42,14 @@ void main() { packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); const name = 'add'; - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, - targetArchitecture: target.architecture, - targetOs: target.os, + targetArchitecture: target, + targetOS: OS.macOS, buildMode: BuildMode.release, - linkModePreference: linkMode == LinkMode.dynamic + linkModePreference: linkMode == DynamicLoadingBundled() ? LinkModePreference.dynamic : LinkModePreference.static, ); @@ -57,7 +57,7 @@ void main() { final cbuilder = CBuilder.library( name: name, - assetId: name, + assetName: name, sources: [addCUri.toFilePath()], ); await cbuilder.run( @@ -67,7 +67,7 @@ void main() { ); final libUri = - tempUri.resolve(target.os.libraryFileName(name, linkMode)); + tempUri.resolve(OS.macOS.libraryFileName(name, linkMode)); final result = await runProcess( executable: Uri.file('objdump'), arguments: ['-t', libUri.path], diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart index bf0e83693..8575c7795 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_windows_host_test.dart @@ -25,8 +25,8 @@ void main() { } const targets = [ - Target.windowsIA32, - Target.windowsX64, + Architecture.ia32, + Architecture.x64, ]; late Uri dumpbinUri; @@ -37,16 +37,16 @@ void main() { }); const dumpbinMachine = { - Target.windowsIA32: 'x86', - Target.windowsX64: 'x64', + Architecture.ia32: 'x86', + Architecture.x64: 'x64', }; - const dumpbinFileType = { - LinkMode.dynamic: 'DLL', - LinkMode.static: 'LIBRARY', + final dumpbinFileType = { + DynamicLoadingBundled(): 'DLL', + StaticLinking(): 'LIBRARY', }; - for (final linkMode in LinkMode.values) { + for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) { for (final target in targets) { test('CBuilder $linkMode library $target', () async { final tempUri = await tempDirForTest(); @@ -54,14 +54,14 @@ void main() { packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); const name = 'add'; - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, - targetOs: target.os, - targetArchitecture: target.architecture, + targetOS: OS.windows, + targetArchitecture: target, buildMode: BuildMode.release, - linkModePreference: linkMode == LinkMode.dynamic + linkModePreference: linkMode == DynamicLoadingBundled() ? LinkModePreference.dynamic : LinkModePreference.static, ); @@ -69,7 +69,7 @@ void main() { final cbuilder = CBuilder.library( name: name, - assetId: name, + assetName: name, sources: [addCUri.toFilePath()], ); await cbuilder.run( @@ -79,7 +79,7 @@ void main() { ); final libUri = - tempUri.resolve(target.os.libraryFileName(name, linkMode)); + tempUri.resolve(OS.windows.libraryFileName(name, linkMode)); expect(await File.fromUri(libUri).exists(), true); final result = await runProcess( executable: dumpbinUri, diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart index 53b5a965b..b801a1e5a 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart @@ -43,17 +43,17 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, buildMode: buildMode, // Ignored by executables. linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), @@ -71,7 +71,7 @@ void main() { ); final executableUri = - tempUri.resolve(Target.current.os.executableFileName(name)); + tempUri.resolve(OS.current.executableFileName(name)); expect(await File.fromUri(executableUri).exists(), true); final result = await runProcess( executable: executableUri, @@ -87,7 +87,7 @@ void main() { (message) => message.contains(helloWorldCUri.toFilePath()), ); - switch ((buildConfig.targetOs, pic)) { + switch ((buildConfig.targetOS, pic)) { case (OS.windows, _) || (_, null): expect(compilerInvocation, isNot(contains('-fPIC'))); expect(compilerInvocation, isNot(contains('-fPIE'))); @@ -119,19 +119,19 @@ void main() { outDir: tempUri, packageName: name, packageRoot: tempUri, - targetOs: OS.current, + targetOS: OS.current, linkModePreference: LinkModePreference.dynamic, ) - : BuildConfig( - outDir: tempUri, + : BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), @@ -141,7 +141,7 @@ void main() { final cbuilder = CBuilder.library( sources: [addCUri.toFilePath()], name: name, - assetId: name, + assetName: name, pic: pic, ); await cbuilder.run( @@ -150,7 +150,7 @@ void main() { logger: logger, ); - final dylibUri = tempUri.resolve(Target.current.os.dylibFileName(name)); + final dylibUri = tempUri.resolve(OS.current.dylibFileName(name)); expect(await File.fromUri(dylibUri).exists(), !dryRun); if (!dryRun) { final dylib = openDynamicLibraryForTest(dylibUri.toFilePath()); @@ -161,7 +161,7 @@ void main() { final compilerInvocation = logMessages.singleWhere( (message) => message.contains(addCUri.toFilePath()), ); - switch ((buildConfig.targetOs, pic)) { + switch ((buildConfig.targetOS, pic)) { case (OS.windows, _) || (_, null): expect(compilerInvocation, isNot(contains('-fPIC'))); expect(compilerInvocation, isNot(contains('-fPIE'))); @@ -214,24 +214,24 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, buildMode: BuildMode.release, // Ignored by executables. linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), ); final buildOutput = BuildOutput(); - final flag = switch (buildConfig.targetOs) { + final flag = switch (buildConfig.targetOS) { OS.windows => '/DFOO=USER_FLAG', _ => '-DFOO=USER_FLAG', }; @@ -247,8 +247,7 @@ void main() { logger: logger, ); - final executableUri = - tempUri.resolve(Target.current.os.executableFileName(name)); + final executableUri = tempUri.resolve(OS.current.executableFileName(name)); expect(await File.fromUri(executableUri).exists(), true); final result = await runProcess( executable: executableUri, @@ -273,16 +272,16 @@ void main() { packageUri.resolve('test/cbuilder/testfiles/includes/src/includes.c'); const name = 'includes'; - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), @@ -291,7 +290,7 @@ void main() { final cbuilder = CBuilder.library( name: name, - assetId: name, + assetName: name, includes: [includeDirectoryUri.toFilePath()], sources: [includesCUri.toFilePath()], ); @@ -301,9 +300,9 @@ void main() { logger: logger, ); - expect(buildOutput.dependencies.dependencies, contains(includesHUri)); + expect(buildOutput.dependencies, contains(includesHUri)); - final dylibUri = tempUri.resolve(Target.current.os.dylibFileName(name)); + final dylibUri = tempUri.resolve(OS.current.dylibFileName(name)); final dylib = openDynamicLibraryForTest(dylibUri.toFilePath()); final x = dylib.lookup('x'); expect(x.value, 42); @@ -318,23 +317,23 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), ); final buildOutput = BuildOutput(); - final stdFlag = switch (buildConfig.targetOs) { + final stdFlag = switch (buildConfig.targetOS) { OS.windows => '/std:$std', _ => '-std=$std', }; @@ -342,7 +341,7 @@ void main() { final cbuilder = CBuilder.library( sources: [addCUri.toFilePath()], name: name, - assetId: name, + assetName: name, std: std, ); await cbuilder.run( @@ -351,7 +350,7 @@ void main() { logger: logger, ); - final dylibUri = tempUri.resolve(Target.current.os.dylibFileName(name)); + final dylibUri = tempUri.resolve(OS.current.dylibFileName(name)); final dylib = openDynamicLibraryForTest(dylibUri.toFilePath()); final add = dylib.lookupFunction[]; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig( + final buildConfig = BuildConfig.build( buildMode: BuildMode.release, - outDir: tempUri, + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, // Ignored by executables. linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), ); final buildOutput = BuildOutput(); - final defaultStdLibLinkFlag = switch (buildConfig.targetOs) { + final defaultStdLibLinkFlag = switch (buildConfig.targetOS) { OS.windows => null, OS.linux => '-l stdc++', OS.macOS => '-l c++', @@ -411,8 +410,7 @@ void main() { logger: logger, ); - final executableUri = - tempUri.resolve(Target.current.os.executableFileName(name)); + final executableUri = tempUri.resolve(OS.current.executableFileName(name)); expect(await File.fromUri(executableUri).exists(), true); final result = await runProcess( executable: executableUri, @@ -441,17 +439,17 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig( + final buildConfig = BuildConfig.build( buildMode: BuildMode.release, - outDir: tempUri, + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, // Ignored by executables. linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), @@ -464,7 +462,7 @@ void main() { cppLinkStdLib: 'stdc++', ); - if (buildConfig.targetOs == OS.windows) { + if (buildConfig.targetOS == OS.windows) { await expectLater( () => cbuilder.run( buildConfig: buildConfig, @@ -481,7 +479,7 @@ void main() { ); final executableUri = - tempUri.resolve(Target.current.os.executableFileName(name)); + tempUri.resolve(OS.current.executableFileName(name)); expect(await File.fromUri(executableUri).exists(), true); final result = await runProcess( executable: executableUri, @@ -512,17 +510,17 @@ Future testDefines({ } const name = 'defines'; - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: name, packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, buildMode: buildMode, // Ignored by executables. linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - cc: cc, + compiler: cc, envScript: envScript, envScriptArgs: envScriptArgs, ), @@ -544,8 +542,7 @@ Future testDefines({ logger: logger, ); - final executableUri = - tempUri.resolve(Target.current.os.executableFileName(name)); + final executableUri = tempUri.resolve(OS.current.executableFileName(name)); expect(await File.fromUri(executableUri).exists(), true); final result = await runProcess( executable: executableUri, diff --git a/pkgs/native_toolchain_c/test/cbuilder/compiler_resolver_test.dart b/pkgs/native_toolchain_c/test/cbuilder/compiler_resolver_test.dart index b77c231d3..328412f72 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/compiler_resolver_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/compiler_resolver_test.dart @@ -40,43 +40,44 @@ void main() { final envScript = [ ...await vcvars64.defaultResolver!.resolve(logger: logger) ].firstOrNull?.uri; - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: 'dummy', packageRoot: tempUri, targetArchitecture: Architecture.current, - targetOs: OS.current, + targetOS: OS.current, buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, cCompiler: CCompilerConfig( - ar: ar, - cc: cc, - ld: ld, + archiver: ar, + compiler: cc, + linker: ld, envScript: envScript, ), ); final resolver = CompilerResolver(buildConfig: buildConfig, logger: logger); final compiler = await resolver.resolveCompiler(); final archiver = await resolver.resolveArchiver(); - expect(compiler.uri, buildConfig.cCompiler.cc); - expect(archiver.uri, buildConfig.cCompiler.ar); + expect(compiler.uri, buildConfig.cCompiler.compiler); + expect(archiver.uri, buildConfig.cCompiler.archiver); }); test('No compiler found', () async { final tempUri = await tempDirForTest(); - final buildConfig = BuildConfig( - outDir: tempUri, + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, packageName: 'dummy', packageRoot: tempUri, targetArchitecture: Architecture.arm64, - targetOs: OS.windows, + targetOS: OS.windows, buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, ); final resolver = CompilerResolver( buildConfig: buildConfig, logger: logger, - host: Target.androidArm64, // This is never a host. + hostOS: OS.android, // This is never a host. + hostArchitecture: Architecture.arm64, // This is never a host. ); expect(resolver.resolveCompiler, throwsA(isA())); expect(resolver.resolveArchiver, throwsA(isA())); diff --git a/pkgs/native_toolchain_c/test/tool/tool_resolver_test.dart b/pkgs/native_toolchain_c/test/tool/tool_resolver_test.dart index 9f30ce08b..18b1a83d9 100644 --- a/pkgs/native_toolchain_c/test/tool/tool_resolver_test.dart +++ b/pkgs/native_toolchain_c/test/tool/tool_resolver_test.dart @@ -59,9 +59,8 @@ void main() { test('RelativeToolResolver', () async { final tempUri = await tempDirForTest(); - final barExeUri = - tempUri.resolve(Target.current.os.executableFileName('bar')); - final bazExeName = Target.current.os.executableFileName('baz'); + final barExeUri = tempUri.resolve(OS.current.executableFileName('bar')); + final bazExeName = OS.current.executableFileName('baz'); final bazExeUri = tempUri.resolve(bazExeName); await File.fromUri(barExeUri).writeAsString('dummy'); await File.fromUri(bazExeUri).writeAsString('dummy'); @@ -90,9 +89,8 @@ void main() { test('logger', () async { final tempUri = await tempDirForTest(); - final barExeUri = - tempUri.resolve(Target.current.os.executableFileName('bar')); - final bazExeName = Target.current.os.executableFileName('baz'); + final barExeUri = tempUri.resolve(OS.current.executableFileName('bar')); + final bazExeName = OS.current.executableFileName('baz'); final bazExeUri = tempUri.resolve(bazExeName); await File.fromUri(barExeUri).writeAsString('dummy'); final barResolver = InstallLocationResolver(