Gradle, npm: resolve node_modules at parents, return local NodeJsExtension at NodeJsPlugin.apply

This commit is contained in:
Sergey Rostov
2019-04-14 13:33:07 +03:00
parent 4f4fc74b16
commit 312830ac4b
11 changed files with 93 additions and 103 deletions
@@ -38,7 +38,7 @@ internal class KotlinJsCompilationTestsConfigurator(
fun configure() {
// apply plugin (cannot do it lazy)
val nodeJs = NodeJsPlugin[target.project]
val nodeJs = NodeJsPlugin.apply(target.project).root
val testJs = project.createOrRegisterTask<KotlinJsTest>(testTaskName) { testJs ->
testJs.group = LifecycleBasePlugin.VERIFICATION_GROUP
@@ -56,6 +56,16 @@ internal class KotlinJsCompilationTestsConfigurator(
testJs.configureConventions()
}
testJs.doGetTask().useNodeJs { }
registerTestTask(testJs)
// project.afterEvaluate {
// testJs.configure {
// if (it.testFramework == null) {
// it.useNodeJs { }
// }
// }
// }
}
}
@@ -9,6 +9,9 @@ import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson
open class NodeJsExtension(val project: Project) {
val root: NodeJsRootExtension
get() = this as? NodeJsRootExtension ?: NodeJsRootExtension[project]
internal val packageJsonHandlers = mutableListOf<PackageJson.() -> Unit>()
fun packageJson(handler: PackageJson.() -> Unit) {
@@ -16,9 +19,9 @@ open class NodeJsExtension(val project: Project) {
}
companion object {
operator fun get(project: Project): NodeJsExtension {
NodeJsPlugin[project.rootProject]
return project.extensions.getByName(NodeJsRootExtension.NODE_JS) as NodeJsExtension
}
operator fun get(project: Project) = NodeJsPlugin.apply(project)
}
}
}
val Project.nodeJs: NodeJsExtension
get() = NodeJsExtension[this]
@@ -4,10 +4,7 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Delete
import org.jetbrains.kotlin.backend.common.onlyIf
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.Companion.NODE_JS
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolver
open class NodeJsPlugin : Plugin<Project> {
override fun apply(project: Project): Unit = project.run {
@@ -15,10 +12,10 @@ open class NodeJsPlugin : Plugin<Project> {
"NodeJsPlugin can be applied only to root project"
}
val nodeJs = this.extensions.create(NODE_JS, NodeJsRootExtension::class.java, this)
this.extensions.create(NODE_JS, NodeJsRootExtension::class.java, this)
tasks.create(NodeJsSetupTask.NAME, NodeJsSetupTask::class.java)
// setupCleanNodeModulesTask(project, nodeJs)
setupCleanNodeModulesTask(project)
allprojects {
if (it != project) {
@@ -27,21 +24,13 @@ open class NodeJsPlugin : Plugin<Project> {
}
}
private fun setupCleanNodeModulesTask(
project: Project,
nodeJs: NodeJsRootExtension
) {
private fun setupCleanNodeModulesTask(project: Project) {
project.tasks.create("cleanNodeModules", Delete::class.java) {
it.description = "Deletes node_modules and package.json file"
it.group = BasePlugin.BUILD_GROUP
val npmProjectLayout = NpmProjectLayout[it.project]
project.delete(npmProjectLayout.nodeModulesDir)
project.afterEvaluate {
if (nodeJs.manageNodeModules) {
project.delete(npmProjectLayout.packageJsonFile)
}
it.doLast {
project.nodeJs.root.packageManager.cleanProject(project)
}
project.tasks.maybeCreate(BasePlugin.CLEAN_TASK_NAME).dependsOn(it)
@@ -49,10 +38,10 @@ open class NodeJsPlugin : Plugin<Project> {
}
companion object {
operator fun get(project: Project): NodeJsRootExtension {
fun apply(project: Project): NodeJsExtension {
val rootProject = project.rootProject
rootProject.plugins.apply(NodeJsPlugin::class.java)
return rootProject.extensions.getByName(NODE_JS) as NodeJsRootExtension
return rootProject.extensions.getByName(NODE_JS) as NodeJsExtension
}
}
}
@@ -28,37 +28,38 @@ open class NodeJsRootExtension(project: Project) : NodeJsExtension(project) {
val nodeJsSetupTask: NodeJsSetupTask
get() = project.tasks.getByName(NodeJsSetupTask.NAME) as NodeJsSetupTask
internal fun buildEnv(): NodeJsEnv {
val platform = NodeJsPlatform.name
val architecture = NodeJsPlatform.architecture
internal val environment: NodeJsEnv
get() {
val platform = NodeJsPlatform.name
val architecture = NodeJsPlatform.architecture
val nodeDir = installationDir.resolve("node-v$nodeVersion-$platform-$architecture")
val isWindows = NodeJsPlatform.name == NodeJsPlatform.WIN
val nodeBinDir = if (isWindows) nodeDir else nodeDir.resolve("bin")
val nodeDir = installationDir.resolve("node-v$nodeVersion-$platform-$architecture")
val isWindows = NodeJsPlatform.name == NodeJsPlatform.WIN
val nodeBinDir = if (isWindows) nodeDir else nodeDir.resolve("bin")
fun getExecutable(command: String, customCommand: String, windowsExtension: String): String {
val finalCommand = if (isWindows && customCommand == command) "$command.$windowsExtension" else customCommand
return if (download) File(nodeBinDir, finalCommand).absolutePath else finalCommand
fun getExecutable(command: String, customCommand: String, windowsExtension: String): String {
val finalCommand = if (isWindows && customCommand == command) "$command.$windowsExtension" else customCommand
return if (download) File(nodeBinDir, finalCommand).absolutePath else finalCommand
}
fun getIvyDependency(): String {
val type = if (isWindows) "zip" else "tar.gz"
return "org.nodejs:node:$nodeVersion:$platform-$architecture@$type"
}
return NodeJsEnv(
nodeDir = nodeDir,
nodeBinDir = nodeBinDir,
nodeExecutable = getExecutable("node", nodeCommand, "exe"),
npmExecutable = getExecutable("npm", npmCommand, "cmd"),
platformName = platform,
architectureName = architecture,
ivyDependency = getIvyDependency()
)
}
fun getIvyDependency(): String {
val type = if (isWindows) "zip" else "tar.gz"
return "org.nodejs:node:$nodeVersion:$platform-$architecture@$type"
}
return NodeJsEnv(
nodeDir = nodeDir,
nodeBinDir = nodeBinDir,
nodeExecutable = getExecutable("node", nodeCommand, "exe"),
npmExecutable = getExecutable("npm", npmCommand, "cmd"),
platformName = platform,
architectureName = architecture,
ivyDependency = getIvyDependency()
)
}
internal fun executeSetup() {
val nodeJsEnv = buildEnv()
val nodeJsEnv = environment
if (download) {
if (!nodeJsEnv.nodeBinDir.isDirectory) {
nodeJsSetupTask.exec()
@@ -69,6 +70,6 @@ open class NodeJsRootExtension(project: Project) : NodeJsExtension(project) {
companion object {
const val NODE_JS: String = "nodeJs"
operator fun get(project: Project): NodeJsRootExtension = NodeJsPlugin[project]
operator fun get(project: Project) = NodeJsPlugin.apply(project.rootProject) as NodeJsRootExtension
}
}
@@ -12,7 +12,6 @@ import org.gradle.api.Project
import org.gradle.process.ExecSpec
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import java.io.File
class NpmProjectLayout(
@@ -36,18 +35,6 @@ class NpmProjectLayout(
exec.args = listOf(findModule(tool)) + args
}
val hoistGradleNodeModules: Boolean
get() = project.nodeJs.root.packageManager.getHoistGradleNodeModules(project)
val gradleNodeModulesDir: File
get() =
if (hoistGradleNodeModules) project.rootProject.npmProject.nodeModulesDir
else nodeModulesDir
fun moduleOutput(compilationTask: Kotlin2JsCompile): File {
return gradleNodeModulesDir.resolve(compilationTask.outputFile.name)
}
@TailRecursive
fun findModule(name: String, src: NpmProjectLayout = this): String {
val file = nodeModulesDir.resolve(name)
@@ -54,7 +54,7 @@ internal class NpmResolver private constructor(val rootProject: Project) {
class ResolvedNow(val resolution: ResolvedProject) : ResolutionCallResult()
}
private val nodeJs = NodeJsPlugin.apply(rootProject)
private val nodeJs = NodeJsPlugin.apply(rootProject).root
private val packageManager = nodeJs.packageManager
private val hoistGradleNodeModules = packageManager.getHoistGradleNodeModules(rootProject)
private val npmPackages = mutableListOf<NpmPackage>()
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.js.testing
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.process.internal.DefaultProcessForkOptions
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
@@ -14,13 +15,14 @@ import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolver
import org.jetbrains.kotlin.gradle.targets.js.testing.karma.KotlinKarma
import org.jetbrains.kotlin.gradle.targets.js.testing.nodejs.KotlinNodeJsTestRunner
import org.jetbrains.kotlin.gradle.targets.js.testing.mocha.KotlinMocha
import org.jetbrains.kotlin.gradle.targets.js.testing.nodejs.KotlinNodeJsTestRunner
import org.jetbrains.kotlin.gradle.tasks.KotlinTest
open class KotlinJsTest : KotlinTest() {
@Internal
private var testFramework: KotlinJsTestFramework = KotlinNodeJsTestRunner()
@SkipWhenEmpty
internal var testFramework: KotlinJsTestFramework? = null
@Input
var debug: Boolean = false
@@ -37,13 +39,20 @@ open class KotlinJsTest : KotlinTest() {
fun useKarma(body: KotlinKarma.() -> Unit) = use(KotlinKarma(), body)
private inline fun <T : KotlinJsTestFramework> use(runner: T, body: T.() -> Unit) {
testFramework = runner.also(body)
private inline fun <T : KotlinJsTestFramework> use(runner: T, body: T.() -> Unit): T {
check(testFramework == null) {
"testFramework already configured for task ${this.path}"
}
val testFramework = runner.also(body)
this.testFramework = testFramework
val dependenciesHolder = runtimeDependencyHandler
if (dependenciesHolder != null) {
testFramework.configure(dependenciesHolder)
}
return testFramework
}
override fun executeTests() {
@@ -57,7 +66,7 @@ open class KotlinJsTest : KotlinTest() {
NpmResolver.resolve(project)
forkOptions.workingDir = NpmProjectLayout[project].nodeWorkDir
forkOptions.executable = NodeJsPlugin[project].buildEnv().nodeExecutable
forkOptions.executable = NodeJsPlugin.apply(project).root.environment.nodeExecutable
val nodeJsArgs = mutableListOf<String>()
@@ -65,6 +74,6 @@ open class KotlinJsTest : KotlinTest() {
nodeJsArgs.add("--inspect-brk")
}
return testFramework.createTestExecutionSpec(this, forkOptions, nodeJsArgs)
return testFramework!!.createTestExecutionSpec(this, forkOptions, nodeJsArgs)
}
}
@@ -54,11 +54,7 @@ class KotlinNodeJsTestRunner : KotlinJsTestFramework {
val args = nodeJsArgs +
testRuntimeNodeModules.map {
npmProjectLayout.nodeModulesDir.resolve(it).also { file ->
check(file.isFile) {
"Cannot find ${file.canonicalPath}"
}
}.canonicalPath
npmProjectLayout.findModule(it)
} +
cliArgs.toList()
@@ -28,7 +28,7 @@ object Yarn : NpmApi {
vararg args: String,
npmProjectLayout: NpmProjectLayout = NpmProjectLayout[project]
) {
val nodeJsEnv = NodeJsPlugin.apply(project).environment
val nodeJsEnv = NodeJsPlugin.apply(project).root.environment
val yarnEnv = YarnPlugin.apply(project).environment
val packageJsonHashFile = project.packageJsonHashFile
@@ -15,14 +15,16 @@ open class YarnPlugin : Plugin<Project> {
"YarnPlugin can be applied only to root project"
}
val nodeJs = NodeJsPlugin.apply(this).root
this.extensions.create(YarnRootExtension.YARN, YarnRootExtension::class.java, this)
tasks.create(YarnSetupTask.NAME, YarnSetupTask::class.java) {
it.dependsOn(NodeJsPlugin[this].nodeJsSetupTask)
it.dependsOn(nodeJs.nodeJsSetupTask)
}
}
companion object {
operator fun get(project: Project): YarnRootExtension {
fun apply(project: Project): YarnRootExtension {
val rootProject = project.rootProject
rootProject.plugins.apply(YarnPlugin::class.java)
return rootProject.extensions.getByName(YarnRootExtension.YARN) as YarnRootExtension
@@ -5,10 +5,10 @@
package org.jetbrains.kotlin.gradle.targets.js.yarn
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs
open class YarnRootExtension(val project: Project) {
private val gradleHome = project.gradle.gradleUserHomeDir.also {
@@ -23,43 +23,36 @@ open class YarnRootExtension(val project: Project) {
val yarnSetupTask: YarnSetupTask
get() = project.tasks.getByName(YarnSetupTask.NAME) as YarnSetupTask
var useWorkspaces: Boolean = false
var disableWorkspaces: Boolean = false
internal fun checkUseWorkspace(): Boolean = if (useWorkspaces) {
check(NodeJsPlugin[project].manageNodeModules) {
"Yarn workspace can be used only with `manageNodeModules`: (add `nodeJs { manageNodeModules = true }` to your root project)"
}
true
} else false
val useWorkspaces: Boolean
get() = project.nodeJs.root.manageNodeModules && !disableWorkspaces
internal fun executeSetup() {
NodeJsPlugin[project].executeSetup()
NodeJsPlugin.apply(project).root.executeSetup()
val env = buildEnv()
val env = environment
if (!env.home.isDirectory) {
yarnSetupTask.setup()
}
}
internal fun buildEnv() = YarnEnv(
downloadUrl = "$downloadBaseUrl/v$version/yarn-v$version.tar.gz",
home = installationDir.resolve("yarn-v$version")
)
internal val environment
get() = YarnEnv(
downloadUrl = "$downloadBaseUrl/v$version/yarn-v$version.tar.gz",
home = installationDir.resolve("yarn-v$version")
)
companion object {
const val YARN: String = "yarn"
operator fun get(project: Project): YarnRootExtension {
val extension = project.extensions.findByType(YarnRootExtension::class.java)
if (extension != null)
return extension
val parentProject = project.parent
if (parentProject != null)
return get(parentProject)
throw GradleException("YarnRootExtension is not installed")
val rootProject = project.rootProject
rootProject.plugins.apply(YarnPlugin::class.java)
return rootProject.extensions.getByName(YARN) as YarnRootExtension
}
}
}
val Project.yarn: YarnRootExtension
get() = YarnRootExtension[this]