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