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() {
);
}
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 {
AndroidSdk(this.directory, [this.ndkDirectory, this.ndkCompiler,
this.ndkCompilerArgs]) {
AndroidSdk(this.directory, [this.ndk]) {
_init();
}
......@@ -116,14 +229,8 @@ class AndroidSdk {
/// 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;
/// Android NDK (can be `null`).
final AndroidNdk ndk;
List<AndroidSdkVersion> _sdkVersions;
AndroidSdkVersion _latestVersion;
......@@ -176,41 +283,6 @@ class AndroidSdk {
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();
if (androidHomeDir == null) {
// No dice.
......@@ -219,20 +291,15 @@ class AndroidSdk {
}
// 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;
}
}
AndroidNdk ndk;
try {
ndk = AndroidNdk.locateNdk(androidHomeDir);
} on AndroidNdkSearchError {
// Ignore AndroidNdkSearchError's but don't ignore any other
// exceptions.
}
return new AndroidSdk(androidHomeDir, ndkDir, ndkCompiler, ndkCompilerArgs);
return new AndroidSdk(androidHomeDir, ndk);
}
static bool validSdkDirectory(String dir) {
......
......@@ -100,9 +100,9 @@ class AndroidWorkflow extends DoctorValidator implements Workflow {
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 at ${androidSdk.ndkDirectory}'));
: 'Android NDK at ${androidSdk.ndk.directory}'));
String sdkVersionText;
if (androidSdk.latestVersion != null) {
......
......@@ -379,7 +379,7 @@ Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String ta
if (buildInfo.fileSystemScheme != null)
command.add('-Pfilesystem-scheme=${buildInfo.fileSystemScheme}');
}
if (buildInfo.preferSharedLibrary && androidSdk.ndkCompiler != null) {
if (buildInfo.preferSharedLibrary && androidSdk.ndk != null) {
command.add('-Pprefer-shared-library=true');
}
if (buildInfo.targetPlatform == TargetPlatform.android_arm64)
......
......@@ -152,9 +152,13 @@ class AOTSnapshotter {
if (platform == TargetPlatform.ios)
buildSharedLibrary = false;
if (buildSharedLibrary && androidSdk.ndkCompiler == null) {
if (buildSharedLibrary && androidSdk.ndk == null) {
final String explanation = AndroidNdk.explainMissingNdk(androidSdk.directory);
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'
'To install the NDK, see instructions at https://developer.android.com/ndk/guides/'
);
......@@ -344,8 +348,8 @@ class AOTSnapshotter {
// (which causes it to not look into the other section and therefore not
// find the correct unwinding information).
final String assemblySo = fs.path.join(outputPath, 'app.so');
return await runCheckedAsync(<String>[androidSdk.ndkCompiler]
..addAll(androidSdk.ndkCompilerArgs)
return await runCheckedAsync(<String>[androidSdk.ndk.compiler]
..addAll(androidSdk.ndk.compilerArgs)
..addAll(<String>[ '-shared', '-nostdlib', '-o', assemblySo, assemblyPath ]));
}
......
......@@ -132,9 +132,11 @@ void main() {
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.directory, realSdkDir);
expect(sdk.ndkDirectory, realNdkDir);
expect(sdk.ndkCompiler, realNdkCompiler);
expect(sdk.ndkCompilerArgs, <String>['--sysroot', realNdkSysroot]);
print(AndroidNdk.explainMissingNdk(sdk.directory));
expect(sdk.ndk, isNotNull);
expect(sdk.ndk.directory, realNdkDir);
expect(sdk.ndk.compiler, realNdkCompiler);
expect(sdk.ndk.compilerArgs, <String>['--sysroot', realNdkSysroot]);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: () => new FakePlatform(operatingSystem: os),
......@@ -149,9 +151,9 @@ void main() {
final String realSdkDir = sdkDir.path;
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.directory, realSdkDir);
expect(sdk.ndkDirectory, null);
expect(sdk.ndkCompiler, null);
expect(sdk.ndkCompilerArgs, null);
expect(sdk.ndk, isNull);
final String explanation = AndroidNdk.explainMissingNdk(sdk.directory);
expect(explanation, contains('Can not locate ndk-bundle'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: () => new FakePlatform(operatingSystem: os),
......
......@@ -659,7 +659,7 @@ void main() {
testUsingContext('returns failure if buildSharedLibrary is true but no NDK is found', () async {
final String outputPath = fs.path.join('build', 'foo');
when(mockAndroidSdk.ndkCompiler).thenReturn(null);
when(mockAndroidSdk.ndk).thenReturn(null);
final int genSnapshotExitCode = await snapshotter.build(
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