[Gradle, Wasm] Standartize binaryen and d8 download instead of download task

^KT-65686 fixed
This commit is contained in:
Ilya Goncharov
2024-02-09 11:18:37 +00:00
committed by Space Team
parent 5bd0bbb159
commit b4e1fa8569
31 changed files with 308 additions and 177 deletions
-6
View File
@@ -1506,12 +1506,6 @@
<sha256 value="61fc639d7c60d80f523c76867b4d83ebb54a4740e8ca1d9c61216668ceb25988" origin="Generated by Gradle"/> <sha256 value="61fc639d7c60d80f523c76867b4d83ebb54a4740e8ca1d9c61216668ceb25988" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="de.undercouch" name="gradle-download-task" version="4.1.1">
<artifact name="gradle-download-task-4.1.1.jar">
<md5 value="5da00a4a96829700216768887a8f3051" origin="Generated by Gradle"/>
<sha256 value="eb08b570e408d4646705e70a949614d439ea2b11455f1652ab0330de8954dab9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="dev.gradleplugins" name="gradle-api" version="6.9"> <component group="dev.gradleplugins" name="gradle-api" version="6.9">
<artifact name="gradle-api-6.9.jar"> <artifact name="gradle-api-6.9.jar">
<md5 value="54b9007742dec27e80c6623d11e57ed4" origin="Generated by Gradle"/> <md5 value="54b9007742dec27e80c6623d11e57ed4" origin="Generated by Gradle"/>
@@ -19,11 +19,7 @@ class KotlinWasmGradlePluginIT : KGPBaseTest() {
@DisplayName("Check wasi target") @DisplayName("Check wasi target")
@GradleTest @GradleTest
fun wasiTarget(gradleVersion: GradleVersion) { fun wasiTarget(gradleVersion: GradleVersion) {
project( project("new-mpp-wasm-wasi-test", gradleVersion) {
"new-mpp-wasm-wasi-test",
gradleVersion,
dependencyManagement = DependencyManagement.DisabledDependencyManagement // requires custom d8 repository during build
) {
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
build(":wasmWasiTest") { build(":wasmWasiTest") {
@@ -57,15 +53,10 @@ class KotlinWasmGradlePluginIT : KGPBaseTest() {
@DisplayName("Check js target") @DisplayName("Check js target")
@GradleTest @GradleTest
fun jsTarget(gradleVersion: GradleVersion) { fun jsTarget(gradleVersion: GradleVersion) {
project( project("new-mpp-wasm-test", gradleVersion) {
"new-mpp-wasm-test",
gradleVersion,
dependencyManagement = DependencyManagement.DisabledDependencyManagement // requires custom repository for :kotlinNodeJsSetup during build
) {
buildGradleKts.modify { buildGradleKts.modify {
transformBuildScriptWithPluginsDsl(it) transformBuildScriptWithPluginsDsl(it)
.replace("<JsEngine>", "nodejs") .replace("<JsEngine>", "nodejs")
.replace("<ApplyBinaryen>", "")
} }
kotlinSourcesDir("wasmJsTest").resolve("Test.kt").writeText( kotlinSourcesDir("wasmJsTest").resolve("Test.kt").writeText(
@@ -101,10 +92,7 @@ class KotlinWasmGradlePluginIT : KGPBaseTest() {
@DisplayName("Check wasi target run") @DisplayName("Check wasi target run")
@GradleTest @GradleTest
fun wasiRun(gradleVersion: GradleVersion) { fun wasiRun(gradleVersion: GradleVersion) {
project( project("new-mpp-wasm-wasi-test", gradleVersion) {
"new-mpp-wasm-wasi-test", gradleVersion,
dependencyManagement = DependencyManagement.DisabledDependencyManagement // requires d8 custom repository during build
) {
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
build(":wasmWasiNodeRun") { build(":wasmWasiNodeRun") {
@@ -1669,22 +1669,18 @@ open class NewMultiplatformIT : BaseGradleIT() {
} }
} }
private fun testWasmTest(engine: String, name: String, useBinaryen: Boolean) = with( private fun testWasmTest(engine: String, name: String) = with(
Project("new-mpp-wasm-test", gradleVersionRequirement = GradleVersionRequired.AtLeast(TestVersions.Gradle.G_7_0)) Project("new-mpp-wasm-test", gradleVersionRequirement = GradleVersionRequired.AtLeast(TestVersions.Gradle.G_7_0))
) { ) {
setupWorkingDir() setupWorkingDir()
gradleBuildScript().modify { gradleBuildScript().modify {
transformBuildScriptWithPluginsDsl(it) transformBuildScriptWithPluginsDsl(it)
.replace("<JsEngine>", engine) .replace("<JsEngine>", engine)
.replace("<ApplyBinaryen>", if (useBinaryen) "applyBinaryen()" else "")
} }
build(":wasmJs${name}Test") { build(":wasmJs${name}Test") {
assertTasksExecuted(":compileKotlinWasmJs") assertTasksExecuted(":compileKotlinWasmJs")
if (useBinaryen) {
assertTasksExecuted(":compileTestDevelopmentExecutableKotlinWasmJsOptimize") assertTasksNotExecuted(":compileTestDevelopmentExecutableKotlinWasmJsOptimize")
} else {
assertTasksNotExecuted(":compileTestDevelopmentExecutableKotlinWasmJsOptimize")
}
assertTasksFailed(":wasmJs${name}Test") assertTasksFailed(":wasmJs${name}Test")
assertTestResults( assertTestResults(
"testProject/new-mpp-wasm-test/TEST-${engine}.xml", "testProject/new-mpp-wasm-test/TEST-${engine}.xml",
@@ -1694,16 +1690,10 @@ open class NewMultiplatformIT : BaseGradleIT() {
} }
@Test @Test
fun testWasmNodeTest() = testWasmTest("nodejs", "Node", useBinaryen = false) fun testWasmNodeTest() = testWasmTest("nodejs", "Node")
@Test @Test
fun testWasmWithBinaryenNodeTest() = testWasmTest("nodejs", "Node", useBinaryen = true) fun testWasmD8Test() = testWasmTest("d8", "D8")
@Test
fun testWasmD8Test() = testWasmTest("d8", "D8", useBinaryen = false)
@Test
fun testWasmWithBinaryenD8Test() = testWasmTest("d8", "D8", useBinaryen = true)
@Test @Test
fun testResolveMetadataCompileClasspathKt50925() { fun testResolveMetadataCompileClasspathKt50925() {
@@ -118,6 +118,45 @@ internal fun getGroovyDependencyManagementBlock(
includeModule("org.nodejs", "node") includeModule("org.nodejs", "node")
} }
} }
ivy {
url = uri("https://nodejs.org/download/v8-canary")
patternLayout {
artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]")
}
metadataSources {
artifact()
}
content {
includeModule("org.nodejs", "node")
}
}
ivy {
url = uri("https://github.com/WebAssembly/binaryen/releases/download")
patternLayout {
artifact("version_[revision]/binaryen-version_[revision]-[classifier].[ext]")
}
metadataSources {
artifact()
}
content {
includeModule("com.github.webassembly", "binaryen")
}
}
ivy {
url = uri("https://storage.googleapis.com/chromium-v8/official/canary")
patternLayout {
artifact("[artifact]-[revision].[ext]")
}
metadataSources {
artifact()
}
content {
includeModule("google.d8", "v8")
}
}
${additionalDependencyRepositories.map { repo -> "maven{ url = \"$repo\" }" }.joinToString("\n")} ${additionalDependencyRepositories.map { repo -> "maven{ url = \"$repo\" }" }.joinToString("\n")}
${localRepo?.absolutePathString()?.let { repo -> "maven{ url = \"${repo.replace("\\", "\\\\")}\" }" } ?: ""} ${localRepo?.absolutePathString()?.let { repo -> "maven{ url = \"${repo.replace("\\", "\\\\")}\" }" } ?: ""}
} }
@@ -232,6 +271,45 @@ internal fun getKotlinDependencyManagementBlock(
includeModule("org.nodejs", "node") includeModule("org.nodejs", "node")
} }
} }
ivy {
url = uri("https://nodejs.org/download/v8-canary")
patternLayout {
artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]")
}
metadataSources {
artifact()
}
content {
includeModule("org.nodejs", "node")
}
}
ivy {
url = uri("https://github.com/WebAssembly/binaryen/releases/download")
patternLayout {
artifact("version_[revision]/binaryen-version_[revision]-[classifier].[ext]")
}
metadataSources {
artifact()
}
content {
includeModule("com.github.webassembly", "binaryen")
}
}
ivy {
url = uri("https://storage.googleapis.com/chromium-v8/official/canary")
patternLayout {
artifact("[artifact]-[revision].[ext]")
}
metadataSources {
artifact()
}
content {
includeModule("google.d8", "v8")
}
}
maven { maven {
url = uri("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies/") url = uri("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies/")
} }
@@ -39,7 +39,7 @@ rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJ
tasks.register("runWebpack", Exec) { tasks.register("runWebpack", Exec) {
dependsOn(tasks.named('browserProductionWebpack')) dependsOn(tasks.named('browserProductionWebpack'))
executable(kotlinNodeJs.requireConfigured().nodeExecutable) executable(kotlinNodeJs.requireConfigured().executable)
workingDir = "${rootProject.buildDir}/js/packages/kotlin-js-dce-mainProject" workingDir = "${rootProject.buildDir}/js/packages/kotlin-js-dce-mainProject"
args = ["../../node_modules/webpack/bin/webpack.js"] args = ["../../node_modules/webpack/bin/webpack.js"]
@@ -39,7 +39,6 @@ kotlin {
} }
} }
} }
<ApplyBinaryen>
} }
sourceSets { sourceSets {
@@ -70,7 +70,6 @@ dependencies {
commonCompileOnly(commonDependency("org.jetbrains.teamcity:serviceMessages")) commonCompileOnly(commonDependency("org.jetbrains.teamcity:serviceMessages"))
commonCompileOnly(libs.gradle.enterprise.gradlePlugin) commonCompileOnly(libs.gradle.enterprise.gradlePlugin)
commonCompileOnly(commonDependency("com.google.code.gson:gson")) commonCompileOnly(commonDependency("com.google.code.gson:gson"))
commonCompileOnly("de.undercouch:gradle-download-task:4.1.1")
commonCompileOnly("com.github.gundy:semver4j:0.16.4:nodeps") { commonCompileOnly("com.github.gundy:semver4j:0.16.4:nodeps") {
exclude(group = "*") exclude(group = "*")
} }
@@ -99,7 +98,6 @@ dependencies {
embedded(libs.guava) { isTransitive = false } embedded(libs.guava) { isTransitive = false }
embedded(commonDependency("org.jetbrains.teamcity:serviceMessages")) { isTransitive = false } embedded(commonDependency("org.jetbrains.teamcity:serviceMessages")) { isTransitive = false }
embedded(project(":kotlin-tooling-metadata")) { isTransitive = false } embedded(project(":kotlin-tooling-metadata")) { isTransitive = false }
embedded("de.undercouch:gradle-download-task:4.1.1")
embedded("com.github.gundy:semver4j:0.16.4:nodeps") { embedded("com.github.gundy:semver4j:0.16.4:nodeps") {
exclude(group = "*") exclude(group = "*")
} }
@@ -148,13 +146,6 @@ tasks {
withType<ShadowJar>().configureEach { withType<ShadowJar>().configureEach {
relocate("com.github.gundy", "$kotlinEmbeddableRootPackage.com.github.gundy") relocate("com.github.gundy", "$kotlinEmbeddableRootPackage.com.github.gundy")
relocate("de.undercouch.gradle.tasks.download", "$kotlinEmbeddableRootPackage.de.undercouch.gradle.tasks.download")
// don't expose external Gradle plugin marker
// workaround from https://github.com/johnrengelman/shadow/issues/505#issuecomment-644098082
transform(DontIncludeResourceTransformer::class.java) {
resource = "META-INF/gradle-plugins/de.undercouch.download.properties"
}
} }
} }
@@ -1,5 +1,6 @@
package org.jetbrains.kotlin.gradle.targets.js package org.jetbrains.kotlin.gradle.targets.js
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.File import java.io.File
interface AbstractEnv { interface AbstractEnv {
@@ -11,4 +12,8 @@ interface AbstractEnv {
val ivyDependency: String val ivyDependency: String
val dir: File val dir: File
val executable: String
val cleanableStore: CleanableStore
} }
@@ -1,6 +1,5 @@
package org.jetbrains.kotlin.gradle.targets.js package org.jetbrains.kotlin.gradle.targets.js
import org.gradle.work.DisableCachingByDefault
import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware
import java.io.File import java.io.File
@@ -131,7 +131,7 @@ abstract class AbstractSetupTask<Env : AbstractEnv, Settings : AbstractSettings<
destinationHashFile: File, destinationHashFile: File,
dist: File, dist: File,
fileHasher: FileHasher, fileHasher: FileHasher,
extract: (File, File) -> Unit, extract: (File) -> Unit,
) { ) {
var distHash: String? = null var distHash: String? = null
val upToDate = destinationHashFile.let { file -> val upToDate = destinationHashFile.let { file ->
@@ -154,7 +154,7 @@ abstract class AbstractSetupTask<Env : AbstractEnv, Settings : AbstractSettings<
destination.deleteRecursively() destination.deleteRecursively()
} }
extract(dist, destination.parentFile) extract(dist)
destinationHashFile.writeText( destinationHashFile.writeText(
CACHE_VERSION + CACHE_VERSION +
@@ -165,7 +165,7 @@ abstract class AbstractSetupTask<Env : AbstractEnv, Settings : AbstractSettings<
) )
} }
abstract fun extract(archive: File, destination: File) abstract fun extract(archive: File)
companion object { companion object {
const val CACHE_VERSION = "2" const val CACHE_VERSION = "2"
@@ -1,14 +1,18 @@
package org.jetbrains.kotlin.gradle.targets.js.binaryen package org.jetbrains.kotlin.gradle.targets.js.binaryen
import org.jetbrains.kotlin.gradle.targets.js.AbstractEnv
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.File import java.io.File
import java.net.URL
data class BinaryenEnv( data class BinaryenEnv(
val cleanableStore: CleanableStore, override val download: Boolean,
val zipPath: File, override val downloadBaseUrl: String?,
val targetPath: File, override val ivyDependency: String,
val executablePath: File, override val executable: String,
override val dir: File,
override val cleanableStore: CleanableStore,
val isWindows: Boolean, val isWindows: Boolean,
val downloadUrl: URL ) : AbstractEnv {
) val executablePath: File
get() = File(executable)
}
@@ -20,10 +20,6 @@ import javax.inject.Inject
abstract class BinaryenExec abstract class BinaryenExec
@Inject @Inject
constructor() : AbstractExecTask<BinaryenExec>(BinaryenExec::class.java) { constructor() : AbstractExecTask<BinaryenExec>(BinaryenExec::class.java) {
@Transient
@get:Internal
lateinit var binaryen: BinaryenRootExtension
@get:Inject @get:Inject
abstract val fs: FileSystemOperations abstract val fs: FileSystemOperations
@@ -68,8 +64,7 @@ constructor() : AbstractExecTask<BinaryenExec>(BinaryenExec::class.java) {
return project.registerTask( return project.registerTask(
name, name,
) { ) {
it.binaryen = binaryen it.executable = binaryen.requireConfigured().executable
it.executable = binaryen.requireConfigured().executablePath.absolutePath
it.dependsOn(binaryen.setupTaskProvider) it.dependsOn(binaryen.setupTaskProvider)
it.dependsOn(compilation.compileTaskProvider) it.dependsOn(compilation.compileTaskProvider)
it.configuration() it.configuration()
@@ -6,15 +6,14 @@
package org.jetbrains.kotlin.gradle.targets.js.binaryen package org.jetbrains.kotlin.gradle.targets.js.binaryen
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware
import org.jetbrains.kotlin.gradle.logging.kotlinInfo import org.jetbrains.kotlin.gradle.logging.kotlinInfo
import org.jetbrains.kotlin.gradle.targets.js.AbstractSettings
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.Serializable
import java.net.URL
open class BinaryenRootExtension(@Transient val rootProject: Project) : ConfigurationPhaseAware<BinaryenEnv>(), Serializable { open class BinaryenRootExtension(
@Transient val rootProject: Project
) : AbstractSettings<BinaryenEnv>() {
init { init {
check(rootProject.rootProject == rootProject) check(rootProject.rootProject == rootProject)
} }
@@ -23,30 +22,41 @@ open class BinaryenRootExtension(@Transient val rootProject: Project) : Configur
rootProject.logger.kotlinInfo("Storing cached files in $it") rootProject.logger.kotlinInfo("Storing cached files in $it")
} }
var installationPath by Property(gradleHome.resolve("binaryen")) override var installationDir by Property(gradleHome.resolve("binaryen"))
var downloadBaseUrl by Property("https://github.com/WebAssembly/binaryen/releases/download/") override var downloadBaseUrl: String? by Property("https://github.com/WebAssembly/binaryen/releases/download")
var version by Property("116") override var version: String by Property("116")
override var download: Boolean by Property(true)
override var command: String by Property("wasm-opt")
val setupTaskProvider: TaskProvider<out Copy> val setupTaskProvider: TaskProvider<BinaryenSetupTask>
get() = rootProject.tasks.withType(Copy::class.java).named(BinaryenRootPlugin.INSTALL_TASK_NAME) get() = rootProject.tasks.withType(BinaryenSetupTask::class.java).named(BinaryenSetupTask.NAME)
override fun finalizeConfiguration(): BinaryenEnv { override fun finalizeConfiguration(): BinaryenEnv {
val requiredVersionName = "binaryen-version_$version-${BinaryenPlatform.platform}" val platform = BinaryenPlatform.platform
val requiredZipName = "$requiredVersionName.tar.gz" val requiredVersionName = "binaryen-version_$version"
val cleanableStore = CleanableStore[installationPath.absolutePath] val cleanableStore = CleanableStore[installationDir.absolutePath]
val targetPath = cleanableStore[requiredVersionName].use() val targetPath = cleanableStore[requiredVersionName].use()
val isWindows = BinaryenPlatform.name == BinaryenPlatform.WIN val isWindows = BinaryenPlatform.name == BinaryenPlatform.WIN
fun getExecutable(command: String, customCommand: String, windowsExtension: String): String {
val finalCommand = if (isWindows && customCommand == command) "$command.$windowsExtension" else customCommand
return if (download)
targetPath
.resolve("bin")
.resolve(finalCommand)
.absolutePath
else
finalCommand
}
return BinaryenEnv( return BinaryenEnv(
download = download,
downloadBaseUrl = downloadBaseUrl,
ivyDependency = "com.github.webassembly:binaryen:$version:$platform@tar.gz",
executable = getExecutable("wasm-opt", command, "exe"),
dir = targetPath,
cleanableStore = cleanableStore, cleanableStore = cleanableStore,
zipPath = cleanableStore[requiredZipName].use(),
targetPath = targetPath,
executablePath = targetPath
.resolve("binaryen-version_$version")
.resolve("bin")
.resolve(if (isWindows) "wasm-opt.exe" else "wasm-opt"),
isWindows = isWindows, isWindows = isWindows,
downloadUrl = URL("${downloadBaseUrl.trimEnd('/')}/version_$version/$requiredZipName"),
) )
} }
@@ -5,15 +5,14 @@
package org.jetbrains.kotlin.gradle.targets.js.binaryen package org.jetbrains.kotlin.gradle.targets.js.binaryen
import de.undercouch.gradle.tasks.download.Download
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.plugins.BasePlugin import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Copy
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenRootExtension.Companion.EXTENSION_NAME import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenRootExtension.Companion.EXTENSION_NAME
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.castIsolatedKotlinPluginClassLoaderAware
open class BinaryenRootPlugin : Plugin<Project> { open class BinaryenRootPlugin : Plugin<Project> {
override fun apply(project: Project) { override fun apply(project: Project) {
@@ -27,23 +26,13 @@ open class BinaryenRootPlugin : Plugin<Project> {
val settings = project.extensions.create(EXTENSION_NAME, BinaryenRootExtension::class.java, project) val settings = project.extensions.create(EXTENSION_NAME, BinaryenRootExtension::class.java, project)
val downloadTask = project.registerTask<Download>("${TASKS_GROUP_NAME}Download") { project.registerTask<BinaryenSetupTask>(BinaryenSetupTask.NAME) {
val env = settings.requireConfigured()
it.group = TASKS_GROUP_NAME it.group = TASKS_GROUP_NAME
it.src(env.downloadUrl) it.description = "Download and install a binaryen"
it.dest(env.zipPath) it.configuration = project.provider {
it.overwrite(false) project.configurations.detachedConfiguration(project.dependencies.create(it.ivyDependency))
it.description = "Download local binaryen version" .also { conf -> conf.isTransitive = false }
} }
project.registerTask<Copy>(INSTALL_TASK_NAME) {
val env = settings.requireConfigured()
it.onlyIf { env.zipPath.exists() && !env.executablePath.exists() }
it.group = TASKS_GROUP_NAME
it.from(project.tarTree(env.zipPath))
it.into(env.targetPath)
it.dependsOn(downloadTask)
it.description = "Install local binaryen version"
} }
project.registerTask<CleanDataTask>("binaryen" + CleanDataTask.NAME_SUFFIX) { project.registerTask<CleanDataTask>("binaryen" + CleanDataTask.NAME_SUFFIX) {
@@ -55,12 +44,14 @@ open class BinaryenRootPlugin : Plugin<Project> {
companion object { companion object {
const val TASKS_GROUP_NAME: String = "binaryen" const val TASKS_GROUP_NAME: String = "binaryen"
const val INSTALL_TASK_NAME: String = "${TASKS_GROUP_NAME}Install"
fun apply(rootProject: Project): BinaryenRootExtension { fun apply(rootProject: Project): BinaryenRootExtension {
check(rootProject == rootProject.rootProject) check(rootProject == rootProject.rootProject)
rootProject.plugins.apply(BinaryenRootPlugin::class.java) rootProject.plugins.apply(BinaryenRootPlugin::class.java)
return rootProject.extensions.getByName(EXTENSION_NAME) as BinaryenRootExtension return rootProject.extensions.getByName(EXTENSION_NAME) as BinaryenRootExtension
} }
val Project.kotlinBinaryenExtension: BinaryenRootExtension
get() = extensions.getByName(EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()
} }
} }
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.targets.js.binaryen
import org.gradle.api.tasks.Internal
import org.gradle.work.DisableCachingByDefault
import org.jetbrains.kotlin.gradle.targets.js.AbstractSetupTask
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenRootPlugin.Companion.kotlinBinaryenExtension
import java.io.File
@DisableCachingByDefault
abstract class BinaryenSetupTask : AbstractSetupTask<BinaryenEnv, BinaryenRootExtension>() {
@Transient
@Internal
override val settings = project.kotlinBinaryenExtension
@get:Internal
override val artifactPattern: String
get() = "version_[revision]/binaryen-version_[revision]-[classifier].[ext]"
@get:Internal
override val artifactModule: String
get() = "com.github.webassembly"
@get:Internal
override val artifactName: String
get() = "binaryen"
override fun extract(archive: File) {
fs.copy {
it.from(archiveOperations.tarTree(archive))
it.into(destination.parentFile)
}
if (!env.isWindows) {
File(env.executable).setExecutable(true)
}
}
companion object {
const val NAME: String = "kotlinBinaryenSetup"
}
}
@@ -1,15 +1,18 @@
package org.jetbrains.kotlin.gradle.targets.js.d8 package org.jetbrains.kotlin.gradle.targets.js.d8
import org.jetbrains.kotlin.gradle.targets.js.AbstractEnv
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.File import java.io.File
import java.net.URL
data class D8Env( data class D8Env(
val cleanableStore: CleanableStore, override val download: Boolean,
val zipPath: File, override val downloadBaseUrl: String?,
val targetPath: File, override val ivyDependency: String,
val executablePath: File, override val executable: String,
override val dir: File,
override val cleanableStore: CleanableStore,
val isWindows: Boolean, val isWindows: Boolean,
val downloadUrl: URL, ) : AbstractEnv {
val ivyDependency: String val executablePath: File
) get() = File(executable)
}
@@ -63,7 +63,7 @@ open class D8Exec : AbstractExecTask<D8Exec>(D8Exec::class.java) {
return project.registerTask( return project.registerTask(
name name
) { ) {
it.executable = d8.requireConfigured().executablePath.absolutePath it.executable = d8.requireConfigured().executable
it.dependsOn(d8.setupTaskProvider) it.dependsOn(d8.setupTaskProvider)
it.dependsOn(compilation.compileTaskProvider) it.dependsOn(compilation.compileTaskProvider)
if (compilation.platformType == KotlinPlatformType.wasm) { if (compilation.platformType == KotlinPlatformType.wasm) {
@@ -6,15 +6,12 @@
package org.jetbrains.kotlin.gradle.targets.js.d8 package org.jetbrains.kotlin.gradle.targets.js.d8
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware
import org.jetbrains.kotlin.gradle.logging.kotlinInfo import org.jetbrains.kotlin.gradle.logging.kotlinInfo
import org.jetbrains.kotlin.gradle.targets.js.AbstractSettings
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.Serializable
import java.net.URL
open class D8RootExtension(@Transient val rootProject: Project) : ConfigurationPhaseAware<D8Env>(), Serializable { open class D8RootExtension(@Transient val rootProject: Project) : AbstractSettings<D8Env>() {
init { init {
check(rootProject.rootProject == rootProject) check(rootProject.rootProject == rootProject)
} }
@@ -23,8 +20,9 @@ open class D8RootExtension(@Transient val rootProject: Project) : ConfigurationP
rootProject.logger.kotlinInfo("Storing cached files in $it") rootProject.logger.kotlinInfo("Storing cached files in $it")
} }
var installationPath by Property(gradleHome.resolve("d8")) override var download: Boolean by Property(true)
var downloadBaseUrl by Property("https://storage.googleapis.com/chromium-v8/official/canary/") override var downloadBaseUrl: String? by Property("https://storage.googleapis.com/chromium-v8/official/canary")
override var installationDir by Property(gradleHome.resolve("d8"))
// Latest version number could be found here https://storage.googleapis.com/chromium-v8/official/canary/v8-linux64-rel-latest.json // Latest version number could be found here https://storage.googleapis.com/chromium-v8/official/canary/v8-linux64-rel-latest.json
// Bash script/command to check that version specified in `VER` is available for all platforms, just copy-paste and run it in terminal: // Bash script/command to check that version specified in `VER` is available for all platforms, just copy-paste and run it in terminal:
@@ -41,27 +39,37 @@ open class D8RootExtension(@Transient val rootProject: Project) : ConfigurationP
fi; fi;
done; done;
*/ */
var version by Property("11.9.85") override var version by Property("11.9.85")
var edition by Property("rel") // rel or dbg var edition by Property("rel") // rel or dbg
override var command by Property("d8")
val setupTaskProvider: TaskProvider<out Copy> val setupTaskProvider: TaskProvider<D8SetupTask>
get() = rootProject.tasks.withType(Copy::class.java).named(D8RootPlugin.INSTALL_TASK_NAME) get() = rootProject.tasks.withType(D8SetupTask::class.java).named(D8SetupTask.NAME)
override fun finalizeConfiguration(): D8Env { override fun finalizeConfiguration(): D8Env {
val requiredVersionName = "v8-${D8Platform.platform}-$edition-$version" val requiredVersionName = "v8-${D8Platform.platform}-$edition-$version"
val requiredZipName = "$requiredVersionName.zip" val cleanableStore = CleanableStore[installationDir.absolutePath]
val cleanableStore = CleanableStore[installationPath.absolutePath]
val targetPath = cleanableStore[requiredVersionName].use() val targetPath = cleanableStore[requiredVersionName].use()
val isWindows = D8Platform.name == D8Platform.WIN val isWindows = D8Platform.name == D8Platform.WIN
fun getExecutable(command: String, customCommand: String, windowsExtension: String): String {
val finalCommand = if (isWindows && customCommand == command) "$command.$windowsExtension" else customCommand
return if (download)
targetPath
.resolve(finalCommand)
.absolutePath
else
finalCommand
}
return D8Env( return D8Env(
download = download,
downloadBaseUrl = downloadBaseUrl,
ivyDependency = "google.d8:v8:${D8Platform.platform}-$edition-$version@zip",
executable = getExecutable("d8", command, "exe"),
dir = targetPath,
cleanableStore = cleanableStore, cleanableStore = cleanableStore,
zipPath = cleanableStore[requiredZipName].use(),
targetPath = targetPath,
executablePath = targetPath.resolve(if (isWindows) "d8.exe" else "d8"),
isWindows = isWindows, isWindows = isWindows,
downloadUrl = URL(downloadBaseUrl),
ivyDependency = "google.d8:v8:${D8Platform.platform}-$edition-$version@zip"
) )
} }
@@ -8,11 +8,11 @@ package org.jetbrains.kotlin.gradle.targets.js.d8
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.plugins.BasePlugin import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Copy
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootExtension.Companion.EXTENSION_NAME import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootExtension.Companion.EXTENSION_NAME
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.castIsolatedKotlinPluginClassLoaderAware
open class D8RootPlugin : Plugin<Project> { open class D8RootPlugin : Plugin<Project> {
@@ -27,39 +27,15 @@ open class D8RootPlugin : Plugin<Project> {
val settings = project.extensions.create(EXTENSION_NAME, D8RootExtension::class.java, project) val settings = project.extensions.create(EXTENSION_NAME, D8RootExtension::class.java, project)
project.gradle.projectsEvaluated { project.registerTask<D8SetupTask>(D8SetupTask.NAME) {
val downloadUrl = settings.requireConfigured().downloadUrl it.group = TASKS_GROUP_NAME
project.repositories.ivy { repo -> it.description = "Download and install a D8"
repo.name = "D8 Distributions at $downloadUrl" it.configuration = project.provider {
repo.url = downloadUrl.toURI() project.configurations.detachedConfiguration(project.dependencies.create(it.ivyDependency))
repo.patternLayout { .also { conf -> conf.isTransitive = false }
it.artifact("[artifact]-[revision].[ext]")
}
repo.metadataSources { it.artifact() }
repo.content { it.includeModule("google.d8", "v8") }
} }
} }
val downloadTask = project.registerTask<Copy>("${TASKS_GROUP_NAME}Download") {
it.group = TASKS_GROUP_NAME
it.description = "Download local d8 version"
val env = settings.requireConfigured()
val configuration = project.configurations.detachedConfiguration(project.dependencies.create(env.ivyDependency))
it.from(project.provider { configuration.singleFile })
it.into(env.zipPath.parentFile)
}
project.registerTask<Copy>(INSTALL_TASK_NAME) {
val env = settings.requireConfigured()
it.onlyIf { env.zipPath.exists() && !env.executablePath.exists() }
it.group = TASKS_GROUP_NAME
it.from(project.zipTree(env.zipPath))
it.into(env.targetPath)
it.dependsOn(downloadTask)
it.description = "Install local d8 version"
}
project.registerTask<CleanDataTask>("d8" + CleanDataTask.NAME_SUFFIX) { project.registerTask<CleanDataTask>("d8" + CleanDataTask.NAME_SUFFIX) {
it.cleanableStoreProvider = project.provider { settings.requireConfigured().cleanableStore } it.cleanableStoreProvider = project.provider { settings.requireConfigured().cleanableStore }
it.group = TASKS_GROUP_NAME it.group = TASKS_GROUP_NAME
@@ -69,12 +45,14 @@ open class D8RootPlugin : Plugin<Project> {
companion object { companion object {
const val TASKS_GROUP_NAME: String = "d8" const val TASKS_GROUP_NAME: String = "d8"
const val INSTALL_TASK_NAME: String = "${TASKS_GROUP_NAME}Install"
fun apply(rootProject: Project): D8RootExtension { fun apply(rootProject: Project): D8RootExtension {
check(rootProject == rootProject.rootProject) check(rootProject == rootProject.rootProject)
rootProject.plugins.apply(D8RootPlugin::class.java) rootProject.plugins.apply(D8RootPlugin::class.java)
return rootProject.extensions.getByName(EXTENSION_NAME) as D8RootExtension return rootProject.extensions.getByName(EXTENSION_NAME) as D8RootExtension
} }
val Project.kotlinD8Extension: D8RootExtension
get() = extensions.getByName(EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()
} }
} }
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.targets.js.d8
import org.gradle.api.tasks.Internal
import org.gradle.work.DisableCachingByDefault
import org.jetbrains.kotlin.gradle.targets.js.AbstractSetupTask
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin.Companion.kotlinD8Extension
import java.io.File
@DisableCachingByDefault
abstract class D8SetupTask : AbstractSetupTask<D8Env, D8RootExtension>() {
@Transient
@Internal
override val settings = project.kotlinD8Extension
@get:Internal
override val artifactPattern: String
get() = "[artifact]-[revision].[ext]"
@get:Internal
override val artifactModule: String
get() = "google.d8"
@get:Internal
override val artifactName: String
get() = "v8"
override fun extract(archive: File) {
fs.copy {
it.from(archiveOperations.zipTree(archive))
//
it.into(destination)
}
if (!env.isWindows) {
File(env.executable).setExecutable(true)
}
}
companion object {
const val NAME: String = "kotlinD8Setup"
}
}
@@ -7,11 +7,11 @@ import java.io.File
data class NodeJsEnv( data class NodeJsEnv(
override val download: Boolean, override val download: Boolean,
val cleanableStore: CleanableStore, override val cleanableStore: CleanableStore,
val rootPackageDir: File, val rootPackageDir: File,
override val dir: File, override val dir: File,
val nodeBinDir: File, val nodeBinDir: File,
val nodeExecutable: String, override val executable: String,
val platformName: String, val platformName: String,
val architectureName: String, val architectureName: String,
override val ivyDependency: String, override val ivyDependency: String,
@@ -21,4 +21,8 @@ data class NodeJsEnv(
) : AbstractEnv { ) : AbstractEnv {
val isWindows: Boolean val isWindows: Boolean
get() = platformName == "win" get() = platformName == "win"
@Deprecated("Use executable instead", ReplaceWith("executable"))
val nodeExecutable
get() = executable
} }
@@ -108,7 +108,7 @@ constructor(
listOf(compilation) listOf(compilation)
) { ) {
it.nodeJs = nodeJs it.nodeJs = nodeJs
it.executable = nodeJs.requireConfigured().nodeExecutable it.executable = nodeJs.requireConfigured().executable
if ((compilation.target as? KotlinJsIrTarget)?.wasmTargetType != KotlinWasmTargetType.WASI) { if ((compilation.target as? KotlinJsIrTarget)?.wasmTargetType != KotlinWasmTargetType.WASI) {
it.workingDir(npmProject.dir) it.workingDir(npmProject.dir)
it.dependsOn( it.dependsOn(
@@ -158,7 +158,7 @@ open class NodeJsRootExtension(
rootPackageDir = rootPackageDirectory.getFile(), rootPackageDir = rootPackageDirectory.getFile(),
dir = nodeDir, dir = nodeDir,
nodeBinDir = nodeBinDir, nodeBinDir = nodeBinDir,
nodeExecutable = getExecutable("node", command, "exe"), executable = getExecutable("node", command, "exe"),
platformName = name, platformName = name,
architectureName = architecture, architectureName = architecture,
ivyDependency = getIvyDependency(), ivyDependency = getIvyDependency(),
@@ -27,7 +27,7 @@ abstract class NodeJsSetupTask : AbstractSetupTask<NodeJsEnv, NodeJsRootExtensio
override val artifactName: String override val artifactName: String
get() = "node" get() = "node"
override fun extract(archive: File, destination: File) { override fun extract(archive: File) {
var fixBrokenSymLinks = false var fixBrokenSymLinks = false
fs.copy { fs.copy {
@@ -40,13 +40,13 @@ abstract class NodeJsSetupTask : AbstractSetupTask<NodeJsEnv, NodeJsRootExtensio
} }
} }
) )
it.into(destination) it.into(destination.parentFile)
} }
fixBrokenSymlinks(this.destination, env.isWindows, fixBrokenSymLinks) fixBrokenSymlinks(this.destination, env.isWindows, fixBrokenSymLinks)
if (!env.isWindows) { if (!env.isWindows) {
File(env.nodeExecutable).setExecutable(true) File(env.executable).setExecutable(true)
} }
} }
@@ -51,6 +51,6 @@ data class NodeJsEnvironment(
internal val NodeJsEnv.asNodeJsEnvironment internal val NodeJsEnv.asNodeJsEnvironment
get() = NodeJsEnvironment( get() = NodeJsEnvironment(
rootPackageDir, rootPackageDir,
nodeExecutable, executable,
packageManager packageManager
) )
@@ -87,7 +87,7 @@ open class NpmProject(@Transient val compilation: KotlinJsIrCompilation) : Seria
} }
private val nodeExecutable by lazy { private val nodeExecutable by lazy {
nodeJs.requireConfigured().nodeExecutable nodeJs.requireConfigured().executable
} }
fun useTool( fun useTool(
@@ -38,7 +38,7 @@ constructor(
@Transient @Transient
private val nodeJs = project.rootProject.kotlinNodeJsExtension private val nodeJs = project.rootProject.kotlinNodeJsExtension
private val nodeExecutable by project.provider { nodeJs.requireConfigured().nodeExecutable } private val nodeExecutable by project.provider { nodeJs.requireConfigured().executable }
@Input @Input
var environment = mutableMapOf<String, String>() var environment = mutableMapOf<String, String>()
@@ -31,7 +31,7 @@ internal class KotlinWasmD8(private val kotlinJsTest: KotlinJsTest) : KotlinJsTe
private val project: Project = compilation.target.project private val project: Project = compilation.target.project
private val d8 = D8RootPlugin.apply(project.rootProject) private val d8 = D8RootPlugin.apply(project.rootProject)
private val d8Executable by project.provider { d8.requireConfigured().executablePath } private val d8Executable by project.provider { d8.requireConfigured().executable }
private val npmProjectDir by project.provider { compilation.npmProject.dir } private val npmProjectDir by project.provider { compilation.npmProject.dir }
override val workingDir: Provider<Directory> override val workingDir: Provider<Directory>
@@ -45,7 +45,7 @@ internal class KotlinWasmD8(private val kotlinJsTest: KotlinJsTest) : KotlinJsTe
): TCServiceMessagesTestExecutionSpec { ): TCServiceMessagesTestExecutionSpec {
val testRunnerFile = writeWasmUnitTestRunner(task.inputFileProperty.get().asFile) val testRunnerFile = writeWasmUnitTestRunner(task.inputFileProperty.get().asFile)
forkOptions.executable = d8Executable.absolutePath forkOptions.executable = d8Executable
forkOptions.workingDir = testRunnerFile.parentFile forkOptions.workingDir = testRunnerFile.parentFile
val clientSettings = TCServiceMessagesClientSettings( val clientSettings = TCServiceMessagesClientSettings(
@@ -12,9 +12,9 @@ import java.io.File
data class YarnEnv( data class YarnEnv(
override val download: Boolean, override val download: Boolean,
override val downloadBaseUrl: String?, override val downloadBaseUrl: String?,
val cleanableStore: CleanableStore, override val cleanableStore: CleanableStore,
override val dir: File, override val dir: File,
val executable: String, override val executable: String,
override val ivyDependency: String, override val ivyDependency: String,
val ignoreScripts: Boolean, val ignoreScripts: Boolean,
val yarnLockMismatchReport: YarnLockMismatchReport, val yarnLockMismatchReport: YarnLockMismatchReport,
@@ -28,11 +28,11 @@ abstract class YarnSetupTask : AbstractSetupTask<YarnEnv, YarnRootExtension>() {
override val artifactName: String override val artifactName: String
get() = "yarn" get() = "yarn"
override fun extract(archive: File, destination: File) { override fun extract(archive: File) {
val dirInTar = archive.name.removeSuffix(".tar.gz") val dirInTar = archive.name.removeSuffix(".tar.gz")
fs.copy { fs.copy {
it.from(archiveOperations.tarTree(archive)) it.from(archiveOperations.tarTree(archive))
it.into(destination) it.into(destination.parentFile)
it.includeEmptyDirs = false it.includeEmptyDirs = false
it.eachFile { fileCopy -> it.eachFile { fileCopy ->
fileCopy.path = fileCopy.path.removePrefix(dirInTar) fileCopy.path = fileCopy.path.removePrefix(dirInTar)
@@ -45,6 +45,7 @@ fun Project.useNodeJsPlugin() {
NodeJsUtils.useNodeJsPlugin(this) NodeJsUtils.useNodeJsPlugin(this)
} }
@Suppress("DEPRECATION")
fun Test.setupNodeJs() { fun Test.setupNodeJs() {
dependsOn(NodeJsUtils.nodeJsPlugin.nodeJsSetupTaskProvider) dependsOn(NodeJsUtils.nodeJsPlugin.nodeJsSetupTaskProvider)
val nodeJsExecutablePath = project.provider { val nodeJsExecutablePath = project.provider {