blob: b5200304f20d07c3a553d8f10f3685511e7c229e [file] [log] [blame]
// 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/native_assets_bundling.dart';
import 'package:dartdev/src/sdk.dart';
import 'package:dartdev/src/utils.dart';
import 'package:file/local.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),
fileSystem: const LocalFileSystem(),
);
final target = Target.current;
final macOSConfig = target.os == OS.macOS
? MacOSConfig(targetVersion: minimumSupportedMacOSVersion)
: null;
final cCompilerConfig = getCCompilerConfig();
final buildResult = await nativeAssetsBuildRunner.build(
configCreator: () => BuildConfigBuilder()
..setupCodeConfig(
targetOS: target.os,
targetArchitecture: target.architecture,
// When running in JIT mode, only dynamic libraries are supported.
linkModePreference: LinkModePreference.dynamic,
macOSConfig: macOSConfig,
cCompilerConfig: cCompilerConfig,
),
configValidator: (config) async => [
...await validateCodeAssetBuildConfig(config),
...await validateDataAssetBuildConfig(config),
],
workingDirectory: workingDirectory,
runPackageName: runPackageName,
linkingEnabled: false,
buildAssetTypes: [
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 dartToolUri = Directory.current.uri.resolve('.dart_tool/');
final outputUri = dartToolUri.resolve('native_assets/');
await Directory.fromUri(outputUri).create(recursive: true);
final kernelAssets = await bundleNativeAssets(
assets,
Target.current,
outputUri,
relocatable: false,
);
return await writeNativeAssetsYaml(
kernelAssets,
dartToolUri,
header: '''# Native assets mapping for host OS in JIT mode.
# Generated by dartdev and package:native_assets_builder.
''',
);
}
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(
const LocalFileSystem(),
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) {
return CCompilerConfig(
archiver: Uri.file(ar),
compiler: Uri.file(cc),
envScript: envScript != null ? Uri.file(envScript) : null,
envScriptArgs: hasEnvScriptArgs ? envScriptArgs : null,
linker: Uri.file(ld),
);
}
return null;
}