Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix relative path calculation #1143

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions common-util/src/main/kotlin/com/google/devtools/ksp/KspOptions.kt
Expand Up @@ -27,6 +27,7 @@ class KspOptions(
val projectBaseDir: File,
val compileClasspath: List<File>,
val javaSourceRoots: List<File>,
val buildDir: File,

val classOutputDir: File,
val javaOutputDir: File,
Expand Down Expand Up @@ -58,6 +59,7 @@ class KspOptions(
var projectBaseDir: File? = null
val compileClasspath: MutableList<File> = mutableListOf()
val javaSourceRoots: MutableList<File> = mutableListOf()
var buildDir: File? = null

var classOutputDir: File? = null
var javaOutputDir: File? = null
Expand Down Expand Up @@ -91,6 +93,7 @@ class KspOptions(
requireNotNull(projectBaseDir) { "A non-null projectBaseDir must be provided" },
compileClasspath,
javaSourceRoots,
requireNotNull(buildDir) { "A non-null buildDir must be provided" },
requireNotNull(classOutputDir) { "A non-null classOutputDir must be provided" },
requireNotNull(javaOutputDir) { "A non-null javaOutputDir must be provided" },
requireNotNull(kotlinOutputDir) { "A non-null kotlinOutputDir must be provided" },
Expand Down Expand Up @@ -181,6 +184,13 @@ enum class KspCliOption(
false
),

BUILD_DIR_OPTION(
"buildDir",
"<buildDir>",
"path to build dirs",
false
),

KSP_OUTPUT_DIR_OPTION(
"kspOutputDir",
"<kspOutputDir>",
Expand Down Expand Up @@ -288,6 +298,7 @@ fun KspOptions.Builder.processOption(option: KspCliOption, value: String) = when
KspCliOption.CACHES_DIR_OPTION -> cachesDir = File(value)
KspCliOption.KSP_OUTPUT_DIR_OPTION -> kspOutputDir = File(value)
KspCliOption.PROJECT_BASE_DIR_OPTION -> projectBaseDir = File(value)
KspCliOption.BUILD_DIR_OPTION -> buildDir = File(value)
KspCliOption.PROCESSING_OPTIONS_OPTION -> {
val (k, v) = value.split('=', ignoreCase = false, limit = 2)
processingOptions.put(k, v)
Expand Down
Expand Up @@ -33,6 +33,7 @@ class CodeGeneratorImpl(
private val kotlinDir: File,
private val resourcesDir: File,
private val projectBase: File,
private val buildDir: File,
private val anyChangesWildcard: KSFile,
private val allSources: List<KSFile>,
private val isIncremental: Boolean
Expand Down Expand Up @@ -93,7 +94,7 @@ class CodeGeneratorImpl(
) {
val path = pathOf(packageName, fileName, extensionName)
val files = classes.map {
it.containingFile ?: NoSourceFile(projectBase, it.qualifiedName?.asString().toString())
it.containingFile ?: NoSourceFile(buildDir, it.qualifiedName?.asString().toString())
}
associate(files, path, extensionToDirectory(extensionName))
}
Expand Down Expand Up @@ -154,18 +155,27 @@ class CodeGeneratorImpl(
associate(sources, file)
}

private val File.relativeFile: File
get() {
val buildDirPrefix = if (buildDir.path.startsWith("/")) buildDir.path else buildDir.path.replace("\\", "/")
return if (this.startsWith(buildDirPrefix))
relativeTo(buildDir)
else
relativeTo(projectBase)
}

private fun associate(sources: List<KSFile>, outputPath: File) {
if (!isIncremental)
return

val output = outputPath.relativeTo(projectBase)
val output = outputPath.relativeTo(buildDir)
sources.forEach { source ->
sourceToOutputs.getOrPut(File(source.filePath).relativeTo(projectBase)) { mutableSetOf() }.add(output)
sourceToOutputs.getOrPut(File(source.filePath).relativeFile) { mutableSetOf() }.add(output)
}
}

val outputs: Set<File>
get() = fileMap.values.mapTo(mutableSetOf()) { it.relativeTo(projectBase) }
get() = fileMap.values.mapTo(mutableSetOf()) { it.relativeTo(buildDir) }

override val generatedFile: Collection<File>
get() = fileOutputStreamMap.keys.map { fileMap[it]!! }
Expand Down
Expand Up @@ -30,6 +30,7 @@ class CodeGeneratorImplTest {
kotlinDir,
resourcesDir,
baseDir,
baseDir,
AnyChanges(baseDir),
emptyList(),
true
Expand Down
Expand Up @@ -166,9 +166,17 @@ object symbolCollector : KSDefaultVisitor<(LookupSymbol) -> Unit, Unit>() {
}
}

internal class RelativeFileToPathConverter(val baseDir: File) : FileToPathConverter {
internal class RelativeFileToPathConverter(
val baseDir: File,
val buildDir: File,
) : FileToPathConverter {
override fun toPath(file: File): String = file.path
override fun toFile(path: String): File = File(path).relativeTo(baseDir)
override fun toFile(path: String): File =
if (path.startsWith(buildDir.path)) {
File(path).relativeTo(buildDir)
} else {
File(path).relativeTo(baseDir)
}
}

class IncrementalContext(
Expand All @@ -194,6 +202,7 @@ class IncrementalContext(
private val rebuild = !cachesUpToDateFile.exists()

private val baseDir = options.projectBaseDir
private val buildDir = options.buildDir

private val logsDir = File(options.cachesDir, "logs").apply { mkdirs() }
private val buildTime = Date().time
Expand All @@ -206,7 +215,7 @@ class IncrementalContext(
// Disable incremental processing if somehow DualLookupTracker failed to be registered.
// This may happen when a platform hasn't support incremental compilation yet. E.g, Common / Metadata.
private val isIncremental = options.incremental && lookupTracker is DualLookupTracker
private val PATH_CONVERTER = RelativeFileToPathConverter(baseDir)
private val PATH_CONVERTER = RelativeFileToPathConverter(baseDir, buildDir)

private val symbolLookupTracker = (lookupTracker as? DualLookupTracker)?.symbolTracker ?: LookupTracker.DO_NOTHING
private val symbolLookupCacheDir = File(options.cachesDir, "symbolLookups")
Expand All @@ -219,7 +228,17 @@ class IncrementalContext(

private val sourceToOutputsMap = FileToFilesMap(File(options.cachesDir, "sourceToOutputs"))

private fun String.toRelativeFile() = File(this).relativeTo(baseDir)
// Ugly, but better than copying the private logics out of stdlib.
// TODO: get rid of `relativeTo` if possible.
private fun String.toRelativeFile(): File {
val buildDirPrefix = if (buildDir.path.startsWith("/")) buildDir.path else buildDir.path.replace("\\", "/")
return if (this.startsWith(buildDirPrefix)) {
File(this).relativeTo(buildDir)
} else {
File(this).relativeTo(baseDir)
}
}

private val KSFile.relativeFile
get() = filePath.toRelativeFile()

Expand Down Expand Up @@ -437,11 +456,11 @@ class IncrementalContext(
}

private fun updateOutputs(outputs: Set<File>, cleanOutputs: Collection<File>) {
val outRoot = options.kspOutputDir
val bakRoot = File(options.cachesDir, "backups")

fun File.abs() = File(baseDir, path)
fun File.bak() = File(bakRoot, abs().toRelativeString(outRoot))
// Assuming that output is always in buildDir.
fun File.abs() = File(buildDir, path)
fun File.bak() = File(bakRoot, path)

// Copy recursively, including last-modified-time of file and its parent dirs.
//
Expand Down
Expand Up @@ -206,6 +206,7 @@ abstract class AbstractKotlinSymbolProcessingExtension(
options.kotlinOutputDir,
options.resourceOutputDir,
options.projectBaseDir,
options.buildDir,
anyChangesWildcard,
ksFiles,
options.incremental
Expand Down
Expand Up @@ -63,6 +63,7 @@ abstract class AbstractKSPCompilerPluginTest : AbstractKSPTest(FrontendKinds.Cla
kotlinOutputDir = File(testRoot, "kspTest/src/main/kotlin")
resourceOutputDir = File(testRoot, "kspTest/src/main/resources")
projectBaseDir = testRoot
buildDir = testRoot
cachesDir = File(testRoot, "kspTest/kspCaches")
kspOutputDir = File(testRoot, "kspTest")
}.build(),
Expand Down
Expand Up @@ -163,6 +163,10 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
"returnOkOnError",
project.findProperty("ksp.return.ok.on.error")?.toString() ?: "true"
)
options += SubpluginOption(
"buildDir",
project.project.buildDir.canonicalPath
)

kspExtension.apOptions.forEach {
options += SubpluginOption("apoption", "${it.key}=${it.value}")
Expand Down
Expand Up @@ -38,6 +38,7 @@ class KSPCmdLineOptionsIT {
"-Xplugin=${kspApiJar.absolutePath}",
"-P", "plugin:$kspPluginId:apclasspath=${processorJar.absolutePath}",
"-P", "plugin:$kspPluginId:projectBaseDir=${project.root}/build",
"-P", "plugin:$kspPluginId:buildDir=${project.root}/build",
"-P", "plugin:$kspPluginId:classOutputDir=${project.root}/build",
"-P", "plugin:$kspPluginId:javaOutputDir=${project.root}/build/out",
"-P", "plugin:$kspPluginId:kotlinOutputDir=${project.root}/build/out",
Expand Down
Expand Up @@ -43,8 +43,8 @@ class PlaygroundIT {

@Test
fun testPlayground() {
// FIXME: `clean` fails to delete files on windows.
Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
// FIXME: `clean` fails to delete files on windows.
val gradleRunner = GradleRunner.create().withProjectDir(project.root)
gradleRunner.buildAndCheck("clean", "build")
gradleRunner.buildAndCheck("clean", "build")
Expand All @@ -65,6 +65,28 @@ class PlaygroundIT {
project.restore("workload/build.gradle.kts")
}

@Test
fun testArbitraryBuildDir() {
Assume.assumeTrue(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
val gradleRunner = GradleRunner.create().withProjectDir(project.root)

File(project.root, "workload/build.gradle.kts")
.appendText("project.buildDir = File(\"D:/build/\")")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that it does seem like there is only one C drive on github windows runners, therefore this test might not be testable on github runners.

val result = gradleRunner.withArguments("build").build()

Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:build")?.outcome)

val artifact = File("D:/build/libs/workload-1.0-SNAPSHOT.jar")
Assert.assertTrue(artifact.exists())

JarFile(artifact).use { jarFile ->
Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0)
Assert.assertTrue(jarFile.getEntry("hello/HELLO.class").size > 0)
Assert.assertTrue(jarFile.getEntry("g/G.class").size > 0)
Assert.assertTrue(jarFile.getEntry("com/example/AClassBuilder.class").size > 0)
}
}

@Test
fun testAllowSourcesFromOtherPlugins() {
fun checkGBuilder() {
Expand Down
Expand Up @@ -71,6 +71,7 @@ class KotlinSymbolProcessing(
options.kotlinOutputDir,
options.resourceOutputDir,
options.projectBaseDir,
options.buildDir,
anyChangesWildcard,
ksFiles,
options.incremental
Expand Down
Expand Up @@ -88,6 +88,7 @@ abstract class AbstractKSPAATest : AbstractKSPTest(FrontendKinds.FIR) {
kotlinOutputDir = File(testRoot, "kspTest/src/main/kotlin")
resourceOutputDir = File(testRoot, "kspTest/src/main/resources")
projectBaseDir = testRoot
buildDir = File(testRoot, "kspTest/build")
cachesDir = File(testRoot, "kspTest/kspCaches")
kspOutputDir = File(testRoot, "kspTest")
}.build()
Expand Down