Permalink
Browse files

Merge branch 'master' into sbt_011_1

Conflicts:
	src/main/scala/AndroidBase.scala
	src/main/scala/AndroidInstall.scala
	src/main/scala/PlainJavaProject.scala
  • Loading branch information...
2 parents daa71e0 + 2146f40 commit c254f1d76c5535d793e004d4c39f02cc8377bfe2 @jberkel committed Oct 20, 2011
View
@@ -11,19 +11,26 @@ the [sbt_011][] branch.
Using a [giter8][] template is the easiest way to create a new
project that uses the plugin. If you don't have giter8 installed:
-
$ curl https://raw.github.com/n8han/conscript/master/setup.sh | sh
$ ~/bin/cs n8han/giter8
Now create a new project with one of the Android templates:
- $ ~/bin/g8 jberkel/android-app
+ $ ~/bin/g8 jberkel/android-app # sbt 0.10.x
+ $ ~/bin/g8 jberkel/android-app -b sbt-0_11 # sbt 0.11.x
This will prompt you to customize a few values (press enter to accept
defaults), then create the project structure and all needed files plus
skeleton tests, specs and activities.
-To build the package:
+Since this plugin is currently not released you'll have to first build and
+install it locally by performing the following as described in the "Hacking on the plugin" section.
+
+ $ git clone git://github.com/jberkel/android-plugin.git
+ $ cd android-plugin
+ $ sbt publish-local
+
+Then, to build the Android package:
$ cd <your app name>
$ export ANDROID_HOME=/path/to/sdk # or ANDROID_SDK_{HOME,ROOT}
@@ -98,6 +105,34 @@ is issued by the plugin when the same ID is used for different types
of a resources; the type of resources retrieved by that ID will be
unpredictable.
+## ProGuard optimizations
+
+The plugin allows to run ProGuard optimizations on your classes. This
+might improve the performance of your application dramatically, but also
+break it easily. By default, the application is only shrinked by ProGuard,
+but not optimized.
+
+If you want to apply optimizations, you specify ProGuard
+optimization options in your project like this:
+
+```scala
+ lazy val someOptimizedProject = Project(
+ id = ...,
+ ...
+ settings = ... ++ inConfig(Android)(
+ proguardOptimizations := Seq(
+ "-optimizationpasses 8",
+ "-dontpreverify",
+ "-allowaccessmodification",
+ "-optimizations !code/simplification/arithmetic"
+ )
+ )
+ )
+```
+
+Please note that you will receive even more warnings from ProGuard
+and dex when you apply optimizations.
+
## Getting screenshots
In the sbt console run:
@@ -151,7 +186,7 @@ Place your Android NDK sources in `src\main\jni`. Add the AndroidNdk.settings to
)
```
-##Hacking on the plugin
+## Hacking on the plugin
If you need make modifications to the plugin itself, you can compile
and install it locally (you need at least sbt 0.10.x to build it):
View
@@ -15,7 +15,7 @@ credentials += Credentials(Path.userHome / ".ivy2" / ".credentials")
libraryDependencies ++= Seq(
"com.google.android.tools" % "ddmlib" % "r10",
- "net.sf.proguard" % "proguard" % "4.4"
+ "net.sf.proguard" % "proguard-base" % "4.6"
)
sbtPlugin := true
@@ -18,8 +18,11 @@
* Github API integration: upload apks to github/S3 ([Jan Berkel][jberkel])
+* Upgrade to proguard 4.6 w/ optional optimization ([Martin Kneissl][mkneissl])
+
[nuriaion]: https://github.com/Nuriaion
[paulbutcher]: https://github.com/paulbutcher/
[jberkel]: https://github.com/jberkel
[geeksville]: https://github.com/geeksville
[philcali]: https://github.com/philcali
+[mkneissl]: https://github.com/mkneissl/
@@ -11,14 +11,13 @@ object AndroidBase {
private def aaptGenerateTask =
(manifestPackage, aaptPath, manifestPath, mainResPath, jarPath, managedJavaPath) map {
(mPackage, aPath, mPath, resPath, jPath, javaPath) =>
- Process (<x>
- {aPath.absolutePath} package --auto-add-overlay -m
- --custom-package {mPackage}
- -M {mPath.head.absolutePath}
- -S {resPath.absolutePath}
- -I {jPath.absolutePath}
- -J {javaPath.absolutePath}
- </x>) !
+
+ Seq(aPath.absolutePath, "package", "--auto-add-overlay", "-m",
+ "--custom-package", mPackage,
+ "-M", mPath.head.absolutePath,
+ "-S", resPath.absolutePath,
+ "-I", jPath.absolutePath,
+ "-J", javaPath.absolutePath) !
javaPath ** "R.java" get
}
@@ -76,6 +75,7 @@ object AndroidBase {
resourcesApkPath <<= (target, resourcesApkName) (_ / _),
packageApkPath <<= (target, packageApkName) (_ / _),
useProguard := true,
+ proguardOptimizations := Seq.empty,
apiLevel <<= (minSdkVersion, platformName) map { (min, pName) =>
min.getOrElse(platformName2ApiLevel(pName))
@@ -86,10 +86,9 @@ object AndroidBase {
proguardOption := "",
proguardExclude <<=
- (libraryJarPath, classDirectory, resourceDirectory, unmanagedClasspath in Compile) map {
- (libPath, classDirectory, resourceDirectory, unmanagedClasspath) =>
- val temp = libPath +++ classDirectory +++ resourceDirectory
- unmanagedClasspath.foldLeft(temp)(_ +++ _.data) get
+ (libraryJarPath, classDirectory, resourceDirectory) map {
+ (libPath, classDirectory, resourceDirectory) =>
+ (libPath +++ classDirectory +++ resourceDirectory) get
},
proguardInJars <<= (fullClasspath, proguardExclude) map {
(runClasspath, proguardExclude) =>
@@ -20,37 +20,38 @@ object AndroidInstall {
private def aaptPackageTask: Project.Initialize[Task[File]] =
(aaptPath, manifestPath, mainResPath, mainAssetsPath, jarPath, resourcesApkPath, streams) map {
(apPath, manPath, rPath, assetPath, jPath, resApkPath, s) =>
-
- val aapt = Process(<x>
- {apPath} package --auto-add-overlay -f
- -M {manPath}
- -S {rPath}
- -A {assetPath}
- -I {jPath}
- -F {resApkPath}
- </x>)
- s.log.debug("packaging: "+aapt)
- aapt.!
+ val aapt = Seq(apPath.absolutePath, "package", "--auto-add-overlay", "-f",
+ "-M", manPath.head.absolutePath,
+ "-S", rPath.absolutePath,
+ "-A", assetPath.absolutePath,
+ "-I", jPath.absolutePath,
+ "-F", resApkPath.absolutePath)
+ s.log.debug("packaging: "+aapt.mkString(" "))
+ aapt.run(false)
resApkPath
}
private def dxTask: Project.Initialize[Task[File]] =
(scalaInstance, dxJavaOpts, dxPath, classDirectory,
- proguardInJars, proguard, classesDexPath, streams) map {
+ proguardInJars, proguard, proguardOptimizations, classesDexPath, streams) map {
(scalaInstance, dxJavaOpts, dxPath, classDirectory,
- proguardInJars, proguard, classesDexPath, streams) =>
+ proguardInJars, proguard, proguardOptimizations, classesDexPath, streams) =>
- val inputs = proguard match {
- case Some(file) => file get
- case None => classDirectory +++ proguardInJars --- scalaInstance.libraryJar get
+ val inputs:PathFinder = proguard match {
+ case Some(file) => file
+ case None => classDirectory +++ proguardInJars --- scalaInstance.libraryJar
}
val uptodate = classesDexPath.exists &&
- !inputs.exists (_.lastModified > classesDexPath.lastModified)
+ !(inputs +++ (classDirectory ** "*.class") get).exists (_.lastModified > classesDexPath.lastModified)
if (!uptodate) {
- val dxCmd = String.format("%s %s --dex --output=%s %s",
- dxPath, dxMemoryParameter(dxJavaOpts), classesDexPath, inputs.mkString(" "))
- streams.log.debug(dxCmd)
+ val noLocals = if (proguardOptimizations.isEmpty) "" else "--no-locals"
+ val dxCmd = (Seq(dxPath.absolutePath,
+ dxMemoryParameter(dxJavaOpts),
+ "--dex", noLocals,
+ "--output="+classesDexPath.absolutePath) ++
+ inputs.get.map(_.absolutePath)).filter(_.length > 0)
+ streams.log.debug(dxCmd.mkString(" "))
streams.log.info("Dexing "+classesDexPath)
streams.log.debug(dxCmd !!)
} else streams.log.debug("dex file uptodate, skipping")
@@ -59,22 +60,26 @@ object AndroidInstall {
}
private def proguardTask: Project.Initialize[Task[Option[File]]] =
- (useProguard, classDirectory, proguardInJars, streams,
+ (useProguard, proguardOptimizations, classDirectory, proguardInJars, streams,
classesMinJarPath, libraryJarPath, manifestPackage, proguardOption) map {
- (useProguard, classDirectory, proguardInJars, streams,
+ (useProguard, proguardOptimizations, classDirectory, proguardInJars, streams,
classesMinJarPath, libraryJarPath, manifestPackage, proguardOption) =>
useProguard match {
case true =>
+ val optimizationOptions = if (proguardOptimizations.isEmpty) Seq("-dontoptimize") else proguardOptimizations
val manifestr = List("!META-INF/MANIFEST.MF", "R.class", "R$*.class",
"TR.class", "TR$.class", "library.properties")
val sep = JFile.pathSeparator
- val args =
- "-injars" :: classDirectory.absolutePath + sep +
- (if (!proguardInJars.isEmpty)
- proguardInJars.map(_+manifestr.mkString("(", ",!**/", ")")).mkString(sep) else "") ::
- "-outjars" :: classesMinJarPath.absolutePath ::
- "-libraryjars" :: libraryJarPath.mkString(sep) ::
- "-dontwarn" :: "-dontoptimize" :: "-dontobfuscate" ::
+ val inJars = ("\"" + classDirectory.absolutePath + "\"") +:
+ proguardInJars.map("\""+_+"\""+manifestr.mkString("(", ",!**/", ")"))
+
+ val args = (
+ "-injars" :: inJars.mkString(sep) ::
+ "-outjars" :: "\""+classesMinJarPath.absolutePath+"\"" ::
+ "-libraryjars" :: libraryJarPath.map("\""+_+"\"").mkString(sep) ::
+ Nil) ++
+ optimizationOptions ++ (
+ "-dontwarn" :: "-dontobfuscate" ::
"-dontnote scala.Enumeration" ::
"-dontnote org.xml.sax.EntityResolver" ::
"-keep public class * extends android.app.Activity" ::
@@ -86,7 +91,7 @@ object AndroidInstall {
"-keep public class * extends android.app.Application" ::
"-keep public class "+manifestPackage+".** { public protected *; }" ::
"-keep public class * implements junit.framework.Test { public void test*(); }" ::
- proguardOption :: Nil
+ proguardOption :: Nil )
val config = new ProGuardConfiguration
new ConfigurationParser(args.toArray[String]).parse(config)
streams.log.debug("executing proguard: "+args.mkString("\n"))
@@ -16,6 +16,7 @@ object AndroidKeys {
/** Proguard Settings */
val proguardOption = SettingKey[String]("proguard-option")
+ val proguardOptimizations = SettingKey[Seq[String]]("proguard-optimizations")
val libraryJarPath = SettingKey[Seq[File]]("library-path")
/** Default Settings */
@@ -8,6 +8,7 @@ object PlainJavaProject {
useProguard := false,
autoScalaLibrary in GlobalScope := false,
manifestPath <<= (baseDirectory, manifestName) map((s,m) => Seq(s / m)),
+ proguardOptimizations := Seq.empty,
mainResPath <<= (baseDirectory, resDirectoryName) (_ / _),
javaSource in Compile <<= (baseDirectory) (_ / "src")
)
@@ -20,6 +20,8 @@ object TypedResources {
}
}
+ val reserved = List("extends", "trait", "type", "val", "var", "with")
+
val resources = layoutResources.get.flatMap { path =>
XML.loadFile(path).descendant_or_self flatMap { node =>
// all nodes
@@ -45,30 +47,37 @@ object TypedResources {
if (v0 != v) s.log.warn("Resource id '%s' mapped to %s and %s" format (k, v0, v))
}
m + (k -> v)
+ }.filterNot {
+ case (id, _) => reserved.contains(id)
}
IO.write(typedResource,
""" |package %s
- |import android.app.Activity
- |import android.view.View
+ |import _root_.android.app.{Activity, Dialog}
+ |import _root_.android.view.View
|
|case class TypedResource[T](id: Int)
|object TR {
|%s
|}
|trait TypedViewHolder {
- | def view: View
- | def findView[T](tr: TypedResource[T]) = view.findViewById(tr.id).asInstanceOf[T]
- |}
- |trait TypedView extends View with TypedViewHolder { def view = this }
- |trait TypedActivityHolder {
- | def activity: Activity
- | def findView[T](tr: TypedResource[T]) = activity.findViewById(tr.id).asInstanceOf[T]
+ | def findViewById( id: Int ): View
+ | def findView[T](tr: TypedResource[T]) = findViewById(tr.id).asInstanceOf[T]
|}
- |trait TypedActivity extends Activity with TypedActivityHolder { def activity = this }
+ |trait TypedView extends View with TypedViewHolder
+ |trait TypedActivityHolder extends TypedViewHolder
+ |trait TypedActivity extends Activity with TypedActivityHolder
+ |trait TypedDialog extends Dialog with TypedViewHolder
|object TypedResource {
- | implicit def view2typed(v: View) = new TypedViewHolder { def view = v }
- | implicit def activity2typed(act: Activity) = new TypedActivityHolder { def activity = act }
+ | implicit def view2typed(v: View) = new TypedViewHolder {
+ | def findViewById( id: Int ) = v.findViewById( id )
+ | }
+ | implicit def activity2typed(a: Activity) = new TypedViewHolder {
+ | def findViewById( id: Int ) = a.findViewById( id )
+ | }
+ | implicit def dialog2typed(d: Dialog) = new TypedViewHolder {
+ | def findViewById( id: Int ) = d.findViewById( id )
+ | }
|}
|""".stripMargin.format(
manifestPackage, resources map { case (id, classname) =>

0 comments on commit c254f1d

Please sign in to comment.