Unverified Commit dd88b204 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] quality pass on Linux build (#55556)

- Update the Linux build to support most of the build configuration, though like windows most only make sense for profile/release.
- Ensure VERBOSE_SCRIPT_LOGGING is set when the logger is verbose
- Automatically run pub get like other build commands
parent d72eea53
...@@ -116,6 +116,29 @@ class BuildInfo { ...@@ -116,6 +116,29 @@ class BuildInfo {
bool get supportsSimulator => isEmulatorBuildMode(mode); bool get supportsSimulator => isEmulatorBuildMode(mode);
String get modeName => getModeName(mode); String get modeName => getModeName(mode);
String get friendlyModeName => getFriendlyModeName(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<String, String> toEnvironmentConfig() {
return <String, String>{
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. /// Information about an Android build to be performed or used.
......
...@@ -34,7 +34,7 @@ class BuildCommand extends FlutterCommand { ...@@ -34,7 +34,7 @@ class BuildCommand extends FlutterCommand {
addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp)); addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp));
addSubcommand(BuildWebCommand(verboseHelp: verboseHelp)); addSubcommand(BuildWebCommand(verboseHelp: verboseHelp));
addSubcommand(BuildMacosCommand(verboseHelp: verboseHelp)); addSubcommand(BuildMacosCommand(verboseHelp: verboseHelp));
addSubcommand(BuildLinuxCommand()); addSubcommand(BuildLinuxCommand(verboseHelp: verboseHelp));
addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp)); addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp));
addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp)); addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp));
} }
......
...@@ -16,10 +16,17 @@ import 'build.dart'; ...@@ -16,10 +16,17 @@ import 'build.dart';
/// A command to build a linux desktop target through a build shell script. /// A command to build a linux desktop target through a build shell script.
class BuildLinuxCommand extends BuildSubCommand { class BuildLinuxCommand extends BuildSubCommand {
BuildLinuxCommand() { BuildLinuxCommand({ bool verboseHelp = false }) {
addTreeShakeIconsFlag(); addTreeShakeIconsFlag();
addBuildModeFlags();
usesTargetOption(); usesTargetOption();
addBuildModeFlags(verboseHelp: verboseHelp);
usesPubOption();
addSplitDebugInfoOption();
addDartObfuscationOption();
usesDartDefineOption();
usesExtraFrontendOptions();
addEnableExperimentation(hide: !verboseHelp);
usesTrackWidgetCreation(verboseHelp: verboseHelp);
} }
@override @override
......
...@@ -14,7 +14,11 @@ import '../plugins.dart'; ...@@ -14,7 +14,11 @@ import '../plugins.dart';
import '../project.dart'; import '../project.dart';
/// Builds the Linux project through the Makefile. /// Builds the Linux project through the Makefile.
Future<void> buildLinux(LinuxProject linuxProject, BuildInfo buildInfo, {String target = 'lib/main.dart'}) async { Future<void> buildLinux(
LinuxProject linuxProject,
BuildInfo buildInfo, {
String target = 'lib/main.dart',
}) async {
if (!linuxProject.makeFile.existsSync()) { if (!linuxProject.makeFile.existsSync()) {
throwToolExit('No Linux desktop project configured. See ' throwToolExit('No Linux desktop project configured. See '
'https://github.com/flutter/flutter/wiki/Desktop-shells#create ' 'https://github.com/flutter/flutter/wiki/Desktop-shells#create '
...@@ -38,10 +42,15 @@ Future<void> buildLinux(LinuxProject linuxProject, BuildInfo buildInfo, {String ...@@ -38,10 +42,15 @@ Future<void> buildLinux(LinuxProject linuxProject, BuildInfo buildInfo, {String
final StringBuffer buffer = StringBuffer(''' final StringBuffer buffer = StringBuffer('''
# Generated code do not commit. # Generated code do not commit.
export FLUTTER_ROOT=${Cache.flutterRoot} export FLUTTER_ROOT=${Cache.flutterRoot}
export TRACK_WIDGET_CREATION=${buildInfo?.trackWidgetCreation == true}
export FLUTTER_TARGET=$target export FLUTTER_TARGET=$target
export PROJECT_DIR=${linuxProject.project.directory.path} export PROJECT_DIR=${linuxProject.project.directory.path}
'''); ''');
final Map<String, String> 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) { if (globals.artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts; final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts;
final String engineOutPath = localEngineArtifacts.engineOutPath; final String engineOutPath = localEngineArtifacts.engineOutPath;
...@@ -73,12 +82,18 @@ export PROJECT_DIR=${linuxProject.project.directory.path} ...@@ -73,12 +82,18 @@ export PROJECT_DIR=${linuxProject.project.directory.path}
); );
int result; int result;
try { try {
result = await processUtils.stream(<String>[ result = await processUtils.stream(
<String>[
'make', 'make',
'-C', '-C',
linuxProject.makeFile.parent.path, linuxProject.makeFile.parent.path,
'BUILD=$buildFlag', 'BUILD=$buildFlag',
], trace: true); ],
environment: <String, String>{
if (globals.logger.isVerbose)
'VERBOSE_SCRIPT_LOGGING': 'true'
}, trace: true,
);
} on ArgumentError { } on ArgumentError {
throwToolExit("make not found. Run 'flutter doctor' for more information."); throwToolExit("make not found. Run 'flutter doctor' for more information.");
} finally { } finally {
......
...@@ -119,22 +119,9 @@ void _writeGeneratedFlutterProperties( ...@@ -119,22 +119,9 @@ void _writeGeneratedFlutterProperties(
'FLUTTER_ROOT': Cache.flutterRoot, 'FLUTTER_ROOT': Cache.flutterRoot,
'FLUTTER_EPHEMERAL_DIR': windowsProject.ephemeralDirectory.path, 'FLUTTER_EPHEMERAL_DIR': windowsProject.ephemeralDirectory.path,
'PROJECT_DIR': windowsProject.project.directory.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) if (target != null)
'FLUTTER_TARGET': target, 'FLUTTER_TARGET': target,
...buildInfo.toEnvironmentConfig(),
}; };
if (globals.artifacts is LocalEngineArtifacts) { if (globals.artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts; final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts;
......
...@@ -87,7 +87,7 @@ void main() { ...@@ -87,7 +87,7 @@ void main() {
setUpMockCoreProjectFiles(); setUpMockCoreProjectFiles();
expect(createTestCommandRunner(command).run( expect(createTestCommandRunner(command).run(
const <String>['build', 'linux'] const <String>['build', 'linux', '--no-pub']
), throwsToolExit(message: 'No Linux desktop project configured')); ), throwsToolExit(message: 'No Linux desktop project configured'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => linuxPlatform, Platform: () => linuxPlatform,
...@@ -101,7 +101,7 @@ void main() { ...@@ -101,7 +101,7 @@ void main() {
setUpMockProjectFilesForBuild(); setUpMockProjectFilesForBuild();
expect(createTestCommandRunner(command).run( expect(createTestCommandRunner(command).run(
const <String>['build', 'linux'] const <String>['build', 'linux', '--no-pub']
), throwsToolExit()); ), throwsToolExit());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => notLinuxPlatform, Platform: () => notLinuxPlatform,
...@@ -115,7 +115,7 @@ void main() { ...@@ -115,7 +115,7 @@ void main() {
setUpMockProjectFilesForBuild(templateVersion: 1); setUpMockProjectFilesForBuild(templateVersion: 1);
expect(createTestCommandRunner(command).run( expect(createTestCommandRunner(command).run(
const <String>['build', 'linux'] const <String>['build', 'linux', '--no-pub']
), throwsToolExit(message: 'flutter create .')); ), throwsToolExit(message: 'flutter create .'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -129,7 +129,7 @@ void main() { ...@@ -129,7 +129,7 @@ void main() {
setUpMockProjectFilesForBuild(templateVersion: 999); setUpMockProjectFilesForBuild(templateVersion: 999);
expect(createTestCommandRunner(command).run( expect(createTestCommandRunner(command).run(
const <String>['build', 'linux'] const <String>['build', 'linux', '--no-pub']
), throwsToolExit(message: 'Upgrade Flutter')); ), throwsToolExit(message: 'Upgrade Flutter'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -146,15 +146,13 @@ void main() { ...@@ -146,15 +146,13 @@ void main() {
'-C', '-C',
'/linux', '/linux',
'BUILD=release', 'BUILD=release',
], onRun: () { ], onRun: () { })
})
]); ]);
setUpMockProjectFilesForBuild(); setUpMockProjectFilesForBuild();
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'linux'] const <String>['build', 'linux', '--no-pub']
); );
expect(fileSystem.file('linux/flutter/ephemeral/generated_config.mk'), exists); expect(fileSystem.file('linux/flutter/ephemeral/generated_config.mk'), exists);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
...@@ -179,7 +177,7 @@ void main() { ...@@ -179,7 +177,7 @@ void main() {
]); ]);
expect(createTestCommandRunner(command).run( expect(createTestCommandRunner(command).run(
const <String>['build', 'linux'] const <String>['build', 'linux', '--no-pub']
), throwsToolExit(message: "make not found. Run 'flutter doctor' for more information.")); ), throwsToolExit(message: "make not found. Run 'flutter doctor' for more information."));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -201,7 +199,7 @@ void main() { ...@@ -201,7 +199,7 @@ void main() {
]); ]);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'linux', '--debug'] const <String>['build', 'linux', '--debug', '--no-pub']
); );
expect(testLogger.statusText, isNot(contains('STDOUT STUFF'))); expect(testLogger.statusText, isNot(contains('STDOUT STUFF')));
expect(testLogger.traceText, contains('STDOUT STUFF')); expect(testLogger.traceText, contains('STDOUT STUFF'));
...@@ -212,6 +210,36 @@ void main() { ...@@ -212,6 +210,36 @@ void main() {
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
}); });
testUsingContext('Linux verbose build sets VERBOSE_SCRIPT_LOGGING', () async {
final BuildCommand command = BuildCommand();
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'make',
'-C',
'/linux',
'BUILD=debug',
],
environment: <String, String>{
'VERBOSE_SCRIPT_LOGGING': 'true'
},
stdout: 'STDOUT STUFF',
),
]);
await createTestCommandRunner(command).run(
const <String>['build', 'linux', '--debug', '-v', '--no-pub']
);
expect(testLogger.statusText, contains('STDOUT STUFF'));
expect(testLogger.traceText, isNot(contains('STDOUT STUFF')));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Platform: () => linuxPlatform,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
});
testUsingContext('Linux build --debug passes debug mode to make', () async { testUsingContext('Linux build --debug passes debug mode to make', () async {
final BuildCommand command = BuildCommand(); final BuildCommand command = BuildCommand();
setUpMockProjectFilesForBuild(); setUpMockProjectFilesForBuild();
...@@ -226,7 +254,7 @@ void main() { ...@@ -226,7 +254,7 @@ void main() {
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'linux', '--debug'] const <String>['build', 'linux', '--debug', '--no-pub']
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -248,7 +276,7 @@ void main() { ...@@ -248,7 +276,7 @@ void main() {
]); ]);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'linux', '--profile'] const <String>['build', 'linux', '--profile', '--no-pub']
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -257,6 +285,64 @@ void main() { ...@@ -257,6 +285,64 @@ void main() {
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
}); });
testUsingContext('Linux build configures Makefile exports', () async {
final BuildCommand command = BuildCommand();
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(command: <String>[
'make',
'-C',
'/linux',
'BUILD=release',
]),
]);
fileSystem.file('lib/other.dart')
.createSync(recursive: true);
await createTestCommandRunner(command).run(
const <String>[
'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<String> configLines = makeConfig.readAsLinesSync();
expect(configLines, containsAll(<String>[
'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: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Platform: () => linuxPlatform,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
});
testUsingContext('linux can extract binary name from Makefile', () async { testUsingContext('linux can extract binary name from Makefile', () async {
fileSystem.file('linux/Makefile') fileSystem.file('linux/Makefile')
..createSync(recursive: true) ..createSync(recursive: true)
...@@ -305,7 +391,7 @@ BINARY_NAME=fizz_bar ...@@ -305,7 +391,7 @@ BINARY_NAME=fizz_bar
testUsingContext('Refuses to build for Linux when feature is disabled', () { testUsingContext('Refuses to build for Linux when feature is disabled', () {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand()); final CommandRunner<void> runner = createTestCommandRunner(BuildCommand());
expect(() => runner.run(<String>['build', 'linux']), expect(() => runner.run(<String>['build', 'linux', '--no-pub']),
throwsToolExit()); throwsToolExit());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false), FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
...@@ -324,7 +410,7 @@ BINARY_NAME=fizz_bar ...@@ -324,7 +410,7 @@ BINARY_NAME=fizz_bar
]); ]);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'linux'] const <String>['build', 'linux', '--no-pub']
); );
expect(testLogger.statusText, contains('🚧')); expect(testLogger.statusText, contains('🚧'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
......
...@@ -93,4 +93,26 @@ void main() { ...@@ -93,4 +93,26 @@ void main() {
expect(getIOSArchForName('x86_64'), DarwinArch.x86_64); expect(getIOSArchForName('x86_64'), DarwinArch.x86_64);
expect(() => getIOSArchForName('bogus'), throwsException); expect(() => getIOSArchForName('bogus'), throwsException);
}); });
test('toEnvironmentConfig encoding of standard values', () {
const BuildInfo buildInfo = BuildInfo(BuildMode.debug, '',
treeShakeIcons: true,
trackWidgetCreation: true,
dartDefines: <String>['foo=2', 'bar=2'],
dartObfuscation: true,
splitDebugInfoPath: 'foo/',
extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
);
expect(buildInfo.toEnvironmentConfig(), <String, String>{
'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',
});
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment