[Gradle, JS] Webpack task with conf cache (w/o install of dependencies)

This commit is contained in:
Ilya Goncharov
2020-12-11 13:56:43 +03:00
committed by Alexander Likhachev
parent e6bfe9a702
commit a41d3e5b04
16 changed files with 76 additions and 53 deletions
@@ -9,6 +9,7 @@ import jetbrains.buildServer.messages.serviceMessages.ServiceMessageParserCallba
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.internal.project.ProjectInternal
import org.gradle.internal.logging.progress.ProgressLogger import org.gradle.internal.logging.progress.ProgressLogger
import org.gradle.internal.service.ServiceRegistry
import org.gradle.process.ExecResult import org.gradle.process.ExecResult
import org.gradle.process.internal.ExecAction import org.gradle.process.internal.ExecAction
import org.gradle.process.internal.ExecActionFactory import org.gradle.process.internal.ExecActionFactory
@@ -18,15 +19,13 @@ import java.io.PipedInputStream
import java.io.PipedOutputStream import java.io.PipedOutputStream
import kotlin.concurrent.thread import kotlin.concurrent.thread
internal fun Project.execWithProgress(description: String, readStdErr: Boolean = false, body: (ExecAction) -> Unit): ExecResult { internal fun ServiceRegistry.execWithProgress(description: String, readStdErr: Boolean = false, body: (ExecAction) -> Unit): ExecResult {
this as ProjectInternal
val stderr = ByteArrayOutputStream() val stderr = ByteArrayOutputStream()
val stdout = StringBuilder() val stdout = StringBuilder()
val stdInPipe = PipedInputStream() val stdInPipe = PipedInputStream()
val exec = services.get(ExecActionFactory::class.java).newExecAction() val exec = get(ExecActionFactory::class.java).newExecAction()
body(exec) body(exec)
return project!!.operation(description) { return operation(description) {
progress(description) progress(description)
exec.standardOutput = PipedOutputStream(stdInPipe) exec.standardOutput = PipedOutputStream(stdInPipe)
val outputReaderThread = thread(name = "output reader for [$description]") { val outputReaderThread = thread(name = "output reader for [$description]") {
@@ -69,14 +68,12 @@ internal fun Project.execWithProgress(description: String, readStdErr: Boolean =
} }
} }
internal fun Project.execWithErrorLogger( internal fun ServiceRegistry.execWithErrorLogger(
description: String, description: String,
body: (ExecAction, ProgressLogger) -> Pair<TeamCityMessageCommonClient, TeamCityMessageCommonClient> body: (ExecAction, ProgressLogger) -> Pair<TeamCityMessageCommonClient, TeamCityMessageCommonClient>
): ExecResult { ): ExecResult {
this as ProjectInternal val exec = get(ExecActionFactory::class.java).newExecAction()
return operation(description) {
val exec = services.get(ExecActionFactory::class.java).newExecAction()
return project!!.operation(description) {
progress(description) progress(description)
val (standardClient, errorClient) = body(exec, this) val (standardClient, errorClient) = body(exec, this)
exec.isIgnoreExitValue = true exec.isIgnoreExitValue = true
@@ -5,18 +5,16 @@
package org.jetbrains.kotlin.gradle.internal package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.internal.logging.events.ProgressStartEvent import org.gradle.internal.logging.events.ProgressStartEvent
import org.gradle.internal.logging.progress.ProgressLogger import org.gradle.internal.logging.progress.ProgressLogger
import org.gradle.internal.service.ServiceRegistry
fun <T> Project.operation( fun <T> ServiceRegistry.operation(
description: String, description: String,
initialStatus: String? = null, initialStatus: String? = null,
body: ProgressLogger.() -> T body: ProgressLogger.() -> T
): T { ): T {
val services = (project as ProjectInternal).services val progressFactory = get(org.gradle.internal.logging.progress.ProgressLoggerFactory::class.java)
val progressFactory = services.get(org.gradle.internal.logging.progress.ProgressLoggerFactory::class.java)
val operation = progressFactory.newOperation(ProgressStartEvent.BUILD_OP_CATEGORY) val operation = progressFactory.newOperation(ProgressStartEvent.BUILD_OP_CATEGORY)
operation.start(description, initialStatus) operation.start(description, initialStatus)
try { try {
@@ -40,9 +40,9 @@ private constructor() {
val detector = MultiplePluginDeclarationDetector() val detector = MultiplePluginDeclarationDetector()
instance = detector instance = detector
gradle.buildFinished { // gradle.buildFinished {
instance = null // instance = null
} // }
return detector return detector
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.js.dukat package org.jetbrains.kotlin.gradle.targets.js.dukat
import org.gradle.api.artifacts.FileCollectionDependency import org.gradle.api.artifacts.FileCollectionDependency
import org.gradle.api.internal.project.ProjectInternal
import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
@@ -103,7 +104,7 @@ internal class DukatCompilationResolverPlugin(
packageJsonIsUpdated, packageJsonIsUpdated,
operation = compilation.name + " > " + DukatExecutor.OPERATION, operation = compilation.name + " > " + DukatExecutor.OPERATION,
compareInputs = true compareInputs = true
).execute() ).execute((project as ProjectInternal).services)
} }
companion object { companion object {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.targets.js.dukat package org.jetbrains.kotlin.gradle.targets.js.dukat
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
@@ -30,7 +31,7 @@ class DukatExecutor(
val shouldSkip: Boolean val shouldSkip: Boolean
get() = inputsFile.isFile && prevVersion == version && !packageJsonIsUpdated get() = inputsFile.isFile && prevVersion == version && !packageJsonIsUpdated
fun execute() { fun execute(services: ServiceRegistry) {
if (typeDefinitions.isEmpty()) { if (typeDefinitions.isEmpty()) {
npmProject.externalsDirRoot.deleteRecursively() npmProject.externalsDirRoot.deleteRecursively()
return return
@@ -53,7 +54,7 @@ class DukatExecutor(
externalsOutputFormat, externalsOutputFormat,
npmProject.externalsDir, npmProject.externalsDir,
operation = operation operation = operation
).execute() ).execute(services)
inputsFile.writeText(inputs) inputsFile.writeText(inputs)
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.targets.js.dukat package org.jetbrains.kotlin.gradle.targets.js.dukat
import org.gradle.internal.service.ServiceRegistry
import org.jetbrains.kotlin.gradle.internal.execWithProgress import org.jetbrains.kotlin.gradle.internal.execWithProgress
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
@@ -19,8 +20,8 @@ class DukatRunner(
val jsInteropJvmEngine: String? = null, val jsInteropJvmEngine: String? = null,
val operation: String = "Generating Kotlin/JS external declarations" val operation: String = "Generating Kotlin/JS external declarations"
) { ) {
fun execute() { fun execute(services: ServiceRegistry) {
compilation.target.project.execWithProgress(operation) { exec -> services.execWithProgress(operation) { exec ->
val args = mutableListOf<String>() val args = mutableListOf<String>()
if (externalsOutputFormat == ExternalsOutputFormat.BINARY) { if (externalsOutputFormat == ExternalsOutputFormat.BINARY) {
@@ -100,7 +100,7 @@ abstract class DukatTask(
qualifiedPackageName, qualifiedPackageName,
null, null,
operation operation
).execute() ).execute(services)
} }
} }
@@ -39,6 +39,6 @@ constructor(
@TaskAction @TaskAction
override fun run() { override fun run() {
executor.execute() executor.execute(services)
} }
} }
@@ -37,13 +37,14 @@ open class KotlinJsIrLink : Kotlin2JsCompile() {
@get:SkipWhenEmpty @get:SkipWhenEmpty
@get:InputDirectory @get:InputDirectory
@get:PathSensitive(PathSensitivity.RELATIVE) @get:PathSensitive(PathSensitivity.RELATIVE)
internal val entryModule: File internal val entryModule: File by lazy {
get() = File( File(
(taskData.compilation as KotlinJsIrCompilation) (taskData.compilation as KotlinJsIrCompilation)
.output .output
.classesDirs .classesDirs
.asPath .asPath
) )
}
override fun skipCondition(inputs: IncrementalTaskInputs): Boolean { override fun skipCondition(inputs: IncrementalTaskInputs): Boolean {
return !inputs.isIncremental && !entryModule.exists() return !inputs.isIncremental && !entryModule.exists()
@@ -25,11 +25,13 @@ val KotlinJsCompilation.npmProject: NpmProject
* More info can be obtained from [KotlinCompilationNpmResolution], which is available after project resolution (after [KotlinNpmInstallTask] execution). * More info can be obtained from [KotlinCompilationNpmResolution], which is available after project resolution (after [KotlinNpmInstallTask] execution).
*/ */
open class NpmProject(@Transient val compilation: KotlinJsCompilation) { open class NpmProject(@Transient val compilation: KotlinJsCompilation) {
val name: String val name: String by lazy {
get() = buildNpmProjectName() buildNpmProjectName()
}
val nodeJs val nodeJs by lazy {
get() = NodeJsRootPlugin.apply(project.rootProject) NodeJsRootPlugin.apply(project.rootProject)
}
val dir: File val dir: File
get() = nodeJs.projectPackagesDir.resolve(name) get() = nodeJs.projectPackagesDir.resolve(name)
@@ -90,7 +92,7 @@ open class NpmProject(@Transient val compilation: KotlinJsCompilation) {
* Require [request] nodejs module and return canonical path to it's main js file. * Require [request] nodejs module and return canonical path to it's main js file.
*/ */
fun require(request: String): String { fun require(request: String): String {
nodeJs.npmResolutionManager.requireAlreadyInstalled(project) // nodeJs.npmResolutionManager.requireAlreadyInstalled(project)
return modules.require(request) return modules.require(request)
} }
@@ -91,7 +91,8 @@ internal class KotlinCompilationNpmResolver(
val project get() = target.project val project get() = target.project
@Transient @Transient
val packageJsonTaskHolder = KotlinPackageJsonTask.create(compilation) val packageJsonTaskHolder: TaskProvider<KotlinPackageJsonTask>? =
KotlinPackageJsonTask.create(compilation)
@Transient @Transient
val publicPackageJsonTaskHolder: TaskProvider<PublicPackageJsonTask> = val publicPackageJsonTaskHolder: TaskProvider<PublicPackageJsonTask> =
@@ -150,7 +151,7 @@ internal class KotlinCompilationNpmResolver(
@Synchronized @Synchronized
fun getResolutionOrResolveIfForced(): KotlinCompilationNpmResolution? { fun getResolutionOrResolveIfForced(): KotlinCompilationNpmResolution? {
if (resolution != null) return resolution if (resolution != null) return resolution
if (packageJsonTaskHolder.get().state.upToDate) return resolve(skipWriting = true) if (packageJsonTaskHolder == null || packageJsonTaskHolder.get().state.upToDate) return resolve(skipWriting = true)
if (resolver.forceFullResolve && resolution == null) { if (resolver.forceFullResolve && resolution == null) {
// need to force all NPM tasks to be configured in IDEA import // need to force all NPM tasks to be configured in IDEA import
project.tasks.implementing(RequiresNpmDependencies::class).all {} project.tasks.implementing(RequiresNpmDependencies::class).all {}
@@ -112,7 +112,10 @@ constructor(
} }
fun useKarma() = useKarma {} fun useKarma() = useKarma {}
fun useKarma(body: KotlinKarma.() -> Unit) = use(KotlinKarma(compilation), body) fun useKarma(body: KotlinKarma.() -> Unit) = use(
KotlinKarma(compilation, services),
body
)
fun useKarma(fn: Closure<*>) { fun useKarma(fn: Closure<*>) {
useKarma { useKarma {
ConfigureUtil.configure(fn, this) ConfigureUtil.configure(fn, this)
@@ -10,6 +10,7 @@ import jetbrains.buildServer.messages.serviceMessages.BaseTestSuiteMessage
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.tasks.testing.TestResultProcessor import org.gradle.api.internal.tasks.testing.TestResultProcessor
import org.gradle.internal.logging.progress.ProgressLogger import org.gradle.internal.logging.progress.ProgressLogger
import org.gradle.internal.service.ServiceRegistry
import org.gradle.process.ProcessForkOptions import org.gradle.process.ProcessForkOptions
import org.gradle.process.internal.ExecHandle import org.gradle.process.internal.ExecHandle
import org.jetbrains.kotlin.gradle.internal.LogType import org.jetbrains.kotlin.gradle.internal.LogType
@@ -35,7 +36,7 @@ import org.jetbrains.kotlin.gradle.utils.property
import org.slf4j.Logger import org.slf4j.Logger
import java.io.File import java.io.File
class KotlinKarma(override val compilation: KotlinJsCompilation) : class KotlinKarma(override val compilation: KotlinJsCompilation, private val services: ServiceRegistry) :
KotlinJsTestFramework { KotlinJsTestFramework {
private val project: Project = compilation.target.project private val project: Project = compilation.target.project
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
@@ -404,7 +405,7 @@ class KotlinKarma(override val compilation: KotlinJsCompilation) :
lateinit var progressLogger: ProgressLogger lateinit var progressLogger: ProgressLogger
override fun wrapExecute(body: () -> Unit) { override fun wrapExecute(body: () -> Unit) {
project.operation("Running and building tests with karma and webpack") { services.operation("Running and building tests with karma and webpack") {
progressLogger = this progressLogger = this
body() body()
} }
@@ -10,6 +10,7 @@ import org.gradle.api.Incubating
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty import org.gradle.api.file.RegularFileProperty
import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.FileResolver
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.plugins.BasePluginConvention import org.gradle.api.plugins.BasePluginConvention
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
@@ -44,9 +45,11 @@ constructor(
init { init {
// TODO: temporary workaround for configuration cache enabled builds // TODO: temporary workaround for configuration cache enabled builds
disableTaskOnConfigurationCacheBuild { nodeJs.npmResolutionManager.toString() } // disableTaskOnConfigurationCacheBuild { nodeJs.npmResolutionManager.toString() }
} }
private val npmProject = compilation.npmProject
@get:Inject @get:Inject
open val fileResolver: FileResolver open val fileResolver: FileResolver
get() = injected get() = injected
@@ -97,7 +100,7 @@ constructor(
@get:OutputFile @get:OutputFile
open val configFile: File by lazy { open val configFile: File by lazy {
compilation.npmProject.dir.resolve("webpack.config.js") npmProject.dir.resolve("webpack.config.js")
} }
@Input @Input
@@ -137,21 +140,30 @@ constructor(
open val outputFile: File open val outputFile: File
get() = destinationDirectory.resolve(outputFileName) get() = destinationDirectory.resolve(outputFileName)
open val configDirectory: File? private val projectDir = project.projectDir
@Optional @InputDirectory get() = project.projectDir.resolve("webpack.config.d").takeIf { it.isDirectory }
@get:Optional
@get:InputDirectory
open val configDirectory: File? by lazy {
projectDir.resolve("webpack.config.d").takeIf { it.isDirectory }
}
@Input @Input
var report: Boolean = false var report: Boolean = false
private val projectReportsDir = project.reportsDir
open val reportDir: File open val reportDir: File
@Internal get() = reportDirProvider.get() @Internal get() = reportDirProvider.get()
@OutputDirectory @get:OutputDirectory
open val reportDirProvider: Provider<File> = entryProperty open val reportDirProvider: Provider<File> by lazy {
.map { it.asFile.nameWithoutExtension } entryProperty
.map { .map { it.asFile.nameWithoutExtension }
project.reportsDir.resolve("webpack").resolve(it) .map {
} projectReportsDir.resolve("webpack").resolve(it)
}
}
open val evaluatedConfigFile: File open val evaluatedConfigFile: File
@Internal get() = evaluatedConfigFileProvider.get() @Internal get() = evaluatedConfigFileProvider.get()
@@ -217,7 +229,8 @@ constructor(
.forEach { it(config) } .forEach { it(config) }
return KotlinWebpackRunner( return KotlinWebpackRunner(
compilation.npmProject, npmProject,
logger,
configFile, configFile,
execHandleFactory, execHandleFactory,
bin, bin,
@@ -237,7 +250,8 @@ constructor(
@TaskAction @TaskAction
fun doExecute() { fun doExecute() {
nodeJs.npmResolutionManager.checkRequiredDependencies(this) println(services.get(org.gradle.internal.logging.progress.ProgressLoggerFactory::class.java))
// nodeJs.npmResolutionManager?.checkRequiredDependencies(this)
val runner = createRunner() val runner = createRunner()
@@ -258,7 +272,7 @@ constructor(
progressReporter = true, progressReporter = true,
progressReporterPathFilter = nodeJs.rootPackageDir.absolutePath progressReporterPathFilter = nodeJs.rootPackageDir.absolutePath
) )
).execute() ).execute(services)
} }
} }
@@ -5,7 +5,9 @@
package org.jetbrains.kotlin.gradle.targets.js.webpack package org.jetbrains.kotlin.gradle.targets.js.webpack
import org.gradle.api.logging.Logger
import org.gradle.internal.logging.progress.ProgressLogger import org.gradle.internal.logging.progress.ProgressLogger
import org.gradle.internal.service.ServiceRegistry
import org.gradle.process.ExecSpec import org.gradle.process.ExecSpec
import org.gradle.process.internal.ExecHandle import org.gradle.process.internal.ExecHandle
import org.gradle.process.internal.ExecHandleFactory import org.gradle.process.internal.ExecHandleFactory
@@ -18,6 +20,7 @@ import java.io.File
internal data class KotlinWebpackRunner( internal data class KotlinWebpackRunner(
val npmProject: NpmProject, val npmProject: NpmProject,
val logger: Logger,
val configFile: File, val configFile: File,
val execHandleFactory: ExecHandleFactory, val execHandleFactory: ExecHandleFactory,
val tool: String, val tool: String,
@@ -25,7 +28,7 @@ internal data class KotlinWebpackRunner(
val nodeArgs: List<String>, val nodeArgs: List<String>,
val config: KotlinWebpackConfig val config: KotlinWebpackConfig
) { ) {
fun execute() = npmProject.project.execWithErrorLogger("webpack") { execAction, progressLogger -> fun execute(services: ServiceRegistry) = services.execWithErrorLogger("webpack") { execAction, progressLogger ->
configureExec( configureExec(
execAction, execAction,
progressLogger progressLogger
@@ -47,7 +50,6 @@ internal data class KotlinWebpackRunner(
clientType: LogType, clientType: LogType,
progressLogger: ProgressLogger? progressLogger: ProgressLogger?
): TeamCityMessageCommonClient { ): TeamCityMessageCommonClient {
val logger = npmProject.project.logger
return TeamCityMessageCommonClient(clientType, logger) return TeamCityMessageCommonClient(clientType, logger)
.apply { .apply {
if (progressLogger != null) { if (progressLogger != null) {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.js.yarn package org.jetbrains.kotlin.gradle.targets.js.yarn
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.jetbrains.kotlin.gradle.internal.execWithProgress import org.jetbrains.kotlin.gradle.internal.execWithProgress
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
@@ -32,7 +33,7 @@ abstract class YarnBasics : NpmApi {
val nodeJs = NodeJsRootPlugin.apply(project) val nodeJs = NodeJsRootPlugin.apply(project)
val yarnPlugin = YarnPlugin.apply(project) val yarnPlugin = YarnPlugin.apply(project)
project.execWithProgress(description) { exec -> (project as ProjectInternal).services.execWithProgress(description) { exec ->
exec.executable = nodeJs.requireConfigured().nodeExecutable exec.executable = nodeJs.requireConfigured().nodeExecutable
exec.args = listOf(yarnPlugin.requireConfigured().home.resolve("bin/yarn.js").absolutePath) + exec.args = listOf(yarnPlugin.requireConfigured().home.resolve("bin/yarn.js").absolutePath) +
args + args +