|
| 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:convert'; |
| 6 | +import 'dart:io'; |
| 7 | + |
| 8 | +/// Runs `dart run build_runner build` using `build` packages from pub instead |
| 9 | +/// of the local versions. This allows the build to run even when the local |
| 10 | +/// versions are in a broken state. |
| 11 | +/// |
| 12 | +/// Usage: in one of the `build` repo packages, instead of running |
| 13 | +/// `dart run build_runner build`, run `dart ../tool/build_runner_build.dart`. |
| 14 | +void main() { |
| 15 | + final pathSegments = Directory.current.uri.pathSegments; |
| 16 | + if (pathSegments[pathSegments.length - 3] != 'build') { |
| 17 | + print('Current directly should be a package inside the build repo.'); |
| 18 | + exit(1); |
| 19 | + } |
| 20 | + |
| 21 | + // Run `pub get` in a temp folder to get paths for published versions of |
| 22 | + // `build` packages that won't break due to local changes. |
| 23 | + final tempDirectory = Directory.systemTemp.createTempSync( |
| 24 | + 'build_runner_build', |
| 25 | + ); |
| 26 | + tempDirectory.createSync(recursive: true); |
| 27 | + File.fromUri(tempDirectory.uri.resolve('pubspec.yaml')).writeAsStringSync(''' |
| 28 | +name: none |
| 29 | +environment: |
| 30 | + sdk: ^3.7.0 |
| 31 | +dependencies: |
| 32 | + build_runner: '2.4.15' |
| 33 | + build_test: any |
| 34 | +'''); |
| 35 | + Process.runSync('dart', ['pub', 'get'], workingDirectory: tempDirectory.path); |
| 36 | + final pubConfig = PackageConfig( |
| 37 | + json.decode( |
| 38 | + File.fromUri( |
| 39 | + tempDirectory.uri.resolve('.dart_tool/package_config.json'), |
| 40 | + ).readAsStringSync(), |
| 41 | + ) |
| 42 | + as Map<String, Object?>, |
| 43 | + ); |
| 44 | + |
| 45 | + // Merge `pubConfig` into the local package config. |
| 46 | + // |
| 47 | + // `build_runner` expects to run with the local package config because it |
| 48 | + // creates a build script that depends on whatever generators the local |
| 49 | + // package uses. |
| 50 | + // |
| 51 | + // So the merged config will have the two things needed: `build` packages |
| 52 | + // not broken by local changes, and whatever generators are needed. |
| 53 | + var mergedConfig = PackageConfig( |
| 54 | + json.decode( |
| 55 | + File.fromUri( |
| 56 | + Directory.current.uri.resolve('../.dart_tool/package_config.json'), |
| 57 | + ).readAsStringSync(), |
| 58 | + ) |
| 59 | + as Map<String, Object?>, |
| 60 | + ); |
| 61 | + |
| 62 | + late String buildRunnerPath; |
| 63 | + for (final package in [ |
| 64 | + 'build', |
| 65 | + 'build_config', |
| 66 | + 'build_daemon', |
| 67 | + 'build_resolvers', |
| 68 | + 'build_runner', |
| 69 | + 'build_runner_core', |
| 70 | + 'build_test', |
| 71 | + ]) { |
| 72 | + final packageConfig = pubConfig.packageNamed(package); |
| 73 | + mergedConfig.packageNamed(package).rootUri = packageConfig.rootUri; |
| 74 | + if (package == 'build_runner') { |
| 75 | + buildRunnerPath = packageConfig.rootUri; |
| 76 | + } |
| 77 | + } |
| 78 | + final mergedConfigFile = File.fromUri( |
| 79 | + tempDirectory.uri.resolve('package_config.json'), |
| 80 | + ); |
| 81 | + mergedConfigFile.writeAsStringSync(json.encode(mergedConfig)); |
| 82 | + |
| 83 | + final buildResult = Process.runSync('dart', [ |
| 84 | + '--packages=${mergedConfigFile.path}', |
| 85 | + 'run', |
| 86 | + '$buildRunnerPath/bin/build_runner.dart', |
| 87 | + 'build', |
| 88 | + '-d', |
| 89 | + ], workingDirectory: Directory.current.path); |
| 90 | + |
| 91 | + stdout.write(buildResult.stdout); |
| 92 | + stderr.write(buildResult.stderr); |
| 93 | + |
| 94 | + if (buildResult.exitCode == 0) { |
| 95 | + tempDirectory.deleteSync(recursive: true); |
| 96 | + } |
| 97 | + exit(buildResult.exitCode); |
| 98 | +} |
| 99 | + |
| 100 | +extension type PackageConfig(Map<String, Object?> node) { |
| 101 | + List<Package> get packages => |
| 102 | + (node['packages'] as List<Object?>) |
| 103 | + .map((p) => Package(p as Map<String, Object?>)) |
| 104 | + .toList(); |
| 105 | + Package packageNamed(String name) => |
| 106 | + packages.singleWhere((package) => package.name == name); |
| 107 | +} |
| 108 | + |
| 109 | +extension type Package(Map<String, Object?> node) { |
| 110 | + String get name => node['name'] as String; |
| 111 | + String get rootUri => node['rootUri'] as String; |
| 112 | + set rootUri(String rootUri) => node['rootUri'] = rootUri; |
| 113 | +} |
0 commit comments