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 |
index eadf9a997c76aec1888035644eb5d7fcd0ff9d6b..58498435690fcff2a053983daf72ebb67f4cfc11 100644 |
--- a/sdk/lib/_internal/pub_generated/lib/src/entrypoint.dart |
+++ b/sdk/lib/_internal/pub_generated/lib/src/entrypoint.dart |
@@ -1,7 +1,14 @@ |
+// 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'; |
@@ -13,31 +20,96 @@ 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(); |
@@ -74,12 +146,10 @@ class Entrypoint { |
})).catchError(((error, stackTrace) { |
log.exception(error, stackTrace); |
}))); |
- } catch (e2) { |
- completer0.completeError(e2); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
} |
- }, onError: (e3) { |
- completer0.completeError(e3); |
- }); |
+ }, onError: completer0.completeError); |
} |
if (_packageSymlinks) { |
_linkSelf(); |
@@ -87,12 +157,10 @@ class Entrypoint { |
} else { |
join3(); |
} |
- } catch (e1) { |
- completer0.completeError(e1); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
} |
- }, onError: (e4) { |
- completer0.completeError(e4); |
- }); |
+ }, onError: completer0.completeError); |
} |
if (_packageSymlinks) { |
cleanDir(packagesDir); |
@@ -110,22 +178,26 @@ class Entrypoint { |
} |
} |
if (!result.succeeded) { |
- completer0.completeError(result.error); |
+ throw result.error; |
+ join0(); |
} else { |
join0(); |
} |
- } catch (e0) { |
- completer0.completeError(e0); |
+ } catch (e2, s2) { |
+ completer0.completeError(e2, s2); |
} |
- }, onError: (e5) { |
- completer0.completeError(e5); |
- }); |
- } catch (e6) { |
- completer0.completeError(e6); |
+ }, 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(() { |
@@ -145,17 +217,20 @@ class Entrypoint { |
graph.transitiveDependencies( |
package.name).map((package) => package.name).toSet(), |
changed); |
- })).map(((package) => package.name)).toSet(); |
+ })).map(((package) { |
+ return package.name; |
+ })).toSet(); |
join1() { |
log.progress("Precompiling dependencies", (() { |
final completer0 = new Completer(); |
scheduleMicrotask(() { |
try { |
var packagesToLoad = unionAll( |
- dependenciesToPrecompile.map( |
- graph.transitiveDependencies)).map(((package) => package.name)).toSet(); |
+ dependenciesToPrecompile.map(graph.transitiveDependencies)).map(((package) { |
+ return package.name; |
+ })).toSet(); |
var it0 = dependenciesToPrecompile.iterator; |
- break0(x4) { |
+ break0() { |
AssetEnvironment.create( |
this, |
BarbackMode.DEBUG, |
@@ -163,7 +238,8 @@ class Entrypoint { |
useDart2JS: false).then((x0) { |
try { |
var environment = x0; |
- environment.barback.errors.listen(((_) {})); |
+ environment.barback.errors.listen(((_) { |
+ })); |
environment.barback.getAllAssets().then((x1) { |
try { |
var assets = x1; |
@@ -180,13 +256,11 @@ class Entrypoint { |
destPath).then((x0) { |
try { |
x0; |
- completer0.complete(null); |
- } catch (e0) { |
- completer0.completeError(e0); |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
} |
- }, onError: (e1) { |
- completer0.completeError(e1); |
- }); |
+ }, onError: completer0.completeError); |
} |
if (!dependenciesToPrecompile.contains( |
asset.id.package)) { |
@@ -194,8 +268,8 @@ class Entrypoint { |
} else { |
join0(); |
} |
- } catch (e2) { |
- completer0.completeError(e2); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
} |
}); |
return completer0.future; |
@@ -206,70 +280,61 @@ class Entrypoint { |
"Precompiled " + |
toSentence(ordered(dependenciesToPrecompile).map(log.bold)) + |
"."); |
- completer0.complete(null); |
- } catch (e2) { |
- completer0.completeError(e2); |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
} |
- }, onError: (e3) { |
- completer0.completeError(e3); |
- }); |
- } catch (e1) { |
- completer0.completeError(e1); |
+ }, onError: completer0.completeError); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
} |
- }, onError: (e4) { |
- completer0.completeError(e4); |
- }); |
- } catch (e0) { |
- completer0.completeError(e0); |
+ }, onError: completer0.completeError); |
+ } catch (e2, s2) { |
+ completer0.completeError(e2, s2); |
} |
- }, onError: (e5) { |
- completer0.completeError(e5); |
- }); |
+ }, onError: completer0.completeError); |
} |
- continue0(x5) { |
+ var trampoline0; |
+ continue0() { |
+ trampoline0 = null; |
if (it0.moveNext()) { |
- Future.wait([]).then((x3) { |
- var package = it0.current; |
- deleteEntry(path.join(depsDir, package)); |
- continue0(null); |
- }); |
+ var package = it0.current; |
+ deleteEntry(path.join(depsDir, package)); |
+ trampoline0 = continue0; |
} else { |
- break0(null); |
+ break0(); |
} |
} |
- continue0(null); |
- } catch (e6) { |
- completer0.completeError(e6); |
+ trampoline0 = continue0; |
+ do trampoline0(); while (trampoline0 != null); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
} |
}); |
return completer0.future; |
})).catchError(((error) { |
- for (var package in dependenciesToPrecompile) { |
+ for (package in dependenciesToPrecompile) { |
deleteEntry(path.join(depsDir, package)); |
} |
throw error; |
})).then((x1) { |
try { |
x1; |
- completer0.complete(null); |
- } catch (e1) { |
- completer0.completeError(e1); |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
} |
- }, onError: (e2) { |
- completer0.completeError(e2); |
- }); |
+ }, onError: completer0.completeError); |
} |
if (dependenciesToPrecompile.isEmpty) { |
completer0.complete(null); |
} else { |
join1(); |
} |
- } catch (e0) { |
- completer0.completeError(e0); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
} |
- }, onError: (e3) { |
- completer0.completeError(e3); |
- }); |
+ }, onError: completer0.completeError); |
} |
if (changed != null) { |
changed = changed.toSet(); |
@@ -277,12 +342,15 @@ class Entrypoint { |
} else { |
join0(); |
} |
- } catch (e4) { |
- completer0.completeError(e4); |
+ } 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(() { |
@@ -297,12 +365,14 @@ class Entrypoint { |
loadPackageGraph().then((x0) { |
try { |
var graph = x0; |
- var executables = new Map.fromIterable( |
- root.immediateDependencies, |
- key: ((dep) => dep.name), |
- value: ((dep) => _executablesForPackage(graph, dep.name, changed))); |
+ 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(x3) { |
+ break0() { |
join2() { |
join3() { |
log.progress("Precompiling executables", (() { |
@@ -311,11 +381,14 @@ class Entrypoint { |
try { |
ensureDir(binDir); |
writeTextFile(sdkVersionPath, "${sdk.version}\n"); |
- var packagesToLoad = unionAll( |
- executables.keys.map( |
- graph.transitiveDependencies)).map(((package) => package.name)).toSet(); |
+ var packagesToLoad = |
+ unionAll(executables.keys.map(graph.transitiveDependencies)).map(((package) { |
+ return package.name; |
+ })).toSet(); |
var executableIds = |
- unionAll(executables.values.map(((ids) => ids.toSet()))); |
+ unionAll(executables.values.map(((ids) { |
+ return ids.toSet(); |
+ }))); |
AssetEnvironment.create( |
this, |
BarbackMode.RELEASE, |
@@ -340,49 +413,41 @@ class Entrypoint { |
executableIds: executables[package]).then((x0) { |
try { |
x0; |
- completer0.complete(null); |
- } catch (e0) { |
- completer0.completeError(e0); |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
} |
- }, onError: (e1) { |
- completer0.completeError(e1); |
- }); |
- } catch (e2) { |
- completer0.completeError(e2); |
+ }, onError: completer0.completeError); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
} |
}); |
return completer0.future; |
}))).then((x1) { |
try { |
x1; |
- completer0.complete(null); |
- } catch (e1) { |
- completer0.completeError(e1); |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
} |
- }, onError: (e2) { |
- completer0.completeError(e2); |
- }); |
- } catch (e0) { |
- completer0.completeError(e0); |
+ }, onError: completer0.completeError); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
} |
- }, onError: (e3) { |
- completer0.completeError(e3); |
- }); |
- } catch (e4) { |
- completer0.completeError(e4); |
+ }, onError: completer0.completeError); |
+ } catch (e, s) { |
+ completer0.completeError(e, s); |
} |
}); |
return completer0.future; |
})).then((x1) { |
try { |
x1; |
- completer0.complete(null); |
- } catch (e1) { |
- completer0.completeError(e1); |
+ completer0.complete(); |
+ } catch (e0, s0) { |
+ completer0.completeError(e0, s0); |
} |
- }, onError: (e2) { |
- completer0.completeError(e2); |
- }); |
+ }, onError: completer0.completeError); |
} |
if (executables.isEmpty) { |
completer0.complete(null); |
@@ -397,31 +462,30 @@ class Entrypoint { |
join2(); |
} |
} |
- continue0(x4) { |
+ var trampoline0; |
+ continue0() { |
+ trampoline0 = null; |
if (it0.moveNext()) { |
- Future.wait([]).then((x2) { |
- var package = it0.current; |
- join4() { |
- continue0(null); |
- } |
- if (executables[package].isEmpty) { |
- executables.remove(package); |
- join4(); |
- } else { |
- join4(); |
- } |
- }); |
+ var package = it0.current; |
+ join4() { |
+ trampoline0 = continue0; |
+ } |
+ if (executables[package].isEmpty) { |
+ executables.remove(package); |
+ join4(); |
+ } else { |
+ join4(); |
+ } |
} else { |
- break0(null); |
+ break0(); |
} |
} |
- continue0(null); |
- } catch (e0) { |
- completer0.completeError(e0); |
+ trampoline0 = continue0; |
+ do trampoline0(); while (trampoline0 != null); |
+ } catch (e1, s1) { |
+ completer0.completeError(e1, s1); |
} |
- }, onError: (e3) { |
- completer0.completeError(e3); |
- }); |
+ }, onError: completer0.completeError); |
} |
if (!sdkMatches) { |
changed = null; |
@@ -436,24 +500,40 @@ class Entrypoint { |
} else { |
join0(); |
} |
- } catch (e4) { |
- completer0.completeError(e4); |
+ } 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( |
@@ -463,46 +543,87 @@ class Entrypoint { |
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) { |
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( |
@@ -511,13 +632,19 @@ class Entrypoint { |
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) { |
@@ -525,8 +652,16 @@ class Entrypoint { |
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]) { |
if (_packageGraph != null) return new Future.value(_packageGraph); |
+ |
return new Future.sync(() { |
if (result != null) { |
return Future.wait(result.packages.map((id) { |
@@ -556,13 +691,19 @@ class Entrypoint { |
return graph; |
}); |
} |
+ |
+ /// 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( |
@@ -572,19 +713,39 @@ class Entrypoint { |
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 []; |
@@ -594,6 +755,11 @@ class Entrypoint { |
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); |