Index: tools/patch_sdk.dart |
diff --git a/tools/patch_sdk.dart b/tools/patch_sdk.dart |
index 33a06fb7c7e61d566e7825388e9759472ed8a863..3e587840e616ba0cb1631817cb8c1ed7f238cc38 100644 |
--- a/tools/patch_sdk.dart |
+++ b/tools/patch_sdk.dart |
@@ -13,8 +13,41 @@ import 'dart:math' as math; |
import 'package:analyzer/analyzer.dart'; |
import 'package:analyzer/src/generated/sdk.dart'; |
import 'package:path/path.dart' as path; |
-import 'package:front_end/src/fasta/compile_platform.dart' as |
- compile_platform; |
+import 'package:front_end/src/fasta/compile_platform.dart' as compile_platform; |
+ |
+import 'package:front_end/src/fasta/outline.dart' show CompileTask; |
+ |
+import 'package:front_end/src/fasta/compiler_command_line.dart' |
+ show CompilerCommandLine; |
+ |
+import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext; |
+ |
+import 'package:front_end/src/fasta/ticker.dart' show Ticker; |
+ |
+/// Set of input files that were read by this script to generate patched SDK. |
+/// We will dump it out into the depfile for ninja to use. |
+/// |
+/// For more information see GN and Ninja references: |
+/// https://chromium.googlesource.com/chromium/src/+/56807c6cb383140af0c03da8f6731d77785d7160/tools/gn/docs/reference.md#depfile_string_File-name-for-input-dependencies-for-actions |
+/// https://ninja-build.org/manual.html#_depfile |
+/// |
+final deps = new Set<String>(); |
+ |
+/// Create [File] object from the given path and register it as a dependency. |
+File getInputFile(String path, {canBeMissing: false}) { |
+ final file = new File(path); |
+ if (!file.existsSync()) { |
+ if (!canBeMissing) throw "patch_sdk.dart expects all inputs to exist"; |
+ return null; |
+ } |
+ deps.add(file.absolute.path); |
+ return file; |
+} |
+ |
+/// Read the given file synchronously as a string and register this path as |
+/// a dependency. |
+String readInputFile(String path, {canBeMissing: false}) => |
+ getInputFile(path, canBeMissing: canBeMissing)?.readAsStringSync(); |
Future main(List<String> argv) async { |
var base = path.fromUri(Platform.script); |
@@ -28,8 +61,8 @@ Future main(List<String> argv) async { |
final sdkExample = path.relative(path.join(repositoryDir, 'sdk')); |
final patchExample = path.relative( |
path.join(repositoryDir, 'out', 'DebugX64', 'obj', 'gen', 'patch')); |
- final outExample = path.relative(path.join(repositoryDir, 'out', 'DebugX64', |
- 'obj', 'gen', 'patched_sdk')); |
+ final outExample = path.relative(path.join( |
+ repositoryDir, 'out', 'DebugX64', 'obj', 'gen', 'patched_sdk')); |
print('For example:'); |
print('\$ $self vm $sdkExample $patchExample $outExample'); |
@@ -37,6 +70,7 @@ Future main(List<String> argv) async { |
} |
var mode = argv[0]; |
+ assert(mode == "vm"); |
var input = argv[1]; |
var sdkLibIn = path.join(input, 'lib'); |
var patchIn = argv[2]; |
@@ -48,12 +82,11 @@ Future main(List<String> argv) async { |
var INTERNAL_PATH = '_internal/compiler/js_lib/'; |
// Copy and patch libraries.dart and version |
- var libContents = new File(path.join(sdkLibIn, '_internal', |
- 'sdk_library_metadata', 'lib', 'libraries.dart')).readAsStringSync(); |
- if (mode == 'vm') { |
- libContents = libContents.replaceAll( |
- ' libraries = const {', |
- ''' libraries = const { |
+ var libContents = readInputFile(path.join( |
+ sdkLibIn, '_internal', 'sdk_library_metadata', 'lib', 'libraries.dart')); |
+ libContents = libContents.replaceAll( |
+ ' libraries = const {', |
+ ''' libraries = const { |
"_builtin": const LibraryInfo( |
"_builtin/_builtin.dart", |
@@ -80,39 +113,25 @@ Future main(List<String> argv) async { |
platforms: VM_PLATFORM), |
'''); |
- } |
_writeSync( |
path.join( |
sdkOut, '_internal', 'sdk_library_metadata', 'lib', 'libraries.dart'), |
libContents); |
- if (mode == 'ddc') { |
- _writeSync(path.join(sdkOut, '..', 'version'), |
- new File(path.join(sdkLibIn, '..', 'version')).readAsStringSync()); |
- } |
// Parse libraries.dart |
var sdkLibraries = _getSdkLibraries(libContents); |
// Enumerate core libraries and apply patches |
for (SdkLibrary library in sdkLibraries) { |
- // TODO(jmesserly): analyzer does not handle the default case of |
- // "both platforms" correctly, and treats it as being supported on neither. |
- // So instead we skip explicitly marked as either VM or dart2js libs. |
- if (mode == 'ddc' ? library.isVmLibrary : library.isDart2JsLibrary) { |
+ if (library.isDart2JsLibrary) { |
continue; |
} |
var libraryOut = path.join(sdkLibIn, library.path); |
- var libraryIn; |
- if (mode == 'ddc' && library.path.contains(INTERNAL_PATH)) { |
- libraryIn = |
- path.join(privateIn, library.path.replaceAll(INTERNAL_PATH, '')); |
- } else { |
- libraryIn = libraryOut; |
- } |
+ var libraryIn = libraryOut; |
- var libraryFile = new File(libraryIn); |
- if (libraryFile.existsSync()) { |
+ var libraryFile = getInputFile(libraryIn, canBeMissing: true); |
+ if (libraryFile != null) { |
var outPaths = <String>[libraryOut]; |
var libraryContents = libraryFile.readAsStringSync(); |
@@ -124,7 +143,8 @@ Future main(List<String> argv) async { |
var partPath = part.uri.stringValue; |
outPaths.add(path.join(path.dirname(libraryOut), partPath)); |
- var partFile = new File(path.join(path.dirname(libraryIn), partPath)); |
+ var partFile = |
+ getInputFile(path.join(path.dirname(libraryIn), partPath)); |
partFiles.add(partFile); |
inputModifyTime = math.max(inputModifyTime, |
partFile.lastModifiedSync().millisecondsSinceEpoch); |
@@ -135,9 +155,8 @@ Future main(List<String> argv) async { |
var patchPath = path.join( |
patchIn, path.basenameWithoutExtension(libraryIn) + '_patch.dart'); |
- var patchFile = new File(patchPath); |
- bool patchExists = patchFile.existsSync(); |
- if (patchExists) { |
+ var patchFile = getInputFile(patchPath, canBeMissing: true); |
+ if (patchFile != null) { |
inputModifyTime = math.max(inputModifyTime, |
patchFile.lastModifiedSync().millisecondsSinceEpoch); |
} |
@@ -162,10 +181,9 @@ Future main(List<String> argv) async { |
if (needsUpdate) { |
var contents = <String>[libraryContents]; |
contents.addAll(partFiles.map((f) => f.readAsStringSync())); |
- if (patchExists) { |
+ if (patchFile != null) { |
var patchContents = patchFile.readAsStringSync(); |
- contents = _patchLibrary( |
- patchFile.path, contents, patchContents); |
+ contents = _patchLibrary(patchFile.path, contents, patchContents); |
} |
for (var i = 0; i < outPaths.length; i++) { |
@@ -175,22 +193,22 @@ Future main(List<String> argv) async { |
} |
} |
- if (mode == 'vm') { |
- for (var tuple in [['_builtin', 'builtin.dart']]) { |
- var vmLibrary = tuple[0]; |
- var dartFile = tuple[1]; |
+ for (var tuple in [ |
+ ['_builtin', 'builtin.dart'] |
+ ]) { |
+ var vmLibrary = tuple[0]; |
+ var dartFile = tuple[1]; |
- // The "dart:_builtin" library is only available for the DartVM. |
- var builtinLibraryIn = path.join(dartDir, 'runtime', 'bin', dartFile); |
- var builtinLibraryOut = path.join(sdkOut, vmLibrary, '${vmLibrary}.dart'); |
- _writeSync(builtinLibraryOut, new File(builtinLibraryIn).readAsStringSync()); |
- } |
+ // The "dart:_builtin" library is only available for the DartVM. |
+ var builtinLibraryIn = path.join(dartDir, 'runtime', 'bin', dartFile); |
+ var builtinLibraryOut = path.join(sdkOut, vmLibrary, '${vmLibrary}.dart'); |
+ _writeSync(builtinLibraryOut, readInputFile(builtinLibraryIn)); |
+ } |
- for (var file in ['loader.dart', 'server.dart', 'vmservice_io.dart']) { |
- var libraryIn = path.join(dartDir, 'runtime', 'bin', 'vmservice', file); |
- var libraryOut = path.join(sdkOut, 'vmservice_io', file); |
- _writeSync(libraryOut, new File(libraryIn).readAsStringSync()); |
- } |
+ for (var file in ['loader.dart', 'server.dart', 'vmservice_io.dart']) { |
+ var libraryIn = path.join(dartDir, 'runtime', 'bin', 'vmservice', file); |
+ var libraryOut = path.join(sdkOut, 'vmservice_io', file); |
+ _writeSync(libraryOut, readInputFile(libraryIn)); |
} |
// TODO(kustermann): We suppress compiler hints/warnings/errors temporarily |
@@ -199,13 +217,47 @@ Future main(List<String> argv) async { |
// been fixed (either in fasta or the dart files in the patched_sdk). |
final capturedLines = <String>[]; |
try { |
+ final platform = path.join(outDir, 'platform.dill'); |
+ |
await runZoned(() async { |
await compile_platform.mainEntryPoint(<String>[ |
'--packages', |
new Uri.file(packagesFile).toString(), |
new Uri.directory(outDir).toString(), |
- path.join(outDir, 'platform.dill') |
+ platform, |
]); |
+ |
+ // platform.dill was generated, now generate platform.dill.d depfile |
+ // that captures all dependencies that participated in the generation. |
+ // There are two types of dependencies: |
+ // (1) all Dart sources that constitute this tool itself |
+ // (2) Dart SDK and patch sources. |
+ // We already collected all inputs from the second category in the deps |
+ // set. To collect inputs from the first category we actually use Fasta: |
+ // we ask Fasta to outline patch_sdk.dart and generate a depfile which |
+ // would list all the sources. |
+ final depfile = "${outDir}.d"; |
+ await CompilerCommandLine.withGlobalOptions("outline", [ |
+ '--packages', |
+ new Uri.file(packagesFile).toString(), |
+ '--platform', |
+ platform, // platform.dill that was just generated |
+ Platform.script.toString() // patch_sdk.dart |
+ ], (CompilerContext c) async { |
+ CompileTask task = |
+ new CompileTask(c, new Ticker(isVerbose: c.options.verbose)); |
+ final kernelTarget = await task.buildOutline(null); |
+ await kernelTarget.writeDepsFile( |
+ new Uri.file(platform), new Uri.file(depfile)); |
+ }); |
+ |
+ // Read depfile generated by Fasta and append deps that we have collected |
+ // during generation of patched_sdk to it. |
+ final list = new File(depfile).readAsStringSync().split(':'); |
+ assert(list.length == 2); |
+ deps.addAll(list[1].split(' ').where((str) => str.isNotEmpty)); |
+ assert(list[0] == 'patched_sdk/platform.dill'); |
+ new File(depfile).writeAsStringSync("${list[0]}: ${deps.join(' ')}\n"); |
}, zoneSpecification: new ZoneSpecification(print: (_, _2, _3, line) { |
capturedLines.add(line); |
})); |
@@ -244,9 +296,8 @@ void _writeSync(String filePath, String contents) { |
/// in the Dart language. Since this feature is only for the convenience of |
/// writing the dart:* libraries, and not a tool given to Dart developers, it |
/// seems like a non-ideal situation. Instead we keep the preprocessing simple. |
-List<String> _patchLibrary(String name, |
- List<String> partsContents, |
- String patchContents) { |
+List<String> _patchLibrary( |
+ String name, List<String> partsContents, String patchContents) { |
var results = <StringEditBuffer>[]; |
// Parse the patch first. We'll need to extract bits of this as we go through |
@@ -275,8 +326,13 @@ List<String> _patchLibrary(String name, |
} |
final String injectedCidFields = [ |
- 'Array', 'ExternalOneByteString', 'GrowableObjectArray', |
- 'ImmutableArray', 'OneByteString', 'TwoByteString', 'Bigint' |
+ 'Array', |
+ 'ExternalOneByteString', |
+ 'GrowableObjectArray', |
+ 'ImmutableArray', |
+ 'OneByteString', |
+ 'TwoByteString', |
+ 'Bigint' |
].map((name) => "static final int cid${name} = 0;").join('\n'); |
/// Merge `@patch` declarations into `external` declarations. |
@@ -302,8 +358,7 @@ class PatchApplier extends GeneralizingAstVisitor { |
// make core libraries compile. Kernel reader will actually ignore these |
// fields and instead inject concrete constants into this class. |
if (node is ClassDeclaration && node.name.name == 'ClassID') { |
- code = code.replaceFirst( |
- new RegExp(r'}$'), injectedCidFields + '}'); |
+ code = code.replaceFirst(new RegExp(r'}$'), injectedCidFields + '}'); |
} |
edits.insert(pos, '\n' + code); |
} |
@@ -449,7 +504,8 @@ String _qualifiedName(Declaration node) { |
var accessor = ''; |
if (node is MethodDeclaration) { |
- if (node.isGetter) accessor = 'get:'; |
+ if (node.isGetter) |
+ accessor = 'get:'; |
else if (node.isSetter) accessor = 'set:'; |
} |
return className + accessor + name; |