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 |