Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a layer between universal + specific native packages #14

Merged
merged 12 commits into from
Jul 1, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 35 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Add the following to your `project/plugins.sbt` or `~/.sbt/plugins.sbt` file:

resolvers += Resolver.url("scalasbt", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns)

addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.5.4")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.6.0-symlink-3")

Then, in the project you wish to use the plugin, add the following settings:

Expand All @@ -25,51 +25,26 @@ or
Using the sbt-native-packger plugin requires a bit of understanding of the underlying packaging mechanisms for each operating system it supports. The [generated documentation](http://scala-sbt.org/sbt-native-packager) for the plugin is still a work in progress.


Here's an example excerpt for the debian + rpm package of [sbt-extras](http://github.com/paulp/sbt-extras) project:
Here's an example excerpt for the native packaging of [sbt-launcher-packge](http://github.com/sbt/sbt-launcher-package) project:

// GENERAL LINUX PACKAGING STUFFS
maintainer := "Josh Suereth <[email protected]>",
packageSummary := "Simple Build Tool for Scala-driven builds",
packageDescription := """This script provides a native way to run the Simple Build Tool,
a build tool for Scala software, also called SBT.""",
linuxPackageMappings <+= (baseDirectory) map { bd =>
(packageMapping((bd / "sbt") -> "/usr/bin/sbt")
withUser "root" withGroup "root" withPerms "0755")
a build tool for Scala software, also called SBT.""",
// Here we remove the jar file and launch lib from the symlinks:
linuxPackageSymlinks <<= linuxPackageSymlinks map { links =>
for {
link <- links
if !(link.destination endsWith "sbt-launch-lib.bash")
if !(link.destination endsWith "sbt-launch.jar")
} yield link
},
linuxPackageMappings <+= (sourceDirectory) map { bd =>
(packageMapping(
(bd / "linux" / "usr/share/man/man1/sbt.1") -> "/usr/share/man/man1/sbt.1.gz"
) withPerms "0644" gzipped) asDocs()
},
linuxPackageMappings <+= (sourceDirectory in Linux) map { bd =>
packageMapping(
(bd / "usr/share/doc/sbt/copyright") -> "/usr/share/doc/sbt/copyright"
) withPerms "0644" asDocs()
},
linuxPackageMappings <+= (sourceDirectory in Linux) map { bd =>
packageMapping(
(bd / "usr/share/doc/sbt") -> "/usr/share/doc/sbt"
) asDocs()
},
linuxPackageMappings <+= (sourceDirectory in Linux) map { bd =>
packageMapping(
(bd / "etc/sbt") -> "/etc/sbt"
) withConfig()
},
linuxPackageMappings <+= (sourceDirectory in Linux) map { bd =>
packageMapping(
(bd / "etc/sbt/sbtopts") -> "/etc/sbt/sbtopts"
) withPerms "0644" withConfig("noreplace")
},
linuxPackageMappings <+= (sbtLaunchJar, sourceDirectory in Linux, sbtVersion) map { (jar, dir, v) =>
packageMapping(dir -> "/usr/lib/sbt",
dir -> ("/usr/lib/sbt/" + v),
jar -> ("/usr/lib/sbt/"+v+"/sbt-launch.jar")) withPerms "0755"
},
// DEBIAN SPECIFIC
name in Debian := "sbt",
// DEBIAN SPECIFIC
name in Debian <<= (sbtVersion) apply { (sv) => "sbt" /* + "-" + (sv split "[^\\d]" take 3 mkString ".")*/ },
version in Debian <<= (version, sbtVersion) apply { (v, sv) =>
sv + "-build-" + (v split "\\." map (_.toInt) dropWhile (_ == 0) map ("%02d" format _) mkString "")
val nums = (v split "[^\\d]")
"%s-%s-build-%03d" format (sv, (nums.init mkString "."), nums.last.toInt + 1)
},
debianPackageDependencies in Debian ++= Seq("curl", "java2-runtime", "bash (>= 2.05a-11)"),
debianPackageRecommends in Debian += "git",
Expand All @@ -81,11 +56,30 @@ Here's an example excerpt for the debian + rpm package of [sbt-extras](http://gi

// RPM SPECIFIC
name in Rpm := "sbt",
version in Rpm <<= sbtVersion.identity,
version in Rpm <<= sbtVersion apply { sv => (sv split "[^\\d]" filterNot (_.isEmpty) mkString ".") },
rpmRelease := "1",
rpmVendor := "typesafe",
rpmUrl := Some("http://github.com/paulp/sbt-extras"),
rpmLicense := Some("BSD"),



// WINDOWS SPECIFIC
name in Windows := "sbt",
version in Windows <<= (sbtVersion) apply { sv =>
(sv split "[^\\d]" filterNot (_.isEmpty)) match {
case Array(major,minor,bugfix, _*) => Seq(major,minor,bugfix, "1") mkString "."
case Array(major,minor) => Seq(major,minor,"0","1") mkString "."
case Array(major) => Seq(major,"0","0","1") mkString "."
}
},
maintainer in Windows := "Typesafe, Inc.",
packageSummary in Windows := "Simple Build Tool",
packageDescription in Windows := "THE reactive build tool.",
wixProductId := "ce07be71-510d-414a-92d4-dff47631848a",
wixProductUpgradeId := "4552fb0e-e257-4dbd-9ecb-dba9dbacf424",
javacOptions := Seq("-source", "1.5", "-target", "1.5"),

// Universal ZIP download install.
name in Universal := "sbt"

The full build, including windows MSI generation, can be found [here](https://github.com/sbt/sbt-launcher-package/blob/full-packaging/project/packaging.scala).
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name := "sbt-native-packager"

organization := "com.typesafe.sbt"

version := "0.5.5"
version := "0.6.0"

scalacOptions in Compile += "-deprecation"

Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.12.1
sbt.version=0.12.4
4 changes: 3 additions & 1 deletion src/main/scala/com/typesafe/sbt/PackagerPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ object SbtNativePackager extends Plugin
with debian.DebianPlugin
with rpm.RpmPlugin
with windows.WindowsPlugin
with universal.UniversalPlugin {
with universal.UniversalPlugin
with GenericPackageSettings {

def packagerSettings = linuxSettings ++
debianSettings ++
Expand All @@ -38,4 +39,5 @@ object SbtNativePackager extends Plugin
}

// TODO - Add a few targets that detect the current OS and build a package for that OS.

}
149 changes: 149 additions & 0 deletions src/main/scala/com/typesafe/sbt/packager/GenericPackageSettings.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.typesafe.sbt
package packager

import Keys._
import sbt._
import sbt.Keys.{name, mappings, sourceDirectory}
import linux.LinuxSymlink
import linux.LinuxPackageMapping

object GenericPackageSettings {
val installLocation = "/usr/share"
}
trait GenericPackageSettings
extends linux.LinuxPlugin
with debian.DebianPlugin
with rpm.RpmPlugin
with windows.WindowsPlugin
with universal.UniversalPlugin {
import GenericPackageSettings._

// This method wires a lot of hand-coded generalities about how to map directories
// into linux, and the conventions we expect.
// It is by no means 100% accurate, but should be ok for the simplest cases.
// For advanced users, use the underlying APIs.
// Right now, it's also pretty focused on command line scripts packages.

/**
* Maps linux file format from the universal from the conventions:
*
* `<project>/src/linux` files are mapped directly into linux packages.
* `<universal>` files are placed under `/usr/share/<package-name>`
* `<universal>/bin` files are given symlinks in `/usr/bin`
* `<universal>/conf` directory is given a symlink to `/etc/<package-name>`
* Files in `conf/` or `etc/` directories are automatically marked as configuration.
* `../man/...1` files are automatically compressed into .gz files.
*/
def mapGenericMappingsToLinux(mappings: Seq[(File, String)])(rename: String => String): Seq[LinuxPackageMapping] = {
val (directories, nondirectories) = mappings.partition(_._1.isDirectory)
val (binaries, nonbinaries) = nondirectories.partition(_._1.canExecute)
val (manPages, nonManPages) = nonbinaries partition {
case (file, name) => (name contains "man/") && (name endsWith ".1")
}
val compressedManPages =
for((file, name) <- manPages)
yield file -> (name + ".gz")
val (configFiles, remaining) = nonManPages partition {
case (file, name) => (name contains "etc/") || (name contains "conf/")
}
def packageMappingWithRename(mappings: (File, String)*): LinuxPackageMapping = {
val renamed =
for((file, name) <- mappings)
yield file -> rename(name)
packageMapping(renamed:_*)
}

Seq(
packageMappingWithRename((binaries ++ directories):_*) withUser "root" withGroup "root" withPerms "0755",
packageMappingWithRename(compressedManPages:_*).gzipped withUser "root" withGroup "root" withPerms "0644",
packageMappingWithRename(configFiles:_*) withConfig() withUser "root" withGroup "root" withPerms "0644",
packageMappingWithRename(remaining:_*) withUser "root" withGroup "root" withPerms "0644"
)
}

def mapGenericFilesToLinux: Seq[Setting[_]] = Seq(
// First we look at the src/linux files
linuxPackageMappings <++= (name in Universal, sourceDirectory in Linux) map { (pkg, dir) =>
mapGenericMappingsToLinux((dir.*** --- dir) x relativeTo(dir))(identity)
},
// Now we look at the src/universal files.
linuxPackageMappings <++= (name in Universal, mappings in Universal) map { (pkg, mappings) =>
// TODO - More windows filters...
def isWindowsFile(f: (File, String)): Boolean =
f._2 endsWith ".bat"

mapGenericMappingsToLinux(mappings filterNot isWindowsFile) { name =>
installLocation + "/" + pkg + "/" + name
}
},
// Now we generate symlinks.
linuxPackageSymlinks <++= (name in Universal, mappings in Universal) map { (pkg, mappings) =>
for {
(file, name) <- mappings
if !file.isDirectory
if name startsWith "bin/"
if !(name endsWith ".bat") // IGNORE windows-y things.
} yield LinuxSymlink("/usr/" + name, installLocation+"/"+pkg+"/"+name)
},
// Map configuration files
linuxPackageSymlinks <++= (name in Universal, mappings in Universal) map { (pkg, mappings) =>
val needsConfLink =
mappings exists { case (file, name) =>
(name startsWith "conf/") && !file.isDirectory
}
if(needsConfLink) Seq(LinuxSymlink(
link="/etc/" + pkg,
destination=installLocation+"/"+pkg+"/conf"))
else Seq.empty
}
)

def mapGenericFilesToWindows: Seq[Setting[_]] = Seq(
mappings in Windows <<= mappings in Universal,
wixFeatures <<= (name in Windows, mappings in Windows) map makeWindowsFeatures
)
// TODO select main script! Filter Config links!
def makeWindowsFeatures(name: String, mappings: Seq[(File, String)]): Seq[windows.WindowsFeature] = {
import windows._

val files =
for {
(file, name) <- mappings
if !file.isDirectory
} yield ComponentFile(name, editable = (name startsWith "conf"))
val corePackage =
WindowsFeature(
id=name+"Core",
title=name,
desc="All core files.",
absent="disallow",
components = files
)
// TODO - Detect bat files to add paths...
val homeEnvVar = name.toUpperCase +"_HOME"
val addBinToPath =
// TODO - we may have issues here...
WindowsFeature(
id="AddBinToPath",
title="Update Enviornment Variables",
desc="Update PATH environment variables (requires restart).",
components = Seq(AddDirectoryToPath("bin"))
)
val configLinks = for {
(file, name) <- mappings
if !file.isDirectory
if name startsWith "conf/"
} yield name
val menuLinks =
WindowsFeature(
id="AddConfigLinks",
title="Configuration start menu links",
desc="Adds start menu shortcuts to edit configuration files.",
components = Seq(AddShortCuts(configLinks))
)
// TODO - Add feature for shortcuts to binary scripts.
Seq(corePackage, addBinToPath, menuLinks)
}


}
48 changes: 41 additions & 7 deletions src/main/scala/com/typesafe/sbt/packager/debian/DebianPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@ package debian
import Keys._
import sbt._
import linux.LinuxPackageMapping
import linux.LinuxSymlink
import linux.LinuxFileMetaData
import com.typesafe.sbt.packager.Hashing
import com.typesafe.sbt.packager.linux.LinuxSymlink

trait DebianPlugin extends Plugin with linux.LinuxPlugin {
val Debian = config("debian") extend Linux

import com.typesafe.sbt.packager.universal.Archives

private[this] final def copyAndFixPerms(from: File, to: File, perms: LinuxFileMetaData, zipped: Boolean = false): Unit = {
if(zipped) IO.gzip(from, to)
else IO.copyFile(from, to, true)
if(zipped) {
IO.withTemporaryDirectory { dir =>
val tmp = dir / from.getName
IO.copyFile(from, tmp)
val zipped = Archives.gzip(tmp)
IO.copyFile(zipped, to, true)
}
} else IO.copyFile(from, to, true)
// If we have a directory, we need to alter the perms.
chmod(to, perms.permissions)
// TODO - Can we do anything about user/group ownership?
Expand All @@ -32,15 +42,15 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin {
debianPackageRecommends := Seq.empty,
debianSignRole := "builder",
target in Debian <<= (target, name in Debian, version in Debian) apply ((t,n,v) => t / (n +"-"+ v)),
name in Debian <<= (name in Linux),
version in Debian <<= (version in Linux),
linuxPackageMappings in Debian <<= linuxPackageMappings,
packageDescription in Debian <<= packageDescription in Linux,
packageSummary in Debian <<= packageSummary in Linux,
maintainer in Debian <<= maintainer in Linux,
debianMaintainerScripts := Seq.empty
) ++ inConfig(Debian)(Seq(
name <<= name,
version <<= version,
packageArchitecture := "all",
maintainer := "",
debianPackageInfo <<=
(name, version, maintainer, packageSummary, packageDescription) apply PackageInfo,
debianPackageMetadata <<=
Expand All @@ -59,6 +69,7 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin {
(data, size, dir) =>
val cfile = dir / "DEBIAN" / "control"
IO.write(cfile, data.makeContent(size), java.nio.charset.Charset.defaultCharset)
chmod(cfile, "0644")
cfile
},
debianConffilesFile <<= (linuxPackageMappings, target) map {
Expand All @@ -71,9 +82,20 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin {
if file.isFile
} yield name
IO.writeLines(cfile, conffiles)
chmod(cfile, "0644")
cfile
},
debianExplodedPackage <<= (linuxPackageMappings, debianControlFile, debianMaintainerScripts, debianConffilesFile, target) map { (mappings, _, maintScripts, _, t) =>
/*debianLinksfile <<= (name, linuxPackageSymlinks, target) map { (name, symlinks, dir) =>
val lfile = dir / "DEBIAN" / (name + ".links")
val content =
for {
LinuxSymlink(link, destination) <- symlinks
} yield link + " " + destination
IO.writeLines(lfile, content)
chmod(lfile, "0644")
lfile
},*/
debianExplodedPackage <<= (linuxPackageMappings, debianControlFile, debianMaintainerScripts, debianConffilesFile, linuxPackageSymlinks, target) map { (mappings, _, maintScripts, _, symlinks, t) =>
// First Create directories, in case we have any without files in them.
for {
LinuxPackageMapping(files, perms, zipped) <- mappings
Expand All @@ -88,11 +110,20 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin {
if !file.isDirectory && file.exists
tfile = t / name
} copyAndFixPerms(file, tfile, perms, zipped)

// Now generate relative symlinks
LinuxSymlink.makeSymLinks(symlinks, t)

// TODO: Fix this ugly hack to permission directories correctly!
for(file <- (t.***).get; if file.isDirectory) chmod(file, "0755")
for {
file <- (t.***).get
if file.isDirectory
if file.getCanonicalPath == file.getAbsolutePath // Ignore symlinks.
} chmod(file, "0755")
// Put the maintainer files in `dir / "DEBIAN"` named as specified.
// Valid values for the name are preinst,postinst,prerm,postrm
for ((file, name) <- maintScripts) copyAndFixPerms(file, t / "DEBIAN" / name, LinuxFileMetaData())

t
},
debianMD5sumsFile <<= (debianExplodedPackage, target) map {
Expand All @@ -103,9 +134,12 @@ trait DebianPlugin extends Plugin with linux.LinuxPlugin {
if file.isFile
if !(name startsWith "DEBIAN")
if !(name contains "debian-binary")
// TODO - detect symlinks...
if file.getCanonicalPath == file.getAbsolutePath
fixedName = if(name startsWith "/") name drop 1 else name
} yield Hashing.md5Sum(file) + " " + fixedName
IO.writeLines(md5file, md5sums)
chmod(md5file, "0644")
md5file
},
packageBin <<= (debianExplodedPackage, debianMD5sumsFile, target, streams) map { (pkgdir, _, tdir, s) =>
Expand Down
Loading