Skip to content
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.
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 @@ -21,6 +21,7 @@ import com.grab.grazel.bazel.starlark.StatementsBuilder
import com.grab.grazel.bazel.starlark.array
import com.grab.grazel.bazel.starlark.load
import com.grab.grazel.bazel.starlark.quote
import com.grab.grazel.util.KSP_MAVEN

/**
* Data holder for KSP processor dependencies.
Expand Down Expand Up @@ -72,7 +73,7 @@ fun kspPluginTarget(processor: KspProcessor): BazelDependency =
private fun KspProcessor.toMavenLabel(): String {
val groupPart = group.replace(".", "_").replace("-", "_")
val namePart = name.replace(".", "_").replace("-", "_")
return "@ksp_maven//:${groupPart}_$namePart"
return "@${KSP_MAVEN}//:${groupPart}_$namePart"
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ internal class DefaultGradleProjectInfo(

override val hasDagger: Boolean by lazy {
workspaceDependencies
.result
.variantDeps
.values
.parallelStream()
.flatMap { it.stream() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.grab.grazel.gradle.dependencies.model.allDependencies
import com.grab.grazel.gradle.dependencies.model.merge
import com.grab.grazel.gradle.dependencies.model.versionInfo
import com.grab.grazel.gradle.variant.DEFAULT_VARIANT
import com.grab.grazel.util.KSP_MAVEN
import com.grab.grazel.util.fromJson
import org.gradle.api.file.RegularFile
import java.util.stream.Collector
Expand Down Expand Up @@ -152,8 +153,8 @@ internal class ComputeWorkspaceDependencies {
)
).toMap()

// Aggregate KSP deps across ALL variants into single bucket
val kspClassPath = compileDependenciesJsons
// Aggregate KSP deps across ALL variants into single bucket, deduplicated by max version
val kspDeps: List<ResolvedDependency> = compileDependenciesJsons
.parallelStream()
.map<ResolveDependenciesResult>(::fromJson)
.flatMap { it.dependencies.getOrDefault(KSP.name, emptySet()).stream() }
Expand All @@ -162,7 +163,7 @@ internal class ComputeWorkspaceDependencies {
ResolvedDependency::shortId,
maxVersionReducer()
)
)
).values.sortedBy(ResolvedDependency::id)

// Clear maps to allow GC
defaultFlatClasspath.clear()
Expand All @@ -171,8 +172,8 @@ internal class ComputeWorkspaceDependencies {
defaultClasspath.clear()
classPaths.clear()
return WorkspaceDependencies(
result = reducedFinalClasspath,
kspResult = kspClassPath,
variantDeps = reducedFinalClasspath,
aggregatedRepos = if (kspDeps.isNotEmpty()) mapOf(KSP_MAVEN to kspDeps) else emptyMap(),
transitiveClasspath = transitiveClasspath
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,13 @@ internal class DefaultDependenciesDataSource @Inject constructor(
project: Project,
variantKey: VariantGraphKey
): List<BazelDependency> {
val validProcessorShortIds = dependencyResolutionService.get().getValidKspProcessorShortIds()
val grazelVariant: Variant<*> = findGrazelVariantByKey(project, variantKey)
return grazelVariant.kspConfiguration
.asSequence()
.flatMap { config -> config.allDependencies.filterIsInstance<ExternalDependency>() }
.distinctBy { it.shortId }
.filter { dep -> "${dep.module.group}:${dep.module.name}" in validProcessorShortIds }
.map { dep ->
// Create a minimal KspProcessor just for target name generation
val processor = KspProcessor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.grab.grazel.gradle.dependencies.DependencyResolutionService.Companion
import com.grab.grazel.gradle.dependencies.model.WorkspaceDependencies
import com.grab.grazel.tasks.internal.ComputeWorkspaceDependenciesTask
import com.grab.grazel.tasks.internal.GenerateBazelScriptsTask
import com.grab.grazel.util.KSP_MAVEN
import com.grab.grazel.util.fromJson
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
Expand Down Expand Up @@ -63,6 +64,13 @@ internal interface DependencyResolutionService : BuildService<DependencyResoluti
*/
fun getTransitiveDependencies(shortId: String): Set<String>

