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

Side by Side Diff: utils/pub/entrypoint.dart

Issue 14297021: Move pub into sdk/lib/_internal. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Disallow package: imports of pub. Created 7 years, 8 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 | Annotate | Revision Log
« no previous file with comments | « utils/pub/directory_tree.dart ('k') | utils/pub/error_group.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library entrypoint;
6
7 import 'dart:async';
8
9 import 'package:pathos/path.dart' as path;
10
11 import 'io.dart';
12 import 'lock_file.dart';
13 import 'log.dart' as log;
14 import 'package.dart';
15 import 'pubspec.dart';
16 import 'sdk.dart' as sdk;
17 import 'system_cache.dart';
18 import 'utils.dart';
19 import 'version.dart';
20 import 'solver/version_solver.dart';
21
22 /// Pub operates over a directed graph of dependencies that starts at a root
23 /// "entrypoint" package. This is typically the package where the current
24 /// working directory is located. An entrypoint knows the [root] package it is
25 /// associated with and is responsible for managing the "packages" directory
26 /// for it.
27 ///
28 /// That directory contains symlinks to all packages used by an app. These links
29 /// point either to the [SystemCache] or to some other location on the local
30 /// filesystem.
31 ///
32 /// While entrypoints are typically applications, a pure library package may end
33 /// up being used as an entrypoint. Also, a single package may be used as an
34 /// entrypoint in one context but not in another. For example, a package that
35 /// contains a reusable library may not be the entrypoint when used by an app,
36 /// but may be the entrypoint when you're running its tests.
37 class Entrypoint {
38 /// The root package this entrypoint is associated with.
39 final Package root;
40
41 /// The system-wide cache which caches packages that need to be fetched over
42 /// the network.
43 final SystemCache cache;
44
45 /// Packages which are either currently being asynchronously installed to the
46 /// directory, or have already been installed.
47 final _installs = new Map<PackageId, Future<PackageId>>();
48
49 /// Loads the entrypoint from a package at [rootDir].
50 Entrypoint(String rootDir, SystemCache cache)
51 : root = new Package.load(null, rootDir, cache.sources),
52 cache = cache;
53
54 // TODO(rnystrom): Make this path configurable.
55 /// The path to the entrypoint's "packages" directory.
56 String get packagesDir => path.join(root.dir, 'packages');
57
58 /// Ensures that the package identified by [id] is installed to the directory.
59 /// Returns the resolved [PackageId].
60 ///
61 /// If this completes successfully, the package is guaranteed to be importable
62 /// using the `package:` scheme.
63 ///
64 /// This will automatically install the package to the system-wide cache as
65 /// well if it requires network access to retrieve (specifically, if
66 /// `id.source.shouldCache` is true).
67 ///
68 /// See also [installDependencies].
69 Future<PackageId> install(PackageId id) {
70 var pendingOrCompleted = _installs[id];
71 if (pendingOrCompleted != null) return pendingOrCompleted;
72
73 var packageDir = path.join(packagesDir, id.name);
74 var future = new Future.sync(() {
75 ensureDir(path.dirname(packageDir));
76
77 if (entryExists(packageDir)) {
78 // TODO(nweiz): figure out when to actually delete the directory, and
79 // when we can just re-use the existing symlink.
80 log.fine("Deleting package directory for ${id.name} before install.");
81 deleteEntry(packageDir);
82 }
83
84 if (id.source.shouldCache) {
85 return cache.install(id).then(
86 (pkg) => createPackageSymlink(id.name, pkg.dir, packageDir));
87 } else {
88 return id.source.install(id, packageDir).then((found) {
89 if (found) return null;
90 fail('Package ${id.name} not found in source "${id.source.name}".');
91 });
92 }
93 }).then((_) => id.resolved);
94
95 _installs[id] = future;
96
97 return future;
98 }
99
100 /// Installs all dependencies of the [root] package to its "packages"
101 /// directory, respecting the [LockFile] if present. Returns a [Future] that
102 /// completes when all dependencies are installed.
103 Future installDependencies() {
104 return new Future.sync(() {
105 return resolveVersions(cache.sources, root, lockFile: loadLockFile());
106 }).then(_installDependencies);
107 }
108
109 /// Installs the latest available versions of all dependencies of the [root]
110 /// package to its "package" directory, writing a new [LockFile]. Returns a
111 /// [Future] that completes when all dependencies are installed.
112 Future updateAllDependencies() {
113 return resolveVersions(cache.sources, root).then(_installDependencies);
114 }
115
116 /// Installs the latest available versions of [dependencies], while leaving
117 /// other dependencies as specified by the [LockFile] if possible. Returns a
118 /// [Future] that completes when all dependencies are installed.
119 Future updateDependencies(List<String> dependencies) {
120 return new Future.sync(() {
121 return resolveVersions(cache.sources, root,
122 lockFile: loadLockFile(), useLatest: dependencies);
123 }).then(_installDependencies);
124 }
125
126 /// Removes the old packages directory, installs all dependencies listed in
127 /// [result], and writes a [LockFile].
128 Future _installDependencies(SolveResult result) {
129 return new Future.sync(() {
130 if (!result.succeeded) throw result.error;
131
132 cleanDir(packagesDir);
133 return Future.wait(result.packages.map((id) {
134 if (id.isRoot) return new Future.value(id);
135 return install(id);
136 }).toList());
137 }).then((ids) {
138 _saveLockFile(ids);
139 _installSelfReference();
140 _linkSecondaryPackageDirs();
141 });
142 }
143
144 /// Loads the list of concrete package versions from the `pubspec.lock`, if it
145 /// exists. If it doesn't, this completes to an empty [LockFile].
146 LockFile loadLockFile() {
147 var lockFilePath = path.join(root.dir, 'pubspec.lock');
148 if (!entryExists(lockFilePath)) return new LockFile.empty();
149 return new LockFile.load(lockFilePath, cache.sources);
150 }
151
152 /// Saves a list of concrete package versions to the `pubspec.lock` file.
153 void _saveLockFile(List<PackageId> packageIds) {
154 var lockFile = new LockFile.empty();
155 for (var id in packageIds) {
156 if (!id.isRoot) lockFile.packages[id.name] = id;
157 }
158
159 var lockFilePath = path.join(root.dir, 'pubspec.lock');
160 writeTextFile(lockFilePath, lockFile.serialize());
161 }
162
163 /// Installs a self-referential symlink in the `packages` directory that will
164 /// allow a package to import its own files using `package:`.
165 void _installSelfReference() {
166 var linkPath = path.join(packagesDir, root.name);
167 // Create the symlink if it doesn't exist.
168 if (entryExists(linkPath)) return;
169 ensureDir(packagesDir);
170 createPackageSymlink(root.name, root.dir, linkPath,
171 isSelfLink: true, relative: true);
172 }
173
174 /// If `bin/`, `test/`, or `example/` directories exist, symlink `packages/`
175 /// into them so that their entrypoints can be run. Do the same for any
176 /// subdirectories of `test/` and `example/`.
177 void _linkSecondaryPackageDirs() {
178 var binDir = path.join(root.dir, 'bin');
179 var exampleDir = path.join(root.dir, 'example');
180 var testDir = path.join(root.dir, 'test');
181 var toolDir = path.join(root.dir, 'tool');
182 var webDir = path.join(root.dir, 'web');
183
184 if (dirExists(binDir)) _linkSecondaryPackageDir(binDir);
185 for (var dir in ['example', 'test', 'tool', 'web']) {
186 _linkSecondaryPackageDirsRecursively(path.join(root.dir, dir));
187 }
188 }
189
190 /// Creates a symlink to the `packages` directory in [dir] and all its
191 /// subdirectories.
192 void _linkSecondaryPackageDirsRecursively(String dir) {
193 if (!dirExists(dir)) return;
194 _linkSecondaryPackageDir(dir);
195 _listDirWithoutPackages(dir)
196 .where(dirExists)
197 .forEach(_linkSecondaryPackageDir);
198 }
199
200 // TODO(nweiz): roll this into [listDir] in io.dart once issue 4775 is fixed.
201 /// Recursively lists the contents of [dir], excluding hidden `.DS_Store`
202 /// files and `package` files.
203 List<String> _listDirWithoutPackages(dir) {
204 return flatten(listDir(dir).map((file) {
205 if (path.basename(file) == 'packages') return [];
206 if (!dirExists(file)) return [];
207 var fileAndSubfiles = [file];
208 fileAndSubfiles.addAll(_listDirWithoutPackages(file));
209 return fileAndSubfiles;
210 }));
211 }
212
213 /// Creates a symlink to the `packages` directory in [dir]. Will replace one
214 /// if already there.
215 void _linkSecondaryPackageDir(String dir) {
216 var symlink = path.join(dir, 'packages');
217 if (entryExists(symlink)) deleteEntry(symlink);
218 createSymlink(packagesDir, symlink, relative: true);
219 }
220 }
OLDNEW
« no previous file with comments | « utils/pub/directory_tree.dart ('k') | utils/pub/error_group.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698