From 0a3ef762f554325ab914445a7e1680d1231c5641 Mon Sep 17 00:00:00 2001 From: Hidenori Matsubayashi Date: Tue, 27 Jul 2021 14:17:50 +0900 Subject: [PATCH] Cross-compile from x64 to arm64 (#20) --- lib/commands/build.dart | 34 ++++++++++++++++++++++++++----- lib/elinux_build_target.dart | 21 ++++++++++++++++++- lib/elinux_builder.dart | 4 ++++ lib/elinux_device.dart | 4 ++++ lib/elinux_doctor.dart | 3 ++- templates/app/CMakeLists.txt.tmpl | 6 ++++++ 6 files changed, 65 insertions(+), 7 deletions(-) diff --git a/lib/commands/build.dart b/lib/commands/build.dart index 2d1a5b7..4571daf 100644 --- a/lib/commands/build.dart +++ b/lib/commands/build.dart @@ -44,6 +44,21 @@ class BuildPackageCommand extends BuildSubCommand with ELinuxExtension { allowed: ['wayland', 'gbm', 'eglstream', 'x11'], help: 'Target backend type that the app will run on devices.', ); + argParser.addOption( + 'target-sysroot', + defaultsTo: '/', + help: 'The root filesystem path of target platform for which ' + 'the app is compiled. This option is valid only ' + 'if the current host and target architectures are different.', + ); + argParser.addOption( + 'system-include-directories', + defaultsTo: null, + help: + 'The additional system include paths to cross-compile for target platform. ' + 'This option is valid only ' + 'if the current host and target architectures are different.', + ); } @override @@ -70,12 +85,21 @@ class BuildPackageCommand extends BuildSubCommand with ELinuxExtension { /// See: [BuildApkCommand.runCommand] in `build_apk.dart` @override Future runCommand() async { + // Not supported cross-building for x64 on arm64. + final String targetArch = stringArg('target-arch'); + final String hostArch = _getCurrentHostPlatformArchName(); + if (hostArch != targetArch && hostArch == 'arm64') { + globals.logger + .printError('Not supported cross-building for x64 on arm64.'); + return FlutterCommandResult.fail(); + } + final BuildInfo buildInfo = await getBuildInfo(); - final ELinuxBuildInfo eLinuxBuildInfo = ELinuxBuildInfo( - buildInfo, - targetArch: stringArg('target-arch'), - targetBackendType: stringArg('target-backend-type'), - ); + final ELinuxBuildInfo eLinuxBuildInfo = ELinuxBuildInfo(buildInfo, + targetArch: targetArch, + targetBackendType: stringArg('target-backend-type'), + targetSysroot: stringArg('target-sysroot'), + systemIncludeDirectories: stringArg('system-include-directories')); validateBuild(eLinuxBuildInfo); displayNullSafetyMode(buildInfo); diff --git a/lib/elinux_build_target.dart b/lib/elinux_build_target.dart index 496d060..202f797 100644 --- a/lib/elinux_build_target.dart +++ b/lib/elinux_build_target.dart @@ -12,6 +12,7 @@ import 'package:file/file.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/process.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; @@ -68,7 +69,7 @@ abstract class ELinuxAssetBundle extends Target { getBuildModeForName(environment.defines[kBuildMode]); final Directory outputDirectory = environment.outputDir .childDirectory('flutter_assets') - ..createSync(recursive: true); + ..createSync(recursive: true); // Only copy the prebuilt runtimes and kernel blob in debug mode. if (buildMode == BuildMode.debug) { @@ -348,11 +349,24 @@ class NativeBundle { // Run the native build. final String cmakeBuildType = buildMode.isPrecompiled ? 'Release' : 'Debug'; + final String targetArch = buildInfo.targetArch; + final String hostArch = _getCurrentHostPlatformArchName(); + final String targetSysroot = buildInfo.targetSysroot; + final String systemIncludeDirectories = buildInfo.systemIncludeDirectories; + final bool needCrossBuild = targetArch != hostArch; + final bool needCrossBuildOptionsForArm64 = targetArch == 'arm64'; RunResult result = await _processUtils.run( [ 'cmake', '-DCMAKE_BUILD_TYPE=$cmakeBuildType', '-DFLUTTER_TARGET_BACKEND_TYPE=${buildInfo.targetBackendType}', + if (needCrossBuild) '-DFLUTTER_TARGET_PLATFORM_SYSROOT=$targetSysroot', + if (needCrossBuild && systemIncludeDirectories != null) + '-DFLUTTER_SYSTEM_INCLUDE_DIRECTORIES=$systemIncludeDirectories', + if (needCrossBuildOptionsForArm64) + '-DCMAKE_C_COMPILER_TARGET=aarch64-linux-gnu', + if (needCrossBuildOptionsForArm64) + '-DCMAKE_CXX_COMPILER_TARGET=aarch64-linux-gnu', eLinuxDir.path, ], workingDirectory: outputDir.path, @@ -389,6 +403,11 @@ class NativeBundle { } } +String _getCurrentHostPlatformArchName() { + final HostPlatform hostPlatform = getCurrentHostPlatform(); + return getNameForHostPlatformArch(hostPlatform); +} + /// Converts [targetArch] to an arch name that corresponds to the `BUILD_ARCH` /// value used by the eLinux native builder. String getELinuxBuildArch(String targetArch) { diff --git a/lib/elinux_builder.dart b/lib/elinux_builder.dart index 613d6d2..5a203c8 100644 --- a/lib/elinux_builder.dart +++ b/lib/elinux_builder.dart @@ -34,12 +34,16 @@ class ELinuxBuildInfo { this.buildInfo, { @required this.targetArch, @required this.targetBackendType, + @required this.targetSysroot, + @required this.systemIncludeDirectories, }) : assert(targetArch != null), assert(targetBackendType != null); final BuildInfo buildInfo; final String targetArch; final String targetBackendType; + final String targetSysroot; + final String systemIncludeDirectories; } /// See: diff --git a/lib/elinux_device.dart b/lib/elinux_device.dart index 426d07f..b8f95c7 100644 --- a/lib/elinux_device.dart +++ b/lib/elinux_device.dart @@ -224,10 +224,14 @@ class ELinuxDevice extends Device { BuildInfo buildInfo, }) async { final FlutterProject project = FlutterProject.current(); + // TODO(hidenori): change the fixed values (|targetSysroot| and |systemIncludeDirectories|) + // to the values from user-specified custom-devices feilds. final ELinuxBuildInfo eLinuxBuildInfo = ELinuxBuildInfo( buildInfo, targetArch: _targetArch, targetBackendType: _backendType, + targetSysroot: '/', + systemIncludeDirectories: null, ); await ELinuxBuilder.buildBundle( project: project, diff --git a/lib/elinux_doctor.dart b/lib/elinux_doctor.dart index afaccef..0865fa4 100644 --- a/lib/elinux_doctor.dart +++ b/lib/elinux_doctor.dart @@ -219,7 +219,8 @@ class ELinuxWorkflow extends Workflow { @override bool get appliesToHostPlatform => - _operatingSystemUtils.hostPlatform != HostPlatform.linux_arm64; + (_operatingSystemUtils.hostPlatform == HostPlatform.linux_x64) || + (_operatingSystemUtils.hostPlatform == HostPlatform.linux_arm64); @override bool get canLaunchDevices => true; diff --git a/templates/app/CMakeLists.txt.tmpl b/templates/app/CMakeLists.txt.tmpl index 8fc02d7..1e2618d 100644 --- a/templates/app/CMakeLists.txt.tmpl +++ b/templates/app/CMakeLists.txt.tmpl @@ -15,6 +15,12 @@ if(FLUTTER_TARGET_PLATFORM_SYSROOT) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + + # Basically we use this include when we got the following error: + # fatal error: 'bits/c++config.h' file not found + if(FLUTTER_TARGET_PLATFORM_SYSROOT) + include_directories(SYSTEM ${FLUTTER_SYSTEM_INCLUDE_DIRECTORIES}) + endif() endif() # Configure build options.