Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1367)

Side by Side Diff: lib/src/entrypoint.dart

Issue 1282533003: Don't implicitly run "pub get". (Closed) Base URL: git@github.com:dart-lang/pub.git@master
Patch Set: Code review changes Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/dart.dart ('k') | lib/src/executable.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 entrypoint is in memory only, as opposed to representing a
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 (!fileExists(lockFilePath)) {
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
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
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) {
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
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.');
416 }
447 417
448 /// Loads the package graph for the application and all of its transitive 418 var packagesModified = new File(packagesFile).lastModifiedSync();
449 /// dependencies. 419 var pubspecModified = new File(pubspecPath).lastModifiedSync();
450 /// 420 if (packagesModified.isBefore(pubspecModified)) {
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 lockFileModified = new File(lockFilePath).lastModifiedSync();
458 if (result != null) { 426 if (packagesModified.isBefore(lockFileModified)) {
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
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 }
OLDNEW
« no previous file with comments | « lib/src/dart.dart ('k') | lib/src/executable.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698