Index: sdk/lib/_internal/pub_generated/lib/src/entrypoint.dart |
diff --git a/sdk/lib/_internal/pub_generated/lib/src/entrypoint.dart b/sdk/lib/_internal/pub_generated/lib/src/entrypoint.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2ae51dc7b12b9c5aaf1ba6ef165cd33c48926c55 |
--- /dev/null |
+++ b/sdk/lib/_internal/pub_generated/lib/src/entrypoint.dart |
@@ -0,0 +1,951 @@ |
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library pub.entrypoint; |
+ |
+import 'dart:async'; |
+ |
+import 'package:path/path.dart' as path; |
+import 'package:barback/barback.dart'; |
+ |
+import 'barback/asset_environment.dart'; |
+import 'io.dart'; |
+import 'lock_file.dart'; |
+import 'log.dart' as log; |
+import 'package.dart'; |
+import 'package_graph.dart'; |
+import 'sdk.dart' as sdk; |
+import 'solver/version_solver.dart'; |
+import 'source/cached.dart'; |
+import 'system_cache.dart'; |
+import 'utils.dart'; |
+ |
+/// The context surrounding the root package pub is operating on. |
+/// |
+/// Pub operates over a directed graph of dependencies that starts at a root |
+/// "entrypoint" package. This is typically the package where the current |
+/// working directory is located. An entrypoint knows the [root] package it is |
+/// associated with and is responsible for managing the "packages" directory |
+/// for it. |
+/// |
+/// That directory contains symlinks to all packages used by an app. These links |
+/// point either to the [SystemCache] or to some other location on the local |
+/// filesystem. |
+/// |
+/// While entrypoints are typically applications, a pure library package may end |
+/// up being used as an entrypoint. Also, a single package may be used as an |
+/// entrypoint in one context but not in another. For example, a package that |
+/// contains a reusable library may not be the entrypoint when used by an app, |
+/// but may be the entrypoint when you're running its tests. |
+class Entrypoint { |
+ /// The root package this entrypoint is associated with. |
+ final Package root; |
+ |
+ /// The system-wide cache which caches packages that need to be fetched over |
+ /// the network. |
+ final SystemCache cache; |
+ |
+ /// Whether to create and symlink a "packages" directory containing links to |
+ /// the installed packages. |
+ final bool _packageSymlinks; |
+ |
+ /// The lockfile for the entrypoint. |
+ /// |
+ /// If not provided to the entrypoint, it will be laoded lazily from disc. |
+ LockFile _lockFile; |
+ |
+ /// The graph of all packages reachable from the entrypoint. |
+ PackageGraph _packageGraph; |
+ |
+ /// Loads the entrypoint from a package at [rootDir]. |
+ /// |
+ /// If [packageSymlinks] is `true`, this will create a "packages" directory |
+ /// with symlinks to the installed packages. This directory will be symlinked |
+ /// into any directory that might contain an entrypoint. |
+ Entrypoint(String rootDir, SystemCache cache, {bool packageSymlinks: true}) |
+ : root = new Package.load(null, rootDir, cache.sources), |
+ cache = cache, |
+ _packageSymlinks = packageSymlinks; |
+ |
+ /// Creates an entrypoint given package and lockfile objects. |
+ Entrypoint.inMemory(this.root, this._lockFile, this.cache) |
+ : _packageSymlinks = false; |
+ |
+ /// The path to the entrypoint's "packages" directory. |
+ String get packagesDir => root.path('packages'); |
+ |
+ /// `true` if the entrypoint package currently has a lock file. |
+ bool get lockFileExists => _lockFile != null || entryExists(lockFilePath); |
+ |
+ LockFile get lockFile { |
+ if (_lockFile != null) return _lockFile; |
+ |
+ if (!lockFileExists) { |
+ _lockFile = new LockFile.empty(); |
+ } else { |
+ _lockFile = new LockFile.load(lockFilePath, cache.sources); |
+ } |
+ |
+ return _lockFile; |
+ } |
+ |
+ /// The path to the entrypoint package's pubspec. |
+ String get pubspecPath => root.path('pubspec.yaml'); |
+ |
+ /// The path to the entrypoint package's lockfile. |
+ String get lockFilePath => root.path('pubspec.lock'); |
+ |
+ /// Gets all dependencies of the [root] package. |
+ /// |
+ /// Performs version resolution according to [SolveType]. |
+ /// |
+ /// [useLatest], if provided, defines a list of packages that will be |
+ /// unlocked and forced to their latest versions. If [upgradeAll] is |
+ /// true, the previous lockfile is ignored and all packages are re-resolved |
+ /// from scratch. Otherwise, it will attempt to preserve the versions of all |
+ /// previously locked packages. |
+ /// |
+ /// Shows a report of the changes made relative to the previous lockfile. If |
+ /// this is an upgrade or downgrade, all transitive dependencies are shown in |
+ /// the report. Otherwise, only dependencies that were changed are shown. If |
+ /// [dryRun] is `true`, no physical changes are made. |
+ Future acquireDependencies(SolveType type, {List<String> useLatest, |
+ bool dryRun: false}) { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ new Future.value( |
+ resolveVersions( |
+ type, |
+ cache.sources, |
+ root, |
+ lockFile: lockFile, |
+ useLatest: useLatest)).then((x0) { |
+ try { |
+ var result = x0; |
+ join0() { |
+ result.showReport(type); |
+ join1() { |
+ join2() { |
+ new Future.value( |
+ Future.wait(result.packages.map(_get))).then((x1) { |
+ try { |
+ var ids = x1; |
+ _saveLockFile(ids); |
+ join3() { |
+ _linkOrDeleteSecondaryPackageDirs(); |
+ result.summarizeChanges(type, dryRun: dryRun); |
+ new Future.value(loadPackageGraph(result)).then((x2) { |
+ try { |
+ var packageGraph = x2; |
+ packageGraph.loadTransformerCache().clearIfOutdated( |
+ result.changedPackages); |
+ join4() { |
+ completer0.complete(); |
+ } |
+ catch0(error, stackTrace) { |
+ try { |
+ log.exception(error, stackTrace); |
+ join4(); |
+ } catch (error, stackTrace) { |
+ completer0.completeError(error, stackTrace); |
+ } |
+ } |
+ try { |
+ new Future.value( |
+ precompileDependencies(changed: result.changedPackages)).then((x3) { |
+ try { |
+ x3; |
+ new Future.value( |
+ precompileExecutables(changed: result.changedPackages)).then((x4) { |
+ try { |
+ x4; |
+ join4(); |
+ } catch (e0, s0) { |
+ catch0(e0, s0); |
+ } |
+ }, onError: catch0); |
+ } catch (e1, s1) { |
+ catch0(e1, s1); |
+ } |
+ }, onError: catch0); |
+ } catch (e2, s2) { |
+ catch0(e2, s2); |
+ } |
+ } catch (e3, s3) { |
+ completer0.completeError(e3, s3); |
+ } |
+ }, onError: completer0.completeError); |
+ } |
+ if (_packageSymlinks) { |
+ _linkSelf(); |
+ join3(); |
+ } else { |
+ join3(); |
+ } |
+ } catch (e4, s4) { |
+ completer0.completeError(e4, s4); |
+ } |
+ }, onError: completer0.completeError); |
+ } |
+ if (_packageSymlinks) { |
+ cleanDir(packagesDir); |
+ join2(); |
+ } else { |
+ deleteEntry(packagesDir); |
+ join2(); |
+ } |
+ } |
+ if (dryRun) { |
+ result.summarizeChanges(type, dryRun: dryRun); |
+ completer0.complete(null); |
+ } else { |
+ join1(); |
+ } |
+ } |
+ if (!result.succeeded) { |
+ throw result.error; |
+ join0(); |
+ } else { |
+ join0(); |
+ } |
+ } catch (e5, s5) { |
+ completer0.completeError(e5, s5); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ } |
+ |
+ /// Precompile any transformed dependencies of the entrypoint. |
+ /// |
+ /// If [changed] is passed, only dependencies whose contents might be changed |
+ /// if one of the given packages changes will be recompiled. |
+ Future precompileDependencies({Iterable<String> changed}) { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ join0() { |
+ new Future.value(loadPackageGraph()).then((x0) { |
+ try { |
+ var graph = x0; |
+ var depsDir = path.join('.pub', 'deps', 'debug'); |
+ var dependenciesToPrecompile = |
+ graph.packages.values.where(((package) { |
+ if (package.pubspec.transformers.isEmpty) return false; |
+ if (graph.isPackageMutable(package.name)) return false; |
+ if (!dirExists(path.join(depsDir, package.name))) return true; |
+ if (changed == null) return true; |
+ return overlaps( |
+ graph.transitiveDependencies( |
+ package.name).map((package) => package.name).toSet(), |
+ changed); |
+ })).map(((package) { |
+ return package.name; |
+ })).toSet(); |
+ join1() { |
+ join2() { |
+ join3() { |
+ completer0.complete(); |
+ } |
+ catch0(_, s0) { |
+ try { |
+ var it0 = dependenciesToPrecompile.iterator; |
+ break0() { |
+ completer0.completeError(_, s0); |
+ } |
+ var trampoline0; |
+ continue0() { |
+ trampoline0 = null; |
+ if (it0.moveNext()) { |
+ var package = it0.current; |
+ deleteEntry(path.join(depsDir, package)); |
+ trampoline0 = continue0; |
+ do trampoline0(); while (trampoline0 != null); |
+ } else { |
+ break0(); |
+ } |
+ } |
+ trampoline0 = continue0; |
+ do trampoline0(); while (trampoline0 != null); |
+ } catch (_, s0) { |
+ completer0.completeError(_, s0); |
+ } |
+ } |
+ try { |
+ new Future.value( |
+ log.progress("Precompiling dependencies", (() { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ var packagesToLoad = unionAll( |
+ dependenciesToPrecompile.map(graph.transitiveDependencies)).map(((package) { |
+ return package.name; |
+ })).toSet(); |
+ new Future.value( |
+ AssetEnvironment.create( |
+ this, |
+ BarbackMode.DEBUG, |
+ packages: packagesToLoad, |
+ useDart2JS: false)).then((x0) { |
+ try { |
+ var environment = x0; |
+ environment.barback.errors.listen(((_) { |
+ })); |
+ new Future.value( |
+ environment.barback.getAllAssets()).then((x1) { |
+ try { |
+ var assets = x1; |
+ new Future.value( |
+ waitAndPrintErrors(assets.map(((asset) { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ join0() { |
+ var destPath = |
+ path.join(depsDir, asset.id.package, path.fromUri(asset.id.path)); |
+ ensureDir(path.dirname(destPath)); |
+ new Future.value( |
+ createFileFromStream(asset.read(), destPath)).then((x0) { |
+ try { |
+ x0; |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } |
+ if (!dependenciesToPrecompile.contains( |
+ asset.id.package)) { |
+ completer0.complete(null); |
+ } else { |
+ join0(); |
+ } |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ })))).then((x2) { |
+ try { |
+ x2; |
+ log.message( |
+ "Precompiled " + |
+ toSentence(ordered(dependenciesToPrecompile).map(log.bold)) + |
+ "."); |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e2, s2) { |
+ completer0.completeError(e2, s2); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ }))).then((x1) { |
+ try { |
+ x1; |
+ join3(); |
+ } catch (e0, s1) { |
+ catch0(e0, s1); |
+ } |
+ }, onError: catch0); |
+ } catch (e1, s2) { |
+ catch0(e1, s2); |
+ } |
+ } |
+ if (dependenciesToPrecompile.isEmpty) { |
+ completer0.complete(null); |
+ } else { |
+ join2(); |
+ } |
+ } |
+ if (dirExists(depsDir)) { |
+ var it1 = dependenciesToPrecompile.iterator; |
+ break1() { |
+ var it2 = listDir(depsDir).iterator; |
+ break2() { |
+ join1(); |
+ } |
+ var trampoline2; |
+ continue2() { |
+ trampoline2 = null; |
+ if (it2.moveNext()) { |
+ var subdir = it2.current; |
+ var package = graph.packages[path.basename(subdir)]; |
+ join4() { |
+ trampoline2 = continue2; |
+ do trampoline2(); while (trampoline2 != null); |
+ } |
+ if (package == null || |
+ package.pubspec.transformers.isEmpty || |
+ graph.isPackageMutable(package.name)) { |
+ deleteEntry(subdir); |
+ join4(); |
+ } else { |
+ join4(); |
+ } |
+ } else { |
+ break2(); |
+ } |
+ } |
+ trampoline2 = continue2; |
+ do trampoline2(); while (trampoline2 != null); |
+ } |
+ var trampoline1; |
+ continue1() { |
+ trampoline1 = null; |
+ if (it1.moveNext()) { |
+ var package = it1.current; |
+ deleteEntry(path.join(depsDir, package)); |
+ trampoline1 = continue1; |
+ do trampoline1(); while (trampoline1 != null); |
+ } else { |
+ break1(); |
+ } |
+ } |
+ trampoline1 = continue1; |
+ do trampoline1(); while (trampoline1 != null); |
+ } else { |
+ join1(); |
+ } |
+ } catch (e2, s3) { |
+ completer0.completeError(e2, s3); |
+ } |
+ }, onError: completer0.completeError); |
+ } |
+ if (changed != null) { |
+ changed = changed.toSet(); |
+ join0(); |
+ } else { |
+ join0(); |
+ } |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ } |
+ |
+ /// Precompiles all executables from dependencies that don't transitively |
+ /// depend on [this] or on a path dependency. |
+ Future precompileExecutables({Iterable<String> changed}) { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ join0() { |
+ var binDir = path.join('.pub', 'bin'); |
+ var sdkVersionPath = path.join(binDir, 'sdk-version'); |
+ var sdkMatches = |
+ fileExists(sdkVersionPath) && |
+ readTextFile(sdkVersionPath) == "${sdk.version}\n"; |
+ join1() { |
+ new Future.value(loadPackageGraph()).then((x0) { |
+ try { |
+ var graph = x0; |
+ var executables = |
+ new Map.fromIterable(root.immediateDependencies, key: ((dep) { |
+ return dep.name; |
+ }), value: ((dep) { |
+ return _executablesForPackage(graph, dep.name, changed); |
+ })); |
+ var it0 = executables.keys.toList().iterator; |
+ break0() { |
+ join2() { |
+ join3() { |
+ new Future.value( |
+ log.progress("Precompiling executables", (() { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ ensureDir(binDir); |
+ writeTextFile(sdkVersionPath, "${sdk.version}\n"); |
+ var packagesToLoad = |
+ unionAll(executables.keys.map(graph.transitiveDependencies)).map(((package) { |
+ return package.name; |
+ })).toSet(); |
+ var executableIds = |
+ unionAll(executables.values.map(((ids) { |
+ return ids.toSet(); |
+ }))); |
+ new Future.value( |
+ AssetEnvironment.create( |
+ this, |
+ BarbackMode.RELEASE, |
+ packages: packagesToLoad, |
+ entrypoints: executableIds, |
+ useDart2JS: false)).then((x0) { |
+ try { |
+ var environment = x0; |
+ environment.barback.errors.listen(((error) { |
+ log.error(log.red("Build error:\n$error")); |
+ })); |
+ new Future.value( |
+ waitAndPrintErrors(executables.keys.map(((package) { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ var dir = path.join(binDir, package); |
+ cleanDir(dir); |
+ new Future.value( |
+ environment.precompileExecutables( |
+ package, |
+ dir, |
+ executableIds: executables[package])).then((x0) { |
+ try { |
+ x0; |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ })))).then((x1) { |
+ try { |
+ x1; |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ }))).then((x1) { |
+ try { |
+ x1; |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } |
+ if (executables.isEmpty) { |
+ completer0.complete(null); |
+ } else { |
+ join3(); |
+ } |
+ } |
+ if (!sdkMatches) { |
+ deleteEntry(binDir); |
+ join2(); |
+ } else { |
+ join2(); |
+ } |
+ } |
+ var trampoline0; |
+ continue0() { |
+ trampoline0 = null; |
+ if (it0.moveNext()) { |
+ var package = it0.current; |
+ join4() { |
+ trampoline0 = continue0; |
+ do trampoline0(); while (trampoline0 != null); |
+ } |
+ if (executables[package].isEmpty) { |
+ executables.remove(package); |
+ join4(); |
+ } else { |
+ join4(); |
+ } |
+ } else { |
+ break0(); |
+ } |
+ } |
+ trampoline0 = continue0; |
+ do trampoline0(); while (trampoline0 != null); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
+ } |
+ }, onError: completer0.completeError); |
+ } |
+ if (!sdkMatches) { |
+ changed = null; |
+ join1(); |
+ } else { |
+ join1(); |
+ } |
+ } |
+ if (changed != null) { |
+ changed = changed.toSet(); |
+ join0(); |
+ } else { |
+ join0(); |
+ } |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ } |
+ |
+ /// Returns the list of all executable assets for [packageName] that should be |
+ /// precompiled. |
+ /// |
+ /// If [changed] isn't `null`, executables for [packageName] will only be |
+ /// compiled if they might depend on a package in [changed]. |
+ List<AssetId> _executablesForPackage(PackageGraph graph, String packageName, |
+ Set<String> changed) { |
+ var package = graph.packages[packageName]; |
+ var binDir = package.path('bin'); |
+ if (!dirExists(binDir)) return []; |
+ if (graph.isPackageMutable(packageName)) return []; |
+ |
+ var executables = package.executableIds; |
+ |
+ // If we don't know which packages were changed, always precompile the |
+ // executables. |
+ if (changed == null) return executables; |
+ |
+ // If any of the package's dependencies changed, recompile the executables. |
+ if (graph.transitiveDependencies( |
+ packageName).any((package) => changed.contains(package.name))) { |
+ return executables; |
+ } |
+ |
+ // If any executables doesn't exist, precompile them regardless of what |
+ // changed. Since we delete the bin directory before recompiling, we need to |
+ // recompile all executables. |
+ var executablesExist = executables.every( |
+ (executable) => |
+ fileExists( |
+ path.join( |
+ '.pub', |
+ 'bin', |
+ packageName, |
+ "${path.url.basename(executable.path)}.snapshot"))); |
+ if (!executablesExist) return executables; |
+ |
+ // Otherwise, we don't need to recompile. |
+ return []; |
+ } |
+ |
+ /// Makes sure the package at [id] is locally available. |
+ /// |
+ /// This automatically downloads the package to the system-wide cache as well |
+ /// if it requires network access to retrieve (specifically, if the package's |
+ /// source is a [CachedSource]). |
+ Future<PackageId> _get(PackageId id) { |
+ if (id.isRoot) return new Future.value(id); |
+ |
+ var source = cache.sources[id.source]; |
+ return new Future.sync(() { |
+ if (!_packageSymlinks) { |
+ if (source is! CachedSource) return null; |
+ return source.downloadToSystemCache(id); |
+ } |
+ |
+ var packageDir = path.join(packagesDir, id.name); |
+ if (entryExists(packageDir)) deleteEntry(packageDir); |
+ return source.get(id, packageDir); |
+ }).then((_) => source.resolveId(id)); |
+ } |
+ |
+ /// Determines whether or not the lockfile is out of date with respect to the |
+ /// pubspec. |
+ /// |
+ /// This will be `false` if there is no lockfile at all, or if the pubspec |
+ /// contains dependencies that are not in the lockfile or that don't match |
+ /// what's in there. |
+ bool _isLockFileUpToDate(LockFile lockFile) { |
+ /// If this is an entrypoint for an in-memory package, trust the in-memory |
+ /// lockfile provided for it. |
+ if (root.dir == null) return true; |
+ |
+ return root.immediateDependencies.every((package) { |
+ var locked = lockFile.packages[package.name]; |
+ if (locked == null) return false; |
+ |
+ if (package.source != locked.source) return false; |
+ if (!package.constraint.allows(locked.version)) return false; |
+ |
+ var source = cache.sources[package.source]; |
+ if (source == null) return false; |
+ |
+ return source.descriptionsEqual(package.description, locked.description); |
+ }); |
+ } |
+ |
+ /// Determines whether all of the packages in the lockfile are already |
+ /// installed and available. |
+ /// |
+ /// Note: this assumes [isLockFileUpToDate] has already been called and |
+ /// returned `true`. |
+ Future<bool> _arePackagesAvailable(LockFile lockFile) { |
+ return Future.wait(lockFile.packages.values.map((package) { |
+ var source = cache.sources[package.source]; |
+ |
+ // This should only be called after [_isLockFileUpToDate] has returned |
+ // `true`, which ensures all of the sources in the lock file are valid. |
+ assert(source != null); |
+ |
+ // We only care about cached sources. Uncached sources aren't "installed". |
+ // If one of those is missing, we want to show the user the file not |
+ // found error later since installing won't accomplish anything. |
+ if (source is! CachedSource) return new Future.value(true); |
+ |
+ // Get the directory. |
+ return source.getDirectory(package).then((dir) { |
+ // See if the directory is there and looks like a package. |
+ return dirExists(dir) || fileExists(path.join(dir, "pubspec.yaml")); |
+ }); |
+ })).then((results) { |
+ // Make sure they are all true. |
+ return results.every((result) => result); |
+ }); |
+ } |
+ |
+ /// Gets dependencies if the lockfile is out of date with respect to the |
+ /// pubspec. |
+ Future ensureLockFileIsUpToDate() { |
+ return new Future.sync(() { |
+ // If we don't have a current lock file, we definitely need to install. |
+ if (!_isLockFileUpToDate(lockFile)) { |
+ if (lockFileExists) { |
+ log.message( |
+ "Your pubspec has changed, so we need to update your lockfile:"); |
+ } else { |
+ log.message( |
+ "You don't have a lockfile, so we need to generate that:"); |
+ } |
+ |
+ return false; |
+ } |
+ |
+ // If we do have a lock file, we still need to make sure the packages |
+ // are actually installed. The user may have just gotten a package that |
+ // includes a lockfile. |
+ return _arePackagesAvailable(lockFile).then((available) { |
+ if (!available) { |
+ log.message( |
+ "You are missing some dependencies, so we need to install them " "first:"); |
+ } |
+ |
+ return available; |
+ }); |
+ }).then((upToDate) { |
+ if (upToDate) return null; |
+ return acquireDependencies(SolveType.GET); |
+ }); |
+ } |
+ |
+ /// Loads the package graph for the application and all of its transitive |
+ /// dependencies. |
+ /// |
+ /// If [result] is passed, this loads the graph from it without re-parsing the |
+ /// lockfile or any pubspecs. Otherwise, before loading, this makes sure the |
+ /// lockfile and dependencies are installed and up to date. |
+ Future<PackageGraph> loadPackageGraph([SolveResult result]) { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ join0() { |
+ new Future.value(log.progress("Loading package graph", (() { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ join0() { |
+ new Future.value(ensureLockFileIsUpToDate()).then((x0) { |
+ try { |
+ x0; |
+ new Future.value( |
+ Future.wait(lockFile.packages.values.map(((id) { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ var source = cache.sources[id.source]; |
+ new Future.value( |
+ source.getDirectory(id)).then((x0) { |
+ try { |
+ var dir = x0; |
+ completer0.complete( |
+ new Package.load(id.name, dir, cache.sources)); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ })))).then((x1) { |
+ try { |
+ var packages = x1; |
+ var packageMap = |
+ new Map.fromIterable(packages, key: ((p) { |
+ return p.name; |
+ })); |
+ packageMap[root.name] = root; |
+ completer0.complete( |
+ new PackageGraph(this, lockFile, packageMap)); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
+ } |
+ }, onError: completer0.completeError); |
+ } |
+ if (result != null) { |
+ new Future.value(Future.wait(result.packages.map(((id) { |
+ final completer0 = new Completer(); |
+ scheduleMicrotask(() { |
+ try { |
+ new Future.value( |
+ cache.sources[id.source].getDirectory(id)).then((x0) { |
+ try { |
+ var dir = x0; |
+ completer0.complete( |
+ new Package(result.pubspecs[id.name], dir)); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ })))).then((x2) { |
+ try { |
+ var packages = x2; |
+ completer0.complete( |
+ new PackageGraph( |
+ this, |
+ new LockFile(result.packages), |
+ new Map.fromIterable(packages, key: ((package) { |
+ return package.name; |
+ })))); |
+ } catch (e2, s2) { |
+ completer0.completeError(e2, s2); |
+ } |
+ }, onError: completer0.completeError); |
+ } else { |
+ join0(); |
+ } |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ }), fine: true)).then((x0) { |
+ try { |
+ var graph = x0; |
+ _packageGraph = graph; |
+ completer0.complete(graph); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
+ } |
+ }, onError: completer0.completeError); |
+ } |
+ if (_packageGraph != null) { |
+ completer0.complete(_packageGraph); |
+ } else { |
+ join0(); |
+ } |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
+ } |
+ }); |
+ return completer0.future; |
+ } |
+ |
+ /// Saves a list of concrete package versions to the `pubspec.lock` file. |
+ void _saveLockFile(List<PackageId> packageIds) { |
+ _lockFile = new LockFile(packageIds); |
+ var lockFilePath = root.path('pubspec.lock'); |
+ writeTextFile(lockFilePath, _lockFile.serialize(root.dir, cache.sources)); |
+ } |
+ |
+ /// Creates a self-referential symlink in the `packages` directory that allows |
+ /// a package to import its own files using `package:`. |
+ void _linkSelf() { |
+ var linkPath = path.join(packagesDir, root.name); |
+ // Create the symlink if it doesn't exist. |
+ if (entryExists(linkPath)) return; |
+ ensureDir(packagesDir); |
+ createPackageSymlink( |
+ root.name, |
+ root.dir, |
+ linkPath, |
+ isSelfLink: true, |
+ relative: true); |
+ } |
+ |
+ /// If [packageSymlinks] is true, add "packages" directories to the whitelist |
+ /// of directories that may contain Dart entrypoints. |
+ /// |
+ /// Otherwise, delete any "packages" directories in the whitelist of |
+ /// directories that may contain Dart entrypoints. |
+ void _linkOrDeleteSecondaryPackageDirs() { |
+ // Only the main "bin" directory gets a "packages" directory, not its |
+ // subdirectories. |
+ var binDir = root.path('bin'); |
+ if (dirExists(binDir)) _linkOrDeleteSecondaryPackageDir(binDir); |
+ |
+ // The others get "packages" directories in subdirectories too. |
+ for (var dir in ['benchmark', 'example', 'test', 'tool', 'web']) { |
+ _linkOrDeleteSecondaryPackageDirsRecursively(root.path(dir)); |
+ } |
+ } |
+ |
+ /// If [packageSymlinks] is true, creates a symlink to the "packages" |
+ /// directory in [dir] and all its subdirectories. |
+ /// |
+ /// Otherwise, deletes any "packages" directories in [dir] and all its |
+ /// subdirectories. |
+ void _linkOrDeleteSecondaryPackageDirsRecursively(String dir) { |
+ if (!dirExists(dir)) return; |
+ _linkOrDeleteSecondaryPackageDir(dir); |
+ _listDirWithoutPackages( |
+ dir).where(dirExists).forEach(_linkOrDeleteSecondaryPackageDir); |
+ } |
+ |
+ // TODO(nweiz): roll this into [listDir] in io.dart once issue 4775 is fixed. |
+ /// Recursively lists the contents of [dir], excluding hidden `.DS_Store` |
+ /// files and `package` files. |
+ List<String> _listDirWithoutPackages(dir) { |
+ return flatten(listDir(dir).map((file) { |
+ if (path.basename(file) == 'packages') return []; |
+ if (!dirExists(file)) return []; |
+ var fileAndSubfiles = [file]; |
+ fileAndSubfiles.addAll(_listDirWithoutPackages(file)); |
+ return fileAndSubfiles; |
+ })); |
+ } |
+ |
+ /// If [packageSymlinks] is true, creates a symlink to the "packages" |
+ /// directory in [dir]. |
+ /// |
+ /// Otherwise, deletes a "packages" directories in [dir] if one exists. |
+ void _linkOrDeleteSecondaryPackageDir(String dir) { |
+ var symlink = path.join(dir, 'packages'); |
+ if (entryExists(symlink)) deleteEntry(symlink); |
+ if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true); |
+ } |
+} |