Home | History | Annotate | Download | only in ant
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      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
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     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.
     15  */
     16 
     17 package com.android.ant;
     18 
     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;
     24 
     25 import java.io.File;
     26 import java.util.ArrayList;
     27 
     28 /**
     29  * Task to execute aapt.
     30  *
     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
     34  * R.java files.
     35  * <p>The following map shows how to use the task for each supported aapt command line
     36  * parameter.</p>
     37  *
     38  * <table border="1">
     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>&lt;res path=""&gt;</td><td>nested element(s)<br>with attribute (Path)</td></tr>
     48  * <tr><td>-0 extension</td><td>&lt;nocompress extension=""&gt;<br>&lt;nocompress&gt;</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>
     52  * </table>
     53  */
     54 public final class AaptExecLoopTask extends Task {
     55 
     56     /**
     57      * Class representing a &lt;nocompress&gt; node in the main task XML.
     58      * This let the developers prevent compression of some files in assets/ and res/raw/
     59      * by extension.
     60      * If the extension is null, this will disable compression for all  files in assets/ and
     61      * res/raw/
     62      */
     63     public final static class NoCompress {
     64         String mExtension;
     65 
     66         /**
     67          * Sets the value of the "extension" attribute.
     68          * @param extention the extension.
     69          */
     70         public void setExtension(String extention) {
     71             mExtension = extention;
     72         }
     73     }
     74 
     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>();
     90 
     91     /**
     92      * Sets the value of the "executable" attribute.
     93      * @param executable the value.
     94      */
     95     public void setExecutable(Path executable) {
     96         mExecutable = TaskHelper.checkSinglePath("executable", executable);
     97     }
     98 
     99     /**
    100      * Sets the value of the "command" attribute.
    101      * @param command the value.
    102      */
    103     public void setCommand(String command) {
    104         mCommand = command;
    105     }
    106 
    107     /**
    108      * Sets the value of the "force" attribute.
    109      * @param force the value.
    110      */
    111     public void setForce(boolean force) {
    112         mForce = force;
    113     }
    114 
    115     /**
    116      * Sets the value of the "verbose" attribute.
    117      * @param verbose the value.
    118      */
    119     public void setVerbose(boolean verbose) {
    120         mVerbose = verbose;
    121     }
    122 
    123     public void setVersioncode(String versionCode) {
    124         if (versionCode.length() > 0) {
    125             try {
    126                 mVersionCode = Integer.decode(versionCode);
    127             } catch (NumberFormatException e) {
    128                 System.out.println(String.format(
    129                         "WARNING: Ignoring invalid version code value '%s'.", versionCode));
    130             }
    131         }
    132     }
    133 
    134     /**
    135      * Sets the value of the "manifest" attribute.
    136      * @param manifest the value.
    137      */
    138     public void setManifest(Path manifest) {
    139         mManifest = TaskHelper.checkSinglePath("manifest", manifest);
    140     }
    141 
    142     /**
    143      * Sets the value of the "resources" attribute.
    144      * @param resources the value.
    145      *
    146      * @deprecated Use nested element(s) <res path="value" />
    147      */
    148     @Deprecated
    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>();
    154         }
    155 
    156         mResources.add(new Path(getProject(), resources.toString()));
    157     }
    158 
    159     /**
    160      * Sets the value of the "assets" attribute.
    161      * @param assets the value.
    162      */
    163     public void setAssets(Path assets) {
    164         mAssets = TaskHelper.checkSinglePath("assets", assets);
    165     }
    166 
    167     /**
    168      * Sets the value of the "androidjar" attribute.
    169      * @param androidJar the value.
    170      */
    171     public void setAndroidjar(Path androidJar) {
    172         mAndroidJar = TaskHelper.checkSinglePath("androidjar", androidJar);
    173     }
    174 
    175     /**
    176      * Sets the value of the "outfolder" attribute.
    177      * @param outFolder the value.
    178      * @deprecated use {@link #setApkfolder(Path)}
    179      */
    180     @Deprecated
    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);
    185     }
    186 
    187     /**
    188      * Sets the value of the "apkfolder" attribute.
    189      * @param apkFolder the value.
    190      */
    191     public void setApkfolder(Path apkFolder) {
    192         mApkFolder = TaskHelper.checkSinglePath("apkfolder", apkFolder);
    193     }
    194 
    195     /**
    196      * Sets the value of the "basename" attribute.
    197      * @param baseName the value.
    198      * @deprecated use {@link #setApkbasename(String)}
    199      */
    200     @Deprecated
    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;
    205     }
    206 
    207     /**
    208      * Sets the value of the "apkbasename" attribute.
    209      * @param apkbaseName the value.
    210      */
    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;
    215     }
    216 
    217     /**
    218      * Sets the value of the resourcefilename attribute
    219      * @param apkName the value
    220      */
    221     public void setResourcefilename(String apkName) {
    222         mApkName = apkName;
    223     }
    224 
    225     /**
    226      * Sets the value of the "rfolder" attribute.
    227      * @param rFolder the value.
    228      */
    229     public void setRfolder(Path rFolder) {
    230         mRFolder = TaskHelper.checkSinglePath("rfolder", rFolder);
    231     }
    232 
    233     public void setresourcefilter(String filter) {
    234         if (filter != null && filter.length() > 0) {
    235             mResourceFilter = filter;
    236         }
    237     }
    238 
    239     /**
    240      * Returns an object representing a nested <var>nocompress</var> element.
    241      */
    242     public Object createNocompress() {
    243         NoCompress nc = new NoCompress();
    244         mNoCompressList.add(nc);
    245         return nc;
    246     }
    247 
    248     /**
    249      * Returns an object representing a nested <var>res</var> element.
    250      */
    251     public Object createRes() {
    252         if (mResources == null) {
    253             mResources = new ArrayList<Path>();
    254         }
    255 
    256         Path path = new Path(getProject());
    257         mResources.add(path);
    258 
    259         return path;
    260     }
    261 
    262     /*
    263      * (non-Javadoc)
    264      *
    265      * Executes the loop. Based on the values inside default.properties, this will
    266      * create alternate temporary ap_ files.
    267      *
    268      * @see org.apache.tools.ant.Task#execute()
    269      */
    270     @Override
    271     public void execute() throws BuildException {
    272         Project taskProject = getProject();
    273 
    274         // first do a full resource package
    275         callAapt(null /*customPackage*/);
    276 
    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");
    284 
    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.
    291                         callAapt(libPkg);
    292                     }
    293                 }
    294             }
    295         }
    296     }
    297 
    298     /**
    299      * Calls aapt with the given parameters.
    300      * @param resourceFilter the resource configuration filter to pass to aapt (if configName is
    301      * non null)
    302      * @param customPackage an optional custom package.
    303      */
    304     private void callAapt(String customPackage) {
    305         Project taskProject = getProject();
    306 
    307         final boolean generateRClass = mRFolder != null && new File(mRFolder).isDirectory();
    308 
    309         if (generateRClass) {
    310         } else if (mResourceFilter == null) {
    311             System.out.println("Creating full resource package...");
    312         } else {
    313             System.out.println(String.format(
    314                     "Creating resource package with filter: (%1$s)...",
    315                     mResourceFilter));
    316         }
    317 
    318         // create a task for the default apk.
    319         ExecTask task = new ExecTask();
    320         task.setExecutable(mExecutable);
    321         task.setFailonerror(true);
    322 
    323         // aapt command. Only "package" is supported at this time really.
    324         task.createArg().setValue(mCommand);
    325 
    326         // force flag
    327         if (mForce) {
    328             task.createArg().setValue("-f");
    329         }
    330 
    331         // verbose flag
    332         if (mVerbose) {
    333             task.createArg().setValue("-v");
    334         }
    335 
    336         if (generateRClass) {
    337             task.createArg().setValue("-m");
    338         }
    339 
    340         // filters if needed
    341         if (mResourceFilter != null) {
    342             task.createArg().setValue("-c");
    343             task.createArg().setValue(mResourceFilter);
    344         }
    345 
    346         // no compress flag
    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;
    354                 break;
    355             }
    356         }
    357 
    358         if (compressNothing == false) {
    359             for (NoCompress nc : mNoCompressList) {
    360                 task.createArg().setValue("-0");
    361                 task.createArg().setValue(nc.mExtension);
    362             }
    363         }
    364 
    365         if (customPackage != null) {
    366             task.createArg().setValue("--custom-package");
    367             task.createArg().setValue(customPackage);
    368         }
    369 
    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");
    374         }
    375 
    376         if (mVersionCode != 0) {
    377             task.createArg().setValue("--version-code");
    378             task.createArg().setValue(Integer.toString(mVersionCode));
    379         }
    380 
    381         // manifest location
    382         if (mManifest != null) {
    383             task.createArg().setValue("-M");
    384             task.createArg().setValue(mManifest);
    385         }
    386 
    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);
    396                     }
    397                 }
    398             }
    399         }
    400 
    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);
    410                 }
    411             }
    412         }
    413 
    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);
    418         }
    419 
    420         // android.jar
    421         if (mAndroidJar != null) {
    422             task.createArg().setValue("-I");
    423             task.createArg().setValue(mAndroidJar);
    424         }
    425 
    426         // apk file. This is based on the apkFolder, apkBaseName, and the configName (if applicable)
    427         String filename = null;
    428         if (mApkName != null) {
    429             filename = mApkName;
    430         } else if (mApkBaseName != null) {
    431             filename = mApkBaseName + ".ap_";
    432         }
    433 
    434         if (filename != null) {
    435             File file = new File(mApkFolder, filename);
    436             task.createArg().setValue("-F");
    437             task.createArg().setValue(file.getAbsolutePath());
    438         }
    439 
    440         // R class generation
    441         if (generateRClass) {
    442             task.createArg().setValue("-J");
    443             task.createArg().setValue(mRFolder);
    444         }
    445 
    446         // final setup of the task
    447         task.setProject(taskProject);
    448         task.setOwningTarget(getOwningTarget());
    449 
    450         // execute it.
    451         task.execute();
    452     }
    453 }
    454