Targeting Android with Scala 2.8 Trunk builds

Edit:

My new way of doing this is to use my Eclipse plugin: https://github.com/banshee/AndroidProguardScala (the readme has instructions for installing and a pointer to a normal Eclipse update site).

The old answer still works, but the new way is just better.

[The old way] My approach:

  • Use the normal Android/eclipse tools for creating a Java project.
  • Add a second project containing the Scala code. That way I get to keep the generated code for future reference (I'm new at both Android and Scala). This project can reference android.jar.
  • The scala project produces a jar file that's used in the java project
  • Use proguard to strip the library. I believe this avoids the need for the scala-android.jar that was used in 2.7

I haven't used this for anything more ambitious than hello, world though, so take it as more of a set of hints.

In the scala project, I add a builder (Builder > New) that's just a shell script called pguard in the root directory of the project containing:

#!/bin/sh
cd $1
PROGUARD=$HOME/dev/proguard/lib/proguard.jar
LIBS=
OUTPUT=lib/proguard.jar
rm -f $OUTPUT
AJAR=/Users/jamesmoore/dev/android-sdk-mac_86/platforms/android-7/android.jar
# java -jar $PROGUARD -injars 'bin:lib/scala-library.jar(!META-INF/MANIFEST.MF,!library.properties)' -outjar $OUTPUT -libraryjars  @proguard.txt
java -Xmx1g -jar $PROGUARD -injars 'bin:lib/scala-library.jar(!META-INF/MANIFEST.MF,!library.properties)' -outjar $OUTPUT -libraryjars $AJAR @proguard.txt

The builder has Location set to:

${build_project}/pguard

And both working directory and arguments set to

${build_project}

Also in the root of the scala project, there's a proguard arguments file @proguard.txt:

-dontwarn
-dontoptimize
-dontobfuscate
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keep public class com.banshee.** {
    public protected *;
}

You'll want to change the -keep arguments to keep your own code, of course.

In the java project, I add the jar file that's produced by the scala project (I use lib/proguard.jar in the script above).

Don't add the scala project as a required project in the java project's build path, though. That will add the scala class files in addition to the jar file and confuse dex (since it'll get both the .class files and the same things in the jar). As far as I can tell, Eclipse will build everything in the workspace, so when you hit the go button, both projects get built.


After much investigation, it really does look like Proguard is essential to keep the size and speed of deploying the application to reasonable levels.

Sadly, there is no suitable way to embed proguard as a build step. Using scripts might be a possibility, but I also need to support Windows, Linux and OSX environments.

I was also unsure about the twin-project solution, as it prevented Scala code from using the generated resources file R.java, which I wanted to be able to do.

In the end, I was able to make both SBT and Ant build an android 2.1 application using Scala 2.8. Ant was the favourite final solution as it works with the same file organisation as Android's eclipse plugin. I've written up the solution here: http://scala-ide.assembla.com/wiki/show/ae55a-oWSr36hpeJe5avMc/Developing_for_Android

Eclipse then launches Ant as an external tool to package and install the application.


I'm now using a modification of my previous answer to run on Windows: just move everything into @proguard_windows.txt so you don't have to worry about running as a script.

My new @proguard_windows.txt looks like:

-injars bin;lib/scala-library.jar(!META-INF/MANIFEST.MF,!library.properties)

-outjar gen/scandroid.jar

-libraryjars lib/android.jar

-dontwarn
-dontoptimize
-dontobfuscate
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers

-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keep public class com.banshee.** { public protected *; }
-keep public class org.xml.sax.EntityResolver { public protected *; }

And note that in windows, you need to use a semicolon for -injars. Nasty.

The builder looks like this:

(running cygwin here, so the cat option path takes a slash)
James@Greine:/cygdrive/c/Users/james/workspace/Scala2$ cat .externalToolBuilders/proguard.launch
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="C:\Windows\System32\java.exe"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="-Xmx1g -jar lib/proguard.jar @proguard_windows.txt"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${build_project}"/>
</launchConfiguration>

You'll want to put this in .externalToolBuilders/proguard.launch.

The interesting thing here is that it's just a java command, not any kind of shell script, so it's fairly easy to port between Windows/Mac (and I'm assuming Linux, but haven't done that yet), since you're just changing the location of the actual java binary.

(Submitting this as a new answer because it's a bit different than the one that got marked as the correct(ish) answer)