View Javadoc
1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    *      http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  package com.simpligility.maven.plugins.android;
15  
16  import org.apache.commons.lang3.SystemUtils;
17  import org.apache.maven.plugin.MojoExecutionException;
18  
19  import java.io.File;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.List;
23  
24  /**
25   * Represents an Android NDK.
26   *
27   * @author Johan Lindquist - johanlindquist@gmail.com
28   * @author Manfred Moser - manfred@simpligility.com
29   */
30  public class AndroidNdk
31  {
32  
33      public static final String PROPER_NDK_HOME_DIRECTORY_MESSAGE = "Please provide a proper Android NDK directory path"
34              + " as configuration parameter <ndk><path>...</path></ndk> in the plugin <configuration/>. As an "
35              + "alternative, you may add the parameter to commandline: -Dandroid.ndk.path=... or set environment "
36              + "variable ANDROID_ND_HOME.";
37  
38      public static final String[] NDK_ARCHITECTURES = { "armeabi", "armeabi-v7a", "arm64-v8a", "mips", "mips64",
39              "x86", "x86_64" };
40  
41      /**
42       * Arm toolchain implementations.
43       */
44      public static final String[] ARM_TOOLCHAIN = {  "arm-linux-androideabi-4.9", "arm-linux-androideabi-4.8",
45              "arm-linux-androideabi-4.7", "arm-linux-androideabi-4.6", "arm-linux-androideabi-4.4.3" };
46  
47      /**
48       * Arm64 toolchain implementations.
49       */
50      public static final String[] ARM64_TOOLCHAIN = {  "aarch64-linux-android-4.9" };
51  
52      /**
53       * x86 toolchain implementations.
54       */
55      public static final String[] X86_TOOLCHAIN = { "x86-4.9", "x86-4.8", "x86-4.7", "x86-4.6", "x86-4.4.3" };
56  
57      /**
58       * x86 toolchain implementations.
59       */
60      public static final String[] X86_64_TOOLCHAIN = { "x86_64-4.9" };
61  
62      /**
63       * Mips toolchain implementations.
64       */
65      public static final String[] MIPS_TOOLCHAIN = { "mipsel-linux-android-4.9", "mipsel-linux-android-4.8",
66              "mipsel-linux-android-4.7", "mipsel-linux-android-4.6", "mipsel-linux-android-4.4.3" };
67  
68      /**
69       * Mips toolchain implementations.
70       */
71      public static final String[] MIPS64_TOOLCHAIN = { "mips64el-linux-android-4.9" };
72  
73      /**
74       * Possible locations for the gdbserver file.
75       */
76      private static final String[] GDB_SERVER_LOCATIONS = { "toolchains/%s/prebuilt/gdbserver",
77                                                             "prebuilt/%s/gdbserver/gdbserver" };
78  
79      private final File ndkPath;
80  
81      public AndroidNdk( File ndkPath )
82      {
83          assertPathIsDirectory( ndkPath );
84          this.ndkPath = ndkPath;
85      }
86  
87      private void assertPathIsDirectory( final File path )
88      {
89          if ( path == null )
90          {
91              throw new InvalidNdkException( PROPER_NDK_HOME_DIRECTORY_MESSAGE );
92          }
93          if ( ! path.isDirectory() )
94          {
95              throw new InvalidNdkException(
96                      "Path \"" + path + "\" is not a directory. " + PROPER_NDK_HOME_DIRECTORY_MESSAGE );
97          }
98      }
99  
100     private File findStripper( String toolchain )
101     {
102         List<String> osDirectories = new ArrayList<String>();
103         String extension = "";
104 
105         if ( SystemUtils.IS_OS_LINUX )
106         {
107             osDirectories.add( "linux-x86" );
108             osDirectories.add( "linux-x86_64" );
109         }
110         else if ( SystemUtils.IS_OS_WINDOWS )
111         {
112             osDirectories.add( "windows" );
113             osDirectories.add( "windows-x86_64" );
114             extension = ".exe";
115         }
116         else if ( SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_MAC_OSX )
117         {
118             osDirectories.add( "darwin-x86" );
119             osDirectories.add( "darwin-x86_64" );
120         }
121 
122         String fileName = "";
123         if ( toolchain.startsWith( "arm" ) )
124         {
125             fileName = "arm-linux-androideabi-strip" + extension;
126         }
127         else if ( toolchain.startsWith( "aarch64" ) )
128         {
129             fileName = "aarch64-linux-android-strip" + extension;
130         }
131         else if ( toolchain.startsWith( "x86_64" ) )
132         {
133             fileName = "x86_64-linux-android-strip" + extension;
134         }
135         else if ( toolchain.startsWith( "x86" ) )
136         {
137             fileName = "i686-linux-android-strip" + extension;
138         }
139         else if ( toolchain.startsWith( "mips64" ) )
140         {
141             fileName = "mips64el-linux-android-strip" + extension;
142         }
143         else if ( toolchain.startsWith( "mips" ) )
144         {
145             fileName = "mipsel-linux-android-strip" + extension;
146         }
147 
148         for ( String osDirectory : osDirectories )
149         {
150             String stripperLocation =
151                 String.format( "toolchains/%s/prebuilt/%s/bin/%s", toolchain, osDirectory, fileName );
152             final File stripper = new File( ndkPath, stripperLocation );
153             if ( stripper.exists() )
154             {
155                 return stripper;
156             }
157         }
158         return null;
159     }
160 
161     public File getStripper( String toolchain ) throws MojoExecutionException
162     {
163         final File stripper = findStripper( toolchain );
164         if ( stripper == null )
165         {
166             throw new MojoExecutionException( "Could not resolve stripper for current OS: " + SystemUtils.OS_NAME );
167         }
168 
169         // Some basic validation
170         if ( ! stripper.exists() )
171         {
172             throw new MojoExecutionException( "Strip binary " + stripper.getAbsolutePath()
173                     + " does not exist, please double check the toolchain and OS used" );
174         }
175 
176         // We should be good to go
177         return stripper;
178     }
179 
180     private String resolveNdkToolchain( String[] toolchains )
181     {
182         for ( String toolchain : toolchains )
183         {
184             File f = findStripper( toolchain );
185             if ( f != null && f.exists() )
186             {
187                 return toolchain;
188             }
189         }
190         return null;
191     }
192 
193     /**
194      * Tries to resolve the toolchain based on the path of the file.
195      *
196      * @param file Native library
197      * @return String
198      * @throws MojoExecutionException When no toolchain is found
199      */
200     public String getToolchain( File file ) throws MojoExecutionException
201     {
202         String resolvedNdkToolchain = null;
203 
204         // try to resolve the toolchain now
205         String ndkArchitecture = file.getParentFile().getName();
206         if ( ndkArchitecture.startsWith( "armeabi" ) )
207         {
208             resolvedNdkToolchain = resolveNdkToolchain( ARM_TOOLCHAIN );
209         }
210         else if ( ndkArchitecture.startsWith( "arm64" ) )
211         {
212             resolvedNdkToolchain = resolveNdkToolchain( ARM64_TOOLCHAIN );
213         }
214         else if ( ndkArchitecture.startsWith( "x86_64" ) )
215         {
216             resolvedNdkToolchain = resolveNdkToolchain( X86_64_TOOLCHAIN );
217         }
218         else if ( ndkArchitecture.startsWith( "x86" ) )
219         {
220             resolvedNdkToolchain = resolveNdkToolchain( X86_TOOLCHAIN );
221         }
222         else if ( ndkArchitecture.startsWith( "mips64" ) )
223         {
224             resolvedNdkToolchain = resolveNdkToolchain( MIPS64_TOOLCHAIN );
225         }
226         else if ( ndkArchitecture.startsWith( "mips" ) )
227         {
228             resolvedNdkToolchain = resolveNdkToolchain( MIPS_TOOLCHAIN );
229         }
230 
231         // if no toolchain can be found
232         if ( resolvedNdkToolchain == null )
233         {
234             throw new MojoExecutionException(
235                 "Can not resolve automatically a toolchain to use. Please specify one." );
236         }
237         return resolvedNdkToolchain;
238     }
239 
240     /**
241      * Returns the complete path for the ndk-build tool, based on this NDK.
242      *
243      * @return the complete path as a <code>String</code>, including the tool's filename.
244      */
245     public String getNdkBuildPath()
246     {
247         if ( SystemUtils.IS_OS_WINDOWS )
248         {
249             return new File( ndkPath, "/ndk-build.cmd" ).getAbsolutePath();
250         }
251         else
252         {
253             return new File( ndkPath, "/ndk-build" ).getAbsolutePath();
254         }
255     }
256 
257     public File getGdbServer( String ndkArchitecture ) throws MojoExecutionException
258     {
259         // create a list of possible gdb server parent folder locations
260         List<String> gdbServerLocations = new ArrayList<String>();
261         if ( ndkArchitecture.startsWith( "armeabi" ) )
262         {
263             gdbServerLocations.add( "android-arm" );
264             gdbServerLocations.add( "android-armeabi" );
265             gdbServerLocations.addAll( Arrays.asList( ARM_TOOLCHAIN ) );
266         }
267         else if ( ndkArchitecture.startsWith( "arm64" ) )
268         {
269             gdbServerLocations.add( "android-arm64" );
270             gdbServerLocations.addAll( Arrays.asList( ARM64_TOOLCHAIN ) );
271         }
272         else if ( ndkArchitecture.startsWith( "x86_64" ) )
273         {
274             gdbServerLocations.add( "android-x86_64" );
275             gdbServerLocations.addAll( Arrays.asList( X86_64_TOOLCHAIN ) );
276         }
277         else if ( ndkArchitecture.startsWith( "x86" ) )
278         {
279             gdbServerLocations.add( "android-x86" );
280             gdbServerLocations.addAll( Arrays.asList( X86_TOOLCHAIN ) );
281         }
282         else if ( ndkArchitecture.startsWith( "mips64" ) )
283         {
284             gdbServerLocations.add( "android-mips64" );
285             gdbServerLocations.addAll( Arrays.asList( MIPS64_TOOLCHAIN ) );
286         }
287         else if ( ndkArchitecture.startsWith( "mips" ) )
288         {
289             gdbServerLocations.add( "android-mips" );
290             gdbServerLocations.addAll( Arrays.asList( MIPS_TOOLCHAIN ) );
291         }
292 
293         // check for the gdb server
294         for ( String location : GDB_SERVER_LOCATIONS )
295         {
296             for ( String gdbServerLocation : gdbServerLocations )
297             {
298                 File gdbServerFile = new File( ndkPath, String.format( location, gdbServerLocation ) );
299                 if ( gdbServerFile.exists() )
300                 {
301                     return gdbServerFile;
302                 }
303             }
304         }
305 
306         //  if we got here, throw an error
307         throw new MojoExecutionException( "gdbserver binary for architecture " + ndkArchitecture
308             + " does not exist, please double check the toolchain and OS used" );
309     }
310 
311 }
312