Skip to content

Commit 49d83f4

Browse files
authored
Compile with dart2js and dart2wasm (#3737)
1 parent 1b33ba9 commit 49d83f4

13 files changed

+786
-164
lines changed

_test/build.both.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
targets:
2+
$default:
3+
builders:
4+
build_web_compilers:entrypoint:
5+
options:
6+
compilers:
7+
dart2wasm:
8+
dart2js:
9+
loader: .dart.js
10+
generate_for:
11+
- web/main.dart
12+
- web/sub/main.dart
13+
- test/configurable_uri_test.dart
14+
- test/configurable_uri_test.dart.browser_test.dart
15+
- test/hello_world_test.dart
16+
- test/hello_world_test.dart.browser_test.dart
17+
- test/hello_world_deferred_test.dart
18+
- test/hello_world_deferred_test.dart.browser_test.dart
19+
- test/hello_world_custom_html_test.dart
20+
- test/hello_world_custom_html_test.dart.browser_test.dart
21+
- test/other_test.dart.browser_test.dart
22+
- test/sub-dir/subdir_test.dart
23+
- test/sub-dir/subdir_test.dart.browser_test.dart

_test/test/dart2wasm_integration_test.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,29 @@ void main() {
5959
);
6060
await _expectWasCompiledWithDart2Wasm();
6161
}, onPlatform: {'windows': const Skip('flaky on windows')});
62+
63+
test('when also enabling dart2js', () async {
64+
await expectTestsPass(
65+
usePrecompiled: true,
66+
buildArgs: [
67+
'--release',
68+
'--config=both',
69+
'--output=${d.sandbox}',
70+
],
71+
testArgs: _testArgs,
72+
);
73+
await _expectWasCompiledWithDart2Wasm();
74+
75+
await d.dir(
76+
'test',
77+
[
78+
d.file(
79+
'hello_world_deferred_test.dart.browser_test.dart2js.js',
80+
startsWith('// Generated by dart2js'),
81+
),
82+
],
83+
).validate();
84+
}, onPlatform: {'windows': const Skip('flaky on windows')});
6285
});
6386
}
6487

build_web_compilers/CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
## 4.0.12-wip
1+
## 4.1.0-wip
22

3-
- Account for dartdevc snapshot in the Dart SDK changing to an AOT snapshot.
43
- Support compiling to WebAssembly by using `dart2wasm` as a value for the
54
compiler option.
5+
- Support compiling with multiple compilers (e.g. `dart2js` and `dart2wasm`)
6+
with a new `compilers` option.
7+
- Account for dartdevc snapshot in the Dart SDK changing to an AOT snapshot.
68
- Bump the min sdk to 3.6.0-165.0.dev.
79

810
## 4.0.11

build_web_compilers/README.md

Lines changed: 173 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,35 @@ then all you need is the `dev_dependency` listed above.
3939

4040
## Configuration
4141

42-
### Configuring the default compiler
43-
4442
By default, this package uses the [Dart development compiler][] (_dartdevc_,
4543
also known as _DDC_) to compile Dart to JavaScript. In release builds (running
46-
the build tool with `--release`, the default compiler is `dart2js`).
44+
the build tool with `--release`), `dart2js` is used as a compiler.
45+
46+
In addition to compiling to JavaScript, this package also supports compiling to
47+
WebAssembly. Currently, `dart2wasm` needs to be enabled with builder options.
48+
To understand the impact of these options, be aware of differences between
49+
compiling to JavaScript and compiling to WebAssembly:
50+
51+
1. Dart has two compilers emitting JavaScript: `dart2js` and `dartdevc` (which
52+
supports incremental rebuilds but is typically only used for development).
53+
For both JavaScript compilers, `build_web_compilers` generates a primary
54+
entrypoint script and additional module files or source maps depending on
55+
compiler options.
56+
2. Compiling with `dart2wasm` generates a WebAssembly module (a `.wasm` file)
57+
and a JavaScript module (a `.mjs` file) exporting functions to instantiate
58+
that module. `dart2wasm` alone generates no entrypoint file that could be
59+
added as a `<script>` tag.
60+
61+
In addition to invoking compilers, `build_web_compilers` can emit an entrypoint
62+
file suitable for `dart2wasm`. When both `dart2wasm` and a JavaScript compiler
63+
are enabled, the entrypoint file runs a feature detection for WasmGC and loads
64+
either the WebAssembly module or the JavaScript file depending on what the
65+
browser supports.
66+
67+
### Compiler arguments
4768

48-
If you would like to opt into dart2js for all builds, you will need to add a
49-
`build.yaml` file, which should look roughly like the following:
69+
To customize compilers, you will need to add a `build.yaml` file configuring
70+
the `build_web_compilers:entrypoint` builder, similar to the following:
5071

