From 9a009603918f927053893ecf56bd1728f5ec9b0b Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 3 Oct 2024 13:46:16 +0200 Subject: [PATCH] (not to be committed yet / not done yet) Allow custom build/link configuration This PR allows users of `package:native_assets_builder` to supply custom build/link configuration. This allows e.g. flutter to add additional configuration to `hook/{build,link}.dart` scripts that require flutter specific things. As opposed to earlier PRs that merged `Foo` and `FooImpl` we have a different approach for build/link config/output as they are something different: The current API (even before the recent refactoring to it) allows read and write access to e.g. assets, ...). We remove this capability as this is conceptually problematic: - Currently those API classes are both mutable and at the same time support operator==/hashCode: => This is problematic as inserting such an object into a set/map and then modifying it means one can later on not find it anymore. Mutable objects can have operator==/hashCode iff it doesn't change (e.g. the default implementation based on identity). Otherwise objects should be immutable if they want to support operator==/hashCode. => For our puroses we have no need for operator==/hashCode and therefore remove this (problematic) capability. - Currently those API classes are serving both the hook writers and the bundling tool. The bundling tool would use Impl versions of those, but in the end operate on the same structures. We now change this to be using the builder pattern: The code that * creates build/link config/output will use a builder object that allows mutationonly * consumes build/link config/output will uses a view object that only allows read access We then make those build/link config/output objects flexible in the sense that * a bundling tool can add more configuration to build/link configuration * a hook can consume this additional configuration To support this we a) Make the builders operate on a json map that allows incrementally add more things to the build/link config. => The bundling tool gives `package:native_assets_builder` a function that creates the initial configuration which allows it to add bundling-tool specific configs (e.g. flutter specific things). => Initializing the configs is split up into groups that belong together => Named with `{Build,Link}ConfigBuilder.setup*()` methods which initialize things that conceptually belong together. => A bundling tool can then e.g. add code asset specifics via `configBuilder.setupCodeConfig(...)` b) Make the hooks operate on a json map that allows viewing this additional information via extension methods. => A hook can use e.g. `config.codeConfig.cCompiler` Since not all bundling tools may want support code assets (web builds), the code asset specific configuration is now moved out of the core packages and is one such bundling-tool specific configuration. => Hook writers can now access it via `config.codeConfig.*` => This sets up the stage to change the CLI protocol to move those code-asset specific things into a subtree of the config (e.g. json['code_asset_config']) => Then we can allow hook writers to easily detect it by introducing a `config.hasCodeConfig` getter. We make various smaller other changes, e.g. * The `package:native_assets_builder` APIs now either return a result on successfull build or `null` in case of error. => This makes callers have to check (due to nullable type of result) whether build/link succeeded or not (easy to forget to check for a `result.success` boolean - as evidenced a number of tests that didn't check this `success` boolean) => It avoids returning a result that is only partial (i.e. has some assets but not all due to some builds failing) * The `Architecture` is now moved to be a code-asset specific configuration => It makes sense for code assets. => It wouldn't make sense for e.g. web builds where there's no code assets available. => For now we keep the target operating system, but even that's somewhat problematic for web builds. * We no longer need the (temporary) `output.{code,data}Assets.all` getters => This is now natural due to the seperation via builder pattern. Overall: * The changes on the bundling tool side are rather minimal: ```diff final buildResult = await nativeAssetsBuildRunner.build( + configCreator: () => BuildConfigBuilder() + ..setupCodeConfig( + linkModePreference: LinkModePreference.dynamic, + targetArchitecture: target.architecture, + targetMacOSVersion: targetMacOSVersion, + cCompilerConfig: _getCCompilerConfig(), + ), workingDirectory: workingDirectory, - target: target, - linkModePreference: LinkModePreference.dynamic, + targetOS: target.os, buildMode: BuildMode.release, includeParentEnvironment: true, - targetMacOSVersion: targetMacOSVersion, linkingEnabled: true, supportedAssetTypes: [ CodeAsset.type, @@ -160,7 +168,7 @@ class BuildCommand extends DartdevCommand { ...await validateCodeAssetsInApplication(assets), ], ); - if (!buildResult.success) { + if (buildResult == null) { stderr.writeln('Native assets build failed.'); return 255; } @ ``` * The changes on the hook writer side are rather minimal as well, e.g.: ``` - final iosVersion = config.targetIOSVersion; + final iosVersion = config.codeConfig.targetIOSVersion; ``` The main changes are all encapsulated within the `package:native_assets_builder` and `package:native_assets_cli` and the *many* tests that need updating. --- .../lib/src/build_runner/build_runner.dart | 615 ++++++++---------- .../lib/src/model/build_dry_run_result.dart | 7 - .../lib/src/model/build_result.dart | 7 - .../lib/src/model/hook_result.dart | 41 +- .../lib/src/model/link_result.dart | 7 - .../build_runner/build_dependencies_test.dart | 4 +- .../build_runner_asset_id_test.dart | 4 +- .../build_runner_build_dry_run_test.dart | 8 +- ...build_runner_build_output_format_test.dart | 2 +- .../build_runner_caching_test.dart | 24 +- .../build_runner/build_runner_cycle_test.dart | 4 +- .../build_runner_failure_test.dart | 10 +- .../build_runner_non_root_package_test.dart | 8 +- .../build_runner_reusability_test.dart | 18 +- .../build_runner_run_in_isolation_test.dart | 4 +- .../test/build_runner/build_runner_test.dart | 8 +- .../concurrency_shared_test_helper.dart | 12 +- .../build_runner/concurrency_test_helper.dart | 10 +- .../build_runner/conflicting_dylib_test.dart | 9 +- .../fail_on_os_sdk_version_test.dart | 6 +- .../test/build_runner/helpers.dart | 107 +-- .../test/build_runner/link_caching_test.dart | 24 +- .../test/build_runner/link_test.dart | 36 +- .../packaging_preference_test.dart | 16 +- .../test/build_runner/resources_test.dart | 4 +- .../test/build_runner/wrong_linker_test.dart | 3 +- .../native_dynamic_linking_test.dart | 38 +- .../test/test_data/transformer_test.dart | 40 +- .../test_data/drop_dylib_link/hook/link.dart | 17 +- .../fail_on_os_sdk_version/hook/build.dart | 7 +- .../hook/link.dart | 7 +- .../native_add_duplicate/hook/build.dart | 5 +- .../package_reading_metadata/hook/build.dart | 30 +- .../test_data/simple_link/hook/link.dart | 2 +- .../wrong_build_output/hook/build.dart | 11 +- .../wrong_build_output_2/hook/build.dart | 11 +- .../wrong_build_output_3/hook/build.dart | 25 +- .../test_data/wrong_linker/hook/build.dart | 13 +- .../wrong_namespace_asset/hook/build.dart | 13 +- .../example/build/local_asset/hook/build.dart | 5 +- .../build/local_asset/test/build_test.dart | 11 +- .../lib/native_assets_cli.dart | 10 +- .../lib/native_assets_cli_internal.dart | 11 +- pkgs/native_assets_cli/lib/src/api/build.dart | 25 +- .../lib/src/api/build_config.dart | 184 ------ .../lib/src/api/build_output.dart | 180 ----- .../lib/src/api/builder.dart | 21 +- .../lib/src/api/hook_config.dart | 153 ----- pkgs/native_assets_cli/lib/src/api/link.dart | 26 +- .../lib/src/api/link_config.dart | 117 ---- .../lib/src/api/link_output.dart | 72 -- .../native_assets_cli/lib/src/api/linker.dart | 5 +- pkgs/native_assets_cli/lib/src/api/test.dart | 134 ---- .../lib/src/code_assets/code_asset.dart | 78 ++- .../src/code_assets/code_asset_bundling.dart | 77 +++ .../lib/src/code_assets/validation.dart | 26 +- pkgs/native_assets_cli/lib/src/config.dart | 538 +++++++++++++++ .../lib/src/data_assets/data_asset.dart | 20 +- .../src/data_assets/data_asset_bundling.dart | 20 + .../lib/src/data_assets/validation.dart | 18 +- .../native_assets_cli/lib/src/json_utils.dart | 1 + pkgs/native_assets_cli/lib/src/link_mode.dart | 2 +- .../lib/src/model/build_config.dart | 210 ------ .../native_assets_cli/lib/src/model/hook.dart | 7 +- .../lib/src/model/hook_config.dart | 548 ---------------- .../lib/src/model/hook_output.dart | 218 ------- .../lib/src/model/link_config.dart | 153 ----- pkgs/native_assets_cli/lib/test.dart | 106 ++- .../test/api/build_config_test.dart | 287 -------- .../test/api/build_output_test.dart | 50 -- .../test/api/build_test.dart | 64 +- .../test/api/link_config_test.dart | 193 ------ .../test/build_config_test.dart | 321 +++++++++ .../test/build_output_test.dart | 92 +++ .../native_assets_cli/test/checksum_test.dart | 107 +++ .../test/example/local_asset_test.dart | 39 +- .../test/example/native_add_library_test.dart | 37 +- .../example/native_dynamic_linking_test.dart | 39 +- .../test/link_config_test.dart | 216 ++++++ .../test/model/build_config_test.dart | 604 ----------------- .../test/model/build_output_test.dart | 313 --------- .../test/model/checksum_test.dart | 110 ---- .../test/model/link_config_test.dart | 517 --------------- .../test/validator/validator_test.dart | 333 ++++------ .../lib/src/cbuilder/cbuilder.dart | 8 +- .../lib/src/cbuilder/clinker.dart | 91 ++- .../lib/src/cbuilder/compiler_resolver.dart | 24 +- .../lib/src/cbuilder/run_cbuilder.dart | 17 +- .../cbuilder/cbuilder_build_failure_test.dart | 34 +- .../cbuilder/cbuilder_cross_android_test.dart | 40 +- .../cbuilder/cbuilder_cross_ios_test.dart | 79 ++- .../cbuilder_cross_linux_host_test.dart | 37 +- .../cbuilder_cross_macos_host_test.dart | 73 ++- .../cbuilder_cross_windows_host_test.dart | 37 +- .../test/cbuilder/cbuilder_test.dart | 286 ++++---- .../test/cbuilder/compiler_resolver_test.dart | 84 ++- .../test/cbuilder/objective_c_test.dart | 34 +- .../test/clinker/build_testfiles.dart | 40 +- .../test/clinker/objects_test.dart | 42 +- .../test/clinker/throws_test.dart | 40 +- .../test/clinker/treeshake_helper.dart | 45 +- 101 files changed, 3060 insertions(+), 5466 deletions(-) delete mode 100644 pkgs/native_assets_cli/lib/src/api/build_config.dart delete mode 100644 pkgs/native_assets_cli/lib/src/api/build_output.dart delete mode 100644 pkgs/native_assets_cli/lib/src/api/hook_config.dart delete mode 100644 pkgs/native_assets_cli/lib/src/api/link_config.dart delete mode 100644 pkgs/native_assets_cli/lib/src/api/link_output.dart delete mode 100644 pkgs/native_assets_cli/lib/src/api/test.dart create mode 100644 pkgs/native_assets_cli/lib/src/code_assets/code_asset_bundling.dart create mode 100644 pkgs/native_assets_cli/lib/src/config.dart create mode 100644 pkgs/native_assets_cli/lib/src/data_assets/data_asset_bundling.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/build_config.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/hook_config.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/hook_output.dart delete mode 100644 pkgs/native_assets_cli/lib/src/model/link_config.dart delete mode 100644 pkgs/native_assets_cli/test/api/build_config_test.dart delete mode 100644 pkgs/native_assets_cli/test/api/build_output_test.dart delete mode 100644 pkgs/native_assets_cli/test/api/link_config_test.dart create mode 100644 pkgs/native_assets_cli/test/build_config_test.dart create mode 100644 pkgs/native_assets_cli/test/build_output_test.dart create mode 100644 pkgs/native_assets_cli/test/checksum_test.dart create mode 100644 pkgs/native_assets_cli/test/link_config_test.dart delete mode 100644 pkgs/native_assets_cli/test/model/build_config_test.dart delete mode 100644 pkgs/native_assets_cli/test/model/build_output_test.dart delete mode 100644 pkgs/native_assets_cli/test/model/checksum_test.dart delete mode 100644 pkgs/native_assets_cli/test/model/link_config_test.dart diff --git a/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart b/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart index 12afbcb11..fce1a52ce 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 @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:logging/logging.dart'; @@ -23,8 +24,14 @@ import 'build_planner.dart'; typedef DependencyMetadata = Map; +typedef ConfigCreator = HookConfigBuilder Function(); + +typedef BuildConfigCreator = BuildConfigBuilder Function(); + +typedef LinkConfigCreator = LinkConfigBuilder Function(); + typedef _HookValidator = Future Function( - HookConfig config, HookOutputImpl output); + HookConfig config, HookOutput output); // A callback that validates the output of a `hook/link.dart` invocation is // valid (it may valid asset-type specific information). @@ -72,39 +79,28 @@ class NativeAssetsBuildRunner { /// The native assets build runner does not support reentrancy for identical /// [api.BuildConfig] and [api.LinkConfig]! For more info see: /// https://github.com/dart-lang/native/issues/1319 - Future build({ - required LinkModePreference linkModePreference, - required Target target, - required Uri workingDirectory, - required BuildMode buildMode, + Future build({ + required BuildConfigCreator configCreator, required BuildValidator buildValidator, required ApplicationAssetValidator applicationAssetValidator, - CCompilerConfig? cCompilerConfig, - IOSSdk? targetIOSSdk, - int? targetIOSVersion, - int? targetMacOSVersion, - int? targetAndroidNdkApi, + required OS targetOS, + required BuildMode buildMode, + required Uri workingDirectory, required bool includeParentEnvironment, PackageLayout? packageLayout, String? runPackageName, - required Iterable supportedAssetTypes, + required List supportedAssetTypes, required bool linkingEnabled, }) async => _run( - validator: (HookConfig config, HookOutputImpl output) => + targetOS: targetOS, + buildMode: buildMode, + configCreator: configCreator, + validator: (HookConfig config, HookOutput output) => buildValidator(config as BuildConfig, output as BuildOutput), - applicationAssetValidator: (assets) async => - linkingEnabled ? [] : applicationAssetValidator(assets), + applicationAssetValidator: applicationAssetValidator, hook: Hook.build, - linkModePreference: linkModePreference, - target: target, workingDirectory: workingDirectory, - buildMode: buildMode, - cCompilerConfig: cCompilerConfig, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - targetAndroidNdkApi: targetAndroidNdkApi, includeParentEnvironment: includeParentEnvironment, packageLayout: packageLayout, runPackageName: runPackageName, @@ -123,40 +119,29 @@ class NativeAssetsBuildRunner { /// The native assets build runner does not support reentrancy for identical /// [api.BuildConfig] and [api.LinkConfig]! For more info see: /// https://github.com/dart-lang/native/issues/1319 - Future link({ - required LinkModePreference linkModePreference, - required Target target, - required Uri workingDirectory, - required BuildMode buildMode, + Future link({ + required ConfigCreator configCreator, required LinkValidator linkValidator, + required OS targetOS, + required BuildMode buildMode, + required Uri workingDirectory, required ApplicationAssetValidator applicationAssetValidator, - CCompilerConfig? cCompilerConfig, - IOSSdk? targetIOSSdk, - int? targetIOSVersion, - int? targetMacOSVersion, - int? targetAndroidNdkApi, required bool includeParentEnvironment, PackageLayout? packageLayout, Uri? resourceIdentifiers, String? runPackageName, - required Iterable supportedAssetTypes, + required List supportedAssetTypes, required BuildResult buildResult, }) async => _run( - validator: (HookConfig config, HookOutputImpl output) => + configCreator: configCreator, + validator: (HookConfig config, HookOutput output) => linkValidator(config as LinkConfig, output as LinkOutput), - applicationAssetValidator: (assets) async => - applicationAssetValidator(assets), + applicationAssetValidator: applicationAssetValidator, + targetOS: targetOS, + buildMode: buildMode, hook: Hook.link, - linkModePreference: linkModePreference, - target: target, workingDirectory: workingDirectory, - buildMode: buildMode, - cCompilerConfig: cCompilerConfig, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - targetAndroidNdkApi: targetAndroidNdkApi, includeParentEnvironment: includeParentEnvironment, packageLayout: packageLayout, runPackageName: runPackageName, @@ -166,62 +151,25 @@ class NativeAssetsBuildRunner { ); /// The common method for running building or linking of assets. - Future _run({ - required Hook hook, - required LinkModePreference linkModePreference, - required Target target, - required Uri workingDirectory, - required BuildMode buildMode, + Future _run({ + required ConfigCreator configCreator, required _HookValidator validator, required ApplicationAssetValidator applicationAssetValidator, - CCompilerConfig? cCompilerConfig, - IOSSdk? targetIOSSdk, - int? targetIOSVersion, - int? targetMacOSVersion, - int? targetAndroidNdkApi, + required OS targetOS, + required BuildMode buildMode, + required Hook hook, + required Uri workingDirectory, required bool includeParentEnvironment, PackageLayout? packageLayout, - Uri? resourceIdentifiers, String? runPackageName, - required Iterable supportedAssetTypes, - BuildResult? buildResult, + required List supportedAssetTypes, bool? linkingEnabled, + BuildResult? buildResult, + Uri? resourceIdentifiers, }) async { assert(hook == Hook.link || buildResult == null); assert(hook == Hook.build || linkingEnabled == null); - // Specifically for running our tests on Dart CI with the test runner, we - // recognize specific variables to setup the C Compiler configuration. - if (cCompilerConfig == null) { - final env = Platform.environment; - final cc = env['DART_HOOK_TESTING_C_COMPILER__CC']; - final ar = env['DART_HOOK_TESTING_C_COMPILER__AR']; - final ld = env['DART_HOOK_TESTING_C_COMPILER__LD']; - final envScript = env['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT']; - final envScriptArgs = - env['DART_HOOK_TESTING_C_COMPILER__ENV_SCRIPT_ARGUMENTS'] - ?.split(' ') - .map((arg) => arg.trim()) - .where((arg) => arg.isNotEmpty) - .toList(); - final hasEnvScriptArgs = - envScriptArgs != null && envScriptArgs.isNotEmpty; - - if (cc != null || - ar != null || - ld != null || - envScript != null || - hasEnvScriptArgs) { - cCompilerConfig = CCompilerConfig( - archiver: ar != null ? Uri.file(ar) : null, - compiler: cc != null ? Uri.file(cc) : null, - envScript: envScript != null ? Uri.file(envScript) : null, - envScriptArgs: hasEnvScriptArgs ? envScriptArgs : null, - linker: ld != null ? Uri.file(ld) : null, - ); - } - } - packageLayout ??= await PackageLayout.fromRootPackageRoot(workingDirectory); final (buildPlan, packageGraph, planSuccess) = await _makePlan( hook: hook, @@ -230,46 +178,79 @@ class NativeAssetsBuildRunner { runPackageName: runPackageName, ); if (!planSuccess) { - return HookResult.failure(); + return null; } - var hookResult = HookResult(); - if (hook == Hook.link) { - hookResult.encodedAssets.addAll(buildResult!.encodedAssets); - } - final metadata = {}; + var hookResult = (hook == Hook.link) + ? HookResult(encodedAssets: buildResult!.encodedAssets) + : HookResult(); + final globalMetadata = {}; for (final package in buildPlan) { - final DependencyMetadata? dependencyMetadata; - switch (hook) { - case Hook.build: - dependencyMetadata = _metadataForPackage( - packageGraph: packageGraph!, - packageName: package.name, - targetMetadata: metadata, - ); - case Hook.link: - dependencyMetadata = null; - } - final config = await _cliConfig( - package, - packageLayout, - target, - buildMode, - linkModePreference, - dependencyMetadata, - linkingEnabled, - cCompilerConfig, - targetIOSSdk, - targetAndroidNdkApi, - targetIOSVersion, - targetMacOSVersion, - supportedAssetTypes, - hook, - resourceIdentifiers, - buildResult, + final configBuilder = configCreator(); + configBuilder.setupHookConfig( + targetOS: targetOS, + supportedAssetTypes: supportedAssetTypes, + buildMode: buildMode, + packageName: package.name, + packageRoot: packageLayout.packageRoot(package.name), ); + if (hook == Hook.build) { + final metadata = {}; + _metadataForPackage( + packageGraph: packageGraph!, + packageName: package.name, + targetMetadata: globalMetadata, + )?.forEach((key, value) => metadata[key] = value); + (configBuilder as BuildConfigBuilder).setupBuildConfig( + dryRun: false, + linkingEnabled: linkingEnabled!, + metadata: metadata, + ); + } else { + (configBuilder as LinkConfigBuilder).setupLinkConfig( + assets: buildResult!.encodedAssetsForLinking[package.name] ?? []); + } - final (hookOutput, packageSuccess) = await _runHookForPackageCached( + final buildDirName = configBuilder.computeChecksum(); + final buildDirUri = + packageLayout.dartToolNativeAssetsBuilder.resolve('$buildDirName/'); + final outDirUri = buildDirUri.resolve('out/'); + final outDir = Directory.fromUri(outDirUri); + if (!await outDir.exists()) { + // TODO(https://dartbug.com/50565): Purge old or unused folders. + await outDir.create(recursive: true); + } + final outputDirectoryShared = packageLayout.dartToolNativeAssetsBuilder + .resolve('shared/${package.name}/$hook/'); + final outDirShared = Directory.fromUri(outputDirectoryShared); + if (!await outDirShared.exists()) { + // TODO(https://dartbug.com/50565): Purge old or unused folders. + await outDirShared.create(recursive: true); + } + + if (hook == Hook.link) { + File? resourcesFile; + if (resourceIdentifiers != null) { + resourcesFile = File.fromUri(buildDirUri.resolve('resources.json')); + await resourcesFile.create(); + await File.fromUri(resourceIdentifiers).copy(resourcesFile.path); + } + (configBuilder as LinkConfigBuilder).setupLinkRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outputDirectoryShared, + recordedUsesFile: resourcesFile?.uri, + ); + } else { + (configBuilder as BuildConfigBuilder).setupBuildRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outputDirectoryShared, + ); + } + + final config = hook == Hook.build + ? BuildConfig(configBuilder.json) + : LinkConfig(configBuilder.json); + final hookOutput = await _runHookForPackageCached( hook, config, validator, @@ -279,9 +260,16 @@ class NativeAssetsBuildRunner { resourceIdentifiers, packageLayout, ); - hookResult = hookResult.copyAdd(hookOutput, packageSuccess); - metadata[config.packageName] = hookOutput.metadata; + if (hookOutput == null) return null; + hookResult = hookResult.copyAdd(hookOutput); + if (hook == Hook.build) { + globalMetadata[package.name] = (hookOutput as BuildOutput).metadata; + } } + // We only perform application wide validation in the final result of + // building all assets (i.e. in the build step if linking is not enabled or + // in the link step if linking is enableD). + if (hook == Hook.build && linkingEnabled!) return hookResult; final errors = await applicationAssetValidator(hookResult.encodedAssets); if (errors.isEmpty) return hookResult; @@ -290,105 +278,7 @@ class NativeAssetsBuildRunner { for (final error in errors) { logger.severe('- $error'); } - return HookResult.failure(); - } - - static Future _cliConfig( - Package package, - PackageLayout packageLayout, - Target target, - BuildMode buildMode, - LinkModePreference linkModePreference, - DependencyMetadata? dependencyMetadata, - bool? linkingEnabled, - CCompilerConfig? cCompilerConfig, - IOSSdk? targetIOSSdk, - int? targetAndroidNdkApi, - int? targetIOSVersion, - int? targetMacOSVersion, - Iterable supportedAssetTypes, - Hook hook, - Uri? resourceIdentifiers, - BuildResult? buildResult, - ) async { - final buildDirName = HookConfigImpl.checksum( - packageName: package.name, - packageRoot: package.root, - targetOS: target.os, - targetArchitecture: target.architecture, - buildMode: buildMode, - linkModePreference: linkModePreference, - targetIOSSdk: targetIOSSdk, - cCompiler: cCompilerConfig, - dependencyMetadata: dependencyMetadata, - targetAndroidNdkApi: targetAndroidNdkApi, - supportedAssetTypes: supportedAssetTypes, - hook: hook, - linkingEnabled: linkingEnabled, - ); - final buildDirUri = - packageLayout.dartToolNativeAssetsBuilder.resolve('$buildDirName/'); - final outputDirectory = buildDirUri.resolve('out/'); - final outDir = Directory.fromUri(outputDirectory); - if (!await outDir.exists()) { - // TODO(https://dartbug.com/50565): Purge old or unused folders. - await outDir.create(recursive: true); - } - - final outputDirectoryShared = packageLayout.dartToolNativeAssetsBuilder - .resolve('shared/${package.name}/$hook/'); - final outDirShared = Directory.fromUri(outputDirectoryShared); - if (!await outDirShared.exists()) { - // TODO(https://dartbug.com/50565): Purge old or unused folders. - await outDirShared.create(recursive: true); - } - - if (hook == Hook.link) { - File? resourcesFile; - if (resourceIdentifiers != null) { - resourcesFile = File.fromUri(buildDirUri.resolve('resources.json')); - await resourcesFile.create(); - await File.fromUri(resourceIdentifiers).copy(resourcesFile.path); - } - - return LinkConfigImpl( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: package.name, - packageRoot: package.root, - targetOS: target.os, - targetArchitecture: target.architecture, - buildMode: buildMode, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - cCompiler: cCompilerConfig, - targetAndroidNdkApi: targetAndroidNdkApi, - recordedUsagesFile: resourcesFile?.uri, - encodedAssets: buildResult!.encodedAssetsForLinking[package.name] ?? [], - supportedAssetTypes: supportedAssetTypes, - linkModePreference: linkModePreference, - ); - } else { - return BuildConfigImpl( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: package.name, - packageRoot: package.root, - targetOS: target.os, - targetArchitecture: target.architecture, - buildMode: buildMode, - linkModePreference: linkModePreference, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - cCompiler: cCompilerConfig, - dependencyMetadata: dependencyMetadata, - linkingEnabled: linkingEnabled, - targetAndroidNdkApi: targetAndroidNdkApi, - supportedAssetTypes: supportedAssetTypes, - ); - } + return null; } /// [workingDirectory] is expected to contain `.dart_tool`. @@ -398,18 +288,43 @@ class NativeAssetsBuildRunner { /// /// If provided, only native assets of all transitive dependencies of /// [runPackageName] are built. - Future buildDryRun({ - required LinkModePreference linkModePreference, + Future buildDryRun({ + required BuildConfigCreator configCreator, + required BuildValidator buildValidator, required OS targetOS, required Uri workingDirectory, - required bool includeParentEnvironment, required bool linkingEnabled, - required BuildValidator buildValidator, + required bool includeParentEnvironment, PackageLayout? packageLayout, String? runPackageName, - required Iterable supportedAssetTypes, + required List supportedAssetTypes, + }) => + _runDryRun( + targetOS: targetOS, + configCreator: configCreator, + validator: (HookConfig config, HookOutput output) => + buildValidator(config as BuildConfig, output as BuildOutput), + workingDirectory: workingDirectory, + includeParentEnvironment: includeParentEnvironment, + packageLayout: packageLayout, + runPackageName: runPackageName, + supportedAssetTypes: supportedAssetTypes, + linkingEnabled: linkingEnabled, + ); + + Future _runDryRun({ + required BuildConfigCreator configCreator, + required _HookValidator validator, + required OS targetOS, + required Uri workingDirectory, + required bool includeParentEnvironment, + PackageLayout? packageLayout, + String? runPackageName, + required bool linkingEnabled, + required List supportedAssetTypes, }) async { const hook = Hook.build; + packageLayout ??= await PackageLayout.fromRootPackageRoot(workingDirectory); final (buildPlan, _, planSuccess) = await _makePlan( hook: hook, @@ -417,38 +332,67 @@ class NativeAssetsBuildRunner { runPackageName: runPackageName, ); if (!planSuccess) { - return HookResult.failure(); + return null; } var hookResult = HookResult(); for (final package in buildPlan) { - final config = await _cliConfigDryRun( - package: package, - packageName: package.name, - packageRoot: packageLayout.packageRoot(package.name), + final configBuilder = configCreator(); + configBuilder.setupHookConfig( targetOS: targetOS, - linkMode: linkModePreference, - buildParentDir: packageLayout.dartToolNativeAssetsBuilder, supportedAssetTypes: supportedAssetTypes, + buildMode: null, // not set in dry-run mode + packageName: package.name, + packageRoot: packageLayout.packageRoot(package.name), + ); + configBuilder.setupBuildConfig( + dryRun: true, linkingEnabled: linkingEnabled, + metadata: const {}, ); + + final buildDirName = configBuilder.computeChecksum(); + final buildDirUri = + packageLayout.dartToolNativeAssetsBuilder.resolve('$buildDirName/'); + final outDirUri = buildDirUri.resolve('out/'); + final outDir = Directory.fromUri(outDirUri); + if (!await outDir.exists()) { + // TODO(https://dartbug.com/50565): Purge old or unused folders. + await outDir.create(recursive: true); + } + final outputDirectoryShared = packageLayout.dartToolNativeAssetsBuilder + .resolve('shared/${package.name}/$hook/'); + final outDirShared = Directory.fromUri(outputDirectoryShared); + if (!await outDirShared.exists()) { + // TODO(https://dartbug.com/50565): Purge old or unused folders. + await outDirShared.create(recursive: true); + } + configBuilder.setupBuildRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outputDirectoryShared, + ); + + final config = hook == Hook.build + ? BuildConfig(configBuilder.json) + : LinkConfig(configBuilder.json); + final packageConfigUri = packageLayout.packageConfigUri; final ( compileSuccess, hookKernelFile, _, ) = await _compileHookForPackageCached( - config, + config.packageName, + config.outputDirectory, + config.packageRoot.resolve('hook/${hook.scriptName}'), packageConfigUri, workingDirectory, includeParentEnvironment, ); - if (!compileSuccess) { - hookResult.copyAdd(HookOutputImpl(), false); - continue; - } + if (!compileSuccess) return null; + // TODO(https://github.com/dart-lang/native/issues/1321): Should dry runs be cached? - final (buildOutput, packageSuccess) = await runUnderDirectoriesLock( + final buildOutput = await runUnderDirectoriesLock( [ Directory.fromUri(config.outputDirectoryShared.parent), Directory.fromUri(config.outputDirectory.parent), @@ -458,8 +402,7 @@ class NativeAssetsBuildRunner { () => _runHookForPackage( hook, config, - (HookConfig config, HookOutputImpl output) => - buildValidator(config as BuildConfig, output as BuildOutput), + validator, packageConfigUri, workingDirectory, includeParentEnvironment, @@ -468,14 +411,15 @@ class NativeAssetsBuildRunner { packageLayout!, ), ); - hookResult = hookResult.copyAdd(buildOutput, packageSuccess); + if (buildOutput == null) return null; + hookResult = hookResult.copyAdd(buildOutput); } return hookResult; } - Future<_PackageBuildRecord> _runHookForPackageCached( + Future _runHookForPackageCached( Hook hook, - HookConfigImpl config, + HookConfig config, _HookValidator validator, Uri packageConfigUri, Uri workingDirectory, @@ -497,20 +441,43 @@ class NativeAssetsBuildRunner { hookKernelFile, hookLastSourceChange, ) = await _compileHookForPackageCached( - config, + config.packageName, + config.outputDirectory, + config.packageRoot.resolve('hook/${hook.scriptName}'), packageConfigUri, workingDirectory, includeParentEnvironment, ); if (!compileSuccess) { - return (HookOutputImpl(), false); + return null; } - final hookOutput = HookOutputImpl.readFromFile(file: config.outputFile); - if (hookOutput != null) { - final lastBuilt = hookOutput.timestamp.roundDownToSeconds(); + final buildOutputFile = + File.fromUri(config.outputDirectory.resolve(hook.outputName)); + if (buildOutputFile.existsSync()) { + late final HookOutput output; + try { + final decode = + const Utf8Decoder().fuse(const JsonDecoder()).convert; + final hookOutputJson = decode(buildOutputFile.readAsBytesSync()) + as Map; + output = hook == Hook.build + ? BuildOutput(hookOutputJson) + : LinkOutput(hookOutputJson); + } on FormatException catch (e) { + logger.severe(''' +Building assets for package:${config.packageName} failed. +${hook.outputName} contained a format error. + +Contents: ${buildOutputFile.readAsStringSync()}. +${e.message} + '''); + return null; + } + + final lastBuilt = output.timestamp.roundDownToSeconds(); final dependenciesLastChange = - await hookOutput.dependenciesModel.lastModified(); + await Dependencies(output.dependencies).lastModified(); if (lastBuilt.isAfter(dependenciesLastChange) && lastBuilt.isAfter(hookLastSourceChange)) { logger.info( @@ -523,7 +490,7 @@ class NativeAssetsBuildRunner { ); // All build flags go into [outDir]. Therefore we do not have to // check here whether the config is equal. - return (hookOutput, true); + return output; } } @@ -542,9 +509,9 @@ class NativeAssetsBuildRunner { ); } - Future<_PackageBuildRecord> _runHookForPackage( + Future _runHookForPackage( Hook hook, - HookConfigImpl config, + HookConfig config, _HookValidator validator, Uri packageConfigUri, Uri workingDirectory, @@ -554,10 +521,12 @@ class NativeAssetsBuildRunner { PackageLayout packageLayout, ) async { final configFile = config.outputDirectory.resolve('../config.json'); - final configFileContents = config.toJsonString(); + final configFileContents = + const JsonEncoder.withIndent(' ').convert(config.json); logger.info('config.json contents: $configFileContents'); await File.fromUri(configFile).writeAsString(configFileContents); - final buildOutputFile = File.fromUri(config.outputFile); + final hookOutputFile = config.outputDirectory.resolve(hook.outputName); + final buildOutputFile = File.fromUri(hookOutputFile); if (await buildOutputFile.exists()) { // Ensure we'll never read outdated build results. await buildOutputFile.delete(); @@ -577,7 +546,7 @@ class NativeAssetsBuildRunner { includeParentEnvironment: includeParentEnvironment, ); - var success = true; + var deleteOutputIfExists = false; if (result.exitCode != 0) { final printWorkingDir = workingDirectory != Directory.current.uri; final commandString = [ @@ -588,8 +557,8 @@ class NativeAssetsBuildRunner { ].join(' '); logger.severe( ''' -Building native assets for package:${config.packageName} failed. -${config.script} returned with exit code: ${result.exitCode}. +Building assets for package:${config.packageName} failed. +${hook.scriptName} returned with exit code: ${result.exitCode}. To reproduce run: $commandString stderr: @@ -598,34 +567,40 @@ stdout: ${result.stdout} ''', ); - success = false; + deleteOutputIfExists = true; + return null; } try { - final output = HookOutputImpl.readFromFile(file: config.outputFile) ?? - HookOutputImpl(); + final decode = const Utf8Decoder().fuse(const JsonDecoder()).convert; + + final hookOutputJson = + decode(buildOutputFile.readAsBytesSync()) as Map; + final output = hook == Hook.build + ? BuildOutput(hookOutputJson) + : LinkOutput(hookOutputJson); 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 errors) { + logger.severe('- $error'); + } + deleteOutputIfExists = true; + return null; } - for (final error in errors) { - logger.severe('- $error'); - } - return (output, success); + return output; } on FormatException catch (e) { logger.severe(''' -Building native assets for package:${config.packageName} failed. -${config.outputName} contained a format error. +Building assets for package:${config.packageName} failed. +${hook.outputName} contained a format error. -Contents: ${File.fromUri(config.outputFile).readAsStringSync()}. +Contents: ${buildOutputFile.readAsStringSync()}. ${e.message} '''); - success = false; - return (HookOutputImpl(), false); + return null; } finally { - if (!success) { + if (deleteOutputIfExists) { if (await buildOutputFile.exists()) { await buildOutputFile.delete(); } @@ -646,21 +621,23 @@ ${e.message} /// one time too many, then not recompiling when recompilation should have /// happened. /// - /// It does not reuse the cached kernel for different [config]s, due to + /// It does not reuse the cached kernel for different configs due to /// reentrancy requirements. For more info see: /// https://github.com/dart-lang/native/issues/1319 Future<(bool success, File kernelFile, DateTime lastSourceChange)> _compileHookForPackageCached( - HookConfigImpl config, + String packageName, + Uri outputDirectory, + Uri scriptUri, Uri packageConfigUri, Uri workingDirectory, bool includeParentEnvironment, ) async { final kernelFile = File.fromUri( - config.outputDirectory.resolve('../hook.dill'), + outputDirectory.resolve('../hook.dill'), ); final depFile = File.fromUri( - config.outputDirectory.resolve('../hook.dill.d'), + outputDirectory.resolve('../hook.dill.d'), ); final bool mustCompile; final DateTime sourceLastChange; @@ -691,7 +668,8 @@ ${e.message} success = true; } else { success = await _compileHookForPackage( - config, + packageName, + scriptUri, packageConfigUri, workingDirectory, includeParentEnvironment, @@ -703,7 +681,8 @@ ${e.message} } Future _compileHookForPackage( - HookConfigImpl config, + String packageName, + Uri scriptUri, Uri packageConfigUri, Uri workingDirectory, bool includeParentEnvironment, @@ -716,7 +695,7 @@ ${e.message} '--packages=${packageConfigUri.toFilePath()}', '--output=${kernelFile.path}', '--depfile=${depFile.path}', - config.script.toFilePath(), + scriptUri.toFilePath(), ]; final compileResult = await runProcess( workingDirectory: workingDirectory, @@ -736,7 +715,7 @@ ${e.message} ].join(' '); logger.severe( ''' -Building native assets for package:${config.packageName} failed. +Building native assets for package:$packageName failed. Compilation of hook returned with exit code: ${compileResult.exitCode}. To reproduce run: $commandString @@ -751,54 +730,6 @@ ${compileResult.stdout} return success; } - static Future _cliConfigDryRun({ - required Package package, - required String packageName, - required Uri packageRoot, - required OS targetOS, - required LinkModePreference linkMode, - required Uri buildParentDir, - required Iterable supportedAssetTypes, - required bool? linkingEnabled, - }) async { - const hook = Hook.build; - final buildDirName = HookConfigImpl.checksumDryRun( - packageName: package.name, - packageRoot: package.root, - targetOS: targetOS, - linkModePreference: linkMode, - supportedAssetTypes: supportedAssetTypes, - hook: hook, - linkingEnabled: linkingEnabled, - ); - - final outDirUri = buildParentDir.resolve('$buildDirName/out/'); - final outDir = Directory.fromUri(outDirUri); - if (!await outDir.exists()) { - await outDir.create(recursive: true); - } - - // Shared between dry run and wet run. - final outputDirectoryShared = - buildParentDir.resolve('shared/${package.name}/$hook/out/'); - final outDirShared = Directory.fromUri(outputDirectoryShared); - if (!await outDirShared.exists()) { - // TODO(https://dartbug.com/50565): Purge old or unused folders. - await outDirShared.create(recursive: true); - } - - return BuildConfigImpl.dryRun( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRoot, - targetOS: targetOS, - linkModePreference: linkMode, - supportedAssetTypes: supportedAssetTypes, - linkingEnabled: linkingEnabled, - ); - } - DependencyMetadata? _metadataForPackage({ required PackageGraph packageGraph, required String packageName, @@ -815,17 +746,19 @@ ${compileResult.stdout} } Future _validate( - HookConfigImpl config, - HookOutputImpl output, + HookConfig config, + HookOutput output, PackageLayout packageLayout, _HookValidator validator, ) async { - final errors = config is BuildConfigImpl - ? await validateBuildOutput(config, output) - : await validateLinkOutput(config as LinkConfig, output); + final errors = config is BuildConfig + ? await validateBuildOutput(config, output as BuildOutput) + : await validateLinkOutput(config as LinkConfig, output as LinkOutput); errors.addAll(await validator(config, output)); - if (config is BuildConfigImpl) { + if (config is BuildConfig) { + // XXX + output as BuildOutput; final packagesWithLink = (await packageLayout.packagesWithAssets(Hook.link)) .map((p) => p.name); @@ -899,8 +832,6 @@ ${compileResult.stdout} } } -typedef _PackageBuildRecord = (HookOutputImpl, bool success); - extension on DateTime { DateTime roundDownToSeconds() => DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch - 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 6a37448c5..9863d2ba1 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 @@ -4,19 +4,12 @@ import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import '../../native_assets_builder.dart'; - /// The result of executing the build hooks in dry run mode from all packages in /// 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 encodedAssets; - /// Whether all hooks completed without errors. - /// - /// All error messages are streamed to [NativeAssetsBuildRunner.logger]. - bool get success; - /// The native assets produced by the hooks, which should be linked. 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 96b2aac2d..a857e7622 100644 --- a/pkgs/native_assets_builder/lib/src/model/build_result.dart +++ b/pkgs/native_assets_builder/lib/src/model/build_result.dart @@ -4,19 +4,12 @@ import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import '../build_runner/build_runner.dart'; - /// The result of executing build hooks from all packages in the dependency tree /// of the entry point application. abstract class BuildResult { /// The files used by the hooks. List get dependencies; - /// Whether all hooks completed without errors. - /// - /// All error messages are streamed to [NativeAssetsBuildRunner.logger]. - bool get success; - /// The native assets produced by the hooks, which should be bundled. List get encodedAssets; 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 52ec4d98f..033d635db 100644 --- a/pkgs/native_assets_builder/lib/src/model/hook_result.dart +++ b/pkgs/native_assets_builder/lib/src/model/hook_result.dart @@ -22,61 +22,48 @@ final class HookResult implements BuildResult, BuildDryRunResult, LinkResult { @override final List dependencies; - /// Whether all hooks completed without errors. - /// - /// All error messages are streamed to [NativeAssetsBuildRunner.logger]. - @override - final bool success; - HookResult._({ required this.encodedAssets, required this.encodedAssetsForLinking, required this.dependencies, - required this.success, }); factory HookResult({ List? encodedAssets, Map>? encodedAssetsForLinking, List? dependencies, - bool success = true, }) => HookResult._( encodedAssets: encodedAssets ?? [], encodedAssetsForLinking: encodedAssetsForLinking ?? {}, dependencies: dependencies ?? [], - success: success, ); - factory HookResult.failure() => HookResult(success: false); - - HookResult copyAdd(HookOutputImpl hookOutput, bool hookSuccess) { - final mergedMaps = - mergeMaps(encodedAssetsForLinking, hookOutput.encodedAssetsForLinking, - value: (encodedAssets1, encodedAssets2) => [ - ...encodedAssets1, - ...encodedAssets2, - ]); + HookResult copyAdd(HookOutput hookOutput) { + final mergedMaps = mergeMaps( + encodedAssetsForLinking, + hookOutput is BuildOutput + ? hookOutput.encodedAssetsForLinking + : >{}, + value: (encodedAssets1, encodedAssets2) => [ + ...encodedAssets1, + ...encodedAssets2, + ]); + final hookOutputAssets = (hookOutput is BuildOutput) + ? hookOutput.encodedAssets + : (hookOutput as LinkOutput).encodedAssets; return HookResult( encodedAssets: [ ...encodedAssets, - ...hookOutput.encodedAssets, + ...hookOutputAssets, ], encodedAssetsForLinking: mergedMaps, dependencies: [ ...dependencies, ...hookOutput.dependencies, ]..sort(_uriCompare), - success: success && hookSuccess, ); } - - HookResult withSuccess(bool success) => HookResult( - encodedAssets: encodedAssets, - encodedAssetsForLinking: encodedAssetsForLinking, - dependencies: dependencies, - success: success, - ); } int _uriCompare(Uri u1, Uri u2) => u1.toString().compareTo(u2.toString()); 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 7b72ef951..b2bb578a4 100644 --- a/pkgs/native_assets_builder/lib/src/model/link_result.dart +++ b/pkgs/native_assets_builder/lib/src/model/link_result.dart @@ -4,8 +4,6 @@ import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import '../build_runner/build_runner.dart'; - /// The result of executing the link hooks in dry run mode from all packages in /// the dependency tree of the entry point application. abstract interface class LinkResult { @@ -15,9 +13,4 @@ abstract interface class LinkResult { /// The files used by the hooks. List get dependencies; - - /// Whether all hooks completed without errors. - /// - /// All error messages are streamed to [NativeAssetsBuildRunner.logger]. - bool get success; } diff --git a/pkgs/native_assets_builder/test/build_runner/build_dependencies_test.dart b/pkgs/native_assets_builder/test/build_runner/build_dependencies_test.dart index e5616093b..98eec9cf5 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,7 +26,7 @@ void main() async { // Trigger a build, should invoke build for libraries with native assets. { final logMessages = []; - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, @@ -34,7 +34,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect( logMessages.join('\n'), stringContainsInOrder( 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 5f75a90ea..59e606757 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 @@ -32,7 +32,7 @@ void main() async { applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); - expect(result.success, false); + expect(result, isNull); expect( fullLog, contains('does not start with "package:wrong_namespace_asset/"'), @@ -66,7 +66,7 @@ void main() async { buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, ); - expect(result.success, true); + expect(result, isNotNull); } }); }); 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 9e3fa7c4f..096f00bc9 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 @@ -23,22 +23,22 @@ void main() async { logger: logger, ); - final dryRunResult = await buildDryRun( + final dryRunResult = (await buildDryRun( packageUri, logger, dartExecutable, linkingEnabled: false, supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, - ); - final buildResult = await build( + ))!; + final buildResult = (await build( packageUri, logger, dartExecutable, supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect(dryRunResult.encodedAssets.length, 1); expect(buildResult.encodedAssets.length, 1); 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 827b9d0ea..b81987c16 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 @@ -38,7 +38,7 @@ void main() async { applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); - expect(result.success, false); + expect(result, isNull); if (package == 'wrong_build_output_3') { // Should re-execute the process on second run. expect( 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 c3ca4d278..9034149ee 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 @@ -28,7 +28,7 @@ void main() async { { final logMessages = []; - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, @@ -36,7 +36,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect( logMessages.join('\n'), contains( @@ -54,7 +54,7 @@ void main() async { { final logMessages = []; - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, @@ -62,7 +62,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect( logMessages.join('\n'), contains('Skipping build for native_add'), @@ -99,14 +99,14 @@ void main() async { await Future.delayed(const Duration(seconds: 1)); { - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; await expectSymbols( asset: CodeAsset.fromEncoded(result.encodedAssets.single), symbols: ['add']); @@ -118,14 +118,14 @@ void main() async { ); { - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; await expectSymbols( asset: CodeAsset.fromEncoded(result.encodedAssets.single), symbols: ['add', 'subtract'], @@ -152,14 +152,14 @@ void main() async { // cached. await Future.delayed(const Duration(seconds: 1)); - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; { final compiledHook = logMessages .where((m) => @@ -179,14 +179,14 @@ void main() async { targetUri: packageUri); { - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; { final compiledHook = logMessages .where((m) => 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 746cc2701..75321d0b5 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 @@ -31,7 +31,7 @@ void main() async { buildValidator: (config, output) async => [], ); final fullLog = logMessages.join('\n'); - expect(result.success, false); + expect(result, isNull); expect( fullLog, contains( @@ -52,7 +52,7 @@ void main() async { applicationAssetValidator: (_) async => [], ); final fullLog = logMessages.join('\n'); - expect(result.success, false); + expect(result, isNull); expect( fullLog, contains( 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 f973b9c90..198ba4252 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 @@ -24,14 +24,14 @@ void main() async { ); { - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect(result.encodedAssets.length, 1); await expectSymbols( asset: CodeAsset.fromEncoded(result.encodedAssets.single), @@ -60,7 +60,7 @@ void main() async { applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); - expect(result.success, false); + expect(result, isNull); expect(fullLog, contains('To reproduce run:')); final reproCommand = fullLog .split('\n') @@ -78,14 +78,14 @@ void main() async { ); { - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect(result.encodedAssets.length, 1); await expectSymbols( asset: CodeAsset.fromEncoded(result.encodedAssets.single), 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 c34013ae3..3799ed120 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 @@ -24,7 +24,7 @@ void main() async { { final logMessages = []; - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, @@ -33,14 +33,14 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect(result.encodedAssets, isEmpty); expect(result.dependencies, isEmpty); } { final logMessages = []; - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, @@ -49,7 +49,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; 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 a19cffb95..c1e091691 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 @@ -27,9 +27,15 @@ void main() async { dartExecutable: dartExecutable, ); + BuildConfigBuilder configCreator() => BuildConfigBuilder() + ..setupCodeConfig( + targetArchitecture: Architecture.current, + linkModePreference: LinkModePreference.dynamic, + ); + await buildRunner.buildDryRun( + configCreator: configCreator, targetOS: Target.current.os, - linkModePreference: LinkModePreference.dynamic, workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, @@ -37,8 +43,8 @@ void main() async { buildValidator: (config, output) async => [], ); await buildRunner.buildDryRun( + configCreator: configCreator, targetOS: Target.current.os, - linkModePreference: LinkModePreference.dynamic, workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, @@ -46,9 +52,9 @@ void main() async { buildValidator: (config, output) async => [], ); await buildRunner.build( + configCreator: configCreator, + targetOS: OS.current, buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - target: Target.current, workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, @@ -57,9 +63,9 @@ void main() async { applicationAssetValidator: (_) async => [], ); await buildRunner.build( + configCreator: configCreator, buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - target: Target.current, + targetOS: OS.current, workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, 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 e5b0ecf49..4443c2a31 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 @@ -46,7 +46,7 @@ void main() async { printOnFailure('cc: $cc'); - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, @@ -63,7 +63,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; 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 9c635ac51..aba3ba9ea 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,7 +32,7 @@ void main() async { // Trigger a build, should invoke build for libraries with native assets. { final logMessages = []; - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, @@ -40,7 +40,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect( logMessages.join('\n'), stringContainsInOrder([ @@ -57,7 +57,7 @@ void main() async { packageLayout = await PackageLayout.fromRootPackageRoot(packageUri); } final logMessages = []; - final result = await build( + final result = (await build( packageUri, logger, dartExecutable, @@ -66,7 +66,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect( false, logMessages.join('\n').contains( 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 bafbc2d5e..0f8f90ba1 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 @@ -20,19 +20,23 @@ void main(List args) async { logger: logger, dartExecutable: dartExecutable, ).build( + configCreator: () => BuildConfigBuilder() + ..setupCodeConfig( + targetArchitecture: target.architecture, + linkModePreference: LinkModePreference.dynamic, + targetAndroidNdkApi: target.os == OS.android ? 30 : null, + ), buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - target: target, + targetOS: target.os, workingDirectory: packageUri, includeParentEnvironment: true, 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) { + if (result == null) { throw Error(); } print('done'); 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 32ac611a6..47c80ddba 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 @@ -24,9 +24,13 @@ void main(List args) async { dartExecutable: dartExecutable, singleHookTimeout: timeout, ).build( + configCreator: () => BuildConfigBuilder() + ..setupCodeConfig( + targetArchitecture: Architecture.current, + linkModePreference: LinkModePreference.dynamic, + ), buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - target: Target.current, + targetOS: OS.current, workingDirectory: packageUri, includeParentEnvironment: true, linkingEnabled: false, @@ -37,7 +41,7 @@ void main(List args) async { ], applicationAssetValidator: validateCodeAssetsInApplication, ); - if (!result.success) { + if (result == null) { throw Error(); } print('done'); 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 78d4ddca8..c0eecc0ec 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 @@ -32,7 +32,7 @@ void main() async { applicationAssetValidator: validateCodeAssetsInApplication, ); final fullLog = logMessages.join('\n'); - expect(result.success, false); + expect(result, isNull); expect( fullLog, contains('Duplicate dynamic library file name'), @@ -52,7 +52,7 @@ void main() async { logger: logger, ); - final buildResult = await build( + final buildResult = (await build( packageUri, logger, linkingEnabled: true, @@ -60,8 +60,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); - expect(buildResult.success, isTrue); + ))!; final linkResult = await link( packageUri, @@ -73,7 +72,7 @@ void main() async { applicationAssetValidator: validateCodeAssetsInApplication, ); // Application validation error due to conflicting dylib name. - expect(linkResult.success, isFalse); + expect(linkResult, isNull); }); }); } 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 021a3d2fd..b2c3b85b9 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 @@ -86,11 +86,11 @@ void main() async { ); final fullLog = logMessages.join('\n'); if (hook == 'build') { - expect(buildResult.success, success); + expect(buildResult, success ? isNotNull : isNull); } else { assert(hook == 'link'); - expect(buildResult.success, true); - expect(linkResult.success, success); + expect(buildResult, isNotNull); + expect(linkResult, success ? isNotNull : isNull); } if (!success) { expect( diff --git a/pkgs/native_assets_builder/test/build_runner/helpers.dart b/pkgs/native_assets_builder/test/build_runner/helpers.dart index daaa8f8b1..b40825943 100644 --- a/pkgs/native_assets_builder/test/build_runner/helpers.dart +++ b/pkgs/native_assets_builder/test/build_runner/helpers.dart @@ -7,7 +7,6 @@ import 'dart:io'; 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:test/test.dart'; import '../helpers.dart'; @@ -29,7 +28,7 @@ Future runPubGet({ expect(result.exitCode, 0); } -Future build( +Future build( Uri packageUri, Logger logger, Uri dartExecutable, { @@ -47,32 +46,36 @@ Future build( int? targetAndroidNdkApi, Target? target, bool linkingEnabled = false, - required Iterable supportedAssetTypes, + required List supportedAssetTypes, }) async => await runWithLog(capturedLogs, () async { final result = await NativeAssetsBuildRunner( logger: logger, dartExecutable: dartExecutable, ).build( + configCreator: () => BuildConfigBuilder() + ..setupCodeConfig( + targetArchitecture: target?.architecture ?? Architecture.current, + linkModePreference: linkModePreference, + cCompilerConfig: cCompilerConfig, + targetIOSSdk: targetIOSSdk, + targetIOSVersion: targetIOSVersion, + targetMacOSVersion: targetMacOSVersion, + targetAndroidNdkApi: targetAndroidNdkApi, + ), buildMode: BuildMode.release, - linkModePreference: linkModePreference, - target: target ?? Target.current, + targetOS: target?.os ?? OS.current, workingDirectory: packageUri, - cCompilerConfig: cCompilerConfig, includeParentEnvironment: includeParentEnvironment, packageLayout: packageLayout, runPackageName: runPackageName, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - targetAndroidNdkApi: targetAndroidNdkApi, linkingEnabled: linkingEnabled, supportedAssetTypes: supportedAssetTypes, buildValidator: buildValidator, applicationAssetValidator: applicationAssetValidator, ); - if (result.success) { + if (result != null) { expect(await result.encodedAssets.allExist(), true); for (final encodedAssetsForLinking in result.encodedAssetsForLinking.values) { @@ -83,7 +86,7 @@ Future build( return result; }); -Future link( +Future link( Uri packageUri, Logger logger, Uri dartExecutable, { @@ -101,39 +104,43 @@ Future link( int? targetMacOSVersion, int? targetAndroidNdkApi, Target? target, - required Iterable supportedAssetTypes, + required List supportedAssetTypes, }) async => await runWithLog(capturedLogs, () async { final result = await NativeAssetsBuildRunner( logger: logger, dartExecutable: dartExecutable, ).link( - linkModePreference: linkModePreference, + configCreator: () => LinkConfigBuilder() + ..setupCodeConfig( + targetArchitecture: target?.architecture ?? Architecture.current, + linkModePreference: linkModePreference, + cCompilerConfig: cCompilerConfig, + targetIOSSdk: targetIOSSdk, + targetIOSVersion: targetIOSVersion, + targetMacOSVersion: targetMacOSVersion, + targetAndroidNdkApi: targetAndroidNdkApi, + ), buildMode: BuildMode.release, - target: target ?? Target.current, + targetOS: target?.os ?? OS.current, workingDirectory: packageUri, - cCompilerConfig: cCompilerConfig, includeParentEnvironment: includeParentEnvironment, packageLayout: packageLayout, buildResult: buildResult, resourceIdentifiers: resourceIdentifiers, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - targetAndroidNdkApi: targetAndroidNdkApi, supportedAssetTypes: supportedAssetTypes, linkValidator: linkValidator, applicationAssetValidator: applicationAssetValidator, ); - if (result.success) { + if (result != null) { expect(await result.encodedAssets.allExist(), true); } return result; }); -Future<(BuildResult, LinkResult)> buildAndLink( +Future<(BuildResult?, LinkResult?)> buildAndLink( Uri packageUri, Logger logger, Uri dartExecutable, { @@ -152,7 +159,7 @@ Future<(BuildResult, LinkResult)> buildAndLink( int? targetAndroidNdkApi, Target? target, Uri? resourceIdentifiers, - required Iterable supportedAssetTypes, + required List supportedAssetTypes, }) async => await runWithLog(capturedLogs, () async { final buildRunner = NativeAssetsBuildRunner( @@ -160,26 +167,30 @@ Future<(BuildResult, LinkResult)> buildAndLink( dartExecutable: dartExecutable, ); final buildResult = await buildRunner.build( + configCreator: () => BuildConfigBuilder() + ..setupCodeConfig( + targetArchitecture: target?.architecture ?? Architecture.current, + linkModePreference: linkModePreference, + cCompilerConfig: cCompilerConfig, + targetIOSSdk: targetIOSSdk, + targetIOSVersion: targetIOSVersion, + targetMacOSVersion: targetMacOSVersion, + targetAndroidNdkApi: targetAndroidNdkApi, + ), buildMode: BuildMode.release, - linkModePreference: linkModePreference, - target: target ?? Target.current, + targetOS: target?.os ?? OS.current, workingDirectory: packageUri, - cCompilerConfig: cCompilerConfig, includeParentEnvironment: includeParentEnvironment, packageLayout: packageLayout, runPackageName: runPackageName, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - targetAndroidNdkApi: targetAndroidNdkApi, linkingEnabled: true, supportedAssetTypes: supportedAssetTypes, buildValidator: buildValidator, applicationAssetValidator: applicationAssetValidator, ); - if (!buildResult.success) { - return (buildResult, HookResult()); + if (buildResult == null) { + return (null, null); } expect(await buildResult.encodedAssets.allExist(), true); @@ -189,26 +200,30 @@ Future<(BuildResult, LinkResult)> buildAndLink( } final linkResult = await buildRunner.link( - linkModePreference: linkModePreference, + configCreator: () => LinkConfigBuilder() + ..setupCodeConfig( + targetArchitecture: target?.architecture ?? Architecture.current, + linkModePreference: linkModePreference, + cCompilerConfig: cCompilerConfig, + targetIOSSdk: targetIOSSdk, + targetIOSVersion: targetIOSVersion, + targetMacOSVersion: targetMacOSVersion, + targetAndroidNdkApi: targetAndroidNdkApi, + ), buildMode: BuildMode.release, - target: target ?? Target.current, + targetOS: target?.os ?? OS.current, workingDirectory: packageUri, - cCompilerConfig: cCompilerConfig, includeParentEnvironment: includeParentEnvironment, packageLayout: packageLayout, buildResult: buildResult, resourceIdentifiers: resourceIdentifiers, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - targetAndroidNdkApi: targetAndroidNdkApi, supportedAssetTypes: supportedAssetTypes, linkValidator: linkValidator, applicationAssetValidator: applicationAssetValidator, ); - if (linkResult.success) { - expect(await buildResult.encodedAssets.allExist(), true); + if (linkResult != null) { + expect(await linkResult.encodedAssets.allExist(), true); } return (buildResult, linkResult); @@ -233,7 +248,7 @@ Future runWithLog( return result; } -Future buildDryRun( +Future buildDryRun( Uri packageUri, Logger logger, Uri dartExecutable, { @@ -244,14 +259,18 @@ Future buildDryRun( List? capturedLogs, PackageLayout? packageLayout, required bool linkingEnabled, - required Iterable supportedAssetTypes, + required List supportedAssetTypes, }) async => runWithLog(capturedLogs, () async { final result = await NativeAssetsBuildRunner( logger: logger, dartExecutable: dartExecutable, ).buildDryRun( - linkModePreference: linkModePreference, + configCreator: () => BuildConfigBuilder() + ..setupCodeConfig( + targetArchitecture: null, + linkModePreference: linkModePreference, + ), targetOS: Target.current.os, workingDirectory: packageUri, includeParentEnvironment: includeParentEnvironment, 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 f43429f8e..21f20657c 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 @@ -33,7 +33,7 @@ void main() async { late LinkResult linkResult; Future runBuild() async { logMessages.clear(); - buildResult = await build( + buildResult = (await build( packageUri, logger, dartExecutable, @@ -42,12 +42,12 @@ void main() async { capturedLogs: logMessages, buildValidator: validateDataAssetBuildOutput, applicationAssetValidator: (_) async => [], - ); + ))!; } Future runLink() async { logMessages.clear(); - linkResult = await link( + linkResult = (await link( packageUri, logger, dartExecutable, @@ -56,11 +56,11 @@ void main() async { capturedLogs: logMessages, linkValidator: validateDataAssetLinkOutput, applicationAssetValidator: (_) async => [], - ); + ))!; } await runBuild(); - expect(buildResult.success, isTrue); + expect(buildResult, isNotNull); expect( logMessages.join('\n'), stringContainsInOrder([ @@ -74,7 +74,7 @@ void main() async { ); await runLink(); - expect(linkResult.success, isTrue); + expect(linkResult, isNotNull); expect( logMessages.join('\n'), stringContainsInOrder([ @@ -88,14 +88,14 @@ void main() async { ); await runBuild(); - expect(buildResult.success, isTrue); + expect(buildResult, isNotNull); expect( logMessages.join('\n'), contains('Skipping build for $packageName'), ); await runLink(); - expect(linkResult.success, isTrue); + expect(linkResult, isNotNull); expect( logMessages.join('\n'), contains('Skipping link for $packageName'), @@ -110,28 +110,28 @@ void main() async { await Future.delayed(const Duration(seconds: 1)); await runBuild(); - expect(buildResult.success, isTrue); + expect(buildResult, isNotNull); expect( logMessages.join('\n'), stringContainsInOrder(['Running', 'hook.dill']), ); await runLink(); - expect(linkResult.success, isTrue); + expect(linkResult, isNotNull); expect( logMessages.join('\n'), stringContainsInOrder(['Running', 'hook.dill']), ); await runBuild(); - expect(buildResult.success, isTrue); + expect(buildResult, isNotNull); expect( logMessages.join('\n'), contains('Skipping build for $packageName'), ); await runLink(); - expect(linkResult.success, isTrue); + expect(linkResult, isNotNull); expect( logMessages.join('\n'), contains('Skipping link for $packageName'), 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 cd261d578..c5006bc31 100644 --- a/pkgs/native_assets_builder/test/build_runner/link_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/link_test.dart @@ -26,7 +26,7 @@ void main() async { logger: logger, ); - final buildResult = await build( + final buildResult = (await build( packageUri, logger, dartExecutable, @@ -34,10 +34,10 @@ void main() async { supportedAssetTypes: [DataAsset.type], buildValidator: validateDataAssetBuildOutput, applicationAssetValidator: (_) async => [], - ); + ))!; expect(buildResult.encodedAssets.length, 0); - final linkResult = await link( + final linkResult = (await link( packageUri, logger, dartExecutable, @@ -45,10 +45,10 @@ void main() async { supportedAssetTypes: [DataAsset.type], linkValidator: validateDataAssetLinkOutput, applicationAssetValidator: (_) async => [], - ); + ))!; expect(linkResult.encodedAssets.length, 2); - final buildNoLinkResult = await build( + final buildNoLinkResult = (await build( packageUri, logger, dartExecutable, @@ -56,7 +56,7 @@ void main() async { supportedAssetTypes: [DataAsset.type], buildValidator: validateDataAssetBuildOutput, applicationAssetValidator: (_) async => [], - ); + ))!; expect(buildNoLinkResult.encodedAssets.length, 4); }); }, @@ -102,8 +102,8 @@ void main() async { buildValidator: validateDataAssetBuildOutput, applicationAssetValidator: (_) async => [], ); - expect(buildResult.success, true); - expect(_getNames(buildResult.encodedAssets), + expect(buildResult, isNotNull); + expect(_getNames(buildResult!.encodedAssets), unorderedEquals(builtHelperAssets)); expect( _getNames(buildResult.encodedAssetsForLinking['complex_link']!), @@ -119,9 +119,9 @@ void main() async { linkValidator: validateDataAssetLinkOutput, applicationAssetValidator: (_) async => [], ); - expect(linkResult.success, true); + expect(linkResult, isNotNull); - expect(_getNames(linkResult.encodedAssets), + expect(_getNames(linkResult!.encodedAssets), unorderedEquals([...builtHelperAssets, ...linkedAssets])); }); }, @@ -138,7 +138,7 @@ void main() async { logger: logger, ); - final buildResult = await build( + final buildResult = (await build( packageUri, logger, dartExecutable, @@ -146,12 +146,12 @@ void main() async { supportedAssetTypes: [DataAsset.type], buildValidator: validateDataAssetBuildOutput, applicationAssetValidator: (_) async => [], - ); + ))!; expect(buildResult.encodedAssets.length, 0); expect(buildResult.encodedAssetsForLinking.length, 0); final logMessages = []; - final linkResult = await link( + final linkResult = (await link( packageUri, logger, dartExecutable, @@ -160,7 +160,7 @@ void main() async { supportedAssetTypes: [DataAsset.type], linkValidator: validateDataAssetLinkOutput, applicationAssetValidator: (_) async => [], - ); + ))!; expect(linkResult.encodedAssets.length, 0); expect( logMessages, @@ -191,7 +191,7 @@ void main() async { logger: logger, ); - final buildResult = await build( + final buildResult = (await build( packageUri, logger, dartExecutable, @@ -199,12 +199,12 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect(buildResult.encodedAssets.length, 0); expect(buildResult.encodedAssetsForLinking.length, 1); final logMessages = []; - final linkResult = await link( + final linkResult = (await link( packageUri, logger, dartExecutable, @@ -213,7 +213,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], linkValidator: validateCodeAssetLinkOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; expect(linkResult.encodedAssets.length, 1); expect(linkResult.encodedAssets.first.type, CodeAsset.type); }); 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 55b20dba4..ae62b4295 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 @@ -21,7 +21,7 @@ void main() async { logger: logger, ); - final resultDynamic = await build( + final resultDynamic = (await build( packageUri, logger, dartExecutable, @@ -29,9 +29,9 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; - final resultPreferDynamic = await build( + final resultPreferDynamic = (await build( packageUri, logger, dartExecutable, @@ -39,9 +39,9 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; - final resultStatic = await build( + final resultStatic = (await build( packageUri, logger, dartExecutable, @@ -49,9 +49,9 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; - final resultPreferStatic = await build( + final resultPreferStatic = (await build( packageUri, logger, dartExecutable, @@ -59,7 +59,7 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, - ); + ))!; // This package honors preferences. expect( 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 14b80af78..3ae036471 100644 --- a/pkgs/native_assets_builder/test/build_runner/resources_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/resources_test.dart @@ -29,7 +29,7 @@ void main() async { logger: logger, ); - final buildResult = await build( + final buildResult = (await build( packageUri, logger, dartExecutable, @@ -37,7 +37,7 @@ void main() async { supportedAssetTypes: [DataAsset.type], buildValidator: validateDataAssetBuildOutput, applicationAssetValidator: (_) async => [], - ); + ))!; Iterable buildFiles() => Directory.fromUri( packageUri.resolve('.dart_tool/native_assets_builder/')) 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 01e637f5a..0f776dd47 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 @@ -30,9 +30,10 @@ void main() async { supportedAssetTypes: [CodeAsset.type], buildValidator: validateCodeAssetBuildOutput, applicationAssetValidator: validateCodeAssetsInApplication, + linkingEnabled: true, ); final fullLog = logMessages.join('\n'); - expect(result.success, false); + expect(result, isNull); expect( fullLog, contains('but that package does not have a link hook'), 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 fbf4947d2..5865d93fb 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 @@ -32,25 +32,26 @@ void main() async { final testPackageUri = testDataUri.resolve('$name/'); final dartUri = Uri.file(Platform.resolvedExecutable); - final config = BuildConfigImpl( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: name, - packageRoot: testPackageUri, - targetOS: OS.current, - version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreference.dynamic, - dryRun: dryRun, - linkingEnabled: false, - targetArchitecture: dryRun ? null : Architecture.current, - buildMode: dryRun ? null : BuildMode.debug, - cCompiler: dryRun ? null : cCompiler, - supportedAssetTypes: [CodeAsset.type], - ); + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageName: name, + packageRoot: testPackageUri, + targetOS: OS.current, + supportedAssetTypes: [CodeAsset.type], + buildMode: dryRun ? null : BuildMode.debug) + ..setupBuildConfig(dryRun: dryRun, linkingEnabled: false) + ..setupBuildRunConfig( + outputDirectory: outputDirectory, + outputDirectoryShared: outputDirectoryShared) + ..setupCodeConfig( + targetArchitecture: dryRun ? null : Architecture.current, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: dryRun ? null : cCompiler, + ); final buildConfigUri = testTempUri.resolve('build_config.json'); File.fromUri(buildConfigUri) - .writeAsStringSync(jsonEncode(config.toJson())); + .writeAsStringSync(jsonEncode(configBuilder.json)); final processResult = await Process.run( dartUri.toFilePath(), @@ -68,8 +69,9 @@ void main() async { expect(processResult.exitCode, 0); final buildOutputUri = outputDirectory.resolve('build_output.json'); - final buildOutput = HookOutputImpl.fromJsonString( - await File.fromUri(buildOutputUri).readAsString()); + final buildOutput = BuildOutput( + json.decode(await File.fromUri(buildOutputUri).readAsString()) + as Map); final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { 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 334fa8724..acd50699b 100644 --- a/pkgs/native_assets_builder/test/test_data/transformer_test.dart +++ b/pkgs/native_assets_builder/test/test_data/transformer_test.dart @@ -45,24 +45,25 @@ void main() async { late BuildOutput output; Future runBuild(Architecture architecture) async { - final config = BuildConfigImpl( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageUri, - targetOS: OS.current, - version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreference.dynamic, - dryRun: false, - linkingEnabled: false, - targetArchitecture: architecture, - buildMode: BuildMode.debug, - supportedAssetTypes: [DataAsset.type], - ); + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageName: packageName, + packageRoot: packageUri, + targetOS: OS.current, + supportedAssetTypes: [DataAsset.type], + buildMode: BuildMode.debug) + ..setupBuildConfig(dryRun: false, linkingEnabled: false) + ..setupBuildRunConfig( + outputDirectory: outputDirectory, + outputDirectoryShared: outputDirectoryShared) + ..setupCodeConfig( + targetArchitecture: architecture, + linkModePreference: LinkModePreference.dynamic, + ); final buildConfigUri = testTempUri.resolve('build_config.json'); - await File.fromUri(buildConfigUri) - .writeAsString(jsonEncode(config.toJson())); + File.fromUri(buildConfigUri) + .writeAsStringSync(jsonEncode(configBuilder.json)); final processResult = await Process.run( dartUri.toFilePath(), @@ -81,8 +82,9 @@ void main() async { stdout = processResult.stdout as String; final buildOutputUri = outputDirectory.resolve('build_output.json'); - output = HookOutputImpl.fromJsonString( - await File.fromUri(buildOutputUri).readAsString()); + output = BuildOutput( + json.decode(await File.fromUri(buildOutputUri).readAsString()) + as Map); } await runBuild(Architecture.x64); @@ -94,7 +96,7 @@ void main() async { ]), ); expect( - output.dataAssets.all, + output.dataAssets, contains( DataAsset( file: outputDirectoryShared.resolve('data_transformed0.json'), 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 fb9921e47..3dfcb16c7 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 @@ -6,13 +6,14 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List arguments) async { await link(arguments, (config, output) async { - print(''' -Received ${config.codeAssets.all.length} encodedAssets: ${config.codeAssets.all.map((e) => e.id)}. -'''); - output.codeAssets.addAll( - config.codeAssets.all.where((asset) => asset.id.endsWith('add'))); - print(''' -Keeping only ${output.codeAssets.all.map((e) => e.id)}. -'''); + for (final codeAsset in config.codeAssets.all) { + print('Got code asset: ${codeAsset.id}'); + if (codeAsset.id.endsWith('add')) { + output.codeAssets.add(codeAsset); + print('-> Keeping ${codeAsset.id}'); + } else { + print('-> Dropping ${codeAsset.id}'); + } + } }); } diff --git a/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version/hook/build.dart b/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version/hook/build.dart index 73ef7a438..1f9200207 100644 --- a/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version/hook/build.dart @@ -15,14 +15,15 @@ const minMacOSVersionForThisPackage = 13; void main(List arguments) async { await build(arguments, (config, output) async { if (config.targetOS == OS.android) { - if (config.targetAndroidNdkApi! < minNdkApiVersionForThisPackage) { + if (config.codeConfig.targetAndroidNdkApi! < + minNdkApiVersionForThisPackage) { throw UnsupportedError( 'The native assets for this package require at ' 'least Android NDK API level $minNdkApiVersionForThisPackage.', ); } } else if (config.targetOS == OS.iOS) { - final iosVersion = config.targetIOSVersion; + final iosVersion = config.codeConfig.targetIOSVersion; // iosVersion is nullable to deal with version skew. if (iosVersion != null && iosVersion < minIosVersionForThisPackage) { throw UnsupportedError( @@ -31,7 +32,7 @@ void main(List arguments) async { ); } } else if (config.targetOS == OS.macOS) { - final macosVersion = config.targetMacOSVersion; + final macosVersion = config.codeConfig.targetMacOSVersion; // macosVersion is nullable to deal with version skew. if (macosVersion != null && macosVersion < minMacOSVersionForThisPackage) { diff --git a/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version_linker/hook/link.dart b/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version_linker/hook/link.dart index 3036e8749..ae888ae8e 100644 --- a/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version_linker/hook/link.dart +++ b/pkgs/native_assets_builder/test_data/fail_on_os_sdk_version_linker/hook/link.dart @@ -15,14 +15,15 @@ const minMacOSVersionForThisPackage = 13; void main(List arguments) async { await link(arguments, (config, output) async { if (config.targetOS == OS.android) { - if (config.targetAndroidNdkApi! < minNdkApiVersionForThisPackage) { + if (config.codeConfig.targetAndroidNdkApi! < + minNdkApiVersionForThisPackage) { throw UnsupportedError( 'The native assets for this package require at ' 'least Android NDK API level $minNdkApiVersionForThisPackage.', ); } } else if (config.targetOS == OS.iOS) { - final iosVersion = config.targetIOSVersion; + final iosVersion = config.codeConfig.targetIOSVersion; // iosVersion is nullable to deal with version skew. if (iosVersion != null && iosVersion < minIosVersionForThisPackage) { throw UnsupportedError( @@ -31,7 +32,7 @@ void main(List arguments) async { ); } } else if (config.targetOS == OS.macOS) { - final macosVersion = config.targetMacOSVersion; + final macosVersion = config.codeConfig.targetMacOSVersion; // macosVersion is nullable to deal with version skew. if (macosVersion != null && macosVersion < minMacOSVersionForThisPackage) { 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 aab673ecc..30fa8ef31 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 @@ -18,16 +18,17 @@ void main(List arguments) async { ], ); // Temp output to prevent outputting the dylib for bundling. - final tempBuildOutput = BuildOutput(); + final outputBuilder = BuildOutputBuilder(); await cbuilder.run( config: config, - output: tempBuildOutput, + output: outputBuilder, logger: Logger('') ..level = Level.ALL ..onRecord.listen((record) { print('${record.level.name}: ${record.time}: ${record.message}'); }), ); + final tempBuildOutput = BuildOutput(outputBuilder.json); output.addEncodedAsset( tempBuildOutput.encodedAssets.single, // Send dylib to linking if linking is enabled. diff --git a/pkgs/native_assets_builder/test_data/package_reading_metadata/hook/build.dart b/pkgs/native_assets_builder/test_data/package_reading_metadata/hook/build.dart index 10d8fcc10..5ae35355f 100644 --- a/pkgs/native_assets_builder/test_data/package_reading_metadata/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/package_reading_metadata/hook/build.dart @@ -7,18 +7,20 @@ import 'package:native_assets_cli/native_assets_cli.dart'; void main(List args) async { - final buildConfig = BuildConfig(args); - if (!buildConfig.dryRun) { - final someValue = - buildConfig.metadatum('package_with_metadata', 'some_key'); - assert(someValue != null); - final someInt = buildConfig.metadatum('package_with_metadata', 'some_int'); - assert(someInt != null); - print({ - 'some_int': someInt, - 'some_key': someValue, - }); - } else { - print('meta data not available in dry run'); - } + await build(args, (buildConfig, _) async { + if (!buildConfig.dryRun) { + final someValue = + buildConfig.metadatum('package_with_metadata', 'some_key'); + assert(someValue != null); + final someInt = + buildConfig.metadatum('package_with_metadata', 'some_int'); + assert(someInt != null); + print({ + 'some_int': someInt, + 'some_key': someValue, + }); + } else { + print('meta data not available in dry run'); + } + }); } diff --git a/pkgs/native_assets_builder/test_data/simple_link/hook/link.dart b/pkgs/native_assets_builder/test_data/simple_link/hook/link.dart index 9e4d9f2ee..3478258ab 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 @@ -10,7 +10,7 @@ void main(List args) async { }); } -void shake(LinkOutput output, Iterable assets) { +void shake(LinkOutputBuilder output, Iterable assets) { for (final asset in assets.skip(2)) { output.dataAssets.add(asset); 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 cd8dac2c2..d1d26aeb1 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 @@ -2,13 +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. +import 'dart:convert'; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/src/args_parser.dart'; void main(List args) async { - final buildConfig = BuildConfigImpl.fromArguments(args); - await File.fromUri(buildConfig.outputFile).writeAsString(_wrongContents); + final configPath = getConfigArgument(args); + final buildConfig = BuildConfig( + json.decode(File(configPath).readAsStringSync()) as Map); + await File.fromUri(buildConfig.outputDirectory.resolve('build_output.json')) + .writeAsString(_wrongContents); } const _wrongContents = ''' diff --git a/pkgs/native_assets_builder/test_data/wrong_build_output_2/hook/build.dart b/pkgs/native_assets_builder/test_data/wrong_build_output_2/hook/build.dart index dfb5b5e20..9f8ff4ed8 100644 --- a/pkgs/native_assets_builder/test_data/wrong_build_output_2/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_build_output_2/hook/build.dart @@ -2,13 +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. +import 'dart:convert'; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/src/args_parser.dart'; void main(List args) async { - final buildConfig = BuildConfigImpl.fromArguments(args); - await File.fromUri(buildConfig.outputFile).writeAsString(_wrongContents); + final configPath = getConfigArgument(args); + final buildConfig = BuildConfig( + json.decode(File(configPath).readAsStringSync()) as Map); + await File.fromUri(buildConfig.outputDirectory.resolve('build_output.json')) + .writeAsString(_wrongContents); } const _wrongContents = ''' 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 cb50e5731..58666acf2 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 @@ -2,20 +2,25 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:convert'; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/src/args_parser.dart'; void main(List args) async { - final buildConfig = BuildConfigImpl.fromArguments(args); - await File.fromUri(buildConfig.outputFile).writeAsString(_rightContents); + final configPath = getConfigArgument(args); + final buildConfig = BuildConfig( + json.decode(File(configPath).readAsStringSync()) as Map); + await File.fromUri(buildConfig.outputDirectory.resolve('build_output.json')) + .writeAsString(_rightContents); exit(1); } -const _rightContents = ''' -timestamp: 2023-07-28 14:22:45.000 -encodedAssets: [] -dependencies: [] -metadata: {} -version: 1.0.0 -'''; +const _rightContents = '''{ + "timestamp": "2023-07-28 14:22:45.000", + "encodedAssets": [], + "dependencies": [], + "metadata": {}, + "version": "1.5.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 8f3c9993c..4e06197eb 100644 --- a/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_linker/hook/build.dart @@ -4,7 +4,7 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; void main(List arguments) async { await build(arguments, (config, output) async { @@ -28,14 +28,3 @@ void main(List arguments) async { ); }); } - -// Copy from `build`, but without the validation. -Future build( - List arguments, - Future Function(BuildConfig config, BuildOutput output) builder, -) async { - final config = BuildConfigImpl.fromArguments(arguments); - final output = HookOutputImpl(); - await builder(config, output); - await output.writeToFile(config: config); -} 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 66ad30529..b923086ce 100644 --- a/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart +++ b/pkgs/native_assets_builder/test_data/wrong_namespace_asset/hook/build.dart @@ -4,7 +4,7 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; void main(List arguments) async { await build(arguments, (config, output) async { @@ -27,14 +27,3 @@ void main(List arguments) async { ); }); } - -// Copy from `build`, but without the validation. -Future build( - List arguments, - Future Function(BuildConfig config, BuildOutput output) builder, -) async { - final config = BuildConfigImpl.fromArguments(arguments); - final output = HookOutputImpl(); - await builder(config, output); - await output.writeToFile(config: config); -} 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 280945122..54540905c 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 @@ -11,7 +11,7 @@ final packageAssetPath = Uri.file('assets/$assetName'); Future main(List args) async { await build(args, (config, output) async { - if (config.linkModePreference == LinkModePreference.static) { + if (config.codeConfig.linkModePreference == LinkModePreference.static) { // Simulate that this build hook only supports dynamic libraries. throw UnsupportedError( 'LinkModePreference.static is not supported.', @@ -38,7 +38,8 @@ Future main(List args) async { file: assetPath, linkMode: DynamicLoadingBundled(), os: config.targetOS, - architecture: config.targetArchitecture, + architecture: + config.dryRun ? null : config.codeConfig.targetArchitecture, ), ); }); 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 eda5bcd25..c21063d6c 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 @@ -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.dart'; import 'package:native_assets_cli/test.dart'; import 'package:test/test.dart'; @@ -12,10 +11,16 @@ void main() { testBuildHook( description: 'test my build hook', mainMethod: build.main, + extraConfigSetup: (config) { + config.setupCodeConfig( + linkModePreference: LinkModePreference.dynamic, + targetArchitecture: Architecture.current, + ); + }, check: (_, output) { - expect(output.codeAssets.all, isNotEmpty); + expect(output.codeAssets, isNotEmpty); expect( - output.codeAssets.all.first.id, + output.codeAssets.first.id, 'package:local_asset/asset.txt', ); }, diff --git a/pkgs/native_assets_cli/lib/native_assets_cli.dart b/pkgs/native_assets_cli/lib/native_assets_cli.dart index 8bac2546a..9cf2d1821 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli.dart @@ -7,12 +7,8 @@ library native_assets_cli; export 'src/api/build.dart' show build; -export 'src/api/build_config.dart' show BuildConfig; -export 'src/api/build_output.dart' show BuildOutput, LinkOutput; export 'src/api/builder.dart' show Builder; -export 'src/api/hook_config.dart' show HookConfig; export 'src/api/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/build_mode.dart' show BuildMode; @@ -21,12 +17,18 @@ export 'src/code_assets/code_asset.dart' show BuildOutputCodeAssets, CodeAsset, + CodeAssetBuildConfig, CodeAssetsBuildOutput, CodeAssetsLinkConfig, CodeAssetsLinkOutput, + CodeConfig, LinkConfigCodeAssets, LinkOutputCodeAssets, OSLibraryNaming; +export 'src/config.dart' show BuildConfig, BuildOutputBuilder; +export 'src/config.dart' show BuildOutput, LinkOutput, LinkOutputBuilder; +export 'src/config.dart' show HookConfig; +export 'src/config.dart' show LinkConfig; export 'src/data_assets/data_asset.dart' show BuildOutputDataAssets, 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 173e9086e..95d1f5ec7 100644 --- a/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart +++ b/pkgs/native_assets_cli/lib/native_assets_cli_internal.dart @@ -17,15 +17,18 @@ library native_assets_cli_internal; export 'native_assets_cli.dart' hide build, link; -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/code_assets/code_asset_bundling.dart' + show CodeAssetBuildConfigBuilder, CodeAssetBuildOutput, CodeAssetLinkOutput; export 'src/code_assets/validation.dart' show validateCodeAssetBuildOutput, validateCodeAssetLinkOutput, validateCodeAssetsInApplication; +export 'src/config.dart' show BuildConfigBuilder, LinkConfigBuilder; +export 'src/config.dart' show BuildOutputBuilder, LinkOutputBuilder; +export 'src/config.dart' show HookConfigBuilder, HookOutput; +export 'src/data_assets/data_asset_bundling.dart' + show DataAssetBuildOutput, DataAssetLinkOutput; export 'src/data_assets/validation.dart' show validateDataAssetBuildOutput, validateDataAssetLinkOutput; 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 b1a0eae93..8becd87eb 100644 --- a/pkgs/native_assets_cli/lib/src/api/build.dart +++ b/pkgs/native_assets_cli/lib/src/api/build.dart @@ -2,9 +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. +import 'dart:convert'; +import 'dart:io'; + +import '../args_parser.dart'; +import '../config.dart'; import '../validation.dart'; -import 'build_config.dart'; -import 'build_output.dart'; /// Runs a native assets build. /// @@ -86,14 +89,22 @@ import 'build_output.dart'; /// ``` Future build( List arguments, - Future Function(BuildConfig config, BuildOutput output) builder, + Future Function(BuildConfig config, BuildOutputBuilder output) builder, ) async { - final config = BuildConfigImpl.fromArguments(arguments); - final output = HookOutputImpl(); + final configPath = getConfigArgument(arguments); + final bytes = File(configPath).readAsBytesSync(); + final jsonConfig = const Utf8Decoder() + .fuse(const JsonDecoder()) + .convert(bytes) as Map; + final config = BuildConfig(jsonConfig); + final output = BuildOutputBuilder(); await builder(config, output); - final errors = await validateBuildOutput(config, output); + final errors = await validateBuildOutput(config, BuildOutput(output.json)); if (errors.isEmpty) { - await output.writeToFile(config: config); + final jsonOutput = + const JsonEncoder().fuse(const Utf8Encoder()).convert(output.json); + await File.fromUri(config.outputDirectory.resolve('build_output.json')) + .writeAsBytes(jsonOutput); } else { final message = [ 'The output contained unsupported output:', diff --git a/pkgs/native_assets_cli/lib/src/api/build_config.dart b/pkgs/native_assets_cli/lib/src/api/build_config.dart deleted file mode 100644 index 08c03389e..000000000 --- a/pkgs/native_assets_cli/lib/src/api/build_config.dart +++ /dev/null @@ -1,184 +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:convert'; -import 'dart:io'; - -import 'package:collection/collection.dart'; -import 'package:pub_semver/pub_semver.dart'; - -import '../architecture.dart'; -import '../args_parser.dart'; -import '../build_mode.dart'; -import '../c_compiler_config.dart'; -import '../ios_sdk.dart'; -import '../json_utils.dart'; -import '../link_mode_preference.dart'; -import '../model/hook.dart'; -import '../model/metadata.dart'; -import '../os.dart'; -import '../utils/json.dart'; -import '../utils/map.dart'; -import 'build.dart'; -import 'build_output.dart'; -import 'deprecation_messages.dart'; -import 'hook_config.dart'; - -part '../model/build_config.dart'; - -/// The configuration for a build hook (`hook/build.dart`) invocation. -/// -/// A package can optionally provide build hook. If such a hook exists, it will -/// be automatically run, by the Flutter and Dart SDK tools. The hook will be -/// run with specific commandline arguments, which [BuildConfig] can parse and -/// provide more convenient access to. -abstract final class BuildConfig implements HookConfig { - /// Metadata from a direct dependency. - /// - /// The [packageName] of is the package name of the direct dependency. - /// - /// Returns `null` if metadata was not provided. - /// - /// Not available during a [dryRun]. Will throw a [StateError] if accessed - /// during a [dryRun]. - @Deprecated(metadataDeprecation) - Object? metadatum(String packageName, String key); - - /// Whether link hooks will be run after the build hooks. - /// - /// 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` - /// - `flutter run --release` - /// - `dart build` - /// - /// If [linkingEnabled] is false, no assets should be added with the - /// `linkInPackage` parameter set. All assets must be ready for bundling. - /// Linking is disabled in Flutter debug builds and Dart JIT configurations. - /// These configurations are optimized for development speed. - /// - `dart run` - /// - `flutter run` (debug mode) - bool get linkingEnabled; - - /// The version of [BuildConfig]. - /// - /// The build config is used in the protocol between the Dart and Flutter SDKs - /// and packages through build hook invocations. - /// - /// We're trying to avoid breaking changes. However, in the case that we have - /// to, the major version mismatch between the Dart or Flutter SDK and build - /// hook (`hook/build.dart`) will lead to a nice error message. - static Version get latestVersion => HookConfigImpl.latestVersion; - - /// Constructs a config by parsing CLI arguments and loading the config file. - /// - /// Build hooks will most likely use [build] instead of this constructor. - /// - /// The [arguments] must be commandline arguments. - /// - /// If provided, [environment] must be a map containing environment variables. - /// If not provided, [environment] defaults to [Platform.environment]. - /// - /// If provided, [workingDirectory] is used to resolves paths inside - /// [environment]. If not provided, [workingDirectory] defaults to - /// [Directory.current]. - /// - /// This async constructor is intended to be used directly in CLI files. - factory BuildConfig( - List arguments, { - Map? environment, - Uri? workingDirectory, - }) => - BuildConfigImpl.fromArguments( - arguments, - environment: environment, - workingDirectory: workingDirectory, - ); - - /// Constructs a config for a non-dry run by providing values for each field. - /// - /// Build hooks will most likely use [build] instead of this constructor. - /// However, for unit testing code which consumes a [BuildConfig], this - /// constructor facilitates easy construction. - /// - /// For the documentation of the parameters, see the equally named fields. - /// - /// Parameter [dependencyMetadata] must be a nested map `{'packageName' : - /// {'key' : 'value'}}` where `packageName` and `key` correspond to the - // ignore: deprecated_member_use_from_same_package - /// parameters in [metadatum]. - factory BuildConfig.build({ - required Uri outputDirectory, - required Uri outputDirectoryShared, - required String packageName, - required Uri packageRoot, - required BuildMode buildMode, - required Architecture targetArchitecture, - required OS targetOS, - IOSSdk? targetIOSSdk, - int? targetIOSVersion, - int? targetMacOSVersion, - int? targetAndroidNdkApi, - CCompilerConfig? cCompiler, - required LinkModePreference linkModePreference, - @Deprecated(metadataDeprecation) - Map>? dependencyMetadata, - required Iterable supportedAssetTypes, - required bool linkingEnabled, - }) => - BuildConfigImpl( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRoot, - buildMode: buildMode, - targetArchitecture: targetArchitecture, - targetOS: targetOS, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - targetAndroidNdkApi: targetAndroidNdkApi, - cCompiler: cCompiler, - linkModePreference: linkModePreference, - dependencyMetadata: dependencyMetadata != null - ? { - for (final entry in dependencyMetadata.entries) - entry.key: Metadata(entry.value.cast()) - } - : null, - linkingEnabled: linkingEnabled, - supportedAssetTypes: supportedAssetTypes, - ); - - /// Constructs a config for a dry run by providing values for each field. - /// - /// Build hooks will most likely use [build] instead of this constructor. - /// However, for unit testing code which consumes a [BuildConfig], this - /// constructor facilitates easy construction. - /// - /// For the documentation of the parameters, see the equally named fields. - factory BuildConfig.dryRun({ - required Uri outputDirectory, - required Uri outputDirectoryShared, - required String packageName, - required Uri packageRoot, - required OS targetOS, - required LinkModePreference linkModePreference, - required bool linkingEnabled, - required Iterable supportedAssetTypes, - }) => - BuildConfigImpl.dryRun( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRoot, - targetOS: targetOS, - linkModePreference: linkModePreference, - supportedAssetTypes: supportedAssetTypes, - linkingEnabled: linkingEnabled, - ); -} diff --git a/pkgs/native_assets_cli/lib/src/api/build_output.dart b/pkgs/native_assets_cli/lib/src/api/build_output.dart deleted file mode 100644 index a435bf066..000000000 --- a/pkgs/native_assets_cli/lib/src/api/build_output.dart +++ /dev/null @@ -1,180 +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:convert'; -import 'dart:io'; - -import 'package:collection/collection.dart'; -import 'package:pub_semver/pub_semver.dart'; - -import '../architecture.dart'; -import '../encoded_asset.dart'; -import '../json_utils.dart'; -import '../model/dependencies.dart'; -import '../model/metadata.dart'; -import '../os.dart'; -import '../utils/datetime.dart'; -import '../utils/file.dart'; -import '../utils/json.dart'; -import '../utils/map.dart'; -import 'build.dart'; -import 'build_config.dart'; -import 'builder.dart'; -import 'deprecation_messages.dart'; -import 'hook_config.dart'; -import 'link.dart'; -import 'linker.dart'; - -part '../model/hook_output.dart'; -part 'link_output.dart'; - -/// The output of a build hook (`hook/build.dart`) invocation. -/// -/// A package can optionally provide build hook (`hook/build.dart`). If such a -/// hook exists, it will be automatically run, by the Flutter and Dart SDK -/// tools. The hook is expect to produce a specific output which [BuildOutput] -/// can produce. -/// -/// For more information see [build]. -/// -/// Designed to be a sink. The [BuildOutput] is not intended to be read from. -/// [Builder]s stream outputs to the build output. For more info see [Builder]. -abstract final class BuildOutput { - /// Start time for the build of this output. - /// - /// The [timestamp] is rounded down to whole seconds, because - /// [File.lastModified] is rounded to whole seconds and caching logic compares - /// these timestamps. - DateTime get timestamp; - - /// The assets produced by this build. - /// - /// In dry runs, the assets for all [Architecture]s for the [OS] specified in - /// the dry run must be provided. - Iterable get encodedAssets; - - /// The assets produced by this build which should be linked. - /// - /// Every key in the map is a package name. These assets in the values are not - /// bundled with the application, but are sent to the link hook of the package - /// specified in the key, which can decide if they are bundled or not. - /// - /// In dry runs, the assets for all [Architecture]s for the [OS] specified in - /// the dry run must be provided. - Map> get encodedAssetsForLinking; - - /// The files used by this build. - /// - /// If any of the files in [dependencies] are modified after [timestamp], the - /// build will be re-run. - /// - /// The (transitive) Dart sources do not have to be added to these - /// dependencies, only non-Dart files. (Note that old Dart and Flutter SDKs - /// do not automatically add the Dart sources. So builds get wrongly cached, - /// try updating to the latest release.) - Iterable get dependencies; - - /// Create a build output. - /// - /// The [timestamp] must be before any [dependencies] are read by the build - /// this output belongs to. If the [BuildOutput] object is created at the - /// beginning of the build hook, [timestamp] can be omitted and will default - /// to [DateTime.now]. The [timestamp] is rounded down to whole seconds, - /// because [File.lastModified] is rounded to whole seconds and caching logic - /// compares these timestamps. - /// - /// The [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 - /// [addDependencies]. If any of these files are modified after [timestamp], - /// the build will be re-run. Typically these dependencies contain the build - /// hook itself, and the source files used in the build. - /// - /// Metadata can be passed to build hook invocations of dependent packages. It - /// must be provided to the constructor as [metadata], or added later with - // ignore: deprecated_member_use_from_same_package - /// [addMetadatum] and [addMetadata]. - factory BuildOutput({ - DateTime? timestamp, - Iterable? encodedAssets, - Iterable? dependencies, - @Deprecated(metadataDeprecation) Map? metadata, - }) => - HookOutputImpl( - timestamp: timestamp, - encodedAssets: encodedAssets?.toList(), - dependencies: Dependencies([...?dependencies]), - metadata: Metadata({...?metadata}), - ); - - /// 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. - /// - /// 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. - /// - /// 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. - /// - /// 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. - /// - /// If any of the files are modified after [timestamp], the build will be - /// re-run. - void addDependency(Uri dependency); - - /// Adds files used by this build. - /// - /// If any of the files are modified after [timestamp], the build will be - /// re-run. - void addDependencies(Iterable dependencies); - - /// Adds metadata to be passed to build hook invocations of dependent - /// packages. - @Deprecated(metadataDeprecation) - void addMetadatum(String key, Object value); - - /// Adds metadata to be passed to build hook invocations of dependent - /// packages. - @Deprecated(metadataDeprecation) - void addMetadata(Map metadata); - - /// The version of [BuildOutput]. - /// - /// The build output is used in the protocol between the Dart and Flutter SDKs - /// and packages through build hook invocations. - static Version get latestVersion => HookOutputImpl.latestVersion; -} diff --git a/pkgs/native_assets_cli/lib/src/api/builder.dart b/pkgs/native_assets_cli/lib/src/api/builder.dart index 99d21c716..40c543c4d 100644 --- a/pkgs/native_assets_cli/lib/src/api/builder.dart +++ b/pkgs/native_assets_cli/lib/src/api/builder.dart @@ -4,8 +4,7 @@ import 'package:logging/logging.dart'; -import 'build_config.dart'; -import 'build_output.dart'; +import '../config.dart'; import 'linker.dart'; /// A builder to be run during a build hook. @@ -48,22 +47,6 @@ import 'linker.dart'; /// }); /// } /// ``` -/// -/// The builder is designed to immediately operate on [BuildConfig]. If a -/// builder should deviate behavior from the build config, this should be -/// configurable through a constructor parameter. For example, if a native -/// compiler should output a static library to be sent to a linker, but the -/// [BuildConfig.linkModePreference] is set to dynamic linking, the builder -/// should have its own `linkModePreference` parameter in the constructor. -/// -/// 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.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. /// @@ -71,7 +54,7 @@ abstract interface class Builder { /// logs to [logger]. Future run({ required BuildConfig config, - required BuildOutput output, + required BuildOutputBuilder output, required Logger? logger, }); } diff --git a/pkgs/native_assets_cli/lib/src/api/hook_config.dart b/pkgs/native_assets_cli/lib/src/api/hook_config.dart deleted file mode 100644 index f43e62e3a..000000000 --- a/pkgs/native_assets_cli/lib/src/api/hook_config.dart +++ /dev/null @@ -1,153 +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:convert'; -import 'dart:io'; - -import 'package:collection/collection.dart'; -import 'package:crypto/crypto.dart'; -import 'package:pub_semver/pub_semver.dart'; - -import '../architecture.dart'; -import '../build_mode.dart'; -import '../c_compiler_config.dart'; -import '../code_assets/code_asset.dart'; -import '../data_assets/data_asset.dart'; -import '../ios_sdk.dart'; -import '../json_utils.dart'; -import '../link_mode.dart'; -import '../link_mode_preference.dart'; -import '../model/hook.dart'; -import '../model/metadata.dart'; -import '../model/target.dart'; -import '../os.dart'; -import '../utils/map.dart'; -import 'build_config.dart'; -import 'link_config.dart'; - -part '../model/hook_config.dart'; - -/// The shared properties of a [LinkConfig] and a [BuildConfig]. -/// -/// This abstraction makes it easier to design APIs intended for both kinds of -/// build hooks, building and linking. -abstract class HookConfig { - /// The directory in which output and intermediate artifacts that are unique - /// to this configuration can be placed. - /// - /// This directory is unique per hook and per configuration. - /// - /// The contents of this directory will not be modified by anything else than - /// the hook itself. - /// - /// The invoker of the the hook will ensure concurrent invocations wait on - /// each other. - Uri get outputDirectory; - - /// The directory in which shared output and intermediate artifacts can be - /// placed. - /// - /// This directory is unique per hook. - /// - /// The contents of this directory will not be modified by anything else than - /// the hook itself. - /// - /// The invoker of the the hook will ensure concurrent invocations wait on - /// each other. - Uri get outputDirectoryShared; - - /// The name of the package the assets are built for. - String get packageName; - - /// The root of the package the assets are built for. - /// - /// Often a package's assets are built because a package is a dependency of - /// another. For this it is convenient to know the packageRoot. - Uri get packageRoot; - - /// The architecture being compiled for. - /// - /// Not specified (`null`) during a [dryRun]. - Architecture? get targetArchitecture; - - /// The operating system being compiled for. - OS get targetOS; - - /// When compiling for iOS, whether to target device or simulator. - /// - /// Only available if [targetOS] is [OS.iOS]. Will throw a [StateError] if - /// accessed otherwise. - /// - /// Not available during a [dryRun]. Will throw a [StateError] if accessed - /// during a [dryRun]. - IOSSdk? get targetIOSSdk; - - /// When compiling for iOS, the lowest iOS version that the compiled code - /// will be compatible with. - /// - /// Only available if [targetOS] is [OS.iOS]. Will throw a [StateError] if - /// accessed otherwise. - /// - /// Not available during a [dryRun]. Will throw a [StateError] if accessed - /// during a [dryRun]. - /// - /// Corresponds to `-mios-version-min=` for the Apple clang compiler. - int? get targetIOSVersion; - - /// When compiling for MacOS, the lowest MacOS version that the compiled code - /// will be compatible with. - /// - /// Only available if [targetOS] is [OS.macOS]. Will throw a [StateError] if - /// accessed otherwise. - /// - /// Not available during a [dryRun]. Will throw a [StateError] if accessed - /// during a [dryRun]. - /// - /// Corresponds to `-mmacosx-version-min=` for the Apple clang - /// compiler. - int? get targetMacOSVersion; - - /// When compiling for Android, the minimum Android SDK API version to that - /// the compiled code will be compatible with. - /// - /// Required when [targetOS] equals [OS.android]. - /// - /// Not available during a [dryRun]. Will throw a [StateError] if accessed - /// during a [dryRun]. - /// - /// For more information about the Android API version, refer to - /// [`minSdkVersion`](https://developer.android.com/ndk/guides/sdk-versions#minsdkversion) - /// in the Android documentation. - int? get targetAndroidNdkApi; - - /// The configuration for invoking the C compiler. - /// - /// Not available during a [dryRun]. Will throw a [StateError] if accessed - /// during a [dryRun]. - CCompilerConfig get cCompiler; - - /// Whether this run is a dry-run, which doesn't build anything. - /// - /// A dry-run only reports information about which assets a build would - /// create, but doesn't actually create files. - bool get dryRun; - - /// The [BuildMode] that the code should be compiled in. - /// - /// Currently [BuildMode.debug] and [BuildMode.release] are the only modes. - /// - /// Not available during a [dryRun]. Will throw a [StateError] if accessed - /// during a [dryRun]. - BuildMode get buildMode; - - /// The asset types that the invoker of this hook supports. - /// - /// Currently known values: - /// * [CodeAsset.type] - /// * [DataAsset.type] - Iterable get supportedAssetTypes; - - /// The preferred [LinkMode] method for [CodeAsset]s. - LinkModePreference get linkModePreference; -} diff --git a/pkgs/native_assets_cli/lib/src/api/link.dart b/pkgs/native_assets_cli/lib/src/api/link.dart index 98edb9e65..975ec280b 100644 --- a/pkgs/native_assets_cli/lib/src/api/link.dart +++ b/pkgs/native_assets_cli/lib/src/api/link.dart @@ -2,9 +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. +import 'dart:convert'; +import 'dart:io'; + +import '../args_parser.dart'; +import '../config.dart'; import '../validation.dart'; -import 'build_output.dart'; -import 'link_config.dart'; /// Runs a native assets link. /// @@ -29,15 +32,22 @@ import 'link_config.dart'; /// ``` Future link( List arguments, - Future Function(LinkConfig config, LinkOutput output) linker, + Future Function(LinkConfig config, LinkOutputBuilder output) linker, ) async { - final config = LinkConfig.fromArguments(arguments) as LinkConfigImpl; - - final output = HookOutputImpl(); + final configPath = getConfigArgument(arguments); + final bytes = File(configPath).readAsBytesSync(); + final jsonConfig = const Utf8Decoder() + .fuse(const JsonDecoder()) + .convert(bytes) as Map; + final config = LinkConfig(jsonConfig); + final output = LinkOutputBuilder(); await linker(config, output); - final errors = await validateLinkOutput(config, output); + final errors = await validateLinkOutput(config, LinkOutput(output.json)); if (errors.isEmpty) { - await output.writeToFile(config: config); + final jsonOutput = + const JsonEncoder().fuse(const Utf8Encoder()).convert(output.json); + await File.fromUri(config.outputDirectory.resolve('link_output.json')) + .writeAsBytes(jsonOutput); } else { final message = [ 'The output contained unsupported output:', diff --git a/pkgs/native_assets_cli/lib/src/api/link_config.dart b/pkgs/native_assets_cli/lib/src/api/link_config.dart deleted file mode 100644 index a029a460d..000000000 --- a/pkgs/native_assets_cli/lib/src/api/link_config.dart +++ /dev/null @@ -1,117 +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:convert'; -import 'dart:io'; - -import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; -import 'package:pub_semver/pub_semver.dart'; - -import '../architecture.dart'; -import '../args_parser.dart'; -import '../build_mode.dart'; -import '../c_compiler_config.dart'; -import '../encoded_asset.dart'; -import '../ios_sdk.dart'; -import '../json_utils.dart'; -import '../link_mode_preference.dart'; -import '../model/hook.dart'; -import '../os.dart'; -import '../utils/map.dart'; -import 'build_config.dart'; -import 'hook_config.dart'; - -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 [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 encodedAssets; - - /// The path to the file containing recorded uses after kernel tree-shaking. - /// - /// The file contents can be parsed using `package:record_use`. - @experimental - Uri? get recordedUsagesFile; - - /// Generate the [LinkConfig] from the input arguments to the linking script. - factory LinkConfig.fromArguments(List arguments) => - LinkConfigImpl.fromArguments(arguments); - - factory LinkConfig.build({ - required Uri outputDirectory, - required Uri outputDirectoryShared, - required String packageName, - required Uri packageRoot, - Architecture? targetArchitecture, - required OS targetOS, - IOSSdk? targetIOSSdk, - int? targetIOSVersion, - int? targetMacOSVersion, - CCompilerConfig? cCompiler, - BuildMode? buildMode, - required Iterable supportedAssetTypes, - int? targetAndroidNdkApi, - required Iterable assets, - required LinkModePreference linkModePreference, - bool? dryRun, - Version? version, - }) => - LinkConfigImpl( - encodedAssets: assets, - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRoot, - buildMode: buildMode, - cCompiler: cCompiler, - targetAndroidNdkApi: targetAndroidNdkApi, - targetArchitecture: targetArchitecture, - targetIOSSdk: targetIOSSdk, - targetOS: targetOS, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - dryRun: dryRun, - linkModePreference: linkModePreference, - supportedAssetTypes: supportedAssetTypes, - version: version, - ); - - factory LinkConfig.dryRun({ - required Uri outputDirectory, - required Uri outputDirectoryShared, - required String packageName, - required Uri packageRoot, - required OS targetOS, - required Iterable supportedAssetTypes, - required Iterable assets, - required LinkModePreference linkModePreference, - Version? version, - }) => - LinkConfigImpl.dryRun( - encodedAssets: assets, - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRoot, - targetOS: targetOS, - supportedAssetTypes: supportedAssetTypes, - linkModePreference: linkModePreference, - version: version, - ); - - /// The version of [BuildConfig]. - /// - /// The build config is used in the protocol between the Dart and Flutter SDKs - /// and packages through build hook invocations. - /// - /// We're trying to avoid breaking changes. However, in the case that we have - /// to, the major version mismatch between the Dart or Flutter SDK and build - /// hook (`hook/build.dart`) will lead to a nice error message. - static Version get latestVersion => HookConfigImpl.latestVersion; -} diff --git a/pkgs/native_assets_cli/lib/src/api/link_output.dart b/pkgs/native_assets_cli/lib/src/api/link_output.dart deleted file mode 100644 index e90ace891..000000000 --- a/pkgs/native_assets_cli/lib/src/api/link_output.dart +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -part of 'build_output.dart'; - -/// The output of a link hook (`hook/link.dart`) invocation. -/// -/// A package can optionally provide link hook (`hook/link.dart`). If such a -/// hook exists, and any build hook outputs packages for linking with it, it -/// will be automatically run, by the Flutter and Dart SDK tools. The hook is -/// expect to produce a specific output which [LinkOutput] can produce. -/// -/// For more information see [link]. -/// -/// Designed to be a sink. The [LinkOutput] is not designed to be read from. -/// [Linker]s stream outputs to the link output. For more info see [Linker]. -abstract final class LinkOutput { - /// Start time for the link of this output. - /// - /// The [timestamp] is rounded down to whole seconds, because - /// [File.lastModified] is rounded to whole seconds and caching logic compares - /// these timestamps. - DateTime get timestamp; - - /// The assets produced by this link. - /// - /// In dry runs, the assets for all [Architecture]s for the [OS] specified in - /// the dry run must be provided. - Iterable get encodedAssets; - - /// The files used by this link. - /// - /// If any of the files in [dependencies] are modified after [timestamp], the - /// link will be re-run. - Iterable get dependencies; - - /// Adds file used by this link. - /// - /// If any of the files are modified after [timestamp], the link will be - /// re-run. - void addDependency(Uri dependency); - - /// Adds files used by this link. - /// - /// If any of the files are modified after [timestamp], the link will be - /// re-run. - void addDependencies(Iterable dependencies); - - /// Adds [EncodedAsset]s produced by this link or dry run. - void addEncodedAsset(EncodedAsset asset); - - /// Adds [EncodedAsset]s produced by this link or dry run. - void addEncodedAssets(Iterable assets); - - factory LinkOutput({ - Iterable? encodedAssets, - Dependencies? dependencies, - DateTime? timestamp, - }) => - HookOutputImpl( - encodedAssets: encodedAssets, - dependencies: dependencies, - timestamp: timestamp, - ); - - /// The version of [LinkOutput]. - /// - /// The link output is used in the protocol between the Dart and Flutter SDKs - /// and packages through build hook invocations. - static Version get latestVersion => HookOutputImpl.latestVersion; -} diff --git a/pkgs/native_assets_cli/lib/src/api/linker.dart b/pkgs/native_assets_cli/lib/src/api/linker.dart index 1bb080c45..a6e034cd0 100644 --- a/pkgs/native_assets_cli/lib/src/api/linker.dart +++ b/pkgs/native_assets_cli/lib/src/api/linker.dart @@ -4,9 +4,8 @@ import 'package:logging/logging.dart'; -import 'build_output.dart'; +import '../config.dart'; import 'builder.dart'; -import 'link_config.dart'; /// A linker to be run during a link hook. /// @@ -37,7 +36,7 @@ abstract interface class Linker { /// logs to [logger]. Future run({ required LinkConfig config, - required LinkOutput output, + required LinkOutputBuilder output, required Logger? logger, }); } diff --git a/pkgs/native_assets_cli/lib/src/api/test.dart b/pkgs/native_assets_cli/lib/src/api/test.dart deleted file mode 100644 index df1782c15..000000000 --- a/pkgs/native_assets_cli/lib/src/api/test.dart +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:io'; - -import 'package:meta/meta.dart' show isTest; -import 'package:test/test.dart'; -import 'package:yaml/yaml.dart'; - -import '../architecture.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'; -import 'build_config.dart'; -import 'build_output.dart'; - -@isTest -Future testBuildHook({ - required String description, - // ignore: inference_failure_on_function_return_type - required Function(List arguments) mainMethod, - required void Function(BuildConfig config, BuildOutput output) check, - BuildMode? buildMode, - Architecture? targetArchitecture, - OS? targetOS, - IOSSdk? targetIOSSdk, - int? targetIOSVersion, - int? targetMacOSVersion, - int? targetAndroidNdkApi, - CCompilerConfig? cCompiler, - LinkModePreference? linkModePreference, - required Iterable supportedAssetTypes, - bool? linkingEnabled, -}) async { - test( - description, - () async { - final tempDir = await _tempDirForTest(); - - final outputDirectory = tempDir.resolve('output/'); - await Directory.fromUri(outputDirectory).create(); - final outputDirectoryShared = tempDir.resolve('output_shared/'); - await Directory.fromUri(outputDirectory).create(); - - final buildConfig = BuildConfig.build( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: await _packageName(), - packageRoot: Directory.current.uri, - buildMode: buildMode ?? BuildMode.release, - targetArchitecture: targetArchitecture ?? Architecture.current, - targetOS: targetOS ?? OS.current, - linkModePreference: linkModePreference ?? LinkModePreference.dynamic, - linkingEnabled: linkingEnabled ?? true, - cCompiler: cCompiler, - supportedAssetTypes: supportedAssetTypes, - targetAndroidNdkApi: targetAndroidNdkApi, - targetIOSSdk: targetIOSSdk, - targetIOSVersion: targetIOSVersion, - targetMacOSVersion: targetMacOSVersion, - ) as BuildConfigImpl; - final buildConfigUri = tempDir.resolve('build_config.json'); - - await _writeBuildConfig(buildConfigUri, buildConfig); - - await mainMethod(['--config=${buildConfigUri.toFilePath()}']); - - final hookOutput = await _readOutput(buildConfig) as BuildOutput; - - check(buildConfig, hookOutput); - - final allEncodedAssets = [ - ...hookOutput.encodedAssets, - ...hookOutput.encodedAssetsForLinking.values.expand((e) => e) - ]; - for (final asset in allEncodedAssets) { - expect(buildConfig.supportedAssetTypes, contains(asset.type)); - } - - for (final asset in hookOutput.dataAssets.all) { - final file = File.fromUri(asset.file); - expect(await file.exists(), true); - } - for (final asset in hookOutput.codeAssets.all) { - if (asset.file != null) { - final file = File.fromUri(asset.file!); - expect(await file.exists(), true); - } - } - }, - ); -} - -Future _writeBuildConfig( - Uri buildConfigUri, - BuildConfigImpl buildConfig, -) async { - final file = File.fromUri(buildConfigUri); - await file.create(); - file.writeAsStringSync(buildConfig.toJsonString()); -} - -Future _readOutput(BuildConfigImpl buildConfig) async { - final hookOutput = HookOutputImpl.fromJsonString( - await File.fromUri(buildConfig.outputFile).readAsString()); - return hookOutput; -} - -Future _packageName() async { - final uri = Directory.current.uri.resolve('pubspec.yaml'); - final readAsString = await File.fromUri(uri).readAsString(); - final yaml = loadYaml(readAsString) as YamlMap; - return yaml['name'] as String; -} - -const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES'; - -Future _tempDirForTest({String? prefix, bool keepTemp = false}) async { - final tempDir = await Directory.systemTemp.createTemp(prefix); - // Deal with Windows temp folder aliases. - final tempUri = - Directory(await tempDir.resolveSymbolicLinks()).uri.normalizePath(); - if ((!Platform.environment.containsKey(keepTempKey) || - Platform.environment[keepTempKey]!.isEmpty) && - !keepTemp) { - addTearDown(() => tempDir.delete(recursive: true)); - } - return tempUri; -} 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 2a13e71ca..c07b91fb7 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 @@ -2,13 +2,14 @@ // 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 '../architecture.dart'; +import '../c_compiler_config.dart'; +import '../config.dart'; import '../encoded_asset.dart'; +import '../ios_sdk.dart'; import '../json_utils.dart'; import '../link_mode.dart'; +import '../link_mode_preference.dart'; import '../os.dart'; import '../utils/json.dart'; import '../utils/map.dart'; @@ -190,12 +191,17 @@ final class CodeAsset { static const String type = 'native_code'; } +/// Build config extension for code assets. +extension CodeAssetBuildConfig on BuildConfig { + CodeConfig get codeConfig => CodeConfig(this); +} + /// Build output extension for code assets. -extension CodeAssetsBuildOutput on BuildOutput { +extension CodeAssetsBuildOutput on BuildOutputBuilder { BuildOutputCodeAssets get codeAssets => BuildOutputCodeAssets(this); } -extension type BuildOutputCodeAssets(BuildOutput _output) { +extension type BuildOutputCodeAssets(BuildOutputBuilder _output) { void add(CodeAsset asset, {String? linkInPackage}) => _output.addEncodedAsset(asset.encode(), linkInPackage: linkInPackage); @@ -204,14 +210,11 @@ extension type BuildOutputCodeAssets(BuildOutput _output) { 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 CodeAssetsLinkConfig on LinkConfig { + CodeConfig get codeConfig => CodeConfig(this); LinkConfigCodeAssets get codeAssets => LinkConfigCodeAssets(this); } @@ -229,18 +232,14 @@ extension type LinkConfigCodeAssets(LinkConfig _config) { } /// Link output extension for code assets. -extension CodeAssetsLinkOutput on LinkOutput { +extension CodeAssetsLinkOutput on LinkOutputBuilder { LinkOutputCodeAssets get codeAssets => LinkOutputCodeAssets(this); } -extension type LinkOutputCodeAssets(LinkOutput _output) { +extension type LinkOutputCodeAssets(LinkOutputBuilder _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 { @@ -275,6 +274,55 @@ extension OSLibraryNaming on OS { } } +class CodeConfig { + final Architecture? _targetArchitecture; + + final LinkModePreference linkModePreference; + final CCompilerConfig cCompiler; + final int? targetIOSVersion; + final int? targetMacOSVersion; + final int? targetAndroidNdkApi; + final IOSSdk? targetIOSSdk; + + CodeConfig(HookConfig config) + : linkModePreference = LinkModePreference.fromString( + config.json.string('link_mode_preference')), + _targetArchitecture = (config is BuildConfig && config.dryRun) + ? null + : Architecture.fromString( + config.json.string('target_architecture')), + cCompiler = (() { + final cCompiler = CCompilerConfig( + archiver: config.json.optionalPath('c_compiler.ar'), + compiler: config.json.optionalPath('c_compiler.cc'), + linker: config.json.optionalPath('c_compiler.ld'), + envScript: config.json.optionalPath('c_compiler.env_script'), + envScriptArgs: switch ( + config.json.optionalList('env_script_arguments')) { + final List l => [ + for (int i = 0; i < l.length; ++i) l.get(i) + ], + null => null, + }, + ); + return cCompiler; + })(), + targetIOSVersion = config.json.optionalInt('target_ios_version'), + targetMacOSVersion = config.json.optionalInt('target_macos_version'), + targetAndroidNdkApi = config.json.optionalInt('target_android_ndk_api'), + targetIOSSdk = switch (config.json.optionalString('target_ios_sdk')) { + final String value => IOSSdk.fromString(value), + null => null, + }; + + Architecture get targetArchitecture { + if (_targetArchitecture == null) { + throw StateError('Cannot access target architecture in dry runs'); + } + return _targetArchitecture; + } +} + /// The default name prefix for dynamic libraries per [OS]. const _dylibPrefix = { OS.android: 'lib', diff --git a/pkgs/native_assets_cli/lib/src/code_assets/code_asset_bundling.dart b/pkgs/native_assets_cli/lib/src/code_assets/code_asset_bundling.dart new file mode 100644 index 000000000..ce8bc7e87 --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/code_assets/code_asset_bundling.dart @@ -0,0 +1,77 @@ +// 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 '../architecture.dart'; +import '../c_compiler_config.dart'; +import '../config.dart'; +import '../ios_sdk.dart'; +import '../link_mode_preference.dart'; +import 'code_asset.dart'; + +// XXX TODO: Migrate to json decoding api. +extension CodeAssetBuildConfigBuilder on HookConfigBuilder { + void setupCodeConfig({ + required Architecture? targetArchitecture, + required LinkModePreference linkModePreference, + CCompilerConfig? cCompilerConfig, + int? targetIOSVersion, + int? targetMacOSVersion, + int? targetAndroidNdkApi, + IOSSdk? targetIOSSdk, + }) { + if (targetArchitecture != null) { + json['target_architecture'] = targetArchitecture.toString(); + } + json['link_mode_preference'] = linkModePreference.toString(); + if (cCompilerConfig != null) { + final ar = cCompilerConfig.archiver?.toFilePath(); + if (ar != null) { + json['c_compiler.ar'] = ar; + } + final cc = cCompilerConfig.compiler?.toFilePath(); + if (ar != null) { + json['c_compiler.cc'] = cc; + } + final ld = cCompilerConfig.linker?.toFilePath(); + if (ar != null) { + json['c_compiler.ld'] = ld; + } + final envScript = cCompilerConfig.envScript?.toFilePath(); + if (envScript != null) { + json['c_compiler.env_script'] = envScript; + } + final envScriptArgs = cCompilerConfig.envScriptArgs; + if (envScriptArgs != null && envScriptArgs.isNotEmpty) { + json['env_script_arguments'] = envScriptArgs; + } + } + + if (targetIOSVersion != null) { + json['target_ios_version'] = targetIOSVersion; + } + if (targetMacOSVersion != null) { + json['target_macos_version'] = targetMacOSVersion; + } + if (targetAndroidNdkApi != null) { + json['target_android_ndk_api'] = targetAndroidNdkApi; + } + if (targetIOSSdk != null) { + json['target_ios_sdk'] = targetIOSSdk.toString(); + } + } +} + +extension CodeAssetBuildOutput on BuildOutput { + List get codeAssets => encodedAssets + .where((asset) => asset.type == CodeAsset.type) + .map(CodeAsset.fromEncoded) + .toList(); +} + +extension CodeAssetLinkOutput on LinkOutput { + List get codeAssets => encodedAssets + .where((asset) => asset.type == CodeAsset.type) + .map(CodeAsset.fromEncoded) + .toList(); +} diff --git a/pkgs/native_assets_cli/lib/src/code_assets/validation.dart b/pkgs/native_assets_cli/lib/src/code_assets/validation.dart index 4910f2ed6..8c333d361 100644 --- a/pkgs/native_assets_cli/lib/src/code_assets/validation.dart +++ b/pkgs/native_assets_cli/lib/src/code_assets/validation.dart @@ -8,17 +8,18 @@ import '../../native_assets_cli_internal.dart'; import '../link_mode.dart'; Future validateCodeAssetBuildOutput( - HookConfig config, + BuildConfig config, BuildOutput output, ) => - _validateCodeAssetBuildOrLinkOutput(config, output as HookOutputImpl, true); + _validateCodeAssetBuildOrLinkOutput(config, config.codeConfig, + output.encodedAssets, config.dryRun, output, true); Future validateCodeAssetLinkOutput( - HookConfig config, + LinkConfig config, LinkOutput output, ) => _validateCodeAssetBuildOrLinkOutput( - config, output as HookOutputImpl, false); + config, config.codeConfig, output.encodedAssets, false, output, false); /// Validates that the given code assets can be used together in an application. /// @@ -40,18 +41,22 @@ Future validateCodeAssetsInApplication( Future _validateCodeAssetBuildOrLinkOutput( HookConfig config, - HookOutputImpl output, + CodeConfig codeConfig, + List encodedAssets, + bool dryRun, + HookOutput output, bool isBuild, ) async { final errors = []; final ids = {}; final fileNameToEncodedAssetId = >{}; - for (final asset in output.encodedAssets) { + for (final asset in encodedAssets) { if (asset.type != CodeAsset.type) continue; _validateCodeAssets( config, - config.dryRun, + codeConfig, + dryRun, CodeAsset.fromEncoded(asset), errors, ids, @@ -66,6 +71,7 @@ Future _validateCodeAssetBuildOrLinkOutput( void _validateCodeAssets( HookConfig config, + CodeConfig codeConfig, bool dryRun, CodeAsset codeAsset, List errors, @@ -81,7 +87,7 @@ void _validateCodeAssets( errors.add('More than one code asset with same "$id" id.'); } - final preference = config.linkModePreference; + final preference = codeConfig.linkModePreference; final linkMode = codeAsset.linkMode; if ((linkMode is DynamicLoading && preference == LinkModePreference.static) || (linkMode is StaticLinking && preference == LinkModePreference.dynamic)) { @@ -101,9 +107,9 @@ void _validateCodeAssets( if (!dryRun) { if (architecture == null) { errors.add('CodeAsset "$id" has no architecture.'); - } else if (architecture != config.targetArchitecture) { + } else if (architecture != codeConfig.targetArchitecture) { errors.add('CodeAsset "$id" has an architecture "$architecture", which ' - 'is not the target architecture "${config.targetArchitecture}".'); + 'is not the target architecture "${codeConfig.targetArchitecture}".'); } } diff --git a/pkgs/native_assets_cli/lib/src/config.dart b/pkgs/native_assets_cli/lib/src/config.dart new file mode 100644 index 000000000..904b36eac --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/config.dart @@ -0,0 +1,538 @@ +// 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:convert' hide json; +import 'dart:io'; + +import 'package:crypto/crypto.dart' show sha256; +import 'package:pub_semver/pub_semver.dart'; + +import 'api/deprecation_messages.dart'; +import 'architecture.dart'; +import 'build_mode.dart'; +import 'encoded_asset.dart'; +import 'json_utils.dart'; +import 'model/metadata.dart'; +import 'os.dart'; +import 'utils/datetime.dart'; +import 'utils/json.dart'; + +/// The shared properties of a [LinkConfig] and a [BuildConfig]. +/// +/// This abstraction makes it easier to design APIs intended for both kinds of +/// build hooks, building and linking. +sealed class HookConfig { + /// The underlying json configuration of this [HookConfig]. + final Map json; + + /// The version of the [HookConfig]. + final Version version; + + /// The directory in which output and intermediate artifacts that are unique + /// to this configuration can be placed. + /// + /// This directory is unique per hook and per configuration. + /// + /// The contents of this directory will not be modified by anything else than + /// the hook itself. + /// + /// The invoker of the the hook will ensure concurrent invocations wait on + /// each other. + final Uri outputDirectory; + + /// The directory in which shared output and intermediate artifacts can be + /// placed. + /// + /// This directory is unique per hook. + /// + /// The contents of this directory will not be modified by anything else than + /// the hook itself. + /// + /// The invoker of the the hook will ensure concurrent invocations wait on + /// each other. + final Uri outputDirectoryShared; + + /// The name of the package the assets are built for. + final String packageName; + + /// The root of the package the assets are built for. + /// + /// Often a package's assets are built because a package is a dependency of + /// another. For this it is convenient to know the packageRoot. + final Uri packageRoot; + + /// The operating system being compiled for. + final OS targetOS; + + /// The [BuildMode] that the code should be compiled in. + /// + /// Currently [BuildMode.debug] and [BuildMode.release] are the only modes. + /// + /// Not available during a dry run. + final BuildMode? _buildMode; + + /// The asset types that the invoker of this hook supports. + final List supportedAssetTypes; + + HookConfig(this.json) + : version = switch (Version.parse(json.string(_versionKey))) { + final Version version => (version.major != currentVersion.major || + version < currentVersion) + ? throw FormatException( + 'Only compatible versions with $currentVersion are supported ' + '(was: $version).') + : version, + }, + outputDirectory = json.path(_outDirConfigKey), + outputDirectoryShared = json.path(_outDirSharedConfigKey), + packageRoot = json.path(_packageRootConfigKey), + packageName = json.string(_packageNameConfigKey), + targetOS = OS.fromString(json.string(_targetOSConfigKey)), + supportedAssetTypes = + json.optionalStringList(_supportedAssetTypesKey) ?? const [], + _buildMode = switch (json.optionalString(_buildModeConfigKey)) { + String value => BuildMode.fromString(value), + null => null, + }; + + BuildMode get buildMode { + if (_buildMode == null) { + throw StateError('Build mode should not be accessed in dry-run mode.'); + } + return _buildMode; + } + + @override + String toString() => const JsonEncoder.withIndent(' ').convert(json); + + // The latest supported config version. + static Version currentVersion = Version(3, 5, 0); +} + +sealed class HookConfigBuilder { + final Map json = { + 'version': HookConfig.currentVersion.toString(), + }; + + void setupHookConfig({ + required Uri packageRoot, + required String packageName, + required OS targetOS, + required List supportedAssetTypes, + required BuildMode? buildMode, + }) { + json[_packageNameConfigKey] = packageName; + json[_packageRootConfigKey] = packageRoot.toFilePath(); + json[_targetOSConfigKey] = targetOS.toString(); + json[_supportedAssetTypesKey] = supportedAssetTypes; + if (buildMode != null) { + json[_buildModeConfigKey] = buildMode.toString(); + } + } + + /// Constructs a checksum for a [BuildConfig]. + /// + /// This can be used to construct an output directory name specific to the + /// [BuildConfig] being built with this [BuildConfigBuilder]. It is therefore + /// assumed the output directory has not been set yet. + String computeChecksum() { + if (json.containsKey(_outDirConfigKey) || + json.containsKey(_outDirSharedConfigKey)) { + // The bundling tools would first calculate the checksum, create an output + // directory and then call [BuildConfigBuilder.setupBuildRunConfig] & + // [LinkConfigBuilder.setupLinkRunConfig]. + throw StateError('The checksum should be generated before setting ' + 'up the run configuration'); + } + final hash = sha256 + .convert(const JsonEncoder().fuse(const Utf8Encoder()).convert(json)) + .toString() + // 256 bit hashes lead to 64 hex character strings. + // To avoid overflowing file paths limits, only use 32. + // Using 16 hex characters would also be unlikely to have collisions. + .substring(0, 32); + return hash; + } + //String computeChecksum() { + //final savedOutDir = json.remove(_outDirConfigKey); + //final savedSharedOutDir = json.remove(_outDirSharedConfigKey); + //final hash = sha256 + //.convert(const JsonEncoder().fuse(const Utf8Encoder()).convert(json)) + //.toString() + //// 256 bit hashes lead to 64 hex character strings. + //// To avoid overflowing file paths limits, only use 32. + //// Using 16 hex characters would also be unlikely to have collisions. + //.substring(0, 32); + //if (savedOutDir != null) json[_outDirConfigKey] = savedOutDir; + //if (savedSharedOutDir != null) { + //json[_outDirSharedConfigKey] = savedSharedOutDir; + //} + //return hash; + //} +} + +const _targetOSConfigKey = 'target_os'; +const _buildModeConfigKey = 'build_mode'; +const _metadataConfigKey = 'metadata'; +const _outDirConfigKey = 'out_dir'; +const _outDirSharedConfigKey = 'out_dir_shared'; +const _packageNameConfigKey = 'package_name'; +const _packageRootConfigKey = 'package_root'; +const _supportedAssetTypesKey = 'supported_asset_types'; + +final class BuildConfig extends HookConfig { + /// Whether this run is a dry-run, which doesn't build anything. + /// + /// A dry-run only reports information about which assets a build would + /// create, but doesn't actually create files. + final bool dryRun; + + final bool linkingEnabled; + + final Map metadata; + + BuildConfig(super.json) + : dryRun = json.getOptional(_dryRunConfigKey) ?? false, + linkingEnabled = json.get(_linkingEnabledKey), + metadata = { + for (final entry + in (json.optionalMap(_dependencyMetadataKey) ?? {}).entries) + entry.key: Metadata.fromJson(as>(entry.value)), + }; + + Object? metadatum(String packageName, String key) => + metadata[packageName]?.metadata[key]; +} + +final class BuildConfigBuilder extends HookConfigBuilder { + void setupBuildConfig({ + required bool dryRun, + required bool linkingEnabled, + Map? metadata, + }) { + json[_dryRunConfigKey] = dryRun; + json[_linkingEnabledKey] = linkingEnabled; + if (metadata != null) { + json[_dependencyMetadataKey] = { + for (final key in metadata.keys) key: metadata[key]!.toJson(), + }; + } + } + + void setupBuildRunConfig({ + required Uri outputDirectory, + required Uri outputDirectoryShared, + }) { + json[_outDirConfigKey] = outputDirectory.toFilePath(); + json[_outDirSharedConfigKey] = outputDirectoryShared.toFilePath(); + } +} + +const _dryRunConfigKey = 'dry_run'; +const _linkingEnabledKey = 'linking_enabled'; + +final class LinkConfig extends HookConfig { + final List encodedAssets; + + final Uri? recordedUsagesFile; + + LinkConfig(super.json) + : encodedAssets = + _parseAssets(json.getOptional>(_assetsKey)), + recordedUsagesFile = json.optionalPath(_recordedUsagesFileConfigKey); +} + +final class LinkConfigBuilder extends HookConfigBuilder { + void setupLinkConfig({ + required List assets, + }) { + json[_assetsKey] = [for (final asset in assets) asset.toJson()]; + } + + void setupLinkRunConfig({ + required Uri outputDirectory, + required Uri outputDirectoryShared, + required Uri? recordedUsesFile, + }) { + json[_outDirConfigKey] = outputDirectory.toFilePath(); + json[_outDirSharedConfigKey] = outputDirectoryShared.toFilePath(); + if (recordedUsesFile != null) { + json[_recordedUsagesFileConfigKey] = recordedUsesFile.toFilePath(); + } + } +} + +List _parseAssets(List? object) => object == null + ? [] + : [ + for (int i = 0; i < object.length; ++i) + EncodedAsset.fromJson(object.mapAt(i)), + ]; + +const _recordedUsagesFileConfigKey = 'resource_identifiers'; +const _assetsKey = 'assets'; +const _versionKey = 'version'; + +sealed class HookOutput { + /// The underlying json configuration of this [HookOutput]. + final Map json; + + /// The version of the [HookConfig]. + final Version version; + + /// Start time for the build of this output. + /// + /// The [timestamp] is rounded down to whole seconds, because + /// [File.lastModified] is rounded to whole seconds and caching logic compares + /// these timestamps. + final DateTime timestamp; + + /// The files used by this build. + /// + /// If any of the files in [dependencies] are modified after [timestamp], the + /// build will be re-run. + /// + /// The (transitive) Dart sources do not have to be added to these + /// dependencies, only non-Dart files. (Note that old Dart and Flutter SDKs + /// do not automatically add the Dart sources. So builds get wrongly cached, + /// try updating to the latest release.) + final List dependencies; + + HookOutput(this.json) + : version = switch (Version.parse(json.string(_versionKey))) { + final Version version => (version.major != latestVersion.major || + version < latestVersion) + ? throw FormatException( + 'Only compatible versions with $latestVersion are supported ' + '(was: $version).') + : version, + }, + timestamp = DateTime.parse(json.string(_timestampKey)), + dependencies = _parseDependencies(json.optionalList(_dependenciesKey)); + + @override + String toString() => const JsonEncoder.withIndent(' ').convert(json); + + /// The version of [HookOutput]. + static final Version latestVersion = Version(1, 5, 0); +} + +List _parseDependencies(List? list) { + if (list == null) return const []; + return [ + for (int i = 0; i < list.length; ++i) list.pathAt(i), + ]; +} + +const String _timestampKey = 'timestamp'; +const String _dependenciesKey = 'dependencies'; + +sealed class HookOutputBuilder { + final Map json = {}; + + HookOutputBuilder() { + json[_versionKey] = HookOutput.latestVersion.toString(); + json[_timestampKey] = DateTime.now().roundDownToSeconds().toString(); + } + + /// Adds file used by this build. + /// + /// If any of the files are modified after [BuildOutput.timestamp], the + // build will be re-run. + void addDependency(Uri uri) { + var dependencies = json[_dependenciesKey] as List?; + dependencies ??= json[_dependenciesKey] = []; + dependencies.add(uri.toFilePath()); + } + + /// Adds files used by this build. + /// + /// If any of the files are modified after [BuildOutput.timestamp], the + // build will be re-run. + void addDependencies(Iterable uris) { + var dependencies = json[_dependenciesKey] as List?; + dependencies ??= json[_dependenciesKey] = []; + dependencies.addAll(uris.map((uri) => uri.toFilePath())); + } +} + +class BuildOutput extends HookOutput { + /// The assets produced by this build. + /// + /// In dry runs, the assets for all [Architecture]s for the [OS] specified in + /// the dry run must be provided. + final List encodedAssets; + + /// The assets produced by this build which should be linked. + /// + /// Every key in the map is a package name. These assets in the values are not + /// bundled with the application, but are sent to the link hook of the package + /// specified in the key, which can decide if they are bundled or not. + /// + /// In dry runs, the assets for all [Architecture]s for the [OS] specified in + /// the dry run must be provided. + final Map> encodedAssetsForLinking; + + /// Metadata passed to dependent build hook invocations. + final Metadata metadata; + + /// Creates a [BuildOutput] from the given [json]. + BuildOutput(super.json) + : encodedAssets = _parseEncodedAssets(json.optionalList(_assetsKey)), + encodedAssetsForLinking = { + for (final MapEntry(:key, :value) + in (json.optionalMap(_assetsForLinkingKey) ?? {}).entries) + key: _parseEncodedAssets(value as List), + }, + metadata = + Metadata.fromJson(json.optionalMap(_metadataConfigKey) ?? {}); +} + +List _parseEncodedAssets(List? json) => json == null + ? const [] + : [ + for (int i = 0; i < json.length; ++i) + EncodedAsset.fromJson(json.mapAt(i)), + ]; + +const _assetsForLinkingKey = 'assetsForLinking'; +const _dependencyMetadataKey = 'dependency_metadata'; + +// XXX TODO +class BuildOutputBuilder extends HookOutputBuilder { + /// Adds metadata to be passed to build hook invocations of dependent + /// packages. + @Deprecated(metadataDeprecation) + void addMetadatum(String key, Object value) { + var map = json[_metadataConfigKey] as Map?; + map ??= json[_metadataConfigKey] = {}; + map[key] = value; + } + + /// Adds metadata to be passed to build hook invocations of dependent + /// packages. + @Deprecated(metadataDeprecation) + void addMetadata(Map metadata) { + var map = json[_metadataConfigKey] as Map?; + map ??= json[_metadataConfigKey] = {}; + map.addAll(metadata); + } + + /// 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. + /// + /// 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}) { + final list = _getEncodedAssetsList(json, linkInPackage); + list.add(asset.toJson()); + } + + /// 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. + /// + /// 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}) { + final list = _getEncodedAssetsList(json, linkInPackage); + for (final asset in assets) { + list.add(asset.toJson()); + } + } +} + +List _getEncodedAssetsList( + Map json, String? linkInPackage) { + if (linkInPackage == null) { + var list = json[_assetsKey] as List?; + list ??= json[_assetsKey] = >[]; + return list; + } + var map = json[_assetsForLinkingKey] as Map?; + map ??= json[_assetsForLinkingKey] = {}; + + var list = map[linkInPackage] as List?; + list ??= map[linkInPackage] = >[]; + return list; +} + +class LinkOutput extends HookOutput { + /// The assets produced by this build. + /// + /// In dry runs, the assets for all [Architecture]s for the [OS] specified in + /// the dry run must be provided. + final List encodedAssets; + + /// Creates a [BuildOutput] from the given [json]. + LinkOutput(super.json) + : encodedAssets = _parseEncodedAssets(json.optionalList(_assetsKey)); +} + +class LinkOutputBuilder extends HookOutputBuilder { + /// Adds [EncodedAsset]s produced by this build. + /// + /// 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) { + var list = json[_assetsKey] as List?; + list ??= json[_assetsKey] = >[]; + list.add(asset.toJson()); + } + + /// Adds [EncodedAsset]s produced by this build. + /// + /// 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) { + var list = json[_assetsKey] as List?; + list ??= json[_assetsKey] = >[]; + for (final asset in assets) { + list.add(asset.toJson()); + } + } +} 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 9c79b0629..248e6715c 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 @@ -2,9 +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 '../api/build_config.dart'; -import '../api/build_output.dart'; -import '../api/link_config.dart'; +import '../config.dart'; import '../encoded_asset.dart'; import '../json_utils.dart'; import '../utils/map.dart'; @@ -93,11 +91,11 @@ final class DataAsset { } /// Build output extension for data assets. -extension DataAssetsBuildOutput on BuildOutput { +extension DataAssetsBuildOutput on BuildOutputBuilder { BuildOutputDataAssets get dataAssets => BuildOutputDataAssets(this); } -extension type BuildOutputDataAssets(BuildOutput _output) { +extension type BuildOutputDataAssets(BuildOutputBuilder _output) { void add(DataAsset asset, {String? linkInPackage}) => _output.addEncodedAsset(asset.encode(), linkInPackage: linkInPackage); @@ -106,10 +104,6 @@ extension type BuildOutputDataAssets(BuildOutput _output) { add(asset, linkInPackage: linkInPackage); } } - - Iterable get all => _output.encodedAssets - .where((e) => e.type == DataAsset.type) - .map(DataAsset.fromEncoded); } /// Link output extension for data assets. @@ -131,18 +125,14 @@ extension type LinkConfigDataAssets(LinkConfig _config) { } /// Link output extension for data assets. -extension DataAssetsLinkOutput on LinkOutput { +extension DataAssetsLinkOutput on LinkOutputBuilder { LinkOutputDataAssets get dataAssets => LinkOutputDataAssets(this); } -extension type LinkOutputDataAssets(LinkOutput _output) { +extension type LinkOutputDataAssets(LinkOutputBuilder _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'; diff --git a/pkgs/native_assets_cli/lib/src/data_assets/data_asset_bundling.dart b/pkgs/native_assets_cli/lib/src/data_assets/data_asset_bundling.dart new file mode 100644 index 000000000..45a17b0fd --- /dev/null +++ b/pkgs/native_assets_cli/lib/src/data_assets/data_asset_bundling.dart @@ -0,0 +1,20 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import '../config.dart'; +import 'data_asset.dart'; + +extension DataAssetBuildOutput on BuildOutput { + List get dataAssets => encodedAssets + .where((asset) => asset.type == DataAsset.type) + .map(DataAsset.fromEncoded) + .toList(); +} + +extension DataAssetLinkOutput on LinkOutput { + List get dataAssets => encodedAssets + .where((asset) => asset.type == DataAsset.type) + .map(DataAsset.fromEncoded) + .toList(); +} diff --git a/pkgs/native_assets_cli/lib/src/data_assets/validation.dart b/pkgs/native_assets_cli/lib/src/data_assets/validation.dart index d6dd9de36..8504b11c9 100644 --- a/pkgs/native_assets_cli/lib/src/data_assets/validation.dart +++ b/pkgs/native_assets_cli/lib/src/data_assets/validation.dart @@ -7,30 +7,32 @@ import 'dart:io'; import '../../native_assets_cli_internal.dart'; Future validateDataAssetBuildOutput( - HookConfig config, + BuildConfig config, BuildOutput output, ) => - _validateDataAssetBuildOrLinkOutput(config, output as HookOutputImpl, true); + _validateDataAssetBuildOrLinkOutput( + config, output.encodedAssets, config.dryRun, true); Future validateDataAssetLinkOutput( - HookConfig config, + LinkConfig config, LinkOutput output, ) => _validateDataAssetBuildOrLinkOutput( - config, output as HookOutputImpl, false); + config, output.encodedAssets, false, false); Future _validateDataAssetBuildOrLinkOutput( HookConfig config, - HookOutputImpl output, + List encodedAssets, + bool dryRun, bool isBuild, ) async { final errors = []; final ids = {}; - for (final asset in output.encodedAssets) { + for (final asset in encodedAssets) { if (asset.type != DataAsset.type) continue; - _validateDataAssets(config, config.dryRun, DataAsset.fromEncoded(asset), - errors, ids, isBuild); + _validateDataAssets( + config, dryRun, DataAsset.fromEncoded(asset), errors, ids, isBuild); } return errors; } diff --git a/pkgs/native_assets_cli/lib/src/json_utils.dart b/pkgs/native_assets_cli/lib/src/json_utils.dart index 1680a1247..08864113a 100644 --- a/pkgs/native_assets_cli/lib/src/json_utils.dart +++ b/pkgs/native_assets_cli/lib/src/json_utils.dart @@ -82,6 +82,7 @@ extension ListJsonUtils on List { } Map mapAt(int index) => get>(index); + Uri pathAt(int index) => _fileSystemPathToUri(get(index)); } void _throwIfNotExists(String key, Uri value) { diff --git a/pkgs/native_assets_cli/lib/src/link_mode.dart b/pkgs/native_assets_cli/lib/src/link_mode.dart index bd02c1a7d..3b66c07dd 100644 --- a/pkgs/native_assets_cli/lib/src/link_mode.dart +++ b/pkgs/native_assets_cli/lib/src/link_mode.dart @@ -2,8 +2,8 @@ // 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 'code_assets/code_asset.dart'; +import 'config.dart'; /// The link mode for a [CodeAsset]. /// diff --git a/pkgs/native_assets_cli/lib/src/model/build_config.dart b/pkgs/native_assets_cli/lib/src/model/build_config.dart deleted file mode 100644 index 01b3ca7ce..000000000 --- a/pkgs/native_assets_cli/lib/src/model/build_config.dart +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -part of '../api/build_config.dart'; - -final class BuildConfigImpl extends HookConfigImpl implements BuildConfig { - @override - Hook get hook => Hook.build; - - @override - //TODO: Should be removed once migration to `hook/` is complete. - Uri get script { - final hookScript = packageRoot.resolve('hook/').resolve(hook.scriptName); - if (File.fromUri(hookScript).existsSync()) { - return hookScript; - } else { - return packageRoot.resolve(hook.scriptName); - } - } - - @override - String get outputName => 'build_output.json'; - - @override - Object? metadatum(String packageName, String key) { - HookConfigImpl.ensureNotDryRun(dryRun); - return _dependencyMetadata?[packageName]?.metadata[key]; - } - - final Map? _dependencyMetadata; - - @override - bool get linkingEnabled { - if (version == Version(1, 3, 0)) { - return true; - } - return _linkingEnabled as bool; - } - - final bool? _linkingEnabled; - - BuildConfigImpl({ - required super.outputDirectory, - required super.outputDirectoryShared, - required super.packageName, - required super.packageRoot, - Version? version, - super.buildMode, - super.cCompiler, - required super.supportedAssetTypes, - super.targetAndroidNdkApi, - required super.targetArchitecture, - super.targetIOSSdk, - super.targetIOSVersion, - super.targetMacOSVersion, - required super.targetOS, - required super.linkModePreference, - Map? dependencyMetadata, - required bool? linkingEnabled, - super.dryRun, - }) : _dependencyMetadata = dependencyMetadata, - _linkingEnabled = linkingEnabled, - super( - hook: Hook.build, - version: version ?? HookConfigImpl.latestVersion, - ) { - if (this.version < Version(1, 4, 0)) { - assert(linkingEnabled == null); - } else { - assert(linkingEnabled != null); - } - } - - BuildConfigImpl.dryRun({ - required super.outputDirectory, - required super.outputDirectoryShared, - required super.packageName, - required super.packageRoot, - required super.targetOS, - required super.linkModePreference, - required bool? linkingEnabled, - required super.supportedAssetTypes, - }) : _dependencyMetadata = null, - _linkingEnabled = linkingEnabled, - super.dryRun( - hook: Hook.build, - version: HookConfigImpl.latestVersion, - ); - - static BuildConfigImpl fromArguments( - List arguments, { - Map? environment, - Uri? workingDirectory, - }) { - final configPath = getConfigArgument(arguments); - final bytes = File(configPath).readAsBytesSync(); - final linkConfigJson = const Utf8Decoder() - .fuse(const JsonDecoder()) - .convert(bytes) as Map; - return fromJson(linkConfigJson); - } - - static const dependencyMetadataConfigKey = 'dependency_metadata'; - - static const linkingEnabledKey = 'linking_enabled'; - - static BuildConfigImpl fromJson(Map config) { - final dryRun = HookConfigImpl.parseDryRun(config) ?? false; - final targetOS = HookConfigImpl.parseTargetOS(config); - return BuildConfigImpl( - outputDirectory: HookConfigImpl.parseOutDir(config), - outputDirectoryShared: HookConfigImpl.parseOutDirShared(config), - packageName: HookConfigImpl.parsePackageName(config), - packageRoot: HookConfigImpl.parsePackageRoot(config), - buildMode: HookConfigImpl.parseBuildMode(config, dryRun), - targetOS: targetOS, - targetArchitecture: - HookConfigImpl.parseTargetArchitecture(config, dryRun, targetOS), - linkModePreference: HookConfigImpl.parseLinkModePreference(config), - dependencyMetadata: parseDependencyMetadata(config), - linkingEnabled: parseHasLinkPhase(config), - version: HookConfigImpl.parseVersion(config), - cCompiler: HookConfigImpl.parseCCompiler(config, dryRun), - supportedAssetTypes: - HookConfigImpl.parseSupportedEncodedAssetTypes(config), - targetAndroidNdkApi: - HookConfigImpl.parseTargetAndroidNdkApi(config, dryRun, targetOS), - targetIOSSdk: HookConfigImpl.parseTargetIOSSdk(config, dryRun, targetOS), - targetIOSVersion: - HookConfigImpl.parseTargetIosVersion(config, dryRun, targetOS), - targetMacOSVersion: - HookConfigImpl.parseTargetMacOSVersion(config, dryRun, targetOS), - dryRun: dryRun, - ); - } - - static Map? parseDependencyMetadata( - Map config) { - final fileValue = config.optionalMap(dependencyMetadataConfigKey); - if (fileValue == null) { - return null; - } - return fileValue - .map((key, defines) => MapEntry(as(key), defines)) - .map( - (packageName, defines) { - if (defines is! Map) { - throw FormatException("Unexpected value '$defines' for key " - "'$dependencyMetadataConfigKey.$packageName' in config file. " - 'Expected a Map.'); - } - return MapEntry( - packageName, - Metadata(defines - .map((key, value) => MapEntry(as(key), as(value))) - .sortOnKey()), - ); - }, - ).sortOnKey(); - } - - static bool? parseHasLinkPhase(Map config) => - config.optionalBool(linkingEnabledKey); - - @override - Map toJson() => { - ...hookToJson(), - if (!dryRun) ...{ - if (_dependencyMetadata != null && _dependencyMetadata.isNotEmpty) - dependencyMetadataConfigKey: _dependencyMetadata.map( - (packageName, metadata) => - MapEntry(packageName, metadata.toJson()), - ), - }, - if (version >= Version(1, 4, 0)) linkingEnabledKey: linkingEnabled, - }.sortOnKey(); - - @override - bool operator ==(Object other) { - if (super != other) { - return false; - } - if (other is! BuildConfigImpl) { - return false; - } - if (!dryRun && - !const DeepCollectionEquality() - .equals(other._dependencyMetadata, _dependencyMetadata)) { - return false; - } - if (_linkingEnabled != other._linkingEnabled) { - return false; - } - return true; - } - - @override - int get hashCode => Object.hashAll([ - super.hashCode, - linkModePreference, - linkingEnabled, - if (!dryRun) ...[ - const DeepCollectionEquality().hash(_dependencyMetadata), - ], - ]); - - @override - String toString() => 'BuildConfig(${toJson()})'; -} diff --git a/pkgs/native_assets_cli/lib/src/model/hook.dart b/pkgs/native_assets_cli/lib/src/model/hook.dart index a7f3d56f3..a53045c38 100644 --- a/pkgs/native_assets_cli/lib/src/model/hook.dart +++ b/pkgs/native_assets_cli/lib/src/model/hook.dart @@ -12,7 +12,12 @@ enum Hook { final String _scriptName; + const Hook(this._scriptName); + String get scriptName => '$_scriptName.dart'; - const Hook(this._scriptName); + String get outputName => switch (this) { + Hook.build => 'build_output.json', + Hook.link => 'link_output.json' + }; } diff --git a/pkgs/native_assets_cli/lib/src/model/hook_config.dart b/pkgs/native_assets_cli/lib/src/model/hook_config.dart deleted file mode 100644 index e209ab38c..000000000 --- a/pkgs/native_assets_cli/lib/src/model/hook_config.dart +++ /dev/null @@ -1,548 +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 '../api/hook_config.dart'; - -abstract class HookConfigImpl implements HookConfig { - final Hook hook; - - @override - final Uri outputDirectory; - - @override - final Uri outputDirectoryShared; - - @override - final String packageName; - - @override - final Uri packageRoot; - - final Version version; - - final BuildMode? _buildMode; - - @override - BuildMode get buildMode { - ensureNotDryRun(dryRun); - return _buildMode!; - } - - final CCompilerConfig _cCompiler; - - @override - CCompilerConfig get cCompiler { - ensureNotDryRun(dryRun); - return _cCompiler; - } - - @override - final bool dryRun; - - @override - final Iterable supportedAssetTypes; - - final int? _targetAndroidNdkApi; - - @override - final LinkModePreference linkModePreference; - - @override - int? get targetAndroidNdkApi { - ensureNotDryRun(dryRun); - return _targetAndroidNdkApi; - } - - @override - final Architecture? targetArchitecture; - - final IOSSdk? _targetIOSSdk; - - @override - IOSSdk? get targetIOSSdk { - ensureNotDryRun(dryRun); - if (targetOS != OS.iOS) { - throw StateError( - 'This field is not available in if targetOS is not OS.iOS.', - ); - } - return _targetIOSSdk; - } - - final int? _targetIOSVersion; - - @override - int? get targetIOSVersion { - ensureNotDryRun(dryRun); - if (targetOS != OS.iOS) { - throw StateError( - 'This field is not available in if targetOS is not OS.iOS.', - ); - } - return _targetIOSVersion; - } - - final int? _targetMacOSVersion; - - @override - int? get targetMacOSVersion { - ensureNotDryRun(dryRun); - if (targetOS != OS.macOS) { - throw StateError( - 'This field is not available in if targetOS is not OS.macOS.', - ); - } - return _targetMacOSVersion; - } - - @override - final OS targetOS; - - /// Output file name based on the protocol version. - /// - /// Makes newer build hooks work with older Dart SDKs. - String get outputName; - - HookConfigImpl({ - required this.hook, - required this.outputDirectory, - required this.outputDirectoryShared, - required this.packageName, - required this.packageRoot, - required this.version, - required BuildMode? buildMode, - required CCompilerConfig? cCompiler, - required this.supportedAssetTypes, - required int? targetAndroidNdkApi, - required this.targetArchitecture, - required IOSSdk? targetIOSSdk, - required int? targetIOSVersion, - required int? targetMacOSVersion, - required this.linkModePreference, - required this.targetOS, - bool? dryRun, - }) : _targetAndroidNdkApi = targetAndroidNdkApi, - _targetIOSSdk = targetIOSSdk, - _targetIOSVersion = targetIOSVersion, - _targetMacOSVersion = targetMacOSVersion, - _buildMode = buildMode, - _cCompiler = cCompiler ?? CCompilerConfig(), - dryRun = dryRun ?? false; - - HookConfigImpl.dryRun({ - required this.hook, - required this.outputDirectory, - required this.outputDirectoryShared, - required this.packageName, - required this.packageRoot, - required this.version, - required this.supportedAssetTypes, - required this.linkModePreference, - required this.targetOS, - }) : _cCompiler = CCompilerConfig(), - dryRun = true, - targetArchitecture = null, - _buildMode = null, - _targetAndroidNdkApi = null, - _targetIOSSdk = null, - _targetIOSVersion = null, - _targetMacOSVersion = null; - - Uri get outputFile => outputDirectory.resolve(outputName); - - // This is currently overriden by [BuildConfig], do account for older versions - // still using a top-level build.dart. - Uri get script => packageRoot.resolve('hook/').resolve(hook.scriptName); - - String toJsonString() => const JsonEncoder.withIndent(' ').convert(toJson()); - - static const outDirConfigKey = 'out_dir'; - static const outDirSharedConfigKey = 'out_dir_shared'; - static const packageNameConfigKey = 'package_name'; - static const packageRootConfigKey = 'package_root'; - static const _versionKey = 'version'; - static const targetAndroidNdkApiConfigKey = 'target_android_ndk_api'; - static const targetIOSVersionConfigKey = 'target_ios_version'; - static const targetMacOSVersionConfigKey = 'target_macos_version'; - static const dryRunConfigKey = 'dry_run'; - static const supportedAssetTypesKey = 'supported_asset_types'; - - Map toJson(); - - Map hookToJson() { - late Map cCompilerJson; - if (!dryRun) { - cCompilerJson = cCompiler.toJson(); - } - - return { - outDirConfigKey: outputDirectory.toFilePath(), - outDirSharedConfigKey: outputDirectoryShared.toFilePath(), - packageNameConfigKey: packageName, - packageRootConfigKey: packageRoot.toFilePath(), - _targetOSConfigKey: targetOS.toString(), - supportedAssetTypesKey: supportedAssetTypes, - _versionKey: version.toString(), - if (dryRun) dryRunConfigKey: dryRun, - if (!dryRun) ...{ - _buildModeConfigKey: buildMode.toString(), - _targetArchitectureKey: targetArchitecture.toString(), - if (targetOS == OS.iOS && targetIOSSdk != null) - _targetIOSSdkConfigKey: targetIOSSdk.toString(), - if (targetOS == OS.iOS && targetIOSVersion != null) - targetIOSVersionConfigKey: targetIOSVersion!, - if (targetOS == OS.macOS && targetMacOSVersion != null) - targetMacOSVersionConfigKey: targetMacOSVersion!, - if (targetAndroidNdkApi != null) - targetAndroidNdkApiConfigKey: targetAndroidNdkApi!, - if (cCompilerJson.isNotEmpty) _compilerConfigKey: cCompilerJson, - }, - _linkModePreferenceConfigKey: linkModePreference.toString(), - }.sortOnKey(); - } - - static Version parseVersion(Map config) { - final version = Version.parse(config.string('version')); - if (version.major > latestVersion.major) { - throw FormatException( - 'The config version $version is newer than this ' - 'package:native_assets_cli config version $latestVersion, ' - 'please update native_assets_cli.', - ); - } - if (version.major < latestVersion.major) { - throw FormatException( - 'The config version $version is newer than this ' - 'package:native_assets_cli config version $latestVersion, ' - 'please update the Dart or Flutter SDK.', - ); - } - return version; - } - - static bool? parseDryRun(Map config) => - config.optionalBool(dryRunConfigKey); - - static Uri parseOutDir(Map config) => - config.path(outDirConfigKey, mustExist: true); - - static Uri parseOutDirShared(Map config) { - final configResult = - config.optionalPath(outDirSharedConfigKey, mustExist: true); - if (configResult != null) { - return configResult; - } - // Backwards compatibility, create a directory next to the output dir. - // This is will not be shared so caching doesn't work, but it will make - // the newer hooks not crash. - final outDir = config.path(outDirConfigKey); - final outDirShared = outDir.resolve('../out_shared/'); - Directory.fromUri(outDirShared).createSync(); - return outDirShared; - } - - static String parsePackageName(Map config) => - config.string(packageNameConfigKey); - - static Uri parsePackageRoot(Map config) => - config.path(packageRootConfigKey, mustExist: true); - - static BuildMode? parseBuildMode(Map config, bool dryRun) { - if (dryRun) { - _throwIfNotNullInDryRun(config, _buildModeConfigKey); - return null; - } else { - return BuildMode.fromString( - config.string( - _buildModeConfigKey, - validValues: BuildMode.values.map((e) => '$e'), - ), - ); - } - } - - static LinkModePreference parseLinkModePreference( - Map config) => - LinkModePreference.fromString( - config.string( - _linkModePreferenceConfigKey, - validValues: LinkModePreference.values.map((e) => '$e'), - ), - ); - - static OS parseTargetOS(Map config) => OS.fromString( - config.string( - _targetOSConfigKey, - validValues: OS.values.map((e) => '$e'), - ), - ); - - static Architecture? parseTargetArchitecture( - Map config, - bool dryRun, - OS? targetOS, - ) { - if (dryRun) { - _throwIfNotNullInDryRun(config, _targetArchitectureKey); - return null; - } else { - final validArchitectures = [ - if (targetOS == null) - ...Architecture.values - else - for (final target in Target.values) - if (target.os == targetOS) target.architecture - ]; - return Architecture.fromString( - config.string( - _targetArchitectureKey, - validValues: validArchitectures.map((e) => '$e'), - ), - ); - } - } - - static IOSSdk? parseTargetIOSSdk( - Map config, bool dryRun, OS? targetOS) { - if (dryRun) { - _throwIfNotNullInDryRun(config, _targetIOSSdkConfigKey); - return null; - } else { - return targetOS == OS.iOS - ? IOSSdk.fromString( - config.string( - _targetIOSSdkConfigKey, - validValues: IOSSdk.values.map((e) => '$e'), - ), - ) - : null; - } - } - - static int? parseTargetAndroidNdkApi( - Map config, - bool dryRun, - OS? targetOS, - ) { - if (dryRun) { - _throwIfNotNullInDryRun(config, targetAndroidNdkApiConfigKey); - return null; - } else { - return (targetOS == OS.android) - ? config.int(targetAndroidNdkApiConfigKey) - : null; - } - } - - static int? parseTargetIosVersion( - Map config, - bool dryRun, - OS? targetOS, - ) { - if (dryRun) { - _throwIfNotNullInDryRun(config, targetIOSVersionConfigKey); - return null; - } else { - return (targetOS == OS.iOS) - ? config.optionalInt(targetIOSVersionConfigKey) - : null; - } - } - - static int? parseTargetMacOSVersion( - Map config, - bool dryRun, - OS? targetOS, - ) { - if (dryRun) { - _throwIfNotNullInDryRun(config, targetMacOSVersionConfigKey); - return null; - } else { - return (targetOS == OS.macOS) - ? config.optionalInt(targetMacOSVersionConfigKey) - : null; - } - } - - static List parseSupportedEncodedAssetTypes( - Map config) => - config.optionalStringList(supportedAssetTypesKey) ?? []; - - static CCompilerConfig parseCCompiler( - Map config, bool dryRun) { - if (dryRun) { - _throwIfNotNullInDryRun(config, _compilerConfigKey); - } - - final cCompilerJson = - config.getOptional>(_compilerConfigKey); - if (cCompilerJson == null) return CCompilerConfig(); - - return CCompilerConfig.fromJson(cCompilerJson); - } - - static void _throwIfNotNullInDryRun( - Map config, String key) { - final object = config.getOptional(key); - if (object != null) { - throw const FormatException('''This field is not available in dry runs. -In Flutter projects, native builds are generated per OS which target multiple -architectures, build modes, etc. Therefore, the list of native assets produced -can _only_ depend on OS.'''); - } - } - - static void ensureNotDryRun(bool dryRun) { - if (dryRun) { - throw StateError('''This field is not available in dry runs. -In Flutter projects, native builds are generated per OS which target multiple -architectures, build modes, etc. Therefore, the list of native assets produced -can _only_ depend on OS.'''); - } - } - - @override - bool operator ==(Object other) { - if (other is! HookConfigImpl) { - return false; - } - if (other.outputDirectory != outputDirectory) return false; - if (other.outputDirectoryShared != outputDirectoryShared) return false; - if (other.packageName != packageName) return false; - if (other.packageRoot != packageRoot) return false; - if (other.dryRun != dryRun) return false; - if (other.targetOS != targetOS) return false; - if (other.linkModePreference != linkModePreference) return false; - if (!const DeepCollectionEquality() - .equals(other.supportedAssetTypes, supportedAssetTypes)) { - return false; - } - if (!dryRun) { - if (other.buildMode != buildMode) return false; - if (other.targetArchitecture != targetArchitecture) return false; - if (targetOS == OS.iOS && other.targetIOSSdk != targetIOSSdk) { - return false; - } - if (other.targetAndroidNdkApi != targetAndroidNdkApi) return false; - if (other.cCompiler != cCompiler) return false; - } - return true; - } - - @override - int get hashCode => Object.hashAll([ - outputDirectory, - outputDirectoryShared, - packageName, - packageRoot, - targetOS, - dryRun, - if (!dryRun) ...[ - buildMode, - targetArchitecture, - if (targetOS == OS.iOS) targetIOSSdk, - targetAndroidNdkApi, - cCompiler, - ], - ]); - - /// Constructs a checksum for a [BuildConfigImpl] based on the fields of a - /// buildconfig that influence the build. - /// - /// This can be used for an [outputDirectory], but should not be used for - /// dry-runs. - /// - /// In particular, it only takes the package name from [packageRoot], so that - /// the hash is equal across checkouts and ignores [outputDirectory] itself. - static String checksum({ - required String packageName, - required Uri packageRoot, - required Architecture targetArchitecture, - required OS targetOS, - required BuildMode buildMode, - IOSSdk? targetIOSSdk, - int? targetAndroidNdkApi, - CCompilerConfig? cCompiler, - required LinkModePreference linkModePreference, - Map? dependencyMetadata, - required Iterable supportedAssetTypes, - Version? version, - required Hook hook, - required bool? linkingEnabled, - }) { - final input = [ - version ?? latestVersion, - packageName, - targetArchitecture.toString(), - targetOS.toString(), - targetIOSSdk.toString(), - targetAndroidNdkApi.toString(), - buildMode.toString(), - linkModePreference.toString(), - cCompiler?.archiver.toString(), - cCompiler?.compiler.toString(), - cCompiler?.envScript.toString(), - cCompiler?.envScriptArgs.toString(), - cCompiler?.linker.toString(), - if (dependencyMetadata != null) - for (final entry in dependencyMetadata.entries) ...[ - entry.key, - json.encode(entry.value.toJson()), - ], - ...supportedAssetTypes, - hook.name, - linkingEnabled, - ].join('###'); - final sha256String = sha256.convert(utf8.encode(input)).toString(); - // 256 bit hashes lead to 64 hex character strings. - // To avoid overflowing file paths limits, only use 32. - // Using 16 hex characters would also be unlikely to have collisions. - const nameLength = 32; - return sha256String.substring(0, nameLength); - } - - static String checksumDryRun({ - required String packageName, - required Uri packageRoot, - required OS targetOS, - required LinkModePreference linkModePreference, - Version? version, - Iterable? supportedAssetTypes, - required Hook hook, - required bool? linkingEnabled, - }) { - final input = [ - version ?? latestVersion, - packageName, - targetOS.toString(), - linkModePreference.toString(), - ...supportedAssetTypes ?? [CodeAsset.type], - hook.name, - linkingEnabled, - ].join('###'); - final sha256String = sha256.convert(utf8.encode(input)).toString(); - // 256 bit hashes lead to 64 hex character strings. - // To avoid overflowing file paths limits, only use 32. - // Using 16 hex characters would also be unlikely to have collisions. - const nameLength = 32; - return sha256String.substring(0, nameLength); - } - - /// The version of [HookConfigImpl]. - /// - /// This class is used in the protocol between the Dart and Flutter SDKs - /// and packages through build hook invocations. - /// - /// If we ever were to make breaking changes, it would be useful to give - /// proper error messages rather than just fail to parse the JSON - /// representation in the protocol. - static Version latestVersion = Version(1, 5, 0); -} - -const String _compilerConfigKey = 'c_compiler'; -const String _buildModeConfigKey = 'build_mode'; -const String _targetOSConfigKey = 'target_os'; -const String _targetArchitectureKey = 'target_architecture'; -const String _targetIOSSdkConfigKey = 'target_ios_sdk'; -const String _linkModePreferenceConfigKey = 'link_mode_preference'; diff --git a/pkgs/native_assets_cli/lib/src/model/hook_output.dart b/pkgs/native_assets_cli/lib/src/model/hook_output.dart deleted file mode 100644 index 0d48d9fb7..000000000 --- a/pkgs/native_assets_cli/lib/src/model/hook_output.dart +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -part of '../api/build_output.dart'; - -final class HookOutputImpl implements BuildOutput, LinkOutput { - @override - final DateTime timestamp; - - final List _assets; - - @override - Iterable get encodedAssets => _assets; - - final Map> _assetsForLinking; - - @override - Map> get encodedAssetsForLinking => - _assetsForLinking; - - final Dependencies _dependencies; - - Dependencies get dependenciesModel => _dependencies; - - @override - Iterable get dependencies => _dependencies.dependencies; - - final Metadata metadata; - - HookOutputImpl({ - DateTime? timestamp, - Iterable? encodedAssets, - Map>? encodedAssetsForLinking, - Dependencies? dependencies, - Metadata? metadata, - }) : timestamp = (timestamp ?? DateTime.now()).roundDownToSeconds(), - _assets = [ - ...?encodedAssets, - ], - _assetsForLinking = encodedAssetsForLinking ?? {}, - // ignore: prefer_const_constructors - _dependencies = dependencies ?? Dependencies([]), - // ignore: prefer_const_constructors - metadata = metadata ?? Metadata({}); - - @override - void addDependency(Uri dependency) => - _dependencies.dependencies.add(dependency); - - @override - void addDependencies(Iterable dependencies) => - _dependencies.dependencies.addAll(dependencies); - - static const _assetsKey = 'assets'; - static const _assetsForLinkingKey = 'assetsForLinking'; - static const _dependenciesKey = 'dependencies'; - static const _metadataKey = 'metadata'; - static const _timestampKey = 'timestamp'; - static const _versionKey = 'version'; - - factory HookOutputImpl.fromJsonString(String jsonString) { - final Object? json = jsonDecode(jsonString); - return HookOutputImpl.fromJson(as>(json)); - } - - factory HookOutputImpl.fromJson(Map jsonMap) { - final outputVersion = Version.parse(get(jsonMap, 'version')); - if (outputVersion.major > latestVersion.major) { - throw FormatException( - 'The output version $outputVersion is newer than the ' - 'package:native_assets_cli config version $latestVersion in Dart or ' - 'Flutter, please update the Dart or Flutter SDK.', - ); - } - if (outputVersion.major < latestVersion.major) { - throw FormatException( - 'The output version $outputVersion is newer than this ' - 'package:native_assets_cli config version $latestVersion in Dart or ' - 'Flutter, please update native_assets_cli.', - ); - } - return HookOutputImpl( - timestamp: DateTime.parse(get(jsonMap, _timestampKey)), - 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: - Metadata.fromJson(get?>(jsonMap, _metadataKey)), - ); - } - - Map toJson(Version version) => { - _timestampKey: timestamp.toString(), - if (_assets.isNotEmpty) - _assetsKey: [ - for (final asset in encodedAssets) asset.toJson(), - ], - if (_assetsForLinking.isNotEmpty) - _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(), - _versionKey: version.toString(), - }..sortOnKey(); - - String toJsonString(Version version) => - const JsonEncoder.withIndent(' ').convert(toJson(version)); - - /// The version of [HookOutputImpl]. - /// - /// This class is used in the protocol between the Dart and Flutter SDKs and - /// packages through build hook invocations. - /// - /// If we ever were to make breaking changes, it would be useful to give - /// proper error messages rather than just fail to parse the JSON - /// representation in the protocol. - /// - /// [BuildOutput.latestVersion] is tied to [BuildConfig.latestVersion]. This - /// enables making the JSON serialization in build hooks dependent on the - /// version of the Dart or Flutter SDK. When there is a need to split the - /// versions of BuildConfig and BuildOutput, the BuildConfig should start - /// passing the highest supported version of BuildOutput. - static Version latestVersion = HookConfigImpl.latestVersion; - - /// Reads the JSON file from [file]. - static HookOutputImpl? readFromFile({required Uri file}) { - final buildOutputFile = File.fromUri(file); - if (buildOutputFile.existsSync()) { - return HookOutputImpl.fromJsonString(buildOutputFile.readAsStringSync()); - } - - return null; - } - - /// Writes the [toJsonString] to the output file specified in the [config]. - Future writeToFile({required HookConfigImpl config}) async { - final configVersion = config.version; - final jsonString = toJsonString(configVersion); - await File.fromUri(config.outputFile) - .writeAsStringCreateDirectory(jsonString); - } - - @override - String toString() => toJsonString(HookConfigImpl.latestVersion); - - @override - bool operator ==(Object other) { - if (other is! HookOutputImpl) { - return false; - } - return other.timestamp == timestamp && - const ListEquality().equals(other._assets, _assets) && - other._dependencies == _dependencies && - other.metadata == metadata; - } - - @override - int get hashCode => Object.hash( - timestamp.hashCode, - const ListEquality().hash(_assets), - _dependencies, - metadata, - ); - - @override - void addMetadatum(String key, Object value) { - metadata.metadata[key] = value; - } - - @override - void addMetadata(Map metadata) { - this.metadata.metadata.addAll(metadata); - } - - Metadata get metadataModel => metadata; - - @override - void addEncodedAsset(EncodedAsset asset, {String? linkInPackage}) { - _getEncodedAssetList(linkInPackage).add(asset); - } - - @override - void addEncodedAssets(Iterable assets, - {String? linkInPackage}) { - _getEncodedAssetList(linkInPackage).addAll(assets.cast()); - } - - List _getEncodedAssetList(String? linkInPackage) => - linkInPackage == null - ? _assets - : (_assetsForLinking[linkInPackage] ??= []); - - HookOutputImpl copyWith({Iterable? encodedAssets}) => - HookOutputImpl( - timestamp: timestamp, - 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 deleted file mode 100644 index 0f46876a7..000000000 --- a/pkgs/native_assets_cli/lib/src/model/link_config.dart +++ /dev/null @@ -1,153 +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 '../api/link_config.dart'; - -/// The input to the linking script. -/// -/// 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 encodedAssets; - - // TODO: Placeholder for the resources.json file URL. We don't want to change - // native_assets_builder when implementing the parsing. - @override - final Uri? recordedUsagesFile; - - LinkConfigImpl({ - required this.encodedAssets, - this.recordedUsagesFile, - required super.outputDirectory, - required super.outputDirectoryShared, - required super.packageName, - required super.packageRoot, - Version? version, - required super.buildMode, - super.cCompiler, - required super.supportedAssetTypes, - super.targetAndroidNdkApi, - super.targetArchitecture, - super.targetIOSSdk, - super.targetIOSVersion, - super.targetMacOSVersion, - required super.targetOS, - required super.linkModePreference, - super.dryRun, - }) : super( - hook: Hook.link, - version: version ?? HookConfigImpl.latestVersion, - ); - - LinkConfigImpl.dryRun({ - required this.encodedAssets, - this.recordedUsagesFile, - required super.outputDirectory, - required super.outputDirectoryShared, - required super.packageName, - required super.packageRoot, - Version? version, - required super.supportedAssetTypes, - required super.linkModePreference, - required super.targetOS, - }) : super.dryRun( - hook: Hook.link, - version: version ?? HookConfigImpl.latestVersion, - ); - - @override - Hook get hook => Hook.link; - - @override - String get outputName => 'link_output.json'; - - @override - Map toJson() => { - ...hookToJson(), - if (recordedUsagesFile != null) - resourceIdentifierKey: recordedUsagesFile!.toFilePath(), - if (encodedAssets.isNotEmpty) - assetsKey: [ - for (final asset in encodedAssets) asset.toJson(), - ], - }.sortOnKey(); - - static LinkConfig fromArguments(List arguments) { - final configPath = getConfigArgument(arguments); - final bytes = File(configPath).readAsBytesSync(); - final linkConfigJson = const Utf8Decoder() - .fuse(const JsonDecoder()) - .convert(bytes) as Map; - return fromJson(linkConfigJson); - } - - static LinkConfigImpl fromJson(Map config) { - final dryRun = HookConfigImpl.parseDryRun(config) ?? false; - final targetOS = HookConfigImpl.parseTargetOS(config); - return LinkConfigImpl( - outputDirectory: HookConfigImpl.parseOutDir(config), - outputDirectoryShared: HookConfigImpl.parseOutDirShared(config), - packageName: HookConfigImpl.parsePackageName(config), - packageRoot: HookConfigImpl.parsePackageRoot(config), - buildMode: HookConfigImpl.parseBuildMode(config, dryRun), - targetOS: targetOS, - targetArchitecture: - HookConfigImpl.parseTargetArchitecture(config, dryRun, targetOS), - linkModePreference: HookConfigImpl.parseLinkModePreference(config), - version: HookConfigImpl.parseVersion(config), - cCompiler: HookConfigImpl.parseCCompiler(config, dryRun), - supportedAssetTypes: - HookConfigImpl.parseSupportedEncodedAssetTypes(config), - targetAndroidNdkApi: - HookConfigImpl.parseTargetAndroidNdkApi(config, dryRun, targetOS), - targetIOSSdk: HookConfigImpl.parseTargetIOSSdk(config, dryRun, targetOS), - targetIOSVersion: - HookConfigImpl.parseTargetIosVersion(config, dryRun, targetOS), - targetMacOSVersion: - HookConfigImpl.parseTargetMacOSVersion(config, dryRun, targetOS), - encodedAssets: [ - for (final json in config.optionalList(assetsKey) ?? []) - EncodedAsset.fromJson(json as Map), - ], - recordedUsagesFile: parseRecordedUsagesUri(config), - dryRun: dryRun, - ); - } - - static Uri? parseRecordedUsagesUri(Map config) => - config.optionalPath(resourceIdentifierKey); - - @override - bool operator ==(Object other) { - if (super != other) { - return false; - } - if (other is! LinkConfigImpl) { - return false; - } - if (other.recordedUsagesFile != recordedUsagesFile) { - return false; - } - if (!const DeepCollectionEquality() - .equals(other.encodedAssets, encodedAssets)) { - return false; - } - return true; - } - - @override - int get hashCode => Object.hashAll([ - super.hashCode, - recordedUsagesFile, - const DeepCollectionEquality().hash(encodedAssets), - ]); - - @override - String toString() => 'LinkConfig(${toJson()})'; -} diff --git a/pkgs/native_assets_cli/lib/test.dart b/pkgs/native_assets_cli/lib/test.dart index af750284a..ca2b329d2 100644 --- a/pkgs/native_assets_cli/lib/test.dart +++ b/pkgs/native_assets_cli/lib/test.dart @@ -1,5 +1,107 @@ -// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -export 'src/api/test.dart' show testBuildHook; +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:meta/meta.dart' show isTest; +import 'package:test/test.dart'; +import 'package:yaml/yaml.dart'; + +import 'native_assets_cli_internal.dart'; + +export 'native_assets_cli_internal.dart'; +export 'src/code_assets/code_asset_bundling.dart'; + +@isTest +Future testBuildHook({ + required String description, + required void Function(BuildConfigBuilder) extraConfigSetup, + required FutureOr Function(List arguments) mainMethod, + required FutureOr Function(BuildConfig config, BuildOutput output) + check, + BuildMode? buildMode, + OS? targetOS, + List? supportedAssetTypes, + bool? linkingEnabled, +}) async { + test( + description, + () async { + final tempDir = await _tempDirForTest(); + final outputDirectory = tempDir.resolve('output/'); + final outputDirectoryShared = tempDir.resolve('output_shared/'); + + await Directory.fromUri(outputDirectory).create(); + await Directory.fromUri(outputDirectoryShared).create(); + + final configBuilder = BuildConfigBuilder(); + configBuilder + ..setupHookConfig( + packageRoot: Directory.current.uri, + packageName: _readPackageNameFromPubspec(), + targetOS: targetOS ?? OS.current, + supportedAssetTypes: supportedAssetTypes ?? [], + buildMode: buildMode ?? BuildMode.release, + ) + ..setupBuildConfig( + dryRun: false, + linkingEnabled: true, + ) + ..setupBuildRunConfig( + outputDirectory: outputDirectory, + outputDirectoryShared: outputDirectoryShared, + ); + extraConfigSetup(configBuilder); + + final config = BuildConfig(configBuilder.json); + + final configUri = tempDir.resolve(Hook.build.outputName); + _writeJsonTo(configUri, config.json); + await mainMethod(['--config=${configUri.toFilePath()}']); + final output = BuildOutput( + _readJsonFrom(config.outputDirectory.resolve(Hook.build.outputName))); + + // Test conformance of protocol invariants. + expect(await validateBuildOutput(config, output), isEmpty); + + // Run user-defined tests. + check(config, output); + }, + ); +} + +void _writeJsonTo(Uri uri, Map json) { + final encoder = const JsonEncoder().fuse(const Utf8Encoder()); + File.fromUri(uri).writeAsBytesSync(encoder.convert(json)); +} + +Map _readJsonFrom(Uri uri) { + final decoder = const Utf8Decoder().fuse(const JsonDecoder()); + final bytes = File.fromUri(uri).readAsBytesSync(); + return decoder.convert(bytes) as Map; +} + +String _readPackageNameFromPubspec() { + final uri = Directory.current.uri.resolve('pubspec.yaml'); + final readAsString = File.fromUri(uri).readAsStringSync(); + final yaml = loadYaml(readAsString) as YamlMap; + return yaml['name'] as String; +} + +const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES'; + +Future _tempDirForTest({String? prefix, bool keepTemp = false}) async { + final tempDir = await Directory.systemTemp.createTemp(prefix); + // Deal with Windows temp folder aliases. + final tempUri = + Directory(await tempDir.resolveSymbolicLinks()).uri.normalizePath(); + if ((!Platform.environment.containsKey(keepTempKey) || + Platform.environment[keepTempKey]!.isEmpty) && + !keepTemp) { + addTearDown(() => tempDir.delete(recursive: true)); + } + return tempUri; +} diff --git a/pkgs/native_assets_cli/test/api/build_config_test.dart b/pkgs/native_assets_cli/test/api/build_config_test.dart deleted file mode 100644 index 613c60dce..000000000 --- a/pkgs/native_assets_cli/test/api/build_config_test.dart +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// ignore_for_file: deprecated_member_use_from_same_package - -import 'dart:io'; - -import 'package:native_assets_cli/native_assets_cli.dart'; -import 'package:native_assets_cli/src/api/build_config.dart'; -import 'package:test/test.dart'; - -void main() async { - late Uri tempUri; - late Uri outDirUri; - late Uri outDir2Uri; - late Uri outputDirectoryShared; - late Uri outputDirectoryShared2; - late String packageName; - late Uri packageRootUri; - late Uri fakeClang; - late Uri fakeLd; - late Uri fakeAr; - late Uri fakeCl; - late Uri fakeVcVars; - - setUp(() async { - tempUri = (await Directory.systemTemp.createTemp()).uri; - outDirUri = tempUri.resolve('out1/'); - await Directory.fromUri(outDirUri).create(); - outDir2Uri = tempUri.resolve('out2/'); - await Directory.fromUri(outDir2Uri).create(); - outputDirectoryShared = tempUri.resolve('out_shared1/'); - await Directory.fromUri(outputDirectoryShared).create(); - outputDirectoryShared2 = tempUri.resolve('out_shared2/'); - await Directory.fromUri(outputDirectoryShared2).create(); - packageName = 'my_package'; - packageRootUri = tempUri.resolve('$packageName/'); - await Directory.fromUri(packageRootUri).create(); - fakeClang = tempUri.resolve('fake_clang'); - await File.fromUri(fakeClang).create(); - fakeLd = tempUri.resolve('fake_ld'); - await File.fromUri(fakeLd).create(); - fakeAr = tempUri.resolve('fake_ar'); - await File.fromUri(fakeAr).create(); - fakeCl = tempUri.resolve('cl.exe'); - await File.fromUri(fakeCl).create(); - fakeVcVars = tempUri.resolve('vcvarsall.bat'); - await File.fromUri(fakeVcVars).create(); - }); - - tearDown(() async { - await Directory.fromUri(tempUri).delete(recursive: true); - }); - - test('BuildConfig ==', () { - final config1 = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - archiver: fakeAr, - ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - - final config2 = BuildConfig.build( - outputDirectory: outDir2Uri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - - expect(config1, equals(config1)); - expect(config1 == config2, false); - expect(config1.outputDirectory != config2.outputDirectory, true); - expect(config1.packageRoot, config2.packageRoot); - expect(config1.targetArchitecture == config2.targetArchitecture, true); - expect(config1.targetOS != config2.targetOS, true); - expect(config1.targetIOSSdk, IOSSdk.iPhoneOS); - expect(() => config2.targetIOSSdk, throwsStateError); - expect(config1.cCompiler.compiler != config2.cCompiler.compiler, true); - expect(config1.cCompiler.linker != config2.cCompiler.linker, true); - expect(config1.cCompiler.archiver != config2.cCompiler.archiver, true); - expect(config1.cCompiler.envScript == config2.cCompiler.envScript, true); - expect(config1.cCompiler.envScriptArgs == config2.cCompiler.envScriptArgs, - true); - expect(config1.cCompiler != config2.cCompiler, true); - expect(config1.linkModePreference, config2.linkModePreference); - expect(config1.supportedAssetTypes, config2.supportedAssetTypes); - expect(config1.linkingEnabled, config2.linkingEnabled); - }); - - test('BuildConfig fromConfig', () { - final buildConfig2 = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - 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', - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_android_ndk_api': 30, - 'target_architecture': 'arm64', - 'target_os': 'android', - 'version': BuildOutput.latestVersion.toString(), - }; - - final fromConfig = BuildConfigImpl.fromJson(config); - expect(fromConfig, equals(buildConfig2)); - }); - - test('BuildConfig.dryRun', () { - final buildConfig2 = BuildConfig.dryRun( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetOS: OS.android, - linkModePreference: LinkModePreference.preferStatic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: true, - ); - - final config = { - 'dry_run': true, - 'supported_asset_types': [CodeAsset.type], - 'linking_enabled': true, - 'link_mode_preference': 'prefer-static', - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_os': 'android', - 'version': BuildOutput.latestVersion.toString(), - }; - - final fromConfig = BuildConfigImpl.fromJson(config); - expect(fromConfig, equals(buildConfig2)); - }); - - test('BuildConfig == dependency metadata', () { - final buildConfig1 = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - dependencyMetadata: { - 'bar': { - 'key': 'value', - 'foo': ['asdf', 'fdsa'], - }, - 'foo': { - 'key': 321, - }, - }, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - final buildConfig2 = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - dependencyMetadata: { - 'bar': { - 'key': 'value', - }, - 'foo': { - 'key': 123, - }, - }, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - expect(buildConfig1, equals(buildConfig1)); - expect(buildConfig1 == buildConfig2, false); - expect(buildConfig1.hashCode == buildConfig2.hashCode, false); - - expect(buildConfig1.metadatum('bar', 'key'), 'value'); - }); - - test('BuildConfig == hasLinkConfig', () { - final buildConfig1 = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.x64, - targetOS: OS.windows, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - linkingEnabled: true, - supportedAssetTypes: [CodeAsset.type], - ); - - final buildConfig2 = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.x64, - targetOS: OS.windows, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - expect(buildConfig1, equals(buildConfig1)); - expect(buildConfig1.hashCode, isNot(buildConfig2.hashCode)); - expect(buildConfig1, isNot(buildConfig2)); - }); - - test('BuildConfig fromArgs', () async { - final buildConfig = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - final configFileContents = (buildConfig as BuildConfigImpl).toJsonString(); - final configUri = tempUri.resolve('config.json'); - final configFile = File.fromUri(configUri); - await configFile.writeAsString(configFileContents); - final buildConfigFromArgs = BuildConfig( - ['--config', configUri.toFilePath()], - environment: {}, // Don't inherit the test environment. - ); - expect(buildConfigFromArgs, buildConfig); - }); - - test('BuildConfig.version', () { - BuildConfig.latestVersion.toString(); - }); -} diff --git a/pkgs/native_assets_cli/test/api/build_output_test.dart b/pkgs/native_assets_cli/test/api/build_output_test.dart deleted file mode 100644 index 9bd10f474..000000000 --- a/pkgs/native_assets_cli/test/api/build_output_test.dart +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:io'; - -import 'package:native_assets_cli/native_assets_cli.dart'; -import 'package:test/test.dart'; - -void main() { - late Uri tempUri; - - setUp(() async { - tempUri = (await Directory.systemTemp.createTemp()).uri; - }); - - tearDown(() async { - await Directory.fromUri(tempUri).delete(recursive: true); - }); - - test('BuildOutput constructor', () { - BuildOutput( - timestamp: DateTime.parse('2022-11-10 13:25:01.000'), - encodedAssets: [ - CodeAsset( - package: 'my_package', - name: 'foo', - file: Uri(path: 'path/to/libfoo.so'), - 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'), - ], - // ignore: deprecated_member_use_from_same_package - metadata: { - 'key': 'value', - }, - ); - }); -} diff --git a/pkgs/native_assets_cli/test/api/build_test.dart b/pkgs/native_assets_cli/test/api/build_test.dart index baae8eba6..1e9e73fb0 100644 --- a/pkgs/native_assets_cli/test/api/build_test.dart +++ b/pkgs/native_assets_cli/test/api/build_test.dart @@ -2,11 +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. +import 'dart:convert'; import 'dart:io'; import 'package:file_testing/file_testing.dart'; -import 'package:native_assets_cli/native_assets_cli.dart'; -import 'package:native_assets_cli/src/api/build_config.dart'; +import 'package:native_assets_cli/native_assets_cli.dart' show build; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:test/test.dart'; void main() async { @@ -15,53 +16,38 @@ void main() async { late Uri outputDirectoryShared; late String packageName; late Uri packageRootUri; - late Uri fakeClang; - late Uri fakeLd; - late Uri fakeAr; - late Uri fakeCl; - late Uri fakeVcVars; late Uri buildConfigUri; - late BuildConfig config1; + late BuildConfig config; setUp(() async { tempUri = (await Directory.systemTemp.createTemp()).uri; outDirUri = tempUri.resolve('out1/'); await Directory.fromUri(outDirUri).create(); outputDirectoryShared = tempUri.resolve('out_shared1/'); - await Directory.fromUri(outputDirectoryShared).create(); packageName = 'my_package'; packageRootUri = tempUri.resolve('$packageName/'); await Directory.fromUri(packageRootUri).create(); - fakeClang = tempUri.resolve('fake_clang'); - await File.fromUri(fakeClang).create(); - fakeLd = tempUri.resolve('fake_ld'); - await File.fromUri(fakeLd).create(); - fakeAr = tempUri.resolve('fake_ar'); - await File.fromUri(fakeAr).create(); - fakeCl = tempUri.resolve('cl.exe'); - await File.fromUri(fakeCl).create(); - fakeVcVars = tempUri.resolve('vcvarsall.bat'); - await File.fromUri(fakeVcVars).create(); - config1 = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - archiver: fakeAr, - ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferDynamic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - final configJson = (config1 as BuildConfigImpl).toJsonString(); + final configBuilder = BuildConfigBuilder(); + configBuilder + ..setupHookConfig( + packageRoot: tempUri, + packageName: packageName, + targetOS: OS.iOS, + supportedAssetTypes: [CodeAsset.type], + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + dryRun: false, + linkingEnabled: false, + ) + ..setupBuildRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outputDirectoryShared, + ); + config = BuildConfig(configBuilder.json); + + final configJson = json.encode(config.json); buildConfigUri = tempUri.resolve('build_config.json'); await File.fromUri(buildConfigUri).writeAsString(configJson); }); @@ -72,7 +58,7 @@ void main() async { output.addDependency(packageRootUri.resolve('foo')); }); final buildOutputUri = - outDirUri.resolve((config1 as BuildConfigImpl).outputName); + config.outputDirectory.resolve(Hook.build.outputName); expect(File.fromUri(buildOutputUri), exists); }); } diff --git a/pkgs/native_assets_cli/test/api/link_config_test.dart b/pkgs/native_assets_cli/test/api/link_config_test.dart deleted file mode 100644 index bd739b65b..000000000 --- a/pkgs/native_assets_cli/test/api/link_config_test.dart +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:io'; - -import 'package:native_assets_cli/native_assets_cli.dart'; -import 'package:native_assets_cli/src/api/link_config.dart'; -import 'package:test/test.dart'; - -void main() async { - late Uri tempUri; - late Uri outDirUri; - late Uri outDir2Uri; - late Uri outputDirectoryShared; - late String packageName; - late Uri packageRootUri; - late Uri fakeClang; - late Uri fakeLd; - late Uri fakeAr; - late Uri fakeCl; - late Uri fakeVcVars; - - setUp(() async { - tempUri = (await Directory.systemTemp.createTemp()).uri; - outDirUri = tempUri.resolve('out1/'); - await Directory.fromUri(outDirUri).create(); - outDir2Uri = tempUri.resolve('out2/'); - await Directory.fromUri(outDir2Uri).create(); - outputDirectoryShared = tempUri.resolve('out_shared1/'); - await Directory.fromUri(outputDirectoryShared).create(); - packageName = 'my_package'; - packageRootUri = tempUri.resolve('$packageName/'); - await Directory.fromUri(packageRootUri).create(); - fakeClang = tempUri.resolve('fake_clang'); - await File.fromUri(fakeClang).create(); - fakeLd = tempUri.resolve('fake_ld'); - await File.fromUri(fakeLd).create(); - fakeAr = tempUri.resolve('fake_ar'); - await File.fromUri(fakeAr).create(); - fakeCl = tempUri.resolve('cl.exe'); - await File.fromUri(fakeCl).create(); - fakeVcVars = tempUri.resolve('vcvarsall.bat'); - await File.fromUri(fakeVcVars).create(); - }); - - tearDown(() async { - await Directory.fromUri(tempUri).delete(recursive: true); - }); - - test('LinkConfig ==', () { - final config1 = LinkConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - archiver: fakeAr, - ), - buildMode: BuildMode.release, - supportedAssetTypes: [CodeAsset.type], - assets: [], - linkModePreference: LinkModePreference.preferStatic, - ); - - final config2 = LinkConfig.build( - outputDirectory: outDir2Uri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - supportedAssetTypes: [CodeAsset.type], - assets: [], - linkModePreference: LinkModePreference.preferStatic, - ); - - expect(config1, equals(config1)); - expect(config1 == config2, false); - expect(config1.outputDirectory != config2.outputDirectory, true); - expect(config1.packageRoot, config2.packageRoot); - expect(config1.targetArchitecture == config2.targetArchitecture, true); - expect(config1.targetOS != config2.targetOS, true); - expect(config1.targetIOSSdk, IOSSdk.iPhoneOS); - expect(() => config2.targetIOSSdk, throwsStateError); - expect(config1.cCompiler.compiler != config2.cCompiler.compiler, true); - expect(config1.cCompiler.linker != config2.cCompiler.linker, true); - expect(config1.cCompiler.archiver != config2.cCompiler.archiver, true); - expect(config1.cCompiler.envScript == config2.cCompiler.envScript, true); - expect(config1.cCompiler.envScriptArgs == config2.cCompiler.envScriptArgs, - true); - expect(config1.cCompiler != config2.cCompiler, true); - expect(config1.supportedAssetTypes, config2.supportedAssetTypes); - }); - - test('LinkConfig fromConfig', () { - final linkConfig2 = LinkConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - 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(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_android_ndk_api': 30, - 'target_architecture': 'arm64', - 'target_os': 'android', - 'version': BuildOutput.latestVersion.toString(), - 'assets': [], - }; - - final fromConfig = LinkConfigImpl.fromJson(config); - expect(fromConfig, equals(linkConfig2)); - }); - - test('LinkConfig.dryRun', () { - final linkConfig2 = LinkConfig.dryRun( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetOS: OS.android, - supportedAssetTypes: [CodeAsset.type], - assets: [], - 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(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_os': 'android', - 'version': BuildOutput.latestVersion.toString(), - 'assets': [], - }; - - final fromConfig = LinkConfigImpl.fromJson(config); - expect(fromConfig, equals(linkConfig2)); - }); - - test('LinkConfig fromArgs', () async { - final linkConfig = LinkConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - assets: [], - linkModePreference: LinkModePreference.preferStatic, - supportedAssetTypes: [CodeAsset.type], - ); - final configFileContents = (linkConfig as LinkConfigImpl).toJsonString(); - final configUri = tempUri.resolve('config.json'); - final configFile = File.fromUri(configUri); - await configFile.writeAsString(configFileContents); - final linkConfigFromArgs = - LinkConfig.fromArguments(['--config', configUri.toFilePath()]); - expect(linkConfigFromArgs, linkConfig); - }); - - test('LinkConfig.version', () { - LinkConfig.latestVersion.toString(); - }); -} diff --git a/pkgs/native_assets_cli/test/build_config_test.dart b/pkgs/native_assets_cli/test/build_config_test.dart new file mode 100644 index 000000000..85ebf0e6b --- /dev/null +++ b/pkgs/native_assets_cli/test/build_config_test.dart @@ -0,0 +1,321 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ignore_for_file: deprecated_member_use_from_same_package + +import 'dart:convert'; +import 'dart:io'; + +import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:test/test.dart'; + +//XXX TODO +//test('envScript', () { +//final buildConfig1 = BuildConfigImpl( +//outputDirectory: outDirUri, +//outputDirectoryShared: outputDirectoryShared, +//packageName: packageName, +//packageRoot: packageRootUri, +//targetArchitecture: Architecture.x64, +//targetOS: OS.windows, +//cCompiler: CCompilerConfig( +//compiler: fakeCl, +//envScript: fakeVcVars, +//envScriptArgs: ['x64'], +//), +//buildMode: BuildMode.release, +//linkModePreference: LinkModePreference.dynamic, +//linkingEnabled: false, +//supportedAssetTypes: [CodeAsset.type], +//); + +//final configFile = buildConfig1.toJson(); +//final fromConfig = BuildConfigImpl.fromJson(configFile); +//expect(fromConfig, equals(buildConfig1)); +//}); + +void main() async { + late Uri outDirUri; + late Uri outputDirectoryShared; + late String packageName; + late Uri packageRootUri; + late Uri fakeClang; + late Uri fakeLd; + late Uri fakeAr; + late Map metadata; + + setUp(() async { + final tempUri = Directory.systemTemp.uri; + outDirUri = tempUri.resolve('out1/'); + outputDirectoryShared = tempUri.resolve('out_shared1/'); + packageName = 'my_package'; + packageRootUri = tempUri.resolve('$packageName/'); + fakeClang = tempUri.resolve('fake_clang'); + fakeLd = tempUri.resolve('fake_ld'); + fakeAr = tempUri.resolve('fake_ar'); + metadata = { + 'bar': const Metadata({ + 'key': 'value', + 'foo': ['asdf', 'fdsa'], + }), + 'foo': const Metadata({ + 'key': 321, + }), + }; + }); + + test('BuildConfigBuilder->JSON->BuildConfig', () { + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageName: packageName, + packageRoot: packageRootUri, + targetOS: OS.android, + buildMode: BuildMode.release, + supportedAssetTypes: [CodeAsset.type], + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + metadata: metadata, + ) + ..setupBuildRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outputDirectoryShared, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.arm64, + targetAndroidNdkApi: 30, + linkModePreference: LinkModePreference.preferStatic, + cCompilerConfig: CCompilerConfig( + compiler: fakeClang, + linker: fakeLd, + archiver: fakeAr, + ), + ); + final config = BuildConfig(configBuilder.json); + + final expectedConfigJson = { + 'build_mode': 'release', + 'supported_asset_types': [CodeAsset.type], + 'dry_run': false, + 'linking_enabled': false, + 'link_mode_preference': 'prefer-static', + 'out_dir': outDirUri.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_android_ndk_api': 30, + 'target_architecture': 'arm64', + 'target_os': 'android', + 'version': HookConfig.currentVersion.toString(), + 'c_compiler.ar': fakeAr.toFilePath(), + 'c_compiler.ld': fakeLd.toFilePath(), + 'c_compiler.cc': fakeClang.toFilePath(), + 'dependency_metadata': { + 'bar': { + 'key': 'value', + 'foo': ['asdf', 'fdsa'], + }, + 'foo': { + 'key': 321, + }, + }, + }; + + expect(config.json, expectedConfigJson); + expect(json.decode(config.toString()), expectedConfigJson); + + expect(config.outputDirectory, outDirUri); + expect(config.outputDirectoryShared, outputDirectoryShared); + + expect(config.packageName, packageName); + expect(config.packageRoot, packageRootUri); + expect(config.targetOS, OS.android); + expect(config.buildMode, BuildMode.release); + expect(config.supportedAssetTypes, [CodeAsset.type]); + + expect(config.linkingEnabled, false); + expect(config.dryRun, false); + expect(config.metadata, metadata); + + final codeConfig = config.codeConfig; + expect(codeConfig.targetArchitecture, Architecture.arm64); + expect(codeConfig.targetAndroidNdkApi, 30); + expect(codeConfig.linkModePreference, LinkModePreference.preferStatic); + expect(codeConfig.cCompiler.compiler, fakeClang); + expect(codeConfig.cCompiler.linker, fakeLd); + expect(codeConfig.cCompiler.archiver, fakeAr); + }); + + test('BuildConfig.dryRun', () { + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageName: packageName, + packageRoot: packageRootUri, + targetOS: OS.android, + buildMode: null, // not available in dry run + supportedAssetTypes: [CodeAsset.type], + ) + ..setupBuildConfig( + linkingEnabled: true, + dryRun: true, + ) + ..setupBuildRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outputDirectoryShared, + ) + ..setupCodeConfig( + targetArchitecture: null, // not available in dry run + cCompilerConfig: null, // not available in dry run + linkModePreference: LinkModePreference.preferStatic, + ); + final config = BuildConfig(configBuilder.json); + + final expectedConfigJson = { + 'dry_run': true, + 'supported_asset_types': [CodeAsset.type], + 'linking_enabled': true, + 'link_mode_preference': 'prefer-static', + 'out_dir': outDirUri.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_os': 'android', + 'version': HookConfig.currentVersion.toString(), + }; + + expect(config.json, expectedConfigJson); + expect(json.decode(config.toString()), expectedConfigJson); + + expect(config.outputDirectory, outDirUri); + expect(config.outputDirectoryShared, outputDirectoryShared); + + expect(config.packageName, packageName); + expect(config.packageRoot, packageRootUri); + expect(config.targetOS, OS.android); + expect(config.supportedAssetTypes, [CodeAsset.type]); + expect(() => config.buildMode, throwsStateError); + + expect(config.linkingEnabled, true); + expect(config.dryRun, true); + expect(config.metadata, {}); + + final codeConfig = config.codeConfig; + expect(() => codeConfig.targetArchitecture, throwsStateError); + expect(codeConfig.targetAndroidNdkApi, null); + expect(codeConfig.linkModePreference, LinkModePreference.preferStatic); + expect(codeConfig.cCompiler.compiler, null); + expect(codeConfig.cCompiler.linker, null); + expect(codeConfig.cCompiler.archiver, null); + }); + + group('BuildConfig format issues', () { + for (final version in ['9001.0.0', '0.0.1']) { + test('BuildConfig version $version', () { + final outDir = outDirUri; + final config = { + 'link_mode_preference': 'prefer-static', + 'out_dir': outDir.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_root': packageRootUri.toFilePath(), + 'target_os': 'linux', + 'version': version, + 'package_name': packageName, + 'supported_asset_types': [CodeAsset.type], + 'dry_run': true, + 'linking_enabled': false, + }; + expect( + () => BuildConfig(config), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains(version) && + e.message.contains(HookConfig.currentVersion.toString()), + )), + ); + }); + } + + test('BuildConfig FormatExceptions', () { + expect( + () => BuildConfig({}), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains( + 'No value was provided for required key: ', + ), + )), + ); + expect( + () => BuildConfig({ + 'version': HookConfig.currentVersion.toString(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_architecture': 'arm64', + 'target_os': 'android', + 'target_android_ndk_api': 30, + 'link_mode_preference': 'prefer-static', + 'linking_enabled': true, + 'supported_asset_types': [CodeAsset.type], + }), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains( + 'No value was provided for required key: out_dir', + ), + )), + ); + expect( + () => BuildConfig({ + 'version': HookConfig.currentVersion.toString(), + 'out_dir': outDirUri.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_architecture': 'arm64', + 'target_os': 'android', + 'target_android_ndk_api': 30, + 'link_mode_preference': 'prefer-static', + 'linking_enabled': true, + 'build_mode': BuildMode.release.name, + 'supported_asset_types': [CodeAsset.type], + 'dependency_metadata': { + 'bar': {'key': 'value'}, + 'foo': [], + }, + }), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains("Unexpected value '[]' ") && + e.message.contains('Expected a Map'), + )), + ); + }); + + test('invalid architecture', () { + final config = { + 'build_mode': 'release', + 'dry_run': false, + 'link_mode_preference': 'prefer-static', + 'out_dir': outDirUri.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_android_ndk_api': 30, + 'target_architecture': 'invalid_architecture', + 'target_os': 'android', + 'supported_asset_types': [CodeAsset.type], + 'version': HookConfig.currentVersion.toString(), + }; + expect( + () => BuildConfig(config), + throwsFormatException, + ); + }); + }); +} diff --git a/pkgs/native_assets_cli/test/build_output_test.dart b/pkgs/native_assets_cli/test/build_output_test.dart new file mode 100644 index 000000000..e1af613f4 --- /dev/null +++ b/pkgs/native_assets_cli/test/build_output_test.dart @@ -0,0 +1,92 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ignore_for_file: deprecated_member_use_from_same_package + +import 'dart:convert'; + +import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:native_assets_cli/src/utils/datetime.dart'; +import 'package:test/test.dart'; + +void main() { + test('BuildOutputBuilder->JSON->BuildOutput', () { + final assets = [ + for (int i = 0; i < 6; i++) + DataAsset(file: Uri(path: 'asset$i'), name: 'assetId$i', package: 'pkg') + .encode(), + ]; + final uris = [ + for (int i = 0; i < 3; ++i) Uri.file('path$i'), + ]; + final metadata0 = { + 'meta-a': 'meta-b', + }; + final metadata1 = { + for (int i = 0; i < 2; ++i) 'meta$i': 'meta$i-value', + }; + final before = DateTime.now().roundDownToSeconds(); + final builder = BuildOutputBuilder(); + final after = DateTime.now().roundDownToSeconds(); + + builder.addDependency(uris.take(1).single); + builder.addDependencies(uris.skip(1).toList()); + builder.addMetadatum(metadata0.keys.single, metadata0.values.single); + builder.addMetadata(metadata1); + + builder.addEncodedAsset(assets.take(1).single); + builder.addEncodedAsset(assets.skip(1).first, + linkInPackage: 'package:linker1'); + builder.addEncodedAssets(assets.skip(2).take(2).toList()); + builder.addEncodedAssets(assets.skip(4).toList(), + linkInPackage: 'package:linker2'); + + final config = BuildOutput(builder.json); + expect(config.timestamp, greaterThanOrEqualTo(before)); + expect(config.timestamp, lessThanOrEqualTo(after)); + expect(config.timestamp, + lessThanOrEqualTo(config.timestamp.roundDownToSeconds())); + + print(const JsonEncoder.withIndent(' ').convert(config.json)); + + final expectedJson = { + 'version': '1.5.0', + 'timestamp': config.timestamp.toString(), + 'dependencies': [ + ...uris.map((uri) => uri.toFilePath()), + ], + 'metadata': { + ...metadata0, + ...metadata1, + }, + 'assets': [ + assets.take(1).single.toJson(), + ...assets.skip(2).take(2).map((e) => e.toJson()), + ], + 'assetsForLinking': { + 'package:linker1': [ + assets.skip(1).first.toJson(), + ], + 'package:linker2': [ + ...assets.skip(4).map((e) => e.toJson()), + ] + } + }; + expect(config.json, equals(expectedJson)); + }); + + for (final version in ['9001.0.0', '0.0.1']) { + test('BuildOutput version $version', () { + expect( + () => BuildOutput({'version': version}), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains(version) && + e.message.contains(HookOutput.latestVersion.toString()), + )), + ); + }); + } +} diff --git a/pkgs/native_assets_cli/test/checksum_test.dart b/pkgs/native_assets_cli/test/checksum_test.dart new file mode 100644 index 000000000..246066ade --- /dev/null +++ b/pkgs/native_assets_cli/test/checksum_test.dart @@ -0,0 +1,107 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:test/test.dart'; + +void main() { + test('checksum', () async { + // metadata, cc, link vs build, metadata, haslink + final checksums = []; + for (final os in [OS.linux, OS.macOS]) { + for (final buildMode in [BuildMode.release, BuildMode.debug]) { + for (final packageName in ['foo', 'bar']) { + for (final assetType in [CodeAsset.type, DataAsset.type]) { + for (final dryRun in [true, false]) { + for (final linking in [true, false]) { + final builder = BuildConfigBuilder() + ..setupHookConfig( + packageRoot: Uri.file('foo'), + packageName: packageName, + targetOS: os, + supportedAssetTypes: [assetType], + buildMode: buildMode, + ) + ..setupBuildConfig(dryRun: dryRun, linkingEnabled: linking); + checksums.add(builder.computeChecksum()); + } + } + } + } + } + } + // As all variants have something different, we expect the checksums to be + // unique. + expect(checksums.toSet().length, checksums.length); + + // If format or algorithm for checksumming changes we'd like to know (by + // needing to update this list). + expect(checksums, [ + '49daf3450cda2d6f703fd1c5615723e1', + '5b071ecc8b32a1dd7ae71cec893da765', + '42e0e9ef9b52243b2a033708c5614692', + '779e7bd31c6671c69c15bffe20b9b882', + 'e1684c0558449dc47dd6e95eed9c8f20', + '42264730ed1bac426fd9b0de85ca355a', + 'fae528a60605783f326c1e61af02a943', + '375be74dab1457c8ba637444654a4a3a', + '390e8e89b417572229bf7f2c02883a3c', + 'd2931555f8c5f3610b45012f63fafae4', + '423dc47a44ce262b6d1d30fd159752fe', + '1616bd24aa7745c19272cb7776c740d8', + '1dfafc3008d703fb5670cae22955e7f0', + '27c8a7891663bdea5791f0a4a0b00f0a', + '33628656d5ba5acff5ba6d672e6266e9', + '6a99fa1285cdbdf07e7f2f16dd9c124f', + '6d6fca403370aa7ea4ae309eb3e19272', + '09aadc73a263858f3b0724c3ca1c886a', + '920c1e3d286c746065652bfd2d740698', + 'cbf2c911f9d3e5b0d644b402d8feebd3', + 'ac36f6d4b530ddbd77d7c9fd93514bfc', + 'a77d9a81e1f66c43ba16743263516fc1', + '7619ec268bb1c4da8116db2605bd2ab8', + '3eaa505790a10afc93b4402799c735d2', + 'c9378ada5a069e0ea1f4906e6e981092', + '2915954b795f02d688269214b3db0cf7', + 'c6c895b2c738ff19eba67044f0dbbbd0', + '953a29fc8f4015a57fb44749dd576837', + '4c6c58417ab2e732e9b2952fedacce68', + '9cfb486089a705226ee8a138d14f7fd7', + '8a9fc2ebf5cc1fa39cd03179a5959fad', + 'b956d47a5613b89e09f00763fc7b30b6', + 'fe690851eb75c9566fbf2060f8b37df9', + 'f29ec64febd078bd188041ccb7d545f7', + '3f54f0ceca274275c71c4fa2ca3ef919', + '9896e8326e43a114ca1fa833a00f0e80', + '8f5c3abb4578159f37a3f585a4e52b62', + 'fd2e5b9cd4ccbe35a7b515f49b44a6f4', + '90d3dfc0dab4617da8b3b261c6d42ed6', + '4491024a82b1ae5545b3bb90f37c47bd', + '491e8c4549b80de2de7366f75deb6fb7', + 'bf8ce548a070836411efc4ffab36f00d', + '5562ecf90c8ea05f8cff98099a16c6bd', + '451b5c9be02a42cb5cf6359355241f6a', + '3b39694a45d1c427dbf24229ad88f969', + 'd4527aec8dc183f1f61819a414fe3a52', + '95ea9db54a39dfc3d7eecbf120b93ee0', + '2ac0ee9854562c6274749fdcc1fb3609', + 'd1ef410112e3f3ed8a98e1dc7e64f653', + '5e05ca0cd3496beebe8f3a3c5dec2e3c', + '3e2febea1b3c111eed26c9a5a6b90ef3', + '969f85ffb945e52278b0b55b895f12a1', + 'd00132dd4a88b01e4071e8b1e1d6cf28', + '5d940c16b18065eb2579a48d12a9cdd0', + '144cbd4cd41f6bf3a3bee7b0776cff40', + '793cd15349782e52b009a5337301c2b9', + '676def9232cfb0ad832311cf6c29defb', + '7010c8c6d8124e35b1c9ad643f219ffd', + 'd0b705242470e18b6caf62dc1765e70f', + 'e0874bd58e0a72f341406fef23979bd0', + '4174d81f0fa117071a58abc5886cce45', + '6221a8463b3978352a1cc6f0b0945f0a', + '154c04f511463b7f73033559561ca88d', + '66fc47c7320a7af96716f17c80b0eb3f', + ]); + }); +} 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 31f15c0a8..629aa3c4c 100644 --- a/pkgs/native_assets_cli/test/example/local_asset_test.dart +++ b/pkgs/native_assets_cli/test/example/local_asset_test.dart @@ -40,25 +40,25 @@ void main() async { final testPackageUri = packageUri.resolve('example/build/$name/'); final dartUri = Uri.file(Platform.resolvedExecutable); - final config = BuildConfigImpl( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: name, - packageRoot: testPackageUri, - targetOS: OS.current, - version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreference.dynamic, - dryRun: dryRun, - linkingEnabled: false, - targetArchitecture: dryRun ? null : Architecture.current, - buildMode: dryRun ? null : BuildMode.debug, - cCompiler: dryRun ? null : cCompiler, - supportedAssetTypes: [CodeAsset.type], - ); + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageRoot: testPackageUri, + packageName: name, + targetOS: OS.current, + supportedAssetTypes: [CodeAsset.type], + buildMode: dryRun ? null : BuildMode.debug) + ..setupBuildRunConfig( + outputDirectory: outputDirectory, + outputDirectoryShared: outputDirectoryShared) + ..setupBuildConfig(linkingEnabled: false, dryRun: dryRun) + ..setupCodeConfig( + targetArchitecture: dryRun ? null : Architecture.current, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: dryRun ? null : cCompiler); final buildConfigUri = testTempUri.resolve('build_config.json'); await File.fromUri(buildConfigUri) - .writeAsString(jsonEncode(config.toJson())); + .writeAsString(jsonEncode(configBuilder.json)); final processResult = await Process.run( dartUri.toFilePath(), @@ -76,12 +76,13 @@ void main() async { expect(processResult.exitCode, 0); final buildOutputUri = outputDirectory.resolve('build_output.json'); - final buildOutput = HookOutputImpl.fromJsonString( - await File.fromUri(buildOutputUri).readAsString()) as BuildOutput; + final buildOutput = BuildOutput( + json.decode(await File.fromUri(buildOutputUri).readAsString()) + as Map); final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { - final codeAsset = buildOutput.codeAssets.all.first; + final codeAsset = buildOutput.codeAssets.first; expect(assets.length, greaterThanOrEqualTo(1)); expect(await File.fromUri(codeAsset.file!).exists(), false); expect(dependencies, []); 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 5c82eca99..3d0d2f9c7 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 @@ -40,25 +40,25 @@ void main() async { final testPackageUri = packageUri.resolve('example/build/$name/'); final dartUri = Uri.file(Platform.resolvedExecutable); - final config = BuildConfigImpl( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: name, - packageRoot: testPackageUri, - targetOS: OS.current, - version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreference.dynamic, - dryRun: dryRun, - linkingEnabled: false, - targetArchitecture: dryRun ? null : Architecture.current, - buildMode: dryRun ? null : BuildMode.debug, - cCompiler: dryRun ? null : cCompiler, - supportedAssetTypes: [CodeAsset.type], - ); + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageRoot: testPackageUri, + packageName: name, + targetOS: OS.current, + supportedAssetTypes: [CodeAsset.type], + buildMode: dryRun ? null : BuildMode.debug) + ..setupBuildRunConfig( + outputDirectory: outputDirectory, + outputDirectoryShared: outputDirectoryShared) + ..setupBuildConfig(linkingEnabled: false, dryRun: dryRun) + ..setupCodeConfig( + targetArchitecture: dryRun ? null : Architecture.current, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: dryRun ? null : cCompiler); final buildConfigUri = testTempUri.resolve('build_config.json'); await File.fromUri(buildConfigUri) - .writeAsString(jsonEncode(config.toJson())); + .writeAsString(jsonEncode(configBuilder.json)); final processResult = await Process.run( dartUri.toFilePath(), @@ -76,8 +76,9 @@ void main() async { expect(processResult.exitCode, 0); final buildOutputUri = outputDirectory.resolve('build_output.json'); - final buildOutput = HookOutputImpl.fromJsonString( - await File.fromUri(buildOutputUri).readAsString()); + final buildOutput = BuildOutput( + json.decode(await File.fromUri(buildOutputUri).readAsString()) + as Map); final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { 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 4d7ea3feb..dada26501 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 @@ -49,25 +49,25 @@ void main() async { final testPackageUri = packageUri.resolve('example/build/$name/'); final dartUri = Uri.file(Platform.resolvedExecutable); - final config = BuildConfigImpl( - outputDirectory: outputDirectory, - outputDirectoryShared: outputDirectoryShared, - packageName: name, - packageRoot: testPackageUri, - targetOS: OS.current, - version: HookConfigImpl.latestVersion, - linkModePreference: LinkModePreference.dynamic, - dryRun: dryRun, - linkingEnabled: false, - targetArchitecture: dryRun ? null : Architecture.current, - buildMode: dryRun ? null : BuildMode.debug, - cCompiler: dryRun ? null : cCompiler, - supportedAssetTypes: [CodeAsset.type], - ); + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageRoot: testPackageUri, + packageName: name, + targetOS: OS.current, + supportedAssetTypes: [CodeAsset.type], + buildMode: dryRun ? null : BuildMode.debug) + ..setupBuildRunConfig( + outputDirectory: outputDirectory, + outputDirectoryShared: outputDirectoryShared) + ..setupBuildConfig(linkingEnabled: false, dryRun: dryRun) + ..setupCodeConfig( + targetArchitecture: dryRun ? null : Architecture.current, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: dryRun ? null : cCompiler); final buildConfigUri = testTempUri.resolve('build_config.json'); - File.fromUri(buildConfigUri) - .writeAsStringSync(jsonEncode(config.toJson())); + await File.fromUri(buildConfigUri) + .writeAsString(jsonEncode(configBuilder.json)); final processResult = await Process.run( dartUri.toFilePath(), @@ -85,8 +85,9 @@ void main() async { expect(processResult.exitCode, 0); final buildOutputUri = outputDirectory.resolve('build_output.json'); - final buildOutput = HookOutputImpl.fromJsonString( - await File.fromUri(buildOutputUri).readAsString()); + final buildOutput = BuildOutput( + json.decode(await File.fromUri(buildOutputUri).readAsString()) + as Map); final assets = buildOutput.encodedAssets; final dependencies = buildOutput.dependencies; if (dryRun) { diff --git a/pkgs/native_assets_cli/test/link_config_test.dart b/pkgs/native_assets_cli/test/link_config_test.dart new file mode 100644 index 000000000..44440d254 --- /dev/null +++ b/pkgs/native_assets_cli/test/link_config_test.dart @@ -0,0 +1,216 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:native_assets_cli/native_assets_cli_internal.dart'; +import 'package:test/test.dart'; + +void main() async { + late Uri outDirUri; + late Uri outputDirectoryShared; + late String packageName; + late Uri packageRootUri; + late Uri fakeClang; + late Uri fakeLd; + late Uri fakeAr; + late List assets; + + setUp(() async { + final tempUri = Directory.systemTemp.uri; + outDirUri = tempUri.resolve('out1/'); + outputDirectoryShared = tempUri.resolve('out_shared1/'); + packageName = 'my_package'; + packageRootUri = tempUri.resolve('$packageName/'); + fakeClang = tempUri.resolve('fake_clang'); + fakeLd = tempUri.resolve('fake_ld'); + fakeAr = tempUri.resolve('fake_ar'); + + assets = [ + DataAsset( + package: packageName, + name: 'name', + file: Uri.file('nonexistent'), + ).encode(), + CodeAsset( + package: packageName, + name: 'name2', + linkMode: DynamicLoadingBundled(), + os: OS.android, + file: Uri.file('not there'), + architecture: Architecture.riscv64, + ).encode(), + ]; + }); + + test('LinkConfigBuilder->JSON->LinkConfig', () { + final configBuilder = LinkConfigBuilder() + ..setupHookConfig( + packageName: packageName, + packageRoot: packageRootUri, + targetOS: OS.android, + buildMode: BuildMode.release, + supportedAssetTypes: [CodeAsset.type], + ) + ..setupLinkConfig(assets: assets) + ..setupLinkRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outputDirectoryShared, + recordedUsesFile: null, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.arm64, + targetAndroidNdkApi: 30, + linkModePreference: LinkModePreference.preferStatic, + cCompilerConfig: CCompilerConfig( + compiler: fakeClang, + linker: fakeLd, + archiver: fakeAr, + ), + ); + final config = LinkConfig(configBuilder.json); + + final expectedConfigJson = { + 'build_mode': 'release', + 'supported_asset_types': [CodeAsset.type], + 'link_mode_preference': 'prefer-static', + 'out_dir': outDirUri.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_android_ndk_api': 30, + 'target_architecture': 'arm64', + 'target_os': 'android', + 'version': HookConfig.currentVersion.toString(), + 'c_compiler.ar': fakeAr.toFilePath(), + 'c_compiler.ld': fakeLd.toFilePath(), + 'c_compiler.cc': fakeClang.toFilePath(), + 'assets': [for (final asset in assets) asset.toJson()], + }; + expect(config.json, expectedConfigJson); + expect(json.decode(config.toString()), expectedConfigJson); + + expect(config.outputDirectory, outDirUri); + expect(config.outputDirectoryShared, outputDirectoryShared); + + expect(config.packageName, packageName); + expect(config.packageRoot, packageRootUri); + expect(config.targetOS, OS.android); + expect(config.buildMode, BuildMode.release); + expect(config.supportedAssetTypes, [CodeAsset.type]); + expect(config.encodedAssets, assets); + + final codeConfig = config.codeConfig; + expect(codeConfig.targetArchitecture, Architecture.arm64); + expect(codeConfig.targetAndroidNdkApi, 30); + expect(codeConfig.linkModePreference, LinkModePreference.preferStatic); + expect(codeConfig.cCompiler.compiler, fakeClang); + expect(codeConfig.cCompiler.linker, fakeLd); + expect(codeConfig.cCompiler.archiver, fakeAr); + }); + + group('LinkConfig FormatExceptions', () { + for (final version in ['9001.0.0', '0.0.1']) { + test('LinkConfig version $version', () { + final outDir = outDirUri; + final config = { + 'link_mode_preference': 'prefer-static', + 'out_dir': outDir.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_root': packageRootUri.toFilePath(), + 'target_os': 'linux', + 'version': version, + 'package_name': packageName, + 'supported_asset_types': [CodeAsset.type], + 'dry_run': true, + }; + expect( + () => LinkConfig(config), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains(version) && + e.message.contains(HookConfig.currentVersion.toString()), + )), + ); + }); + } + + test('invalid architecture', () { + final config = { + 'supported_asset_types': [CodeAsset.type], + 'build_mode': 'release', + 'dry_run': false, + 'link_mode_preference': 'prefer-static', + 'out_dir': outDirUri.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_android_ndk_api': 30, + 'target_architecture': 'invalid_architecture', + 'target_os': 'android', + 'version': HookOutput.latestVersion.toString(), + }; + expect( + () => LinkConfig(config), + throwsFormatException, + ); + }); + + test('LinkConfig FormatExceptions', () { + expect( + () => LinkConfig({}), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains('No value was provided for required key: '), + )), + ); + expect( + () => LinkConfig({ + 'version': HookConfig.currentVersion.toString(), + 'supported_asset_types': [CodeAsset.type], + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_architecture': 'arm64', + 'target_os': 'android', + 'target_android_ndk_api': 30, + 'assets': [], + }), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains( + 'No value was provided for required key: out_dir', + ), + )), + ); + expect( + () => LinkConfig({ + 'version': HookConfig.currentVersion.toString(), + 'supported_asset_types': [CodeAsset.type], + 'out_dir': outDirUri.toFilePath(), + 'out_dir_shared': outputDirectoryShared.toFilePath(), + 'package_name': packageName, + 'package_root': packageRootUri.toFilePath(), + 'target_architecture': 'arm64', + 'target_os': 'android', + 'target_android_ndk_api': 30, + 'build_mode': BuildMode.release.name, + 'assets': 'astring', + 'link_mode_preference': LinkModePreference.preferStatic.name, + }), + throwsA(predicate( + (e) => + e is FormatException && + e.message.contains( + "Unexpected value 'astring' for key '.assets' in config file. " + 'Expected a List?.', + ), + )), + ); + }); + }); +} diff --git a/pkgs/native_assets_cli/test/model/build_config_test.dart b/pkgs/native_assets_cli/test/model/build_config_test.dart deleted file mode 100644 index 08afdbb0d..000000000 --- a/pkgs/native_assets_cli/test/model/build_config_test.dart +++ /dev/null @@ -1,604 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:io'; - -import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import 'package:test/test.dart'; - -void main() async { - const packageName = 'my_package'; - late Uri tempUri; - late Uri outDirUri; - late Uri outDir2Uri; - late Uri outputDirectoryShared; - late Uri outputDirectoryShared2; - late Uri packageRootUri; - late Uri fakeClang; - late Uri fakeLd; - late Uri fakeAr; - late Uri fakeCl; - late Uri fakeVcVars; - - setUp(() async { - tempUri = (await Directory.systemTemp.createTemp()).uri; - outDirUri = tempUri.resolve('out1/'); - await Directory.fromUri(outDirUri).create(); - outDir2Uri = tempUri.resolve('out2/'); - await Directory.fromUri(outDir2Uri).create(); - outputDirectoryShared = tempUri.resolve('out_shared1/'); - await Directory.fromUri(outputDirectoryShared).create(); - outputDirectoryShared2 = tempUri.resolve('out_shared2/'); - await Directory.fromUri(outputDirectoryShared2).create(); - packageRootUri = tempUri.resolve('$packageName/'); - await Directory.fromUri(packageRootUri).create(); - fakeClang = tempUri.resolve('fake_clang'); - await File.fromUri(fakeClang).create(); - fakeLd = tempUri.resolve('fake_ld'); - await File.fromUri(fakeLd).create(); - fakeAr = tempUri.resolve('fake_ar'); - await File.fromUri(fakeAr).create(); - fakeCl = tempUri.resolve('cl.exe'); - await File.fromUri(fakeCl).create(); - fakeVcVars = tempUri.resolve('vcvarsall.bat'); - await File.fromUri(fakeVcVars).create(); - }); - - tearDown(() async { - await Directory.fromUri(tempUri).delete(recursive: true); - }); - - test('BuildConfig ==', () { - final config1 = BuildConfigImpl( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - archiver: fakeAr, - ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - final config2 = BuildConfigImpl( - outputDirectory: outDir2Uri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - expect(config1, equals(config1)); - expect(config1 == config2, false); - expect(config1.outputDirectory != config2.outputDirectory, true); - expect(config1.packageRoot, config2.packageRoot); - expect(config1.targetArchitecture == config2.targetArchitecture, true); - expect(config1.targetOS != config2.targetOS, true); - expect(config1.targetIOSSdk, IOSSdk.iPhoneOS); - expect(() => config2.targetIOSSdk, throwsStateError); - expect(config1.cCompiler.compiler != config2.cCompiler.compiler, true); - expect(config1.cCompiler.linker != config2.cCompiler.linker, true); - expect(config1.cCompiler.archiver != config2.cCompiler.archiver, true); - expect(config1.cCompiler.envScript == config2.cCompiler.envScript, true); - expect(config1.cCompiler.envScriptArgs == config2.cCompiler.envScriptArgs, - true); - expect(config1.cCompiler != config2.cCompiler, true); - expect(config1.linkModePreference, config2.linkModePreference); - expect(config1.linkingEnabled, config2.linkingEnabled); - }); - - test('BuildConfig fromConfig', () { - final buildConfig2 = BuildConfigImpl( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - 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, - 'link_mode_preference': 'prefer-static', - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_android_ndk_api': 30, - 'target_architecture': 'arm64', - 'target_os': 'android', - 'version': HookOutputImpl.latestVersion.toString(), - }; - - final fromConfig = BuildConfigImpl.fromJson(config); - expect(fromConfig, equals(buildConfig2)); - }); - - test('BuildConfig.dryRun', () { - final buildConfig2 = BuildConfigImpl.dryRun( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - 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(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_os': 'android', - 'version': HookOutputImpl.latestVersion.toString(), - }; - - final fromConfig = BuildConfigImpl.fromJson(config); - expect(fromConfig, equals(buildConfig2)); - }); - - test('BuildConfig toJson fromConfig', () { - final buildConfig1 = BuildConfigImpl( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - final configFile = buildConfig1.toJson(); - final fromConfig = BuildConfigImpl.fromJson(configFile); - expect(fromConfig, equals(buildConfig1)); - }); - - test('BuildConfig == dependency metadata', () { - final buildConfig1 = BuildConfigImpl( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - dependencyMetadata: { - 'bar': const Metadata({ - 'key': 'value', - 'foo': ['asdf', 'fdsa'], - }), - 'foo': const Metadata({ - 'key': 321, - }), - }, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - final buildConfig2 = BuildConfigImpl( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - dependencyMetadata: { - 'bar': const Metadata({ - 'key': 'value', - }), - 'foo': const Metadata({ - 'key': 123, - }), - }, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - expect(buildConfig1, equals(buildConfig1)); - expect(buildConfig1 == buildConfig2, false); - expect(buildConfig1.hashCode == buildConfig2.hashCode, false); - }); - - test('BuildConfig toJson fromJson', () { - final outDir = outDirUri; - final buildConfig1 = BuildConfigImpl( - outputDirectory: outDir, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - // This map should be sorted on key for two layers. - dependencyMetadata: { - 'foo': const Metadata({ - 'z': ['z', 'a'], - 'a': 321, - }), - 'bar': const Metadata({ - 'key': 'value', - }), - }, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - final jsonObject = buildConfig1.toJson(); - final expectedJson = { - 'build_mode': 'release', - 'c_compiler': {'cc': fakeClang.toFilePath(), 'ld': fakeLd.toFilePath()}, - 'dependency_metadata': { - 'bar': {'key': 'value'}, - 'foo': { - 'a': 321, - 'z': ['z', 'a'] - } - }, - 'linking_enabled': false, - 'link_mode_preference': 'prefer-static', - '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', - 'version': '${HookConfigImpl.latestVersion}' - }; - expect( - jsonObject, - equals(expectedJson), - ); - - final buildConfig2 = BuildConfigImpl.fromJson(jsonObject); - expect(buildConfig2, buildConfig1); - }); - - test('BuildConfig FormatExceptions', () { - expect( - () => BuildConfigImpl.fromJson({}), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains( - 'No value was provided for required key: target_os', - ), - )), - ); - expect( - () => BuildConfigImpl.fromJson({ - 'version': HookConfigImpl.latestVersion.toString(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_architecture': 'arm64', - 'target_os': 'android', - 'target_android_ndk_api': 30, - 'link_mode_preference': 'prefer-static', - 'supported_asset_types': [CodeAsset.type], - }), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains( - 'No value was provided for required key: out_dir', - ), - )), - ); - expect( - () => BuildConfigImpl.fromJson({ - 'version': HookConfigImpl.latestVersion.toString(), - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_architecture': 'arm64', - 'target_os': 'android', - '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': [], - }, - }), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains( - "Unexpected value '[]' for key 'dependency_metadata.foo' in " - 'config file. Expected a Map.', - ), - )), - ); - expect( - () => BuildConfigImpl.fromJson({ - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'version': HookConfigImpl.latestVersion.toString(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_architecture': 'arm64', - 'target_os': 'android', - 'link_mode_preference': 'prefer-static', - 'supported_asset_types': [CodeAsset.type], - 'build_mode': BuildMode.release.name, - }), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains( - 'No value was provided for required key: target_android_ndk_api', - ), - )), - ); - }); - - test('BuildConfig toString', () { - final config = BuildConfigImpl( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - config.toString(); - }); - - test('BuildConfig fromArgs', () async { - final buildConfig = BuildConfigImpl( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.preferStatic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - final configFileContents = buildConfig.toJsonString(); - final configUri = tempUri.resolve('config.json'); - final configFile = File.fromUri(configUri); - await configFile.writeAsString(configFileContents); - final buildConfig2 = BuildConfigImpl.fromArguments( - ['--config', configUri.toFilePath()], - environment: {}, // Don't inherit the test environment. - ); - expect(buildConfig2, buildConfig); - }); - - test('envScript', () { - final buildConfig1 = BuildConfigImpl( - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetArchitecture: Architecture.x64, - targetOS: OS.windows, - cCompiler: CCompilerConfig( - compiler: fakeCl, - envScript: fakeVcVars, - envScriptArgs: ['x64'], - ), - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - - final configFile = buildConfig1.toJson(); - final fromConfig = BuildConfigImpl.fromJson(configFile); - expect(fromConfig, equals(buildConfig1)); - }); - - for (final version in ['9001.0.0', '0.0.1']) { - test('BuildConfig version $version', () { - final outDir = outDirUri; - final config = { - 'link_mode_preference': 'prefer-static', - 'out_dir': outDir.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_root': tempUri.toFilePath(), - 'target_os': 'linux', - 'version': version, - 'package_name': packageName, - 'supported_asset_types': [CodeAsset.type], - 'dry_run': true, - }; - expect( - () => BuildConfigImpl.fromJson(config), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains(version) && - e.message.contains(HookConfigImpl.latestVersion.toString()), - )), - ); - }); - } - - test('BuildConfig invalid target os architecture combination', () { - final outDir = outDirUri; - final config = { - 'link_mode_preference': 'prefer-static', - 'out_dir': outDir.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': tempUri.toFilePath(), - 'target_os': 'windows', - 'target_architecture': 'arm', - 'build_mode': 'debug', - 'supported_asset_types': [CodeAsset.type], - 'version': HookConfigImpl.latestVersion.toString(), - }; - expect( - () => BuildConfigImpl.fromJson(config), - throwsA(predicate( - (e) => e is FormatException && e.message.contains('arm'), - )), - ); - }); - - test('BuildConfig dry_run access invalid args', () { - final outDir = outDirUri; - final config = { - 'link_mode_preference': 'prefer-static', - 'out_dir': outDir.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': tempUri.toFilePath(), - 'target_os': 'windows', - 'target_architecture': 'arm64', - 'build_mode': 'debug', - 'dry_run': true, - 'supported_asset_types': [CodeAsset.type], - 'version': HookConfigImpl.latestVersion.toString(), - }; - expect( - () => BuildConfigImpl.fromJson(config), - throwsA(predicate( - (e) => - e is FormatException && e.message.contains('In Flutter projects'), - )), - ); - }); - - test('BuildConfig dry_run access invalid args', () { - final outDir = outDirUri; - final config = { - 'dry_run': true, - 'linking_enabled': true, - 'link_mode_preference': 'prefer-static', - 'out_dir': outDir.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': tempUri.toFilePath(), - 'target_os': 'android', - 'supported_asset_types': [CodeAsset.type], - 'version': HookConfigImpl.latestVersion.toString(), - }; - final buildConfig = BuildConfigImpl.fromJson(config); - expect( - () => buildConfig.targetAndroidNdkApi, - throwsA(predicate( - (e) => e is StateError && e.message.contains('In Flutter projects'), - )), - ); - }); - - test('BuildConfig dry_run target arch', () { - final outDir = outDirUri; - final config = { - 'dry_run': true, - 'linking_enabled': false, - 'link_mode_preference': 'prefer-static', - 'out_dir': outDir.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': tempUri.toFilePath(), - 'target_os': 'windows', - 'supported_asset_types': [CodeAsset.type], - 'version': HookConfigImpl.latestVersion.toString(), - }; - final buildConfig = BuildConfigImpl.fromJson(config); - expect(buildConfig.targetArchitecture, isNull); - }); - - test('BuildConfig dry_run toString', () { - final buildConfig = BuildConfigImpl.dryRun( - packageName: packageName, - outputDirectoryShared: outputDirectoryShared, - outputDirectory: outDirUri, - packageRoot: tempUri, - targetOS: OS.windows, - linkModePreference: LinkModePreference.dynamic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - buildConfig.toJsonString(); - // No crash. - }); - - test('invalid architecture', () { - final config = { - 'build_mode': 'release', - 'dry_run': false, - 'link_mode_preference': 'prefer-static', - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_android_ndk_api': 30, - 'target_architecture': 'invalid_architecture', - 'target_os': 'android', - 'supported_asset_types': [CodeAsset.type], - 'version': HookOutputImpl.latestVersion.toString(), - }; - expect( - () => BuildConfigImpl.fromJson(config), - throwsFormatException, - ); - }); -} diff --git a/pkgs/native_assets_cli/test/model/build_output_test.dart b/pkgs/native_assets_cli/test/model/build_output_test.dart deleted file mode 100644 index 7f28a0ffb..000000000 --- a/pkgs/native_assets_cli/test/model/build_output_test.dart +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:io'; - -import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:test/test.dart'; - -void main() { - late Uri tempUri; - - setUp(() async { - tempUri = (await Directory.systemTemp.createTemp()).uri; - }); - - tearDown(() async { - await Directory.fromUri(tempUri).delete(recursive: true); - }); - - final jsonEncoding = { - 'timestamp': '2022-11-10 13:25:01.000', - 'assets': [ - { - 'architecture': 'x64', - 'file': Uri.file('path/to/libfoo.so').toFilePath(), - 'id': 'package:my_package/foo', - 'link_mode': {'type': 'dynamic_loading_bundle'}, - 'os': 'android', - 'type': 'native_code' - }, - { - 'architecture': 'x64', - 'id': 'package:my_package/foo2', - 'link_mode': { - 'type': 'dynamic_loading_system', - 'uri': Uri.file('path/to/libfoo2.so').toFilePath(), - }, - 'os': 'android', - 'type': 'native_code' - }, - { - 'architecture': 'x64', - 'id': 'package:my_package/foo3', - 'link_mode': {'type': 'dynamic_loading_process'}, - 'os': 'android', - 'type': 'native_code' - }, - { - 'architecture': 'x64', - 'id': 'package:my_package/foo4', - 'link_mode': {'type': 'dynamic_loading_executable'}, - 'os': 'android', - 'type': 'native_code' - } - ], - 'assetsForLinking': { - 'my_package': [ - { - 'name': 'data', - 'package': 'my_package', - 'file': Uri.file('path/to/data').toFilePath(), - 'type': 'data' - } - ], - 'my_package_2': [ - { - 'name': 'data', - 'package': 'my_package', - 'file': Uri.file('path/to/data2').toFilePath(), - 'type': 'data' - } - ] - }, - 'dependencies': [ - Uri.file('path/to/file.ext').toFilePath(), - ], - 'metadata': {'key': 'value'}, - 'version': '${HookOutputImpl.latestVersion}' - }; - - test('built info json', () { - final buildOutput = getBuildOutput(); - final json = buildOutput.toJson(HookOutputImpl.latestVersion); - expect(json, jsonEncoding); - - final buildOutput2 = HookOutputImpl.fromJson(json); - expect(buildOutput.hashCode, buildOutput2.hashCode); - expect(buildOutput, buildOutput2); - }); - - test('BuildOutput.toString', getBuildOutput().toString); - - test('BuildOutput.hashCode', () { - final buildOutput = getBuildOutput(); - final buildOutput2 = HookOutputImpl.fromJson(jsonEncoding); - expect(buildOutput.hashCode, buildOutput2.hashCode); - - final buildOutput3 = HookOutputImpl( - timestamp: DateTime.parse('2022-11-10 13:25:01.000'), - ); - expect(buildOutput.hashCode != buildOutput3.hashCode, true); - }); - - test('BuildOutput.readFromFile BuildOutput.writeToFile', () async { - final outDir = tempUri.resolve('out_dir/'); - final outDirShared = tempUri.resolve('out_dir_shared/'); - final packageRoot = tempUri.resolve('package_root/'); - await Directory.fromUri(outDir).create(); - await Directory.fromUri(outDirShared).create(); - await Directory.fromUri(packageRoot).create(); - final config = BuildConfigImpl( - outputDirectory: outDir, - outputDirectoryShared: outDirShared, - packageName: 'dontcare', - packageRoot: packageRoot, - buildMode: BuildMode.debug, - targetArchitecture: Architecture.arm64, - targetOS: OS.macOS, - linkModePreference: LinkModePreference.dynamic, - linkingEnabled: false, - supportedAssetTypes: [CodeAsset.type], - ); - final buildOutput = getBuildOutput(); - await buildOutput.writeToFile(config: config); - final buildOutput2 = HookOutputImpl.readFromFile(file: config.outputFile); - expect(buildOutput2, buildOutput); - }); - - test('BuildOutput.readFromFile BuildOutput.writeToFile V1.1.0', () async { - final outDir = tempUri.resolve('out_dir/'); - final outDirShared = tempUri.resolve('out_dir_shared/'); - final packageRoot = tempUri.resolve('package_root/'); - await Directory.fromUri(outDir).create(); - await Directory.fromUri(outDirShared).create(); - await Directory.fromUri(packageRoot).create(); - final config = BuildConfigImpl( - outputDirectory: outDir, - outputDirectoryShared: outDirShared, - packageName: 'dontcare', - packageRoot: packageRoot, - buildMode: BuildMode.debug, - targetArchitecture: Architecture.arm64, - targetOS: OS.macOS, - 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); - final buildOutput2 = HookOutputImpl.readFromFile(file: config.outputFile); - expect(buildOutput2, buildOutput); - }); - - test('Round timestamp', () { - final buildOutput3 = HookOutputImpl( - timestamp: DateTime.parse('2022-11-10 13:25:01.372257'), - ); - expect(buildOutput3.timestamp, DateTime.parse('2022-11-10 13:25:01.000')); - }); - - for (final version in ['9001.0.0', '0.0.1']) { - test('BuildOutput version $version', () { - expect( - () => HookOutputImpl.fromJsonString('{"version": "$version"}'), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains(version) && - e.message.contains(HookOutputImpl.latestVersion.toString()), - )), - ); - }); - } - - test('BuildOutput dependencies can be modified', () { - final buildOutput = HookOutputImpl(); - expect( - () => buildOutput.addDependencies([Uri.file('path/to/file.ext')]), - returnsNormally, - ); - }); - - test('BuildOutput setters', () { - final buildOutput = HookOutputImpl( - timestamp: DateTime.parse('2022-11-10 13:25:01.000'), - encodedAssets: [ - CodeAsset( - package: 'my_package', - name: 'foo', - file: Uri(path: 'path/to/libfoo.so'), - 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'), - Uri.file('path/to/file2.ext'), - ]), - metadata: const Metadata({ - 'key': 'value', - 'key2': 'value2', - }), - ); - - final buildOutput2 = HookOutputImpl( - timestamp: DateTime.parse('2022-11-10 13:25:01.000'), - ); - buildOutput2.addEncodedAsset( - CodeAsset( - package: 'my_package', - name: 'foo', - file: Uri(path: 'path/to/libfoo.so'), - linkMode: DynamicLoadingBundled(), - os: OS.android, - architecture: Architecture.x64, - ).encode(), - ); - 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'), - ); - buildOutput2.addDependencies([ - Uri.file('path/to/file2.ext'), - ]); - buildOutput2.addMetadata({ - 'key': 'value', - }); - buildOutput2.addMetadatum('key2', 'value2'); - - expect(buildOutput2, equals(buildOutput)); - expect( - buildOutput2.dependenciesModel, equals(buildOutput.dependenciesModel)); - expect(buildOutput2.metadataModel, equals(buildOutput.metadataModel)); - }); -} - -HookOutputImpl getBuildOutput({bool withLinkedAssets = true}) => HookOutputImpl( - timestamp: DateTime.parse('2022-11-10 13:25:01.000'), - encodedAssets: [ - CodeAsset( - package: 'my_package', - name: 'foo', - file: Uri(path: 'path/to/libfoo.so'), - 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(), - ], - 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, - dependencies: Dependencies([ - Uri.file('path/to/file.ext'), - ]), - metadata: const Metadata({ - 'key': 'value', - }), - ); diff --git a/pkgs/native_assets_cli/test/model/checksum_test.dart b/pkgs/native_assets_cli/test/model/checksum_test.dart deleted file mode 100644 index cff247336..000000000 --- a/pkgs/native_assets_cli/test/model/checksum_test.dart +++ /dev/null @@ -1,110 +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 'package:native_assets_cli/native_assets_cli_internal.dart'; -import 'package:test/test.dart'; - -import '../helpers.dart'; - -void main() { - const packageName = 'my_package'; - test('checksum', () async { - await inTempDir((tempUri) async { - final nativeAddUri = tempUri.resolve('native_add/'); - final fakeClangUri = tempUri.resolve('fake_clang'); - await File.fromUri(fakeClangUri).create(); - - final name1 = HookConfigImpl.checksum( - packageName: packageName, - packageRoot: nativeAddUri, - targetArchitecture: Architecture.x64, - targetOS: OS.linux, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - hook: Hook.build, - version: HookConfigImpl.latestVersion, - linkingEnabled: true, - ); - - // Using the checksum for a build folder should be stable. - expect(name1, '8780162e48a4539f01ea483fda6c1efc'); - - // Build folder different due to metadata. - final name2 = HookConfigImpl.checksum( - packageName: packageName, - packageRoot: nativeAddUri, - targetArchitecture: Architecture.x64, - targetOS: OS.linux, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - dependencyMetadata: { - 'foo': const Metadata({'key': 'value'}) - }, - hook: Hook.build, - version: HookConfigImpl.latestVersion, - linkingEnabled: true, - ); - printOnFailure([name1, name2].toString()); - expect(name1 != name2, true); - - // Build folder different due to cc. - final name3 = HookConfigImpl.checksum( - packageName: packageName, - packageRoot: nativeAddUri, - targetArchitecture: Architecture.x64, - targetOS: OS.linux, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - cCompiler: CCompilerConfig( - compiler: fakeClangUri, - ), - hook: Hook.build, - version: HookConfigImpl.latestVersion, - linkingEnabled: true, - ); - printOnFailure([name1, name3].toString()); - expect(name1 != name3, true); - - // Build folder different due to hook. - final name4 = HookConfigImpl.checksum( - packageName: packageName, - packageRoot: nativeAddUri, - targetArchitecture: Architecture.x64, - targetOS: OS.linux, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - cCompiler: CCompilerConfig( - compiler: fakeClangUri, - ), - hook: Hook.link, - version: HookConfigImpl.latestVersion, - linkingEnabled: true, - ); - printOnFailure([name1, name4].toString()); - expect(name1 != name4, true); - - // Build folder different due to haslinkPhase. - final name5 = HookConfigImpl.checksum( - packageName: packageName, - packageRoot: nativeAddUri, - targetArchitecture: Architecture.x64, - targetOS: OS.linux, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - hook: Hook.build, - version: HookConfigImpl.latestVersion, - linkingEnabled: false, - ); - printOnFailure([name1, name5].toString()); - expect(name1, isNot(name5)); - }); - }); -} diff --git a/pkgs/native_assets_cli/test/model/link_config_test.dart b/pkgs/native_assets_cli/test/model/link_config_test.dart deleted file mode 100644 index 1f07cc4e2..000000000 --- a/pkgs/native_assets_cli/test/model/link_config_test.dart +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:io'; - -import 'package:native_assets_cli/native_assets_cli_internal.dart'; -import 'package:test/test.dart'; - -void main() async { - late Uri tempUri; - late Uri outDirUri; - late Uri outDir2Uri; - late Uri outputDirectoryShared; - late Uri outputDirectoryShared2; - const packageName = 'my_package'; - late Uri packageRootUri; - late Uri fakeClang; - late Uri fakeLd; - late Uri fakeAr; - late Uri fakeCl; - late Uri fakeVcVars; - late Uri recordedUsagesFile; - final assets = [ - DataAsset( - package: packageName, - name: 'name', - file: Uri.file('nonexistent'), - ).encode(), - CodeAsset( - package: packageName, - name: 'name2', - linkMode: DynamicLoadingBundled(), - os: OS.android, - file: Uri.file('not there'), - architecture: Architecture.riscv64, - ).encode(), - ]; - - setUp(() async { - tempUri = (await Directory.systemTemp.createTemp()).uri; - outDirUri = tempUri.resolve('out1/'); - await Directory.fromUri(outDirUri).create(); - outDir2Uri = tempUri.resolve('out2/'); - await Directory.fromUri(outDir2Uri).create(); - outputDirectoryShared = tempUri.resolve('out_shared1/'); - await Directory.fromUri(outputDirectoryShared).create(); - outputDirectoryShared2 = tempUri.resolve('out_shared2/'); - await Directory.fromUri(outputDirectoryShared2).create(); - packageRootUri = tempUri.resolve('$packageName/'); - await Directory.fromUri(packageRootUri).create(); - fakeClang = tempUri.resolve('fake_clang'); - await File.fromUri(fakeClang).create(); - fakeLd = tempUri.resolve('fake_ld'); - await File.fromUri(fakeLd).create(); - fakeAr = tempUri.resolve('fake_ar'); - await File.fromUri(fakeAr).create(); - fakeCl = tempUri.resolve('cl.exe'); - await File.fromUri(fakeCl).create(); - fakeVcVars = tempUri.resolve('vcvarsall.bat'); - await File.fromUri(fakeVcVars).create(); - recordedUsagesFile = tempUri.resolve('recorded_usages.json'); - File.fromUri(recordedUsagesFile).createSync(); - }); - - tearDown(() async { - await Directory.fromUri(tempUri).delete(recursive: true); - }); - - test('LinkConfig ==', () { - final config1 = LinkConfigImpl( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - archiver: fakeAr, - ), - buildMode: BuildMode.release, - encodedAssets: assets, - recordedUsagesFile: recordedUsagesFile, - linkModePreference: LinkModePreference.preferStatic, - ); - - final config2 = LinkConfigImpl( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: outDir2Uri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - encodedAssets: [], - recordedUsagesFile: null, - linkModePreference: LinkModePreference.preferStatic, - ); - - expect(config1, equals(config1)); - expect(config1 == config2, false); - expect(config1.outputDirectory != config2.outputDirectory, true); - expect(config1.packageRoot, config2.packageRoot); - expect(config1.targetArchitecture == config2.targetArchitecture, true); - expect(config1.targetOS != config2.targetOS, true); - expect(config1.targetIOSSdk, IOSSdk.iPhoneOS); - expect(() => config2.targetIOSSdk, throwsStateError); - expect(config1.cCompiler.compiler != config2.cCompiler.compiler, true); - expect(config1.cCompiler.linker != config2.cCompiler.linker, true); - expect(config1.cCompiler.archiver != config2.cCompiler.archiver, true); - expect(config1.cCompiler.envScript == config2.cCompiler.envScript, true); - expect(config1.cCompiler.envScriptArgs == config2.cCompiler.envScriptArgs, - true); - expect(config1.cCompiler != config2.cCompiler, true); - expect(config1.encodedAssets != config2.encodedAssets, true); - }); - - test('LinkConfig fromConfig', () { - final buildConfig2 = LinkConfigImpl( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - 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(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_android_ndk_api': 30, - 'target_architecture': 'arm64', - 'target_os': 'android', - 'version': HookOutputImpl.latestVersion.toString(), - 'assets': [for (final asset in assets) asset.toJson()], - }; - - final fromConfig = LinkConfigImpl.fromJson(config); - expect(fromConfig, equals(buildConfig2)); - }); - - test('LinkConfig.dryRun', () { - final buildConfig2 = LinkConfigImpl.dryRun( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetOS: OS.android, - 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(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_os': 'android', - 'version': HookOutputImpl.latestVersion.toString(), - 'assets': [], - }; - - final fromConfig = LinkConfigImpl.fromJson(config); - expect(fromConfig, equals(buildConfig2)); - }); - - test('LinkConfig toJson fromConfig', () { - final buildConfig1 = LinkConfigImpl( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: packageRootUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - ), - buildMode: BuildMode.release, - encodedAssets: assets, - linkModePreference: LinkModePreference.preferStatic, - ); - - final configFile = buildConfig1.toJson(); - final fromConfig = LinkConfigImpl.fromJson(configFile); - expect(fromConfig, equals(buildConfig1)); - }); - - test('LinkConfig toJson fromJson', () { - final outDir = outDirUri; - final buildConfig1 = LinkConfigImpl( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: outDir, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - ), - buildMode: BuildMode.release, - encodedAssets: assets, - linkModePreference: LinkModePreference.preferStatic, - ); - - final jsonObject = buildConfig1.toJson(); - final expectedJson = { - '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(), - 'target_architecture': 'arm64', - 'target_ios_sdk': 'iphoneos', - 'target_os': 'ios', - 'version': '${HookConfigImpl.latestVersion}', - 'link_mode_preference': 'prefer-static', - }; - expect(jsonObject, equals(expectedJson)); - - final buildConfig2 = LinkConfigImpl.fromJson(jsonObject); - expect(buildConfig2, buildConfig1); - }); - - test('LinkConfig FormatExceptions', () { - expect( - () => LinkConfigImpl.fromJson({}), - throwsA(predicate( - (e) => - e is FormatException && - e.message - .contains('No value was provided for required key: target_os'), - )), - ); - expect( - () => LinkConfigImpl.fromJson({ - 'version': HookConfigImpl.latestVersion.toString(), - 'supported_asset_types': [CodeAsset.type], - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_architecture': 'arm64', - 'target_os': 'android', - 'target_android_ndk_api': 30, - 'assets': [], - }), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains( - 'No value was provided for required key: out_dir', - ), - )), - ); - expect( - () => LinkConfigImpl.fromJson({ - 'version': HookConfigImpl.latestVersion.toString(), - 'supported_asset_types': [CodeAsset.type], - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_architecture': 'arm64', - 'target_os': 'android', - 'target_android_ndk_api': 30, - 'build_mode': BuildMode.release.name, - 'assets': 'astring', - 'link_mode_preference': LinkModePreference.preferStatic.name, - }), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains( - "Unexpected value 'astring' for key '.assets' in config file. " - 'Expected a List?.', - ), - )), - ); - expect( - () => LinkConfigImpl.fromJson({ - 'supported_asset_types': [CodeAsset.type], - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'version': HookConfigImpl.latestVersion.toString(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_architecture': 'arm64', - 'target_os': 'android', - 'build_mode': BuildMode.release.name, - 'link_mode_preference': LinkModePreference.preferStatic.name, - }), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains( - 'No value was provided for required key: target_android_ndk_api', - ), - )), - ); - }); - - test('LinkConfig toString', () { - final config = LinkConfigImpl( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - cCompiler: CCompilerConfig( - compiler: fakeClang, - linker: fakeLd, - ), - buildMode: BuildMode.release, - encodedAssets: assets, - linkModePreference: LinkModePreference.preferStatic, - ); - expect(config.toString(), isNotEmpty); - }); - - test('LinkConfig fromArgs', () async { - final buildConfig = LinkConfigImpl( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.android, - targetAndroidNdkApi: 30, - buildMode: BuildMode.release, - encodedAssets: assets, - recordedUsagesFile: recordedUsagesFile, - linkModePreference: LinkModePreference.preferStatic, - ); - final configFileContents = buildConfig.toJsonString(); - final configUri = tempUri.resolve('config.json'); - final configFile = File.fromUri(configUri); - await configFile.writeAsString(configFileContents); - final buildConfig2 = - LinkConfigImpl.fromArguments(['--config', configUri.toFilePath()]); - expect(buildConfig2, buildConfig); - }); - - for (final version in ['9001.0.0', '0.0.1']) { - test('LinkConfig version $version', () { - final outDir = outDirUri; - final config = { - 'link_mode_preference': 'prefer-static', - 'out_dir': outDir.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_root': tempUri.toFilePath(), - 'target_os': 'linux', - 'version': version, - 'package_name': packageName, - 'dry_run': true, - }; - expect( - () => LinkConfigImpl.fromJson(config), - throwsA(predicate( - (e) => - e is FormatException && - e.message.contains(version) && - e.message.contains(HookConfigImpl.latestVersion.toString()), - )), - ); - }); - } - - test('LinkConfig invalid target os architecture combination', () { - final outDir = outDirUri; - final config = { - 'link_mode_preference': 'prefer-static', - 'out_dir': outDir.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': tempUri.toFilePath(), - 'target_os': 'windows', - 'target_architecture': 'arm', - 'build_mode': 'debug', - 'version': HookConfigImpl.latestVersion.toString(), - }; - expect( - () => LinkConfigImpl.fromJson(config), - throwsA(predicate( - (e) => e is FormatException && e.message.contains('arm'), - )), - ); - }); - - test('LinkConfig dry_run access invalid args', () { - final outDir = outDirUri; - final config = { - 'link_mode_preference': 'prefer-static', - 'out_dir': outDir.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': tempUri.toFilePath(), - 'target_os': 'windows', - 'target_architecture': 'arm64', - 'build_mode': 'debug', - 'dry_run': true, - 'version': HookConfigImpl.latestVersion.toString(), - }; - expect( - () => LinkConfigImpl.fromJson(config), - throwsA(predicate( - (e) => - e is FormatException && e.message.contains('In Flutter projects'), - )), - ); - }); - - test('LinkConfig dry_run access invalid args', () { - 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, - 'package_root': tempUri.toFilePath(), - 'target_os': 'android', - 'dry_run': true, - 'version': HookConfigImpl.latestVersion.toString(), - }; - final buildConfig = LinkConfigImpl.fromJson(config); - expect( - () => buildConfig.targetAndroidNdkApi, - throwsA(predicate( - (e) => e is StateError && e.message.contains('In Flutter projects'), - )), - ); - }); - - 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(), - 'package_name': packageName, - 'package_root': tempUri.toFilePath(), - 'target_os': 'windows', - 'dry_run': true, - 'version': HookConfigImpl.latestVersion.toString(), - }; - final buildConfig = LinkConfigImpl.fromJson(config); - expect(buildConfig.targetArchitecture, isNull); - }); - - test('LinkConfig dry_run toString', () { - final buildConfig = LinkConfigImpl.dryRun( - supportedAssetTypes: [CodeAsset.type], - packageName: packageName, - outputDirectory: outDirUri, - outputDirectoryShared: outputDirectoryShared, - packageRoot: tempUri, - targetOS: OS.windows, - encodedAssets: assets, - linkModePreference: LinkModePreference.preferStatic, - ); - expect(buildConfig.toJsonString(), isNotEmpty); - }); - - test('invalid architecture', () { - final config = { - 'supported_asset_types': [CodeAsset.type], - 'build_mode': 'release', - 'dry_run': false, - 'link_mode_preference': 'prefer-static', - 'out_dir': outDirUri.toFilePath(), - 'out_dir_shared': outputDirectoryShared.toFilePath(), - 'package_name': packageName, - 'package_root': packageRootUri.toFilePath(), - 'target_android_ndk_api': 30, - 'target_architecture': 'invalid_architecture', - 'target_os': 'android', - 'version': HookOutputImpl.latestVersion.toString(), - }; - expect( - () => LinkConfigImpl.fromJson(config), - throwsFormatException, - ); - }); -} diff --git a/pkgs/native_assets_cli/test/validator/validator_test.dart b/pkgs/native_assets_cli/test/validator/validator_test.dart index e37ab9de7..ba732ce20 100644 --- a/pkgs/native_assets_cli/test/validator/validator_test.dart +++ b/pkgs/native_assets_cli/test/validator/validator_test.dart @@ -29,35 +29,92 @@ void main() { await Directory.fromUri(tempUri).delete(recursive: true); }); + BuildConfig makeCodeBuildConfig( + {LinkModePreference linkModePreference = LinkModePreference.dynamic}) { + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageName: packageName, + packageRoot: tempUri, + targetOS: OS.iOS, + buildMode: BuildMode.release, + supportedAssetTypes: [CodeAsset.type], + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupBuildRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outDirSharedUri, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.arm64, + targetIOSSdk: IOSSdk.iPhoneOS, + linkModePreference: linkModePreference, + ); + return BuildConfig(configBuilder.json); + } + + LinkConfig makeCodeLinkConfig() { + final configBuilder = LinkConfigBuilder() + ..setupHookConfig( + packageName: packageName, + packageRoot: tempUri, + targetOS: OS.iOS, + buildMode: BuildMode.release, + supportedAssetTypes: [CodeAsset.type], + ) + ..setupLinkConfig(assets: []) + ..setupLinkRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outDirSharedUri, + recordedUsesFile: null, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.arm64, + targetIOSSdk: IOSSdk.iPhoneOS, + linkModePreference: LinkModePreference.dynamic, + ); + return LinkConfig(configBuilder.json); + } + + BuildConfig makeDataBuildConfig() { + final configBuilder = BuildConfigBuilder() + ..setupHookConfig( + packageName: packageName, + packageRoot: tempUri, + targetOS: OS.iOS, + buildMode: BuildMode.release, + supportedAssetTypes: [DataAsset.type]) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupBuildRunConfig( + outputDirectory: outDirUri, + outputDirectoryShared: outDirSharedUri, + ); + return BuildConfig(configBuilder.json); + } + test('linking not enabled', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeCodeBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.codeAssets.add( + outputBuilder.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', file: assetFile.uri, linkMode: DynamicLoadingBundled(), os: config.targetOS, - architecture: config.targetArchitecture, + architecture: config.codeConfig.targetArchitecture, ), linkInPackage: 'bar', ); - final errors = await validateBuildOutput(config, output); + final errors = + await validateBuildOutput(config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains('linkingEnabled is false')), @@ -65,28 +122,17 @@ void main() { }); test('supported asset type', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeCodeBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.dataAssets.add(DataAsset( + outputBuilder.dataAssets.add(DataAsset( package: config.packageName, name: 'foo.txt', file: assetFile.uri, )); - final errors = await validateBuildOutput(config, output); + final errors = + await validateBuildOutput(config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains('"data" is not a supported asset type')), @@ -94,27 +140,16 @@ void main() { }); test('file exists', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [DataAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeCodeBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); - output.dataAssets.add(DataAsset( + outputBuilder.dataAssets.add(DataAsset( package: config.packageName, name: 'foo.txt', file: assetFile.uri, )); - final errors = await validateDataAssetBuildOutput(config, output); + final errors = await validateDataAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains('which does not exist')), @@ -122,28 +157,17 @@ void main() { }); test('file not set', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); - output.codeAssets.add(CodeAsset( + final config = makeCodeBuildConfig(); + final outputBuilder = BuildOutputBuilder(); + outputBuilder.codeAssets.add(CodeAsset( package: config.packageName, name: 'foo.dylib', - architecture: config.targetArchitecture, + architecture: config.codeConfig.targetArchitecture, os: config.targetOS, linkMode: DynamicLoadingBundled(), )); - final errors = await validateCodeAssetBuildOutput(config, output); + final errors = await validateCodeAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains('has no file')), @@ -155,33 +179,23 @@ void main() { (LinkModePreference.dynamic, StaticLinking()), ]) { test('native code asset wrong linking $linkModePreference', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: linkModePreference, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = + makeCodeBuildConfig(linkModePreference: linkModePreference); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.codeAssets.add( + outputBuilder.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', file: assetFile.uri, linkMode: linkMode, os: config.targetOS, - architecture: config.targetArchitecture, + architecture: config.codeConfig.targetArchitecture, ), ); - final errors = await validateCodeAssetBuildOutput(config, output); + final errors = await validateCodeAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains( @@ -192,23 +206,11 @@ void main() { } test('native code wrong architecture', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeCodeBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.codeAssets.add( + outputBuilder.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', @@ -218,7 +220,8 @@ void main() { architecture: Architecture.x64, ), ); - final errors = await validateCodeAssetBuildOutput(config, output); + final errors = await validateCodeAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains( @@ -228,23 +231,11 @@ void main() { }); test('native code no architecture', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeCodeBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.codeAssets.add( + outputBuilder.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', @@ -253,7 +244,8 @@ void main() { os: config.targetOS, ), ); - final errors = await validateCodeAssetBuildOutput(config, output); + final errors = await validateCodeAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains( @@ -263,33 +255,22 @@ void main() { }); test('native code asset wrong os', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeCodeBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.codeAssets.add( + outputBuilder.codeAssets.add( CodeAsset( package: config.packageName, name: 'foo.dart', file: assetFile.uri, linkMode: DynamicLoadingBundled(), os: OS.windows, - architecture: config.targetArchitecture, + architecture: config.codeConfig.targetArchitecture, ), ); - final errors = await validateCodeAssetBuildOutput(config, output); + final errors = await validateCodeAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains( @@ -299,28 +280,17 @@ void main() { }); test('asset id in wrong package', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [DataAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeDataBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.dataAssets.add(DataAsset( + outputBuilder.dataAssets.add(DataAsset( package: 'different_package', name: 'foo.txt', file: assetFile.uri, )); - final errors = await validateDataAssetBuildOutput(config, output); + final errors = await validateDataAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains('Data asset must have package name my_package')), @@ -328,23 +298,11 @@ void main() { }); test('duplicate asset id', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [DataAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeDataBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.dataAssets.addAll([ + outputBuilder.dataAssets.addAll([ DataAsset( package: config.packageName, name: 'foo.txt', @@ -356,7 +314,8 @@ void main() { file: assetFile.uri, ), ]); - final errors = await validateDataAssetBuildOutput(config, output); + final errors = await validateDataAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( errors, contains(contains('More than one')), @@ -364,28 +323,17 @@ void main() { }); test('link hook validation', () async { - final config = LinkConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - assets: [], - ); - final output = LinkOutput(); + final config = makeCodeLinkConfig(); + final outputBuilder = LinkOutputBuilder(); final assetFile = File.fromUri(outDirUri.resolve('foo.dylib')); await assetFile.writeAsBytes([1, 2, 3]); - output.dataAssets.add(DataAsset( + outputBuilder.dataAssets.add(DataAsset( package: config.packageName, name: 'foo.txt', file: assetFile.uri, )); - final errors = await validateLinkOutput(config, output); + final errors = + await validateLinkOutput(config, LinkOutput(outputBuilder.json)); expect( errors, contains(contains('"data" is not a supported asset type')), @@ -393,31 +341,19 @@ void main() { }); test('duplicate dylib name', () async { - final config = BuildConfig.build( - outputDirectory: outDirUri, - outputDirectoryShared: outDirSharedUri, - packageName: packageName, - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - supportedAssetTypes: [CodeAsset.type], - linkingEnabled: false, - ); - final output = BuildOutput(); + final config = makeCodeBuildConfig(); + final outputBuilder = BuildOutputBuilder(); final fileName = config.targetOS.dylibFileName('foo'); final assetFile = File.fromUri(outDirUri.resolve(fileName)); await assetFile.writeAsBytes([1, 2, 3]); - output.codeAssets.addAll([ + outputBuilder.codeAssets.addAll([ CodeAsset( package: config.packageName, name: 'src/foo.dart', file: assetFile.uri, linkMode: DynamicLoadingBundled(), os: config.targetOS, - architecture: config.targetArchitecture, + architecture: config.codeConfig.targetArchitecture, ), CodeAsset( package: config.packageName, @@ -425,10 +361,11 @@ void main() { file: assetFile.uri, linkMode: DynamicLoadingBundled(), os: config.targetOS, - architecture: config.targetArchitecture, + architecture: config.codeConfig.targetArchitecture, ), ]); - final errors = await validateCodeAssetBuildOutput(config, output); + final errors = await validateCodeAssetBuildOutput( + config, BuildOutput(outputBuilder.json)); expect( 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 34c28ae1e..2468589cd 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart @@ -101,7 +101,7 @@ class CBuilder extends CTool implements Builder { @override Future run({ required BuildConfig config, - required BuildOutput output, + required BuildOutputBuilder output, required Logger? logger, String? linkInPackage, }) async { @@ -113,7 +113,7 @@ class CBuilder extends CTool implements Builder { final packageRoot = config.packageRoot; await Directory.fromUri(outDir).create(recursive: true); final linkMode = - getLinkMode(linkModePreference ?? config.linkModePreference); + getLinkMode(linkModePreference ?? config.codeConfig.linkModePreference); final libUri = outDir.resolve(config.targetOS.libraryFileName(name, linkMode)); final exeUri = outDir.resolve(config.targetOS.executableFileName(name)); @@ -132,6 +132,7 @@ class CBuilder extends CTool implements Builder { if (!config.dryRun) { final task = RunCBuilder( config: config, + codeConfig: config.codeConfig, logger: logger, sources: sources, includes: includes, @@ -169,7 +170,8 @@ class CBuilder extends CTool implements Builder { file: libUri, linkMode: linkMode, os: config.targetOS, - architecture: config.dryRun ? null : config.targetArchitecture, + architecture: + config.dryRun ? null : config.codeConfig.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 3ac153901..55c5ef997 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart @@ -44,7 +44,7 @@ class CLinker extends CTool implements Linker { @override Future run({ required LinkConfig config, - required LinkOutput output, + required LinkOutputBuilder output, required Logger? logger, }) async { if (OS.current != OS.linux || config.targetOS != OS.linux) { @@ -55,7 +55,7 @@ class CLinker extends CTool implements Linker { final packageRoot = config.packageRoot; await Directory.fromUri(outDir).create(recursive: true); final linkMode = - getLinkMode(linkModePreference ?? config.linkModePreference); + getLinkMode(linkModePreference ?? config.codeConfig.linkModePreference); final libUri = outDir.resolve(config.targetOS.libraryFileName(name, linkMode)); final sources = [ @@ -66,55 +66,50 @@ class CLinker extends CTool implements Linker { for (final directory in this.includes) packageRoot.resolveUri(Uri.file(directory)), ]; - if (!config.dryRun) { - final task = RunCBuilder( - config: config, - linkerOptions: linkerOptions, - logger: logger, - sources: sources, - includes: includes, - frameworks: frameworks, - dynamicLibrary: linkMode == DynamicLoadingBundled() ? libUri : null, - staticLibrary: linkMode == StaticLinking() ? libUri : null, - // ignore: invalid_use_of_visible_for_testing_member - installName: installName, - flags: flags, - defines: defines, - pic: pic, - std: std, - language: language, - cppLinkStdLib: cppLinkStdLib, - ); - await task.run(); - } + final task = RunCBuilder( + config: config, + codeConfig: config.codeConfig, + linkerOptions: linkerOptions, + logger: logger, + sources: sources, + includes: includes, + frameworks: frameworks, + dynamicLibrary: linkMode == DynamicLoadingBundled() ? libUri : null, + staticLibrary: linkMode == StaticLinking() ? libUri : null, + // ignore: invalid_use_of_visible_for_testing_member + installName: installName, + flags: flags, + defines: defines, + pic: pic, + std: std, + language: language, + cppLinkStdLib: cppLinkStdLib, + ); + await task.run(); if (assetName != null) { - output.codeAssets.add( - 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.codeConfig.targetArchitecture, + )); } - if (!config.dryRun) { - final includeFiles = await Stream.fromIterable(includes) - .asyncExpand( - (include) => Directory(include.toFilePath()) - .list(recursive: true) - .where((entry) => entry is File) - .map((file) => file.uri), - ) - .toList(); + final includeFiles = await Stream.fromIterable(includes) + .asyncExpand( + (include) => Directory(include.toFilePath()) + .list(recursive: true) + .where((entry) => entry is File) + .map((file) => file.uri), + ) + .toList(); - output.addDependencies({ - // Note: We use a Set here to deduplicate the dependencies. - ...sources, - ...includeFiles, - }); - } + output.addDependencies({ + // Note: We use a Set here to deduplicate the dependencies. + ...sources, + ...includeFiles, + }); } } diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/compiler_resolver.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/compiler_resolver.dart index b33a12199..392d6324f 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/compiler_resolver.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/compiler_resolver.dart @@ -21,12 +21,14 @@ import '../tool/tool_instance.dart'; // For example use Clang or MSVC on Windows. class CompilerResolver { final HookConfig hookConfig; + final CodeConfig codeConfig; final Logger? logger; final OS hostOS; final Architecture hostArchitecture; CompilerResolver({ required this.hookConfig, + required this.codeConfig, required this.logger, OS? hostOS, // Only visible for testing. Architecture? hostArchitecture, // Only visible for testing. @@ -48,7 +50,7 @@ class CompilerResolver { } final targetOS = hookConfig.targetOS; - final targetArchitecture = hookConfig.targetArchitecture; + final targetArchitecture = codeConfig.targetArchitecture; final errorMessage = "No tools configured on host '${hostOS}_$hostArchitecture' with target " "'${targetOS}_$targetArchitecture'."; @@ -59,7 +61,7 @@ class CompilerResolver { /// Select the right compiler for cross compiling to the specified target. Tool? _selectCompiler() { final targetOS = hookConfig.targetOS; - final targetArch = hookConfig.targetArchitecture; + final targetArch = codeConfig.targetArchitecture; // TODO(dacoharkes): Support falling back on other tools. if (targetArch == hostArchitecture && @@ -99,7 +101,7 @@ class CompilerResolver { } Future _tryLoadCompilerFromConfig() async { - final configCcUri = hookConfig.cCompiler.compiler; + final configCcUri = codeConfig.cCompiler.compiler; if (configCcUri != null) { assert(await File.fromUri(configCcUri).exists()); logger?.finer('Using compiler ${configCcUri.toFilePath()} ' @@ -134,7 +136,7 @@ class CompilerResolver { } final targetOS = hookConfig.targetOS; - final targetArchitecture = hookConfig.targetArchitecture; + final targetArchitecture = codeConfig.targetArchitecture; final errorMessage = "No tools configured on host '${hostOS}_$hostArchitecture' with target " "'${targetOS}_$targetArchitecture'."; @@ -145,7 +147,7 @@ class CompilerResolver { /// Select the right archiver for cross compiling to the specified target. Tool? _selectArchiver() { final targetOS = hookConfig.targetOS; - final targetArchitecture = hookConfig.targetArchitecture; + final targetArchitecture = codeConfig.targetArchitecture; // TODO(dacoharkes): Support falling back on other tools. if (targetArchitecture == hostArchitecture && @@ -184,7 +186,7 @@ class CompilerResolver { } Future _tryLoadArchiverFromConfig() async { - final configArUri = hookConfig.cCompiler.archiver; + final configArUri = codeConfig.cCompiler.archiver; if (configArUri != null) { assert(await File.fromUri(configArUri).exists()); logger?.finer('Using archiver ${configArUri.toFilePath()} ' @@ -197,7 +199,7 @@ class CompilerResolver { } Future toolchainEnvironmentScript(ToolInstance compiler) async { - final fromConfig = hookConfig.cCompiler.envScript; + final fromConfig = codeConfig.cCompiler.envScript; if (fromConfig != null) { logger?.fine('Using envScript from config: $fromConfig'); return fromConfig; @@ -211,7 +213,7 @@ class CompilerResolver { } List? toolchainEnvironmentScriptArguments() { - final fromConfig = hookConfig.cCompiler.envScriptArgs; + final fromConfig = codeConfig.cCompiler.envScriptArgs; if (fromConfig != null) { logger?.fine('Using envScriptArgs from config: $fromConfig'); return fromConfig; @@ -223,7 +225,7 @@ class CompilerResolver { Future resolveLinker() async { final targetOS = hookConfig.targetOS; - final targetArchitecture = hookConfig.targetArchitecture; + final targetArchitecture = codeConfig.targetArchitecture; // First, check if the launcher provided a direct path to the compiler. var result = await _tryLoadLinkerFromConfig(); @@ -245,7 +247,7 @@ class CompilerResolver { } Future _tryLoadLinkerFromConfig() async { - final configLdUri = hookConfig.cCompiler.linker; + final configLdUri = codeConfig.cCompiler.linker; if (configLdUri != null) { assert(await File.fromUri(configLdUri).exists()); logger?.finer('Using linker ${configLdUri.toFilePath()} ' @@ -260,7 +262,7 @@ class CompilerResolver { /// Select the right compiler for cross compiling to the specified target. Tool? _selectLinker() { final targetOS = hookConfig.targetOS; - final targetArchitecture = hookConfig.targetArchitecture; + final targetArchitecture = codeConfig.targetArchitecture; if (targetOS == OS.macOS || targetOS == OS.iOS) return appleLd; if (targetOS == OS.android) return androidNdkLld; diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart index 3b31c92c4..4cf0c99d3 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart @@ -22,6 +22,7 @@ class RunCBuilder { /// should be run. final LinkerOptions? linkerOptions; final HookConfig config; + final CodeConfig codeConfig; final Logger? logger; final List sources; final List includes; @@ -47,6 +48,7 @@ class RunCBuilder { RunCBuilder({ required this.config, + required this.codeConfig, this.linkerOptions, this.logger, this.sources = const [], @@ -76,7 +78,8 @@ class RunCBuilder { } } - late final _resolver = CompilerResolver(hookConfig: config, logger: logger); + late final _resolver = CompilerResolver( + hookConfig: config, codeConfig: codeConfig, logger: logger); Future compiler() async => await _resolver.resolveCompiler(); @@ -130,7 +133,7 @@ class RunCBuilder { final IOSSdk? targetIosSdk; if (config.targetOS == OS.iOS) { - targetIosSdk = config.targetIOSSdk; + targetIosSdk = codeConfig.targetIOSSdk; } else { targetIosSdk = null; } @@ -141,18 +144,18 @@ class RunCBuilder { final int? targetAndroidNdkApi; if (config.targetOS == OS.android) { final minimumApi = - config.targetArchitecture == Architecture.riscv64 ? 35 : 21; - targetAndroidNdkApi = max(config.targetAndroidNdkApi!, minimumApi); + codeConfig.targetArchitecture == Architecture.riscv64 ? 35 : 21; + targetAndroidNdkApi = max(codeConfig.targetAndroidNdkApi!, minimumApi); } else { targetAndroidNdkApi = null; } final targetIOSVersion = - config.targetOS == OS.iOS ? config.targetIOSVersion : null; + config.targetOS == OS.iOS ? codeConfig.targetIOSVersion : null; final targetMacOSVersion = - config.targetOS == OS.macOS ? config.targetMacOSVersion : null; + config.targetOS == OS.macOS ? codeConfig.targetMacOSVersion : null; - final architecture = config.targetArchitecture; + final architecture = codeConfig.targetArchitecture; final sourceFiles = sources.map((e) => e.toFilePath()).toList(); final objectFiles = []; if (staticLibrary != null) { 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 1d9b85f37..ea920c0f7 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 @@ -10,7 +10,7 @@ library; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:test/test.dart'; @@ -30,20 +30,30 @@ void main() { await File.fromUri(addCUri).writeAsString(addCBrokenContents); const name = 'add'; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - linkModePreference: LinkModePreference.dynamic, - buildMode: BuildMode.release, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( sources: [addCUri.toFilePath()], 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 12c0cd713..dcbd0e236 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 @@ -4,7 +4,7 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:native_toolchain_c/src/utils/run_process.dart'; import 'package:test/test.dart'; @@ -109,23 +109,33 @@ Future buildLib( final tempUriShared = tempUri.resolve('shared/'); await Directory.fromUri(tempUriShared).create(); - - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.android, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: targetArchitecture, + cCompilerConfig: cCompiler, + targetAndroidNdkApi: androidNdkApi, + linkModePreference: linkMode == DynamicLoadingBundled() + ? LinkModePreference.dynamic + : LinkModePreference.static, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUriShared, - packageName: name, - packageRoot: tempUri, - targetArchitecture: targetArchitecture, - targetOS: OS.android, - targetAndroidNdkApi: androidNdkApi, - buildMode: BuildMode.release, - linkModePreference: linkMode == DynamicLoadingBundled() - ? LinkModePreference.dynamic - : LinkModePreference.static, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: 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 34afa5f47..06577cdd3 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 @@ -10,7 +10,7 @@ library; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:native_toolchain_c/src/utils/run_process.dart'; import 'package:test/test.dart'; @@ -63,22 +63,34 @@ void main() { 'test/cbuilder/testfiles/add_objective_c/src/add.m'), Language() => throw UnimplementedError(), }; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.iOS, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: target, + linkModePreference: linkMode == DynamicLoadingBundled() + ? LinkModePreference.dynamic + : LinkModePreference.static, + targetIOSSdk: targetIOSSdk, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: target, - targetOS: OS.iOS, - buildMode: BuildMode.release, - linkModePreference: linkMode == DynamicLoadingBundled() - ? LinkModePreference.dynamic - : LinkModePreference.static, - targetIOSSdk: targetIOSSdk, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: name, @@ -209,23 +221,34 @@ Future buildLib( final addCUri = packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); const name = 'add'; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.iOS, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: targetArchitecture, + linkModePreference: linkMode == DynamicLoadingBundled() + ? LinkModePreference.dynamic + : LinkModePreference.static, + targetIOSSdk: IOSSdk.iPhoneOS, + targetIOSVersion: targetIOSVersion, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: targetArchitecture, - targetOS: OS.iOS, - targetIOSSdk: IOSSdk.iPhoneOS, - targetIOSVersion: targetIOSVersion, - buildMode: BuildMode.release, - linkModePreference: linkMode == DynamicLoadingBundled() - ? LinkModePreference.dynamic - : LinkModePreference.static, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: 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 c659f2f7a..63e3aaeec 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 @@ -7,7 +7,7 @@ library; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:test/test.dart'; @@ -36,21 +36,32 @@ void main() { packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); const name = 'add'; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.linux, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: target, + linkModePreference: linkMode == DynamicLoadingBundled() + ? LinkModePreference.dynamic + : LinkModePreference.static, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: target, - targetOS: OS.linux, - buildMode: BuildMode.release, - linkModePreference: linkMode == DynamicLoadingBundled() - ? LinkModePreference.dynamic - : LinkModePreference.static, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: 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 a24bd5c7b..9a92e3649 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 @@ -10,7 +10,7 @@ library; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:native_toolchain_c/src/utils/run_process.dart'; import 'package:test/test.dart'; @@ -49,21 +49,31 @@ void main() { }; const name = 'add'; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.macOS, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: target, + linkModePreference: linkMode == DynamicLoadingBundled() + ? LinkModePreference.dynamic + : LinkModePreference.static, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: target, - targetOS: OS.macOS, - buildMode: BuildMode.release, - linkModePreference: linkMode == DynamicLoadingBundled() - ? LinkModePreference.dynamic - : LinkModePreference.static, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: name, @@ -139,22 +149,33 @@ Future buildLib( final addCUri = packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); const name = 'add'; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.iOS, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: targetArchitecture, + linkModePreference: linkMode == DynamicLoadingBundled() + ? LinkModePreference.dynamic + : LinkModePreference.static, + targetMacOSVersion: targetMacOSVersion, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: targetArchitecture, - targetOS: OS.macOS, - targetMacOSVersion: targetMacOSVersion, - buildMode: BuildMode.release, - linkModePreference: linkMode == DynamicLoadingBundled() - ? LinkModePreference.dynamic - : LinkModePreference.static, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: 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 9fe63c0ee..5cba2fba3 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 @@ -10,7 +10,7 @@ library; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:native_toolchain_c/src/native_toolchain/msvc.dart'; import 'package:native_toolchain_c/src/utils/run_process.dart'; @@ -55,21 +55,32 @@ void main() { packageUri.resolve('test/cbuilder/testfiles/add/src/add.c'); const name = 'add'; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.windows, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: target, + linkModePreference: linkMode == DynamicLoadingBundled() + ? LinkModePreference.dynamic + : LinkModePreference.static, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetOS: OS.windows, - targetArchitecture: target, - buildMode: BuildMode.release, - linkModePreference: linkMode == DynamicLoadingBundled() - ? LinkModePreference.dynamic - : LinkModePreference.static, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: name, diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart index faa0c7ece..3482144d8 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart @@ -11,7 +11,7 @@ library; import 'dart:ffi'; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:native_toolchain_c/src/utils/run_process.dart'; import 'package:test/test.dart'; @@ -44,21 +44,32 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: buildMode, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + // Ignored by executables. + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - buildMode: buildMode, - // Ignored by executables. - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); + final cbuilder = CBuilder.executable( name: name, sources: [helloWorldCUri.toFilePath()], @@ -115,31 +126,29 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = dryRun - ? BuildConfig.dryRun( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: tempUri, - outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetOS: OS.current, - linkModePreference: LinkModePreference.dynamic, - linkingEnabled: false, - ) - : BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: tempUri, - outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, - ); - final buildOutput = BuildOutput(); + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: dryRun, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: dryRun ? null : cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( + outputDirectory: tempUri, + outputDirectoryShared: tempUri2, + ); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( sources: [addCUri.toFilePath()], @@ -218,21 +227,30 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + // Ignored by executables. + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - buildMode: BuildMode.release, - // Ignored by executables. - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final flag = switch (buildConfig.targetOS) { OS.windows => '/DFOO=USER_FLAG', @@ -276,20 +294,30 @@ void main() { packageUri.resolve('test/cbuilder/testfiles/includes/src/includes.c'); const name = 'includes'; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + // Ignored by executables. + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutputBuilder = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: name, @@ -299,10 +327,11 @@ void main() { ); await cbuilder.run( config: buildConfig, - output: buildOutput, + output: buildOutputBuilder, logger: logger, ); + final buildOutput = BuildOutput(buildOutputBuilder.json); expect(buildOutput.dependencies, contains(includesHUri)); final dylibUri = tempUri.resolve(OS.current.dylibFileName(name)); @@ -321,20 +350,30 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + // Ignored by executables. + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final stdFlag = switch (buildConfig.targetOS) { OS.windows => '/std:$std', @@ -379,21 +418,30 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], - buildMode: BuildMode.release, + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + // Ignored by executables. + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - // Ignored by executables. - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final defaultStdLibLinkFlag = switch (buildConfig.targetOS) { OS.windows => null, @@ -443,21 +491,31 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], - buildMode: BuildMode.release, + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + // Ignored by executables. + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - // Ignored by executables. - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); + final cbuilder = CBuilder.executable( name: name, sources: [helloWorldCppUri.toFilePath()], @@ -514,21 +572,31 @@ Future testDefines({ } const name = 'defines'; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: buildMode, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + // Ignored by executables. + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - buildMode: buildMode, - // Ignored by executables. - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); + final cbuilder = CBuilder.executable( name: name, sources: [definesCUri.toFilePath()], 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 4f1b58e98..6c13c5257 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/compiler_resolver_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/compiler_resolver_test.dart @@ -9,7 +9,7 @@ library; import 'package:collection/collection.dart'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/src/cbuilder/compiler_resolver.dart'; import 'package:native_toolchain_c/src/native_toolchain/apple_clang.dart'; import 'package:native_toolchain_c/src/native_toolchain/clang.dart'; @@ -41,48 +41,74 @@ void main() { final envScript = [ ...await msvc.vcvars64.defaultResolver!.resolve(logger: logger) ].firstOrNull?.uri; - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: 'dummy', + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: CCompilerConfig( + archiver: ar, + compiler: cc, + linker: ld, + envScript: envScript, + ), + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: 'dummy', - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - cCompiler: CCompilerConfig( - archiver: ar, - compiler: cc, - linker: ld, - envScript: envScript, - ), - linkingEnabled: false, ); - final resolver = CompilerResolver(hookConfig: buildConfig, logger: logger); + final buildConfig = BuildConfig(buildConfigBuilder.json); + final resolver = CompilerResolver( + hookConfig: buildConfig, + codeConfig: buildConfig.codeConfig, + logger: logger); final compiler = await resolver.resolveCompiler(); final archiver = await resolver.resolveArchiver(); - expect(compiler.uri, buildConfig.cCompiler.compiler); - expect(archiver.uri, buildConfig.cCompiler.archiver); + expect(compiler.uri, buildConfig.codeConfig.cCompiler.compiler); + expect(archiver.uri, buildConfig.codeConfig.cCompiler.archiver); }); test('No compiler found', () async { final tempUri = await tempDirForTest(); final tempUri2 = await tempDirForTest(); - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: tempUri, + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: 'dummy', + packageRoot: tempUri, + targetOS: OS.windows, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.arm64, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectoryShared: tempUri2, - packageName: 'dummy', - packageRoot: tempUri, - targetArchitecture: Architecture.arm64, - targetOS: OS.windows, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - linkingEnabled: false, + outputDirectory: tempUri, ); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final resolver = CompilerResolver( hookConfig: buildConfig, + codeConfig: buildConfig.codeConfig, logger: logger, hostOS: OS.android, // This is never a host. hostArchitecture: Architecture.arm64, // This is never a host. 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 4d76aaf14..737a3e941 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/objective_c_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/objective_c_test.dart @@ -11,7 +11,7 @@ library; import 'dart:ffi'; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:test/test.dart'; @@ -36,20 +36,30 @@ void main() { final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], - buildMode: BuildMode.release, + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: OS.current, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: Architecture.current, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: Architecture.current, - targetOS: OS.current, - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutput = BuildOutputBuilder(); final cbuilder = CBuilder.library( name: name, diff --git a/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart b/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart index 57f37bbda..39c22e4fe 100644 --- a/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart +++ b/pkgs/native_toolchain_c/test/clinker/build_testfiles.dart @@ -4,7 +4,7 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import '../helpers.dart'; @@ -26,20 +26,31 @@ Future buildTestArchive( final logMessages = []; final logger = createCapturingLogger(logMessages); - final buildConfig = BuildConfig.build( - supportedAssetTypes: [CodeAsset.type], + final buildConfigBuilder = BuildConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: name, + packageRoot: tempUri, + targetOS: os, + buildMode: BuildMode.release, + ) + ..setupBuildConfig( + linkingEnabled: false, + dryRun: false, + ) + ..setupCodeConfig( + targetArchitecture: architecture, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + buildConfigBuilder.setupBuildRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: name, - packageRoot: tempUri, - targetArchitecture: architecture, - targetOS: os, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - cCompiler: cCompiler, - linkingEnabled: false, ); - final buildOutput = BuildOutput(); + + final buildConfig = BuildConfig(buildConfigBuilder.json); + final buildOutputBuilder = BuildOutputBuilder(); + final cbuilder = CBuilder.library( name: name, assetName: '', @@ -48,9 +59,10 @@ Future buildTestArchive( ); await cbuilder.run( config: buildConfig, - output: buildOutput, + output: buildOutputBuilder, logger: logger, ); - return buildOutput.codeAssets.all.first.file!; + final buildOutput = BuildOutput(buildOutputBuilder.json); + return buildOutput.codeAssets.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 1f9654732..3e5300e86 100644 --- a/pkgs/native_toolchain_c/test/clinker/objects_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/objects_test.dart @@ -9,7 +9,7 @@ library; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:test/test.dart'; @@ -27,25 +27,36 @@ Future main() async { const name = 'mylibname'; test('link two objects', () async { - final linkOutput = LinkOutput(); final tempUri = await tempDirForTest(); final tempUri2 = await tempDirForTest(); final uri = await buildTestArchive(tempUri, tempUri2, os, architecture); - final linkConfig = LinkConfig.build( - supportedAssetTypes: [CodeAsset.type], + + final linkConfigBuilder = LinkConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: 'testpackage', + packageRoot: tempUri, + targetOS: os, + buildMode: BuildMode.debug, + ) + ..setupLinkConfig( + assets: [], + ) + ..setupCodeConfig( + targetArchitecture: architecture, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + linkConfigBuilder.setupLinkRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: 'testpackage', - packageRoot: tempUri, - targetArchitecture: architecture, - targetOS: os, - buildMode: BuildMode.debug, - linkModePreference: LinkModePreference.dynamic, - assets: [], - cCompiler: cCompiler, + recordedUsesFile: null, ); - printOnFailure(linkConfig.cCompiler.toString()); + final linkConfig = LinkConfig(linkConfigBuilder.json); + final linkOutput = LinkOutputBuilder(); + + printOnFailure(linkConfig.codeConfig.cCompiler.toString()); printOnFailure(Platform.environment.keys.toList().toString()); await CLinker.library( name: name, @@ -58,8 +69,9 @@ Future main() async { logger: logger, ); - expect(linkOutput.codeAssets.all, hasLength(1)); - final asset = linkOutput.codeAssets.all.first; + final codeAssets = LinkOutput(linkOutput.json).codeAssets; + expect(codeAssets, hasLength(1)); + final asset = codeAssets.first; expect(asset, isA()); await expectSymbols( asset: asset, diff --git a/pkgs/native_toolchain_c/test/clinker/throws_test.dart b/pkgs/native_toolchain_c/test/clinker/throws_test.dart index 48e5838aa..fc88a5d39 100644 --- a/pkgs/native_toolchain_c/test/clinker/throws_test.dart +++ b/pkgs/native_toolchain_c/test/clinker/throws_test.dart @@ -4,7 +4,7 @@ import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:test/test.dart'; @@ -23,25 +23,37 @@ Future main() async { final tempUri = await tempDirForTest(); final tempUri2 = await tempDirForTest(); + final linkConfigBuilder = LinkConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: 'testpackage', + packageRoot: tempUri, + targetOS: os, + buildMode: BuildMode.debug, + ) + ..setupLinkConfig( + assets: [], + ) + ..setupCodeConfig( + targetArchitecture: Architecture.x64, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + linkConfigBuilder.setupLinkRunConfig( + outputDirectoryShared: tempUri2, + outputDirectory: tempUri, + recordedUsesFile: null, + ); + final linkHookConfig = LinkConfig(linkConfigBuilder.json); + final cLinker = CLinker.library( name: 'mylibname', linkerOptions: LinkerOptions.manual(), ); await expectLater( () => cLinker.run( - config: LinkConfig.build( - supportedAssetTypes: [CodeAsset.type], - outputDirectory: tempUri, - outputDirectoryShared: tempUri2, - packageName: 'testpackage', - packageRoot: tempUri, - targetArchitecture: Architecture.x64, - targetOS: os, - buildMode: BuildMode.debug, - linkModePreference: LinkModePreference.dynamic, - assets: [], - ), - output: LinkOutput(), + config: linkHookConfig, + output: LinkOutputBuilder(), logger: logger, ), throwsUnsupportedError, diff --git a/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart b/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart index 6bf1b5d71..8a746e516 100644 --- a/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart +++ b/pkgs/native_toolchain_c/test/clinker/treeshake_helper.dart @@ -9,7 +9,7 @@ library; import 'dart:io'; -import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:native_assets_cli/native_assets_cli_internal.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:test/test.dart'; @@ -62,30 +62,41 @@ Future runTests(List architectures) async { architecture, ); - final linkOutput = LinkOutput(); - - final config = LinkConfig.build( - supportedAssetTypes: [CodeAsset.type], + final linkConfigBuilder = LinkConfigBuilder() + ..setupHookConfig( + supportedAssetTypes: [CodeAsset.type], + packageName: 'testpackage', + packageRoot: tempUri, + targetOS: os, + buildMode: BuildMode.release, + //outputDirectoryShared: tempUri2, + ) + ..setupLinkConfig( + assets: [], + ) + ..setupCodeConfig( + targetArchitecture: architecture, + linkModePreference: LinkModePreference.dynamic, + cCompilerConfig: cCompiler, + ); + linkConfigBuilder.setupLinkRunConfig( outputDirectory: tempUri, outputDirectoryShared: tempUri2, - packageName: 'testpackage', - packageRoot: tempUri, - targetArchitecture: architecture, - targetOS: os, - buildMode: BuildMode.release, - linkModePreference: LinkModePreference.dynamic, - assets: [], - cCompiler: cCompiler, + recordedUsesFile: null, ); - printOnFailure(config.cCompiler.toString()); + final linkConfig = LinkConfig(linkConfigBuilder.json); + final linkOutputBuilder = LinkOutputBuilder(); + + printOnFailure(linkConfig.codeConfig.cCompiler.toString()); printOnFailure(Platform.environment.keys.toList().toString()); await clinker.linker([testArchive.toFilePath()]).run( - config: config, - output: linkOutput, + config: linkConfig, + output: linkOutputBuilder, logger: logger, ); - final asset = linkOutput.codeAssets.all.first; + final linkOutput = LinkOutput(linkOutputBuilder.json); + final asset = linkOutput.codeAssets.first; final filePath = asset.file!.toFilePath(); final machine = await readelfMachine(filePath);