2.2 Dart compile Dart 部分实现

由《1.1 Dart 命令——Dart SDK 的入口》的架构组成可知,dart 命令由 C++ 和 Dart 两部分组成。Dart 部分主要处理命令行参数解析转换,之后以创建新进程的方式,调用 dart 命令的 C++ 实现部分。在本文中主要分析 Dart 侧的实现。

dart compile 的实现位于 pkg/dartdev/lib/src/commands/compile.dart。核心的类是 CompileCommand,内部根据不同的编译类型,细分为多个子指令。


如何调试 Dart 命令

既然 dart 命令是使用 Dart 开发的,可以很方便地进行调试,使用 dart run dartdev 的入口即可:

[maxiee@archlinux maxiee_learn]$ /home/maxiee/Code/dart-sdk/sdk/out/ReleaseX64/dart-sdk/bin/dart run /home/maxiee/Code/dart-sdk/sdk/pkg/dartdev/bin/dartdev.dart compile jit-snapshot main.dart 

需要注意的是,我们应当使用与 Dart SDK 源码匹配的 Dart 版本来运行。或者向我上面命令这样,我先从源码编译了 Dart SDK,然后用编译出来的 dart 来运行它自己的源码。


CompileSnapshotCommand

JIT snapshot、AOT snapshot、Kernal 编译均由 CompileSnapshotCommand 命令负责。run 方法为功能实现:

@override
FutureOr<int> run() async {
  final args = argResults!;
  // ...

  // 源码路径
  final String sourcePath = args.rest[0];
  // ...

  // Determine output file name.
  // 如果用户指定输出文件名,则直接使用
  // 如果未指定,以源码文件名加对应模式下的后缀自动生成
  // 后缀:kernel-dill, JIT-jit, AOT-aot
  String? outputFile = args[outputFileOption.flag];
  if (outputFile == null) {
    final inputWithoutDart = sourcePath.endsWith('.dart')
        ? sourcePath.substring(0, sourcePath.length - 5)
        : sourcePath;
    outputFile = '$inputWithoutDart.$fileExt';
  }

  // 启用的实验功能
  // 获取 define 环境变量
  final enabledExperiments = args.enabledExperiments;
  final environmentVars = args['define'] ?? <String, String>{};

  // Build arguments.
  // 生成构建参数
  final buildArgs = <String>[];
  // 编译类型
  buildArgs.add('--snapshot-kind=$formatName'); 
  // 输出文件
  buildArgs.add('--snapshot=${path.canonicalize(outputFile)}'); 

  // 是否开启健全空安全
  final bool soundNullSafety = args['sound-null-safety'];
  if (!soundNullSafety) {
    if (!shouldAllowNoSoundNullSafety()) {
      return compileErrorExitCode;
    }
    // 非健全空安全
    buildArgs.add('--no-sound-null-safety');
  }

  // packagesOption
  final String? packages = args[packagesOption.flag];
  if (packages != null) {
    buildArgs.add('--packages=$packages');
  }

  // 日志等级
  final String? verbosity = args[verbosityOption.flag];
  buildArgs.add('--verbosity=$verbosity');

  if (enabledExperiments.isNotEmpty) {
    buildArgs.add("--enable-experiment=${enabledExperiments.join(',')}");
  }
  if (verbose) { // 唠叨
    buildArgs.add('-v');
  }
  if (environmentVars.isNotEmpty) {
    buildArgs.addAll(environmentVars.map<String>((e) => '--define=$e'));
  }
  buildArgs.add(path.canonicalize(sourcePath));

  // Add the training arguments.
  if (args.rest.length > 1) {
    buildArgs.addAll(args.rest.sublist(1));
  }

  log.stdout('Compiling $sourcePath to $commandName file $outputFile.');
  // TODO(bkonyi): perform compilation in same process.
  // 启动 dart 进程
  final process = await startDartProcess(sdk, buildArgs);
  routeToStdout(process);
  return process.exitCode;
}

dartdev 中的 dart 并不负责具体编译,具体编译由 startDartProcess 调用实际的编译命令进行:

/// A utility method to start a Dart VM instance with the given arguments and an
/// optional current working directory.
///
/// [arguments] should contain the snapshot path.
Future<Process> startDartProcess(
  Sdk sdk,
  List<String> arguments, {
  String? cwd,
}) {
  log.trace('${sdk.dart} ${arguments.join(' ')}');
  print('${sdk.dart} ${arguments.join(' ')}');
  print('CWD: $cwd');
  return Process.start(sdk.dart, arguments, workingDirectory: cwd);
}

我加了两行日志,输出:

/home/maxiee/Code/dart-sdk/sdk/out/ReleaseX64/dart-sdk/bin/dart --snapshot-kind=app-jit --snapshot=/home/maxiee/Code/dart-sdk/sdk/maxiee_learn/main.jit --verbosity=all /home/maxiee/Code/dart-sdk/sdk/maxiee_learn/main.dart
CWD: null

Process.start 是 Dart 语言的启动进程方法,可见在新进程中,运行的还是同一个 dart 命令,但这次传入了不同的参数。

接下来会来到 Dart C++ 部分的实现。


本文作者:Maeiee

本文链接:2.2 Dart compile Dart 部分实现

版权声明:如无特别声明,本文即为原创文章,版权归 Maeiee 所有,未经允许不得转载!


喜欢我文章的朋友请随缘打赏,鼓励我创作更多更好的作品!