| Index: pkg/polymer/lib/component_build.dart
|
| diff --git a/pkg/polymer/lib/component_build.dart b/pkg/polymer/lib/component_build.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..316124eb45feb2614fb2f3e12b31841d9e559988
|
| --- /dev/null
|
| +++ b/pkg/polymer/lib/component_build.dart
|
| @@ -0,0 +1,166 @@
|
| +// Copyright (c) 2012, 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.
|
| +
|
| +/**
|
| + * Common logic to make it easy to create a `build.dart` for your project.
|
| + *
|
| + * The `build.dart` script is invoked automatically by the Editor whenever a
|
| + * file in the project changes. It must be placed in the root of a project
|
| + * (where pubspec.yaml lives) and should be named exactly 'build.dart'.
|
| + *
|
| + * A common `build.dart` would look as follows:
|
| + *
|
| + * import 'dart:io';
|
| + * import 'package:polymer/component_build.dart';
|
| + *
|
| + * main() => build(new Options().arguments, ['web/index.html']);
|
| + */
|
| +library build_utils;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:io';
|
| +import 'dart:json' as json;
|
| +import 'package:args/args.dart';
|
| +
|
| +import 'dwc.dart' as dwc;
|
| +import 'src/utils.dart';
|
| +import 'src/compiler_options.dart';
|
| +
|
| +/**
|
| + * Set up 'build.dart' to compile with the dart web components compiler every
|
| + * [entryPoints] listed. On clean commands, the directory where [entryPoints]
|
| + * live will be scanned for generated files to delete them.
|
| + */
|
| +// TODO(jmesserly): we need a better way to automatically detect input files
|
| +Future<List<dwc.CompilerResult>> build(List<String> arguments,
|
| + List<String> entryPoints,
|
| + {bool printTime: true, bool shouldPrint: true}) {
|
| + bool useColors = stdioType(stdout) == StdioType.TERMINAL;
|
| + return asyncTime('Total time', () {
|
| + var args = _processArgs(arguments);
|
| + var tasks = new FutureGroup();
|
| + var lastTask = new Future.value(null);
|
| + tasks.add(lastTask);
|
| +
|
| + var changedFiles = args["changed"];
|
| + var removedFiles = args["removed"];
|
| + var cleanBuild = args["clean"];
|
| + var machineFormat = args["machine"];
|
| + // Also trigger a full build if the script was run from the command line
|
| + // with no arguments
|
| + var fullBuild = args["full"] || (!machineFormat && changedFiles.isEmpty &&
|
| + removedFiles.isEmpty && !cleanBuild);
|
| +
|
| + var options = CompilerOptions.parse(args.rest, checkUsage: false);
|
| +
|
| + // [outputOnlyDirs] contains directories known to only have output files.
|
| + // When outputDir is not specified, we create a new directory which only
|
| + // contains output files. If options.outputDir is specified, we don't know
|
| + // if the output directory may also have input files. In which case,
|
| + // [_handleCleanCommand] and [_isInputFile] are more conservative.
|
| + //
|
| + // TODO(sigmund): get rid of this. Instead, use the compiler to understand
|
| + // which files are input or output files.
|
| + var outputOnlyDirs = options.outputDir == null ? []
|
| + : entryPoints.map((e) => _outDir(e)).toList();
|
| +
|
| + if (cleanBuild) {
|
| + _handleCleanCommand(outputOnlyDirs);
|
| + } else if (fullBuild
|
| + || changedFiles.any((f) => _isInputFile(f, outputOnlyDirs))
|
| + || removedFiles.any((f) => _isInputFile(f, outputOnlyDirs))) {
|
| + for (var file in entryPoints) {
|
| + var dwcArgs = new List.from(args.rest);
|
| + if (machineFormat) dwcArgs.add('--json_format');
|
| + if (!useColors) dwcArgs.add('--no-colors');
|
| + // We'll set 'out/' as the out folder, unless an output directory was
|
| + // already specified in the command line.
|
| + if (options.outputDir == null) dwcArgs.addAll(['-o', _outDir(file)]);
|
| + dwcArgs.add(file);
|
| + // Chain tasks to that we run one at a time.
|
| + lastTask = lastTask.then((_) => dwc.run(dwcArgs, printTime: printTime,
|
| + shouldPrint: shouldPrint));
|
| + if (machineFormat) {
|
| + lastTask = lastTask.then((res) {
|
| + appendMessage(Map jsonMessage) {
|
| + var message = json.stringify([jsonMessage]);
|
| + if (shouldPrint) print(message);
|
| + res.messages.add(message);
|
| + }
|
| + // Print for the Editor messages about mappings and generated files
|
| + res.outputs.forEach((out, input) {
|
| + if (out.endsWith(".html") && input != null) {
|
| + appendMessage({
|
| + "method": "mapping",
|
| + "params": {"from": input, "to": out},
|
| + });
|
| + }
|
| + appendMessage({"method": "generated", "params": {"file": out}});
|
| + });
|
| + return res;
|
| + });
|
| + }
|
| + tasks.add(lastTask);
|
| + }
|
| + }
|
| + return tasks.future.then((r) => r.where((v) => v != null));
|
| + }, printTime: printTime, useColors: useColors);
|
| +}
|
| +
|
| +String _outDir(String file) => path.join(path.dirname(file), 'out');
|
| +
|
| +/** Tell whether [filePath] is a generated file. */
|
| +bool _isGeneratedFile(String filePath, List<String> outputOnlyDirs) {
|
| + var dirPrefix = path.dirname(filePath);
|
| + for (var outDir in outputOnlyDirs) {
|
| + if (dirPrefix.startsWith(outDir)) return true;
|
| + }
|
| + return path.basename(filePath).startsWith('_');
|
| +}
|
| +
|
| +/** Tell whether [filePath] is an input file. */
|
| +bool _isInputFile(String filePath, List<String> outputOnlyDirs) {
|
| + var ext = path.extension(filePath);
|
| + return (ext == '.dart' || ext == '.html') &&
|
| + !_isGeneratedFile(filePath, outputOnlyDirs);
|
| +}
|
| +
|
| +/**
|
| + * Delete all generated files. Currently we only delete files under directories
|
| + * that are known to contain only generated code.
|
| + */
|
| +void _handleCleanCommand(List<String> outputOnlyDirs) {
|
| + for (var dirPath in outputOnlyDirs) {
|
| + var dir = new Directory(dirPath);
|
| + if (!dir.existsSync()) continue;
|
| + for (var f in dir.listSync(recursive: false)) {
|
| + if (f is File && _isGeneratedFile(f.path, outputOnlyDirs)) f.deleteSync();
|
| + }
|
| + }
|
| +}
|
| +
|
| +/** Process the command-line arguments. */
|
| +ArgResults _processArgs(List<String> arguments) {
|
| + var parser = new ArgParser()
|
| + ..addOption("changed", help: "the file has changed since the last build",
|
| + allowMultiple: true)
|
| + ..addOption("removed", help: "the file was removed since the last build",
|
| + allowMultiple: true)
|
| + ..addFlag("clean", negatable: false, help: "remove any build artifacts")
|
| + ..addFlag("full", negatable: false, help: "perform a full build")
|
| + ..addFlag("machine", negatable: false,
|
| + help: "produce warnings in a machine parseable format")
|
| + ..addFlag("help", abbr: 'h',
|
| + negatable: false, help: "displays this help and exit");
|
| + var args = parser.parse(arguments);
|
| + if (args["help"]) {
|
| + print('A build script that invokes the web-ui compiler (dwc).');
|
| + print('Usage: dart build.dart [options] [-- [dwc-options]]');
|
| + print('\nThese are valid options expected by build.dart:');
|
| + print(parser.getUsage());
|
| + print('\nThese are valid options expected by dwc:');
|
| + dwc.run(['-h']).then((_) => exit(0));
|
| + }
|
| + return args;
|
| +}
|
|
|