[deps] Roll dart-lang/native

This CL refactors the native assets implementation in Dartdev:

* It introduces a `DartNativeAssetsBuilder` that wraps all logic.
* The code now explicitly searches for a package config, and invokes
  `pub get` if that file does not exist but a pubspec does.
  (For most `dartdev` commands, `pub get` was already running later
  implicitly.)
* The code now explicitly searches for a `runPackageName` and skips
  building native assets if a package name cannot be found.

Change-Id: Ib5562fc64c3a756c99fee2e2daa763eb6fb1e855
Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-arm64-try,pkg-win-release-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404583
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Hossein Yousefi <yousefi@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
diff --git a/DEPS b/DEPS
index acf5a7b..bd78007 100644
--- a/DEPS
+++ b/DEPS
@@ -141,7 +141,7 @@
   "markdown_rev": "19aaded4300d24bedcbf52ade792b203ddf030b0",
   "material_color_utilities_rev": "799b6ba2f3f1c28c67cc7e0b4f18e0c7d7f3c03e",
   # dart-native-interop-team@ is rolling breaking changes manually while the assets features are in experimental.
-  "native_rev": "d1d9aa5e62d239580b8bf17e36db6d47c308fc89", # disable tools/rev_sdk_deps.dart
+  "native_rev": "3ed5d3d07246cd792693584c2c42216739a634b7", # disable tools/rev_sdk_deps.dart
   "protobuf_rev": "b7dd58cdbd879beee4c3fbf8ee80fce8e97bad26",
   "pub_rev": "710265bae23ad5860f33287fba10b5c369f19a93", # disable tools/rev_sdk_deps.dart
   "shelf_rev": "bf799519cda2898a7c5af06dcfdd5fe6443afd79",
diff --git a/pkg/dartdev/lib/src/commands/build.dart b/pkg/dartdev/lib/src/commands/build.dart
index 25ce5cc..386c218 100644
--- a/pkg/dartdev/lib/src/commands/build.dart
+++ b/pkg/dartdev/lib/src/commands/build.dart
@@ -10,14 +10,9 @@
 import 'package:dartdev/src/experiments.dart';
 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:front_end/src/api_prototype/compiler_options.dart'
     show Verbosity;
-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:package_config/package_config.dart' as package_config;
 import 'package:path/path.dart' as path;
 import 'package:vm/target_os.dart'; // For possible --target-os values.
 
@@ -132,61 +127,23 @@
     }
     await outputDir.create(recursive: true);
 
-    // Start native asset generation here.
     stdout.writeln('Building native assets.');
