Index: lib/deferred_library_check.dart |
diff --git a/lib/deferred_library_check.dart b/lib/deferred_library_check.dart |
index d472eae345809f2ad894f68a587478a8ec759cb2..59ac1d5a9863e6f97895d33e5d393333415b2a80 100644 |
--- a/lib/deferred_library_check.dart |
+++ b/lib/deferred_library_check.dart |
@@ -6,31 +6,36 @@ |
/// given in a YAML file. The format of the YAML file is: |
/// |
/// main: |
-/// packages: |
+/// include: |
/// - some_package |
/// - other_package |
/// |
/// foo: |
-/// packages: |
+/// include: |
/// - foo |
Siggi Cherem (dart-lang)
2015/12/18 02:29:41
should we always implicitly include the foo packag
Harry Terkelsen
2015/12/18 21:22:21
What I'm seeing is more like:
import 'package:foo
|
/// - bar |
/// |
/// baz: |
-/// packages: |
+/// include: |
/// - baz |
/// - quux |
+/// exclude: |
+/// - zardoz |
/// |
/// The YAML file consists of a list of declarations, one for each deferred |
/// part expected in the output. At least one of these parts must be named |
/// "main"; this is the main part that contains the program entrypoint. Each |
/// top-level part contains a list of package names that are expected to be |
-/// contained in that part. Any package that is not explicitly listed is |
-/// expected to be in the main part. For instance, in the example YAML above |
-/// the part named "baz" is expected to contain the packages "baz" and "quux". |
+/// contained in that part, a list of package names that are expected to be in |
+/// another part, or both. For instance, in the example YAML above the part |
+/// named "baz" is expected to contain the packages "baz" and "quux" and not to |
+/// contain the package "zardoz". |
/// |
/// The names for parts given in the specification YAML file (besides "main") |
-/// are arbitrary and just used for reporting when the output does not meet the |
-/// specification. |
+/// are the same as the name given to the deferred import in the dart file. For |
+/// instance, if you have `import 'package:foo/foo.dart' deferred as foo;` in |
Siggi Cherem (dart-lang)
2015/12/18 02:29:41
same here
Harry Terkelsen
2015/12/18 21:22:21
Done.
|
+/// your dart file, then the corresponding name in the specification file is |
+/// 'foo'. |
library dart2js_info.deferred_library_check; |
import 'info.dart'; |
@@ -38,51 +43,64 @@ import 'package:quiver/collection.dart'; |
List<ManifestComplianceFailure> checkDeferredLibraryManifest( |
AllInfo info, Map manifest) { |
- // For each part in the manifest, record the expected "packages" for that |
- // part. |
- var packages = <String, String>{}; |
+ var includedPackages = new Multimap<String, String>(); |
+ var excludedPackages = new Multimap<String, String>(); |
for (var part in manifest.keys) { |
- for (var package in manifest[part]['packages']) { |
- if (packages.containsKey(package)) { |
- throw new ArgumentError.value( |
- manifest, |
- 'manifest', |
- 'You cannot specify that package "$package" maps to both parts ' |
- '"$part" and "${packages[package]}".'); |
- } |
- packages[package] = part; |
+ for (var package in manifest[part]['include'] ?? []) { |
+ includedPackages.add(part, package); |
+ } |
+ for (var package in manifest[part]['exclude'] ?? []) { |
+ excludedPackages.add(part, package); |
+ } |
+ } |
+ |
+ // There are 2 types of parts that are valid to mention in the specification |
+ // file. These are the main part and directly imported deferred parts. The |
+ // main part is always named 'main'; the directly imported deferred parts are |
+ // the outputUnits whose list of 'imports' contains a single import. If the |
+ // part is shared, it will have more than one import since it will include the |
+ // imports of all the top-level deferred parts that will load the shared part. |
+ List<String> validParts = ['main'] |
Siggi Cherem (dart-lang)
2015/12/18 02:29:41
we might want to validate that there are no 2 part
Harry Terkelsen
2015/12/18 21:22:21
Do you still think we should do this since the nam
Siggi Cherem (dart-lang)
2015/12/18 21:34:25
Good point - nope, it makes sense to leave it as i
|
+ ..addAll(info.outputUnits |
+ .where((unit) => unit.imports.length == 1) |
+ .map((unit) => unit.imports.single)); |
Siggi Cherem (dart-lang)
2015/12/18 02:29:41
do we need to get the package name from the librar
Harry Terkelsen
2015/12/18 21:22:21
unit.imports is a list of the names of the deferre
Siggi Cherem (dart-lang)
2015/12/18 21:34:25
interesting - I really thought that these were the
Harry Terkelsen
2015/12/18 21:42:53
I think if there are two with the same name then o
|
+ List<String> mentionedParts = [] |
+ ..addAll(includedPackages.keys) |
+ ..addAll(excludedPackages.keys); |
+ var partNameFailures = <_InvalidPartName>[]; |
+ for (var part in mentionedParts) { |
+ if (!validParts.contains(part)) { |
+ partNameFailures.add(new _InvalidPartName(part, validParts)); |
} |
} |
+ if (partNameFailures.isNotEmpty) { |
+ return partNameFailures; |
+ } |
- var guessedPartMapping = new BiMap<String, String>(); |
- guessedPartMapping['main'] = 'main'; |
+ var mentionedPackages = new Set() |
+ ..addAll(includedPackages.values) |
+ ..addAll(excludedPackages.values); |
+ var actualIncludedPackages = new Multimap<String, String>(); |
var failures = <ManifestComplianceFailure>[]; |
checkInfo(BasicInfo info) { |
+ if (info.size == 0) return; |
var lib = _getLibraryOf(info); |
if (lib != null && _isPackageUri(lib.uri)) { |
var packageName = _getPackageName(lib.uri); |
- var outputUnitName = info.outputUnit.name; |
- var expectedPart; |
- if (packages.containsKey(packageName)) { |
- expectedPart = packages[packageName]; |
+ if (!mentionedPackages.contains(packageName)) return; |
+ var containingParts = <String>[]; |
+ if (info.outputUnit.name == 'main') { |
+ containingParts.add('main'); |
} else { |
- expectedPart = 'main'; |
+ containingParts.addAll(info.outputUnit.imports); |
} |
- var expectedOutputUnit = guessedPartMapping[expectedPart]; |
- if (expectedOutputUnit == null) { |
- guessedPartMapping[expectedPart] = outputUnitName; |
- } else { |
- if (expectedOutputUnit != outputUnitName) { |
- // TODO(het): add options for how to treat unspecified packages |
- if (!packages.containsKey(packageName)) { |
- failures.add(new ManifestComplianceFailure(info.name, packageName)); |
- } else { |
- var actualPart = guessedPartMapping.inverse[outputUnitName]; |
- failures.add(new ManifestComplianceFailure( |
- info.name, packageName, expectedPart, actualPart)); |
- } |
+ for (var part in containingParts) { |
+ actualIncludedPackages.add(part, packageName); |
+ if (excludedPackages[part].contains(packageName)) { |
+ failures |
+ .add(new _PartContainedExcludedPackage(part, packageName, info)); |
} |
} |
} |
@@ -91,6 +109,12 @@ List<ManifestComplianceFailure> checkDeferredLibraryManifest( |
info.functions.forEach(checkInfo); |
info.fields.forEach(checkInfo); |
+ includedPackages.forEach((part, package) { |
+ if (!actualIncludedPackages.containsKey(part) || |
+ !actualIncludedPackages[part].contains(package)) { |
+ failures.add(new _PartDidNotContainPackage(part, package)); |
+ } |
+ }); |
return failures; |
} |
@@ -113,21 +137,40 @@ String _getPackageName(Uri uri) { |
} |
class ManifestComplianceFailure { |
- final String infoName; |
- final String packageName; |
- final String expectedPart; |
- final String actualPart; |
+ const ManifestComplianceFailure(); |
+} |
- const ManifestComplianceFailure(this.infoName, this.packageName, |
- [this.expectedPart, this.actualPart]); |
+class _InvalidPartName extends ManifestComplianceFailure { |
+ final String part; |
+ final List<String> validPartNames; |
+ const _InvalidPartName(this.part, this.validPartNames); |
String toString() { |
- if (expectedPart == null && actualPart == null) { |
- return '"$infoName" from package "$packageName" was not declared ' |
- 'to be in an explicit part but was not in the main part'; |
- } else { |
- return '"$infoName" from package "$packageName" was specified to ' |
- 'be in part $expectedPart but is in part $actualPart'; |
- } |
+ return 'Manifest file declares invalid part "$part". ' |
+ 'Valid part names are: $validPartNames'; |
+ } |
+} |
+ |
+class _PartContainedExcludedPackage extends ManifestComplianceFailure { |
+ final String part; |
+ final String package; |
+ final BasicInfo info; |
+ const _PartContainedExcludedPackage(this.part, this.package, this.info); |
+ |
+ String toString() { |
+ return 'Part "$part" was specified to exclude package "$package" but it ' |
+ 'actually contains ${kindToString(info.kind)} "${info.name}" which ' |
+ 'is from package "$package"'; |
+ } |
+} |
+ |
+class _PartDidNotContainPackage extends ManifestComplianceFailure { |
+ final String part; |
+ final String package; |
+ const _PartDidNotContainPackage(this.part, this.package); |
+ |
+ String toString() { |
+ return 'Part "$part" was specified to include package "$package" but it ' |
+ 'does not contain any elements from that package.'; |
} |
} |