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
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart
index 7ffd46f..39f90f5 100644
--- a/packages/flutter_tools/lib/src/android/android_sdk.dart
+++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -139,7 +139,8 @@
       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 @@
         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 @@
       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);
   }
 
diff --git a/packages/flutter_tools/test/android/android_sdk_test.dart b/packages/flutter_tools/test/android/android_sdk_test.dart
index a84813b..9ef1711 100644
--- a/packages/flutter_tools/test/android/android_sdk_test.dart
+++ b/packages/flutter_tools/test/android/android_sdk_test.dart
@@ -160,6 +160,40 @@
           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']) {
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart
index 1ce3b7f..58ba611 100644
--- a/packages/flutter_tools/test/src/mocks.dart
+++ b/packages/flutter_tools/test/src/mocks.dart
@@ -41,6 +41,7 @@
   static Directory createSdkDirectory({
     bool withAndroidN = false,
     String withNdkDir,
+    int ndkVersion = 16,
     bool withNdkSysroot = false,
     bool withSdkManager = true,
   }) {
@@ -65,16 +66,29 @@
       _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(