Unverified Commit 4cc0ab2d authored by Hidenori Matsubayashi's avatar Hidenori Matsubayashi Committed by GitHub

[flutter_tools] Add ARM64 Linux host and cross-building option support (#61221)

parent 97a9f2ae
......@@ -73,3 +73,4 @@ Anurag Roy <anuragr9847@gmail.com>
Andrey Kabylin <andrey@kabylin.ru>
vimerzhao <vimerzhao@gmail.com>
Pedro Massango <pedromassango.developer@gmail.com>
Hidenori Matsubayashi <Hidenori.Matsubayashi@sony.com>
......@@ -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*)
......
......@@ -113,6 +113,7 @@ Future<void> main(List<String> args) async {
fileSystem: globals.fs,
cache: globals.cache,
platform: globals.platform,
operatingSystemUtils: globals.os,
),
frontendServer: frontendServer,
engineDartBinary: dartSdk,
......
......@@ -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,
......
......@@ -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
......
......@@ -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);
......
......@@ -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.');
......
......@@ -313,6 +313,7 @@ class AOTSnapshotter {
TargetPlatform.ios,
TargetPlatform.darwin_x64,
TargetPlatform.linux_x64,
TargetPlatform.linux_arm64,
TargetPlatform.windows_x64,
].contains(platform);
}
......
......@@ -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(<String>['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 {
......
......@@ -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;
}
......@@ -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: <String>[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<Target> get dependencies => const <Target>[
KernelSnapshot(),
UnpackLinux(),
List<Target> get dependencies => <Target>[
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: <String, DevFSContent>{
'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<Source> get inputs => <Source>[
......@@ -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<Source> get outputs => const <Source>[];
......@@ -214,15 +218,15 @@ class ProfileBundleLinuxAssets extends BundleLinuxAssets {
@override
List<Target> get dependencies => <Target>[
...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<Source> get outputs => const <Source>[];
......@@ -230,6 +234,6 @@ class ReleaseBundleLinuxAssets extends BundleLinuxAssets {
@override
List<Target> get dependencies => <Target>[
...super.dependencies,
const LinuxAotBundle(AotElfRelease(TargetPlatform.linux_x64)),
LinuxAotBundle(AotElfRelease(targetPlatform)),
];
}
......@@ -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<List<String>> getBinaryDirs() {
// Currently only Linux supports both arm64 and x64.
final String arch = cache.getHostPlatformArchName();
return <List<String>>[
<String>['common', 'flutter_patched_sdk.zip'],
<String>['common', 'flutter_patched_sdk_product.zip'],
if (cache.includeAllPlatforms) ...<List<String>>[
<String>['windows-x64', 'windows-x64/artifacts.zip'],
<String>['linux-x64', 'linux-x64/artifacts.zip'],
<String>['linux-$arch', 'linux-$arch/artifacts.zip'],
<String>['darwin-x64', 'darwin-x64/artifacts.zip'],
]
else if (_platform.isWindows)
......@@ -1009,7 +1016,7 @@ class FlutterSdk extends EngineCachedArtifact {
else if (_platform.isMacOS)
<String>['darwin-x64', 'darwin-x64/artifacts.zip']
else if (_platform.isLinux)
<String>['linux-x64', 'linux-x64/artifacts.zip'],
<String>['linux-$arch', 'linux-$arch/artifacts.zip'],
];
}
......@@ -1091,7 +1098,12 @@ class LinuxEngineArtifacts extends EngineCachedArtifact {
@override
List<List<String>> getBinaryDirs() {
if (_platform.isLinux || ignorePlatformFiltering) {
return _linuxDesktopBinaryDirs;
final String arch = cache.getHostPlatformArchName();
return <List<String>>[
<String>['linux-$arch', 'linux-$arch/linux-$arch-flutter-gtk.zip'],
<String>['linux-$arch-profile', 'linux-$arch-profile/linux-$arch-flutter-gtk.zip'],
<String>['linux-$arch-release', 'linux-$arch-release/linux-$arch-flutter-gtk.zip'],
];
}
return const <List<String>>[];
}
......@@ -1480,9 +1492,11 @@ class FontSubsetArtifacts extends EngineCachedArtifact {
@override
List<List<String>> getBinaryDirs() {
const Map<String, List<String>> artifacts = <String, List<String>> {
// Currently only Linux supports both arm64 and x64.
final String arch = cache.getHostPlatformArchName();
final Map<String, List<String>> artifacts = <String, List<String>> {
'macos': <String>['darwin-x64', 'darwin-x64/$artifactName.zip'],
'linux': <String>['linux-x64', 'linux-x64/$artifactName.zip'],
'linux': <String>['linux-$arch', 'linux-$arch/$artifactName.zip'],
'windows': <String>['windows-x64', 'windows-x64/$artifactName.zip'],
};
if (cache.includeAllPlatforms) {
......@@ -1613,12 +1627,6 @@ const List<List<String>> _windowsDesktopBinaryDirs = <List<String>>[
<String>['windows-x64-release', 'windows-x64-release/windows-x64-flutter.zip'],
];
const List<List<String>> _linuxDesktopBinaryDirs = <List<String>>[
<String>['linux-x64', 'linux-x64/linux-x64-flutter-gtk.zip'],
<String>['linux-x64-profile', 'linux-x64-profile/linux-x64-flutter-gtk.zip'],
<String>['linux-x64-release', 'linux-x64-release/linux-x64-flutter-gtk.zip'],
];
const List<List<String>> _macOSDesktopBinaryDirs = <List<String>>[
<String>['darwin-x64', 'darwin-x64/FlutterMacOS.framework.zip'],
<String>['darwin-x64-profile', 'darwin-x64-profile/FlutterMacOS.framework.zip'],
......
......@@ -41,9 +41,12 @@ const List<Target> _kDefaultTargets = <Target>[
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(),
......
......@@ -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));
}
......
......@@ -34,6 +34,7 @@ class BuildBundleCommand extends BuildSubCommand {
'ios',
'darwin-x64',
'linux-x64',
'linux-arm64',
'windows-x64',
],
)
......
......@@ -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: <String>['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<FlutterCommandResult> 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();
}
......
......@@ -105,6 +105,7 @@ Future<T> runInContext<T>(
AndroidWorkflow: () => AndroidWorkflow(
androidSdk: globals.androidSdk,
featureFlags: featureFlags,
operatingSystemUtils: globals.os,
),
ApplicationPackageFactory: () => ApplicationPackageFactory(
userMessages: globals.userMessages,
......@@ -117,6 +118,7 @@ Future<T> runInContext<T>(
fileSystem: globals.fs,
cache: globals.cache,
platform: globals.platform,
operatingSystemUtils: globals.os,
),
AssetBundleFactory: () {
return AssetBundleFactory.defaultInstance(
......
......@@ -394,6 +394,7 @@ class FlutterDeviceManager extends DeviceManager {
config: config,
logger: logger,
artifacts: artifacts,
operatingSystemUtils: operatingSystemUtils,
),
MacOSDevices(
processManager: processManager,
......
......@@ -33,6 +33,9 @@ Future<void> 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<void> 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<void> 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<void> buildLinux(
}
}
Future<void> _runCmake(String buildModeName, Directory sourceDir, Directory buildDir) async {
Future<void> _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<void> _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,
......
......@@ -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<TargetPlatform> get targetPlatform async => TargetPlatform.linux_x64;
Future<TargetPlatform> 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,
);
}
......
......@@ -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;
......
......@@ -1410,6 +1410,7 @@ DevelopmentArtifact _artifactFromTargetPlatform(TargetPlatform targetPlatform) {
}
return null;
case TargetPlatform.linux_x64:
case TargetPlatform.linux_arm64:
if (featureFlags.isLinuxEnabled) {
return DevelopmentArtifact.linux;
}
......
......@@ -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');
......
......@@ -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
......
......@@ -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
......
......@@ -118,6 +118,7 @@ void main() {
cache: globals.cache,
fileSystem: fileSystem,
platform: platform,
operatingSystemUtils: FakeOperatingSystemUtils(),
);
Cache.flutterRoot = Cache.defaultFlutterRoot(
fileSystem: fileSystem,
......
......@@ -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(<FakeCommand>[]),
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(<FakeCommand>[]),
fileSystem: MemoryFileSystem.test(),
......@@ -115,6 +120,7 @@ void main() {
featureFlags: TestFeatureFlags(
isAndroidEnabled: false,
),
operatingSystemUtils: FakeOperatingSystemUtils(),
),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
......
......@@ -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;
}
......@@ -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 {}
......@@ -151,6 +151,16 @@ void main() {
group('host platform', () {
testWithoutContext('unknown defaults to Linux', () async {
fakeProcessManager.addCommand(
const FakeCommand(
command: <String>[
'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: <String>[
'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: <String>[
'uname',
'-m',
],
stdout: 'aarch64',
),
);
final OperatingSystemUtils utils =
createOSUtils(FakePlatform(operatingSystem: 'linux'));
expect(utils.hostPlatform, HostPlatform.linux_arm64);
});
testWithoutContext('macOS ARM', () async {
fakeProcessManager.addCommands(
<FakeCommand>[
......
......@@ -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);
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: <String, String>{
kBuildMode: 'debug',
},
artifacts: artifacts,
processManager: FakeProcessManager.any(),
fileSystem: fileSystem,
logger: BufferLogger.test(),
);
testEnvironment.buildDir.createSync(recursive: true);
final String headersPath = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_x64, mode: BuildMode.debug);
expect(fileSystem.file('linux/flutter/ephemeral/$headersPath/foo.h'), exists);
await const UnpackLinux(TargetPlatform.linux_arm64).build(testEnvironment);
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/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 headersPath = artifacts.getArtifactPath(Artifact.linuxHeaders, platform: TargetPlatform.linux_x64, mode: BuildMode.debug);
fileSystem.file('$headersPath/foo.h').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 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);
}
......@@ -23,7 +23,36 @@ import 'package:process/process.dart';
import '../src/common.dart';
import '../src/context.dart';
const FakeCommand unameCommandForX64 = FakeCommand(
command: <String>[
'uname',
'-m',
],
stdout: 'x86_64',
);
const FakeCommand unameCommandForArm64 = FakeCommand(
command: <String>[
'uname',
'-m',
],
stdout: 'aarch64',
);
void main() {
FakeProcessManager fakeProcessManager;
setUp(() {
fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
});
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(), <List<String>>[<String>['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(), <List<String>>[<String>['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(<FakeCommand>[
const FakeCommand(
command: <String>[
'which',
'sysctl'
],
stdout: '/sbin/sysctl',
),
const FakeCommand(
command: <String>[
'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(), <List<String>>[
<String>['darwin-x64', 'darwin-x64/font-subset.zip'],
<String>['linux-x64', 'linux-x64/font-subset.zip'],
<String>['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(), <List<String>>[
<String>['darwin-x64', 'darwin-x64/font-subset.zip'],
<String>['linux-x64', 'linux-x64/font-subset.zip'],
<String>['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(), <List<String>>[
<String>['darwin-x64', 'darwin-x64/font-subset.zip'], // arm64 macOS hosts are not supported now
<String>['linux-arm64', 'linux-arm64/font-subset.zip'],
<String>['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(<Matcher>[
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(), <List<String>>[
<String>['linux-x64', 'linux-x64/linux-x64-flutter-gtk.zip'],
<String>['linux-x64-profile', 'linux-x64-profile/linux-x64-flutter-gtk.zip'],
<String>['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(), <List<String>>[
<String>['linux-arm64', 'linux-arm64/linux-arm64-flutter-gtk.zip'],
<String>['linux-arm64-profile', 'linux-arm64-profile/linux-arm64-flutter-gtk.zip'],
<String>['linux-arm64-release', 'linux-arm64-release/linux-arm64-flutter-gtk.zip'],
]);
});
testWithoutContext('Cache can delete stampfiles of artifacts', () {
......
......@@ -28,7 +28,7 @@ void main() {
testUsingContext('All build commands support null safety options', () {
final List<FlutterCommand> commands = <FlutterCommand>[
BuildWindowsCommand(verboseHelp: false),
BuildLinuxCommand(verboseHelp: false),
BuildLinuxCommand(verboseHelp: false, operatingSystemUtils: globals.os),
BuildMacosCommand(verboseHelp: false),
BuildWebCommand(verboseHelp: false),
BuildApkCommand(verboseHelp: false),
......
......@@ -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();
......
......@@ -839,6 +839,7 @@ void main() {
cache: cache,
fileSystem: fileSystem,
platform: FakePlatform(operatingSystem: 'linux'),
operatingSystemUtils: globals.os,
);
expect(artifacts.getArtifactPath(
Artifact.fuchsiaFlutterRunner,
......
......@@ -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;
}
......@@ -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.'));
});
});
});
......
......@@ -71,8 +71,8 @@ void main() {
final List<Device> devices = await discoverer.discoverDevices(timeout: const Duration(seconds: 10));
expect(devices, hasLength(1));
});
});
group('startApp', () {
FlutterTesterDevice device;
List<String> logLines;
......@@ -106,6 +106,7 @@ void main() {
buildDirectory: 'build',
logger: BufferLogger.test(),
flutterVersion: MockFlutterVersion(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
logLines = <String>[];
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(),
);
}
......
......@@ -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');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment