Skip to content

Commit

Permalink
Merge branch 'release/0.1.7'
Browse files Browse the repository at this point in the history
  • Loading branch information
xerial committed Feb 20, 2013
2 parents 916dc2f + e15556d commit 80b97ea
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 96 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -21,7 +21,7 @@ Add `sbt-pack` plugin:

**project/plugins.sbt**

addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.6")
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.7")


Import `xerial.sbt.Pack.packSettings` into your project settings. Then set `packMain` variable, a mapping from the your program names to their corresponding main classes. The main classes must be Scala objects that define `def main(args:Array[])` method:
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
@@ -1 +1 @@
sbt.version=0.12.2
sbt.version=0.12.0
158 changes: 86 additions & 72 deletions src/main/scala/xerial/sbt/Pack.scala
Expand Up @@ -20,14 +20,15 @@ import java.io.ByteArrayOutputStream
object Pack extends sbt.Plugin {

val pack: TaskKey[File] = TaskKey[File]("pack", "create a distributable package of the project")
val packMain : SettingKey[Map[String, String]] = SettingKey[Map[String, String]]("packMain", "prog_name -> main class table")
val packMain: SettingKey[Map[String, String]] = SettingKey[Map[String, String]]("packMain", "prog_name -> main class table")
val packDir: SettingKey[String] = SettingKey[String]("pack-dir")
val packExclude = SettingKey[Seq[String]]("pack-exclude")
val packAllClasspaths = TaskKey[Seq[Classpath]]("pack-all-classpaths")
val packLibJars = TaskKey[Seq[File]]("pack-lib-jars")
val packUpdateReports = TaskKey[Seq[sbt.UpdateReport]]("pack-dependent-modules")
val packMacIconFile = SettingKey[String]("pack-mac-icon-file", "icon file name for Mac")
val packResourceDir = SettingKey[String]("pack-resource-dir", "pack resource directory. default = src/pack")
val packAllUnmanagedJars = TaskKey[Seq[Classpath]]("pack-all-unmanaged")

val packSettings = Seq[sbt.Project.Setting[_]](
packDir := "pack",
Expand All @@ -36,6 +37,7 @@ object Pack extends sbt.Plugin {
packMacIconFile := "icon-mac.png",
packResourceDir := "src/pack",
packAllClasspaths <<= (thisProjectRef, buildStructure) flatMap getFromAllProjects(dependencyClasspath.task in Runtime),
packAllUnmanagedJars <<= (thisProjectRef, buildStructure, packExclude) flatMap getFromSelectedProjects(unmanagedJars.task in Compile),
packLibJars <<= (thisProjectRef, buildStructure, packExclude) flatMap getFromSelectedProjects(packageBin.task in Runtime),
packUpdateReports <<= (thisProjectRef, buildStructure, packExclude) flatMap getFromSelectedProjects(update.task)
) ++ Seq(packTask)
Expand All @@ -46,26 +48,25 @@ object Pack extends sbt.Plugin {

private def getFromSelectedProjects[T](targetTask: SettingKey[Task[T]])(currentProject: ProjectRef, structure: Load.BuildStructure, exclude: Seq[String]): Task[Seq[T]] = {
def allProjectRefs(currentProject: ProjectRef): Seq[ProjectRef] = {
def isExcluded(p:ProjectRef) = exclude.contains(p.project)
def isExcluded(p: ProjectRef) = exclude.contains(p.project)
val children = Project.getProject(currentProject, structure).toSeq.flatMap(_.aggregate)
(currentProject +: (children flatMap ( allProjectRefs(_) ))) filterNot(isExcluded)
(currentProject +: (children flatMap (allProjectRefs(_)))) filterNot (isExcluded)
}

val projects: Seq[ProjectRef] = allProjectRefs(currentProject)
projects.flatMap(p => targetTask in p get structure.data).join
}

private case class ModuleEntry(org:String, name:String, revision:String) {
private case class ModuleEntry(org: String, name: String, revision: String) {
override def toString = "%s:%s:%s".format(org, name, revision)
}

private implicit def moduleEntryOrdering = Ordering.by[ModuleEntry, (String, String, String)](m => (m.org, m.name, m.revision))

private def packTask = pack <<=
(name, packMain, packDir, version, packLibJars, streams, target, baseDirectory, packUpdateReports, packMacIconFile, packResourceDir) map {
(name, mainTable, packDir, ver, libs, out, target, base, reports, macIcon, resourceDir) => {
private def packTask = pack <<= (name, packMain, packDir, version, packLibJars, streams, target, baseDirectory, packUpdateReports, packMacIconFile, packResourceDir, packAllUnmanagedJars) map {
(name, mainTable, packDir, ver, libs, out, target, base, reports, macIcon, resourceDir, unmanaged) => {

val dependentJars = collection.immutable.SortedMap.empty[ModuleEntry, File] ++
(for{
val dependentJars = collection.immutable.SortedMap.empty[ModuleEntry, File] ++ (for {
r <- reports
c <- r.configurations if c.configuration == "runtime"
m <- c.modules
Expand All @@ -74,89 +75,102 @@ object Pack extends sbt.Plugin {
ModuleEntry(mid.organization, mid.name, mid.revision) -> file
})

def rpath(f:RichFile) = f.relativeTo(base) map { _.toString } getOrElse(f.toString)

val distDir = target / packDir
out.log.info("Creating a distributable package in " + rpath(distDir))
IO.delete(distDir)
distDir.mkdirs()

val libDir = distDir / "lib"
out.log.info("Copying libraries to " + rpath(libDir))
libDir.mkdirs()
out.log.info("project jars:\n" + libs.mkString("\n"))
libs.foreach(l => IO.copyFile(l, libDir / l.getName))
out.log.info("project dependencies:\n" + dependentJars.keys.mkString("\n"))
for((m, f) <- dependentJars) {
IO.copyFile(f, libDir / "%s-%s.jar".format(m.name, m.revision))
}
def rpath(f: RichFile) = f.relativeTo(base) map {
_.toString
} getOrElse (f.toString)

val distDir = target / packDir
out.log.info("Creating a distributable package in " + rpath(distDir))
IO.delete(distDir)
distDir.mkdirs()

val libDir = distDir / "lib"
out.log.info("Copying libraries to " + rpath(libDir))
libDir.mkdirs()
out.log.info("project jars:\n" + libs.mkString("\n"))
libs.foreach(l => IO.copyFile(l, libDir / l.getName))
out.log.info("project dependencies:\n" + dependentJars.keys.mkString("\n"))
for ((m, f) <- dependentJars) {
IO.copyFile(f, libDir / "%s-%s.jar".format(m.name, m.revision))
}
out.log.info("unmanaged dependencies:")
for(m <- unmanaged; um <- m; f = um.data) {
out.log.info(f.getPath)
IO.copyFile(f, libDir / f.getName)
}

val binDir = distDir / "bin"
out.log.info("Create a bin folder: " + rpath(binDir))
binDir.mkdirs()
val binDir = distDir / "bin"
out.log.info("Create a bin folder: " + rpath(binDir))
binDir.mkdirs()

def read(path:String) : String = Resource.open(this.getClass, path) { f =>
def read(path: String): String = Resource.open(this.getClass, path) {
f =>
val b = new ByteArrayOutputStream
val buf = new Array[Byte](8192)
var ret = 0
while({ ret = f.read(buf, 0, buf.length); ret != -1 }) {
while ({ret = f.read(buf, 0, buf.length); ret != -1}) {
b.write(buf, 0, ret)
}
new String(b.toByteArray)
}
def write(path:String, content:String) {
val p = distDir / path
out.log.info("Generating %s".format(rpath(p)))
IO.write(p, content)
}

// Create launch scripts
out.log.info("Generating launch scripts")
if(mainTable.isEmpty) {
out.log.warn("No mapping (progran name) -> MainClass is defined. Please set packMain variable (Map[String, String]) in your sbt project settings.")
}
}

val mains = for((name, mainClass) <- mainTable) yield {
out.log.info("main class for %s: %s".format(name, mainClass))
Map[Any, String]("PROG_NAME"->name, "MAIN_CLASS"->mainClass, "MAC_ICON_FILE" -> macIcon)
}

for(m <- mains) {
val launchScript = StringTemplate.eval(read("pack/script/launch.template"))(m)
val progName = m("PROG_NAME").replaceAll(" ", "") // remove white spaces
write("bin/%s".format(progName), launchScript)
}
def write(path: String, content: String) {
val p = distDir / path
out.log.info("Generating %s".format(rpath(p)))
IO.write(p, content)
}

// Create launch scripts
out.log.info("Generating launch scripts")
if (mainTable.isEmpty) {
out.log.warn("No mapping (progran name) -> MainClass is defined. Please set packMain variable (Map[String, String]) in your sbt project settings.")
}

// Create Makefile
val globalVar = Map[Any, String]("PROG_NAME" -> name)
val makefile = StringTemplate.eval(read("pack/script/Makefile.template"))(globalVar) +
(for(p <- mainTable.keys) yield
"\t" + """ln -sf "../$(PROG)/current/bin/%s" "$(PREFIX)/bin/%s"""".format(p, p)).mkString("\n") + "\n"
for ((name, mainClass) <- mainTable) {
out.log.info("main class for %s: %s".format(name, mainClass))
val m = Map("PROG_NAME" -> name, "MAIN_CLASS" -> mainClass, "MAC_ICON_FILE" -> macIcon)
val launchScript = StringTemplate.eval(read("pack/script/launch.template"))(m)
val progName = m("PROG_NAME").replaceAll(" ", "") // remove white spaces
write("bin/%s".format(progName), launchScript)
}

val otherResourceDir = base / resourceDir
val binScriptsDir = otherResourceDir / "bin"
val additionalLines : Array[String] = for(script <- Option(binScriptsDir.listFiles) getOrElse Array.empty) yield {
"\t" + """ln -sf "../$(PROG)/current/bin/%s" "$(PREFIX)/bin/%s"""".format(script.getName, script.getName)
// Create Makefile
val makefile = {
val globalVar = Map("PROG_NAME" -> name)
val b = new StringBuilder
b.append(StringTemplate.eval(read("pack/script/Makefile.template"))(globalVar))
for(p <- mainTable.keys) {
b.append("\t")
b.append("""ln -sf "../$(PROG)/current/bin/%s" "$(PREFIX)/bin/%s"""".format(p, p))
b.append("+n")
}
b.result
}

write("Makefile", makefile + additionalLines.mkString("\n") + "\n")

// Copy other scripts
IO.copyDirectory(otherResourceDir, distDir)
val otherResourceDir = base / resourceDir
val binScriptsDir = otherResourceDir / "bin"
val additionalLines: Array[String] = for (script <- Option(binScriptsDir.listFiles) getOrElse Array.empty) yield {
"\t" + """ln -sf "../$(PROG)/current/bin/%s" "$(PREFIX)/bin/%s"""".format(script.getName, script.getName)
}

// chmod +x the bin directory
if (!System.getProperty("os.name", "").contains("Windows")) {
scala.sys.process.Process("chmod -R +x %s".format(binDir)).run
}
write("Makefile", makefile + additionalLines.mkString("\n") + "\n")

// Output the version number
write("VERSION", "version:=" + ver + "\n")
out.log.info("done.")
// Copy other scripts
IO.copyDirectory(otherResourceDir, distDir)

distDir
// chmod +x the bin directory
if (!System.getProperty("os.name", "").contains("Windows")) {
scala.sys.process.Process("chmod -R +x %s".format(binDir)).run
}
}

// Output the version number
write("VERSION", "version:=" + ver + "\n")
out.log.info("done.")
distDir
}
}


}
Expand Down
26 changes: 7 additions & 19 deletions src/main/scala/xerial/sbt/StringTemplate.scala
Expand Up @@ -13,8 +13,7 @@ package xerial.sbt

object StringTemplate {

def eval(template: String)(properties: Map[Any, String]) = new StringTemplate(template).eval(properties)

def eval(template: String)(properties: Map[String, String]) = new StringTemplate(template).eval(properties)

}

Expand All @@ -24,19 +23,10 @@ object StringTemplate {
*/
class StringTemplate(template: String) {

def eval(property: Map[Any, String]): String = apply(property)
def eval(property: Map[String, String]): String = apply(property)

private def convert(properties: Map[Any, String]): Map[Symbol, String] = {
(for ((k, v) <- properties) yield {
k match {
case s: Symbol => s -> v.toString
case _ => Symbol(k.toString) -> v.toString
}
}).toMap
}

def apply(property: Map[Any, String]): String = {
val p = convert(property)
def apply(property: Map[String, String]): String = {
val p = property

val pattern = """\{\{([^\}]+)\}\}""".r
val out = new StringBuilder
Expand All @@ -51,12 +41,10 @@ class StringTemplate(template: String) {
out.append(line.substring(cursor, m.start))
}
val key = line.substring(m.start + 2, m.end-2)

val k = Symbol(key)
if (p.contains(k)) {
out.append(p(k))
if (p.contains(key)) {
out.append(p(key))
}
cursor = m.end;
cursor = m.end
}

if (cursor < line.length)
Expand Down
2 changes: 1 addition & 1 deletion src/sbt-test/sbt-pack/command-launcher/project/plugins.sbt
@@ -1,4 +1,4 @@
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.6")
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.7")



2 changes: 1 addition & 1 deletion src/sbt-test/sbt-pack/min-project/project/plugins.sbt
@@ -1,4 +1,4 @@
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.6")
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.7")



2 changes: 1 addition & 1 deletion version.sbt
@@ -1,2 +1,2 @@

version in ThisBuild := "0.1.6"
version in ThisBuild := "0.1.7"

0 comments on commit 80b97ea

Please sign in to comment.