Skip to content

Commit 4a94ec7

Browse files
committed
Merge pull request #494 from metasim/feature/javapackager-format
Added translation of `jvmopts` contents and enhanced example app.
2 parents 5d03d9f + 802eeec commit 4a94ec7

File tree

9 files changed

+275
-92
lines changed

9 files changed

+275
-92
lines changed

README.md

+7-29
Original file line numberDiff line numberDiff line change
@@ -79,38 +79,16 @@ packageBin in Debian <<= debianJDebPackaging in Debian
7979

8080
JDK 8 from Oracle includes the tool `javapackager` (née `javafxpackager`) to generate application
8181
launchers and native installers for MacOS X, Windows, and Linux. This plugin complements the existing
82-
`sbt-native-packager` formats by taking the staged output from `JavaAppPackaging` and `Universal`
83-
settings and passing them through `javapackager`.
82+
`sbt-native-packager` formats by taking the settings and staged output from `JavaAppPackaging`
83+
and passing them through `javapackager`.
8484

85-
This plugin's most significant complement to the core capabilities is the generation of
86-
MacOS X App bundles and derived `.dmg` and `.pkg` packages. It can also generate Linux `.deb` and `.rpm`
87-
packages, and Windows `.exe` and `.msi` installers, provided the requisite tools are available on the
88-
build platform.
85+
This plugin's most significant complement to the core `sbt-native-packager` capabilities is the
86+
generation of MacOS X App bundles, and associated `.dmg` and `.pkg` package formats.
87+
It can also generate Windows `.exe` and `.msi` installers provided the requisite tools are
88+
available on the Windows build platform.
8989

