Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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.barback.build_environment; | 5 library pub.barback.asset_environment; |
| 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:barback/barback.dart'; |
| 11 import 'package:path/path.dart' as path; | 11 import 'package:path/path.dart' as path; |
| 12 import 'package:stack_trace/stack_trace.dart'; | 12 import 'package:stack_trace/stack_trace.dart'; |
| 13 import 'package:watcher/watcher.dart'; | 13 import 'package:watcher/watcher.dart'; |
| 14 | 14 |
| 15 import '../entrypoint.dart'; | 15 import '../entrypoint.dart'; |
| 16 import '../io.dart'; | 16 import '../io.dart'; |
| 17 import '../log.dart' as log; | 17 import '../log.dart' as log; |
| 18 import '../package.dart'; | 18 import '../package.dart'; |
| 19 import '../package_graph.dart'; | 19 import '../package_graph.dart'; |
| 20 import '../sdk.dart' as sdk; | 20 import '../sdk.dart' as sdk; |
| 21 import 'admin_server.dart'; | 21 import 'admin_server.dart'; |
| 22 import 'build_directory.dart'; | 22 import 'barback_server.dart'; |
| 23 import 'dart_forwarding_transformer.dart'; | 23 import 'dart_forwarding_transformer.dart'; |
| 24 import 'dart2js_transformer.dart'; | 24 import 'dart2js_transformer.dart'; |
| 25 import 'load_all_transformers.dart'; | 25 import 'load_all_transformers.dart'; |
| 26 import 'pub_package_provider.dart'; | 26 import 'pub_package_provider.dart'; |
| 27 import 'barback_server.dart'; | 27 import 'source_directory.dart'; |
| 28 | 28 |
| 29 /// The entire "visible" state of the assets of a package and all of its | 29 /// The entire "visible" state of the assets of a package and all of its |
| 30 /// dependencies, taking into account the user's configuration when running pub. | 30 /// dependencies, taking into account the user's configuration when running pub. |
| 31 /// | 31 /// |
| 32 /// Where [PackageGraph] just describes the entrypoint's dependencies as | 32 /// Where [PackageGraph] just describes the entrypoint's dependencies as |
| 33 /// specified by pubspecs, this includes "transient" information like the mode | 33 /// specified by pubspecs, this includes "transient" information like the mode |
| 34 /// that the user is running pub in, or which directories they want to build. | 34 /// that the user is running pub in, or which directories they want to |
| 35 class BuildEnvironment { | 35 /// transform. |
| 36 class AssetEnvironment { | |
| 36 /// Creates a new build environment for working with the assets used by | 37 /// Creates a new build environment for working with the assets used by |
| 37 /// [entrypoint] and its dependencies. | 38 /// [entrypoint] and its dependencies. |
| 38 /// | 39 /// |
| 39 /// Spawns an HTTP server for each directory in [rootDirectories]. These | 40 /// Spawns an HTTP server for each directory in [rootDirectories]. These |
| 40 /// servers will be on [hostname] and have ports based on [basePort]. | 41 /// servers will be on [hostname] and have ports based on [basePort]. |
| 41 /// [basePort] itself is reserved for "web/" and `basePort + 1` is reserved | 42 /// [basePort] itself is reserved for "web/" and `basePort + 1` is reserved |
| 42 /// for "test/"; further ports will be allocated for other root directories as | 43 /// for "test/"; further ports will be allocated for other root directories as |
| 43 /// necessary. If [basePort] is zero, each server will have an ephemeral port. | 44 /// necessary. If [basePort] is zero, each server will have an ephemeral port. |
| 44 /// | 45 /// |
| 45 /// Loads all used transformers using [mode] (including dart2js if | 46 /// Loads all used transformers using [mode] (including dart2js if |
| 46 /// [useDart2JS] is true). | 47 /// [useDart2JS] is true). |
| 47 /// | 48 /// |
| 48 /// Includes [rootDirectories] in the root package, as well as "lib" and | 49 /// Includes [rootDirectories] in the root package, as well as "lib" and |
| 49 /// "asset". | 50 /// "asset". |
| 50 /// | 51 /// |
| 51 /// If [watcherType] is not [WatcherType.NONE], watches source assets for | 52 /// If [watcherType] is not [WatcherType.NONE], watches source assets for |
| 52 /// modification. | 53 /// modification. |
| 53 /// | 54 /// |
| 54 /// Returns a [Future] that completes to the environment once the inputs, | 55 /// Returns a [Future] that completes to the environment once the inputs, |
| 55 /// transformers, and server are loaded and ready. | 56 /// transformers, and server are loaded and ready. |
| 56 static Future<BuildEnvironment> create(Entrypoint entrypoint, | 57 static Future<AssetEnvironment> create(Entrypoint entrypoint, |
| 57 String hostname, int basePort, BarbackMode mode, WatcherType watcherType, | 58 String hostname, int basePort, BarbackMode mode, WatcherType watcherType, |
| 58 {bool useDart2JS: true}) { | 59 {bool useDart2JS: true}) { |
| 59 return entrypoint.loadPackageGraph().then((graph) { | 60 return entrypoint.loadPackageGraph().then((graph) { |
| 60 log.fine("Loaded package graph."); | 61 log.fine("Loaded package graph."); |
| 61 var barback = new Barback(new PubPackageProvider(graph)); | 62 var barback = new Barback(new PubPackageProvider(graph)); |
| 62 barback.log.listen(_log); | 63 barback.log.listen(_log); |
| 63 | 64 |
| 64 var environment = new BuildEnvironment._(graph, barback, mode, | 65 var environment = new AssetEnvironment._(graph, barback, mode, |
| 65 watcherType, hostname, basePort); | 66 watcherType, hostname, basePort); |
| 66 | 67 |
| 67 return environment._load(useDart2JS: useDart2JS) | 68 return environment._load(useDart2JS: useDart2JS) |
| 68 .then((_) => environment); | 69 .then((_) => environment); |
| 69 }); | 70 }); |
| 70 } | 71 } |
| 71 | 72 |
| 72 /// The server for the Web Socket API and admin interface. | 73 /// The server for the Web Socket API and admin interface. |
| 73 AdminServer _adminServer; | 74 AdminServer _adminServer; |
| 74 | 75 |
| 75 /// The public directories in the root package that are available for | 76 /// The public directories in the root package that are available part of the |
| 76 /// building, keyed by their root directory. | 77 /// build environment, keyed by their root directory. |
|
nweiz
2014/04/15 01:22:47
"available part of the build environment" -> "part
Bob Nystrom
2014/04/16 17:24:10
Done.
| |
| 77 final _directories = new Map<String, BuildDirectory>(); | 78 final _directories = new Map<String, SourceDirectory>(); |
| 78 | 79 |
| 79 /// The [Barback] instance used to process assets in this environment. | 80 /// The [Barback] instance used to process assets in this environment. |
| 80 final Barback barback; | 81 final Barback barback; |
| 81 | 82 |
| 82 /// The root package being built. | 83 /// The root package being built. |
| 83 Package get rootPackage => graph.entrypoint.root; | 84 Package get rootPackage => graph.entrypoint.root; |
| 84 | 85 |
| 85 /// The underlying [PackageGraph] being built. | 86 /// The underlying [PackageGraph] being built. |
| 86 final PackageGraph graph; | 87 final PackageGraph graph; |
| 87 | 88 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 113 /// When resumed, all pending source updates are sent to barback. | 114 /// When resumed, all pending source updates are sent to barback. |
| 114 /// | 115 /// |
| 115 /// This lets pub serve and pub build create an environment and bind several | 116 /// This lets pub serve and pub build create an environment and bind several |
| 116 /// servers before barback starts building and producing results | 117 /// servers before barback starts building and producing results |
| 117 /// asynchronously. | 118 /// asynchronously. |
| 118 /// | 119 /// |
| 119 /// If this is `null`, then the environment is "live" and all updates will | 120 /// If this is `null`, then the environment is "live" and all updates will |
| 120 /// go to barback immediately. | 121 /// go to barback immediately. |
| 121 Set<AssetId> _modifiedSources; | 122 Set<AssetId> _modifiedSources; |
| 122 | 123 |
| 123 BuildEnvironment._(this.graph, this.barback, this.mode, this._watcherType, | 124 AssetEnvironment._(this.graph, this.barback, this.mode, this._watcherType, |
| 124 this._hostname, this._basePort); | 125 this._hostname, this._basePort); |
| 125 | 126 |
| 126 /// Gets the built-in [Transformer]s that should be added to [package]. | 127 /// Gets the built-in [Transformer]s that should be added to [package]. |
| 127 /// | 128 /// |
| 128 /// Returns `null` if there are none. | 129 /// Returns `null` if there are none. |
| 129 Iterable<Transformer> getBuiltInTransformers(Package package) { | 130 Iterable<Transformer> getBuiltInTransformers(Package package) { |
| 130 // Built-in transformers only apply to the root package. | 131 // Built-in transformers only apply to the root package. |
| 131 if (package.name != rootPackage.name) return null; | 132 if (package.name != rootPackage.name) return null; |
| 132 | 133 |
| 133 // The built-in transformers are for dart2js and forwarding assets around | 134 // The built-in transformers are for dart2js and forwarding assets around |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 183 | 184 |
| 184 // If not using an ephemeral port, find the lowest-numbered available one. | 185 // If not using an ephemeral port, find the lowest-numbered available one. |
| 185 if (port != 0) { | 186 if (port != 0) { |
| 186 var boundPorts = _directories.values.map((directory) => directory.port) | 187 var boundPorts = _directories.values.map((directory) => directory.port) |
| 187 .toSet(); | 188 .toSet(); |
| 188 while (boundPorts.contains(port)) { | 189 while (boundPorts.contains(port)) { |
| 189 port++; | 190 port++; |
| 190 } | 191 } |
| 191 } | 192 } |
| 192 | 193 |
| 193 var buildDirectory = new BuildDirectory( | 194 var sourceDirectory = new SourceDirectory( |
| 194 this, rootDirectory, _hostname, port); | 195 this, rootDirectory, _hostname, port); |
| 195 _directories[rootDirectory] = buildDirectory; | 196 _directories[rootDirectory] = sourceDirectory; |
| 196 | 197 |
| 197 return _provideDirectorySources(rootPackage, rootDirectory) | 198 return _provideDirectorySources(rootPackage, rootDirectory) |
| 198 .then((subscription) { | 199 .then((subscription) { |
| 199 buildDirectory.watchSubscription = subscription; | 200 sourceDirectory.watchSubscription = subscription; |
| 200 return buildDirectory.serve(); | 201 return sourceDirectory.serve(); |
| 201 }); | 202 }); |
| 202 } | 203 } |
| 203 | 204 |
| 204 /// Stops the server bound to [rootDirectory]. | 205 /// Stops the server bound to [rootDirectory]. |
| 205 /// | 206 /// |
| 206 /// Also removes any source files within that directory from barback. Returns | 207 /// Also removes any source files within that directory from barback. Returns |
| 207 /// the URL of the unbound server, of `null` if [rootDirectory] was not | 208 /// the URL of the unbound server, of `null` if [rootDirectory] was not |
| 208 /// bound to a server. | 209 /// bound to a server. |
| 209 Future<Uri> unserveDirectory(String rootDirectory) { | 210 Future<Uri> unserveDirectory(String rootDirectory) { |
| 210 log.fine("Unserving $rootDirectory."); | 211 log.fine("Unserving $rootDirectory."); |
| 211 var directory = _directories.remove(rootDirectory); | 212 var directory = _directories.remove(rootDirectory); |
| 212 if (directory == null) return new Future.value(); | 213 if (directory == null) return new Future.value(); |
| 213 | 214 |
| 214 return directory.server.then((server) { | 215 return directory.server.then((server) { |
| 215 var url = server.url; | 216 var url = server.url; |
| 216 return directory.close().then((_) { | 217 return directory.close() |
| 217 // Remove the sources from barback, unless some other build directory | 218 .then((_) => _removeDirectorySources(rootDirectory)) |
| 218 // includes them. | 219 .then((_) => url); |
| 219 return _removeDirectorySources(rootDirectory); | |
| 220 }).then((_) => url); | |
| 221 }); | 220 }); |
| 222 } | 221 } |
| 223 | 222 |
| 224 /// Gets the build directory that contains [assetPath] within the entrypoint | 223 /// Gets the source directory that contains [assetPath] within the entrypoint |
| 225 /// package. | 224 /// package. |
| 226 /// | 225 /// |
| 227 /// If [assetPath] is not contained within a build directory, this will | 226 /// If [assetPath] is not contained within a source directory, this throws |
| 228 /// throw an exception. | 227 /// an exception. |
| 229 String getBuildDirectoryContaining(String assetPath) => | 228 String getSourceDirectoryContaining(String assetPath) => |
| 230 _directories.values | 229 _directories.values |
| 231 .firstWhere((dir) => path.isWithin(dir.directory, assetPath)) | 230 .firstWhere((dir) => path.isWithin(dir.directory, assetPath)) |
| 232 .directory; | 231 .directory; |
| 233 | 232 |
| 234 /// Return all URLs serving [assetPath] in this environment. | 233 /// Return all URLs serving [assetPath] in this environment. |
| 235 Future<List<Uri>> getUrlsForAssetPath(String assetPath) { | 234 Future<List<Uri>> getUrlsForAssetPath(String assetPath) { |
| 236 // Check the three (mutually-exclusive) places the path could be pointing. | 235 // Check the three (mutually-exclusive) places the path could be pointing. |
| 237 return _lookUpPathInServerRoot(assetPath).then((urls) { | 236 return _lookUpPathInServerRoot(assetPath).then((urls) { |
| 238 if (urls.isNotEmpty) return urls; | 237 if (urls.isNotEmpty) return urls; |
| 239 return _lookUpPathInPackagesDirectory(assetPath); | 238 return _lookUpPathInPackagesDirectory(assetPath); |
| (...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 489 Future _updateDirectorySources(Package package, String dir) { | 488 Future _updateDirectorySources(Package package, String dir) { |
| 490 return _listDirectorySources(package, dir).then((ids) { | 489 return _listDirectorySources(package, dir).then((ids) { |
| 491 if (_modifiedSources == null) { | 490 if (_modifiedSources == null) { |
| 492 barback.updateSources(ids); | 491 barback.updateSources(ids); |
| 493 } else { | 492 } else { |
| 494 _modifiedSources.addAll(ids); | 493 _modifiedSources.addAll(ids); |
| 495 } | 494 } |
| 496 }); | 495 }); |
| 497 } | 496 } |
| 498 | 497 |
| 499 /// Removes all of the files in [dir] in the root package from barback unless | 498 /// Removes all of the files in [dir] in the root package from barback. |
| 500 /// some other build directory still contains them. | |
| 501 Future _removeDirectorySources(String dir) { | 499 Future _removeDirectorySources(String dir) { |
| 502 return _listDirectorySources(rootPackage, dir, where: (relative) { | 500 return _listDirectorySources(rootPackage, dir).then((ids) { |
| 503 // TODO(rnystrom): This is O(n*m) where n is the number of files and | |
| 504 // m is the number of served directories. Consider something more | |
| 505 // optimal if this becomes a bottleneck. | |
| 506 // Don't remove a source if some other directory still includes it. | |
| 507 return !_directories.keys.any((dir) => path.isWithin(dir, relative)); | |
| 508 }).then((ids) { | |
| 509 if (_modifiedSources == null) { | 501 if (_modifiedSources == null) { |
| 510 barback.removeSources(ids); | 502 barback.removeSources(ids); |
| 511 } else { | 503 } else { |
| 512 _modifiedSources.removeAll(ids); | 504 _modifiedSources.removeAll(ids); |
| 513 } | 505 } |
| 514 }); | 506 }); |
| 515 } | 507 } |
| 516 | 508 |
| 517 /// Lists all of the source assets in [dir] inside [package]. | 509 /// Lists all of the source assets in [dir] inside [package]. |
| 518 /// | 510 /// |
| 519 /// For large packages, listing the contents is a performance bottleneck, so | 511 /// For large packages, listing the contents is a performance bottleneck, so |
| 520 /// this is optimized for our needs in here instead of using the more general | 512 /// this is optimized for our needs in here instead of using the more general |
| 521 /// but slower [listDir]. | 513 /// but slower [listDir]. |
| 522 /// | 514 Future<List<AssetId>> _listDirectorySources(Package package, String dir) { |
| 523 /// If [where] is given, then it is used to filter the resulting list of | |
| 524 /// packages. Only assets whose relative path within [package] matches that | |
| 525 /// will be included in the results. | |
| 526 Future<List<AssetId>> _listDirectorySources(Package package, String dir, | |
| 527 {bool where(String relativePath)}) { | |
| 528 var subdirectory = path.join(package.dir, dir); | 515 var subdirectory = path.join(package.dir, dir); |
| 529 if (!dirExists(subdirectory)) return new Future.value([]); | 516 if (!dirExists(subdirectory)) return new Future.value([]); |
| 530 | 517 |
| 531 return new Directory(subdirectory).list(recursive: true, followLinks: true) | 518 return new Directory(subdirectory).list(recursive: true, followLinks: true) |
| 532 .expand((entry) { | 519 .expand((entry) { |
| 533 // Skip directories and (broken) symlinks. | 520 // Skip directories and (broken) symlinks. |
| 534 if (entry is Directory) return []; | 521 if (entry is Directory) return []; |
| 535 if (entry is Link) return []; | 522 if (entry is Link) return []; |
| 536 | 523 |
| 537 var relative = path.normalize( | 524 var relative = path.normalize( |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 549 // When pub's dart2js transformer then tries to create the same file | 536 // When pub's dart2js transformer then tries to create the same file |
| 550 // name, we get a build error. To avoid that, just don't consider | 537 // name, we get a build error. To avoid that, just don't consider |
| 551 // that file to be a source. | 538 // that file to be a source. |
| 552 // TODO(rnystrom): Remove these when the Editor no longer generates | 539 // TODO(rnystrom): Remove these when the Editor no longer generates |
| 553 // .js files and users have had enough time that they no longer have | 540 // .js files and users have had enough time that they no longer have |
| 554 // these files laying around. See #15859. | 541 // these files laying around. See #15859. |
| 555 if (relative.endsWith(".dart.js")) return []; | 542 if (relative.endsWith(".dart.js")) return []; |
| 556 if (relative.endsWith(".dart.js.map")) return []; | 543 if (relative.endsWith(".dart.js.map")) return []; |
| 557 if (relative.endsWith(".dart.precompiled.js")) return []; | 544 if (relative.endsWith(".dart.precompiled.js")) return []; |
| 558 | 545 |
| 559 if (where != null && !where(relative)) return []; | |
| 560 | |
| 561 return [new AssetId(package.name, relative)]; | 546 return [new AssetId(package.name, relative)]; |
| 562 }).toList(); | 547 }).toList(); |
| 563 } | 548 } |
| 564 | 549 |
| 565 /// Adds a file watcher for [dir] within [package], if the directory exists | 550 /// Adds a file watcher for [dir] within [package], if the directory exists |
| 566 /// and the package needs watching. | 551 /// and the package needs watching. |
| 567 Future<StreamSubscription<WatchEvent>> _watchDirectorySources( | 552 Future<StreamSubscription<WatchEvent>> _watchDirectorySources( |
| 568 Package package, String dir) { | 553 Package package, String dir) { |
| 569 // If this package comes from a cached source, its contents won't change so | 554 // If this package comes from a cached source, its contents won't change so |
| 570 // we don't need to monitor it. `packageId` will be null for the | 555 // we don't need to monitor it. `packageId` will be null for the |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 724 String toString() => "polling"; | 709 String toString() => "polling"; |
| 725 } | 710 } |
| 726 | 711 |
| 727 class _NoneWatcherType implements WatcherType { | 712 class _NoneWatcherType implements WatcherType { |
| 728 const _NoneWatcherType(); | 713 const _NoneWatcherType(); |
| 729 | 714 |
| 730 DirectoryWatcher create(String directory) => null; | 715 DirectoryWatcher create(String directory) => null; |
| 731 | 716 |
| 732 String toString() => "none"; | 717 String toString() => "none"; |
| 733 } | 718 } |
| OLD | NEW |