15

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.

James Haigh
  • 1,192
  • 1
  • 12
  • 25
  • This seems rather silly. I haven't heard of any open-source project jumping through hoops like this. – Matt Ball May 13 '12 at 06:26
  • 1
    The 2 main motives are that diffs for a renamed directory are a pain, and that this and many other Open-source projects don't have their own domain but rather are hosted on a code hosting site like sourceforge.net, etc., and forks/merges are commonplace. – James Haigh May 13 '12 at 06:45

3 Answers3

20

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

Matt Wolfe
  • 8,924
  • 8
  • 60
  • 77
  • Ah, my build script (generated by android update), _is_ 'barebones' and contains ``. So I can commit this file to the repo with some modifications. Right, let me see... – James Haigh May 13 '12 at 07:11
  • Yeah some people say its a bad idea but I've been doing it for well over a year without any issues. They obviously leave those hooks for you but there isn't any guarantee that in the next android version they could change things. IIRC the other thing I had to do for it to play nice with Eclipse was add: However for the changes above you shouldn't need this. I only needed it so that I could reuse some of the targets in the tools/ant/build.xml from my script, and to do that I would then just had to use androidbuild.[targetname] – Matt Wolfe May 13 '12 at 07:25
  • If i didn't have the "as=androidbuild" section then eclipse would complain that the targets didn't exist and eclipse would fail to build my project even though I wasn't using my ant script from eclipse in the first place. – Matt Wolfe May 13 '12 at 07:26
  • Which step uses `src/`? Aapt? Can I directly rename in that step or do I need to use `-pre-build` as an intermediate step to copy from say `src/name/*` to `tmp/pac/kage/name/*`? – James Haigh May 13 '12 at 08:08
  • And how do I make `ant` convert dots to slashes to generate the path? – James Haigh May 13 '12 at 08:17
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/11193/discussion-between-james-haigh-and-matt-wolfe) – James Haigh May 13 '12 at 12:55
  • I could do with something earlier than `-pre-build`, and `-pre-build` doesn't always get run anyway. Other than that, I'm really _really_ close. – James Haigh May 13 '12 at 20:03
  • `-prebuild` target has two minor flaws: the first line should not close `target` element, i.e., it should omit the slash character. Second, `filter` element is missing starting quote on value `${app.packagename}`. Please fix them so the xml is ready for copy paste. – Juuso Ohtonen Apr 08 '14 at 05:50
7

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.

James Haigh
  • 1,192
  • 1
  • 12
  • 25
  • So far there _is_ only one extra step for the `README` which is good news: `$ echo 'app.packagename=your.package.name' >> local.properties` – James Haigh May 13 '12 at 18:21
  • Would recommend adding this at the end as I was getting an error int the build xml. This because Android's if task requires element, while ant-contrib's if task does not support it. Because we can not change Android's build files and must comply with them, we are forced to use Android's if task in all places – Jack May 14 '13 at 10:38
  • how to change directory to build/ ? – Jason Aug 27 '13 at 02:01
1

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
Juuso Ohtonen
  • 8,826
  • 9
  • 65
  • 98