1 package com.jayway.maven.plugins.android;
2
3 import com.jayway.maven.plugins.android.configuration.Zipalign;
4 import org.apache.maven.plugin.MojoExecutionException;
5
6 import java.io.File;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import static com.jayway.maven.plugins.android.common.AndroidExtension.APK;
11
12 /**
13 * Implementation for the zipaplign goal. Implements parsing parameters from pom or command line arguments and sets
14 * useful defaults as well.
15 *
16 * @author Manfred Moser <manfred@simpligility.com>
17 */
18 public abstract class AbstractZipalignMojo extends AbstractAndroidMojo
19 {
20
21 /**
22 * The configuration for the zipalign goal. As soon as a zipalign goal is invoked the command will be executed
23 * unless the skip parameter is set. By default the input file is the apk produced by the build in target. The
24 * outputApk will use the postfix -aligned.apk. The following shows a default full configuration of the zipalign
25 * goal as an example for changes as plugin configuration.
26 * <pre>
27 * <zipalign>
28 * <skip>false</skip>
29 * <verbose>true</verbose>
30 * <inputApk>${project.build.directory}/${project.artifactId}.apk</inputApk>
31 * <outputApk>${project.build.directory}/${project.artifactId}-aligned.apk</outputApk>
32 * </zipalign>
33 * </pre>
34 * <p/>
35 * Values can also be configured as properties on the command line as android.zipalign.*
36 * or in pom or settings file as properties like zipalign.*.
37 *
38 * @parameter
39 */
40 private Zipalign zipalign;
41
42 /**
43 * Skip the zipalign goal execution.
44 *
45 * @parameter expression="${android.zipalign.skip}"
46 * @see com.jayway.maven.plugins.android.configuration.Zipalign#skip
47 */
48 private Boolean zipalignSkip;
49
50 /**
51 * Activate verbose output for the zipalign goal execution.
52 *
53 * @parameter expression="${android.zipalign.verbose}"
54 * @see com.jayway.maven.plugins.android.configuration.Zipalign#verbose
55 */
56 private Boolean zipalignVerbose;
57
58 /**
59 * The apk file to be zipaligned. Per default the file is taken from build directory (target normally) using the
60 * build final name as file name and apk as extension.
61 *
62 * @parameter expression="${android.zipalign.inputApk}"
63 * @see com.jayway.maven.plugins.android.configuration.Zipalign#inputApk
64 */
65 private String zipalignInputApk;
66
67 /**
68 * The apk file produced by the zipalign goal. Per default the file is placed into the build directory (target
69 * normally) using the build final name appended with "-aligned" as file name and apk as extension.
70 *
71 * @parameter expression="${android.zipalign.outputApk}"
72 * @see com.jayway.maven.plugins.android.configuration.Zipalign#outputApk
73 */
74 private String zipalignOutputApk;
75
76 private Boolean parsedSkip;
77 private Boolean parsedVerbose;
78 private String parsedInputApk;
79 private String parsedOutputApk;
80
81 /**
82 * the apk file to be zipaligned.
83 */
84 private File apkFile;
85 /**
86 * the output apk file for the zipalign process.
87 */
88 private File alignedApkFile;
89
90 /**
91 * actually do the zipalign
92 *
93 * @throws MojoExecutionException
94 */
95 protected void zipalign() throws MojoExecutionException
96 {
97
98 // If we're not on a supported packaging with just skip (Issue 87)
99 // http://code.google.com/p/maven-android-plugin/issues/detail?id=87
100 if ( ! SUPPORTED_PACKAGING_TYPES.contains( project.getPackaging() ) )
101 {
102 getLog().info( "Skipping zipalign on " + project.getPackaging() );
103 return;
104 }
105
106 parseParameters();
107 if ( parsedSkip )
108 {
109 getLog().info( "Skipping zipalign" );
110 }
111 else
112 {
113 CommandExecutor executor = CommandExecutor.Factory.createDefaultCommmandExecutor();
114 executor.setLogger( this.getLog() );
115
116 String command = getAndroidSdk().getZipalignPath();
117
118 List<String> parameters = new ArrayList<String>();
119 if ( parsedVerbose )
120 {
121 parameters.add( "-v" );
122 }
123 parameters.add( "-f" ); // force overwriting existing output file
124 parameters.add( "4" ); // byte alignment has to be 4!
125 parameters.add( parsedInputApk );
126 parameters.add( parsedOutputApk );
127
128 try
129 {
130 getLog().info( "Running command: " + command );
131 getLog().info( "with parameters: " + parameters );
132 executor.executeCommand( command, parameters );
133
134 // Attach the resulting artifact (Issue 88)
135 // http://code.google.com/p/maven-android-plugin/issues/detail?id=88
136 File aligned = new File( parsedOutputApk );
137 if ( aligned.exists() )
138 {
139 projectHelper.attachArtifact( project, APK, "aligned", aligned );
140 getLog().info( "Attach " + aligned.getAbsolutePath() + " to the project" );
141 }
142 else
143 {
144 getLog().error( "Cannot attach " + aligned.getAbsolutePath() + " to the project - the file does "
145 + "not exist" );
146 }
147 }
148 catch ( ExecutionException e )
149 {
150 throw new MojoExecutionException( "", e );
151 }
152 }
153 }
154
155 private void parseParameters()
156 {
157 getLog().debug( "Parsing parameters" );
158 // <zipalign> exist in pom file
159 if ( zipalign != null )
160 {
161 // <zipalign><skip> exists in pom file
162 if ( zipalign.isSkip() != null )
163 {
164 parsedSkip = zipalign.isSkip();
165 }
166 else
167 {
168 parsedSkip = determineSkip();
169 }
170
171 // <zipalign><verbose> exists in pom file
172 if ( zipalign.isVerbose() != null )
173 {
174 parsedVerbose = zipalign.isVerbose();
175 }
176 else
177 {
178 parsedVerbose = determineVerbose();
179 }
180
181 // <zipalign><inputApk> exists in pom file
182 if ( zipalign.getInputApk() != null )
183 {
184 parsedInputApk = zipalign.getInputApk();
185 }
186 else
187 {
188 parsedInputApk = determineInputApk();
189 }
190
191
192 // <zipalign><outputApk> exists in pom file
193 if ( zipalign.getOutputApk() != null )
194 {
195 parsedOutputApk = zipalign.getOutputApk();
196 }
197 else
198 {
199 parsedOutputApk = determineOutputApk();
200 }
201
202 }
203 // command line options
204 else
205 {
206 parsedSkip = determineSkip();
207 parsedVerbose = determineVerbose();
208 parsedInputApk = determineInputApk();
209 parsedOutputApk = determineOutputApk();
210 }
211
212 getLog().debug( "skip:" + parsedSkip );
213 getLog().debug( "verbose:" + parsedVerbose );
214 getLog().debug( "inputApk:" + parsedInputApk );
215 getLog().debug( "outputApk:" + parsedOutputApk );
216 }
217
218 /**
219 * Get skip value for zipalign from command line option.
220 *
221 * @return if available return command line value otherwise return default false.
222 */
223 private Boolean determineSkip()
224 {
225 Boolean enabled;
226 if ( zipalignSkip != null )
227 {
228 enabled = zipalignSkip;
229 }
230 else
231 {
232 getLog().debug( "Using default for zipalign.skip=false" );
233 enabled = Boolean.FALSE;
234 }
235 return enabled;
236 }
237
238 /**
239 * Get verbose value for zipalign from command line option.
240 *
241 * @return if available return command line value otherwise return default false.
242 */
243 private Boolean determineVerbose()
244 {
245 Boolean enabled;
246 if ( zipalignVerbose != null )
247 {
248 enabled = zipalignVerbose;
249 }
250 else
251 {
252 getLog().debug( "Using default for zipalign.verbose=false" );
253 enabled = Boolean.FALSE;
254 }
255 return enabled;
256 }
257
258 /**
259 * Gets the apk file location from basedir/target/finalname.apk
260 *
261 * @return absolute path.
262 */
263 private String getApkLocation()
264 {
265 if ( apkFile == null )
266 {
267 apkFile = new File( project.getBuild().getDirectory(), project.getBuild().getFinalName() + "." + APK );
268 }
269 return apkFile.getAbsolutePath();
270 }
271
272 /**
273 * Gets the apk file location from basedir/target/finalname-aligned.apk. "-aligned" is the inserted string for the
274 * output file.
275 *
276 * @return absolute path.
277 */
278 private String getAlignedApkLocation()
279 {
280 if ( alignedApkFile == null )
281 {
282 alignedApkFile = new File( project.getBuild().getDirectory(),
283 project.getBuild().getFinalName() + "-aligned." + APK );
284 }
285 return alignedApkFile.getAbsolutePath();
286 }
287
288 /**
289 * Get inputApk value for zipalign from command line option.
290 *
291 * @return if available return command line value otherwise return default.
292 */
293 private String determineInputApk()
294 {
295 String inputApk;
296 if ( zipalignInputApk != null )
297 {
298 inputApk = zipalignInputApk;
299 }
300 else
301 {
302 String inputPath = getApkLocation();
303 getLog().debug( "Using default for zipalign.inputApk: " + inputPath );
304 inputApk = inputPath;
305 }
306 return inputApk;
307 }
308
309 /**
310 * Get outputApk value for zipalign from command line option.
311 *
312 * @return if available return command line value otherwise return default.
313 */
314 private String determineOutputApk()
315 {
316 String outputApk;
317 if ( zipalignOutputApk != null )
318 {
319 outputApk = zipalignOutputApk;
320 }
321 else
322 {
323 String outputPath = getAlignedApkLocation();
324 getLog().debug( "Using default for zipalign.outputApk: " + outputPath );
325 outputApk = outputPath;
326 }
327 return outputApk;
328 }
329
330 }