Add support for NDK discovery and add --prefer-shared-library option (#12788)
* Add support for NDK discovery and add --prefer-shared-library option
We would like to be able to use native tools (e.g. simpleperf, gdb) with
precompiled flutter apps. The native tools work much better with *.so
files instead of the custom formats the Dart VM uses by default.
The reason for using blobs / instruction snapshots is that we do not
want to force flutter users to install the Android NDK.
This CL adds a `--prefer-shared-library` flag to e.g. `flutter build
apk` which will use the NDK compiler (if available) to turn the
precompiled app assembly file to an `*.so` file. If the NDK compiler is
not available it will default to the default behavior.
* Rebase, add test for NDK detection, augment flutter.gradle with @Input for flag
* Use InMemoryFileSystem for test
* Remove unused import
* Address some analyzer warnings
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart
index fb88234..a3946d5 100644
--- a/packages/flutter_tools/lib/src/android/android_sdk.dart
+++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -57,63 +57,131 @@
}
class AndroidSdk {
- AndroidSdk(this.directory) {
+ AndroidSdk(this.directory, [this.ndkDirectory, this.ndkCompiler,
+ this.ndkCompilerArgs]) {
_init();
}
+ /// The path to the Android SDK.
final String directory;
+ /// The path to the NDK (can be `null`).
+ final String ndkDirectory;
+
+ /// The path to the NDK compiler (can be `null`).
+ final String ndkCompiler;
+
+ /// The mandatory arguments to the NDK compiler (can be `null`).
+ final List<String> ndkCompilerArgs;
+
List<AndroidSdkVersion> _sdkVersions;
AndroidSdkVersion _latestVersion;
static AndroidSdk locateAndroidSdk() {
- String androidHomeDir;
+ String findAndroidHomeDir() {
+ String androidHomeDir;
+ if (config.containsKey('android-sdk')) {
+ androidHomeDir = config.getValue('android-sdk');
+ } else if (platform.environment.containsKey(kAndroidHome)) {
+ androidHomeDir = platform.environment[kAndroidHome];
+ } else if (platform.isLinux) {
+ if (homeDirPath != null)
+ androidHomeDir = fs.path.join(homeDirPath, 'Android', 'Sdk');
+ } else if (platform.isMacOS) {
+ if (homeDirPath != null)
+ androidHomeDir = fs.path.join(homeDirPath, 'Library', 'Android', 'sdk');
+ } else if (platform.isWindows) {
+ if (homeDirPath != null)
+ androidHomeDir = fs.path.join(homeDirPath, 'AppData', 'Local', 'Android', 'sdk');
+ }
- if (config.containsKey('android-sdk')) {
- androidHomeDir = config.getValue('android-sdk');
- } else if (platform.environment.containsKey(kAndroidHome)) {
- androidHomeDir = platform.environment[kAndroidHome];
- } else if (platform.isLinux) {
- if (homeDirPath != null)
- androidHomeDir = fs.path.join(homeDirPath, 'Android', 'Sdk');
- } else if (platform.isMacOS) {
- if (homeDirPath != null)
- androidHomeDir = fs.path.join(homeDirPath, 'Library', 'Android', 'sdk');
- } else if (platform.isWindows) {
- if (homeDirPath != null)
- androidHomeDir = fs.path.join(homeDirPath, 'AppData', 'Local', 'Android', 'sdk');
+ if (androidHomeDir != null) {
+ if (validSdkDirectory(androidHomeDir))
+ return androidHomeDir;
+ if (validSdkDirectory(fs.path.join(androidHomeDir, 'sdk')))
+ return fs.path.join(androidHomeDir, 'sdk');
+ }
+
+ // in build-tools/$version/aapt
+ final List<File> aaptBins = os.whichAll('aapt');
+ for (File aaptBin in aaptBins) {
+ // Make sure we're using the aapt from the SDK.
+ aaptBin = fs.file(aaptBin.resolveSymbolicLinksSync());
+ final String dir = aaptBin.parent.parent.parent.path;
+ if (validSdkDirectory(dir))
+ return dir;
+ }
+
+ // in platform-tools/adb
+ final List<File> adbBins = os.whichAll('adb');
+ for (File adbBin in adbBins) {
+ // Make sure we're using the adb from the SDK.
+ adbBin = fs.file(adbBin.resolveSymbolicLinksSync());
+ final String dir = adbBin.parent.parent.path;
+ if (validSdkDirectory(dir))
+ return dir;
+ }
+
+ return null;
}
- if (androidHomeDir != null) {
- if (validSdkDirectory(androidHomeDir))
- return new AndroidSdk(androidHomeDir);
- if (validSdkDirectory(fs.path.join(androidHomeDir, 'sdk')))
- return new AndroidSdk(fs.path.join(androidHomeDir, 'sdk'));
+ String findNdk(String androidHomeDir) {
+ final String ndkDirectory = fs.path.join(androidHomeDir, 'ndk-bundle');
+ if (fs.isDirectorySync(ndkDirectory)) {
+ return ndkDirectory;
+ }
+ return null;
}
- // in build-tools/$version/aapt
- final List<File> aaptBins = os.whichAll('aapt');
- for (File aaptBin in aaptBins) {
- // Make sure we're using the aapt from the SDK.
- aaptBin = fs.file(aaptBin.resolveSymbolicLinksSync());
- final String dir = aaptBin.parent.parent.parent.path;
- if (validSdkDirectory(dir))
- return new AndroidSdk(dir);
+ String findNdkCompiler(String ndkDirectory) {
+ String directory;
+ if (platform.isLinux) {
+ directory = 'linux-x86_64';
+ } else if (platform.isMacOS) {
+ directory = 'darwin-x86_64';
+ }
+ if (directory != null) {
+ final String ndkCompiler = fs.path.join(ndkDirectory,
+ 'toolchains', 'arm-linux-androideabi-4.9', 'prebuilt', directory,
+ 'bin', 'arm-linux-androideabi-gcc');
+ if (fs.isFileSync(ndkCompiler)) {
+ return ndkCompiler;
+ }
+ }
+ return null;
}
- // in platform-tools/adb
- final List<File> adbBins = os.whichAll('adb');
- for (File adbBin in adbBins) {
- // Make sure we're using the adb from the SDK.
- adbBin = fs.file(adbBin.resolveSymbolicLinksSync());
- final String dir = adbBin.parent.parent.path;
- if (validSdkDirectory(dir))
- return new AndroidSdk(dir);
+ List<String> computeNdkCompilerArgs(String ndkDirectory) {
+ final String armPlatform = fs.path.join(ndkDirectory, 'platforms',
+ 'android-9', 'arch-arm');
+ if (fs.isDirectorySync(armPlatform)) {
+ return <String>['--sysroot', armPlatform];
+ }
+ return null;
}
- // No dice.
- printTrace('Unable to locate an Android SDK.');
- return null;
+ final String androidHomeDir = findAndroidHomeDir();
+ if (androidHomeDir == null) {
+ // No dice.
+ printTrace('Unable to locate an Android SDK.');
+ return null;
+ }
+
+ // Try to find the NDK compiler. If we can't find it, it's also ok.
+ final String ndkDir = findNdk(androidHomeDir);
+ String ndkCompiler;
+ List<String> ndkCompilerArgs;
+ if (ndkDir != null) {
+ ndkCompiler = findNdkCompiler(ndkDir);
+ if (ndkCompiler != null) {
+ ndkCompilerArgs = computeNdkCompilerArgs(ndkDir);
+ if (ndkCompilerArgs == null) {
+ ndkCompiler = null;
+ }
+ }
+ }
+
+ return new AndroidSdk(androidHomeDir, ndkDir, ndkCompiler, ndkCompilerArgs);
}
static bool validSdkDirectory(String dir) {