I want to build an Android application using ant (as generated by the android tool). The application needs external libraries that are available as .jar. Since other (related) projects need the same libraries, I want to store them in a common place, outside of /libs (let's say "../somewhere/else/something.jar"). The application additionally needs a shared object, which is stored in /libs/armeabi/libSomething.so (it is copied there by ndk-build).
Does anybody know how I must configure my stuff to embed the classes in ../somewhere/else/something.jar into my .APK?
I tried various ways in ant.properties
...
java.compiler.classpath=../somewhere/else/something.jar
jar.libs.dir=libs:../somewhere/else
external.libs.dir=libs:../../ext/android/tomtom
and build.xml
...
<package-helper>
<extra-jars>
<jarfile path="../somewhere/else/something.jar" />
</extra-jars>
</package-helper>
...
<!-- From http://stackoverflow.com/questions/3217643/how-to-add-external-jar-libraries-to-an-android-project-from-the-command-line -->
<target name="-pre-compile">
<!-- HACK to add the android-support-v4.jar to the classpath directly from the SDK -->
<echo>ORIGINAL jars.path : ${toString:project.all.jars.path}</echo>
<path id="project.all.jars.path.hacked">
<path path="${toString:project.all.jars.path}"/>
<!-- <path path="${sdk.dir}/extras/android/support/v4/android-support-v4.jar"/> -->
</path>
<path id="project.all.jars.path">
<path path="${toString:project.all.jars.path.hacked}"/>
</path>
<echo>HACKED jars.path : ${toString:project.all.jars.path}</echo>
None of the above mentioned ways worked... :(
Does anybody have a solution? I am searching and trying now for hours, and I can't imagine that nobody ever had the same problem.
Thanks for any help!!!
Related
this is an android project and my ant build script sometimes fails when it decides to treat warnings as errors when running the javac program. Seriously, it only does this sometimes, which is a different question I may ask.
It will print errors and abruptly cancel the build
[javac] 1 error
[javac] 9 warnings
as I did deeper I see the "error" is
error: warnings found and -Werror specified
which is not anything I explicitly set. Now this may be an argument buried deep in the build.xml file, or maybe in this particular sub library's build.xml file in one particular condition I don't currently know about
It is the android facebook sdk that causes this, sometimes. But there is no Werror argument within the ant build.xml files but I want to disable it or work around it
This is for a build server, where I have other conditions to stop a build. Inconsistent ant and javac issues don't really have a place.
but any insight about it is appreciated.
The file "tools/ant/build.xml" under my Android SDK directory contains the following:
<property name="java.compilerargs" value="" />
Perhaps the Android SDK used by the build that fails due to warnings being treated like errors includes "-Werror" in the compiler args? (If not, a recursive grep for "compilerargs" in the directory of the offending Android SDK instance could find the culprit.)
UPDATE:
On the other hand, that's in my Android SDK, the property is not in itself mandatory -- it just happens to be used here:
<!-- Compiles this project's .java files into .class files. -->
<target name="-compile" depends="-pre-build, -build-setup, -code-gen, -pre-compile">
<do-only-if-manifest-hasCode elseText="hasCode = false. Skipping...">
<!-- merge the project's own classpath and the tested project's classpath -->
<path id="project.javac.classpath">
<path refid="project.all.jars.path" />
<path refid="tested.project.classpath" />
<path path="${java.compiler.classpath}" />
</path>
<javac encoding="${java.encoding}"
source="${java.source}" target="${java.target}"
debug="true" extdirs="" includeantruntime="false"
destdir="${out.classes.absolute.dir}"
bootclasspathref="project.target.class.path"
verbose="${verbose}"
classpathref="project.javac.classpath"
fork="${need.javac.fork}">
<src path="${source.absolute.dir}" />
<src path="${gen.absolute.dir}" />
<compilerarg line="${java.compilerargs}" />
</javac>
The element that has to be there is the "compilerarg" one on the next-to-last line, so a grep for "compilerarg" instead of "compilerargs" would be the better choice.
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>
Findbugs is a great software and my team uses it while working on our Android project. In Eclipse everything is nice and shiny, however now we're trying to automate our builds with ant and generate Findbugs results automatically for each build.
It's seems not that difficult. I followed this tutorial:
https://wiki.jenkins-ci.org/display/JENKINS/Building+an+Android+app+and+test+project#BuildinganAndroidappandtestproject-FindBugs
One minor issue is that I had to change ${android.jar} to ${project.target.android.jar}.
The worse part are The following classes needed for analysis were missing: warnings for classes that come form library projects we use. Some of them are our own and we would like to scan them with Findbugs, too. To make matters more complicated, one of these libs uses another lib (also our own and needs scanning), so it looks like this:
Project A --uses--> Library B --uses--> Library C
Here I thought that since Android SDK can handle all these dependencies (Library C is compiled when I issue ant debug for Project A), I can somehow make use of it, get the list of libs my project depends on and provide it to the findbugs task. Unfortunately I haven't managed to do that.
For now I settled with manually entering all libs, some into class property of findbugs task, some into auxClasspath, which has the upside that I can only analyse some of the libraries the project depends on. Still I hope that what I originally tried to do is possible. Can anybody show me how to extract a path-element
I've finally found that part of SDK build script that puts together a "classpath" of all the jars (classes.jar-s of all the libraries, including Library C from the "diagram" and jars from the libs/ folder of the project and all the libs).
My final custom-rules.xml with findbugs target looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<project name="Project_custom" default="findbugs">
<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"/>
<target name="findbugs">
<gettarget
androidJarFileOut="project.target.android.jar"
androidAidlFileOut="project.target.framework.aidl"
bootClassPathOut="project.target.class.path"
targetApiOut="project.target.apilevel"
minSdkVersionOut="project.minSdkVersion" />
<dependency
libraryFolderPathOut="project.library.folder.path"
libraryPackagesOut="project.library.packages"
libraryManifestFilePathOut="project.library.manifest.file.path"
libraryResFolderPathOut="project.library.res.folder.path"
libraryBinAidlFolderPathOut="project.library.bin.aidl.folder.path"
libraryNativeFolderPathOut="project.library.native.folder.path"
jarLibraryPathOut="project.all.jars.path"
targetApi="${project.target.apilevel}"
verbose="${verbose}" />
<findbugs home="${findbugs.home}" output="xml" outputFile="findbugs-results.xml">
<auxClasspath>
<pathelement location="${project.target.android.jar}" />
<path refid="project.all.jars.path" />
</auxClasspath>
<class location="${out.dir}" />
</findbugs>
</target>
</project>
Edit: I've upgraded the target to run indepent of the build targets, i.e. now you can run just ant findbugs not ant debug findbugs.
I added some extra definition and worked. Thank you
<target name="findbugs">
<mkdir dir="reports" />
<gettarget
androidJarFileOut="project.target.android.jar"
androidAidlFileOut="project.target.framework.aidl"
bootClassPathOut="project.target.class.path"
targetApiOut="project.target.apilevel"
minSdkVersionOut="project.minSdkVersion" />
<dependency
libraryFolderPathOut="project.library.folder.path"
libraryPackagesOut="project.library.packages"
libraryManifestFilePathOut="project.library.manifest.file.path"
libraryResFolderPathOut="project.library.res.folder.path"
libraryBinAidlFolderPathOut="project.library.bin.aidl.folder.path"
libraryNativeFolderPathOut="project.library.native.folder.path"
jarLibraryPathOut="project.all.jars.path"
libraryRFilePathOut="project.library.rfile.path"
buildToolsFolder="${sdk.dir}/build-tools"
renderscriptSupportLibsOut="project.rs.support.libs.path"
renderscriptSupportMode="${renderscript.support.mode}"
targetApi="${project.target.apilevel}"
verbose="${verbose}" />
<findbugs home="${findbugs.home}" output="xml:withMessages" outputFile="reports/findbugs.xml" excludeFilter="findbugs-exclude.xml" maxRank="9">
<!-- auxClasspath path="${project.target.android.jar}" / -->
<auxClasspath>
<pathelement location="${project.target.android.jar}" />
<path refid="project.all.jars.path" />
</auxClasspath>
<sourcePath path="${basedir}/src/" />
<class location="${basedir}/bin/classes/" />
</findbugs>
</target>
I'm working on an Open-source project. As it is intended that anyone can download the source and build it themselves, I do not want to hard-code the package name anywhere - including the directory structure.
I use ant for building. Apparently I can modify build.xml, but I believe this is overwritten by android update. Whatever is used will be committed to the Git repo, and it should not be too complicated.
Currently the process to build the code straight from the Git repo is fairly simple. Here's an excerpt from the README file:
$ cd ~/src/isokeys/IsoKeys
$ android list targets # I build against API level 10.
$ android update project --name IsoKeys --target 1 --path ./ # Only needed first time.
$ ant debug && adb -d install -r bin/IsoKeys-debug.apk
To me, it makes sense to put the package name in local.properties, because this is .gitignore'd. As the package name won't be anywhere else, the build will fail without doing this. So there needs to be at least 1 extra step in the README, but I want to keep it to a minimum.
Edit: Of course, another requirement is that diffs make sense - which they don't if you manually rename the package name.
I did something similar (but not for this reason) which required updating the manifest at build time. The way I accomplished this was by making a second AndroidManifest and putting it under a directory named config.
So in config/AndroidManifest you could have something like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="#CONFIG.APP_PACKAGE_NAME#"
android:versionCode="#CONFIG.APP_VERSION_CODE#"
android:versionName="#CONFIG.APP_VERSION#">
<!-- EVERYTHING ELSE GOES HERE -->
</manifest>
Then you can use the regular bare bones build.xml ant script with just a few modifications (no need to copy the whole script from the android build system as they added some hooks for you to use without reinventing the wheel).
The build script should be reading local.properties by default, but if not add (or uncomment) a line like this:
<property file="local.properties" />
In your build script you should see a task called "-pre-build", change it like this:
<target name="-pre-build">
<copy file="config/AndroidManifest.xml" todir="." overwrite="true" encoding="utf-8">
<filterset>
<filter token="CONFIG.APP_PACKAGE_NAME" value="${app.packagename}" />
<filter token="CONFIG.APP_VERSION" value="${app.version}" />
<filter token="CONFIG.APP_VERSION_CODE" value="${app.versioncode}" />
</filterset>
</copy>
</target>
Then your local.properties file you would put the package name, version name/code like so:
app.version=1.0
app.versioncode=1
app.packagename=com.mypackage.name
Now you just need to make sure in your manifest that you fully qualify all of your activities/services/broadcast listeners etc.. That means you always specify the full package of your source code. If you want the package for your own source code to be dynamic you could replace out each of the prefixes to each class.. But that seems kind of silly.. It is easy enough to package your code up under your own package name and they can use it from any project by simply including the source or a jar in their project.
-- UPDATE --
Oh and one other thing you can do to notify the user that they must define a package name is use the fail tag in your build xml like this:
<fail message="app.packagename is missing. This must be defined in your local.properties file" unless="app.packagename" />
Put this after the line which reads the local.properties file
With thanks to Matt Wolfe for his help, I'm posting a partial answer with my efforts so far.
I noticed that the default barebones build.xml would also import custom_rules.xml:
<import file="custom_rules.xml" optional="true" />
So I created this file and started tinkering. This is what I have come up with so far:
<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules" default="debug">
<target name="-pre-build">
<fail message="Please define app.packagename in your local.properties file." unless="app.packagename" />
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="/usr/share/java/ant-contrib.jar"/>
</classpath>
</taskdef>
<!-- How do I check for propertyregex?
<fail message="Depends on ant-contrib's propertyregex for app.packagename.path." unless="propertyregex" />
-->
<propertyregex property="app.packagename.path"
input="${app.packagename}/"
regexp="\."
replace="/"
global="true"
/>
<copy todir="build/" overwrite="true" encoding="utf-8">
<fileset dir="./">
<include name="AndroidManifest.xml" />
<include name="res/**" />
<include name="lib/**" />
</fileset>
<filterset>
<filter token="CONFIG.APP_PACKAGE_NAME" value="${app.packagename}" />
</filterset>
</copy>
<copy todir="build/src/${app.packagename.path}" overwrite="true" encoding="utf-8">
<fileset dir="./src/isokeys/">
<include name="**" />
</fileset>
<filterset>
<filter token="CONFIG.APP_PACKAGE_NAME" value="${app.packagename}" />
</filterset>
</copy>
</target>
<target name="-pre-clean" description="Removes output files created by -pre-build.">
<delete file="build/AndroidManifest.xml" verbose="${verbose}" />
<delete dir="build/res/" verbose="${verbose}" />
<delete dir="build/lib/" verbose="${verbose}" />
<delete dir="build/src/" verbose="${verbose}" />
</target>
<!-- NOW CHANGE DIRECTORY TO build/ BEFORE HANDING BACK OVER TO build.xml!!! -->
</project>
This sets everything up in build/ (which has the added bonus of keeping things neat and tidy), now the intention is for the sdk tools build.xml to run from this build/ directory. However, I can't find any way of cd'ing.
Easiest way might be replace the package name as late as possible. This way, you don't even have to touch your code. There is a nice article named Renaming the Android Manifest package(http://www.piwai.info/renaming-android-manifest-package/). Summary:
You can use aapt --rename-manifest-package to modify the package name
Alternatively, if you want package name replacement to be a part of the ant build process, you can override the -package-resources target:
copy the -package-resources target from SDK's build.xml
add manifestpackage parameter
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.