diff --git a/AUTHORS b/AUTHORS index 4790aac879..f43f4d12e4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -73,3 +73,4 @@ Anurag Roy Andrey Kabylin vimerzhao Pedro Massango +Hidenori Matsubayashi diff --git a/bin/internal/update_dart_sdk.sh b/bin/internal/update_dart_sdk.sh index fba7a23459..b513418be5 100755 --- a/bin/internal/update_dart_sdk.sh +++ b/bin/internal/update_dart_sdk.sh @@ -58,13 +58,24 @@ if [ ! -f "$ENGINE_STAMP" ] || [ "$ENGINE_VERSION" != `cat "$ENGINE_STAMP"` ]; t } >&2 echo "Downloading Dart SDK from Flutter engine $ENGINE_VERSION..." + # On x64 stdout is "uname -m: x86_64" + # On arm64 stdout is "uname -m: aarch64, arm64_v8a" + case "$(uname -m)" in + x86_64) + ARCH="x64" + ;; + *) + ARCH="arm64" + ;; + esac + case "$(uname -s)" in Darwin) DART_ZIP_NAME="dart-sdk-darwin-x64.zip" IS_USER_EXECUTABLE="-perm +100" ;; Linux) - DART_ZIP_NAME="dart-sdk-linux-x64.zip" + DART_ZIP_NAME="dart-sdk-linux-${ARCH}.zip" IS_USER_EXECUTABLE="-perm /u+x" ;; MINGW*) diff --git a/packages/flutter_tools/bin/fuchsia_attach.dart b/packages/flutter_tools/bin/fuchsia_attach.dart index 6ee4340b19..0d08cb0872 100644 --- a/packages/flutter_tools/bin/fuchsia_attach.dart +++ b/packages/flutter_tools/bin/fuchsia_attach.dart @@ -113,6 +113,7 @@ Future main(List args) async { fileSystem: globals.fs, cache: globals.cache, platform: globals.platform, + operatingSystemUtils: globals.os, ), frontendServer: frontendServer, engineDartBinary: dartSdk, diff --git a/packages/flutter_tools/bin/tool_backend.dart b/packages/flutter_tools/bin/tool_backend.dart index ee1e61cd53..0ba4cb6d14 100644 --- a/packages/flutter_tools/bin/tool_backend.dart +++ b/packages/flutter_tools/bin/tool_backend.dart @@ -54,7 +54,7 @@ or else 'flutter' ]); - final String bundlePlatform = targetPlatform == 'windows-x64' ? 'windows' : 'linux'; + final String bundlePlatform = targetPlatform == 'windows-x64' ? 'windows' : targetPlatform; final String target = '${buildMode}_bundle_${bundlePlatform}_assets'; final Process assembleProcess = await Process.start( flutterExecutable, diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart index cc3a55630a..726ff0a597 100644 --- a/packages/flutter_tools/lib/src/android/android_workflow.dart +++ b/packages/flutter_tools/lib/src/android/android_workflow.dart @@ -16,6 +16,7 @@ import '../base/os.dart'; import '../base/platform.dart'; import '../base/user_messages.dart' hide userMessages; import '../base/version.dart'; +import '../build_info.dart'; import '../convert.dart'; import '../doctor.dart'; import '../features.dart'; @@ -45,14 +46,19 @@ class AndroidWorkflow implements Workflow { AndroidWorkflow({ @required AndroidSdk androidSdk, @required FeatureFlags featureFlags, + @required OperatingSystemUtils operatingSystemUtils, }) : _androidSdk = androidSdk, - _featureFlags = featureFlags; + _featureFlags = featureFlags, + _operatingSystemUtils = operatingSystemUtils; final AndroidSdk _androidSdk; final FeatureFlags _featureFlags; + final OperatingSystemUtils _operatingSystemUtils; @override - bool get appliesToHostPlatform => _featureFlags.isAndroidEnabled; + bool get appliesToHostPlatform => _featureFlags.isAndroidEnabled + // Android Studio is not currently supported on Linux Arm64 Hosts. + && _operatingSystemUtils.hostPlatform != HostPlatform.linux_arm64; @override bool get canListDevices => _androidSdk != null diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index 740674ab2b..c028e25db9 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -101,6 +101,7 @@ class ApplicationPackageFactory { } return WebApplicationPackage(FlutterProject.current()); case TargetPlatform.linux_x64: + case TargetPlatform.linux_arm64: return applicationBinary == null ? LinuxApp.fromLinuxProject(FlutterProject.current().linux) : LinuxApp.fromPrebuiltApp(applicationBinary); diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index fc2e1808db..7dbc0e3e46 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -9,6 +9,7 @@ import 'package:process/process.dart'; import 'base/common.dart'; import 'base/file_system.dart'; +import 'base/os.dart'; import 'base/platform.dart'; import 'base/utils.dart'; import 'build_info.dart'; @@ -219,6 +220,7 @@ abstract class Artifacts { fileSystem: globals.fs, processManager: globals.processManager, platform: globals.platform, + operatingSystemUtils: globals.os, ); } @@ -245,13 +247,16 @@ class CachedArtifacts implements Artifacts { @required FileSystem fileSystem, @required Platform platform, @required Cache cache, + @required OperatingSystemUtils operatingSystemUtils, }) : _fileSystem = fileSystem, _platform = platform, - _cache = cache; + _cache = cache, + _operatingSystemUtils = operatingSystemUtils; final FileSystem _fileSystem; final Platform _platform; final Cache _cache; + final OperatingSystemUtils _operatingSystemUtils; @override String getArtifactPath( @@ -270,6 +275,7 @@ class CachedArtifacts implements Artifacts { return _getIosArtifactPath(artifact, platform, mode, environmentType); case TargetPlatform.darwin_x64: case TargetPlatform.linux_x64: + case TargetPlatform.linux_arm64: case TargetPlatform.windows_x64: return _getDesktopArtifactPath(artifact, platform, mode); case TargetPlatform.fuchsia_arm64: @@ -278,7 +284,7 @@ class CachedArtifacts implements Artifacts { case TargetPlatform.tester: case TargetPlatform.web_javascript: default: // could be null, but that can't be specified as a case. - return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform(_platform), mode); + return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform(_platform, _operatingSystemUtils), mode); } } @@ -294,7 +300,7 @@ class CachedArtifacts implements Artifacts { final String engineDir = _getEngineArtifactsPath(platform, mode); return _fileSystem.path.join(engineDir, _artifactToFileName(artifact)); } - return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform(_platform), mode); + return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform(_platform, _operatingSystemUtils), mode); } String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) { @@ -480,6 +486,7 @@ class CachedArtifacts implements Artifacts { final String platformName = getNameForTargetPlatform(platform); switch (platform) { case TargetPlatform.linux_x64: + case TargetPlatform.linux_arm64: case TargetPlatform.darwin_x64: case TargetPlatform.windows_x64: // TODO(jonahwilliams): remove once debug desktop artifacts are uploaded @@ -516,12 +523,13 @@ class CachedArtifacts implements Artifacts { bool get isLocalEngine => false; } -TargetPlatform _currentHostPlatform(Platform platform) { +TargetPlatform _currentHostPlatform(Platform platform, OperatingSystemUtils operatingSystemUtils) { if (platform.isMacOS) { return TargetPlatform.darwin_x64; } if (platform.isLinux) { - return TargetPlatform.linux_x64; + return operatingSystemUtils.hostPlatform == HostPlatform.linux_x64 ? + TargetPlatform.linux_x64 : TargetPlatform.linux_arm64; } if (platform.isWindows) { return TargetPlatform.windows_x64; @@ -529,19 +537,6 @@ TargetPlatform _currentHostPlatform(Platform platform) { throw UnimplementedError('Host OS not supported.'); } -HostPlatform _currentHostPlatformAsHost(Platform platform) { - if (platform.isMacOS) { - return HostPlatform.darwin_x64; - } - if (platform.isLinux) { - return HostPlatform.linux_x64; - } - if (platform.isWindows) { - return HostPlatform.windows_x64; - } - throw UnimplementedError('Host OS not supported.'); -} - String _getIosEngineArtifactPath(String engineDirectory, EnvironmentType environmentType, FileSystem fileSystem) { final Directory xcframeworkDirectory = fileSystem @@ -583,10 +578,12 @@ class LocalEngineArtifacts implements Artifacts { @required Cache cache, @required ProcessManager processManager, @required Platform platform, + @required OperatingSystemUtils operatingSystemUtils, }) : _fileSystem = fileSystem, _cache = cache, _processManager = processManager, - _platform = platform; + _platform = platform, + _operatingSystemUtils = operatingSystemUtils; final String engineOutPath; // TODO(goderbauer): This should be private. final String _hostEngineOutPath; @@ -594,6 +591,7 @@ class LocalEngineArtifacts implements Artifacts { final Cache _cache; final ProcessManager _processManager; final Platform _platform; + final OperatingSystemUtils _operatingSystemUtils; @override String getArtifactPath( @@ -602,7 +600,7 @@ class LocalEngineArtifacts implements Artifacts { BuildMode mode, EnvironmentType environmentType, }) { - platform ??= _currentHostPlatform(_platform); + platform ??= _currentHostPlatform(_platform, _operatingSystemUtils); final bool isDirectoryArtifact = artifact == Artifact.flutterWebSdk || artifact == Artifact.flutterPatchedSdkPath; final String artifactFileName = isDirectoryArtifact ? null : _artifactToFileName(artifact, platform, mode); switch (artifact) { @@ -745,12 +743,11 @@ class LocalEngineArtifacts implements Artifacts { } String _flutterTesterPath(TargetPlatform platform) { - final HostPlatform hostPlatform = _currentHostPlatformAsHost(_platform); - if (hostPlatform == HostPlatform.linux_x64) { + if (_platform.isLinux) { return _fileSystem.path.join(engineOutPath, _artifactToFileName(Artifact.flutterTester)); - } else if (hostPlatform == HostPlatform.darwin_x64) { + } else if (_platform.isMacOS) { return _fileSystem.path.join(engineOutPath, 'flutter_tester'); - } else if (hostPlatform == HostPlatform.windows_x64) { + } else if (_platform.isWindows) { return _fileSystem.path.join(engineOutPath, 'flutter_tester.exe'); } throw Exception('Unsupported platform $platform.'); diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index a1fe82f8b8..74e40a37a9 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart @@ -313,6 +313,7 @@ class AOTSnapshotter { TargetPlatform.ios, TargetPlatform.darwin_x64, TargetPlatform.linux_x64, + TargetPlatform.linux_arm64, TargetPlatform.windows_x64, ].contains(platform); } diff --git a/packages/flutter_tools/lib/src/base/os.dart b/packages/flutter_tools/lib/src/base/os.dart index 6dd472973c..b64965602a 100644 --- a/packages/flutter_tools/lib/src/base/os.dart +++ b/packages/flutter_tools/lib/src/base/os.dart @@ -261,8 +261,30 @@ class _PosixUtils extends OperatingSystemUtils { @override String get pathVarSeparator => ':'; + HostPlatform _hostPlatform; + @override - HostPlatform hostPlatform = HostPlatform.linux_x64; + HostPlatform get hostPlatform { + if (_hostPlatform == null) { + final RunResult hostPlatformCheck = + _processUtils.runSync(['uname', '-m']); + // On x64 stdout is "uname -m: x86_64" + // On arm64 stdout is "uname -m: aarch64, arm64_v8a" + if (hostPlatformCheck.exitCode != 0) { + _logger.printError( + 'Error trying to run uname -m' + '\nstdout: ${hostPlatformCheck.stdout}' + '\nstderr: ${hostPlatformCheck.stderr}', + ); + _hostPlatform = HostPlatform.linux_x64; + } else if (hostPlatformCheck.stdout.trim().endsWith('x86_64')) { + _hostPlatform = HostPlatform.linux_x64; + } else { + _hostPlatform = HostPlatform.linux_arm64; + } + } + return _hostPlatform; + } } class _MacOSUtils extends _PosixUtils { @@ -297,8 +319,6 @@ class _MacOSUtils extends _PosixUtils { return _name; } - HostPlatform _hostPlatform; - // On ARM returns arm64, even when this process is running in Rosetta. @override HostPlatform get hostPlatform { diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index c0fa736713..0fcf78244c 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart @@ -456,6 +456,7 @@ enum HostPlatform { darwin_x64, darwin_arm, linux_x64, + linux_arm64, windows_x64, } @@ -467,6 +468,8 @@ String getNameForHostPlatform(HostPlatform platform) { return 'darwin-arm'; case HostPlatform.linux_x64: return 'linux-x64'; + case HostPlatform.linux_arm64: + return 'linux-arm64'; case HostPlatform.windows_x64: return 'windows-x64'; } @@ -480,6 +483,7 @@ enum TargetPlatform { // darwin_arm64 not yet supported, macOS desktop targets run in Rosetta as x86. darwin_x64, linux_x64, + linux_arm64, windows_x64, fuchsia_arm64, fuchsia_x64, @@ -574,6 +578,8 @@ String getNameForTargetPlatform(TargetPlatform platform, {DarwinArch darwinArch} return 'darwin-x64'; case TargetPlatform.linux_x64: return 'linux-x64'; + case TargetPlatform.linux_arm64: + return 'linux-arm64'; case TargetPlatform.windows_x64: return 'windows-x64'; case TargetPlatform.fuchsia_arm64: @@ -613,6 +619,8 @@ TargetPlatform getTargetPlatformForName(String platform) { return TargetPlatform.darwin_x64; case 'linux-x64': return TargetPlatform.linux_x64; + case 'linux-arm64': + return TargetPlatform.linux_arm64; case 'windows-x64': return TargetPlatform.windows_x64; case 'web-javascript': @@ -683,7 +691,8 @@ HostPlatform getCurrentHostPlatform() { return HostPlatform.darwin_x64; } if (globals.platform.isLinux) { - return HostPlatform.linux_x64; + // support x64 and arm64 architecture. + return globals.os.hostPlatform; } if (globals.platform.isWindows) { return HostPlatform.windows_x64; @@ -747,8 +756,12 @@ String getWebBuildDirectory() { } /// Returns the Linux build output directory. -String getLinuxBuildDirectory() { - return globals.fs.path.join(getBuildDirectory(), 'linux'); +String getLinuxBuildDirectory([TargetPlatform targetPlatform]) { + final String arch = (targetPlatform == null) ? + _getCurrentHostPlatformArchName() : + getNameForTargetPlatformArch(targetPlatform); + final String subDirs = 'linux/' + arch; + return globals.fs.path.join(getBuildDirectory(), subDirs); } /// Returns the Windows build output directory. @@ -813,3 +826,40 @@ enum NullSafetyMode { /// The null safety mode was not detected. Only supported for 'flutter test'. autodetect, } + +String _getCurrentHostPlatformArchName() { + final HostPlatform hostPlatform = getCurrentHostPlatform(); + return getNameForHostPlatformArch(hostPlatform); +} + +String getNameForTargetPlatformArch(TargetPlatform platform) { + switch (platform) { + case TargetPlatform.linux_x64: + case TargetPlatform.darwin_x64: + case TargetPlatform.windows_x64: + return 'x64'; + case TargetPlatform.linux_arm64: + return 'arm64'; + default: + break; + } + assert(false); + return null; +} + +String getNameForHostPlatformArch(HostPlatform platform) { + switch (platform) { + case HostPlatform.darwin_x64: + return 'x64'; + case HostPlatform.darwin_arm: + return 'arm'; + case HostPlatform.linux_x64: + return 'x64'; + case HostPlatform.linux_arm64: + return 'arm64'; + case HostPlatform.windows_x64: + return 'x64'; + } + assert(false); + return null; +} 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 f28fe24aba..e9155d76ec 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/linux.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/linux.dart @@ -26,7 +26,9 @@ const String _kLinuxDepfile = 'linux_engine_sources.d'; /// Copies the Linux desktop embedding files to the copy directory. class UnpackLinux extends Target { - const UnpackLinux(); + const UnpackLinux(this.targetPlatform); + + final TargetPlatform targetPlatform; @override String get name => 'unpack_linux'; @@ -52,13 +54,13 @@ class UnpackLinux extends Target { .getArtifactPath( Artifact.linuxDesktopPath, mode: buildMode, - platform: TargetPlatform.linux_x64, + platform: targetPlatform, ); final String headersPath = environment.artifacts .getArtifactPath( Artifact.linuxHeaders, mode: buildMode, - platform: TargetPlatform.linux_x64, + platform: targetPlatform, ); final Directory outputDirectory = environment.fileSystem.directory( environment.fileSystem.path.join( @@ -75,7 +77,7 @@ class UnpackLinux extends Target { clientSourcePaths: [headersPath], icuDataPath: environment.artifacts.getArtifactPath( Artifact.icuData, - platform: TargetPlatform.linux_x64, + platform: targetPlatform, ) ); final DepfileService depfileService = DepfileService( @@ -91,12 +93,14 @@ class UnpackLinux extends Target { /// Creates a bundle for the Linux desktop target. abstract class BundleLinuxAssets extends Target { - const BundleLinuxAssets(); + const BundleLinuxAssets(this.targetPlatform); + + final TargetPlatform targetPlatform; @override - List get dependencies => const [ - KernelSnapshot(), - UnpackLinux(), + List get dependencies => [ + const KernelSnapshot(), + UnpackLinux(targetPlatform), ]; @override @@ -132,7 +136,7 @@ abstract class BundleLinuxAssets extends Target { final Depfile depfile = await copyAssets( environment, outputDirectory, - targetPlatform: TargetPlatform.linux_x64, + targetPlatform: targetPlatform, additionalContent: { 'version.json': DevFSStringContent(versionInfo), } @@ -186,10 +190,10 @@ class LinuxAotBundle extends Target { } class DebugBundleLinuxAssets extends BundleLinuxAssets { - const DebugBundleLinuxAssets(); + const DebugBundleLinuxAssets(TargetPlatform targetPlatform) : super(targetPlatform); @override - String get name => 'debug_bundle_linux_assets'; + String get name => 'debug_bundle_${getNameForTargetPlatform(targetPlatform)}_assets'; @override List get inputs => [ @@ -203,10 +207,10 @@ class DebugBundleLinuxAssets extends BundleLinuxAssets { } class ProfileBundleLinuxAssets extends BundleLinuxAssets { - const ProfileBundleLinuxAssets(); + const ProfileBundleLinuxAssets(TargetPlatform targetPlatform) : super(targetPlatform); @override - String get name => 'profile_bundle_linux_assets'; + String get name => 'profile_bundle_${getNameForTargetPlatform(targetPlatform)}_assets'; @override List get outputs => const []; @@ -214,15 +218,15 @@ class ProfileBundleLinuxAssets extends BundleLinuxAssets { @override List get dependencies => [ ...super.dependencies, - const LinuxAotBundle(AotElfProfile(TargetPlatform.linux_x64)), + LinuxAotBundle(AotElfProfile(targetPlatform)), ]; } class ReleaseBundleLinuxAssets extends BundleLinuxAssets { - const ReleaseBundleLinuxAssets(); + const ReleaseBundleLinuxAssets(TargetPlatform targetPlatform) : super(targetPlatform); @override - String get name => 'release_bundle_linux_assets'; + String get name => 'release_bundle_${getNameForTargetPlatform(targetPlatform)}_assets'; @override List get outputs => const []; @@ -230,6 +234,6 @@ class ReleaseBundleLinuxAssets extends BundleLinuxAssets { @override List get dependencies => [ ...super.dependencies, - const LinuxAotBundle(AotElfRelease(TargetPlatform.linux_x64)), + LinuxAotBundle(AotElfRelease(targetPlatform)), ]; } diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index fe751d7eb6..2e730952c1 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -23,6 +23,7 @@ import 'base/os.dart' show OperatingSystemUtils; import 'base/platform.dart'; import 'base/process.dart'; import 'base/user_messages.dart'; +import 'build_info.dart'; import 'convert.dart'; import 'dart/package_map.dart'; import 'dart/pub.dart'; @@ -430,6 +431,10 @@ class Cache { } } + String getHostPlatformArchName() { + return getNameForHostPlatformArch(_osUtils.hostPlatform); + } + /// Return a directory in the cache dir. For `pkg`, this will return `bin/cache/pkg`. Directory getCacheDir(String name) { final Directory dir = _fileSystem.directory(_fileSystem.path.join(getRoot().path, name)); @@ -996,12 +1001,14 @@ class FlutterSdk extends EngineCachedArtifact { @override List> getBinaryDirs() { + // Currently only Linux supports both arm64 and x64. + final String arch = cache.getHostPlatformArchName(); return >[ ['common', 'flutter_patched_sdk.zip'], ['common', 'flutter_patched_sdk_product.zip'], if (cache.includeAllPlatforms) ...>[ ['windows-x64', 'windows-x64/artifacts.zip'], - ['linux-x64', 'linux-x64/artifacts.zip'], + ['linux-$arch', 'linux-$arch/artifacts.zip'], ['darwin-x64', 'darwin-x64/artifacts.zip'], ] else if (_platform.isWindows) @@ -1009,7 +1016,7 @@ class FlutterSdk extends EngineCachedArtifact { else if (_platform.isMacOS) ['darwin-x64', 'darwin-x64/artifacts.zip'] else if (_platform.isLinux) - ['linux-x64', 'linux-x64/artifacts.zip'], + ['linux-$arch', 'linux-$arch/artifacts.zip'], ]; } @@ -1091,7 +1098,12 @@ class LinuxEngineArtifacts extends EngineCachedArtifact { @override List> getBinaryDirs() { if (_platform.isLinux || ignorePlatformFiltering) { - return _linuxDesktopBinaryDirs; + final String arch = cache.getHostPlatformArchName(); + return >[ + ['linux-$arch', 'linux-$arch/linux-$arch-flutter-gtk.zip'], + ['linux-$arch-profile', 'linux-$arch-profile/linux-$arch-flutter-gtk.zip'], + ['linux-$arch-release', 'linux-$arch-release/linux-$arch-flutter-gtk.zip'], + ]; } return const >[]; } @@ -1480,9 +1492,11 @@ class FontSubsetArtifacts extends EngineCachedArtifact { @override List> getBinaryDirs() { - const Map> artifacts = > { + // Currently only Linux supports both arm64 and x64. + final String arch = cache.getHostPlatformArchName(); + final Map> artifacts = > { 'macos': ['darwin-x64', 'darwin-x64/$artifactName.zip'], - 'linux': ['linux-x64', 'linux-x64/$artifactName.zip'], + 'linux': ['linux-$arch', 'linux-$arch/$artifactName.zip'], 'windows': ['windows-x64', 'windows-x64/$artifactName.zip'], }; if (cache.includeAllPlatforms) { @@ -1613,12 +1627,6 @@ const List> _windowsDesktopBinaryDirs = >[ ['windows-x64-release', 'windows-x64-release/windows-x64-flutter.zip'], ]; -const List> _linuxDesktopBinaryDirs = >[ - ['linux-x64', 'linux-x64/linux-x64-flutter-gtk.zip'], - ['linux-x64-profile', 'linux-x64-profile/linux-x64-flutter-gtk.zip'], - ['linux-x64-release', 'linux-x64-release/linux-x64-flutter-gtk.zip'], -]; - const List> _macOSDesktopBinaryDirs = >[ ['darwin-x64', 'darwin-x64/FlutterMacOS.framework.zip'], ['darwin-x64-profile', 'darwin-x64-profile/FlutterMacOS.framework.zip'], diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart index bd4e24f72a..26412bf6f4 100644 --- a/packages/flutter_tools/lib/src/commands/assemble.dart +++ b/packages/flutter_tools/lib/src/commands/assemble.dart @@ -41,9 +41,12 @@ const List _kDefaultTargets = [ ProfileMacOSBundleFlutterAssets(), ReleaseMacOSBundleFlutterAssets(), // Linux targets - DebugBundleLinuxAssets(), - ProfileBundleLinuxAssets(), - ReleaseBundleLinuxAssets(), + DebugBundleLinuxAssets(TargetPlatform.linux_x64), + DebugBundleLinuxAssets(TargetPlatform.linux_arm64), + ProfileBundleLinuxAssets(TargetPlatform.linux_x64), + ProfileBundleLinuxAssets(TargetPlatform.linux_arm64), + ReleaseBundleLinuxAssets(TargetPlatform.linux_x64), + ReleaseBundleLinuxAssets(TargetPlatform.linux_arm64), // Web targets WebServiceWorker(), ReleaseAndroidApplication(), diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart index faa1da9917..cbe2393bcc 100644 --- a/packages/flutter_tools/lib/src/commands/build.dart +++ b/packages/flutter_tools/lib/src/commands/build.dart @@ -35,7 +35,10 @@ class BuildCommand extends FlutterCommand { addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp)); addSubcommand(BuildWebCommand(verboseHelp: verboseHelp)); addSubcommand(BuildMacosCommand(verboseHelp: verboseHelp)); - addSubcommand(BuildLinuxCommand(verboseHelp: verboseHelp)); + addSubcommand(BuildLinuxCommand( + operatingSystemUtils: globals.os, + verboseHelp: verboseHelp + )); addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp)); addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp)); } diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart index 86e51c63a1..347477ca06 100644 --- a/packages/flutter_tools/lib/src/commands/build_bundle.dart +++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart @@ -34,6 +34,7 @@ class BuildBundleCommand extends BuildSubCommand { 'ios', 'darwin-x64', 'linux-x64', + 'linux-arm64', 'windows-x64', ], ) diff --git a/packages/flutter_tools/lib/src/commands/build_linux.dart b/packages/flutter_tools/lib/src/commands/build_linux.dart index 693e7988f1..18d67893ff 100644 --- a/packages/flutter_tools/lib/src/commands/build_linux.dart +++ b/packages/flutter_tools/lib/src/commands/build_linux.dart @@ -4,8 +4,11 @@ // @dart = 2.8 +import 'package:meta/meta.dart'; + import '../base/analyze_size.dart'; import '../base/common.dart'; +import '../base/os.dart'; import '../build_info.dart'; import '../cache.dart'; import '../features.dart'; @@ -17,10 +20,29 @@ import 'build.dart'; /// A command to build a linux desktop target through a build shell script. class BuildLinuxCommand extends BuildSubCommand { - BuildLinuxCommand({ bool verboseHelp = false }) { + BuildLinuxCommand({ + @required OperatingSystemUtils operatingSystemUtils, + bool verboseHelp = false, + }) : _operatingSystemUtils = operatingSystemUtils { addCommonDesktopBuildOptions(verboseHelp: verboseHelp); + final String defaultTargetPlatform = + (_operatingSystemUtils.hostPlatform == HostPlatform.linux_arm64) ? + 'linux-arm64' : 'linux-x64'; + argParser.addOption('target-platform', + defaultsTo: defaultTargetPlatform, + allowed: ['linux-arm64', 'linux-x64'], + help: 'The target platform for which the app is compiled.', + ); + 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.', + ); } + final OperatingSystemUtils _operatingSystemUtils; + @override final String name = 'linux'; @@ -39,12 +61,29 @@ class BuildLinuxCommand extends BuildSubCommand { Future runCommand() async { final BuildInfo buildInfo = await getBuildInfo(); final FlutterProject flutterProject = FlutterProject.current(); + final TargetPlatform targetPlatform = + getTargetPlatformForName(stringArg('target-platform')); + final bool needCrossBuild = + getNameForHostPlatformArch(_operatingSystemUtils.hostPlatform) + != getNameForTargetPlatformArch(targetPlatform); + if (!featureFlags.isLinuxEnabled) { throwToolExit('"build linux" is not currently supported.'); } if (!globals.platform.isLinux) { throwToolExit('"build linux" only supported on Linux hosts.'); } + // Cross-building for x64 targets on arm64 hosts is not supported. + if (_operatingSystemUtils.hostPlatform != HostPlatform.linux_x64 && + targetPlatform != TargetPlatform.linux_arm64) { + throwToolExit('"cross-building" only supported on Linux x64 hosts.'); + } + // TODO(fujino): https://github.com/flutter/flutter/issues/74929 + if (_operatingSystemUtils.hostPlatform == HostPlatform.linux_x64 && + targetPlatform == TargetPlatform.linux_arm64) { + throwToolExit( + 'Cross-build from Linux x64 host to Linux arm64 target is not currently supported.'); + } displayNullSafetyMode(buildInfo); await buildLinux( flutterProject.linux, @@ -55,6 +94,9 @@ class BuildLinuxCommand extends BuildSubCommand { logger: globals.logger, flutterUsage: globals.flutterUsage, ), + needCrossBuild: needCrossBuild, + targetPlatform: targetPlatform, + targetSysroot: stringArg('target-sysroot'), ); return FlutterCommandResult.success(); } diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart index 2625c16df1..1d69fcfbc6 100644 --- a/packages/flutter_tools/lib/src/context_runner.dart +++ b/packages/flutter_tools/lib/src/context_runner.dart @@ -105,6 +105,7 @@ Future runInContext( AndroidWorkflow: () => AndroidWorkflow( androidSdk: globals.androidSdk, featureFlags: featureFlags, + operatingSystemUtils: globals.os, ), ApplicationPackageFactory: () => ApplicationPackageFactory( userMessages: globals.userMessages, @@ -117,6 +118,7 @@ Future runInContext( fileSystem: globals.fs, cache: globals.cache, platform: globals.platform, + operatingSystemUtils: globals.os, ), AssetBundleFactory: () { return AssetBundleFactory.defaultInstance( diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index d6bbc427c9..0931bd52d1 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -394,6 +394,7 @@ class FlutterDeviceManager extends DeviceManager { config: config, logger: logger, artifacts: artifacts, + operatingSystemUtils: operatingSystemUtils, ), MacOSDevices( processManager: processManager, diff --git a/packages/flutter_tools/lib/src/linux/build_linux.dart b/packages/flutter_tools/lib/src/linux/build_linux.dart index ca6518cd69..92bd822fef 100644 --- a/packages/flutter_tools/lib/src/linux/build_linux.dart +++ b/packages/flutter_tools/lib/src/linux/build_linux.dart @@ -33,6 +33,9 @@ Future buildLinux( BuildInfo buildInfo, { String target = 'lib/main.dart', SizeAnalyzer sizeAnalyzer, + bool needCrossBuild = false, + TargetPlatform targetPlatform = TargetPlatform.linux_x64, + String targetSysroot = '/', }) async { if (!linuxProject.cmakeFile.existsSync()) { throwToolExit('No Linux desktop project configured. See ' @@ -68,14 +71,16 @@ Future buildLinux( ); try { final String buildModeName = getNameForBuildMode(buildInfo.mode ?? BuildMode.release); - final Directory buildDirectory = globals.fs.directory(getLinuxBuildDirectory()).childDirectory(buildModeName); - await _runCmake(buildModeName, linuxProject.cmakeFile.parent, buildDirectory); + final Directory buildDirectory = + globals.fs.directory(getLinuxBuildDirectory(targetPlatform)).childDirectory(buildModeName); + await _runCmake(buildModeName, linuxProject.cmakeFile.parent, buildDirectory, + needCrossBuild, targetPlatform, targetSysroot); await _runBuild(buildDirectory); } finally { status.cancel(); } if (buildInfo.codeSizeDirectory != null && sizeAnalyzer != null) { - final String arch = getNameForTargetPlatform(TargetPlatform.linux_x64); + final String arch = getNameForTargetPlatform(targetPlatform); final File codeSizeFile = globals.fs.directory(buildInfo.codeSizeDirectory) .childFile('snapshot.$arch.json'); final File precompilerTrace = globals.fs.directory(buildInfo.codeSizeDirectory) @@ -84,7 +89,7 @@ Future buildLinux( aotSnapshot: codeSizeFile, // This analysis is only supported for release builds. outputDirectory: globals.fs.directory( - globals.fs.path.join(getLinuxBuildDirectory(), 'release', 'bundle'), + globals.fs.path.join(getLinuxBuildDirectory(targetPlatform), 'release', 'bundle'), ), precompilerTrace: precompilerTrace, type: 'linux', @@ -109,12 +114,15 @@ Future buildLinux( } } -Future _runCmake(String buildModeName, Directory sourceDir, Directory buildDir) async { +Future _runCmake(String buildModeName, Directory sourceDir, Directory buildDir, + bool needCrossBuild, TargetPlatform targetPlatform, String targetSysroot) async { final Stopwatch sw = Stopwatch()..start(); await buildDir.create(recursive: true); final String buildFlag = toTitleCase(buildModeName); + final bool needCrossBuildOptionsForArm64 = needCrossBuild + && targetPlatform == TargetPlatform.linux_arm64; int result; try { result = await globals.processUtils.stream( @@ -123,6 +131,15 @@ Future _runCmake(String buildModeName, Directory sourceDir, Directory buil '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=$buildFlag', + '-DFLUTTER_TARGET_PLATFORM=' + getNameForTargetPlatform(targetPlatform), + // Support cross-building for arm64 targets on x64 hosts. + // (Cross-building for x64 on arm64 hosts isn't supported now.) + if (needCrossBuild) + '-DFLUTTER_TARGET_PLATFORM_SYSROOT=$targetSysroot', + if (needCrossBuildOptionsForArm64) + '-DCMAKE_C_COMPILER_TARGET=aarch64-linux-gnu', + if (needCrossBuildOptionsForArm64) + '-DCMAKE_CXX_COMPILER_TARGET=aarch64-linux-gnu', sourceDir.path, ], workingDirectory: buildDir.path, diff --git a/packages/flutter_tools/lib/src/linux/linux_device.dart b/packages/flutter_tools/lib/src/linux/linux_device.dart index e122cea9a8..fab26805d0 100644 --- a/packages/flutter_tools/lib/src/linux/linux_device.dart +++ b/packages/flutter_tools/lib/src/linux/linux_device.dart @@ -27,15 +27,20 @@ class LinuxDevice extends DesktopDevice { @required Logger logger, @required FileSystem fileSystem, @required OperatingSystemUtils operatingSystemUtils, - }) : super( - 'linux', - platformType: PlatformType.linux, - ephemeral: false, - logger: logger, - processManager: processManager, - fileSystem: fileSystem, - operatingSystemUtils: operatingSystemUtils, - ); + }) : _operatingSystemUtils = operatingSystemUtils, + super( + 'linux', + platformType: PlatformType.linux, + ephemeral: false, + logger: logger, + processManager: processManager, + fileSystem: fileSystem, + operatingSystemUtils: operatingSystemUtils, + ); + + final OperatingSystemUtils _operatingSystemUtils; + + TargetPlatform _targetPlatform; @override bool isSupported() => true; @@ -44,7 +49,17 @@ class LinuxDevice extends DesktopDevice { String get name => 'Linux'; @override - Future get targetPlatform async => TargetPlatform.linux_x64; + Future get targetPlatform async { + if (_targetPlatform == null) { + if (_operatingSystemUtils.hostPlatform == HostPlatform.linux_x64) { + _targetPlatform = TargetPlatform.linux_x64; + } else { + _targetPlatform = TargetPlatform.linux_arm64; + } + } + + return _targetPlatform; + } @override bool isSupportedForProject(FlutterProject flutterProject) { @@ -61,6 +76,7 @@ class LinuxDevice extends DesktopDevice { FlutterProject.current().linux, buildInfo, target: mainPath, + targetPlatform: _targetPlatform, ); } diff --git a/packages/flutter_tools/lib/src/migrations/cmake_custom_command_migration.dart b/packages/flutter_tools/lib/src/migrations/cmake_custom_command_migration.dart index 1162051197..75828fb928 100644 --- a/packages/flutter_tools/lib/src/migrations/cmake_custom_command_migration.dart +++ b/packages/flutter_tools/lib/src/migrations/cmake_custom_command_migration.dart @@ -56,9 +56,20 @@ class CmakeCustomCommandMigration extends ProjectMigrator { final String addCustomCommandReplacement = '$addCustomCommandOriginal\n VERBATIM'; newProjectContents = newProjectContents.replaceAll(addCustomCommandOriginal, addCustomCommandReplacement); } + + // CMake's add_custom_command() should add FLUTTER_TARGET_PLATFORM to support multi-architecture. + // However, developers would get the following warning every time if we do nothing. + // ------------------------------ + // CMake Warning: + // Manually-specified variables were not used by the project: + // FLUTTER_TARGET_PLATFORM + // ------------------------------ + if (addCustomCommandOriginal?.contains('linux-x64') == true) { + newProjectContents = newProjectContents.replaceAll('linux-x64', r'${FLUTTER_TARGET_PLATFORM}'); + } } if (originalProjectContents != newProjectContents) { - logger.printStatus('add_custom_command() missing VERBATIM, updating.'); + logger.printStatus('add_custom_command() missing VERBATIM or FLUTTER_TARGET_PLATFORM, updating.'); _cmakeFile.writeAsStringSync(newProjectContents.toString()); } return true; diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index eee8df5a2d..36bdcedad8 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -1410,6 +1410,7 @@ DevelopmentArtifact _artifactFromTargetPlatform(TargetPlatform targetPlatform) { } return null; case TargetPlatform.linux_x64: + case TargetPlatform.linux_arm64: if (featureFlags.isLinuxEnabled) { return DevelopmentArtifact.linux; } diff --git a/packages/flutter_tools/lib/src/tester/flutter_tester.dart b/packages/flutter_tools/lib/src/tester/flutter_tester.dart index d99e7efd1c..5ab4fc6d16 100644 --- a/packages/flutter_tools/lib/src/tester/flutter_tester.dart +++ b/packages/flutter_tools/lib/src/tester/flutter_tester.dart @@ -15,6 +15,7 @@ import '../base/config.dart'; import '../base/file_system.dart'; import '../base/io.dart'; import '../base/logger.dart'; +import '../base/os.dart'; import '../build_info.dart'; import '../bundle.dart'; import '../convert.dart'; @@ -55,12 +56,14 @@ class FlutterTesterDevice extends Device { @required String buildDirectory, @required FileSystem fileSystem, @required Artifacts artifacts, + @required OperatingSystemUtils operatingSystemUtils, }) : _processManager = processManager, _flutterVersion = flutterVersion, _logger = logger, _buildDirectory = buildDirectory, _fileSystem = fileSystem, _artifacts = artifacts, + _operatingSystemUtils = operatingSystemUtils, super( deviceId, platformType: null, @@ -74,6 +77,7 @@ class FlutterTesterDevice extends Device { final String _buildDirectory; final FileSystem _fileSystem; final Artifacts _artifacts; + final OperatingSystemUtils _operatingSystemUtils; Process _process; final DevicePortForwarder _portForwarder = const NoOpDevicePortForwarder(); @@ -162,7 +166,7 @@ class FlutterTesterDevice extends Device { mainPath: mainPath, applicationKernelFilePath: applicationKernelFilePath, trackWidgetCreation: buildInfo.trackWidgetCreation, - platform: getTargetPlatformForName(getNameForHostPlatform(getCurrentHostPlatform())), + platform: getTargetPlatformForName(getNameForHostPlatform(_operatingSystemUtils.hostPlatform)), treeShakeIcons: buildInfo.treeShakeIcons, ); @@ -270,6 +274,7 @@ class FlutterTesterDevices extends PollingDeviceDiscovery { @required Logger logger, @required FlutterVersion flutterVersion, @required Config config, + @required OperatingSystemUtils operatingSystemUtils, }) : _testerDevice = FlutterTesterDevice( kTesterDeviceId, fileSystem: fileSystem, @@ -278,6 +283,7 @@ class FlutterTesterDevices extends PollingDeviceDiscovery { buildDirectory: getBuildDirectory(config, fileSystem), logger: logger, flutterVersion: flutterVersion, + operatingSystemUtils: operatingSystemUtils, ), super('Flutter tester'); 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 d0573504e8..23a19a4673 100644 --- a/packages/flutter_tools/templates/app/linux.tmpl/CMakeLists.txt.tmpl +++ b/packages/flutter_tools/templates/app/linux.tmpl/CMakeLists.txt.tmpl @@ -8,6 +8,16 @@ cmake_policy(SET CMP0063 NEW) set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + # Configure build options. if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Debug" CACHE diff --git a/packages/flutter_tools/templates/app/linux.tmpl/flutter/CMakeLists.txt b/packages/flutter_tools/templates/app/linux.tmpl/flutter/CMakeLists.txt index a1da1b9e53..6dc9705582 100644 --- a/packages/flutter_tools/templates/app/linux.tmpl/flutter/CMakeLists.txt +++ b/packages/flutter_tools/templates/app/linux.tmpl/flutter/CMakeLists.txt @@ -82,7 +82,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - linux-x64 ${CMAKE_BUILD_TYPE} + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} VERBATIM ) add_custom_target(flutter_assemble DEPENDS 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 e614b936f6..ef3508affa 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 @@ -8,8 +8,10 @@ import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/utils.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cmake.dart'; import 'package:flutter_tools/src/commands/build.dart'; @@ -17,6 +19,7 @@ import 'package:flutter_tools/src/commands/build_linux.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/reporting/reporting.dart'; +import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; import '../../src/common.dart'; @@ -39,7 +42,6 @@ final Platform notLinuxPlatform = FakePlatform( } ); - void main() { setUpAll(() { Cache.disableLocking(); @@ -69,16 +71,20 @@ void main() { } // Returns the command matching the build_linux call to cmake. - FakeCommand cmakeCommand(String buildMode, {void Function() onRun}) { + FakeCommand cmakeCommand(String buildMode, { + String target = 'x64', + void Function() onRun, + }) { return FakeCommand( command: [ 'cmake', '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=${toTitleCase(buildMode)}', + '-DFLUTTER_TARGET_PLATFORM=linux-$target', '/linux', ], - workingDirectory: 'build/linux/$buildMode', + workingDirectory: 'build/linux/$target/$buildMode', onRun: onRun, ); } @@ -86,6 +92,7 @@ void main() { // Returns the command matching the build_linux call to ninja. FakeCommand ninjaCommand(String buildMode, { Map environment, + String target = 'x64', void Function() onRun, String stdout = '', }) { @@ -93,7 +100,7 @@ void main() { command: [ 'ninja', '-C', - 'build/linux/$buildMode', + 'build/linux/$target/$buildMode', 'install', ], environment: environment, @@ -148,6 +155,7 @@ void main() { ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), }); testUsingContext('Handles argument error from missing cmake', () async { @@ -167,6 +175,7 @@ void main() { ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), }); testUsingContext('Handles argument error from missing ninja', () async { @@ -187,6 +196,7 @@ void main() { ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), }); testUsingContext('Linux build does not spew stdout to status logger', () async { @@ -209,6 +219,7 @@ void main() { ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), }); testUsingContext('Linux build extracts errors from stdout', () async { @@ -218,7 +229,7 @@ void main() { // This contains a mix of routine build output and various types of errors // (Dart error, compile error, link error), edited down for compactness. const String stdout = r''' -ninja: Entering directory `build/linux/release' +ninja: Entering directory `build/linux/x64/release' [1/6] Generating /foo/linux/flutter/ephemeral/libflutter_linux_gtk.so, /foo/linux/flutter/ephemeral/flutter_linux/flutter_linux.h, _phony lib/main.dart:4:3: Error: Method not found: 'foo'. [2/6] Building CXX object CMakeFiles/foo.dir/main.cc.o @@ -262,6 +273,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), }); testUsingContext('Linux verbose build sets VERBOSE_SCRIPT_LOGGING', () async { @@ -287,9 +299,10 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), }); - testUsingContext('Linux build --debug passes debug mode to cmake and ninja', () async { + testUsingContext('Linux on x64 build --debug passes debug mode to cmake and ninja', () async { final BuildCommand command = BuildCommand(); setUpMockProjectFilesForBuild(); processManager = FakeProcessManager.list([ @@ -297,7 +310,6 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg ninjaCommand('debug'), ]); - await createTestCommandRunner(command).run( const ['build', 'linux', '--debug', '--no-pub'] ); @@ -306,9 +318,29 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), }); - testUsingContext('Linux build --profile passes profile mode to make', () async { + testUsingContext('Linux on ARM64 build --debug passes debug mode to cmake and ninja', () async { + final BuildCommand command = BuildCommand(); + setUpMockProjectFilesForBuild(); + processManager = FakeProcessManager.list([ + cmakeCommand('debug', target: 'arm64'), + ninjaCommand('debug', target: 'arm64'), + ]); + + await createTestCommandRunner(command).run( + const ['build', 'linux', '--debug', '--no-pub'] + ); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + Platform: () => linuxPlatform, + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), + }); + + testUsingContext('Linux on x64 build --profile passes profile mode to make', () async { final BuildCommand command = BuildCommand(); setUpMockProjectFilesForBuild(); processManager = FakeProcessManager.list([ @@ -324,6 +356,38 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), + }); + + testUsingContext('Linux on ARM64 build --profile passes profile mode to make', () async { + final BuildCommand command = BuildCommand(); + setUpMockProjectFilesForBuild(); + processManager = FakeProcessManager.list([ + cmakeCommand('profile', target: 'arm64'), + ninjaCommand('profile', target: 'arm64'), + ]); + + await createTestCommandRunner(command).run( + const ['build', 'linux', '--profile', '--no-pub'] + ); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + Platform: () => linuxPlatform, + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), + }); + + testUsingContext('Not support Linux cross-build for x64 on arm64', () async { + final BuildCommand command = BuildCommand(); + + expect(createTestCommandRunner(command).run( + const ['build', 'linux', '--no-pub', '--target-platform=linux-x64'] + ), throwsToolExit()); + }, overrides: { + Platform: () => linuxPlatform, + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), }); testUsingContext('Linux build configures CMake exports', () async { @@ -385,6 +449,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg ProcessManager: () => processManager, Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + OperatingSystemUtils: () => FakeOperatingSystemUtils(), }); testUsingContext('linux can extract binary name from CMake file', () async { @@ -417,14 +482,14 @@ set(BINARY_NAME "fizz_bar") }); testUsingContext('hidden when not enabled on Linux host', () { - expect(BuildLinuxCommand().hidden, true); + expect(BuildLinuxCommand(operatingSystemUtils: FakeOperatingSystemUtils()).hidden, true); }, overrides: { FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false), Platform: () => notLinuxPlatform, }); testUsingContext('Not hidden when enabled and on Linux host', () { - expect(BuildLinuxCommand().hidden, false); + expect(BuildLinuxCommand(operatingSystemUtils: FakeOperatingSystemUtils()).hidden, false); }, overrides: { FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), Platform: () => linuxPlatform, @@ -453,7 +518,7 @@ set(BINARY_NAME "fizz_bar") }), ]); - fileSystem.file('build/linux/release/bundle/libapp.so') + fileSystem.file('build/linux/x64/release/bundle/libapp.so') ..createSync(recursive: true) ..writeAsBytesSync(List.filled(10000, 0)); @@ -472,5 +537,68 @@ set(BINARY_NAME "fizz_bar") Platform: () => linuxPlatform, FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), Usage: () => usage, + OperatingSystemUtils: () => FakeOperatingSystemUtils(), + }); + + testUsingContext('Linux on ARM64 build --release passes, and check if the LinuxBuildDirectory for arm64 can be referenced correctly by using analytics', () async { + final BuildCommand command = BuildCommand(); + setUpMockProjectFilesForBuild(); + processManager = FakeProcessManager.list([ + cmakeCommand('release', target: 'arm64'), + ninjaCommand('release', target: 'arm64', onRun: () { + fileSystem.file('build/flutter_size_01/snapshot.linux-arm64.json') + ..createSync(recursive: true) + ..writeAsStringSync(''' +[ + { + "l": "dart:_internal", + "c": "SubListIterable", + "n": "[Optimized] skip", + "s": 2400 + } +]'''); + fileSystem.file('build/flutter_size_01/trace.linux-arm64.json') + ..createSync(recursive: true) + ..writeAsStringSync('{}'); + }), + ]); + + fileSystem.file('build/linux/arm64/release/bundle/libapp.so') + ..createSync(recursive: true) + ..writeAsBytesSync(List.filled(10000, 0)); + + await createTestCommandRunner(command).run( + const ['build', 'linux', '--no-pub', '--analyze-size'] + ); + + // check if libapp.so of "build/linux/arm64/release" directory can be referenced. + expect(testLogger.statusText, contains('libapp.so (Dart AOT)')); + expect(usage.events, contains( + const TestUsageEvent('code-size-analysis', 'linux'), + )); + }, overrides: { + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + Platform: () => linuxPlatform, + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + Usage: () => usage, + OperatingSystemUtils: () => CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), }); } + +class CustomFakeOperatingSystemUtils extends Fake implements OperatingSystemUtils { + CustomFakeOperatingSystemUtils({ + HostPlatform hostPlatform = HostPlatform.linux_x64 + }) : _hostPlatform = hostPlatform; + + final HostPlatform _hostPlatform; + + @override + String get name => 'Linux'; + + @override + HostPlatform get hostPlatform => _hostPlatform; + + @override + List whichAll(String execName) => []; +} diff --git a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart index b2857bb50c..c7778000e5 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart @@ -118,6 +118,7 @@ void main() { cache: globals.cache, fileSystem: fileSystem, platform: platform, + operatingSystemUtils: FakeOperatingSystemUtils(), ); Cache.flutterRoot = Cache.defaultFlutterRoot( fileSystem: fileSystem, diff --git a/packages/flutter_tools/test/general.shard/android/android_device_discovery_test.dart b/packages/flutter_tools/test/general.shard/android/android_device_discovery_test.dart index 6600a6b1e2..a755ab127f 100644 --- a/packages/flutter_tools/test/general.shard/android/android_device_discovery_test.dart +++ b/packages/flutter_tools/test/general.shard/android/android_device_discovery_test.dart @@ -15,6 +15,7 @@ import 'package:flutter_tools/src/device.dart'; import 'package:test/fake.dart'; import '../../src/common.dart'; +import '../../src/context.dart'; import '../../src/fake_process_manager.dart'; import '../../src/testbed.dart'; @@ -25,6 +26,7 @@ void main() { androidWorkflow = AndroidWorkflow( androidSdk: FakeAndroidSdk(), featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ); }); @@ -35,6 +37,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: FakeAndroidSdk(null), featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), processManager: FakeProcessManager.list([]), fileSystem: MemoryFileSystem.test(), @@ -55,6 +58,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: FakeAndroidSdk('adb'), featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), processManager: fakeProcessManager, fileSystem: MemoryFileSystem.test(), @@ -74,6 +78,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: FakeAndroidSdk(null), featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), processManager: FakeProcessManager.list([]), fileSystem: MemoryFileSystem.test(), @@ -115,6 +120,7 @@ void main() { featureFlags: TestFeatureFlags( isAndroidEnabled: false, ), + operatingSystemUtils: FakeOperatingSystemUtils(), ), processManager: FakeProcessManager.any(), fileSystem: MemoryFileSystem.test(), diff --git a/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart b/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart index 7e18ae59a2..23140736be 100644 --- a/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart +++ b/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart @@ -9,9 +9,11 @@ import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_workflow.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/user_messages.dart'; import 'package:flutter_tools/src/base/version.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/doctor.dart'; import 'package:mockito/mockito.dart'; @@ -49,6 +51,7 @@ void main() { final AndroidWorkflow androidWorkflow = AndroidWorkflow( featureFlags: TestFeatureFlags(), androidSdk: null, + operatingSystemUtils: FakeOperatingSystemUtils(), ); expect(androidWorkflow.canLaunchDevices, false); @@ -62,6 +65,7 @@ void main() { final AndroidWorkflow androidWorkflow = AndroidWorkflow( featureFlags: TestFeatureFlags(), androidSdk: androidSdk, + operatingSystemUtils: FakeOperatingSystemUtils(), ); expect(androidWorkflow.canLaunchDevices, false); @@ -69,6 +73,18 @@ void main() { expect(androidWorkflow.canListEmulators, false); }); + // Android Studio is not currently supported on Linux Arm64 hosts. + testWithoutContext('Not supported AndroidStudio on Linux Arm Hosts', () { + final MockAndroidSdk androidSdk = MockAndroidSdk(); + when(androidSdk.adbPath).thenReturn(null); + final AndroidWorkflow androidWorkflow = AndroidWorkflow( + featureFlags: TestFeatureFlags(), + androidSdk: androidSdk, + operatingSystemUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), + ); + + expect(androidWorkflow.appliesToHostPlatform, false); + }); testWithoutContext('licensesAccepted returns LicensesAccepted.unknown if cannot find sdkmanager', () async { processManager.canRunSucceeds = false; @@ -394,3 +410,17 @@ void main() { ); }); } + +class CustomFakeOperatingSystemUtils extends Fake implements OperatingSystemUtils { + CustomFakeOperatingSystemUtils({ + HostPlatform hostPlatform = HostPlatform.linux_x64 + }) : _hostPlatform = hostPlatform; + + final HostPlatform _hostPlatform; + + @override + String get name => 'Linux'; + + @override + HostPlatform get hostPlatform => _hostPlatform; +} diff --git a/packages/flutter_tools/test/general.shard/artifacts_test.dart b/packages/flutter_tools/test/general.shard/artifacts_test.dart index 989601aa9b..1486fbaa1d 100644 --- a/packages/flutter_tools/test/general.shard/artifacts_test.dart +++ b/packages/flutter_tools/test/general.shard/artifacts_test.dart @@ -8,11 +8,9 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; -import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; -import 'package:mockito/mockito.dart'; import '../src/common.dart'; import '../src/context.dart'; @@ -34,12 +32,13 @@ void main() { fileSystem: fileSystem, platform: platform, logger: BufferLogger.test(), - osUtils: MockOperatingSystemUtils(), + osUtils: FakeOperatingSystemUtils(), ); artifacts = CachedArtifacts( fileSystem: fileSystem, cache: cache, platform: platform, + operatingSystemUtils: FakeOperatingSystemUtils(), ); }); @@ -114,6 +113,10 @@ void main() { artifacts.getArtifactPath(Artifact.flutterTester), fileSystem.path.join('root', 'bin', 'cache', 'artifacts', 'engine', 'linux-x64', 'flutter_tester'), ); + expect( + artifacts.getArtifactPath(Artifact.flutterTester, platform: TargetPlatform.linux_arm64), + fileSystem.path.join('root', 'bin', 'cache', 'artifacts', 'engine', 'linux-arm64', 'flutter_tester'), + ); }); testWithoutContext('precompiled web artifact paths are correct', () { @@ -183,7 +186,7 @@ void main() { fileSystem: fileSystem, platform: platform, logger: BufferLogger.test(), - osUtils: MockOperatingSystemUtils(), + osUtils: FakeOperatingSystemUtils(), ); artifacts = LocalEngineArtifacts( fileSystem.path.join(fileSystem.currentDirectory.path, 'out', 'android_debug_unopt'), @@ -192,6 +195,7 @@ void main() { fileSystem: fileSystem, platform: platform, processManager: FakeProcessManager.any(), + operatingSystemUtils: FakeOperatingSystemUtils(), ); }); @@ -302,6 +306,7 @@ void main() { fileSystem: fileSystem, platform: FakePlatform(operatingSystem: 'windows'), processManager: FakeProcessManager.any(), + operatingSystemUtils: FakeOperatingSystemUtils(), ); expect(artifacts.getArtifactPath(Artifact.engineDartBinary), contains('.exe')); @@ -312,5 +317,3 @@ void main() { }); }); } - -class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} diff --git a/packages/flutter_tools/test/general.shard/base/os_test.dart b/packages/flutter_tools/test/general.shard/base/os_test.dart index 0178dbe9ee..8ef2372fc3 100644 --- a/packages/flutter_tools/test/general.shard/base/os_test.dart +++ b/packages/flutter_tools/test/general.shard/base/os_test.dart @@ -151,6 +151,16 @@ void main() { group('host platform', () { testWithoutContext('unknown defaults to Linux', () async { + fakeProcessManager.addCommand( + const FakeCommand( + command: [ + 'uname', + '-m', + ], + stdout: 'x86_64', + ), + ); + final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'fuchsia')); expect(utils.hostPlatform, HostPlatform.linux_x64); @@ -162,12 +172,38 @@ void main() { expect(utils.hostPlatform, HostPlatform.windows_x64); }); - testWithoutContext('Linux', () async { + testWithoutContext('Linux x64', () async { + fakeProcessManager.addCommand( + const FakeCommand( + command: [ + 'uname', + '-m', + ], + stdout: 'x86_64', + ), + ); + final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'linux')); expect(utils.hostPlatform, HostPlatform.linux_x64); }); + testWithoutContext('Linux ARM', () async { + fakeProcessManager.addCommand( + const FakeCommand( + command: [ + 'uname', + '-m', + ], + stdout: 'aarch64', + ), + ); + + final OperatingSystemUtils utils = + createOSUtils(FakePlatform(operatingSystem: 'linux')); + expect(utils.hostPlatform, HostPlatform.linux_arm64); + }); + testWithoutContext('macOS ARM', () async { fakeProcessManager.addCommands( [ 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 bbfaae4ca0..2da267591c 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 @@ -20,7 +20,7 @@ import '../../../src/common.dart'; import '../../../src/context.dart'; void main() { - testWithoutContext('Copies files to correct cache directory, excluding unrelated code', () async { + testWithoutContext('Copies files to correct cache directory, excluding unrelated code on a x64 host', () async { final FileSystem fileSystem = MemoryFileSystem.test(); final Artifacts artifacts = Artifacts.test(); setUpCacheDirectory(fileSystem, artifacts); @@ -37,16 +37,57 @@ void main() { ); testEnvironment.buildDir.createSync(recursive: true); - await const UnpackLinux().build(testEnvironment); + await const UnpackLinux(TargetPlatform.linux_x64).build(testEnvironment); expect(fileSystem.file('linux/flutter/ephemeral/libflutter_linux_gtk.so'), exists); - - final String headersPath = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_x64, mode: BuildMode.debug); - expect(fileSystem.file('linux/flutter/ephemeral/$headersPath/foo.h'), exists); - - final String icuDataPath = artifacts.getArtifactPath(Artifact.icuData, platform: TargetPlatform.linux_x64); - expect(fileSystem.file('linux/flutter/ephemeral/$icuDataPath'), exists); expect(fileSystem.file('linux/flutter/ephemeral/unrelated-stuff'), isNot(exists)); + + // Check if the target files are copied correctly. + final String headersPathForX64 = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_x64, mode: BuildMode.debug); + final String headersPathForArm64 = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_arm64, mode: BuildMode.debug); + expect(fileSystem.file('linux/flutter/ephemeral/$headersPathForX64/foo.h'), exists); + expect(fileSystem.file('linux/flutter/ephemeral/$headersPathForArm64/foo.h'), isNot(exists)); + + final String icuDataPathForX64 = artifacts.getArtifactPath(Artifact.icuData, platform: TargetPlatform.linux_x64); + final String icuDataPathForArm64 = artifacts.getArtifactPath(Artifact.icuData, platform: TargetPlatform.linux_arm64); + expect(fileSystem.file('linux/flutter/ephemeral/$icuDataPathForX64'), exists); + expect(fileSystem.file('linux/flutter/ephemeral/$icuDataPathForArm64'), isNot(exists)); + }); + + // This test is basically the same logic as the above test. + // The difference is the target CPU architecture. + testWithoutContext('Copies files to correct cache directory, excluding unrelated code on a arm64 host', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + final Artifacts artifacts = Artifacts.test(); + setUpCacheDirectory(fileSystem, artifacts); + + final Environment testEnvironment = Environment.test( + fileSystem.currentDirectory, + defines: { + kBuildMode: 'debug', + }, + artifacts: artifacts, + processManager: FakeProcessManager.any(), + fileSystem: fileSystem, + logger: BufferLogger.test(), + ); + testEnvironment.buildDir.createSync(recursive: true); + + await const UnpackLinux(TargetPlatform.linux_arm64).build(testEnvironment); + + expect(fileSystem.file('linux/flutter/ephemeral/libflutter_linux_gtk.so'), exists); + expect(fileSystem.file('linux/flutter/ephemeral/unrelated-stuff'), isNot(exists)); + + // Check if the target files are copied correctly. + final String headersPathForX64 = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_x64, mode: BuildMode.debug); + final String headersPathForArm64 = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_arm64, mode: BuildMode.debug); + expect(fileSystem.file('linux/flutter/ephemeral/$headersPathForX64/foo.h'), isNot(exists)); + expect(fileSystem.file('linux/flutter/ephemeral/$headersPathForArm64/foo.h'), exists); + + final String icuDataPathForX64 = artifacts.getArtifactPath(Artifact.icuData, platform: TargetPlatform.linux_x64); + final String icuDataPathForArm64 = artifacts.getArtifactPath(Artifact.icuData, platform: TargetPlatform.linux_arm64); + expect(fileSystem.file('linux/flutter/ephemeral/$icuDataPathForX64'), isNot(exists)); + expect(fileSystem.file('linux/flutter/ephemeral/$icuDataPathForArm64'), exists); }); // Only required for the test below that still depends on the context. @@ -85,7 +126,8 @@ void main() { } )); - await const DebugBundleLinuxAssets().build(testEnvironment); + await const DebugBundleLinuxAssets(TargetPlatform.linux_x64).build(testEnvironment); + final Directory output = testEnvironment.outputDir .childDirectory('flutter_assets'); @@ -103,6 +145,11 @@ void main() { ProcessManager: () => FakeProcessManager.any(), }); + testWithoutContext('DebugBundleLinuxAssets\'s name depends on target platforms', () async { + expect(const DebugBundleLinuxAssets(TargetPlatform.linux_x64).name, 'debug_bundle_linux-x64_assets'); + expect(const DebugBundleLinuxAssets(TargetPlatform.linux_arm64).name, 'debug_bundle_linux-arm64_assets'); + }); + testUsingContext('ProfileBundleLinuxAssets copies artifacts to out directory', () async { final Environment testEnvironment = Environment.test( fileSystem.currentDirectory, @@ -121,7 +168,7 @@ void main() { testEnvironment.buildDir.childFile('app.so').createSync(); await const LinuxAotBundle(AotElfProfile(TargetPlatform.linux_x64)).build(testEnvironment); - await const ProfileBundleLinuxAssets().build(testEnvironment); + await const ProfileBundleLinuxAssets(TargetPlatform.linux_x64).build(testEnvironment); final Directory libDir = testEnvironment.outputDir .childDirectory('lib'); final Directory assetsDir = testEnvironment.outputDir @@ -137,6 +184,11 @@ void main() { ProcessManager: () => FakeProcessManager.any(), }); + testWithoutContext('ProfileBundleLinuxAssets\'s name depends on target platforms', () async { + expect(const ProfileBundleLinuxAssets(TargetPlatform.linux_x64).name, 'profile_bundle_linux-x64_assets'); + expect(const ProfileBundleLinuxAssets(TargetPlatform.linux_arm64).name, 'profile_bundle_linux-arm64_assets'); + }); + testUsingContext('ReleaseBundleLinuxAssets copies artifacts to out directory', () async { final Environment testEnvironment = Environment.test( fileSystem.currentDirectory, @@ -155,7 +207,7 @@ void main() { testEnvironment.buildDir.childFile('app.so').createSync(); await const LinuxAotBundle(AotElfRelease(TargetPlatform.linux_x64)).build(testEnvironment); - await const ReleaseBundleLinuxAssets().build(testEnvironment); + await const ReleaseBundleLinuxAssets(TargetPlatform.linux_x64).build(testEnvironment); final Directory libDir = testEnvironment.outputDir .childDirectory('lib'); final Directory assetsDir = testEnvironment.outputDir @@ -170,16 +222,28 @@ void main() { FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), }); + + testWithoutContext('ReleaseBundleLinuxAssets\'s name depends on target platforms', () async { + expect(const ReleaseBundleLinuxAssets(TargetPlatform.linux_x64).name, 'release_bundle_linux-x64_assets'); + expect(const ReleaseBundleLinuxAssets(TargetPlatform.linux_arm64).name, 'release_bundle_linux-arm64_assets'); + }); } void setUpCacheDirectory(FileSystem fileSystem, Artifacts artifacts) { - final String desktopPath = artifacts.getArtifactPath(Artifact.linuxDesktopPath, platform: TargetPlatform.linux_x64, mode: BuildMode.debug); - fileSystem.file('$desktopPath/unrelated-stuff').createSync(recursive: true); - fileSystem.file('$desktopPath/libflutter_linux_gtk.so').createSync(recursive: true); + final String desktopPathForX64 = artifacts.getArtifactPath(Artifact.linuxDesktopPath, platform: TargetPlatform.linux_x64, mode: BuildMode.debug); + final String desktopPathForArm64 = artifacts.getArtifactPath(Artifact.linuxDesktopPath, platform: TargetPlatform.linux_arm64, mode: BuildMode.debug); + fileSystem.file('$desktopPathForX64/unrelated-stuff').createSync(recursive: true); + fileSystem.file('$desktopPathForX64/libflutter_linux_gtk.so').createSync(recursive: true); + fileSystem.file('$desktopPathForArm64/unrelated-stuff').createSync(recursive: true); + fileSystem.file('$desktopPathForArm64/libflutter_linux_gtk.so').createSync(recursive: true); - final String headersPath = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_x64, mode: BuildMode.debug); - fileSystem.file('$headersPath/foo.h').createSync(recursive: true); + final String headersPathForX64 = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_x64, mode: BuildMode.debug); + final String headersPathForArm64 = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_arm64, mode: BuildMode.debug); + fileSystem.file('$headersPathForX64/foo.h').createSync(recursive: true); + fileSystem.file('$headersPathForArm64/foo.h').createSync(recursive: true); fileSystem.file(artifacts.getArtifactPath(Artifact.icuData, platform: TargetPlatform.linux_x64)).createSync(); + fileSystem.file(artifacts.getArtifactPath(Artifact.icuData, platform: TargetPlatform.linux_arm64)).createSync(); + fileSystem.file('packages/flutter_tools/lib/src/build_system/targets/linux.dart').createSync(recursive: true); } diff --git a/packages/flutter_tools/test/general.shard/cache_test.dart b/packages/flutter_tools/test/general.shard/cache_test.dart index 68112c9d02..429194b056 100644 --- a/packages/flutter_tools/test/general.shard/cache_test.dart +++ b/packages/flutter_tools/test/general.shard/cache_test.dart @@ -23,7 +23,36 @@ import 'package:process/process.dart'; import '../src/common.dart'; import '../src/context.dart'; +const FakeCommand unameCommandForX64 = FakeCommand( + command: [ + 'uname', + '-m', + ], + stdout: 'x86_64', +); + +const FakeCommand unameCommandForArm64 = FakeCommand( + command: [ + 'uname', + '-m', + ], + stdout: 'aarch64', +); + void main() { + FakeProcessManager fakeProcessManager; + + setUp(() { + fakeProcessManager = FakeProcessManager.list([]); + }); + + Cache createCache(Platform platform) { + return Cache.test( + platform: platform, + processManager: fakeProcessManager + ); + } + group('Cache.checkLockAcquired', () { setUp(() { Cache.enableLocking(); @@ -365,16 +394,28 @@ void main() { expect(artifacts.developmentArtifact, DevelopmentArtifact.universal); }); - testWithoutContext('FontSubset artifacts on linux', () { - final Cache cache = Cache.test(); + testWithoutContext('FontSubset artifacts on x64 linux', () { + fakeProcessManager.addCommand(unameCommandForX64); + + final Cache cache = createCache(FakePlatform(operatingSystem: 'linux')); final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux')); cache.includeAllPlatforms = false; expect(artifacts.getBinaryDirs(), >[['linux-x64', 'linux-x64/font-subset.zip']]); }); + testWithoutContext('FontSubset artifacts on arm64 linux', () { + fakeProcessManager.addCommand(unameCommandForArm64); + + final Cache cache = createCache(FakePlatform(operatingSystem: 'linux')); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux')); + cache.includeAllPlatforms = false; + + expect(artifacts.getBinaryDirs(), >[['linux-arm64', 'linux-arm64/font-subset.zip']]); + }); + testWithoutContext('FontSubset artifacts on windows', () { - final Cache cache = Cache.test(); + final Cache cache = createCache(FakePlatform(operatingSystem: 'windows')); final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'windows')); cache.includeAllPlatforms = false; @@ -382,7 +423,24 @@ void main() { }); testWithoutContext('FontSubset artifacts on macos', () { - final Cache cache = Cache.test(); + fakeProcessManager.addCommands([ + const FakeCommand( + command: [ + 'which', + 'sysctl' + ], + stdout: '/sbin/sysctl', + ), + const FakeCommand( + command: [ + 'sysctl', + 'hw.optional.arm64', + ], + stdout: 'hw.optional.arm64: 0', + ), + ]); + + final Cache cache = createCache(FakePlatform(operatingSystem: 'macos')); final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'macos')); cache.includeAllPlatforms = false; @@ -390,23 +448,41 @@ void main() { }); testWithoutContext('FontSubset artifacts on fuchsia', () { - final Cache cache = Cache.test(); + fakeProcessManager.addCommand(unameCommandForX64); + + final Cache cache = createCache(FakePlatform(operatingSystem: 'fuchsia')); final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'fuchsia')); cache.includeAllPlatforms = false; expect(artifacts.getBinaryDirs, throwsToolExit(message: 'Unsupported operating system: fuchsia')); }); - testWithoutContext('FontSubset artifacts for all platforms', () { - final Cache cache = Cache.test(); - final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'fuchsia')); - cache.includeAllPlatforms = true; + testWithoutContext('FontSubset artifacts for all platforms on x64 hosts', () { + fakeProcessManager.addCommand(unameCommandForX64); - expect(artifacts.getBinaryDirs(), >[ - ['darwin-x64', 'darwin-x64/font-subset.zip'], - ['linux-x64', 'linux-x64/font-subset.zip'], - ['windows-x64', 'windows-x64/font-subset.zip'], - ]); + final Cache cache = createCache(FakePlatform(operatingSystem: 'fuchsia')); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'fuchsia')); + cache.includeAllPlatforms = true; + + expect(artifacts.getBinaryDirs(), >[ + ['darwin-x64', 'darwin-x64/font-subset.zip'], + ['linux-x64', 'linux-x64/font-subset.zip'], + ['windows-x64', 'windows-x64/font-subset.zip'], + ]); + }); + + testWithoutContext('FontSubset artifacts for all platforms on arm64 hosts', () { + fakeProcessManager.addCommand(unameCommandForArm64); + + final Cache cache = createCache(FakePlatform(operatingSystem: 'fuchsia')); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'fuchsia')); + cache.includeAllPlatforms = true; + + expect(artifacts.getBinaryDirs(), >[ + ['darwin-x64', 'darwin-x64/font-subset.zip'], // arm64 macOS hosts are not supported now + ['linux-arm64', 'linux-arm64/font-subset.zip'], + ['windows-x64', 'windows-x64/font-subset.zip'], // arm64 macOS hosts are not supported now + ]); }); testWithoutContext('macOS desktop artifacts ignore filtering when requested', () { @@ -444,7 +520,9 @@ void main() { }); testWithoutContext('Linux desktop artifacts ignore filtering when requested', () { - final Cache cache = Cache.test(); + fakeProcessManager.addCommand(unameCommandForX64); + + final Cache cache = createCache(FakePlatform(operatingSystem: 'linux')); final LinuxEngineArtifacts artifacts = LinuxEngineArtifacts( cache, platform: FakePlatform(operatingSystem: 'macos'), @@ -455,17 +533,36 @@ void main() { expect(artifacts.getBinaryDirs(), isNotEmpty); }); - testWithoutContext('Linux desktop artifacts include profile and release artifacts', () { - final Cache cache = Cache.test(); - final LinuxEngineArtifacts artifacts = LinuxEngineArtifacts( - cache, - platform: FakePlatform(operatingSystem: 'linux'), - ); + testWithoutContext('Linux desktop artifacts for x64 include profile and release artifacts', () { + fakeProcessManager.addCommand(unameCommandForX64); - expect(artifacts.getBinaryDirs(), containsAll([ - contains(contains('profile')), - contains(contains('release')), - ])); + final Cache cache = createCache(FakePlatform(operatingSystem: 'linux')); + final LinuxEngineArtifacts artifacts = LinuxEngineArtifacts( + cache, + platform: FakePlatform(operatingSystem: 'linux'), + ); + + expect(artifacts.getBinaryDirs(), >[ + ['linux-x64', 'linux-x64/linux-x64-flutter-gtk.zip'], + ['linux-x64-profile', 'linux-x64-profile/linux-x64-flutter-gtk.zip'], + ['linux-x64-release', 'linux-x64-release/linux-x64-flutter-gtk.zip'], + ]); + }); + + testWithoutContext('Linux desktop artifacts for arm64 include profile and release artifacts', () { + fakeProcessManager.addCommand(unameCommandForArm64); + + final Cache cache = createCache(FakePlatform(operatingSystem: 'linux')); + final LinuxEngineArtifacts artifacts = LinuxEngineArtifacts( + cache, + platform: FakePlatform(operatingSystem: 'linux'), + ); + + expect(artifacts.getBinaryDirs(), >[ + ['linux-arm64', 'linux-arm64/linux-arm64-flutter-gtk.zip'], + ['linux-arm64-profile', 'linux-arm64-profile/linux-arm64-flutter-gtk.zip'], + ['linux-arm64-release', 'linux-arm64-release/linux-arm64-flutter-gtk.zip'], + ]); }); testWithoutContext('Cache can delete stampfiles of artifacts', () { diff --git a/packages/flutter_tools/test/general.shard/commands/build_test.dart b/packages/flutter_tools/test/general.shard/commands/build_test.dart index 80cde3758a..be7caa8861 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_test.dart @@ -28,7 +28,7 @@ void main() { testUsingContext('All build commands support null safety options', () { final List commands = [ BuildWindowsCommand(verboseHelp: false), - BuildLinuxCommand(verboseHelp: false), + BuildLinuxCommand(verboseHelp: false, operatingSystemUtils: globals.os), BuildMacosCommand(verboseHelp: false), BuildWebCommand(verboseHelp: false), BuildApkCommand(verboseHelp: false), diff --git a/packages/flutter_tools/test/general.shard/emulator_test.dart b/packages/flutter_tools/test/general.shard/emulator_test.dart index 77aa8c1ee2..3dbd22c358 100644 --- a/packages/flutter_tools/test/general.shard/emulator_test.dart +++ b/packages/flutter_tools/test/general.shard/emulator_test.dart @@ -79,6 +79,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: mockSdk, featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), ); @@ -101,6 +102,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: null, featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), ); @@ -135,6 +137,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: mockSdk, featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), ); final CreateEmulatorResult result = await emulatorManager.createEmulator(); @@ -176,6 +179,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: mockSdk, featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), ); final CreateEmulatorResult result = await emulatorManager.createEmulator(); @@ -212,6 +216,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: mockSdk, featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), ); final CreateEmulatorResult result = await emulatorManager.createEmulator(name: 'test'); @@ -250,6 +255,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: mockSdk, featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), ); final CreateEmulatorResult result = await emulatorManager.createEmulator(name: 'existing-avd-1'); @@ -291,6 +297,7 @@ void main() { androidWorkflow: AndroidWorkflow( androidSdk: mockSdk, featureFlags: TestFeatureFlags(), + operatingSystemUtils: FakeOperatingSystemUtils(), ), ); final CreateEmulatorResult result = await emulatorManager.createEmulator(); diff --git a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart index dff03316da..b84c4832ec 100644 --- a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart +++ b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart @@ -839,6 +839,7 @@ void main() { cache: cache, fileSystem: fileSystem, platform: FakePlatform(operatingSystem: 'linux'), + operatingSystemUtils: globals.os, ); expect(artifacts.getArtifactPath( Artifact.fuchsiaFlutterRunner, diff --git a/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart b/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart index 07efaec735..15dda0bbf6 100644 --- a/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart +++ b/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart @@ -53,6 +53,16 @@ void main() { expect(device.supportsRuntimeMode(BuildMode.jitRelease), false); }); + testWithoutContext('LinuxDevice on arm64 hosts is arm64', () async { + final LinuxDevice deviceArm64Host = LinuxDevice( + processManager: FakeProcessManager.any(), + logger: BufferLogger.test(), + fileSystem: MemoryFileSystem.test(), + operatingSystemUtils: FakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64), + ); + expect(await deviceArm64Host.targetPlatform, TargetPlatform.linux_arm64); + }); + testWithoutContext('LinuxDevice: no devices listed if platform unsupported', () async { expect(await LinuxDevices( fileSystem: MemoryFileSystem.test(), @@ -159,6 +169,15 @@ FlutterProject setUpFlutterProject(Directory directory) { class MockLinuxApp extends Mock implements LinuxApp {} class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils { + FakeOperatingSystemUtils({ + HostPlatform hostPlatform = HostPlatform.linux_x64 + }) : _hostPlatform = hostPlatform; + + final HostPlatform _hostPlatform; + @override String get name => 'Linux'; + + @override + HostPlatform get hostPlatform => _hostPlatform; } diff --git a/packages/flutter_tools/test/general.shard/migrations/cmake_project_migration_test.dart b/packages/flutter_tools/test/general.shard/migrations/cmake_project_migration_test.dart index c332f31b47..db64e9fa0b 100644 --- a/packages/flutter_tools/test/general.shard/migrations/cmake_project_migration_test.dart +++ b/packages/flutter_tools/test/general.shard/migrations/cmake_project_migration_test.dart @@ -90,7 +90,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - linux-x64 ${CMAKE_BUILD_TYPE} + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} VERBATIM ) '''; @@ -117,7 +117,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - linux-x64 ${CMAKE_BUILD_TYPE} + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} ) '''); @@ -128,6 +128,22 @@ add_custom_command( expect(cmakeProjectMigration.migrate(), isTrue); expect(managedCmakeFile.readAsStringSync(), r''' +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +'''); + + expect(testLogger.statusText, contains('add_custom_command() missing VERBATIM or FLUTTER_TARGET_PLATFORM, updating.')); + }); + + testWithoutContext('is migrated to use FLUTTER_TARGET_PLATFORM', () { + managedCmakeFile.writeAsStringSync(r''' add_custom_command( OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/_phony_ @@ -139,7 +155,25 @@ add_custom_command( ) '''); - expect(testLogger.statusText, contains('add_custom_command() missing VERBATIM, updating.')); + final CmakeCustomCommandMigration cmakeProjectMigration = CmakeCustomCommandMigration( + mockCmakeProject, + testLogger, + ); + expect(cmakeProjectMigration.migrate(), isTrue); + + expect(managedCmakeFile.readAsStringSync(), r''' +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +'''); + + expect(testLogger.statusText, contains('add_custom_command() missing VERBATIM or FLUTTER_TARGET_PLATFORM, updating.')); }); }); }); diff --git a/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart b/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart index ae8d93fba8..9d987ea2c5 100644 --- a/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart +++ b/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart @@ -71,8 +71,8 @@ void main() { final List devices = await discoverer.discoverDevices(timeout: const Duration(seconds: 10)); expect(devices, hasLength(1)); }); - }); + group('startApp', () { FlutterTesterDevice device; List logLines; @@ -106,6 +106,7 @@ void main() { buildDirectory: 'build', logger: BufferLogger.test(), flutterVersion: MockFlutterVersion(), + operatingSystemUtils: FakeOperatingSystemUtils(), ); logLines = []; device.getLogReader().logLines.listen(logLines.add); @@ -169,7 +170,7 @@ Hello! final LaunchResult result = await device.startApp(app, mainPath: mainPath, - debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug) + debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), ); expect(result.started, isTrue); @@ -188,6 +189,7 @@ FlutterTesterDevices setUpFlutterTesterDevices() { fileSystem: MemoryFileSystem.test(), config: Config.test(), flutterVersion: MockFlutterVersion(), + operatingSystemUtils: FakeOperatingSystemUtils(), ); } diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart index e6c555b936..9e81a2d4df 100644 --- a/packages/flutter_tools/test/src/testbed.dart +++ b/packages/flutter_tools/test/src/testbed.dart @@ -396,6 +396,9 @@ class FakeCache implements Cache { return globals.fs.currentDirectory; } + @override + String getHostPlatformArchName() => 'x64'; + @override File getLicenseFile() { return globals.fs.currentDirectory.childFile('LICENSE');