/**
* Returns shortIds of KSP processors that have a valid [kt_ksp_plugin] target generated
* (i.e. whose processorClass was successfully extracted from the processor JAR).
* Processors absent from this set should not generate Bazel target references.
*/
fun getValidKspProcessorShortIds(): Set<String>

fun init(workspaceDependenciesJson: File): WorkspaceDependencies

companion object {
Expand All @@ -80,6 +88,15 @@ internal abstract class DefaultDependencyResolutionService : DependencyResolutio
private val mavenStoreLock = Mutex()
private val transitiveDepsStoreLock = Mutex()

override fun getValidKspProcessorShortIds(): Set<String> =
workspaceDependencies
?.aggregatedRepos
?.get(KSP_MAVEN)
?.filter { it.processorClass != null }
?.map { it.shortId }
?.toSet()
?: emptySet()

override fun getMavenDependency(
variants: Set<String>,
group: String,
Expand Down Expand Up @@ -116,12 +133,18 @@ internal abstract class DefaultDependencyResolutionService : DependencyResolutio
mavenStoreLock.withLock {
if (mavenInstallStore == null) {
mavenInstallStore = DefaultMavenInstallStore().apply {
workspaceDependencies.result.forEach { (variantName, dependencies) ->
workspaceDependencies.variantDeps.forEach { (variantName, dependencies) ->
dependencies.forEach { dependency ->
val (group, name, _) = dependency.id.split(":")
set(variantName, group, name)
}
}
workspaceDependencies.aggregatedRepos.forEach { (repoName, dependencies) ->
dependencies.forEach { dependency ->
val (group, name, _) = dependency.id.split(":")
set(repoName, group, name)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.grab.grazel.gradle.dependencies.model

import com.grab.grazel.bazel.starlark.BazelDependency
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.Versioned
Expand Down Expand Up @@ -124,8 +125,19 @@ internal data class OverrideTarget(

@Serializable
internal data class WorkspaceDependencies(
val result: Map<String, List<ResolvedDependency>>,
val kspResult: Map<String, ResolvedDependency> = emptyMap(),
/**
* Compile/runtime dependencies grouped by Gradle variant name (e.g. "default", "demoDebug").
* Each entry maps 1:1 to a `maven_install` repository via [toMavenRepoName].
* Keys are variant names, not repo names.
*/
@SerialName("result")
val variantDeps: Map<String, List<ResolvedDependency>>,
/**
* Dependencies aggregated across all variants into a single repo, keyed directly by
* maven repo name (e.g. "ksp_maven"). Unlike [variantDeps], these do not go through
* the variant deduplication/reduction pipeline.
*/
val aggregatedRepos: Map<String, List<ResolvedDependency>> = emptyMap(),
val transitiveClasspath: Map<String, Set<String>> = emptyMap()
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.grab.grazel.GrazelExtension
import com.grab.grazel.bazel.exec.bazelCommand
import com.grab.grazel.bazel.starlark.BazelDependency.MavenDependency
import com.grab.grazel.di.GradleServices
import com.grab.grazel.gradle.dependencies.model.ResolvedDependency
import com.grab.grazel.gradle.dependencies.model.WorkspaceDependencies
import com.grab.grazel.util.NoOpProgressLogger
import com.grab.grazel.util.WORKSPACE
Expand Down Expand Up @@ -138,15 +139,11 @@ constructor(
return true
} else {
failWhenOutOfDate(workspaceFile, true)
return workspaceDependencies.result.any { (repo, deps) ->
// Build any dependency with nobuild and check it fails due to maven_install.json
// being out of date

fun checkRepoOutOfDate(mavenRepo: String, deps: List<ResolvedDependency>): Boolean {
val dep = deps.first()
val (group, name) = dep.shortId.split(":")
val mavenRepo = repo.toMavenRepoName()

progress.progress("Checking $mavenRepo's pin status")

val target = MavenDependency(
repo = mavenRepo,
group = group,
Expand All @@ -167,8 +164,16 @@ constructor(
ignoreExit = true,
errorOutputStream = outputStream,
)
outputStream.isOutOfDate
}.also {
return outputStream.isOutOfDate
}

return (
workspaceDependencies.variantDeps.any { (variantName, deps) ->
checkRepoOutOfDate(variantName.toMavenRepoName(), deps)
} || workspaceDependencies.aggregatedRepos.any { (repoName, deps) ->
checkRepoOutOfDate(repoName, deps)
}
).also {
// Revert the changes to the workspace file
failWhenOutOfDate(workspaceFile, false)
progress.completed()
Expand Down Expand Up @@ -207,8 +212,13 @@ constructor(

if (shouldRun) {
logger.quiet("Repinning all artifacts".ansiCyan)
val pinScripts = workspaceDependencies.result.mapValues { (repo, _) ->
val mavenRepoName = repo.toMavenRepoName()
val allRepos: Map<String, List<ResolvedDependency>> = buildMap {
workspaceDependencies.variantDeps.forEach { (variantName, deps) ->
put(variantName.toMavenRepoName(), deps)
}
putAll(workspaceDependencies.aggregatedRepos)
}
val pinScripts = allRepos.mapValues { (mavenRepoName, _) ->
val scriptPath = layout
.buildDirectory
.file("grazel/maven/${mavenRepoName}_pin.sh").apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ constructor(
externalArtifacts: Set<String>,
externalRepositories: Set<String>,
): Set<MavenInstallData> {
val result = workspaceDependencies.result
val result = workspaceDependencies.variantDeps
.mapNotNullTo(TreeSet(compareBy(MavenInstallData::name))) { (variantName, artifacts) ->
val mavenInstallName = variantName.toMavenRepoName()
val allArtifacts = artifacts + grazelExtension
Expand Down Expand Up @@ -125,24 +125,23 @@ constructor(
)
}

// Generate ksp_maven from aggregated KSP deps
if (workspaceDependencies.kspResult.isNotEmpty()) {
val kspArtifacts = workspaceDependencies.kspResult.values.toList()
val kspMavenInstallArtifacts = kspArtifacts
// Generate maven_install entries for aggregated repos (e.g. ksp_maven)
workspaceDependencies.aggregatedRepos.forEach { (repoName, artifacts) ->
val mavenInstallArtifacts = artifacts
.mapTo(TreeSet(compareBy(MavenInstallArtifact::id)), ::toMavenInstallArtifact)
if (mavenInstallArtifacts.isEmpty()) return@forEach

val kspRepositories = calculateRepositoriesIncludingTransitives(kspArtifacts)

val kspMavenInstallJson = layout
val repositories = calculateRepositoriesIncludingTransitives(artifacts)
val mavenInstallJson = layout
.projectDirectory
.file("ksp_maven_install.json").asFile
.file("${repoName}_install.json").asFile

result.add(
MavenInstallData(
name = "ksp_maven",
artifacts = kspMavenInstallArtifacts,
name = repoName,
artifacts = mavenInstallArtifacts,
externalArtifacts = emptySet(),
repositories = kspRepositories,
repositories = repositories,
externalRepositories = emptySet(),
jetifierConfig = JetifierConfig(isEnabled = false, artifacts = emptySet()),
failOnMissingChecksum = false,
Expand All @@ -151,8 +150,8 @@ constructor(
resolveTimeout = mavenInstallExtension.resolveTimeout,
artifactPinning = mavenInstallExtension.artifactPinning.enabled.get(),
versionConflictPolicy = mavenInstallExtension.versionConflictPolicy,
mavenInstallJson = kspMavenInstallJson.name,
isMavenInstallJsonEnabled = mavenInstallExtension.artifactPinning.enabled.get() && kspMavenInstallJson.exists(),
mavenInstallJson = mavenInstallJson.name,
isMavenInstallJsonEnabled = mavenInstallExtension.artifactPinning.enabled.get() && mavenInstallJson.exists(),
additionalCoursierOptions = mavenInstallExtension.additionalCoursierOptions.get()
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import com.grab.grazel.extension.KspProcessorConfig
import com.grab.grazel.gradle.GradleProjectInfo
import com.grab.grazel.gradle.dependencies.model.WorkspaceDependencies
import com.grab.grazel.migrate.BazelFileBuilder
import com.grab.grazel.util.KSP_MAVEN
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import javax.inject.Inject
import javax.inject.Singleton

Expand All @@ -40,6 +43,8 @@ internal class RootBazelFileBuilder(
private val workspaceDependencies: WorkspaceDependencies,
) : BazelFileBuilder {

private val logger: Logger = Logging.getLogger(RootBazelFileBuilder::class.java)

@Singleton
class Factory
@Inject
Expand Down Expand Up @@ -72,12 +77,18 @@ internal class RootBazelFileBuilder(
}

private fun buildKspProcessors(): Set<KspProcessor> {
if (workspaceDependencies.kspResult.isEmpty()) return emptySet()
val kspDeps = workspaceDependencies.aggregatedRepos[KSP_MAVEN] ?: return emptySet()
val kspProcessorConfigs = grazelExtension.rules.kotlin.ksp.processors
return workspaceDependencies.kspResult.values
return kspDeps
.mapNotNull { dep ->
val processorClass = dep.processorClass
if (processorClass.isNullOrEmpty()) return@mapNotNull null
if (processorClass.isNullOrEmpty()) {
logger.warn(
"Grazel: KSP processor ${dep.shortId} has no " +
"SymbolProcessorProvider service entry — skipping kt_ksp_plugin rule generation."
)
return@mapNotNull null
}
val (group, name, version) = dep.id.split(":")
val config = kspProcessorConfigs["$group:$name"] ?: KspProcessorConfig()
KspProcessor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ internal const val BUILD_BAZEL_IGNORE = "BUILD$BAZEL_IGNORE"

// Buildifier FILE NAME
internal const val BUILDIFIER = "buildifier"

internal const val KSP_MAVEN = "ksp_maven"
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class DefaultDependenciesDataSourceTest {
.get().apply {
populateMavenStore(
workspaceDependencies = WorkspaceDependencies(
result = buildMap {
variantDeps = buildMap {
put(
"debug", listOf(
from("com.android.support:appcompat-v7:28.0.0:debug:false:null"),
Expand Down Expand Up @@ -230,7 +230,7 @@ class DefaultDependenciesDataSourceTest {
(dependencyResolutionService as DefaultDependencyResolutionService).apply {
populateTransitiveDependenciesStore(
WorkspaceDependencies(
result = emptyMap(),
variantDeps = emptyMap(),
transitiveClasspath = mapOf(
"com.android.support:appcompat-v7" to setOf(
"com.android.support:support-v4:28.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class DefaultDependencyResolutionServiceTest {
)

return WorkspaceDependencies(
result = mapOf(
variantDeps = mapOf(
"variant1" to variant1Dependencies,
"variant2" to variant2Dependencies
),
Expand All @@ -282,7 +282,7 @@ class DefaultDependencyResolutionServiceTest {
)

return WorkspaceDependencies(
result = mapOf(
variantDeps = mapOf(
"variant1" to variant1Dependencies
),
transitiveClasspath = transitiveClasspath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class DefaultAndroidLibraryDataExtractorTest {

dependencyResolutionService.get().populateMavenStore(
workspaceDependencies = WorkspaceDependencies(
result = buildMap {
variantDeps = buildMap {
put(
"maven", listOf(
from("com.android.databinding:viewbinding:1.0.0:maven:false:null"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class DefaultArtifactPinnerTest {
val gradleServices = GradleServices.from(rootProject)
artifactPinner.shouldRunPinning(
workspace,
workspaceDependencies = WorkspaceDependencies(result = buildMap {
workspaceDependencies = WorkspaceDependencies(variantDeps = buildMap {
put(
DEFAULT_VARIANT,
listOf(ResolvedDependency.from("androidx.annotation:annotation:1.2.0:maven:false:null"))
Expand Down Expand Up @@ -146,7 +146,7 @@ class DefaultArtifactPinnerTest {
assertTrue("Pinning is done and maven install json is generated") {
artifactPinner.pinArtifacts(
workspace,
workspaceDependencies = WorkspaceDependencies(result = buildMap {
workspaceDependencies = WorkspaceDependencies(variantDeps = buildMap {
put(
DEFAULT_VARIANT,
listOf(ResolvedDependency.from("androidx.annotation:annotation:1.1.0:maven:false:null"))
Expand Down
Loading
Loading