| // 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:async'; |
| import 'dart:io'; |
| |
| import 'package:dartdev/src/sdk.dart'; |
| import 'package:dartdev/src/utils.dart'; |
| import 'package:logging/logging.dart'; |
| import 'package:native_assets_builder/native_assets_builder.dart'; |
| import 'package:native_assets_cli/code_assets_builder.dart'; |
| import 'package:native_assets_cli/data_assets_builder.dart'; |
| import 'package:native_assets_cli/native_assets_cli_internal.dart'; |
| |
| import 'core.dart'; |
| |
| /// Compiles all native assets for host OS in JIT mode. |
| /// |
| /// If provided, only native assets of all transitive dependencies of |
| /// [runPackageName] are built. |
| Future<List<EncodedAsset>?> compileNativeAssetsJit({ |
| required bool verbose, |
| String? runPackageName, |
| }) async { |
| final workingDirectory = Directory.current.uri; |
| // TODO(https://github.com/dart-lang/package_config/issues/126): Use |
| // package config resolution from package:package_config. |
| if (!await File.fromUri( |
| workingDirectory.resolve('.dart_tool/package_config.json')) |
| .exists()) { |
| if (await File.fromUri(workingDirectory.resolve('pubspec.yaml')).exists()) { |
| // Silently run `pub get`, this is what would happen in |
| // `getExecutableForCommand` later. |
| final result = await Process.run(sdk.dart, ['pub', 'get']); |
| if (result.exitCode != 0) { |
| return null; |
| } |
| } else { |
| return null; |
| } |
| } |
| final nativeAssetsBuildRunner = NativeAssetsBuildRunner( |
| // This always runs in JIT mode. |
| dartExecutable: Uri.file(sdk.dart), |
| logger: logger(verbose), |
| ); |
| final target = Target.current; |
| final targetMacOSVersion = |
| target.os == OS.macOS ? minimumSupportedMacOSVersion : null; |
| final cCompilerConfig = getCCompilerConfig(); |
| final buildResult = await nativeAssetsBuildRunner.build( |
| configCreator: () => BuildConfigBuilder() |
| ..setupCodeConfig( |
| targetArchitecture: target.architecture, |
| // When running in JIT mode, only dynamic libraries are supported. |
| linkModePreference: LinkModePreference.dynamic, |
| targetMacOSVersion: targetMacOSVersion, |
| cCompilerConfig: cCompilerConfig, |
| ), |
| configValidator: (config) async => [ |
| ...await validateCodeAssetBuildConfig(config), |
| ...await validateDataAssetBuildConfig(config), |
| ], |
| workingDirectory: workingDirectory, |
| targetOS: target.os, |
| // Dart has no concept of release vs debug, default to release. |
| buildMode: BuildMode.release, |
| includeParentEnvironment: true, |
| runPackageName: runPackageName, |
| linkingEnabled: false, |
| supportedAssetTypes: [ |
| CodeAsset.type, |
| ], |
| buildValidator: (config, output) async => [ |
| ...await validateDataAssetBuildOutput(config, output), |
| ...await validateCodeAssetBuildOutput(config, output), |
| ], |
| applicationAssetValidator: (assets) async => [ |
| ...await validateCodeAssetInApplication(assets), |
| ], |
| ); |
| if (buildResult == null) return null; |
| return buildResult.encodedAssets; |
| } |
| |
| /// Compiles all native assets for host OS in JIT mode, and creates the |
| /// native assets yaml file. |
| /// |
| /// If provided, only native assets of all transitive dependencies of |
| /// [runPackageName] are built. |
| /// |
| /// Used in `dart run` and `dart test`. |
| Future<Uri?> compileNativeAssetsJitYamlFile({ |
| required bool verbose, |
| String? runPackageName, |
| }) async { |
| final assets = await compileNativeAssetsJit( |
| verbose: verbose, |
| runPackageName: runPackageName, |
| ); |
| if (assets == null) return null; |
| final codeAssets = assets |
| .where((e) => e.type == CodeAsset.type) |
| .map(CodeAsset.fromEncoded) |
| .toList(); |
| final dataAssets = assets |
| .where((e) => e.type == DataAsset.type) |
| .map(DataAsset.fromEncoded) |
| .toList(); |
| final kernelAssets = KernelAssets([ |
| ...[ |
| for (final asset in codeAssets) _targetLocation(asset), |
| ], |
| ...[ |
| for (final asset in dataAssets) _dataTargetLocation(asset), |
| ] |
| ]); |
| |
| final workingDirectory = Directory.current.uri; |
| final assetsUri = workingDirectory.resolve('.dart_tool/native_assets.yaml'); |
| final nativeAssetsYaml = '''# Native assets mapping for host OS in JIT mode. |
| # Generated by dartdev and package:native_assets_builder. |
| ${kernelAssets.toNativeAssetsFile()}'''; |
| final assetFile = File(assetsUri.toFilePath()); |
| await assetFile.writeAsString(nativeAssetsYaml); |
| return assetsUri; |
| } |
| |
| KernelAsset _targetLocation(CodeAsset asset) { |
| final linkMode = asset.linkMode; |
| final KernelAssetPath kernelAssetPath; |
| switch (linkMode) { |
| case DynamicLoadingSystem _: |
| kernelAssetPath = KernelAssetSystemPath(linkMode.uri); |
| case LookupInExecutable _: |
| kernelAssetPath = KernelAssetInExecutable(); |
| case LookupInProcess _: |
| kernelAssetPath = KernelAssetInProcess(); |
| case DynamicLoadingBundled _: |
| kernelAssetPath = KernelAssetAbsolutePath(asset.file!); |
| default: |
| throw Exception( |
| 'Unsupported CodeAsset linkMode ${linkMode.runtimeType} in asset $asset', |
| ); |
| } |
| return KernelAsset( |
| id: asset.id, |
| target: Target.fromArchitectureAndOS(asset.architecture!, asset.os), |
| path: kernelAssetPath, |
| ); |
| } |
| |
| KernelAsset _dataTargetLocation(DataAsset asset) { |
| return KernelAsset( |
| id: asset.id, |
| target: Target.current, |
| path: KernelAssetAbsolutePath(asset.file), |
| ); |
| } |
| |
| Future<bool> warnOnNativeAssets() async { |
| final workingDirectory = Directory.current.uri; |
| if (!await File.fromUri( |
| workingDirectory.resolve('.dart_tool/package_config.json')) |
| .exists()) { |
| // If `pub get` hasn't run, we can't know, so don't error. |
| return false; |
| } |
| try { |
| final packageLayout = |
| await PackageLayout.fromRootPackageRoot(workingDirectory); |
| final packagesWithNativeAssets = [ |
| ...await packageLayout.packagesWithAssets(Hook.build), |
| ...await packageLayout.packagesWithAssets(Hook.link) |
| ]; |
| if (packagesWithNativeAssets.isEmpty) { |
| return false; |
| } |
| final packageNames = packagesWithNativeAssets.map((p) => p.name).join(' '); |
| log.stderr( |
| 'Package(s) $packageNames require the native assets feature to be enabled. ' |
| 'Enable native assets with `--enable-experiment=native-assets`.', |
| ); |
| } on FormatException catch (e) { |
| // This can be thrown if the package_config.json is malformed or has |
| // duplicate entries. |
| log.stderr( |
| 'Error encountered while parsing package_config.json: ${e.message}', |
| ); |
| } |
| return true; |
| } |
| |
| Logger logger(bool verbose) => Logger('') |
| ..onRecord.listen((LogRecord record) { |
| final levelValue = record.level.value; |
| if (levelValue >= Level.SEVERE.value) { |
| log.stderr(record.message); |
| } else if (levelValue >= Level.WARNING.value || |
| verbose && levelValue >= Level.INFO.value) { |
| log.stdout(record.message); |
| } else { |
| // Note, this is ignored by default. |
| log.trace(record.message); |
| } |
| }); |
| |
| CCompilerConfig? getCCompilerConfig() { |
| // Specifically for running our tests on Dart CI with the test runner, we |
| // recognize specific variables to setup the C Compiler configuration. |
| 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) { |
| return 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, |
| ); |
| } |
| return null; |
| } |