Skip to content

Instantly share code, notes, and snippets.

@takedasoft
Created July 22, 2010 17:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takedasoft/486306 to your computer and use it in GitHub Desktop.
Save takedasoft/486306 to your computer and use it in GitHub Desktop.
/**
sbt-android-plugin/script/create_project
をobjectに定義したもの。
> scalac create_project.scala
> scala -cp . CreateProject MyProject ....
@see http://github.com/jberkel/android-plugin
*/
object CreateProject {
def main( args:Array[String] ){
val e = args.elements
var apiLevel = "7"
var platform = "android-" + apiLevel
var activity = "MainActivity"
var scalaVersion = """\d\.\d\.\d""".r.findFirstIn(scala.util.Properties.versionString).getOrElse("2.7.7")
def usage {
println("create_project <name> <pkg> [--platform <name>] " +
"[--scala-version <version>] [--api-level <level>] [--activity <name>]")
exit(1)
}
def fail(s: String) = throw new RuntimeException(s)
def take(i: Iterator[String]) = if (e.hasNext) e.next else null
val (project, pkg) = (take(e), take(e))
while (e.hasNext) e.next match {
case "--activity" => activity = take(e)
case "--api-level" => apiLevel = take(e); platform = "android-" + apiLevel
case "--platform" => platform = take(e)
case "--scala-version" => scalaVersion = take(e)
case _ => usage
}
if (pkg == null || project == null)
usage
// android needs at least 2 package components for some weird reason
if (!pkg.matches("""[a-z]+\.([a-z]+\.?)+[a-z]$"""))
fail("invalid package name, need 2 package components (e.g. com.bar)")
if (!project.matches("""[a-zA-Z]\w+"""))
fail("invalid project name: " + project)
case class Config(
val project:String, val target: String, val platform:String,
val sdkVersion:String, val `package`:String, val scalaVersion:String, val activity:String)
val config = Config(project, project, platform, apiLevel, pkg, scalaVersion, activity)
val manifestXml = <manifest xmlns:android="http://schemas.android.com/apk/res/android" package={config.`package`}
android:versionCode="1"
android:versionName="0.1">
<uses-sdk android:minSdkVersion={config.sdkVersion}/>
<application android:label="@string/app_name" android:icon="@drawable/app_icon">
<activity android:name={"."+config.activity} android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
val testManifestXml = <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package={config.`package` + ".tests"}>
<uses-sdk android:minSdkVersion={config.sdkVersion}/>
<application>
<uses-library android:name="android.test.runner"/>
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage={config.`package`}
android:label="Tests"/>
</manifest>
val buildProperties = """
|project.name=#{project}
|sbt.version=0.7.4
|project.version=0.1
|def.scala.version=2.7.7
|build.scala.versions=#{scalaVersion}
|project.initialize=false
|""".stripMargin.trim
val pluginDef = """
|import sbt._
|
|class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
| val android = "org.scala-tools.sbt" % "sbt-android-plugin" % "0.5.0"
|}""".stripMargin.trim
val projectDef = """
|import sbt._
|
|trait Defaults {
| def androidPlatformName = "#{platform}"
|}
|class #{project_class}(info: ProjectInfo) extends ParentProject(info) {
| override def shouldCheckOutputDirectories = false
| override def updateAction = task { None }
|
| lazy val main = project(".", "#{project}", new MainProject(_))
| lazy val tests = project("tests", "tests", new TestProject(_), main)
|
| class MainProject(info: ProjectInfo) extends AndroidProject(info) with Defaults with MarketPublish {
| val keyalias = "change-me"
| val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test"
| }
|
| class TestProject(info: ProjectInfo) extends AndroidTestProject(info) with Defaults
|}""".stripMargin.trim
val specDef = """
|import #{package}
|import org.scalatest.matchers.ShouldMatchers
|import org.scalatest.Spec
|
|class Specs extends Spec with ShouldMatchers {
| describe("a spec") {
| it("should do something") {
| }
| }
|}""".stripMargin.trim
val activityDef = """
|package #{package}
|
|import _root_.android.app.Activity
|import _root_.android.os.Bundle
|import _root_.android.widget.TextView
|
|class #{activity} extends Activity {
| override def onCreate(savedInstanceState: Bundle) {
| super.onCreate(savedInstanceState)
| setContentView(new TextView(this) {
| setText("hello, world")
| })
| }
|}""".stripMargin.trim
val testDef = """
|package #{package}.tests
|
|import junit.framework.Assert._
|import _root_.android.test.AndroidTestCase
|
|class UnitTests extends AndroidTestCase {
| def testPackageIsCorrect {
| assertEquals("#{package}", getContext.getPackageName)
| }
|}""".stripMargin.trim
class Generator(val config:Config) {
import java.io.File
import scala.xml.Node
val Resources = List("drawable", "layout", "values", "xml")
val Manifest = new File(config.target, "src/main/AndroidManifest.xml")
val TestManifest = new File(config.target, "tests/src/main/AndroidManifest.xml")
val Strings = new File(config.target, "src/main/res/values/strings.xml")
val Project = new File(config.target, "project/build/" + config.project.capitalize + ".scala")
val Plugins = new File(config.target, "project/plugins/Plugins.scala")
val BuildProperties = new File(config.target, "project/build.properties")
val Spec = new File(config.target, "src/test/scala/Specs.scala")
val Test = new File(config.target, "tests/src/main/scala/UnitTests.scala")
val Activity = new File(config.target, "src/main/scala/Activity.scala")
val AppIcon = new File(config.target, "src/main/res/drawable/app_icon.png")
val dirs2create = List(
"src" :: "main" :: "scala" :: Nil,
"src" :: "main" :: "java" :: Nil,
"src" :: "main" :: "assets" :: Nil,
"src" :: "test" :: "scala" :: Nil,
"project" :: "plugins" :: Nil,
"project" :: "build" :: Nil
) ++ Resources.map("src" :: "main" :: "res" :: _ :: Nil)
val testdirs2create = List(
"tests" :: "src" :: "main" :: "scala" :: Nil,
"tests" :: "src" :: "main" :: "res" :: Nil,
"tests" :: "src" :: "main" :: "assets" :: Nil
)
def resources(m: Map[String,String]) = {
val strings = for ((name,value) <- m) yield { <string name={name}>{value}</string> }
<resources>{strings}</resources>
}
def fail(s: String) = throw new RuntimeException(s)
def pretty(n: Node) = new scala.xml.PrettyPrinter(100, 4).format(n)
implicit def string2file(s: Seq[String]):File = s.foldLeft[File](new File(".")) { (f,p) => new File(f, p) }
def mkdir_p(f: File) = if (!f.mkdirs) fail("could not create directory")
def writeFile(f: File, s: String) {
val w = new java.io.FileWriter(f)
w.write(s)
w.close
}
def expand(template: String): String = doExpand(template, v => v match {
case "project_class" => config.project.capitalize
case "project" => config.project
case "package" => config.`package`
case "activity" => config.activity.capitalize
case "platform" => config.platform
case "scalaVersion" => config.scalaVersion
case _ => fail("unknown substitution: " + v)
})
def doExpand(template: String, map : (String) => String ): String = {
val m = java.util.regex.Pattern.compile("#\\{(.+?)\\}").matcher(template)
val sb = new StringBuffer
while (m.find()) m.appendReplacement(sb, map(m.group(1)))
m.appendTail(sb)
sb.toString
}
def createDirs = {
if (new File(config.target).exists)
fail("target directory " + config.target + " already exists")
(dirs2create ++ testdirs2create).foreach { d=> mkdir_p(config.target :: d) }
}
def createIcon:Unit = createIcon(AppIcon, 48, 48)
def createIcon(file: File, width: Int, height:Int) {
val bi = new java.awt.image.BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_ARGB)
val g = bi.createGraphics()
val gradient = new java.awt.GradientPaint(0, 0, java.awt.Color.red, width, height, java.awt.Color.blue)
g.setPaint(gradient)
g.fill(new java.awt.geom.RoundRectangle2D.Double(0, 0, width, height, 10, 10))
//g.setPaint(java.awt.Color.black)
//g.drawString(config.project, 10, height / 2)
javax.imageio.ImageIO.write(bi, "PNG", file)
}
def createManifests(manifestXml: Node, testManifestXml:Node) {
writeFile(Manifest, pretty(manifestXml))
writeFile(TestManifest, pretty(testManifestXml))
}
def createProject(pluginDef:String, projectDef:String, buildProperties:String) {
writeFile(Plugins, pluginDef)
writeFile(Project, expand(projectDef))
writeFile(BuildProperties, expand(buildProperties))
}
def createActivity(activityDef: String) = writeFile(Activity, expand(activityDef))
def createResources = writeFile(Strings, pretty(resources(Map("app_name"->config.project.capitalize))))
def createSpecs(specDef: String) = writeFile(Spec, expand(specDef))
def createTests(testDef: String) = writeFile(Test, expand(testDef))
}
val gen = new Generator(config)
gen.createDirs
gen.createManifests(manifestXml, testManifestXml)
gen.createProject(pluginDef, projectDef, buildProperties)
gen.createActivity(activityDef)
gen.createIcon
gen.createResources
gen.createSpecs(specDef)
gen.createTests(testDef)
println("generated project in directory '"+config.target+"'")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment