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.androidndk;
15  
16  import com.simpligility.maven.plugins.androidndk.configuration.ArchitectureToolchainMappings;
17  import com.simpligility.maven.plugins.androidndk.phase05compile.NdkBuildMojo;
18  import org.apache.commons.lang3.SystemUtils;
19  import org.apache.maven.plugin.MojoExecutionException;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.List;
25  
26  /**
27   * Represents an Android NDK.
28   *
29   * @author Johan Lindquist <johanlindquist@gmail.com>
30   * @author Manfred Moser <manfred@simpligility.com>
31   */
32  public class AndroidNdk
33  {
34  
35      public static final String PROPER_NDK_HOME_DIRECTORY_MESSAGE = "Please provide a proper Android NDK directory path as configuration parameter <ndk><path>...</path></ndk>"
36              + " in the plugin <configuration/>. As an alternative, you may add the parameter to commandline: -Dandroid.ndk.path=... or set environment  variable "
37              + NdkBuildMojo.ENV_ANDROID_NDK_HOME + ".";
38  
39      public static final String[] NDK_ARCHITECTURES = { "arm64-v8a", "armeabi", "armeabi-v7a", "mips", "mips64", "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", "arm-linux-androideabi-4.7", "arm-linux-androideabi-4.6",
45              "arm-linux-androideabi-4.4.3", "arm-linux-androidabi-clang3.5", "arm-linux-androidabi-clang3.6" };
46  
47      /**
48       * ARM 64-bit toolchain implementations.
49       */
50      public static final String[] ARM_64_TOOLCHAIN = { "aarch64-linux-android-4.9", "aarch64-linux-android-clang3.5", "aarch64-linux-android-clang3.6" };
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", "x86-clang3.5", "x86-clang3.6" };
56  
57      /**
58       * x86 64-bit toolchain implementations.
59       */
60      public static final String[] X86_64_TOOLCHAIN = { "x86_64-4.9", "x86_64-clang3.5", "x86_64-clang3.6" };
61  
62      /**
63       * Mips toolchain implementations.
64       */
65      public static final String[] MIPS_TOOLCHAIN = { "mipsel-linux-android-4.9", "mipsel-linux-android-4.8", "mipsel-linux-android-4.7", "mipsel-linux-android-4.6", "mipsel-linux-android-4.4.3",
66              "mipsel-linux-android-clang3.5", "mipsel-linux-android-clang3.6" };
67  
68      public static final String[] MIPS_64_TOOLCHAIN = { "mips64el-linux-android-4.9", "mips64el-linux-android-clang3.5", "mips64el-linux-android-clang3.6" };
69  
70      /**
71       * Possible locations for the gdbserver file.
72       */
73      private static final String[] GDB_SERVER_LOCATIONS = { "toolchains/%s/prebuilt/gdbserver", "prebuilt/%s/gdbserver/gdbserver" };
74  
75      /**
76       * Locations of toolchains
77       */
78      private static final String TOOLCHAIN_LOCATION = "toolchains/%s";
79  
80      private final File ndkPath;
81  
82      public AndroidNdk( File ndkPath )
83      {
84          assertPathIsDirectory( ndkPath );
85          this.ndkPath = ndkPath;
86      }
87  
88      private void assertPathIsDirectory( final File path )
89      {
90          if ( path == null )
91          {
92              throw new InvalidNdkException( PROPER_NDK_HOME_DIRECTORY_MESSAGE );
93          }
94          if ( !path.isDirectory() )
95          {
96              throw new InvalidNdkException(
97                      "Path \"" + path + "\" is not a directory. " + PROPER_NDK_HOME_DIRECTORY_MESSAGE );
98          }
99      }
100 
101     /**
102      * Returns the complete path for the ndk-build tool, based on this NDK.
103      *
104      * @return the complete path as a <code>String</code>, including the tool's filename.
105      */
106     public String getNdkBuildPath()
107     {
108         if ( SystemUtils.IS_OS_WINDOWS )
109         {
110             return new File( ndkPath, "/ndk-build.cmd" ).getAbsolutePath();
111         }
112         else
113         {
114             return new File( ndkPath, "/ndk-build" ).getAbsolutePath();
115         }
116     }
117 
118     public File getGdbServer( String ndkArchitecture ) throws MojoExecutionException
119     {
120         // create a list of possible gdb server parent folder locations
121         List<String> gdbServerLocations = new ArrayList<String>();
122         if ( ndkArchitecture.startsWith( "arm64-v8a" ) )
123         {
124             gdbServerLocations.add( "android-arm64" );
125             gdbServerLocations.addAll( Arrays.asList( ARM_64_TOOLCHAIN ) );
126         }
127         else if ( ndkArchitecture.startsWith( "arm" ) )
128         {
129             gdbServerLocations.add( "android-arm" );
130             gdbServerLocations.addAll( Arrays.asList( ARM_TOOLCHAIN ) );
131         }
132         // x86_64 is before x86!
133         else if ( ndkArchitecture.startsWith( "x86_64" ) )
134         {
135             gdbServerLocations.add( "android-x86_64" );
136             gdbServerLocations.addAll( Arrays.asList( X86_TOOLCHAIN ) );
137         }
138         else if ( ndkArchitecture.startsWith( "x86" ) )
139         {
140             gdbServerLocations.add( "android-x86" );
141             gdbServerLocations.addAll( Arrays.asList( X86_TOOLCHAIN ) );
142         }
143         else if ( ndkArchitecture.startsWith( "mips" ) )
144         {
145             gdbServerLocations.add( "android-mips" );
146             gdbServerLocations.addAll( Arrays.asList( MIPS_TOOLCHAIN ) );
147         }
148 
149         // check for the gdb server
150         for ( String location : GDB_SERVER_LOCATIONS )
151         {
152             for ( String gdbServerLocation : gdbServerLocations )
153             {
154                 File gdbServerFile = new File( ndkPath, String.format( location, gdbServerLocation ) );
155                 if ( gdbServerFile.exists() )
156                 {
157                     return gdbServerFile;
158                 }
159             }
160         }
161 
162         //  if we got here, throw an error
163         throw new MojoExecutionException( "gdbserver binary for architecture " + ndkArchitecture
164                 + " does not exist, please double check the toolchain and OS used" );
165     }
166 
167     /**
168      * Retrieves, based on the architecture and possibly toolchain mappings, the toolchain for the architecture.
169      * <br/>
170      * <strong>Note:</strong> This method will return the <strong>default</strong> toolchain as defined by the NDK if
171      * not specified in the <code>NDKArchitectureToolchainMappings</code>.
172      *
173      * @param ndkArchitecture                  Architecture to resolve toolchain for
174      * @param architectureToolchainMappings User mappings of architecture to toolchain
175      * @return Toolchain to be used for the architecture
176      * @throws MojoExecutionException If a toolchain can not be resolved
177      */
178     public String getToolchainFromArchitecture( final String ndkArchitecture, final ArchitectureToolchainMappings architectureToolchainMappings ) throws MojoExecutionException
179     {
180         // arm64 is before arm!
181         if ( ndkArchitecture.startsWith( "arm64-v8a" ) )
182         {
183             if ( architectureToolchainMappings != null )
184             {
185                 return architectureToolchainMappings.getArm64 ();
186             }
187             return findHighestSupportedToolchain( AndroidNdk.ARM_64_TOOLCHAIN );
188         }
189         else if ( ndkArchitecture.startsWith( "arm" ) )
190         {
191             if ( architectureToolchainMappings != null )
192             {
193                 return architectureToolchainMappings.getArmeabi();
194             }
195             return findHighestSupportedToolchain( AndroidNdk.ARM_TOOLCHAIN );
196         }
197         // x86_64 is before x86!
198         else if ( ndkArchitecture.startsWith( "x86_64" ) )
199         {
200             if ( architectureToolchainMappings != null )
201             {
202                 return architectureToolchainMappings.getX8664 ();
203             }
204             return findHighestSupportedToolchain( AndroidNdk.X86_64_TOOLCHAIN );
205         }
206         else if ( ndkArchitecture.startsWith( "x86" ) )
207         {
208             if ( architectureToolchainMappings != null )
209             {
210                 return architectureToolchainMappings.getX86();
211             }
212             return findHighestSupportedToolchain( AndroidNdk.X86_TOOLCHAIN );
213         }
214         else if ( ndkArchitecture.startsWith( "mips" ) )
215         {
216             if ( architectureToolchainMappings != null )
217             {
218                 return architectureToolchainMappings.getMips();
219             }
220             return findHighestSupportedToolchain( AndroidNdk.MIPS_TOOLCHAIN );
221         }
222 
223         //  if we got here, throw an error
224         throw new MojoExecutionException( "Toolchain for architecture " + ndkArchitecture + " does not exist, please double check the setup" );
225     }
226 
227     public static void validateToolchainDirectory( File toolchainDirectory ) throws MojoExecutionException
228     {
229         if ( !toolchainDirectory.exists() )
230         {
231             //  if we got here, throw an error
232             throw new MojoExecutionException( "Toolchain directory " + toolchainDirectory + " does not exist" );
233         }
234         if ( !toolchainDirectory.canRead() )
235         {
236             //  if we got here, throw an error
237             throw new MojoExecutionException( "Toolchain directory " + toolchainDirectory + " exist but can not be read" );
238         }
239     }
240 
241     private String findHighestSupportedToolchain( final String[] toolchains ) throws MojoExecutionException
242     {
243         for ( String toolchain : toolchains )
244         {
245             File toolchainDirectory = new File( ndkPath, String.format( TOOLCHAIN_LOCATION, toolchain ) );
246             if ( toolchainDirectory.exists() )
247             {
248                 AndroidNdk.validateToolchainDirectory( toolchainDirectory );
249                 return toolchain;
250             }
251         }
252 
253         //  if we got here, throw an error
254         throw new MojoExecutionException( "No valid toolchain could be found in the " + ndkPath + "/toolchains directory" );
255     }
256 }
257