| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 barback.asset_cascade; | 5 library barback.asset_cascade; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:stack_trace/stack_trace.dart'; | |
| 11 | |
| 12 import 'asset.dart'; | 10 import 'asset.dart'; |
| 13 import 'asset_id.dart'; | 11 import 'asset_id.dart'; |
| 14 import 'asset_node.dart'; | 12 import 'asset_node.dart'; |
| 13 import 'build_result.dart'; |
| 15 import 'cancelable_future.dart'; | 14 import 'cancelable_future.dart'; |
| 16 import 'errors.dart'; | 15 import 'errors.dart'; |
| 17 import 'change_batch.dart'; | 16 import 'change_batch.dart'; |
| 18 import 'package_graph.dart'; | 17 import 'package_graph.dart'; |
| 19 import 'phase.dart'; | 18 import 'phase.dart'; |
| 20 import 'transformer.dart'; | 19 import 'transformer.dart'; |
| 21 import 'utils.dart'; | 20 import 'utils.dart'; |
| 22 | 21 |
| 23 /// The asset cascade for an individual package. | 22 /// The asset cascade for an individual package. |
| 24 /// | 23 /// |
| 25 /// This keeps track of which [Transformer]s are applied to which assets, and | 24 /// This keeps track of which [Transformer]s are applied to which assets, and |
| 26 /// re-runs those transformers when their dependencies change. The transformed | 25 /// re-runs those transformers when their dependencies change. The transformed |
| 27 /// assets are accessible via [getAssetById]. | 26 /// asset nodes are accessible via [getAssetNode]. |
| 28 /// | 27 /// |
| 29 /// A cascade consists of one or more [Phases], each of which has one or more | 28 /// A cascade consists of one or more [Phases], each of which has one or more |
| 30 /// [Transformer]s that run in parallel, potentially on the same inputs. The | 29 /// [Transformer]s that run in parallel, potentially on the same inputs. The |
| 31 /// inputs of the first phase are the source assets for this cascade's package. | 30 /// inputs of the first phase are the source assets for this cascade's package. |
| 32 /// The inputs of each successive phase are the outputs of the previous phase, | 31 /// The inputs of each successive phase are the outputs of the previous phase, |
| 33 /// as well as any assets that haven't yet been transformed. | 32 /// as well as any assets that haven't yet been transformed. |
| 34 class AssetCascade { | 33 class AssetCascade { |
| 35 /// The name of the package whose assets are managed. | 34 /// The name of the package whose assets are managed. |
| 36 final String package; | 35 final String package; |
| 37 | 36 |
| 38 /// The [PackageGraph] that tracks all [AssetCascade]s for all dependencies of | 37 /// The [PackageGraph] that tracks all [AssetCascade]s for all dependencies of |
| 39 /// the current app. | 38 /// the current app. |
| 40 final PackageGraph _graph; | 39 final PackageGraph graph; |
| 41 | 40 |
| 42 /// The controllers for the [AssetNode]s that provide information about this | 41 /// The controllers for the [AssetNode]s that provide information about this |
| 43 /// cascade's package's source assets. | 42 /// cascade's package's source assets. |
| 44 final _sourceControllerMap = new Map<AssetId, AssetNodeController>(); | 43 final _sourceControllerMap = new Map<AssetId, AssetNodeController>(); |
| 45 | 44 |
| 46 /// Futures for source assets that are currently being loaded. | 45 /// Futures for source assets that are currently being loaded. |
| 47 /// | 46 /// |
| 48 /// These futures are cancelable so that if an asset is updated after a load | 47 /// These futures are cancelable so that if an asset is updated after a load |
| 49 /// has been kicked off, the previous load can be ignored in favor of a new | 48 /// has been kicked off, the previous load can be ignored in favor of a new |
| 50 /// one. | 49 /// one. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 82 | 81 |
| 83 /// Whether any source assets have been updated or removed since processing | 82 /// Whether any source assets have been updated or removed since processing |
| 84 /// last began. | 83 /// last began. |
| 85 var _newChanges = false; | 84 var _newChanges = false; |
| 86 | 85 |
| 87 /// Creates a new [AssetCascade]. | 86 /// Creates a new [AssetCascade]. |
| 88 /// | 87 /// |
| 89 /// It loads source assets within [package] using [provider] and then uses | 88 /// It loads source assets within [package] using [provider] and then uses |
| 90 /// [transformerPhases] to generate output files from them. | 89 /// [transformerPhases] to generate output files from them. |
| 91 //TODO(rnystrom): Better way of specifying transformers and their ordering. | 90 //TODO(rnystrom): Better way of specifying transformers and their ordering. |
| 92 AssetCascade(this._graph, this.package, | 91 AssetCascade(this.graph, this.package, |
| 93 Iterable<Iterable<Transformer>> transformerPhases) { | 92 Iterable<Iterable<Transformer>> transformerPhases) { |
| 94 // Flatten the phases to a list so we can traverse backwards to wire up | 93 // Flatten the phases to a list so we can traverse backwards to wire up |
| 95 // each phase to its next. | 94 // each phase to its next. |
| 96 var phases = transformerPhases.toList(); | 95 var phases = transformerPhases.toList(); |
| 97 | 96 |
| 98 // Each phase writes its outputs as inputs to the next phase after it. | 97 // Each phase writes its outputs as inputs to the next phase after it. |
| 99 // Add a phase at the end for the final outputs of the last phase. | 98 // Add a phase at the end for the final outputs of the last phase. |
| 100 phases.add([]); | 99 phases.add([]); |
| 101 | 100 |
| 102 Phase nextPhase = null; | 101 Phase nextPhase = null; |
| 103 for (var transformers in phases.reversed) { | 102 for (var transformers in phases.reversed) { |
| 104 nextPhase = new Phase(this, _phases.length, transformers.toList(), | 103 nextPhase = new Phase(this, _phases.length, transformers.toList(), |
| 105 nextPhase); | 104 nextPhase); |
| 106 nextPhase.onDirty.listen((_) { | 105 nextPhase.onDirty.listen((_) { |
| 107 _newChanges = true; | 106 _newChanges = true; |
| 108 _waitForProcess(); | 107 _waitForProcess(); |
| 109 }); | 108 }); |
| 110 _phases.insert(0, nextPhase); | 109 _phases.insert(0, nextPhase); |
| 111 } | 110 } |
| 112 } | 111 } |
| 113 | 112 |
| 114 /// Gets the asset identified by [id]. | 113 /// Gets the asset identified by [id]. |
| 115 /// | 114 /// |
| 116 /// If [id] is for a generated or transformed asset, this will wait until | 115 /// If [id] is for a generated or transformed asset, this will wait until it |
| 117 /// it has been created and return it. If the asset cannot be found, throws | 116 /// has been created and return it. If the asset cannot be found, returns |
| 118 /// [AssetNotFoundException]. | 117 /// null. |
| 119 Future<Asset> getAssetById(AssetId id) { | 118 Future<AssetNode> getAssetNode(AssetId id) { |
| 120 assert(id.package == package); | 119 assert(id.package == package); |
| 121 | 120 |
| 122 // TODO(rnystrom): Waiting for the entire build to complete is unnecessary | 121 // TODO(rnystrom): Waiting for the entire build to complete is unnecessary |
| 123 // in some cases. Should optimize: | 122 // in some cases. Should optimize: |
| 124 // * [id] may be generated before the compilation is finished. We should | 123 // * [id] may be generated before the compilation is finished. We should |
| 125 // be able to quickly check whether there are any more in-place | 124 // be able to quickly check whether there are any more in-place |
| 126 // transformations that can be run on it. If not, we can return it early. | 125 // transformations that can be run on it. If not, we can return it early. |
| 127 // * If [id] has never been generated and all active transformers provide | 126 // * If [id] has never been generated and all active transformers provide |
| 128 // metadata about the file names of assets it can emit, we can prove that | 127 // metadata about the file names of assets it can emit, we can prove that |
| 129 // none of them can emit [id] and fail early. | 128 // none of them can emit [id] and fail early. |
| 130 return newFuture(() { | 129 return newFuture(() { |
| 131 var node = _getAssetNode(id); | 130 var node = _getAssetNode(id); |
| 132 | 131 |
| 133 // If the requested asset is available, we can just return it. | 132 // If the requested asset is available, we can just return it. |
| 134 if (node != null) return node.asset; | 133 if (node != null) return node; |
| 135 | 134 |
| 136 // If there's a build running, that build might generate the asset, so we | 135 // If there's a build running, that build might generate the asset, so we |
| 137 // wait for it to complete and then try again. | 136 // wait for it to complete and then try again. |
| 138 if (_processDone != null) { | 137 if (_processDone != null) { |
| 139 return _processDone.then((_) => getAssetById(id)); | 138 return _processDone.then((_) => getAssetNode(id)); |
| 140 } | 139 } |
| 141 | 140 |
| 142 // If the asset hasn't been built and nothing is building now, the asset | 141 // If the asset hasn't been built and nothing is building now, the asset |
| 143 // won't be generated, so we throw an error. | 142 // won't be generated, so we return null. |
| 144 throw new AssetNotFoundException(id); | 143 return null; |
| 145 }); | 144 }); |
| 146 } | 145 } |
| 147 | 146 |
| 148 // Returns the post-transformation asset node for [id], if one is available. | 147 // Returns the post-transformation asset node for [id], if one is available. |
| 149 // | 148 // |
| 150 // This will only return a node that has an asset available, and only if that | 149 // This will only return a node that has an asset available, and only if that |
| 151 // node is guaranteed not to be consumed by any transforms. If the phase is | 150 // node is guaranteed not to be consumed by any transforms. If the phase is |
| 152 // still working to figure out if a node will be consumed by a transformer, | 151 // still working to figure out if a node will be consumed by a transformer, |
| 153 // that node won't be returned. | 152 // that node won't be returned. |
| 154 AssetNode _getAssetNode(AssetId id) { | 153 AssetNode _getAssetNode(AssetId id) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 176 } else { | 175 } else { |
| 177 _sourceControllerMap[id] = new AssetNodeController(id); | 176 _sourceControllerMap[id] = new AssetNodeController(id); |
| 178 _phases.first.addInput(_sourceControllerMap[id].node); | 177 _phases.first.addInput(_sourceControllerMap[id].node); |
| 179 } | 178 } |
| 180 | 179 |
| 181 // If this source was already loading, cancel the old load, since it may | 180 // If this source was already loading, cancel the old load, since it may |
| 182 // return out-of-date contents for the asset. | 181 // return out-of-date contents for the asset. |
| 183 if (_loadingSources.containsKey(id)) _loadingSources[id].cancel(); | 182 if (_loadingSources.containsKey(id)) _loadingSources[id].cancel(); |
| 184 | 183 |
| 185 _loadingSources[id] = | 184 _loadingSources[id] = |
| 186 new CancelableFuture<Asset>(_graph.provider.getAsset(id)); | 185 new CancelableFuture<Asset>(graph.provider.getAsset(id)); |
| 187 _loadingSources[id].whenComplete(() { | 186 _loadingSources[id].whenComplete(() { |
| 188 _loadingSources.remove(id); | 187 _loadingSources.remove(id); |
| 189 }).then((asset) { | 188 }).then((asset) { |
| 190 var controller = _sourceControllerMap[id].setAvailable(asset); | 189 var controller = _sourceControllerMap[id].setAvailable(asset); |
| 191 }).catchError((error) { | 190 }).catchError((error) { |
| 192 reportError(error); | 191 reportError(error); |
| 193 | 192 |
| 194 // TODO(nweiz): propagate error information through asset nodes. | 193 // TODO(nweiz): propagate error information through asset nodes. |
| 195 _sourceControllerMap.remove(id).setRemoved(); | 194 _sourceControllerMap.remove(id).setRemoved(); |
| 196 }); | 195 }); |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 | 264 |
| 266 // Otherwise, everything is done. | 265 // Otherwise, everything is done. |
| 267 return; | 266 return; |
| 268 } | 267 } |
| 269 | 268 |
| 270 // Process that phase and then loop onto the next. | 269 // Process that phase and then loop onto the next. |
| 271 return future.then((_) => _process()); | 270 return future.then((_) => _process()); |
| 272 }); | 271 }); |
| 273 } | 272 } |
| 274 } | 273 } |
| 275 | |
| 276 /// An event indicating that the cascade has finished building all assets. | |
| 277 /// | |
| 278 /// A build can end either in success or failure. If there were no errors during | |
| 279 /// the build, it's considered to be a success; any errors render it a failure, | |
| 280 /// although individual assets may still have built successfully. | |
| 281 class BuildResult { | |
| 282 /// All errors that occurred during the build. | |
| 283 final List errors; | |
| 284 | |
| 285 /// `true` if the build succeeded. | |
| 286 bool get succeeded => errors.isEmpty; | |
| 287 | |
| 288 BuildResult(Iterable errors) | |
| 289 : errors = errors.toList(); | |
| 290 | |
| 291 /// Creates a build result indicating a successful build. | |
| 292 /// | |
| 293 /// This equivalent to a build result with no errors. | |
| 294 BuildResult.success() | |
| 295 : this([]); | |
| 296 | |
| 297 String toString() { | |
| 298 if (succeeded) return "success"; | |
| 299 | |
| 300 return "errors:\n" + errors.map((error) { | |
| 301 var stackTrace = getAttachedStackTrace(error); | |
| 302 if (stackTrace != null) stackTrace = new Trace.from(stackTrace); | |
| 303 | |
| 304 var msg = new StringBuffer(); | |
| 305 msg.write(prefixLines(error.toString())); | |
| 306 if (stackTrace != null) { | |
| 307 msg.write("\n\n"); | |
| 308 msg.write("Stack trace:\n"); | |
| 309 msg.write(prefixLines(stackTrace.toString())); | |
| 310 } | |
| 311 return msg.toString(); | |
| 312 }).join("\n\n"); | |
| 313 } | |
| 314 } | |
| OLD | NEW |