|
| 1 | +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| 2 | +// for details. All rights reserved. Use of this source code is governed by a |
| 3 | +// BSD-style license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +import 'dart:io'; |
| 6 | + |
| 7 | +import 'package:build_runner_core/build_runner_core.dart'; |
| 8 | +import 'package:io/io.dart'; |
| 9 | +import 'package:package_config/package_config.dart'; |
| 10 | + |
| 11 | +import '../build_script_generate/build_script_generate.dart'; |
| 12 | +import 'compiler.dart'; |
| 13 | +import 'depfiles.dart'; |
| 14 | +import 'runner.dart'; |
| 15 | + |
| 16 | +// DO NOT SUBMIT |
| 17 | +// ignore_for_file: deprecated_member_use |
| 18 | +// ignore_for_file: only_throw_errors |
| 19 | + |
| 20 | +class Bootstrapper { |
| 21 | + final Depfile buildRunnerDepfile = Depfile( |
| 22 | + depfilePath: '.dart_tool/build/entrypoint/build_runner.deps', |
| 23 | + digestPath: '.dart_tool/build/entrypoint/build_runner.digest', |
| 24 | + output: null, |
| 25 | + inputs: [], |
| 26 | + ); |
| 27 | + final Depfile buildSourceDepfile = Depfile( |
| 28 | + depfilePath: '.dart_tool/build/entrypoint/build.dart.deps', |
| 29 | + digestPath: '.dart_tool/build/entrypoint/build.dart.digest', |
| 30 | + output: '.dart_tool/build/entrypoint/build.dart', |
| 31 | + inputs: [], |
| 32 | + ); |
| 33 | + final Depfile dillDepfile = Depfile( |
| 34 | + depfilePath: '.dart_tool/build/entrypoint/build.dart.dill.deps', |
| 35 | + digestPath: '.dart_tool/build/entrypoint/build.dart.dill.digest', |
| 36 | + output: '.dart_tool/build/entrypoint/build.dart.dill', |
| 37 | + inputs: [], |
| 38 | + ); |
| 39 | + |
| 40 | + PackageConfig? config; |
| 41 | + bool? buildRunnerHasChanged; |
| 42 | + bool? buildYamlHasChanged; |
| 43 | + bool? buildDillHasChanged; |
| 44 | + |
| 45 | + bool runningFromBuildScript() { |
| 46 | + return StackTrace.current.toString().contains( |
| 47 | + '.dart_tool/build/entrypoint/build.dart', |
| 48 | + ); |
| 49 | + } |
| 50 | + |
| 51 | + Future<bool> needsRebuild() async { |
| 52 | + if (!runningFromBuildScript()) return false; |
| 53 | + buildLog.debug(Platform.script.toString()); |
| 54 | + // TODO(davidmorgan): fix for workspace, error handling. |
| 55 | + config ??= (await findPackageConfig(Directory.current, recurse: true))!; |
| 56 | + |
| 57 | + // TODO(davidmorgan): is this the right thing to check? |
| 58 | + buildLog.debug('needsRebuild?'); |
| 59 | + final r1 = buildRunnerDepfile.outputIsUpToDate(); |
| 60 | + final r2 = buildSourceDepfile.outputIsUpToDate(); |
| 61 | + final r3 = dillDepfile.outputIsUpToDate(); |
| 62 | + final result = !r1 || !r2 || !r3; |
| 63 | + buildLog.debug('needsRebuild? $r1 $r2 $r3 --> $result'); |
| 64 | + return result; |
| 65 | + } |
| 66 | + |
| 67 | + Future<int> run(List<String> args, {List<String>? experiments}) async { |
| 68 | + buildRunnerDepfile.output = Platform.script.path; |
| 69 | + while (true) { |
| 70 | + // TODO(davidmorgan): fix for workspace, error handling. |
| 71 | + config = (await findPackageConfig(Directory.current, recurse: true))!; |
| 72 | + |
| 73 | + _checkBuildRunner(); |
| 74 | + await _checkBuildSource(force: buildRunnerHasChanged!); |
| 75 | + await _checkBuildDill(); |
| 76 | + |
| 77 | + final exitCode = await Runner().run(args); |
| 78 | + if (exitCode != ExitCode.tempFail.code) { |
| 79 | + return exitCode; |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + void _checkBuildRunner() { |
| 85 | + buildLog.debug('Check build_runner.'); |
| 86 | + final package = config!['build_runner']!.packageUriRoot; |
| 87 | + final script = package.resolve('../bin/build_runner.dart'); |
| 88 | + if (buildRunnerDepfile.outputIsUpToDate()) { |
| 89 | + buildLog.debug('build runner is up to date'); |
| 90 | + buildRunnerHasChanged = false; |
| 91 | + } else { |
| 92 | + buildLog.debug('build runner update'); |
| 93 | + buildRunnerHasChanged = true; |
| 94 | + buildRunnerDepfile.clear(); |
| 95 | + buildRunnerDepfile.addScriptDeps( |
| 96 | + scriptPath: script.path, |
| 97 | + packageConfig: config!, |
| 98 | + ); |
| 99 | + buildRunnerDepfile.write(); |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + Future<void> _checkBuildSource({required bool force}) async { |
| 104 | + if (!force && buildSourceDepfile.outputIsUpToDate()) { |
| 105 | + buildLog.debug('build script up to date'); |
| 106 | + buildYamlHasChanged = false; |
| 107 | + } else { |
| 108 | + buildLog.debug('build script update (force: $force)'); |
| 109 | + buildYamlHasChanged = true; |
| 110 | + final buildScript = await generateBuildScript(); |
| 111 | + File(buildSourceDepfile.output!).writeAsStringSync(buildScript.content); |
| 112 | + buildSourceDepfile.clear(); |
| 113 | + buildSourceDepfile.addDeps(buildScript.inputs); |
| 114 | + buildSourceDepfile.addScriptDeps( |
| 115 | + scriptPath: buildSourceDepfile.output!, |
| 116 | + packageConfig: config!, |
| 117 | + ); |
| 118 | + buildSourceDepfile.write(); |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + Future<void> _checkBuildDill() async { |
| 123 | + final compiler = Compiler(); |
| 124 | + if (dillDepfile.outputIsUpToDate()) { |
| 125 | + buildLog.debug('dill up to date'); |
| 126 | + buildDillHasChanged = false; |
| 127 | + } else { |
| 128 | + buildLog.debug('dill update'); |
| 129 | + buildDillHasChanged = true; |
| 130 | + |
| 131 | + final result = await compiler.compile(); |
| 132 | + if (!result.succeeded) throw 'failed'; |
| 133 | + // TODO(davidmorgan): this is weird. |
| 134 | + dillDepfile.loadDeps(); |
| 135 | + dillDepfile.write(); |
| 136 | + } |
| 137 | + } |
| 138 | +} |
0 commit comments