5172
```yaml
5273
targets:
@@ -58,28 +79,103 @@ targets:
5879
- test/**.browser_test.dart
5980
- web/**.dart
6081
options:
61-
compiler: dart2js
62-
# List any dart2js specific args here, or omit it.
63-
dart2js_args:
64-
- -O2
82+
compilers:
83+
# All compilers listed here are enabled:
84+
dart2js:
85+
# List any dart2js specific args here, or omit it.
86+
args:
87+
- O2
88+
dart2wasm:
89+
args:
90+
- O3
91+
```
92+
93+
`build_runner` runs development builds by default, but can use builders with a
94+
different configuration for `--release` builds. For instance, if you wanted to
95+
compile with `dartdevc` only on development and `dart2js` + `dart2wasm` for
96+
release builds, you can use this configuration as a starting point:
97+
98+
```yaml
99+
targets:
100+
$default:
101+
builders:
102+
build_web_compilers:entrypoint:
103+
generate_for:
104+
- test/**.browser_test.dart
105+
- web/**.dart
106+
dev_options:
107+
compilers:
108+
dartdevc:
109+
release_options:
110+
compilers:
111+
dart2js:
112+
args:
113+
- O2
114+
dart2wasm:
115+
args:
116+
- O3
65117
```
66118

67-
In addition to DDC and dart2js, this package also supports the dart2wasm
68-
compiler for compiling to WebAssembly with a JavaScript loader. It can be
69-
enabled by using `compiler: dart2wasm` in the build configuration:
119+
### Customizing emitted file names
120+
121+
The file names emitted by `build_web_compilers` can be changed. The default
122+
names depend on which compilers are enabled:
123+
124+
1. When only `dart2js` or `dartdevc` is enabled, a single `.dart.js` entrypoint
125+
is emitted.
126+
2. When only `dart2wasm` is enabled, a single `.dart.js` entrypoint (loading
127+
a generated `.wasm` module through a `.mjs` helper file) is generated.
128+
3. When both `dart2wasm` and a JavaScript compiler are enabled, then:
129+
- The output of the JavaScript compiler is named `.dart2js.js` or `.ddc.js`
130+
depending on the compiler.
131+
- `dart2wasm` continues to emit a `.wasm` and a `.mjs` file.
132+
- An entrypoint loader named `.dart.js` that loads the appropriate output
133+
depending on browser features is generated.
134+
135+
All names can be overridden by using the `loader` option or the `extension`
136+
flag in compiler options:
70137

71138
```yaml
72139
targets:
73140
$default:
74141
builders:
75142
build_web_compilers:entrypoint:
76143
options:
77-
compiler: dart2wasm
78-
# List flags that should be forwarded to `dart compile wasm`
79-
dart2wasm_args:
80-
- -O2
144+
loader: .load.js
145+
compilers:
146+
dart2js:
147+
extension: .old.js
148+
dart2wasm:
149+
extension: .new.mjs
150+
```
151+
152+
This configuration uses both `dart2js` and `dart2wasm`, but names the final
153+
entrypoint for a `main.dart` file `main.load.js`. That loader will either load
154+
a `.new.mjs` file for WebAssembly or a `.old.js` for pure JavaScript.
155+
156+
Note that the `loader` option is ignored when `dart2wasm` is not enabled, as
157+
it's the compiler requiring an additional loader to be emitted.
158+
159+
### Customizing the WebAssembly loader
160+
161+
In some cases, for instance when targeting Node.JS, the generated loader for
162+
`dart2wasm` may be unsuitable. The builtin loader can be disabled by setting
163+
the option to null:
164+
165+
```yaml
166+
targets:
167+
$default:
168+
builders:
169+
build_web_compilers:entrypoint:
170+
options:
171+
loader: null
172+
compilers:
173+
dart2js:
174+
dart2wasm:
81175
```
82176

177+
In this case, you need to use another builder or a predefined loader instead.
178+
83179
### Configuring -D environment variables
84180

