Fix androidSdk NPE (#47187)
diff --git a/packages/flutter_tools/lib/src/android/android_builder.dart b/packages/flutter_tools/lib/src/android/android_builder.dart
index 50e2f1b..ec21efa 100644
--- a/packages/flutter_tools/lib/src/android/android_builder.dart
+++ b/packages/flutter_tools/lib/src/android/android_builder.dart
@@ -86,7 +86,7 @@
buildNumber: buildNumber,
);
} finally {
- androidSdk.reinitialize();
+ androidSdk?.reinitialize();
}
}
@@ -106,7 +106,7 @@
localGradleErrors: gradleErrors,
);
} finally {
- androidSdk.reinitialize();
+ androidSdk?.reinitialize();
}
}
@@ -126,7 +126,7 @@
localGradleErrors: gradleErrors,
);
} finally {
- androidSdk.reinitialize();
+ androidSdk?.reinitialize();
}
}
}
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart
index 7e744f7..7adc6c7 100644
--- a/packages/flutter_tools/lib/src/android/android_sdk.dart
+++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -55,7 +55,7 @@
if (sdk?.latestVersion == null) {
return os.which('adb')?.path;
} else {
- return sdk.adbPath;
+ return sdk?.adbPath;
}
}
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index 28ee994..6bfd93c 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -149,7 +149,7 @@
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
environment: gradleEnvironment,
);
- androidSdk.reinitialize();
+ androidSdk?.reinitialize();
progress.stop();
}
@@ -224,9 +224,13 @@
bool shouldBuildPluginAsAar = false,
int retries = 1,
}) async {
- if (androidSdk == null) {
- exitWithNoSdkMessage();
- }
+ assert(project != null);
+ assert(androidBuildInfo != null);
+ assert(target != null);
+ assert(isBuildingBundle != null);
+ assert(localGradleErrors != null);
+ assert(androidSdk != null);
+
if (!project.android.isUsingGradle) {
_exitWithProjectNotUsingGradleMessage();
}
@@ -489,10 +493,7 @@
assert(target != null);
assert(androidBuildInfo != null);
assert(outputDirectory != null);
-
- if (androidSdk == null) {
- exitWithNoSdkMessage();
- }
+ assert(androidSdk != null);
final FlutterManifest manifest = project.manifest;
if (!manifest.isModule && !manifest.isPlugin) {
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index cb761d0..b0e1f47 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -41,7 +41,7 @@
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
- if (androidSdk?.licensesAvailable == true && androidSdk.latestVersion == null) {
+ if (androidSdk?.licensesAvailable == true && androidSdk?.latestVersion == null) {
await checkGradleDependencies();
}
return applicationBinary == null
diff --git a/packages/flutter_tools/lib/src/commands/build_aar.dart b/packages/flutter_tools/lib/src/commands/build_aar.dart
index 96c7c1c..84a83d8 100644
--- a/packages/flutter_tools/lib/src/commands/build_aar.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aar.dart
@@ -5,6 +5,8 @@
import 'dart:async';
import '../android/android_builder.dart';
+import '../android/android_sdk.dart';
+import '../android/gradle_utils.dart';
import '../base/common.dart';
import '../base/os.dart';
import '../build_info.dart';
@@ -85,6 +87,9 @@
@override
Future<FlutterCommandResult> runCommand() async {
+ if (androidSdk == null) {
+ exitWithNoSdkMessage();
+ }
final Set<AndroidBuildInfo> androidBuildInfo = <AndroidBuildInfo>{};
final Iterable<AndroidArch> targetArchitectures =
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index 366ae57..05948bc 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -5,6 +5,8 @@
import 'dart:async';
import '../android/android_builder.dart';
+import '../android/android_sdk.dart';
+import '../android/gradle_utils.dart';
import '../base/terminal.dart';
import '../build_info.dart';
import '../cache.dart';
@@ -77,6 +79,9 @@
@override
Future<FlutterCommandResult> runCommand() async {
+ if (androidSdk == null) {
+ exitWithNoSdkMessage();
+ }
final BuildInfo buildInfo = getBuildInfo();
final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(
buildInfo,
diff --git a/packages/flutter_tools/lib/src/commands/build_appbundle.dart b/packages/flutter_tools/lib/src/commands/build_appbundle.dart
index 74c0f5c..7748e8d 100644
--- a/packages/flutter_tools/lib/src/commands/build_appbundle.dart
+++ b/packages/flutter_tools/lib/src/commands/build_appbundle.dart
@@ -5,6 +5,8 @@
import 'dart:async';
import '../android/android_builder.dart';
+import '../android/android_sdk.dart';
+import '../android/gradle_utils.dart';
import '../build_info.dart';
import '../cache.dart';
import '../project.dart';
@@ -69,6 +71,9 @@
@override
Future<FlutterCommandResult> runCommand() async {
+ if (androidSdk == null) {
+ exitWithNoSdkMessage();
+ }
final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(getBuildInfo(),
targetArchs: stringsArg('target-platform').map<AndroidArch>(getAndroidArchForName),
shrink: boolArg('shrink'),
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index 2671a03..798caa7 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -29,14 +29,15 @@
class FlutterProjectFactory {
FlutterProjectFactory();
- final Map<String, FlutterProject> _projects =
+ @visibleForTesting
+ final Map<String, FlutterProject> projects =
<String, FlutterProject>{};
/// Returns a [FlutterProject] view of the given directory or a ToolExit error,
/// if `pubspec.yaml` or `example/pubspec.yaml` is invalid.
FlutterProject fromDirectory(Directory directory) {
assert(directory != null);
- return _projects.putIfAbsent(directory.path, /* ifAbsent */ () {
+ return projects.putIfAbsent(directory.path, /* ifAbsent */ () {
final FlutterManifest manifest = FlutterProject._readManifest(
directory.childFile(bundle.defaultManifestPath).path,
);
diff --git a/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart b/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart
index f3ba526..303dcd0 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart
@@ -5,13 +5,12 @@
import 'dart:io' show Process, ProcessResult;
import 'package:args/command_runner.dart';
-import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build_aar.dart';
+import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
@@ -90,18 +89,14 @@
Directory tempDir;
AndroidSdk mockAndroidSdk;
Usage mockUsage;
- FileSystem memoryFileSystem;
setUp(() {
mockUsage = MockUsage();
when(mockUsage.isFirstRun).thenReturn(true);
- memoryFileSystem = MemoryFileSystem();
- tempDir = memoryFileSystem.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
- memoryFileSystem.currentDirectory = tempDir;
+ tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
mockProcessManager = MockProcessManager();
-
when(mockProcessManager.run(any,
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment')))
@@ -118,65 +113,49 @@
when(mockAndroidSdk.directory).thenReturn('irrelevant');
});
+ tearDown(() {
+ tryToDelete(tempDir);
+ });
+
group('AndroidSdk', () {
testUsingContext('validateSdkWellFormed() not called, sdk reinitialized', () async {
- final Directory gradleCacheDir = memoryFileSystem
- .directory('/flutter_root/bin/cache/artifacts/gradle_wrapper')
- ..createSync(recursive: true);
- gradleCacheDir.childFile(platform.isWindows ? 'gradlew.bat' : 'gradlew').createSync();
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--template=module']);
- tempDir.childFile('pubspec.yaml')
- ..createSync(recursive: true)
- ..writeAsStringSync('''name: test
-environment:
- sdk: ">=2.1.0 <3.0.0"
-dependencies:
- flutter:
- sdk: flutter
-dev_dependencies:
- flutter_test:
- sdk: flutter
-flutter:
- plugin:
- androidPackage: com.example.blah
- pluginClass: BlahPlugin
-''');
- tempDir.childFile('.packages').createSync(recursive: true);
- final Directory androidDir = tempDir.childDirectory('android');
- androidDir
- .childFile('build.gradle')
- .createSync(recursive: true);
- androidDir
- .childDirectory('app')
- .childFile('build.gradle')
- ..createSync(recursive: true)
- ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
- androidDir
- .childFile('gradle.properties')
- .createSync(recursive: true);
- androidDir
- .childDirectory('gradle')
- .childDirectory('wrapper')
- .childFile('gradle-wrapper.properties')
- .createSync(recursive: true);
- tempDir
- .childDirectory('build')
- .childDirectory('outputs')
- .childDirectory('repo')
- .createSync(recursive: true);
- tempDir
- .childDirectory('lib')
- .childFile('main.dart')
- .createSync(recursive: true);
- await runBuildAarCommand(tempDir.path);
+ await expectLater(
+ runBuildAarCommand(
+ projectPath,
+ arguments: <String>['--no-pub'],
+ ),
+ throwsToolExit(),
+ );
verifyNever(mockAndroidSdk.validateSdkWellFormed());
verify(mockAndroidSdk.reinitialize()).called(1);
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
- FileSystem: () => memoryFileSystem,
+ });
+
+ testUsingContext('throws throwsToolExit if AndroidSdk is null', () async {
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--template=module']);
+
+ await expectLater(() async {
+ await runBuildAarCommand(
+ projectPath,
+ arguments: <String>['--no-pub'],
+ );
+ }, throwsToolExit(
+ message: '[!] No Android SDK found. Try setting the ANDROID_HOME environment variable',
+ ));
+ },
+ overrides: <Type, Generator>{
+ AndroidSdk: () => null,
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
+ ProcessManager: () => mockProcessManager,
});
});
});
@@ -191,7 +170,6 @@
await runner.run(<String>[
'aar',
'--no-pub',
- '--flutter-root=/flutter_root',
...?arguments,
fs.path.join(target, 'lib', 'main.dart'),
]);
diff --git a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
index 29c49f6..88900ab 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
@@ -5,7 +5,6 @@
import 'dart:io';
import 'package:args/command_runner.dart';
-import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/context.dart';
@@ -140,70 +139,15 @@
});
group('AndroidSdk', () {
- FileSystem memoryFileSystem;
- setUp(() {
- memoryFileSystem = MemoryFileSystem();
-
- tempDir = memoryFileSystem.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
- memoryFileSystem.currentDirectory = tempDir;
-
- gradlew = memoryFileSystem.path.join(tempDir.path, 'flutter_project', 'android',
- platform.isWindows ? 'gradlew.bat' : 'gradlew');
- });
testUsingContext('validateSdkWellFormed() not called, sdk reinitialized', () async {
- final Directory gradleCacheDir = memoryFileSystem
- .directory('/flutter_root/bin/cache/artifacts/gradle_wrapper')
- ..createSync(recursive: true);
-
- gradleCacheDir.childFile(platform.isWindows ? 'gradlew.bat' : 'gradlew').createSync();
-
- tempDir.childFile('pubspec.yaml')
- ..createSync(recursive: true)
- ..writeAsStringSync('''name: test
-environment:
- sdk: ">=2.1.0 <3.0.0"
-dependencies:
- flutter:
- sdk: flutter
-dev_dependencies:
- flutter_test:
- sdk: flutter
-flutter:
-''');
- tempDir.childFile('.packages').createSync(recursive: true);
- final Directory androidDir = tempDir.childDirectory('android');
- androidDir
- .childFile('build.gradle')
- .createSync(recursive: true);
- androidDir
- .childDirectory('app')
- .childFile('build.gradle')
- ..createSync(recursive: true)
- ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
- androidDir
- .childFile('gradle.properties')
- .createSync(recursive: true);
- androidDir
- .childDirectory('gradle')
- .childDirectory('wrapper')
- .childFile('gradle-wrapper.properties')
- .createSync(recursive: true);
- tempDir
- .childDirectory('build')
- .childDirectory('outputs')
- .childDirectory('repo')
- .createSync(recursive: true);
- tempDir
- .childDirectory('lib')
- .childFile('main.dart')
- .createSync(recursive: true);
- when(mockProcessManager.run(any,
- workingDirectory: anyNamed('workingDirectory'),
- environment: anyNamed('environment')))
- .thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(0, 0, 'any', '')));
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--template=app']);
await expectLater(
- runBuildApkCommand(tempDir.path, arguments: <String>['--no-pub', '--flutter-root=/flutter_root']),
+ runBuildApkCommand(
+ projectPath,
+ arguments: <String>['--no-pub'],
+ ),
throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1'),
);
@@ -212,7 +156,26 @@
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
- FileSystem: () => memoryFileSystem,
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
+ ProcessManager: () => mockProcessManager,
+ });
+
+ testUsingContext('throws throwsToolExit if AndroidSdk is null', () async {
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--template=app']);
+
+ await expectLater(() async {
+ await runBuildApkCommand(
+ projectPath,
+ arguments: <String>['--no-pub'],
+ );
+ }, throwsToolExit(
+ message: '[!] No Android SDK found. Try setting the ANDROID_HOME environment variable',
+ ));
+ },
+ overrides: <Type, Generator>{
+ AndroidSdk: () => null,
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
});
});
@@ -451,18 +414,6 @@
return command;
}
-class FakeFlutterProjectFactory extends FlutterProjectFactory {
- FakeFlutterProjectFactory(this.directoryOverride) :
- assert(directoryOverride != null);
-
- final Directory directoryOverride;
-
- @override
- FlutterProject fromDirectory(Directory _) {
- return super.fromDirectory(directoryOverride.childDirectory('flutter_project'));
- }
-}
-
class MockAndroidSdk extends Mock implements AndroidSdk {}
class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {}
diff --git a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
index 1acf9e8..bccbd0d 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
@@ -5,7 +5,6 @@
import 'dart:io';
import 'package:args/command_runner.dart';
-import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/context.dart';
@@ -125,67 +124,15 @@
});
group('AndroidSdk', () {
- FileSystem memoryFileSystem;
-
- setUp(() {
- memoryFileSystem = MemoryFileSystem();
-
- tempDir = memoryFileSystem.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
- memoryFileSystem.currentDirectory = tempDir;
-
- gradlew = memoryFileSystem.path.join(tempDir.path, 'flutter_project', 'android',
- platform.isWindows ? 'gradlew.bat' : 'gradlew');
- });
-
testUsingContext('validateSdkWellFormed() not called, sdk reinitialized', () async {
- final Directory gradleCacheDir = memoryFileSystem
- .directory('/flutter_root/bin/cache/artifacts/gradle_wrapper')
- ..createSync(recursive: true);
- gradleCacheDir.childFile(platform.isWindows ? 'gradlew.bat' : 'gradlew').createSync();
-
- tempDir.childFile('pubspec.yaml')
- ..createSync(recursive: true)
- ..writeAsStringSync('''name: test
-environment:
- sdk: ">=2.1.0 <3.0.0"
-dependencies:
- flutter:
- sdk: flutter
-dev_dependencies:
- flutter_test:
- sdk: flutter
-flutter:
-''');
- tempDir.childFile('.packages').createSync(recursive: true);
- final Directory androidDir = tempDir.childDirectory('android');
- androidDir.childFile('build.gradle').createSync(recursive: true);
- androidDir
- .childDirectory('app')
- .childFile('build.gradle')
- ..createSync(recursive: true)
- ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
- androidDir
- .childFile('gradle.properties')
- .createSync(recursive: true);
- androidDir
- .childDirectory('gradle')
- .childDirectory('wrapper')
- .childFile('gradle-wrapper.properties')
- .createSync(recursive: true);
- tempDir.childDirectory('build')
- .childDirectory('outputs')
- .childDirectory('repo')
- .createSync(recursive: true);
- tempDir.childDirectory('lib')
- .childFile('main.dart')
- .createSync(recursive: true);
- when(mockProcessManager.run(any,
- workingDirectory: anyNamed('workingDirectory'),
- environment: anyNamed('environment')))
- .thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(0, 0, 'any', '')));
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--template=app']);
await expectLater(
- runBuildAppBundleCommand(tempDir.path, arguments: <String>['--no-pub', '--flutter-root=/flutter_root']),
+ runBuildAppBundleCommand(
+ projectPath,
+ arguments: <String>['--no-pub'],
+ ),
throwsToolExit(message: 'Gradle task bundleRelease failed with exit code 1'),
);
@@ -194,8 +141,27 @@
},
overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
ProcessManager: () => mockProcessManager,
- FileSystem: () => memoryFileSystem,
+ });
+
+ testUsingContext('throws throwsToolExit if AndroidSdk is null', () async {
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--template=app']);
+
+ await expectLater(() async {
+ await runBuildAppBundleCommand(
+ projectPath,
+ arguments: <String>['--no-pub'],
+ );
+ }, throwsToolExit(
+ message: '[!] No Android SDK found. Try setting the ANDROID_HOME environment variable',
+ ));
+ },
+ overrides: <Type, Generator>{
+ AndroidSdk: () => null,
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
+ ProcessManager: () => mockProcessManager,
});
});
@@ -437,18 +403,6 @@
return command;
}
-class FakeFlutterProjectFactory extends FlutterProjectFactory {
- FakeFlutterProjectFactory(this._directoryOverride) :
- assert(_directoryOverride != null);
-
- final Directory _directoryOverride;
-
- @override
- FlutterProject fromDirectory(Directory _) {
- return super.fromDirectory(_directoryOverride.childDirectory('flutter_project'));
- }
-}
-
class MockAndroidSdk extends Mock implements AndroidSdk {}
class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {}
diff --git a/packages/flutter_tools/test/src/android_common.dart b/packages/flutter_tools/test/src/android_common.dart
index c156d5a..01eec17 100644
--- a/packages/flutter_tools/test/src/android_common.dart
+++ b/packages/flutter_tools/test/src/android_common.dart
@@ -5,6 +5,7 @@
import 'package:meta/meta.dart';
import 'package:flutter_tools/src/android/android_builder.dart';
+import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/project.dart';
@@ -33,3 +34,18 @@
@required String target,
}) async {}
}
+
+/// Creates a [FlutterProject] in a directory named [flutter_project]
+/// within [directoryOverride].
+class FakeFlutterProjectFactory extends FlutterProjectFactory {
+ FakeFlutterProjectFactory(this.directoryOverride) :
+ assert(directoryOverride != null);
+
+ final Directory directoryOverride;
+
+ @override
+ FlutterProject fromDirectory(Directory _) {
+ projects.clear();
+ return super.fromDirectory(directoryOverride.childDirectory('flutter_project'));
+ }
+}