blob: aef8968a7d8cae9d28a7f7e50299391676974594 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../build_info.dart' show BuildMode;
import '../convert.dart';
import 'compile.dart';
enum CompileTarget { js, wasm }
sealed class WebCompilerConfig {
const WebCompilerConfig({
required this.renderer,
this.optimizationLevel,
required this.sourceMaps,
});
/// Build environment flag for [optimizationLevel].
static const kOptimizationLevel = 'OptimizationLevel';
/// Build environment flag for [sourceMaps].
static const kSourceMapsEnabled = 'SourceMaps';
/// Calculates the optimization level for the compiler for the given
/// build mode.
int optimizationLevelForBuildMode(BuildMode mode);
/// The compiler optimization level specified by the user.
///
/// Valid values are O0 (lowest, debug default) to O4 (highest, release default).
/// If the value is null, the user hasn't specified an optimization level and an
/// appropriate default for the build mode will be used instead.
final int? optimizationLevel;
/// `true` if the compiler build should output source maps.
final bool sourceMaps;
/// Returns which target this compiler outputs (js or wasm)
CompileTarget get compileTarget;
final WebRendererMode renderer;
List<String> toCommandOptions(BuildMode buildMode);
String get buildKey;
Map<String, Object> get buildEventAnalyticsValues => <String, Object>{
'optimizationLevel': ?optimizationLevel,
};
Map<String, dynamic> get _buildKeyMap => <String, dynamic>{
'optimizationLevel': optimizationLevel,
'webRenderer': renderer.name,
};
}
/// Configuration for the Dart-to-Javascript compiler (dart2js).
class JsCompilerConfig extends WebCompilerConfig {
const JsCompilerConfig({
this.csp = false,
this.dumpInfo = false,
this.nativeNullAssertions = false,
super.optimizationLevel,
this.noFrequencyBasedMinification = false,
super.sourceMaps = true,
this.minify,
super.renderer = WebRendererMode.defaultForJs,
});
/// Instantiates [JsCompilerConfig] suitable for the `flutter run` command.
const JsCompilerConfig.run({
required bool nativeNullAssertions,
required WebRendererMode renderer,
}) : this(nativeNullAssertions: nativeNullAssertions, renderer: renderer);
/// Whether to disable dynamic generation code to satisfy CSP policies.
final bool csp;
/// If `--dump-info` should be passed to the compiler.
final bool dumpInfo;
/// If minification should be used in the JS compiler.
///
/// If `null`, minifies in release mode only.
final bool? minify;
/// Whether native null assertions are enabled.
final bool nativeNullAssertions;
// If `--no-frequency-based-minification` should be passed to dart2js
// TODO(kevmoo): consider renaming this to be "positive". Double negatives are confusing.
final bool noFrequencyBasedMinification;
@override
CompileTarget get compileTarget => CompileTarget.js;
/// Arguments to use in both phases: full JS compile and CFE-only.
///
/// NOTE: MOST args should be passed here!
List<String> toSharedCommandOptions(BuildMode buildMode) => <String>[
if (nativeNullAssertions) '--native-null-assertions',
if (!sourceMaps) '--no-source-maps',
if (buildMode == BuildMode.debug) '--enable-asserts',
'-O${optimizationLevelForBuildMode(buildMode)}',
if (minify ?? buildMode == BuildMode.release) '--minify' else '--no-minify',
if (noFrequencyBasedMinification) '--no-frequency-based-minification',
if (csp) '--csp',
];
@override
int optimizationLevelForBuildMode(BuildMode mode) =>
optimizationLevel ??
switch (mode) {
// dart2js optimization level 0 is not well supported. Use
// 1 instead.
BuildMode.debug => 1,
BuildMode.profile || BuildMode.release => 4,
BuildMode.jitRelease => throw ArgumentError('Invalid build mode for web'),
};
/// Arguments to use in the full JS compile, but not CFE-only.
///
/// Includes the contents of [toSharedCommandOptions]. That is where MOST
/// JS compiler flags should be passed!
@override
List<String> toCommandOptions(BuildMode buildMode) => <String>[
...toSharedCommandOptions(buildMode),
if (dumpInfo) '--stage=dump-info-all',
];
@override
String get buildKey {
final settings = <String, dynamic>{
...super._buildKeyMap,
'csp': csp,
'dumpInfo': dumpInfo,
'nativeNullAssertions': nativeNullAssertions,
'noFrequencyBasedMinification': noFrequencyBasedMinification,
'minify': minify,
WebCompilerConfig.kSourceMapsEnabled: sourceMaps,
};
return jsonEncode(settings);
}
}
/// Configuration for the Wasm compiler.
class WasmCompilerConfig extends WebCompilerConfig {
const WasmCompilerConfig({
super.optimizationLevel,
this.stripWasm = true,
this.minify,
this.dryRun = false,
super.sourceMaps = true,
super.renderer = WebRendererMode.defaultForWasm,
});
/// Build environment for [stripWasm].
static const kStripWasm = 'StripWasm';
/// Whether to strip the wasm file of static symbols.
final bool stripWasm;
final bool? minify;
final bool dryRun;
@override
CompileTarget get compileTarget => CompileTarget.wasm;
@override
int optimizationLevelForBuildMode(BuildMode mode) =>
optimizationLevel ??
switch (mode) {
BuildMode.debug => 0,
// The optimization level of O2 uses only sound optimizations. We default
// to this level because our web benchmarks have shown that the difference
// between O2 and O4 is marginal enough that we would prefer soundness here.
BuildMode.profile || BuildMode.release => 2,
BuildMode.jitRelease => throw ArgumentError('Invalid build mode for web'),
};
@override
List<String> toCommandOptions(BuildMode buildMode) {
final bool stripSymbols = buildMode == BuildMode.release && stripWasm;
return <String>[
'-O${optimizationLevelForBuildMode(buildMode)}',
'--${stripSymbols ? '' : 'no-'}strip-wasm',
if (!sourceMaps) '--no-source-maps',
if (minify ?? buildMode == BuildMode.release) '--minify' else '--no-minify',
if (buildMode == BuildMode.debug) '--extra-compiler-option=--enable-asserts',
if (dryRun) '--extra-compiler-option=--dry-run',
];
}
@override
String get buildKey {
final settings = <String, dynamic>{
...super._buildKeyMap,
kStripWasm: stripWasm,
'minify': minify,
'dryRun': dryRun,
WebCompilerConfig.kSourceMapsEnabled: sourceMaps,
};
return jsonEncode(settings);
}
@override
Map<String, Object> get buildEventAnalyticsValues => <String, Object>{
...super.buildEventAnalyticsValues,
'dryRun': dryRun,
};
}