85181
dartdevc is a modular compiler, so in order to ensure consistent builds
@@ -96,22 +192,71 @@ global_options:
96192
ANOTHER_VAR: false
97193
```
98194

99-
For dart2js, use the `dart2js_args` option. This may be configured globally, or
100-
per target.
195+
These may also be specified on the command line with a `--define` argument.
196+
197+
```sh
198+
webdev serve -- '--define=build_web_compilers:ddc=environment={"SOME_VAR":"changed"}'
199+
```
200+
201+
For dart2js, use the `args` option within the `dart2js` compiler entry. This
202+
may be configured globally, or per target.
203+
204+
```yaml
205+
targets:
206+
$default:
207+
builders:
208+
build_web_compilers:entrypoint:
209+
options:
210+
compilers:
211+
dart2js:
212+
args:
213+
- -DSOME_VAR=some value
214+
- -DANOTHER_VAR=true
215+
```
216+
217+
To apply variables across multiple compilers, they have to be added to each
218+
one:
101219

102220
```yaml
103221
targets:
104222
$default:
105223
builders:
106224
build_web_compilers:entrypoint:
107225
options:
226+
compilers:
227+
dart2js:
228+
args:
229+
- -DSOME_VAR=some value
230+
- -DANOTHER_VAR=true
231+
dart2wasm:
232+
args:
233+
- -DSOME_VAR=some value
234+
- -DANOTHER_VAR=true
235+
```
236+
237+
### Legacy builder options
238+
239+
Previous versions of `build_web_compilers` only supported a single enabled
240+
compiler that would be enabled with the `compiler` option.
241+
If you only want to use `dart2js` for all builds, you can use that option:
242+
243+
```yaml
244+
targets:
245+
$default:
246+
builders:
247+
build_web_compilers:entrypoint:
248+
# These are globs for the entrypoints you want to compile.
249+
generate_for:
250+
- test/**.browser_test.dart
251+
- web/**.dart
252+
options:
253+
compiler: dart2js
254+
# List any dart2js specific args here, or omit it.
108255
dart2js_args:
109-
- -DSOME_VAR=some value
110-
- -DANOTHER_VAR=true
256+
- -O2
111257
```
112258

113-
Similarly, options are passed to `dart compile wasm` when using the
114-
`dart2wasm_args` option:
259+
Similarly, only compiling with `dart2wasm`:
115260

116261
```yaml
117262
targets:
@@ -120,16 +265,15 @@ targets:
120265
build_web_compilers:entrypoint:
121266
options:
122267
compiler: dart2wasm
268+
# List flags that should be forwarded to `dart compile wasm`
123269
dart2wasm_args:
124-
- -DSOME_VAR=some value
125-
- -DANOTHER_VAR=true
270+
- -O2
126271
```
127272
128-
These may also be specified on the command line with a `--define` argument.
129-
130-
```sh
131-
webdev serve -- '--define=build_web_compilers:ddc=environment={"SOME_VAR":"changed"}'
132-
```
273+
When no option is set, the `compiler` option is implicitly set to `dart2js` on
274+
release builds and to `dartdevc` otherwise.
275+
Note that the `compilers` option takes precedence over the `compiler` option
276+
when set.
133277

134278
## Manual Usage
135279

build_web_compilers/lib/src/dart2js_bootstrap.dart

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@ Future<void> bootstrapDart2Js(
2525
BuildStep buildStep,
2626
List<String> dart2JsArgs, {
2727
required bool? nativeNullAssertions,
28+
String entrypointExtension = jsEntrypointExtension,
2829
}) =>
29-
_resourcePool.withResource(() => _bootstrapDart2Js(buildStep, dart2JsArgs,
30-
nativeNullAssertions: nativeNullAssertions));
30+
_resourcePool.withResource(() => _bootstrapDart2Js(
31+
buildStep,
32+
dart2JsArgs,
33+
nativeNullAssertions: nativeNullAssertions,
34+
entrypointExtension: entrypointExtension,
35+
));
3136

3237
Future<void> _bootstrapDart2Js(
3338
BuildStep buildStep,
3439
List<String> dart2JsArgs, {
3540
required bool? nativeNullAssertions,
41+
required String entrypointExtension,
3642
}) async {
3743
var dartEntrypointId = buildStep.inputId;
3844
var moduleId =
@@ -74,7 +80,7 @@ https://github.com/dart-lang/build/blob/master/docs/faq.md#how-can-i-resolve-ski
7480
var jsOutputPath = p.withoutExtension(dartUri.scheme == 'package'
7581
? 'packages/${dartUri.path}'
7682
: dartUri.path.substring(1)) +
77-
jsEntrypointExtension;
83+
entrypointExtension;
7884
var librariesSpec = p.joinAll([sdkDir, 'lib', 'libraries.json']);
7985
_validateUserArgs(dart2JsArgs);
8086
args = dart2JsArgs.toList()
@@ -102,7 +108,7 @@ https://github.com/dart-lang/build/blob/master/docs/faq.md#how-can-i-resolve-ski
102108
...args,
103109
],
104110
workingDirectory: scratchSpace.tempDir.path);
105-
var jsOutputId = dartEntrypointId.changeExtension(jsEntrypointExtension);
111+
var jsOutputId = dartEntrypointId.changeExtension(entrypointExtension);
106112
var jsOutputFile = scratchSpace.fileFor(jsOutputId);
107113
if (result.exitCode == 0 && await jsOutputFile.exists()) {
108114
log.info('${result.stdout}\n${result.stderr}');
@@ -111,7 +117,7 @@ https://github.com/dart-lang/build/blob/master/docs/faq.md#how-can-i-resolve-ski
111117
var fileGlob = Glob('$dartFile.js*');
112118
var archive = Archive();
113119
await for (var jsFile in fileGlob.list(root: rootDir)) {
114-
if (jsFile.path.endsWith(jsEntrypointExtension) ||
120+
if (jsFile.path.endsWith(entrypointExtension) ||
115121
jsFile.path.endsWith(jsEntrypointSourceMapExtension)) {
116122
// These are explicitly output, and are not part of the archive.
117123
continue;

0 commit comments

Comments
 (0)