forked from sbt/sbt-native-packager
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added RPM building support (very rough). * Attempted to generalize debian plugin so that settings can be shared between deb + rpm builds. * Using Config inheritance FOR ALL ITS WORTH. Someone will hate me later.
- Loading branch information
Showing
7 changed files
with
326 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package com.typesafe.packager.rpm | ||
|
||
import sbt._ | ||
|
||
object RpmHelper { | ||
|
||
/** Returns the host vendor for an rpm. */ | ||
def hostVendor = | ||
Process(Seq("rpm", "-E", "%{_host_vendor}")) !! | ||
|
||
def buildRpm(spec: RpmSpec, workArea: File, log: sbt.Logger): File = { | ||
// TODO - check the spec for errors. | ||
buildWorkArea(workArea) | ||
copyFiles(spec,workArea, log) | ||
writeSpecFile(spec, workArea, log) | ||
|
||
buildPackage(workArea, spec, log) | ||
// We should probably return the File that was created. | ||
val rpmname = "%s-%s-%s-%s.rpm" format (spec.meta.name, spec.meta.version, spec.meta.release, spec.meta.arch) | ||
workArea / "RPMS" / spec.meta.arch / rpmname | ||
} | ||
|
||
private[this] def copyFiles(spec: RpmSpec, workArea: File, log: sbt.Logger): Unit = { | ||
// TODO - special treatment of icon... | ||
val buildroot = workArea / "tmp-buildroot" | ||
|
||
def copyWithZip(from: File, to: File, zipped: Boolean): Unit = | ||
if(zipped) IO.gzip(from, to) | ||
else IO.copyFile(from, to, true) | ||
// We don't have to do any permission modifications since that's in the | ||
// the .spec file. | ||
for { | ||
mapping <- spec.mappings | ||
(file, dest) <- mapping.mappings | ||
if file.exists && !file.isDirectory() | ||
target = buildroot / dest | ||
} copyWithZip(file, target, mapping.zipped) | ||
} | ||
|
||
private[this] def writeSpecFile(spec: RpmSpec, workArea: File, log: sbt.Logger): File = { | ||
val specdir = workArea / "SPECS" | ||
val rpmBuildroot = workArea / "buildroot" | ||
val tmpBuildRoot = workArea / "tmp-buildroot" | ||
val specfile = specdir / (spec.meta.name + ".spec") | ||
log.debug("Creating SPEC file: " + specfile.getAbsolutePath) | ||
IO.write(specfile, spec.writeSpec(rpmBuildroot, tmpBuildRoot)) | ||
specfile | ||
} | ||
|
||
private[this] def buildPackage( | ||
workArea: File, | ||
spec: RpmSpec, | ||
log: sbt.Logger): Unit = { | ||
val buildRoot = workArea / "buildroot" | ||
val specsDir = workArea / "SPECS" | ||
val gpg = false | ||
// TODO - Full GPG support (with GPG plugin). | ||
val args: Seq[String] = Seq( | ||
"rpmbuild", | ||
"-bb", | ||
"-buildroot", buildRoot.getAbsolutePath, | ||
"--define", "_topdir " + workArea.getAbsolutePath, | ||
"--target", spec.meta.arch + '-' + spec.meta.vendor + '-' + spec.meta.os | ||
) ++ ( | ||
if(gpg) Seq("--define", "_gpg_name " + "<insert keyname>", "--sign") | ||
else Seq.empty | ||
) ++ Seq(spec.meta.name + ".spec") | ||
Process(args, Some(specsDir)) ! log | ||
} | ||
|
||
private[this] val topleveldirs = Seq("BUILD","RPMS","SOURCES","SPECS","SRPMS","tmp-buildroot","buildroot") | ||
|
||
/** Builds the work area and returns the tmp build root, and rpm build root. */ | ||
private[this] def buildWorkArea(workArea: File): Unit = { | ||
if(!workArea.exists) workArea.mkdirs() | ||
// TODO - validate workarea | ||
// Clean out work area | ||
topleveldirs map (workArea / _) foreach { d => | ||
if(d.exists()) IO.delete(d) | ||
d.mkdir() | ||
} | ||
} | ||
|
||
def evalMacro(macro: String): String = | ||
Process(Seq("rpm", "--eval", '%' + macro)) !! | ||
} |
137 changes: 137 additions & 0 deletions
137
src/main/scala/com/typesafe/packager/rpm/RpmMetadata.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package com.typesafe.packager | ||
package rpm | ||
|
||
import linux.{LinuxPackageMapping,LinuxFileMetaData} | ||
import sbt._ | ||
|
||
case class RpmMetadata( | ||
name: String, | ||
version: String, | ||
release: String, | ||
arch: String, | ||
vendor: String, | ||
os: String) { | ||
} | ||
|
||
|
||
case class RpmDescription( | ||
summary: Option[String] = None, | ||
license: Option[String] = None, | ||
distribution: Option[String] = None, | ||
//vendor: Option[String] = None, | ||
url: Option[String] = None, | ||
group: Option[String] = None, | ||
packager: Option[String] = None, | ||
icon: Option[String] = None | ||
) | ||
|
||
case class RpmDependencies( | ||
provides: Seq[String] = Seq.empty, | ||
requirements: Seq[String] = Seq.empty, | ||
prereq: Seq[String] = Seq.empty, | ||
obsoletes: Seq[String] = Seq.empty, | ||
conflicts: Seq[String] = Seq.empty) { | ||
def contents: String = { | ||
val sb = new StringBuilder | ||
def appendSetting(prefix: String, values: Seq[String]) = | ||
values foreach (v => sb append (prefix + v + "\n")) | ||
appendSetting("Provides: ", provides) | ||
appendSetting("Requires: ", requirements) | ||
appendSetting("PreReq: ", prereq) | ||
appendSetting("Obsoletes: ", obsoletes) | ||
appendSetting("Conflicts: ", conflicts) | ||
sb.toString | ||
} | ||
} | ||
|
||
case class RpmSpec(meta: RpmMetadata, | ||
desc: RpmDescription = RpmDescription(), | ||
deps: RpmDependencies = RpmDependencies(), | ||
mappings: Seq[LinuxPackageMapping] = Seq.empty) { | ||
|
||
private[this] def makeFilesLine(target: String, meta: LinuxFileMetaData, isDir: Boolean): String = { | ||
val sb = new StringBuilder | ||
meta.config.toLowerCase match { | ||
case "false" => () | ||
case "true" => sb append "%config " | ||
case x => sb append ("%config("+x+") ") | ||
} | ||
if(meta.docs) sb append "%doc " | ||
if(isDir) sb append "%dir " | ||
// TODO - map dirs... | ||
sb append "%attr(" | ||
sb append meta.permissions | ||
sb append ',' | ||
sb append meta.user | ||
sb append ',' | ||
sb append meta.group | ||
sb append ") " | ||
sb append target | ||
sb append '\n' | ||
sb.toString | ||
} | ||
|
||
private[this] def fileSection: String = { | ||
val sb = new StringBuilder | ||
sb append "\n%files\n" | ||
// TODO - default attribute string. | ||
for { | ||
mapping <- mappings | ||
(file, dest) <- mapping.mappings | ||
} sb append makeFilesLine(dest, mapping.fileData, file.isDirectory) | ||
sb.toString | ||
} | ||
|
||
private[this] def installSection(root: File): String = { | ||
val sb = new StringBuilder | ||
sb append "\n" | ||
sb append "%install\n" | ||
sb append "if [ -e $RPM_BUILD_ROOT ]; " | ||
sb append "then\n" | ||
sb append " mv " | ||
sb append root.getAbsolutePath | ||
sb append "/* $RPM_BUILD_ROOT\n" | ||
sb append "else\n" | ||
sb append " mv " | ||
sb append root.getAbsolutePath | ||
sb append " $RPM_BUILD_ROOT\n" | ||
sb append "fi\n" | ||
sb.toString | ||
} | ||
|
||
// TODO - This is *very* tied to RPM helper, may belong *in* RpmHelper | ||
def writeSpec(rpmRoot: File, tmpRoot: File): String = { | ||
val sb = new StringBuilder | ||
sb append ("Name: %s\n" format meta.name) | ||
sb append ("Version: %s\n" format meta.version) | ||
sb append ("Release: %s\n" format meta.release) | ||
|
||
desc.summary foreach { v => sb append ("Summary: %s\n" format v)} | ||
desc.license foreach { v => sb append ("License: %s\n" format v)} | ||
desc.distribution foreach { v => sb append ("Distribution: %s\n" format v)} | ||
// TODO - Icon | ||
|
||
sb append ("Vendor: %s\n" format meta.vendor) | ||
desc.url foreach { v => sb append ("URL: %s\n" format v)} | ||
desc.group foreach { v => sb append ("Group: %s\n" format v)} | ||
desc.packager foreach { v => sb append ("Packager: %s\n" format v)} | ||
|
||
sb append deps.contents | ||
|
||
// TODO - autoprov + autoreq | ||
|
||
sb append ("BuildRoot: %s\n\n" format rpmRoot.getAbsolutePath) | ||
|
||
// write build as moving everything into RPM directory. | ||
sb append installSection(tmpRoot) | ||
// TODO - Allow symlinks | ||
// TODO - Allow scriptlets for installation | ||
// "%prep", "%pretrans", "%pre", "%post", "%preun", "%postun", "%posttrans", "%verifyscript", "%clean" | ||
// Write file mappings | ||
sb append fileSection | ||
// TODO - Write triggers... | ||
// TODO - Write changelog... | ||
|
||
sb.toString | ||
} | ||
} |
Oops, something went wrong.