Skip to content

Commit

Permalink
add method jdkDownload
Browse files Browse the repository at this point in the history
  • Loading branch information
siordache committed Jul 6, 2020
1 parent 44895a9 commit b29d2c8
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 25 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ dependencies {

shadowJar {
configurations = [project.configurations.plugin]
classifier = null
archiveClassifier = null
dependencies {
include(dependency("org.ow2.asm:asm:$asmVersion"))
include(dependency("org.ow2.asm:asm-commons:$asmVersion"))
Expand Down
43 changes: 32 additions & 11 deletions doc/user_guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ consisting only of a module descriptor. The module descriptor specifies that the
_depends on_: `prepareMergedJarsDir`
<<jpackageImage>>:: Uses the https://jdk.java.net/jpackage/[jpackage] tool to create a platform-specific application image. +
_depends on_: `prepareModulesDir` +
_This task is experimental._
<<jpackage>>:: Uses the https://jdk.java.net/jpackage/[jpackage] tool to create a platform-specific application installer. +
_depends on_: `jpackageImage` +
_This task is experimental._

A detailed description of these tasks is given in <<taskDetails>>

Expand Down Expand Up @@ -141,7 +139,20 @@ determine the location of the image directory and of the image archive. +
&nbsp;&nbsp;&nbsp;&nbsp; _Methods:_ +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [maroon]##addOptions##(String... [purple]##options##): an alternative way of setting the `options` property. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [maroon]##addExtraModulePath##(String [purple]##path##): pass the specified path to the `--module-path` option of jlink. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This method can be used to specify the location of the platform-specific OpenJFX modules.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This method can be used to specify the location of the platform-specific OpenJFX modules. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [maroon]##jdkDownload##(String [purple]##downloadUrl##, Closure [purple]##downloadConfig##=null): helper method for setting [purple]##jdkHome##. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; It downloads and unpacks a JDK distribution from the given URL. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The optional closure allows configuring the following parameters: +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - [purple]##downloadDir##: the directory in which the distribution is downloaded and unpacked. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _defaultValue_: `_buildDir_/jdks/_targetPlatform-name_` +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - [purple]##archiveName##: the name under which the archived distribution should be saved. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _defaultValue_: `jdk` +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - [purple]##archiveExtension##: accepted values: `tar.gz` and `zip`. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _defaultValue_: `null` (inferred from the URL) +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - [purple]##pathToHome##: the relative path to the JDK home in the unpacked distribution. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _defaultValue_: `null` (inferred by scanning the unpacked distribution) +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - [purple]##overwrite##: if `true`, the plugin overwrites an already existing distribution. +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _defaultValue_: `false`

[cols="1,100", frame=none, grid=none]
|===
Expand All @@ -151,23 +162,33 @@ a| a| .Usage example
jlink {
...
targetPlatform("linux-s390x") {
jdkHome = "/usr/lib/jvm/linux-s390x/jdk-11.0.2+9"
jdkHome = "/usr/lib/jvm/linux-s390x/jdk-14.0.1_7"
addOptions("--endian", "big")
addExtraModulePath("/usr/lib/openjfx/linux-s390x/jmods")
}
targetPlatform("mac") {
jdkHome = "/usr/lib/jvm/mac/jdk-11.0.2+9"
addExtraModulePath("/usr/lib/openjfx/mac/jmods")
}
targetPlatform("win") {
jdkHome = "/usr/lib/jvm/win/jdk-11.0.2+9"
jdkHome = jdkDownload("https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.1%2B7.1/OpenJDK14U-jdk_x64_windows_hotspot_14.0.1_7.zip")
addExtraModulePath("/usr/lib/openjfx/win/jmods")
}
targetPlatform("mac") {
jdkHome = jdkDownload("https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.1%2B7/OpenJDK14U-jdk_x64_mac_hotspot_14.0.1_7.tar.gz") {
downloadDir = "$buildDir/myMac"
archiveName = "my-mac-jdk"
archiveExtension = "tar.gz"
pathToHome = "jdk-14.0.1+7/Contents/Home"
overwrite = true
}
addExtraModulePath("/usr/lib/openjfx/mac/jmods")
}
...
}
----
|===



[[scriptBlocks]]
== Script blocks

Expand Down Expand Up @@ -416,7 +437,7 @@ jlink {

=== jpackage

This experimental script block allows you to customize the https://jdk.java.net/jpackage/[jpackage]-based generation of platform-specific application images and installers.
This script block allows you to customize the https://jdk.java.net/jpackage/[jpackage]-based generation of platform-specific application images and installers.

jpackageHome:: The path to the JDK providing the jpackage tool. +
_defaultValue_: the first non-empty value from: +
Expand Down Expand Up @@ -482,7 +503,7 @@ targetPlatformName:: This property is required only when using the `targetPlatfo
It specifies which of the images produced by jlink should be used as runtime image by jpackage.
Its value must match the name provided in one of the calls to the `targetPlatform` method. +
_defaultValue_: null +
_usage example_: `targetPlatform = "linux"`
_usage example_: `targetPlatformName = "linux"`


_Usage example_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.beryx.jlink.data

import groovy.transform.CompileStatic
import groovy.transform.ToString
import org.beryx.jlink.util.Util
import org.gradle.api.Action
import org.gradle.api.Project
Expand All @@ -30,6 +29,7 @@ import static org.beryx.jlink.util.Util.EXEC_EXTENSION

@CompileStatic
class JlinkPluginExtension {
private final Project project
final Property<String> jlinkBasePath
final Property<String> imageName
final DirectoryProperty imageDir
Expand Down Expand Up @@ -57,6 +57,7 @@ class JlinkPluginExtension {
final Property<JPackageData> jpackageData

JlinkPluginExtension(Project project) {
this.project = project
jlinkBasePath = project.objects.property(String)
jlinkBasePath.set(project.provider{"$project.buildDir/jlinkbase" as String})

Expand Down Expand Up @@ -147,11 +148,11 @@ class JlinkPluginExtension {
}

void targetPlatform(String name, String jdkHome, List<String> options = []) {
Util.putToMapProvider(targetPlatforms, name, new TargetPlatform(name, jdkHome, options))
Util.putToMapProvider(targetPlatforms, name, new TargetPlatform(project, name, jdkHome, options))
}

void targetPlatform(String name, Action<TargetPlatform> action) {
def targetPlatform = new TargetPlatform(name)
def targetPlatform = new TargetPlatform(project, name)
action.execute(targetPlatform)
Util.putToMapProvider(targetPlatforms, name, targetPlatform)
}
Expand Down
50 changes: 47 additions & 3 deletions src/main/groovy/org/beryx/jlink/data/TargetPlatform.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,69 @@
package org.beryx.jlink.data

import groovy.transform.CompileStatic
import org.beryx.jlink.util.JdkUtil
import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging

@CompileStatic
class TargetPlatform implements Serializable {
private static final Logger LOGGER = Logging.getLogger(TargetPlatform.class)

transient private final Project project
final String name
String jdkHome
private Serializable jdkHome
List<String> options = []
List<String> extraModulePaths = []

TargetPlatform(String name, String jdkHome = '', List<String> options = []) {
TargetPlatform(Project project, String name, String jdkHome = '', List<String> options = []) {
this.project = project
this.name = name
this.jdkHome = jdkHome
this.@jdkHome = jdkHome
this.options.addAll(options)
}

String getJdkHome() {
(this.@jdkHome == null) ? null : this.@jdkHome.toString()
}

void setJdkHome(Serializable jdkHome) {
this.@jdkHome = jdkHome
}

void addOptions(String... opts) {
opts.each { String opt -> options.add(opt) }
}

void addExtraModulePath(String path) {
extraModulePaths << path
}

private static class LazyString implements Serializable {
final Closure<String> closure
LazyString(Closure<String> closure) {
this.closure = closure
}

@Lazy String string = closure.call()

@Override
String toString() {
string
}
}

LazyString jdkDownload(String downloadUrl, Closure downloadConfig = null) {
def options = new JdkUtil.JdkDownloadOptions(project, name, downloadUrl)
if(downloadConfig) {
downloadConfig.delegate = options
downloadConfig(options)
}
return new LazyString({
def relativePathToHome = JdkUtil.downloadFrom(downloadUrl, options)
def pathToHome = "$options.downloadDir/$relativePathToHome"
LOGGER.info("Home of downloaded JDK distribution: $pathToHome")
return pathToHome as String
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.beryx.jlink.util.Util
import org.beryx.jlink.data.CreateMergedModuleTaskData
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.file.CopySpec
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging

Expand All @@ -41,16 +42,16 @@ class CreateMergedModuleTaskImpl extends BaseTaskImpl<CreateMergedModuleTaskData
LOGGER.info("taskData: $taskData")
}

@CompileDynamic
void execute() {
def jarFilePath = "$td.tmpMergedModuleDirPath/$td.mergedModuleJar.name"
Util.createJar(project, jarFilePath, td.mergedJarsDir)
def modInfoDir = genModuleInfo(project.file(jarFilePath), project.file(td.tmpJarsDirPath), td.mergedModuleName)
compileModuleInfo(project.file(modInfoDir), project.file(jarFilePath), project.file(td.tmpModuleInfoDirPath))
LOGGER.info("Copy from $td.mergedJarsDir into ${td.tmpModuleInfoDirPath}...")
project.copy {
from td.mergedJarsDir
into td.tmpModuleInfoDirPath
project.copy { CopySpec spec ->
spec.from td.mergedJarsDir
spec.into td.tmpModuleInfoDirPath
spec.exclude "**/module-info.class"
}
Util.createJar(project, td.mergedModuleJar, project.file(td.tmpModuleInfoDirPath))
}
Expand All @@ -60,7 +61,7 @@ class CreateMergedModuleTaskImpl extends BaseTaskImpl<CreateMergedModuleTaskData
}

File genModuleInfo(File jarFile, File targetDir, String moduleName) {
LOGGER.info("Generating module-info in ${targetDir}...")
LOGGER.info("Generating module-info of module $moduleName in ${targetDir}...")
project.delete(targetDir)
targetDir.mkdirs()
def moduleInfoFile = genModuleInfoJdeps(jarFile, targetDir)
Expand Down Expand Up @@ -128,7 +129,7 @@ class CreateMergedModuleTaskImpl extends BaseTaskImpl<CreateMergedModuleTaskData

@CompileDynamic
def compileModuleInfo(File moduleInfoJavaDir, File moduleJar, File targetDir) {
LOGGER.info("Compiling module-info from ${moduleInfoJavaDir}...")
LOGGER.info("Compiling module-info from ${moduleInfoJavaDir} into ${targetDir}...")
project.delete(targetDir)
project.copy {
from(project.zipTree(moduleJar))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ class JPackageImageTaskImpl extends BaseTaskImpl<JPackageTaskData> {

@CompileDynamic
void execute() {
LOGGER.warn("The jpackage task is experimental. Use it at your own risk.")
def result = project.exec {
ignoreExitValue = true
standardOutput = new ByteArrayOutputStream()
Expand Down
117 changes: 117 additions & 0 deletions src/main/groovy/org/beryx/jlink/util/JdkUtil.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.beryx.jlink.util

import groovy.transform.CompileStatic
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.file.CopySpec
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging

@CompileStatic
class JdkUtil {
private static final Logger LOGGER = Logging.getLogger(JdkUtil.class)

static class JdkDownloadOptions implements Serializable {
transient final Project project
String downloadDir
String archiveName
String archiveExtension
String pathToHome
boolean overwrite

JdkDownloadOptions(Project project, String targetPlatform, String downloadUrl) {
this.project = project
this.downloadDir = "$project.buildDir/jdks/$targetPlatform"
this.archiveName = "jdk"
def urlPath = new URL(downloadUrl).path
if(urlPath.endsWith(".tar.gz")) archiveExtension = "tar.gz"
if(urlPath.endsWith(".zip")) archiveExtension = "zip"
}

void validate() {
if(!project) throw new GradleException("Internal error: project not set in JdkDownloadOptions")
if(!downloadDir) throw new GradleException("Please provide a value for 'downloadDir' when calling the 'jdkDownload' method")
if(!archiveName) throw new GradleException("Please provide a value for 'archiveName' when calling the 'jdkDownload' method")
if(!archiveExtension) throw new GradleException("Cannot infer the archive type. Please provide a value for 'archiveExtension' when calling the 'jdkDownload' method. Accepted values: 'tar.gz', 'zip'")
}
}

static String downloadFrom(String url, JdkDownloadOptions options) {
options.validate()
def archiveFile = new File("${options.downloadDir}/${options.archiveName}.${options.archiveExtension}")
def archiveDir = archiveFile.parentFile
maybeCreateDir(archiveDir)
if(options.overwrite) {
archiveFile.delete()
if(archiveFile.exists()) throw new GradleException("Cannot delete $archiveFile")
} else {
def pathToHome = options.pathToHome ?: detectPathToHome(archiveDir)
if(pathToHome) {
LOGGER.info("JDK found at $archiveDir/$pathToHome; skipping download from $url")
return pathToHome
}
}
if(!archiveFile.file) {
LOGGER.info("Downloading from $url into $archiveFile")
new URL(url).withInputStream{ is -> archiveFile.withOutputStream{ it << is }}
}
return unpackJdk(archiveDir, archiveFile, options)
}

private static String unpackJdk(File archiveDir, File archiveFile, JdkDownloadOptions options) {
if(!archiveFile.file) throw new GradleException("Archive file $archiveFile does not exist.")
LOGGER.info("Unpacking $archiveFile")
options.project.copy { CopySpec spec ->
spec.from ((options.archiveExtension == 'tar.gz')
? options.project.tarTree(options.project.resources.gzip(archiveFile))
: options.project.zipTree(archiveFile))
spec.into archiveDir
}
if(options.pathToHome) {
if(!isJdkHome(new File("$archiveDir/$options.pathToHome"))) {
LOGGER.warn("JDK home not found at $archiveDir/$options.pathToHome! Please check the the unpacked archive.")
}
return options.pathToHome
} else {
def pathToHome = detectPathToHome(archiveDir)
if(!pathToHome) throw new GradleException("Cannot detect JDK home in $archiveDir. Check the unpacked archive and/or try setting `pathToHome` when calling the 'jdkDownload' method")
return pathToHome
}
}

private static String detectPathToHome(File dir) {
def dirs = [dir]
dir.eachDirRecurse { dirs << it }
def homeDir = dirs.find { isJdkHome(it) }
if(!homeDir) return null
def relPath = dir.toPath().relativize(homeDir.toPath())
return relPath ?: '.'
}

private static boolean isJdkHome(File dir) {
new File("$dir/bin/java").file || new File("$dir/bin/java.exe").file
}

private static void maybeCreateDir(File dir) {
if (!dir.directory) {
if (dir.exists()) throw new GradleException("$dir exists but it's not a directory.")
dir.mkdirs()
if (!dir.directory) throw new GradleException("Cannot create directory $dir.")
}
}
}

0 comments on commit b29d2c8

Please sign in to comment.