View Javadoc
1   /*******************************************************************************
2    * Copyright (c) 2008, 2011 Sonatype Inc. and others.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *    Sonatype Inc. - initial API and implementation
10   *******************************************************************************/
11  package com.simpligility.maven.plugins.androidndk.common;
12  
13  import java.io.File;
14  import java.io.IOException;
15  
16  import com.android.SdkConstants;
17  import com.google.common.io.PatternFilenameFilter;
18  import org.apache.commons.io.FileUtils;
19  import org.apache.maven.artifact.Artifact;
20  import org.apache.maven.plugin.MojoExecutionException;
21  import org.apache.maven.project.MavenProject;
22  import org.codehaus.plexus.archiver.ArchiverException;
23  import org.codehaus.plexus.archiver.UnArchiver;
24  import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
25  import org.codehaus.plexus.logging.Logger;
26  import org.codehaus.plexus.logging.console.ConsoleLogger;
27  
28  /**
29   * Provides convenience methods for unpacking Android libraries so that their contents can be used in the build.
30   */
31  public final class UnpackedLibHelper
32  {
33      private final ArtifactResolverHelper artifactResolverHelper;
34      private final Logger log;
35  
36      public static final String AAR_NATIVE_LIBRARIES_FOLDER = "jni";
37      public static final String APKLIB_NATIVE_LIBRARIES_FOLDER = "libs";
38  
39  
40      // ${project.build.directory}/unpacked-libs
41      private final File unpackedLibsDirectory;
42  
43      public UnpackedLibHelper ( ArtifactResolverHelper artifactResolverHelper, MavenProject project, Logger log,
44                                 File unpackedLibsFolder )
45      {
46          this.artifactResolverHelper = artifactResolverHelper;
47          if ( unpackedLibsFolder != null )
48          {
49              // if absolute then use it.
50              // if relative then make it relative to the basedir of the project.
51              this.unpackedLibsDirectory = unpackedLibsFolder.isAbsolute()
52                      ? unpackedLibsFolder
53                      : new File ( project.getBasedir(), unpackedLibsFolder.getPath() );
54          }
55          else
56          {
57              // If not specified then default to target/unpacked-libs
58              final File targetFolder = new File ( project.getBuild().getDirectory() );
59              this.unpackedLibsDirectory = new File ( targetFolder, "unpacked-libs" );
60          }
61          this.log = log;
62      }
63  
64      public void extractApklib( Artifact apklibArtifact ) throws MojoExecutionException
65      {
66          final File apkLibFile = artifactResolverHelper.resolveArtifactToFile( apklibArtifact );
67          if ( apkLibFile.isDirectory() )
68          {
69              log.warn(
70                      "The apklib artifact points to '" + apkLibFile + "' which is a directory; skipping unpacking it." );
71              return;
72          }
73  
74          final UnArchiver unArchiver = new ZipUnArchiver ( apkLibFile )
75          {
76              @Override
77              protected Logger getLogger()
78              {
79                  return new ConsoleLogger( log.getThreshold(), "dependencies-unarchiver" );
80              }
81          };
82  
83          final File apklibDirectory = getUnpackedLibFolder( apklibArtifact );
84          apklibDirectory.mkdirs();
85          unArchiver.setDestDirectory( apklibDirectory );
86          log.debug( "Extracting APKLIB to " + apklibDirectory );
87          try
88          {
89              unArchiver.extract();
90          }
91          catch ( ArchiverException e )
92          {
93              throw new MojoExecutionException ( "ArchiverException while extracting " + apklibDirectory
94                      + ". Message: " + e.getLocalizedMessage(), e );
95          }
96      }
97  
98      public void extractAarLib( Artifact aarArtifact ) throws MojoExecutionException
99      {
100         final File aarFile = artifactResolverHelper.resolveArtifactToFile( aarArtifact );
101         if ( aarFile.isDirectory() )
102         {
103             log.warn(
104                     "The aar artifact points to '" + aarFile + "' which is a directory; skipping unpacking it." );
105             return;
106         }
107 
108         final UnArchiver unArchiver = new ZipUnArchiver ( aarFile )
109         {
110             @Override
111             protected Logger getLogger()
112             {
113                 return new ConsoleLogger( log.getThreshold(), "dependencies-unarchiver" );
114             }
115         };
116 
117         final File aarDirectory = getUnpackedLibFolder( aarArtifact );
118         aarDirectory.mkdirs();
119         unArchiver.setDestDirectory( aarDirectory );
120         log.debug( "Extracting AAR to " + aarDirectory );
121         try
122         {
123             unArchiver.extract();
124         }
125         catch ( ArchiverException e )
126         {
127             throw new MojoExecutionException ( "ArchiverException while extracting " + aarDirectory.getAbsolutePath()
128                     + ". Message: " + e.getLocalizedMessage(), e );
129         }
130 
131         // Move native libraries from libs to jni folder for legacy AARs.
132         // This ensures backward compatibility with older AARs where libs are in "libs" folder.
133         final File jniFolder = new File ( aarDirectory, UnpackedLibHelper.AAR_NATIVE_LIBRARIES_FOLDER );
134         final File libsFolder = new File ( aarDirectory, UnpackedLibHelper.APKLIB_NATIVE_LIBRARIES_FOLDER );
135         if ( !jniFolder.exists() && libsFolder.isDirectory() && libsFolder.exists() )
136         {
137             String[] natives = libsFolder.list( new PatternFilenameFilter( "^.*(?<!(?i)\\.jar)$" ) );
138             if ( natives.length > 0 )
139             {
140                 log.debug( "Moving AAR native libraries from libs to jni folder" );
141                 for ( String nativeLibPath : natives )
142                 {
143                     try
144                     {
145                         FileUtils.moveToDirectory( new File ( libsFolder, nativeLibPath ), jniFolder, true );
146                     }
147                     catch ( IOException e )
148                     {
149                         throw new MojoExecutionException (
150                                 "Could not move native libraries from " + libsFolder, e );
151                     }
152                 }
153             }
154         }
155     }
156 
157     public File getArtifactToFile( Artifact artifact ) throws MojoExecutionException
158     {
159         final File artifactFile = artifactResolverHelper.resolveArtifactToFile( artifact );
160         return artifactFile;
161     }
162 
163     public File getUnpackedLibsFolder()
164     {
165         return unpackedLibsDirectory;
166     }
167 
168     public File getUnpackedLibFolder( Artifact artifact )
169     {
170         return new File ( unpackedLibsDirectory.getAbsolutePath(),
171                 getShortenedGroupId( artifact.getGroupId() )
172                 + "_"
173                 + artifact.getArtifactId()
174                 + "_"
175                 + artifact.getBaseVersion()
176         );
177     }
178 
179     public File getUnpackedClassesJar( Artifact artifact )
180     {
181         return new File ( getUnpackedLibFolder( artifact ), SdkConstants.FN_CLASSES_JAR );
182     }
183 
184     public File getUnpackedApkLibSourceFolder( Artifact artifact )
185     {
186         return new File ( getUnpackedLibFolder( artifact ), "src" );
187     }
188 
189     public File getUnpackedLibResourceFolder( Artifact artifact )
190     {
191         return new File ( getUnpackedLibFolder( artifact ), "res" );
192     }
193 
194     public File getUnpackedLibAssetsFolder( Artifact artifact )
195     {
196         return new File ( getUnpackedLibFolder( artifact ), "assets" );
197     }
198 
199     /**
200      * @param artifact  Android dependency that is being referenced.
201      * @return Folder where the unpacked native libraries are located.
202      * @see http://tools.android.com/tech-docs/new-build-system/aar-format
203      */
204     public File getUnpackedLibNativesFolder( Artifact artifact )
205     {
206         if ( AndroidExtension.AAR.equals( artifact.getType() ) )
207         {
208             return new File ( getUnpackedLibFolder( artifact ), UnpackedLibHelper.AAR_NATIVE_LIBRARIES_FOLDER );
209         }
210         else
211         {
212             return new File ( getUnpackedLibFolder( artifact ), UnpackedLibHelper.APKLIB_NATIVE_LIBRARIES_FOLDER );
213         }
214     }
215 
216     public File getJarFileForApk( Artifact artifact )
217     {
218         final String fileName = artifact.getFile().getName();
219         final String modifiedFileName = fileName.substring( 0, fileName.lastIndexOf( "." ) ) + ".jar";
220         return new File ( artifact.getFile().getParentFile(), modifiedFileName );
221     }
222 
223     /**
224      * @param groupId   An a dot separated groupId (eg org.apache.maven)
225      * @return A shortened (and potentially non-unique) version of the groupId, that consists of the first letter
226      *      of each part of the groupId. Eg oam for org.apache.maven
227      */
228     private String getShortenedGroupId( String groupId )
229     {
230         final String[] parts = groupId.split( "\\." );
231         final StringBuilder sb = new StringBuilder ();
232         for ( final String part : parts )
233         {
234             sb.append( part.charAt( 0 ) );
235         }
236         return sb.toString();
237     }
238 
239     /**
240      * @return True if this project constructs an APK as opposed to an AAR or APKLIB.
241      */
242     public boolean isAPKBuild( MavenProject project )
243     {
244         return AndroidExtension.APK.equals( project.getPackaging() );
245     }
246 }