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

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

Issue 1534093002: Improve the detection lockfile freshness. (Closed) Base URL: git@github.com:dart-lang/pub.git@master
Patch Set: Created 5 years 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 | « no previous file | lib/src/global_packages.dart » ('j') | lib/src/lock_file.dart » ('J')
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 import 'dart:io';
9 9
10 import 'package:barback/barback.dart';
10 import 'package:package_config/packages_file.dart' as packages_file; 11 import 'package:package_config/packages_file.dart' as packages_file;
11 import 'package:path/path.dart' as p; 12 import 'package:path/path.dart' as p;
12 import 'package:barback/barback.dart'; 13 import 'package:pub_semver/pub_semver.dart';
13 14
14 import 'barback/asset_environment.dart'; 15 import 'barback/asset_environment.dart';
15 import 'io.dart'; 16 import 'io.dart';
16 import 'lock_file.dart'; 17 import 'lock_file.dart';
17 import 'log.dart' as log; 18 import 'log.dart' as log;
18 import 'package.dart'; 19 import 'package.dart';
19 import 'package_graph.dart'; 20 import 'package_graph.dart';
20 import 'sdk.dart' as sdk; 21 import 'sdk.dart' as sdk;
21 import 'solver/version_solver.dart'; 22 import 'solver/version_solver.dart';
22 import 'source/cached.dart'; 23 import 'source/cached.dart';
24 import 'source/unknown.dart';
23 import 'system_cache.dart'; 25 import 'system_cache.dart';
24 import 'utils.dart'; 26 import 'utils.dart';
25 27
28 /// A RegExp to match the SDK constraint in a lock file.
29 final _sdkConstraint = new RegExp(r'^sdk: "?([^"]*)"?$', multiLine: true);
30
26 /// The context surrounding the root package pub is operating on. 31 /// The context surrounding the root package pub is operating on.
27 /// 32 ///
28 /// Pub operates over a directed graph of dependencies that starts at a root 33 /// Pub operates over a directed graph of dependencies that starts at a root
29 /// "entrypoint" package. This is typically the package where the current 34 /// "entrypoint" package. This is typically the package where the current
30 /// working directory is located. An entrypoint knows the [root] package it is 35 /// working directory is located. An entrypoint knows the [root] package it is
31 /// associated with and is responsible for managing the "packages" directory 36 /// associated with and is responsible for managing the "packages" directory
32 /// for it. 37 /// for it.
33 /// 38 ///
34 /// That directory contains symlinks to all packages used by an app. These links 39 /// That directory contains symlinks to all packages used by an app. These links
35 /// point either to the [SystemCache] or to some other location on the local 40 /// point either to the [SystemCache] or to some other location on the local
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 /// dependencies. 84 /// dependencies.
80 /// 85 ///
81 /// Throws a [DataError] if the `.packages` file isn't up-to-date relative to 86 /// Throws a [DataError] if the `.packages` file isn't up-to-date relative to
82 /// the pubspec and the lockfile. 87 /// the pubspec and the lockfile.
83 PackageGraph get packageGraph { 88 PackageGraph get packageGraph {
84 if (_packageGraph != null) return _packageGraph; 89 if (_packageGraph != null) return _packageGraph;
85 90
86 assertUpToDate(); 91 assertUpToDate();
87 var packages = new Map.fromIterable(lockFile.packages.values, 92 var packages = new Map.fromIterable(lockFile.packages.values,
88 key: (id) => id.name, 93 key: (id) => id.name,
89 value: (id) { 94 value: (id) => cache.sources.load(id));
90 var dir = cache.sources[id.source].getDirectory(id);
91 return new Package.load(id.name, dir, cache.sources);
92 });
93 packages[root.name] = root; 95 packages[root.name] = root;
94 96
95 _packageGraph = new PackageGraph(this, lockFile, packages); 97 _packageGraph = new PackageGraph(this, lockFile, packages);
96 return _packageGraph; 98 return _packageGraph;
97 } 99 }
98 PackageGraph _packageGraph; 100 PackageGraph _packageGraph;
99 101
100 /// The path to the entrypoint's "packages" directory. 102 /// The path to the entrypoint's "packages" directory.
101 String get packagesDir => root.path('packages'); 103 String get packagesDir => root.path('packages');
102 104
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 } 169 }
168 170
169 // Install the packages and maybe link them into the entrypoint. 171 // Install the packages and maybe link them into the entrypoint.
170 if (_packageSymlinks) { 172 if (_packageSymlinks) {
171 cleanDir(packagesDir); 173 cleanDir(packagesDir);
172 } else { 174 } else {
173 deleteEntry(packagesDir); 175 deleteEntry(packagesDir);
174 } 176 }
175 177
176 await Future.wait(result.packages.map(_get)); 178 await Future.wait(result.packages.map(_get));
177 _saveLockFile(result.packages); 179 _saveLockFile(result);
178 180
179 if (_packageSymlinks) _linkSelf(); 181 if (_packageSymlinks) _linkSelf();
180 _linkOrDeleteSecondaryPackageDirs(); 182 _linkOrDeleteSecondaryPackageDirs();
181 183
182 result.summarizeChanges(type, dryRun: dryRun); 184 result.summarizeChanges(type, dryRun: dryRun);
183 185
184 /// Build a package graph from the version solver results so we don't 186 /// Build a package graph from the version solver results so we don't
185 /// have to reload and reparse all the pubspecs. 187 /// have to reload and reparse all the pubspecs.
186 _packageGraph = new PackageGraph.fromSolveResult(this, result); 188 _packageGraph = new PackageGraph.fromSolveResult(this, result);
187 packageGraph.loadTransformerCache().clearIfOutdated(result.changedPackages); 189 packageGraph.loadTransformerCache().clearIfOutdated(result.changedPackages);
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after
413 if (_inMemory) return; 415 if (_inMemory) return;
414 416
415 if (!entryExists(lockFilePath)) { 417 if (!entryExists(lockFilePath)) {
416 dataError('No pubspec.lock file found, please run "pub get" first.'); 418 dataError('No pubspec.lock file found, please run "pub get" first.');
417 } 419 }
418 420
419 if (!entryExists(packagesFile)) { 421 if (!entryExists(packagesFile)) {
420 dataError('No .packages file found, please run "pub get" first.'); 422 dataError('No .packages file found, please run "pub get" first.');
421 } 423 }
422 424
425 // Manually parse the lockfile because a full YAML parse is relatively slow
426 // and this is on the hot path for "pub run".
427 var lockFileText = readTextFile(lockFilePath);
428 var hasPathDependencies = lockFileText.contains("\n source: path\n");
Bob Nystrom 2015/12/21 20:55:03 This feels really sketchy to me. We don't for cert
nweiz 2016/01/04 23:27:29 Discussed offline.
429
423 var pubspecModified = new File(pubspecPath).lastModifiedSync(); 430 var pubspecModified = new File(pubspecPath).lastModifiedSync();
424 var lockFileModified = new File(lockFilePath).lastModifiedSync(); 431 var lockFileModified = new File(lockFilePath).lastModifiedSync();
425 432
426 var touchedLockFile = false; 433 var touchedLockFile = false;
427 if (lockFileModified.isBefore(pubspecModified)) { 434 if (lockFileModified.isBefore(pubspecModified) ||
435 hasPathDependencies) {
428 if (_isLockFileUpToDate() && _arePackagesAvailable()) { 436 if (_isLockFileUpToDate() && _arePackagesAvailable()) {
429 touchedLockFile = true; 437 touchedLockFile = true;
430 touch(lockFilePath); 438 touch(lockFilePath);
431 } else { 439 } else {
432 dataError('The pubspec.yaml file has changed since the pubspec.lock ' 440 dataError('The pubspec.yaml file has changed since the pubspec.lock '
433 'file was generated, please run "pub get" again.'); 441 'file was generated, please run "pub get" again.');
434 } 442 }
435 } 443 }
436 444
437 var packagesModified = new File(packagesFile).lastModifiedSync(); 445 var packagesModified = new File(packagesFile).lastModifiedSync();
438 if (packagesModified.isBefore(lockFileModified)) { 446 if (packagesModified.isBefore(lockFileModified)) {
439 if (_isPackagesFileUpToDate()) { 447 if (_isPackagesFileUpToDate()) {
440 touch(packagesFile); 448 touch(packagesFile);
441 } else { 449 } else {
442 dataError('The pubspec.lock file has changed since the .packages file ' 450 dataError('The pubspec.lock file has changed since the .packages file '
443 'was generated, please run "pub get" again.'); 451 'was generated, please run "pub get" again.');
444 } 452 }
445 } else if (touchedLockFile) { 453 } else if (touchedLockFile) {
446 touch(packagesFile); 454 touch(packagesFile);
447 } 455 }
456
457 var sdkConstraint = _sdkConstraint.firstMatch(lockFileText);
458 if (sdkConstraint != null) {
459 var parsedConstraint = new VersionConstraint.parse(sdkConstraint[1]);
460 if (!parsedConstraint.allows(sdk.version)) {
461 dataError("Dart ${sdk.version} is incompatible with your dependencies' "
462 "SDK constraints. Please run \"pub get\" again.");
463 }
464 }
448 } 465 }
449 466
450 /// Determines whether or not the lockfile is out of date with respect to the 467 /// Determines whether or not the lockfile is out of date with respect to the
451 /// pubspec. 468 /// pubspec.
452 /// 469 ///
453 /// This will be `false` if the pubspec contains dependencies that are not in 470 /// This will be `false` if any mutable pubspec contains dependencies that are
454 /// the lockfile or that don't match what's in there. 471 /// not in the lockfile or that don't match what's in there.
455 bool _isLockFileUpToDate() { 472 bool _isLockFileUpToDate() {
456 return root.immediateDependencies.every((package) { 473 if (!root.immediateDependencies.every(_isDependencyUpToDate)) return false;
457 var locked = lockFile.packages[package.name];
458 if (locked == null) return false;
459 474
460 if (package.source != locked.source) return false; 475 var overrides = root.dependencyOverrides.map((dep) => dep.name).toSet();
461 476
462 if (!package.constraint.allows(locked.version)) return false; 477 // Check that path dependencies' pubspecs are also still satisfied, since
478 // they're mutable and may have changed since the last get.
479 return lockFile.packages.values.every((id) {
480 if (id.source != 'path') return true;
Bob Nystrom 2015/12/21 20:55:03 Instead of checking for path specifically, how abo
nweiz 2016/01/04 23:27:29 Done.
463 481
464 var source = cache.sources[package.source]; 482 return cache.sources.load(id).dependencies.every((dep) {
465 if (source == null) return false; 483 if (overrides.contains(dep.name)) return true;
484 return _isDependencyUpToDate(dep);
485 });
Bob Nystrom 2015/12/21 20:55:03 return cache.sources.load(id).dependencies.every(
nweiz 2016/01/04 23:27:29 Done.
486 });
487 }
466 488
467 return source.descriptionsEqual(package.description, locked.description); 489 /// Returns whether the locked version of [dep] matches the dependency.
468 }); 490 bool _isDependencyUpToDate(PackageDep dep) {
491 var locked = lockFile.packages[dep.name];
492 if (locked == null) return false;
493
494 if (dep.source != locked.source) return false;
495
496 if (!dep.constraint.allows(locked.version)) return false;
497
498 var source = cache.sources[dep.source];
499 if (source == null) return false;
500
501 return source.descriptionsEqual(dep.description, locked.description);
469 } 502 }
470 503
471 /// Determines whether all of the packages in the lockfile are already 504 /// Determines whether all of the packages in the lockfile are already
472 /// installed and available. 505 /// installed and available.
473 ///
474 /// Note: this assumes [_isLockFileUpToDate] has already been called and
475 /// returned `true`.
476 bool _arePackagesAvailable() { 506 bool _arePackagesAvailable() {
477 return lockFile.packages.values.every((package) { 507 return lockFile.packages.values.every((package) {
478 var source = cache.sources[package.source]; 508 var source = cache.sources[package.source];
479 509 if (source is UnknownSource) return false;
480 // This should only be called after [_isLockFileUpToDate] has returned
481 // `true`, which ensures all of the sources in the lock file are valid.
482 assert(source != null);
483 510
484 // We only care about cached sources. Uncached sources aren't "installed". 511 // We only care about cached sources. Uncached sources aren't "installed".
485 // If one of those is missing, we want to show the user the file not 512 // If one of those is missing, we want to show the user the file not
486 // found error later since installing won't accomplish anything. 513 // found error later since installing won't accomplish anything.
487 if (source is! CachedSource) return true; 514 if (source is! CachedSource) return true;
488 515
489 // Get the directory. 516 // Get the directory.
490 var dir = source.getDirectory(package); 517 var dir = source.getDirectory(package);
491 // See if the directory is there and looks like a package. 518 // See if the directory is there and looks like a package.
492 return dirExists(dir) && fileExists(p.join(dir, "pubspec.yaml")); 519 return dirExists(dir) && fileExists(p.join(dir, "pubspec.yaml"));
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
535 return false; 562 return false;
536 } 563 }
537 564
538 // Make sure that the packages file agrees with the lock file about the 565 // Make sure that the packages file agrees with the lock file about the
539 // path to the package. 566 // path to the package.
540 return p.normalize(packagesFilePath) == p.normalize(lockFilePath); 567 return p.normalize(packagesFilePath) == p.normalize(lockFilePath);
541 }); 568 });
542 } 569 }
543 570
544 /// Saves a list of concrete package versions to the `pubspec.lock` file. 571 /// Saves a list of concrete package versions to the `pubspec.lock` file.
545 void _saveLockFile(List<PackageId> packageIds) { 572 void _saveLockFile(SolveResult result) {
546 _lockFile = new LockFile(packageIds, cache.sources); 573 _lockFile = result.lockFile;
547 var lockFilePath = root.path('pubspec.lock'); 574 var lockFilePath = root.path('pubspec.lock');
548 writeTextFile(lockFilePath, _lockFile.serialize(root.dir)); 575 writeTextFile(lockFilePath, _lockFile.serialize(root.dir));
549 } 576 }
550 577
551 /// Creates a self-referential symlink in the `packages` directory that allows 578 /// Creates a self-referential symlink in the `packages` directory that allows
552 /// a package to import its own files using `package:`. 579 /// a package to import its own files using `package:`.
553 void _linkSelf() { 580 void _linkSelf() {
554 var linkPath = p.join(packagesDir, root.name); 581 var linkPath = p.join(packagesDir, root.name);
555 // Create the symlink if it doesn't exist. 582 // Create the symlink if it doesn't exist.
556 if (entryExists(linkPath)) return; 583 if (entryExists(linkPath)) return;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
605 /// If [packageSymlinks] is true, creates a symlink to the "packages" 632 /// If [packageSymlinks] is true, creates a symlink to the "packages"
606 /// directory in [dir]. 633 /// directory in [dir].
607 /// 634 ///
608 /// Otherwise, deletes a "packages" directories in [dir] if one exists. 635 /// Otherwise, deletes a "packages" directories in [dir] if one exists.
609 void _linkOrDeleteSecondaryPackageDir(String dir) { 636 void _linkOrDeleteSecondaryPackageDir(String dir) {
610 var symlink = p.join(dir, 'packages'); 637 var symlink = p.join(dir, 'packages');
611 if (entryExists(symlink)) deleteEntry(symlink); 638 if (entryExists(symlink)) deleteEntry(symlink);
612 if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true); 639 if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true);
613 } 640 }
614 } 641 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/global_packages.dart » ('j') | lib/src/lock_file.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698