Unverified Commit 06e2b181 authored by Andrew Kolos's avatar Andrew Kolos Committed by GitHub

[tools] use Java class for all java-searching behavior (#127354)

Fixes #124252, finishing work on the umbrella tracking issue, #126126.

Essentially, after this PR, no (non-test) code should be be referencing/invoking the java home or binary paths.
parent 2bba614a
......@@ -28,8 +28,8 @@ SPEC CHECKSUMS:
connectivity_macos: 5dae6ee11d320fac7c05f0d08bd08fc32b5514d9
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
PODFILE CHECKSUM: 2e6060c123c393d6beb3ee5b7beaf789de4d2e47
COCOAPODS: 1.11.3
COCOAPODS: 1.12.0
......@@ -38,7 +38,6 @@ class AndroidSdk {
reinitialize();
}
static const String javaHomeEnvironmentVariable = 'JAVA_HOME';
/// The Android SDK root directory.
final Directory directory;
......
......@@ -28,18 +28,6 @@ import 'android_studio_validator.dart';
final RegExp _dotHomeStudioVersionMatcher =
RegExp(r'^\.?(AndroidStudio[^\d]*)([\d.]+)');
// TODO(andrewkolos): this global variable is used in several places to provide
// a java binary to multiple Java-dependent tools, including the Android SDK
// and Gradle. If this is null, these tools will implicitly fall back to current
// JAVA_HOME env variable and then to any java found on PATH.
//
// This logic is consistent with that used by flutter doctor to find a valid JDK,
// but this consistency is fragile--the implementations of this logic
// exist independently of each other.
//
// See https://github.com/flutter/flutter/issues/124252.
String? get javaPath => globals.androidStudio?.javaPath;
class AndroidStudio {
/// A [version] value of null represents an unknown version.
AndroidStudio(
......@@ -173,6 +161,9 @@ class AndroidStudio {
/// The path of the JDK bundled with Android Studio.
///
/// This will be null if the bundled JDK could not be found or run.
///
/// If you looking to invoke the java binary or add it to the system
/// environment variables, consider using the [Java] class instead.
String? get javaPath => _javaPath;
bool get isValid => _isValid;
......
......@@ -75,84 +75,58 @@ class AndroidWorkflow implements Workflow {
/// Android Studio.
class AndroidValidator extends DoctorValidator {
AndroidValidator({
required Java? java,
required AndroidSdk? androidSdk,
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required Logger logger,
required Platform platform,
required ProcessManager processManager,
required UserMessages userMessages,
}) : _androidSdk = androidSdk,
_androidStudio = androidStudio,
_fileSystem = fileSystem,
}) : _java = java,
_androidSdk = androidSdk,
_logger = logger,
_platform = platform,
_processManager = processManager,
_userMessages = userMessages,
super('Android toolchain - develop for Android devices');
final Java? _java;
final AndroidSdk? _androidSdk;
final AndroidStudio? _androidStudio;
final FileSystem _fileSystem;
final Logger _logger;
final Platform _platform;
final ProcessManager _processManager;
final UserMessages _userMessages;
@override
String get slowWarning => '${_task ?? 'This'} is taking a long time...';
String? _task;
/// Finds the semantic version anywhere in a text.
static final RegExp _javaVersionPattern = RegExp(r'(\d+)(\.(\d+)(\.(\d+))?)?');
/// `java -version` response is not only a number, but also includes other
/// information eg. `openjdk version "1.7.0_212"`.
/// This method extracts only the semantic version from that response.
static String? _extractJavaVersion(String? text) {
if (text == null || text.isEmpty) {
return null;
}
final Match? match = _javaVersionPattern.firstMatch(text);
if (match == null) {
return null;
}
return text.substring(match.start, match.end);
}
/// Returns false if we cannot determine the Java version or if the version
/// is older that the minimum allowed version of 1.8.
Future<bool> _checkJavaVersion(String javaBinary, List<ValidationMessage> messages) async {
Future<bool> _checkJavaVersion(List<ValidationMessage> messages) async {
_task = 'Checking Java status';
try {
if (!_processManager.canRun(javaBinary)) {
messages.add(ValidationMessage.error(_userMessages.androidCantRunJavaBinary(javaBinary)));
if (_java?.binaryPath == null) {
messages.add(ValidationMessage.error(_userMessages.androidMissingJdk));
return false;
}
messages.add(ValidationMessage(_userMessages.androidJdkLocation(_java!.binaryPath)));
if (!_java!.canRun()) {
messages.add(ValidationMessage.error(_userMessages.androidCantRunJavaBinary(_java!.binaryPath)));
return false;
}
String? javaVersionText;
Version? javaVersion;
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) {
final List<String> versionLines = (result.stderr as String).split('\n');
javaVersionText = versionLines.length >= 2 ? versionLines[1] : versionLines[0];
}
javaVersion = _java!.version;
} on Exception catch (error) {
_logger.printTrace(error.toString());
}
final Version? javaVersion = Version.parse(_extractJavaVersion(javaVersionText));
if (javaVersionText == null || javaVersionText.isEmpty || javaVersion == null) {
if (javaVersion == null) {
// Could not determine the java version.
messages.add(ValidationMessage.error(_userMessages.androidUnknownJavaVersion));
return false;
}
if (javaVersion < kAndroidJavaMinVersion) {
messages.add(ValidationMessage.error(_userMessages.androidJavaMinimumVersion(javaVersionText)));
messages.add(ValidationMessage.error(_userMessages.androidJavaMinimumVersion(javaVersion.toString())));
return false;
}
messages.add(ValidationMessage(_userMessages.androidJavaVersion(javaVersionText)));
messages.add(ValidationMessage(_userMessages.androidJavaVersion(javaVersion.toString())));
return true;
} finally {
_task = null;
......@@ -234,22 +208,9 @@ class AndroidValidator extends DoctorValidator {
}
_task = 'Finding Java binary';
// Now check for the JDK.
final String? javaBinary = Java.find(
logger: _logger,
androidStudio: _androidStudio,
fileSystem: _fileSystem,
platform: _platform,
processManager: _processManager,
)?.binaryPath;
if (javaBinary == null) {
messages.add(ValidationMessage.error(_userMessages.androidMissingJdk));
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
}
messages.add(ValidationMessage(_userMessages.androidJdkLocation(javaBinary)));
// Check JDK version.
if (!await _checkJavaVersion(javaBinary, messages)) {
if (!await _checkJavaVersion(messages)) {
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
}
......
......@@ -30,10 +30,10 @@ import '../globals.dart' as globals;
import '../project.dart';
import '../reporting/reporting.dart';
import 'android_builder.dart';
import 'android_sdk.dart';
import 'android_studio.dart';
import 'gradle_errors.dart';
import 'gradle_utils.dart';
import 'java.dart';
import 'migrations/android_studio_java_gradle_conflict_migration.dart';
import 'migrations/top_level_gradle_build_file_migration.dart';
import 'multidex.dart';
......@@ -132,6 +132,7 @@ const Duration kMaxRetryTime = Duration(seconds: 10);
/// An implementation of the [AndroidBuilder] that delegates to gradle.
class AndroidGradleBuilder implements AndroidBuilder {
AndroidGradleBuilder({
required Java? java,
required Logger logger,
required ProcessManager processManager,
required FileSystem fileSystem,
......@@ -140,7 +141,8 @@ class AndroidGradleBuilder implements AndroidBuilder {
required GradleUtils gradleUtils,
required Platform platform,
required AndroidStudio? androidStudio,
}) : _logger = logger,
}) : _java = java,
_logger = logger,
_fileSystem = fileSystem,
_artifacts = artifacts,
_usage = usage,
......@@ -149,6 +151,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
_fileSystemUtils = FileSystemUtils(fileSystem: fileSystem, platform: platform),
_processUtils = ProcessUtils(logger: logger, processManager: processManager);
final Java? _java;
final Logger _logger;
final ProcessUtils _processUtils;
final FileSystem _fileSystem;
......@@ -422,15 +425,11 @@ class AndroidGradleBuilder implements AndroidBuilder {
..start();
int exitCode = 1;
try {
final String? javaHome = globals.java?.javaHome;
exitCode = await _processUtils.stream(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{
if (javaHome != null)
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
},
environment: _java?.environment,
mapFunction: consumeLog,
);
} on ProcessException catch (exception) {
......@@ -692,15 +691,11 @@ class AndroidGradleBuilder implements AndroidBuilder {
..start();
RunResult result;
try {
final String? javaHome = globals.java?.javaHome;
result = await _processUtils.run(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{
if (javaHome != null)
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
},
environment: _java?.environment,
);
} finally {
status.stop();
......@@ -745,15 +740,11 @@ class AndroidGradleBuilder implements AndroidBuilder {
..start();
RunResult result;
try {
final String? javaHome = globals.java?.javaHome;
result = await _processUtils.run(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{
if (javaHome != null)
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
},
environment: _java?.environment,
);
} finally {
status.stop();
......
......@@ -11,8 +11,6 @@ import '../base/terminal.dart';
import '../globals.dart' as globals;
import '../project.dart';
import '../reporting/reporting.dart';
import 'android_sdk.dart';
import 'android_studio.dart';
import 'gradle_utils.dart';
import 'multidex.dart';
......@@ -379,10 +377,7 @@ final GradleHandledError flavorUndefinedHandler = GradleHandledError(
],
throwOnError: true,
workingDirectory: project.android.hostAppGradleRoot.path,
environment: <String, String>{
if (javaPath != null)
AndroidSdk.javaHomeEnvironmentVariable: javaPath!,
},
environment: globals.java?.environment,
);
// Extract build types and product flavors.
final Set<String> variants = <String>{};
......
......@@ -9,13 +9,14 @@ import '../base/logger.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/process.dart';
import '../base/version.dart';
import 'android_studio.dart';
const String _javaHomeEnvironmentVariable = 'JAVA_HOME';
const String _kJavaExecutable = 'java';
const String _javaExecutable = 'java';
/// Represents an installation of Java.
class Java {
Java({
required this.javaHome,
required this.binaryPath,
......@@ -31,6 +32,15 @@ class Java {
_processManager = processManager,
_processUtils = ProcessUtils(processManager: processManager, logger: logger);
/// Within the Java ecosystem, this environment variable is typically set
/// the install location of a Java Runtime Environment (JRE) or Java
/// Development Kit (JDK).
///
/// Tools that depend on Java and need to find it will often check this
/// variable. If you are looking to set `JAVA_HOME` when stating a process,
/// consider using the [environment] instance property instead.
static String javaHomeEnvironmentVariable = 'JAVA_HOME';
/// Finds the Java runtime environment that should be used for all java-dependent
/// operations across the tool.
///
......@@ -41,13 +51,6 @@ class Java {
/// 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,
......@@ -89,7 +92,7 @@ class Java {
);
}
/// The path of the runtime's home directory.
/// The path of the runtime environments' home directory.
///
/// This should only be used for logging and validation purposes.
/// If you need to set JAVA_HOME when starting a process, consider
......@@ -98,7 +101,7 @@ class Java {
/// a new method to this class instead.
final String? javaHome;
/// The path of the runtime's java binary.
/// The path of the runtime environments' 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
......@@ -120,7 +123,7 @@ class Java {
/// processes, such as Gradle or Android SDK tools (avdmanager, sdkmanager, etc.)
Map<String, String> get environment {
return <String, String>{
if (javaHome != null) _javaHomeEnvironmentVariable: javaHome!,
if (javaHome != null) javaHomeEnvironmentVariable: javaHome!,
'PATH': _fileSystem.path.dirname(binaryPath) +
_os.pathVarSeparator +
_platform.environment['PATH']!,
......@@ -129,7 +132,7 @@ class Java {
/// Returns the version of java in the format \d(.\d)+(.\d)+
/// Returns null if version could not be determined.
late final JavaVersion? version = (() {
late final Version? version = (() {
final RunResult result = _processUtils.runSync(
<String>[binaryPath, '--version'],
environment: environment,
......@@ -138,7 +141,38 @@ class Java {
_logger.printTrace('java --version failed: exitCode: ${result.exitCode}'
' stdout: ${result.stdout} stderr: ${result.stderr}');
}
return JavaVersion.tryParseFromJavaOutput(result.stdout, logger: _logger);
final String rawVersionOutput = result.stdout;
final List<String> versionLines = rawVersionOutput.split('\n');
// Should look something like 'openjdk 19.0.2 2023-01-17'.
final String longVersionText = 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? version = matches.first.group(0);
if (version == null || version.split('_').isEmpty) {
_logger.printWarning(_formatJavaVersionWarning(rawVersionOutput));
return null;
}
// Trim away _d+ from versions 1.8 and below.
final String versionWithoutBuildInfo = version.split('_').first;
final Version? parsedVersion = Version.parse(versionWithoutBuildInfo);
if (parsedVersion == null) {
return null;
}
return Version.withText(
parsedVersion.major,
parsedVersion.minor,
parsedVersion.patch,
longVersionText,
);
})();
bool canRun() {
......@@ -156,7 +190,7 @@ String? _findJavaHome({
return androidStudioJavaPath;
}
final String? javaHomeEnv = platform.environment[_javaHomeEnvironmentVariable];
final String? javaHomeEnv = platform.environment[Java.javaHomeEnvironmentVariable];
if (javaHomeEnv != null) {
return javaHomeEnv;
}
......@@ -175,7 +209,7 @@ String? _findJavaBinary({
}
// Fallback to PATH based lookup.
return operatingSystemUtils.which(_kJavaExecutable)?.path;
return operatingSystemUtils.which(_javaExecutable)?.path;
}
// Returns a user visible String that says the tool failed to parse
......@@ -187,44 +221,3 @@ String _formatJavaVersionWarning(String javaVersionRaw) {
'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);
}
}
......@@ -99,14 +99,12 @@ class AndroidStudioJavaGradleConflictMigration extends ProjectMigrator {
return;
}
final String? javaVersionString = _java?.version?.number;
final Version? javaVersion = Version.parse(javaVersionString);
if (javaVersion == null) {
if (_java?.version == null) {
logger.printTrace(javaVersionNotFound);
return;
}
if (javaVersion.major != flamingoBundledJava.major) {
if (_java!.version!.major != flamingoBundledJava.major) {
logger.printTrace(javaVersionNot17);
return;
}
......
......@@ -88,6 +88,7 @@ Future<T> runInContext<T>(
overrides: overrides,
fallbacks: <Type, Generator>{
AndroidBuilder: () => AndroidGradleBuilder(
java: globals.java,
logger: globals.logger,
processManager: globals.processManager,
fileSystem: globals.fs,
......@@ -111,12 +112,10 @@ Future<T> runInContext<T>(
AndroidSdk: AndroidSdk.locateAndroidSdk,
AndroidStudio: AndroidStudio.latestValid,
AndroidValidator: () => AndroidValidator(
androidStudio: globals.androidStudio,
java: globals.java,
androidSdk: globals.androidSdk,
fileSystem: globals.fs,
logger: globals.logger,
platform: globals.platform,
processManager: globals.processManager,
userMessages: globals.userMessages,
),
AndroidWorkflow: () => AndroidWorkflow(
......
......@@ -7,8 +7,7 @@ import 'dart:async';
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import 'android/android_sdk.dart';
import 'android/android_studio.dart';
import 'android/java.dart';
import 'base/common.dart';
import 'base/error_handling_io.dart';
import 'base/file_system.dart';
......@@ -387,12 +386,17 @@ class AndroidGenSnapshotArtifacts extends EngineCachedArtifact {
/// A cached artifact containing the Maven dependencies used to build Android projects.
///
/// This is a no-op if the android SDK is not available.
///
/// Set [Java] to `null` to indicate that no Java/JDK installation could be found.
class AndroidMavenArtifacts extends ArtifactSet {
AndroidMavenArtifacts(this.cache, {
required Java? java,
required Platform platform,
}) : _platform = platform,
}) : _java = java,
_platform = platform,
super(DevelopmentArtifact.androidMaven);
final Java? _java;
final Platform _platform;
final Cache cache;
......@@ -404,6 +408,8 @@ class AndroidMavenArtifacts extends ArtifactSet {
OperatingSystemUtils operatingSystemUtils,
{bool offline = false}
) async {
// TODO(andrewkolos): Should this really be no-op if the Android SDK
// is unavailable? https://github.com/flutter/flutter/issues/127848
if (globals.androidSdk == null) {
return;
}
......@@ -424,10 +430,7 @@ class AndroidMavenArtifacts extends ArtifactSet {
'--project-cache-dir', tempDir.path,
'resolveDependencies',
],
environment: <String, String>{
if (javaPath != null)
AndroidSdk.javaHomeEnvironmentVariable: javaPath!,
},
environment: _java?.environment,
);
if (processResult.exitCode != 0) {
logger.printError('Failed to download the Android dependencies');
......
......@@ -14,6 +14,7 @@ import 'base/error_handling_io.dart';
import 'base/file_system.dart';
import 'base/logger.dart';
import 'base/utils.dart';
import 'base/version.dart';
import 'bundle.dart' as bundle;
import 'cmake_project.dart';
import 'features.dart';
......@@ -568,7 +569,7 @@ class AndroidProject extends FlutterProjectPlatform {
hostAppGradleRoot, globals.logger, globals.processManager);
final String? agpVersion =
gradle.getAgpVersion(hostAppGradleRoot, globals.logger);
final String? javaVersion = globals.java?.version?.number;
final String? javaVersion = _versionToParsableString(globals.java?.version);
// Assume valid configuration.
String description = validJavaGradleAgpString;
......@@ -893,3 +894,12 @@ class CompatibilityResult {
final bool success;
final String description;
}
/// Converts a [Version] to a string that can be parsed by [Version.parse].
String? _versionToParsableString(Version? version) {
if (version == null) {
return null;
}
return '${version.major}.${version.minor}.${version.patch}';
}
......@@ -46,6 +46,7 @@ void main() {
testUsingContext('Can immediately tool exit on recognized exit code/stderr', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -138,6 +139,7 @@ void main() {
testUsingContext('Verbose mode for APKs includes Gradle stacktrace and sets debug log level', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: BufferLogger.test(verbose: true),
processManager: processManager,
fileSystem: fileSystem,
......@@ -205,6 +207,7 @@ void main() {
testUsingContext('Can retry build on recognized exit code/stderr', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -310,6 +313,7 @@ void main() {
testUsingContext('Converts recognized ProcessExceptions into tools exits', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -402,6 +406,7 @@ void main() {
testUsingContext('rethrows unrecognized ProcessException', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -466,6 +471,7 @@ void main() {
testUsingContext('logs success event after a successful retry', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -569,6 +575,7 @@ void main() {
testUsingContext('performs code size analysis and sends analytics', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -670,6 +677,7 @@ void main() {
testUsingContext('indicates that an APK has been built successfully', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -797,6 +805,7 @@ android {
testUsingContext('can call custom gradle task getBuildOptions and parse the result', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -831,6 +840,7 @@ BuildVariant: paidProfile
testUsingContext('getBuildOptions returns empty list if gradle returns error', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -861,6 +871,7 @@ Gradle Crashed
testUsingContext("doesn't indicate how to consume an AAR when printHowToConsumeAar is false", () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -926,6 +937,7 @@ Gradle Crashed
testUsingContext('Verbose mode for AARs includes Gradle stacktrace and sets debug log level', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: BufferLogger.test(verbose: true),
processManager: processManager,
fileSystem: fileSystem,
......@@ -984,6 +996,7 @@ Gradle Crashed
testUsingContext('gradle exit code and stderr is forwarded to tool exit', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1043,6 +1056,7 @@ Gradle Crashed
testUsingContext('build apk uses selected local engine with arm32 ABI', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1121,6 +1135,7 @@ Gradle Crashed
testUsingContext('build apk uses selected local engine with arm64 ABI', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1199,6 +1214,7 @@ Gradle Crashed
testUsingContext('build apk uses selected local engine with x86 ABI', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1277,6 +1293,7 @@ Gradle Crashed
testUsingContext('build apk uses selected local engine with x64 ABI', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1356,6 +1373,7 @@ Gradle Crashed
testUsingContext('honors --no-android-gradle-daemon setting', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1416,6 +1434,7 @@ Gradle Crashed
testUsingContext('build aar uses selected local engine with arm32 ABI', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1503,6 +1522,7 @@ Gradle Crashed
testUsingContext('build aar uses selected local engine with x64 ABI', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1590,6 +1610,7 @@ Gradle Crashed
testUsingContext('build aar uses selected local engine with x86 ABI', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1677,6 +1698,7 @@ Gradle Crashed
testUsingContext('build aar uses selected local engine on x64 ABI', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
......@@ -1773,5 +1795,5 @@ class FakeGradleUtils extends Fake implements GradleUtils {
class FakeAndroidStudio extends Fake implements AndroidStudio {
@override
String get javaPath => 'java';
String get javaPath => '/android-studio/jbr';
}
......@@ -6,7 +6,6 @@ import 'package:file/file.dart';
import 'package:file/memory.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';
......@@ -47,8 +46,8 @@ 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');
const Version _javaVersion17 = Version.withText(17, 0, 2, 'openjdk 17.0.2');
const Version _javaVersion16 = Version.withText(16, 0, 2, 'openjdk 16.0.2');
void main() {
group('Android migration', () {
......@@ -284,7 +283,7 @@ class FakeAndroidStudio extends Fake implements AndroidStudio {
class FakeErroringJava extends FakeJava {
@override
JavaVersion get version {
Version get version {
throw Exception('How did this happen?');
}
}
......
......@@ -384,7 +384,7 @@ void main() {
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.empty(),
Platform: () => FakePlatform(environment: <String, String>{
AndroidSdk.javaHomeEnvironmentVariable: 'java-home-path',
Java.javaHomeEnvironmentVariable: 'java-home-path',
}),
Config: () => Config,
AndroidStudio: () => FakeAndroidStudioWithoutJdk(),
......
......@@ -436,11 +436,9 @@ Review licenses that have not been accepted (y/N)?
..cmdlineToolsAvailable = true
..directory = fileSystem.directory('/foo/bar');
final ValidationResult validationResult = await AndroidValidator(
androidStudio: FakeAndroidStudio(),
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
userMessages: UserMessages(),
).validate();
......@@ -457,12 +455,6 @@ Review licenses that have not been accepted (y/N)?
});
testUsingContext('detects minimum required SDK and buildtools', () async {
processManager.addCommand(const FakeCommand(
command: <String>[
'which',
'java',
], exitCode: 1,
));
final FakeAndroidSdkVersion sdkVersion = FakeAndroidSdkVersion()
..sdkLevel = 28
..buildToolsVersion = Version(26, 0, 3);
......@@ -483,15 +475,14 @@ Review licenses that have not been accepted (y/N)?
);
final AndroidValidator androidValidator = AndroidValidator(
androidStudio: null,
java: null,
androidSdk: sdk,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
userMessages: UserMessages(),
);
ValidationResult validationResult = await androidValidator.validate();
expect(validationResult.type, ValidationType.missing);
expect(
......@@ -531,15 +522,14 @@ Review licenses that have not been accepted (y/N)?
..directory = fileSystem.directory('/foo/bar');
final AndroidValidator androidValidator = AndroidValidator(
androidStudio: null,
java: FakeJava(),
androidSdk: sdk,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
userMessages: UserMessages(),
);
final String errorMessage = UserMessages().androidMissingCmdTools;
final ValidationResult validationResult = await androidValidator.validate();
......@@ -556,13 +546,11 @@ Review licenses that have not been accepted (y/N)?
testUsingContext('detects minimum required java version', () async {
// Test with older version of JDK
const String javaVersionText = 'openjdk version "1.7.0_212"';
processManager.addCommand(const FakeCommand(
command: <String>[
'home/java/bin/java',
'-version',
], stderr: javaVersionText,
));
final Platform platform = FakePlatform()..environment = <String, String>{
'HOME': '/home/me',
Java.javaHomeEnvironmentVariable: 'home/java',
'PATH': '',
};
final FakeAndroidSdkVersion sdkVersion = FakeAndroidSdkVersion()
..sdkLevel = 29
..buildToolsVersion = Version(28, 0, 3);
......@@ -576,15 +564,14 @@ Review licenses that have not been accepted (y/N)?
..sdkManagerPath = '/foo/bar/sdkmanager';
sdk.latestVersion = sdkVersion;
const String javaVersionText = 'openjdk version "1.7.0_212"';
final String errorMessage = UserMessages().androidJavaMinimumVersion(javaVersionText);
final ValidationResult validationResult = await AndroidValidator(
java: FakeJava(version: const Version.withText(1, 7, 0, javaVersionText)),
androidSdk: sdk,
androidStudio: null,
fileSystem: fileSystem,
logger: logger,
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', AndroidSdk.javaHomeEnvironmentVariable: 'home/java'},
processManager: processManager,
platform: platform,
userMessages: UserMessages(),
).validate();
expect(validationResult.type, ValidationType.partial);
......@@ -602,12 +589,10 @@ Review licenses that have not been accepted (y/N)?
testWithoutContext('Mentions `flutter config --android-sdk if user has no AndroidSdk`', () async {
final ValidationResult validationResult = await AndroidValidator(
java: FakeJava(),
androidSdk: null,
androidStudio: null,
fileSystem: fileSystem,
logger: logger,
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', AndroidSdk.javaHomeEnvironmentVariable: 'home/java'},
processManager: processManager,
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', Java.javaHomeEnvironmentVariable: 'home/java'},
userMessages: UserMessages(),
).validate();
......
......@@ -6,6 +6,7 @@ import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/android/gradle_errors.dart';
import 'package:flutter_tools/src/android/gradle_utils.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/logger.dart';
......@@ -774,6 +775,7 @@ assembleFooTest
);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{
Java: () => FakeJava(),
GradleUtils: () => FakeGradleUtils(),
Platform: () => fakePlatform('android'),
FileSystem: () => fileSystem,
......@@ -816,6 +818,7 @@ assembleProfile
);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{
Java: () => FakeJava(),
GradleUtils: () => FakeGradleUtils(),
Platform: () => fakePlatform('android'),
FileSystem: () => fileSystem,
......
......@@ -9,6 +9,7 @@ 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/version.dart';
import 'package:test/fake.dart';
import 'package:webdriver/async_io.dart';
......@@ -59,13 +60,12 @@ OpenJDK 64-Bit Server VM Zulu19.32+15-CA (build 19.0.2+7, mixed mode, sharing)
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');
expect(java.version!.toString(), 'OpenJDK Runtime Environment Zulu19.32+15-CA (build 19.0.2+7)');
expect(java.version, equals(Version(19, 0, 2)));
});
testWithoutContext('finds JAVA_HOME if it is set and the JDK bundled with Android Studio could not be found', () {
......@@ -78,7 +78,7 @@ OpenJDK 64-Bit Server VM Zulu19.32+15-CA (build 19.0.2+7, mixed mode, sharing)
logger: logger,
fileSystem: fs,
platform: FakePlatform(environment: <String, String>{
'JAVA_HOME': javaHome,
Java.javaHomeEnvironmentVariable: javaHome,
}),
processManager: processManager,
)!;
......@@ -160,9 +160,9 @@ 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');
final Version version = java.version!;
expect(version.toString(), 'Java(TM) SE Runtime Environment (build 1.8.0_202-b10)');
expect(version, equals(Version(1, 8, 0)));
});
testWithoutContext('parses jdk 11 windows', () {
addJavaVersionCommand('''
......@@ -170,9 +170,9 @@ 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');
final Version version = java.version!;
expect(version.toString(), 'Java(TM) SE Runtime Environment (build 11.0.14+10-b13)');
expect(version, equals(Version(11, 0, 14)));
});
testWithoutContext('parses jdk 11 mac/linux', () {
......@@ -181,9 +181,9 @@ 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');
final Version version = java.version!;
expect(version.toString(), 'OpenJDK Runtime Environment Zulu11.62+17-CA (build 11.0.18+10-LTS)');
expect(version, equals(Version(11, 0, 18)));
});
testWithoutContext('parses jdk 17', () {
......@@ -192,9 +192,9 @@ 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');
final Version version = java.version!;
expect(version.toString(), 'OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)');
expect(version, equals(Version(17, 0, 6)));
});
testWithoutContext('parses jdk 19', () {
......@@ -203,9 +203,9 @@ 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');
final Version version = java.version!;
expect(version.toString(), 'OpenJDK Runtime Environment Homebrew (build 19.0.2)');
expect(version, equals(Version(19, 0, 2)));
});
// https://chrome-infra-packages.appspot.com/p/flutter/java/openjdk/
......@@ -215,16 +215,16 @@ 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');
final Version version = java.version!;
expect(version.toString(), 'OpenJDK Runtime Environment 18.9 (build 11.0.2+9)');
expect(version, equals(Version(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');
final Version version = java.version!;
expect(version.toString(), 'openjdk 19.0 2023-01-17');
expect(version, equals(Version(19, 0, null)));
});
});
});
......
......@@ -1042,7 +1042,11 @@ void main() {
});
testWithoutContext('AndroidMavenArtifacts has a specified development artifact', () async {
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache!, platform: FakePlatform());
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(
cache!,
java: FakeJava(),
platform: FakePlatform(),
);
expect(mavenArtifacts.developmentArtifact, DevelopmentArtifact.androidMaven);
});
......@@ -1050,7 +1054,11 @@ void main() {
final String? oldRoot = Cache.flutterRoot;
Cache.flutterRoot = '';
try {
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache!, platform: FakePlatform());
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(
cache!,
java: FakeJava(),
platform: FakePlatform(),
);
expect(await mavenArtifacts.isUpToDate(memoryFileSystem!), isFalse);
final Directory gradleWrapperDir = cache!.getArtifactDirectory('gradle_wrapper')..createSync(recursive: true);
......@@ -1082,7 +1090,11 @@ void main() {
});
testUsingContext('AndroidMavenArtifacts is a no-op if the Android SDK is absent', () async {
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache!, platform: FakePlatform());
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(
cache!,
java: FakeJava(),
platform: FakePlatform(),
);
expect(await mavenArtifacts.isUpToDate(memoryFileSystem!), isFalse);
await mavenArtifacts.update(FakeArtifactUpdater(), BufferLogger.test(), memoryFileSystem!, FakeOperatingSystemUtils());
......
......@@ -11,6 +11,7 @@ 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/version.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/convert.dart';
......@@ -431,7 +432,7 @@ dependencies {
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
java = FakeJava(version: JavaVersion(longText: '17.0.2', number: '17.0.2'));
java = FakeJava(version: Version(17, 0, 2));
processManager = FakeProcessManager.empty();
androidStudio = FakeAndroidStudio();
androidSdk =
......@@ -462,7 +463,7 @@ dependencies {
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
java = FakeJava(version: JavaVersion(longText: '1.8.0_242', number: '1.8.0_242'));
java = FakeJava(version: const Version.withText(1, 8, 0, '1.8.0_242'));
processManager = FakeProcessManager.empty();
androidStudio = FakeAndroidStudio();
androidSdk =
......@@ -495,7 +496,7 @@ dependencies {
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
processManager = FakeProcessManager.empty();
java = FakeJava(version: JavaVersion(longText: '11.0.14', number: '11.0.14'));
java = FakeJava(version: Version(11, 0, 14));
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
......@@ -530,7 +531,7 @@ dependencies {
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
processManager = FakeProcessManager.empty();
java = FakeJava(version: JavaVersion(longText: javaV, number: javaV));
java = FakeJava(version: Version.parse(javaV));
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
......@@ -581,7 +582,7 @@ dependencies {
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
processManager = FakeProcessManager.empty();
java = FakeJava(version: JavaVersion(longText: '17.0.2', number: '17.0.2'));
java = FakeJava(version: Version(17, 0, 2));
androidStudio = FakeAndroidStudio();
androidSdk =
FakeAndroidSdkWithDir(fileSystem.currentDirectory);
......@@ -621,7 +622,7 @@ dependencies {
final AndroidStudio androidStudio;
final FakeAndroidSdkWithDir androidSdk;
final FileSystem fileSystem = getFileSystemForPlatform();
java = FakeJava(version: JavaVersion(longText: '11.0.2', number: '11.0.2'));
java = FakeJava(version: Version(11, 0, 2));
processManager = FakeProcessManager.empty();
androidStudio = FakeAndroidStudio();
androidSdk =
......
......@@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/file_system.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/version.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/features.dart';
......@@ -619,15 +620,12 @@ class FakeJava extends Fake implements Java {
FakeJava({
this.javaHome = '/android-studio/jbr',
String binary = '/android-studio/jbr/bin/java',
JavaVersion? version,
Version? version,
bool canRun = true,
}): binaryPath = binary,
version = version ?? JavaVersion(
longText: 'openjdk 19.0.2 2023-01-17',
number: '19.0.2',
),
version = version ?? const Version.withText(19, 0, 2, 'openjdk 19.0.2 2023-01-17'),
_environment = <String, String>{
if (javaHome != null) 'JAVA_HOME': javaHome,
if (javaHome != null) Java.javaHomeEnvironmentVariable: javaHome,
'PATH': '/android-studio/jbr/bin',
},
_canRun = canRun;
......@@ -645,7 +643,7 @@ class FakeJava extends Fake implements Java {
Map<String, String> get environment => _environment;
@override
JavaVersion? version;
Version? version;
@override
bool 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