Unverified Commit 8f65ee9f authored by Vyacheslav Egorov's avatar Vyacheslav Egorov Committed by GitHub

Fix --build-shared-library on newer NDKs (#26642)

Newer NDKs switched to clang which by default uses system linker, instead
we need to force it to use appropriate toolchain linker by passing
-fuse-ld= command line flag.

Fixes #23458
parent a172de01
......@@ -139,7 +139,8 @@ class AndroidNdk {
return ndkDirectory;
}
String findCompiler(String ndkDirectory) {
// Returns list that contains toolchain bin folder and compiler binary name.
List<String> findToolchainAndCompiler(String ndkDirectory) {
String directory;
if (platform.isLinux) {
directory = 'linux-x86_64';
......@@ -149,14 +150,16 @@ class AndroidNdk {
throw AndroidNdkSearchError('Only Linux and macOS are supported');
}
final String ndkCompiler = fs.path.join(ndkDirectory,
final String toolchainBin = fs.path.join(ndkDirectory,
'toolchains', 'arm-linux-androideabi-4.9', 'prebuilt', directory,
'bin', 'arm-linux-androideabi-gcc');
'bin');
final String ndkCompiler = fs.path.join(toolchainBin,
'arm-linux-androideabi-gcc');
if (!fs.isFileSync(ndkCompiler)) {
throw AndroidNdkSearchError('Can not locate GCC binary, tried $ndkCompiler');
}
return ndkCompiler;
return <String>[toolchainBin, ndkCompiler];
}
List<String> findSysroot(String ndkDirectory) {
......@@ -202,9 +205,48 @@ class AndroidNdk {
return <String>['--sysroot', armPlatform];
}
int findNdkMajorVersion(String ndkDirectory) {
final String propertiesFile = fs.path.join(ndkDirectory, 'source.properties');
if (!fs.isFileSync(propertiesFile)) {
throw AndroidNdkSearchError('Can not establish ndk-bundle version: $propertiesFile not found');
}
// Parse source.properties: each line has Key = Value format.
final Iterable<String> propertiesFileLines = fs.file(propertiesFile)
.readAsStringSync()
.split('\n')
.map<String>((String line) => line.trim())
.where((String line) => line.isNotEmpty);
final Map<String, String> properties = Map<String, String>.fromIterable(
propertiesFileLines.map<List<String>>((String line) => line.split(' = ')),
key: (dynamic split) => split[0],
value: (dynamic split) => split[1]);
if (!properties.containsKey('Pkg.Revision')) {
throw AndroidNdkSearchError('Can not establish ndk-bundle version: $propertiesFile does not contain Pkg.Revision');
}
// Extract major version from Pkg.Revision property which looks like <ndk-version>.x.y.
return int.parse(properties['Pkg.Revision'].split('.').first);
}
final String ndkDir = findBundle(androidHomeDir);
final String ndkCompiler = findCompiler(ndkDir);
final int ndkVersion = findNdkMajorVersion(ndkDir);
final List<String> ndkToolchainAndCompiler = findToolchainAndCompiler(ndkDir);
final String ndkToolchain = ndkToolchainAndCompiler[0];
final String ndkCompiler = ndkToolchainAndCompiler[1];
final List<String> ndkCompilerArgs = findSysroot(ndkDir);
if (ndkVersion >= 18) {
// Newer versions of NDK use clang instead of gcc, which falls back to
// system linker instead of using toolchain linker. Force clang to
// use appropriate linker by passing -fuse-ld=<path-to-ld> command line
// flag.
final String ndkLinker = fs.path.join(ndkToolchain, 'arm-linux-androideabi-ld');
if (!fs.isFileSync(ndkLinker)) {
throw AndroidNdkSearchError('Can not locate linker binary, tried $ndkLinker');
}
ndkCompilerArgs.add('-fuse-ld=$ndkLinker');
}
return AndroidNdk._(ndkDir, ndkCompiler, ndkCompilerArgs);
}
......
......@@ -160,6 +160,40 @@ void main() {
FileSystem: () => fs,
Platform: () => FakePlatform(operatingSystem: os),
});
testUsingContext('newer NDK require explicit -fuse-ld on $os', () {
sdkDir = MockAndroidSdk.createSdkDirectory(
withAndroidN: true, withNdkDir: osDir, withNdkSysroot: true, ndkVersion: 18);
Config.instance.setValue('android-sdk', sdkDir.path);
final String realSdkDir = sdkDir.path;
final String realNdkDir = fs.path.join(realSdkDir, 'ndk-bundle');
final String realNdkToolchainBin = fs.path.join(
realNdkDir,
'toolchains',
'arm-linux-androideabi-4.9',
'prebuilt',
osDir,
'bin');
final String realNdkCompiler = fs.path.join(
realNdkToolchainBin,
'arm-linux-androideabi-gcc');
final String realNdkLinker = fs.path.join(
realNdkToolchainBin,
'arm-linux-androideabi-ld');
final String realNdkSysroot =
fs.path.join(realNdkDir, 'platforms', 'android-9', 'arch-arm');
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.directory, realSdkDir);
expect(sdk.ndk, isNotNull);
expect(sdk.ndk.directory, realNdkDir);
expect(sdk.ndk.compiler, realNdkCompiler);
expect(sdk.ndk.compilerArgs, <String>['--sysroot', realNdkSysroot, '-fuse-ld=$realNdkLinker']);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: () => FakePlatform(operatingSystem: os),
});
});
for (String os in <String>['linux', 'macos']) {
......
......@@ -41,6 +41,7 @@ class MockAndroidSdk extends Mock implements AndroidSdk {
static Directory createSdkDirectory({
bool withAndroidN = false,
String withNdkDir,
int ndkVersion = 16,
bool withNdkSysroot = false,
bool withSdkManager = true,
}) {
......@@ -65,16 +66,29 @@ class MockAndroidSdk extends Mock implements AndroidSdk {
_createSdkFile(dir, 'tools/bin/sdkmanager');
if (withNdkDir != null) {
final String ndkCompiler = fs.path.join(
final String ndkToolchainBin = fs.path.join(
'ndk-bundle',
'toolchains',
'arm-linux-androideabi-4.9',
'prebuilt',
withNdkDir,
'bin',
);
final String ndkCompiler = fs.path.join(
ndkToolchainBin,
'arm-linux-androideabi-gcc',
);
final String ndkLinker = fs.path.join(
ndkToolchainBin,
'arm-linux-androideabi-ld',
);
_createSdkFile(dir, ndkCompiler);
_createSdkFile(dir, ndkLinker);
_createSdkFile(dir, fs.path.join('ndk-bundle', 'source.properties'), contents: '''
Pkg.Desc = Android NDK[]
Pkg.Revision = $ndkVersion.1.5063045
''');
}
if (withNdkSysroot) {
final String armPlatform = fs.path.join(
......
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