Unverified Commit 4931b467 authored by Vyacheslav Egorov's avatar Vyacheslav Egorov Committed by GitHub

Make --build-shared-library more robust. (#17420)

* Search for a suitable ARM sysroot instead of hardcoding it;
* Add facility to explain why NDK was not found;
parent f9bb4289
...@@ -104,9 +104,122 @@ String getAvdPath() { ...@@ -104,9 +104,122 @@ String getAvdPath() {
); );
} }
class AndroidNdkSearchError {
AndroidNdkSearchError(this.reason);
/// The message explaining why NDK was not found.
final String reason;
}
class AndroidNdk {
AndroidNdk._(this.directory, this.compiler, this.compilerArgs);
/// The path to the NDK.
final String directory;
/// The path to the NDK compiler.
final String compiler;
/// The mandatory arguments to the NDK compiler.
final List<String> compilerArgs;
/// Locate NDK within the given SDK or throw [AndroidNdkSearchError].
static AndroidNdk locateNdk(String androidHomeDir) {
if (androidHomeDir == null) {
throw new AndroidNdkSearchError('Can not locate NDK because no SDK is found');
}
String findBundle(String androidHomeDir) {
final String ndkDirectory = fs.path.join(androidHomeDir, 'ndk-bundle');
if (!fs.isDirectorySync(ndkDirectory)) {
throw new AndroidNdkSearchError('Can not locate ndk-bundle, tried: $ndkDirectory');
}
return ndkDirectory;
}
String findCompiler(String ndkDirectory) {
String directory;
if (platform.isLinux) {
directory = 'linux-x86_64';
} else if (platform.isMacOS) {
directory = 'darwin-x86_64';
} else {
throw new AndroidNdkSearchError('Only Linux and macOS are supported');
}
final String ndkCompiler = fs.path.join(ndkDirectory,
'toolchains', 'arm-linux-androideabi-4.9', 'prebuilt', directory,
'bin', 'arm-linux-androideabi-gcc');
if (!fs.isFileSync(ndkCompiler)) {
throw new AndroidNdkSearchError('Can not locate GCC binary, tried $ndkCompiler');
}
return ndkCompiler;
}
List<String> findSysroot(String ndkDirectory) {
// If entity represents directory with name android-<version> that
// contains arch-arm subdirectory then returns version, otherwise
// returns null.
int toPlatformVersion(FileSystemEntity entry) {
if (entry is! Directory) {
return null;
}
if (!fs.isDirectorySync(fs.path.join(entry.path, 'arch-arm'))) {
return null;
}
final String name = fs.path.basename(entry.path);
const String platformPrefix = 'android-';
if (!name.startsWith(platformPrefix)) {
return null;
}
return int.tryParse(name.substring(platformPrefix.length));
}
final String platformsDir = fs.path.join(ndkDirectory, 'platforms');
final List<int> versions = fs
.directory(platformsDir)
.listSync()
.map(toPlatformVersion)
.where((int version) => version != null)
.toList(growable: false);
versions.sort();
final int suitableVersion = versions
.firstWhere((int version) => version >= 9, orElse: () => null);
if (suitableVersion == null) {
throw new AndroidNdkSearchError('Can not locate a suitable platform ARM sysroot (need android-9 or newer), tried to look in $platformsDir');
}
final String armPlatform = fs.path.join(ndkDirectory, 'platforms',
'android-$suitableVersion', 'arch-arm');
return <String>['--sysroot', armPlatform];
}
final String ndkDir = findBundle(androidHomeDir);
final String ndkCompiler = findCompiler(ndkDir);
final List<String> ndkCompilerArgs = findSysroot(ndkDir);
return new AndroidNdk._(ndkDir, ndkCompiler, ndkCompilerArgs);
}
/// Returns a descriptive message explaining why NDK can not be found within
/// the given SDK.
static String explainMissingNdk(String androidHomeDir) {
try {
locateNdk(androidHomeDir);
return 'Unexpected error: found NDK on the second try';
} on AndroidNdkSearchError catch (e) {
return e.reason;
}
}
}
class AndroidSdk { class AndroidSdk {
AndroidSdk(this.directory, [this.ndkDirectory, this.ndkCompiler, AndroidSdk(this.directory, [this.ndk]) {
this.ndkCompilerArgs]) {
_init(); _init();
} }
...@@ -116,14 +229,8 @@ class AndroidSdk { ...@@ -116,14 +229,8 @@ class AndroidSdk {
/// The path to the Android SDK. /// The path to the Android SDK.
final String directory; final String directory;
/// The path to the NDK (can be `null`). /// Android NDK (can be `null`).
final String ndkDirectory; final AndroidNdk ndk;
/// 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; List<AndroidSdkVersion> _sdkVersions;
AndroidSdkVersion _latestVersion; AndroidSdkVersion _latestVersion;
...@@ -176,41 +283,6 @@ class AndroidSdk { ...@@ -176,41 +283,6 @@ class AndroidSdk {
return null; return null;
} }
String findNdk(String androidHomeDir) {
final String ndkDirectory = fs.path.join(androidHomeDir, 'ndk-bundle');
if (fs.isDirectorySync(ndkDirectory)) {
return ndkDirectory;
}
return null;
}
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;
}
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;
}
final String androidHomeDir = findAndroidHomeDir(); final String androidHomeDir = findAndroidHomeDir();
if (androidHomeDir == null) { if (androidHomeDir == null) {
// No dice. // No dice.
...@@ -219,20 +291,15 @@ class AndroidSdk { ...@@ -219,20 +291,15 @@ class AndroidSdk {
} }
// Try to find the NDK compiler. If we can't find it, it's also ok. // Try to find the NDK compiler. If we can't find it, it's also ok.
final String ndkDir = findNdk(androidHomeDir); AndroidNdk ndk;
String ndkCompiler; try {
List<String> ndkCompilerArgs; ndk = AndroidNdk.locateNdk(androidHomeDir);
if (ndkDir != null) { } on AndroidNdkSearchError {
ndkCompiler = findNdkCompiler(ndkDir); // Ignore AndroidNdkSearchError's but don't ignore any other
if (ndkCompiler != null) { // exceptions.
ndkCompilerArgs = computeNdkCompilerArgs(ndkDir);
if (ndkCompilerArgs == null) {
ndkCompiler = null;
}
}
} }
return new AndroidSdk(androidHomeDir, ndkDir, ndkCompiler, ndkCompilerArgs); return new AndroidSdk(androidHomeDir, ndk);
} }
static bool validSdkDirectory(String dir) { static bool validSdkDirectory(String dir) {
......
...@@ -100,9 +100,9 @@ class AndroidWorkflow extends DoctorValidator implements Workflow { ...@@ -100,9 +100,9 @@ class AndroidWorkflow extends DoctorValidator implements Workflow {
messages.add(new ValidationMessage('Android SDK at ${androidSdk.directory}')); messages.add(new ValidationMessage('Android SDK at ${androidSdk.directory}'));
messages.add(new ValidationMessage(androidSdk.ndkDirectory == null messages.add(new ValidationMessage(androidSdk.ndk == null
? 'Android NDK location not configured (optional; useful for native profiling support)' ? 'Android NDK location not configured (optional; useful for native profiling support)'
: 'Android NDK at ${androidSdk.ndkDirectory}')); : 'Android NDK at ${androidSdk.ndk.directory}'));
String sdkVersionText; String sdkVersionText;
if (androidSdk.latestVersion != null) { if (androidSdk.latestVersion != null) {
......
...@@ -379,7 +379,7 @@ Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String ta ...@@ -379,7 +379,7 @@ Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String ta
if (buildInfo.fileSystemScheme != null) if (buildInfo.fileSystemScheme != null)
command.add('-Pfilesystem-scheme=${buildInfo.fileSystemScheme}'); command.add('-Pfilesystem-scheme=${buildInfo.fileSystemScheme}');
} }
if (buildInfo.preferSharedLibrary && androidSdk.ndkCompiler != null) { if (buildInfo.preferSharedLibrary && androidSdk.ndk != null) {
command.add('-Pprefer-shared-library=true'); command.add('-Pprefer-shared-library=true');
} }
if (buildInfo.targetPlatform == TargetPlatform.android_arm64) if (buildInfo.targetPlatform == TargetPlatform.android_arm64)
......
...@@ -152,9 +152,13 @@ class AOTSnapshotter { ...@@ -152,9 +152,13 @@ class AOTSnapshotter {
if (platform == TargetPlatform.ios) if (platform == TargetPlatform.ios)
buildSharedLibrary = false; buildSharedLibrary = false;
if (buildSharedLibrary && androidSdk.ndkCompiler == null) { if (buildSharedLibrary && androidSdk.ndk == null) {
final String explanation = AndroidNdk.explainMissingNdk(androidSdk.directory);
printError( printError(
'Could not find NDK in Android SDK at ${androidSdk.directory}.\n' 'Could not find NDK in Android SDK at ${androidSdk.directory}:\n'
'\n'
' $explanation\n'
'\n'
'Unable to build with --build-shared-library\n' 'Unable to build with --build-shared-library\n'
'To install the NDK, see instructions at https://developer.android.com/ndk/guides/' 'To install the NDK, see instructions at https://developer.android.com/ndk/guides/'
); );
...@@ -344,8 +348,8 @@ class AOTSnapshotter { ...@@ -344,8 +348,8 @@ class AOTSnapshotter {
// (which causes it to not look into the other section and therefore not // (which causes it to not look into the other section and therefore not
// find the correct unwinding information). // find the correct unwinding information).
final String assemblySo = fs.path.join(outputPath, 'app.so'); final String assemblySo = fs.path.join(outputPath, 'app.so');
return await runCheckedAsync(<String>[androidSdk.ndkCompiler] return await runCheckedAsync(<String>[androidSdk.ndk.compiler]
..addAll(androidSdk.ndkCompilerArgs) ..addAll(androidSdk.ndk.compilerArgs)
..addAll(<String>[ '-shared', '-nostdlib', '-o', assemblySo, assemblyPath ])); ..addAll(<String>[ '-shared', '-nostdlib', '-o', assemblySo, assemblyPath ]));
} }
......
...@@ -132,9 +132,11 @@ void main() { ...@@ -132,9 +132,11 @@ void main() {
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk(); final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.directory, realSdkDir); expect(sdk.directory, realSdkDir);
expect(sdk.ndkDirectory, realNdkDir); print(AndroidNdk.explainMissingNdk(sdk.directory));
expect(sdk.ndkCompiler, realNdkCompiler); expect(sdk.ndk, isNotNull);
expect(sdk.ndkCompilerArgs, <String>['--sysroot', realNdkSysroot]); expect(sdk.ndk.directory, realNdkDir);
expect(sdk.ndk.compiler, realNdkCompiler);
expect(sdk.ndk.compilerArgs, <String>['--sysroot', realNdkSysroot]);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
Platform: () => new FakePlatform(operatingSystem: os), Platform: () => new FakePlatform(operatingSystem: os),
...@@ -149,9 +151,9 @@ void main() { ...@@ -149,9 +151,9 @@ void main() {
final String realSdkDir = sdkDir.path; final String realSdkDir = sdkDir.path;
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk(); final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.directory, realSdkDir); expect(sdk.directory, realSdkDir);
expect(sdk.ndkDirectory, null); expect(sdk.ndk, isNull);
expect(sdk.ndkCompiler, null); final String explanation = AndroidNdk.explainMissingNdk(sdk.directory);
expect(sdk.ndkCompilerArgs, null); expect(explanation, contains('Can not locate ndk-bundle'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
Platform: () => new FakePlatform(operatingSystem: os), Platform: () => new FakePlatform(operatingSystem: os),
......
...@@ -659,7 +659,7 @@ void main() { ...@@ -659,7 +659,7 @@ void main() {
testUsingContext('returns failure if buildSharedLibrary is true but no NDK is found', () async { testUsingContext('returns failure if buildSharedLibrary is true but no NDK is found', () async {
final String outputPath = fs.path.join('build', 'foo'); final String outputPath = fs.path.join('build', 'foo');
when(mockAndroidSdk.ndkCompiler).thenReturn(null); when(mockAndroidSdk.ndk).thenReturn(null);
final int genSnapshotExitCode = await snapshotter.build( final int genSnapshotExitCode = await snapshotter.build(
platform: TargetPlatform.android_arm, platform: TargetPlatform.android_arm,
......
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