diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index 70e45ccc04f..33edb96423f 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart @@ -116,6 +116,29 @@ class BuildInfo { bool get supportsSimulator => isEmulatorBuildMode(mode); String get modeName => getModeName(mode); String get friendlyModeName => getFriendlyModeName(mode); + + /// Convert to a structued string encoded structure appropriate for usage as + /// environment variables or to embed in other scripts. + /// + /// Fields that are `null` are excluded from this configration. + Map toEnvironmentConfig() { + return { + if (dartDefines?.isNotEmpty ?? false) + 'DART_DEFINES': dartDefines.join(','), + if (dartObfuscation != null) + 'DART_OBFUSCATION': dartObfuscation.toString(), + if (extraFrontEndOptions?.isNotEmpty ?? false) + 'EXTRA_FRONT_END_OPTIONS': extraFrontEndOptions.join(','), + if (extraGenSnapshotOptions?.isNotEmpty ?? false) + 'EXTRA_GEN_SNAPSHOT_OPTIONS': extraGenSnapshotOptions.join(','), + if (splitDebugInfoPath != null) + 'SPLIT_DEBUG_INFO': splitDebugInfoPath, + if (trackWidgetCreation != null) + 'TRACK_WIDGET_CREATION': trackWidgetCreation.toString(), + if (treeShakeIcons != null) + 'TREE_SHAKE_ICONS': treeShakeIcons.toString(), + }; + } } /// Information about an Android build to be performed or used. diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index 65454fd9c09..3b215fe49d0 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -34,7 +34,7 @@ class BuildCommand extends FlutterCommand { addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp)); addSubcommand(BuildWebCommand(verboseHelp: verboseHelp)); addSubcommand(BuildMacosCommand(verboseHelp: verboseHelp)); - addSubcommand(BuildLinuxCommand()); + addSubcommand(BuildLinuxCommand(verboseHelp: verboseHelp)); addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp)); addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp)); } diff --git a/packages/flutter_tools/lib/src/commands/build_linux.dart b/packages/flutter_tools/lib/src/commands/build_linux.dart index fa4f0731db5..f9d10ef1591 100644 --- a/packages/flutter_tools/lib/src/commands/build_linux.dart +++ b/packages/flutter_tools/lib/src/commands/build_linux.dart @@ -16,10 +16,17 @@ import 'build.dart'; /// A command to build a linux desktop target through a build shell script. class BuildLinuxCommand extends BuildSubCommand { - BuildLinuxCommand() { + BuildLinuxCommand({ bool verboseHelp = false }) { addTreeShakeIconsFlag(); - addBuildModeFlags(); usesTargetOption(); + addBuildModeFlags(verboseHelp: verboseHelp); + usesPubOption(); + addSplitDebugInfoOption(); + addDartObfuscationOption(); + usesDartDefineOption(); + usesExtraFrontendOptions(); + addEnableExperimentation(hide: !verboseHelp); + usesTrackWidgetCreation(verboseHelp: verboseHelp); } @override diff --git a/packages/flutter_tools/lib/src/linux/build_linux.dart b/packages/flutter_tools/lib/src/linux/build_linux.dart index 3e886540590..609f2a9c41e 100644 --- a/packages/flutter_tools/lib/src/linux/build_linux.dart +++ b/packages/flutter_tools/lib/src/linux/build_linux.dart @@ -14,7 +14,11 @@ import '../plugins.dart'; import '../project.dart'; /// Builds the Linux project through the Makefile. -Future buildLinux(LinuxProject linuxProject, BuildInfo buildInfo, {String target = 'lib/main.dart'}) async { +Future buildLinux( + LinuxProject linuxProject, + BuildInfo buildInfo, { + String target = 'lib/main.dart', + }) async { if (!linuxProject.makeFile.existsSync()) { throwToolExit('No Linux desktop project configured. See ' 'https://github.com/flutter/flutter/wiki/Desktop-shells#create ' @@ -38,10 +42,15 @@ Future buildLinux(LinuxProject linuxProject, BuildInfo buildInfo, {String final StringBuffer buffer = StringBuffer(''' # Generated code do not commit. export FLUTTER_ROOT=${Cache.flutterRoot} -export TRACK_WIDGET_CREATION=${buildInfo?.trackWidgetCreation == true} export FLUTTER_TARGET=$target export PROJECT_DIR=${linuxProject.project.directory.path} '''); + final Map environmentConfig = buildInfo.toEnvironmentConfig(); + for (final String key in environmentConfig.keys) { + final String value = environmentConfig[key]; + buffer.writeln('export $key=$value'); + } + if (globals.artifacts is LocalEngineArtifacts) { final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts; final String engineOutPath = localEngineArtifacts.engineOutPath; @@ -73,12 +82,18 @@ export PROJECT_DIR=${linuxProject.project.directory.path} ); int result; try { - result = await processUtils.stream([ - 'make', - '-C', - linuxProject.makeFile.parent.path, - 'BUILD=$buildFlag', - ], trace: true); + result = await processUtils.stream( + [ + 'make', + '-C', + linuxProject.makeFile.parent.path, + 'BUILD=$buildFlag', + ], + environment: { + if (globals.logger.isVerbose) + 'VERBOSE_SCRIPT_LOGGING': 'true' + }, trace: true, + ); } on ArgumentError { throwToolExit("make not found. Run 'flutter doctor' for more information."); } finally { diff --git a/packages/flutter_tools/lib/src/windows/build_windows.dart b/packages/flutter_tools/lib/src/windows/build_windows.dart index 0c6622098b4..ade96850445 100644 --- a/packages/flutter_tools/lib/src/windows/build_windows.dart +++ b/packages/flutter_tools/lib/src/windows/build_windows.dart @@ -119,22 +119,9 @@ void _writeGeneratedFlutterProperties( 'FLUTTER_ROOT': Cache.flutterRoot, 'FLUTTER_EPHEMERAL_DIR': windowsProject.ephemeralDirectory.path, 'PROJECT_DIR': windowsProject.project.directory.path, - if (buildInfo.trackWidgetCreation != null) - 'TRACK_WIDGET_CREATION': buildInfo.trackWidgetCreation.toString(), - if (buildInfo.treeShakeIcons != null) - 'TREE_SHAKE_ICONS': buildInfo.treeShakeIcons.toString(), - if (buildInfo.extraGenSnapshotOptions?.isNotEmpty ?? false) - 'EXTRA_GEN_SNAPSHOT_OPTIONS': buildInfo.extraGenSnapshotOptions.join(','), - if (buildInfo.extraFrontEndOptions?.isNotEmpty ?? false) - 'EXTRA_FRONT_END_OPTIONS': buildInfo.extraFrontEndOptions.join(','), - if (buildInfo.dartDefines?.isNotEmpty ?? false) - 'DART_DEFINES': buildInfo.dartDefines.join(','), - if (buildInfo.dartObfuscation != null) - 'DART_OBFUSCATION': buildInfo.dartObfuscation.toString(), - if (buildInfo.splitDebugInfoPath != null) - 'SPLIT_DEBUG_INFO': buildInfo.splitDebugInfoPath, if (target != null) 'FLUTTER_TARGET': target, + ...buildInfo.toEnvironmentConfig(), }; if (globals.artifacts is LocalEngineArtifacts) { final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart index 47754b28718..3213d9e553b 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart @@ -87,7 +87,7 @@ void main() { setUpMockCoreProjectFiles(); expect(createTestCommandRunner(command).run( - const ['build', 'linux'] + const ['build', 'linux', '--no-pub'] ), throwsToolExit(message: 'No Linux desktop project configured')); }, overrides: { Platform: () => linuxPlatform, @@ -101,7 +101,7 @@ void main() { setUpMockProjectFilesForBuild(); expect(createTestCommandRunner(command).run( - const ['build', 'linux'] + const ['build', 'linux', '--no-pub'] ), throwsToolExit()); }, overrides: { Platform: () => notLinuxPlatform, @@ -115,7 +115,7 @@ void main() { setUpMockProjectFilesForBuild(templateVersion: 1); expect(createTestCommandRunner(command).run( - const ['build', 'linux'] + const ['build', 'linux', '--no-pub'] ), throwsToolExit(message: 'flutter create .')); }, overrides: { FileSystem: () => fileSystem, @@ -129,7 +129,7 @@ void main() { setUpMockProjectFilesForBuild(templateVersion: 999); expect(createTestCommandRunner(command).run( - const ['build', 'linux'] + const ['build', 'linux', '--no-pub'] ), throwsToolExit(message: 'Upgrade Flutter')); }, overrides: { FileSystem: () => fileSystem, @@ -146,15 +146,13 @@ void main() { '-C', '/linux', 'BUILD=release', - ], onRun: () { - - }) + ], onRun: () { }) ]); setUpMockProjectFilesForBuild(); await createTestCommandRunner(command).run( - const ['build', 'linux'] + const ['build', 'linux', '--no-pub'] ); expect(fileSystem.file('linux/flutter/ephemeral/generated_config.mk'), exists); }, overrides: { @@ -179,7 +177,7 @@ void main() { ]); expect(createTestCommandRunner(command).run( - const ['build', 'linux'] + const ['build', 'linux', '--no-pub'] ), throwsToolExit(message: "make not found. Run 'flutter doctor' for more information.")); }, overrides: { FileSystem: () => fileSystem, @@ -201,7 +199,7 @@ void main() { ]); await createTestCommandRunner(command).run( - const ['build', 'linux', '--debug'] + const ['build', 'linux', '--debug', '--no-pub'] ); expect(testLogger.statusText, isNot(contains('STDOUT STUFF'))); expect(testLogger.traceText, contains('STDOUT STUFF')); @@ -212,6 +210,36 @@ void main() { FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), }); + testUsingContext('Linux verbose build sets VERBOSE_SCRIPT_LOGGING', () async { + final BuildCommand command = BuildCommand(); + setUpMockProjectFilesForBuild(); + processManager = FakeProcessManager.list([ + const FakeCommand( + command: [ + 'make', + '-C', + '/linux', + 'BUILD=debug', + ], + environment: { + 'VERBOSE_SCRIPT_LOGGING': 'true' + }, + stdout: 'STDOUT STUFF', + ), + ]); + + await createTestCommandRunner(command).run( + const ['build', 'linux', '--debug', '-v', '--no-pub'] + ); + expect(testLogger.statusText, contains('STDOUT STUFF')); + expect(testLogger.traceText, isNot(contains('STDOUT STUFF'))); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + Platform: () => linuxPlatform, + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + }); + testUsingContext('Linux build --debug passes debug mode to make', () async { final BuildCommand command = BuildCommand(); setUpMockProjectFilesForBuild(); @@ -226,7 +254,7 @@ void main() { await createTestCommandRunner(command).run( - const ['build', 'linux', '--debug'] + const ['build', 'linux', '--debug', '--no-pub'] ); }, overrides: { FileSystem: () => fileSystem, @@ -248,7 +276,7 @@ void main() { ]); await createTestCommandRunner(command).run( - const ['build', 'linux', '--profile'] + const ['build', 'linux', '--profile', '--no-pub'] ); }, overrides: { FileSystem: () => fileSystem, @@ -257,6 +285,64 @@ void main() { FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), }); + testUsingContext('Linux build configures Makefile exports', () async { + final BuildCommand command = BuildCommand(); + setUpMockProjectFilesForBuild(); + processManager = FakeProcessManager.list([ + const FakeCommand(command: [ + 'make', + '-C', + '/linux', + 'BUILD=release', + ]), + ]); + fileSystem.file('lib/other.dart') + .createSync(recursive: true); + + await createTestCommandRunner(command).run( + const [ + 'build', + 'linux', + '--target=lib/other.dart', + '--no-pub', + '--track-widget-creation', + '--split-debug-info=foo/', + '--enable-experiment=non-nullable', + '--obfuscate', + '--dart-define=foo.bar=2', + '--dart-define=fizz.far=3', + '--tree-shake-icons', + ] + ); + + final File makeConfig = fileSystem.currentDirectory + .childDirectory('linux') + .childDirectory('flutter') + .childDirectory('ephemeral') + .childFile('generated_config.mk'); + + expect(makeConfig, exists); + + final List configLines = makeConfig.readAsLinesSync(); + + expect(configLines, containsAll([ + 'export DART_DEFINES=foo.bar=2,fizz.far=3', + 'export DART_OBFUSCATION=true', + 'export EXTRA_FRONT_END_OPTIONS=--enable-experiment=non-nullable', + 'export EXTRA_GEN_SNAPSHOT_OPTIONS=--enable-experiment=non-nullable', + 'export SPLIT_DEBUG_INFO=foo/', + 'export TRACK_WIDGET_CREATION=true', + 'export TREE_SHAKE_ICONS=true', + 'export FLUTTER_ROOT=$_kTestFlutterRoot', + 'export FLUTTER_TARGET=lib/other.dart', + ])); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + Platform: () => linuxPlatform, + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + }); + testUsingContext('linux can extract binary name from Makefile', () async { fileSystem.file('linux/Makefile') ..createSync(recursive: true) @@ -305,7 +391,7 @@ BINARY_NAME=fizz_bar testUsingContext('Refuses to build for Linux when feature is disabled', () { final CommandRunner runner = createTestCommandRunner(BuildCommand()); - expect(() => runner.run(['build', 'linux']), + expect(() => runner.run(['build', 'linux', '--no-pub']), throwsToolExit()); }, overrides: { FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false), @@ -324,7 +410,7 @@ BINARY_NAME=fizz_bar ]); await createTestCommandRunner(command).run( - const ['build', 'linux'] + const ['build', 'linux', '--no-pub'] ); expect(testLogger.statusText, contains('🚧')); }, overrides: { diff --git a/packages/flutter_tools/test/general.shard/build_info_test.dart b/packages/flutter_tools/test/general.shard/build_info_test.dart index 6fdd1692045..4085bebfd46 100644 --- a/packages/flutter_tools/test/general.shard/build_info_test.dart +++ b/packages/flutter_tools/test/general.shard/build_info_test.dart @@ -93,4 +93,26 @@ void main() { expect(getIOSArchForName('x86_64'), DarwinArch.x86_64); expect(() => getIOSArchForName('bogus'), throwsException); }); + + test('toEnvironmentConfig encoding of standard values', () { + const BuildInfo buildInfo = BuildInfo(BuildMode.debug, '', + treeShakeIcons: true, + trackWidgetCreation: true, + dartDefines: ['foo=2', 'bar=2'], + dartObfuscation: true, + splitDebugInfoPath: 'foo/', + extraFrontEndOptions: ['--enable-experiment=non-nullable', 'bar'], + extraGenSnapshotOptions: ['--enable-experiment=non-nullable', 'fizz'], + ); + + expect(buildInfo.toEnvironmentConfig(), { + 'TREE_SHAKE_ICONS': 'true', + 'TRACK_WIDGET_CREATION': 'true', + 'DART_DEFINES': 'foo=2,bar=2', + 'DART_OBFUSCATION': 'true', + 'SPLIT_DEBUG_INFO': 'foo/', + 'EXTRA_FRONT_END_OPTIONS': '--enable-experiment=non-nullable,bar', + 'EXTRA_GEN_SNAPSHOT_OPTIONS': '--enable-experiment=non-nullable,fizz', + }); + }); }