90-
For details on the capabilities of `javapackager`, see the [windows](http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javapackager.html) and [Unix](http://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html) references. (Note: only a few of the possible
91-
settings are exposed through this plugin. Please submit a github issue or pull request if something
92-
specific is desired.)
90+
For usage details see the [JDKPackager Plugin guide](http://www.scala-sbt.org/sbt-native-packager/formats/jdkpackager.html).
9391

94-
Using this plugin requires running SBT under JDK 8, or setting `jdkPackagerTool` to the location
95-
of the tool.
96-
97-
To use, first get your application working per `JavaAppPackaging` instructions (including `mainClass`),
98-
then add
99-
100-
```scala
101-
enablePlugins(JDKPackagerPlugin)
102-
```
103-
104-
to your build file. Then run `sbt jdkPackager:packageBin`.
105-
106-
By default, the plugin makes the installer type that is native to the current build platform in
107-
the directory `target/jdkpackager`. The key `jdkPackageType` can be used to modify this behavior.
108-
Run `help jdkPackageType` in sbt for details. The most popular setting is likely to be `jdkAppIcon`. See
109-
[the example build file](test-project-jdkpackager/build.sbt) on how to use it.
110-
111-
To take it for a test spin, run `sbt jdkPackager:packageBin` in the `test-project-jdkpackager` directory
112-
and then look in the `target/jdkpackager/bundles` directory for the result. See [Oracle documentation](http://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html) for Windows and
113-
Linux build prerequisites.
11492

11593
## Documentation ##
11694

src/main/scala/com/typesafe/sbt/packager/jdkpackager/JDKPackagerHelper.scala

+64-45
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import sbt._
1010
*/
1111
object JDKPackagerHelper {
1212

13-
// Try to compute determine a default path for the java packager tool.
13+
/** Attempts to compute the path to the `javapackager` tool. */
1414
def locateJDKPackagerTool(): Option[File] = {
1515
val jdkHome = sys.props.get("java.home").map(p file(p))
1616

@@ -26,32 +26,12 @@ object JDKPackagerHelper {
2626
.filter(_.exists())
2727
}
2828

29-
def mkProcess(
30-
tool: File,
31-
mode: String,
32-
argMap: Map[String, String],
33-
log: Logger) = {
34-
35-
val invocation = Seq(tool.getAbsolutePath, mode, "-v")
36-
37-
val argSeq = argMap.map(p Seq(p._1, p._2)).flatten[String].filter(_.length > 0)
38-
val args = invocation ++ argSeq
39-
40-
val argString = args.map {
41-
case s if s.contains(" ") s""""$s""""
42-
case s s
43-
}.mkString(" ")
44-
log.debug(s"Package command: $argString")
45-
46-
// To help debug arguments
47-
val script = file(argMap("-outdir")) / "jdkpackager.sh"
48-
IO.write(script, s"#!/bin/bash\n$argString\n")
49-
chmod(script, "766")
50-
51-
Process(args)
52-
}
53-
54-
def mkArgMap(
29+
/**
30+
* Generates key-value pairs to be converted into command line arguments fed to `javapackager`.
31+
* If an argument is mono/standalone (not key/value) then the key stores the complete argument
32+
* and the value is the empty string.
33+
*/
34+
private[jdkpackager] def makeArgMap(
5535
name: String,
5636
version: String,
5737
description: String,
@@ -62,40 +42,79 @@ object JDKPackagerHelper {
6242
basename: String,
6343
iconPath: Option[File],
6444
outputDir: File,
65-
sourceDir: File) = {
45+
sourceDir: File): Map[String, String] = {
6646

67-
def iconArg = iconPath
47+
val iconArg = iconPath.toSeq
6848
.map(_.getAbsolutePath)
69-
.map(p Map(s"-Bicon=$p" -> ""))
70-
.getOrElse(Map.empty)
49+
.map(p s"-Bicon=$p")
7150

72-
def mainClassArg = mainClass
51+
val mainClassArg = mainClass
7352
.map(c Map("-appclass" -> c))
7453
.getOrElse(Map.empty)
7554

76-
// val cpSep = sys.props("path.separator")
77-
// val cp = classpath.map(p ⇒ "lib/" + p)
78-
// val cpStr = cp.mkString(cpSep)
55+
// Make a setting?
56+
val jvmOptsFile = (sourceDir ** "jvmopts").getPaths.headOption.map(file)
57+
58+
val jvmOptsArgs = jvmOptsFile.toSeq.flatMap { jvmopts
59+
IO.readLines(jvmopts).map {
60+
case a if a startsWith "-X" s"-BjvmOptions=$a"
61+
case b if b startsWith "-D" s"-BjvmProperties=${b.drop(2)}"
62+
case c "" // Ignoring others.... is this OK?
63+
}.filter(_.nonEmpty)
64+
}
7965

8066
// See http://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html and
8167
// http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javapackager.html for
8268
// command line options. NB: Built-in `-help` is incomplete.
83-
Map(
69+
70+
// TODO:
71+
// * copyright/license ( -BlicenseFile=LICENSE )
72+
// * environment variables?
73+
// * category ?
74+
75+
val pairs = Map(
8476
"-name" -> name,
8577
"-srcdir" -> sourceDir.getAbsolutePath,
8678
"-native" -> packageType,
8779
"-outdir" -> outputDir.getAbsolutePath,
8880
"-outfile" -> basename,
8981
"-description" -> description,
90-
"-vendor" -> maintainer,
91-
s"-BappVersion=$version" -> "",
92-
s"-BmainJar=lib/$mainJar" -> ""
93-
) ++ mainClassArg ++ iconArg
82+
"-vendor" -> maintainer
83+
) ++ mainClassArg
9484

95-
// TODO:
96-
// * copyright/license
97-
// * JVM options
98-
// * application arguments
99-
// * environment variables?
85+
val singles = Seq(
86+
s"-BappVersion=$version",
87+
s"-BmainJar=lib/$mainJar"
88+
) ++ iconArg ++ jvmOptsArgs
89+
90+
// Merge singles into argument pair map. (Need a cleaner abstraction)
91+
pairs ++ singles.map((_, "")).toMap
92+
}
93+
94+
/** Generates a configure Process instance, ready to run. */
95+
private[jdkpackager] def makeProcess(
96+
tool: File,
97+
mode: String,
98+
argMap: Map[String, String],
99+
log: Logger) = {
100+
101+
val invocation = Seq(tool.getAbsolutePath, mode, "-v")
102+
103+
val argSeq = argMap.map(p Seq(p._1, p._2)).flatten[String].filter(_.length > 0)
104+
val args = invocation ++ argSeq
105+
106+
val argString = args.map {
107+
case s if s.contains(" ") s""""$s""""
108+
case s s
109+
}.mkString(" ")
110+
log.debug(s"Package command: $argString")
111+
112+
// To help debug arguments, create a bash script doing the same.
113+
val script = file(argMap("-outdir")) / "jdkpackager.sh"
114+
IO.write(script, s"#!/bin/bash\n$argString\n")
115+
chmod(script, "766")
116+
117+
Process(args)
100118
}
119+
101120
}

src/main/scala/com/typesafe/sbt/packager/jdkpackager/JDKPackagerKeys.scala

+18-5
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,28 @@ package com.typesafe.sbt.packager.jdkpackager
33
import sbt._
44

55
/**
6-
* Keys specific to deployment via the `javapackger` too.
6+
* Keys specific to deployment via the `javapackger` tool.
7+
*
8+
* @author <a href="mailto:[email protected]">Simeon H.K. Fitch</a>
9+
* @since 2/11/15
710
*/
811
trait JDKPackagerKeys {
912
val jdkPackagerTool = SettingKey[Option[File]]("jdkPackagerTool",
10-
"Path to `javapackager` or `javafxpackager` tool in JDK")
13+
"Path to `javapackager` or `javafxpackager` tool in JDK.")
14+
1115
val packagerArgMap = TaskKey[Map[String, String]]("packagerArgMap",
1216
"An intermediate task for computing the command line argument key/value pairs passed to " +
1317
"`javapackager -deploy`")
18+
1419
val jdkPackagerBasename = SettingKey[String]("jdkPackagerOutputBasename",
1520
"Filename sans extension for generated installer package.")
16-
val jdkPackageType = SettingKey[String]("jdkPackageType",
21+
22+
val jdkPackagerType = SettingKey[String]("jdkPackagerType",
1723
"""Value passed as the `-native` argument to `javapackager -deploy`.
1824
| Per `javapackager` documentation, this may be one of the following:
1925
|
2026
| * `all`: Runs all of the installers for the platform on which it is running,
21-
| and creates a disk image for the application. This value is used if type is not specified.
27+
| and creates a disk image for the application.
2228
| * `installer`: Runs all of the installers for the platform on which it is running.
2329
| * `image`: Creates a disk image for the application. On OS X, the image is
2430
| the .app file. On Linux, the image is the directory that gets installed.
@@ -30,13 +36,20 @@ trait JDKPackagerKeys {
3036
| * `exe`: Generates a Windows .exe package.
3137
| * `msi`: Generates a Windows Installer package.
3238
|
33-
| (NB: Your mileage may vary.)
39+
| Defaults to `image`.
3440
""".stripMargin)
3541

3642
val jdkAppIcon = SettingKey[Option[File]]("jdkAppIcon",
3743
"""Path to platform-specific application icon:
3844
| * `icns`: MacOS
3945
| * `ico`: Windows
4046
| * `png`: Linux
47+
|
48+
| Defaults to generic Java icon.
4149
""".stripMargin)
50+
51+
// To consider:
52+
// val jvmOptionFile = SettingKey[Option[File]]("jvmOptionFile",
53+
// "File with line delimited options to pass to the application JVM. " +
54+
// "Defaults to `stage/conf/jvmopts")
4255
}

src/main/scala/com/typesafe/sbt/packager/jdkpackager/JDKPackagerPlugin.scala

+8-7
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ object JDKPackagerPlugin extends AutoPlugin {
2727
def javaPackagerSettings: Seq[Setting[_]] = Seq(
2828
jdkPackagerTool := JDKPackagerHelper.locateJDKPackagerTool(),
2929
jdkAppIcon := None,
30-
jdkPackageType := "image",
31-
jdkPackagerBasename <<= packageName apply (_ + "-pkg"),
32-
mappings in JDKPackager <<= (mappings in Universal) map (_.filter(!_._2.startsWith("bin/")))
30+
jdkPackagerType := "image",
31+
jdkPackagerBasename <<= packageName apply (_ + "-pkg")
32+
// jvmOptionFile <<= sourceDirectory apply (src ⇒ Some(src / "conf" / "jvmopts")))
33+
// mappings in JDKPackager <<= (mappings in Universal) map (_.filter(!_._2.startsWith("bin/")))
3334
) ++ inConfig(JDKPackager)(
3435
Seq(
3536
sourceDirectory <<= sourceDirectory apply (_ / dirname),
@@ -45,13 +46,13 @@ object JDKPackagerPlugin extends AutoPlugin {
4546
version,
4647
packageDescription,
4748
maintainer,
48-
jdkPackageType,
49+
jdkPackagerType,
4950
ClasspathJarPlugin.autoImport.classspathJarName,
5051
mainClass,
5152
jdkPackagerBasename,
5253
jdkAppIcon,
5354
target,
54-
stage in Universal) map JDKPackagerHelper.mkArgMap
55+
stage in Universal) map JDKPackagerHelper.makeArgMap
5556
) ++ makePackageBuilder
5657
)
5758

@@ -60,10 +61,10 @@ object JDKPackagerPlugin extends AutoPlugin {
6061
)
6162

6263
private def makePackageBuilder = Seq(
63-
packageBin <<= (jdkPackagerTool, jdkPackageType, packagerArgMap, target, streams) map { (pkgTool, pkg, args, target, s)
64+
packageBin <<= (jdkPackagerTool, jdkPackagerType, packagerArgMap, target, streams) map { (pkgTool, pkg, args, target, s)
6465

6566
val tool = checkTool(pkgTool)
66-
val proc = JDKPackagerHelper.mkProcess(tool, "-deploy", args, s.log)
67+
val proc = JDKPackagerHelper.makeProcess(tool, "-deploy", args, s.log)
6768

6869
(proc ! s.log) match {
6970
case 0 ()

src/sphinx/formats/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ Packaging Formats
1212
rpm.rst
1313
docker.rst
1414
windows.rst
15+
jdkpackager.rst

0 commit comments

Comments
 (0)