| 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 |