Gradle, JS: introduce inner browser and nodejs inner targets

#KT-30747 Fixed
This commit is contained in:
Sergey Rostov
2019-04-25 10:34:35 +03:00
parent 066efc3732
commit 3797ec9255
12 changed files with 220 additions and 130 deletions
@@ -0,0 +1,66 @@
/*
* 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
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask
class KotlinJsBrowser(
target: KotlinOnlyTarget<KotlinJsCompilation>
) : KotlinJsInnerTargetConfigurator(target) {
override fun configureDefaultTestFramework(it: KotlinJsTest) {
it.useNodeJs { }
}
override fun configureRun(compilation: KotlinJsCompilation) {
val project = compilation.target.project
val npmProject = project.npmProject
val compileKotlinTask = compilation.compileKotlinTask
compilation.dependencies {
runtimeOnly(npm("webpack", "4.29.6"))
runtimeOnly(npm("webpack-cli", "3.3.0"))
runtimeOnly(npm("webpack-bundle-analyzer", "3.3.2"))
// for source map support only
runtimeOnly(npm("source-map-loader", "0.2.4"))
runtimeOnly(npm("source-map-support", "0.5.12"))
}
project.createOrRegisterTask<KotlinWebpack>(disambiguateCamelCased("webpack")) {
it.dependsOn(compileKotlinTask)
it.entry = npmProject.compileOutput(compileKotlinTask)
project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(it)
}
compilation.dependencies {
runtimeOnly(npm("webpack-dev-server", "3.3.1"))
}
project.createOrRegisterTask<KotlinWebpack>(disambiguateCamelCased("run")) {
it.dependsOn(compileKotlinTask)
it.bin = "webpack-dev-server"
it.entry = npmProject.compileOutput(compileKotlinTask)
val projectDir = target.project.projectDir.canonicalPath
it.devServer = KotlinWebpackConfig.DevServer(
contentBase = listOf("$projectDir/src/main/resources")
)
it.outputs.upToDateWhen { false }
project.tasks.maybeCreate("run").dependsOn(it)
}
}
}
@@ -1,7 +1,14 @@
/*
* 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
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask
@@ -9,31 +16,42 @@ import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
import org.jetbrains.kotlin.gradle.testing.internal.registerTestTask
import org.jetbrains.kotlin.utils.addIfNotNull
internal open class KotlinJsCompilationTestsConfigurator(val compilation: KotlinJsCompilation) {
private val target get() = compilation.target
private val disambiguationClassifier get() = target.disambiguationClassifier
private val project get() = target.project
private val compileTask get() = compilation.compileKotlinTask
abstract class KotlinJsInnerTargetConfigurator(val target: KotlinOnlyTarget<KotlinJsCompilation>) {
val project get() = target.project
private val disambiguationClassifier get() = "browser"
private fun disambiguate(name: String, includeCompilation: Boolean = false): MutableList<String> {
fun configure() {
configureTests()
configureRun()
}
private fun disambiguate(name: String): MutableList<String> {
val components = mutableListOf<String>()
components.addIfNotNull(disambiguationClassifier)
if (includeCompilation) components.add(compilation.name)
components.addIfNotNull(target.disambiguationClassifier)
components.add(disambiguationClassifier)
components.add(name)
return components
}
private fun disambiguateCamelCased(name: String, includeCompilation: Boolean): String {
val components = disambiguate(name, includeCompilation)
protected fun disambiguateCamelCased(name: String): String {
val components = disambiguate(name)
return components.first() + components.drop(1).joinToString("") { it.capitalize() }
}
private val testTaskName: String
get() = disambiguateCamelCased("test", false)
private fun configureTests() {
target.compilations.all { compilation ->
if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) {
configureTests(compilation)
}
}
}
private fun configureTests(compilation: KotlinJsCompilation) {
val compileTask = compilation.compileKotlinTask
val testTaskName = disambiguateCamelCased("test")
fun configure() {
// apply plugin (cannot be done at task instantiation time)
val nodeJs = NodeJsPlugin.apply(target.project).root
@@ -51,6 +69,8 @@ internal open class KotlinJsCompilationTestsConfigurator(val compilation: Kotlin
testJs.nodeModulesToLoad.add(compileTask.outputFile.name)
testJs.configureConventions()
project.tasks.maybeCreate("test").dependsOn(testJs)
}
registerTestTask(testJs)
@@ -64,7 +84,14 @@ internal open class KotlinJsCompilationTestsConfigurator(val compilation: Kotlin
}
}
protected open fun configureDefaultTestFramework(it: KotlinJsTest) {
it.useNodeJs { }
protected abstract fun configureDefaultTestFramework(it: KotlinJsTest)
fun configureRun() {
target.compilations.all { compilation ->
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
configureRun(compilation)
}
}
}
protected abstract fun configureRun(compilation: KotlinJsCompilation)
}
@@ -28,8 +28,6 @@ open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
super.configureCompilations(platformTarget)
platformTarget.compilations.all {
platformTarget.project.npmProject.configureCompilation(it)
it.compileKotlinTask.kotlinOptions {
moduleKind = "commonjs"
sourceMap = true
@@ -37,44 +35,4 @@ open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
}
}
}
override fun configureTest(target: KotlinOnlyTarget<KotlinJsCompilation>) {
target.compilations.all { compilation ->
if (isTestCompilation(compilation)) {
newTestsConfigurator(compilation).configure()
}
}
}
internal open fun newTestsConfigurator(compilation: KotlinJsCompilation) =
KotlinJsCompilationTestsConfigurator(compilation)
companion object {
internal fun isTestCompilation(it: KotlinJsCompilation) =
it.name == KotlinCompilation.TEST_COMPILATION_NAME
}
}
class KotlinJsSingleTargetConfigurator(kotlinPluginVersion: String) :
KotlinJsTargetConfigurator(kotlinPluginVersion) {
override fun configureTarget(target: KotlinOnlyTarget<KotlinJsCompilation>) {
super.configureTarget(target)
configureApplication(target)
}
private fun configureApplication(target: KotlinOnlyTarget<KotlinJsCompilation>) {
target.compilations.all {
if (it.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
KotlinWebpack.configure(it)
}
}
}
override fun newTestsConfigurator(compilation: KotlinJsCompilation) =
object : KotlinJsCompilationTestsConfigurator(compilation) {
override fun configureDefaultTestFramework(it: KotlinJsTest) {
it.useKarma { }
}
}
}
@@ -3,7 +3,7 @@
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.targets.nodejs
package org.jetbrains.kotlin.gradle.targets.js
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
@@ -15,14 +15,12 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask
class KotlinNodeJsSingleTargetConfigurator(kotlinPluginVersion: String) :
class KotlinNodeJs(kotlinPluginVersion: String) :
KotlinJsTargetConfigurator(kotlinPluginVersion) {
override fun configureTarget(target: KotlinOnlyTarget<KotlinJsCompilation>) {
super.configureTarget(target)
configureApplication(target)
}
private fun configureApplication(target: KotlinOnlyTarget<KotlinJsCompilation>) {
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency
val karmaChromeLauncher = NpmPackageVersion("karma-chrome-launcher", "2.2.0")
val karmaPhantomjsLauncher = NpmPackageVersion("karma-phantomjs-launcher", "*")
// karma-firefox-launcher
// karma-opera-launcher
// karma-ie-launcher
// karma-safari-launcher
data class NpmPackageVersion(val name: String, val version: String) {
fun npm(project: Project) = NpmDependency(project, null, name, version)
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.dsl
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
interface KotlinJsTargetDsl {
fun browser()
fun browser(body: KotlinJsBrowserDsl.() -> Unit)
fun nodejs()
fun nodejs(body: KotlinJsNodeDsl.() -> Unit)
}
interface KotlinJsInnerTargetDsl {
fun testTask(body: KotlinJsTest.() -> Unit)
}
interface KotlinJsBrowserDsl : KotlinJsInnerTargetDsl {
fun runTask(body: KotlinWebpack.() -> Unit)
}
interface KotlinJsNodeDsl : KotlinJsInnerTargetDsl {
fun runTask(body: NodeJsExec.() -> Unit)
}
@@ -79,8 +79,6 @@ open class NpmProject(
else NpmProject[parent].findModuleEntry(name)
}
open fun configureCompilation(compilation: KotlinJsCompilation) = Unit
override fun toString() = "NpmProject($nodeWorkDir)"
companion object {
@@ -39,15 +39,6 @@ enum class NpmProjectLayout {
) {
override val compileOutputCopyDest: File?
get() = nodeWorkDir
override fun configureCompilation(compilation: KotlinJsCompilation) {
// val moduleName = compilation.compileKotlinTask.moduleName
// workDir.mkdirs()
// compilation.kotlinOptions.outputFile = workDir.resolve("$moduleName.js").absolutePath
}
// override fun moduleOutput(compilationTask: Kotlin2JsCompile) =
// compilationTask.outputFile
}
}
},
@@ -6,16 +6,47 @@
package org.jetbrains.kotlin.gradle.targets.js.testing.karma
import com.google.gson.GsonBuilder
import org.gradle.api.Project
import org.gradle.api.tasks.Input
import org.gradle.process.ProcessForkOptions
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
import org.jetbrains.kotlin.gradle.targets.js.NpmPackageVersion
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
import org.jetbrains.kotlin.gradle.targets.js.karmaChromeLauncher
import org.jetbrains.kotlin.gradle.targets.js.karmaPhantomjsLauncher
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework
class KotlinKarma : KotlinJsTestFramework {
class KotlinKarma(val project: Project) : KotlinJsTestFramework {
private val config: KarmaConfig = KarmaConfig()
private val requiredDependencies = mutableSetOf<NpmPackageVersion>()
fun useChrome() = useBrowser("Chrome", karmaChromeLauncher)
fun useChromeCanary() = useBrowser("ChromeCanary", karmaChromeLauncher)
fun useChromeHeadless() = useBrowser("ChromeHeadless", karmaChromeLauncher)
fun usePhantomJS() = useBrowser("PhantomJS", karmaPhantomjsLauncher)
fun useFirefox() = useBrowser("Firefox", TODO())
fun useBrowser(id: String, dependency: NpmPackageVersion) {
config.browsers.add(id)
requiredDependencies.add(dependency)
}
fun useMocha() {
}
fun useWebpack() {
}
override fun configure(dependenciesHolder: HasKotlinDependencies) {
dependenciesHolder.dependencies {
runtimeOnly(npm("karma", "4.0.1"))
@@ -38,12 +69,12 @@ class KotlinKarma : KotlinJsTestFramework {
val clientSettings = TCServiceMessagesClientSettings(
task.name,
testNameSuffix = task.targetName,
prepandSuiteName = true,
prependSuiteName = true,
stackTraceParser = ::parseNodeJsStackTraceAsJvm,
ignoreOutOfRootNodes = true
)
val npmProjectLayout = NpmProjectLayout[task.project]
val npmProjectLayout = NpmProject[task.project]
val files = task.nodeModulesToLoad.map {
npmProjectLayout.nodeModulesDir.resolve(it).also { file ->
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.testing.karma
// https://karma-runner.github.io/3.0/config/configuration-file.html
class KarmaConfig {
var singleRun: Boolean = true
var autoWatch: Boolean = false
var basePath: String? = null
val files: MutableList<String> = mutableListOf()
val frameworks: MutableList<String> = mutableListOf()
val browsers: MutableList<String> = mutableListOf()
val reporters: MutableList<String> = mutableListOf()
val preprocessors: MutableMap<String, List<String>> = mutableMapOf()
}
@@ -111,52 +111,4 @@ open class KotlinWebpack : DefaultTask() {
}
}
companion object {
fun configure(compilation: KotlinCompilationToRunnableFiles<*>) {
val project = compilation.target.project
val npmProject = project.npmProject
compilation.dependencies {
runtimeOnly(npm("webpack", "4.29.6"))
runtimeOnly(npm("webpack-cli", "3.3.0"))
runtimeOnly(npm("webpack-bundle-analyzer", "3.3.2"))
// for source map support only
runtimeOnly(npm("source-map-loader", "0.2.4"))
runtimeOnly(npm("source-map-support", "0.5.12"))
}
project.createOrRegisterTask<KotlinWebpack>("webpack") {
val compileKotlinTask = compilation.compileKotlinTask
compileKotlinTask as Kotlin2JsCompile
it.dependsOn(compileKotlinTask)
it.entry = npmProject.compileOutput(compileKotlinTask)
it.outputs.upToDateWhen { false }
}
compilation.dependencies {
runtimeOnly(npm("webpack-dev-server", "3.3.1"))
}
project.createOrRegisterTask<KotlinWebpack>("run") {
val compileKotlinTask = compilation.compileKotlinTask
compileKotlinTask as Kotlin2JsCompile
it.dependsOn(compileKotlinTask)
it.bin = "webpack-dev-server"
it.entry = npmProject.compileOutput(compileKotlinTask)
val projectDir = compilation.target.project.projectDir.canonicalPath
it.devServer = KotlinWebpackConfig.DevServer(
contentBase = listOf("$projectDir/src/main/resources")
)
it.outputs.upToDateWhen { false }
}
}
}
}
@@ -21,6 +21,7 @@ import org.gradle.internal.operations.BuildOperationExecutor
import org.gradle.internal.reflect.Instantiator
import org.gradle.internal.remote.internal.inet.InetAddressFactory
import org.jetbrains.kotlin.gradle.utils.injected
import java.io.File
import java.util.*
import javax.inject.Inject
@@ -33,14 +34,14 @@ open class AggregateTestReport : DefaultTask() {
* Returns the set of binary test results to include in the report.
*/
@Suppress("MemberVisibilityCanBePrivate")
val testResultDirs: FileCollection
val testResultDirs: Collection<File>
@PathSensitive(PathSensitivity.NONE)
@InputFiles
@SkipWhenEmpty
get() {
val dirs = UnionFileCollection()
val dirs = mutableListOf<File>()
testTasks.forEach {
dirs.addToUnion(project.files(it.binResultsDir).builtBy(it))
dirs.add(it.binResultsDir)
}
return dirs
}
@@ -49,7 +50,7 @@ open class AggregateTestReport : DefaultTask() {
var ignoreFailures: Boolean = false
@Nested
val reports: TestTaskReports
val reports: DefaultTestTaskReports
protected open val instantiator: Instantiator
@Inject get() = injected
@@ -121,7 +122,7 @@ open class AggregateTestReport : DefaultTask() {
testReport.generateReport(resultsProvider, html.destination)
}
} else {
logger.info("{} - no binary test results found in dirs: {}.", path, testResultDirs.files)
logger.info("{} - no binary test results found in dirs: {}.", path, testResultDirs)
didWork = false
}
} finally {
@@ -148,7 +149,7 @@ open class AggregateTestReport : DefaultTask() {
val resultsProviders = LinkedList<TestResultsProvider>()
try {
val resultDirs = testResultDirs
return if (resultDirs.files.size == 1) BinaryResultBackedTestResultsProvider(resultDirs.singleFile)
return if (resultDirs.size == 1) BinaryResultBackedTestResultsProvider(resultDirs.single())
else AggregateTestResultsProvider(resultDirs.mapTo(resultsProviders) { BinaryResultBackedTestResultsProvider(it) })
} catch (e: RuntimeException) {
stoppable(resultsProviders).stop()