-
Notifications
You must be signed in to change notification settings - Fork 436
/
JDKPackagerHelper.scala
151 lines (129 loc) · 5 KB
/
JDKPackagerHelper.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
package com.typesafe.sbt.packager.jdkpackager
import com.typesafe.sbt.packager.chmod
import sbt._
import scala.util.Try
import com.typesafe.sbt.packager.archetypes.JavaAppPackaging
/**
* Support/helper functions for interacting with the `javapackager` tool.
* @author <a href="mailto:fitch@datamininglab.com">Simeon H.K. Fitch</a>
* @since 2/11/15
*/
object JDKPackagerHelper {
/** Attempts to compute the path to the `javapackager` tool. */
def locateJDKPackagerTool(javaHome: Option[File]): Option[File] = {
val toolname = sys.props("os.name").toLowerCase match {
case os if os.contains("win") ⇒ "javapackager.exe"
case _ ⇒ "javapackager"
}
// This approach to getting JDK bits is borrowed from: http://stackoverflow.com/a/25163628/296509
// Starting with an ordered list of possible java directory sources, create derivative and
// then test for the tool. It's nasty looking because there's no canonical way of finding the
// JDK from the JRE, and JDK_HOME isn't always defined.
Seq(
// Build-defined
javaHome,
// Environment override
sys.env.get("JDK_HOME").map(file),
sys.env.get("JAVA_HOME").map(file),
// MacOS X
Try("/usr/libexec/java_home".!!.trim).toOption.map(file),
// From system properties
sys.props.get("java.home").map(file)
)
// Unlift Option-s
.flatten
// For each base directory, add the parent variant to cover nested JREs on Unix.
.flatMap {
f ⇒ Seq(f, f.getAbsoluteFile)
}
// On Windows we're often running in the JRE and not the JDK. If JDK is installed,
// it's likely to be in a parallel directory, with the "jre" prefix changed to "jdk"
.flatMap { f ⇒
if (f.getName.startsWith("jre")) {
Seq(f, f.getParentFile / ("jdk" + f.getName.drop(3)))
} else Seq(f)
}
// Now search for the tool
.map { n =>
n / "bin" / toolname
}
.find(_.exists)
}
/**
* Generates key-value pairs to be converted into command line arguments fed to `javapackager`.
* If an argument is mono/standalone (not key/value) then the key stores the complete argument
* and the value is the empty string.
*/
private[jdkpackager] def makeArgMap(
name: String,
version: String,
description: String,
maintainer: String,
packageType: String,
mainJar: File,
mainClass: Option[String],
basename: String,
iconPath: Option[File],
outputDir: File,
sourceDir: File): Map[String, String] = {
val iconArg = iconPath.toSeq
.map(_.getAbsolutePath)
.map(p ⇒ s"-Bicon=$p")
val mainClassArg = mainClass
.map(c ⇒ Map("-appclass" -> c))
.getOrElse(Map.empty)
val vendorArg = if (maintainer.nonEmpty)
Map("-vendor" -> maintainer) else Map.empty
val descriptionArg = if (description.nonEmpty)
Map("-description" -> description) else Map.empty
// Make a setting?
val jvmOptsFile = (sourceDir ** JavaAppPackaging.appIniLocation).getPaths.headOption.map(file)
val jvmOptsArgs = jvmOptsFile.toSeq.flatMap { jvmopts ⇒
IO.readLines(jvmopts).map {
case a if a startsWith "-X" ⇒ s"-BjvmOptions=$a"
case b if b startsWith "-D" ⇒ s"-BjvmProperties=${b.drop(2)}"
case c ⇒ "" // Ignoring others.... is this OK?
}.filter(_.nonEmpty)
}
// See http://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html and
// http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javapackager.html for
// command line options. NB: Built-in `-help` is incomplete.
// TODO:
// * copyright/license ( -BlicenseFile=LICENSE )
// * environment variables?
// * category ?
val pairs = Map(
"-name" -> name,
"-srcdir" -> sourceDir.getAbsolutePath,
"-native" -> packageType,
"-outdir" -> outputDir.getAbsolutePath,
"-outfile" -> basename
) ++ mainClassArg ++ vendorArg ++ descriptionArg
val singles = Seq(
s"-BappVersion=$version",
s"-BmainJar=lib/${mainJar.getName}"
) ++ iconArg ++ jvmOptsArgs
// Merge singles into argument pair map. (Need a cleaner abstraction)
pairs ++ singles.map((_, "")).toMap
}
/** Generates a configure Process instance, ready to run. */
private[jdkpackager] def makeProcess(
tool: File,
mode: String,
argMap: Map[String, String],
log: Logger) = {
val invocation = Seq(tool.getAbsolutePath, mode, "-v")
val argSeq = argMap.map(p ⇒ Seq(p._1, p._2)).flatten[String].filter(_.length > 0)
val args = invocation ++ argSeq
val argString = args.map {
case s if s.contains(" ") ⇒ s""""$s""""
case s ⇒ s
}.mkString(" ")
log.debug(s"Package command: $argString")
// To help debug arguments, create a bash script doing the same.
val script = file(argMap("-outdir")) / "jdkpackager.sh"
IO.write(script, s"#!/bin/bash\n$argString\n")
Try(chmod(script, "766"))
Process(args)
}
}