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

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

Issue 1307853004: Improve the heuristics for "pub get" is needed. (Closed) Base URL: git@github.com:dart-lang/pub.git@master
Patch Set: Created 5 years, 3 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
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:path/path.dart' as path; 10 import 'package:package_config/packages_file.dart' as packages_file;
11 import 'package:path/path.dart' as p;
11 import 'package:barback/barback.dart'; 12 import 'package:barback/barback.dart';
12 13
13 import 'barback/asset_environment.dart'; 14 import 'barback/asset_environment.dart';
14 import 'io.dart'; 15 import 'io.dart';
15 import 'lock_file.dart'; 16 import 'lock_file.dart';
16 import 'log.dart' as log; 17 import 'log.dart' as log;
17 import 'package.dart'; 18 import 'package.dart';
18 import 'package_graph.dart'; 19 import 'package_graph.dart';
19 import 'sdk.dart' as sdk; 20 import 'sdk.dart' as sdk;
20 import 'solver/version_solver.dart'; 21 import 'solver/version_solver.dart';
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 /// Precompile any transformed dependencies of the entrypoint. 195 /// Precompile any transformed dependencies of the entrypoint.
195 /// 196 ///
196 /// If [changed] is passed, only dependencies whose contents might be changed 197 /// If [changed] is passed, only dependencies whose contents might be changed
197 /// if one of the given packages changes will be recompiled. 198 /// if one of the given packages changes will be recompiled.
198 Future precompileDependencies({Iterable<String> changed}) async { 199 Future precompileDependencies({Iterable<String> changed}) async {
199 if (changed != null) changed = changed.toSet(); 200 if (changed != null) changed = changed.toSet();
200 201
201 // Just precompile the debug version of a package. We're mostly interested 202 // Just precompile the debug version of a package. We're mostly interested
202 // in improving speed for development iteration loops, which usually use 203 // in improving speed for development iteration loops, which usually use
203 // debug mode. 204 // debug mode.
204 var depsDir = path.join('.pub', 'deps', 'debug'); 205 var depsDir = p.join('.pub', 'deps', 'debug');
205 206
206 var dependenciesToPrecompile = packageGraph.packages.values 207 var dependenciesToPrecompile = packageGraph.packages.values
207 .where((package) { 208 .where((package) {
208 if (package.pubspec.transformers.isEmpty) return false; 209 if (package.pubspec.transformers.isEmpty) return false;
209 if (packageGraph.isPackageMutable(package.name)) return false; 210 if (packageGraph.isPackageMutable(package.name)) return false;
210 if (!dirExists(path.join(depsDir, package.name))) return true; 211 if (!dirExists(p.join(depsDir, package.name))) return true;
211 if (changed == null) return true; 212 if (changed == null) return true;
212 213
213 /// Only recompile [package] if any of its transitive dependencies have 214 /// Only recompile [package] if any of its transitive dependencies have
214 /// changed. We check all transitive dependencies because it's possible 215 /// changed. We check all transitive dependencies because it's possible
215 /// that a transformer makes decisions based on their contents. 216 /// that a transformer makes decisions based on their contents.
216 return overlaps( 217 return overlaps(
217 packageGraph.transitiveDependencies(package.name) 218 packageGraph.transitiveDependencies(package.name)
218 .map((package) => package.name).toSet(), 219 .map((package) => package.name).toSet(),
219 changed); 220 changed);
220 }).map((package) => package.name).toSet(); 221 }).map((package) => package.name).toSet();
221 222
222 if (dirExists(depsDir)) { 223 if (dirExists(depsDir)) {
223 // Delete any cached dependencies that are going to be recached. 224 // Delete any cached dependencies that are going to be recached.
224 for (var package in dependenciesToPrecompile) { 225 for (var package in dependenciesToPrecompile) {
225 deleteEntry(path.join(depsDir, package)); 226 deleteEntry(p.join(depsDir, package));
226 } 227 }
227 228
228 // Also delete any cached dependencies that should no longer be cached. 229 // Also delete any cached dependencies that should no longer be cached.
229 for (var subdir in listDir(depsDir)) { 230 for (var subdir in listDir(depsDir)) {
230 var package = packageGraph.packages[path.basename(subdir)]; 231 var package = packageGraph.packages[p.basename(subdir)];
231 if (package == null || package.pubspec.transformers.isEmpty || 232 if (package == null || package.pubspec.transformers.isEmpty ||
232 packageGraph.isPackageMutable(package.name)) { 233 packageGraph.isPackageMutable(package.name)) {
233 deleteEntry(subdir); 234 deleteEntry(subdir);
234 } 235 }
235 } 236 }
236 } 237 }
237 238
238 if (dependenciesToPrecompile.isEmpty) return; 239 if (dependenciesToPrecompile.isEmpty) return;
239 240
240 try { 241 try {
241 await log.progress("Precompiling dependencies", () async { 242 await log.progress("Precompiling dependencies", () async {
242 var packagesToLoad = 243 var packagesToLoad =
243 unionAll(dependenciesToPrecompile.map( 244 unionAll(dependenciesToPrecompile.map(
244 packageGraph.transitiveDependencies)) 245 packageGraph.transitiveDependencies))
245 .map((package) => package.name).toSet(); 246 .map((package) => package.name).toSet();
246 247
247 var environment = await AssetEnvironment.create(this, BarbackMode.DEBUG, 248 var environment = await AssetEnvironment.create(this, BarbackMode.DEBUG,
248 packages: packagesToLoad, useDart2JS: false); 249 packages: packagesToLoad, useDart2JS: false);
249 250
250 /// Ignore barback errors since they'll be emitted via [getAllAssets] 251 /// Ignore barback errors since they'll be emitted via [getAllAssets]
251 /// below. 252 /// below.
252 environment.barback.errors.listen((_) {}); 253 environment.barback.errors.listen((_) {});
253 254
254 // TODO(nweiz): only get assets from [dependenciesToPrecompile] so as 255 // TODO(nweiz): only get assets from [dependenciesToPrecompile] so as
255 // not to trigger unnecessary lazy transformers. 256 // not to trigger unnecessary lazy transformers.
256 var assets = await environment.barback.getAllAssets(); 257 var assets = await environment.barback.getAllAssets();
257 await waitAndPrintErrors(assets.map((asset) async { 258 await waitAndPrintErrors(assets.map((asset) async {
258 if (!dependenciesToPrecompile.contains(asset.id.package)) return; 259 if (!dependenciesToPrecompile.contains(asset.id.package)) return;
259 260
260 var destPath = path.join( 261 var destPath = p.join(
261 depsDir, asset.id.package, path.fromUri(asset.id.path)); 262 depsDir, asset.id.package, p.fromUri(asset.id.path));
262 ensureDir(path.dirname(destPath)); 263 ensureDir(p.dirname(destPath));
263 await createFileFromStream(asset.read(), destPath); 264 await createFileFromStream(asset.read(), destPath);
264 })); 265 }));
265 266
266 log.message("Precompiled " + 267 log.message("Precompiled " +
267 toSentence(ordered(dependenciesToPrecompile).map(log.bold)) + "."); 268 toSentence(ordered(dependenciesToPrecompile).map(log.bold)) + ".");
268 }); 269 });
269 } catch (_) { 270 } catch (_) {
270 // TODO(nweiz): When barback does a better job of associating errors with 271 // TODO(nweiz): When barback does a better job of associating errors with
271 // assets (issue 19491), catch and handle compilation errors on a 272 // assets (issue 19491), catch and handle compilation errors on a
272 // per-package basis. 273 // per-package basis.
273 for (var package in dependenciesToPrecompile) { 274 for (var package in dependenciesToPrecompile) {
274 deleteEntry(path.join(depsDir, package)); 275 deleteEntry(p.join(depsDir, package));
275 } 276 }
276 rethrow; 277 rethrow;
277 } 278 }
278 } 279 }
279 280
280 /// Precompiles all executables from dependencies that don't transitively 281 /// Precompiles all executables from dependencies that don't transitively
281 /// depend on [this] or on a path dependency. 282 /// depend on [this] or on a path dependency.
282 Future precompileExecutables({Iterable<String> changed}) async { 283 Future precompileExecutables({Iterable<String> changed}) async {
283 if (changed != null) changed = changed.toSet(); 284 if (changed != null) changed = changed.toSet();
284 285
285 var binDir = path.join('.pub', 'bin'); 286 var binDir = p.join('.pub', 'bin');
286 var sdkVersionPath = path.join(binDir, 'sdk-version'); 287 var sdkVersionPath = p.join(binDir, 'sdk-version');
287 288
288 // If the existing executable was compiled with a different SDK, we need to 289 // If the existing executable was compiled with a different SDK, we need to
289 // recompile regardless of what changed. 290 // recompile regardless of what changed.
290 // TODO(nweiz): Use the VM to check this when issue 20802 is fixed. 291 // TODO(nweiz): Use the VM to check this when issue 20802 is fixed.
291 var sdkMatches = fileExists(sdkVersionPath) && 292 var sdkMatches = fileExists(sdkVersionPath) &&
292 readTextFile(sdkVersionPath) == "${sdk.version}\n"; 293 readTextFile(sdkVersionPath) == "${sdk.version}\n";
293 if (!sdkMatches) changed = null; 294 if (!sdkMatches) changed = null;
294 295
295 // Clean out any outdated snapshots. 296 // Clean out any outdated snapshots.
296 if (dirExists(binDir)) { 297 if (dirExists(binDir)) {
297 for (var entry in listDir(binDir)) { 298 for (var entry in listDir(binDir)) {
298 if (!dirExists(entry)) continue; 299 if (!dirExists(entry)) continue;
299 300
300 var package = path.basename(entry); 301 var package = p.basename(entry);
301 if (!packageGraph.packages.containsKey(package) || 302 if (!packageGraph.packages.containsKey(package) ||
302 packageGraph.isPackageMutable(package)) { 303 packageGraph.isPackageMutable(package)) {
303 deleteEntry(entry); 304 deleteEntry(entry);
304 } 305 }
305 } 306 }
306 } 307 }
307 308
308 var executables = new Map.fromIterable(root.immediateDependencies, 309 var executables = new Map.fromIterable(root.immediateDependencies,
309 key: (dep) => dep.name, 310 key: (dep) => dep.name,
310 value: (dep) => _executablesForPackage(dep.name, changed)); 311 value: (dep) => _executablesForPackage(dep.name, changed));
(...skipping 19 matching lines...) Expand all
330 executables.values.map((ids) => ids.toSet())); 331 executables.values.map((ids) => ids.toSet()));
331 var environment = await AssetEnvironment.create(this, BarbackMode.RELEASE, 332 var environment = await AssetEnvironment.create(this, BarbackMode.RELEASE,
332 packages: packagesToLoad, 333 packages: packagesToLoad,
333 entrypoints: executableIds, 334 entrypoints: executableIds,
334 useDart2JS: false); 335 useDart2JS: false);
335 environment.barback.errors.listen((error) { 336 environment.barback.errors.listen((error) {
336 log.error(log.red("Build error:\n$error")); 337 log.error(log.red("Build error:\n$error"));
337 }); 338 });
338 339
339 await waitAndPrintErrors(executables.keys.map((package) async { 340 await waitAndPrintErrors(executables.keys.map((package) async {
340 var dir = path.join(binDir, package); 341 var dir = p.join(binDir, package);
341 cleanDir(dir); 342 cleanDir(dir);
342 await environment.precompileExecutables(package, dir, 343 await environment.precompileExecutables(package, dir,
343 executableIds: executables[package]); 344 executableIds: executables[package]);
344 })); 345 }));
345 }); 346 });
346 } 347 }
347 348
348 /// Returns the list of all executable assets for [packageName] that should be 349 /// Returns the list of all executable assets for [packageName] that should be
349 /// precompiled. 350 /// precompiled.
350 /// 351 ///
(...skipping 15 matching lines...) Expand all
366 // If any of the package's dependencies changed, recompile the executables. 367 // If any of the package's dependencies changed, recompile the executables.
367 if (packageGraph.transitiveDependencies(packageName) 368 if (packageGraph.transitiveDependencies(packageName)
368 .any((package) => changed.contains(package.name))) { 369 .any((package) => changed.contains(package.name))) {
369 return executables; 370 return executables;
370 } 371 }
371 372
372 // If any executables don't exist, precompile them regardless of what 373 // If any executables don't exist, precompile them regardless of what
373 // changed. Since we delete the bin directory before recompiling, we need to 374 // changed. Since we delete the bin directory before recompiling, we need to
374 // recompile all executables. 375 // recompile all executables.
375 var executablesExist = executables.every((executable) => 376 var executablesExist = executables.every((executable) =>
376 fileExists(path.join('.pub', 'bin', packageName, 377 fileExists(p.join('.pub', 'bin', packageName,
377 "${path.url.basename(executable.path)}.snapshot"))); 378 "${p.url.basename(executable.path)}.snapshot")));
378 if (!executablesExist) return executables; 379 if (!executablesExist) return executables;
379 380
380 // Otherwise, we don't need to recompile. 381 // Otherwise, we don't need to recompile.
381 return []; 382 return [];
382 } 383 }
383 384
384 /// Makes sure the package at [id] is locally available. 385 /// Makes sure the package at [id] is locally available.
385 /// 386 ///
386 /// This automatically downloads the package to the system-wide cache as well 387 /// This automatically downloads the package to the system-wide cache as well
387 /// if it requires network access to retrieve (specifically, if the package's 388 /// if it requires network access to retrieve (specifically, if the package's
388 /// source is a [CachedSource]). 389 /// source is a [CachedSource]).
389 Future<PackageId> _get(PackageId id) { 390 Future<PackageId> _get(PackageId id) {
390 if (id.isRoot) return new Future.value(id); 391 if (id.isRoot) return new Future.value(id);
391 392
392 var source = cache.sources[id.source]; 393 var source = cache.sources[id.source];
393 return new Future.sync(() { 394 return new Future.sync(() {
394 if (!_packageSymlinks) { 395 if (!_packageSymlinks) {
395 if (source is! CachedSource) return null; 396 if (source is! CachedSource) return null;
396 return source.downloadToSystemCache(id); 397 return source.downloadToSystemCache(id);
397 } 398 }
398 399
399 var packageDir = path.join(packagesDir, id.name); 400 var packageDir = p.join(packagesDir, id.name);
400 if (entryExists(packageDir)) deleteEntry(packageDir); 401 if (entryExists(packageDir)) deleteEntry(packageDir);
401 return source.get(id, packageDir); 402 return source.get(id, packageDir);
402 }).then((_) => source.resolveId(id)); 403 }).then((_) => source.resolveId(id));
403 } 404 }
404 405
405 /// Throws a [DataError] if the `.packages` file doesn't exist or if it's 406 /// Throws a [DataError] if the `.packages` file doesn't exist or if it's
406 /// out-of-date relative to the lockfile or the pubspec. 407 /// out-of-date relative to the lockfile or the pubspec.
407 void assertUpToDate() { 408 void assertUpToDate() {
408 if (_inMemory) return; 409 if (_inMemory) return;
409 410
410 if (!entryExists(lockFilePath)) { 411 if (!entryExists(lockFilePath)) {
411 dataError('No pubspec.lock file found, please run "pub get" first.'); 412 dataError('No pubspec.lock file found, please run "pub get" first.');
412 } 413 }
413 414
414 if (!entryExists(packagesFile)) { 415 if (!entryExists(packagesFile)) {
415 dataError('No .packages file found, please run "pub get" first.'); 416 dataError('No .packages file found, please run "pub get" first.');
416 } 417 }
417 418
418 var packagesModified = new File(packagesFile).lastModifiedSync();
419 var pubspecModified = new File(pubspecPath).lastModifiedSync(); 419 var pubspecModified = new File(pubspecPath).lastModifiedSync();
420 if (packagesModified.isBefore(pubspecModified)) { 420 var lockFileModified = new File(lockFilePath).lastModifiedSync();
421 dataError('The pubspec.yaml file has changed since the .packages file ' 421
422 'was generated, please run "pub get" again.'); 422 var touchedLockFile = false;
423 if (lockFileModified.isBefore(pubspecModified)) {
424 if (_isLockFileUpToDate() && _arePackagesAvailable()) {
425 touchedLockFile = true;
426 touch(lockFilePath);
427 } else {
428 dataError('The pubspec.yaml file has changed since the pubspec.lock '
429 'file was generated, please run "pub get" again.');
430 }
423 } 431 }
424 432
425 var lockFileModified = new File(lockFilePath).lastModifiedSync(); 433 var packagesModified = new File(packagesFile).lastModifiedSync();
426 if (packagesModified.isBefore(lockFileModified)) { 434 if (packagesModified.isBefore(lockFileModified)) {
427 dataError('The pubspec.lock file has changed since the .packages file ' 435 if (_isPackagesFileUpToDate()) {
428 'was generated, please run "pub get" again.'); 436 touch(packagesFile);
437 } else {
438 dataError('The pubspec.lock file has changed since the .packages file '
439 'was generated, please run "pub get" again.');
440 }
441 } else if (touchedLockFile) {
442 touch(packagesFile);
429 } 443 }
430 } 444 }
431 445
446 /// Determines whether or not the lockfile is out of date with respect to the
447 /// pubspec.
448 ///
449 /// This will be `false` if the pubspec contains dependencies that are not in
450 /// the lockfile or that don't match what's in there.
451 bool _isLockFileUpToDate() {
452 return root.immediateDependencies.every((package) {
453 var locked = lockFile.packages[package.name];
454 if (locked == null) return false;
455
456 if (package.source != locked.source) return false;
457
458 if (!package.constraint.allows(locked.version)) return false;
459
460 var source = cache.sources[package.source];
461 if (source == null) return false;
462
463 return source.descriptionsEqual(package.description, locked.description);
464 });
465 }
466
467 /// Determines whether all of the packages in the lockfile are already
468 /// installed and available.
469 ///
470 /// Note: this assumes [_isLockFileUpToDate] has already been called and
471 /// returned `true`.
472 bool _arePackagesAvailable() {
473 return lockFile.packages.values.every((package) {
474 var source = cache.sources[package.source];
475
476 // This should only be called after [_isLockFileUpToDate] has returned
477 // `true`, which ensures all of the sources in the lock file are valid.
478 assert(source != null);
479
480 // We only care about cached sources. Uncached sources aren't "installed".
481 // If one of those is missing, we want to show the user the file not
482 // found error later since installing won't accomplish anything.
483 if (source is! CachedSource) return true;
484
485 // Get the directory.
486 var dir = source.getDirectory(package);
487 // See if the directory is there and looks like a package.
488 return dirExists(dir) && fileExists(p.join(dir, "pubspec.yaml"));
489 });
490 }
491
492 /// Determines whether or not the `.packages` file is out of date with respect
493 /// to the lockfile.
494 ///
495 /// This will be `false` if the packages file contains dependencies that are
496 /// not in the lockfile or that don't match what's in there.
497 bool _isPackagesFileUpToDate() {
498 var packages = packages_file.parse(
499 new File(packagesFile).readAsBytesSync(),
500 p.toUri(packagesFile));
501
502 return lockFile.packages.values.every((lockFileId) {
503 var source = cache.sources[lockFileId.source];
504
505 // It's very unlikely that the lockfile is invalid here, but it's not
506 // impossible—for example, the user may have a very old application
507 // package with a checked-in lockfile that's newer than the pubspec, but
508 // that contains sdk dependencies.
509 if (source == null) return false;
510
511 var packagesFileUri = packages[lockFileId.name];
512 if (packagesFileUri == null) return false;
513
514 // Pub only generates "file:" and relative URIs.
515 if (packagesFileUri.scheme != 'file' &&
516 packagesFileUri.scheme.isNotEmpty) {
517 return false;
518 }
519
520 // Get the dirname of the .packages path, since it's pointing to lib/.
521 var packagesFilePath = p.dirname(
522 p.join(root.dir, p.fromUri(packagesFileUri)));
523 var lockFilePath = p.join(root.dir, source.getDirectory(lockFileId));
524
525 // For cached sources, Make sure the directory exists and looks like a
Bob Nystrom 2015/08/31 19:32:21 "Make" -> "make"
nweiz 2015/08/31 20:41:29 Done.
526 // package. This is also done by [_arePackagesAvailable] but that may not
527 // be run if the lockfile is newer than the pubspec.
528 if (source is CachedSource &&
529 !dirExists(packagesFilePath) ||
530 !fileExists(p.join(packagesFilePath, "pubspec.yaml"))) {
531 return false;
532 }
533
534 // Make sure that the packages file agrees with the lock file about the
535 // path to the package.
536 return p.normalize(packagesFilePath) == p.normalize(lockFilePath);
537 });
538 }
539
432 /// Saves a list of concrete package versions to the `pubspec.lock` file. 540 /// Saves a list of concrete package versions to the `pubspec.lock` file.
433 void _saveLockFile(List<PackageId> packageIds) { 541 void _saveLockFile(List<PackageId> packageIds) {
434 _lockFile = new LockFile(packageIds, cache.sources); 542 _lockFile = new LockFile(packageIds, cache.sources);
435 var lockFilePath = root.path('pubspec.lock'); 543 var lockFilePath = root.path('pubspec.lock');
436 writeTextFile(lockFilePath, _lockFile.serialize(root.dir)); 544 writeTextFile(lockFilePath, _lockFile.serialize(root.dir));
437 } 545 }
438 546
439 /// Creates a self-referential symlink in the `packages` directory that allows 547 /// Creates a self-referential symlink in the `packages` directory that allows
440 /// a package to import its own files using `package:`. 548 /// a package to import its own files using `package:`.
441 void _linkSelf() { 549 void _linkSelf() {
442 var linkPath = path.join(packagesDir, root.name); 550 var linkPath = p.join(packagesDir, root.name);
443 // Create the symlink if it doesn't exist. 551 // Create the symlink if it doesn't exist.
444 if (entryExists(linkPath)) return; 552 if (entryExists(linkPath)) return;
445 ensureDir(packagesDir); 553 ensureDir(packagesDir);
446 createPackageSymlink(root.name, root.dir, linkPath, 554 createPackageSymlink(root.name, root.dir, linkPath,
447 isSelfLink: true, relative: true); 555 isSelfLink: true, relative: true);
448 } 556 }
449 557
450 /// If [packageSymlinks] is true, add "packages" directories to the whitelist 558 /// If [packageSymlinks] is true, add "packages" directories to the whitelist
451 /// of directories that may contain Dart entrypoints. 559 /// of directories that may contain Dart entrypoints.
452 /// 560 ///
(...skipping 22 matching lines...) Expand all
475 _listDirWithoutPackages(dir) 583 _listDirWithoutPackages(dir)
476 .where(dirExists) 584 .where(dirExists)
477 .forEach(_linkOrDeleteSecondaryPackageDir); 585 .forEach(_linkOrDeleteSecondaryPackageDir);
478 } 586 }
479 587
480 // TODO(nweiz): roll this into [listDir] in io.dart once issue 4775 is fixed. 588 // TODO(nweiz): roll this into [listDir] in io.dart once issue 4775 is fixed.
481 /// Recursively lists the contents of [dir], excluding hidden `.DS_Store` 589 /// Recursively lists the contents of [dir], excluding hidden `.DS_Store`
482 /// files and `package` files. 590 /// files and `package` files.
483 List<String> _listDirWithoutPackages(dir) { 591 List<String> _listDirWithoutPackages(dir) {
484 return flatten(listDir(dir).map((file) { 592 return flatten(listDir(dir).map((file) {
485 if (path.basename(file) == 'packages') return []; 593 if (p.basename(file) == 'packages') return [];
486 if (!dirExists(file)) return []; 594 if (!dirExists(file)) return [];
487 var fileAndSubfiles = [file]; 595 var fileAndSubfiles = [file];
488 fileAndSubfiles.addAll(_listDirWithoutPackages(file)); 596 fileAndSubfiles.addAll(_listDirWithoutPackages(file));
489 return fileAndSubfiles; 597 return fileAndSubfiles;
490 })); 598 }));
491 } 599 }
492 600
493 /// If [packageSymlinks] is true, creates a symlink to the "packages" 601 /// If [packageSymlinks] is true, creates a symlink to the "packages"
494 /// directory in [dir]. 602 /// directory in [dir].
495 /// 603 ///
496 /// Otherwise, deletes a "packages" directories in [dir] if one exists. 604 /// Otherwise, deletes a "packages" directories in [dir] if one exists.
497 void _linkOrDeleteSecondaryPackageDir(String dir) { 605 void _linkOrDeleteSecondaryPackageDir(String dir) {
498 var symlink = path.join(dir, 'packages'); 606 var symlink = p.join(dir, 'packages');
499 if (entryExists(symlink)) deleteEntry(symlink); 607 if (entryExists(symlink)) deleteEntry(symlink);
500 if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true); 608 if (_packageSymlinks) createSymlink(packagesDir, symlink, relative: true);
501 } 609 }
502 } 610 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698