A couple weeks back, the development team was challenged to start reflecting on our work and making contributions to the EverTrue blog. While this may be the first technical post, I am sure it will usher in some more diverse and interesting content for all to enjoy. This just happened to come at a time where I was working to solve a somewhat troubling issue in regards to our Android automated build system, so it was only logical to condense my efforts post-implementation into a medium others can benefit from.
Here at EverTrue, we prefer not to re-invent the wheel, so when we needed a stable automated build system for our Android applications, ant was a solid choice. Yet, we encountered a major issue that arose with renaming manifest package names in the automated deployment process. Surely there must be a solution as it must be supported. Well, upon inspection it is was, at one point. Much to my dismay, I could not find a solid source of information on what/where/how this problem manifested, and in this post I hope to centralize my findings in the hopes of saving others the burden.
Problem: So, the major problem encountered was the absence of the “ManifestPackage” option in AaptExecTask. An ant Exec Task is a chunk of code someone else has written to be executed in your ant script to perform a basic function. AaptExecTask is the ant Exec Task for aapt, the command line utility. If you are unfamiliar with aapt itself, brushen up, it is very useful. Ultimately, the benefit of using AaptExecTask in the build.xml script is that it provides an interface between the ant script and the actual calls to aapt so that you do not have to. It does some nice things, such as managing your library projects, paths, and other properties.
Reason: From what I have gathered, the ManifestPackage attribute of AaptExecTask(The ant wrapper of aapt command line utility) was missing from rev 14 of the android sdk, possibibly included in rev 17, and as of my looking, missing from Rev 19 and not in master of the source. The best source I could find was issue #21336, but it appears this patch was removed, lost or not included in the current sdk. Now, the reasons for not including it across rev’s may be wide and different, but for the more recent releases, it is my guess that they are not including it to encourage the use of library projects. Sure, this a great solution for many cases, but I would rather not manage 30+ projects for different package names.
Solution: Now, I would not call this a solution, as in a perfect world the android devs would just include the option back in and package it for rev 20. Then again, in the case that it does not happen, this is a workaround that will at least get you up and running. It includes replacing the AaptExecTask in your ant build.xml file with an aapt exec command that will allow you to use the –rename-manifest option manually. The tradeoffs to doing this is you do not get the nice management that AaptExecTask does, and you will need to manually change this any time you add / remove library projects, but this is well worth it in our case. The big one in additon to this, is any time a new revision of the sdk tools are released, there is a chance that this will break. Hopefully, it will be included before then.
Normally, the AaptExecTask looks something like this:
<aapt executable="${aapt}"
command="package"
versioncode="${version.code}"
versionname="${version.name}"
debug="${build.is.packaging.debug}"
manifest="AndroidManifest.xml"
assets="${asset.absolute.dir}"
androidjar="${android.jar}"
apkfolder="${out.absolute.dir}"
nocrunch="${build.packaging.nocrunch}"
resourcefilename="${resource.package.file.name}"
resourcefilter="${aapt.resource.filter}"
projectLibrariesResName="project.libraries.res"
projectLibrariesPackageName="project.libraries.package"
previousBuildType="${build.last.target}"
buildType="${build.target}">
<res path="${out.res.absolute.dir}" />
<res path="${resource.absolute.dir}" />
<!-- <nocompress /> forces no compression on any files in assets or res/raw -->
<!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
</aapt>
(Note: If you are unfamiliar with ant syntax -> ${VARIABLE_NAME} is how ant refers to variables)
As mentioned previously, this is funneling command line parameters (command, verbose, manifest, etc.) into AaptExecTask to be managed efficiently. So, we need to mimic everything that AaptExecTask is doing, on top of adding our –rename-manifest option. The end result would look something like this:
<echo>----------</echo>
<echo>Handling Resources...</echo>
<exec executable="${aapt}">
<arg value="package" />
<arg value="-m" />
<arg value="-J" />
<arg path="./gen" />
<arg value="--version-code"/>
<arg value="${v}"/>
<arg value="-M" />
<arg path="AndroidManifest.xml" />
<arg value="-I" />
<arg path="${android.jar}" />
<arg value="-A" />
<arg path="./assets"/>
<!-- Relevant parameters -->
<arg value="--rename-manifest-package"/>
<arg value="${NEWPACKAGENAME}"/>
<arg value="--auto-add-overlay"/>
<arg value="--generate-dependencies"/>
<arg value="--extra-packages"/>
<arg value=":LIBRARY_DEP_ONE_PACKAGENAME:LIBRARY_DEP_TWO_PACKAGENAME"/>
<arg value="-S"/>
<arg value="./res"/>
<arg value="-S"/>
<arg value="../PATH_TO_LIBRARY_DEP_ONE/res"/>
<arg value="-S"/>
<arg value="../PATH_TO_LIBRARY_DEP_TWO/res"/>
</exec>
If you can look quickly and see everything that has happened, excellent. Otherwise, I’ll step through to give a more accurate detailed view. For most of these arguments, (-m
, -J
, --version-code
, -M
, -I
, -A
) they work exactly the same as a normal aapt
command, and you should just look at aapt --help
to see their function as I am sure they will give a better description. Yes, I am purposely not telling you, it is such a useful tool to know. I will mention, however that for --version-code
, we have an ant variable ${v}
for version code, as we release updates, we use ant to auto-increment the version number from another target. You can also notice that you may either supply relative path names (ex. ./assets/
or use the ant variable ${asset.absolute.dir}
. There are many variables in place that may suit your needs, take a peek at the source build.xml file that is mentioned later.
As for the relevant rest:
–rename-manifest-package (${NEWPACKAGENAME})
Here, we supply the new package name that we are changing to. It may be supplied as an ant param, or calulated from another task, it is up to your preference.
-auto-add-overlay
Always used when including library projects. It is by default included in AaptExecTask whenever a library project is detected
–generate-dependencies
Used when including dependent library projects. From aapt –help -> “generate dependency files in the same directories for R.java and resource package”
–extra-packages
Here, you include the package names of every dependent library project deliminated by “:”
-S (./res)
-S (../PATH_TO_LIBRARY_DEP_ONE/res)
-S (../PATH_TO_LIBRARY_DEP_TWO/res)
With -S, you must supply the Resource folders of your project as well as every library project referenced
Implementation: Finally, in order to actually get this code into your ant config, you will need to override the ant tasks that call this aapt task. You will need to find the build.xml file from your sdk. For rev 19, it is located at $SDK_HOME/tools/ant/build.xml. Aapt is called twice within the build.xml of rev 19, within targets “-code-gen” and “-package-resources”. A target is a specific goal in ant to be accomplished; comparably, it functions as a method/function, but has the ability to be tied to other targets to allow for execution order. In order to override a specific target, you copy the target block from the build.xml in your sdk to your local build.xml above the import statement, and the local behavior in your build.xml file overrides that of the imported. So, you would copy both of these tasks, place them in your build.xml, modify them to include the above changes, and then pending correct implementation, you shall achieve renaming of manifest package.
<!-- Code Generation: compile resources (aapt -> R.java), aidl, renderscript -->
<target name="-code-gen">
<do-only-if-manifest-hasCode>
.
.
.
<echo>----------</echo>
<echo>Handling Resources...</echo>
<aapt executable="${aapt}"
command="package"
verbose="${verbose}"
manifest="AndroidManifest.xml"
androidjar="${android.jar}"
rfolder="${gen.absolute.dir}"
nonConstantId="${android.library}"
projectLibrariesResName="project.libraries.res"
projectLibrariesPackageName="project.libraries.package">
<res path="${out.res.absolute.dir}" />
<res path="${resource.absolute.dir}" />
</aapt>
.
.
.
</do-only-if-manifest-hasCode>
</target>
<target name="-package-resources" depends="-crunch">
<do-only-if-not-library elseText="Library project: do not package resources..." >
<aapt executable="${aapt}"
command="package"
versioncode="${version.code}"
versionname="${version.name}"
debug="${build.is.packaging.debug}"
manifest="AndroidManifest.xml"
assets="${asset.absolute.dir}"
androidjar="${android.jar}"
apkfolder="${out.absolute.dir}"
nocrunch="${build.packaging.nocrunch}"
resourcefilename="${resource.package.file.name}"
resourcefilter="${aapt.resource.filter}"
projectLibrariesResName="project.libraries.res"
projectLibrariesPackageName="project.libraries.package"
previousBuildType="${build.last.target}"
buildType="${build.target}">
<res path="${out.res.absolute.dir}" />
<res path="${resource.absolute.dir}" />
<!-- <nocompress /> forces no compression on any files in assets or res/raw -->
<!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
</aapt>
</do-only-if-not-library>
</target>
Conclusion: Post implementation, you should have a functional ant script where you can use “–rename-manifest-package”. However, like previously mentioned, you must take care every time the sdk tools are updated, check the sdk tool’s build.xml for changes and complement them. Or, if you need to modify a different aapt parameter or perhaps a different android exec task, I would suggest grabbing a copy of the source and reading the content yourself, it is a very valuable source of information.