diff options
Diffstat (limited to 'anttasks/src/com/android/ant/AaptExecTask.java')
-rw-r--r-- | anttasks/src/com/android/ant/AaptExecTask.java | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecTask.java b/anttasks/src/com/android/ant/AaptExecTask.java new file mode 100644 index 0000000..3e2350f --- /dev/null +++ b/anttasks/src/com/android/ant/AaptExecTask.java @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ant; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.ExecTask; +import org.apache.tools.ant.types.Path; + +import java.io.File; +import java.util.ArrayList; + +/** + * Task to execute aapt. + * + * <p>It does not follow the exec task format, instead it has its own parameters, which maps + * directly to aapt.</p> + * <p>It is able to run aapt several times if library setup requires generating several + * R.java files. + * <p>The following map shows how to use the task for each supported aapt command line + * parameter.</p> + * + * <table border="1"> + * <tr><td><b>Aapt Option</b></td><td><b>Ant Name</b></td><td><b>Type</b></td></tr> + * <tr><td>path to aapt</td><td>executable</td><td>attribute (Path)</td> + * <tr><td>command</td><td>command</td><td>attribute (String)</td> + * <tr><td>-v</td><td>verbose</td><td>attribute (boolean)</td></tr> + * <tr><td>-f</td><td>force</td><td>attribute (boolean)</td></tr> + * <tr><td>-M AndroidManifest.xml</td><td>manifest</td><td>attribute (Path)</td></tr> + * <tr><td>-I base-package</td><td>androidjar</td><td>attribute (Path)</td></tr> + * <tr><td>-A asset-source-dir</td><td>assets</td><td>attribute (Path</td></tr> + * <tr><td>-S resource-sources</td><td><res path=""></td><td>nested element(s)<br>with attribute (Path)</td></tr> + * <tr><td>-0 extension</td><td><nocompress extension=""><br><nocompress></td><td>nested element(s)<br>with attribute (String)</td></tr> + * <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> + * <tr><td>-J R-file-dir</td><td>rfolder</td><td>attribute (Path)<br>-m always enabled</td></tr> + * <tr><td></td><td></td><td></td></tr> + * </table> + */ +public final class AaptExecTask extends BaseTask { + + /** + * Class representing a <nocompress> node in the main task XML. + * This let the developers prevent compression of some files in assets/ and res/raw/ + * by extension. + * If the extension is null, this will disable compression for all files in assets/ and + * res/raw/ + */ + public final static class NoCompress { + String mExtension; + + /** + * Sets the value of the "extension" attribute. + * @param extention the extension. + */ + public void setExtension(String extention) { + mExtension = extention; + } + } + + private String mExecutable; + private String mCommand; + private boolean mForce = true; // true due to legacy reasons + private boolean mDebug = false; + private boolean mVerbose = false; + private boolean mUseCrunchCache = false; + private int mVersionCode = 0; + private String mVersionName; + private String mManifest; + private ArrayList<Path> mResources; + private String mAssets; + private String mAndroidJar; + private String mApkFolder; + private String mApkName; + private String mResourceFilter; + private String mRFolder; + private final ArrayList<NoCompress> mNoCompressList = new ArrayList<NoCompress>(); + private String mProjectLibrariesResName; + private String mProjectLibrariesPackageName; + + /** + * Sets the value of the "executable" attribute. + * @param executable the value. + */ + public void setExecutable(Path executable) { + mExecutable = TaskHelper.checkSinglePath("executable", executable); + } + + /** + * Sets the value of the "command" attribute. + * @param command the value. + */ + public void setCommand(String command) { + mCommand = command; + } + + /** + * Sets the value of the "force" attribute. + * @param force the value. + */ + public void setForce(boolean force) { + mForce = force; + } + + /** + * Sets the value of the "verbose" attribute. + * @param verbose the value. + */ + public void setVerbose(boolean verbose) { + mVerbose = verbose; + } + + /** + * Sets the value of the "usecrunchcache" attribute + * @param usecrunch whether to use the crunch cache. + */ + public void setNoCrunch(boolean nocrunch) { + mUseCrunchCache = nocrunch; + } + + public void setVersioncode(String versionCode) { + if (versionCode.length() > 0) { + try { + mVersionCode = Integer.decode(versionCode); + } catch (NumberFormatException e) { + System.out.println(String.format( + "WARNING: Ignoring invalid version code value '%s'.", versionCode)); + } + } + } + + /** + * Sets the value of the "versionName" attribute + * @param versionName the value + */ + public void setVersionname(String versionName) { + mVersionName = versionName; + } + + public void setDebug(boolean value) { + mDebug = value; + } + + /** + * Sets the value of the "manifest" attribute. + * @param manifest the value. + */ + public void setManifest(Path manifest) { + mManifest = TaskHelper.checkSinglePath("manifest", manifest); + } + + /** + * Sets the value of the "resources" attribute. + * @param resources the value. + * + * @deprecated Use nested element(s) <res path="value" /> + */ + @Deprecated + public void setResources(Path resources) { + System.out.println("WARNNG: Using deprecated 'resources' attribute in AaptExecLoopTask." + + "Use nested element(s) <res path=\"value\" /> instead."); + if (mResources == null) { + mResources = new ArrayList<Path>(); + } + + mResources.add(new Path(getProject(), resources.toString())); + } + + /** + * Sets the value of the "assets" attribute. + * @param assets the value. + */ + public void setAssets(Path assets) { + mAssets = TaskHelper.checkSinglePath("assets", assets); + } + + /** + * Sets the value of the "androidjar" attribute. + * @param androidJar the value. + */ + public void setAndroidjar(Path androidJar) { + mAndroidJar = TaskHelper.checkSinglePath("androidjar", androidJar); + } + + /** + * Sets the value of the "outfolder" attribute. + * @param outFolder the value. + * @deprecated use {@link #setApkfolder(Path)} + */ + @Deprecated + public void setOutfolder(Path outFolder) { + System.out.println("WARNNG: Using deprecated 'outfolder' attribute in AaptExecLoopTask." + + "Use 'apkfolder' (path) instead."); + mApkFolder = TaskHelper.checkSinglePath("outfolder", outFolder); + } + + /** + * Sets the value of the "apkfolder" attribute. + * @param apkFolder the value. + */ + public void setApkfolder(Path apkFolder) { + mApkFolder = TaskHelper.checkSinglePath("apkfolder", apkFolder); + } + + /** + * Sets the value of the resourcefilename attribute + * @param apkName the value + */ + public void setResourcefilename(String apkName) { + mApkName = apkName; + } + + /** + * Sets the value of the "rfolder" attribute. + * @param rFolder the value. + */ + public void setRfolder(Path rFolder) { + mRFolder = TaskHelper.checkSinglePath("rfolder", rFolder); + } + + public void setresourcefilter(String filter) { + if (filter != null && filter.length() > 0) { + mResourceFilter = filter; + } + } + + public void setProjectLibrariesResName(String projectLibrariesResName) { + mProjectLibrariesResName = projectLibrariesResName; + } + + public void setProjectLibrariesPackageName(String projectLibrariesPackageName) { + mProjectLibrariesPackageName = projectLibrariesPackageName; + } + + + /** + * Returns an object representing a nested <var>nocompress</var> element. + */ + public Object createNocompress() { + NoCompress nc = new NoCompress(); + mNoCompressList.add(nc); + return nc; + } + + /** + * Returns an object representing a nested <var>res</var> element. + */ + public Object createRes() { + if (mResources == null) { + mResources = new ArrayList<Path>(); + } + + Path path = new Path(getProject()); + mResources.add(path); + + return path; + } + + /* + * (non-Javadoc) + * + * Executes the loop. Based on the values inside project.properties, this will + * create alternate temporary ap_ files. + * + * @see org.apache.tools.ant.Task#execute() + */ + @Override + public void execute() throws BuildException { + if (mProjectLibrariesResName == null) { + throw new BuildException("Missing attribute projectLibrariesResName"); + } + if (mProjectLibrariesPackageName == null) { + throw new BuildException("Missing attribute projectLibrariesPackageName"); + } + + Project taskProject = getProject(); + + String libPkgProp = null; + + // if the parameters indicate generation of the R class, check if + // more R classes need to be created for libraries. + if (mRFolder != null && new File(mRFolder).isDirectory()) { + libPkgProp = taskProject.getProperty(mProjectLibrariesPackageName); + if (libPkgProp != null) { + // Replace ";" with ":" since that's what aapt expects + libPkgProp = libPkgProp.replace(';', ':'); + } + } + // Call aapt. If there are libraries, we'll pass a non-null string of libs. + callAapt(libPkgProp); + } + + @Override + protected String getExecTaskName() { + return "aapt"; + } + + /** + * Calls aapt with the given parameters. + * @param resourceFilter the resource configuration filter to pass to aapt (if configName is + * non null) + * @param extraPackages an optional list of colon-separated packages. Can be null + * Ex: com.foo.one:com.foo.two:com.foo.lib + */ + private void callAapt(String extraPackages) { + Project taskProject = getProject(); + + final boolean generateRClass = mRFolder != null && new File(mRFolder).isDirectory(); + + // Get whether we have libraries + Object libResRef = taskProject.getReference(mProjectLibrariesResName); + + // Set up our folders to check for changed files + ArrayList<File> watchPaths = new ArrayList<File>(); + // We need to watch for changes in the main project res folder + for (Path pathList : mResources) { + for (String path : pathList.list()) { + watchPaths.add(new File(path)); + } + } + // and if libraries exist, in their res folders + if (libResRef instanceof Path) { + for (String path : ((Path)libResRef).list()) { + watchPaths.add(new File(path)); + } + } + // If we're here to generate a .ap_ file we need to watch assets as well + if (!generateRClass) { + File assetsDir = new File(mAssets); + if (mAssets != null && assetsDir.isDirectory()) { + watchPaths.add(assetsDir); + } + } + + // Now we figure out what we need to do + if (generateRClass) { + // Check to see if our dependencies have changed. If not, then skip + if (initDependencies(mRFolder + File.separator + "R.java.d", watchPaths) + && dependenciesHaveChanged() == false) { + System.out.println("No changed resources. R.java and Manifest.java untouched."); + return; + } else { + System.out.println("Generating resource IDs..."); + } + } else { + // Find our dependency file. It should have the same name as our target .ap_ but + // with a .d extension + String dependencyFilePath = mApkFolder + File.separator + mApkName; + dependencyFilePath += ".d"; + + // Check to see if our dependencies have changed + if (initDependencies(dependencyFilePath , watchPaths) + && dependenciesHaveChanged() == false) { + System.out.println("No changed resources or assets. " + mApkName + + " remains untouched"); + return; + } + if (mResourceFilter == null) { + System.out.println("Creating full resource package..."); + } else { + System.out.println(String.format( + "Creating resource package with filter: (%1$s)...", + mResourceFilter)); + } + } + + // create a task for the default apk. + ExecTask task = new ExecTask(); + task.setExecutable(mExecutable); + task.setFailonerror(true); + + task.setTaskName(getExecTaskName()); + + // aapt command. Only "package" is supported at this time really. + task.createArg().setValue(mCommand); + + // No crunch flag + if (mUseCrunchCache) { + task.createArg().setValue("--no-crunch"); + } + + // force flag + if (mForce) { + task.createArg().setValue("-f"); + } + + // verbose flag + if (mVerbose) { + task.createArg().setValue("-v"); + } + + if (mDebug) { + task.createArg().setValue("--debug-mode"); + } + + if (generateRClass) { + task.createArg().setValue("-m"); + } + + // filters if needed + if (mResourceFilter != null) { + task.createArg().setValue("-c"); + task.createArg().setValue(mResourceFilter); + } + + // no compress flag + // first look to see if there's a NoCompress object with no specified extension + boolean compressNothing = false; + for (NoCompress nc : mNoCompressList) { + if (nc.mExtension == null) { + task.createArg().setValue("-0"); + task.createArg().setValue(""); + compressNothing = true; + break; + } + } + + if (compressNothing == false) { + for (NoCompress nc : mNoCompressList) { + task.createArg().setValue("-0"); + task.createArg().setValue(nc.mExtension); + } + } + + if (extraPackages != null) { + task.createArg().setValue("--extra-packages"); + task.createArg().setValue(extraPackages); + } + + // if the project contains libraries, force auto-add-overlay + if (libResRef != null) { + task.createArg().setValue("--auto-add-overlay"); + } + + if (mVersionCode != 0) { + task.createArg().setValue("--version-code"); + task.createArg().setValue(Integer.toString(mVersionCode)); + } + + if ((mVersionName != null) && (mVersionName.length() > 0)) { + task.createArg().setValue("--version-name"); + task.createArg().setValue(mVersionName); + } + + // manifest location + if (mManifest != null) { + task.createArg().setValue("-M"); + task.createArg().setValue(mManifest); + } + + // resources locations. + if (mResources.size() > 0) { + for (Path pathList : mResources) { + for (String path : pathList.list()) { + // This may not exists, and aapt doesn't like it, so we check first. + File res = new File(path); + if (res.isDirectory()) { + task.createArg().setValue("-S"); + task.createArg().setValue(path); + } + } + } + } + + // add other resources coming from library project + if (libResRef instanceof Path) { + for (String path : ((Path)libResRef).list()) { + // This may not exists, and aapt doesn't like it, so we check first. + File res = new File(path); + if (res.isDirectory()) { + task.createArg().setValue("-S"); + task.createArg().setValue(path); + } + } + } + + // assets location. This may not exists, and aapt doesn't like it, so we check first. + if (mAssets != null && new File(mAssets).isDirectory()) { + task.createArg().setValue("-A"); + task.createArg().setValue(mAssets); + } + + // android.jar + if (mAndroidJar != null) { + task.createArg().setValue("-I"); + task.createArg().setValue(mAndroidJar); + } + + // apk file. This is based on the apkFolder, apkBaseName, and the configName (if applicable) + String filename = null; + if (mApkName != null) { + filename = mApkName; + } + + if (filename != null) { + File file = new File(mApkFolder, filename); + task.createArg().setValue("-F"); + task.createArg().setValue(file.getAbsolutePath()); + } + + // R class generation + if (generateRClass) { + task.createArg().setValue("-J"); + task.createArg().setValue(mRFolder); + } + + // Use dependency generation + task.createArg().setValue("--generate-dependencies"); + + // final setup of the task + task.setProject(taskProject); + task.setOwningTarget(getOwningTarget()); + + // execute it. + task.execute(); + } +} |