[flutter_tools] Support profile and release builds on Linux (#57135)
diff --git a/packages/flutter_tools/bin/tool_backend.dart b/packages/flutter_tools/bin/tool_backend.dart
index 7ffd7f0..4f55437f 100644
--- a/packages/flutter_tools/bin/tool_backend.dart
+++ b/packages/flutter_tools/bin/tool_backend.dart
@@ -45,10 +45,7 @@
final String flutterExecutable = path.join(
flutterRoot, 'bin', Platform.isWindows ? 'flutter.bat' : 'flutter');
final String bundlePlatform = targetPlatform == 'windows-x64' ? 'windows' : 'linux';
- // TODO(jonahwilliams): currently all Linux builds are debug builds. Remove the
- // hardcoded mode when profile and release support is added.
- final String bundleMode = targetPlatform == 'windows-x64' ? buildMode : 'debug';
- final String target = '${bundleMode}_bundle_${bundlePlatform}_assets';
+ final String target = '${buildMode}_bundle_${bundlePlatform}_assets';
final Process assembleProcess = await Process.start(
flutterExecutable,
@@ -61,7 +58,7 @@
'--output=build',
'-dTargetPlatform=$targetPlatform',
'-dTrackWidgetCreation=$trackWidgetCreation',
- '-dBuildMode=$bundleMode',
+ '-dBuildMode=$buildMode',
'-dTargetFile=$flutterTarget',
'-dTreeShakeIcons="$treeShakeIcons"',
'-dDartObfuscation=$dartObfuscation',
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart
index 754c3b1..9532f33 100644
--- a/packages/flutter_tools/lib/src/base/build.dart
+++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -298,6 +298,7 @@
TargetPlatform.android_x64,
TargetPlatform.ios,
TargetPlatform.darwin_x64,
+ TargetPlatform.linux_x64,
TargetPlatform.windows_x64,
].contains(platform);
}
diff --git a/packages/flutter_tools/lib/src/build_system/targets/dart.dart b/packages/flutter_tools/lib/src/build_system/targets/dart.dart
index ded87bd..dd5ba5b 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/dart.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/dart.dart
@@ -340,7 +340,7 @@
KernelSnapshot(),
];
- final TargetPlatform targetPlatform;
+ final TargetPlatform targetPlatform;
}
/// Generate an ELF binary from a dart kernel file in release mode.
@@ -373,7 +373,7 @@
KernelSnapshot(),
];
- final TargetPlatform targetPlatform;
+ final TargetPlatform targetPlatform;
}
/// Copies the pre-built flutter aot bundle.
diff --git a/packages/flutter_tools/lib/src/build_system/targets/linux.dart b/packages/flutter_tools/lib/src/build_system/targets/linux.dart
index 9d0a8e3..00a03c4 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/linux.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/linux.dart
@@ -98,12 +98,9 @@
}
}
-/// Creates a debug bundle for the Linux desktop target.
-class DebugBundleLinuxAssets extends Target {
- const DebugBundleLinuxAssets();
-
- @override
- String get name => 'debug_bundle_linux_assets';
+/// Creates a bundle for the Linux desktop target.
+abstract class BundleLinuxAssets extends Target {
+ const BundleLinuxAssets();
@override
List<Target> get dependencies => const <Target>[
@@ -113,18 +110,12 @@
@override
List<Source> get inputs => const <Source>[
- Source.pattern('{BUILD_DIR}/app.dill'),
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/linux.dart'),
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
...IconTreeShaker.inputs,
];
@override
- List<Source> get outputs => const <Source>[
- Source.pattern('{OUTPUT_DIR}/flutter_assets/kernel_blob.bin'),
- ];
-
- @override
List<String> get depfiles => const <String>[
'flutter_assets.d',
];
@@ -132,7 +123,7 @@
@override
Future<void> build(Environment environment) async {
if (environment.defines[kBuildMode] == null) {
- throw MissingDefineException(kBuildMode, 'debug_bundle_linux_assets');
+ throw MissingDefineException(kBuildMode, 'bundle_linux_assets');
}
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final Directory outputDirectory = environment.outputDir
@@ -157,3 +148,89 @@
);
}
}
+
+/// A wrapper for AOT compilation that copies app.so into the output directory.
+class LinuxAotBundle extends Target {
+ /// Create a [LinuxAotBundle] wrapper for [aotTarget].
+ const LinuxAotBundle(this.aotTarget);
+
+ /// The [AotElfBase] subclass that produces the app.so.
+ final AotElfBase aotTarget;
+
+ @override
+ String get name => 'linux_aot_bundle';
+
+ @override
+ List<Source> get inputs => const <Source>[
+ Source.pattern('{BUILD_DIR}/app.so'),
+ ];
+
+ @override
+ List<Source> get outputs => const <Source>[
+ Source.pattern('{OUTPUT_DIR}/lib/libapp.so'),
+ ];
+
+ @override
+ List<Target> get dependencies => <Target>[
+ aotTarget,
+ ];
+
+ @override
+ Future<void> build(Environment environment) async {
+ final File outputFile = environment.buildDir.childFile('app.so');
+ final Directory outputDirectory = environment.outputDir.childDirectory('lib');
+ if (!outputDirectory.existsSync()) {
+ outputDirectory.createSync(recursive: true);
+ }
+ outputFile.copySync(outputDirectory.childFile('libapp.so').path);
+ }
+}
+
+class DebugBundleLinuxAssets extends BundleLinuxAssets {
+ const DebugBundleLinuxAssets();
+
+ @override
+ String get name => 'debug_bundle_linux_assets';
+
+ @override
+ List<Source> get inputs => <Source>[
+ const Source.pattern('{BUILD_DIR}/app.dill'),
+ ];
+
+ @override
+ List<Source> get outputs => <Source>[
+ const Source.pattern('{OUTPUT_DIR}/flutter_assets/kernel_blob.bin'),
+ ];
+}
+
+class ProfileBundleLinuxAssets extends BundleLinuxAssets {
+ const ProfileBundleLinuxAssets();
+
+ @override
+ String get name => 'profile_bundle_linux_assets';
+
+ @override
+ List<Source> get outputs => const <Source>[];
+
+ @override
+ List<Target> get dependencies => <Target>[
+ ...super.dependencies,
+ const LinuxAotBundle(AotElfProfile(TargetPlatform.linux_x64)),
+ ];
+}
+
+class ReleaseBundleLinuxAssets extends BundleLinuxAssets {
+ const ReleaseBundleLinuxAssets();
+
+ @override
+ String get name => 'release_bundle_linux_assets';
+
+ @override
+ List<Source> get outputs => const <Source>[];
+
+ @override
+ List<Target> get dependencies => <Target>[
+ ...super.dependencies,
+ const LinuxAotBundle(AotElfRelease(TargetPlatform.linux_x64)),
+ ];
+}
diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart
index 09443ef..74aa1ad 100644
--- a/packages/flutter_tools/lib/src/commands/assemble.dart
+++ b/packages/flutter_tools/lib/src/commands/assemble.dart
@@ -40,6 +40,8 @@
ReleaseMacOSBundleFlutterAssets(),
// Linux targets
DebugBundleLinuxAssets(),
+ ProfileBundleLinuxAssets(),
+ ReleaseBundleLinuxAssets(),
// Web targets
WebServiceWorker(),
ReleaseAndroidApplication(),
diff --git a/packages/flutter_tools/lib/src/linux/build_linux.dart b/packages/flutter_tools/lib/src/linux/build_linux.dart
index d24c4fd..42c92fa 100644
--- a/packages/flutter_tools/lib/src/linux/build_linux.dart
+++ b/packages/flutter_tools/lib/src/linux/build_linux.dart
@@ -55,15 +55,6 @@
createPluginSymlinks(linuxProject.project);
- if (!buildInfo.isDebug) {
- const String warning = '🚧 ';
- globals.printStatus(warning * 20);
- globals.printStatus('Warning: Only debug is currently implemented for Linux. This is effectively a debug build.');
- globals.printStatus('See https://github.com/flutter/flutter/issues/38478 for details and updates.');
- globals.printStatus(warning * 20);
- globals.printStatus('');
- }
-
final Status status = globals.logger.startProgress(
'Building Linux application...',
timeout: null,
diff --git a/packages/flutter_tools/templates/app/linux.tmpl/CMakeLists.txt.tmpl b/packages/flutter_tools/templates/app/linux.tmpl/CMakeLists.txt.tmpl
index a181158..2fb186a 100644
--- a/packages/flutter_tools/templates/app/linux.tmpl/CMakeLists.txt.tmpl
+++ b/packages/flutter_tools/templates/app/linux.tmpl/CMakeLists.txt.tmpl
@@ -82,3 +82,9 @@
" COMPONENT Runtime)
INSTALL(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the lib directory on non-Debug builds only.
+if (NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ INSTALL(DIRECTORY "${PROJECT_BUILD_DIR}/lib"
+ DESTINATION "${CMAKE_INSTALL_PREFIX}" COMPONENT Runtime)
+endif()
diff --git a/packages/flutter_tools/templates/app/linux.tmpl/flutter/.template_version b/packages/flutter_tools/templates/app/linux.tmpl/flutter/.template_version
index d8263ee..e440e5c 100644
--- a/packages/flutter_tools/templates/app/linux.tmpl/flutter/.template_version
+++ b/packages/flutter_tools/templates/app/linux.tmpl/flutter/.template_version
@@ -1 +1 @@
-2
\ No newline at end of file
+3
\ No newline at end of file
diff --git a/packages/flutter_tools/templates/app/linux.tmpl/main.cc b/packages/flutter_tools/templates/app/linux.tmpl/main.cc
index e92332d..5211469 100644
--- a/packages/flutter_tools/templates/app/linux.tmpl/main.cc
+++ b/packages/flutter_tools/templates/app/linux.tmpl/main.cc
@@ -16,9 +16,10 @@
// Runs the application in headless mode, without a window.
void RunHeadless(const std::string& icu_data_path,
const std::string& assets_path,
- const std::vector<std::string>& arguments) {
+ const std::vector<std::string>& arguments,
+ const std::string& aot_library_path) {
flutter::FlutterEngine engine;
- engine.Start(icu_data_path, assets_path, arguments);
+ engine.Start(icu_data_path, assets_path, arguments, aot_library_path);
RegisterPlugins(&engine);
while (true) {
engine.RunEventLoopWithTimeout();
@@ -32,6 +33,9 @@
std::string assets_path = data_directory + "/flutter_assets";
std::string icu_data_path = data_directory + "/icudtl.dat";
+ std::string lib_directory = "lib";
+ std::string aot_library_path = lib_directory + "/libapp.so";
+
// Arguments for the Flutter Engine.
std::vector<std::string> arguments;
@@ -43,10 +47,10 @@
// Start the engine.
if (!flutter_controller.CreateWindow(window_properties, assets_path,
- arguments)) {
+ arguments, aot_library_path)) {
if (getenv("DISPLAY") == nullptr) {
std::cout << "No DISPLAY; falling back to headless mode." << std::endl;
- RunHeadless(icu_data_path, assets_path, arguments);
+ RunHeadless(icu_data_path, assets_path, arguments, aot_library_path);
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
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 e85dbb9..c88a4bd 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
@@ -401,25 +401,6 @@
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
});
- testUsingContext('Release build prints an under-construction warning', () async {
- final BuildCommand command = BuildCommand();
- setUpMockProjectFilesForBuild();
- processManager = FakeProcessManager.list(<FakeCommand>[
- cmakeCommand('release'),
- ninjaCommand('release'),
- ]);
-
- await createTestCommandRunner(command).run(
- const <String>['build', 'linux', '--no-pub']
- );
- expect(testLogger.statusText, contains('🚧'));
- }, overrides: <Type, Generator>{
- FileSystem: () => fileSystem,
- ProcessManager: () => processManager,
- Platform: () => linuxPlatform,
- FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
- });
-
testUsingContext('hidden when not enabled on Linux host', () {
expect(BuildLinuxCommand().hidden, true);
}, overrides: <Type, Generator>{
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
index 05a6e90..9daba1f 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
@@ -7,6 +7,7 @@
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/targets/dart.dart';
import 'package:flutter_tools/src/build_system/targets/linux.dart';
@@ -100,6 +101,72 @@
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
+
+ testUsingContext('ProfileBundleLinuxAssets copies artifacts to out directory', () async {
+ final Environment testEnvironment = Environment.test(
+ fileSystem.currentDirectory,
+ defines: <String, String>{
+ kBuildMode: 'profile',
+ },
+ artifacts: MockArtifacts(),
+ processManager: FakeProcessManager.any(),
+ fileSystem: fileSystem,
+ logger: BufferLogger.test(),
+ );
+
+ testEnvironment.buildDir.createSync(recursive: true);
+
+ // Create input files.
+ testEnvironment.buildDir.childFile('app.so').createSync();
+
+ await const LinuxAotBundle(AotElfProfile(TargetPlatform.linux_x64)).build(testEnvironment);
+ await const ProfileBundleLinuxAssets().build(testEnvironment);
+ final Directory libDir = testEnvironment.outputDir
+ .childDirectory('lib');
+ final Directory assetsDir = testEnvironment.outputDir
+ .childDirectory('flutter_assets');
+
+ expect(libDir.childFile('libapp.so'), exists);
+ expect(assetsDir.childFile('AssetManifest.json'), exists);
+ // No bundled fonts
+ expect(assetsDir.childFile('FontManifest.json'), isNot(exists));
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+
+ testUsingContext('ReleaseBundleLinuxAssets copies artifacts to out directory', () async {
+ final Environment testEnvironment = Environment.test(
+ fileSystem.currentDirectory,
+ defines: <String, String>{
+ kBuildMode: 'release',
+ },
+ artifacts: MockArtifacts(),
+ processManager: FakeProcessManager.any(),
+ fileSystem: fileSystem,
+ logger: BufferLogger.test(),
+ );
+
+ testEnvironment.buildDir.createSync(recursive: true);
+
+ // Create input files.
+ testEnvironment.buildDir.childFile('app.so').createSync();
+
+ await const LinuxAotBundle(AotElfRelease(TargetPlatform.linux_x64)).build(testEnvironment);
+ await const ReleaseBundleLinuxAssets().build(testEnvironment);
+ final Directory libDir = testEnvironment.outputDir
+ .childDirectory('lib');
+ final Directory assetsDir = testEnvironment.outputDir
+ .childDirectory('flutter_assets');
+
+ expect(libDir.childFile('libapp.so'), exists);
+ expect(assetsDir.childFile('AssetManifest.json'), exists);
+ // No bundled fonts
+ expect(assetsDir.childFile('FontManifest.json'), isNot(exists));
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
}
void setUpCacheDirectory(FileSystem fileSystem) {