@@ -104,9 +104,122 @@ String getAvdPath() {
104104 );
105105}
106106
107+ class AndroidNdkSearchError {
108+ AndroidNdkSearchError (this .reason);
109+
110+ /// The message explaining why NDK was not found.
111+ final String reason;
112+ }
113+
114+ class AndroidNdk {
115+ AndroidNdk ._(this .directory, this .compiler, this .compilerArgs);
116+
117+ /// The path to the NDK.
118+ final String directory;
119+
120+ /// The path to the NDK compiler.
121+ final String compiler;
122+
123+ /// The mandatory arguments to the NDK compiler.
124+ final List <String > compilerArgs;
125+
126+ /// Locate NDK within the given SDK or throw [AndroidNdkSearchError] .
127+ static AndroidNdk locateNdk (String androidHomeDir) {
128+ if (androidHomeDir == null ) {
129+ throw new AndroidNdkSearchError ('Can not locate NDK because no SDK is found' );
130+ }
131+
132+ String findBundle (String androidHomeDir) {
133+ final String ndkDirectory = fs.path.join (androidHomeDir, 'ndk-bundle' );
134+ if (! fs.isDirectorySync (ndkDirectory)) {
135+ throw new AndroidNdkSearchError ('Can not locate ndk-bundle, tried: $ndkDirectory ' );
136+ }
137+ return ndkDirectory;
138+ }
139+
140+ String findCompiler (String ndkDirectory) {
141+ String directory;
142+ if (platform.isLinux) {
143+ directory = 'linux-x86_64' ;
144+ } else if (platform.isMacOS) {
145+ directory = 'darwin-x86_64' ;
146+ } else {
147+ throw new AndroidNdkSearchError ('Only Linux and macOS are supported' );
148+ }
149+
150+ final String ndkCompiler = fs.path.join (ndkDirectory,
151+ 'toolchains' , 'arm-linux-androideabi-4.9' , 'prebuilt' , directory,
152+ 'bin' , 'arm-linux-androideabi-gcc' );
153+ if (! fs.isFileSync (ndkCompiler)) {
154+ throw new AndroidNdkSearchError ('Can not locate GCC binary, tried $ndkCompiler ' );
155+ }
156+
157+ return ndkCompiler;
158+ }
159+
160+ List <String > findSysroot (String ndkDirectory) {
161+ // If entity represents directory with name android-<version> that
162+ // contains arch-arm subdirectory then returns version, otherwise
163+ // returns null.
164+ int toPlatformVersion (FileSystemEntity entry) {
165+ if (entry is ! Directory ) {
166+ return null ;
167+ }
168+
169+ if (! fs.isDirectorySync (fs.path.join (entry.path, 'arch-arm' ))) {
170+ return null ;
171+ }
172+
173+ final String name = fs.path.basename (entry.path);
174+
175+ const String platformPrefix = 'android-' ;
176+ if (! name.startsWith (platformPrefix)) {
177+ return null ;
178+ }
179+
180+ return int .tryParse (name.substring (platformPrefix.length));
181+ }
182+
183+ final String platformsDir = fs.path.join (ndkDirectory, 'platforms' );
184+ final List <int > versions = fs
185+ .directory (platformsDir)
186+ .listSync ()
187+ .map (toPlatformVersion)
188+ .where ((int version) => version != null )
189+ .toList (growable: false );
190+ versions.sort ();
191+
192+ final int suitableVersion = versions
193+ .firstWhere ((int version) => version >= 9 , orElse: () => null );
194+ if (suitableVersion == null ) {
195+ throw new AndroidNdkSearchError ('Can not locate a suitable platform ARM sysroot (need android-9 or newer), tried to look in $platformsDir ' );
196+ }
197+
198+ final String armPlatform = fs.path.join (ndkDirectory, 'platforms' ,
199+ 'android-$suitableVersion ' , 'arch-arm' );
200+ return < String > ['--sysroot' , armPlatform];
201+ }
202+
203+ final String ndkDir = findBundle (androidHomeDir);
204+ final String ndkCompiler = findCompiler (ndkDir);
205+ final List <String > ndkCompilerArgs = findSysroot (ndkDir);
206+ return new AndroidNdk ._(ndkDir, ndkCompiler, ndkCompilerArgs);
207+ }
208+
209+ /// Returns a descriptive message explaining why NDK can not be found within
210+ /// the given SDK.
211+ static String explainMissingNdk (String androidHomeDir) {
212+ try {
213+ locateNdk (androidHomeDir);
214+ return 'Unexpected error: found NDK on the second try' ;
215+ } on AndroidNdkSearchError catch (e) {
216+ return e.reason;
217+ }
218+ }
219+ }
220+
107221class AndroidSdk {
108- AndroidSdk (this .directory, [this .ndkDirectory, this .ndkCompiler,
109- this .ndkCompilerArgs]) {
222+ AndroidSdk (this .directory, [this .ndk]) {
110223 _init ();
111224 }
112225
@@ -116,14 +229,8 @@ class AndroidSdk {
116229 /// The path to the Android SDK.
117230 final String directory;
118231
119- /// The path to the NDK (can be `null` ).
120- final String ndkDirectory;
121-
122- /// The path to the NDK compiler (can be `null` ).
123- final String ndkCompiler;
124-
125- /// The mandatory arguments to the NDK compiler (can be `null` ).
126- final List <String > ndkCompilerArgs;
232+ /// Android NDK (can be `null` ).
233+ final AndroidNdk ndk;
127234
128235 List <AndroidSdkVersion > _sdkVersions;
129236 AndroidSdkVersion _latestVersion;
@@ -176,41 +283,6 @@ class AndroidSdk {
176283 return null ;
177284 }
178285
179- String findNdk (String androidHomeDir) {
180- final String ndkDirectory = fs.path.join (androidHomeDir, 'ndk-bundle' );
181- if (fs.isDirectorySync (ndkDirectory)) {
182- return ndkDirectory;
183- }
184- return null ;
185- }
186-
187- String findNdkCompiler (String ndkDirectory) {
188- String directory;
189- if (platform.isLinux) {
190- directory = 'linux-x86_64' ;
191- } else if (platform.isMacOS) {
192- directory = 'darwin-x86_64' ;
193- }
194- if (directory != null ) {
195- final String ndkCompiler = fs.path.join (ndkDirectory,
196- 'toolchains' , 'arm-linux-androideabi-4.9' , 'prebuilt' , directory,
197- 'bin' , 'arm-linux-androideabi-gcc' );
198- if (fs.isFileSync (ndkCompiler)) {
199- return ndkCompiler;
200- }
201- }
202- return null ;
203- }
204-
205- List <String > computeNdkCompilerArgs (String ndkDirectory) {
206- final String armPlatform = fs.path.join (ndkDirectory, 'platforms' ,
207- 'android-9' , 'arch-arm' );
208- if (fs.isDirectorySync (armPlatform)) {
209- return < String > ['--sysroot' , armPlatform];
210- }
211- return null ;
212- }
213-
214286 final String androidHomeDir = findAndroidHomeDir ();
215287 if (androidHomeDir == null ) {
216288 // No dice.
@@ -219,20 +291,15 @@ class AndroidSdk {
219291 }
220292
221293 // Try to find the NDK compiler. If we can't find it, it's also ok.
222- final String ndkDir = findNdk (androidHomeDir);
223- String ndkCompiler;
224- List <String > ndkCompilerArgs;
225- if (ndkDir != null ) {
226- ndkCompiler = findNdkCompiler (ndkDir);
227- if (ndkCompiler != null ) {
228- ndkCompilerArgs = computeNdkCompilerArgs (ndkDir);
229- if (ndkCompilerArgs == null ) {
230- ndkCompiler = null ;
231- }
232- }
294+ AndroidNdk ndk;
295+ try {
296+ ndk = AndroidNdk .locateNdk (androidHomeDir);
297+ } on AndroidNdkSearchError {
298+ // Ignore AndroidNdkSearchError's but don't ignore any other
299+ // exceptions.
233300 }
234301
235- return new AndroidSdk (androidHomeDir, ndkDir, ndkCompiler, ndkCompilerArgs );
302+ return new AndroidSdk (androidHomeDir, ndk );
236303 }
237304
238305 static bool validSdkDirectory (String dir) {
0 commit comments