Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(210)

Side by Side Diff: tool/global_compile.dart

Issue 1965013003: Modify global compile tool to compile by package (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: Comment and format Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env dart 1 #!/usr/bin/env dart
2 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 2 // Copyright (c) 2016, 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 import 'dart:async';
6 import 'dart:io'; 7 import 'dart:io';
7 8
8 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; 9 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
9 import 'package:args/args.dart' show ArgParser; 10 import 'package:args/args.dart' show ArgParser;
10 import 'package:dev_compiler/src/analyzer/context.dart' 11 import 'package:dev_compiler/src/analyzer/context.dart'
11 show createAnalysisContextWithSources; 12 show createAnalysisContextWithSources;
12 import 'package:dev_compiler/src/analyzer/context.dart' 13 import 'package:dev_compiler/src/analyzer/context.dart'
13 show createAnalysisContextWithSources, AnalyzerOptions; 14 show createAnalysisContextWithSources, AnalyzerOptions;
14 import 'package:path/path.dart' as path; 15 import 'package:path/path.dart' as path;
15 16
17 const ENTRY = "main";
Jennifer Messerly 2016/05/10 20:43:00 style nit: this should not be upper camel case :)
18
16 void main(List<String> args) { 19 void main(List<String> args) {
17 // Parse flags. 20 // Parse flags.
18 var parser = new ArgParser() 21 var parser = new ArgParser()
19 ..addOption('out', abbr: 'o', defaultsTo: 'out.js') 22 ..addOption('out',
20 ..addFlag('unsafe-force-compile', negatable: false) 23 help: 'Output file (defaults to "out.js")',
21 ..addOption('package-root', abbr: 'p', defaultsTo: 'packages/'); 24 abbr: 'o',
25 defaultsTo: 'out.js')
26 ..addFlag('unsafe-force-compile',
Jennifer Messerly 2016/05/10 20:43:00 I may have asked this before but, shouldn't this j
27 help: 'Generate code with undefined behavior', negatable: false)
28 ..addOption('package-root',
29 help: 'Directory containing packages',
30 abbr: 'p',
31 defaultsTo: 'packages/')
32 ..addFlag('log', help: 'Show individual build commands')
33 ..addOption('tmp',
Jennifer Messerly 2016/05/10 20:43:00 any reason to not always use system temp? I'm tryi
34 help:
35 'Directory for temporary artifacts (defaults to a system tmp directo ry)');
Jennifer Messerly 2016/05/10 20:43:00 long line
22 36
23 var options = parser.parse(args); 37 var options = parser.parse(args);
24 if (options.rest.length != 1) { 38 if (options.rest.length != 1) {
25 throw 'Expected a single dart entrypoint.'; 39 throw 'Expected a single dart entrypoint.';
26 } 40 }
27 var entry = options.rest.first; 41 var entry = options.rest.first;
28 var outfile = options['out']; 42 var outfile = options['out'] as String;
29 var packageRoot = options['package-root']; 43 var packageRoot = options['package-root'] as String;
30 var unsafe = options['unsafe-force-compile']; 44 var unsafe = options['unsafe-force-compile'] as bool;
31 45 var log = options['log'] as bool;
32 // Build an invocation to dartdevc. 46 var tmp = options['tmp'] as String;
47
48 // Build an invocation to dartdevc
33 var dartPath = Platform.resolvedExecutable; 49 var dartPath = Platform.resolvedExecutable;
34 var ddcPath = path.dirname(path.dirname(Platform.script.toFilePath())); 50 var ddcPath = path.dirname(path.dirname(Platform.script.toFilePath()));
35 var command = [ 51 var template = [
36 '$ddcPath/bin/dartdevc.dart', 52 '$ddcPath/bin/dartdevc.dart',
37 'compile', 53 'compile',
38 '--no-source-map', // Invalid as we're just concatenating files below 54 '--no-source-map', // Invalid as we're just concatenating files below
39 '-p', 55 '-p',
40 packageRoot, 56 packageRoot
41 '-o',
42 outfile
43 ]; 57 ];
44 if (unsafe) { 58 if (unsafe) {
45 command.add('--unsafe-force-compile'); 59 template.add('--unsafe-force-compile');
46 } 60 }
47 61
48 // Compute the transitive closure 62 // Compute the transitive closure
49 var watch = new Stopwatch()..start(); 63 var total = new Stopwatch()..start();
64 var partial = new Stopwatch()..start();
65
66 // TODO(vsm): We're using the analyzer just to compute the import/export/part
67 // dependence graph. This is expensive. Is there a lighterweight way to do
68 // this?
Jennifer Messerly 2016/05/10 20:43:00 yes indeed there is! See https://github.com/dart-
50 var context = createAnalysisContextWithSources(new AnalyzerOptions()); 69 var context = createAnalysisContextWithSources(new AnalyzerOptions());
51 var inputSet = new Set<String>(); 70 transitiveFiles(context, entry, Directory.current.path);
52 transitiveFiles(inputSet, context, entry, Directory.current.path); 71 orderModules();
53 command.addAll(inputSet); 72 computeTransitiveDependences();
54 var result = Process.runSync(dartPath, command); 73
55 74 var graphTime = partial.elapsedMilliseconds / 1000;
56 if (result.exitCode == 0) { 75 print('Computed global build graph in $graphTime seconds');
57 print(result.stdout); 76
58 } else { 77 // Prepend Dart runtime files to the output
59 print('ERROR:');
60 print(result.stdout);
61 print(result.stderr);
62 exit(1);
63 }
64 var time = watch.elapsedMilliseconds / 1000;
65 print('Successfully compiled ${inputSet.length} files in $time seconds');
66
67 // Prepend Dart runtime files to the output.
68 var out = new File(outfile); 78 var out = new File(outfile);
69 var code = out.readAsStringSync();
70 var dartLibrary = 79 var dartLibrary =
71 new File(path.join(ddcPath, 'lib', 'runtime', 'dart_library.js')) 80 new File(path.join(ddcPath, 'lib', 'runtime', 'dart_library.js'))
72 .readAsStringSync(); 81 .readAsStringSync();
82 out.writeAsStringSync(dartLibrary);
73 var dartSdk = new File(path.join(ddcPath, 'lib', 'runtime', 'dart_sdk.js')) 83 var dartSdk = new File(path.join(ddcPath, 'lib', 'runtime', 'dart_sdk.js'))
74 .readAsStringSync(); 84 .readAsStringSync();
75 out.writeAsStringSync(dartLibrary);
76 out.writeAsStringSync(dartSdk, mode: FileMode.APPEND); 85 out.writeAsStringSync(dartSdk, mode: FileMode.APPEND);
77 out.writeAsStringSync(code, mode: FileMode.APPEND); 86
78 87 // Linearize module concatenation for deterministic output
79 // Append the entry point invocation. 88 var last = new Future.value();
80 var moduleName = path.basenameWithoutExtension(outfile); 89 for (var module in orderedModules) {
81 var libraryName = 90 linearizerMap[module] = last;
82 path.withoutExtension(entry).replaceAll(path.separator, '__'); 91 var completer = new Completer();
83 out.writeAsStringSync('dart_library.start("$moduleName", "$libraryName");\n', 92 completerMap[module] = completer;
84 mode: FileMode.APPEND); 93 last = completer.future;
94 }
95
96 // Build modules asynchronously
Jennifer Messerly 2016/05/10 20:43:00 Would it be easier to generate a Makefile and just
97 var tmpdir = (tmp == null)
98 ? Directory.systemTemp
99 .createTempSync(outfile.replaceAll(path.separator, '__'))
100 : new Directory(tmp)..createSync();
101 for (var module in orderedModules) {
102 var file = tmpdir.path + path.separator + module + '.js';
103 var command = new List.from(template)..addAll(['-o', file]);
104 var dependences = transitiveDependenceMap[module];
105 for (var dependence in dependences) {
106 var summary = tmpdir.path + path.separator + dependence + '.sum';
107 command.addAll(['-s', summary]);
108 }
109 var infiles = fileMap[module];
110 command.addAll(infiles);
111
112 var immediateDeps =
113 dependenceMap.containsKey(module) ? dependenceMap[module] : <String>[];
114 var waitList = immediateDeps.map((dep) => readyMap[dep]);
115 var future = Future.wait(waitList);
116 readyMap[module] = future.then((_) {
117 var ready = Process.run(dartPath, command);
118 if (log) {
119 print(command.join(' '));
120 }
121 return ready.then((result) {
Jennifer Messerly 2016/05/10 20:43:00 this could be an `await` https://www.dartlang.org/
122 if (result.exitCode != 0) {
123 print('ERROR: compiling $module');
124 print(result.stdout);
125 print(result.stderr);
126 out.deleteSync();
127 exit(1);
128 }
129 print('Compiled $module (${infiles.length} files)');
130 print(result.stdout);
131
132 // Schedule module append once the previous module is written
133 var codefile = new File(file);
134 linearizerMap[module]
135 .then((_) => codefile.readAsString())
136 .then((code) =>
137 out.writeAsString(code, mode: FileMode.APPEND, flush: true))
138 .then((_) => completerMap[module].complete());
139 });
140 });
141 }
142
143 last.then((_) {
Jennifer Messerly 2016/05/10 20:43:00 same this could be async/await
144 var time = total.elapsedMilliseconds / 1000;
145 print('Successfully compiled ${inputSet.length} files in $time seconds');
146
147 // Append the entry point invocation.
148 var libraryName =
149 path.withoutExtension(entry).replaceAll(path.separator, '__');
150 out.writeAsStringSync('dart_library.start("$ENTRY", "$libraryName");\n',
151 mode: FileMode.APPEND);
152 });
153 }
154
155 final inputSet = new Set<String>();
156 final dependenceMap = new Map<String, Set<String>>();
Jennifer Messerly 2016/05/10 20:43:00 all of these maps do make me wonder if we should h
157 final transitiveDependenceMap = new Map<String, Set<String>>();
158 final fileMap = new Map<String, Set<String>>();
159
160 final readyMap = new Map<String, Future>();
161 final linearizerMap = new Map<String, Future>();
162 final completerMap = new Map<String, Completer>();
163
164 final orderedModules = new List<String>();
165 final visitedModules = new Set<String>();
166
167 void orderModules(
Jennifer Messerly 2016/05/10 20:43:00 fyi ... I didn't look at all this build graph stuf
168 [String module = ENTRY, List<String> stack, Set<String> visited]) {
169 if (stack == null) {
170 assert(visited == null);
171 stack = new List<String>();
172 visited = new Set<String>();
173 }
174 if (visited.contains(module)) return;
175 visited.add(module);
176 if (stack.contains(module)) {
177 print(stack);
178 throw 'Circular dependence on $module';
179 }
180 stack.add(module);
181 var dependences = dependenceMap[module];
182 if (dependences != null) {
183 for (var dependence in dependences) {
184 orderModules(dependence, stack, visited);
185 }
186 }
187 orderedModules.add(module);
188 assert(module == stack.last);
189 stack.removeLast();
190 }
191
192 void computeTransitiveDependences() {
193 for (var module in orderedModules) {
194 var transitiveSet = new Set<String>();
195 if (dependenceMap.containsKey(module)) {
196 transitiveSet.addAll(dependenceMap[module]);
197 for (var dependence in dependenceMap[module]) {
198 transitiveSet.addAll(transitiveDependenceMap[dependence]);
199 }
200 }
201 transitiveDependenceMap[module] = transitiveSet;
202 }
203 }
204
205 String getModule(String uri) {
206 var sourceUri = Uri.parse(uri);
207 if (sourceUri.scheme == 'dart') {
208 return 'dart';
209 } else if (sourceUri.scheme == 'package') {
210 return path.split(sourceUri.path)[0];
211 } else {
212 return ENTRY;
213 }
214 }
215
216 bool processFile(String file) {
217 inputSet.add(file);
218
219 var module = getModule(file);
220 fileMap.putIfAbsent(module, () => new Set<String>());
221 return fileMap[module].add(file);
222 }
223
224 void processDependence(String from, String to) {
225 var fromModule = getModule(from);
226 var toModule = getModule(to);
227 if (fromModule == toModule || toModule == 'dart') return;
228 dependenceMap.putIfAbsent(fromModule, () => new Set<String>());
229 dependenceMap[fromModule].add(toModule);
85 } 230 }
86 231
87 String canonicalize(String uri, String root) { 232 String canonicalize(String uri, String root) {
88 var sourceUri = Uri.parse(uri); 233 var sourceUri = Uri.parse(uri);
89 if (sourceUri.scheme == '') { 234 if (sourceUri.scheme == '') {
90 sourceUri = path.toUri( 235 sourceUri = path.toUri(
91 path.isAbsolute(uri) ? path.absolute(uri) : path.join(root, uri)); 236 path.isAbsolute(uri) ? path.absolute(uri) : path.join(root, uri));
92 } 237 }
93 return sourceUri.toString(); 238 return sourceUri.toString();
94 } 239 }
95 240
96 void transitiveFiles(Set<String> results, AnalysisContext context, 241 void transitiveFiles(AnalysisContext context, String entryPoint, String root) {
97 String entryPoint, String root) {
98 entryPoint = canonicalize(entryPoint, root); 242 entryPoint = canonicalize(entryPoint, root);
99 if (entryPoint.startsWith('dart:')) return; 243 if (entryPoint.startsWith('dart:')) return;
100 var entryDir = path.dirname(entryPoint); 244 var entryDir = path.dirname(entryPoint);
101 if (results.add(entryPoint)) { 245 if (processFile(entryPoint)) {
102 // Process this 246 // Process this
103 var source = context.sourceFactory.forUri(entryPoint); 247 var source = context.sourceFactory.forUri(entryPoint);
104 if (source == null) { 248 if (source == null) {
105 throw new Exception('could not create a source for $entryPoint.' 249 throw new Exception('could not create a source for $entryPoint.'
106 ' The file name is in the wrong format or was not found.'); 250 ' The file name is in the wrong format or was not found.');
107 } 251 }
108 var library = context.computeLibraryElement(source); 252 var library = context.computeLibraryElement(source);
109 for (var entry in library.imports) { 253 for (var entry in library.imports) {
110 if (entry.uri == null) continue; 254 if (entry.uri == null) continue;
111 transitiveFiles(results, context, entry.uri, entryDir); 255 processDependence(entryPoint, canonicalize(entry.uri, entryDir));
256 transitiveFiles(context, entry.uri, entryDir);
112 } 257 }
113 for (var entry in library.exports) { 258 for (var entry in library.exports) {
114 transitiveFiles(results, context, entry.uri, entryDir); 259 processDependence(entryPoint, canonicalize(entry.uri, entryDir));
260 transitiveFiles(context, entry.uri, entryDir);
115 } 261 }
116 for (var part in library.parts) { 262 for (var part in library.parts) {
117 results.add(canonicalize(part.uri, entryDir)); 263 processFile(canonicalize(part.uri, entryDir));
118 } 264 }
119 } 265 }
120 } 266 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698