Unverified Commit 15f2119f authored by Dan Field's avatar Dan Field Committed by GitHub

Reland "Allow for gradle downloading missing SDK assets" (#28097) (#28355)

* Allow for gradle downloading missing SDK assets if SDK licenses are present.

*  Improvements for windows testing
parent b484a912
...@@ -372,7 +372,7 @@ class AndroidDevice extends Device { ...@@ -372,7 +372,7 @@ class AndroidDevice extends Device {
if (buildInfo.targetPlatform == null && devicePlatform == TargetPlatform.android_arm64) if (buildInfo.targetPlatform == null && devicePlatform == TargetPlatform.android_arm64)
buildInfo = buildInfo.withTargetPlatform(TargetPlatform.android_arm64); buildInfo = buildInfo.withTargetPlatform(TargetPlatform.android_arm64);
if (!prebuiltApplication) { if (!prebuiltApplication || androidSdk.licensesAvailable && androidSdk.latestVersion == null) {
printTrace('Building APK'); printTrace('Building APK');
final FlutterProject project = await FlutterProject.current(); final FlutterProject project = await FlutterProject.current();
await buildApk( await buildApk(
......
...@@ -263,7 +263,7 @@ class AndroidNdk { ...@@ -263,7 +263,7 @@ class AndroidNdk {
class AndroidSdk { class AndroidSdk {
AndroidSdk(this.directory, [this.ndk]) { AndroidSdk(this.directory, [this.ndk]) {
_init(); reinitialize();
} }
static const String _javaHomeEnvironmentVariable = 'JAVA_HOME'; static const String _javaHomeEnvironmentVariable = 'JAVA_HOME';
...@@ -278,6 +278,23 @@ class AndroidSdk { ...@@ -278,6 +278,23 @@ class AndroidSdk {
List<AndroidSdkVersion> _sdkVersions; List<AndroidSdkVersion> _sdkVersions;
AndroidSdkVersion _latestVersion; AndroidSdkVersion _latestVersion;
/// Whether the `platform-tools` directory exists in the Android SDK.
///
/// It is possible to have an Android SDK folder that is missing this with
/// the expectation that it will be downloaded later, e.g. by gradle or the
/// sdkmanager. The [licensesAvailable] property should be used to determine
/// whether the licenses are at least possibly accepted.
bool get platformToolsAvailable => fs.directory(fs.path.join(directory, 'platform-tools')).existsSync();
/// Whether the `licenses` directory exists in the Android SDK.
///
/// The existence of this folder normally indicates that the SDK licenses have
/// been accepted, e.g. via the sdkmanager, Android Studio, or by copying them
/// from another workstation such as in CI scenarios. If these files are valid
/// gradle or the sdkmanager will be able to download and use other parts of
/// the SDK on demand.
bool get licensesAvailable => fs.directory(fs.path.join(directory, 'licenses')).existsSync();
static AndroidSdk locateAndroidSdk() { static AndroidSdk locateAndroidSdk() {
String findAndroidHomeDir() { String findAndroidHomeDir() {
String androidHomeDir; String androidHomeDir;
...@@ -348,14 +365,14 @@ class AndroidSdk { ...@@ -348,14 +365,14 @@ class AndroidSdk {
} }
static bool validSdkDirectory(String dir) { static bool validSdkDirectory(String dir) {
return fs.isDirectorySync(fs.path.join(dir, 'platform-tools')); return fs.isDirectorySync(fs.path.join(dir, 'licenses'));
} }
List<AndroidSdkVersion> get sdkVersions => _sdkVersions; List<AndroidSdkVersion> get sdkVersions => _sdkVersions;
AndroidSdkVersion get latestVersion => _latestVersion; AndroidSdkVersion get latestVersion => _latestVersion;
String get adbPath => getPlatformToolsPath('adb'); String get adbPath => getPlatformToolsPath(platform.isWindows ? 'adb.exe' : 'adb');
String get emulatorPath => getEmulatorPath(); String get emulatorPath => getEmulatorPath();
...@@ -376,8 +393,8 @@ class AndroidSdk { ...@@ -376,8 +393,8 @@ class AndroidSdk {
/// Validate the Android SDK. This returns an empty list if there are no /// Validate the Android SDK. This returns an empty list if there are no
/// issues; otherwise, it returns a list of issues found. /// issues; otherwise, it returns a list of issues found.
List<String> validateSdkWellFormed() { List<String> validateSdkWellFormed() {
if (!processManager.canRun(adbPath)) if (adbPath == null || !processManager.canRun(adbPath))
return <String>['Android SDK file not found: $adbPath.']; return <String>['Android SDK file not found: ${adbPath ?? 'adb'}.'];
if (sdkVersions.isEmpty || latestVersion == null) { if (sdkVersions.isEmpty || latestVersion == null) {
final StringBuffer msg = StringBuffer('No valid Android SDK platforms found in ${_platformsDir.path}.'); final StringBuffer msg = StringBuffer('No valid Android SDK platforms found in ${_platformsDir.path}.');
...@@ -396,7 +413,10 @@ class AndroidSdk { ...@@ -396,7 +413,10 @@ class AndroidSdk {
} }
String getPlatformToolsPath(String binaryName) { String getPlatformToolsPath(String binaryName) {
return fs.path.join(directory, 'platform-tools', binaryName); final String path = fs.path.join(directory, 'platform-tools', binaryName);
if (fs.file(path).existsSync())
return path;
return null;
} }
String getEmulatorPath() { String getEmulatorPath() {
...@@ -420,7 +440,11 @@ class AndroidSdk { ...@@ -420,7 +440,11 @@ class AndroidSdk {
return null; return null;
} }
void _init() { /// Sets up various paths used internally.
///
/// This method should be called in a case where the tooling may have updated
/// SDK artifacts, such as after running a gradle build.
void reinitialize() {
List<Version> buildTools = <Version>[]; // 19.1.0, 22.0.1, ... List<Version> buildTools = <Version>[]; // 19.1.0, 22.0.1, ...
final Directory buildToolsDir = fs.directory(fs.path.join(directory, 'build-tools')); final Directory buildToolsDir = fs.directory(fs.path.join(directory, 'build-tools'));
......
...@@ -105,6 +105,11 @@ class AndroidValidator extends DoctorValidator { ...@@ -105,6 +105,11 @@ class AndroidValidator extends DoctorValidator {
return ValidationResult(ValidationType.missing, messages); return ValidationResult(ValidationType.missing, messages);
} }
if (androidSdk.licensesAvailable && !androidSdk.platformToolsAvailable) {
messages.add(ValidationMessage.hint(userMessages.androidSdkLicenseOnly(kAndroidHome)));
return ValidationResult(ValidationType.partial, messages);
}
messages.add(ValidationMessage(userMessages.androidSdkLocation(androidSdk.directory))); messages.add(ValidationMessage(userMessages.androidSdkLocation(androidSdk.directory)));
messages.add(ValidationMessage(androidSdk.ndk == null messages.add(ValidationMessage(androidSdk.ndk == null
...@@ -249,7 +254,9 @@ class AndroidLicenseValidator extends DoctorValidator { ...@@ -249,7 +254,9 @@ class AndroidLicenseValidator extends DoctorValidator {
} }
} }
_ensureCanRunSdkManager(); if (!_canRunSdkManager()) {
return LicensesAccepted.unknown;
}
final Process process = await runCommand( final Process process = await runCommand(
<String>[androidSdk.sdkManagerPath, '--licenses'], <String>[androidSdk.sdkManagerPath, '--licenses'],
...@@ -279,7 +286,9 @@ class AndroidLicenseValidator extends DoctorValidator { ...@@ -279,7 +286,9 @@ class AndroidLicenseValidator extends DoctorValidator {
return false; return false;
} }
_ensureCanRunSdkManager(); if (!_canRunSdkManager()) {
throwToolExit(userMessages.androidMissingSdkManager(androidSdk.sdkManagerPath));
}
final Version sdkManagerVersion = Version.parse(androidSdk.sdkManagerVersion); final Version sdkManagerVersion = Version.parse(androidSdk.sdkManagerVersion);
if (sdkManagerVersion == null || sdkManagerVersion.major < 26) { if (sdkManagerVersion == null || sdkManagerVersion.major < 26) {
...@@ -306,10 +315,9 @@ class AndroidLicenseValidator extends DoctorValidator { ...@@ -306,10 +315,9 @@ class AndroidLicenseValidator extends DoctorValidator {
return exitCode == 0; return exitCode == 0;
} }
static void _ensureCanRunSdkManager() { static bool _canRunSdkManager() {
assert(androidSdk != null); assert(androidSdk != null);
final String sdkManagerPath = androidSdk.sdkManagerPath; final String sdkManagerPath = androidSdk.sdkManagerPath;
if (!processManager.canRun(sdkManagerPath)) return processManager.canRun(sdkManagerPath);
throwToolExit(userMessages.androidMissingSdkManager(sdkManagerPath));
} }
} }
...@@ -8,7 +8,6 @@ import 'package:meta/meta.dart'; ...@@ -8,7 +8,6 @@ import 'package:meta/meta.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../globals.dart';
import '../project.dart'; import '../project.dart';
import 'android_sdk.dart'; import 'android_sdk.dart';
...@@ -32,18 +31,11 @@ Future<void> buildApk({ ...@@ -32,18 +31,11 @@ Future<void> buildApk({
if (androidSdk == null) if (androidSdk == null)
throwToolExit('No Android SDK found. Try setting the ANDROID_SDK_ROOT environment variable.'); throwToolExit('No Android SDK found. Try setting the ANDROID_SDK_ROOT environment variable.');
final List<String> validationResult = androidSdk.validateSdkWellFormed(); await buildGradleProject(
if (validationResult.isNotEmpty) {
for (String message in validationResult) {
printError(message, wrap: false);
}
throwToolExit('Try re-installing or updating your Android SDK.');
}
return buildGradleProject(
project: project, project: project,
buildInfo: buildInfo, buildInfo: buildInfo,
target: target, target: target,
isBuildingBundle: false isBuildingBundle: false
); );
androidSdk.reinitialize();
} }
...@@ -112,6 +112,21 @@ Future<GradleProject> _gradleProject() async { ...@@ -112,6 +112,21 @@ Future<GradleProject> _gradleProject() async {
return _cachedGradleProject; return _cachedGradleProject;
} }
/// Runs `gradlew dependencies`, ensuring that dependencies are resolved and
/// potentially downloaded.
Future<void> checkGradleDependencies() async {
final Status progress = logger.startProgress('Ensuring gradle dependencies are up to date...', timeout: kSlowOperation);
final FlutterProject flutterProject = await FlutterProject.current();
final String gradle = await _ensureGradle(flutterProject);
await runCheckedAsync(
<String>[gradle, 'dependencies'],
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
environment: _gradleEnv,
);
androidSdk.reinitialize();
progress.stop();
}
// Note: Dependencies are resolved and possibly downloaded as a side-effect // Note: Dependencies are resolved and possibly downloaded as a side-effect
// of calculating the app properties using Gradle. This may take minutes. // of calculating the app properties using Gradle. This may take minutes.
Future<GradleProject> _readGradleProject() async { Future<GradleProject> _readGradleProject() async {
......
...@@ -34,6 +34,9 @@ class ApplicationPackageFactory { ...@@ -34,6 +34,9 @@ class ApplicationPackageFactory {
case TargetPlatform.android_arm64: case TargetPlatform.android_arm64:
case TargetPlatform.android_x64: case TargetPlatform.android_x64:
case TargetPlatform.android_x86: case TargetPlatform.android_x86:
if (androidSdk?.licensesAvailable == true && androidSdk.latestVersion == null) {
await checkGradleDependencies();
}
return applicationBinary == null return applicationBinary == null
? await AndroidApk.fromAndroidProject((await FlutterProject.current()).android) ? await AndroidApk.fromAndroidProject((await FlutterProject.current()).android)
: AndroidApk.fromApk(applicationBinary); : AndroidApk.fromApk(applicationBinary);
......
...@@ -45,6 +45,15 @@ class UserMessages { ...@@ -45,6 +45,15 @@ class UserMessages {
String androidCantRunJavaBinary(String javaBinary) => 'Cannot execute $javaBinary to determine the version'; String androidCantRunJavaBinary(String javaBinary) => 'Cannot execute $javaBinary to determine the version';
String get androidUnknownJavaVersion => 'Could not determine java version'; String get androidUnknownJavaVersion => 'Could not determine java version';
String androidJavaVersion(String javaVersion) => 'Java version $javaVersion'; String androidJavaVersion(String javaVersion) => 'Java version $javaVersion';
String androidSdkLicenseOnly(String envKey) =>
'Android SDK contains licenses only.\n'
'Your first build of an Android application will take longer than usual, '
'while gradle downloads the missing components. This functionality will '
'only work if the licenses in the licenses folder in $envKey are valid.\n'
'If the Android SDK has been installed to another location, set $envKey to that location.\n'
'You may also want to add it to your PATH environment variable.\n\n'
'Certain features, such as `flutter emulators` and `flutter devices`, will '
'not work without the currently missing SDK components.';
String androidBadSdkDir(String envKey, String homeDir) => String androidBadSdkDir(String envKey, String homeDir) =>
'$envKey = $homeDir\n' '$envKey = $homeDir\n'
'but Android SDK not found at this location.'; 'but Android SDK not found at this location.';
...@@ -53,7 +62,7 @@ class UserMessages { ...@@ -53,7 +62,7 @@ class UserMessages {
'Install Android Studio from: https://developer.android.com/studio/index.html\n' 'Install Android Studio from: https://developer.android.com/studio/index.html\n'
'On first launch it will assist you in installing the Android SDK components.\n' 'On first launch it will assist you in installing the Android SDK components.\n'
'(or visit https://flutter.io/setup/#android-setup for detailed instructions).\n' '(or visit https://flutter.io/setup/#android-setup for detailed instructions).\n'
'If Android SDK has been installed to a custom location, set $envKey to that location.\n' 'If the Android SDK has been installed to a custom location, set $envKey to that location.\n'
'You may also want to add it to your PATH environment variable.\n'; 'You may also want to add it to your PATH environment variable.\n';
String androidSdkLocation(String directory) => 'Android SDK at $directory'; String androidSdkLocation(String directory) => 'Android SDK at $directory';
String androidSdkPlatformToolsVersion(String platform, String tools) => String androidSdkPlatformToolsVersion(String platform, String tools) =>
...@@ -75,7 +84,11 @@ class UserMessages { ...@@ -75,7 +84,11 @@ class UserMessages {
String get androidLicensesAll => 'All Android licenses accepted.'; String get androidLicensesAll => 'All Android licenses accepted.';
String get androidLicensesSome => 'Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses'; String get androidLicensesSome => 'Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses';
String get androidLicensesNone => 'Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses'; String get androidLicensesNone => 'Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses';
String get androidLicensesUnknown => 'Android license status unknown.'; String get androidLicensesUnknown =>
'Android license status unknown.\n'
'Try re-installing or updating your Android SDK Manager.\n'
'See https://developer.android.com/studio/#downloads or visit '
'https://flutter.io/setup/#android-setup for detailed instructions.';
String androidSdkManagerOutdated(String managerPath) => String androidSdkManagerOutdated(String managerPath) =>
'A newer version of the Android SDK is required. To update, run:\n' 'A newer version of the Android SDK is required. To update, run:\n'
'$managerPath --update\n'; '$managerPath --update\n';
......
...@@ -27,16 +27,23 @@ void main() { ...@@ -27,16 +27,23 @@ void main() {
}); });
group('getAdbDevices', () { group('getAdbDevices', () {
final MockProcessManager mockProcessManager = MockProcessManager();
testUsingContext('throws on missing adb path', () { testUsingContext('throws on missing adb path', () {
final Directory sdkDir = MockAndroidSdk.createSdkDirectory(); final Directory sdkDir = MockAndroidSdk.createSdkDirectory();
Config.instance.setValue('android-sdk', sdkDir.path); Config.instance.setValue('android-sdk', sdkDir.path);
final File adbExe = fs.file(getAdbPath(androidSdk)); final File adbExe = fs.file(getAdbPath(androidSdk));
adbExe.deleteSync(); when(mockProcessManager.runSync(
<String>[adbExe.path, 'devices', '-l'],
))
.thenAnswer(
(_) => throw ArgumentError(adbExe.path),
);
expect(() => getAdbDevices(), throwsToolExit(message: RegExp('Unable to run "adb".*${adbExe.path}'))); expect(() => getAdbDevices(), throwsToolExit(message: RegExp('Unable to run "adb".*${adbExe.path}')));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => MockAndroidSdk(), AndroidSdk: () => MockAndroidSdk(),
FileSystem: () => MemoryFileSystem(), FileSystem: () => MemoryFileSystem(),
ProcessManager: () => mockProcessManager,
}); });
testUsingContext('physical devices', () { testUsingContext('physical devices', () {
......
...@@ -225,12 +225,13 @@ class MockBrokenAndroidSdk extends Mock implements AndroidSdk { ...@@ -225,12 +225,13 @@ class MockBrokenAndroidSdk extends Mock implements AndroidSdk {
bool withSdkManager = true, bool withSdkManager = true,
}) { }) {
final Directory dir = fs.systemTempDirectory.createTempSync('flutter_mock_android_sdk.'); final Directory dir = fs.systemTempDirectory.createTempSync('flutter_mock_android_sdk.');
final String exe = platform.isWindows ? '.exe' : '';
_createSdkFile(dir, 'licenses/dummy');
_createSdkFile(dir, 'platform-tools/adb$exe');
_createSdkFile(dir, 'platform-tools/adb'); _createSdkFile(dir, 'build-tools/sda/aapt$exe');
_createSdkFile(dir, 'build-tools/af/aapt$exe');
_createSdkFile(dir, 'build-tools/sda/aapt'); _createSdkFile(dir, 'build-tools/ljkasd/aapt$exe');
_createSdkFile(dir, 'build-tools/af/aapt');
_createSdkFile(dir, 'build-tools/ljkasd/aapt');
_createSdkFile(dir, 'platforms/android-22/android.jar'); _createSdkFile(dir, 'platforms/android-22/android.jar');
_createSdkFile(dir, 'platforms/android-23/android.jar'); _createSdkFile(dir, 'platforms/android-23/android.jar');
......
...@@ -42,11 +42,12 @@ void main() { ...@@ -42,11 +42,12 @@ void main() {
return (List<String> command) => MockProcess(stdout: stdoutStream); return (List<String> command) => MockProcess(stdout: stdoutStream);
} }
testUsingContext('licensesAccepted throws if cannot run sdkmanager', () async { testUsingContext('licensesAccepted returns LicensesAccepted.unknown if cannot run sdkmanager', () async {
processManager.succeed = false; processManager.succeed = false;
when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager'); when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager');
final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(); final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator();
expect(licenseValidator.licensesAccepted, throwsToolExit()); final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
expect(licenseStatus, LicensesAccepted.unknown);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => sdk, AndroidSdk: () => sdk,
FileSystem: () => fs, FileSystem: () => fs,
...@@ -178,8 +179,27 @@ void main() { ...@@ -178,8 +179,27 @@ void main() {
Stdio: () => stdio, Stdio: () => stdio,
}); });
testUsingContext('detects license-only SDK installation', () async {
when(sdk.licensesAvailable).thenReturn(true);
when(sdk.platformToolsAvailable).thenReturn(false);
final ValidationResult validationResult = await AndroidValidator().validate();
expect(validationResult.type, ValidationType.partial);
expect(
validationResult.messages.last.message,
userMessages.androidSdkLicenseOnly(kAndroidHome),
);
}, overrides: <Type, Generator>{
AndroidSdk: () => sdk,
FileSystem: () => fs,
Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'},
ProcessManager: () => processManager,
Stdio: () => stdio,
});
testUsingContext('detects minium required SDK and buildtools', () async { testUsingContext('detects minium required SDK and buildtools', () async {
final AndroidSdkVersion mockSdkVersion = MockAndroidSdkVersion(); final AndroidSdkVersion mockSdkVersion = MockAndroidSdkVersion();
when(sdk.licensesAvailable).thenReturn(true);
when(sdk.platformToolsAvailable).thenReturn(true);
// Test with invalid SDK and build tools // Test with invalid SDK and build tools
when(mockSdkVersion.sdkLevel).thenReturn(26); when(mockSdkVersion.sdkLevel).thenReturn(26);
......
...@@ -3,18 +3,24 @@ ...@@ -3,18 +3,24 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:convert'; import 'dart:convert';
import 'dart:io' show ProcessResult;
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/application_package.dart'; import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/ios/ios_workflow.dart'; import 'package:flutter_tools/src/ios/ios_workflow.dart';
import 'package:process/process.dart';
import 'src/common.dart'; import 'src/common.dart';
import 'src/context.dart'; import 'src/context.dart';
...@@ -24,7 +30,78 @@ final Map<Type, Generator> noColorTerminalOverride = <Type, Generator>{ ...@@ -24,7 +30,78 @@ final Map<Type, Generator> noColorTerminalOverride = <Type, Generator>{
Platform: _kNoColorTerminalPlatform, Platform: _kNoColorTerminalPlatform,
}; };
class MockitoProcessManager extends Mock implements ProcessManager {}
class MockitoAndroidSdk extends Mock implements AndroidSdk {}
class MockitoAndroidSdkVersion extends Mock implements AndroidSdkVersion {}
void main() { void main() {
group('Apk with partial Android SDK works', () {
AndroidSdk sdk;
ProcessManager mockProcessManager;
MemoryFileSystem fs;
File gradle;
final Map<Type, Generator> overrides = <Type, Generator>{
AndroidSdk: () => sdk,
ProcessManager: () => mockProcessManager,
FileSystem: () => fs,
};
setUp(() async {
sdk = MockitoAndroidSdk();
mockProcessManager = MockitoProcessManager();
fs = MemoryFileSystem();
Cache.flutterRoot = '../..';
when(sdk.licensesAvailable).thenReturn(true);
when(mockProcessManager.canRun(any)).thenReturn(true);
when(mockProcessManager.run(
any,
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(1, 0, 'stdout', 'stderr'));
when(mockProcessManager.runSync(any)).thenReturn(ProcessResult(1, 0, 'stdout', 'stderr'));
final FlutterProject project = await FlutterProject.current();
gradle = fs.file(project.android.hostAppGradleRoot.childFile(
platform.isWindows ? 'gradlew.bat' : 'gradlew',
).path)..createSync(recursive: true);
});
testUsingContext('Licenses available, build tools not, apk exists', () async {
when(sdk.latestVersion).thenReturn(null);
final FlutterProject project = await FlutterProject.current();
final File gradle = project.android.hostAppGradleRoot.childFile(
platform.isWindows ? 'gradlew.bat' : 'gradlew',
)..createSync(recursive: true);
await ApplicationPackageFactory.instance.getPackageForPlatform(
TargetPlatform.android_arm,
applicationBinary: fs.file('app.apk'),
);
verify(
mockProcessManager.run(
argThat(equals(<String>[gradle.path, 'dependencies'])),
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
),
).called(1);
}, overrides: overrides);
testUsingContext('Licenses available, build tools available, does not call gradle dependencies', () async {
final AndroidSdkVersion sdkVersion = MockitoAndroidSdkVersion();
when(sdk.latestVersion).thenReturn(sdkVersion);
await ApplicationPackageFactory.instance.getPackageForPlatform(
TargetPlatform.android_arm,
);
verifyNever(
mockProcessManager.run(
argThat(equals(<String>[gradle.path, 'dependencies'])),
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
),
);
}, overrides: overrides);
});
group('ApkManifestData', () { group('ApkManifestData', () {
test('Parses manifest with an Activity that has enabled set to true, action set to android.intent.action.MAIN and category set to android.intent.category.LAUNCHER', () { test('Parses manifest with an Activity that has enabled set to true, action set to android.intent.action.MAIN and category set to android.intent.category.LAUNCHER', () {
final ApkManifestData data = ApkManifestData.parseFromXmlDump(_aaptDataWithExplicitEnabledAndMainLauncherActivity); final ApkManifestData data = ApkManifestData.parseFromXmlDump(_aaptDataWithExplicitEnabledAndMainLauncherActivity);
......
...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/android/android_sdk.dart' show AndroidSdk; ...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/android/android_sdk.dart' show AndroidSdk;
import 'package:flutter_tools/src/application_package.dart'; import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/file_system.dart' hide IOSink; import 'package:flutter_tools/src/base/file_system.dart' hide IOSink;
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/devfs.dart';
...@@ -44,16 +45,26 @@ class MockAndroidSdk extends Mock implements AndroidSdk { ...@@ -44,16 +45,26 @@ class MockAndroidSdk extends Mock implements AndroidSdk {
int ndkVersion = 16, int ndkVersion = 16,
bool withNdkSysroot = false, bool withNdkSysroot = false,
bool withSdkManager = true, bool withSdkManager = true,
bool withPlatformTools = true,
bool withBuildTools = true,
}) { }) {
final Directory dir = fs.systemTempDirectory.createTempSync('flutter_mock_android_sdk.'); final Directory dir = fs.systemTempDirectory.createTempSync('flutter_mock_android_sdk.');
final String exe = platform.isWindows ? '.exe' : '';
final String bat = platform.isWindows ? '.bat' : '';
_createSdkFile(dir, 'platform-tools/adb'); _createDir(dir, 'licenses');
_createSdkFile(dir, 'build-tools/19.1.0/aapt'); if (withPlatformTools) {
_createSdkFile(dir, 'build-tools/22.0.1/aapt'); _createSdkFile(dir, 'platform-tools/adb$exe');
_createSdkFile(dir, 'build-tools/23.0.2/aapt'); }
if (withBuildTools) {
_createSdkFile(dir, 'build-tools/19.1.0/aapt$exe');
_createSdkFile(dir, 'build-tools/22.0.1/aapt$exe');
_createSdkFile(dir, 'build-tools/23.0.2/aapt$exe');
if (withAndroidN) if (withAndroidN)
_createSdkFile(dir, 'build-tools/24.0.0-preview/aapt'); _createSdkFile(dir, 'build-tools/24.0.0-preview/aapt$exe');
}
_createSdkFile(dir, 'platforms/android-22/android.jar'); _createSdkFile(dir, 'platforms/android-22/android.jar');
_createSdkFile(dir, 'platforms/android-23/android.jar'); _createSdkFile(dir, 'platforms/android-23/android.jar');
...@@ -63,7 +74,7 @@ class MockAndroidSdk extends Mock implements AndroidSdk { ...@@ -63,7 +74,7 @@ class MockAndroidSdk extends Mock implements AndroidSdk {
} }
if (withSdkManager) if (withSdkManager)
_createSdkFile(dir, 'tools/bin/sdkmanager'); _createSdkFile(dir, 'tools/bin/sdkmanager$bat');
if (withNdkDir != null) { if (withNdkDir != null) {
final String ndkToolchainBin = fs.path.join( final String ndkToolchainBin = fs.path.join(
......
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