OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, 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 /// Analyse a directory structure and find packages resolvers for each |
| 6 /// sub-directory. |
| 7 /// |
| 8 /// The resolvers are generally the same that would be found by using |
| 9 /// the `discovery.dart` library on each sub-directory in turn, |
| 10 /// but more efficiently and with some heuristics for directories that |
| 11 /// wouldn't otherwise have a package resolution strategy, or that are |
| 12 /// determined to be "package directories" themselves. |
| 13 library package_config.discovery_analysis; |
| 14 |
| 15 import "dart:io" show File, Directory; |
| 16 import "dart:collection" show HashMap; |
| 17 |
| 18 import "package:path/path.dart" as path; |
| 19 |
| 20 import "packages.dart"; |
| 21 import "packages_file.dart" as pkgfile; |
| 22 import "src/packages_impl.dart"; |
| 23 import "src/packages_io_impl.dart"; |
| 24 |
| 25 /// Associates a [Packages] package resolution strategy with a directory. |
| 26 /// |
| 27 /// The package resolution applies to the directory and any sub-directory |
| 28 /// that doesn't have its own overriding child [PackageContext]. |
| 29 abstract class PackageContext { |
| 30 /// The directory that introduced the [packages] resolver. |
| 31 Directory get directory; |
| 32 |
| 33 /// A [Packages] resolver that applies to the directory. |
| 34 /// |
| 35 /// Introduced either by a `.packages` file or a `packages/` directory. |
| 36 Packages get packages; |
| 37 |
| 38 /// Child contexts that apply to sub-directories of [directory]. |
| 39 List<PackageContext> get children; |
| 40 |
| 41 /// Look up the [PackageContext] that applies to a specific directory. |
| 42 /// |
| 43 /// The directory must be inside [directory]. |
| 44 PackageContext operator[](Directory directory); |
| 45 |
| 46 /// A map from directory to package resolver. |
| 47 /// |
| 48 /// Has an entry for this package context and for each child context |
| 49 /// contained in this one. |
| 50 Map<Directory, Packages> asMap(); |
| 51 |
| 52 /// Analyze [directory] and sub-directories for package resolution strategies. |
| 53 /// |
| 54 /// Returns a mapping from sub-directories to [Packages] objects. |
| 55 /// |
| 56 /// The analysis assumes that there are no `.packages` files in a parent |
| 57 /// directory of `directory`. If there is, its corresponding `Packages` object |
| 58 /// should be provided as `root`. |
| 59 static PackageContext findAll(Directory directory, |
| 60 {Packages root: Packages.noPackages}) { |
| 61 if (!directory.existsSync()) { |
| 62 throw new ArgumentError("Directory not found: $directory"); |
| 63 } |
| 64 List contexts = []; |
| 65 void findRoots(Directory directory) { |
| 66 Packages packages; |
| 67 List oldContexts; |
| 68 File packagesFile = new File(path.join(directory.path, ".packages")); |
| 69 if (packagesFile.existsSync()) { |
| 70 packages = _loadPackagesFile(packagesFile); |
| 71 oldContexts = contexts; |
| 72 contexts = []; |
| 73 } else { |
| 74 Directory packagesDir = |
| 75 new Directory(path.join(directory.path, "packages")); |
| 76 if (packagesDir.existsSync()) { |
| 77 packages = new FilePackagesDirectoryPackages(packagesDir); |
| 78 oldContexts = contexts; |
| 79 contexts = []; |
| 80 } |
| 81 } |
| 82 for (var entry in directory.listSync()) { |
| 83 if (entry is Directory) { |
| 84 if (packages == null || !entry.path.endsWith("/packages")) { |
| 85 findRoots(entry); |
| 86 } |
| 87 } |
| 88 } |
| 89 if (packages != null) { |
| 90 oldContexts.add(new _PackageContext(directory, packages, contexts)); |
| 91 contexts = oldContexts; |
| 92 } |
| 93 } |
| 94 findRoots(directory); |
| 95 // If the root is not itself context root, add a the wrapper context. |
| 96 if (contexts.length == 1 && |
| 97 contexts[0].directory == directory) { |
| 98 return contexts[0]; |
| 99 } |
| 100 return new _PackageContext(directory, root, contexts); |
| 101 } |
| 102 } |
| 103 |
| 104 class _PackageContext implements PackageContext { |
| 105 final Directory directory; |
| 106 final Packages packages; |
| 107 final List<PackageContext> children; |
| 108 _PackageContext(this.directory, this.packages, List<PackageContext> children) |
| 109 : children = new List<PackageContext>.unmodifiable(children); |
| 110 |
| 111 Map<Directory, Packages> asMap() { |
| 112 var result = new HashMap<Directory, Packages>(); |
| 113 recurse(_PackageContext current) { |
| 114 result[current.directory] = current.packages; |
| 115 for (var child in current.children) { |
| 116 recurse(child); |
| 117 } |
| 118 } |
| 119 recurse(this); |
| 120 return result; |
| 121 } |
| 122 |
| 123 PackageContext operator[](Directory directory) { |
| 124 String path = directory.path; |
| 125 if (!path.startsWith(this.directory.path)) { |
| 126 throw new ArgumentError("Not inside $path: $directory"); |
| 127 } |
| 128 _PackageContext current = this; |
| 129 // The current path is know to agree with directory until deltaIndex. |
| 130 int deltaIndex = current.directory.path.length; |
| 131 List children = current.children; |
| 132 int i = 0; |
| 133 while (i < children.length) { |
| 134 // TODO(lrn): Sort children and use binary search. |
| 135 _PackageContext child = children[i]; |
| 136 String childPath = child.directory.path; |
| 137 if (_stringsAgree(path, childPath, deltaIndex, childPath.length)) { |
| 138 deltaIndex = childPath.length; |
| 139 if (deltaIndex == path.length) { |
| 140 return child; |
| 141 } |
| 142 current = child; |
| 143 children = current.children; |
| 144 i = 0; |
| 145 continue; |
| 146 } |
| 147 i++; |
| 148 } |
| 149 return current; |
| 150 } |
| 151 |
| 152 static bool _stringsAgree(String a, String b, int start, int end) { |
| 153 if (a.length < end || b.length < end) return false; |
| 154 for (int i = start; i < end; i++) { |
| 155 if (a.codeUnitAt(i) != b.codeUnitAt(i)) return false; |
| 156 } |
| 157 return true; |
| 158 } |
| 159 } |
| 160 |
| 161 Packages _loadPackagesFile(File file) { |
| 162 var uri = new Uri.file(file.path); |
| 163 var bytes = file.readAsBytesSync(); |
| 164 var map = pkgfile.parse(bytes, uri); |
| 165 return new MapPackages(map); |
| 166 } |
OLD | NEW |