-    final packageConfig = await findPackageConfigUri(sourceUri);
-    final runPackageName = await findRootPackageName(sourceUri);
-    final workingDirectory = Directory.current.uri;
-    final packageLayout = PackageLayout.fromPackageConfig(
-      LocalFileSystem(),
-      await package_config.loadPackageConfigUri(packageConfig!),
-      packageConfig,
+    final packageConfig = await DartNativeAssetsBuilder.ensurePackageConfig(
+      sourceUri,
     );
-    final target = Target.current;
-    final macOSConfig = target.os == OS.macOS
-        ? MacOSConfig(targetVersion: minimumSupportedMacOSVersion)
-        : null;
-    final nativeAssetsBuildRunner = NativeAssetsBuildRunner(
-      fileSystem: const LocalFileSystem(),
-      dartExecutable: Uri.file(sdk.dart),
-      logger: logger(verbose),
+    final runPackageName = await DartNativeAssetsBuilder.findRootPackageName(
+      sourceUri,
     );
-
-    final cCompilerConfig = getCCompilerConfig();
-
-    final buildResult = await nativeAssetsBuildRunner.build(
-      inputCreator: () => BuildInputBuilder()
-        ..config.setupCode(
-          targetOS: target.os,
-          linkModePreference: LinkModePreference.dynamic,
-          targetArchitecture: target.architecture,
-          macOS: macOSConfig,
-          cCompiler: cCompilerConfig,
-        ),
-      inputValidator: (config) async => [
-        ...await validateDataAssetBuildInput(config),
-        ...await validateCodeAssetBuildInput(config),
-      ],
-      workingDirectory: workingDirectory,
-      packageLayout: packageLayout,
-      runPackageName: runPackageName,
-      linkingEnabled: true,
-      buildAssetTypes: [
-        CodeAsset.type,
-      ],
-      buildValidator: (config, output) async => [
-        ...await validateDataAssetBuildOutput(config, output),
-        ...await validateCodeAssetBuildOutput(config, output),
-      ],
-      applicationAssetValidator: (assets) async => [
-        ...await validateCodeAssetInApplication(assets),
-      ],
+    final builder = DartNativeAssetsBuilder(
+      packageConfigUri: packageConfig!,
+      runPackageName: runPackageName!,
+      verbose: verbose,
     );
+    final buildResult = await builder.buildNativeAssetsAOT();
     if (buildResult == null) {
       stderr.writeln('Native assets build failed.');
       return 255;
     }
-    // End native asset generation here.
 
     final tempDir = Directory.systemTemp.createTempSync();
     try {
@@ -211,38 +168,10 @@
         recordedUsagesFile: recordedUsagesPath,
       );
 
-      // Start linking here.
-      final linkResult = await nativeAssetsBuildRunner.link(
-        inputCreator: () => LinkInputBuilder()
-          ..config.setupCode(
-            targetOS: target.os,
-            targetArchitecture: target.architecture,
-            linkModePreference: LinkModePreference.dynamic,
-            macOS: macOSConfig,
-            cCompiler: cCompilerConfig,
-          ),
-        inputValidator: (config) async => [
-          ...await validateDataAssetLinkInput(config),
-          ...await validateCodeAssetLinkInput(config),
-        ],
-        resourceIdentifiers:
-            recordUseEnabled ? Uri.file(recordedUsagesPath!) : null,
-        workingDirectory: workingDirectory,
-        runPackageName: runPackageName,
-        packageLayout: packageLayout,
+      final linkResult = await builder.linkNativeAssetsAOT(
+        recordedUsagesPath: recordedUsagesPath,
         buildResult: buildResult,
-        buildAssetTypes: [
-          CodeAsset.type,
-        ],
-        linkValidator: (config, output) async => [
-          ...await validateDataAssetLinkOutput(config, output),
-          ...await validateCodeAssetLinkOutput(config, output),
-        ],
-        applicationAssetValidator: (assets) async => [
-          ...await validateCodeAssetInApplication(assets),
-        ],
       );
-
       if (linkResult == null) {
         stderr.writeln('Native assets link failed.');
         return 255;
@@ -265,7 +194,7 @@
       if (allAssets.isNotEmpty) {
         final kernelAssets = await bundleNativeAssets(
           allAssets,
-          target,
+          builder.target,
           outputUri,
           relocatable: true,
           verbose: true,
diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart
index 75cb77e..fdc2d4c 100644
--- a/pkg/dartdev/lib/src/commands/compile.dart
+++ b/pkg/dartdev/lib/src/commands/compile.dart
@@ -558,23 +558,35 @@
       return compileErrorExitCode;
     }
 
-    if (!nativeAssetsExperimentEnabled) {
-      if (await warnOnNativeAssets()) {
-        return 255;
-      }
-    } else {
-      final assets = await compileNativeAssetsJit(
-        verbose: verbose,
-        runPackageName: await findRootPackageName(Directory.current.uri),
+    final packageConfig = await DartNativeAssetsBuilder.ensurePackageConfig(
+      Directory.current.uri,
+    );
+    if (packageConfig != null) {
+      final runPackageName = await DartNativeAssetsBuilder.findRootPackageName(
+        Directory.current.uri,
       );
-      if (assets == null) {
-        stderr.writeln('Native assets build failed.');
-        return 255;
-      }
-      if (assets.isNotEmpty) {
-        stderr.writeln(
-            "'dart compile' does currently not support native assets.");
-        return 255;
+      if (runPackageName != null) {
+        final builder = DartNativeAssetsBuilder(
+          packageConfigUri: packageConfig,
+          runPackageName: runPackageName,
+          verbose: verbose,
+        );
+        if (!nativeAssetsExperimentEnabled) {
+          if (await builder.warnOnNativeAssets()) {
+            return 255;
+          }
+        } else {
+          final assets = await builder.compileNativeAssetsJit();
+          if (assets == null) {
+            stderr.writeln('Native assets build failed.');
+            return 255;
+          }
+          if (assets.isNotEmpty) {
+            stderr.writeln(
+                "'dart compile' does currently not support native assets.");
+            return 255;
+          }
+        }
       }
     }
 
diff --git a/pkg/dartdev/lib/src/commands/language_server.dart b/pkg/dartdev/lib/src/commands/language_server.dart
index 1e0ff3b..65ec628 100644
--- a/pkg/dartdev/lib/src/commands/language_server.dart
+++ b/pkg/dartdev/lib/src/commands/language_server.dart
@@ -52,7 +52,7 @@
         sdk.analysisServerSnapshot,
         args,
         packageConfigOverride: null,
-        useExecProcess : false,
+        useExecProcess: false,
       );
       return 0;
     } catch (e, st) {
diff --git a/pkg/dartdev/lib/src/commands/run.dart b/pkg/dartdev/lib/src/commands/run.dart
index cb28882..74817db 100644
--- a/pkg/dartdev/lib/src/commands/run.dart
+++ b/pkg/dartdev/lib/src/commands/run.dart
@@ -371,22 +371,34 @@
     }
 
     String? nativeAssets;
-    if (!nativeAssetsExperimentEnabled) {
-      if (await warnOnNativeAssets()) {
-        return errorExitCode;
-      }
-    } else {
+    final packageConfig = await DartNativeAssetsBuilder.ensurePackageConfig(
+      Directory.current.uri,
+    );
+    if (packageConfig != null) {
       final runPackageName = getPackageForCommand(mainCommand) ??
-          await findRootPackageName(Directory.current.uri);
-      final assetsYamlFileUri = await compileNativeAssetsJitYamlFile(
-        verbose: verbose,
-        runPackageName: runPackageName,
-      );
-      if (assetsYamlFileUri == null) {
-        log.stderr('Error: Compiling native assets failed.');
-        return errorExitCode;
+          await DartNativeAssetsBuilder.findRootPackageName(
+            Directory.current.uri,
+          );
+      if (runPackageName != null) {
+        final builder = DartNativeAssetsBuilder(
+          packageConfigUri: packageConfig,
+          runPackageName: runPackageName,
+          verbose: verbose,
+        );
+        if (!nativeAssetsExperimentEnabled) {
+          if (await builder.warnOnNativeAssets()) {
+            return errorExitCode;
+          }
+        } else {
+          final assetsYamlFileUri =
+              await builder.compileNativeAssetsJitYamlFile();
+          if (assetsYamlFileUri == null) {
+            log.stderr('Error: Compiling native assets failed.');
+            return errorExitCode;
+          }
+          nativeAssets = assetsYamlFileUri.toFilePath();
+        }
       }
-      nativeAssets = assetsYamlFileUri.toFilePath();
     }
 
     final String? residentCompilerInfoFileArg =
diff --git a/pkg/dartdev/lib/src/commands/test.dart b/pkg/dartdev/lib/src/commands/test.dart
index 7a2ea62..cfa73ee 100644
--- a/pkg/dartdev/lib/src/commands/test.dart
+++ b/pkg/dartdev/lib/src/commands/test.dart
@@ -47,21 +47,33 @@
     final args = argResults!;
 
     String? nativeAssets;
-    if (!nativeAssetsExperimentEnabled) {
-      if (await warnOnNativeAssets()) {
-        return DartdevCommand.errorExitCode;
-      }
-    } else {
-      final assetsYamlFileUri =
-          await compileNativeAssetsJitYamlFile(
-        verbose: verbose,
-        runPackageName: await findRootPackageName(Directory.current.uri),
+    final packageConfig = await DartNativeAssetsBuilder.ensurePackageConfig(
+      Directory.current.uri,
+    );
+    if (packageConfig != null) {
+      final runPackageName = await DartNativeAssetsBuilder.findRootPackageName(
+        Directory.current.uri,
       );
-      if (assetsYamlFileUri == null) {
-        log.stderr('Error: Compiling native assets failed.');
-        return DartdevCommand.errorExitCode;
+      if (runPackageName != null) {
+        final builder = DartNativeAssetsBuilder(
+          packageConfigUri: packageConfig,
+          runPackageName: runPackageName,
+          verbose: verbose,
+        );
+        if (!nativeAssetsExperimentEnabled) {
+          if (await builder.warnOnNativeAssets()) {
+            return DartdevCommand.errorExitCode;
+          }
+        } else {
+          final assetsYamlFileUri =
+              await builder.compileNativeAssetsJitYamlFile();
+          if (assetsYamlFileUri == null) {
+            log.stderr('Error: Compiling native assets failed.');
+            return DartdevCommand.errorExitCode;
+          }
+          nativeAssets = assetsYamlFileUri.toFilePath();
+        }
       }
-      nativeAssets = assetsYamlFileUri.toFilePath();
     }
 
     try {
diff --git a/pkg/dartdev/lib/src/native_assets.dart b/pkg/dartdev/lib/src/native_assets.dart
index ad6c371..014e189 100644
--- a/pkg/dartdev/lib/src/native_assets.dart
+++ b/pkg/dartdev/lib/src/native_assets.dart
@@ -18,228 +18,304 @@
 
 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,
-  required String runPackageName,
-}) async {
-  final workingDirectory = Directory.current.uri;
-  var packageConfig = await findPackageConfigUri(workingDirectory);
-  // TODO(https://github.com/dart-lang/package_config/issues/126): Use
-  // package config resolution from package:package_config.
-  if (packageConfig == null) {
-    final pubspecMaybe = await _findPubspec(workingDirectory);
-    if (pubspecMaybe != null) {
-      // 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;
+class DartNativeAssetsBuilder {
+  final Uri packageConfigUri;
+  final String runPackageName;
+  final bool verbose;
+
+  static const _fileSystem = LocalFileSystem();
+
+  late final Future<PackageLayout> _packageLayout = () async {
+    return PackageLayout.fromPackageConfig(
+      _fileSystem,
+      await package_config.loadPackageConfigUri(packageConfigUri),
+      packageConfigUri,
+      runPackageName,
+    );
+  }();
+
+  late final _logger = 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);
       }
-      packageConfig = await findPackageConfigUri(workingDirectory);
-    } else {
-      return null;
-    }
-  }
-  final packageLayout = PackageLayout.fromPackageConfig(
-    LocalFileSystem(),
-    await package_config.loadPackageConfigUri(packageConfig!),
-    packageConfig,
-  );
-  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(
-    inputCreator: () => BuildInputBuilder()
-      ..config.setupCode(
-        targetOS: target.os,
-        targetArchitecture: target.architecture,
-        // When running in JIT mode, only dynamic libraries are supported.
-        linkModePreference: LinkModePreference.dynamic,
-        macOS: macOSConfig,
-        cCompiler: cCompilerConfig,
-      ),
-    inputValidator: (config) async => [
-      ...await validateCodeAssetBuildInput(config),
-      ...await validateDataAssetBuildInput(config),
-    ],
-    workingDirectory: workingDirectory,
-    runPackageName: runPackageName,
-    packageLayout: packageLayout,
-    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,
-  required 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.fromWorkingDirectory(
-      const LocalFileSystem(),
-      workingDirectory,
+  late final Future<NativeAssetsBuildRunner> _nativeAssetsBuildRunner =
+      () async {
+    return NativeAssetsBuildRunner(
+      // This always runs in JIT mode.
+      dartExecutable: Uri.file(sdk.dart),
+      logger: _logger,
+      fileSystem: const LocalFileSystem(),
+      packageLayout: await _packageLayout,
     );
-    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);
-    }
+  DartNativeAssetsBuilder({
+    required this.packageConfigUri,
+    required this.runPackageName,
+    required this.verbose,
   });
 
-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;
+  /// 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() async {
+    final buildResult = await _buildNativeAssetsShared(linkingEnabled: false);
+    if (buildResult == null) return null;
+    return buildResult.encodedAssets;
+  }
 
-  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),
+  /// 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() async {
+    final assets = await compileNativeAssetsJit();
+    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.
+''',
     );
   }
-  return null;
-}
 
-// TODO(https://github.com/dart-lang/package_config/issues/126): Expose this
-// logic in package:package_config.
-Future<Uri?> findPackageConfigUri(Uri uri) async {
-  while (true) {
-    final candidate = uri.resolve('.dart_tool/package_config.json');
-    final file = File.fromUri(candidate);
-    if (await file.exists()) {
-      return file.uri;
+  Future<bool> warnOnNativeAssets() async {
+    final workingDirectory = Directory.current.uri;
+
+    try {
+      final packageLayout = await PackageLayout.fromWorkingDirectory(
+        _fileSystem,
+        workingDirectory,
+        runPackageName,
+      );
+      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}',
+      );
     }
-    final parent = uri.resolve('..');
-    if (parent == uri) {
+    return true;
+  }
+
+  Future<BuildResult?> _buildNativeAssetsShared({
+    required bool linkingEnabled,
+  }) async {
+    final buildResult = await (await _nativeAssetsBuildRunner).build(
+      inputCreator: () => BuildInputBuilder()
+        ..config.setupCode(
+          targetOS: target.os,
+          linkModePreference: LinkModePreference.dynamic,
+          targetArchitecture: target.architecture,
+          macOS: _macOSConfig,
+          cCompiler: _cCompilerConfig,
+        ),
+      inputValidator: (config) async => [
+        ...await validateDataAssetBuildInput(config),
+        ...await validateCodeAssetBuildInput(config),
+      ],
+      linkingEnabled: linkingEnabled,
+      buildAssetTypes: [
+        CodeAsset.type,
+      ],
+      buildValidator: (config, output) async => [
+        ...await validateDataAssetBuildOutput(config, output),
+        ...await validateCodeAssetBuildOutput(config, output),
+      ],
+      applicationAssetValidator: (assets) async => [
+        ...await validateCodeAssetInApplication(assets),
+      ],
+    );
+    return buildResult;
+  }
+
+  Future<BuildResult?> buildNativeAssetsAOT() {
+    return _buildNativeAssetsShared(linkingEnabled: true);
+  }
+
+  Future<LinkResult?> linkNativeAssetsAOT({
+    required String? recordedUsagesPath,
+    required BuildResult buildResult,
+  }) async {
+    final linkResult = await (await _nativeAssetsBuildRunner).link(
+      inputCreator: () => LinkInputBuilder()
+        ..config.setupCode(
+          targetOS: target.os,
+          targetArchitecture: target.architecture,
+          linkModePreference: LinkModePreference.dynamic,
+          macOS: _macOSConfig,
+          cCompiler: _cCompilerConfig,
+        ),
+      inputValidator: (config) async => [
+        ...await validateDataAssetLinkInput(config),
+        ...await validateCodeAssetLinkInput(config),
+      ],
+      resourceIdentifiers:
+          recordedUsagesPath != null ? Uri.file(recordedUsagesPath) : null,
+      buildResult: buildResult,
+      buildAssetTypes: [
+        CodeAsset.type,
+      ],
+      linkValidator: (config, output) async => [
+        ...await validateDataAssetLinkOutput(config, output),
+        ...await validateCodeAssetLinkOutput(config, output),
+      ],
+      applicationAssetValidator: (assets) async => [
+        ...await validateCodeAssetInApplication(assets),
+      ],
+    );
+    return linkResult;
+  }
+
+  /// Dart does not do cross compilation. Target is always host.
+  late final target = Target.current;
+
+  late final _macOSConfig = target.os == OS.macOS
+      ? MacOSCodeConfig(targetVersion: minimumSupportedMacOSVersion)
+      : null;
+
+  late final _cCompilerConfig = _getCCompilerConfig(target.os);
+
+  CCompilerConfig? _getCCompilerConfig(OS targetOS) {
+    // 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();
+    if (cc != null && ar != null && ld != null) {
+      return CCompilerConfig(
+        archiver: Uri.file(ar),
+        compiler: Uri.file(cc),
+        linker: Uri.file(ld),
+        windows: targetOS == OS.windows
+            ? WindowsCCompilerConfig(
+                developerCommandPrompt: envScript == null
+                    ? null
+                    : DeveloperCommandPrompt(
+                        script: Uri.file(envScript),
+                        arguments: envScriptArgs ?? [],
+                      ),
+              )
+            : null,
+      );
+    }
+    return null;
+  }
+
+  /// Runs `pub get` if no package config can be found.
+  ///
+  /// Returns `null` if no package config can be found, even after pub get.
+  static Future<Uri?> ensurePackageConfig(Uri uri) async {
+    var packageConfig = await _findPackageConfigUri(uri);
+    // TODO(https://github.com/dart-lang/package_config/issues/126): Use
+    // package config resolution from package:package_config.
+    if (packageConfig == null) {
+      final pubspecMaybe = await _findPubspec(uri);
+      if (pubspecMaybe != null) {
+        // 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;
+        }
+        packageConfig = await _findPackageConfigUri(uri);
+      } else {
+        return null;
+      }
+    }
+    return packageConfig;
+  }
+
+  /// Finds the package config uri.
+  ///
+  /// Returns `null` if no package config can be found.
+  // TODO(https://github.com/dart-lang/package_config/issues/126): Expose this
+  // logic in package:package_config.
+  static Future<Uri?> _findPackageConfigUri(Uri uri) async {
+    while (true) {
+      final candidate = uri.resolve('.dart_tool/package_config.json');
+      final file = File.fromUri(candidate);
+      if (await file.exists()) {
+        return file.uri;
+      }
+      final parent = uri.resolve('..');
+      if (parent == uri) {
+        return null;
+      }
+      uri = parent;
+    }
+  }
+
+  static Future<Uri?> _findPubspec(Uri uri) async {
+    while (true) {
+      final candidate = uri.resolve('pubspec.yaml');
+      if (await File.fromUri(candidate).exists()) {
+        return candidate;
+      }
+      final parent = uri.resolve('..');
+      if (parent == uri) {
+        return null;
+      }
+      uri = parent;
+    }
+  }
+
+  /// Tries to find the package name that [uri] is in.
+  ///
+  /// Returns `null` if package cannnot be determined.
+  static Future<String?> findRootPackageName(Uri uri) async {
+    final pubspec = await _findPubspec(uri);
+    if (pubspec == null) {
       return null;
     }
-    uri = parent;
+    return pubspec.resolve('./').pathSegments.lastWhere((e) => e.isNotEmpty);
   }
 }
-
-Future<Uri?> _findPubspec(Uri uri) async {
-  while (true) {
-    final candidate = uri.resolve('pubspec.yaml');
-    if (await File.fromUri(candidate).exists()) {
-      return candidate;
-    }
-    final parent = uri.resolve('..');
-    if (parent == uri) {
-      return null;
-    }
-    uri = parent;
-  }
-}
-
-Future<String> findRootPackageName(Uri uri) async {
-  final pubspec = await _findPubspec(uri);
-  return pubspec!.resolve('./').pathSegments.lastWhere((e) => e.isNotEmpty);
-}
diff --git a/pkg/dartdev/lib/src/vm_interop_handler.dart b/pkg/dartdev/lib/src/vm_interop_handler.dart
index 79f705e..169c62a 100644
--- a/pkg/dartdev/lib/src/vm_interop_handler.dart
+++ b/pkg/dartdev/lib/src/vm_interop_handler.dart
@@ -54,7 +54,7 @@
     final port = _port;
     if (port == null) return;
     final message = <dynamic>[
-      useExecProcess? _kResultRunExec : _kResultRun,
+      useExecProcess ? _kResultRunExec : _kResultRun,
       script,
       packageConfigOverride,
       markMainIsolateAsSystemIsolate,