ANT build for Android Proguard obfuscation - android

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.

Related

Ant build in Eclipse

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

Using Ant how can I dex a directory of jars?

I have a directory full of jars (felix bundles). I want to iterate through all of these jars and create dex'd versions. My intent is to deploy each of these dex'd jars as standalone apk's since they are bundles. Feel free to straighten me out if I am approaching this from the wrong direction.
This first part is just to try and create a corresponding .dex file for each jar. However when I run this I am getting a "no resources specified" error coming out of Ant.
Is this the right approach, or is there a simpler approach to just input a jar and output a dex'd version of that jar? The ${file} is valid as it is spitting out the name of the file in the echo command.
<target name="dexBundles" description="Run dex on all the bundles">
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${basedir}/libs/ant-contrib.jar" />
<echo>Starting</echo>
<for param="file">
<path>
<fileset dir="${pre.dex.dir}">
<include name="**/*.jar" />
</fileset>
</path>
<sequential>
<echo message="#{file}" />
<echo>Converting jar file #{file} into ${post.dex.dir}/#{file}.class...</echo>
<apply executable="${dx}" failonerror="true" parallel="true" verbose="true">
<arg value="--dex" />
<arg value="--output=${post.dex.dir}/${file}.dex" />
<arg path="#{file}" />
</apply>
</sequential>
</for>
<echo>Finished</echo>
</target>
Give this a go:
<target name="dexBundles" description="Run dex on all the bundles">
<apply executable="${exec.dx}" dest="${post.dex.dir}/" parallel="false">
<arg value="--dex"/>
<arg value="--output="/>
<targetfile/>
<srcfile/>
<fileset dir="${pre.dex.dir}" includes="**/*.jar"/>
<mapper type="glob" from="*.jar" to="*.dex"/>
</apply>
</target>
It looks like the ant apply task allows you to iterate over a file set without need for ant-contrib (specifically that page has an example that looks for *.c in a directory, compiles them, and renames them to *.o in a specified directory that should be directly applicable). Unfortunately, it looks like you'll lose the traceability provided by your echo messages.
For the record, I believe the error message is actually being generated by dx.bat not ant directly, but I am not certain, and I don't know why.
Hope that helps.
see the document of the "Ant apply task",it's said:“At least one fileset or filelist is required”
so,this line "no resources specified" is printed to notify u that to write some fileset or filelist.....
use "exec" instead.

Jenkins mobile air android packaging error

I've searched google and SO for solutions, but could not find any. I'm developing a mobile AIR app for android, and i use Jenkins as a local CI system. My project compiles fine, however, during the ADT packaging something goes wrong. I've copied the ADT packaging targets from the following examples:
http://blog.terrenceryan.com/using-ant-to-package-the-same-air-app-to-multiple-devices/
and
https://gist.github.com/630170
However, i am getting this output in Jenkins: http://d.pr/i/y2gJ
This is the packaging part in my build.xml file (with important property names and values used):
...
...
<property name="APP_NAME" value="Hightide"/>
<property name="ANDROID_HOME" value="${user.home}/../../../Supermaggel/SDKS/android-sdk-macosx" />
<property name="APP_DESCRIPTOR" value="${SOURCE_DIR}/${APP_NAME}-app.xml" />
<property name="SWF_FILE" value="${APP_NAME}.swf" />
<property name="OUTPUT_LOCATION_ANDROID" location="${BUILD_DIR}/android" />
<property name="OUTPUT_SWF_ANDROID" location="${OUTPUT_LOCATION_ANDROID}/${SWF_FILE}" />
<property name="OUTPUT_APK_ANDROID" value="OUTPUT_LOCATION_ANDROID/${APP_NAME}.apk" />
...
...
<!-- PACKAGE ANDROID -->
<target name="package-android">
<echo message="Packaging for Android"/>
<exec executable="${ADT}" dir="${OUTPUT_LOCATION_ANDROID}">
<arg line="-package"/>
<arg line="-target apk"/>
<arg line="-storetype pkcs12"/>
<arg line="-keystore ${KEYSTORE_ANDROID}" />
<arg line="-storepass ${STOREPASS_ANDROID}" />
<arg line="${APP_NAME}"/> <!-- output .APK -->
<arg line="${APP_DESCRIPTOR}"/> <!-- app descriptor location -->
<arg line="${OUTPUT_SWF_ANDROID}"/> <!-- output -->
</exec>
</target>
I am using Jenkins ver. 1.486, Flash Builder 4.6, AIR 3.3.
Can anyone point out what is going wrong during the packaging? any arguments missing or interpreted wrong?
I found out what caused it. The ADT cli tool gives exit code 2, which means something is wrong with the parameters... i spent hours looking at it, trying to figure out what was wrong. for ADT you need to pass in relative paths, not 'absolute' ones (or, relative from the project root.) so instead of ${PROJECT_ROOT}/packagedir/${APPNAME}.apk just use packagedir/${APPNAME}.apk or something similar, for all paths.

Testing with Robolectric and ANT

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

Android - Proguard obfuscate not able to find android classes

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.

Categories

Resources