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;
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart
index a70503f..cb8399c 100644
--- a/packages/flutter_tools/lib/src/android/android_sdk.dart
+++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -104,9 +104,122 @@
   );
 }
 
+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 @@
   /// 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 @@
       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 @@
     }
 
     // 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) {