Add support for flutter build web --debug. (#157186)

There have been various requests for this for a while. See https://github.com/flutter/flutter/issues/96283 for an example. This has become more important with dart2wasm builds in the mix, as the profiling versions of the dart2wasm builds are a lot less debuggable than the debug versions. Most of this is already wired up so it just requires taking out a few explicit checks for it and making sure that we compile with the right optimization levels when compiling debug.
This commit is contained in:
Jackson Gardner 2024-10-21 08:35:11 -07:00 committed by GitHub
parent 3512745071
commit e19d352fc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 35 additions and 42 deletions

View File

@ -28,7 +28,7 @@ class BuildWebCommand extends BuildSubCommand {
usesPubOption(); usesPubOption();
usesBuildNumberOption(); usesBuildNumberOption();
usesBuildNameOption(); usesBuildNameOption();
addBuildModeFlags(verboseHelp: verboseHelp, excludeDebug: true); addBuildModeFlags(verboseHelp: verboseHelp);
usesDartDefineOption(); usesDartDefineOption();
addEnableExperimentation(hide: !verboseHelp); addEnableExperimentation(hide: !verboseHelp);
addNullSafetyModeOptions(hide: !verboseHelp); addNullSafetyModeOptions(hide: !verboseHelp);
@ -62,7 +62,6 @@ class BuildWebCommand extends BuildSubCommand {
abbr: 'O', abbr: 'O',
help: help:
'Sets the optimization level used for Dart compilation to JavaScript/Wasm.', 'Sets the optimization level used for Dart compilation to JavaScript/Wasm.',
defaultsTo: '${WebCompilerConfig.kDefaultOptimizationLevel}',
allowed: const <String>['0', '1', '2', '3', '4'], allowed: const <String>['0', '1', '2', '3', '4'],
); );
argParser.addFlag( argParser.addFlag(
@ -134,10 +133,11 @@ class BuildWebCommand extends BuildSubCommand {
throwToolExit('"build web" is not currently supported. To enable, run "flutter config --enable-web".'); throwToolExit('"build web" is not currently supported. To enable, run "flutter config --enable-web".');
} }
final int optimizationLevel = int.parse(stringArg('optimization-level')!); final String? optimizationLevelArg = stringArg('optimization-level');
final int? optimizationLevel = optimizationLevelArg != null ? int.parse(optimizationLevelArg) : null;
final String? dart2jsOptimizationLevelValue = stringArg('dart2js-optimization'); final String? dart2jsOptimizationLevelValue = stringArg('dart2js-optimization');
final int jsOptimizationLevel = dart2jsOptimizationLevelValue != null final int? jsOptimizationLevel = dart2jsOptimizationLevelValue != null
? int.parse(dart2jsOptimizationLevelValue.substring(1)) ? int.parse(dart2jsOptimizationLevelValue.substring(1))
: optimizationLevel; : optimizationLevel;
@ -191,9 +191,6 @@ class BuildWebCommand extends BuildSubCommand {
final String target = stringArg('target')!; final String target = stringArg('target')!;
final BuildInfo buildInfo = await getBuildInfo(); final BuildInfo buildInfo = await getBuildInfo();
if (buildInfo.isDebug) {
throwToolExit('debug builds cannot be built directly for the web. Try using "flutter run"');
}
final String? baseHref = stringArg('base-href'); final String? baseHref = stringArg('base-href');
if (baseHref != null && !(baseHref.startsWith('/') && baseHref.endsWith('/'))) { if (baseHref != null && !(baseHref.startsWith('/') && baseHref.endsWith('/'))) {
throwToolExit( throwToolExit(

View File

@ -13,22 +13,29 @@ enum CompileTarget {
sealed class WebCompilerConfig { sealed class WebCompilerConfig {
const WebCompilerConfig({required this.renderer, const WebCompilerConfig({required this.renderer,
required this.optimizationLevel, this.optimizationLevel,
required this.sourceMaps}); required this.sourceMaps});
/// The default optimization level for dart2js/dart2wasm.
static const int kDefaultOptimizationLevel = 4;
/// Build environment flag for [optimizationLevel]. /// Build environment flag for [optimizationLevel].
static const String kOptimizationLevel = 'OptimizationLevel'; static const String kOptimizationLevel = 'OptimizationLevel';
/// Build environment flag for [sourceMaps]. /// Build environment flag for [sourceMaps].
static const String kSourceMapsEnabled = 'SourceMaps'; static const String kSourceMapsEnabled = 'SourceMaps';
/// The compiler optimization level. /// Calculates the optimization level for dart2js/dart2wasm for the given
/// build mode.
int optimizationLevelForBuildMode(BuildMode mode) =>
optimizationLevel ?? switch (mode) {
BuildMode.debug => 0,
BuildMode.profile || BuildMode.release => 4,
BuildMode.jitRelease => throw ArgumentError('Invalid build mode for web'),
};
/// The compiler optimization level specified by the user.
/// ///
/// Valid values are O0 (lowest, debug default) to O4 (highest, release default). /// Valid values are O0 (lowest, debug default) to O4 (highest, release default).
final int optimizationLevel; /// 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. /// `true` if the compiler build should output source maps.
final bool sourceMaps; final bool sourceMaps;
@ -40,7 +47,7 @@ sealed class WebCompilerConfig {
String get buildKey; String get buildKey;
Map<String, Object> get buildEventAnalyticsValues => <String, Object>{ Map<String, Object> get buildEventAnalyticsValues => <String, Object>{
'optimizationLevel': optimizationLevel, if (optimizationLevel != null) 'optimizationLevel': optimizationLevel!,
}; };
@ -56,7 +63,7 @@ class JsCompilerConfig extends WebCompilerConfig {
this.csp = false, this.csp = false,
this.dumpInfo = false, this.dumpInfo = false,
this.nativeNullAssertions = false, this.nativeNullAssertions = false,
super.optimizationLevel = WebCompilerConfig.kDefaultOptimizationLevel, super.optimizationLevel,
this.noFrequencyBasedMinification = false, this.noFrequencyBasedMinification = false,
super.sourceMaps = true, super.sourceMaps = true,
super.renderer = WebRendererMode.defaultForJs, super.renderer = WebRendererMode.defaultForJs,
@ -68,7 +75,6 @@ class JsCompilerConfig extends WebCompilerConfig {
required WebRendererMode renderer, required WebRendererMode renderer,
}) : this( }) : this(
nativeNullAssertions: nativeNullAssertions, nativeNullAssertions: nativeNullAssertions,
optimizationLevel: WebCompilerConfig.kDefaultOptimizationLevel ,
renderer: renderer, renderer: renderer,
); );
@ -108,13 +114,22 @@ class JsCompilerConfig extends WebCompilerConfig {
if (buildMode == BuildMode.debug) '--enable-asserts', if (buildMode == BuildMode.debug) '--enable-asserts',
]; ];
@override
int optimizationLevelForBuildMode(BuildMode mode) {
final int level = super.optimizationLevelForBuildMode(mode);
// dart2js optimization level 0 is not well supported. Use
// 1 instead.
return level == 0 ? 1 : level;
}
/// Arguments to use in the full JS compile, but not CFE-only. /// Arguments to use in the full JS compile, but not CFE-only.
/// ///
/// Includes the contents of [toSharedCommandOptions]. /// Includes the contents of [toSharedCommandOptions].
List<String> toCommandOptions(BuildMode buildMode) => <String>[ List<String> toCommandOptions(BuildMode buildMode) => <String>[
if (buildMode != BuildMode.release) '--no-minify', if (buildMode != BuildMode.release) '--no-minify',
...toSharedCommandOptions(buildMode), ...toSharedCommandOptions(buildMode),
'-O$optimizationLevel', '-O${optimizationLevelForBuildMode(buildMode)}',
if (dumpInfo) '--stage=dump-info-all', if (dumpInfo) '--stage=dump-info-all',
if (noFrequencyBasedMinification) '--no-frequency-based-minification', if (noFrequencyBasedMinification) '--no-frequency-based-minification',
if (csp) '--csp', if (csp) '--csp',
@ -137,7 +152,7 @@ class JsCompilerConfig extends WebCompilerConfig {
/// Configuration for the Wasm compiler. /// Configuration for the Wasm compiler.
class WasmCompilerConfig extends WebCompilerConfig { class WasmCompilerConfig extends WebCompilerConfig {
const WasmCompilerConfig({ const WasmCompilerConfig({
super.optimizationLevel = WebCompilerConfig.kDefaultOptimizationLevel, super.optimizationLevel,
this.stripWasm = true, this.stripWasm = true,
super.sourceMaps = true, super.sourceMaps = true,
super.renderer = WebRendererMode.defaultForWasm, super.renderer = WebRendererMode.defaultForWasm,
@ -155,7 +170,7 @@ class WasmCompilerConfig extends WebCompilerConfig {
List<String> toCommandOptions(BuildMode buildMode) { List<String> toCommandOptions(BuildMode buildMode) {
final bool stripSymbols = buildMode == BuildMode.release && stripWasm; final bool stripSymbols = buildMode == BuildMode.release && stripWasm;
return <String>[ return <String>[
'-O$optimizationLevel', '-O${optimizationLevelForBuildMode(buildMode)}',
'--${stripSymbols ? '' : 'no-'}strip-wasm', '--${stripSymbols ? '' : 'no-'}strip-wasm',
if (!sourceMaps) '--no-source-maps', if (!sourceMaps) '--no-source-maps',
if (buildMode == BuildMode.debug) '--extra-compiler-option=--enable-asserts', if (buildMode == BuildMode.debug) '--extra-compiler-option=--enable-asserts',

View File

@ -83,25 +83,6 @@ void main() {
ProcessManager: () => processManager, ProcessManager: () => processManager,
}); });
testUsingContext('Refuses to build a debug build for web', () async {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
));
expect(() => runner.run(<String>['build', 'web', '--debug', '--no-pub']),
throwsA(isA<UsageException>()));
}, overrides: <Type, Generator>{
Platform: () => fakePlatform,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => processManager,
});
testUsingContext('Refuses to build for web when feature is disabled', () async { testUsingContext('Refuses to build for web when feature is disabled', () async {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand( final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts, artifacts: artifacts,

View File

@ -909,7 +909,7 @@ void main() {
'--no-minify', '--no-minify',
'--no-source-maps', '--no-source-maps',
'--enable-asserts', '--enable-asserts',
'-O4', '-O1',
'-o', '-o',
environment.buildDir.childFile('main.dart.js').absolute.path, environment.buildDir.childFile('main.dart.js').absolute.path,
environment.buildDir.childFile('app.dill').absolute.path, environment.buildDir.childFile('app.dill').absolute.path,

View File

@ -107,7 +107,7 @@ void main() {
label: 'web-compile', label: 'web-compile',
parameters: CustomDimensions( parameters: CustomDimensions(
buildEventSettings: buildEventSettings:
'optimizationLevel: 4; web-renderer: skwasm,canvaskit; web-target: wasm,js;', 'optimizationLevel: 0; web-renderer: skwasm,canvaskit; web-target: wasm,js;',
), ),
), ),
@ -121,7 +121,7 @@ void main() {
Event.flutterBuildInfo( Event.flutterBuildInfo(
label: 'web-compile', label: 'web-compile',
buildType: 'web', buildType: 'web',
settings: 'optimizationLevel: 4; web-renderer: skwasm,canvaskit; web-target: wasm,js;', settings: 'optimizationLevel: 0; web-renderer: skwasm,canvaskit; web-target: wasm,js;',
), ),
]), ]),
); );