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 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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |