From fdc3b5b957f1641a8b48bffd6948c844e050f4b8 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 27 Sep 2024 20:36:01 +0200 Subject: [PATCH 1/5] Allow custom asset types in the build system This is the first PR that will increase the flexibility of the Dart Build system as well as makes it more layered. The main points being addressed in this PR are: * A bundling tool has to specify which asset types it supports. So we make `supportedAssetTypes` required everywhere. => Across various layers in the code base we make this required => We remove a baked-in `[CodeAsset.type]` fallback in various places (Making this explicit increases size of LOCs in this CL due to the many tests) * The core building infrastructure in `pkg/native_assets_builder/lib/*` no longer knows anything about `CodeAsset` or `DataAsset`. Instead it only knows about `EncodedAsset` which represents the type of an asset and it's json encoding. => The core still verifies certain protocol consistency (e.g. that a hook only emitted asset types that the config allows) => The higher levels pass the `supportedAssetTypes`. => The higher levels now also pass `buildValidator` / `linkValidator` that validate per-asset related things (e.g. the id of a data asset to start with the package name) for a given package. => The higher levels now also pass a `applicationAssetValidator` that validates consistency of assets across all packages (e.g. uniqueness of dylib filenames across all dependencies) => This centralizes the logic about per-asset-type information in the bundling tool (i.e. user of `package:native_assets_builder`). => Bundling tools now have to expand `CodeAsset`s in dry-run to all architectures. * The bundling tool (e.g. `flutter build`) doesn't have to implement validation logic itself, instead it will support certain asset types by importing the "bundling logic" for that asset types (which includes - for now - validation logic). * All the validation code now simply returns `ValidationError` which is a list of errors. If the list is empty then there were no errors. => This removes redundancy between a `success` boolean and the list of errors (it was successsfull iff there were no errors) => This simplifies code that aggregates errors from different sources. * Moves the `fromJson` / `toJson` towards be purely read/write a json schema and are not responsible for anything else. => The validation routines are responsible for validating semantics. * The hook writer API now changes to work on per-asset-type specific extension methods. For example what used to be ``` output.addAsset(DataAsset(...)); ``` is now ``` output.dataAssets.add(DataAsset(...)); ``` The `BuildOutput.encodedAsset` getter is (with this PR) temporarily visible to hook writers, which could use it like this: ``` output.encodedAssets.add(DataAsset(...).encode()); ``` but a following refactoring that increases the flexibility of the protocol more will also get rid of the user-visible `encodedAsset` getter (if we choose so). So this is only temporarily exposed. * `hook/link.dart` scripts have to mark any input files it's output depends on in the `LinkOutput.addDependency`. If the set of inputs is the same but one input's file changed then a `hook/link.dart` - doesn't have to rerun if it just drops all assets or outputs them unchanged - has to re-run if it copies a input, changes it's contents, merge with other inputs, etc => A `hook/link.dart` should - just like a `hook/build.dart` add dependencies if the inputs are used to produce the output => Before this was somewhat hard-coded into the `package:native_assets_builder`. --- pkgs/native_assets_builder/CHANGELOG.md | 2 +- .../lib/src/build_runner/build_runner.dart | 139 +++++----- .../lib/src/model/build_dry_run_result.dart | 4 +- .../lib/src/model/build_result.dart | 4 +- .../lib/src/model/hook_result.dart | 54 ++-- .../lib/src/model/link_result.dart | 2 +- .../build_runner/build_dependencies_test.dart | 13 +- .../test/build_runner/build_planner_test.dart | 1 - .../build_runner_asset_id_test.dart | 6 + .../build_runner_build_dry_run_test.dart | 45 ++-- ...build_runner_build_output_format_test.dart | 3 + .../build_runner_caching_test.dart | 68 ++++- .../build_runner/build_runner_cycle_test.dart | 5 + .../build_runner_failure_test.dart | 32 ++- .../build_runner_non_root_package_test.dart | 10 +- .../build_runner_reusability_test.dart | 11 +- .../build_runner_run_in_isolation_test.dart | 6 +- .../test/build_runner/build_runner_test.dart | 18 +- .../build_runner/concurrency_shared_test.dart | 1 - .../concurrency_shared_test_helper.dart | 4 +- .../build_runner/concurrency_test_helper.dart | 7 +- .../build_runner/conflicting_dylib_test.dart | 17 +- .../fail_on_os_sdk_version_test.dart | 13 +- .../test/build_runner/helpers.dart | 51 ++-- .../test/build_runner/link_caching_test.dart | 10 +- .../test/build_runner/link_test.dart | 77 +++--- .../test/build_runner/metadata_test.dart | 11 +- .../packaging_preference_test.dart | 22 +- .../test/build_runner/resources_test.dart | 6 + .../test/build_runner/wrong_linker_test.dart | 3 + pkgs/native_assets_builder/test/helpers.dart | 33 +-- .../native_dynamic_linking_test.dart | 6 +- .../test/test_data/transformer_test.dart | 5 +- .../test_data/add_asset_link/hook/link.dart | 4 +- .../test_data/complex_link/hook/build.dart | 2 +- .../test_data/complex_link/hook/link.dart | 5 +- .../complex_link_helper/hook/build.dart | 2 +- .../test_data/drop_dylib_link/hook/link.dart | 7 +- .../hook/build.dart | 2 +- .../native_add_duplicate/hook/build.dart | 4 +- .../native_add_duplicate/hook/link.dart | 2 +- .../no_asset_for_link/hook/link.dart | 8 +- .../simple_data_asset/hook/build.dart | 2 +- .../test_data/simple_link/hook/build.dart | 2 +- .../test_data/simple_link/hook/link.dart | 16 +- .../test_data/transformer/hook/build.dart | 2 +- .../treeshaking_native_libs/hook/link.dart | 4 +- .../wrong_build_output/hook/build.dart | 2 +- .../wrong_build_output_3/hook/build.dart | 2 +- .../test_data/wrong_linker/hook/build.dart | 2 +- .../wrong_namespace_asset/hook/build.dart | 2 +- .../example/build/local_asset/hook/build.dart | 2 +- .../build/local_asset/test/build_test.dart | 6 +- .../link/package_with_assets/hook/build.dart | 4 +- .../link/package_with_assets/hook/link.dart | 7 +- .../lib/native_assets_cli.dart | 21 +- .../lib/native_assets_cli_internal.dart | 13 +- pkgs/native_assets_cli/lib/src/api/build.dart | 10 +- .../lib/src/api/build_config.dart | 10 +- .../lib/src/api/build_output.dart | 26 +- .../lib/src/api/builder.dart | 10 +- .../lib/src/api/hook_config.dart | 3 +- pkgs/native_assets_cli/lib/src/api/link.dart | 23 +- .../lib/src/api/link_config.dart | 18 +- .../lib/src/api/link_output.dart | 14 +- pkgs/native_assets_cli/lib/src/api/test.dart | 36 ++- pkgs/native_assets_cli/lib/src/asset.dart | 95 ------- .../lib/src/{ => code_assets}/code_asset.dart | 111 ++++++-- .../lib/src/code_assets/validation.dart | 144 +++++++++++ .../native_assets_cli/lib/src/data_asset.dart | 87 ------- .../lib/src/data_assets/data_asset.dart | 155 ++++++++++++ .../lib/src/data_assets/validation.dart | 58 +++++ .../lib/src/encoded_asset.dart | 45 ++++ .../native_assets_cli/lib/src/json_utils.dart | 2 + pkgs/native_assets_cli/lib/src/link_mode.dart | 2 +- .../lib/src/link_mode_preference.dart | 2 +- .../lib/src/model/build_config.dart | 16 +- .../lib/src/model/hook_config.dart | 12 +- .../lib/src/model/hook_output.dart | 79 +++--- .../lib/src/model/link_config.dart | 37 +-- .../native_assets_cli/lib/src/validation.dart | 69 +++++ .../lib/src/validator/validator.dart | 237 ------------------ .../test/api/asset_test.dart | 14 -- .../test/api/build_config_test.dart | 8 + .../test/api/build_output_test.dart | 6 +- .../test/api/build_test.dart | 1 + .../test/api/link_config_test.dart | 4 + .../test/example/local_asset_test.dart | 8 +- .../test/example/native_add_library_test.dart | 3 +- .../example/native_dynamic_linking_test.dart | 3 +- pkgs/native_assets_cli/test/helpers.dart | 31 +-- .../test/model/asset_test.dart | 6 +- .../test/model/build_config_test.dart | 23 ++ .../test/model/build_output_test.dart | 32 +-- .../test/model/checksum_test.dart | 3 + .../test/model/link_config_test.dart | 49 ++-- .../test/validator/validator_test.dart | 95 +++---- .../lib/src/cbuilder/cbuilder.dart | 20 +- .../lib/src/cbuilder/clinker.dart | 20 +- .../lib/src/cbuilder/ctool.dart | 2 +- .../cbuilder/cbuilder_build_failure_test.dart | 1 + .../cbuilder/cbuilder_cross_android_test.dart | 1 + .../cbuilder/cbuilder_cross_ios_test.dart | 2 + .../cbuilder_cross_linux_host_test.dart | 1 + .../cbuilder_cross_macos_host_test.dart | 2 + .../cbuilder_cross_windows_host_test.dart | 1 + .../test/cbuilder/cbuilder_test.dart | 9 + .../test/cbuilder/compiler_resolver_test.dart | 2 + .../test/cbuilder/objective_c_test.dart | 1 + .../test/clinker/build_testfiles.dart | 3 +- .../test/clinker/objects_test.dart | 7 +- .../test/clinker/throws_test.dart | 1 + .../test/clinker/treeshake_helper.dart | 3 +- 113 files changed, 1444 insertions(+), 1031 deletions(-) delete mode 100644 pkgs/native_assets_cli/lib/src/asset.dart rename pkgs/native_assets_cli/lib/src/{ => code_assets}/code_asset.dart (73%) create mode 100644 pkgs/native_assets_cli/lib/src/code_assets/validation.dart delete mode 100644 pkgs/native_assets_cli/lib/src/data_asset.dart create mode 100644 pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart create mode 100644 pkgs/native_assets_cli/lib/src/data_assets/validation.dart create mode 100644 pkgs/native_assets_cli/lib/src/encoded_asset.dart create mode 100644 pkgs/native_assets_cli/lib/src/validation.dart delete mode 100644 pkgs/native_assets_cli/lib/src/validator/validator.dart diff --git a/pkgs/native_assets_builder/CHANGELOG.md b/pkgs/native_assets_builder/CHANGELOG.md index 8909f3e54..a00609e4a 100644 --- a/pkgs/native_assets_builder/CHANGELOG.md +++ b/pkgs/native_assets_builder/CHANGELOG.md @@ -10,7 +10,7 @@ dart-lang/native repository to make it clear those are not intended to be used by end-users. - Remove link-dry-run concept as it's unused by Flutter Tools & Dart SDK -- Bump `native_assets_cli` to `0.9.0` +- Bump `native_assets_cli` to `0.9.0`. ## 0.8.3 diff --git a/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart b/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart index c2e4df213..160b706fc 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 @@ -23,6 +23,18 @@ import 'build_planner.dart'; typedef DependencyMetadata = Map; +typedef HookValidator = Future Function( + HookConfig config, HookOutputImpl output); + +typedef BuildValidator = Future Function( + BuildConfig config, BuildOutput outup); + +typedef LinkValidator = Future Function( + LinkConfig config, LinkOutput output); + +typedef ApplicationAssetValidator = Future Function( + List assets); + /// The programmatic API to be used by Dart launchers to invoke native builds. /// /// These methods are invoked by launchers such as dartdev (for `dart run`) @@ -58,6 +70,8 @@ class NativeAssetsBuildRunner { required Target target, required Uri workingDirectory, required BuildMode buildMode, + required BuildValidator buildValidator, + required ApplicationAssetValidator applicationAssetValidator, CCompilerConfig? cCompilerConfig, IOSSdk? targetIOSSdk, int? targetIOSVersion, @@ -66,10 +80,14 @@ class NativeAssetsBuildRunner { required bool includeParentEnvironment, PackageLayout? packageLayout, String? runPackageName, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, required bool linkingEnabled, }) async => _run( + validator: (HookConfig config, HookOutputImpl output) => + buildValidator(config as BuildConfig, output as BuildOutput), + applicationAssetValidator: (assets) async => + linkingEnabled ? [] : applicationAssetValidator(assets), hook: Hook.build, linkModePreference: linkModePreference, target: target, @@ -103,6 +121,8 @@ class NativeAssetsBuildRunner { required Target target, required Uri workingDirectory, required BuildMode buildMode, + required LinkValidator linkValidator, + required ApplicationAssetValidator applicationAssetValidator, CCompilerConfig? cCompilerConfig, IOSSdk? targetIOSSdk, int? targetIOSVersion, @@ -112,10 +132,14 @@ class NativeAssetsBuildRunner { PackageLayout? packageLayout, Uri? resourceIdentifiers, String? runPackageName, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, required BuildResult buildResult, }) async => _run( + validator: (HookConfig config, HookOutputImpl output) => + linkValidator(config as LinkConfig, output as LinkOutput), + applicationAssetValidator: (assets) async => + applicationAssetValidator(assets), hook: Hook.link, linkModePreference: linkModePreference, target: target, @@ -141,6 +165,8 @@ class NativeAssetsBuildRunner { required Target target, required Uri workingDirectory, required BuildMode buildMode, + required HookValidator validator, + required ApplicationAssetValidator applicationAssetValidator, CCompilerConfig? cCompilerConfig, IOSSdk? targetIOSSdk, int? targetIOSVersion, @@ -150,7 +176,7 @@ class NativeAssetsBuildRunner { PackageLayout? packageLayout, Uri? resourceIdentifiers, String? runPackageName, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, BuildResult? buildResult, bool? linkingEnabled, }) async { @@ -236,6 +262,7 @@ class NativeAssetsBuildRunner { final (hookOutput, packageSuccess) = await _runHookForPackageCached( hook, config, + validator, packageLayout.packageConfigUri, workingDirectory, includeParentEnvironment, @@ -246,17 +273,14 @@ class NativeAssetsBuildRunner { metadata[config.packageName] = hookOutput.metadata; } - // Note the caller will need to check whether there are no duplicates - // between the build and link hook. - final validateResult = validateNoDuplicateDylibs(hookResult.assets); - if (validateResult.isNotEmpty) { - for (final error in validateResult) { - logger.severe(error); - } - hookResult = hookResult.copyAdd(HookOutputImpl(), false); - } + final errors = await applicationAssetValidator(hookResult.encodedAssets); + if (errors.isEmpty) return hookResult; - return hookResult; + logger.severe('Application asset verification failed:'); + for (final error in errors) { + logger.severe('- $error'); + } + return HookResult.failure(); } static Future _cliConfig( @@ -272,7 +296,7 @@ class NativeAssetsBuildRunner { int? targetAndroidNdkApi, int? targetIOSVersion, int? targetMacOSVersion, - Iterable? supportedAssetTypes, + Iterable supportedAssetTypes, Hook hook, Uri? resourceIdentifiers, BuildResult? buildResult, @@ -331,7 +355,7 @@ class NativeAssetsBuildRunner { cCompiler: cCompilerConfig, targetAndroidNdkApi: targetAndroidNdkApi, recordedUsagesFile: resourcesFile?.uri, - assets: buildResult!.assetsForLinking[package.name] ?? [], + encodedAssets: buildResult!.encodedAssetsForLinking[package.name] ?? [], supportedAssetTypes: supportedAssetTypes, linkModePreference: linkModePreference, ); @@ -370,9 +394,10 @@ class NativeAssetsBuildRunner { required Uri workingDirectory, required bool includeParentEnvironment, required bool linkingEnabled, + required BuildValidator buildValidator, PackageLayout? packageLayout, String? runPackageName, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, }) async { const hook = Hook.build; packageLayout ??= await PackageLayout.fromRootPackageRoot(workingDirectory); @@ -413,7 +438,7 @@ class NativeAssetsBuildRunner { continue; } // TODO(https://github.com/dart-lang/native/issues/1321): Should dry runs be cached? - var (buildOutput, packageSuccess) = await runUnderDirectoriesLock( + final (buildOutput, packageSuccess) = await runUnderDirectoriesLock( [ Directory.fromUri(config.outputDirectoryShared.parent), Directory.fromUri(config.outputDirectory.parent), @@ -423,6 +448,8 @@ class NativeAssetsBuildRunner { () => _runHookForPackage( hook, config, + (HookConfig config, HookOutputImpl output) => + buildValidator(config as BuildConfig, output as BuildOutput), packageConfigUri, workingDirectory, includeParentEnvironment, @@ -431,38 +458,15 @@ class NativeAssetsBuildRunner { packageLayout!, ), ); - buildOutput = _expandArchsCodeAssets(buildOutput); hookResult = hookResult.copyAdd(buildOutput, packageSuccess); } return hookResult; } - HookOutputImpl _expandArchsCodeAssets(HookOutputImpl buildOutput) { - final assets = []; - for (final asset in buildOutput.assets) { - switch (asset) { - case CodeAsset _: - 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 DataAsset _: - assets.add(asset); - } - } - return buildOutput.copyWith(assets: assets); - } - Future<_PackageBuildRecord> _runHookForPackageCached( Hook hook, HookConfigImpl config, + HookValidator validator, Uri packageConfigUri, Uri workingDirectory, bool includeParentEnvironment, @@ -497,25 +501,13 @@ class NativeAssetsBuildRunner { final lastBuilt = hookOutput.timestamp.roundDownToSeconds(); final dependenciesLastChange = await hookOutput.dependenciesModel.lastModified(); - late final DateTime assetsLastChange; - if (hook == Hook.link) { - assetsLastChange = await (config as LinkConfigImpl) - .assets - .map((a) => a.file) - .whereType() - .map((u) => u.fileSystemEntity) - .lastModified(); - } if (lastBuilt.isAfter(dependenciesLastChange) && - lastBuilt.isAfter(hookLastSourceChange) && - (hook == Hook.build || lastBuilt.isAfter(assetsLastChange))) { + lastBuilt.isAfter(hookLastSourceChange)) { logger.info( [ 'Skipping ${hook.name} for ${config.packageName} in $outDir.', 'Last build on $lastBuilt.', 'Last dependencies change on $dependenciesLastChange.', - if (hook == Hook.link) - 'Last assets for linking change on $assetsLastChange.', 'Last hook change on $hookLastSourceChange.', ].join(' '), ); @@ -528,6 +520,7 @@ class NativeAssetsBuildRunner { return await _runHookForPackage( hook, config, + validator, packageConfigUri, workingDirectory, includeParentEnvironment, @@ -542,6 +535,7 @@ class NativeAssetsBuildRunner { Future<_PackageBuildRecord> _runHookForPackage( Hook hook, HookConfigImpl config, + HookValidator validator, Uri packageConfigUri, Uri workingDirectory, bool includeParentEnvironment, @@ -601,16 +595,12 @@ ${result.stdout} final output = HookOutputImpl.readFromFile(file: config.outputFile) ?? HookOutputImpl(); - final validateResult = await validate( - config, - output, - packageLayout, - ); - success &= validateResult.success; - if (!validateResult.success) { + final errors = await validate(config, output, packageLayout, validator); + success &= errors.isEmpty; + if (errors.isNotEmpty) { logger.severe('package:${config.packageName}` has invalid output.'); } - for (final error in validateResult.errors) { + for (final error in errors) { logger.severe('- $error'); } return (output, success); @@ -758,7 +748,7 @@ ${compileResult.stdout} required OS targetOS, required LinkModePreference linkMode, required Uri buildParentDir, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, required bool? linkingEnabled, }) async { const hook = Hook.build; @@ -814,32 +804,33 @@ ${compileResult.stdout} }; } - Future validate( + Future validate( HookConfigImpl config, HookOutputImpl output, PackageLayout packageLayout, + HookValidator validator, ) async { - var (errors: errors, success: success) = config is BuildConfigImpl - ? await validateBuild(config, output) - : await validateLink(config as LinkConfigImpl, output); + final errors = config is BuildConfigImpl + ? await validateBuildOutput(config, output) + : await validateLinkOutput(config as LinkConfig, output); + errors.addAll(await validator(config, output)); if (config is BuildConfigImpl) { final packagesWithLink = (await packageLayout.packagesWithAssets(Hook.link)) .map((p) => p.name); - for (final targetPackage in output.assetsForLinking.keys) { + for (final targetPackage in output.encodedAssetsForLinking.keys) { if (!packagesWithLink.contains(targetPackage)) { - for (final asset in output.assetsForLinking[targetPackage]!) { - success &= false; + for (final asset in output.encodedAssetsForLinking[targetPackage]!) { errors.add( - 'Asset "${asset.id}" is sent to package "$targetPackage" for' + 'Asset "$asset" is sent to package "$targetPackage" for' ' linking, but that package does not have a link hook.', ); } } } } - return (errors: errors, success: success); + return errors; } Future<(List plan, PackageGraph? dependencyGraph, bool success)> @@ -878,9 +869,9 @@ ${compileResult.stdout} // Link hooks are skipped if no assets for linking are provided. buildPlan = []; final skipped = []; - final assetsForLinking = buildResult?.assetsForLinking; + final encodedAssetsForLinking = buildResult?.encodedAssetsForLinking; for (final package in packagesWithHook) { - if (assetsForLinking![package.name]?.isNotEmpty ?? false) { + if (encodedAssetsForLinking![package.name]?.isNotEmpty ?? false) { buildPlan.add(package); } else { skipped.add(package.name); diff --git a/pkgs/native_assets_builder/lib/src/model/build_dry_run_result.dart b/pkgs/native_assets_builder/lib/src/model/build_dry_run_result.dart index 1379cf244..6a37448c5 100644 --- a/pkgs/native_assets_builder/lib/src/model/build_dry_run_result.dart +++ b/pkgs/native_assets_builder/lib/src/model/build_dry_run_result.dart @@ -10,7 +10,7 @@ import '../../native_assets_builder.dart'; /// the dependency tree of the entry point application. abstract interface class BuildDryRunResult { /// The native assets produced by the hooks, which should be bundled. - List get assets; + List get encodedAssets; /// Whether all hooks completed without errors. /// @@ -18,5 +18,5 @@ abstract interface class BuildDryRunResult { bool get success; /// The native assets produced by the hooks, which should be linked. - Map> get assetsForLinking; + Map> get encodedAssetsForLinking; } diff --git a/pkgs/native_assets_builder/lib/src/model/build_result.dart b/pkgs/native_assets_builder/lib/src/model/build_result.dart index b38680563..96b2aac2d 100644 --- a/pkgs/native_assets_builder/lib/src/model/build_result.dart +++ b/pkgs/native_assets_builder/lib/src/model/build_result.dart @@ -18,8 +18,8 @@ abstract class BuildResult { bool get success; /// The native assets produced by the hooks, which should be bundled. - List get assets; + List get encodedAssets; /// The native assets produced by the hooks, which should be linked. - Map> get assetsForLinking; + Map> get encodedAssetsForLinking; } diff --git a/pkgs/native_assets_builder/lib/src/model/hook_result.dart b/pkgs/native_assets_builder/lib/src/model/hook_result.dart index 3e0644991..52ec4d98f 100644 --- a/pkgs/native_assets_builder/lib/src/model/hook_result.dart +++ b/pkgs/native_assets_builder/lib/src/model/hook_result.dart @@ -10,13 +10,13 @@ import '../../native_assets_builder.dart'; /// The result from a [NativeAssetsBuildRunner.build] or /// [NativeAssetsBuildRunner.link]. final class HookResult implements BuildResult, BuildDryRunResult, LinkResult { - /// The native assets produced by the hooks, which should be bundled. + /// The native encodedAssets produced by the hooks, which should be bundled. @override - final List assets; + final List encodedAssets; - /// The assets produced by the hooks, which should be linked. + /// The encodedAssets produced by the hooks, which should be linked. @override - final Map> assetsForLinking; + final Map> encodedAssetsForLinking; /// The files used by the hooks. @override @@ -29,21 +29,21 @@ final class HookResult implements BuildResult, BuildDryRunResult, LinkResult { final bool success; HookResult._({ - required this.assets, - required this.assetsForLinking, + required this.encodedAssets, + required this.encodedAssetsForLinking, required this.dependencies, required this.success, }); factory HookResult({ - List? assets, - Map>? assetsForLinking, + List? encodedAssets, + Map>? encodedAssetsForLinking, List? dependencies, bool success = true, }) => HookResult._( - assets: assets ?? [], - assetsForLinking: assetsForLinking ?? {}, + encodedAssets: encodedAssets ?? [], + encodedAssetsForLinking: encodedAssetsForLinking ?? {}, dependencies: dependencies ?? [], success: success, ); @@ -51,28 +51,18 @@ final class HookResult implements BuildResult, BuildDryRunResult, LinkResult { factory HookResult.failure() => HookResult(success: false); HookResult copyAdd(HookOutputImpl hookOutput, bool hookSuccess) { - final mergedMaps = mergeMaps( - assetsForLinking, - hookOutput.assetsForLinking, - value: (assets1, assets2) { - final twoInOne = assets1.where((asset) => assets2.contains(asset)); - final oneInTwo = assets2.where((asset) => assets1.contains(asset)); - if (twoInOne.isNotEmpty || oneInTwo.isNotEmpty) { - throw ArgumentError( - 'Found duplicate IDs, ${oneInTwo.map((e) => e.id).toList()}'); - } - return [ - ...assets1, - ...assets2, - ]; - }, - ); + final mergedMaps = + mergeMaps(encodedAssetsForLinking, hookOutput.encodedAssetsForLinking, + value: (encodedAssets1, encodedAssets2) => [ + ...encodedAssets1, + ...encodedAssets2, + ]); return HookResult( - assets: [ - ...assets, - ...hookOutput.assets, + encodedAssets: [ + ...encodedAssets, + ...hookOutput.encodedAssets, ], - assetsForLinking: mergedMaps, + encodedAssetsForLinking: mergedMaps, dependencies: [ ...dependencies, ...hookOutput.dependencies, @@ -82,8 +72,8 @@ final class HookResult implements BuildResult, BuildDryRunResult, LinkResult { } HookResult withSuccess(bool success) => HookResult( - assets: assets, - assetsForLinking: assetsForLinking, + encodedAssets: encodedAssets, + encodedAssetsForLinking: encodedAssetsForLinking, dependencies: dependencies, success: success, ); diff --git a/pkgs/native_assets_builder/lib/src/model/link_result.dart b/pkgs/native_assets_builder/lib/src/model/link_result.dart index e3f6877f7..a717f9618 100644 --- a/pkgs/native_assets_builder/lib/src/model/link_result.dart +++ b/pkgs/native_assets_builder/lib/src/model/link_result.dart @@ -10,7 +10,7 @@ import '../build_runner/build_runner.dart'; /// the dependency tree of the entry point application. abstract interface class LinkResult { /// The native assets produced by the hooks, which should be bundled. - List get assets; + List get encodedAssets; /// The files used by the hooks. List get dependencies; diff --git a/pkgs/native_assets_builder/test/build_runner/build_dependencies_test.dart b/pkgs/native_assets_builder/test/build_runner/build_dependencies_test.dart index 20e5b9509..e5616093b 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_dependencies_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_dependencies_test.dart @@ -26,8 +26,15 @@ void main() async { // Trigger a build, should invoke build for libraries with native assets. { final logMessages = []; - final result = await build(packageUri, logger, dartExecutable, - capturedLogs: logMessages); + final result = await build( + packageUri, + logger, + dartExecutable, + capturedLogs: logMessages, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); expect( logMessages.join('\n'), stringContainsInOrder( @@ -39,7 +46,7 @@ void main() async { ], ), ); - expect(result.assets.length, 2); + expect(result.encodedAssets.length, 2); expect( result.dependencies, [ diff --git a/pkgs/native_assets_builder/test/build_runner/build_planner_test.dart b/pkgs/native_assets_builder/test/build_runner/build_planner_test.dart index b4a03a30d..4eddad787 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_planner_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_planner_test.dart @@ -6,7 +6,6 @@ import 'dart:io'; import 'package:native_assets_builder/native_assets_builder.dart'; import 'package:native_assets_builder/src/build_runner/build_planner.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_asset_id_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_asset_id_test.dart index fdf8273ac..5f75a90ea 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_asset_id_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_asset_id_test.dart @@ -27,6 +27,9 @@ void main() async { packageUri, createCapturingLogger(logMessages, level: Level.SEVERE), dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); expect(result.success, false); @@ -59,6 +62,9 @@ void main() async { packageUri, logger, dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); expect(result.success, true); } diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_build_dry_run_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_build_dry_run_test.dart index d91cb8961..9e3fa7c4f 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_build_dry_run_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_build_dry_run_test.dart @@ -5,7 +5,6 @@ import 'dart:io'; import 'package:file_testing/file_testing.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -29,29 +28,39 @@ void main() async { logger, dartExecutable, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, ); - final dryRunAssets = dryRunResult.assets.toList(); - final result = await build( + final buildResult = await build( packageUri, logger, dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); - // 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[i]; - final buildAsset = result.assets[0]; - expect(dryRunAsset.id, buildAsset.id); - // The build runner expands CodeAssets to all architectures. - expect(buildAsset.file, isNotNull); - if (dryRunAsset is CodeAsset && buildAsset is CodeAsset) { - expect(dryRunAsset.architecture, isNotNull); - expect(buildAsset.architecture, isNotNull); - expect(dryRunAsset.os, buildAsset.os); - expect(dryRunAsset.linkMode, buildAsset.linkMode); - } - } + expect(dryRunResult.encodedAssets.length, 1); + expect(buildResult.encodedAssets.length, 1); + + final dryRunAsset = dryRunResult.encodedAssets[0]; + expect(dryRunAsset.type, CodeAsset.type); + final dryRunCodeAsset = CodeAsset.fromEncoded(dryRunAsset); + + final buildAsset = buildResult.encodedAssets[0]; + final buildCodeAsset = CodeAsset.fromEncoded(buildAsset); + expect(buildAsset.type, CodeAsset.type); + + // Common across dry-run & build + expect(dryRunCodeAsset.id, buildCodeAsset.id); + expect(dryRunCodeAsset.os, buildCodeAsset.os); + expect(dryRunCodeAsset.linkMode, buildCodeAsset.linkMode); + expect(dryRunCodeAsset.file?.pathSegments.last, + buildCodeAsset.file?.pathSegments.last); + + // Different in dry-run: No architecture + expect(dryRunCodeAsset.architecture, isNull); + expect(buildCodeAsset.architecture, isNotNull); final nativeAssetsBuilderDirectory = packageUri.resolve('.dart_tool/native_assets_builder/'); diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_build_output_format_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_build_output_format_test.dart index e148f16de..827b9d0ea 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_build_output_format_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_build_output_format_test.dart @@ -33,6 +33,9 @@ void main() async { packageUri, createCapturingLogger(logMessages, level: Level.SEVERE), dartExecutable, + supportedAssetTypes: [], + buildValidator: (config, output) async => [], + applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); expect(result.success, false); 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 d20862d2a..c3ca4d278 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,7 +4,6 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -29,8 +28,15 @@ void main() async { { final logMessages = []; - final result = await build(packageUri, logger, dartExecutable, - capturedLogs: logMessages); + final result = await build( + packageUri, + logger, + dartExecutable, + capturedLogs: logMessages, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); expect( logMessages.join('\n'), contains( @@ -48,8 +54,15 @@ void main() async { { final logMessages = []; - final result = await build(packageUri, logger, dartExecutable, - capturedLogs: logMessages); + final result = await build( + packageUri, + logger, + dartExecutable, + capturedLogs: logMessages, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); expect( logMessages.join('\n'), contains('Skipping build for native_add'), @@ -86,9 +99,17 @@ void main() async { await Future.delayed(const Duration(seconds: 1)); { - final result = await build(packageUri, logger, dartExecutable); + final result = await build( + packageUri, + logger, + dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); await expectSymbols( - asset: result.assets.single as CodeAsset, symbols: ['add']); + asset: CodeAsset.fromEncoded(result.encodedAssets.single), + symbols: ['add']); } await copyTestProjects( @@ -97,9 +118,16 @@ void main() async { ); { - final result = await build(packageUri, logger, dartExecutable); + final result = await build( + packageUri, + logger, + dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); await expectSymbols( - asset: result.assets.single as CodeAsset, + asset: CodeAsset.fromEncoded(result.encodedAssets.single), symbols: ['add', 'subtract'], ); } @@ -124,7 +152,14 @@ void main() async { // cached. await Future.delayed(const Duration(seconds: 1)); - final result = await build(packageUri, logger, dartExecutable); + final result = await build( + packageUri, + logger, + dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); { final compiledHook = logMessages .where((m) => @@ -135,7 +170,7 @@ void main() async { } logMessages.clear(); await expectSymbols( - asset: result.assets.single as CodeAsset, + asset: CodeAsset.fromEncoded(result.encodedAssets.single), symbols: ['add'], ); @@ -144,7 +179,14 @@ void main() async { targetUri: packageUri); { - final result = await build(packageUri, logger, dartExecutable); + final result = await build( + packageUri, + logger, + dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); { final compiledHook = logMessages .where((m) => @@ -155,7 +197,7 @@ void main() async { } logMessages.clear(); await expectSymbols( - asset: result.assets.single as CodeAsset, + asset: CodeAsset.fromEncoded(result.encodedAssets.single), symbols: ['add', 'multiply'], ); } diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_cycle_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_cycle_test.dart index f99251865..746cc2701 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_cycle_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_cycle_test.dart @@ -27,6 +27,8 @@ void main() async { createCapturingLogger(logMessages, level: Level.SEVERE), dartExecutable, linkingEnabled: false, + supportedAssetTypes: [], + buildValidator: (config, output) async => [], ); final fullLog = logMessages.join('\n'); expect(result.success, false); @@ -45,6 +47,9 @@ void main() async { packageUri, createCapturingLogger(logMessages, level: Level.SEVERE), dartExecutable, + supportedAssetTypes: [], + buildValidator: (config, output) async => [], + applicationAssetValidator: (_) async => [], ); final fullLog = logMessages.join('\n'); expect(result.success, false); 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 d4939bec6..f973b9c90 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,7 +5,6 @@ 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'; @@ -25,10 +24,18 @@ void main() async { ); { - final result = await build(packageUri, logger, dartExecutable); - expect(result.assets.length, 1); + final result = await build( + packageUri, + logger, + dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); + expect(result.encodedAssets.length, 1); await expectSymbols( - asset: result.assets.single as CodeAsset, symbols: ['add']); + asset: CodeAsset.fromEncoded(result.encodedAssets.single), + symbols: ['add']); expect( result.dependencies, [ @@ -48,6 +55,9 @@ void main() async { packageUri, createCapturingLogger(logMessages, level: Level.SEVERE), dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); expect(result.success, false); @@ -68,10 +78,18 @@ void main() async { ); { - final result = await build(packageUri, logger, dartExecutable); - expect(result.assets.length, 1); + final result = await build( + packageUri, + logger, + dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); + expect(result.encodedAssets.length, 1); await expectSymbols( - asset: result.assets.single as CodeAsset, symbols: ['add']); + asset: CodeAsset.fromEncoded(result.encodedAssets.single), + symbols: ['add']); expect( result.dependencies, [ diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_non_root_package_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_non_root_package_test.dart index 1df7626a3..c34013ae3 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_non_root_package_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_non_root_package_test.dart @@ -30,8 +30,11 @@ void main() async { dartExecutable, capturedLogs: logMessages, runPackageName: 'some_dev_dep', + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); - expect(result.assets, isEmpty); + expect(result.encodedAssets, isEmpty); expect(result.dependencies, isEmpty); } @@ -43,8 +46,11 @@ void main() async { dartExecutable, capturedLogs: logMessages, runPackageName: 'native_add', + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); - expect(result.assets, isNotEmpty); + expect(result.encodedAssets, isNotEmpty); 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 6568332ab..a19cffb95 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 @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:native_assets_builder/src/build_runner/build_runner.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -34,6 +33,8 @@ void main() async { workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, + supportedAssetTypes: [], + buildValidator: (config, output) async => [], ); await buildRunner.buildDryRun( targetOS: Target.current.os, @@ -41,6 +42,8 @@ void main() async { workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, + supportedAssetTypes: [], + buildValidator: (config, output) async => [], ); await buildRunner.build( buildMode: BuildMode.release, @@ -49,6 +52,9 @@ void main() async { workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, + supportedAssetTypes: [], + buildValidator: (config, output) async => [], + applicationAssetValidator: (_) async => [], ); await buildRunner.build( buildMode: BuildMode.release, @@ -57,6 +63,9 @@ void main() async { workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, + supportedAssetTypes: [], + buildValidator: (config, output) async => [], + applicationAssetValidator: (_) async => [], ); }); }); 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 bfb9b738f..e5b0ecf49 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 @@ -4,7 +4,6 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -61,8 +60,11 @@ void main() async { ), // Prevent any other environment variables. includeParentEnvironment: false, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); - expect(result.assets.length, 1); + expect(result.encodedAssets.length, 1); }); }); } diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_test.dart index cdd19fa95..9c635ac51 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_runner_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_test.dart @@ -32,15 +32,22 @@ void main() async { // Trigger a build, should invoke build for libraries with native assets. { final logMessages = []; - final result = await build(packageUri, logger, dartExecutable, - capturedLogs: logMessages); + final result = await build( + packageUri, + logger, + dartExecutable, + capturedLogs: logMessages, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, + ); expect( logMessages.join('\n'), stringContainsInOrder([ 'native_add${Platform.pathSeparator}hook' '${Platform.pathSeparator}build.dart', ])); - expect(result.assets.length, 1); + expect(result.encodedAssets.length, 1); } // Trigger a build, should not invoke anything. @@ -56,6 +63,9 @@ void main() async { dartExecutable, capturedLogs: logMessages, packageLayout: packageLayout, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); expect( false, @@ -64,7 +74,7 @@ void main() async { '${Platform.pathSeparator}build.dart', ), ); - expect(result.assets.length, 1); + expect(result.encodedAssets.length, 1); } }); }); diff --git a/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test.dart b/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test.dart index 1b095e6dd..3dc75b307 100644 --- a/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:native_assets_builder/src/utils/run_process.dart' show RunProcessResult; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; diff --git a/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test_helper.dart b/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test_helper.dart index 1e127a21b..bafbc2d5e 100644 --- a/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test_helper.dart +++ b/pkgs/native_assets_builder/test/build_runner/concurrency_shared_test_helper.dart @@ -4,7 +4,6 @@ import 'package:logging/logging.dart'; import 'package:native_assets_builder/native_assets_builder.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import '../helpers.dart'; @@ -29,6 +28,9 @@ void main(List args) async { linkingEnabled: false, supportedAssetTypes: [DataAsset.type], targetAndroidNdkApi: target.os == OS.android ? 30 : null, + buildValidator: (config, output) async => + await validateDataAssetBuildOutput(config, output), + applicationAssetValidator: (_) async => [], ); if (!result.success) { throw Error(); diff --git a/pkgs/native_assets_builder/test/build_runner/concurrency_test_helper.dart b/pkgs/native_assets_builder/test/build_runner/concurrency_test_helper.dart index fe37397bb..32ac611a6 100644 --- a/pkgs/native_assets_builder/test/build_runner/concurrency_test_helper.dart +++ b/pkgs/native_assets_builder/test/build_runner/concurrency_test_helper.dart @@ -4,7 +4,6 @@ import 'package:logging/logging.dart'; import 'package:native_assets_builder/native_assets_builder.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import '../helpers.dart'; @@ -31,6 +30,12 @@ void main(List args) async { workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type, DataAsset.type], + buildValidator: (config, output) async => [ + ...await validateCodeAssetBuildOutput(config, output), + ...await validateDataAssetBuildOutput(config, output), + ], + applicationAssetValidator: validateCodeAssetsInApplication, ); if (!result.success) { throw Error(); diff --git a/pkgs/native_assets_builder/test/build_runner/conflicting_dylib_test.dart b/pkgs/native_assets_builder/test/build_runner/conflicting_dylib_test.dart index e3e13bc02..ddd48256a 100644 --- a/pkgs/native_assets_builder/test/build_runner/conflicting_dylib_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/conflicting_dylib_test.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:logging/logging.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -28,6 +27,9 @@ void main() async { packageUri, createCapturingLogger(logMessages, level: Level.SEVERE), dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); expect(result.success, false); @@ -55,6 +57,9 @@ void main() async { logger, linkingEnabled: true, dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); expect(buildResult.success, isTrue); @@ -63,11 +68,17 @@ void main() async { logger, dartExecutable, buildResult: buildResult, + supportedAssetTypes: [CodeAsset.type], + linkValidator: validateCodeAssetLinkOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); expect(linkResult.success, isTrue); - final allAssets = [...buildResult.assets, ...linkResult.assets]; - final validateResult = validateNoDuplicateDylibs(allAssets); + final allAssets = [ + ...buildResult.encodedAssets, + ...linkResult.encodedAssets + ].where((e) => e.type == CodeAsset.type).toList(); + final validateResult = await validateCodeAssetsInApplication(allAssets); expect(validateResult, isNotEmpty); }); }); diff --git a/pkgs/native_assets_builder/test/build_runner/fail_on_os_sdk_version_test.dart b/pkgs/native_assets_builder/test/build_runner/fail_on_os_sdk_version_test.dart index 0e2055130..021a3d2fd 100644 --- a/pkgs/native_assets_builder/test/build_runner/fail_on_os_sdk_version_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/fail_on_os_sdk_version_test.dart @@ -3,9 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:logging/logging.dart'; -import 'package:native_assets_cli/native_assets_cli.dart' show OS; -import 'package:native_assets_cli/native_assets_cli_internal.dart' - show IOSSdk, Target; import 'package:test/test.dart'; import '../helpers.dart'; @@ -76,6 +73,16 @@ void main() async { packageUri, createCapturingLogger(logMessages, level: Level.SEVERE), dartExecutable, + supportedAssetTypes: [CodeAsset.type, DataAsset.type], + buildValidator: (config, output) async => [ + ...await validateCodeAssetBuildOutput(config, output), + ...await validateDataAssetBuildOutput(config, output), + ], + linkValidator: (config, output) async => [ + ...await validateCodeAssetLinkOutput(config, output), + ...await validateDataAssetLinkOutput(config, output), + ], + applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); if (hook == 'build') { diff --git a/pkgs/native_assets_builder/test/build_runner/helpers.dart b/pkgs/native_assets_builder/test/build_runner/helpers.dart index ba809a893..daaa8f8b1 100644 --- a/pkgs/native_assets_builder/test/build_runner/helpers.dart +++ b/pkgs/native_assets_builder/test/build_runner/helpers.dart @@ -5,11 +5,9 @@ import 'dart:async'; import 'dart:io'; -import 'package:file_testing/file_testing.dart'; import 'package:logging/logging.dart'; import 'package:native_assets_builder/native_assets_builder.dart'; import 'package:native_assets_builder/src/model/hook_result.dart'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -35,6 +33,8 @@ Future build( Uri packageUri, Logger logger, Uri dartExecutable, { + required BuildValidator buildValidator, + required ApplicationAssetValidator applicationAssetValidator, LinkModePreference linkModePreference = LinkModePreference.dynamic, CCompilerConfig? cCompilerConfig, bool includeParentEnvironment = true, @@ -47,7 +47,7 @@ Future build( int? targetAndroidNdkApi, Target? target, bool linkingEnabled = false, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, }) async => await runWithLog(capturedLogs, () async { final result = await NativeAssetsBuildRunner( @@ -68,12 +68,15 @@ Future build( targetAndroidNdkApi: targetAndroidNdkApi, linkingEnabled: linkingEnabled, supportedAssetTypes: supportedAssetTypes, + buildValidator: buildValidator, + applicationAssetValidator: applicationAssetValidator, ); if (result.success) { - await expectAssetsExist(result.assets); - for (final assetsForLinking in result.assetsForLinking.values) { - await expectAssetsExist(assetsForLinking); + expect(await result.encodedAssets.allExist(), true); + for (final encodedAssetsForLinking + in result.encodedAssetsForLinking.values) { + expect(await encodedAssetsForLinking.allExist(), true); } } @@ -84,6 +87,8 @@ Future link( Uri packageUri, Logger logger, Uri dartExecutable, { + required LinkValidator linkValidator, + required ApplicationAssetValidator applicationAssetValidator, LinkModePreference linkModePreference = LinkModePreference.dynamic, CCompilerConfig? cCompilerConfig, bool includeParentEnvironment = true, @@ -96,7 +101,7 @@ Future link( int? targetMacOSVersion, int? targetAndroidNdkApi, Target? target, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, }) async => await runWithLog(capturedLogs, () async { final result = await NativeAssetsBuildRunner( @@ -117,10 +122,12 @@ Future link( targetMacOSVersion: targetMacOSVersion, targetAndroidNdkApi: targetAndroidNdkApi, supportedAssetTypes: supportedAssetTypes, + linkValidator: linkValidator, + applicationAssetValidator: applicationAssetValidator, ); if (result.success) { - await expectAssetsExist(result.assets); + expect(await result.encodedAssets.allExist(), true); } return result; @@ -132,6 +139,9 @@ Future<(BuildResult, LinkResult)> buildAndLink( Uri dartExecutable, { LinkModePreference linkModePreference = LinkModePreference.dynamic, CCompilerConfig? cCompilerConfig, + required LinkValidator linkValidator, + required BuildValidator buildValidator, + required ApplicationAssetValidator applicationAssetValidator, bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, @@ -142,7 +152,7 @@ Future<(BuildResult, LinkResult)> buildAndLink( int? targetAndroidNdkApi, Target? target, Uri? resourceIdentifiers, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, }) async => await runWithLog(capturedLogs, () async { final buildRunner = NativeAssetsBuildRunner( @@ -164,15 +174,18 @@ Future<(BuildResult, LinkResult)> buildAndLink( targetAndroidNdkApi: targetAndroidNdkApi, linkingEnabled: true, supportedAssetTypes: supportedAssetTypes, + buildValidator: buildValidator, + applicationAssetValidator: applicationAssetValidator, ); if (!buildResult.success) { return (buildResult, HookResult()); } - await expectAssetsExist(buildResult.assets); - for (final assetsForLinking in buildResult.assetsForLinking.values) { - await expectAssetsExist(assetsForLinking); + expect(await buildResult.encodedAssets.allExist(), true); + for (final encodedAssetsForLinking + in buildResult.encodedAssetsForLinking.values) { + expect(await encodedAssetsForLinking.allExist(), true); } final linkResult = await buildRunner.link( @@ -190,10 +203,12 @@ Future<(BuildResult, LinkResult)> buildAndLink( targetMacOSVersion: targetMacOSVersion, targetAndroidNdkApi: targetAndroidNdkApi, supportedAssetTypes: supportedAssetTypes, + linkValidator: linkValidator, + applicationAssetValidator: applicationAssetValidator, ); if (linkResult.success) { - await expectAssetsExist(buildResult.assets); + expect(await buildResult.encodedAssets.allExist(), true); } return (buildResult, linkResult); @@ -222,13 +237,14 @@ Future buildDryRun( Uri packageUri, Logger logger, Uri dartExecutable, { + required BuildValidator buildValidator, LinkModePreference linkModePreference = LinkModePreference.dynamic, CCompilerConfig? cCompilerConfig, bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, required bool linkingEnabled, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, }) async => runWithLog(capturedLogs, () async { final result = await NativeAssetsBuildRunner( @@ -242,16 +258,11 @@ Future buildDryRun( packageLayout: packageLayout, linkingEnabled: linkingEnabled, supportedAssetTypes: supportedAssetTypes, + buildValidator: buildValidator, ); return result; }); -Future expectAssetsExist(List assets) async { - for (final asset in assets) { - expect(File.fromUri(asset.file!), exists); - } -} - Future expectSymbols({ required CodeAsset asset, required List symbols, diff --git a/pkgs/native_assets_builder/test/build_runner/link_caching_test.dart b/pkgs/native_assets_builder/test/build_runner/link_caching_test.dart index 0104ed64c..f43429f8e 100644 --- a/pkgs/native_assets_builder/test/build_runner/link_caching_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/link_caching_test.dart @@ -5,14 +5,12 @@ import 'dart:io'; import 'package:native_assets_builder/native_assets_builder.dart'; -import 'package:native_assets_cli/src/asset.dart'; import 'package:test/test.dart'; import '../helpers.dart'; import 'helpers.dart'; void main() async { - const supportedAssetTypes = [DataAsset.type]; const packageName = 'simple_link'; test('link hook caching', () async { @@ -40,8 +38,10 @@ void main() async { logger, dartExecutable, linkingEnabled: true, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], capturedLogs: logMessages, + buildValidator: validateDataAssetBuildOutput, + applicationAssetValidator: (_) async => [], ); } @@ -52,8 +52,10 @@ void main() async { logger, dartExecutable, buildResult: buildResult, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], capturedLogs: logMessages, + linkValidator: validateDataAssetLinkOutput, + applicationAssetValidator: (_) async => [], ); } diff --git a/pkgs/native_assets_builder/test/build_runner/link_test.dart b/pkgs/native_assets_builder/test/build_runner/link_test.dart index d7cc00e33..2a7d5d76f 100644 --- a/pkgs/native_assets_builder/test/build_runner/link_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/link_test.dart @@ -4,7 +4,6 @@ import 'dart:io'; -import 'package:native_assets_cli/src/asset.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -13,8 +12,6 @@ import 'helpers.dart'; const Timeout longTimeout = Timeout(Duration(minutes: 5)); void main() async { - const supportedAssetTypes = [DataAsset.type]; - test( 'simple_link linking', timeout: longTimeout, @@ -34,27 +31,33 @@ void main() async { logger, dartExecutable, linkingEnabled: true, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], + buildValidator: validateDataAssetBuildOutput, + applicationAssetValidator: (_) async => [], ); - expect(buildResult.assets.length, 0); + expect(buildResult.encodedAssets.length, 0); final linkResult = await link( packageUri, logger, dartExecutable, buildResult: buildResult, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], + linkValidator: validateDataAssetLinkOutput, + applicationAssetValidator: (_) async => [], ); - expect(linkResult.assets.length, 2); + expect(linkResult.encodedAssets.length, 2); final buildNoLinkResult = await build( packageUri, logger, dartExecutable, linkingEnabled: false, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], + buildValidator: validateDataAssetBuildOutput, + applicationAssetValidator: (_) async => [], ); - expect(buildNoLinkResult.assets.length, 4); + expect(buildNoLinkResult.encodedAssets.length, 4); }); }, ); @@ -78,11 +81,11 @@ void main() async { 'assets/data_0.json', 'assets/data_1.json', ]; - final assetsForLinking = [ + final encodedAssetsForLinking = [ ...helperAssetsForLinking, ...mainAssetsForLinking, ]; - final linkedAssets = assetsForLinking.skip(1); + final linkedAssets = encodedAssetsForLinking.skip(1); await copyTestProjects(targetUri: tempUri); final packageUri = tempUri.resolve('complex_link/'); @@ -95,14 +98,16 @@ void main() async { logger, dartExecutable, linkingEnabled: true, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], + buildValidator: validateDataAssetBuildOutput, + applicationAssetValidator: (_) async => [], ); expect(buildResult.success, true); + expect(_getNames(buildResult.encodedAssets), + unorderedEquals(builtHelperAssets)); expect( - _getNames(buildResult.assets), unorderedEquals(builtHelperAssets)); - expect( - _getNames(buildResult.assetsForLinking['complex_link']!), - unorderedEquals(assetsForLinking), + _getNames(buildResult.encodedAssetsForLinking['complex_link']!), + unorderedEquals(encodedAssetsForLinking), ); final linkResult = await link( @@ -110,11 +115,14 @@ void main() async { logger, dartExecutable, buildResult: buildResult, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], + linkValidator: validateDataAssetLinkOutput, + applicationAssetValidator: (_) async => [], ); expect(linkResult.success, true); - expect(_getNames(linkResult.assets), unorderedEquals(linkedAssets)); + expect( + _getNames(linkResult.encodedAssets), unorderedEquals(linkedAssets)); }); }, ); @@ -135,10 +143,12 @@ void main() async { logger, dartExecutable, linkingEnabled: true, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], + buildValidator: validateDataAssetBuildOutput, + applicationAssetValidator: (_) async => [], ); - expect(buildResult.assets.length, 0); - expect(buildResult.assetsForLinking.length, 0); + expect(buildResult.encodedAssets.length, 0); + expect(buildResult.encodedAssetsForLinking.length, 0); final logMessages = []; final linkResult = await link( @@ -147,9 +157,11 @@ void main() async { dartExecutable, buildResult: buildResult, capturedLogs: logMessages, - supportedAssetTypes: supportedAssetTypes, + supportedAssetTypes: [DataAsset.type], + linkValidator: validateDataAssetLinkOutput, + applicationAssetValidator: (_) async => [], ); - expect(linkResult.assets.length, 0); + expect(linkResult.encodedAssets.length, 0); expect( logMessages, contains( @@ -184,9 +196,12 @@ void main() async { logger, dartExecutable, linkingEnabled: true, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); - expect(buildResult.assets.length, 0); - expect(buildResult.assetsForLinking.length, 1); + expect(buildResult.encodedAssets.length, 0); + expect(buildResult.encodedAssetsForLinking.length, 1); final logMessages = []; final linkResult = await link( @@ -195,13 +210,17 @@ void main() async { dartExecutable, buildResult: buildResult, capturedLogs: logMessages, + supportedAssetTypes: [CodeAsset.type], + linkValidator: validateCodeAssetLinkOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); - expect(linkResult.assets.length, 1); - expect(linkResult.assets.first, isA()); + expect(linkResult.encodedAssets.length, 1); + expect(linkResult.encodedAssets.first.type, CodeAsset.type); }); }, ); } -Iterable _getNames(List assets) => - assets.whereType().map((asset) => asset.name); +Iterable _getNames(List assets) => assets + .where((e) => e.type == DataAsset.type) + .map((e) => DataAsset.fromEncoded(e).name); diff --git a/pkgs/native_assets_builder/test/build_runner/metadata_test.dart b/pkgs/native_assets_builder/test/build_runner/metadata_test.dart index d38863533..8fcb39afe 100644 --- a/pkgs/native_assets_builder/test/build_runner/metadata_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/metadata_test.dart @@ -26,8 +26,15 @@ void main() async { // Trigger a build, should invoke build for libraries with native assets. { final logMessages = []; - await build(packageUri, logger, dartExecutable, - capturedLogs: logMessages); + await build( + packageUri, + logger, + dartExecutable, + capturedLogs: logMessages, + supportedAssetTypes: ['foo'], + buildValidator: (config, output) async => [], + applicationAssetValidator: (_) async => [], + ); expect( logMessages.join('\n'), stringContainsInOrder([ 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 2129b4ed5..55b20dba4 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 @@ -2,7 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -27,6 +26,9 @@ void main() async { logger, dartExecutable, linkModePreference: LinkModePreference.dynamic, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); final resultPreferDynamic = await build( @@ -34,6 +36,9 @@ void main() async { logger, dartExecutable, linkModePreference: LinkModePreference.preferDynamic, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); final resultStatic = await build( @@ -41,6 +46,9 @@ void main() async { logger, dartExecutable, linkModePreference: LinkModePreference.static, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); final resultPreferStatic = await build( @@ -48,23 +56,27 @@ void main() async { logger, dartExecutable, linkModePreference: LinkModePreference.preferStatic, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); // This package honors preferences. expect( - (resultDynamic.assets.single as CodeAsset).linkMode, + CodeAsset.fromEncoded(resultDynamic.encodedAssets.single).linkMode, DynamicLoadingBundled(), ); expect( - (resultPreferDynamic.assets.single as CodeAsset).linkMode, + CodeAsset.fromEncoded(resultPreferDynamic.encodedAssets.single) + .linkMode, DynamicLoadingBundled(), ); expect( - (resultStatic.assets.single as CodeAsset).linkMode, + CodeAsset.fromEncoded(resultStatic.encodedAssets.single).linkMode, StaticLinking(), ); expect( - (resultPreferStatic.assets.single as CodeAsset).linkMode, + CodeAsset.fromEncoded(resultPreferStatic.encodedAssets.single).linkMode, StaticLinking(), ); }); diff --git a/pkgs/native_assets_builder/test/build_runner/resources_test.dart b/pkgs/native_assets_builder/test/build_runner/resources_test.dart index a46027f34..14b80af78 100644 --- a/pkgs/native_assets_builder/test/build_runner/resources_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/resources_test.dart @@ -34,6 +34,9 @@ void main() async { logger, dartExecutable, linkingEnabled: true, + supportedAssetTypes: [DataAsset.type], + buildValidator: validateDataAssetBuildOutput, + applicationAssetValidator: (_) async => [], ); Iterable buildFiles() => Directory.fromUri( @@ -49,6 +52,9 @@ void main() async { dartExecutable, buildResult: buildResult, resourceIdentifiers: resourcesUri, + supportedAssetTypes: [DataAsset.type], + linkValidator: validateDataAssetLinkOutput, + applicationAssetValidator: (_) async => [], ); expect(buildFiles(), anyElement(endsWith('resources.json'))); }); diff --git a/pkgs/native_assets_builder/test/build_runner/wrong_linker_test.dart b/pkgs/native_assets_builder/test/build_runner/wrong_linker_test.dart index 1b7f98a6d..01e637f5a 100644 --- a/pkgs/native_assets_builder/test/build_runner/wrong_linker_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/wrong_linker_test.dart @@ -27,6 +27,9 @@ void main() async { packageUri, createCapturingLogger(logMessages, level: Level.SEVERE), dartExecutable, + supportedAssetTypes: [CodeAsset.type], + buildValidator: validateCodeAssetBuildOutput, + applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); expect(result.success, false); diff --git a/pkgs/native_assets_builder/test/helpers.dart b/pkgs/native_assets_builder/test/helpers.dart index 21d22a538..85759c73b 100644 --- a/pkgs/native_assets_builder/test/helpers.dart +++ b/pkgs/native_assets_builder/test/helpers.dart @@ -13,6 +13,8 @@ import 'package:native_assets_cli/native_assets_cli_internal.dart' as internal; import 'package:test/test.dart'; import 'package:yaml/yaml.dart'; +export 'package:native_assets_cli/native_assets_cli_internal.dart'; + extension UriExtension on Uri { String get name => pathSegments.where((e) => e != '').last; @@ -170,21 +172,24 @@ extension on String { Uri asFileUri() => Uri.file(this); } -extension AssetIterable on Iterable { +extension AssetIterable on Iterable { Future allExist() async { - final allResults = await Future.wait(map((e) => e.exists())); - final missing = allResults.contains(false); - return !missing; - } -} - -extension on Asset { - Future exists() async { - final path_ = file; - return switch (path_) { - null => true, - _ => await path_.fileSystemEntity.exists(), - }; + for (final encodedAsset in this) { + if (encodedAsset.type == DataAsset.type) { + final dataAsset = DataAsset.fromEncoded(encodedAsset); + if (!await dataAsset.file.fileSystemEntity.exists()) { + return false; + } + } else if (encodedAsset.type == CodeAsset.type) { + final codeAsset = CodeAsset.fromEncoded(encodedAsset); + if (!await (codeAsset.file?.fileSystemEntity.exists() ?? true)) { + return false; + } + } else { + throw UnimplementedError('Unknown asset type ${encodedAsset.type}'); + } + } + return true; } } diff --git a/pkgs/native_assets_builder/test/test_data/native_dynamic_linking_test.dart b/pkgs/native_assets_builder/test/test_data/native_dynamic_linking_test.dart index 165a76e44..fbf4947d2 100644 --- a/pkgs/native_assets_builder/test/test_data/native_dynamic_linking_test.dart +++ b/pkgs/native_assets_builder/test/test_data/native_dynamic_linking_test.dart @@ -11,7 +11,6 @@ library; import 'dart:convert'; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../helpers.dart'; @@ -46,6 +45,7 @@ void main() async { targetArchitecture: dryRun ? null : Architecture.current, buildMode: dryRun ? null : BuildMode.debug, cCompiler: dryRun ? null : cCompiler, + supportedAssetTypes: [CodeAsset.type], ); final buildConfigUri = testTempUri.resolve('build_config.json'); @@ -70,7 +70,7 @@ void main() async { final buildOutputUri = outputDirectory.resolve('build_output.json'); final buildOutput = HookOutputImpl.fromJsonString( await File.fromUri(buildOutputUri).readAsString()); - final assets = buildOutput.assets; + final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { expect(assets.length, greaterThanOrEqualTo(3)); @@ -88,6 +88,8 @@ void main() async { ); final addLibraryPath = assets + .where((e) => e.type == CodeAsset.type) + .map(CodeAsset.fromEncoded) .firstWhere((asset) => asset.id.endsWith('add.dart')) .file! .toFilePath(); diff --git a/pkgs/native_assets_builder/test/test_data/transformer_test.dart b/pkgs/native_assets_builder/test/test_data/transformer_test.dart index e26c8be5e..334fa8724 100644 --- a/pkgs/native_assets_builder/test/test_data/transformer_test.dart +++ b/pkgs/native_assets_builder/test/test_data/transformer_test.dart @@ -11,7 +11,6 @@ library; import 'dart:convert'; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; import '../build_runner/helpers.dart'; @@ -43,7 +42,7 @@ void main() async { final dartUri = Uri.file(Platform.resolvedExecutable); late String stdout; - late HookOutputImpl output; + late BuildOutput output; Future runBuild(Architecture architecture) async { final config = BuildConfigImpl( @@ -95,7 +94,7 @@ void main() async { ]), ); expect( - output.assets, + output.dataAssets.all, contains( DataAsset( file: outputDirectoryShared.resolve('data_transformed0.json'), diff --git a/pkgs/native_assets_builder/test_data/add_asset_link/hook/link.dart b/pkgs/native_assets_builder/test_data/add_asset_link/hook/link.dart index 371081747..ccfbb9775 100644 --- a/pkgs/native_assets_builder/test_data/add_asset_link/hook/link.dart +++ b/pkgs/native_assets_builder/test_data/add_asset_link/hook/link.dart @@ -6,9 +6,9 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List arguments) async { await link(arguments, (config, output) async { - final builtDylib = config.assets.first as CodeAsset; + final builtDylib = config.codeAssets.all.first; output - ..addAsset( + ..codeAssets.add( CodeAsset( package: 'add_asset_link', name: 'dylib_add_link', diff --git a/pkgs/native_assets_builder/test_data/complex_link/hook/build.dart b/pkgs/native_assets_builder/test_data/complex_link/hook/build.dart index 08fa5e4e2..0ac49c85c 100644 --- a/pkgs/native_assets_builder/test_data/complex_link/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/complex_link/hook/build.dart @@ -24,7 +24,7 @@ void main(List args) async { .toFilePath(windows: false) .substring(config.packageRoot.toFilePath(windows: false).length); - output.addAsset( + output.dataAssets.add( DataAsset( package: packageName, name: name, diff --git a/pkgs/native_assets_builder/test_data/complex_link/hook/link.dart b/pkgs/native_assets_builder/test_data/complex_link/hook/link.dart index 8e393e7c2..113586781 100644 --- a/pkgs/native_assets_builder/test_data/complex_link/hook/link.dart +++ b/pkgs/native_assets_builder/test_data/complex_link/hook/link.dart @@ -7,9 +7,10 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List args) async { await link( args, - (config, output) async => output.addAssets(treeshake(config.assets)), + (config, output) async => + output.dataAssets.addAll(treeshake(config.dataAssets.all)), ); } -Iterable treeshake(Iterable assets) => +Iterable treeshake(Iterable assets) => assets.where((asset) => !asset.id.endsWith('assets/data_helper_2.json')); diff --git a/pkgs/native_assets_builder/test_data/complex_link_helper/hook/build.dart b/pkgs/native_assets_builder/test_data/complex_link_helper/hook/build.dart index a4c60e03a..79ff0dfdf 100644 --- a/pkgs/native_assets_builder/test_data/complex_link_helper/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/complex_link_helper/hook/build.dart @@ -25,7 +25,7 @@ void main(List args) async { .substring(config.packageRoot.toFilePath(windows: false).length); final forLinking = name.contains('2') || name.contains('3'); - output.addAsset( + output.dataAssets.add( DataAsset( package: packageName, name: name, diff --git a/pkgs/native_assets_builder/test_data/drop_dylib_link/hook/link.dart b/pkgs/native_assets_builder/test_data/drop_dylib_link/hook/link.dart index b2f15c0d9..b3e266d9b 100644 --- a/pkgs/native_assets_builder/test_data/drop_dylib_link/hook/link.dart +++ b/pkgs/native_assets_builder/test_data/drop_dylib_link/hook/link.dart @@ -7,11 +7,12 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List arguments) async { await link(arguments, (config, output) async { print(''' -Received ${config.assets.length} assets: ${config.assets.map((e) => e.id)}. +Received ${config.codeAssets.all.length} encodedAssets: ${config.codeAssets.all.map((e) => e.id)}. '''); - output.addAssets(config.assets.where((asset) => asset.id.endsWith('add'))); + output.codeAssets.addAll( + config.codeAssets.all.where((asset) => asset.id.endsWith('add'))); print(''' -Keeping only ${output.assets.map((e) => e.id)}. +Keeping only ${output.codeAssets.all.map((e) => e.id)}. '''); output.addDependency(config.packageRoot.resolve('hook/link.dart')); }); diff --git a/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version_link/hook/build.dart b/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version_link/hook/build.dart index 3cece99b0..5ac16fef7 100644 --- a/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version_link/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version_link/hook/build.dart @@ -6,7 +6,7 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List arguments) async { await build(arguments, (config, output) async { - output.addAsset( + output.dataAssets.add( DataAsset( name: 'data', file: config.packageRoot.resolve('assets/data.json'), diff --git a/pkgs/native_assets_builder/test_data/native_add_duplicate/hook/build.dart b/pkgs/native_assets_builder/test_data/native_add_duplicate/hook/build.dart index dc2b2b4c6..aab673ecc 100644 --- a/pkgs/native_assets_builder/test_data/native_add_duplicate/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/native_add_duplicate/hook/build.dart @@ -28,8 +28,8 @@ void main(List arguments) async { print('${record.level.name}: ${record.time}: ${record.message}'); }), ); - output.addAsset( - tempBuildOutput.assets.single, + output.addEncodedAsset( + tempBuildOutput.encodedAssets.single, // Send dylib to linking if linking is enabled. linkInPackage: config.linkingEnabled ? packageName : null, ); diff --git a/pkgs/native_assets_builder/test_data/native_add_duplicate/hook/link.dart b/pkgs/native_assets_builder/test_data/native_add_duplicate/hook/link.dart index 21d879ca6..b69973d47 100644 --- a/pkgs/native_assets_builder/test_data/native_add_duplicate/hook/link.dart +++ b/pkgs/native_assets_builder/test_data/native_add_duplicate/hook/link.dart @@ -7,6 +7,6 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List args) async { await link(args, (config, output) async { // Simply output the dylib in the link hook. - output.addAssets(config.assets); + output.codeAssets.addAll(config.codeAssets.all); }); } diff --git a/pkgs/native_assets_builder/test_data/no_asset_for_link/hook/link.dart b/pkgs/native_assets_builder/test_data/no_asset_for_link/hook/link.dart index d617fa723..e73ad42ea 100644 --- a/pkgs/native_assets_builder/test_data/no_asset_for_link/hook/link.dart +++ b/pkgs/native_assets_builder/test_data/no_asset_for_link/hook/link.dart @@ -6,11 +6,7 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List arguments) async { await link(arguments, (config, output) async { - output.addAssets( - config.assets, - ); - output.addDependency( - config.packageRoot.resolve('hook/link.dart'), - ); + output.addEncodedAssets(config.encodedAssets); + output.addDependency(config.packageRoot.resolve('hook/link.dart')); }); } diff --git a/pkgs/native_assets_builder/test_data/simple_data_asset/hook/build.dart b/pkgs/native_assets_builder/test_data/simple_data_asset/hook/build.dart index 3e18d4c11..e7aed2a3c 100644 --- a/pkgs/native_assets_builder/test_data/simple_data_asset/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/simple_data_asset/hook/build.dart @@ -24,7 +24,7 @@ void main(List args) async { .toFilePath(windows: false) .substring(config.packageRoot.toFilePath(windows: false).length); - output.addAsset( + output.dataAssets.add( DataAsset( package: config.packageName, name: name, diff --git a/pkgs/native_assets_builder/test_data/simple_link/hook/build.dart b/pkgs/native_assets_builder/test_data/simple_link/hook/build.dart index 08fa5e4e2..0ac49c85c 100644 --- a/pkgs/native_assets_builder/test_data/simple_link/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/simple_link/hook/build.dart @@ -24,7 +24,7 @@ void main(List args) async { .toFilePath(windows: false) .substring(config.packageRoot.toFilePath(windows: false).length); - output.addAsset( + output.dataAssets.add( DataAsset( package: packageName, name: name, diff --git a/pkgs/native_assets_builder/test_data/simple_link/hook/link.dart b/pkgs/native_assets_builder/test_data/simple_link/hook/link.dart index 1c019fcee..9e4d9f2ee 100644 --- a/pkgs/native_assets_builder/test_data/simple_link/hook/link.dart +++ b/pkgs/native_assets_builder/test_data/simple_link/hook/link.dart @@ -5,10 +5,16 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List args) async { - await link( - args, - (config, output) async => output.addAssets(shake(config.assets)), - ); + await link(args, (config, output) async { + shake(output, config.dataAssets.all); + }); } -Iterable shake(Iterable assets) => assets.skip(2); +void shake(LinkOutput output, Iterable assets) { + for (final asset in assets.skip(2)) { + output.dataAssets.add(asset); + + // If the file changes we'd like to re-run the linker. + output.addDependency(asset.file); + } +} diff --git a/pkgs/native_assets_builder/test_data/transformer/hook/build.dart b/pkgs/native_assets_builder/test_data/transformer/hook/build.dart index 9b1f2308c..9f5956bed 100644 --- a/pkgs/native_assets_builder/test_data/transformer/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/transformer/hook/build.dart @@ -40,7 +40,7 @@ void main(List arguments) async { } } - output.addAsset( + output.dataAssets.add( DataAsset( package: config.packageName, name: name, diff --git a/pkgs/native_assets_builder/test_data/treeshaking_native_libs/hook/link.dart b/pkgs/native_assets_builder/test_data/treeshaking_native_libs/hook/link.dart index 22d7600f6..07c868d95 100644 --- a/pkgs/native_assets_builder/test_data/treeshaking_native_libs/hook/link.dart +++ b/pkgs/native_assets_builder/test_data/treeshaking_native_libs/hook/link.dart @@ -12,9 +12,9 @@ void main(List arguments) async { (config, output) async { final linker = CLinker.library( name: config.packageName, - assetName: config.assets.single.id.split('/').skip(1).join('/'), + assetName: config.codeAssets.all.single.id.split('/').skip(1).join('/'), linkerOptions: LinkerOptions.treeshake(symbols: ['add']), - sources: [config.assets.single.file!.toFilePath()], + sources: [config.codeAssets.all.single.file!.toFilePath()], ); await linker.run( config: config, diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output/hook/build.dart b/pkgs/native_assets_builder/test_data/wrong_build_output/hook/build.dart index 969f76335..cd8dac2c2 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_build_output/hook/build.dart @@ -13,7 +13,7 @@ void main(List args) async { const _wrongContents = ''' timestamp: 2023-07-28 14:22:45.000 -assets: [] +encodedAssets: [] dependencies: [] metadata: {} version: 9001.0.0 diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output_3/hook/build.dart b/pkgs/native_assets_builder/test_data/wrong_build_output_3/hook/build.dart index c0c95d799..cb50e5731 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output_3/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_build_output_3/hook/build.dart @@ -14,7 +14,7 @@ void main(List args) async { const _rightContents = ''' timestamp: 2023-07-28 14:22:45.000 -assets: [] +encodedAssets: [] dependencies: [] metadata: {} version: 1.0.0 diff --git a/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart b/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart index b8730f013..8f3c9993c 100644 --- a/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart @@ -15,7 +15,7 @@ void main(List arguments) async { await File.fromUri(assetUri).writeAsBytes([1, 2, 3]); } - output.addAsset( + output.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo', diff --git a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart index 0cf220b41..66ad30529 100644 --- a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart @@ -15,7 +15,7 @@ void main(List arguments) async { await File.fromUri(assetUri).writeAsBytes([1, 2, 3]); } - output.addAsset( + output.codeAssets.add( CodeAsset( package: 'other_package', name: 'foo', diff --git a/pkgs/native_assets_cli/example/build/local_asset/hook/build.dart b/pkgs/native_assets_cli/example/build/local_asset/hook/build.dart index 19fa40160..280945122 100644 --- a/pkgs/native_assets_cli/example/build/local_asset/hook/build.dart +++ b/pkgs/native_assets_cli/example/build/local_asset/hook/build.dart @@ -30,7 +30,7 @@ Future main(List args) async { ]); } - output.addAsset( + output.codeAssets.add( // TODO: Change to DataAsset once the Dart/Flutter SDK can consume it. CodeAsset( package: packageName, diff --git a/pkgs/native_assets_cli/example/build/local_asset/test/build_test.dart b/pkgs/native_assets_cli/example/build/local_asset/test/build_test.dart index 0befd2e4a..eda5bcd25 100644 --- a/pkgs/native_assets_cli/example/build/local_asset/test/build_test.dart +++ b/pkgs/native_assets_cli/example/build/local_asset/test/build_test.dart @@ -13,12 +13,12 @@ void main() { description: 'test my build hook', mainMethod: build.main, check: (_, output) { - expect(output.assets, isNotEmpty); - expect(output.assets.first, isA()); + expect(output.codeAssets.all, isNotEmpty); expect( - (output.assets.first as CodeAsset).id, + output.codeAssets.all.first.id, 'package:local_asset/asset.txt', ); }, + supportedAssetTypes: [CodeAsset.type], ); } diff --git a/pkgs/native_assets_cli/example/link/package_with_assets/hook/build.dart b/pkgs/native_assets_cli/example/link/package_with_assets/hook/build.dart index 08fa5e4e2..371096caa 100644 --- a/pkgs/native_assets_cli/example/link/package_with_assets/hook/build.dart +++ b/pkgs/native_assets_cli/example/link/package_with_assets/hook/build.dart @@ -24,12 +24,12 @@ void main(List args) async { .toFilePath(windows: false) .substring(config.packageRoot.toFilePath(windows: false).length); - output.addAsset( + output.addEncodedAsset( DataAsset( package: packageName, name: name, file: dataAsset.uri, - ), + ).encode(), linkInPackage: config.linkingEnabled ? packageName : null, ); // TODO(https://github.com/dart-lang/native/issues/1208): Report diff --git a/pkgs/native_assets_cli/example/link/package_with_assets/hook/link.dart b/pkgs/native_assets_cli/example/link/package_with_assets/hook/link.dart index a5cd73bfc..6f7a46ff8 100644 --- a/pkgs/native_assets_cli/example/link/package_with_assets/hook/link.dart +++ b/pkgs/native_assets_cli/example/link/package_with_assets/hook/link.dart @@ -20,9 +20,10 @@ void main(List args) async { final usedAssets = (usages.instancesOf(multiplyIdentifier) ?? []).map((e) => (e.instanceConstant.fields.values.first as StringConstant).value); - output.addAssets(config.assets - .whereType() - .where((asset) => usedAssets.contains(asset.name))); + output.addEncodedAssets(config.encodedAssets + .where((e) => e.type == DataAsset.type) + .where( + (asset) => usedAssets.contains(DataAsset.fromEncoded(asset).name))); }); } diff --git a/pkgs/native_assets_cli/lib/native_assets_cli.dart b/pkgs/native_assets_cli/lib/native_assets_cli.dart index 21bc081b6..7fbad509b 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli.dart @@ -15,9 +15,28 @@ export 'src/api/link.dart' show link; export 'src/api/link_config.dart' show LinkConfig; export 'src/api/linker.dart' show Linker; export 'src/architecture.dart' show Architecture; -export 'src/asset.dart' show Asset, CodeAsset, DataAsset, OSLibraryNaming; export 'src/build_mode.dart' show BuildMode; export 'src/c_compiler_config.dart' show CCompilerConfig; +export 'src/code_assets/code_asset.dart' + show + BuildOutputCodeAssets, + CodeAsset, + CodeAssetsBuildOutputExt, + CodeAssetsLinkConfigExt, + CodeAssetsLinkOutputExt, + LinkConfigCodeAssets, + LinkOutputCodeAssets, + OSLibraryNaming; +export 'src/data_assets/data_asset.dart' + show + BuildOutputDataAssets, + DataAsset, + DataAssetsBuildOutputExt, + DataAssetsLinkConfigExt, + DataAssetsLinkOutputExt, + LinkConfigDataAssets, + LinkOutputDataAssets; +export 'src/encoded_asset.dart' show EncodedAsset; export 'src/ios_sdk.dart' show IOSSdk; export 'src/link_mode.dart' show 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 2d1b478c9..173e9086e 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart @@ -21,11 +21,18 @@ export 'src/api/build_config.dart' show BuildConfigImpl; export 'src/api/build_output.dart' show HookOutputImpl; export 'src/api/hook_config.dart' show HookConfigImpl; export 'src/api/link_config.dart' show LinkConfigImpl; -export 'src/asset.dart' show Asset, CodeAsset, DataAsset; +export 'src/code_assets/validation.dart' + show + validateCodeAssetBuildOutput, + validateCodeAssetLinkOutput, + validateCodeAssetsInApplication; +export 'src/data_assets/validation.dart' + show validateDataAssetBuildOutput, validateDataAssetLinkOutput; +export 'src/encoded_asset.dart' show EncodedAsset; export 'src/model/dependencies.dart'; export 'src/model/hook.dart'; export 'src/model/metadata.dart'; export 'src/model/resource_identifiers.dart'; export 'src/model/target.dart' show Target; -export 'src/validator/validator.dart' - show ValidateResult, validateBuild, validateLink, validateNoDuplicateDylibs; +export 'src/validation.dart' + show ValidationErrors, validateBuildOutput, validateLinkOutput; diff --git a/pkgs/native_assets_cli/lib/src/api/build.dart b/pkgs/native_assets_cli/lib/src/api/build.dart index ef7dfd3fa..e3fdbcd5f 100644 --- a/pkgs/native_assets_cli/lib/src/api/build.dart +++ b/pkgs/native_assets_cli/lib/src/api/build.dart @@ -2,7 +2,7 @@ // 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 '../validator/validator.dart'; +import '../validation.dart'; import 'build_config.dart'; import 'build_output.dart'; @@ -70,7 +70,7 @@ import 'build_output.dart'; /// ]); /// } /// -/// output.addAsset( +/// output.addEncodedAsset( /// // TODO: Change to DataAsset once the Dart/Flutter SDK can consume it. /// CodeAsset( /// package: packageName, @@ -91,13 +91,13 @@ Future build( final config = BuildConfigImpl.fromArguments(arguments); final output = HookOutputImpl(); await builder(config, output); - final validateResult = await validateBuild(config, output); - if (validateResult.success) { + final errors = await validateBuildOutput(config, output); + if (errors.isEmpty) { await output.writeToFile(config: config); } else { final message = [ 'The output contained unsupported output:', - for (final error in validateResult.errors) '- $error', + for (final error in errors) '- $error', ].join('\n'); throw UnsupportedError(message); } 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 c7d880cfb..08c03389e 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/build_config.dart @@ -10,7 +10,6 @@ import 'package:pub_semver/pub_semver.dart'; import '../architecture.dart'; import '../args_parser.dart'; -import '../asset.dart'; import '../build_mode.dart'; import '../c_compiler_config.dart'; import '../ios_sdk.dart'; @@ -48,8 +47,9 @@ abstract final class BuildConfig implements HookConfig { /// Whether link hooks will be run after the build hooks. /// - /// If [linkingEnabled] is true, [BuildOutput.addAsset] may be called with the - /// `linkInPackage` parameter so that assets can be linked in a link hook. + /// If [linkingEnabled] is true, [BuildOutput.addEncodedAsset] may be called + /// with the`linkInPackage` parameter so that assets can be linked in a link + /// hook. /// Linking is enabled in Flutter release builds and Dart AOT configurations. /// These configurations are optimized for app size. /// - `flutter build` @@ -127,7 +127,7 @@ abstract final class BuildConfig implements HookConfig { required LinkModePreference linkModePreference, @Deprecated(metadataDeprecation) Map>? dependencyMetadata, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, required bool linkingEnabled, }) => BuildConfigImpl( @@ -169,7 +169,7 @@ abstract final class BuildConfig implements HookConfig { required OS targetOS, required LinkModePreference linkModePreference, required bool linkingEnabled, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, }) => BuildConfigImpl.dryRun( outputDirectory: outputDirectory, 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 d15bc4b8e..6f76cbb8e 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_output.dart +++ b/pkgs/native_assets_cli/lib/src/api/build_output.dart @@ -9,7 +9,8 @@ import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; import '../architecture.dart'; -import '../asset.dart'; +import '../encoded_asset.dart'; +import '../json_utils.dart'; import '../model/dependencies.dart'; import '../model/metadata.dart'; import '../os.dart'; @@ -51,7 +52,7 @@ abstract final class BuildOutput { /// /// In dry runs, the assets for all [Architecture]s for the [OS] specified in /// the dry run must be provided. - Iterable get assets; + Iterable get encodedAssets; /// The assets produced by this build which should be linked. /// @@ -61,7 +62,7 @@ abstract final class BuildOutput { /// /// In dry runs, the assets for all [Architecture]s for the [OS] specified in /// the dry run must be provided. - Map> get assetsForLinking; + Map> get encodedAssetsForLinking; /// The files used by this build. /// @@ -83,10 +84,9 @@ abstract final class BuildOutput { /// 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 [CodeAsset]s can be - /// omitted. + /// The [EncodedAsset]s produced by this build or dry-run can be provided to + /// the constructor as [encodedAssets], or can be added later using + /// [addEncodedAsset] and [addEncodedAssets]. /// /// The files used by this build must be provided to the constructor as /// [dependencies], or can be added later with [addDependency] and @@ -100,31 +100,31 @@ abstract final class BuildOutput { /// [addMetadatum] and [addMetadata]. factory BuildOutput({ DateTime? timestamp, - Iterable? assets, + Iterable? encodedAssets, Iterable? dependencies, @Deprecated(metadataDeprecation) Map? metadata, }) => HookOutputImpl( timestamp: timestamp, - assets: assets?.cast().toList(), + encodedAssets: encodedAssets?.toList(), dependencies: Dependencies([...?dependencies]), metadata: Metadata({...?metadata}), ); - /// Adds [Asset]s produced by this build or dry run. + /// Adds [EncodedAsset]s produced by this build or dry run. /// /// If the [linkInPackage] argument is specified, the asset will not be /// bundled during the build step, but sent as input to the link hook of the /// specified package, where it can be further processed and possibly bundled. - void addAsset(Asset asset, {String? linkInPackage}); + void addEncodedAsset(EncodedAsset asset, {String? linkInPackage}); - /// Adds [Asset]s produced by this build or dry run. + /// Adds [EncodedAsset]s produced by this build or dry run. /// /// If the [linkInPackage] argument is specified, the assets will not be /// bundled during the build step, but sent as input to the link hook of the /// specified package, where they can be further processed and possibly /// bundled. - void addAssets(Iterable assets, {String? linkInPackage}); + void addEncodedAssets(Iterable assets, {String? linkInPackage}); /// Adds file used by this build. /// diff --git a/pkgs/native_assets_cli/lib/src/api/builder.dart b/pkgs/native_assets_cli/lib/src/api/builder.dart index 11b8a6d18..99d21c716 100644 --- a/pkgs/native_assets_cli/lib/src/api/builder.dart +++ b/pkgs/native_assets_cli/lib/src/api/builder.dart @@ -59,11 +59,11 @@ import 'linker.dart'; /// The builder is designed to immediately operate on [BuildOutput]. If a /// builder should output something else than standard, it should be /// configurable through a constructor parameter. For example to send an asset -/// for linking to the output ([BuildOutput.addAsset] with `linkInPackage` set), -/// the builder should have a constructor parameter. (Instead of capturing the -/// BuildOutput as a return value and manually manipulating it in the build -/// hook.) This ensures that builder is in control of what combination of build -/// outputs are valid. +/// for linking to the output ([BuildOutput.addEncodedAsset] with +/// `linkInPackage` set), the builder should have a constructor parameter. +/// (Instead of capturing the BuildOutput as a return value and manually +/// manipulating it in the build hook.) This ensures that builder is in control +/// of what combination of build outputs are valid. abstract interface class Builder { /// Runs this build. /// diff --git a/pkgs/native_assets_cli/lib/src/api/hook_config.dart b/pkgs/native_assets_cli/lib/src/api/hook_config.dart index de31b34a1..f43e62e3a 100644 --- a/pkgs/native_assets_cli/lib/src/api/hook_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/hook_config.dart @@ -10,9 +10,10 @@ import 'package:crypto/crypto.dart'; import 'package:pub_semver/pub_semver.dart'; import '../architecture.dart'; -import '../asset.dart'; import '../build_mode.dart'; import '../c_compiler_config.dart'; +import '../code_assets/code_asset.dart'; +import '../data_assets/data_asset.dart'; import '../ios_sdk.dart'; import '../json_utils.dart'; import '../link_mode.dart'; diff --git a/pkgs/native_assets_cli/lib/src/api/link.dart b/pkgs/native_assets_cli/lib/src/api/link.dart index 82ae3acc2..98edb9e65 100644 --- a/pkgs/native_assets_cli/lib/src/api/link.dart +++ b/pkgs/native_assets_cli/lib/src/api/link.dart @@ -2,8 +2,7 @@ // 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'; -import '../validator/validator.dart'; +import '../validation.dart'; import 'build_output.dart'; import 'link_config.dart'; @@ -13,7 +12,7 @@ import 'link_config.dart'; /// files. Each individual asset is assigned a unique asset ID. /// /// The linking script may receive assets from build scripts, which are accessed -/// through [LinkConfig.assets]. They will only be bundled with the final +/// through [LinkConfig.encodedAssets]. They will only be bundled with the final /// application if included in the [LinkOutput]. /// /// @@ -22,9 +21,9 @@ import 'link_config.dart'; /// /// void main(List args) async { /// await link(args, (config, output) async { -/// final dataAssets = config.assets +/// final dataEncodedAssets = config.assets /// .whereType(); -/// output.addAssets(dataAssets); +/// output.addEncodedAssets(dataEncodedAssets); /// }); /// } /// ``` @@ -34,21 +33,15 @@ Future link( ) async { final config = LinkConfig.fromArguments(arguments) as LinkConfigImpl; - // The built assets are dependencies of linking, as the linking should be - // rerun if they change. - final builtAssetsFiles = - config.assets.map((asset) => asset.file).whereType().toList(); - final output = HookOutputImpl( - dependencies: Dependencies(builtAssetsFiles), - ); + final output = HookOutputImpl(); await linker(config, output); - final validateResult = await validateLink(config, output); - if (validateResult.success) { + final errors = await validateLinkOutput(config, output); + if (errors.isEmpty) { await output.writeToFile(config: config); } else { final message = [ 'The output contained unsupported output:', - for (final error in validateResult.errors) '- $error', + for (final error in errors) '- $error', ].join('\n'); throw UnsupportedError(message); } diff --git a/pkgs/native_assets_cli/lib/src/api/link_config.dart b/pkgs/native_assets_cli/lib/src/api/link_config.dart index 0353d0512..a029a460d 100644 --- a/pkgs/native_assets_cli/lib/src/api/link_config.dart +++ b/pkgs/native_assets_cli/lib/src/api/link_config.dart @@ -10,9 +10,9 @@ import 'package:pub_semver/pub_semver.dart'; import '../architecture.dart'; import '../args_parser.dart'; -import '../asset.dart'; import '../build_mode.dart'; import '../c_compiler_config.dart'; +import '../encoded_asset.dart'; import '../ios_sdk.dart'; import '../json_utils.dart'; import '../link_mode_preference.dart'; @@ -27,11 +27,11 @@ part '../model/link_config.dart'; /// The configuration for a link hook (`hook/link.dart`) invocation. /// /// It consists of a subset of the fields from the [BuildConfig] already passed -/// to the build hook and the [assets] from the build step. +/// to the build hook and the [encodedAssets] from the build step. abstract class LinkConfig implements HookConfig { /// The list of assets to be linked. These are the assets generated by a /// `build.dart` script destined for this packages `link.dart`. - Iterable get assets; + Iterable get encodedAssets; /// The path to the file containing recorded uses after kernel tree-shaking. /// @@ -55,15 +55,15 @@ abstract class LinkConfig implements HookConfig { int? targetMacOSVersion, CCompilerConfig? cCompiler, BuildMode? buildMode, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, int? targetAndroidNdkApi, - required Iterable assets, + required Iterable assets, required LinkModePreference linkModePreference, bool? dryRun, Version? version, }) => LinkConfigImpl( - assets: assets.cast(), + encodedAssets: assets, outputDirectory: outputDirectory, outputDirectoryShared: outputDirectoryShared, packageName: packageName, @@ -88,13 +88,13 @@ abstract class LinkConfig implements HookConfig { required String packageName, required Uri packageRoot, required OS targetOS, - Iterable? supportedAssetTypes, - required Iterable assets, + required Iterable supportedAssetTypes, + required Iterable assets, required LinkModePreference linkModePreference, Version? version, }) => LinkConfigImpl.dryRun( - assets: assets.cast(), + encodedAssets: assets, outputDirectory: outputDirectory, outputDirectoryShared: outputDirectoryShared, packageName: packageName, diff --git a/pkgs/native_assets_cli/lib/src/api/link_output.dart b/pkgs/native_assets_cli/lib/src/api/link_output.dart index 77b7bb030..e90ace891 100644 --- a/pkgs/native_assets_cli/lib/src/api/link_output.dart +++ b/pkgs/native_assets_cli/lib/src/api/link_output.dart @@ -27,7 +27,7 @@ abstract final class LinkOutput { /// /// In dry runs, the assets for all [Architecture]s for the [OS] specified in /// the dry run must be provided. - Iterable get assets; + Iterable get encodedAssets; /// The files used by this link. /// @@ -47,19 +47,19 @@ abstract final class LinkOutput { /// re-run. void addDependencies(Iterable dependencies); - /// Adds [Asset]s produced by this link or dry run. - void addAsset(Asset asset); + /// Adds [EncodedAsset]s produced by this link or dry run. + void addEncodedAsset(EncodedAsset asset); - /// Adds [Asset]s produced by this link or dry run. - void addAssets(Iterable assets); + /// Adds [EncodedAsset]s produced by this link or dry run. + void addEncodedAssets(Iterable assets); factory LinkOutput({ - Iterable? assets, + Iterable? encodedAssets, Dependencies? dependencies, DateTime? timestamp, }) => HookOutputImpl( - assets: assets, + encodedAssets: encodedAssets, dependencies: dependencies, timestamp: timestamp, ); diff --git a/pkgs/native_assets_cli/lib/src/api/test.dart b/pkgs/native_assets_cli/lib/src/api/test.dart index 4efd0dbd0..070e74825 100644 --- a/pkgs/native_assets_cli/lib/src/api/test.dart +++ b/pkgs/native_assets_cli/lib/src/api/test.dart @@ -9,9 +9,10 @@ import 'package:test/test.dart'; import 'package:yaml/yaml.dart'; import '../architecture.dart'; -import '../asset.dart'; import '../build_mode.dart'; import '../c_compiler_config.dart'; +import '../code_assets/code_asset.dart'; +import '../data_assets/data_asset.dart'; import '../ios_sdk.dart'; import '../link_mode_preference.dart'; import '../os.dart'; @@ -33,7 +34,7 @@ Future testBuildHook({ int? targetAndroidNdkApi, CCompilerConfig? cCompiler, LinkModePreference? linkModePreference, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, bool? linkingEnabled, }) async { test( @@ -73,19 +74,30 @@ Future testBuildHook({ check(buildConfig, hookOutput); - final allAssets = [ - ...hookOutput.assets, - ...hookOutput.assetsForLinking.values.expand((e) => e) + final allEncodedAssets = [ + ...hookOutput.encodedAssets, + ...hookOutput.encodedAssetsForLinking.values.expand((e) => e) ]; - for (final asset in allAssets.where((asset) => asset.file != null)) { - final file = File.fromUri(asset.file!); - expect(await file.exists(), true); + for (final asset in allEncodedAssets) { + expect(buildConfig.supportedAssetTypes, contains(asset.type)); } - if (allAssets.any((asset) => asset is CodeAsset)) { - expect(buildConfig.supportedAssetTypes, CodeAsset.type); + final dataAssets = allEncodedAssets + .where((e) => e.type == DataAsset.type) + .map(DataAsset.fromEncoded) + .toList(); + for (final asset in dataAssets) { + final file = File.fromUri(asset.file); + expect(await file.exists(), true); } - if (allAssets.any((asset) => asset is DataAsset)) { - expect(buildConfig.supportedAssetTypes, DataAsset.type); + final codeAssets = allEncodedAssets + .where((e) => e.type == CodeAsset.type) + .map(CodeAsset.fromEncoded) + .toList(); + for (final asset in codeAssets) { + if (asset.file != null) { + final file = File.fromUri(asset.file!); + expect(await file.exists(), true); + } } }, ); diff --git a/pkgs/native_assets_cli/lib/src/asset.dart b/pkgs/native_assets_cli/lib/src/asset.dart deleted file mode 100644 index ef2385bdc..000000000 --- a/pkgs/native_assets_cli/lib/src/asset.dart +++ /dev/null @@ -1,95 +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 'api/build_config.dart'; -import 'api/build_output.dart'; -import 'architecture.dart'; -import 'json_utils.dart'; -import 'link_mode.dart'; -import 'os.dart'; -import 'utils/map.dart'; - -part 'code_asset.dart'; -part 'data_asset.dart'; - -/// Data or code bundled with a Dart or Flutter application. -/// -/// 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 [CodeAsset] 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; - - /// 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; - - /// A json representation of this [Asset]. - Map toJson(); - - static List listFromJson(List? list) { - final assets = []; - if (list == null) return assets; - for (var i = 0; i < list.length; ++i) { - final jsonMap = list.mapAt(i); - final type = jsonMap[_typeKey]; - switch (type) { - case CodeAsset.type: - assets.add(CodeAsset.fromJson(jsonMap)); - case DataAsset.type: - assets.add(DataAsset.fromJson(jsonMap)); - default: - // Do nothing, some other launcher might define it's own asset types. - } - } - return assets; - } - - static List> listToJson(Iterable assets) => [ - for (final asset in assets) asset.toJson(), - ]; -} - -const _architectureKey = 'architecture'; -const _fileKey = 'file'; -const _idKey = 'id'; -const _linkModeKey = 'link_mode'; -const _nameKey = 'name'; -const _osKey = 'os'; -const _packageKey = 'package'; -const _typeKey = 'type'; diff --git a/pkgs/native_assets_cli/lib/src/code_asset.dart b/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart similarity index 73% rename from pkgs/native_assets_cli/lib/src/code_asset.dart rename to pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart index 51789000e..b699701d6 100644 --- a/pkgs/native_assets_cli/lib/src/code_asset.dart +++ b/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart @@ -2,9 +2,18 @@ // 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'; +import '../api/build_config.dart'; +import '../api/build_output.dart'; +import '../api/link_config.dart'; +import '../architecture.dart'; +import '../encoded_asset.dart'; +import '../json_utils.dart'; +import '../link_mode.dart'; +import '../os.dart'; +import '../utils/json.dart'; +import '../utils/map.dart'; -/// A code [Asset] which respects the native application binary interface (ABI). +/// 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 @@ -45,9 +54,8 @@ part of 'asset.dart'; /// "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. -final class CodeAsset implements Asset { +final class CodeAsset { /// The id of this code asset. - @override final String id; /// The operating system this asset can run on. @@ -73,7 +81,6 @@ final class CodeAsset implements Asset { /// If the [linkMode] is [DynamicLoadingSystem], [LookupInProcess], or /// [LookupInExecutable] the file must be omitted in the [BuildOutput] for /// [BuildConfig.dryRun]. - @override final Uri? file; /// Constructs a native code asset. @@ -101,24 +108,18 @@ final class CodeAsset implements Asset { required this.os, required this.file, required 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 CodeAsset.fromEncoded(EncodedAsset asset) { + assert(asset.type == CodeAsset.type); + final jsonMap = asset.encoding; - factory CodeAsset.fromJson(Map jsonMap) { - final linkMode = LinkMode.fromJson(jsonMap.map$(_linkModeKey)); + final linkMode = + LinkMode.fromJson(as>(jsonMap[_linkModeKey])); final fileString = jsonMap.optionalString(_fileKey); final Uri? file; if (fileString != null) { - file = Uri(path: fileString); + file = Uri.file(fileString); } else { file = null; } @@ -176,19 +177,77 @@ final class CodeAsset implements Asset { file, ); - @override - Map toJson() => { + EncodedAsset encode() => EncodedAsset( + CodeAsset.type, + { if (architecture != null) _architectureKey: architecture.toString(), if (file != null) _fileKey: file!.toFilePath(), _idKey: id, _linkModeKey: linkMode.toJson(), _osKey: os.toString(), - _typeKey: CodeAsset.type, - }..sortOnKey(); + }..sortOnKey()); static const String type = 'native_code'; } +/// Build output extension for code assets. +extension CodeAssetsBuildOutputExt on BuildOutput { + BuildOutputCodeAssets get codeAssets => BuildOutputCodeAssets(this); +} + +class BuildOutputCodeAssets { + final BuildOutput _output; + + BuildOutputCodeAssets(this._output); + + void add(CodeAsset asset, {String? linkInPackage}) => + _output.addEncodedAsset(asset.encode(), linkInPackage: linkInPackage); + + void addAll(Iterable assets, {String? linkInPackage}) { + for (final asset in assets) { + add(asset, linkInPackage: linkInPackage); + } + } + + Iterable get all => _output.encodedAssets + .where((e) => e.type == CodeAsset.type) + .map(CodeAsset.fromEncoded); +} + +/// Link output extension for code assets. +extension CodeAssetsLinkConfigExt on LinkConfig { + LinkConfigCodeAssets get codeAssets => LinkConfigCodeAssets(this); +} + +class LinkConfigCodeAssets { + final LinkConfig _config; + + LinkConfigCodeAssets(this._config); + + Iterable get all => _config.encodedAssets + .where((e) => e.type == CodeAsset.type) + .map(CodeAsset.fromEncoded); +} + +/// Link output extension for code assets. +extension CodeAssetsLinkOutputExt on LinkOutput { + LinkOutputCodeAssets get codeAssets => LinkOutputCodeAssets(this); +} + +class LinkOutputCodeAssets { + final LinkOutput _output; + + LinkOutputCodeAssets(this._output); + + void add(CodeAsset asset) => _output.addEncodedAsset(asset.encode()); + + void addAll(Iterable assets) => assets.forEach(add); + + Iterable get all => _output.encodedAssets + .where((e) => e.type == CodeAsset.type) + .map(CodeAsset.fromEncoded); +} + extension OSLibraryNaming on OS { /// The default dynamic library file name on this os. String dylibFileName(String name) { @@ -263,3 +322,9 @@ const _executableExtension = { OS.macOS: '', OS.windows: 'exe', }; + +const _idKey = 'id'; +const _linkModeKey = 'link_mode'; +const _fileKey = 'file'; +const _osKey = 'os'; +const _architectureKey = 'architecture'; diff --git a/pkgs/native_assets_cli/lib/src/code_assets/validation.dart b/pkgs/native_assets_cli/lib/src/code_assets/validation.dart new file mode 100644 index 000000000..4910f2ed6 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/code_assets/validation.dart @@ -0,0 +1,144 @@ +// 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 '../../native_assets_cli_internal.dart'; +import '../link_mode.dart'; + +Future validateCodeAssetBuildOutput( + HookConfig config, + BuildOutput output, +) => + _validateCodeAssetBuildOrLinkOutput(config, output as HookOutputImpl, true); + +Future validateCodeAssetLinkOutput( + HookConfig config, + LinkOutput output, +) => + _validateCodeAssetBuildOrLinkOutput( + config, output as HookOutputImpl, false); + +/// Validates that the given code assets can be used together in an application. +/// +/// Some restrictions - e.g. unique shared library names - have to be validated +/// on the entire application build and not on individual `hook/build.dart` +/// invocations. +Future validateCodeAssetsInApplication( + List assets) async { + final fileNameToEncodedAssetId = >{}; + for (final asset in assets) { + if (asset.type != CodeAsset.type) continue; + _groupCodeAssetsByFilename( + CodeAsset.fromEncoded(asset), fileNameToEncodedAssetId); + } + final errors = []; + _validateNoDuplicateDylibNames(errors, fileNameToEncodedAssetId); + return errors; +} + +Future _validateCodeAssetBuildOrLinkOutput( + HookConfig config, + HookOutputImpl output, + bool isBuild, +) async { + final errors = []; + final ids = {}; + final fileNameToEncodedAssetId = >{}; + + for (final asset in output.encodedAssets) { + if (asset.type != CodeAsset.type) continue; + _validateCodeAssets( + config, + config.dryRun, + CodeAsset.fromEncoded(asset), + errors, + ids, + isBuild, + ); + _groupCodeAssetsByFilename( + CodeAsset.fromEncoded(asset), fileNameToEncodedAssetId); + } + _validateNoDuplicateDylibNames(errors, fileNameToEncodedAssetId); + return errors; +} + +void _validateCodeAssets( + HookConfig config, + bool dryRun, + CodeAsset codeAsset, + List errors, + Set ids, + bool isBuild, +) { + final id = codeAsset.id; + final prefix = 'package:${config.packageName}/'; + if (isBuild && !id.startsWith(prefix)) { + errors.add('Code asset "$id" does not start with "$prefix".'); + } + if (!ids.add(id)) { + errors.add('More than one code asset with same "$id" id.'); + } + + final preference = config.linkModePreference; + final linkMode = codeAsset.linkMode; + if ((linkMode is DynamicLoading && preference == LinkModePreference.static) || + (linkMode is StaticLinking && preference == LinkModePreference.dynamic)) { + errors.add('CodeAsset "$id" has a link mode "$linkMode", which ' + 'is not allowed by by the config link mode preference ' + '"$preference".'); + } + + final os = codeAsset.os; + if (config.targetOS != os) { + final error = 'CodeAsset "$id" has a os "$os", which ' + 'is not the target os "${config.targetOS}".'; + errors.add(error); + } + + final architecture = codeAsset.architecture; + if (!dryRun) { + if (architecture == null) { + errors.add('CodeAsset "$id" has no architecture.'); + } else if (architecture != config.targetArchitecture) { + errors.add('CodeAsset "$id" has an architecture "$architecture", which ' + 'is not the target architecture "${config.targetArchitecture}".'); + } + } + + final file = codeAsset.file; + if (file == null && !dryRun) { + errors.add('CodeAsset "$id" has no file.'); + } + if (file != null && !dryRun && !File.fromUri(file).existsSync()) { + errors.add('CodeAsset "$id" has a file "${file.toFilePath()}", which ' + 'does not exist.'); + } +} + +void _groupCodeAssetsByFilename( + CodeAsset codeAsset, + Map> fileNameToEncodedAssetId, +) { + final file = codeAsset.file; + if (file != null) { + final fileName = file.pathSegments.where((s) => s.isNotEmpty).last; + fileNameToEncodedAssetId[fileName] ??= {}; + fileNameToEncodedAssetId[fileName]!.add(codeAsset.id); + } +} + +void _validateNoDuplicateDylibNames( + List errors, Map> fileNameToEncodedAssetId) { + for (final fileName in fileNameToEncodedAssetId.keys) { + final assetIds = fileNameToEncodedAssetId[fileName]!; + if (assetIds.length > 1) { + final assetIdsString = assetIds.map((e) => '"$e"').join(', '); + final error = + 'Duplicate dynamic library file name "$fileName" for the following' + ' asset ids: $assetIdsString.'; + errors.add(error); + } + } +} diff --git a/pkgs/native_assets_cli/lib/src/data_asset.dart b/pkgs/native_assets_cli/lib/src/data_asset.dart deleted file mode 100644 index f8493b875..000000000 --- a/pkgs/native_assets_cli/lib/src/data_asset.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -part of '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. -final class DataAsset extends Asset { - /// The file to be bundled with the Dart or Flutter application. - /// - /// 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. - @override - final Uri file; - - /// The name of this asset, which must be unique for the package. - final String name; - - /// The package which contains this asset. - final String package; - - /// The identifier for this data asset. - /// - /// An [DataAsset] 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. - @override - String get id => 'package:$package/$name'; - - DataAsset({ - required this.file, - required this.name, - required this.package, - }); - - /// Constructs a [DataAsset] from a json representation obtained via - /// [DataAsset.toJson]. - factory DataAsset.fromJson(Map jsonMap) => DataAsset( - name: jsonMap.string(_nameKey), - package: jsonMap.string(_packageKey), - file: jsonMap.path(_fileKey), - ); - - @override - bool operator ==(Object other) { - if (other is! DataAsset) { - return false; - } - return other.package == package && - other.file.toFilePath() == file.toFilePath() && - other.name == name; - } - - @override - int get hashCode => Object.hash( - package, - name, - file.toFilePath(), - ); - - @override - Map toJson() => { - _nameKey: name, - _packageKey: package, - _fileKey: file.toFilePath(), - _typeKey: DataAsset.type, - }..sortOnKey(); - - @override - String toString() => 'DataAsset(${toJson()})'; - - static const String type = 'data'; -} diff --git a/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart b/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart new file mode 100644 index 000000000..9b3fb4189 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart @@ -0,0 +1,155 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import '../api/build_config.dart'; +import '../api/build_output.dart'; +import '../api/link_config.dart'; +import '../encoded_asset.dart'; +import '../json_utils.dart'; +import '../utils/map.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 [DataAsset.file]. The Dart and Flutter SDK will +/// bundle this code in the final application. +final class DataAsset { + /// The file to be bundled with the Dart or Flutter application. + /// + /// 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. + final Uri file; + + /// The name of this asset, which must be unique for the package. + final String name; + + /// The package which contains this asset. + final String package; + + /// The identifier for this data asset. + /// + /// An [DataAsset] 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. + String get id => 'package:$package/$name'; + + DataAsset({ + required this.file, + required this.name, + required this.package, + }); + + /// Constructs a [DataAsset] from an [EncodedAsset]. + factory DataAsset.fromEncoded(EncodedAsset asset) { + assert(asset.type == DataAsset.type); + final jsonMap = asset.encoding; + return DataAsset( + name: jsonMap.string(_nameKey), + package: jsonMap.string(_packageKey), + file: jsonMap.path(_fileKey), + ); + } + + @override + bool operator ==(Object other) { + if (other is! DataAsset) { + return false; + } + return other.package == package && + other.file.toFilePath() == file.toFilePath() && + other.name == name; + } + + @override + int get hashCode => Object.hash( + package, + name, + file.toFilePath(), + ); + + EncodedAsset encode() => EncodedAsset( + DataAsset.type, + { + _nameKey: name, + _packageKey: package, + _fileKey: file.toFilePath(), + }..sortOnKey()); + + @override + String toString() => 'DataAsset(${encode().encoding})'; + + static const String type = 'data'; +} + +/// Build output extension for data assets. +extension DataAssetsBuildOutputExt on BuildOutput { + BuildOutputDataAssets get dataAssets => BuildOutputDataAssets(this); +} + +class BuildOutputDataAssets { + final BuildOutput _output; + + BuildOutputDataAssets(this._output); + + void add(DataAsset asset, {String? linkInPackage}) => + _output.addEncodedAsset(asset.encode(), linkInPackage: linkInPackage); + + void addAll(Iterable assets, {String? linkInPackage}) { + for (final asset in assets) { + add(asset, linkInPackage: linkInPackage); + } + } + + Iterable get all => _output.encodedAssets + .where((e) => e.type == DataAsset.type) + .map(DataAsset.fromEncoded); +} + +/// Link output extension for data assets. +extension DataAssetsLinkConfigExt on LinkConfig { + LinkConfigDataAssets get dataAssets => LinkConfigDataAssets(this); +} + +class LinkConfigDataAssets { + final LinkConfig _config; + + LinkConfigDataAssets(this._config); + + Iterable get all => _config.encodedAssets + .where((e) => e.type == DataAsset.type) + .map(DataAsset.fromEncoded); +} + +/// Link output extension for data assets. +extension DataAssetsLinkOutputExt on LinkOutput { + LinkOutputDataAssets get dataAssets => LinkOutputDataAssets(this); +} + +class LinkOutputDataAssets { + final LinkOutput _output; + + LinkOutputDataAssets(this._output); + + void add(DataAsset asset) => _output.addEncodedAsset(asset.encode()); + + void addAll(Iterable assets) => assets.forEach(add); + + Iterable get all => _output.encodedAssets + .where((e) => e.type == DataAsset.type) + .map(DataAsset.fromEncoded); +} + +const _nameKey = 'name'; +const _packageKey = 'package'; +const _fileKey = 'file'; diff --git a/pkgs/native_assets_cli/lib/src/data_assets/validation.dart b/pkgs/native_assets_cli/lib/src/data_assets/validation.dart new file mode 100644 index 000000000..d6dd9de36 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/data_assets/validation.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:io'; + +import '../../native_assets_cli_internal.dart'; + +Future validateDataAssetBuildOutput( + HookConfig config, + BuildOutput output, +) => + _validateDataAssetBuildOrLinkOutput(config, output as HookOutputImpl, true); + +Future validateDataAssetLinkOutput( + HookConfig config, + LinkOutput output, +) => + _validateDataAssetBuildOrLinkOutput( + config, output as HookOutputImpl, false); + +Future _validateDataAssetBuildOrLinkOutput( + HookConfig config, + HookOutputImpl output, + bool isBuild, +) async { + final errors = []; + final ids = {}; + + for (final asset in output.encodedAssets) { + if (asset.type != DataAsset.type) continue; + _validateDataAssets(config, config.dryRun, DataAsset.fromEncoded(asset), + errors, ids, isBuild); + } + return errors; +} + +void _validateDataAssets( + HookConfig config, + bool dryRun, + DataAsset dataAsset, + List errors, + Set ids, + bool isBuild, +) { + if (isBuild && dataAsset.package != config.packageName) { + errors.add('Data asset must have package name ${config.packageName}'); + } + if (!ids.add(dataAsset.name)) { + errors.add('More than one code asset with same "${dataAsset.name}" name.'); + } + final file = dataAsset.file; + if (!dryRun && (!File.fromUri(file).existsSync())) { + errors.add( + 'EncodedAsset "${dataAsset.name}" has a file "${file.toFilePath()}", ' + 'which does not exist.'); + } +} diff --git a/pkgs/native_assets_cli/lib/src/encoded_asset.dart b/pkgs/native_assets_cli/lib/src/encoded_asset.dart new file mode 100644 index 000000000..2d2d08681 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/encoded_asset.dart @@ -0,0 +1,45 @@ +// 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:collection/collection.dart'; + +import 'json_utils.dart'; + +/// An encoding of a particular asset type. +final class EncodedAsset { + /// The type of the asset (e.g. whether it's a code asset, data asset or ...) + final String type; + + /// The json encoding of the asset. + final Map encoding; + + EncodedAsset(this.type, this.encoding); + + /// Decode an [EncodedAsset] from json. + factory EncodedAsset.fromJson(Map json) => + EncodedAsset(json.get(_typeKey), { + for (final key in json.keys) + if (key != _typeKey) key: json[key] + }); + + /// Encode this [EncodedAsset] tojson. + Map toJson() => { + for (final key in encoding.keys) key: encoding[key], + _typeKey: type, + }; + + @override + String toString() => 'EncodedAsset($type, $encoding)'; + + @override + int get hashCode => Object.hash(type, const DeepCollectionEquality().hash); + + @override + bool operator ==(Object other) => + other is EncodedAsset && + type == other.type && + const DeepCollectionEquality().equals(encoding, other.encoding); +} + +const String _typeKey = 'type'; diff --git a/pkgs/native_assets_cli/lib/src/json_utils.dart b/pkgs/native_assets_cli/lib/src/json_utils.dart index e177ff4a1..1680a1247 100644 --- a/pkgs/native_assets_cli/lib/src/json_utils.dart +++ b/pkgs/native_assets_cli/lib/src/json_utils.dart @@ -41,6 +41,8 @@ extension MapJsonUtils on Map { return value.cast(); } + List stringList(String key) => get>(key).cast(); + List list(String key) => get>(key); List? optionalList(String key) => getOptional>(key); Map map$(String key) => get>(key); diff --git a/pkgs/native_assets_cli/lib/src/link_mode.dart b/pkgs/native_assets_cli/lib/src/link_mode.dart index 53b3295c7..bd02c1a7d 100644 --- a/pkgs/native_assets_cli/lib/src/link_mode.dart +++ b/pkgs/native_assets_cli/lib/src/link_mode.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'api/build_config.dart'; -import 'asset.dart'; +import 'code_assets/code_asset.dart'; /// The link mode for a [CodeAsset]. /// diff --git a/pkgs/native_assets_cli/lib/src/link_mode_preference.dart b/pkgs/native_assets_cli/lib/src/link_mode_preference.dart index f1d667515..f5a37580b 100644 --- a/pkgs/native_assets_cli/lib/src/link_mode_preference.dart +++ b/pkgs/native_assets_cli/lib/src/link_mode_preference.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'asset.dart'; +import 'code_assets/code_asset.dart'; /// The preferred linkMode method for [CodeAsset]s. final class LinkModePreference { 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 c7a64f4e5..01b3ca7ce 100644 --- a/pkgs/native_assets_cli/lib/src/model/build_config.dart +++ b/pkgs/native_assets_cli/lib/src/model/build_config.dart @@ -40,11 +40,6 @@ final class BuildConfigImpl extends HookConfigImpl implements BuildConfig { final bool? _linkingEnabled; - static List _supportedAssetTypesBackwardsCompatibility( - Iterable? supportedAssetTypes, - ) => - supportedAssetTypes?.toList() ?? [CodeAsset.type]; - BuildConfigImpl({ required super.outputDirectory, required super.outputDirectoryShared, @@ -53,7 +48,7 @@ final class BuildConfigImpl extends HookConfigImpl implements BuildConfig { Version? version, super.buildMode, super.cCompiler, - Iterable? supportedAssetTypes, + required super.supportedAssetTypes, super.targetAndroidNdkApi, required super.targetArchitecture, super.targetIOSSdk, @@ -69,8 +64,6 @@ final class BuildConfigImpl extends HookConfigImpl implements BuildConfig { super( hook: Hook.build, version: version ?? HookConfigImpl.latestVersion, - supportedAssetTypes: - _supportedAssetTypesBackwardsCompatibility(supportedAssetTypes), ) { if (this.version < Version(1, 4, 0)) { assert(linkingEnabled == null); @@ -87,14 +80,12 @@ final class BuildConfigImpl extends HookConfigImpl implements BuildConfig { required super.targetOS, required super.linkModePreference, required bool? linkingEnabled, - Iterable? supportedAssetTypes, + required super.supportedAssetTypes, }) : _dependencyMetadata = null, _linkingEnabled = linkingEnabled, super.dryRun( hook: Hook.build, version: HookConfigImpl.latestVersion, - supportedAssetTypes: - _supportedAssetTypesBackwardsCompatibility(supportedAssetTypes), ); static BuildConfigImpl fromArguments( @@ -131,7 +122,8 @@ final class BuildConfigImpl extends HookConfigImpl implements BuildConfig { linkingEnabled: parseHasLinkPhase(config), version: HookConfigImpl.parseVersion(config), cCompiler: HookConfigImpl.parseCCompiler(config, dryRun), - supportedAssetTypes: HookConfigImpl.parseSupportedAssetTypes(config), + supportedAssetTypes: + HookConfigImpl.parseSupportedEncodedAssetTypes(config), targetAndroidNdkApi: HookConfigImpl.parseTargetAndroidNdkApi(config, dryRun, targetOS), targetIOSSdk: HookConfigImpl.parseTargetIOSSdk(config, dryRun, targetOS), diff --git a/pkgs/native_assets_cli/lib/src/model/hook_config.dart b/pkgs/native_assets_cli/lib/src/model/hook_config.dart index 5445e76fd..e209ab38c 100644 --- a/pkgs/native_assets_cli/lib/src/model/hook_config.dart +++ b/pkgs/native_assets_cli/lib/src/model/hook_config.dart @@ -182,8 +182,7 @@ abstract class HookConfigImpl implements HookConfig { packageNameConfigKey: packageName, packageRootConfigKey: packageRoot.toFilePath(), _targetOSConfigKey: targetOS.toString(), - if (supportedAssetTypes.isNotEmpty) - supportedAssetTypesKey: supportedAssetTypes, + supportedAssetTypesKey: supportedAssetTypes, _versionKey: version.toString(), if (dryRun) dryRunConfigKey: dryRun, if (!dryRun) ...{ @@ -366,8 +365,9 @@ abstract class HookConfigImpl implements HookConfig { } } - static List parseSupportedAssetTypes(Map config) => - config.optionalStringList(supportedAssetTypesKey) ?? [CodeAsset.type]; + static List parseSupportedEncodedAssetTypes( + Map config) => + config.optionalStringList(supportedAssetTypesKey) ?? []; static CCompilerConfig parseCCompiler( Map config, bool dryRun) { @@ -466,7 +466,7 @@ can _only_ depend on OS.'''); CCompilerConfig? cCompiler, required LinkModePreference linkModePreference, Map? dependencyMetadata, - Iterable? supportedAssetTypes, + required Iterable supportedAssetTypes, Version? version, required Hook hook, required bool? linkingEnabled, @@ -490,7 +490,7 @@ can _only_ depend on OS.'''); entry.key, json.encode(entry.value.toJson()), ], - ...supportedAssetTypes ?? [CodeAsset.type], + ...supportedAssetTypes, hook.name, linkingEnabled, ].join('###'); diff --git a/pkgs/native_assets_cli/lib/src/model/hook_output.dart b/pkgs/native_assets_cli/lib/src/model/hook_output.dart index ac8060586..9e6947a20 100644 --- a/pkgs/native_assets_cli/lib/src/model/hook_output.dart +++ b/pkgs/native_assets_cli/lib/src/model/hook_output.dart @@ -8,15 +8,16 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { @override final DateTime timestamp; - final List _assets; + final List _assets; @override - Iterable get assets => _assets; + Iterable get encodedAssets => _assets; - final Map> _assetsForLinking; + final Map> _assetsForLinking; @override - Map> get assetsForLinking => _assetsForLinking; + Map> get encodedAssetsForLinking => + _assetsForLinking; final Dependencies _dependencies; @@ -29,15 +30,15 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { HookOutputImpl({ DateTime? timestamp, - Iterable? assets, - Map>? assetsForLinking, + Iterable? encodedAssets, + Map>? encodedAssetsForLinking, Dependencies? dependencies, Metadata? metadata, }) : timestamp = (timestamp ?? DateTime.now()).roundDownToSeconds(), _assets = [ - ...?assets, + ...?encodedAssets, ], - _assetsForLinking = assetsForLinking ?? {}, + _assetsForLinking = encodedAssetsForLinking ?? {}, // ignore: prefer_const_constructors _dependencies = dependencies ?? Dependencies([]), // ignore: prefer_const_constructors @@ -63,7 +64,7 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { return HookOutputImpl.fromJson(as>(json)); } - factory HookOutputImpl.fromJson(Map jsonMap) { + factory HookOutputImpl.fromJson(Map jsonMap) { final outputVersion = Version.parse(get(jsonMap, 'version')); if (outputVersion.major > latestVersion.major) { throw FormatException( @@ -81,11 +82,19 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { } return HookOutputImpl( timestamp: DateTime.parse(get(jsonMap, _timestampKey)), - assets: Asset.listFromJson(get?>(jsonMap, _assetsKey)), - assetsForLinking: - get?>(jsonMap, _assetsForLinkingKey)?.map( - (packageName, assets) => MapEntry( - packageName, Asset.listFromJson(as>(assets)))), + encodedAssets: [ + for (final json in (jsonMap.optionalList(_assetsKey) ?? [])) + EncodedAsset.fromJson(json as Map), + ], + encodedAssetsForLinking: { + for (final MapEntry(:key, :value) + in (get?>(jsonMap, _assetsForLinkingKey) ?? {}) + .entries) + key: [ + for (final json in value as List) + EncodedAsset.fromJson(json as Map), + ], + }, dependencies: Dependencies.fromJson(get?>(jsonMap, _dependenciesKey)), metadata: @@ -95,13 +104,16 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { Map toJson(Version version) => { _timestampKey: timestamp.toString(), - if (_assets.isNotEmpty) _assetsKey: Asset.listToJson(_assets), + if (_assets.isNotEmpty) + _assetsKey: [ + for (final asset in encodedAssets) asset.toJson(), + ], if (_assetsForLinking.isNotEmpty) - _assetsForLinkingKey: - _assetsForLinking.map((packageName, assets) => MapEntry( - packageName, - Asset.listToJson(assets), - )), + _assetsForLinkingKey: { + for (final MapEntry(:key, :value) + in encodedAssetsForLinking.entries) + key: [for (final asset in value) asset.toJson()], + }, if (_dependencies.dependencies.isNotEmpty) _dependenciesKey: _dependencies.toJson(), if (metadata.metadata.isNotEmpty) _metadataKey: metadata.toJson(), @@ -154,7 +166,7 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { return false; } return other.timestamp == timestamp && - const ListEquality().equals(other._assets, _assets) && + const ListEquality().equals(other._assets, _assets) && other._dependencies == _dependencies && other.metadata == metadata; } @@ -162,7 +174,7 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { @override int get hashCode => Object.hash( timestamp.hashCode, - const ListEquality().hash(_assets), + const ListEquality().hash(_assets), _dependencies, metadata, ); @@ -180,23 +192,26 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { Metadata get metadataModel => metadata; @override - void addAsset(Asset asset, {String? linkInPackage}) { - _getAssetList(linkInPackage).add(asset); + void addEncodedAsset(EncodedAsset asset, {String? linkInPackage}) { + _getEncodedAssetList(linkInPackage).add(asset); } @override - void addAssets(Iterable assets, {String? linkInPackage}) { - _getAssetList(linkInPackage).addAll(assets.cast()); + void addEncodedAssets(Iterable assets, + {String? linkInPackage}) { + _getEncodedAssetList(linkInPackage).addAll(assets.cast()); } - List _getAssetList(String? linkInPackage) => linkInPackage == null - ? _assets - : (_assetsForLinking[linkInPackage] ??= []); + List _getEncodedAssetList(String? linkInPackage) => + linkInPackage == null + ? _assets + : (_assetsForLinking[linkInPackage] ??= []); - HookOutputImpl copyWith({Iterable? assets}) => HookOutputImpl( + HookOutputImpl copyWith({Iterable? encodedAssets}) => + HookOutputImpl( timestamp: timestamp, - assets: assets?.toList() ?? _assets, - assetsForLinking: assetsForLinking, + encodedAssets: encodedAssets?.toList() ?? _assets, + encodedAssetsForLinking: encodedAssetsForLinking, dependencies: _dependencies, metadata: metadata, ); diff --git a/pkgs/native_assets_cli/lib/src/model/link_config.dart b/pkgs/native_assets_cli/lib/src/model/link_config.dart index f3507979a..0f46876a7 100644 --- a/pkgs/native_assets_cli/lib/src/model/link_config.dart +++ b/pkgs/native_assets_cli/lib/src/model/link_config.dart @@ -6,15 +6,15 @@ part of '../api/link_config.dart'; /// The input to the linking script. /// -/// It consists of the fields inherited from the [HookConfig] and the [assets] -/// from the build step. +/// It consists of the fields inherited from the [HookConfig] and the +/// [encodedAssets] from the build step. class LinkConfigImpl extends HookConfigImpl implements LinkConfig { static const resourceIdentifierKey = 'resource_identifiers'; static const assetsKey = 'assets'; @override - final Iterable assets; + final Iterable encodedAssets; // TODO: Placeholder for the resources.json file URL. We don't want to change // native_assets_builder when implementing the parsing. @@ -22,7 +22,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { final Uri? recordedUsagesFile; LinkConfigImpl({ - required this.assets, + required this.encodedAssets, this.recordedUsagesFile, required super.outputDirectory, required super.outputDirectoryShared, @@ -31,7 +31,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { Version? version, required super.buildMode, super.cCompiler, - Iterable? supportedAssetTypes, + required super.supportedAssetTypes, super.targetAndroidNdkApi, super.targetArchitecture, super.targetIOSSdk, @@ -43,24 +43,22 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { }) : super( hook: Hook.link, version: version ?? HookConfigImpl.latestVersion, - supportedAssetTypes: supportedAssetTypes ?? [CodeAsset.type], ); LinkConfigImpl.dryRun({ - required this.assets, + required this.encodedAssets, this.recordedUsagesFile, required super.outputDirectory, required super.outputDirectoryShared, required super.packageName, required super.packageRoot, Version? version, - Iterable? supportedAssetTypes, + required super.supportedAssetTypes, required super.linkModePreference, required super.targetOS, }) : super.dryRun( hook: Hook.link, version: version ?? HookConfigImpl.latestVersion, - supportedAssetTypes: supportedAssetTypes ?? [CodeAsset.type], ); @override @@ -74,7 +72,10 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { ...hookToJson(), if (recordedUsagesFile != null) resourceIdentifierKey: recordedUsagesFile!.toFilePath(), - assetsKey: Asset.listToJson(assets), + if (encodedAssets.isNotEmpty) + assetsKey: [ + for (final asset in encodedAssets) asset.toJson(), + ], }.sortOnKey(); static LinkConfig fromArguments(List arguments) { @@ -101,7 +102,8 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { linkModePreference: HookConfigImpl.parseLinkModePreference(config), version: HookConfigImpl.parseVersion(config), cCompiler: HookConfigImpl.parseCCompiler(config, dryRun), - supportedAssetTypes: HookConfigImpl.parseSupportedAssetTypes(config), + supportedAssetTypes: + HookConfigImpl.parseSupportedEncodedAssetTypes(config), targetAndroidNdkApi: HookConfigImpl.parseTargetAndroidNdkApi(config, dryRun, targetOS), targetIOSSdk: HookConfigImpl.parseTargetIOSSdk(config, dryRun, targetOS), @@ -109,7 +111,10 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { HookConfigImpl.parseTargetIosVersion(config, dryRun, targetOS), targetMacOSVersion: HookConfigImpl.parseTargetMacOSVersion(config, dryRun, targetOS), - assets: parseAssets(config), + encodedAssets: [ + for (final json in config.optionalList(assetsKey) ?? []) + EncodedAsset.fromJson(json as Map), + ], recordedUsagesFile: parseRecordedUsagesUri(config), dryRun: dryRun, ); @@ -118,9 +123,6 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { static Uri? parseRecordedUsagesUri(Map config) => config.optionalPath(resourceIdentifierKey); - static List parseAssets(Map config) => - Asset.listFromJson(config.optionalList(assetsKey)); - @override bool operator ==(Object other) { if (super != other) { @@ -132,7 +134,8 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { if (other.recordedUsagesFile != recordedUsagesFile) { return false; } - if (!const DeepCollectionEquality().equals(other.assets, assets)) { + if (!const DeepCollectionEquality() + .equals(other.encodedAssets, encodedAssets)) { return false; } return true; @@ -142,7 +145,7 @@ class LinkConfigImpl extends HookConfigImpl implements LinkConfig { int get hashCode => Object.hashAll([ super.hashCode, recordedUsagesFile, - const DeepCollectionEquality().hash(assets), + const DeepCollectionEquality().hash(encodedAssets), ]); @override diff --git a/pkgs/native_assets_cli/lib/src/validation.dart b/pkgs/native_assets_cli/lib/src/validation.dart new file mode 100644 index 000000000..5a17dc31f --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/validation.dart @@ -0,0 +1,69 @@ +// 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 '../native_assets_cli_internal.dart'; + +typedef ValidationErrors = List; + +/// Invoked by package:native_assets_builder +Future validateBuildOutput( + BuildConfig config, + BuildOutput output, +) async { + final errors = [ + ..._validateAssetsForLinking(config, output), + ..._validateOutputAssetTypes(config, output.encodedAssets), + ]; + if (config.linkingEnabled) { + for (final assets in output.encodedAssetsForLinking.values) { + errors.addAll(_validateOutputAssetTypes(config, assets)); + } + } + return errors; +} + +/// Invoked by package:native_assets_builder +Future validateLinkOutput( + LinkConfig config, + LinkOutput output, +) async { + final errors = [ + ..._validateOutputAssetTypes(config, output.encodedAssets), + ]; + return errors; +} + +/// Only output asset types that are supported by the embedder. +List _validateOutputAssetTypes( + HookConfig config, + Iterable assets, +) { + final errors = []; + final supportedAssetTypes = config.supportedAssetTypes; + for (final asset in assets) { + if (!supportedAssetTypes.contains(asset.type)) { + final error = + 'Asset with type "${asset.type}" is not a supported asset type ' + '(${supportedAssetTypes.join(' ')} are supported)'; + errors.add(error); + } + } + return errors; +} + +/// EncodedAssetsForLinking should be empty if linking is not supported. +List _validateAssetsForLinking( + BuildConfig config, + BuildOutput output, +) { + final errors = []; + if (!config.linkingEnabled) { + if (output.encodedAssetsForLinking.isNotEmpty) { + const error = 'BuildOutput.assetsForLinking is not empty while ' + 'BuildConfig.linkingEnabled is false'; + errors.add(error); + } + } + return errors; +} diff --git a/pkgs/native_assets_cli/lib/src/validator/validator.dart b/pkgs/native_assets_cli/lib/src/validator/validator.dart deleted file mode 100644 index 1cfc5f2ea..000000000 --- a/pkgs/native_assets_cli/lib/src/validator/validator.dart +++ /dev/null @@ -1,237 +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 '../api/build_config.dart'; -import '../api/build_output.dart'; -import '../api/hook_config.dart'; -import '../api/link_config.dart'; -import '../asset.dart'; -import '../link_mode.dart'; -import '../link_mode_preference.dart'; - -typedef ValidateResult = ({ - bool success, - List errors, -}); - -Future validateBuild( - BuildConfig config, - BuildOutput output, -) async { - output as HookOutputImpl; - final errors = [ - ...validateAssetsForLinking(config, output), - ...validateOutputAssetTypes(config, output), - if (!config.dryRun) ...await validateFilesExist(config, output), - ...validateCodeAssets(config, output), - ...validateAssetId(config, output), - if (!config.dryRun) ...validateNoDuplicateAssetIds(output), - ...validateNoDuplicateDylibs(output.assets), - ]; - return ( - success: errors.isEmpty, - errors: errors, - ); -} - -Future validateLink( - LinkConfig config, - LinkOutput output, -) async { - output as HookOutputImpl; - final errors = [ - ...validateOutputAssetTypes(config, output), - if (!config.dryRun) ...await validateFilesExist(config, output), - ...validateCodeAssets(config, output), - if (!config.dryRun) ...validateNoDuplicateAssetIds(output), - ...validateNoDuplicateDylibs(output.assets), - ]; - - return ( - success: errors.isEmpty, - errors: errors, - ); -} - -/// AssetsForLinking should be empty if linking is not supported. -List validateAssetsForLinking( - BuildConfig config, - BuildOutput output, -) { - final errors = []; - if (!config.linkingEnabled) { - if (output.assetsForLinking.isNotEmpty) { - const error = 'BuildOutput.assetsForLinking is not empty while ' - 'BuildConfig.linkingEnabled is false'; - errors.add(error); - } - } - return errors; -} - -/// Only output asset types that are supported by the embedder. -List validateOutputAssetTypes( - HookConfig config, - HookOutputImpl output, -) { - final errors = []; - final supportedAssetTypes = config.supportedAssetTypes; - for (final asset in output.assets) { - if (!supportedAssetTypes.contains(asset.type)) { - final error = - 'Asset "${asset.id}" has asset type "${asset.type}", which is ' - 'not in supportedAssetTypes'; - errors.add(error); - } - } - return errors; -} - -/// Files mentioned in assets must exist. -Future> validateFilesExist( - HookConfig config, - HookOutputImpl output, -) async { - final errors = []; - - await Future.wait(output.allAssets.map((asset) async { - final file = asset.file; - if (file == null && !config.dryRun) { - final error = 'Asset "${asset.id}" has no file.'; - errors.add(error); - } - if (file != null && !config.dryRun && !await File.fromUri(file).exists()) { - final error = - 'Asset "${asset.id}" has a file "${asset.file!.toFilePath()}", which ' - 'does not exist.'; - errors.add(error); - } - })); - - return errors; -} - -extension on Asset { - String get type { - switch (this) { - case CodeAsset _: - return CodeAsset.type; - case DataAsset _: - return DataAsset.type; - } - throw UnsupportedError('Unknown asset type'); - } -} - -extension on HookOutputImpl { - Iterable get allAssets => - [...assets, ...assetsForLinking.values.expand((e) => e)]; -} - -/// Native code assets for bundling should have a supported linking type. -List validateCodeAssets( - HookConfig config, - HookOutputImpl output, -) { - final errors = []; - final linkModePreference = config.linkModePreference; - for (final asset in output.assets.whereType()) { - final linkMode = asset.linkMode; - if ((linkMode is DynamicLoading && - linkModePreference == LinkModePreference.static) || - (linkMode is StaticLinking && - linkModePreference == LinkModePreference.dynamic)) { - final error = 'Asset "${asset.id}" has a link mode "$linkMode", which ' - 'is not allowed by by the config link mode preference ' - '"$linkModePreference".'; - errors.add(error); - } - - final os = asset.os; - if (config.targetOS != os) { - final error = 'Asset "${asset.id}" has a os "$os", which ' - 'is not the target os "${config.targetOS}".'; - errors.add(error); - } - - final architecture = asset.architecture; - if (!config.dryRun) { - if (architecture == null) { - final error = 'Asset "${asset.id}" has no architecture.'; - errors.add(error); - } else if (architecture != config.targetArchitecture) { - final error = - 'Asset "${asset.id}" has an architecture "$architecture", which ' - 'is not the target architecture "${config.targetArchitecture}".'; - errors.add(error); - } - } - } - return errors; -} - -/// Build hooks must only output assets in their own package namespace. -List validateAssetId( - HookConfig config, - BuildOutput output, -) { - final errors = []; - final packageName = config.packageName; - for (final asset in output.assets) { - if (!asset.id.startsWith('package:$packageName/')) { - final error = 'Asset "${asset.id}" does not start with ' - '"package:$packageName/".'; - errors.add(error); - } - } - return errors; -} - -List validateNoDuplicateAssetIds( - BuildOutput output, -) { - final errors = []; - final assetIds = {}; - for (final asset in output.assets) { - if (assetIds.contains(asset.id)) { - final error = 'Duplicate asset id: "${asset.id}".'; - errors.add(error); - } else { - assetIds.add(asset.id); - } - } - return errors; -} - -List validateNoDuplicateDylibs( - Iterable assets, -) { - final errors = []; - final fileNameToAssetId = >{}; - for (final asset in assets.whereType()) { - if (asset.linkMode is! DynamicLoadingBundled) { - continue; - } - final file = asset.file; - if (file == null) { - continue; - } - final fileName = file.pathSegments.where((s) => s.isNotEmpty).last; - fileNameToAssetId[fileName] ??= {}; - fileNameToAssetId[fileName]!.add(asset.id); - } - for (final fileName in fileNameToAssetId.keys) { - final assetIds = fileNameToAssetId[fileName]!; - if (assetIds.length > 1) { - final assetIdsString = assetIds.map((e) => '"$e"').join(', '); - final error = - 'Duplicate dynamic library file name "$fileName" for the following' - ' asset ids: $assetIdsString.'; - errors.add(error); - } - } - return errors; -} diff --git a/pkgs/native_assets_cli/test/api/asset_test.dart b/pkgs/native_assets_cli/test/api/asset_test.dart index 92a6af506..8c260e281 100644 --- a/pkgs/native_assets_cli/test/api/asset_test.dart +++ b/pkgs/native_assets_cli/test/api/asset_test.dart @@ -69,18 +69,4 @@ void main() { LookupInProcess().toString(); LookupInExecutable().toString(); }); - - test('Errors', () { - expect( - () => CodeAsset( - 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 0480536f7..613c60dce 100644 --- a/pkgs/native_assets_cli/test/api/build_config_test.dart +++ b/pkgs/native_assets_cli/test/api/build_config_test.dart @@ -119,10 +119,12 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final config = { 'build_mode': 'release', + 'supported_asset_types': [CodeAsset.type], 'dry_run': false, 'linking_enabled': false, 'link_mode_preference': 'prefer-static', @@ -154,6 +156,7 @@ void main() async { final config = { 'dry_run': true, + 'supported_asset_types': [CodeAsset.type], 'linking_enabled': true, 'link_mode_preference': 'prefer-static', 'out_dir': outDirUri.toFilePath(), @@ -189,6 +192,7 @@ void main() async { }, }, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final buildConfig2 = BuildConfig.build( @@ -210,6 +214,7 @@ void main() async { }, }, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); expect(buildConfig1, equals(buildConfig1)); @@ -230,6 +235,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: true, + supportedAssetTypes: [CodeAsset.type], ); final buildConfig2 = BuildConfig.build( @@ -242,6 +248,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); expect(buildConfig1, equals(buildConfig1)); @@ -261,6 +268,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final configFileContents = (buildConfig as BuildConfigImpl).toJsonString(); final configUri = tempUri.resolve('config.json'); 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 797703871..9bd10f474 100644 --- a/pkgs/native_assets_cli/test/api/build_output_test.dart +++ b/pkgs/native_assets_cli/test/api/build_output_test.dart @@ -21,7 +21,7 @@ void main() { test('BuildOutput constructor', () { BuildOutput( timestamp: DateTime.parse('2022-11-10 13:25:01.000'), - assets: [ + encodedAssets: [ CodeAsset( package: 'my_package', name: 'foo', @@ -29,14 +29,14 @@ void main() { linkMode: DynamicLoadingBundled(), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), CodeAsset( package: 'my_package', name: 'foo2', linkMode: DynamicLoadingSystem(Uri(path: 'path/to/libfoo2.so')), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), ], dependencies: [ Uri.file('path/to/file.ext'), diff --git a/pkgs/native_assets_cli/test/api/build_test.dart b/pkgs/native_assets_cli/test/api/build_test.dart index 186aa7969..baae8eba6 100644 --- a/pkgs/native_assets_cli/test/api/build_test.dart +++ b/pkgs/native_assets_cli/test/api/build_test.dart @@ -59,6 +59,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferDynamic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final configJson = (config1 as BuildConfigImpl).toJsonString(); buildConfigUri = tempUri.resolve('build_config.json'); diff --git a/pkgs/native_assets_cli/test/api/link_config_test.dart b/pkgs/native_assets_cli/test/api/link_config_test.dart index c4cf243bd..bd739b65b 100644 --- a/pkgs/native_assets_cli/test/api/link_config_test.dart +++ b/pkgs/native_assets_cli/test/api/link_config_test.dart @@ -112,11 +112,13 @@ void main() async { buildMode: BuildMode.release, assets: [], linkModePreference: LinkModePreference.preferStatic, + supportedAssetTypes: [CodeAsset.type], ); final config = { 'build_mode': 'release', 'dry_run': false, + 'supported_asset_types': [CodeAsset.type], 'link_mode_preference': 'prefer-static', 'out_dir': outDirUri.toFilePath(), 'out_dir_shared': outputDirectoryShared.toFilePath(), @@ -147,6 +149,7 @@ void main() async { final config = { 'dry_run': true, + 'supported_asset_types': [CodeAsset.type], 'link_mode_preference': 'prefer-static', 'out_dir': outDirUri.toFilePath(), 'out_dir_shared': outputDirectoryShared.toFilePath(), @@ -173,6 +176,7 @@ void main() async { buildMode: BuildMode.release, assets: [], linkModePreference: LinkModePreference.preferStatic, + supportedAssetTypes: [CodeAsset.type], ); final configFileContents = (linkConfig as LinkConfigImpl).toJsonString(); final configUri = tempUri.resolve('config.json'); diff --git a/pkgs/native_assets_cli/test/example/local_asset_test.dart b/pkgs/native_assets_cli/test/example/local_asset_test.dart index b556a402d..a3d822b33 100644 --- a/pkgs/native_assets_cli/test/example/local_asset_test.dart +++ b/pkgs/native_assets_cli/test/example/local_asset_test.dart @@ -53,6 +53,7 @@ void main() async { targetArchitecture: dryRun ? null : Architecture.current, buildMode: dryRun ? null : BuildMode.debug, cCompiler: dryRun ? null : cCompiler, + supportedAssetTypes: [CodeAsset.type], ); final buildConfigUri = testTempUri.resolve('build_config.json'); @@ -77,11 +78,14 @@ void main() async { final buildOutputUri = outputDirectory.resolve('build_output.json'); final buildOutput = HookOutputImpl.fromJsonString( await File.fromUri(buildOutputUri).readAsString()); - final assets = buildOutput.assets; + final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { expect(assets.length, greaterThanOrEqualTo(1)); - expect(await File.fromUri(assets.first.file!).exists(), false); + final first = assets.first; + expect(first.type, CodeAsset.type); + final codeAsset = CodeAsset.fromEncoded(first); + expect(await File.fromUri(codeAsset.file!).exists(), false); expect(dependencies, []); } else { expect(assets.length, 1); 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 8f3fb3cb6..5c82eca99 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 @@ -53,6 +53,7 @@ void main() async { targetArchitecture: dryRun ? null : Architecture.current, buildMode: dryRun ? null : BuildMode.debug, cCompiler: dryRun ? null : cCompiler, + supportedAssetTypes: [CodeAsset.type], ); final buildConfigUri = testTempUri.resolve('build_config.json'); @@ -77,7 +78,7 @@ void main() async { final buildOutputUri = outputDirectory.resolve('build_output.json'); final buildOutput = HookOutputImpl.fromJsonString( await File.fromUri(buildOutputUri).readAsString()); - final assets = buildOutput.assets; + final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { expect(assets.length, greaterThanOrEqualTo(1)); diff --git a/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart b/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart index 00b1f706b..4d7ea3feb 100644 --- a/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart +++ b/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart @@ -62,6 +62,7 @@ void main() async { targetArchitecture: dryRun ? null : Architecture.current, buildMode: dryRun ? null : BuildMode.debug, cCompiler: dryRun ? null : cCompiler, + supportedAssetTypes: [CodeAsset.type], ); final buildConfigUri = testTempUri.resolve('build_config.json'); @@ -86,7 +87,7 @@ void main() async { final buildOutputUri = outputDirectory.resolve('build_output.json'); final buildOutput = HookOutputImpl.fromJsonString( await File.fromUri(buildOutputUri).readAsString()); - final assets = buildOutput.assets; + final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { expect(assets.length, greaterThanOrEqualTo(3)); diff --git a/pkgs/native_assets_cli/test/helpers.dart b/pkgs/native_assets_cli/test/helpers.dart index 36a45c744..6f02de316 100644 --- a/pkgs/native_assets_cli/test/helpers.dart +++ b/pkgs/native_assets_cli/test/helpers.dart @@ -127,21 +127,24 @@ extension on String { Uri asFileUri() => Uri.file(this); } -extension AssetIterable on Iterable { +extension AssetIterable on Iterable { Future allExist() async { - final allResults = await Future.wait(map((e) => e.exists())); - final missing = allResults.contains(false); - return !missing; - } -} - -extension on Asset { - Future exists() async { - final path_ = file; - return switch (path_) { - null => true, - _ => await path_.fileSystemEntity.exists(), - }; + for (final encodedAsset in this) { + if (encodedAsset.type == DataAsset.type) { + final dataAsset = DataAsset.fromEncoded(encodedAsset); + if (!await dataAsset.file.fileSystemEntity.exists()) { + return false; + } + } else if (encodedAsset.type == CodeAsset.type) { + final codeAsset = CodeAsset.fromEncoded(encodedAsset); + if (!await (codeAsset.file?.fileSystemEntity.exists() ?? true)) { + return false; + } + } else { + throw UnimplementedError('Unknown asset type ${encodedAsset.type}'); + } + } + return true; } } diff --git a/pkgs/native_assets_cli/test/model/asset_test.dart b/pkgs/native_assets_cli/test/model/asset_test.dart index 5d3df2fdf..e6d268812 100644 --- a/pkgs/native_assets_cli/test/model/asset_test.dart +++ b/pkgs/native_assets_cli/test/model/asset_test.dart @@ -73,8 +73,8 @@ void main() { ), ]; final assets = [ - ...nativeCodeAssets, - ...dataAssets, + for (final asset in nativeCodeAssets) asset.encode(), + for (final asset in dataAssets) asset.encode(), ]; final assetsJsonEncoding = [ @@ -143,7 +143,7 @@ void main() { test('asset json', () { final json = [for (final item in assets) item.toJson()]; expect(json, assetsJsonEncoding); - final assets2 = Asset.listFromJson(json); + final assets2 = [for (final e in json) EncodedAsset.fromJson(e)]; expect(assets, assets2); }); 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 34dcc89ad..08afdbb0d 100644 --- a/pkgs/native_assets_cli/test/model/build_config_test.dart +++ b/pkgs/native_assets_cli/test/model/build_config_test.dart @@ -66,6 +66,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final config2 = BuildConfigImpl( @@ -79,6 +80,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); expect(config1, equals(config1)); @@ -112,9 +114,11 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final config = { + 'supported_asset_types': [CodeAsset.type], 'build_mode': 'release', 'dry_run': false, 'linking_enabled': false, @@ -142,10 +146,12 @@ void main() async { targetOS: OS.android, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final config = { 'dry_run': true, + 'supported_asset_types': [CodeAsset.type], 'linking_enabled': false, 'link_mode_preference': 'prefer-static', 'out_dir': outDirUri.toFilePath(), @@ -176,6 +182,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final configFile = buildConfig1.toJson(); @@ -204,6 +211,7 @@ void main() async { }), }, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final buildConfig2 = BuildConfigImpl( @@ -225,6 +233,7 @@ void main() async { }), }, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); expect(buildConfig1, equals(buildConfig1)); @@ -259,6 +268,7 @@ void main() async { }), }, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final jsonObject = buildConfig1.toJson(); @@ -313,6 +323,7 @@ void main() async { 'target_os': 'android', 'target_android_ndk_api': 30, 'link_mode_preference': 'prefer-static', + 'supported_asset_types': [CodeAsset.type], }), throwsA(predicate( (e) => @@ -334,6 +345,7 @@ void main() async { 'target_android_ndk_api': 30, 'link_mode_preference': 'prefer-static', 'build_mode': BuildMode.release.name, + 'supported_asset_types': [CodeAsset.type], 'dependency_metadata': { 'bar': {'key': 'value'}, 'foo': [], @@ -358,6 +370,7 @@ void main() async { 'target_architecture': 'arm64', 'target_os': 'android', 'link_mode_preference': 'prefer-static', + 'supported_asset_types': [CodeAsset.type], 'build_mode': BuildMode.release.name, }), throwsA(predicate( @@ -386,6 +399,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); config.toString(); }); @@ -402,6 +416,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.preferStatic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final configFileContents = buildConfig.toJsonString(); final configUri = tempUri.resolve('config.json'); @@ -430,6 +445,7 @@ void main() async { buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final configFile = buildConfig1.toJson(); @@ -448,6 +464,7 @@ void main() async { 'target_os': 'linux', 'version': version, 'package_name': packageName, + 'supported_asset_types': [CodeAsset.type], 'dry_run': true, }; expect( @@ -473,6 +490,7 @@ void main() async { 'target_os': 'windows', 'target_architecture': 'arm', 'build_mode': 'debug', + 'supported_asset_types': [CodeAsset.type], 'version': HookConfigImpl.latestVersion.toString(), }; expect( @@ -495,6 +513,7 @@ void main() async { 'target_architecture': 'arm64', 'build_mode': 'debug', 'dry_run': true, + 'supported_asset_types': [CodeAsset.type], 'version': HookConfigImpl.latestVersion.toString(), }; expect( @@ -517,6 +536,7 @@ void main() async { 'package_name': packageName, 'package_root': tempUri.toFilePath(), 'target_os': 'android', + 'supported_asset_types': [CodeAsset.type], 'version': HookConfigImpl.latestVersion.toString(), }; final buildConfig = BuildConfigImpl.fromJson(config); @@ -539,6 +559,7 @@ void main() async { 'package_name': packageName, 'package_root': tempUri.toFilePath(), 'target_os': 'windows', + 'supported_asset_types': [CodeAsset.type], 'version': HookConfigImpl.latestVersion.toString(), }; final buildConfig = BuildConfigImpl.fromJson(config); @@ -554,6 +575,7 @@ void main() async { targetOS: OS.windows, linkModePreference: LinkModePreference.dynamic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); buildConfig.toJsonString(); // No crash. @@ -571,6 +593,7 @@ void main() async { 'target_android_ndk_api': 30, 'target_architecture': 'invalid_architecture', 'target_os': 'android', + 'supported_asset_types': [CodeAsset.type], 'version': HookOutputImpl.latestVersion.toString(), }; expect( 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 80c47c4ad..7f28a0ffb 100644 --- a/pkgs/native_assets_cli/test/model/build_output_test.dart +++ b/pkgs/native_assets_cli/test/model/build_output_test.dart @@ -120,6 +120,7 @@ void main() { targetOS: OS.macOS, linkModePreference: LinkModePreference.dynamic, linkingEnabled: false, + supportedAssetTypes: [CodeAsset.type], ); final buildOutput = getBuildOutput(); await buildOutput.writeToFile(config: config); @@ -145,6 +146,7 @@ void main() { linkModePreference: LinkModePreference.dynamic, version: Version(1, 1, 0), linkingEnabled: null, // version < 1.4.0 + supportedAssetTypes: [CodeAsset.type], ); final buildOutput = getBuildOutput(withLinkedAssets: false); await buildOutput.writeToFile(config: config); @@ -184,7 +186,7 @@ void main() { test('BuildOutput setters', () { final buildOutput = HookOutputImpl( timestamp: DateTime.parse('2022-11-10 13:25:01.000'), - assets: [ + encodedAssets: [ CodeAsset( package: 'my_package', name: 'foo', @@ -192,14 +194,14 @@ void main() { linkMode: DynamicLoadingBundled(), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), CodeAsset( package: 'my_package', name: 'foo2', linkMode: DynamicLoadingSystem(Uri(path: 'path/to/libfoo2.so')), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), ], dependencies: Dependencies([ Uri.file('path/to/file.ext'), @@ -214,7 +216,7 @@ void main() { final buildOutput2 = HookOutputImpl( timestamp: DateTime.parse('2022-11-10 13:25:01.000'), ); - buildOutput2.addAsset( + buildOutput2.addEncodedAsset( CodeAsset( package: 'my_package', name: 'foo', @@ -222,16 +224,16 @@ void main() { linkMode: DynamicLoadingBundled(), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), ); - buildOutput2.addAssets([ + buildOutput2.addEncodedAssets([ CodeAsset( package: 'my_package', name: 'foo2', linkMode: DynamicLoadingSystem(Uri(path: 'path/to/libfoo2.so')), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), ]); buildOutput2.addDependency( Uri.file('path/to/file.ext'), @@ -253,7 +255,7 @@ void main() { HookOutputImpl getBuildOutput({bool withLinkedAssets = true}) => HookOutputImpl( timestamp: DateTime.parse('2022-11-10 13:25:01.000'), - assets: [ + encodedAssets: [ CodeAsset( package: 'my_package', name: 'foo', @@ -261,44 +263,44 @@ HookOutputImpl getBuildOutput({bool withLinkedAssets = true}) => HookOutputImpl( linkMode: DynamicLoadingBundled(), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), CodeAsset( package: 'my_package', name: 'foo2', linkMode: DynamicLoadingSystem(Uri(path: 'path/to/libfoo2.so')), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), CodeAsset( package: 'my_package', name: 'foo3', linkMode: LookupInProcess(), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), CodeAsset( package: 'my_package', name: 'foo4', linkMode: LookupInExecutable(), os: OS.android, architecture: Architecture.x64, - ), + ).encode(), ], - assetsForLinking: withLinkedAssets + encodedAssetsForLinking: withLinkedAssets ? { 'my_package': [ DataAsset( file: Uri.file('path/to/data'), name: 'data', package: 'my_package', - ) + ).encode() ], 'my_package_2': [ DataAsset( file: Uri.file('path/to/data2'), name: 'data', package: 'my_package', - ) + ).encode() ] } : null, diff --git a/pkgs/native_assets_cli/test/model/checksum_test.dart b/pkgs/native_assets_cli/test/model/checksum_test.dart index f005c213b..cff247336 100644 --- a/pkgs/native_assets_cli/test/model/checksum_test.dart +++ b/pkgs/native_assets_cli/test/model/checksum_test.dart @@ -41,6 +41,7 @@ void main() { targetOS: OS.linux, buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, + supportedAssetTypes: [CodeAsset.type], dependencyMetadata: { 'foo': const Metadata({'key': 'value'}) }, @@ -59,6 +60,7 @@ void main() { targetOS: OS.linux, buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, + supportedAssetTypes: [CodeAsset.type], cCompiler: CCompilerConfig( compiler: fakeClangUri, ), @@ -77,6 +79,7 @@ void main() { targetOS: OS.linux, buildMode: BuildMode.release, linkModePreference: LinkModePreference.dynamic, + supportedAssetTypes: [CodeAsset.type], cCompiler: CCompilerConfig( compiler: fakeClangUri, ), diff --git a/pkgs/native_assets_cli/test/model/link_config_test.dart b/pkgs/native_assets_cli/test/model/link_config_test.dart index 64fa70f5e..c59588b97 100644 --- a/pkgs/native_assets_cli/test/model/link_config_test.dart +++ b/pkgs/native_assets_cli/test/model/link_config_test.dart @@ -26,7 +26,7 @@ void main() async { package: packageName, name: 'name', file: Uri.file('nonexistent'), - ), + ).encode(), CodeAsset( package: packageName, name: 'name2', @@ -34,8 +34,8 @@ void main() async { os: OS.android, file: Uri.file('not there'), architecture: Architecture.riscv64, - ) - ]; + ).encode(), + ].cast(); setUp(() async { tempUri = (await Directory.systemTemp.createTemp()).uri; @@ -69,6 +69,7 @@ void main() async { test('LinkConfig ==', () { final config1 = LinkConfigImpl( + supportedAssetTypes: [CodeAsset.type], outputDirectory: outDirUri, outputDirectoryShared: outputDirectoryShared, packageName: packageName, @@ -82,12 +83,13 @@ void main() async { archiver: fakeAr, ), buildMode: BuildMode.release, - assets: assets, + encodedAssets: assets, recordedUsagesFile: recordedUsagesFile, linkModePreference: LinkModePreference.preferStatic, ); final config2 = LinkConfigImpl( + supportedAssetTypes: [CodeAsset.type], outputDirectory: outDir2Uri, outputDirectoryShared: outputDirectoryShared, packageName: packageName, @@ -96,7 +98,7 @@ void main() async { targetOS: OS.android, targetAndroidNdkApi: 30, buildMode: BuildMode.release, - assets: [], + encodedAssets: [], recordedUsagesFile: null, linkModePreference: LinkModePreference.preferStatic, ); @@ -116,11 +118,12 @@ void main() async { expect(config1.cCompiler.envScriptArgs == config2.cCompiler.envScriptArgs, true); expect(config1.cCompiler != config2.cCompiler, true); - expect(config1.assets != config2.assets, true); + expect(config1.encodedAssets != config2.encodedAssets, true); }); test('LinkConfig fromConfig', () { final buildConfig2 = LinkConfigImpl( + supportedAssetTypes: [CodeAsset.type], outputDirectory: outDirUri, outputDirectoryShared: outputDirectoryShared, packageName: packageName, @@ -129,12 +132,13 @@ void main() async { targetOS: OS.android, targetAndroidNdkApi: 30, buildMode: BuildMode.release, - assets: assets, + encodedAssets: assets, linkModePreference: LinkModePreference.preferStatic, ); final config = { 'build_mode': 'release', + 'supported_asset_types': [CodeAsset.type], 'dry_run': false, 'link_mode_preference': 'prefer-static', 'out_dir': outDirUri.toFilePath(), @@ -145,7 +149,7 @@ void main() async { 'target_architecture': 'arm64', 'target_os': 'android', 'version': HookOutputImpl.latestVersion.toString(), - 'assets': Asset.listToJson(assets), + 'assets': [for (final asset in assets) asset.toJson()], }; final fromConfig = LinkConfigImpl.fromJson(config); @@ -154,17 +158,19 @@ void main() async { test('LinkConfig.dryRun', () { final buildConfig2 = LinkConfigImpl.dryRun( + supportedAssetTypes: [CodeAsset.type], outputDirectory: outDirUri, outputDirectoryShared: outputDirectoryShared, packageName: packageName, packageRoot: packageRootUri, targetOS: OS.android, - assets: [], + encodedAssets: [], linkModePreference: LinkModePreference.preferStatic, ); final config = { 'dry_run': true, + 'supported_asset_types': [CodeAsset.type], 'link_mode_preference': 'prefer-static', 'out_dir': outDirUri.toFilePath(), 'out_dir_shared': outputDirectoryShared.toFilePath(), @@ -181,6 +187,7 @@ void main() async { test('LinkConfig toJson fromConfig', () { final buildConfig1 = LinkConfigImpl( + supportedAssetTypes: [CodeAsset.type], outputDirectory: outDirUri, outputDirectoryShared: outputDirectoryShared, packageName: packageName, @@ -193,7 +200,7 @@ void main() async { linker: fakeLd, ), buildMode: BuildMode.release, - assets: assets, + encodedAssets: assets, linkModePreference: LinkModePreference.preferStatic, ); @@ -205,6 +212,7 @@ void main() async { test('LinkConfig toJson fromJson', () { final outDir = outDirUri; final buildConfig1 = LinkConfigImpl( + supportedAssetTypes: [CodeAsset.type], outputDirectory: outDir, outputDirectoryShared: outputDirectoryShared, packageName: packageName, @@ -217,20 +225,20 @@ void main() async { linker: fakeLd, ), buildMode: BuildMode.release, - assets: assets, + encodedAssets: assets, linkModePreference: LinkModePreference.preferStatic, ); final jsonObject = buildConfig1.toJson(); final expectedJson = { - 'assets': Asset.listToJson(assets), + 'assets': [for (final asset in assets) asset.toJson()], + 'supported_asset_types': [CodeAsset.type], 'build_mode': 'release', 'c_compiler': {'cc': fakeClang.toFilePath(), 'ld': fakeLd.toFilePath()}, 'out_dir': outDir.toFilePath(), 'out_dir_shared': outputDirectoryShared.toFilePath(), 'package_name': packageName, 'package_root': tempUri.toFilePath(), - 'supported_asset_types': [CodeAsset.type], 'target_architecture': 'arm64', 'target_ios_sdk': 'iphoneos', 'target_os': 'ios', @@ -256,6 +264,7 @@ void main() async { expect( () => LinkConfigImpl.fromJson({ 'version': HookConfigImpl.latestVersion.toString(), + 'supported_asset_types': [CodeAsset.type], 'package_name': packageName, 'package_root': packageRootUri.toFilePath(), 'target_architecture': 'arm64', @@ -274,6 +283,7 @@ void main() async { expect( () => LinkConfigImpl.fromJson({ 'version': HookConfigImpl.latestVersion.toString(), + 'supported_asset_types': [CodeAsset.type], 'out_dir': outDirUri.toFilePath(), 'out_dir_shared': outputDirectoryShared.toFilePath(), 'package_name': packageName, @@ -296,6 +306,7 @@ void main() async { ); expect( () => LinkConfigImpl.fromJson({ + 'supported_asset_types': [CodeAsset.type], 'out_dir': outDirUri.toFilePath(), 'out_dir_shared': outputDirectoryShared.toFilePath(), 'version': HookConfigImpl.latestVersion.toString(), @@ -318,6 +329,7 @@ void main() async { test('LinkConfig toString', () { final config = LinkConfigImpl( + supportedAssetTypes: [CodeAsset.type], outputDirectory: outDirUri, outputDirectoryShared: outputDirectoryShared, packageName: packageName, @@ -330,7 +342,7 @@ void main() async { linker: fakeLd, ), buildMode: BuildMode.release, - assets: assets, + encodedAssets: assets, linkModePreference: LinkModePreference.preferStatic, ); expect(config.toString(), isNotEmpty); @@ -338,6 +350,7 @@ void main() async { test('LinkConfig fromArgs', () async { final buildConfig = LinkConfigImpl( + supportedAssetTypes: [CodeAsset.type], outputDirectory: outDirUri, outputDirectoryShared: outputDirectoryShared, packageName: packageName, @@ -346,7 +359,7 @@ void main() async { targetOS: OS.android, targetAndroidNdkApi: 30, buildMode: BuildMode.release, - assets: assets, + encodedAssets: assets, recordedUsagesFile: recordedUsagesFile, linkModePreference: LinkModePreference.preferStatic, ); @@ -432,6 +445,7 @@ void main() async { final outDir = outDirUri; final config = { 'link_mode_preference': 'prefer-static', + 'supported_asset_types': [CodeAsset.type], 'out_dir': outDir.toFilePath(), 'out_dir_shared': outputDirectoryShared.toFilePath(), 'package_name': packageName, @@ -452,6 +466,7 @@ void main() async { test('LinkConfig dry_run target arch', () { final outDir = outDirUri; final config = { + 'supported_asset_types': [CodeAsset.type], 'link_mode_preference': 'prefer-static', 'out_dir': outDir.toFilePath(), 'out_dir_shared': outputDirectoryShared.toFilePath(), @@ -467,12 +482,13 @@ void main() async { test('LinkConfig dry_run toString', () { final buildConfig = LinkConfigImpl.dryRun( + supportedAssetTypes: [CodeAsset.type], packageName: packageName, outputDirectory: outDirUri, outputDirectoryShared: outputDirectoryShared, packageRoot: tempUri, targetOS: OS.windows, - assets: assets, + encodedAssets: assets, linkModePreference: LinkModePreference.preferStatic, ); expect(buildConfig.toJsonString(), isNotEmpty); @@ -480,6 +496,7 @@ void main() async { test('invalid architecture', () { final config = { + 'supported_asset_types': [CodeAsset.type], 'build_mode': 'release', 'dry_run': false, 'link_mode_preference': 'prefer-static', diff --git a/pkgs/native_assets_cli/test/validator/validator_test.dart b/pkgs/native_assets_cli/test/validator/validator_test.dart index fa831a5bd..e37ab9de7 100644 --- a/pkgs/native_assets_cli/test/validator/validator_test.dart +++ b/pkgs/native_assets_cli/test/validator/validator_test.dart @@ -4,8 +4,7 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; -import 'package:native_assets_cli/src/validator/validator.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; void main() { @@ -47,7 +46,7 @@ void main() { final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAsset( + output.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', @@ -58,10 +57,9 @@ void main() { ), linkInPackage: 'bar', ); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateBuildOutput(config, output); expect( - result.errors, + errors, contains(contains('linkingEnabled is false')), ); }); @@ -83,16 +81,15 @@ void main() { final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAsset(DataAsset( + output.dataAssets.add(DataAsset( package: config.packageName, name: 'foo.txt', file: assetFile.uri, )); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateBuildOutput(config, output); expect( - result.errors, - contains(contains('which is not in supportedAssetTypes')), + errors, + contains(contains('"data" is not a supported asset type')), ); }); @@ -112,15 +109,14 @@ void main() { ); final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); - output.addAsset(DataAsset( + output.dataAssets.add(DataAsset( package: config.packageName, name: 'foo.txt', file: assetFile.uri, )); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateDataAssetBuildOutput(config, output); expect( - result.errors, + errors, contains(contains('which does not exist')), ); }); @@ -140,17 +136,16 @@ void main() { linkingEnabled: false, ); final output = BuildOutput(); - output.addAsset(CodeAsset( + output.codeAssets.add(CodeAsset( package: config.packageName, name: 'foo.dylib', architecture: config.targetArchitecture, os: config.targetOS, linkMode: DynamicLoadingBundled(), )); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateCodeAssetBuildOutput(config, output); expect( - result.errors, + errors, contains(contains('has no file')), ); }); @@ -176,7 +171,7 @@ void main() { final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAsset( + output.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', @@ -186,10 +181,9 @@ void main() { architecture: config.targetArchitecture, ), ); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateCodeAssetBuildOutput(config, output); expect( - result.errors, + errors, contains(contains( 'which is not allowed by by the config link mode preference', )), @@ -214,7 +208,7 @@ void main() { final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAsset( + output.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', @@ -224,10 +218,9 @@ void main() { architecture: Architecture.x64, ), ); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateCodeAssetBuildOutput(config, output); expect( - result.errors, + errors, contains(contains( 'which is not the target architecture', )), @@ -251,7 +244,7 @@ void main() { final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAsset( + output.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', @@ -260,10 +253,9 @@ void main() { os: config.targetOS, ), ); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateCodeAssetBuildOutput(config, output); expect( - result.errors, + errors, contains(contains( 'has no architecture', )), @@ -287,7 +279,7 @@ void main() { final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAsset( + output.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', @@ -297,10 +289,9 @@ void main() { architecture: config.targetArchitecture, ), ); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateCodeAssetBuildOutput(config, output); expect( - result.errors, + errors, contains(contains( 'which is not the target os', )), @@ -324,16 +315,15 @@ void main() { final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAsset(DataAsset( + output.dataAssets.add(DataAsset( package: 'different_package', name: 'foo.txt', file: assetFile.uri, )); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateDataAssetBuildOutput(config, output); expect( - result.errors, - contains(contains('does not start with')), + errors, + contains(contains('Data asset must have package name my_package')), ); }); @@ -354,7 +344,7 @@ void main() { final output = BuildOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAssets([ + output.dataAssets.addAll([ DataAsset( package: config.packageName, name: 'foo.txt', @@ -366,11 +356,10 @@ void main() { file: assetFile.uri, ), ]); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateDataAssetBuildOutput(config, output); expect( - result.errors, - contains(contains('Duplicate asset id')), + errors, + contains(contains('More than one')), ); }); @@ -391,16 +380,15 @@ void main() { final output = LinkOutput(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.addAsset(DataAsset( + output.dataAssets.add(DataAsset( package: config.packageName, name: 'foo.txt', file: assetFile.uri, )); - final result = await validateLink(config, output); - expect(result.success, isFalse); + final errors = await validateLinkOutput(config, output); expect( - result.errors, - contains(contains('which is not in supportedAssetTypes')), + errors, + contains(contains('"data" is not a supported asset type')), ); }); @@ -422,7 +410,7 @@ void main() { final fileName = config.targetOS.dylibFileName('foo'); final assetFile = File.fromUri(outDirUri.resolve(fileName)); await assetFile.writeAsBytes([1, 2, 3]); - output.addAssets([ + output.codeAssets.addAll([ CodeAsset( package: config.packageName, name: 'src/foo.dart', @@ -440,10 +428,9 @@ void main() { architecture: config.targetArchitecture, ), ]); - final result = await validateBuild(config, output); - expect(result.success, isFalse); + final errors = await validateCodeAssetBuildOutput(config, output); expect( - result.errors, + errors, contains(contains('Duplicate dynamic library file name')), ); }); diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart index 1f38aa885..34c28ae1e 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart @@ -162,17 +162,15 @@ class CBuilder extends CTool implements Builder { } if (assetName != null) { - output.addAssets( - [ - CodeAsset( - package: config.packageName, - name: assetName!, - file: libUri, - linkMode: linkMode, - os: config.targetOS, - architecture: config.dryRun ? null : config.targetArchitecture, - ) - ], + output.codeAssets.add( + CodeAsset( + package: config.packageName, + name: assetName!, + file: libUri, + linkMode: linkMode, + os: config.targetOS, + architecture: config.dryRun ? null : config.targetArchitecture, + ), linkInPackage: linkInPackage, ); } diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart index 45790535e..3ac153901 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart @@ -89,17 +89,15 @@ class CLinker extends CTool implements Linker { } if (assetName != null) { - output.addAssets( - [ - CodeAsset( - package: config.packageName, - name: assetName!, - file: libUri, - linkMode: linkMode, - os: config.targetOS, - architecture: config.dryRun ? null : config.targetArchitecture, - ) - ], + output.codeAssets.add( + CodeAsset( + package: config.packageName, + name: assetName!, + file: libUri, + linkMode: linkMode, + os: config.targetOS, + architecture: config.dryRun ? null : config.targetArchitecture, + ), ); } if (!config.dryRun) { diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart index e38bdbf7d..3902ea6d0 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart @@ -24,7 +24,7 @@ abstract class CTool { /// Asset identifier. /// - /// Used to output the [LinkConfig.assets]. + /// Used to output the [CodeAsset]. /// /// If omitted, no asset will be added to the build output. final String? assetName; 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 e8f9182d1..1d9b85f37 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 @@ -31,6 +31,7 @@ void main() { const name = 'add'; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_android_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_android_test.dart index b00696455..12c0cd713 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 @@ -111,6 +111,7 @@ Future buildLib( await Directory.fromUri(tempUriShared).create(); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUriShared, packageName: name, 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 6f4b08599..34afa5f47 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 @@ -64,6 +64,7 @@ void main() { Language() => throw UnimplementedError(), }; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -209,6 +210,7 @@ Future buildLib( const name = 'add'; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, 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 9a3e42769..c659f2f7a 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 @@ -37,6 +37,7 @@ void main() { const name = 'add'; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, 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 79ad82bd2..a24bd5c7b 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 @@ -50,6 +50,7 @@ void main() { const name = 'add'; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -139,6 +140,7 @@ Future buildLib( const name = 'add'; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, 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 a68fa3ac9..9fe63c0ee 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 @@ -56,6 +56,7 @@ void main() { const name = 'add'; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart index 77e35132b..faa0c7ece 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart @@ -45,6 +45,7 @@ void main() { final logger = createCapturingLogger(logMessages); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -116,6 +117,7 @@ void main() { final buildConfig = dryRun ? BuildConfig.dryRun( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -125,6 +127,7 @@ void main() { linkingEnabled: false, ) : BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -216,6 +219,7 @@ void main() { final logger = createCapturingLogger(logMessages); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -273,6 +277,7 @@ void main() { const name = 'includes'; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -317,6 +322,7 @@ void main() { final logger = createCapturingLogger(logMessages); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -374,6 +380,7 @@ void main() { final logger = createCapturingLogger(logMessages); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], buildMode: BuildMode.release, outputDirectory: tempUri, outputDirectoryShared: tempUri2, @@ -437,6 +444,7 @@ void main() { final logger = createCapturingLogger(logMessages); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], buildMode: BuildMode.release, outputDirectory: tempUri, outputDirectoryShared: tempUri2, @@ -507,6 +515,7 @@ Future testDefines({ const name = 'defines'; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, 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 86be27864..4f1b58e98 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/compiler_resolver_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/compiler_resolver_test.dart @@ -42,6 +42,7 @@ void main() { ...await msvc.vcvars64.defaultResolver!.resolve(logger: logger) ].firstOrNull?.uri; final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: 'dummy', @@ -69,6 +70,7 @@ void main() { final tempUri = await tempDirForTest(); final tempUri2 = await tempDirForTest(); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: 'dummy', diff --git a/pkgs/native_toolchain_c/test/cbuilder/objective_c_test.dart b/pkgs/native_toolchain_c/test/cbuilder/objective_c_test.dart index 50f725994..4d76aaf14 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/objective_c_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/objective_c_test.dart @@ -37,6 +37,7 @@ void main() { final logger = createCapturingLogger(logMessages); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], buildMode: BuildMode.release, outputDirectory: tempUri, outputDirectoryShared: tempUri2, diff --git a/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart b/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart index e215afb44..57f37bbda 100644 --- a/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart +++ b/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart @@ -27,6 +27,7 @@ Future buildTestArchive( final logger = createCapturingLogger(logMessages); final buildConfig = BuildConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: name, @@ -51,5 +52,5 @@ Future buildTestArchive( logger: logger, ); - return buildOutput.assets.first.file!; + return buildOutput.codeAssets.all.first.file!; } diff --git a/pkgs/native_toolchain_c/test/clinker/objects_test.dart b/pkgs/native_toolchain_c/test/clinker/objects_test.dart index 1d9cd47e0..1f9654732 100644 --- a/pkgs/native_toolchain_c/test/clinker/objects_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/objects_test.dart @@ -33,6 +33,7 @@ Future main() async { final uri = await buildTestArchive(tempUri, tempUri2, os, architecture); final linkConfig = LinkConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: 'testpackage', @@ -57,11 +58,11 @@ Future main() async { logger: logger, ); - expect(linkOutput.assets, hasLength(1)); - final asset = linkOutput.assets.first; + expect(linkOutput.codeAssets.all, hasLength(1)); + final asset = linkOutput.codeAssets.all.first; expect(asset, isA()); await expectSymbols( - asset: asset as CodeAsset, + asset: asset, symbols: [ 'my_func', 'my_other_func', diff --git a/pkgs/native_toolchain_c/test/clinker/throws_test.dart b/pkgs/native_toolchain_c/test/clinker/throws_test.dart index e69a39f9c..48e5838aa 100644 --- a/pkgs/native_toolchain_c/test/clinker/throws_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/throws_test.dart @@ -30,6 +30,7 @@ Future main() async { await expectLater( () => cLinker.run( config: LinkConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: 'testpackage', diff --git a/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart b/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart index 56eec3a70..6bf1b5d71 100644 --- a/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart +++ b/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart @@ -65,6 +65,7 @@ Future runTests(List architectures) async { final linkOutput = LinkOutput(); final config = LinkConfig.build( + supportedAssetTypes: [CodeAsset.type], outputDirectory: tempUri, outputDirectoryShared: tempUri2, packageName: 'testpackage', @@ -84,7 +85,7 @@ Future runTests(List architectures) async { logger: logger, ); - final asset = linkOutput.assets.first as CodeAsset; + final asset = linkOutput.codeAssets.all.first; final filePath = asset.file!.toFilePath(); final machine = await readelfMachine(filePath); From de2a89f110f51b0e855d71153cab98b626829646 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 2 Oct 2024 11:29:47 +0200 Subject: [PATCH 2/5] Update CHANGELOG.md files --- pkgs/native_assets_builder/CHANGELOG.md | 3 +++ pkgs/native_assets_cli/CHANGELOG.md | 3 +++ pkgs/native_assets_cli/lib/src/model/hook_output.dart | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkgs/native_assets_builder/CHANGELOG.md b/pkgs/native_assets_builder/CHANGELOG.md index a00609e4a..50a1b629f 100644 --- a/pkgs/native_assets_builder/CHANGELOG.md +++ b/pkgs/native_assets_builder/CHANGELOG.md @@ -11,6 +11,9 @@ by end-users. - Remove link-dry-run concept as it's unused by Flutter Tools & Dart SDK - Bump `native_assets_cli` to `0.9.0`. +- **Breaking change**: Remove asset-type specific logic from `package:native_assets_builder`. + Bundling tools have to now supply `supportedAssetTypes` and corresponding + validation routines. ## 0.8.3 diff --git a/pkgs/native_assets_cli/CHANGELOG.md b/pkgs/native_assets_cli/CHANGELOG.md index 3805a1643..71aff12e5 100644 --- a/pkgs/native_assets_cli/CHANGELOG.md +++ b/pkgs/native_assets_cli/CHANGELOG.md @@ -20,6 +20,9 @@ - **Breaking change** Moved some methods to be extension methods. - Some classes in the `BuildConfig` and `BuildOutput` now expose `fromJson` and `toJson`. +- **Breaking change** Removed `Asset` class, removed `{Build,Link}Output.assets*`. + Hook writers should now use e.g. `output.dataAssets.add(DataAsset(...))` + instead of `output.addAsset(DataAsset(...))`. ## 0.8.0 diff --git a/pkgs/native_assets_cli/lib/src/model/hook_output.dart b/pkgs/native_assets_cli/lib/src/model/hook_output.dart index 9e6947a20..0d48d9fb7 100644 --- a/pkgs/native_assets_cli/lib/src/model/hook_output.dart +++ b/pkgs/native_assets_cli/lib/src/model/hook_output.dart @@ -83,7 +83,7 @@ final class HookOutputImpl implements BuildOutput, LinkOutput { return HookOutputImpl( timestamp: DateTime.parse(get(jsonMap, _timestampKey)), encodedAssets: [ - for (final json in (jsonMap.optionalList(_assetsKey) ?? [])) + for (final json in jsonMap.optionalList(_assetsKey) ?? []) EncodedAsset.fromJson(json as Map), ], encodedAssetsForLinking: { From 4b3f2d0596f1bad8f21ef03b052e903373f65634 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 4 Oct 2024 09:33:49 +0200 Subject: [PATCH 3/5] address comments --- .../lib/src/build_runner/build_runner.dart | 21 ++++++++++------ .../link/package_with_assets/hook/build.dart | 4 ++-- .../link/package_with_assets/hook/link.dart | 6 ++--- .../lib/native_assets_cli.dart | 12 +++++----- pkgs/native_assets_cli/lib/src/api/build.dart | 2 +- .../lib/src/api/build_output.dart | 24 +++++++++++++++++++ pkgs/native_assets_cli/lib/src/api/test.dart | 15 ++++-------- .../lib/src/code_assets/code_asset.dart | 6 ++--- .../lib/src/data_assets/data_asset.dart | 6 ++--- .../lib/src/encoded_asset.dart | 3 ++- pkgs/native_assets_cli/lib/src/utils/map.dart | 5 ++-- .../test/example/local_asset_test.dart | 6 ++--- .../test/model/link_config_test.dart | 2 +- 13 files changed, 66 insertions(+), 46 deletions(-) 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 160b706fc..c17774f3b 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 @@ -23,15 +23,22 @@ import 'build_planner.dart'; typedef DependencyMetadata = Map; -typedef HookValidator = Future Function( +typedef _HookValidator = Future Function( HookConfig config, HookOutputImpl output); +// A callback that validates the output of a `hook/link.dart` invocation is +// valid (it may valid asset-type specific information). typedef BuildValidator = Future Function( BuildConfig config, BuildOutput outup); +// A callback that validates the output of a `hook/link.dart` invocation is +// valid (it may valid asset-type specific information). typedef LinkValidator = Future Function( LinkConfig config, LinkOutput output); +// A callback that validates assets emitted across all packages are valid / can +// be used together (it may valid asset-type specific information - e.g. that +// there are no classes in shared library filenames). typedef ApplicationAssetValidator = Future Function( List assets); @@ -165,7 +172,7 @@ class NativeAssetsBuildRunner { required Target target, required Uri workingDirectory, required BuildMode buildMode, - required HookValidator validator, + required _HookValidator validator, required ApplicationAssetValidator applicationAssetValidator, CCompilerConfig? cCompilerConfig, IOSSdk? targetIOSSdk, @@ -466,7 +473,7 @@ class NativeAssetsBuildRunner { Future<_PackageBuildRecord> _runHookForPackageCached( Hook hook, HookConfigImpl config, - HookValidator validator, + _HookValidator validator, Uri packageConfigUri, Uri workingDirectory, bool includeParentEnvironment, @@ -535,7 +542,7 @@ class NativeAssetsBuildRunner { Future<_PackageBuildRecord> _runHookForPackage( Hook hook, HookConfigImpl config, - HookValidator validator, + _HookValidator validator, Uri packageConfigUri, Uri workingDirectory, bool includeParentEnvironment, @@ -595,7 +602,7 @@ ${result.stdout} final output = HookOutputImpl.readFromFile(file: config.outputFile) ?? HookOutputImpl(); - final errors = await validate(config, output, packageLayout, validator); + final errors = await _validate(config, output, packageLayout, validator); success &= errors.isEmpty; if (errors.isNotEmpty) { logger.severe('package:${config.packageName}` has invalid output.'); @@ -804,11 +811,11 @@ ${compileResult.stdout} }; } - Future validate( + Future _validate( HookConfigImpl config, HookOutputImpl output, PackageLayout packageLayout, - HookValidator validator, + _HookValidator validator, ) async { final errors = config is BuildConfigImpl ? await validateBuildOutput(config, output) diff --git a/pkgs/native_assets_cli/example/link/package_with_assets/hook/build.dart b/pkgs/native_assets_cli/example/link/package_with_assets/hook/build.dart index 371096caa..0ac49c85c 100644 --- a/pkgs/native_assets_cli/example/link/package_with_assets/hook/build.dart +++ b/pkgs/native_assets_cli/example/link/package_with_assets/hook/build.dart @@ -24,12 +24,12 @@ void main(List args) async { .toFilePath(windows: false) .substring(config.packageRoot.toFilePath(windows: false).length); - output.addEncodedAsset( + output.dataAssets.add( DataAsset( package: packageName, name: name, file: dataAsset.uri, - ).encode(), + ), linkInPackage: config.linkingEnabled ? packageName : null, ); // TODO(https://github.com/dart-lang/native/issues/1208): Report diff --git a/pkgs/native_assets_cli/example/link/package_with_assets/hook/link.dart b/pkgs/native_assets_cli/example/link/package_with_assets/hook/link.dart index 6f7a46ff8..d77ea69bc 100644 --- a/pkgs/native_assets_cli/example/link/package_with_assets/hook/link.dart +++ b/pkgs/native_assets_cli/example/link/package_with_assets/hook/link.dart @@ -20,10 +20,8 @@ void main(List args) async { final usedAssets = (usages.instancesOf(multiplyIdentifier) ?? []).map((e) => (e.instanceConstant.fields.values.first as StringConstant).value); - output.addEncodedAssets(config.encodedAssets - .where((e) => e.type == DataAsset.type) - .where( - (asset) => usedAssets.contains(DataAsset.fromEncoded(asset).name))); + output.dataAssets.addAll(config.dataAssets.all + .where((dataAsset) => usedAssets.contains(dataAsset.name))); }); } diff --git a/pkgs/native_assets_cli/lib/native_assets_cli.dart b/pkgs/native_assets_cli/lib/native_assets_cli.dart index 7fbad509b..8bac2546a 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli.dart @@ -21,9 +21,9 @@ export 'src/code_assets/code_asset.dart' show BuildOutputCodeAssets, CodeAsset, - CodeAssetsBuildOutputExt, - CodeAssetsLinkConfigExt, - CodeAssetsLinkOutputExt, + CodeAssetsBuildOutput, + CodeAssetsLinkConfig, + CodeAssetsLinkOutput, LinkConfigCodeAssets, LinkOutputCodeAssets, OSLibraryNaming; @@ -31,9 +31,9 @@ export 'src/data_assets/data_asset.dart' show BuildOutputDataAssets, DataAsset, - DataAssetsBuildOutputExt, - DataAssetsLinkConfigExt, - DataAssetsLinkOutputExt, + DataAssetsBuildOutput, + DataAssetsLinkConfig, + DataAssetsLinkOutput, LinkConfigDataAssets, LinkOutputDataAssets; export 'src/encoded_asset.dart' show EncodedAsset; diff --git a/pkgs/native_assets_cli/lib/src/api/build.dart b/pkgs/native_assets_cli/lib/src/api/build.dart index e3fdbcd5f..b1a0eae93 100644 --- a/pkgs/native_assets_cli/lib/src/api/build.dart +++ b/pkgs/native_assets_cli/lib/src/api/build.dart @@ -70,7 +70,7 @@ import 'build_output.dart'; /// ]); /// } /// -/// output.addEncodedAsset( +/// output.codeAssets.add( /// // TODO: Change to DataAsset once the Dart/Flutter SDK can consume it. /// CodeAsset( /// package: packageName, 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 6f76cbb8e..a435bf066 100644 --- a/pkgs/native_assets_cli/lib/src/api/build_output.dart +++ b/pkgs/native_assets_cli/lib/src/api/build_output.dart @@ -116,6 +116,18 @@ abstract final class BuildOutput { /// If the [linkInPackage] argument is specified, the asset will not be /// bundled during the build step, but sent as input to the link hook of the /// specified package, where it can be further processed and possibly bundled. + /// + /// Note to hook writers. Prefer using the `.add` method on the extension for + /// the specific asset type being added: + /// + /// ```dart + /// main(List arguments) async { + /// await build((config, output) { + /// output.codeAssets.add(CodeAsset(...)); + /// output.dataAssets.add(DataAsset(...)); + /// }); + /// } + /// ``` void addEncodedAsset(EncodedAsset asset, {String? linkInPackage}); /// Adds [EncodedAsset]s produced by this build or dry run. @@ -124,6 +136,18 @@ abstract final class BuildOutput { /// bundled during the build step, but sent as input to the link hook of the /// specified package, where they can be further processed and possibly /// bundled. + /// + /// Note to hook writers. Prefer using the `.addAll` method on the extension + /// for the specific asset type being added: + /// + /// ```dart + /// main(List arguments) async { + /// await build((config, output) { + /// output.codeAssets.addAll([CodeAsset(...), ...]); + /// output.dataAssets.addAll([DataAsset(...), ...]); + /// }); + /// } + /// ``` void addEncodedAssets(Iterable assets, {String? linkInPackage}); /// Adds file used by this build. diff --git a/pkgs/native_assets_cli/lib/src/api/test.dart b/pkgs/native_assets_cli/lib/src/api/test.dart index 070e74825..df1782c15 100644 --- a/pkgs/native_assets_cli/lib/src/api/test.dart +++ b/pkgs/native_assets_cli/lib/src/api/test.dart @@ -70,7 +70,7 @@ Future testBuildHook({ await mainMethod(['--config=${buildConfigUri.toFilePath()}']); - final hookOutput = await _readOutput(buildConfig); + final hookOutput = await _readOutput(buildConfig) as BuildOutput; check(buildConfig, hookOutput); @@ -81,19 +81,12 @@ Future testBuildHook({ for (final asset in allEncodedAssets) { expect(buildConfig.supportedAssetTypes, contains(asset.type)); } - final dataAssets = allEncodedAssets - .where((e) => e.type == DataAsset.type) - .map(DataAsset.fromEncoded) - .toList(); - for (final asset in dataAssets) { + + for (final asset in hookOutput.dataAssets.all) { final file = File.fromUri(asset.file); expect(await file.exists(), true); } - final codeAssets = allEncodedAssets - .where((e) => e.type == CodeAsset.type) - .map(CodeAsset.fromEncoded) - .toList(); - for (final asset in codeAssets) { + for (final asset in hookOutput.codeAssets.all) { if (asset.file != null) { final file = File.fromUri(asset.file!); expect(await file.exists(), true); diff --git a/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart b/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart index b699701d6..1164ee98b 100644 --- a/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart +++ b/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart @@ -191,7 +191,7 @@ final class CodeAsset { } /// Build output extension for code assets. -extension CodeAssetsBuildOutputExt on BuildOutput { +extension CodeAssetsBuildOutput on BuildOutput { BuildOutputCodeAssets get codeAssets => BuildOutputCodeAssets(this); } @@ -215,7 +215,7 @@ class BuildOutputCodeAssets { } /// Link output extension for code assets. -extension CodeAssetsLinkConfigExt on LinkConfig { +extension CodeAssetsLinkConfig on LinkConfig { LinkConfigCodeAssets get codeAssets => LinkConfigCodeAssets(this); } @@ -230,7 +230,7 @@ class LinkConfigCodeAssets { } /// Link output extension for code assets. -extension CodeAssetsLinkOutputExt on LinkOutput { +extension CodeAssetsLinkOutput on LinkOutput { LinkOutputCodeAssets get codeAssets => LinkOutputCodeAssets(this); } diff --git a/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart b/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart index 9b3fb4189..79026b338 100644 --- a/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart +++ b/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart @@ -93,7 +93,7 @@ final class DataAsset { } /// Build output extension for data assets. -extension DataAssetsBuildOutputExt on BuildOutput { +extension DataAssetsBuildOutput on BuildOutput { BuildOutputDataAssets get dataAssets => BuildOutputDataAssets(this); } @@ -117,7 +117,7 @@ class BuildOutputDataAssets { } /// Link output extension for data assets. -extension DataAssetsLinkConfigExt on LinkConfig { +extension DataAssetsLinkConfig on LinkConfig { LinkConfigDataAssets get dataAssets => LinkConfigDataAssets(this); } @@ -132,7 +132,7 @@ class LinkConfigDataAssets { } /// Link output extension for data assets. -extension DataAssetsLinkOutputExt on LinkOutput { +extension DataAssetsLinkOutput on LinkOutput { LinkOutputDataAssets get dataAssets => LinkOutputDataAssets(this); } diff --git a/pkgs/native_assets_cli/lib/src/encoded_asset.dart b/pkgs/native_assets_cli/lib/src/encoded_asset.dart index 2d2d08681..4232bb890 100644 --- a/pkgs/native_assets_cli/lib/src/encoded_asset.dart +++ b/pkgs/native_assets_cli/lib/src/encoded_asset.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'json_utils.dart'; +import 'utils/map.dart'; /// An encoding of a particular asset type. final class EncodedAsset { @@ -27,7 +28,7 @@ final class EncodedAsset { Map toJson() => { for (final key in encoding.keys) key: encoding[key], _typeKey: type, - }; + }..sortOnKey(); @override String toString() => 'EncodedAsset($type, $encoding)'; diff --git a/pkgs/native_assets_cli/lib/src/utils/map.dart b/pkgs/native_assets_cli/lib/src/utils/map.dart index bb767ed99..8948770fb 100644 --- a/pkgs/native_assets_cli/lib/src/utils/map.dart +++ b/pkgs/native_assets_cli/lib/src/utils/map.dart @@ -2,13 +2,12 @@ // 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. -extension MapSorting, V extends Object> on Map { +extension MapSorting, V extends Object?> on Map { Map sortOnKey() { final result = {}; final keysSorted = keys.toList()..sort(); for (final key in keysSorted) { - final value = this[key]!; - result[key] = value; + result[key] = this[key] as V; } return result; } diff --git a/pkgs/native_assets_cli/test/example/local_asset_test.dart b/pkgs/native_assets_cli/test/example/local_asset_test.dart index a3d822b33..31f15c0a8 100644 --- a/pkgs/native_assets_cli/test/example/local_asset_test.dart +++ b/pkgs/native_assets_cli/test/example/local_asset_test.dart @@ -77,14 +77,12 @@ void main() async { final buildOutputUri = outputDirectory.resolve('build_output.json'); final buildOutput = HookOutputImpl.fromJsonString( - await File.fromUri(buildOutputUri).readAsString()); + await File.fromUri(buildOutputUri).readAsString()) as BuildOutput; final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { + final codeAsset = buildOutput.codeAssets.all.first; expect(assets.length, greaterThanOrEqualTo(1)); - final first = assets.first; - expect(first.type, CodeAsset.type); - final codeAsset = CodeAsset.fromEncoded(first); expect(await File.fromUri(codeAsset.file!).exists(), false); expect(dependencies, []); } else { diff --git a/pkgs/native_assets_cli/test/model/link_config_test.dart b/pkgs/native_assets_cli/test/model/link_config_test.dart index c59588b97..1f07cc4e2 100644 --- a/pkgs/native_assets_cli/test/model/link_config_test.dart +++ b/pkgs/native_assets_cli/test/model/link_config_test.dart @@ -35,7 +35,7 @@ void main() async { file: Uri.file('not there'), architecture: Architecture.riscv64, ).encode(), - ].cast(); + ]; setUp(() async { tempUri = (await Directory.systemTemp.createTemp()).uri; From be7e9eda26c654d5d5ef043e60f1227614cf2ec7 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 4 Oct 2024 09:47:24 +0200 Subject: [PATCH 4/5] Use extension types instead of classes --- .../lib/src/code_assets/code_asset.dart | 18 +++--------------- .../lib/src/data_assets/data_asset.dart | 18 +++--------------- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart b/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart index 1164ee98b..88576821f 100644 --- a/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart +++ b/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart @@ -195,11 +195,7 @@ extension CodeAssetsBuildOutput on BuildOutput { BuildOutputCodeAssets get codeAssets => BuildOutputCodeAssets(this); } -class BuildOutputCodeAssets { - final BuildOutput _output; - - BuildOutputCodeAssets(this._output); - +extension type BuildOutputCodeAssets(BuildOutput _output) { void add(CodeAsset asset, {String? linkInPackage}) => _output.addEncodedAsset(asset.encode(), linkInPackage: linkInPackage); @@ -219,11 +215,7 @@ extension CodeAssetsLinkConfig on LinkConfig { LinkConfigCodeAssets get codeAssets => LinkConfigCodeAssets(this); } -class LinkConfigCodeAssets { - final LinkConfig _config; - - LinkConfigCodeAssets(this._config); - +extension type LinkConfigCodeAssets(LinkConfig _config) { Iterable get all => _config.encodedAssets .where((e) => e.type == CodeAsset.type) .map(CodeAsset.fromEncoded); @@ -234,11 +226,7 @@ extension CodeAssetsLinkOutput on LinkOutput { LinkOutputCodeAssets get codeAssets => LinkOutputCodeAssets(this); } -class LinkOutputCodeAssets { - final LinkOutput _output; - - LinkOutputCodeAssets(this._output); - +extension type LinkOutputCodeAssets(LinkOutput _output) { void add(CodeAsset asset) => _output.addEncodedAsset(asset.encode()); void addAll(Iterable assets) => assets.forEach(add); diff --git a/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart b/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart index 79026b338..98864398b 100644 --- a/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart +++ b/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart @@ -97,11 +97,7 @@ extension DataAssetsBuildOutput on BuildOutput { BuildOutputDataAssets get dataAssets => BuildOutputDataAssets(this); } -class BuildOutputDataAssets { - final BuildOutput _output; - - BuildOutputDataAssets(this._output); - +extension type BuildOutputDataAssets(BuildOutput _output) { void add(DataAsset asset, {String? linkInPackage}) => _output.addEncodedAsset(asset.encode(), linkInPackage: linkInPackage); @@ -121,11 +117,7 @@ extension DataAssetsLinkConfig on LinkConfig { LinkConfigDataAssets get dataAssets => LinkConfigDataAssets(this); } -class LinkConfigDataAssets { - final LinkConfig _config; - - LinkConfigDataAssets(this._config); - +extension type LinkConfigDataAssets(LinkConfig _config) { Iterable get all => _config.encodedAssets .where((e) => e.type == DataAsset.type) .map(DataAsset.fromEncoded); @@ -136,11 +128,7 @@ extension DataAssetsLinkOutput on LinkOutput { LinkOutputDataAssets get dataAssets => LinkOutputDataAssets(this); } -class LinkOutputDataAssets { - final LinkOutput _output; - - LinkOutputDataAssets(this._output); - +extension type LinkOutputDataAssets(LinkOutput _output) { void add(DataAsset asset) => _output.addEncodedAsset(asset.encode()); void addAll(Iterable assets) => assets.forEach(add); From 01c97bc1db92b9d32c5c0ebcf38d7f686539a146 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Fri, 4 Oct 2024 12:43:18 +0200 Subject: [PATCH 5/5] update build_output_CHANGELOG.md and add note about dependency management to `LinkConfig.{codeAssets,dataAssets}.all` --- .../lib/src/code_assets/code_asset.dart | 7 +++++++ .../lib/src/data_assets/data_asset.dart | 7 +++++++ ...uild_output_CHANGELOG.md => hook_config_CHANGELOG.md} | 9 +++++++++ 3 files changed, 23 insertions(+) rename pkgs/native_assets_cli/lib/src/model/{build_output_CHANGELOG.md => hook_config_CHANGELOG.md} (73%) diff --git a/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart b/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart index 88576821f..2a13e71ca 100644 --- a/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart +++ b/pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart @@ -216,6 +216,13 @@ extension CodeAssetsLinkConfig on LinkConfig { } extension type LinkConfigCodeAssets(LinkConfig _config) { + // Returns the code assets that were sent to this linker. + // + // NOTE: If the linker implementation depends on the contents of the files the + // code assets refer (e.g. looks at static archives and links them) then the + // linker script has to add those files as dependencies via + // [LinkOutput.addDependency] to ensure the linker script will be re-run if + // the content of the files changes. Iterable get all => _config.encodedAssets .where((e) => e.type == CodeAsset.type) .map(CodeAsset.fromEncoded); diff --git a/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart b/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart index 98864398b..9c79b0629 100644 --- a/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart +++ b/pkgs/native_assets_cli/lib/src/data_assets/data_asset.dart @@ -118,6 +118,13 @@ extension DataAssetsLinkConfig on LinkConfig { } extension type LinkConfigDataAssets(LinkConfig _config) { + // Returns the data assets that were sent to this linker. + // + // NOTE: If the linker implementation depends on the contents of the files of + // the data assets (e.g. by transforming them, merging with other files, etc) + // then the linker script has to add those files as dependencies via + // [LinkOutput.addDependency] to ensure the linker script will be re-run if + // the content of the files changes. Iterable get all => _config.encodedAssets .where((e) => e.type == DataAsset.type) .map(DataAsset.fromEncoded); diff --git a/pkgs/native_assets_cli/lib/src/model/build_output_CHANGELOG.md b/pkgs/native_assets_cli/lib/src/model/hook_config_CHANGELOG.md similarity index 73% rename from pkgs/native_assets_cli/lib/src/model/build_output_CHANGELOG.md rename to pkgs/native_assets_cli/lib/src/model/hook_config_CHANGELOG.md index 43daa84e6..b295f1428 100644 --- a/pkgs/native_assets_cli/lib/src/model/build_output_CHANGELOG.md +++ b/pkgs/native_assets_cli/lib/src/model/hook_config_CHANGELOG.md @@ -1,6 +1,15 @@ ## 1.6.0 - No changes, but rev version due to BuildConfig change. +- **Breaking change** Link hooks now have to explicitly add any file contents + they rely on via `output.addDependency()` to ensure they re-run if the + content of those files changes. (Previously if a linker script obtained code + or data assets, the files referred to by those assets were implicitly added as + a dependency, but adding custom asset types changed this behavior) + NOTE: Newer Dart & Flutter SDKs will no longer add those dependencies + implicitly which may make some older linker implementations that do not add + dependencies explicitly not work correctly anymore: The linker scripts have + to be updated to add those dependencies explicitly. ## 1.5.0