2 * Copyright (C) 2009 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.ant;
19 import org.apache.tools.ant.BuildException;
20 import org.apache.tools.ant.Project;
21 import org.apache.tools.ant.Task;
22 import org.apache.tools.ant.taskdefs.ExecTask;
23 import org.apache.tools.ant.types.Path;
26 import java.util.ArrayList;
29 * Task to execute aapt.
31 * <p>It does not follow the exec task format, instead it has its own parameters, which maps
32 * directly to aapt.</p>
33 * <p>It is able to run aapt several times if library setup requires generating several
35 * <p>The following map shows how to use the task for each supported aapt command line
39 * <tr><td><b>Aapt Option</b></td><td><b>Ant Name</b></td><td><b>Type</b></td></tr>
40 * <tr><td>path to aapt</td><td>executable</td><td>attribute (Path)</td>
41 * <tr><td>command</td><td>command</td><td>attribute (String)</td>
42 * <tr><td>-v</td><td>verbose</td><td>attribute (boolean)</td></tr>
43 * <tr><td>-f</td><td>force</td><td>attribute (boolean)</td></tr>
44 * <tr><td>-M AndroidManifest.xml</td><td>manifest</td><td>attribute (Path)</td></tr>
45 * <tr><td>-I base-package</td><td>androidjar</td><td>attribute (Path)</td></tr>
46 * <tr><td>-A asset-source-dir</td><td>assets</td><td>attribute (Path</td></tr>
47 * <tr><td>-S resource-sources</td><td><res path=""></td><td>nested element(s)<br>with attribute (Path)</td></tr>
48 * <tr><td>-0 extension</td><td><nocompress extension=""><br><nocompress></td><td>nested element(s)<br>with attribute (String)</td></tr>
49 * <tr><td>-F apk-file</td><td>apkfolder<br>outfolder<br>apkbasename<br>basename</td><td>attribute (Path)<br>attribute (Path) deprecated<br>attribute (String)<br>attribute (String) deprecated</td></tr>
50 * <tr><td>-J R-file-dir</td><td>rfolder</td><td>attribute (Path)<br>-m always enabled</td></tr>
51 * <tr><td></td><td></td><td></td></tr>
54 public final class AaptExecLoopTask extends Task {
57 * Class representing a <nocompress> node in the main task XML.
58 * This let the developers prevent compression of some files in assets/ and res/raw/
60 * If the extension is null, this will disable compression for all files in assets/ and
63 public final static class NoCompress {
67 * Sets the value of the "extension" attribute.
68 * @param extention the extension.
70 public void setExtension(String extention) {
71 mExtension = extention;
75 private String mExecutable;
76 private String mCommand;
77 private boolean mForce = true; // true due to legacy reasons
78 private boolean mVerbose = false;
79 private int mVersionCode = 0;
80 private String mManifest;
81 private ArrayList<Path> mResources;
82 private String mAssets;
83 private String mAndroidJar;
84 private String mApkFolder;
85 @Deprecated private String mApkBaseName;
86 private String mApkName;
87 private String mResourceFilter;
88 private String mRFolder;
89 private final ArrayList<NoCompress> mNoCompressList = new ArrayList<NoCompress>();
92 * Sets the value of the "executable" attribute.
93 * @param executable the value.
95 public void setExecutable(Path executable) {
96 mExecutable = TaskHelper.checkSinglePath("executable", executable);
100 * Sets the value of the "command" attribute.
101 * @param command the value.
103 public void setCommand(String command) {
108 * Sets the value of the "force" attribute.
109 * @param force the value.
111 public void setForce(boolean force) {
116 * Sets the value of the "verbose" attribute.
117 * @param verbose the value.
119 public void setVerbose(boolean verbose) {
123 public void setVersioncode(String versionCode) {
124 if (versionCode.length() > 0) {
126 mVersionCode = Integer.decode(versionCode);
127 } catch (NumberFormatException e) {
128 System.out.println(String.format(
129 "WARNING: Ignoring invalid version code value '%s'.", versionCode));
135 * Sets the value of the "manifest" attribute.
136 * @param manifest the value.
138 public void setManifest(Path manifest) {
139 mManifest = TaskHelper.checkSinglePath("manifest", manifest);
143 * Sets the value of the "resources" attribute.
144 * @param resources the value.
146 * @deprecated Use nested element(s) <res path="value" />
149 public void setResources(Path resources) {
150 System.out.println("WARNNG: Using deprecated 'resources' attribute in AaptExecLoopTask." +
151 "Use nested element(s) <res path=\"value\" /> instead.");
152 if (mResources == null) {
153 mResources = new ArrayList<Path>();
156 mResources.add(new Path(getProject(), resources.toString()));
160 * Sets the value of the "assets" attribute.
161 * @param assets the value.
163 public void setAssets(Path assets) {
164 mAssets = TaskHelper.checkSinglePath("assets", assets);
168 * Sets the value of the "androidjar" attribute.
169 * @param androidJar the value.
171 public void setAndroidjar(Path androidJar) {
172 mAndroidJar = TaskHelper.checkSinglePath("androidjar", androidJar);
176 * Sets the value of the "outfolder" attribute.
177 * @param outFolder the value.
178 * @deprecated use {@link #setApkfolder(Path)}
181 public void setOutfolder(Path outFolder) {
182 System.out.println("WARNNG: Using deprecated 'outfolder' attribute in AaptExecLoopTask." +
183 "Use 'apkfolder' (path) instead.");
184 mApkFolder = TaskHelper.checkSinglePath("outfolder", outFolder);
188 * Sets the value of the "apkfolder" attribute.
189 * @param apkFolder the value.
191 public void setApkfolder(Path apkFolder) {
192 mApkFolder = TaskHelper.checkSinglePath("apkfolder", apkFolder);
196 * Sets the value of the "basename" attribute.
197 * @param baseName the value.
198 * @deprecated use {@link #setApkbasename(String)}
201 public void setBasename(String baseName) {
202 System.out.println("WARNNG: Using deprecated 'basename' attribute in AaptExecLoopTask." +
203 "Use 'resourcefilename' (string) instead.");
204 mApkBaseName = baseName;
208 * Sets the value of the "apkbasename" attribute.
209 * @param apkbaseName the value.
211 public void setApkbasename(String apkbaseName) {
212 System.out.println("WARNNG: Using deprecated 'apkbasename' attribute in AaptExecLoopTask." +
213 "Use 'resourcefilename' (string) instead.");
214 mApkBaseName = apkbaseName;
218 * Sets the value of the resourcefilename attribute
219 * @param apkName the value
221 public void setResourcefilename(String apkName) {
226 * Sets the value of the "rfolder" attribute.
227 * @param rFolder the value.
229 public void setRfolder(Path rFolder) {
230 mRFolder = TaskHelper.checkSinglePath("rfolder", rFolder);
233 public void setresourcefilter(String filter) {
234 if (filter != null && filter.length() > 0) {
235 mResourceFilter = filter;
240 * Returns an object representing a nested <var>nocompress</var> element.
242 public Object createNocompress() {
243 NoCompress nc = new NoCompress();
244 mNoCompressList.add(nc);
249 * Returns an object representing a nested <var>res</var> element.
251 public Object createRes() {
252 if (mResources == null) {
253 mResources = new ArrayList<Path>();
256 Path path = new Path(getProject());
257 mResources.add(path);
265 * Executes the loop. Based on the values inside default.properties, this will
266 * create alternate temporary ap_ files.
268 * @see org.apache.tools.ant.Task#execute()
271 public void execute() throws BuildException {
272 Project taskProject = getProject();
274 // first do a full resource package
275 callAapt(null /*customPackage*/);
277 // if the parameters indicate generation of the R class, check if
278 // more R classes need to be created for libraries.
279 if (mRFolder != null && new File(mRFolder).isDirectory()) {
280 String libPkgProp = taskProject.getProperty("android.libraries.package");
281 if (libPkgProp != null) {
282 // get the main package to compare in case the libraries use the same
283 String mainPackage = taskProject.getProperty("manifest.package");
285 String[] libPkgs = libPkgProp.split(";");
286 for (String libPkg : libPkgs) {
287 if (libPkg.length() > 0 && mainPackage.equals(libPkg) == false) {
288 // FIXME: instead of recreating R.java from scratch, maybe copy
289 // the files (R.java and manifest.java)? This would force to replace
290 // the package line on the fly.
299 * Calls aapt with the given parameters.
300 * @param resourceFilter the resource configuration filter to pass to aapt (if configName is
302 * @param customPackage an optional custom package.
304 private void callAapt(String customPackage) {
305 Project taskProject = getProject();
307 final boolean generateRClass = mRFolder != null && new File(mRFolder).isDirectory();
309 if (generateRClass) {
310 } else if (mResourceFilter == null) {
311 System.out.println("Creating full resource package...");
313 System.out.println(String.format(
314 "Creating resource package with filter: (%1$s)...",
318 // create a task for the default apk.
319 ExecTask task = new ExecTask();
320 task.setExecutable(mExecutable);
321 task.setFailonerror(true);
323 // aapt command. Only "package" is supported at this time really.
324 task.createArg().setValue(mCommand);
328 task.createArg().setValue("-f");
333 task.createArg().setValue("-v");
336 if (generateRClass) {
337 task.createArg().setValue("-m");
341 if (mResourceFilter != null) {
342 task.createArg().setValue("-c");
343 task.createArg().setValue(mResourceFilter);
347 // first look to see if there's a NoCompress object with no specified extension
348 boolean compressNothing = false;
349 for (NoCompress nc : mNoCompressList) {
350 if (nc.mExtension == null) {
351 task.createArg().setValue("-0");
352 task.createArg().setValue("");
353 compressNothing = true;
358 if (compressNothing == false) {
359 for (NoCompress nc : mNoCompressList) {
360 task.createArg().setValue("-0");
361 task.createArg().setValue(nc.mExtension);
365 if (customPackage != null) {
366 task.createArg().setValue("--custom-package");
367 task.createArg().setValue(customPackage);
370 // if the project contains libraries, force auto-add-overlay
371 Object libSrc = taskProject.getReference("android.libraries.res");
372 if (libSrc != null) {
373 task.createArg().setValue("--auto-add-overlay");
376 if (mVersionCode != 0) {
377 task.createArg().setValue("--version-code");
378 task.createArg().setValue(Integer.toString(mVersionCode));
382 if (mManifest != null) {
383 task.createArg().setValue("-M");
384 task.createArg().setValue(mManifest);
387 // resources locations.
388 if (mResources.size() > 0) {
389 for (Path pathList : mResources) {
390 for (String path : pathList.list()) {
391 // This may not exists, and aapt doesn't like it, so we check first.
392 File res = new File(path);
393 if (res.isDirectory()) {
394 task.createArg().setValue("-S");
395 task.createArg().setValue(path);
401 // add other resources coming from library project
402 Object libPath = taskProject.getReference("android.libraries.res");
403 if (libPath instanceof Path) {
404 for (String path : ((Path)libPath).list()) {
405 // This may not exists, and aapt doesn't like it, so we check first.
406 File res = new File(path);
407 if (res.isDirectory()) {
408 task.createArg().setValue("-S");
409 task.createArg().setValue(path);
414 // assets location. This may not exists, and aapt doesn't like it, so we check first.
415 if (mAssets != null && new File(mAssets).isDirectory()) {
416 task.createArg().setValue("-A");
417 task.createArg().setValue(mAssets);
421 if (mAndroidJar != null) {
422 task.createArg().setValue("-I");
423 task.createArg().setValue(mAndroidJar);
426 // apk file. This is based on the apkFolder, apkBaseName, and the configName (if applicable)
427 String filename = null;
428 if (mApkName != null) {
430 } else if (mApkBaseName != null) {
431 filename = mApkBaseName + ".ap_";
434 if (filename != null) {
435 File file = new File(mApkFolder, filename);
436 task.createArg().setValue("-F");
437 task.createArg().setValue(file.getAbsolutePath());
440 // R class generation
441 if (generateRClass) {
442 task.createArg().setValue("-J");
443 task.createArg().setValue(mRFolder);
446 // final setup of the task
447 task.setProject(taskProject);
448 task.setOwningTarget(getOwningTarget());