-
Notifications
You must be signed in to change notification settings - Fork 439
/
RpmHelper.scala
157 lines (142 loc) · 5.33 KB
/
RpmHelper.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package com.typesafe.sbt.packager.rpm
import sbt._
import com.typesafe.sbt.packager.linux.LinuxSymlink
import com.typesafe.sbt.packager.sourceDateEpoch
object RpmHelper {
/** Returns the host vendor for an rpm. */
def hostVendor =
sys.process.Process(Seq("rpm", "-E", "%{_host_vendor}")) !!
/**
* Prepares the staging directory for the rpm build command.
*
* @param spec
* The RpmSpec
* @param workArea
* The target
* @param log
* Logger
* @return
* the `workArea`
*/
def stage(spec: RpmSpec, workArea: File, log: sbt.Logger): File = {
buildWorkArea(workArea)
copyFiles(spec, workArea, log)
writeSpecFile(spec, workArea, log)
spec.validate(log)
sourceDateEpoch(workArea)
workArea
}
private[rpm] def defaultRpmArtifactPath(stagingArea: File, meta: RpmMetadata): File =
stagingArea / "RPMS" / meta.arch / s"${meta.name}-${meta.version}-${meta.release}.${meta.arch}.rpm"
/**
* Build the rpm package
*
* @param spec
* The RpmSpec
* @param stagingArea
* Prepared staging area
* @param log
* Logger
* @return
* The rpm package
*/
def buildRpm(spec: RpmSpec, stagingArea: File, log: sbt.Logger): File = {
buildPackage(stagingArea, spec, log)
defaultRpmArtifactPath(stagingArea, spec.meta)
}
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 = {
log.debug("Copying %s to %s".format(from, to))
if (zipped) IO.gzip(from, to)
else IO.copyFile(from, to, true)
}
// First make sure directories are there....
IO createDirectories (for {
mapping <- spec.mappings
(file, dest) <- mapping.mappings
if file.isDirectory
target = buildroot / dest
} yield target)
// 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)
LinuxSymlink.makeSymLinks(spec.symlinks, buildroot, relativeLinks = false)
}
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).
IO.withTemporaryDirectory { tmpRpmBuildDir =>
val args: Seq[String] = (spec.setarch match {
case Some(arch) => Seq("setarch", arch)
case None => Seq()
}) ++ Seq(
"rpmbuild",
"-bb",
"--target",
spec.meta.arch + '-' + spec.meta.vendor + '-' + spec.meta.os,
"--buildroot",
buildRoot.getAbsolutePath,
"--define",
"_topdir " + workArea.getAbsolutePath,
"--define",
"_tmppath " + tmpRpmBuildDir.getAbsolutePath,
"--define",
"%use_source_date_epoch_as_buildtime 1",
"--define",
"%clamp_mtime_to_source_date_epoch 1"
) ++ (
if (gpg) Seq("--define", "_gpg_name " + "<insert keyname>", "--sign")
else Seq.empty
) ++ Seq(spec.meta.name + ".spec")
log.debug("Executing rpmbuild with: " + args.mkString(" "))
// RPM outputs to standard error in non-error cases. So just collect all the output, then dump
// it all to either error log or info log depending on the exit status
val outputBuffer = collection.mutable.ArrayBuffer.empty[String]
sys.process.Process(args, Some(specsDir)) ! sys.process.ProcessLogger(o => outputBuffer.append(o)) match {
case 0 =>
// Workaround for #1246 - random tests fail with a NullPointerException in the sbt ConsoleLogger
// I wasn't able to reproduce this locally and there aren't any user reports on this, so we catch
// the NPE and log via println
try outputBuffer.foreach(log.info(_))
catch {
case e: NullPointerException =>
outputBuffer.foreach(println(_))
}
case code =>
outputBuffer.foreach(log.error(_))
sys.error("Unable to run rpmbuild, check output for details. Errorcode " + code)
}
}
}
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(mcro: String): String =
sys.process.Process(Seq("rpm", "--eval", '%' + mcro)).!!
}