Unverified Commit 0a63cd70 authored by Andrew Kolos's avatar Andrew Kolos Committed by GitHub

[tool] Move Java functions to their own file (#126086)

This is the first step in unifying Java-finding logic across the tool. If curious, see #126029 for an example of what all the changes will probably entail.

Moves java-related functionality like `AndroidSdk.findJavaHome` to a new class, `Java`.

See tracking issue https://github.com/flutter/flutter/issues/126126 for more.
parent a308666d
......@@ -2,17 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:meta/meta.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/process.dart';
import '../base/version.dart';
import '../convert.dart';
import '../globals.dart' as globals;
import 'android_studio.dart';
import 'java.dart';
// ANDROID_HOME is deprecated.
// See https://developer.android.com/studio/command-line/variables.html#envar
......@@ -36,16 +32,18 @@ final RegExp _sdkVersionRe = RegExp(r'^ro.build.version.sdk=([0-9]+)$');
// $ANDROID_SDK_ROOT/platforms/android-23/android.jar
// $ANDROID_SDK_ROOT/platforms/android-N/android.jar
class AndroidSdk {
AndroidSdk(this.directory) {
AndroidSdk(this.directory, {
Java? java,
}): _java = java {
reinitialize();
}
static const String javaHomeEnvironmentVariable = 'JAVA_HOME';
static const String _javaExecutable = 'java';
/// The Android SDK root directory.
final Directory directory;
final Java? _java;
List<AndroidSdkVersion> _sdkVersions = <AndroidSdkVersion>[];
AndroidSdkVersion? _latestVersion;
......@@ -411,162 +409,6 @@ class AndroidSdk {
return null;
}
/// Returns the version of java in the format \d(.\d)+(.\d)+
/// Returns null if version not found.
String? getJavaVersion({
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
required ProcessUtils processUtils,
}) {
final String? javaBinary = findJavaBinary(
androidStudio: androidStudio,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
platform: platform,
);
if (javaBinary == null) {
globals.printTrace('Could not find java binary to get version.');
return null;
}
final RunResult result = processUtils.runSync(
<String>[javaBinary, '--version'],
environment: sdkManagerEnv,
);
if (result.exitCode != 0) {
globals.printTrace(
'java --version failed: exitCode: ${result.exitCode} stdout: ${result.stdout} stderr: ${result.stderr}');
return null;
}
return parseJavaVersion(result.stdout);
}
/// Extracts JDK version from the output of java --version.
@visibleForTesting
static String? parseJavaVersion(String rawVersionOutput) {
// The contents that matter come in the format '11.0.18' or '1.8.0_202'.
final RegExp jdkVersionRegex = RegExp(r'\d+\.\d+(\.\d+(?:_\d+)?)?');
final Iterable<RegExpMatch> matches =
jdkVersionRegex.allMatches(rawVersionOutput);
if (matches.isEmpty) {
globals.logger.printWarning(_formatJavaVersionWarning(rawVersionOutput));
return null;
}
final String? versionString = matches.first.group(0);
if (versionString == null || versionString.split('_').isEmpty) {
globals.logger.printWarning(_formatJavaVersionWarning(rawVersionOutput));
return null;
}
// Trim away _d+ from versions 1.8 and below.
return versionString.split('_').first;
}
/// A value that would be appropriate to use as JAVA_HOME.
///
/// This method considers jdk in the following order:
/// * the JDK bundled with Android Studio, if one is found;
/// * the JAVA_HOME in the ambient environment, if set;
String? get javaHome {
return findJavaHome(
androidStudio: globals.androidStudio,
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
platform: globals.platform,
);
}
static String? findJavaHome({
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
}) {
if (androidStudio?.javaPath != null) {
globals.printTrace("Using Android Studio's java.");
return androidStudio!.javaPath!;
}
final String? javaHomeEnv = platform.environment[javaHomeEnvironmentVariable];
if (javaHomeEnv != null) {
globals.printTrace('Using JAVA_HOME from environment valuables.');
return javaHomeEnv;
}
return null;
}
/// Finds the java binary that is used for all operations across the tool.
///
/// This comes from [findJavaHome] if that method returns non-null;
/// otherwise, it gets from searching PATH.
// TODO(andrewkolos): To prevent confusion when debugging Android-related
// issues (see https://github.com/flutter/flutter/issues/122609 for an example),
// this logic should be consistently followed by any Java-dependent operation
// across the the tool (building Android apps, interacting with the Android SDK, etc.).
// Currently, this consistency is fragile since the logic used for building
// Android apps exists independently of this method.
// See https://github.com/flutter/flutter/issues/124252.
static String? findJavaBinary({
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
}) {
final String? javaHome = findJavaHome(
androidStudio: androidStudio,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
platform: platform,
);
if (javaHome != null) {
return fileSystem.path.join(javaHome, 'bin', 'java');
}
// Fallback to PATH based lookup.
final String? pathJava = operatingSystemUtils.which(_javaExecutable)?.path;
if (pathJava != null) {
globals.printTrace('Using java from PATH.');
} else {
globals.printTrace('Could not find java path.');
}
return pathJava;
}
// Returns a user visible String that says the tool failed to parse
// the version of java along with the output.
static String _formatJavaVersionWarning(String javaVersionRaw) {
return 'Could not parse java version from: \n'
'$javaVersionRaw \n'
'If there is a version please look for an existing bug '
'https://github.com/flutter/flutter/issues/'
' and if one does not exist file a new issue.';
}
Map<String, String>? _sdkManagerEnv;
/// Returns an environment with the Java folder added to PATH for use in calling
/// Java-based Android SDK commands such as sdkmanager and avdmanager.
Map<String, String> get sdkManagerEnv {
if (_sdkManagerEnv == null) {
// If we can locate Java, then add it to the path used to run the Android SDK manager.
_sdkManagerEnv = <String, String>{};
final String? javaBinary = findJavaBinary(
androidStudio: globals.androidStudio,
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
platform: globals.platform,
);
if (javaBinary != null && globals.platform.environment['PATH'] != null) {
_sdkManagerEnv!['PATH'] = globals.fs.path.dirname(javaBinary) +
globals.os.pathVarSeparator +
globals.platform.environment['PATH']!;
}
}
return _sdkManagerEnv!;
}
/// Returns the version of the Android SDK manager tool or null if not found.
String? get sdkManagerVersion {
if (sdkManagerPath == null || !globals.processManager.canRun(sdkManagerPath)) {
......@@ -577,7 +419,7 @@ class AndroidSdk {
}
final RunResult result = globals.processUtils.runSync(
<String>[sdkManagerPath!, '--version'],
environment: sdkManagerEnv,
environment: _java?.environment,
);
if (result.exitCode != 0) {
globals.printTrace('sdkmanager --version failed: exitCode: ${result.exitCode} stdout: ${result.stdout} stderr: ${result.stderr}');
......
......@@ -11,7 +11,6 @@ import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/user_messages.dart' hide userMessages;
import '../base/version.dart';
......@@ -20,6 +19,7 @@ import '../doctor_validator.dart';
import '../features.dart';
import 'android_sdk.dart';
import 'android_studio.dart';
import 'java.dart';
const int kAndroidSdkMinVersion = 29;
final Version kAndroidJavaMinVersion = Version(1, 8, 0);
......@@ -86,12 +86,6 @@ class AndroidValidator extends DoctorValidator {
_androidStudio = androidStudio,
_fileSystem = fileSystem,
_logger = logger,
_operatingSystemUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
),
_platform = platform,
_processManager = processManager,
_userMessages = userMessages,
......@@ -101,7 +95,6 @@ class AndroidValidator extends DoctorValidator {
final AndroidStudio? _androidStudio;
final FileSystem _fileSystem;
final Logger _logger;
final OperatingSystemUtils _operatingSystemUtils;
final Platform _platform;
final ProcessManager _processManager;
final UserMessages _userMessages;
......@@ -138,6 +131,8 @@ class AndroidValidator extends DoctorValidator {
}
String? javaVersionText;
try {
// TODO(andrewkolos): Use Java class to find version instead of using duplicate
// code. See https://github.com/flutter/flutter/issues/124252.
_logger.printTrace('java -version');
final ProcessResult result = await _processManager.run(<String>[javaBinary, '-version']);
if (result.exitCode == 0) {
......@@ -240,12 +235,13 @@ class AndroidValidator extends DoctorValidator {
_task = 'Finding Java binary';
// Now check for the JDK.
final String? javaBinary = AndroidSdk.findJavaBinary(
final String? javaBinary = Java.find(
logger: _logger,
androidStudio: _androidStudio,
fileSystem: _fileSystem,
operatingSystemUtils: _operatingSystemUtils,
platform: _platform,
);
processManager: _processManager,
)?.binaryPath;
if (javaBinary == null) {
messages.add(ValidationMessage.error(_userMessages.androidMissingJdk));
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
......@@ -266,18 +262,18 @@ class AndroidValidator extends DoctorValidator {
/// SDK have been accepted.
class AndroidLicenseValidator extends DoctorValidator {
AndroidLicenseValidator({
required Java? java,
required AndroidSdk? androidSdk,
required Platform platform,
required OperatingSystemUtils operatingSystemUtils,
required FileSystem fileSystem,
required ProcessManager processManager,
required Logger logger,
required AndroidStudio? androidStudio,
required Stdio stdio,
required UserMessages userMessages,
}) : _androidSdk = androidSdk,
}) : _java = java,
_androidSdk = androidSdk,
_platform = platform,
_operatingSystemUtils = operatingSystemUtils,
_fileSystem = fileSystem,
_processManager = processManager,
_logger = logger,
......@@ -286,10 +282,10 @@ class AndroidLicenseValidator extends DoctorValidator {
_userMessages = userMessages,
super('Android license subvalidator');
final Java? _java;
final AndroidSdk? _androidSdk;
final AndroidStudio? _androidStudio;
final Stdio _stdio;
final OperatingSystemUtils _operatingSystemUtils;
final Platform _platform;
final FileSystem _fileSystem;
final ProcessManager _processManager;
......@@ -330,12 +326,13 @@ class AndroidLicenseValidator extends DoctorValidator {
}
Future<bool> _checkJavaVersionNoOutput() async {
final String? javaBinary = AndroidSdk.findJavaBinary(
final String? javaBinary = Java.find(
logger: _logger,
androidStudio: _androidStudio,
fileSystem: _fileSystem,
operatingSystemUtils: _operatingSystemUtils,
platform: _platform,
);
processManager: _processManager,
)?.binaryPath;
if (javaBinary == null) {
return false;
}
......@@ -387,7 +384,7 @@ class AndroidLicenseValidator extends DoctorValidator {
try {
final Process process = await _processManager.start(
<String>[_androidSdk!.sdkManagerPath!, '--licenses'],
environment: _androidSdk!.sdkManagerEnv,
environment: _java?.environment,
);
process.stdin.write('n\n');
// We expect logcat streams to occasionally contain invalid utf-8,
......@@ -427,7 +424,7 @@ class AndroidLicenseValidator extends DoctorValidator {
try {
final Process process = await _processManager.start(
<String>[_androidSdk!.sdkManagerPath!, '--licenses'],
environment: _androidSdk!.sdkManagerEnv,
environment: _java?.environment,
);
// The real stdin will never finish streaming. Pipe until the child process
......
......@@ -147,8 +147,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
_gradleUtils = gradleUtils,
_androidStudio = androidStudio,
_fileSystemUtils = FileSystemUtils(fileSystem: fileSystem, platform: platform),
_processUtils = ProcessUtils(logger: logger, processManager: processManager),
_platform = platform;
_processUtils = ProcessUtils(logger: logger, processManager: processManager);
final Logger _logger;
final ProcessUtils _processUtils;
......@@ -158,7 +157,6 @@ class AndroidGradleBuilder implements AndroidBuilder {
final GradleUtils _gradleUtils;
final FileSystemUtils _fileSystemUtils;
final AndroidStudio? _androidStudio;
final Platform _platform;
/// Builds the AAR and POM files for the current Flutter module or plugin.
@override
......@@ -268,11 +266,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
AndroidStudioJavaGradleConflictMigration(_logger,
project: project.android,
androidStudio: _androidStudio,
fileSystem: _fileSystem,
processUtils: _processUtils,
platform: _platform,
os: globals.os,
androidSdk: globals.androidSdk)
java: globals.java)
,
];
......@@ -428,7 +422,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
..start();
int exitCode = 1;
try {
final String? javaHome = globals.androidSdk?.javaHome;
final String? javaHome = globals.java?.javaHome;
exitCode = await _processUtils.stream(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
......@@ -698,7 +692,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
..start();
RunResult result;
try {
final String? javaHome = globals.androidSdk?.javaHome;
final String? javaHome = globals.java?.javaHome;
result = await _processUtils.run(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
......@@ -751,7 +745,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
..start();
RunResult result;
try {
final String? javaHome = globals.androidSdk?.javaHome;
final String? javaHome = globals.java?.javaHome;
result = await _processUtils.run(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:process/process.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/process.dart';
import 'android_studio.dart';
const String _javaHomeEnvironmentVariable = 'JAVA_HOME';
const String _kJavaExecutable = 'java';
/// Represents an installation of Java.
class Java {
Java({
required this.javaHome,
required this.binaryPath,
required Logger logger,
required FileSystem fileSystem,
required OperatingSystemUtils os,
required Platform platform,
required ProcessManager processManager,
}): _logger = logger,
_fileSystem = fileSystem,
_os = os,
_platform = platform,
_processManager = processManager,
_processUtils = ProcessUtils(processManager: processManager, logger: logger);
/// Finds the Java runtime environment that should be used for all java-dependent
/// operations across the tool.
///
/// This searches for Java in the following places, in order:
///
/// 1. the runtime environment bundled with Android Studio;
/// 2. the runtime environment found in the JAVA_HOME env variable, if set; or
/// 3. the java binary found on PATH.
///
/// Returns null if no java binary could be found.
// TODO(andrewkolos): To prevent confusion when debugging Android-related
// issues (see https://github.com/flutter/flutter/issues/122609 for an example),
// this logic should be consistently followed by any Java-dependent operation
// across the the tool (building Android apps, interacting with the Android SDK, etc.).
// Currently, this consistency is fragile since the logic used for building
// Android apps exists independently of this method.
// See https://github.com/flutter/flutter/issues/124252.
static Java? find({
required AndroidStudio? androidStudio,
required Logger logger,
required FileSystem fileSystem,
required Platform platform,
required ProcessManager processManager,
}) {
final OperatingSystemUtils os = OperatingSystemUtils(
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager
);
final String? home = _findJavaHome(
logger: logger,
androidStudio: androidStudio,
platform: platform
);
final String? binary = _findJavaBinary(
logger: logger,
javaHome: home,
fileSystem: fileSystem,
operatingSystemUtils: os,
platform: platform
);
if (binary == null) {
return null;
}
return Java(
javaHome: home,
binaryPath: binary,
logger: logger,
fileSystem: fileSystem,
os: os,
platform: platform,
processManager: processManager,
);
}
/// The path of the runtime's home directory.
///
/// This should only be used for logging and validation purposes.
/// If you need to set JAVA_HOME when starting a process, consider
/// using [environment] instead.
/// If you need to inspect the files of the runtime, considering adding
/// a new method to this class instead.
final String? javaHome;
/// The path of the runtime's java binary.
///
/// This should be only used for logging and validation purposes.
/// If you need to invoke the binary directly, consider adding a new method
/// to this class instead.
final String binaryPath;
final Logger _logger;
final FileSystem _fileSystem;
final OperatingSystemUtils _os;
final Platform _platform;
final ProcessManager _processManager;
final ProcessUtils _processUtils;
/// Returns an environment variable map with
/// 1. JAVA_HOME set if this object has a known home directory, and
/// 2. The java binary folder appended onto PATH, if the binary location is known.
///
/// This map should be used as the environment when invoking any Java-dependent
/// processes, such as Gradle or Android SDK tools (avdmanager, sdkmanager, etc.)
Map<String, String> get environment {
return <String, String>{
if (javaHome != null) _javaHomeEnvironmentVariable: javaHome!,
'PATH': _fileSystem.path.dirname(binaryPath) +
_os.pathVarSeparator +
_platform.environment['PATH']!,
};
}
/// Returns the version of java in the format \d(.\d)+(.\d)+
/// Returns null if version could not be determined.
late final JavaVersion? version = (() {
final RunResult result = _processUtils.runSync(
<String>[binaryPath, '--version'],
environment: environment,
);
if (result.exitCode != 0) {
_logger.printTrace('java --version failed: exitCode: ${result.exitCode}'
' stdout: ${result.stdout} stderr: ${result.stderr}');
}
return JavaVersion.tryParseFromJavaOutput(result.stdout, logger: _logger);
})();
bool canRun() {
return _processManager.canRun(binaryPath);
}
}
String? _findJavaHome({
required Logger logger,
required AndroidStudio? androidStudio,
required Platform platform,
}) {
final String? androidStudioJavaPath = androidStudio?.javaPath;
if (androidStudioJavaPath != null) {
return androidStudioJavaPath;
}
final String? javaHomeEnv = platform.environment[_javaHomeEnvironmentVariable];
if (javaHomeEnv != null) {
return javaHomeEnv;
}
return null;
}
String? _findJavaBinary({
required Logger logger,
required String? javaHome,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
}) {
if (javaHome != null) {
return fileSystem.path.join(javaHome, 'bin', 'java');
}
// Fallback to PATH based lookup.
return operatingSystemUtils.which(_kJavaExecutable)?.path;
}
// Returns a user visible String that says the tool failed to parse
// the version of java along with the output.
String _formatJavaVersionWarning(String javaVersionRaw) {
return 'Could not parse java version from: \n'
'$javaVersionRaw \n'
'If there is a version please look for an existing bug '
'https://github.com/flutter/flutter/issues/ '
'and if one does not exist file a new issue.';
}
class JavaVersion {
JavaVersion({
required this.longText,
required this.number
});
/// Typically the first line of the output from `java --version`.
/// For example, `"openjdk 19.0.2 2023-01-17"`.
final String longText;
/// The version number. For example, `"19.0.2."`.
final String number;
/// Extracts JDK version from the output of java --version.
static JavaVersion? tryParseFromJavaOutput(String rawVersionOutput, {
required Logger logger,
}) {
final List<String> versionLines = rawVersionOutput.split('\n');
final String longText = versionLines.length >= 2 ? versionLines[1] : versionLines[0];
// The contents that matter come in the format '11.0.18' or '1.8.0_202'.
final RegExp jdkVersionRegex = RegExp(r'\d+\.\d+(\.\d+(?:_\d+)?)?');
final Iterable<RegExpMatch> matches =
jdkVersionRegex.allMatches(rawVersionOutput);
if (matches.isEmpty) {
logger.printWarning(_formatJavaVersionWarning(rawVersionOutput));
return null;
}
final String? rawShortText = matches.first.group(0);
if (rawShortText == null || rawShortText.split('_').isEmpty) {
logger.printWarning(_formatJavaVersionWarning(rawVersionOutput));
return null;
}
// Trim away _d+ from versions 1.8 and below.
final String shortText = rawShortText.split('_').first;
return JavaVersion(longText: longText, number: shortText);
}
}
......@@ -5,15 +5,12 @@
import 'package:meta/meta.dart';
import '../../base/file_system.dart';
import '../../base/os.dart';
import '../../base/platform.dart';
import '../../base/process.dart';
import '../../base/project_migrator.dart';
import '../../base/version.dart';
import '../../project.dart';
import '../android_sdk.dart';
import '../android_studio.dart';
import '../gradle_utils.dart';
import '../java.dart';
// Android Studio 2022.2 "Flamingo" is the first to bundle a Java 17 JDK.
// Previous versions bundled a Java 11 JDK.
......@@ -77,25 +74,14 @@ class AndroidStudioJavaGradleConflictMigration extends ProjectMigrator {
super.logger,
{required AndroidProject project,
AndroidStudio? androidStudio,
required FileSystem fileSystem,
required ProcessUtils processUtils,
required Platform platform,
required OperatingSystemUtils os,
AndroidSdk? androidSdk,
required Java? java,
}) : _gradleWrapperPropertiesFile = getGradleWrapperFile(project.hostAppGradleRoot),
_androidStudio = androidStudio,
_fileSystem = fileSystem,
_processUtils = processUtils,
_platform = platform,
_os = os,
_androidSdk = androidSdk;
_java = java;
final File _gradleWrapperPropertiesFile;
final AndroidStudio? _androidStudio;
final FileSystem _fileSystem;
final ProcessUtils _processUtils;
final Platform _platform;
final OperatingSystemUtils _os;
final AndroidSdk? _androidSdk;
final Java? _java;
@override
void migrate() {
......@@ -113,13 +99,7 @@ class AndroidStudioJavaGradleConflictMigration extends ProjectMigrator {
return;
}
final String? javaVersionString = _androidSdk?.getJavaVersion(
androidStudio: _androidStudio,
fileSystem: _fileSystem,
operatingSystemUtils: _os,
platform: _platform,
processUtils: _processUtils,
);
final String? javaVersionString = _java?.version?.number;
final Version? javaVersion = Version.parse(javaVersionString);
if (javaVersion == null) {
logger.printTrace(javaVersionNotFound);
......
......@@ -1360,6 +1360,7 @@ class EmulatorDomain extends Domain {
EmulatorManager emulators = EmulatorManager(
fileSystem: globals.fs,
logger: globals.logger,
java: globals.java,
androidSdk: globals.androidSdk,
processManager: globals.processManager,
androidWorkflow: androidWorkflow!,
......
......@@ -14,6 +14,7 @@ import 'android/android_studio.dart';
import 'android/android_workflow.dart';
import 'android/gradle.dart';
import 'android/gradle_utils.dart';
import 'android/java.dart';
import 'application_package.dart';
import 'artifacts.dart';
import 'asset.dart';
......@@ -97,11 +98,11 @@ Future<T> runInContext<T>(
androidStudio: globals.androidStudio,
),
AndroidLicenseValidator: () => AndroidLicenseValidator(
operatingSystemUtils: globals.os,
platform: globals.platform,
userMessages: globals.userMessages,
processManager: globals.processManager,
androidStudio: globals.androidStudio,
java: globals.java,
androidSdk: globals.androidSdk,
logger: globals.logger,
fileSystem: globals.fs,
......@@ -216,6 +217,7 @@ Future<T> runInContext<T>(
Doctor: () => Doctor(logger: globals.logger),
DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance,
EmulatorManager: () => EmulatorManager(
java: globals.java,
androidSdk: globals.androidSdk,
processManager: globals.processManager,
logger: globals.logger,
......@@ -253,6 +255,13 @@ Future<T> runInContext<T>(
xcode: globals.xcode!,
platform: globals.platform,
),
Java: () => Java.find(
androidStudio: globals.androidStudio,
logger: globals.logger,
fileSystem: globals.fs,
platform: globals.platform,
processManager: globals.processManager
),
LocalEngineLocator: () => LocalEngineLocator(
userMessages: userMessages,
logger: globals.logger,
......
......@@ -10,6 +10,7 @@ import 'package:process/process.dart';
import 'android/android_emulator.dart';
import 'android/android_sdk.dart';
import 'android/android_workflow.dart';
import 'android/java.dart';
import 'base/context.dart';
import 'base/file_system.dart';
import 'base/logger.dart';
......@@ -22,12 +23,14 @@ EmulatorManager? get emulatorManager => context.get<EmulatorManager>();
/// A class to get all available emulators.
class EmulatorManager {
EmulatorManager({
required Java? java,
AndroidSdk? androidSdk,
required Logger logger,
required ProcessManager processManager,
required AndroidWorkflow androidWorkflow,
required FileSystem fileSystem,
}) : _androidSdk = androidSdk,
}) : _java = java,
_androidSdk = androidSdk,
_processUtils = ProcessUtils(logger: logger, processManager: processManager),
_androidEmulators = AndroidEmulators(
androidSdk: androidSdk,
......@@ -39,6 +42,7 @@ class EmulatorManager {
_emulatorDiscoverers.add(_androidEmulators);
}
final Java? _java;
final AndroidSdk? _androidSdk;
final AndroidEmulators _androidEmulators;
final ProcessUtils _processUtils;
......@@ -152,7 +156,7 @@ class EmulatorManager {
'-n', emulatorName,
'-k', sdkId,
'-d', device,
], environment: _androidSdk?.sdkManagerEnv,
], environment: _java?.environment,
);
return CreateEmulatorResult(
emulatorName,
......@@ -175,7 +179,7 @@ class EmulatorManager {
'-c',
];
final RunResult runResult = await _processUtils.run(args,
environment: _androidSdk?.sdkManagerEnv);
environment: _java?.environment);
if (runResult.exitCode != 0) {
return null;
}
......@@ -205,7 +209,7 @@ class EmulatorManager {
'-n', 'temp',
];
final RunResult runResult = await _processUtils.run(args,
environment: _androidSdk?.sdkManagerEnv);
environment: _java?.environment);
// Get the list of IDs that match our criteria
final List<String> availableIDs = runResult.stderr
......
......@@ -9,6 +9,7 @@ import 'package:unified_analytics/unified_analytics.dart';
import 'android/android_sdk.dart';
import 'android/android_studio.dart';
import 'android/gradle_utils.dart';
import 'android/java.dart';
import 'artifacts.dart';
import 'base/bot_detector.dart';
import 'base/config.dart';
......@@ -308,3 +309,9 @@ final RegExp kVMServiceMessageRegExp = RegExp(r'The Dart VM service is listening
// The official tool no longer allows non-null safe builds. This can be
// overridden in other clients.
NonNullSafeBuilds get nonNullSafeBuilds => context.get<NonNullSafeBuilds>() ?? NonNullSafeBuilds.notAllowed;
/// Contains information about the JRE/JDK to use for Java-dependent operations.
///
/// A value of [null] indicates that no installation of java could be found on
/// the host machine.
Java? get java => context.get<Java>();
......@@ -568,13 +568,7 @@ class AndroidProject extends FlutterProjectPlatform {
hostAppGradleRoot, globals.logger, globals.processManager);
final String? agpVersion =
gradle.getAgpVersion(hostAppGradleRoot, globals.logger);
final String? javaVersion = globals.androidSdk?.getJavaVersion(
androidStudio: globals.androidStudio,
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
platform: globals.platform,
processUtils: globals.processUtils,
);
final String? javaVersion = globals.java?.version?.number;
// Assume valid configuration.
String description = validJavaGradleAgpString;
......
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/java.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
......@@ -20,6 +21,7 @@ import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fake_devices.dart';
import '../../src/fakes.dart';
void main() {
Daemon? daemon;
......@@ -104,6 +106,8 @@ void main() {
final bool supportsRuntimeMode = await device.supportsRuntimeMode(BuildMode.release);
expect(fakeDevice.supportsRuntimeModeCalledBuildMode, BuildMode.release);
expect(supportsRuntimeMode, true);
}, overrides: <Type, Generator>{
Java: () => FakeJava(),
});
testUsingContext('redirects logs', () async {
......@@ -183,6 +187,7 @@ void main() {
expect(fakeDevice.stopAppPackage, applicationPackage);
expect(stopAppResult, true);
}, overrides: <Type, Generator>{
Java: () => FakeJava(),
ApplicationPackageFactory: () => applicationPackageFactory,
FileSystem: () => memoryFileSystem,
ProcessManager: () => fakeProcessManager,
......@@ -212,6 +217,7 @@ void main() {
expect(await screenshotOutputFile.readAsBytes(), screenshot);
}, overrides: <Type, Generator>{
Java: () => FakeJava(),
FileSystem: () => memoryFileSystem,
ProcessManager: () => fakeProcessManager,
});
......
......@@ -447,9 +447,6 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
@override
final Directory directory;
@override
String? get javaHome => 'java';
}
class FakeAndroidStudio extends Fake implements AndroidStudio {
......
......@@ -230,7 +230,4 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
@override
final Directory directory;
@override
String? get javaHome => 'java';
}
......@@ -389,8 +389,6 @@ class FakeDoctor extends Fake implements Doctor {
}
}
class FakeAndroidStudio extends Fake implements AndroidStudio {}
class FakeClock extends Fake implements SystemClock {
List<int> times = <int>[];
......
......@@ -4,9 +4,9 @@
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/android/gradle_utils.dart';
import 'package:flutter_tools/src/android/java.dart';
import 'package:flutter_tools/src/android/migrations/android_studio_java_gradle_conflict_migration.dart';
import 'package:flutter_tools/src/android/migrations/top_level_gradle_build_file_migration.dart';
import 'package:flutter_tools/src/base/logger.dart';
......@@ -19,6 +19,7 @@ import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
const String otherGradleVersionWrapper = r'''
distributionBase=GRADLE_USER_HOME
......@@ -46,6 +47,9 @@ zipStorePath=wrapper/dists
final Version androidStudioDolphin = Version(2021, 3, 1);
final JavaVersion _javaVersion17 = JavaVersion(longText: 'openjdk 17.0.2', number: '17.0.2');
final JavaVersion _javaVersion16 = JavaVersion(longText: 'openjdk 16.0.2', number: '16.0.2');
void main() {
group('Android migration', () {
group('migrate the Gradle "clean" task to lazy declaration', () {
......@@ -136,14 +140,10 @@ tasks.register("clean", Delete) {
testWithoutContext('skipped if files are missing', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeJava(version: _javaVersion17),
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioDolphin),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
migration.migrate();
expect(gradleWrapperPropertiesFile.existsSync(), isFalse);
......@@ -153,13 +153,9 @@ tasks.register("clean", Delete) {
testWithoutContext('skipped if android studio is null', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeJava(version: _javaVersion17),
bufferLogger,
project: project,
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
......@@ -170,14 +166,10 @@ tasks.register("clean", Delete) {
testWithoutContext('skipped if android studio version is null', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeJava(version: _javaVersion17),
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: null),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
......@@ -188,14 +180,10 @@ tasks.register("clean", Delete) {
testWithoutContext('skipped if error is encountered in migrate()', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeErroringJava(),
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeErroringAndroidSdk(),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
......@@ -206,14 +194,10 @@ tasks.register("clean", Delete) {
testWithoutContext('skipped if android studio version is less than flamingo', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeJava(),
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioDolphin),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
......@@ -223,14 +207,10 @@ tasks.register("clean", Delete) {
testWithoutContext('skipped if bundled java version is less than 17', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeJava(version: _javaVersion16),
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '16'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
......@@ -241,14 +221,10 @@ tasks.register("clean", Delete) {
testWithoutContext('nothing is changed if gradle version not one that was '
'used by flutter create', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeJava(version: _javaVersion17),
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(otherGradleVersionWrapper);
migration.migrate();
......@@ -259,14 +235,10 @@ tasks.register("clean", Delete) {
testWithoutContext('change is made with one of the specific gradle versions'
' we migrate for', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeJava(version: _javaVersion17),
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate);
migration.migrate();
......@@ -278,14 +250,10 @@ tasks.register("clean", Delete) {
testWithoutContext('change is not made when opt out flag is set', () {
final AndroidStudioJavaGradleConflictMigration migration = AndroidStudioJavaGradleConflictMigration(
java: FakeJava(version: _javaVersion17),
bufferLogger,
project: project,
androidStudio: FakeAndroidStudio(version: androidStudioFlamingo),
fileSystem: FakeFileSystem(),
processUtils: FakeProcessUtils(),
platform: FakePlatform(),
os: FakeOperatingSystemUtils(),
androidSdk: FakeAndroidSdk(javaVersion: '17'),
);
gradleWrapperPropertiesFile.writeAsStringSync(gradleWrapperToMigrate + optOutFlag);
migration.migrate();
......@@ -314,37 +282,10 @@ class FakeAndroidStudio extends Fake implements AndroidStudio {
Version? get version => _version;
}
class FakeAndroidSdk extends Fake implements AndroidSdk {
FakeAndroidSdk({required String javaVersion}) {
_javaVersion = javaVersion;
}
late String _javaVersion;
@override
String? getJavaVersion({
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
required ProcessUtils processUtils,
}) {
return _javaVersion;
}
}
class FakeErroringAndroidSdk extends Fake implements AndroidSdk {
FakeErroringAndroidSdk();
class FakeErroringJava extends FakeJava {
@override
String? getJavaVersion({
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
required ProcessUtils processUtils,
}) {
throw const FileSystemException();
JavaVersion get version {
throw Exception('How did this happen?');
}
}
......
......@@ -5,19 +5,16 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/android/java.dart';
import 'package:flutter_tools/src/base/config.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/base/process.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:test/fake.dart';
import '../../integration.shard/test_utils.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart' show FakeAndroidStudio, FakeOperatingSystemUtils;
void main() {
late MemoryFileSystem fileSystem;
......@@ -354,18 +351,18 @@ void main() {
final String androidStudioBundledJdkHome = globals.androidStudio!.javaPath!;
final String expectedJavaBinaryPath = globals.fs.path.join(androidStudioBundledJdkHome, 'bin', 'java');
final String? foundJavaBinaryPath = AndroidSdk.findJavaBinary(
final String? foundJavaBinaryPath = Java.find(
logger: globals.logger,
androidStudio: globals.androidStudio,
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
platform: globals.platform,
);
processManager: globals.processManager,
)?.binaryPath;
expect(foundJavaBinaryPath, expectedJavaBinaryPath);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
OperatingSystemUtils: () => FakeOperatingSystemUtilsWithJava(),
Platform: () => FakePlatform(),
Config: () => Config,
AndroidStudio: () => FakeAndroidStudioWithJdk(),
......@@ -374,18 +371,18 @@ void main() {
testUsingContext('returns the current value of JAVA_HOME if it is set and the JDK bundled with Android Studio could not be found', () {
final String expectedJavaBinaryPath = globals.fs.path.join('java-home-path', 'bin', 'java');
final String? foundJavaBinaryPath = AndroidSdk.findJavaBinary(
final String? foundJavaBinaryPath = Java.find(
logger: globals.logger,
androidStudio: globals.androidStudio,
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
platform: globals.platform,
);
processManager: globals.processManager,
)?.binaryPath;
expect(foundJavaBinaryPath, expectedJavaBinaryPath);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
OperatingSystemUtils: () => FakeOperatingSystemUtilsWithJava(),
ProcessManager: () => FakeProcessManager.empty(),
Platform: () => FakePlatform(environment: <String, String>{
AndroidSdk.javaHomeEnvironmentVariable: 'java-home-path',
}),
......@@ -394,168 +391,54 @@ void main() {
});
testUsingContext('returns the java binary found on PATH if no other can be found', () {
final String? foundJavaBinaryPath = AndroidSdk.findJavaBinary(
final String? foundJavaBinaryPath = Java.find(
logger: globals.logger,
androidStudio: globals.androidStudio,
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
platform: globals.platform,
);
processManager: globals.processManager,
)?.binaryPath;
expect(foundJavaBinaryPath, globals.os.which('java')!.path);
expect(foundJavaBinaryPath, 'java');
}, overrides: <Type, Generator>{
Logger: () => BufferLogger.test(),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
OperatingSystemUtils: () => FakeOperatingSystemUtilsWithJava(),
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['which', 'java'],
stdout: 'java',
),
]),
Platform: () => FakePlatform(),
Config: () => Config,
AndroidStudio: () => FakeAndroidStudioWithoutJdk(),
});
testUsingContext('returns null if no java binary could be found', () {
final String? foundJavaBinaryPath = AndroidSdk.findJavaBinary(
final String? foundJavaBinaryPath = Java.find(
logger: globals.logger,
androidStudio: globals.androidStudio,
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
platform: globals.platform,
);
processManager: globals.processManager,
)?.binaryPath;
expect(foundJavaBinaryPath, null);
}, overrides: <Type, Generator>{
Logger: () => BufferLogger.test(),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
OperatingSystemUtils: () => FakeOperatingSystemUtilsWithoutJava(),
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['which', 'java'],
exitCode: 1,
),
]),
Platform: () => FakePlatform(),
Config: () => Config,
AndroidStudio: () => FakeAndroidStudioWithoutJdk(),
});
});
});
group('java version', () {
const String exampleJdk8Output = '''
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b10, mixed mode)
''';
// Example strings came from actual terminal output.
testWithoutContext('parses jdk 8', () {
expect(AndroidSdk.parseJavaVersion(exampleJdk8Output), '1.8.0');
});
testWithoutContext('parses jdk 11 windows', () {
const String exampleJdkOutput = '''
java version "11.0.14"
Java(TM) SE Runtime Environment (build 11.0.14+10-b13)
Java HotSpot(TM) 64-Bit Server VM (build 11.0.14+10-b13, mixed mode)
''';
expect(AndroidSdk.parseJavaVersion(exampleJdkOutput), '11.0.14');
});
testWithoutContext('parses jdk 11 mac/linux', () {
const String exampleJdkOutput = '''
openjdk version "11.0.18" 2023-01-17 LTS
OpenJDK Runtime Environment Zulu11.62+17-CA (build 11.0.18+10-LTS)
OpenJDK 64-Bit Server VM Zulu11.62+17-CA (build 11.0.18+10-LTS, mixed mode)
''';
expect(AndroidSdk.parseJavaVersion(exampleJdkOutput), '11.0.18');
});
testWithoutContext('parses jdk 17', () {
const String exampleJdkOutput = '''
openjdk 17.0.6 2023-01-17
OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
OpenJDK 64-Bit Server VM (build 17.0.6+0-17.0.6b802.4-9586694, mixed mode)
''';
expect(AndroidSdk.parseJavaVersion(exampleJdkOutput), '17.0.6');
});
testWithoutContext('parses jdk 19', () {
const String exampleJdkOutput = '''
openjdk 19.0.2 2023-01-17
OpenJDK Runtime Environment Homebrew (build 19.0.2)
OpenJDK 64-Bit Server VM Homebrew (build 19.0.2, mixed mode, sharing)
''';
expect(AndroidSdk.parseJavaVersion(exampleJdkOutput), '19.0.2');
});
// https://chrome-infra-packages.appspot.com/p/flutter/java/openjdk/
testWithoutContext('parses jdk output from ci', () {
const String exampleJdkOutput = '''
openjdk 11.0.2 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
''';
expect(AndroidSdk.parseJavaVersion(exampleJdkOutput), '11.0.2');
});
testWithoutContext('parses jdk two number versions', () {
const String exampleJdkOutput = 'openjdk 19.0 2023-01-17';
expect(AndroidSdk.parseJavaVersion(exampleJdkOutput), '19.0');
});
testUsingContext('getJavaBinary with AS install', () {
final Directory sdkDir = createSdkDirectory(fileSystem: fileSystem);
config.setValue('android-sdk', sdkDir.path);
final AndroidStudio androidStudio = FakeAndroidStudio();
final String javaPath = AndroidSdk.findJavaBinary(
androidStudio: androidStudio,
fileSystem: fileSystem,
operatingSystemUtils: FakeOperatingSystemUtils(),
platform: platform)!;
// Built from the implementation of findJavaBinary android studio case.
final String expectedJavaPath = '${androidStudio.javaPath}/bin/java';
expect(javaPath, expectedJavaPath);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Config: () => config,
Platform: () => FakePlatform(environment: <String, String>{}),
});
group('java', () {
late AndroidStudio androidStudio;
setUp(() {
androidStudio = FakeAndroidStudio();
});
testUsingContext('getJavaVersion finds AS java and parses version', () {
final Directory sdkDir = createSdkDirectory(fileSystem: fileSystem);
config.setValue('android-sdk', sdkDir.path);
final ProcessUtils processUtils = ProcessUtils(
processManager: processManager, logger: BufferLogger.test());
// Built from the implementation of findJavaBinary android studio case.
final String expectedJavaPath = '${androidStudio.javaPath}/bin/java';
processManager.addCommand(FakeCommand(
command: <String>[
expectedJavaPath,
'--version',
],
stdout: exampleJdk8Output,
));
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk()!;
final String? javaVersion = sdk.getJavaVersion(
androidStudio: androidStudio,
fileSystem: fileSystem,
operatingSystemUtils: FakeOperatingSystemUtils(),
platform: FakePlatform(),
processUtils: processUtils,
);
expect(javaVersion, '1.8.0');
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
AndroidStudio: () => androidStudio,
Config: () => config,
Platform: () => FakePlatform(environment: <String, String>{}),
});
});
});
}
/// A broken SDK installation.
......@@ -646,20 +529,3 @@ class FakeAndroidStudioWithoutJdk extends Fake implements AndroidStudio {
@override
String? get javaPath => null;
}
class FakeOperatingSystemUtilsWithJava extends Fake implements OperatingSystemUtils {
@override
File? which(String execName) {
if (execName == 'java') {
return globals.fs.file('/fake/which/java/path');
}
return null;
}
}
class FakeOperatingSystemUtilsWithoutJava extends Fake implements OperatingSystemUtils {
@override
File? which(String execName) {
return null;
}
}
......@@ -122,6 +122,7 @@ void main() {
sdk.sdkManagerPath = '/foo/bar/sdkmanager';
processManager.excludedExecutables.add('/foo/bar/sdkmanager');
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -130,7 +131,6 @@ void main() {
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
......@@ -141,6 +141,7 @@ void main() {
sdk.sdkManagerPath = '/foo/bar/sdkmanager';
processManager.excludedExecutables.add('/foo/bar/sdkmanager');
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -149,7 +150,6 @@ void main() {
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
......@@ -165,6 +165,7 @@ void main() {
], stdout: 'asdasassad',
));
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -173,7 +174,6 @@ void main() {
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final LicensesAccepted result = await licenseValidator.licensesAccepted;
......@@ -194,6 +194,7 @@ All SDK package licenses accepted.
));
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -202,7 +203,6 @@ All SDK package licenses accepted.
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final LicensesAccepted result = await licenseValidator.licensesAccepted;
......@@ -224,6 +224,7 @@ Review licenses that have not been accepted (y/N)?
));
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -232,7 +233,6 @@ Review licenses that have not been accepted (y/N)?
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final LicensesAccepted result = await licenseValidator.licensesAccepted;
......@@ -254,6 +254,7 @@ Review licenses that have not been accepted (y/N)?
));
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -262,7 +263,6 @@ Review licenses that have not been accepted (y/N)?
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final LicensesAccepted result = await licenseValidator.licensesAccepted;
......@@ -280,6 +280,7 @@ Review licenses that have not been accepted (y/N)?
));
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -288,7 +289,6 @@ Review licenses that have not been accepted (y/N)?
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
expect(await licenseValidator.runLicenseManager(), isTrue);
......@@ -299,6 +299,7 @@ Review licenses that have not been accepted (y/N)?
processManager.excludedExecutables.add('/foo/bar/sdkmanager');
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -307,7 +308,6 @@ Review licenses that have not been accepted (y/N)?
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
expect(licenseValidator.runLicenseManager(), throwsToolExit());
......@@ -328,6 +328,7 @@ Review licenses that have not been accepted (y/N)?
final BufferLogger logger = BufferLogger.test();
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -336,7 +337,6 @@ Review licenses that have not been accepted (y/N)?
logger: logger,
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
await licenseValidator.runLicenseManager();
......@@ -349,6 +349,7 @@ Review licenses that have not been accepted (y/N)?
processManager.excludedExecutables.add('/foo/bar/sdkmanager');
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -357,7 +358,6 @@ Review licenses that have not been accepted (y/N)?
logger: BufferLogger.test(),
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
expect(licenseValidator.runLicenseManager(), throwsToolExit());
......@@ -376,6 +376,7 @@ Review licenses that have not been accepted (y/N)?
);
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
processManager: processManager,
......@@ -384,7 +385,6 @@ Review licenses that have not been accepted (y/N)?
logger: logger,
userMessages: UserMessages(),
androidStudio: FakeAndroidStudio(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
await expectLater(
......@@ -620,9 +620,6 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
@override
List<String> validateSdkWellFormed() => <String>[];
@override
Map<String, String> get sdkManagerEnv => <String, String>{};
}
class FakeAndroidSdkVersion extends Fake implements AndroidSdkVersion {
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/android/java.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:test/fake.dart';
import 'package:webdriver/async_io.dart';
import '../../integration.shard/test_utils.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
void main() {
late Logger logger;
late FileSystem fs;
late Platform platform;
late FakeProcessManager processManager;
setUp(() {
logger = BufferLogger.test();
fs = MemoryFileSystem.test();
platform = FakePlatform(environment: <String, String>{
'PATH': '',
});
processManager = FakeProcessManager.empty();
});
group(Java, () {
group('find', () {
testWithoutContext('finds the JDK bundled with Android Studio, if it exists', () {
final AndroidStudio androidStudio = _FakeAndroidStudioWithJdk();
final String androidStudioBundledJdkHome = androidStudio.javaPath!;
final String expectedJavaBinaryPath = fs.path.join(androidStudioBundledJdkHome, 'bin', 'java');
processManager.addCommand(FakeCommand(
command: <String>[
expectedJavaBinaryPath,
'--version',
],
stdout: '''
openjdk 19.0.2 2023-01-17
OpenJDK Runtime Environment Zulu19.32+15-CA (build 19.0.2+7)
OpenJDK 64-Bit Server VM Zulu19.32+15-CA (build 19.0.2+7, mixed mode, sharing)
'''
));
final Java java = Java.find(
androidStudio: androidStudio,
logger: logger,
fileSystem: fs,
platform: platform,
processManager: processManager,
)!;
final JavaVersion version = java.version!;
expect(java.javaHome, androidStudioBundledJdkHome);
expect(java.binaryPath, expectedJavaBinaryPath);
expect(version.longText, 'OpenJDK Runtime Environment Zulu19.32+15-CA (build 19.0.2+7)');
expect(version.number, '19.0.2');
});
testWithoutContext('finds JAVA_HOME if it is set and the JDK bundled with Android Studio could not be found', () {
final AndroidStudio androidStudio = _FakeAndroidStudioWithoutJdk();
const String javaHome = '/java/home';
final String expectedJavaBinaryPath = fs.path.join(javaHome, 'bin', 'java');
final Java java = Java.find(
androidStudio: androidStudio,
logger: logger,
fileSystem: fs,
platform: FakePlatform(environment: <String, String>{
'JAVA_HOME': javaHome,
}),
processManager: processManager,
)!;
expect(java.javaHome, javaHome);
expect(java.binaryPath, expectedJavaBinaryPath);
});
testWithoutContext('returns the java binary found on PATH if no other can be found', () {
final AndroidStudio androidStudio = _FakeAndroidStudioWithoutJdk();
final OperatingSystemUtils os = _FakeOperatingSystemUtilsWithJava(fileSystem);
processManager.addCommand(
const FakeCommand(
command: <String>['which', 'java'],
stdout: '/fake/which/java/path',
),
);
final Java java = Java.find(
androidStudio: androidStudio,
logger: logger,
fileSystem: fs,
platform: platform,
processManager: processManager,
)!;
expect(java.binaryPath, os.which('java')!.path);
expect(java.javaHome, isNull);
});
testWithoutContext('returns null if no java could be found', () {
final AndroidStudio androidStudio = _FakeAndroidStudioWithoutJdk();
processManager.addCommand(
const FakeCommand(
command: <String>['which', 'java'],
exitCode: 1,
),
);
final Java? java = Java.find(
androidStudio: androidStudio,
logger: logger,
fileSystem: fs,
platform: platform,
processManager: processManager,
);
expect(java, isNull);
});
});
group('getVersionString', () {
late Java java;
setUp(() {
processManager = FakeProcessManager.empty();
java = Java(
fileSystem: fs,
logger: logger,
os: FakeOperatingSystemUtils(),
platform: platform,
processManager: processManager,
binaryPath: 'javaHome/bin/java',
javaHome: 'javaHome',
);
});
void addJavaVersionCommand(String output) {
processManager.addCommand(
FakeCommand(
command: <String>[java.binaryPath, '--version'],
stdout: output,
),
);
}
testWithoutContext('parses jdk 8', () {
addJavaVersionCommand('''
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b10, mixed mode)
''');
final JavaVersion version = java.version!;
expect(version.longText, 'Java(TM) SE Runtime Environment (build 1.8.0_202-b10)');
expect(version.number, '1.8.0');
});
testWithoutContext('parses jdk 11 windows', () {
addJavaVersionCommand('''
java version "11.0.14"
Java(TM) SE Runtime Environment (build 11.0.14+10-b13)
Java HotSpot(TM) 64-Bit Server VM (build 11.0.14+10-b13, mixed mode)
''');
final JavaVersion version = java.version!;
expect(version.longText, 'Java(TM) SE Runtime Environment (build 11.0.14+10-b13)');
expect(version.number, '11.0.14');
});
testWithoutContext('parses jdk 11 mac/linux', () {
addJavaVersionCommand('''
openjdk version "11.0.18" 2023-01-17 LTS
OpenJDK Runtime Environment Zulu11.62+17-CA (build 11.0.18+10-LTS)
OpenJDK 64-Bit Server VM Zulu11.62+17-CA (build 11.0.18+10-LTS, mixed mode)
''');
final JavaVersion version = java.version!;
expect(version.longText, 'OpenJDK Runtime Environment Zulu11.62+17-CA (build 11.0.18+10-LTS)');
expect(version.number, '11.0.18');
});
testWithoutContext('parses jdk 17', () {
addJavaVersionCommand('''
openjdk 17.0.6 2023-01-17
OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
OpenJDK 64-Bit Server VM (build 17.0.6+0-17.0.6b802.4-9586694, mixed mode)
''');
final JavaVersion version = java.version!;
expect(version.longText, 'OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)');
expect(version.number, '17.0.6');
});
testWithoutContext('parses jdk 19', () {
addJavaVersionCommand('''
openjdk 19.0.2 2023-01-17
OpenJDK Runtime Environment Homebrew (build 19.0.2)
OpenJDK 64-Bit Server VM Homebrew (build 19.0.2, mixed mode, sharing)
''');
final JavaVersion version = java.version!;
expect(version.longText, 'OpenJDK Runtime Environment Homebrew (build 19.0.2)');
expect(version.number, '19.0.2');
});
// https://chrome-infra-packages.appspot.com/p/flutter/java/openjdk/
testWithoutContext('parses jdk output from ci', () {
addJavaVersionCommand('''
openjdk 11.0.2 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
''');
final JavaVersion version = java.version!;
expect(version.longText, 'OpenJDK Runtime Environment 18.9 (build 11.0.2+9)');
expect(version.number, '11.0.2');
});
testWithoutContext('parses jdk two number versions', () {
addJavaVersionCommand('openjdk 19.0 2023-01-17');
final JavaVersion version = java.version!;
expect(version.longText, 'openjdk 19.0 2023-01-17');
expect(version.number, '19.0');
});
});
});
}
class _FakeAndroidStudioWithJdk extends Fake implements AndroidStudio {
@override
String? get javaPath => '/fake/android_studio/java/path/';
}
class _FakeAndroidStudioWithoutJdk extends Fake implements AndroidStudio {
@override
String? get javaPath => null;
}
class _FakeOperatingSystemUtilsWithJava extends FakeOperatingSystemUtils {
_FakeOperatingSystemUtilsWithJava(this._fileSystem);
final FileSystem _fileSystem;
@override
File? which(String execName) {
if (execName == 'java') {
return _fileSystem.file('/fake/which/java/path');
}
throw const InvalidArgumentException(null, null);
}
}
......@@ -67,6 +67,7 @@ void main() {
testUsingContext('getEmulators', () async {
// Test that EmulatorManager.getEmulators() doesn't throw.
final EmulatorManager emulatorManager = EmulatorManager(
java: FakeJava(),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
......@@ -89,6 +90,7 @@ void main() {
testUsingContext('getEmulators with no Android SDK', () async {
// Test that EmulatorManager.getEmulators() doesn't throw when there's no Android SDK.
final EmulatorManager emulatorManager = EmulatorManager(
java: FakeJava(),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
......@@ -109,6 +111,7 @@ void main() {
testWithoutContext('getEmulatorsById', () async {
final TestEmulatorManager testEmulatorManager = TestEmulatorManager(emulators,
java: FakeJava(),
logger: BufferLogger.test(),
processManager: fakeProcessManager,
androidWorkflow: AndroidWorkflow(
......@@ -129,6 +132,7 @@ void main() {
testUsingContext('create emulator with a missing avdmanager does not crash.', () async {
sdk.avdManagerPath = null;
final EmulatorManager emulatorManager = EmulatorManager(
java: FakeJava(),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
......@@ -152,6 +156,7 @@ void main() {
// iOS discovery uses context.
testUsingContext('create emulator with an empty name does not fail', () async {
final EmulatorManager emulatorManager = EmulatorManager(
java: FakeJava(),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
......@@ -191,6 +196,7 @@ void main() {
testWithoutContext('create emulator with a unique name does not throw', () async {
final EmulatorManager emulatorManager = EmulatorManager(
java: FakeJava(),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
......@@ -227,6 +233,7 @@ void main() {
testWithoutContext('create emulator with an existing name errors', () async {
final EmulatorManager emulatorManager = EmulatorManager(
java: FakeJava(),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
......@@ -266,6 +273,7 @@ void main() {
// iOS discovery uses context.
testUsingContext('create emulator without a name but when default exists adds a suffix', () async {
final EmulatorManager emulatorManager = EmulatorManager(
java: FakeJava(),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
......@@ -342,6 +350,7 @@ void main() {
class TestEmulatorManager extends EmulatorManager {
TestEmulatorManager(this.allEmulators, {
required super.java,
required super.logger,
required super.processManager,
required super.androidWorkflow,
......@@ -393,7 +402,4 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
@override
String getAvdPath() => 'avd';
@override
Map<String, String> get sdkManagerEnv => <String, String>{};
}
......@@ -7,11 +7,10 @@ import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/android/gradle_utils.dart' as gradle_utils;
import 'package:flutter_tools/src/android/java.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/base/process.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/convert.dart';
......@@ -407,9 +406,7 @@ void main() {
});
group('java gradle agp compatibility', () {
Future<FlutterProject?> configureJavaGradleAgpForTest(
FakeAndroidSdkWithDir androidSdk, {
required String javaV,
Future<FlutterProject?> configureGradleAgpForTest({
required String gradleV,
required String agpV,
}) async {
......@@ -422,7 +419,6 @@ dependencies {
''';
});
addGradleWrapperFile(project.directory, gradleV);
androidSdk.javaVersion = javaV;
return project;
}
......@@ -431,22 +427,22 @@ dependencies {
// especially important for filesystem.
group('_', () {
final FakeProcessManager processManager;
final Java java;
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
java = FakeJava(version: JavaVersion(longText: '17.0.2', number: '17.0.2'));
processManager = FakeProcessManager.empty();
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory, androidStudio);
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
fileSystem.currentDirectory
.childDirectory(androidStudio.javaPath!)
.createSync();
_testInMemory(
'flamingo values are compatible',
() async {
final FlutterProject? project = await configureJavaGradleAgpForTest(
androidSdk,
javaV: '17.0.2',
final FlutterProject? project = await configureGradleAgpForTest(
gradleV: '8.0',
agpV: '7.4.2',
);
......@@ -454,6 +450,7 @@ dependencies {
await project!.android.hasValidJavaGradleAgpVersions();
expect(value.success, isTrue);
},
java: java,
androidStudio: androidStudio,
processManager: processManager,
androidSdk: androidSdk,
......@@ -461,22 +458,22 @@ dependencies {
});
group('_', () {
final FakeProcessManager processManager;
final Java java;
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
java = FakeJava(version: JavaVersion(longText: '1.8.0_242', number: '1.8.0_242'));
processManager = FakeProcessManager.empty();
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory, androidStudio);
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
fileSystem.currentDirectory
.childDirectory(androidStudio.javaPath!)
.createSync();
_testInMemory(
'java 8 era values are compatible',
() async {
final FlutterProject? project = await configureJavaGradleAgpForTest(
androidSdk,
javaV: '1.8.0_242',
final FlutterProject? project = await configureGradleAgpForTest(
gradleV: '6.7.1',
agpV: '4.2.0',
);
......@@ -484,6 +481,7 @@ dependencies {
await project!.android.hasValidJavaGradleAgpVersions();
expect(value.success, isTrue);
},
java: java,
androidStudio: androidStudio,
processManager: processManager,
androidSdk: androidSdk,
......@@ -492,22 +490,22 @@ dependencies {
group('_', () {
final FakeProcessManager processManager;
final Java java;
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
processManager = FakeProcessManager.empty();
java = FakeJava(version: JavaVersion(longText: '11.0.14', number: '11.0.14'));
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory, androidStudio);
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
fileSystem.currentDirectory
.childDirectory(androidStudio.javaPath!)
.createSync();
_testInMemory(
'electric eel era values are compatible',
() async {
final FlutterProject? project = await configureJavaGradleAgpForTest(
androidSdk,
javaV: '11.0.14',
final FlutterProject? project = await configureGradleAgpForTest(
gradleV: '7.3.3',
agpV: '7.2.0',
);
......@@ -515,32 +513,35 @@ dependencies {
await project!.android.hasValidJavaGradleAgpVersions();
expect(value.success, isTrue);
},
java: java,
androidStudio: androidStudio,
processManager: processManager,
androidSdk: androidSdk,
);
});
group('_', () {
const String javaV = '17.0.2';
const String gradleV = '6.7.3';
const String agpV = '7.2.0';
final FakeProcessManager processManager;
final Java java;
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
processManager = FakeProcessManager.empty();
java = FakeJava(version: JavaVersion(longText: javaV, number: javaV));
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory, androidStudio);
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
fileSystem.currentDirectory
.childDirectory(androidStudio.javaPath!)
.createSync();
_testInMemory(
'incompatible everything',
() async {
const String javaV = '17.0.2';
const String gradleV = '6.7.3';
const String agpV = '7.2.0';
final FlutterProject? project = await configureJavaGradleAgpForTest(
androidSdk,
javaV: javaV,
final FlutterProject? project = await configureGradleAgpForTest(
gradleV: gradleV,
agpV: agpV,
);
......@@ -563,32 +564,34 @@ dependencies {
expect(value.description, contains(RegExp(javaV)));
expect(value.description, contains(RegExp(gradleV)));
},
java: java,
androidStudio: androidStudio,
processManager: processManager,
androidSdk: androidSdk,
);
});
group('_', () {
const String javaV = '17.0.2';
const String gradleV = '6.7.3';
const String agpV = '4.2.0';
final FakeProcessManager processManager;
final Java java;
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
processManager = FakeProcessManager.empty();
java = FakeJava(version: JavaVersion(longText: '17.0.2', number: '17.0.2'));
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory, androidStudio);
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
fileSystem.currentDirectory
.childDirectory(androidStudio.javaPath!)
.createSync();
_testInMemory(
'incompatible java/gradle only',
() async {
const String javaV = '17.0.2';
const String gradleV = '6.7.3';
const String agpV = '4.2.0';
final FlutterProject? project = await configureJavaGradleAgpForTest(
androidSdk,
javaV: javaV,
final FlutterProject? project = await configureGradleAgpForTest(
gradleV: gradleV,
agpV: agpV,
);
......@@ -606,6 +609,7 @@ dependencies {
expect(value.description, contains(RegExp(javaV)));
expect(value.description, contains(RegExp(gradleV)));
},
java: java,
androidStudio: androidStudio,
processManager: processManager,
androidSdk: androidSdk,
......@@ -613,25 +617,24 @@ dependencies {
});
group('_', () {
final FakeProcessManager processManager;
final Java java;
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
java = FakeJava(version: JavaVersion(longText: '11.0.2', number: '11.0.2'));
processManager = FakeProcessManager.empty();
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory, androidStudio);
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
fileSystem.currentDirectory
.childDirectory(androidStudio.javaPath!)
.createSync();
_testInMemory(
'incompatible gradle/agp only',
() async {
const String javaV = '11.0.2';
const String gradleV = '7.0.3';
const String agpV = '7.1.0';
final FlutterProject? project = await configureJavaGradleAgpForTest(
androidSdk,
javaV: javaV,
final FlutterProject? project = await configureGradleAgpForTest(
gradleV: gradleV,
agpV: agpV,
);
......@@ -649,6 +652,7 @@ dependencies {
expect(value.description, contains(RegExp(gradleV)));
expect(value.description, contains(RegExp(agpV)));
},
java: java,
androidStudio: androidStudio,
processManager: processManager,
androidSdk: androidSdk,
......@@ -1404,6 +1408,7 @@ void _testInMemory(
String description,
Future<void> Function() testMethod, {
FileSystem? fileSystem,
Java? java,
AndroidStudio? androidStudio,
ProcessManager? processManager,
AndroidSdk? androidSdk,
......@@ -1466,6 +1471,7 @@ void _testInMemory(
overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => processManager ?? FakeProcessManager.any(),
Java : () => java,
AndroidStudio: () => androidStudio ?? FakeAndroidStudio(),
// Intentionlly null if not set. Some ios tests fail if this is a fake.
AndroidSdk: () => androidSdk,
......@@ -1667,37 +1673,10 @@ class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterprete
}
class FakeAndroidSdkWithDir extends Fake implements AndroidSdk {
FakeAndroidSdkWithDir(this._directory, AndroidStudio _androidStudio) {
_javaPath = '${_androidStudio.javaPath}/bin/java';
}
late String _javaPath;
String? javaVersion;
FakeAndroidSdkWithDir(this._directory);
final Directory _directory;
@override
late bool platformToolsAvailable;
@override
late bool licensesAvailable;
@override
AndroidSdkVersion? latestVersion;
@override
Directory get directory => _directory;
@override
Map<String, String> get sdkManagerEnv => <String, String>{'PATH': _javaPath};
@override
String? getJavaVersion({
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
required ProcessUtils processUtils,
}) {
return javaVersion;
}
}
......@@ -7,6 +7,7 @@ import 'dart:io' as io show IOSink, ProcessSignal, Stdout, StdoutException;
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/android/java.dart';
import 'package:flutter_tools/src/base/bot_detector.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
......@@ -588,6 +589,7 @@ class FakeFlutterProjectFactory implements FlutterProjectFactory {
}
class FakeAndroidSdk extends Fake implements AndroidSdk {
@override
late bool platformToolsAvailable;
......@@ -602,3 +604,41 @@ class FakeAndroidStudio extends Fake implements AndroidStudio {
@override
String get javaPath => 'java';
}
class FakeJava extends Fake implements Java {
FakeJava({
this.javaHome = '/android-studio/jbr',
String binary = '/android-studio/jbr/bin/java',
JavaVersion? version,
bool canRun = true,
}): binaryPath = binary,
version = version ?? JavaVersion(
longText: 'openjdk 19.0.2 2023-01-17',
number: '19.0.2',
),
_environment = <String, String>{
if (javaHome != null) 'JAVA_HOME': javaHome,
'PATH': '/android-studio/jbr/bin',
},
_canRun = canRun;
@override
String? javaHome;
@override
String binaryPath;
final Map<String, String> _environment;
final bool _canRun;
@override
Map<String, String> get environment => _environment;
@override
JavaVersion? version;
@override
bool canRun() {
return _canRun;
}
}
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