diff --git a/lib/src/barback/asset_environment.dart b/lib/src/barback/asset_environment.dart index c07d8600244d3ae048ae7aa933d5e6ceea805b75..e3c8d26d9b590f8338d912d17e7567da1b4a803d 100644 --- a/lib/src/barback/asset_environment.dart +++ b/lib/src/barback/asset_environment.dart @@ -60,15 +60,19 @@ class AssetEnvironment { /// entrypoints are loaded. Each entrypoint is expected to refer to a Dart /// library. /// + /// If [environmentConstants] is passed, the constants it defines are passed + /// on to the built-in dart2js transformer. + /// /// Returns a [Future] that completes to the environment once the inputs, /// transformers, and server are loaded and ready. static Future<AssetEnvironment> create(Entrypoint entrypoint, BarbackMode mode, {WatcherType watcherType, String hostname, int basePort, Iterable<String> packages, Iterable<AssetId> entrypoints, - bool useDart2JS: true}) { + Map<String, String> environmentConstants, bool useDart2JS: true}) { if (watcherType == null) watcherType = WatcherType.NONE; if (hostname == null) hostname = "localhost"; if (basePort == null) basePort = 0; + if (environmentConstants == null) environmentConstants = {}; return log.progress("Loading asset environment", () async { var graph = await entrypoint.loadPackageGraph(); @@ -77,7 +81,7 @@ class AssetEnvironment { barback.log.listen(_log); var environment = new AssetEnvironment._(graph, barback, mode, - watcherType, hostname, basePort); + watcherType, hostname, basePort, environmentConstants); await environment._load(entrypoints: entrypoints, useDart2JS: useDart2JS); return environment; @@ -125,6 +129,9 @@ class AssetEnvironment { /// The mode to run the transformers in. final BarbackMode mode; + /// Constants to passed to the built-in dart2js transformer. + final Map<String, String> environmentConstants; + /// The [Transformer]s that should be appended by default to the root /// package's transformer cascade. Will be empty if there are none. final _builtInTransformers = <Transformer>[]; @@ -158,7 +165,8 @@ class AssetEnvironment { Set<AssetId> _modifiedSources; AssetEnvironment._(this.graph, this.barback, this.mode, - this._watcherType, this._hostname, this._basePort); + this._watcherType, this._hostname, this._basePort, + this.environmentConstants); /// Gets the built-in [Transformer]s that should be added to [package]. /// diff --git a/lib/src/barback/dart2js_transformer.dart b/lib/src/barback/dart2js_transformer.dart index 8c7ff14c0ebc63467759ac3ca87c8bc4f7dbe34a..9097e0747d2d38c37826e1478e9091d0f47fe76a 100644 --- a/lib/src/barback/dart2js_transformer.dart +++ b/lib/src/barback/dart2js_transformer.dart @@ -166,13 +166,15 @@ class Dart2JSTransformer extends Transformer implements LazyTransformer { /// Parses and returns the "environment" configuration option. Map<String, String> get _configEnvironment { - if (!_settings.configuration.containsKey('environment')) return null; + if (!_settings.configuration.containsKey('environment')) { + return _environment.environmentConstants; + } var environment = _settings.configuration['environment']; if (environment is Map && environment.keys.every((key) => key is String) && environment.values.every((key) => key is String)) { - return environment; + return mergeMaps(environment, _environment.environmentConstants); } throw new FormatException('Invalid value for \$dart2js.environment: ' diff --git a/lib/src/command/build.dart b/lib/src/command/build.dart index 1a88922c2fc695bd85274821596ada401681426a..b07cd95d617b7c054fd607f49dba7d198cf5dc3c 100644 --- a/lib/src/command/build.dart +++ b/lib/src/command/build.dart @@ -35,6 +35,10 @@ class BuildCommand extends BarbackCommand { int builtFiles = 0; BuildCommand() { + argParser.addOption("define", abbr: "D", + help: "Defines an environment constant for dart2js.", + allowMultiple: true, splitCommas: false); + argParser.addOption("format", help: "How output should be displayed.", allowed: ["text", "json"], defaultsTo: "text"); @@ -50,10 +54,16 @@ class BuildCommand extends BarbackCommand { var errorsJson = []; var logJson = []; + var environmentConstants = new Map.fromIterable(argResults["define"], + key: (pair) => pair.split("=").first, + value: (pair) => pair.split("=").last); + // Since this server will only be hit by the transformer loader and isn't // user-facing, just use an IPv4 address to avoid a weird bug on the // OS X buildbots. - return AssetEnvironment.create(entrypoint, mode, useDart2JS: true) + return AssetEnvironment.create(entrypoint, mode, + environmentConstants: environmentConstants, + useDart2JS: true) .then((environment) { // Show in-progress errors, but not results. Those get handled // implicitly by getAllAssets(). diff --git a/lib/src/command/serve.dart b/lib/src/command/serve.dart index 32074fc90fbd20bb3479b8148d5a21c5cd091b42..c5b47469379f7a510c2f5288c34d6e165c6ae0c5 100644 --- a/lib/src/command/serve.dart +++ b/lib/src/command/serve.dart @@ -59,6 +59,9 @@ class ServeCommand extends BarbackCommand { final _completer = new Completer(); ServeCommand() { + argParser.addOption("define", abbr: "D", + help: "Defines an environment constant for dart2js.", + allowMultiple: true, splitCommas: false); argParser.addOption('hostname', defaultsTo: 'localhost', help: 'The hostname to listen on.'); argParser.addOption('port', defaultsTo: '8080', @@ -88,9 +91,13 @@ class ServeCommand extends BarbackCommand { var watcherType = argResults['force-poll'] ? WatcherType.POLLING : WatcherType.AUTO; + var environmentConstants = new Map.fromIterable(argResults["define"], + key: (pair) => pair.split("=").first, + value: (pair) => pair.split("=").last); + var environment = await AssetEnvironment.create(entrypoint, mode, watcherType: watcherType, hostname: hostname, basePort: port, - useDart2JS: useDart2JS); + useDart2JS: useDart2JS, environmentConstants: environmentConstants); var directoryLength = sourceDirectories.map((dir) => dir.length) .reduce(math.max); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 14d81a67a4138ea4e2c6aac75429c1a6cc05a132..d0eb936f7f818d5e0b4eac6013e3952cbb251cd0 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -371,6 +371,16 @@ Future<Map> mapFromIterableAsync(Iterable iter, {key(element), })).then((_) => map); } +/// Returns a new map with all entries in both [map1] and [map2]. +/// +/// If there are overlapping keys, [map2]'s value wins. +Map mergeMaps(Map map1, Map map2) { + var result = {}; + result.addAll(map1); + result.addAll(map2); + return result; +} + /// Returns the transitive closure of [graph]. /// /// This assumes [graph] represents a graph with a vertex for each key and an diff --git a/test/dart2js/environment_constant_test.dart b/test/dart2js/environment_constant_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..dab1260f1abca74a83a6c0d0786caf98a5c46b0c --- /dev/null +++ b/test/dart2js/environment_constant_test.dart @@ -0,0 +1,65 @@ +// Copyright (c) 2014, 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. + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../descriptor.dart' as d; +import '../serve/utils.dart'; +import '../test_pub.dart'; + +main() { + initConfig(); + group("passes environment constants to dart2js", () { + setUp(() { + // Dart2js can take a long time to compile dart code, so we increase the + // timeout to cope with that. + currentSchedule.timeout *= 3; + + d.dir(appPath, [ + d.appPubspec(), + d.dir('web', [ + d.file('file.dart', + 'void main() => print(const String.fromEnvironment("name"));') + ]) + ]).create(); + }); + + integration('from "pub build"', () { + schedulePub(args: ["build", "--define", "name=fblthp"], + output: new RegExp(r'Built 1 file to "build".')); + + d.dir(appPath, [ + d.dir('build', [ + d.dir('web', [ + d.matcherFile('file.dart.js', contains('fblthp')), + ]) + ]) + ]).validate(); + }); + + integration('from "pub serve"', () { + pubServe(args: ["--define", "name=fblthp"]); + requestShouldSucceed("file.dart.js", contains("fblthp")); + endPubServe(); + }); + + integration('which takes precedence over the pubspec', () { + d.dir(appPath, [ + d.pubspec({ + "name": "myapp", + "transformers": [ + {"\$dart2js": {"environment": {"name": "slartibartfast"}}} + ] + }) + ]).create(); + + pubServe(args: ["--define", "name=fblthp"]); + requestShouldSucceed("file.dart.js", allOf([ + contains("fblthp"), + isNot(contains("slartibartfast")) + ])); + endPubServe(); + }); + }); +}