I’m working on setting up Robolectric in a continuous build environment, and I have some problems setting it all up.
My android Eclipse project structure consists of an android project and a JUnit4 test project as described in the Robolectric “Quick Start for Eclipse” quick guide. My sample tests works fine from within Eclipse, but I need to be able to test with ant also. How would I build my ANT build.xml to support this? and how should I apply the same changes that are done with the Eclipse test project?
I’ve been looking into the build.xml file of the RobolectricSample project, but that consists of a single project with both production and test code located under the projects src folder. I understand this is how maven assumes things are (???), but I’d like to go for ANT only.
This is old but hopefully this helps someone else. I've done this recently...robolectric, mockito, Jenkins, and ant. Here is the ant build script that I run. Basically you just need to setup the path to your libraries and setup a target to start the test. I also copy the android.jar file and the maps.jar file in the lib folder of the test project, this seems to make life easier but perhaps you can do it some better way.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project name="unit_tests" default="test-report-junit" basedir=".">
<description>
Sample Robolectric Ant Build
</description>
<!-- set global properties for this build, if you have libraries, include them here with a relative path...I have samples as library1, library2, library3, located on the same level as your project, but you may need to modified this to get it to work for your setup. -->
<property name="libs.dir" value="./lib/"/>
<property name="build.dir" value="./build/"/>
<property name="android.library1.classpath" value="./../../library1/bin/classes/"/>
<property name="android.library2.classpath" value="./../../library2/bin/classes/"/>
<property name="android.library3.classpath" value="./../../library3/bin/classes/"/>
<property name="test.report.dir" value="./test-reports/"/>
<property name="test.html.dir" value="./test-report-html/"/>
<property name="source.dir" value="./src/"/>
<filelist id="android_jars" dir="${libs.dir}">
<file name="android.jar"/>
<file name="maps.jar"/>
</filelist>
<filelist id="libs_jars" dir="${libs.dir}">
<file name="junit.jar"/>
<file name="hamcrest.jar"/>
<file name="json.jar"/>
<file name="google-play-services.jar"/>
<file name="mockito-all-1.9.5.jar"/>
<file name="robolectric-1.1-jar-with-dependencies.jar"/>
</filelist>
<path id="compile_classpath">
<filelist refid="libs_jars"/>
<filelist refid="android_jars"/>
<pathelement path="${android.project.classpath}"/>
<pathelement path="${android.library1.classpath}"/>
<pathelement path="${android.library2.classpath}"/>
<pathelement path="${android.library3.classpath}"/>
<pathelement path="${build.dir}"/>
</path>
<path id="junit_classpath">
<pathelement path="${build.dir}"/>
<pathelement path="${android.library1.classpath}"/>
<pathelement path="${android.library2.classpath}"/>
<pathelement path="${android.library3.classpath}"/>
<!-- NOTE: junit.jar must come before android.jar! -->
<filelist refid="libs_jars"/>
<filelist refid="android_jars"/>
</path>
<!-- targets -->
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<mkdir dir="${build.dir}"/>
</target>
<target name="compile" depends="init" description="compile test source">
<javac srcdir="${source.dir}" destdir="${build.dir}" debug="true" >
<classpath refid="compile_classpath" />
</javac>
<copy todir="build">
<fileset dir="src" includes="**/*.xml,**/*.properties,**/*.txt,**/*.ico" />
</copy>
</target>
<target name="test-run" depends="compile" description="Run JUnit tests">
<mkdir dir="${test.report.dir}"/>
<echo message="Running JUnit Tests in directory ${source.dir}..."/>
<junit showoutput="true" printsummary="yes" failureproperty="junit.failure" fork="yes" forkmode="once" maxmemory="512m">
<formatter type="plain"/>
<formatter type="xml"/>
<batchtest todir="${test.report.dir}">
<fileset dir="${source.dir}">
<include name="**/*Test.java"/>
</fileset>
</batchtest>
<classpath refid="junit_classpath"/>
</junit>
<fail if="junit.failure" message="Unit test(s) failed. See reports!"/>
</target>
<target name="test-report-junit" depends="test-run" description="Generate JUnit HTML reports">
<mkdir dir="${test.html.dir}"/>
<junitreport todir="${test.report.dir}">
<fileset dir="${test.report.dir}" includes="TEST-*.xml"/>
<report format="frames" todir="${test.html.dir}"/>
</junitreport>
</target>
<target name="clean" description="Clean Up" >
<delete dir="${build.dir}"/>
<delete dir="${test.report.dir}"/>
<delete dir="${test.html.dir}"/>
<delete file="${basedir}/tmp/cached-robolectric-classes.jar"/>
</target>
</project>
Finally, I run the following command from Jenkins to get it all started:
ant -f ./build-ant.xml test-report-junit
Related
I am trying to use custom build file in order to display the current git version in my Android application. I haven't used ant before and I have not really an idea how to use it. I read plenty of topics in SO and searched quite a lot in Google but I cannot figure it out. I don't really have the time to learn everything about ant but I need this thing running. At the bottom, you can find the code.
Current status
The file custom_rules.xml is imported in the build.xml created by Eclipse. The macrodef part is invoked but the targets not. I tried to change the External Tools Configurations, tab Targets but whenever I check a target (no matter in which ant file), I get a message:
Unknown argument: -pre-build
for example (when I put checkmark on -pre-build). I tried adding this line:
<import file="${sdk.dir}/tools/ant/build.xml" />
and defining sdk.dir but that doesn't change anything. What am I missing? As I said, I have no idea about ant and the only tutorial that helped me was this one.
Current code (custom_rules.xml)
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse.ant.import ?>
<project name="Custom build">
<macrodef name="git" taskname="#{taskname}">
<attribute name="command" />
<attribute name="dir" default="" />
<attribute name="property" default="" />
<attribute name="taskname" default="" />
<attribute name="failonerror" default="on" />
<element name="args" optional="true" />
<sequential>
<exec executable="git" dir="#{dir}" outputproperty="#{property}"
failifexecutionfails="#{failonerror}" failonerror="#{failonerror}">
<arg value="#{command}" />
<args/>
</exec>
</sequential>
</macrodef>
<target name="-pre-build">
<git command="rev-list" property="versioning.code" taskname="versioning">
<args>
<arg value="master" />
<arg value="--first-parent" />
<arg value="--count" />
</args>
</git>
<git command="describe" property="versioning.name" taskname="versioning">
<args>
<arg value="--always" />
</args>
</git>
<echo level="info" taskname="versioning">${versioning.code}, ${versioning.name}</echo>
<replaceregexp file="AndroidManifest.xml" match='android:versionCode=".*"' replace='android:versionCode="${versioning.code}"' />
<replaceregexp file="AndroidManifest.xml" match='android:versionName=".*"' replace='android:versionName="${versioning.name}"' />
</target>
<target name="-post-build" >
<replaceregexp file="AndroidManifest.xml" match='android:versionCode=".*"' replace='android:versionCode="0"' />
<replaceregexp file="AndroidManifest.xml" match='android:versionName=".*"' replace='android:versionName="0"' />
</target>
</project>
I just figured it out.
I renamed the targets to pre-build and post-build without -.
I went to External Tools Configurations and selected build.xml to the left. On the right, I went to the tab Targets, checked my two targets (in addition to the build target which already had a checkmark) and set the order to be pre-build, build, post-build.
I went to the project properties and I selected Builders on the left. I created a new builder using the build.xml file and having the three targets from the previous bullet point, in the same order. I placed this builder before the Java builder.
I removed the post-build target as it puts the version back to 0 and seems to do that earlier than I would like.
I am still not sure that this is the optimal solution. Also, the solution fails when there is no git versioning. I tried using this code for solving the issue but it didn't worked. Nevertheless, it is the best I could do and it helps me getting the info I need in the app.
Use Git executable with Argument --version and catch the output in a property for further usage, f.e. :
<project>
<exec executable="git" outputproperty="gitversion">
<arg value="--version"/>
</exec>
<echo>$${gitversion} => ${gitversion}</echo>
</project>
output :
[echo] ${gitversion} => git version 1.8.3.msysgit.0
I added ViewPagerIndicator to my project (it's an Android library project), and my tests work fine from IntelliJ, but fail when I run them from ANT. To be clear, only the test that is referencing classes from this Android Library project is failing, the rest are passing without any issue. I'm guessing something isn't right in my build.xml? Anyone else run into this?
Here's the stack trace:
java.lang.NoClassDefFoundError:
Lcom/viewpagerindicator/CirclePageIndicator;
com.google.inject.internal.util.$ComputationException:
java.lang.NoClassDefFoundError:
Lcom/viewpagerindicator/CirclePageIndicator; at
com.google.inject.internal.util.$MapMaker$StrategyImpl.compute(MapMaker.java:553)
at
com.google.inject.internal.util.$MapMaker$StrategyImpl.compute(MapMaker.java:419)
at
com.google.inject.internal.util.$CustomConcurrentHashMap$ComputingImpl.get(CustomConcurrentHashMap.java:2041)
at
com.google.inject.internal.FailableCache.get(FailableCache.java:50)
at
com.google.inject.internal.MembersInjectorStore.get(MembersInjectorStore.java:65)
at
com.google.inject.internal.InjectorImpl.getMembersInjector(InjectorImpl.java:950)
at
com.google.inject.internal.InjectorImpl.getMembersInjector(InjectorImpl.java:957)
at
com.google.inject.internal.InjectorImpl.injectMembers(InjectorImpl.java:943)
at
roboguice.inject.ContextScopedRoboInjector.injectMembersWithoutViews(ContextScopedRoboInjector.java:243)
at roboguice.activity.RoboActivity.onCreate(RoboActivity.java:78) at
com.mycompany.myproduct.activities.TutorialActivity.onCreate(TutorialActivity.java:36)
at
com.mycompany.myproduct.activities.TutorialActivityTest.setup(TutorialActivityTest.java:37)
at
com.xtremelabs.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:292)
I figured this out.
In the junit task under the test target in build.xml (I took the snippet from the AndroidIntellijStarter project on Github), you need to add a classpath reference pointing to the classes.jar file that gets built for every library project you have included in your project.
There might be a more elegant way to achieve this, but it's what I came up with on short notice:
<target name="test" depends="compile.tests" description="test all">
<mkdir dir="${out.dir}/out/reports/tests"/>
<junit showoutput="true" failureproperty="junit.failure">
<formatter type="plain" usefile="false" if="junit.console.out"/>
<formatter type="plain"/>
<formatter type="xml"/>
<batchtest todir="${out.dir}/out/reports/tests">
<fileset dir="${tested.project.test.absolute.dir}">
<include name="**/*Test.java"/>
</fileset>
</batchtest>
<classpath>
<!-- Project -->
<pathelement path="${out.classes.absolute.dir}"/>
<pathelement path="${out.test.classes.absolute.dir}"/>
<fileset dir="${extensible.libs.classpath}" includes="**/*.jar"/>
<!-- Library Project Here -->
<fileset dir="path/to/classes/jar" includes="*.jar"/>
<!-- Robolectric -->
<fileset dir="submodules/robolectric/lib/main" includes="*.jar"/>
<pathelement path="submodules/robolectric/bin/mainClasses"/>
<!-- Android -->
<path refid="android.target.classpath"/>
</classpath>
</junit>
<fail if="junit.failure" message="Unit test(s) failed. See reports!"/>
</target>
Given
Commons - simple java project
AndroidLibrary1 - android library
AndroidLibrary2 - android library
AndroidProject - android project
And the projects have such references to each other:
AndroidLibrary1 -> Commons
AndroidLibrary2 -> Commons
AndroidProject -> AndroidLibrary1, AndroidLibrary2
Problem
When I build AndroidProject I get such error:
java.lang.IllegalArgumentException: already added: Lcom/test/Bar;
where "com.test.Bar" is a class from "Commons" project, that used by both AndroidLibrary1 and AndroidLibrary2.
Environment
Eclipse 3.7.1
android-sdk-windows_r15
Any ideas how to fix this?
EDIT:
Found related discussion.
After considerable amount of time and regex black magic I have found this solution:
<target name="-post-compile">
<macrodef name="dex-helper">
<element name="external-libs" optional="yes" />
<attribute name="nolocals" default="false" />
<sequential>
<!-- sets the primary input for dex. If a pre-dex task sets it to
something else this has no effect -->
<property name="out.dex.input.absolute.dir" value="${out.classes.absolute.dir}" />
<!-- set the secondary dx input: the project (and library) jar files
If a pre-dex task sets it to something else this has no effect -->
<if>
<condition>
<isreference refid="out.dex.jar.input.ref" />
</condition>
<else>
<path id="out.dex.jar.input.ref">
<path refid="jar.libs.ref" />
</path>
</else>
</if>
<if>
<condition>
<length string="${toString:out.dex.jar.input.ref}" trim="true" when="greater" length="0"/>
</condition>
<then>
<echo message="${toString:out.dex.jar.input.ref}" file="antler.tmp" />
<loadfile property="out.dex.jar.input.ref.fixed" srcFile="antler.tmp">
<filterchain>
<tokenfilter>
<replaceregex
pattern="(?<=;|^)[^;]+\\libs\\([^\\;]+)(?:;|$)(?=(?:(?<=;|^)[^;]+(?:;|$))*(?<=;|^)[^;]+\\\1(?:;|$))"
replace="" flags="g"/>
</tokenfilter>
</filterchain>
</loadfile>
<delete file="antler.tmp"/>
<path id="out.dex.jar.input.ref.fixed" path="${out.dex.jar.input.ref.fixed}"/>
</then>
<else>
<path id="out.dex.jar.input.ref.fixed" />
</else>
</if>
<dex executable="${dx}"
output="${intermediate.dex.file}"
nolocals="#{nolocals}"
verbose="${verbose}">
<path path="${out.dex.input.absolute.dir}"/>
<path refid="out.dex.jar.input.ref.fixed" />
<external-libs />
</dex>
</sequential>
</macrodef>
</target>
This is what you should add to your build.xml file before the <import file="${sdk.dir}/tools/ant/build.xml" /> line. In the case you have the same JAR in the libs folder in several of your projects, this code will remove the duplicate entries from the build system based on the file names.
Now, how this works
I will use the word "library" for JAR in libs folders and "library project" for the Android library projects on which other projects may depend. The problem is that when Android system builds projects with library projects, it just bundles all the libraries into one heap ignoring the fact that there may be duplicates. So we have to fix the paths.
So when the build gets to the point where the compilation is complete, we redefine the part which is responsible for converting Java code into DEX. The code above is exactly as in the Android build tools, except for the following part:
<if>
<condition>
<length string="${toString:out.dex.jar.input.ref}" trim="true" when="greater" length="0"/>
</condition>
<then>
<echo message="${toString:out.dex.jar.input.ref}" file="antler.tmp" />
<loadfile property="out.dex.jar.input.ref.fixed" srcFile="antler.tmp">
<filterchain>
<tokenfilter>
<replaceregex
pattern="(?<=;|^)[^;]+\\libs\\([^\\;]+)(?:;|$)(?=(?:(?<=;|^)[^;]+(?:;|$))*(?<=;|^)[^;]+\\\1(?:;|$))"
replace="" flags="g"/>
</tokenfilter>
</filterchain>
</loadfile>
<delete file="antler.tmp"/>
<path id="out.dex.jar.input.ref.fixed" path="${out.dex.jar.input.ref.fixed}"/>
</then>
<else>
<path id="out.dex.jar.input.ref.fixed" />
</else>
</if>
We have the unclean paths in out.dex.jar.input.ref, and our goal is to create paths without the duplicate entries (they will be stored in out.dex.jar.input.ref.fixed). Unfortunately we have to unload the path to a file, change it, and load it back, because ant for some weird reasons can't work with regex in properties (unless you are using external libraries like ant-contrib).
The regex which does the trick is (?<=;|^)[^;]+\\libs\\([^\\;]+)(?:;|$)(?=(?:(?<=;|^)[^;]+(?:;|$))*(?<=;|^)[^;]+\\\1(?:;|$)). This horrible mess of symbols matches the path which ends with libs\<something.jar> and has somewhere further another path which ends with <something.jar>. My regex uses Windows separator, so if you are under Linux or Mac OS X, you should change those \\ to the necessary separator or make the regex universal. Sorry, I was just in no mood to fix it having spent too much time on it already. If you want to check out how the regex works, you can do so on Regexr.
After we have removed the duplicates, we just have to load back the string to a new path, out.dex.jar.input.ref.fixed and use it to run dex. Hope my solution helps.
I am unable to run obfuscate on my android project. I keep getting a 100 errors each saying the similar thing -
[javac] /MyPath/LocationReceiver.java:34: cannot find symbol
[javac] symbol : class Intent
[javac] location: class com.myPath.LocationReceiver
[javac] public final void onHandleIntent(Intent intent) {
Where would I need to look? My build.xml file has all the correct android paths specified.
My build file -
<property name="src.dir" value="src/com"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="input.jar.file" value="${build.dir}/temp.jar"/>
<property name="obfuscated_sdk.jar.file" value="${build.dir}/MyName.jar"/>
<property name="proguard-home" value="/MyPath/proguard4.6" />
<property name="android-home" value="/MyPath/android-sdk" />
<property name="android-version" value="8" />
<property name="android-platform-specific" value="${android-home}/platforms/android-${android-version}" />
<property name="android-jar" value="${android-platform-specific}/android.jar" />
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="build-sdk" depends="clean" description="compiles the sdk java files">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
source="1.6"
debuglevel="lines">
<classpath>
<path>
<pathelement location="${android-jar}" />
</path>
</classpath>
</javac>
</target>
<target name="create.input.jar" depends="build-sdk">
<jar destfile="${input.jar.file}" basedir="${classes.dir}">
<manifest>
<attribute name="MyName" value="v1.0"/>
</manifest>
</jar>
</target>
<target name="Obfuscate" depends="create.input.jar" description="shrink compiled classes">
<taskdef resource="proguard/ant/task.properties" classpath="/MyPath/proguard4.6/lib/proguard.jar" />
<proguard>
-libraryjars "/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/classes.jar"
-libraryjars "/MyPath/android-sdk/platforms/android-8/android.jar"
-injars ${input.jar.file}
-outjars ${obfuscated_sdk.jar.file}
</proguard>
<delete file="${input.jar.file}"/>
<delete dir="${classes.dir}"/>
<delete dir="bin"/>
</target>
This sounds like a classpath problem. It probably doesn't have anything to do with ProGuard, since the Android build system only invokes that when exporting a signed .apk file. Also, I believe that Eclipse uses its own build system and does not rely on build.xml.
Things to try:
Make sure that you created your project as an Android project. If not, start over again.
Right click on the project name in the package explorer and select Android Tools > Fix Project Properties.
Open the project properties, select Android and make sure you have a Project Build Target selected. Then select Java Build Path, and make sure that an Android library shows up in the Libraries pane.
Can anyone share with sample/simple obfuscation ANT task for Android? Provided that I do have complete APK and I need just pass *class hru Proguard and then prepare *.dex to build APK
I have found solution:
Get Proguard - copy proguard.jar into known directory (say MyProject/proguard)
Prepare proguard.cfg -describing what and how optimize/obfuscate. This process thoroughly described in Proguard's manual
Prepare following ANT's build.xml (or smth like this one) - great thanx to this guy
UPDATE complete build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyProject" default="zipalign" basedir=".">
<property name="target" value="android-8"/>
<!--property file="default.properties" /-->
<property name="encoding" value="UTF-8"/>
<!-- dirs -->
<property name="sdk.dir" location="Location of Android SDK"/>
<property name="proguard.dir" value="proguard" />
<property name="src.dir" value="src"/>
<property name="gen.dir" value="gen"/>
<property name="res.dir" value="res"/>
<property name="assets.dir" value="assets"/>
<property name="libs.dir" value="libs"/>
<property name="out.classes.unoptimized.dir" value="out"/>
<property name="out.classes.optimized.dir" value="out/optimized"/>
<!-- files -->
<property name="manifest.file" value="AndroidManifest.xml"/>
<property name="signed.apk" value="${ant.project.name}-signed.apk"/>
<property name="unsigned.apk" value="${ant.project.name}-unsigned.apk"/>
<property name="final.apk" value="${ant.project.name}.apk"/>
<property name="android.jar" value="${sdk.dir}/tools/platforms/${target}/android.jar"/>
<property name="unoptimized" value="unoptimized.jar" />
<property name="optimized" value="optimized.jar" />
<property name="proguard.config" value="${proguard.dir}/proguard.cfg"/>
<!-- tools -->
<property name="dx.jar" value="${sdk.dir}/platform-tools/lib/dx.jar"/>
<property name="aapt" value="${sdk.dir}/platforms/${target}/tools/aapt.exe"/>
<property name="zipalign" value="${sdk.dir}/tools/zipalign.exe"/>
<property name="jarsign" value="jarsigner.exe location is here"/>
<property name="keystore" value="Your key store is here"/>
<property name="keyalias" value="Your key alias is here"/>
<path id="android.antlibs">
<pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
<pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
<pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
<pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
<pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
</path>
<taskdef name="setup"
classname="com.android.ant.SetupTask"
classpathref="android.antlibs" />
<setup import="false"/>
<!--taskdef name="aaptexec"
classname="com.android.ant.AaptExecLoopTask"
classpathref="android.antlibs" /-->
<target name="clean" description="Removes output files created by other targets.">
<echo>Cleaning...</echo>
<delete dir="${out.classes.unoptimized.dir}" verbose="true" />
<delete dir="${out.classes.optimized.dir}" verbose="true" />
</target>
<target name="dirs">
<echo>Creating output directories if needed...</echo>
<mkdir dir="${out.classes.unoptimized.dir}" />
<mkdir dir="${out.classes.optimized.dir}" />
</target>
<!-- Compiles this project's .java files into .class files. -->
<target name="compile" depends="dirs"
description="Compiles project's .java files into .class files">
<echo>Compiling sources...</echo>
<javac encoding="${encoding}" target="1.6" debug="true" extdirs=""
destdir="${out.classes.unoptimized.dir}"
bootclasspathref="android.target.classpath"
includeantruntime="true">
<src path="${src.dir}" />
<src path="${gen.dir}" />
<classpath>
<fileset dir="${libs.dir}" includes="*.jar" />
</classpath>
</javac>
</target>
<target name="preobfuscate" depends="compile">
<echo>Preparing to obfuscation...</echo>
<jar destfile="${unoptimized}"
basedir="${out.classes.unoptimized.dir}"
includes="**/**"
excludes="optimized/**"
/>
</target>
<!-- Obfuscation with ProGuard -->
<target name="optimize" unless="nooptimize" depends="preobfuscate">
<echo>Proguard obfuscation...</echo>
<java jar="${proguard.dir}/proguard.jar" fork="true" failonerror="true">
<jvmarg value="-Dmaximum.inlined.code.length=16" />
<arg value="#${proguard.dir}/proguard.cfg" />
<arg value="-injars ${unoptimized}" />
<arg value="-outjars ${optimized}" />
<arg value="-libraryjars ${android.jar}" />
</java>
<unzip src="${optimized}" dest="${out.classes.optimized.dir}" />
<!-- Delete optimized jar (now unzipped into bin directory) -->
<delete file="${optimized}"/>
<delete file="${unoptimized}"/>
</target>
<target name="dex" description="Converting JVM bytecodes into Dalvik bytecodes" depends="optimize">
<echo>Converting bytecodes to Dalvik VM bytecodes...</echo>
<java jar="${dx.jar}" fork="true">
<arg line="--dex --verbose --output=${out.classes.optimized.dir}/classes.dex ${out.classes.optimized.dir}"/>
</java>
</target>
<target name="aapt" depends="dex" description="compile resources">
<echo>Packing resources...</echo>
<exec executable="${aapt}" logerror="true" osfamily="windows">
<arg line="p
-f
-M ${manifest.file}
-I ${android.jar}
-S ${res.dir}
-A ${assets.dir}
-F ${out.classes.optimized.dir}/${unsigned.apk}
-m -J ${gen.dir}"/>
</exec>
</target>
<target name="sign" depends="aapt" description="sign apk">
<input message="Please enter keystore password (store:${keystore}):"
addproperty="keystore.password" />
<echo>Signing apk...</echo>
<exec executable="${jarsign}" logerror="true" osfamily="windows">
<arg line="-verbose
-keystore ${keystore}
-storepass ${keystore.password}
-signedjar ${out.classes.optimized.dir}/${signed.apk}
${out.classes.optimized.dir}/${unsigned.apk} ${keyalias}"/>
</exec>
</target>
<target name="zipalign" depends="sign" description="zip align">
<echo>Aligning apk...</echo>
<exec executable="${zipalign}" logerror="true" osfamily="windows">
<arg line="-f
-v
4
${out.classes.optimized.dir}/${signed.apk}
${final.apk}"/>
</exec>
</target>
</project>
This ANT task has to be added to Eclipse's builders (Properties/Builders) tasks after Java builder and before Android package builder.
Press "Build All" (it's better to off Automatic Build check in Eclipse menu)
The Android build process first compiles Java source files (.java) to Java class files (.class), then converts these class files into Dalvik code (classes.dex), and finally packages this Dalvik code in an APK file.
ProGuard reads and writes Java class files, so it has to be inserted into this pipeline between the compilation step and the conversion step. It doesn't read or write Dalvik code itself, so it can't work on the APK file.
The Android SDK documentation on ProGuard discusses how to enable the obfuscation step in the Ant build for android-9. In short, you have to add a line "proguard.config=proguard.cfg" to the file default.properties, and then run "ant release".
Attention: barmaley's reply is from year 2011, and seem to be valid for Android SDK Tools version either 8 or 10.
I tried adapting this solution using Android SDK Tools version 18.1.1, but kept failing on the error:taskdef class com.android.ant.SetupTask cannot be found
Eventually, what I did was this:
rm build.xml
android update project -p .
If you don't have the SDK Tools in your PATH, you'll need to use the full path to the android tool, for example on Windows: C:\Android\sdk\tools
This created a fresh build.xml which is compliant with the current SDK Tools, and seem to automate a lot of the manual work that is described in barmaley's reply.
After that I was able to run ant release, which took care of building and obfuscating the result .apk file out of the box.
In order to automate obfuscation via ant, you'll need to:
Enable Proguard obfuscator (obviously)
Create an ant.properties file and fill it with the appropriate key.store params (see this SO reply for details).
The proGuard obfuscation process needs .class files so you can't launch an Ant before IDE build (.java) or after (.dex packed).
Have a look on this post where it's explained how add the proGuard step in your global Ant build:
http://www.androidengineer.com/2010/07/optimizing-obfuscating-and-shrinking.html
If you really want to use the IDEA build, you can try the following.
After the IDEA build unpack the apk with apktool.
Convert the .dex files to .class with dex2jar
Run proGuard as the previous post show you
Sorry that I don't attach you the links of apktool and dexjar but as I'm newbie I can't post more than one hyperlink.