Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,17 @@ fun getInputsForTask(
return try {
val inputs = mutableListOf<Any>()
val externalDependencies = mutableListOf<String>()
val dependentTaskOutputExtensions = mutableSetOf<String>()

// Collect outputs from dependent tasks
// Collect outputs from dependent tasks - group by extension for glob patterns
val tasksToProcess = dependsOnTasks ?: getDependsOnTask(task)
tasksToProcess.forEach { dependentTask ->
dependentTask.outputs.files.files.forEach { outputFile ->
if (isFileInWorkspace(outputFile, workspaceRoot)) {
val relativePath = toRelativePathOrGlob(outputFile, workspaceRoot)
inputs.add(mapOf("dependentTasksOutputFiles" to relativePath))
val extension = outputFile.extension
if (extension.isNotEmpty()) {
dependentTaskOutputExtensions.add(extension)
}
}
}
}
Expand All @@ -158,9 +161,12 @@ fun getInputsForTask(
}

// File matches gitignore pattern - treat as dependentTasksOutputFiles (build artifact)
// Group by extension for glob patterns
gitIgnoreClassifier.isIgnored(inputFile) -> {
val relativePathOrGlob = toRelativePathOrGlob(inputFile, workspaceRoot)
inputs.add(mapOf("dependentTasksOutputFiles" to relativePathOrGlob))
val extension = inputFile.extension
if (extension.isNotEmpty()) {
dependentTaskOutputExtensions.add(extension)
}
}

// Regular source file - add as direct input
Expand All @@ -170,7 +176,12 @@ fun getInputsForTask(
}
}

// Step 3: Add external dependencies if any
// Add consolidated dependentTasksOutputFiles entries using glob patterns by extension
dependentTaskOutputExtensions.forEach { extension ->
inputs.add(mapOf("dependentTasksOutputFiles" to "**/*.$extension"))
}

// Add external dependencies if any
if (externalDependencies.isNotEmpty()) {
inputs.add(mapOf("externalDependencies" to externalDependencies))
}
Expand All @@ -188,18 +199,6 @@ private fun isFileInWorkspace(file: File, workspaceRoot: String): Boolean {
return file.path.startsWith(workspaceRoot + File.separator)
}

/** Converts a file to a relative path. If it's a directory, returns a glob pattern. */
private fun toRelativePathOrGlob(file: File, workspaceRoot: String): String {
val relativePath = file.path.substring(workspaceRoot.length + 1)
val isFile = file.name.contains('.') || (file.exists() && file.isFile)

return if (isFile) {
relativePath
} else {
"$relativePath${File.separator}**${File.separator}*"
}
}

/**
* Get outputs for task
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,25 +152,22 @@ class ProcessTaskUtilsTest {

assertNotNull(result)

// Should contain dependentTasksOutputFiles for the output
assertTrue(
result!!.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "dist/output.jar" })
// Should contain consolidated dependentTasksOutputFiles glob pattern for jar extension
assertTrue(result!!.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.jar" })

// Should contain the non-conflicting input file
assertTrue(result.any { it == "{projectRoot}/src/main.kt" })
}

@Test
fun `test getInputsForTask directory vs file patterns`() {
fun `test getInputsForTask consolidates by extension`() {
val dependentTask = project.tasks.register("dependentTask").get()

// Add file output (should get exact path)
val outputFile = java.io.File("$workspaceRoot/dist/app.jar")
dependentTask.outputs.file(outputFile)

// Add directory output (should get /**/* pattern)
val outputDir = java.io.File("$workspaceRoot/dist/classes")
dependentTask.outputs.dir(outputDir)
// Add file outputs with different extensions
val jarFile = java.io.File("$workspaceRoot/dist/app.jar")
val classFile = java.io.File("$workspaceRoot/dist/classes/Main.class")
dependentTask.outputs.file(jarFile)
dependentTask.outputs.file(classFile)

