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);