Chromium Code Reviews| Index: pkg/polymer/lib/deploy.dart |
| diff --git a/pkg/polymer/lib/deploy.dart b/pkg/polymer/lib/deploy.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..483a73bf9f995e1fd1f5ad6ce8e0b7e19cf40932 |
| --- /dev/null |
| +++ b/pkg/polymer/lib/deploy.dart |
| @@ -0,0 +1,194 @@ |
| +// Copyright (c) 2013, 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. |
| + |
| +/** |
| + * Temporary deploy command used to create a version of the app that can be |
| + * compiled with dart2js and deployed. This library should go away once `pub |
| + * deploy` can be configured to run barback transformers. |
| + * |
| + * From an application package you can run this program by calling dart with a |
| + * 'package:' url to this file: |
| + * |
| + * dart package:polymer/deploy.dart |
| + */ |
| +library polymer.deploy; |
| + |
| +import 'dart:async'; |
| +import 'dart:io'; |
| +import 'dart:json' as json; |
| + |
| +import 'package:barback/barback.dart'; |
| +import 'package:path/path.dart' as path; |
| +import 'package:polymer/src/transform.dart' show phases; |
| +import 'package:stack_trace/stack_trace.dart'; |
| +import 'package:yaml/yaml.dart'; |
| + |
| +main() { |
| + print('polymer/deploy.dart: creating a deploy target for "$_currentPackage"'); |
| + var barback = new Barback(new _PolymerDeployProvider()); |
| + _initializeBarback(barback); |
| + _attachListeners(barback); |
| + _emitAllFiles(barback); |
| +} |
| + |
| +/** Tell barback which transformers to use and which assets to process. */ |
| +void _initializeBarback(Barback barback) { |
| + var assets = []; |
| + for (var package in _packageDirs.keys) { |
| + // Do not process packages like 'polymer' where there is nothing to do. |
| + if (_ignoredPackages.contains(package)) continue; |
| + barback.updateTransformers(package, phases); |
| + |
| + // notify barback to process anything under 'lib' and 'asset' |
| + for (var filepath in _listDir(package, 'lib')) { |
| + assets.add(new AssetId(package, filepath)); |
| + } |
| + |
| + for (var filepath in _listDir(package, 'asset')) { |
| + assets.add(new AssetId(package, filepath)); |
| + } |
| + } |
| + |
| + // In case of the current package, include also 'web'. |
| + for (var filepath in _listDir(_currentPackage, 'web')) { |
| + assets.add(new AssetId(_currentPackage, filepath)); |
| + } |
| + barback.updateSources(assets); |
| +} |
| + |
| +/** Return the relative path of each file under [subDir] in a [package]. */ |
| +Iterable<String> _listDir(String package, String subDir) { |
| + var packageDir = _packageDirs[package]; |
| + if (packageDir == null) return const []; |
| + var dir = new Directory(path.join(packageDir, subDir)); |
| + if (!dir.existsSync()) return const []; |
| + return dir.listSync(recursive: true, followLinks: false) |
| + .where((f) => f is File) |
| + .map((f) => path.relative(f.path, from: packageDir)); |
| +} |
| + |
| +/** Attach error listeners on [barback] so we can report errors. */ |
| +void _attachListeners(Barback barback) { |
| + // Listen for errors and results |
| + barback.errors.listen((e) { |
| + var trace = getAttachedStackTrace(e); |
| + if (trace != null) { |
| + print(Trace.format(trace)); |
| + } |
| + print('error running barback: $e'); |
| + exit(1); |
| + }); |
| + |
| + barback.results.listen((result) { |
| + if (!result.succeeded) { |
| + print("build failed with errors: ${result.errors}"); |
| + exit(1); |
| + } |
| + }); |
| +} |
| + |
| +/** Ensure [dirpath] exists. */ |
| +void _ensureDir(var dirpath) { |
| + new Directory(dirpath).createSync(recursive: true); |
| +} |
| + |
| +/** |
| + * Emits all outputs of [barback] and copies files that we didn't process (like |
| + * polymer's libraries). |
| + */ |
| +Future _emitAllFiles(Barback barback) { |
| + return barback.getAllAssets().then((assets) { |
| + // Copy all the assets we transformed |
| + var futures = []; |
| + for (var asset in assets) { |
| + var id = asset.id; |
| + var filepath; |
| + if (id.package == _currentPackage && id.path.startsWith('web/')) { |
| + filepath = path.join('out', id.path); |
|
Jennifer Messerly
2013/08/23 02:07:00
should the "out" folder be configurable?
Siggi Cherem (dart-lang)
2013/08/23 02:30:55
haha, I had a TODO for that, and somehow deleted i
|
| + } else if (id.path.startsWith('lib/')) { |
| + filepath = path.join('out', 'web', 'packages', id.package, |
| + id.path.substring(4)); |
| + } else { |
| + // TODO(sigmund): do something about other assets? |
| + continue; |
| + } |
| + |
| + _ensureDir(path.dirname(filepath)); |
| + futures.add(asset.readAsString() |
| + .then((content) => new File(filepath).writeAsStringSync(content))); |
| + } |
| + return Future.wait(futures); |
| + }).then((_) { |
| + // Copy also all the files we didn't process |
| + for (var package in _ignoredPackages) { |
| + for (var relpath in _listDir(package, 'lib')) { |
| + var inpath = path.join(_packageDirs[package], relpath); |
| + var outpath = path.join('out', 'web', 'packages', package, |
| + relpath.substring(4)); |
| + _ensureDir(path.dirname(outpath)); |
| + new File(outpath).writeAsBytesSync(new File(inpath).readAsBytesSync()); |
|
Jennifer Messerly
2013/08/23 02:07:00
this might be a good use case for async reading/wr
Siggi Cherem (dart-lang)
2013/08/23 02:30:55
Nice, Done.
|
| + } |
| + } |
| + print('Done! All files written under the "out" directory'); |
| + }); |
| +} |
| + |
| +/** A simple provider that reads files directly from the pub cache. */ |
| +class _PolymerDeployProvider implements PackageProvider { |
| + |
| + Iterable<String> get packages => _packageDirs.keys; |
| + _PolymerDeployProvider(); |
| + |
| + Future<Asset> getAsset(AssetId id) => |
| + new Future.value(new Asset.fromPath(id, path.join( |
| + _packageDirs[id.package], |
| + // Assets always use the posix style paths |
| + path.joinAll(path.posix.split(id.path))))); |
| +} |
| + |
| + |
| +/** The current package extracted from the pubspec.yaml file. */ |
| +String _currentPackage = () { |
| + var pubspec = new File('pubspec.yaml'); |
| + if (!pubspec.existsSync()) { |
| + print('error: pubspec.yaml file not found, please run this script from ' |
| + 'your package root directory.'); |
| + return null; |
| + } |
| + return loadYaml(pubspec.readAsStringSync())['name']; |
| +}(); |
| + |
| +/** |
| + * Maps package names to the path in the file system where to find the sources |
| + * of such package. This map will contain an entry for the current package and |
| + * everything it depends on (extracted via `pub list-pacakge-dirs`). |
| + */ |
| +Map<String, String> _packageDirs = () { |
| + var pub = path.join(path.dirname(new Options().executable), |
| + Platform.isWindows ? 'pub.bat' : 'pub'); |
| + var result = Process.runSync(pub, ['list-package-dirs']); |
| + if (result.exitCode != 0) { |
| + print("unexpected error invoking 'pub':"); |
| + print(result.stdout); |
| + print(result.stderr); |
| + exit(result.exitCode); |
| + } |
| + var map = json.parse(result.stdout)["packages"]; |
| + map.forEach((k, v) { map[k] = path.dirname(v); }); |
| + map[_currentPackage] = '.'; |
| + return map; |
| +}(); |
| + |
| +/** |
| + * Internal packages used by polymer which we can copy directly to the output |
| + * folder without having to process them with barback. |
| + */ |
| +Set<String> _ignoredPackages = |
| + (const [ 'analyzer_experimental', 'args', 'barback', 'browser', 'csslib', |
|
Jennifer Messerly
2013/08/23 02:07:00
hmm, I wonder if there's anywhere else we can get
Siggi Cherem (dart-lang)
2013/08/23 02:30:55
Added a TODO.
Pub makes it easy to do this for th
|
| + 'custom_element', 'fancy_syntax', 'html5lib', 'html_import', 'js', |
| + 'logging', 'mdv', 'meta', 'mutation_observer', 'observe', 'path', |
| + 'polymer', 'polymer_expressions', 'serialization', 'shadow_dom', |
| + 'source_maps', 'stack_trace', 'unittest', |
| + 'unmodifiable_collection', 'yaml' |
| + ]).toSet(); |