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;
This commit is contained in:
committed by
GitHub
parent
f9bb4289e9
commit
4931b46772
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user