Skip to content

Commit

Permalink
Disentangle and align flutter build web --wasm flags (#143517)
Browse files Browse the repository at this point in the history
* Make `flutter build web` have one option that determins the
optimization level: `-O<level>` / `--optimization-level=<level>` =>
Defaulting to -O4 => Will apply to both dart2js and dart2wasm

* Deprecate `--dart2js-optimization=O<level>`

* Disentagle concept of optimization from concept of static symbols =>
Add a `--strip-wasm` / `--no-strip-wasm` flag that determins whether
static symbols are kept in the resulting wasm file.

* Remove copy&past'ed code in the tests for wasm build tests

* Cleanup some artifacts code, now that we no longer use `wasm-opt`
inside flutter tools
  • Loading branch information
mkustermann committed Feb 15, 2024
1 parent 2d4f5a6 commit 178898e
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 303 deletions.
4 changes: 2 additions & 2 deletions dev/devicelab/lib/tasks/web_benchmarks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ Future<TaskResult> runWebBenchmark(WebBenchmarkOptions benchmarkOptions) async {
await evalFlutter('build', options: <String>[
'web',
if (benchmarkOptions.useWasm) ...<String>[
'--O4',
'--wasm',
'--wasm-opt=debug',
'--omit-type-checks',
'--no-strip-wasm',
],
'--dart-define=FLUTTER_WEB_ENABLE_PROFILING=true',
if (!benchmarkOptions.useWasm) '--web-renderer=${benchmarkOptions.webRenderer}',
Expand Down
19 changes: 0 additions & 19 deletions packages/flutter_tools/lib/src/artifacts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ enum Artifact {
dart2jsSnapshot,
/// The dart snapshot of the dart2wasm compiler.
dart2wasmSnapshot,
/// The wasm-opt binary that ships with the dart-sdk
wasmOptBinary,

/// The root of the Linux desktop sources.
linuxDesktopPath,
Expand Down Expand Up @@ -194,8 +192,6 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
return 'dart2js.dart.snapshot';
case Artifact.dart2wasmSnapshot:
return 'dart2wasm_product.snapshot';
case Artifact.wasmOptBinary:
return 'wasm-opt$exe';
case Artifact.frontendServerSnapshotForEngineDartSdk:
return 'frontend_server_aot.dart.snapshot';
case Artifact.linuxDesktopPath:
Expand Down Expand Up @@ -566,7 +562,6 @@ class CachedArtifacts implements Artifacts {
case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.wasmOptBinary:
case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.constFinder:
case Artifact.flutterFramework:
Expand Down Expand Up @@ -608,7 +603,6 @@ class CachedArtifacts implements Artifacts {
case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.wasmOptBinary:
case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.constFinder:
case Artifact.flutterMacOSFramework:
Expand Down Expand Up @@ -668,7 +662,6 @@ class CachedArtifacts implements Artifacts {
case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.wasmOptBinary:
case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.icuData:
case Artifact.isolateSnapshotData:
Expand Down Expand Up @@ -708,11 +701,6 @@ class CachedArtifacts implements Artifacts {
_dartSdkPath(_cache), 'bin', 'snapshots',
_artifactToFileName(artifact, _platform),
);
case Artifact.wasmOptBinary:
return _fileSystem.path.join(
_dartSdkPath(_cache), 'bin', 'utils',
_artifactToFileName(artifact, _platform),
);
case Artifact.flutterTester:
case Artifact.vmSnapshotData:
case Artifact.isolateSnapshotData:
Expand Down Expand Up @@ -1039,8 +1027,6 @@ class CachedLocalEngineArtifacts implements Artifacts {
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', artifactFileName);
case Artifact.wasmOptBinary:
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'utils', artifactFileName);
case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath();
case Artifact.flutterPreviewDevice:
Expand Down Expand Up @@ -1178,11 +1164,6 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
_getDartSdkPath(), 'bin', 'snapshots',
_artifactToFileName(artifact, _platform, mode),
);
case Artifact.wasmOptBinary:
return _fileSystem.path.join(
_getDartSdkPath(), 'bin', 'utils',
_artifactToFileName(artifact, _platform, mode),
);
case Artifact.genSnapshot:
case Artifact.flutterTester:
case Artifact.flutterFramework:
Expand Down
50 changes: 29 additions & 21 deletions packages/flutter_tools/lib/src/commands/build_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ class BuildWebCommand extends BuildSubCommand {
usesWebRendererOption();
usesWebResourcesCdnFlag();

//
// Common compilation options among JavaScript and Wasm
//
argParser.addOption(
'optimization-level',
abbr: 'O',
help:
'Sets the optimization level used for Dart compilation to JavaScript/Wasm.',
defaultsTo: '${WebCompilerConfig.kDefaultOptimizationLevel}',
allowed: const <String>['1', '2', '3', '4'],
);

//
// JavaScript compilation options
//
Expand All @@ -72,10 +84,9 @@ class BuildWebCommand extends BuildSubCommand {
);
argParser.addOption('dart2js-optimization',
help: 'Sets the optimization level used for Dart compilation to JavaScript. '
'Valid values range from O1 to O4.',
defaultsTo: JsCompilerConfig.kDart2jsDefaultOptimizationLevel,
allowed: const <String>['O1', 'O2', 'O3', 'O4'],
);
'Deprecated: Please use "-O=<level>" / "--optimization-level=<level>".',
allowed: const <String>['O1', 'O2', 'O3', 'O4'],
);
argParser.addFlag('dump-info', negatable: false,
help: 'Passes "--dump-info" to the Javascript compiler which generates '
'information about the generated code is a .js.info.json file.'
Expand All @@ -98,19 +109,9 @@ class BuildWebCommand extends BuildSubCommand {
hide: !featureFlags.isFlutterWebWasmEnabled,
);
argParser.addFlag(
'omit-type-checks',
help: 'Omit type checks in Wasm output.\n'
'Reduces code size and improves performance, but may affect runtime correctness. Use with care.',
negatable: false,
hide: !featureFlags.isFlutterWebWasmEnabled,
);
argParser.addOption(
'wasm-opt',
help:
'Optimize output wasm using the Binaryen (https://github.com/WebAssembly/binaryen) tool.',
defaultsTo: WasmOptLevel.defaultValue.cliName,
allowed: WasmOptLevel.values.map<String>((WasmOptLevel e) => e.cliName),
allowedHelp: CliEnum.allowedHelp(WasmOptLevel.values),
'strip-wasm',
help: 'Whether to strip the resulting wasm file of static symbol names.',
defaultsTo: true,
hide: !featureFlags.isFlutterWebWasmEnabled,
);
}
Expand Down Expand Up @@ -138,6 +139,13 @@ class BuildWebCommand extends BuildSubCommand {
throwToolExit('"build web" is not currently supported. To enable, run "flutter config --enable-web".');
}

final int optimizationLevel = int.parse(stringArg('optimization-level')!);

final String? dart2jsOptimizationLevelValue = stringArg('dart2js-optimization');
final int jsOptimizationLevel = dart2jsOptimizationLevelValue != null
? int.parse(dart2jsOptimizationLevelValue.substring(1))
: optimizationLevel;

final List<WebCompilerConfig> compilerConfigs;
if (boolArg('wasm')) {
if (!featureFlags.isFlutterWebWasmEnabled) {
Expand All @@ -155,13 +163,13 @@ class BuildWebCommand extends BuildSubCommand {

compilerConfigs = <WebCompilerConfig>[
WasmCompilerConfig(
omitTypeChecks: boolArg('omit-type-checks'),
wasmOpt: WasmOptLevel.values.byName(stringArg('wasm-opt')!),
optimizationLevel: optimizationLevel,
stripWasm: boolArg('strip-wasm'),
renderer: WebRendererMode.skwasm,
),
JsCompilerConfig(
csp: boolArg('csp'),
optimizationLevel: stringArg('dart2js-optimization') ?? JsCompilerConfig.kDart2jsDefaultOptimizationLevel,
optimizationLevel: jsOptimizationLevel,
dumpInfo: boolArg('dump-info'),
nativeNullAssertions: boolArg('native-null-assertions'),
noFrequencyBasedMinification: boolArg('no-frequency-based-minification'),
Expand All @@ -175,7 +183,7 @@ class BuildWebCommand extends BuildSubCommand {
}
compilerConfigs = <WebCompilerConfig>[JsCompilerConfig(
csp: boolArg('csp'),
optimizationLevel: stringArg('dart2js-optimization') ?? JsCompilerConfig.kDart2jsDefaultOptimizationLevel,
optimizationLevel: jsOptimizationLevel,
dumpInfo: boolArg('dump-info'),
nativeNullAssertions: boolArg('native-null-assertions'),
noFrequencyBasedMinification: boolArg('no-frequency-based-minification'),
Expand Down
115 changes: 36 additions & 79 deletions packages/flutter_tools/lib/src/web/compiler_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import '../base/utils.dart';
import '../convert.dart';
import 'compile.dart';

Expand All @@ -12,15 +11,33 @@ enum CompileTarget {
}

sealed class WebCompilerConfig {
const WebCompilerConfig({required this.renderer});
const WebCompilerConfig({required this.renderer, required this.optimizationLevel});

/// The default optimization level for dart2js/dart2wasm.
static const int kDefaultOptimizationLevel = 4;

/// Build environment flag for [optimizationLevel].
static const String kOptimizationLevel = 'OptimizationLevel';

/// The compiler optimization level.
///
/// Valid values are O1 (lowest, profile default) to O4 (highest, release default).
final int optimizationLevel;

/// Returns which target this compiler outputs (js or wasm)
CompileTarget get compileTarget;
final WebRendererMode renderer;

String get buildKey;

Map<String, Object> get buildEventAnalyticsValues => <String, Object>{};
Map<String, Object> get buildEventAnalyticsValues => <String, Object>{
'optimizationLevel': optimizationLevel,
};


Map<String, dynamic> get _buildKeyMap => <String, dynamic>{
'optimizationLevel': optimizationLevel,
};
}

/// Configuration for the Dart-to-Javascript compiler (dart2js).
Expand All @@ -29,7 +46,7 @@ class JsCompilerConfig extends WebCompilerConfig {
this.csp = false,
this.dumpInfo = false,
this.nativeNullAssertions = false,
this.optimizationLevel = kDart2jsDefaultOptimizationLevel,
super.optimizationLevel = WebCompilerConfig.kDefaultOptimizationLevel,
this.noFrequencyBasedMinification = false,
this.sourceMaps = true,
super.renderer = WebRendererMode.auto,
Expand All @@ -41,18 +58,10 @@ class JsCompilerConfig extends WebCompilerConfig {
required WebRendererMode renderer,
}) : this(
nativeNullAssertions: nativeNullAssertions,
optimizationLevel: kDart2jsDefaultOptimizationLevel,
optimizationLevel: WebCompilerConfig.kDefaultOptimizationLevel ,
renderer: renderer,
);

/// The default optimization level for dart2js.
///
/// Maps to [kDart2jsOptimization].
static const String kDart2jsDefaultOptimizationLevel = 'O4';

/// Build environment flag for [optimizationLevel].
static const String kDart2jsOptimization = 'Dart2jsOptimization';

/// Build environment flag for [dumpInfo].
static const String kDart2jsDumpInfo = 'Dart2jsDumpInfo';

Expand Down Expand Up @@ -82,12 +91,6 @@ class JsCompilerConfig extends WebCompilerConfig {
// TODO(kevmoo): consider renaming this to be "positive". Double negatives are confusing.
final bool noFrequencyBasedMinification;

/// The compiler optimization level.
///
/// Valid values are O1 (lowest, profile default) to O4 (highest, release default).
// TODO(kevmoo): consider storing this as an [int] and validating it!
final String optimizationLevel;

/// `true` if the JavaScript compiler build should output source maps.
final bool sourceMaps;

Expand All @@ -105,7 +108,7 @@ class JsCompilerConfig extends WebCompilerConfig {
/// Includes the contents of [toSharedCommandOptions].
List<String> toCommandOptions() => <String>[
...toSharedCommandOptions(),
'-$optimizationLevel',
'-O$optimizationLevel',
if (dumpInfo) '--dump-info',
if (noFrequencyBasedMinification) '--no-frequency-based-minification',
if (csp) '--csp',
Expand All @@ -114,11 +117,11 @@ class JsCompilerConfig extends WebCompilerConfig {
@override
String get buildKey {
final Map<String, dynamic> settings = <String, dynamic>{
...super._buildKeyMap,
'csp': csp,
'dumpInfo': dumpInfo,
'nativeNullAssertions': nativeNullAssertions,
'noFrequencyBasedMinification': noFrequencyBasedMinification,
'optimizationLevel': optimizationLevel,
'sourceMaps': sourceMaps,
};
return jsonEncode(settings);
Expand All @@ -128,79 +131,33 @@ class JsCompilerConfig extends WebCompilerConfig {
/// Configuration for the Wasm compiler.
class WasmCompilerConfig extends WebCompilerConfig {
const WasmCompilerConfig({
this.omitTypeChecks = false,
this.wasmOpt = WasmOptLevel.defaultValue,
super.optimizationLevel = WebCompilerConfig.kDefaultOptimizationLevel,
this.stripWasm = true,
super.renderer = WebRendererMode.auto,
});

/// Build environment for [omitTypeChecks].
static const String kOmitTypeChecks = 'WasmOmitTypeChecks';

/// Build environment for [wasmOpt].
static const String kRunWasmOpt = 'RunWasmOpt';

/// If `omit-type-checks` should be passed to `dart2wasm`.
final bool omitTypeChecks;
/// Build environment for [stripWasm].
static const String kStripWasm = 'StripWasm';

/// Run wasm-opt on the resulting module.
final WasmOptLevel wasmOpt;
/// Whether to strip the wasm file of static symbols.
final bool stripWasm;

@override
CompileTarget get compileTarget => CompileTarget.wasm;

List<String> toCommandOptions() {
// -O1: Optimizes
// -O2: Same as -O1 but also minifies (still semantics preserving)
// -O3: Same as -O2 but also omits implicit type checks.
// -O4: Same as -O3 but also omits explicit type checks.
// (NOTE: This differs from dart2js -O4 semantics atm.)

// Ortogonal: The name section is always kept by default and we emit it only
// in [WasmOptLevel.full] mode (similar to `--strip` of static symbols in
// AOT mode).
final String level = !omitTypeChecks ? '-O2' : '-O4';
return switch (wasmOpt) {
WasmOptLevel.none => <String>['-O0'],
WasmOptLevel.debug => <String>[level, '--no-minify'],
WasmOptLevel.full => <String>[level, '--no-name-section'],
};
return <String>[
'-O$optimizationLevel',
'--${stripWasm? 'no-' : ''}name-section',
];
}

@override
Map<String, Object> get buildEventAnalyticsValues => <String, Object>{
...super.buildEventAnalyticsValues,
kOmitTypeChecks: omitTypeChecks.toString(),
kRunWasmOpt: wasmOpt.name,
};

@override
String get buildKey {
final Map<String, dynamic> settings = <String, dynamic>{
'omitTypeChecks': omitTypeChecks,
'wasmOpt': wasmOpt.name,
...super._buildKeyMap,
'stripWasm': stripWasm,
};
return jsonEncode(settings);
}

}

enum WasmOptLevel implements CliEnum {
full,
debug,
none;

static const WasmOptLevel defaultValue = WasmOptLevel.full;

@override
String get cliName => name;

@override
String get helpText => switch (this) {
WasmOptLevel.none =>
'wasm-opt is not run. Fastest build; bigger, slower output.',
WasmOptLevel.debug =>
'Similar to `${WasmOptLevel.full.name}`, but member names are preserved. Debugging is easier, but size is a bit bigger.',
WasmOptLevel.full =>
'wasm-opt is run. Build time is slower, but output is smaller and faster.',
};
}
7 changes: 0 additions & 7 deletions packages/flutter_tools/test/general.shard/artifacts_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -427,13 +427,6 @@ void main() {
fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk',
'bin', 'snapshots', 'dart2js.dart.snapshot'),
);
expect(
artifacts.getArtifactPath(
Artifact.wasmOptBinary,
platform: TargetPlatform.web_javascript),
fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk',
'bin', 'utils', 'wasm-opt'),
);
});

testWithoutContext('getEngineType', () {
Expand Down
Loading

0 comments on commit 178898e

Please sign in to comment.