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

Side by Side Diff: mojo/public/dart/mojom/lib/src/generate.dart

Issue 1441033002: Move mojo and mojom from mojo/public/dart to mojo/dart/packages (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 1 month 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
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /// This library generates Mojo bindings for a Dart package.
6
7 library generate;
8
9 import 'dart:async';
10 import 'dart:convert';
11 import 'dart:developer' as dev;
12 import 'dart:io';
13
14 import 'package:mojom/src/utils.dart';
15 import 'package:path/path.dart' as path;
16
17 part 'mojom_finder.dart';
18
19 class MojomGenerator {
20 static dev.Counter _genMs;
21 final bool _errorOnDuplicate;
22 final bool _dryRun;
23 final Directory _mojoSdk;
24
25 Map<String, String> _duplicateDetection;
26 int _generationMs;
27
28 MojomGenerator(this._mojoSdk,
29 {bool errorOnDuplicate: true, bool profile: false, bool dryRun: false})
30 : _errorOnDuplicate = errorOnDuplicate,
31 _dryRun = dryRun,
32 _generationMs = 0,
33 _duplicateDetection = new Map<String, String>() {
34 if (_genMs == null) {
35 _genMs = new dev.Counter("mojom generation",
36 "Time spent waiting for bindings generation script in ms.");
37 dev.Metrics.register(_genMs);
38 }
39 }
40
41 generate(PackageInfo info) async {
42 // Count the .mojom files, and find the modification time of the most
43 // recently modified one.
44 int mojomCount = info.mojomFiles.length;
45 DateTime newestMojomTime;
46 for (File mojom in info.mojomFiles) {
47 DateTime mojomTime = await getModificationTime(mojom);
48 if ((newestMojomTime == null) || newestMojomTime.isBefore(mojomTime)) {
49 newestMojomTime = mojomTime;
50 }
51 }
52
53 // Count the .mojom.dart files, and find the modification time of the
54 // least recently modified one.
55 List mojomDartInfo = await _findOldestMojomDart(info.packageDir);
56 DateTime oldestMojomDartTime = null;
57 int mojomDartCount = 0;
58 if (mojomDartInfo != null) {
59 oldestMojomDartTime = mojomDartInfo[0];
60 mojomDartCount = mojomDartInfo[1];
61 }
62
63 // If we don't have enough .mojom.dart files, or if a .mojom file is
64 // newer than the oldest .mojom.dart file, then regenerate.
65 if ((mojomDartCount < mojomCount) ||
66 _shouldRegenerate(newestMojomTime, oldestMojomDartTime)) {
67 for (File mojom in info.mojomFiles) {
68 await _generateForMojom(
69 mojom, info.importDir, info.packageDir, info.name);
70 }
71 // Delete any .mojom.dart files that are still older than mojomTime.
72 await _deleteOldMojomDart(info.packageDir, newestMojomTime);
73 }
74 }
75
76 /// Under [package]/lib, returns the oldest modification time for a
77 /// .mojom.dart file.
78 _findOldestMojomDart(Directory package) async {
79 int mojomDartCount = 0;
80 DateTime oldestModTime;
81 Directory libDir = new Directory(path.join(package.path, 'lib'));
82 if (!await libDir.exists()) return null;
83 await for (var file in libDir.list(recursive: true, followLinks: false)) {
84 if (file is! File) continue;
85 if (!isMojomDart(file.path)) continue;
86 DateTime modTime = (await file.stat()).modified;
87 if ((oldestModTime == null) || oldestModTime.isAfter(modTime)) {
88 oldestModTime = modTime;
89 }
90 mojomDartCount++;
91 }
92 return [oldestModTime, mojomDartCount];
93 }
94
95 // Delete .mojom.dart files under [package] that are [olderThanThis].
96 _deleteOldMojomDart(Directory package, DateTime olderThanThis) async {
97 Directory libDir = new Directory(path.join(package.path, 'lib'));
98 assert(await libDir.exists());
99 await for (var file in libDir.list(recursive: true, followLinks: false)) {
100 if (file is! File) continue;
101 if (!isMojomDart(file.path)) continue;
102 DateTime modTime = (await file.stat()).modified;
103 if (modTime.isBefore(olderThanThis)) {
104 log.warning("Deleting stale .mojom.dart: $file");
105 await file.delete();
106 }
107 }
108 }
109
110 /// If the .mojoms file or the newest .mojom is newer than the oldest
111 /// .mojom.dart, then regenerate everything.
112 bool _shouldRegenerate(DateTime mojomTime, DateTime mojomDartTime) {
113 return (mojomTime == null) ||
114 (mojomDartTime == null) ||
115 mojomTime.isAfter(mojomDartTime);
116 }
117
118 _runBindingsGeneration(String script, List<String> arguments) async {
119 var result;
120 var stopwatch = new Stopwatch()..start();
121 result = await Process.run(script, arguments);
122 stopwatch.stop();
123 _genMs.value += stopwatch.elapsedMilliseconds;
124 return result;
125 }
126
127 _generateForMojom(File mojom, Directory importDir, Directory destination,
128 String packageName) async {
129 if (!isMojom(mojom.path)) return;
130 log.info("_generateForMojom($mojom)");
131
132 final script = path.join(
133 _mojoSdk.path, 'tools', 'bindings', 'mojom_bindings_generator.py');
134 final sdkInc = path.normalize(path.join(_mojoSdk.path, '..', '..'));
135 final outputDir = await destination.createTemp();
136 final output = outputDir.path;
137 final arguments = [
138 '--use_bundled_pylibs',
139 '-g',
140 'dart',
141 '-o',
142 output,
143 // TODO(zra): Are other include paths needed?
144 '-I',
145 sdkInc,
146 '-I',
147 importDir.path,
148 mojom.path
149 ];
150
151 log.info('Generating $mojom');
152 log.info('$script ${arguments.join(" ")}');
153 log.info('dryRun = $_dryRun');
154 if (!_dryRun) {
155 final result = await _runBindingsGeneration(script, arguments);
156 if (result.exitCode != 0) {
157 log.info("bindings generation result = ${result.exitCode}");
158 await outputDir.delete(recursive: true);
159 throw new GenerationError("$script failed:\n"
160 "code: ${result.exitCode}\n"
161 "stderr: ${result.stderr}\n"
162 "stdout: ${result.stdout}");
163 } else {
164 log.info("bindings generation result = 0");
165 }
166
167 // Generated .mojom.dart is under $output/dart-pkg/$PACKAGE/lib/$X
168 // Move $X to |destination|/lib/$X.
169 // Throw an exception if $PACKGE != [packageName].
170 final generatedDirName = path.join(output, 'dart-pkg');
171 final generatedDir = new Directory(generatedDirName);
172 log.info("generatedDir= $generatedDir");
173 assert(await generatedDir.exists());
174 await for (var genpack in generatedDir.list()) {
175 if (genpack is! Directory) continue;
176 log.info("genpack = $genpack");
177 var libDir = new Directory(path.join(genpack.path, 'lib'));
178 var name = path.relative(genpack.path, from: generatedDirName);
179 log.info("Found generated lib dir: $libDir");
180
181 if (packageName != name) {
182 await outputDir.delete(recursive: true);
183 throw new GenerationError(
184 "Tried to generate for package $name in package $packageName");
185 }
186
187 var copyDest = new Directory(path.join(destination.path, 'lib'));
188 log.info("Copy $libDir to $copyDest");
189 await _copyBindings(copyDest, libDir);
190 }
191
192 await outputDir.delete(recursive: true);
193 }
194 }
195
196 /// Searches for .mojom.dart files under [sourceDir] and copies them to
197 /// [destDir].
198 _copyBindings(Directory destDir, Directory sourceDir) async {
199 var sourceList = sourceDir.list(recursive: true, followLinks: false);
200 await for (var mojom in sourceList) {
201 if (mojom is! File) continue;
202 if (!isMojomDart(mojom.path)) continue;
203 log.info("Found $mojom");
204
205 final relative = path.relative(mojom.path, from: sourceDir.path);
206 final dest = path.join(destDir.path, relative);
207 final destDirectory = new Directory(path.dirname(dest));
208
209 if (_errorOnDuplicate && _duplicateDetection.containsKey(dest)) {
210 String original = _duplicateDetection[dest];
211 throw new GenerationError(
212 'Conflict: Both ${original} and ${mojom.path} supply ${dest}');
213 }
214 _duplicateDetection[dest] = mojom.path;
215
216 log.info('Copying $mojom to $dest');
217 if (!_dryRun) {
218 final File source = new File(mojom.path);
219 final File destFile = new File(dest);
220 if (await destFile.exists()) {
221 await destFile.delete();
222 }
223 log.info("Ensuring $destDirectory exists");
224 await destDirectory.create(recursive: true);
225 await source.copy(dest);
226 await markFileReadOnly(dest);
227 }
228 }
229 }
230 }
231
232 /// Given the location of the Mojo SDK and a root directory from which to begin
233 /// a search. Find .mojom files, and generate bindings for the relevant
234 /// packages.
235 class TreeGenerator {
236 final MojomGenerator _generator;
237 final Directory _mojoSdk;
238 final Directory _mojomRootDir;
239 final Directory _dartRootDir;
240 final List<String> _skip;
241 final bool _dryRun;
242
243 Set<String> _processedPackages;
244
245 int errors;
246
247 TreeGenerator(
248 Directory mojoSdk, this._mojomRootDir, this._dartRootDir, this._skip,
249 {bool dryRun: false})
250 : _mojoSdk = mojoSdk,
251 _dryRun = dryRun,
252 _generator = new MojomGenerator(mojoSdk, dryRun: dryRun),
253 _processedPackages = new Set<String>(),
254 errors = 0;
255
256 generate() async {
257 var mojomFinder = new MojomFinder(_mojomRootDir, _dartRootDir, _skip);
258 List<PackageInfo> packageInfos = await mojomFinder.find();
259 for (PackageInfo info in packageInfos) {
260 await _runGenerate(info);
261 }
262 }
263
264 _runGenerate(PackageInfo info) async {
265 try {
266 log.info('Generating bindings for ${info.name}');
267 await _generator.generate(info);
268 log.info('Done generating bindings for ${info.name}');
269 } on GenerationError catch (e) {
270 log.severe('Bindings generation failed for package ${info.name}: $e');
271 errors += 1;
272 }
273 }
274
275 bool _shouldSkip(File f) => containsPrefix(f.path, _skip);
276 }
277
278 /// Given the root of a directory tree to check, and the root of a directory
279 /// tree containing the canonical generated bindings, checks that the files
280 /// match, and recommends actions to take in case they don't. The canonical
281 /// directory should contain a subdirectory for each package that might be
282 /// encountered while traversing the directory tree being checked.
283 class TreeChecker {
284 final Directory _mojoSdk;
285 final Directory _mojomRootDir;
286 final Directory _dartRootDir;
287 final Directory _canonical;
288 final List<String> _skip;
289 int _errors;
290
291 TreeChecker(this._mojoSdk, this._mojomRootDir, this._dartRootDir,
292 this._canonical, this._skip)
293 : _errors = 0;
294
295 check() async {
296 // Generate missing .mojoms files if needed.
297 var mojomFinder = new MojomFinder(_mojomRootDir, _dartRootDir, _skip);
298 List<PackageInfo> packageInfos = await mojomFinder.find();
299
300 for (PackageInfo info in packageInfos) {
301 log.info("Checking package at ${info.packageDir}");
302 await _checkAll(info.packageDir);
303 await _checkSame(info.packageDir);
304 }
305
306 // If there were multiple mismatches, explain how to regenerate the bindings
307 // for the whole tree.
308 if (_errors > 1) {
309 String dart = makeRelative(Platform.executable);
310 String packRoot = (Platform.packageRoot == "")
311 ? ""
312 : "-p " + makeRelative(Platform.packageRoot);
313 String scriptPath = makeRelative(path.fromUri(Platform.script));
314 String mojoSdk = makeRelative(_mojoSdk.path);
315 String root = makeRelative(_mojomRootDir.path);
316 String dartRoot = makeRelative(_dartRootDir.path);
317 String skips = _skip.map((s) => "-s " + makeRelative(s)).join(" ");
318 stderr.writeln('It looks like there were multiple problems. '
319 'You can run the following command to regenerate bindings for your '
320 'whole tree:\n'
321 '\t$dart $packRoot $scriptPath gen -m $mojoSdk -r $root -o $dartRoot '
322 '$skips');
323 }
324 }
325
326 int get errors => _errors;
327
328 // Check that the files are the same.
329 _checkSame(Directory package) async {
330 Directory libDir = new Directory(path.join(package.path, 'lib'));
331 await for (var entry in libDir.list(recursive: true, followLinks: false)) {
332 if (entry is! File) continue;
333 if (!isMojomDart(entry.path)) continue;
334
335 String relPath = path.relative(entry.path, from: package.parent.path);
336 File canonicalFile = new File(path.join(_canonical.path, relPath));
337 if (!await canonicalFile.exists()) {
338 log.info("No canonical file for $entry");
339 continue;
340 }
341
342 log.info("Comparing $entry with $canonicalFile");
343 int fileComparison = await compareFiles(entry, canonicalFile);
344 if (fileComparison != 0) {
345 String entryPath = makeRelative(entry.path);
346 String canonicalPath = makeRelative(canonicalFile.path);
347 if (fileComparison > 0) {
348 stderr.writeln('The generated file:\n\t$entryPath\n'
349 'is newer thanthe canonical file\n\t$canonicalPath\n,'
350 'and they are different. Regenerate canonical files?');
351 } else {
352 String dart = makeRelative(Platform.executable);
353 String packRoot = (Platform.packageRoot == "")
354 ? ""
355 : "-p " + makeRelative(Platform.packageRoot);
356 String root = makeRelative(_mojomRootDir.path);
357 String packagePath = makeRelative(package.path);
358 String scriptPath = makeRelative(path.fromUri(Platform.script));
359 String mojoSdk = makeRelative(_mojoSdk.path);
360 String skips = _skip.map((s) => "-s " + makeRelative(s)).join(" ");
361 stderr.writeln('For the package: $packagePath\n'
362 'The generated file:\n\t$entryPath\n'
363 'is older than the canonical file:\n\t$canonicalPath\n'
364 'and they are different. Regenerate by running:\n'
365 '\t$dart $packRoot $scriptPath single -m $mojoSdk -r $root '
366 '-p $packagePath $skips');
367 }
368 _errors++;
369 return;
370 }
371 }
372 }
373
374 // Check that every .mojom.dart in the canonical package is also in the
375 // package we are checking.
376 _checkAll(Directory package) async {
377 String packageName = path.relative(package.path, from: package.parent.path);
378 String canonicalPackagePath =
379 path.join(_canonical.path, packageName, 'lib');
380 Directory canonicalPackage = new Directory(canonicalPackagePath);
381 if (!await canonicalPackage.exists()) return;
382
383 var canonicalPackages =
384 canonicalPackage.list(recursive: true, followLinks: false);
385 await for (var entry in canonicalPackages) {
386 if (entry is! File) continue;
387 if (!isMojomDart(entry.path)) continue;
388
389 String relPath = path.relative(entry.path, from: canonicalPackage.path);
390 File genFile = new File(path.join(package.path, 'lib', relPath));
391 log.info("Checking that $genFile exists");
392 if (!await genFile.exists()) {
393 String dart = makeRelative(Platform.executable);
394 String packRoot = (Platform.packageRoot == "")
395 ? ""
396 : "-p " + makeRelative(Platform.packageRoot);
397 String root = makeRelative(_mojomRootDir.path);
398 String genFilePath = makeRelative(genFile.path);
399 String packagePath = makeRelative(package.path);
400 String scriptPath = makeRelative(path.fromUri(Platform.script));
401 String mojoSdk = makeRelative(_mojoSdk.path);
402 String skips = _skip.map((s) => "-s " + makeRelative(s)).join(" ");
403 stderr.writeln('The generated file:\n\t$genFilePath\n'
404 'is needed but does not exist. Run the command\n'
405 '\t$dart $packRoot $scriptPath single -m $mojoSdk -r $root '
406 '-p $packagePath $skips');
407 _errors++;
408 return;
409 }
410 }
411 }
412
413 bool _shouldSkip(File f) => containsPrefix(f.path, _skip);
414 }
OLDNEW
« no previous file with comments | « mojo/public/dart/mojom/lib/src/commands/single.dart ('k') | mojo/public/dart/mojom/lib/src/mojom_finder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698