Unverified Commit 5ee5490a authored by Marcus Tomlinson's avatar Marcus Tomlinson Committed by GitHub

[flutter_tools] Support profile and release builds on Linux (#57135)

parent 03c74eb4
......@@ -45,10 +45,7 @@ or
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 @@ or
'--output=build',
'-dTargetPlatform=$targetPlatform',
'-dTrackWidgetCreation=$trackWidgetCreation',
'-dBuildMode=$bundleMode',
'-dBuildMode=$buildMode',
'-dTargetFile=$flutterTarget',
'-dTreeShakeIcons="$treeShakeIcons"',
'-dDartObfuscation=$dartObfuscation',
......
......@@ -298,6 +298,7 @@ class AOTSnapshotter {
TargetPlatform.android_x64,
TargetPlatform.ios,
TargetPlatform.darwin_x64,
TargetPlatform.linux_x64,
TargetPlatform.windows_x64,
].contains(platform);
}
......
......@@ -340,7 +340,7 @@ class AotElfProfile extends AotElfBase {
KernelSnapshot(),
];
final TargetPlatform targetPlatform;
final TargetPlatform targetPlatform;
}
/// Generate an ELF binary from a dart kernel file in release mode.
......@@ -373,7 +373,7 @@ class AotElfRelease extends AotElfBase {
KernelSnapshot(),
];
final TargetPlatform targetPlatform;
final TargetPlatform targetPlatform;
}
/// Copies the pre-built flutter aot bundle.
......
......@@ -98,12 +98,9 @@ class UnpackLinux extends Target {
}
}
/// 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,17 +110,11 @@ class DebugBundleLinuxAssets extends Target {
@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 @@ class DebugBundleLinuxAssets extends Target {
@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 @@ class DebugBundleLinuxAssets extends Target {
);
}
}
/// 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)),
];
}
......@@ -40,6 +40,8 @@ const List<Target> _kDefaultTargets = <Target>[
ReleaseMacOSBundleFlutterAssets(),
// Linux targets
DebugBundleLinuxAssets(),
ProfileBundleLinuxAssets(),
ReleaseBundleLinuxAssets(),
// Web targets
WebServiceWorker(),
ReleaseAndroidApplication(),
......
......@@ -55,15 +55,6 @@ Future<void> buildLinux(
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,
......
......@@ -82,3 +82,9 @@ INSTALL(CODE "
" 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()
......@@ -16,9 +16,10 @@ namespace {
// 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 @@ int main(int argc, char **argv) {
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 @@ int main(int argc, char **argv) {
// 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;
......
......@@ -401,25 +401,6 @@ set(BINARY_NAME "fizz_bar")
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>{
......
......@@ -7,6 +7,7 @@ import 'package:file_testing/file_testing.dart';
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 @@ void main() {
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) {
......
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