Commit of cross-compile logic ported from java builder
commit
b318c44c2e
|
@ -0,0 +1,124 @@
|
|||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/**/codeStyles/
|
||||
.idea/**/codeStyleSettings.xml
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/shelf/
|
||||
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
######################
|
||||
# End JetBrains IDEs #
|
||||
######################
|
||||
|
||||
|
||||
# From https://github.com/github/gitignore/blob/master/Gradle.gitignore
|
||||
.gradle
|
||||
/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
|
||||
|
||||
# From https://github.com/github/gitignore/blob/master/Java.gitignore
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
|
||||
|
||||
##########################################################
|
||||
# Specific to this module
|
||||
.iml
|
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
* Copyright 2012 dorkbox, llc
|
||||
*
|
||||
* 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 dorkbox.crossCompile
|
||||
|
||||
import de.undercouch.gradle.tasks.download.Download
|
||||
import de.undercouch.gradle.tasks.download.Verify
|
||||
import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream
|
||||
import org.apache.commons.compress.compressors.lzma.LZMACompressorOutputStream
|
||||
import org.apache.commons.compress.compressors.pack200.Pack200CompressorInputStream
|
||||
import org.apache.commons.compress.compressors.pack200.Pack200CompressorOutputStream
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.plugins.JavaBasePlugin
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.gradle.api.tasks.compile.GroovyCompile
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.*
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Downloads JDKs and configures the bootstrap classpath when cross-compiling java projects
|
||||
*/
|
||||
class PrepareJdk : Plugin<Project> {
|
||||
|
||||
companion object {
|
||||
data class JDK(val file: File, val checksum: String)
|
||||
|
||||
private val versionInfo = HashMap<JavaVersion, JDK>()
|
||||
|
||||
private const val compressSuffix = ".pack.lzma"
|
||||
private const val jarSuffix = ".jar"
|
||||
|
||||
val logger: Logger = LoggerFactory.getLogger(PrepareJdk::class.java)
|
||||
}
|
||||
|
||||
|
||||
override fun apply(project: Project) {
|
||||
project.plugins.apply(JavaBasePlugin::class.java)
|
||||
|
||||
project.afterEvaluate {
|
||||
// don't waste time if this is not a java project
|
||||
val convention: JavaPluginConvention? = project.convention.plugins["java"] as? JavaPluginConvention
|
||||
var needsJdk = false
|
||||
if (convention != null) {
|
||||
// check if we need to extract anything..
|
||||
|
||||
if (convention.targetCompatibility != JavaVersion.current()) {
|
||||
needsJdk = true
|
||||
}
|
||||
|
||||
project.tasks.forEach {
|
||||
if (!needsJdk && it is JavaCompile) {
|
||||
if (JavaVersion.toVersion(it.targetCompatibility) != JavaVersion.current()) {
|
||||
needsJdk = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsJdk) {
|
||||
// if there is a clean task (usually the first thing to run, if run), run after the clean task, otherwise run first.
|
||||
|
||||
val hasClean = project.gradle.startParameter.taskNames.filter { it.toLowerCase().contains("clean") }
|
||||
if (hasClean.isNotEmpty()) {
|
||||
val task = project.tasks.last { it.name == hasClean.last() }
|
||||
|
||||
task.doLast {
|
||||
setupDownload(project)
|
||||
}
|
||||
}
|
||||
else {
|
||||
setupDownload(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupDownload(project: Project) {
|
||||
downloadAndExtractJdk(project)
|
||||
|
||||
project.tasks.forEach {
|
||||
if (it is AbstractCompile) {
|
||||
if (JavaVersion.toVersion(it.targetCompatibility) != JavaVersion.current()) {
|
||||
// only supports JAVA and GROOVY
|
||||
configureTaskBootstrapClassPath(project, it, JavaVersion.toVersion(it.targetCompatibility))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadAndExtractJdk(project: Project) {
|
||||
val outputDir = File(project.buildDir, "jdkRuntimes")
|
||||
if (!outputDir.exists()) outputDir.mkdirs()
|
||||
|
||||
logger.info("Preparing cross-compile environment")
|
||||
|
||||
// download the JDK runtimes from github
|
||||
val prefix = "https://github.com/dorkbox/JavaBuilder/raw/master/jdkRuntimes"
|
||||
|
||||
versionInfo[JavaVersion.VERSION_1_6] = JDK(File(outputDir, "openJdk6_rt.jar.pack.lzma").absoluteFile, "313a8b3fe4736520f7a4b6de37f1c80698502ee7")
|
||||
versionInfo[JavaVersion.VERSION_1_7] = JDK(File(outputDir, "openJdk7_rt.jar.pack.lzma").absoluteFile, "b42aa62d1772d1f2e8f93664c1e8cb866374e511")
|
||||
versionInfo[JavaVersion.VERSION_1_8] = JDK(File(outputDir, "openJdk8_rt.jar.pack.lzma").absoluteFile, "98616c3fc020750dce84f02bc65e5f66b839b29d")
|
||||
|
||||
// if we are offline, DO NOT try to download anything
|
||||
if (!project.gradle.startParameter.isOffline) {
|
||||
// download JDKS we don't know about
|
||||
val downloadJdkList = getDownloadJdkList(prefix)
|
||||
val elements = versionInfo.map { it.value.file.name }
|
||||
|
||||
downloadJdkList.removeAll(elements)
|
||||
|
||||
for (jdk in downloadJdkList) {
|
||||
try {
|
||||
val version = JavaVersion.toVersion(jdk.substring(7, jdk.indexOf('_')))
|
||||
versionInfo[version] = JDK(File(outputDir, jdk).absoluteFile, "")
|
||||
} catch (e: Exception) {
|
||||
logger.error("Unable to parse/download $jdk")
|
||||
}
|
||||
}
|
||||
|
||||
// download all JDKS ...
|
||||
for (jdk in versionInfo.values) {
|
||||
downloadJDK(project, prefix, jdk.file, jdk.checksum)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val jarFiles = getFiles(outputDir, jarSuffix)
|
||||
val compressedFiles = getFiles(outputDir, compressSuffix)
|
||||
var hasFiles = false
|
||||
|
||||
|
||||
// discover which files need compressing
|
||||
val iterator = jarFiles.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val jarFile = iterator.next()
|
||||
logger.debug("JarFile $jarFile")
|
||||
hasFiles = true
|
||||
|
||||
val file = getCompressedFile(jarFile)
|
||||
|
||||
// Don't always need to compress the jdk files. This checks if the compressed version exists
|
||||
if (file.canRead() && file.length() > 0) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// discover which files need un-compressing
|
||||
val iterator2 = compressedFiles.iterator()
|
||||
while (iterator2.hasNext()) {
|
||||
val compressedFile = iterator2.next()
|
||||
logger.debug("CompressedFile $compressedFile")
|
||||
hasFiles = true
|
||||
|
||||
val file = getUncompressedFile(compressedFile)
|
||||
|
||||
// Don't always need to decompress the jdk files. This checks if the extracted version exists
|
||||
if (file.canRead() && file.length() > 0) {
|
||||
iterator2.remove()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!hasFiles) {
|
||||
throw GradleException("Unable to find or extract jar files, none were found in $outputDir")
|
||||
}
|
||||
|
||||
if (!compressedFiles.isEmpty() || !jarFiles.isEmpty()) {
|
||||
logger.info("Preparing cross compile environment")
|
||||
}
|
||||
|
||||
|
||||
// NOTE: we also compress files so we can automatically create NEW files if we need to
|
||||
for (inputFile in jarFiles) {
|
||||
// pack200 + LZMA
|
||||
logger.debug("Compressing $inputFile")
|
||||
|
||||
|
||||
var inputStream: InputStream? = null
|
||||
var outputStream: OutputStream? = null
|
||||
try {
|
||||
inputStream = FileInputStream(inputFile)
|
||||
inputStream = BufferedInputStream(inputStream)
|
||||
|
||||
outputStream = FileOutputStream(getCompressedFile(inputFile))
|
||||
outputStream = BufferedOutputStream(outputStream)
|
||||
|
||||
// now pack and compress
|
||||
outputStream = LZMACompressorOutputStream(outputStream)
|
||||
outputStream = Pack200CompressorOutputStream(outputStream)
|
||||
|
||||
inputStream.copyTo(outputStream)
|
||||
} catch (e: Exception) {
|
||||
logger.error("Error compressing files", e)
|
||||
} finally {
|
||||
close(outputStream)
|
||||
close(inputStream)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (inputFile in compressedFiles) {
|
||||
// unLZMA + unpack200
|
||||
logger.debug("Extracting $inputFile")
|
||||
|
||||
var inputStream: InputStream? = null
|
||||
var outputStream: OutputStream? = null
|
||||
try {
|
||||
inputStream = FileInputStream(inputFile)
|
||||
inputStream = BufferedInputStream(inputStream)
|
||||
|
||||
outputStream = FileOutputStream(getUncompressedFile(inputFile))
|
||||
outputStream = BufferedOutputStream(outputStream)
|
||||
|
||||
// now uncompress and unpack
|
||||
inputStream = LZMACompressorInputStream(inputStream)
|
||||
inputStream = Pack200CompressorInputStream(inputStream)
|
||||
|
||||
inputStream.copyTo(outputStream)
|
||||
} catch (e: Exception) {
|
||||
logger.error("Error extracting files", e)
|
||||
} finally {
|
||||
close(outputStream)
|
||||
close(inputStream)
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Done preparing cross-compile environment")
|
||||
}
|
||||
|
||||
private fun getDownloadJdkList(prefix: String): ArrayList<String> {
|
||||
val jdkRuntimes = ArrayList<String>()
|
||||
|
||||
with(URL(prefix).openConnection() as HttpURLConnection) {
|
||||
// optional default is GET
|
||||
requestMethod = "GET"
|
||||
|
||||
logger.debug("Sending 'GET' request to URL : $url")
|
||||
logger.debug("Response Code : $responseCode")
|
||||
|
||||
|
||||
BufferedReader(InputStreamReader(inputStream)).use {
|
||||
val jdkDirName = "jdkRuntimes"
|
||||
val lzmaExtension = ".jar.pack.lzma"
|
||||
|
||||
var inputLine = it.readLine()
|
||||
while (inputLine != null) {
|
||||
val indexJdk = inputLine.lastIndexOf(jdkDirName)
|
||||
if (indexJdk > -1) {
|
||||
val startIndex = indexJdk + jdkDirName.length + 1
|
||||
val indexLzma = inputLine.indexOf(lzmaExtension, startIndex, false)
|
||||
if (indexLzma > -1) {
|
||||
val message = inputLine.substring(startIndex, indexLzma + lzmaExtension.length)
|
||||
jdkRuntimes.add(message)
|
||||
logger.debug(message)
|
||||
}
|
||||
}
|
||||
|
||||
inputLine = it.readLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return jdkRuntimes
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun configureTaskBootstrapClassPath(project: Project, task: Task, targetVersion: JavaVersion) {
|
||||
val location = versionInfo[targetVersion]?.file
|
||||
|
||||
if (location != null) {
|
||||
val file = getUncompressedFile(location)
|
||||
|
||||
val bootstrapClasspath = project.files(file)
|
||||
val bootClasspath = bootstrapClasspath.joinToString(File.pathSeparator)
|
||||
|
||||
|
||||
if (task is JavaCompile) {
|
||||
logger.debug("Configuring task ${task.name} with $bootClasspath")
|
||||
|
||||
if (project.gradle.versionGreaterThan("4.2.1")) {
|
||||
task.options.bootstrapClasspath = bootstrapClasspath
|
||||
}
|
||||
else {
|
||||
task.options.bootClasspath = bootClasspath
|
||||
}
|
||||
}
|
||||
else if (task is GroovyCompile) {
|
||||
logger.debug("Configuring task ${task.name} with $bootClasspath")
|
||||
|
||||
if (project.gradle.versionGreaterThan("4.2.1")) {
|
||||
task.options.bootstrapClasspath = bootstrapClasspath
|
||||
}
|
||||
else {
|
||||
task.options.bootClasspath = bootClasspath
|
||||
}
|
||||
}
|
||||
// project.plugins.withId("kotlin") {
|
||||
// logger.debug("Configuring task ${task.name} with $bootClasspath ?????")
|
||||
//
|
||||
// withType(KotlinCompile::class.java) {
|
||||
// it.kotlinOptions.jdkHome = file.jdkHome
|
||||
// }
|
||||
// }
|
||||
}
|
||||
else {
|
||||
logger.error("Unable to determine bootstrap path $targetVersion for ${task.name}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun getFiles(directory: File, suffix: String): MutableList<File> {
|
||||
val outputFiles = ArrayList<File>()
|
||||
|
||||
if (directory.isDirectory) {
|
||||
val files = directory.listFiles()
|
||||
for (file in files) {
|
||||
val name = file.name
|
||||
|
||||
if (name.endsWith(suffix)) {
|
||||
outputFiles.add(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outputFiles
|
||||
}
|
||||
|
||||
private fun close(inputStream: InputStream?) {
|
||||
try {
|
||||
inputStream?.close()
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun close(outputStream: OutputStream?) {
|
||||
try {
|
||||
outputStream?.close()
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUncompressedFile(compressedFile: File): File {
|
||||
val nameLength = compressedFile.name.length
|
||||
val fixedName = compressedFile.name.substring(0, nameLength - compressSuffix.length)
|
||||
|
||||
return File(compressedFile.parentFile, fixedName)
|
||||
}
|
||||
|
||||
private fun getCompressedFile(jarFile: File): File {
|
||||
return File(jarFile.parentFile, jarFile.name + compressSuffix)
|
||||
}
|
||||
|
||||
|
||||
private fun Gradle.versionGreaterThan(version: String): Boolean = versionCompareTo(version) > 0
|
||||
private fun Gradle.versionCompareTo(version: String): Int {
|
||||
return GradleVersion.version(gradleVersion).compareTo(GradleVersion.version(version))
|
||||
}
|
||||
|
||||
/**
|
||||
* checksum can be empty string to do a basic "does this file exist" check to determine if the file needs downloading
|
||||
* Download, if necessary, the specified JDK
|
||||
*/
|
||||
private fun downloadJDK(project: Project, url: String, file: File, sha1Checksum: String) {
|
||||
var valid = false
|
||||
val fileName = file.name
|
||||
|
||||
if (!sha1Checksum.isEmpty()) {
|
||||
val verify = project.tasks.create("verify$fileName", Verify::class.java)
|
||||
verify.src(file)
|
||||
verify.algorithm("SHA1")
|
||||
verify.checksum(sha1Checksum)
|
||||
|
||||
try {
|
||||
verify.verify()
|
||||
valid = true
|
||||
} catch (ignored: Exception) {
|
||||
// verify throws an exception if it cannot verify the file.
|
||||
}
|
||||
}
|
||||
else {
|
||||
// lame way to verify, but tnable to find or extract jar fihere is no checksum...
|
||||
valid = file.canRead() && file.length() > 0
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
val download = project.tasks.create("download$fileName", Download::class.java)
|
||||
|
||||
download.src("$url/$fileName")
|
||||
download.dest(file)
|
||||
download.quiet(true)
|
||||
download.overwrite(false)
|
||||
|
||||
try {
|
||||
download.download()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Unable to download $url", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue