Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2017, 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:async'; | |
| 6 import 'dart:io'; | |
| 7 | |
| 8 import 'package:front_end/compiler_options.dart'; | |
| 9 import 'package:front_end/dependency_grapher.dart'; | |
| 10 import 'package:path/path.dart' as pathos; | |
| 11 | |
| 12 main() async { | |
| 13 exit(await new _SubpackageRelationshipsTest().run()); | |
| 14 } | |
| 15 | |
| 16 /// Map from subpackage name to the rules for what the subpackage is allowed to | |
| 17 /// depend directly on. | |
| 18 /// | |
| 19 /// Each subdirectory of `lib/src` is considered a subpackage. Files in | |
| 20 /// `lib/src` but not in a subdirectory are considered to be in the `lib/src` | |
| 21 /// subpackage. Files outside of `lib/src` (but still in `lib`) are considered | |
| 22 /// to be in the `lib` subpackage. | |
| 23 /// | |
| 24 /// TODO(paulberry): stuff in lib/src shouldn't depend on lib; lib should just | |
| 25 /// re-export stuff in lib/src. | |
| 26 /// TODO(paulberry): remove dependencies on analyzer. | |
| 27 final subpackageRules = { | |
|
Paul Berry
2017/01/12 21:32:16
Note: this describes the dependencies that exist t
| |
| 28 'lib': new SubpackageRules( | |
| 29 mayImportAnalyzer: true, | |
| 30 allowedDependencies: ['lib/src', 'lib/src/base']), | |
| 31 'lib/src': new SubpackageRules( | |
| 32 mayImportAnalyzer: true, | |
| 33 allowedDependencies: ['lib', 'lib/src/base', 'lib/src/scanner']), | |
| 34 'lib/src/base': new SubpackageRules( | |
| 35 mayImportAnalyzer: true, allowedDependencies: ['lib']), | |
| 36 'lib/src/scanner': new SubpackageRules(allowedDependencies: ['lib/src/base']), | |
| 37 }; | |
| 38 | |
| 39 /// Rules for what a subpackage may depend directly on. | |
| 40 class SubpackageRules { | |
| 41 /// Indicates whether the subpackage may directly depend on analyzer. | |
| 42 final bool mayImportAnalyzer; | |
| 43 | |
| 44 /// Indicates which other subpackages a given subpackage may directly depend | |
| 45 /// on. | |
| 46 final List<String> allowedDependencies; | |
| 47 | |
| 48 SubpackageRules( | |
| 49 {this.mayImportAnalyzer: false, this.allowedDependencies: const []}); | |
| 50 } | |
| 51 | |
| 52 class _SubpackageRelationshipsTest { | |
| 53 /// File uri of the root of the front_end package. | |
| 54 final frontEndRootUri = Platform.script.resolve('..'); | |
|
danrubel
2017/01/12 21:54:54
Will this test fail if run from the wrong director
Paul Berry
2017/01/12 22:25:01
Platform.script is the path to the script file (me
| |
| 55 | |
| 56 /// Indicates whether any problems have been reported yet. | |
| 57 bool problemsReported = false; | |
| 58 | |
| 59 /// Check for problems resulting from URI [src] having a direct dependency on | |
| 60 /// URI [dst]. | |
| 61 void checkDependency(Uri src, Uri dst) { | |
| 62 if (dst.scheme == 'dart') return; | |
| 63 if (dst.scheme != 'package') { | |
| 64 problem('$src depends on $dst, which is neither a package: or dart: URI'); | |
| 65 return; | |
| 66 } | |
| 67 var srcSubpackage = subpackageForUri(src); | |
| 68 if (srcSubpackage == null) return; | |
| 69 var srcSubpackageRules = subpackageRules[srcSubpackage]; | |
| 70 if (srcSubpackageRules == null) { | |
| 71 problem('$src is in subpackage "$srcSubpackage", which is not found in ' | |
| 72 'subpackageRules'); | |
| 73 return; | |
| 74 } | |
| 75 if (!srcSubpackageRules.mayImportAnalyzer && | |
| 76 dst.pathSegments[0] == 'analyzer') { | |
| 77 problem('$src depends on $dst, but subpackage "$srcSubpackage" may not ' | |
| 78 'import analyzer'); | |
| 79 } | |
| 80 var dstSubPackage = subpackageForUri(dst); | |
| 81 if (dstSubPackage == null) return; | |
| 82 if (dstSubPackage == srcSubpackage) return; | |
| 83 if (!srcSubpackageRules.allowedDependencies.contains(dstSubPackage)) { | |
| 84 problem('$src depends on $dst, but subpackage "$srcSubpackage" is not ' | |
| 85 'allowed to depend on subpackage "$dstSubPackage"'); | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 /// Finds all files in the front_end's "lib" directory and returns their Uris | |
| 90 /// (as "package:" URIs). | |
| 91 Future<List<Uri>> findFrontEndUris() async { | |
|
Siggi Cherem (dart-lang)
2017/01/12 22:39:12
FWIW - for simple unit tests, I'm in favor of keep
Paul Berry
2017/01/12 23:35:57
Fair enough. Done.
| |
| 92 var frontEndUris = <Uri>[]; | |
| 93 var frontEndRootPath = pathos.fromUri(frontEndRootUri); | |
| 94 await for (var entity in new Directory(frontEndRootPath) | |
| 95 .list(recursive: true, followLinks: false)) { | |
| 96 if (entity is File && entity.path.endsWith('.dart')) { | |
| 97 var posixRelativePath = pathos | |
| 98 .relative(entity.path, from: frontEndRootPath) | |
| 99 .replaceAll(pathos.separator, '/'); | |
|
Siggi Cherem (dart-lang)
2017/01/12 22:39:12
alternatively, use the various pathos contexts (in
Paul Berry
2017/01/12 23:35:57
Done.
| |
| 100 if (!posixRelativePath.startsWith('lib/')) continue; | |
|
Siggi Cherem (dart-lang)
2017/01/12 22:39:12
consider making the frontEndRootPath be `Platform.
Paul Berry
2017/01/12 23:35:57
Done.
| |
| 101 frontEndUris.add(Uri.parse( | |
| 102 posixRelativePath.replaceFirst('lib/', 'package:front_end/'))); | |
| 103 } | |
| 104 } | |
| 105 return frontEndUris; | |
| 106 } | |
| 107 | |
| 108 /// Reports a single problem. | |
| 109 void problem(String description) { | |
| 110 print(description); | |
| 111 problemsReported = true; | |
| 112 } | |
| 113 | |
| 114 /// Tests all subpackage relationships in the front end, and returns an | |
| 115 /// appropriate exit code. | |
| 116 Future<int> run() async { | |
| 117 var frontEndUris = await findFrontEndUris(); | |
| 118 var packagesFileUri = frontEndRootUri.resolve('../../.packages'); | |
| 119 var graph = await graphForProgram( | |
| 120 frontEndUris, | |
| 121 new CompilerOptions() | |
| 122 ..packagesFileUri = packagesFileUri | |
| 123 ..chaseDependencies = true); | |
| 124 for (var i = 0; i < graph.topologicallySortedCycles.length; i++) { | |
|
Siggi Cherem (dart-lang)
2017/01/12 22:39:12
Do we need to use the graph/walker API and compute
Paul Berry
2017/01/12 23:35:57
Regarding computation of library cycles, I agree t
Siggi Cherem (dart-lang)
2017/01/13 00:02:06
Sounds good, no problem.
Last year some unit test
| |
| 125 for (var library in graph.topologicallySortedCycles[i].libraries.values) { | |
| 126 for (var dependency in library.dependencies) { | |
| 127 checkDependency(library.uri, dependency.uri); | |
| 128 } | |
| 129 } | |
| 130 } | |
| 131 return problemsReported ? 1 : 0; | |
| 132 } | |
| 133 | |
| 134 /// Determines which subpackage [src] is in. | |
| 135 /// | |
| 136 /// If [src] is not part of the front end, `null` is returned. | |
| 137 String subpackageForUri(Uri src) { | |
| 138 if (src.scheme != 'package') return null; | |
| 139 if (src.pathSegments[0] != 'front_end') return null; | |
| 140 if (src.pathSegments[1] != 'src') return 'lib'; | |
| 141 if (src.pathSegments.length == 3) return 'lib/src'; | |
| 142 return 'lib/src/${src.pathSegments[2]}'; | |
| 143 } | |
| 144 } | |
| OLD | NEW |