val mainTask = project.tasks.register("mainTask").get()
mainTask.dependsOn(dependentTask)
Expand All @@ -182,15 +179,14 @@ class ProcessTaskUtilsTest {

assertNotNull(result)

// File should get exact path
assertTrue(
result!!.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "dist/app.jar" })
// Should have consolidated glob patterns by extension
assertTrue(result!!.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.jar" })
assertTrue(result.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.class" })

// Directory should get glob pattern
assertTrue(
result.any {
it is Map<*, *> && (it["dependentTasksOutputFiles"] as String).endsWith("/**/*")
})
// Should only have 2 dependentTasksOutputFiles entries (one per extension)
val dependentTasksOutputFilesCount =
result.count { it is Map<*, *> && it.containsKey("dependentTasksOutputFiles") }
assertEquals(2, dependentTasksOutputFilesCount)
}

@Test
Expand Down Expand Up @@ -232,14 +228,14 @@ class ProcessTaskUtilsTest {
assertNotNull(resultWithoutPreComputed)
assertEquals(resultWithPreComputed!!.size, resultWithoutPreComputed!!.size)

// Should contain dependentTasksOutputFiles for the dependent task output
// Should contain consolidated dependentTasksOutputFiles glob pattern
assertTrue(
resultWithPreComputed.any {
it is Map<*, *> && it["dependentTasksOutputFiles"] == "dist/output.jar"
it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.jar"
})
assertTrue(
resultWithoutPreComputed.any {
it is Map<*, *> && it["dependentTasksOutputFiles"] == "dist/output.jar"
it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.jar"
})

// Should contain the input file
Expand Down Expand Up @@ -285,7 +281,7 @@ class ProcessTaskUtilsTest {
}

@Test
fun `test dependentTasksOutputFiles generation with reused dependsOnTasks`() {
fun `test dependentTasksOutputFiles consolidation with multiple tasks`() {
val project = ProjectBuilder.builder().build()
val workspaceRoot = project.rootDir.path
val projectRoot = project.projectDir.path
Expand All @@ -296,14 +292,14 @@ class ProcessTaskUtilsTest {
dependentTask1.outputs.file(fileOutput)

val dependentTask2 = project.tasks.register("dependentTask2").get()
val dirOutput = java.io.File("$workspaceRoot/build/classes")
dependentTask2.outputs.dir(dirOutput)
val classFile = java.io.File("$workspaceRoot/build/classes/Main.class")
dependentTask2.outputs.file(classFile)

val dependentTask3 = project.tasks.register("dependentTask3").get()
val multipleOutputs =
listOf(
java.io.File("$workspaceRoot/reports/test.xml"),
java.io.File("$workspaceRoot/reports/coverage"))
java.io.File("$workspaceRoot/reports/another.jar"))
dependentTask3.outputs.files(multipleOutputs)

// Create main task that depends on all three
Expand Down Expand Up @@ -331,36 +327,20 @@ class ProcessTaskUtilsTest {

assertNotNull(result)

// Should contain dependentTasksOutputFiles for file output (exact path)
assertTrue(
result!!.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "dist/app.jar" })

// Should contain dependentTasksOutputFiles for directory output (with /**/* pattern)
assertTrue(
result.any {
it is Map<*, *> && (it["dependentTasksOutputFiles"] as String) == "build/classes/**/*"
})

// Should contain dependentTasksOutputFiles for test report file
assertTrue(
result.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "reports/test.xml" })

// Should contain dependentTasksOutputFiles for coverage directory (with /**/* pattern)
assertTrue(
result.any {
it is Map<*, *> &&
(it["dependentTasksOutputFiles"] as String) == "reports/coverage/**/*"
})
// Should have consolidated glob patterns by extension
assertTrue(result!!.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.jar" })
assertTrue(result.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.class" })
assertTrue(result.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.xml" })

// Should contain regular input files
assertTrue(result.any { it == "{projectRoot}/src/main.kt" })
assertTrue(result.any { it == "{projectRoot}/config/app.properties" })

// Verify we have the expected number of dependentTasksOutputFiles entries (4 outputs from 3
// tasks)
// Verify we have exactly 3 dependentTasksOutputFiles entries (one per unique extension: jar,
// class, xml)
val dependentTasksOutputFilesCount =
result.count { it is Map<*, *> && it.containsKey("dependentTasksOutputFiles") }
assertEquals(4, dependentTasksOutputFilesCount)
assertEquals(3, dependentTasksOutputFilesCount)

// Verify we have the expected number of regular input files (2)
val regularInputsCount = result.count { it is String && it.startsWith("{projectRoot}") }
Expand Down Expand Up @@ -410,17 +390,11 @@ class ProcessTaskUtilsTest {
// Config file should be regular input
assertTrue(result.any { it == "{projectRoot}/config/app.properties" })

// Build file should be dependentTasksOutputFiles (matches gitignore)
assertTrue(
result.any {
it is Map<*, *> && (it["dependentTasksOutputFiles"] as String).contains("build")
})
// Build file (class extension) should be consolidated into dependentTasksOutputFiles glob
assertTrue(result.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.class" })

// Log file should be dependentTasksOutputFiles (matches gitignore)
assertTrue(
result.any {
it is Map<*, *> && (it["dependentTasksOutputFiles"] as String).contains("log")
})
// Log file should be consolidated into dependentTasksOutputFiles glob
assertTrue(result.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.log" })
}

@Test
Expand All @@ -443,8 +417,8 @@ class ProcessTaskUtilsTest {
// Add inputs
val javaSource = java.io.File("$workspaceRoot/src/Main.java") // Not ignored
val compiledClass =
java.io.File("$workspaceRoot/dist/production/Main.class") // Ignored (*.class pattern)
val jarTarget = java.io.File("$workspaceRoot/dist/app.jar") // Ignored (target)
java.io.File("$workspaceRoot/dist/production/Main.class") // Ignored (dist directory)
val jarTarget = java.io.File("$workspaceRoot/dist/app.jar") // Ignored (dist directory)

mainTask.inputs.files(javaSource, compiledClass, jarTarget)

Expand All @@ -457,15 +431,10 @@ class ProcessTaskUtilsTest {

assertTrue(result!!.any { it == "{projectRoot}/src/Main.java" })

assertTrue(
result.any {
it is Map<*, *> && (it["dependentTasksOutputFiles"] as String).contains("Main.class")
})
// Both ignored files should be consolidated into glob patterns by extension
assertTrue(result.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.class" })

assertTrue(
result.any {
it is Map<*, *> && (it["dependentTasksOutputFiles"] as String).contains("dist")
})
assertTrue(result.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.jar" })
}
}

Expand All @@ -477,8 +446,8 @@ class ProcessTaskUtilsTest {
buildFile.writeText("// test build file")

val dependentTask = project.tasks.register("compile").get()
val outputFile = java.io.File("${project.rootDir.path}/build/classes")
dependentTask.outputs.dir(outputFile)
val outputFile = java.io.File("${project.rootDir.path}/build/classes/Main.class")
dependentTask.outputs.file(outputFile)

val mainTask = project.tasks.register("test").get()
mainTask.dependsOn(dependentTask)
Expand Down Expand Up @@ -515,14 +484,11 @@ class ProcessTaskUtilsTest {
assertEquals(1, dependsOn!!.size)
assertEquals("testProject:compile", dependsOn[0])

// Verify inputs contain both regular inputs and dependentTasksOutputFiles
// Verify inputs contain both regular inputs and consolidated dependentTasksOutputFiles
val inputs = result["inputs"] as? List<*>
assertNotNull(inputs)
assertTrue(inputs!!.any { it == "{projectRoot}/src/test.kt" })
assertTrue(
inputs.any {
it is Map<*, *> && (it["dependentTasksOutputFiles"] as String) == "build/classes/**/*"
})
assertTrue(inputs.any { it is Map<*, *> && it["dependentTasksOutputFiles"] == "**/*.class" })
}

@Nested
Expand Down
Loading