OLD | NEW |
1 #!/usr/bin/env dart | 1 #!/usr/bin/env dart |
2 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 2 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
5 | 5 |
6 /// Command line tool to merge the SDK libraries and our patch files. | 6 /// Command line tool to merge the SDK libraries and our patch files. |
7 /// This is currently designed as an offline tool, but we could automate it. | 7 /// This is currently designed as an offline tool, but we could automate it. |
8 | 8 |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 import 'dart:math' as math; | 10 import 'dart:math' as math; |
11 | 11 |
12 import 'package:analyzer/analyzer.dart'; | 12 import 'package:analyzer/analyzer.dart'; |
13 import 'package:analyzer/src/generated/sdk.dart'; | 13 import 'package:analyzer/src/generated/sdk.dart'; |
14 import 'package:path/path.dart' as path; | 14 import 'package:path/path.dart' as path; |
15 | 15 |
16 void main(List<String> argv) { | 16 void main(List<String> argv) { |
17 var self = path.relative(path.fromUri(Platform.script)); | 17 var self = path.relative(path.fromUri(Platform.script)); |
18 if (argv.length < 2) { | 18 if (argv.length < 3) { |
19 var toolDir = path.relative(path.dirname(path.fromUri(Platform.script))); | 19 var toolDir = path.relative(path.dirname(path.fromUri(Platform.script))); |
20 | 20 |
21 var inputExample = path.join(toolDir, 'input_sdk'); | 21 var inputExample = path.join(toolDir, '..', '..', '..'); |
| 22 var patchExample = path.join(toolDir, 'input_sdk'); |
22 var outExample = | 23 var outExample = |
23 path.relative(path.normalize(path.join('gen', 'patched_sdk'))); | 24 path.relative(path.normalize(path.join('gen', 'patched_sdk'))); |
24 | 25 |
25 print('Usage: $self INPUT_DIR OUTPUT_DIR'); | 26 print('Usage: $self INPUT_SDK_DIR PATCH_DIR OUTPUT_DIR'); |
26 print('For example:'); | 27 print('For example:'); |
27 print('\$ $self $inputExample $outExample'); | 28 print('\$ $self $inputExample $patchExample $outExample'); |
28 exit(1); | 29 exit(1); |
29 } | 30 } |
30 | 31 |
31 var selfModifyTime = new File(self).lastModifiedSync().millisecondsSinceEpoch; | 32 var selfModifyTime = new File(self).lastModifiedSync().millisecondsSinceEpoch; |
32 | 33 |
33 var input = argv[0]; | 34 var inputDir = argv[0]; |
34 var sdkLibIn = path.join(input, 'lib'); | 35 var patchDir = argv[1]; |
35 var patchIn = path.join(input, 'patch'); | 36 var sdkLibIn = path.join(inputDir, 'sdk', 'lib'); |
36 var privateIn = path.join(input, 'private'); | 37 var patchIn = path.join(patchDir, 'patch'); |
37 var sdkOut = path.join(argv[1], 'lib'); | 38 var privateIn = path.join(patchDir, 'private'); |
| 39 var sdkOut = path.join(argv[2], 'lib'); |
38 | 40 |
39 var INTERNAL_PATH = '_internal/compiler/js_lib/'; | 41 var INTERNAL_PATH = '_internal/js_runtime/lib/'; |
40 | 42 |
41 // Copy libraries.dart and version | 43 // Copy libraries.dart and version |
42 var libContents = new File(path.join(sdkLibIn, '_internal', 'libraries.dart')) | 44 var librariesDart = path.join(patchDir, 'libraries.dart'); |
43 .readAsStringSync(); | 45 var libContents = new File(librariesDart).readAsStringSync(); |
| 46 // TODO(jmesserly): can we remove this? |
44 _writeSync(path.join(sdkOut, '_internal', 'libraries.dart'), libContents); | 47 _writeSync(path.join(sdkOut, '_internal', 'libraries.dart'), libContents); |
45 _writeSync( | 48 _writeSync( |
46 path.join( | 49 path.join( |
47 sdkOut, '_internal', 'sdk_library_metadata', 'lib', 'libraries.dart'), | 50 sdkOut, '_internal', 'sdk_library_metadata', 'lib', 'libraries.dart'), |
48 libContents); | 51 libContents); |
49 _writeSync(path.join(sdkOut, '..', 'version'), | 52 _writeSync(path.join(sdkOut, '..', 'version'), |
50 new File(path.join(sdkLibIn, '..', 'version')).readAsStringSync()); | 53 new File(path.join(inputDir, 'tools', 'VERSION')).readAsStringSync()); |
51 | 54 |
52 // Parse libraries.dart | 55 // Parse libraries.dart |
53 var sdkLibraries = _getSdkLibraries(libContents); | 56 var sdkLibraries = _getSdkLibraries(libContents); |
54 | 57 |
55 // Enumerate core libraries and apply patches | 58 // Enumerate core libraries and apply patches |
56 for (SdkLibrary library in sdkLibraries) { | 59 for (SdkLibrary library in sdkLibraries) { |
57 // TODO(jmesserly): analyzer does not handle the default case of | 60 // TODO(jmesserly): analyzer does not handle the default case of |
58 // "both platforms" correctly, and treats it as being supported on neither. | 61 // "both platforms" correctly, and treats it as being supported on neither. |
59 // So instead we skip explicitly marked as VM libs. | 62 // So instead we skip explicitly marked as VM libs. |
60 if (library.isVmLibrary) continue; | 63 if (library.isVmLibrary) continue; |
61 | 64 |
62 var libraryOut = path.join(sdkLibIn, library.path); | 65 var libraryOut = path.join(sdkLibIn, library.path); |
| 66 var libraryOverride = path.join(patchDir, 'lib', library.path); |
63 var libraryIn; | 67 var libraryIn; |
64 if (library.path.contains(INTERNAL_PATH)) { | 68 if (library.path.contains(INTERNAL_PATH)) { |
65 libraryIn = | 69 libraryIn = |
66 path.join(privateIn, library.path.replaceAll(INTERNAL_PATH, '')); | 70 path.join(privateIn, library.path.replaceAll(INTERNAL_PATH, '')); |
| 71 } else if (new File(libraryOverride).existsSync()) { |
| 72 libraryIn = libraryOverride; |
67 } else { | 73 } else { |
68 libraryIn = libraryOut; | 74 libraryIn = libraryOut; |
69 } | 75 } |
70 | 76 |
71 var libraryFile = new File(libraryIn); | 77 var libraryFile = new File(libraryIn); |
72 if (libraryFile.existsSync()) { | 78 if (libraryFile.existsSync()) { |
73 var outPaths = <String>[libraryOut]; | 79 var outPaths = <String>[libraryOut]; |
74 var libraryContents = libraryFile.readAsStringSync(); | 80 var libraryContents = libraryFile.readAsStringSync(); |
75 | 81 |
76 int inputModifyTime = math.max(selfModifyTime, | 82 int inputModifyTime = math.max(selfModifyTime, |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 } | 123 } |
118 | 124 |
119 if (needsUpdate) { | 125 if (needsUpdate) { |
120 var contents = <String>[libraryContents]; | 126 var contents = <String>[libraryContents]; |
121 contents.addAll(partFiles.map((f) => f.readAsStringSync())); | 127 contents.addAll(partFiles.map((f) => f.readAsStringSync())); |
122 if (patchExists) { | 128 if (patchExists) { |
123 var patchContents = patchFile.readAsStringSync(); | 129 var patchContents = patchFile.readAsStringSync(); |
124 contents = _patchLibrary(contents, patchContents); | 130 contents = _patchLibrary(contents, patchContents); |
125 } | 131 } |
126 | 132 |
127 for (var i = 0; i < outPaths.length; i++) { | 133 if (contents != null) { |
128 _writeSync(outPaths[i], contents[i]); | 134 for (var i = 0; i < outPaths.length; i++) { |
| 135 _writeSync(outPaths[i], contents[i]); |
| 136 } |
| 137 } else { |
| 138 exitCode = 2; |
129 } | 139 } |
130 } | 140 } |
131 } | 141 } |
132 } | 142 } |
133 } | 143 } |
134 | 144 |
135 /// Writes a file, creating the directory if needed. | 145 /// Writes a file, creating the directory if needed. |
136 void _writeSync(String filePath, String contents) { | 146 void _writeSync(String filePath, String contents) { |
137 var outDir = new Directory(path.dirname(filePath)); | 147 var outDir = new Directory(path.dirname(filePath)); |
138 if (!outDir.existsSync()) outDir.createSync(recursive: true); | 148 if (!outDir.existsSync()) outDir.createSync(recursive: true); |
(...skipping 21 matching lines...) Expand all Loading... |
160 /// writing the dart:* libraries, and not a tool given to Dart developers, it | 170 /// writing the dart:* libraries, and not a tool given to Dart developers, it |
161 /// seems like a non-ideal situation. Instead we keep the preprocessing simple. | 171 /// seems like a non-ideal situation. Instead we keep the preprocessing simple. |
162 List<String> _patchLibrary(List<String> partsContents, String patchContents) { | 172 List<String> _patchLibrary(List<String> partsContents, String patchContents) { |
163 var results = <StringEditBuffer>[]; | 173 var results = <StringEditBuffer>[]; |
164 | 174 |
165 // Parse the patch first. We'll need to extract bits of this as we go through | 175 // Parse the patch first. We'll need to extract bits of this as we go through |
166 // the other files. | 176 // the other files. |
167 var patchFinder = new PatchFinder.parseAndVisit(patchContents); | 177 var patchFinder = new PatchFinder.parseAndVisit(patchContents); |
168 | 178 |
169 // Merge `external` declarations with the corresponding `@patch` code. | 179 // Merge `external` declarations with the corresponding `@patch` code. |
| 180 bool failed = false; |
170 for (var partContent in partsContents) { | 181 for (var partContent in partsContents) { |
171 var partEdits = new StringEditBuffer(partContent); | 182 var partEdits = new StringEditBuffer(partContent); |
172 var partUnit = parseCompilationUnit(partContent); | 183 var partUnit = parseCompilationUnit(partContent); |
173 partUnit.accept(new PatchApplier(partEdits, patchFinder)); | 184 var patcher = new PatchApplier(partEdits, patchFinder); |
| 185 partUnit.accept(patcher); |
| 186 if (!failed) failed = patcher.patchWasMissing; |
174 results.add(partEdits); | 187 results.add(partEdits); |
175 } | 188 } |
| 189 if (failed) return null; |
176 return new List<String>.from(results.map((e) => e.toString())); | 190 return new List<String>.from(results.map((e) => e.toString())); |
177 } | 191 } |
178 | 192 |
179 /// Merge `@patch` declarations into `external` declarations. | 193 /// Merge `@patch` declarations into `external` declarations. |
180 class PatchApplier extends GeneralizingAstVisitor { | 194 class PatchApplier extends GeneralizingAstVisitor { |
181 final StringEditBuffer edits; | 195 final StringEditBuffer edits; |
182 final PatchFinder patch; | 196 final PatchFinder patch; |
183 | 197 |
184 bool _isLibrary = true; // until proven otherwise. | 198 bool _isLibrary = true; // until proven otherwise. |
| 199 bool patchWasMissing = false; |
185 | 200 |
186 PatchApplier(this.edits, this.patch); | 201 PatchApplier(this.edits, this.patch); |
187 | 202 |
188 @override | 203 @override |
189 visitCompilationUnit(CompilationUnit node) { | 204 visitCompilationUnit(CompilationUnit node) { |
190 super.visitCompilationUnit(node); | 205 super.visitCompilationUnit(node); |
191 if (_isLibrary) _mergeUnpatched(node); | 206 if (_isLibrary) _mergeUnpatched(node); |
192 } | 207 } |
193 | 208 |
194 void _merge(AstNode node, int pos) { | 209 void _merge(AstNode node, int pos) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 void _maybePatch(AstNode node) { | 266 void _maybePatch(AstNode node) { |
252 if (node is FieldDeclaration) return; | 267 if (node is FieldDeclaration) return; |
253 | 268 |
254 var externalKeyword = (node as dynamic).externalKeyword; | 269 var externalKeyword = (node as dynamic).externalKeyword; |
255 if (externalKeyword == null) return; | 270 if (externalKeyword == null) return; |
256 | 271 |
257 var name = _qualifiedName(node); | 272 var name = _qualifiedName(node); |
258 var patchNode = patch.patches[name]; | 273 var patchNode = patch.patches[name]; |
259 if (patchNode == null) { | 274 if (patchNode == null) { |
260 print('warning: patch not found for $name: $node'); | 275 print('warning: patch not found for $name: $node'); |
| 276 patchWasMissing = true; |
261 return; | 277 return; |
262 } | 278 } |
263 | 279 |
264 Annotation patchMeta = patchNode.metadata.lastWhere(_isPatchAnnotation); | 280 Annotation patchMeta = patchNode.metadata.lastWhere(_isPatchAnnotation); |
265 int start = patchMeta.endToken.next.offset; | 281 int start = patchMeta.endToken.next.offset; |
266 var code = patch.contents.substring(start, patchNode.end); | 282 var code = patch.contents.substring(start, patchNode.end); |
267 | 283 |
268 // Const factory constructors can't be legally parsed from the patch file, | 284 // Const factory constructors can't be legally parsed from the patch file, |
269 // so we need to omit the "const" there, but still preserve it. | 285 // so we need to omit the "const" there, but still preserve it. |
270 if (node is ConstructorDeclaration && | 286 if (node is ConstructorDeclaration && |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
438 if (diff != 0) return diff; | 454 if (diff != 0) return diff; |
439 return end - other.end; | 455 return end - other.end; |
440 } | 456 } |
441 } | 457 } |
442 | 458 |
443 List<SdkLibrary> _getSdkLibraries(String contents) { | 459 List<SdkLibrary> _getSdkLibraries(String contents) { |
444 var libraryBuilder = new SdkLibrariesReader_LibraryBuilder(true); | 460 var libraryBuilder = new SdkLibrariesReader_LibraryBuilder(true); |
445 parseCompilationUnit(contents).accept(libraryBuilder); | 461 parseCompilationUnit(contents).accept(libraryBuilder); |
446 return libraryBuilder.librariesMap.sdkLibraries; | 462 return libraryBuilder.librariesMap.sdkLibraries; |
447 } | 463 } |
OLD | NEW |