OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library pub.entrypoint; | 5 library pub.entrypoint; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | |
8 | 9 |
9 import 'package:path/path.dart' as path; | 10 import 'package:path/path.dart' as path; |
10 import 'package:barback/barback.dart'; | 11 import 'package:barback/barback.dart'; |
11 | 12 |
12 import 'barback/asset_environment.dart'; | 13 import 'barback/asset_environment.dart'; |
13 import 'io.dart'; | 14 import 'io.dart'; |
14 import 'lock_file.dart'; | 15 import 'lock_file.dart'; |
15 import 'log.dart' as log; | 16 import 'log.dart' as log; |
16 import 'package.dart'; | 17 import 'package.dart'; |
17 import 'package_graph.dart'; | 18 import 'package_graph.dart'; |
(...skipping 25 matching lines...) Expand all Loading... | |
43 final Package root; | 44 final Package root; |
44 | 45 |
45 /// The system-wide cache which caches packages that need to be fetched over | 46 /// The system-wide cache which caches packages that need to be fetched over |
46 /// the network. | 47 /// the network. |
47 final SystemCache cache; | 48 final SystemCache cache; |
48 | 49 |
49 /// Whether to create and symlink a "packages" directory containing links to | 50 /// Whether to create and symlink a "packages" directory containing links to |
50 /// the installed packages. | 51 /// the installed packages. |
51 final bool _packageSymlinks; | 52 final bool _packageSymlinks; |
52 | 53 |
54 /// Whether this entrypoing is in memory only, as opposed to representing a | |
Bob Nystrom
2015/08/10 21:54:28
"entrypoint"
nweiz
2015/08/10 22:32:47
Done.
| |
55 /// real directory on disk. | |
56 final bool _inMemory; | |
57 | |
53 /// The lockfile for the entrypoint. | 58 /// The lockfile for the entrypoint. |
54 /// | 59 /// |
55 /// If not provided to the entrypoint, it will be laoded lazily from disc. | 60 /// If not provided to the entrypoint, it will be loaded lazily from disk. |
56 LockFile _lockFile; | |
57 | |
58 /// The graph of all packages reachable from the entrypoint. | |
59 PackageGraph _packageGraph; | |
60 | |
61 /// Loads the entrypoint from a package at [rootDir]. | |
62 /// | |
63 /// If [packageSymlinks] is `true`, this will create a "packages" directory | |
64 /// with symlinks to the installed packages. This directory will be symlinked | |
65 /// into any directory that might contain an entrypoint. | |
66 Entrypoint(String rootDir, SystemCache cache, {bool packageSymlinks: true}) | |
67 : root = new Package.load(null, rootDir, cache.sources), | |
68 cache = cache, | |
69 _packageSymlinks = packageSymlinks; | |
70 | |
71 /// Creates an entrypoint given package and lockfile objects. | |
72 Entrypoint.inMemory(this.root, this._lockFile, this.cache) | |
73 : _packageSymlinks = false; | |
74 | |
75 /// The path to the entrypoint's "packages" directory. | |
76 String get packagesDir => root.path('packages'); | |
77 | |
78 /// The path to the entrypoint's ".packages" file. | |
79 String get packagesFile => root.path('.packages'); | |
80 | |
81 /// `true` if the entrypoint package currently has a lock file. | |
82 bool get lockFileExists => _lockFile != null || entryExists(lockFilePath); | |
83 | |
84 LockFile get lockFile { | 61 LockFile get lockFile { |
85 if (_lockFile != null) return _lockFile; | 62 if (_lockFile != null) return _lockFile; |
86 | 63 |
87 if (!lockFileExists) { | 64 if (!entryExists(lockFilePath)) { |
Bob Nystrom
2015/08/10 21:54:28
fileExists()? I suppose it could be a link, but a
nweiz
2015/08/10 22:32:47
Done.
| |
88 _lockFile = new LockFile.empty(cache.sources); | 65 _lockFile = new LockFile.empty(cache.sources); |
89 } else { | 66 } else { |
90 _lockFile = new LockFile.load(lockFilePath, cache.sources); | 67 _lockFile = new LockFile.load(lockFilePath, cache.sources); |
91 } | 68 } |
92 | 69 |
93 return _lockFile; | 70 return _lockFile; |
94 } | 71 } |
72 LockFile _lockFile; | |
73 | |
74 /// The package graph for the application and all of its transitive | |
75 /// dependencies. | |
76 /// | |
77 /// Throws a [DataError] if the `.packages` file isn't up-to-date relative to | |
78 /// the pubspec and the lockfile. | |
79 PackageGraph get packageGraph { | |
80 if (_packageGraph != null) return _packageGraph; | |
81 | |
82 assertUpToDate(); | |
83 var packages = new Map.fromIterable(lockFile.packages.values, | |
84 key: (id) => id.name, | |
85 value: (id) { | |
86 var dir = cache.sources[id.source].getDirectory(id); | |
87 return new Package.load(id.name, dir, cache.sources); | |
88 }); | |
89 packages[root.name] = root; | |
90 | |
91 _packageGraph = new PackageGraph(this, lockFile, packages); | |
92 return _packageGraph; | |
93 } | |
94 PackageGraph _packageGraph; | |
95 | |
96 /// The path to the entrypoint's "packages" directory. | |
97 String get packagesDir => root.path('packages'); | |
98 | |
99 /// The path to the entrypoint's ".packages" file. | |
100 String get packagesFile => root.path('.packages'); | |
95 | 101 |
96 /// The path to the entrypoint package's pubspec. | 102 /// The path to the entrypoint package's pubspec. |
97 String get pubspecPath => root.path('pubspec.yaml'); | 103 String get pubspecPath => root.path('pubspec.yaml'); |
98 | 104 |
99 /// The path to the entrypoint package's lockfile. | 105 /// The path to the entrypoint package's lockfile. |
100 String get lockFilePath => root.path('pubspec.lock'); | 106 String get lockFilePath => root.path('pubspec.lock'); |
101 | 107 |
108 /// Loads the entrypoint from a package at [rootDir]. | |
109 /// | |
110 /// If [packageSymlinks] is `true`, this will create a "packages" directory | |
111 /// with symlinks to the installed packages. This directory will be symlinked | |
112 /// into any directory that might contain an entrypoint. | |
113 Entrypoint(String rootDir, SystemCache cache, {bool packageSymlinks: true}) | |
114 : root = new Package.load(null, rootDir, cache.sources), | |
115 cache = cache, | |
116 _packageSymlinks = packageSymlinks, | |
117 _inMemory = false; | |
118 | |
119 /// Creates an entrypoint given package and lockfile objects. | |
120 Entrypoint.inMemory(this.root, this._lockFile, this.cache) | |
121 : _packageSymlinks = false, | |
122 _inMemory = true; | |
123 | |
124 /// Creates an entrypoint given a package and a [solveResult], from which the | |
125 /// package graph and lockfile will be computed. | |
126 Entrypoint.fromSolveResult(this.root, this.cache, SolveResult solveResult) | |
127 : _packageSymlinks = false, | |
128 _inMemory = true { | |
129 _packageGraph = new PackageGraph.fromSolveResult(this, solveResult); | |
130 _lockFile = _packageGraph.lockFile; | |
131 } | |
132 | |
102 /// Gets all dependencies of the [root] package. | 133 /// Gets all dependencies of the [root] package. |
103 /// | 134 /// |
104 /// Performs version resolution according to [SolveType]. | 135 /// Performs version resolution according to [SolveType]. |
105 /// | 136 /// |
106 /// [useLatest], if provided, defines a list of packages that will be | 137 /// [useLatest], if provided, defines a list of packages that will be |
107 /// unlocked and forced to their latest versions. If [upgradeAll] is | 138 /// unlocked and forced to their latest versions. If [upgradeAll] is |
108 /// true, the previous lockfile is ignored and all packages are re-resolved | 139 /// true, the previous lockfile is ignored and all packages are re-resolved |
109 /// from scratch. Otherwise, it will attempt to preserve the versions of all | 140 /// from scratch. Otherwise, it will attempt to preserve the versions of all |
110 /// previously locked packages. | 141 /// previously locked packages. |
111 /// | 142 /// |
112 /// Shows a report of the changes made relative to the previous lockfile. If | 143 /// Shows a report of the changes made relative to the previous lockfile. If |
113 /// this is an upgrade or downgrade, all transitive dependencies are shown in | 144 /// this is an upgrade or downgrade, all transitive dependencies are shown in |
114 /// the report. Otherwise, only dependencies that were changed are shown. If | 145 /// the report. Otherwise, only dependencies that were changed are shown. If |
115 /// [dryRun] is `true`, no physical changes are made. | 146 /// [dryRun] is `true`, no physical changes are made. |
147 /// | |
148 /// Updates [lockFile] and [packageRoot] accordingly. | |
116 Future acquireDependencies(SolveType type, {List<String> useLatest, | 149 Future acquireDependencies(SolveType type, {List<String> useLatest, |
117 bool dryRun: false}) async { | 150 bool dryRun: false}) async { |
118 var result = await resolveVersions(type, cache.sources, root, | 151 var result = await resolveVersions(type, cache.sources, root, |
119 lockFile: lockFile, useLatest: useLatest); | 152 lockFile: lockFile, useLatest: useLatest); |
120 if (!result.succeeded) throw result.error; | 153 if (!result.succeeded) throw result.error; |
121 | 154 |
122 result.showReport(type); | 155 result.showReport(type); |
123 | 156 |
124 if (dryRun) { | 157 if (dryRun) { |
125 result.summarizeChanges(type, dryRun: dryRun); | 158 result.summarizeChanges(type, dryRun: dryRun); |
(...skipping 10 matching lines...) Expand all Loading... | |
136 var ids = await Future.wait(result.packages.map(_get)); | 169 var ids = await Future.wait(result.packages.map(_get)); |
137 _saveLockFile(ids); | 170 _saveLockFile(ids); |
138 | 171 |
139 if (_packageSymlinks) _linkSelf(); | 172 if (_packageSymlinks) _linkSelf(); |
140 _linkOrDeleteSecondaryPackageDirs(); | 173 _linkOrDeleteSecondaryPackageDirs(); |
141 | 174 |
142 result.summarizeChanges(type, dryRun: dryRun); | 175 result.summarizeChanges(type, dryRun: dryRun); |
143 | 176 |
144 /// Build a package graph from the version solver results so we don't | 177 /// Build a package graph from the version solver results so we don't |
145 /// have to reload and reparse all the pubspecs. | 178 /// have to reload and reparse all the pubspecs. |
146 var packageGraph = await loadPackageGraph(result); | 179 _packageGraph = new PackageGraph.fromSolveResult(this, result); |
147 packageGraph.loadTransformerCache().clearIfOutdated(result.changedPackages); | 180 packageGraph.loadTransformerCache().clearIfOutdated(result.changedPackages); |
148 | 181 |
149 try { | 182 try { |
150 await precompileDependencies(changed: result.changedPackages); | 183 await precompileDependencies(changed: result.changedPackages); |
151 await precompileExecutables(changed: result.changedPackages); | 184 await precompileExecutables(changed: result.changedPackages); |
152 } catch (error, stackTrace) { | 185 } catch (error, stackTrace) { |
153 // Just log exceptions here. Since the method is just about acquiring | 186 // Just log exceptions here. Since the method is just about acquiring |
154 // dependencies, it shouldn't fail unless that fails. | 187 // dependencies, it shouldn't fail unless that fails. |
155 log.exception(error, stackTrace); | 188 log.exception(error, stackTrace); |
156 } | 189 } |
157 | 190 |
158 writeTextFile(packagesFile, lockFile.packagesFile(root.name)); | 191 writeTextFile(packagesFile, lockFile.packagesFile(root.name)); |
159 } | 192 } |
160 | 193 |
161 /// Precompile any transformed dependencies of the entrypoint. | 194 /// Precompile any transformed dependencies of the entrypoint. |
162 /// | 195 /// |
163 /// If [changed] is passed, only dependencies whose contents might be changed | 196 /// If [changed] is passed, only dependencies whose contents might be changed |
164 /// if one of the given packages changes will be recompiled. | 197 /// if one of the given packages changes will be recompiled. |
165 Future precompileDependencies({Iterable<String> changed}) async { | 198 Future precompileDependencies({Iterable<String> changed}) async { |
166 if (changed != null) changed = changed.toSet(); | 199 if (changed != null) changed = changed.toSet(); |
167 | 200 |
168 var graph = await loadPackageGraph(); | |
169 | |
170 // Just precompile the debug version of a package. We're mostly interested | 201 // Just precompile the debug version of a package. We're mostly interested |
171 // in improving speed for development iteration loops, which usually use | 202 // in improving speed for development iteration loops, which usually use |
172 // debug mode. | 203 // debug mode. |
173 var depsDir = path.join('.pub', 'deps', 'debug'); | 204 var depsDir = path.join('.pub', 'deps', 'debug'); |
174 | 205 |
175 var dependenciesToPrecompile = graph.packages.values.where((package) { | 206 var dependenciesToPrecompile = packageGraph.packages.values |
207 .where((package) { | |
176 if (package.pubspec.transformers.isEmpty) return false; | 208 if (package.pubspec.transformers.isEmpty) return false; |
177 if (graph.isPackageMutable(package.name)) return false; | 209 if (packageGraph.isPackageMutable(package.name)) return false; |
178 if (!dirExists(path.join(depsDir, package.name))) return true; | 210 if (!dirExists(path.join(depsDir, package.name))) return true; |
179 if (changed == null) return true; | 211 if (changed == null) return true; |
180 | 212 |
181 /// Only recompile [package] if any of its transitive dependencies have | 213 /// Only recompile [package] if any of its transitive dependencies have |
182 /// changed. We check all transitive dependencies because it's possible | 214 /// changed. We check all transitive dependencies because it's possible |
183 /// that a transformer makes decisions based on their contents. | 215 /// that a transformer makes decisions based on their contents. |
184 return overlaps( | 216 return overlaps( |
185 graph.transitiveDependencies(package.name) | 217 packageGraph.transitiveDependencies(package.name) |
186 .map((package) => package.name).toSet(), | 218 .map((package) => package.name).toSet(), |
187 changed); | 219 changed); |
188 }).map((package) => package.name).toSet(); | 220 }).map((package) => package.name).toSet(); |
189 | 221 |
190 if (dirExists(depsDir)) { | 222 if (dirExists(depsDir)) { |
191 // Delete any cached dependencies that are going to be recached. | 223 // Delete any cached dependencies that are going to be recached. |
192 for (var package in dependenciesToPrecompile) { | 224 for (var package in dependenciesToPrecompile) { |
193 deleteEntry(path.join(depsDir, package)); | 225 deleteEntry(path.join(depsDir, package)); |
194 } | 226 } |
195 | 227 |
196 // Also delete any cached dependencies that should no longer be cached. | 228 // Also delete any cached dependencies that should no longer be cached. |
197 for (var subdir in listDir(depsDir)) { | 229 for (var subdir in listDir(depsDir)) { |
198 var package = graph.packages[path.basename(subdir)]; | 230 var package = packageGraph.packages[path.basename(subdir)]; |
199 if (package == null || package.pubspec.transformers.isEmpty || | 231 if (package == null || package.pubspec.transformers.isEmpty || |
200 graph.isPackageMutable(package.name)) { | 232 packageGraph.isPackageMutable(package.name)) { |
201 deleteEntry(subdir); | 233 deleteEntry(subdir); |
202 } | 234 } |
203 } | 235 } |
204 } | 236 } |
205 | 237 |
206 if (dependenciesToPrecompile.isEmpty) return; | 238 if (dependenciesToPrecompile.isEmpty) return; |
207 | 239 |
208 try { | 240 try { |
209 await log.progress("Precompiling dependencies", () async { | 241 await log.progress("Precompiling dependencies", () async { |
210 var packagesToLoad = | 242 var packagesToLoad = |
211 unionAll(dependenciesToPrecompile.map(graph.transitiveDependencies)) | 243 unionAll(dependenciesToPrecompile.map( |
244 packageGraph.transitiveDependencies)) | |
212 .map((package) => package.name).toSet(); | 245 .map((package) => package.name).toSet(); |
213 | 246 |
214 var environment = await AssetEnvironment.create(this, BarbackMode.DEBUG, | 247 var environment = await AssetEnvironment.create(this, BarbackMode.DEBUG, |
215 packages: packagesToLoad, useDart2JS: false); | 248 packages: packagesToLoad, useDart2JS: false); |
216 | 249 |
217 /// Ignore barback errors since they'll be emitted via [getAllAssets] | 250 /// Ignore barback errors since they'll be emitted via [getAllAssets] |
218 /// below. | 251 /// below. |
219 environment.barback.errors.listen((_) {}); | 252 environment.barback.errors.listen((_) {}); |
220 | 253 |
221 // TODO(nweiz): only get assets from [dependenciesToPrecompile] so as | 254 // TODO(nweiz): only get assets from [dependenciesToPrecompile] so as |
(...skipping 30 matching lines...) Expand all Loading... | |
252 var binDir = path.join('.pub', 'bin'); | 285 var binDir = path.join('.pub', 'bin'); |
253 var sdkVersionPath = path.join(binDir, 'sdk-version'); | 286 var sdkVersionPath = path.join(binDir, 'sdk-version'); |
254 | 287 |
255 // If the existing executable was compiled with a different SDK, we need to | 288 // If the existing executable was compiled with a different SDK, we need to |
256 // recompile regardless of what changed. | 289 // recompile regardless of what changed. |
257 // TODO(nweiz): Use the VM to check this when issue 20802 is fixed. | 290 // TODO(nweiz): Use the VM to check this when issue 20802 is fixed. |
258 var sdkMatches = fileExists(sdkVersionPath) && | 291 var sdkMatches = fileExists(sdkVersionPath) && |
259 readTextFile(sdkVersionPath) == "${sdk.version}\n"; | 292 readTextFile(sdkVersionPath) == "${sdk.version}\n"; |
260 if (!sdkMatches) changed = null; | 293 if (!sdkMatches) changed = null; |
261 | 294 |
262 var graph = await loadPackageGraph(); | |
263 | |
264 // Clean out any outdated snapshots. | 295 // Clean out any outdated snapshots. |
265 if (dirExists(binDir)) { | 296 if (dirExists(binDir)) { |
266 for (var entry in listDir(binDir)) { | 297 for (var entry in listDir(binDir)) { |
267 if (!dirExists(entry)) continue; | 298 if (!dirExists(entry)) continue; |
268 | 299 |
269 var package = path.basename(entry); | 300 var package = path.basename(entry); |
270 if (!graph.packages.containsKey(package) || | 301 if (!packageGraph.packages.containsKey(package) || |
271 graph.isPackageMutable(package)) { | 302 packageGraph.isPackageMutable(package)) { |
272 deleteEntry(entry); | 303 deleteEntry(entry); |
273 } | 304 } |
274 } | 305 } |
275 } | 306 } |
276 | 307 |
277 var executables = new Map.fromIterable(root.immediateDependencies, | 308 var executables = new Map.fromIterable(root.immediateDependencies, |
278 key: (dep) => dep.name, | 309 key: (dep) => dep.name, |
279 value: (dep) => _executablesForPackage(graph, dep.name, changed)); | 310 value: (dep) => _executablesForPackage(dep.name, changed)); |
280 | 311 |
281 for (var package in executables.keys.toList()) { | 312 for (var package in executables.keys.toList()) { |
282 if (executables[package].isEmpty) executables.remove(package); | 313 if (executables[package].isEmpty) executables.remove(package); |
283 } | 314 } |
284 | 315 |
285 if (!sdkMatches) deleteEntry(binDir); | 316 if (!sdkMatches) deleteEntry(binDir); |
286 if (executables.isEmpty) return; | 317 if (executables.isEmpty) return; |
287 | 318 |
288 await log.progress("Precompiling executables", () async { | 319 await log.progress("Precompiling executables", () async { |
289 ensureDir(binDir); | 320 ensureDir(binDir); |
290 | 321 |
291 // Make sure there's a trailing newline so our version file matches the | 322 // Make sure there's a trailing newline so our version file matches the |
292 // SDK's. | 323 // SDK's. |
293 writeTextFile(sdkVersionPath, "${sdk.version}\n"); | 324 writeTextFile(sdkVersionPath, "${sdk.version}\n"); |
294 | 325 |
295 var packagesToLoad = | 326 var packagesToLoad = |
296 unionAll(executables.keys.map(graph.transitiveDependencies)) | 327 unionAll(executables.keys.map(packageGraph.transitiveDependencies)) |
297 .map((package) => package.name).toSet(); | 328 .map((package) => package.name).toSet(); |
298 var executableIds = unionAll( | 329 var executableIds = unionAll( |
299 executables.values.map((ids) => ids.toSet())); | 330 executables.values.map((ids) => ids.toSet())); |
300 var environment = await AssetEnvironment.create(this, BarbackMode.RELEASE, | 331 var environment = await AssetEnvironment.create(this, BarbackMode.RELEASE, |
301 packages: packagesToLoad, | 332 packages: packagesToLoad, |
302 entrypoints: executableIds, | 333 entrypoints: executableIds, |
303 useDart2JS: false); | 334 useDart2JS: false); |
304 environment.barback.errors.listen((error) { | 335 environment.barback.errors.listen((error) { |
305 log.error(log.red("Build error:\n$error")); | 336 log.error(log.red("Build error:\n$error")); |
306 }); | 337 }); |
307 | 338 |
308 await waitAndPrintErrors(executables.keys.map((package) async { | 339 await waitAndPrintErrors(executables.keys.map((package) async { |
309 var dir = path.join(binDir, package); | 340 var dir = path.join(binDir, package); |
310 cleanDir(dir); | 341 cleanDir(dir); |
311 await environment.precompileExecutables(package, dir, | 342 await environment.precompileExecutables(package, dir, |
312 executableIds: executables[package]); | 343 executableIds: executables[package]); |
313 })); | 344 })); |
314 }); | 345 }); |
315 } | 346 } |
316 | 347 |
317 /// Returns the list of all executable assets for [packageName] that should be | 348 /// Returns the list of all executable assets for [packageName] that should be |
318 /// precompiled. | 349 /// precompiled. |
319 /// | 350 /// |
320 /// If [changed] isn't `null`, executables for [packageName] will only be | 351 /// If [changed] isn't `null`, executables for [packageName] will only be |
321 /// compiled if they might depend on a package in [changed]. | 352 /// compiled if they might depend on a package in [changed]. |
322 List<AssetId> _executablesForPackage(PackageGraph graph, String packageName, | 353 List<AssetId> _executablesForPackage(String packageName, |
323 Set<String> changed) { | 354 Set<String> changed) { |
Bob Nystrom
2015/08/10 21:54:28
Does this fit on the previous line now?
| |
324 var package = graph.packages[packageName]; | 355 var package = packageGraph.packages[packageName]; |
325 var binDir = package.path('bin'); | 356 var binDir = package.path('bin'); |
326 if (!dirExists(binDir)) return []; | 357 if (!dirExists(binDir)) return []; |
327 if (graph.isPackageMutable(packageName)) return []; | 358 if (packageGraph.isPackageMutable(packageName)) return []; |
328 | 359 |
329 var executables = package.executableIds; | 360 var executables = package.executableIds; |
330 | 361 |
331 // If we don't know which packages were changed, always precompile the | 362 // If we don't know which packages were changed, always precompile the |
332 // executables. | 363 // executables. |
333 if (changed == null) return executables; | 364 if (changed == null) return executables; |
334 | 365 |
335 // If any of the package's dependencies changed, recompile the executables. | 366 // If any of the package's dependencies changed, recompile the executables. |
336 if (graph.transitiveDependencies(packageName) | 367 if (packageGraph.transitiveDependencies(packageName) |
337 .any((package) => changed.contains(package.name))) { | 368 .any((package) => changed.contains(package.name))) { |
338 return executables; | 369 return executables; |
339 } | 370 } |
340 | 371 |
341 // If any executables don't exist, precompile them regardless of what | 372 // If any executables don't exist, precompile them regardless of what |
342 // changed. Since we delete the bin directory before recompiling, we need to | 373 // changed. Since we delete the bin directory before recompiling, we need to |
343 // recompile all executables. | 374 // recompile all executables. |
344 var executablesExist = executables.every((executable) => | 375 var executablesExist = executables.every((executable) => |
345 fileExists(path.join('.pub', 'bin', packageName, | 376 fileExists(path.join('.pub', 'bin', packageName, |
346 "${path.url.basename(executable.path)}.snapshot"))); | 377 "${path.url.basename(executable.path)}.snapshot"))); |
(...skipping 17 matching lines...) Expand all Loading... | |
364 if (source is! CachedSource) return null; | 395 if (source is! CachedSource) return null; |
365 return source.downloadToSystemCache(id); | 396 return source.downloadToSystemCache(id); |
366 } | 397 } |
367 | 398 |
368 var packageDir = path.join(packagesDir, id.name); | 399 var packageDir = path.join(packagesDir, id.name); |
369 if (entryExists(packageDir)) deleteEntry(packageDir); | 400 if (entryExists(packageDir)) deleteEntry(packageDir); |
370 return source.get(id, packageDir); | 401 return source.get(id, packageDir); |
371 }).then((_) => source.resolveId(id)); | 402 }).then((_) => source.resolveId(id)); |
372 } | 403 } |
373 | 404 |
374 /// Determines whether or not the lockfile is out of date with respect to the | 405 /// Throws a [DataError] if the `.packages` file doesn't exist or if it's |
375 /// pubspec. | 406 /// out-of-date relative to the lockfile or the pubspec. |
376 /// | 407 void assertUpToDate() { |
377 /// This will be `false` if there is no lockfile at all, or if the pubspec | 408 if (_inMemory) return; |
378 /// contains dependencies that are not in the lockfile or that don't match | |
379 /// what's in there. | |
380 bool _isLockFileUpToDate(LockFile lockFile) { | |
381 /// If this is an entrypoint for an in-memory package, trust the in-memory | |
382 /// lockfile provided for it. | |
383 if (root.dir == null) return true; | |
384 | 409 |
385 return root.immediateDependencies.every((package) { | 410 if (!entryExists(lockFilePath)) { |
386 var locked = lockFile.packages[package.name]; | 411 dataError('No pubspec.lock file found, please run "pub get" first.'); |
387 if (locked == null) return false; | |
388 | |
389 if (package.source != locked.source) return false; | |
390 if (!package.constraint.allows(locked.version)) return false; | |
391 | |
392 var source = cache.sources[package.source]; | |
393 if (source == null) return false; | |
394 | |
395 return source.descriptionsEqual(package.description, locked.description); | |
396 }); | |
397 } | |
398 | |
399 /// Determines whether all of the packages in the lockfile are already | |
400 /// installed and available. | |
401 /// | |
402 /// Note: this assumes [isLockFileUpToDate] has already been called and | |
403 /// returned `true`. | |
404 bool _arePackagesAvailable(LockFile lockFile) { | |
405 return lockFile.packages.values.every((package) { | |
406 var source = cache.sources[package.source]; | |
407 | |
408 // This should only be called after [_isLockFileUpToDate] has returned | |
409 // `true`, which ensures all of the sources in the lock file are valid. | |
410 assert(source != null); | |
411 | |
412 // We only care about cached sources. Uncached sources aren't "installed". | |
413 // If one of those is missing, we want to show the user the file not | |
414 // found error later since installing won't accomplish anything. | |
415 if (source is! CachedSource) return true; | |
416 | |
417 // Get the directory. | |
418 var dir = source.getDirectory(package); | |
419 // See if the directory is there and looks like a package. | |
420 return dirExists(dir) || fileExists(path.join(dir, "pubspec.yaml")); | |
421 }); | |
422 } | |
423 | |
424 /// Gets dependencies if the lockfile is out of date with respect to the | |
425 /// pubspec. | |
426 Future ensureLockFileIsUpToDate() async { | |
427 if (!lockFileExists) { | |
428 log.message( | |
429 "You don't have a lockfile, so we need to generate that:"); | |
430 } else if (_isLockFileUpToDate(lockFile)) { | |
431 // If we do have a lock file, we still need to make sure the packages are | |
432 // actually installed. The user may have just gotten a package that | |
433 // includes a lockfile. | |
434 if (_arePackagesAvailable(lockFile)) return; | |
435 | |
436 // If we don't have a current lock file, we definitely need to install. | |
437 log.message( | |
438 "You are missing some dependencies, so we need to install them " | |
439 "first:"); | |
440 } else { | |
441 log.message( | |
442 "Your pubspec has changed, so we need to update your lockfile:"); | |
443 } | 412 } |
444 | 413 |
445 await acquireDependencies(SolveType.GET); | 414 if (!entryExists(packagesFile)) { |
446 } | 415 dataError('No .packages file found, please run "pub get" first.'); |
Bob Nystrom
2015/08/10 21:54:28
We should definitely figure out how this should be
nweiz
2015/08/10 22:32:46
I was planning to send out an email on dart-announ
| |
416 } | |
447 | 417 |
448 /// Loads the package graph for the application and all of its transitive | 418 var packagesMtime = new File(packagesFile).lastModifiedSync(); |
Bob Nystrom
2015/08/10 21:54:28
"Mtime" is a bit weird (it should at least be "MTi
nweiz
2015/08/10 22:32:47
Done.
| |
449 /// dependencies. | 419 var pubspecMtime = new File(pubspecPath).lastModifiedSync(); |
450 /// | 420 if (packagesMtime.isBefore(pubspecMtime)) { |
451 /// If [result] is passed, this loads the graph from it without re-parsing the | 421 dataError('The pubspec.yaml file has changed since the .packages file ' |
452 /// lockfile or any pubspecs. Otherwise, before loading, this makes sure the | 422 'was generated, please run "pub get" again.'); |
453 /// lockfile and dependencies are installed and up to date. | 423 } |
454 Future<PackageGraph> loadPackageGraph([SolveResult result]) async { | |
455 if (_packageGraph != null) return _packageGraph; | |
456 | 424 |
457 var graph = await log.progress("Loading package graph", () async { | 425 var lockFileMtime = new File(lockFilePath).lastModifiedSync(); |
458 if (result != null) { | 426 if (packagesMtime.isBefore(lockFileMtime)) { |
459 var packages = new Map.fromIterable(result.packages, | 427 dataError('The pubspec.lock file has changed since the .packages file ' |
460 key: (id) => id.name, | 428 'was generated, please run "pub get" again.'); |
461 value: (id) { | 429 } |
462 if (id.name == root.name) return root; | |
463 | |
464 return new Package(result.pubspecs[id.name], | |
465 cache.sources[id.source].getDirectory(id)); | |
466 }); | |
467 | |
468 return new PackageGraph( | |
469 this, | |
470 new LockFile(result.packages, cache.sources), | |
471 packages); | |
472 } | |
473 | |
474 await ensureLockFileIsUpToDate(); | |
475 var packages = new Map.fromIterable(lockFile.packages.values, | |
476 key: (id) => id.name, | |
477 value: (id) { | |
478 var dir = cache.sources[id.source].getDirectory(id); | |
479 return new Package.load(id.name, dir, cache.sources); | |
480 }); | |
481 packages[root.name] = root; | |
482 return new PackageGraph(this, lockFile, packages); | |
483 }, fine: true); | |
484 | |
485 _packageGraph = graph; | |
486 return graph; | |
487 } | 430 } |
488 | 431 |
489 /// Saves a list of concrete package versions to the `pubspec.lock` file. | 432 /// Saves a list of concrete package versions to the `pubspec.lock` file. |
490 void _saveLockFile(List<PackageId> packageIds) { | 433 void _saveLockFile(List<PackageId> packageIds) { |
491 _lockFile = new LockFile(packageIds, cache.sources); | 434 _lockFile = new LockFile(packageIds, cache.sources); |
492 var lockFilePath = root.path('pubspec.lock'); | 435 var lockFilePath = root.path('pubspec.lock'); |
493 writeTextFile(lockFilePath, _lockFile.serialize(root.dir)); | 436 writeTextFile(lockFilePath, _lockFile.serialize(root.dir)); |
494 } | 437 } |
495 | 438 |
496 /// Creates a self-referential symlink in the `packages` directory that allows | 439 /// Creates a self-referential symlink in the `packages` directory that allows |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
550 /// If [packageSymlinks] is true, creates a symlink to the "packages" | 493 /// If [packageSymlinks] is true, creates a symlink to the "packages" |
551 /// directory in [dir]. | 494 /// directory in [dir]. |
552 /// | 495 /// |
553 /// Otherwise, deletes a "packages" directories in [dir] if one exists. | 496 /// Otherwise, deletes a "packages" directories in [dir] if one exists. |
554 void _linkOrDeleteSecondaryPackageDir(String dir) { | 497 void _linkOrDeleteSecondaryPackageDir(String dir) { |
555 var symlink = path.join(dir, 'packages'); | 498 var symlink = path.join(dir, 'packages'); |
556 if (entryExists(symlink)) deleteEntry(symlink); | 499 if (entryExists(symlink)) deleteEntry(symlink); |
557 if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true); | 500 if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true); |
558 } | 501 } |
559 } | 502 } |
OLD | NEW |