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 {
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<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.
......
......@@ -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));
}
......
......@@ -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
......
......@@ -14,7 +14,11 @@ import '../plugins.dart';
import '../project.dart';
/// 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()) {
throwToolExit('No Linux desktop project configured. See '
'https://github.com/flutter/flutter/wiki/Desktop-shells#create '
......@@ -38,10 +42,15 @@ Future<void> 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<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) {
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(<String>[
result = await processUtils.stream(
<String>[
'make',
'-C',
linuxProject.makeFile.parent.path,
'BUILD=$buildFlag',
], trace: true);
],
environment: <String, String>{
if (globals.logger.isVerbose)
'VERBOSE_SCRIPT_LOGGING': 'true'
}, trace: true,
);
} on ArgumentError {
throwToolExit("make not found. Run 'flutter doctor' for more information.");
} finally {
......
......@@ -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;
......
......@@ -87,7 +87,7 @@ void main() {
setUpMockCoreProjectFiles();
expect(createTestCommandRunner(command).run(
const <String>['build', 'linux']
const <String>['build', 'linux', '--no-pub']
), throwsToolExit(message: 'No Linux desktop project configured'));
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
......@@ -101,7 +101,7 @@ void main() {
setUpMockProjectFilesForBuild();
expect(createTestCommandRunner(command).run(
const <String>['build', 'linux']
const <String>['build', 'linux', '--no-pub']
), throwsToolExit());
}, overrides: <Type, Generator>{
Platform: () => notLinuxPlatform,
......@@ -115,7 +115,7 @@ void main() {
setUpMockProjectFilesForBuild(templateVersion: 1);
expect(createTestCommandRunner(command).run(
const <String>['build', 'linux']
const <String>['build', 'linux', '--no-pub']
), throwsToolExit(message: 'flutter create .'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
......@@ -129,7 +129,7 @@ void main() {
setUpMockProjectFilesForBuild(templateVersion: 999);
expect(createTestCommandRunner(command).run(
const <String>['build', 'linux']
const <String>['build', 'linux', '--no-pub']
), throwsToolExit(message: 'Upgrade Flutter'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
......@@ -146,15 +146,13 @@ void main() {
'-C',
'/linux',
'BUILD=release',
], onRun: () {
})
], onRun: () { })
]);
setUpMockProjectFilesForBuild();
await createTestCommandRunner(command).run(
const <String>['build', 'linux']
const <String>['build', 'linux', '--no-pub']
);
expect(fileSystem.file('linux/flutter/ephemeral/generated_config.mk'), exists);
}, overrides: <Type, Generator>{
......@@ -179,7 +177,7 @@ void main() {
]);
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."));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
......@@ -201,7 +199,7 @@ void main() {
]);
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.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(<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 {
final BuildCommand command = BuildCommand();
setUpMockProjectFilesForBuild();
......@@ -226,7 +254,7 @@ void main() {
await createTestCommandRunner(command).run(
const <String>['build', 'linux', '--debug']
const <String>['build', 'linux', '--debug', '--no-pub']
);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
......@@ -248,7 +276,7 @@ void main() {
]);
await createTestCommandRunner(command).run(
const <String>['build', 'linux', '--profile']
const <String>['build', 'linux', '--profile', '--no-pub']
);
}, overrides: <Type, Generator>{
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(<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 {
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<void> runner = createTestCommandRunner(BuildCommand());
expect(() => runner.run(<String>['build', 'linux']),
expect(() => runner.run(<String>['build', 'linux', '--no-pub']),
throwsToolExit());
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
......@@ -324,7 +410,7 @@ BINARY_NAME=fizz_bar
]);
await createTestCommandRunner(command).run(
const <String>['build', 'linux']
const <String>['build', 'linux', '--no-pub']
);
expect(testLogger.statusText, contains('🚧'));
}, overrides: <Type, Generator>{
......
......@@ -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: <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