Skip to content

Commit

Permalink
fix: get closer to supporting isolated projects.
Browse files Browse the repository at this point in the history
Don't mutate another project's dependencies.
  • Loading branch information
autonomousapps committed Apr 3, 2024
1 parent ef5cc23 commit a4630ea
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ abstract class AbstractFunctionalSpec extends Specification {
}
}

// TODO only needed due to some CC issues in 7.4, remove an replace with above, once 7.5 becomes the minimum.
// TODO only needed due to some CC issues in 7.4, remove and replace with above, once 7.5 becomes the minimum.
protected static List<GradleVersion> gradleVersionsCC() {
return gradleVersions().collect { it == GradleVersions.minGradleVersion ? GRADLE_7_5 : it }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ abstract class AbstractProject extends AbstractGradleProject {
private static final String PRINT_ADVICE = "dependency.analysis.print.build.health=true"
protected static final String ADDITIONAL_PROPERTIES = GradleProperties.of(PRINT_ADVICE, NO_AUTO_APPLY)

/** Applies the 'org.jetbrains.kotlin.jvm' plugin. */
protected static final List<Plugin> kotlinOnly = [Plugins.kotlinNoVersion]

/** Applies the 'org.jetbrains.kotlin.jvm' and 'com.autonomousapps.dependency-analysis' plugins. */
protected static final List<Plugin> kotlin = [Plugins.kotlinNoVersion, Plugins.dependencyAnalysisNoVersion]

/** Applies the 'java-library' and 'com.autonomousapps.dependency-analysis' plugins. */
protected static final List<Plugin> javaLibrary = [Plugin.javaLibrary, Plugins.dependencyAnalysisNoVersion]

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.autonomousapps.jvm

import com.autonomousapps.jvm.projects.NoAutoApplyProject

import static com.autonomousapps.utils.Runner.build
import static com.google.common.truth.Truth.assertThat

/** Here we test what happens when some modules don't have DAGP applied. */
final class NoAutoApplySpec extends AbstractJvmSpec {

def "can do complete analysis with autoapply=false (#gradleVersion)"() {
given:
def project = new NoAutoApplyProject()
gradleProject = project.gradleProject

when:
build(gradleVersion, gradleProject.rootDir, 'buildHealth')

then:
assertThat(project.actualBuildHealth()).containsExactlyElementsIn(project.expectedBuildHealth())

where:
gradleVersion << gradleVersions()
}

def "can do targeted analysis with autoapply=false (#gradleVersion)"() {
given:
def project = new NoAutoApplyProject(':proj1')
gradleProject = project.gradleProject

when:
def result = build(gradleVersion, gradleProject.rootDir, 'buildHealth')

then:
assertThat(project.actualBuildHealth()).containsExactlyElementsIn(project.expectedBuildHealth())

and: 'No tasks from :proj1 were requested'
def proj1Tasks = result.tasks.findAll { it.path.startsWith(':proj1') }
assertThat(proj1Tasks).isEmpty()

where:
gradleVersion << gradleVersions()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.autonomousapps.jvm.projects

import com.autonomousapps.AbstractProject
import com.autonomousapps.kit.GradleProject
import com.autonomousapps.kit.Source
import com.autonomousapps.kit.SourceType
import com.autonomousapps.kit.gradle.Plugin
import com.autonomousapps.model.ProjectAdvice

import static com.autonomousapps.AdviceHelper.actualProjectAdvice
import static com.autonomousapps.AdviceHelper.emptyProjectAdviceFor
import static com.autonomousapps.utils.Strings.ensurePrefix

final class NoAutoApplyProject extends AbstractProject {

private final Set<String> noApplies
final GradleProject gradleProject

/**
* @param noApplies the set of projects (by path) that should not have DAGP applied.
*/
NoAutoApplyProject(String... noApplies = []) {
this.noApplies = noApplies.collect { ensurePrefix(it) } as Set<String>
gradleProject = build()
}

private GradleProject build() {
return newGradleProjectBuilder()
.withSubproject('proj1') { s ->
s.sources = proj1Sources
s.withBuildScript { bs ->
bs.plugins = plugins('proj1')
}
}
.withSubproject('proj2') { s ->
s.sources = proj2Sources
s.withBuildScript { bs ->
bs.plugins = plugins('proj2')
}
}
.withSubproject('proj3') { s ->
s.sources = proj3Sources
s.withBuildScript { bs ->
bs.plugins = plugins('proj3')
}
}
.write()
}

private List<Plugin> plugins(String projectPath) {
if (noApplies.contains(ensurePrefix(projectPath))) {
return kotlinOnly
} else {
return kotlin
}
}

def proj1Sources = [
new Source(
SourceType.KOTLIN, "One", "com/example/one",
"""\
package com.example.one
class One""".stripIndent()
)
]

def proj2Sources = [
new Source(
SourceType.KOTLIN, "Two", "com/example/two",
"""\
package com.example.two
class Two""".stripIndent()
)
]

def proj3Sources = [
new Source(
SourceType.KOTLIN, "Three", "com/example/three",
"""\
package com.example.three
class Three""".stripIndent()
)
]

Set<ProjectAdvice> actualBuildHealth() {
return actualProjectAdvice(gradleProject)
}

Set<ProjectAdvice> expectedBuildHealth() {
return [':proj1', ':proj2', ':proj3']
.findAll { !noApplies.contains(it) }
.collect { emptyProjectAdviceFor(it) }
}
}
15 changes: 15 additions & 0 deletions src/functionalTest/groovy/com/autonomousapps/utils/Strings.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.autonomousapps.utils

final class Strings {

private Strings() {
}

static String ensurePrefix(String string, String prefix = ':') {
if (string.startsWith(prefix)) {
return string
} else {
return prefix + string
}
}
}
21 changes: 14 additions & 7 deletions src/main/kotlin/com/autonomousapps/internal/artifacts/Publisher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ import org.gradle.api.provider.Provider
*/
internal class Publisher<T : Named>(
project: Project,
// TODO: ultimately this should not need to be exposed
val declarableName: String,
attr: Attr<T>,
val declarableName: String,
) {

companion object {
Expand All @@ -40,11 +39,19 @@ internal class Publisher<T : Named>(
project: Project,
artifact: DagpArtifacts.Kind,
): Publisher<DagpArtifacts> {
return Publisher(
project,
artifact.declarableName,
Attr(DagpArtifacts.DAGP_ARTIFACTS_ATTRIBUTE, artifact.artifactName)
)
return if (project.extensions.extraProperties.has(artifact.artifactName)) {
@Suppress("UNCHECKED_CAST")
project.extensions.extraProperties[artifact.artifactName] as Publisher<DagpArtifacts>
} else {
Publisher(
project = project,
declarableName = artifact.declarableName,
attr = Attr(DagpArtifacts.DAGP_ARTIFACTS_ATTRIBUTE, artifact.artifactName)
).also {
// memoize the value
project.extensions.extraProperties[artifact.artifactName] = it
}
}
}
}

Expand Down
8 changes: 0 additions & 8 deletions src/main/kotlin/com/autonomousapps/subplugin/ProjectPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -951,14 +951,6 @@ internal class ProjectPlugin(private val project: Project) {
// Publish our artifacts, and add project dependencies on root project to this project
projectHealthPublisher.publish(filterAdviceTask.flatMap { it.output })
resolvedDependenciesPublisher.publish(computeResolvedDependenciesTask.flatMap { it.output })

// TODO(tsr): this is cross-project configuration and violates isolated projects (but not CC).
// would prefer to do this at the root, but need to validate in the situation when not every subproject applies
// DAGP.
rootProject.dependencies {
add(projectHealthPublisher.declarableName, project(path))
add(resolvedDependenciesPublisher.declarableName, project(path))
}
}

/** Get the buildPath of the current build from the root component of the resolution result. */
Expand Down
49 changes: 34 additions & 15 deletions src/main/kotlin/com/autonomousapps/subplugin/RootPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import com.autonomousapps.getExtension
import com.autonomousapps.internal.RootOutputPaths
import com.autonomousapps.internal.advice.DslKind
import com.autonomousapps.internal.artifacts.DagpArtifacts
import com.autonomousapps.internal.artifacts.Publisher.Companion.interProjectPublisher
import com.autonomousapps.internal.artifacts.Resolver.Companion.interProjectResolver
import com.autonomousapps.internal.artifactsFor
import com.autonomousapps.internal.utils.log
import com.autonomousapps.tasks.BuildHealthTask
import com.autonomousapps.tasks.ComputeDuplicateDependenciesTask
Expand Down Expand Up @@ -52,20 +54,6 @@ internal class RootPlugin(private val project: Project) {
conditionallyApplyToSubprojects()
}

/** Only apply to all subprojects if user hasn't requested otherwise. See [shouldAutoApply]. */
private fun Project.conditionallyApplyToSubprojects() {
if (!shouldAutoApply()) {
logger.debug("Not applying plugin to all subprojects. User must apply to each manually")
return
}

logger.debug("Applying plugin to all subprojects")
subprojects {
logger.debug("Auto-applying to $path.")
apply(plugin = DEPENDENCY_ANALYSIS_PLUGIN)
}
}

/** Check for presence of flags that no longer have an effect. */
private fun Project.checkFlags() {
val clearArtifacts = providers.gradleProperty(FLAG_CLEAR_ARTIFACTS)
Expand Down Expand Up @@ -97,7 +85,7 @@ internal class RootPlugin(private val project: Project) {
}

val generateBuildHealthTask = tasks.register<GenerateBuildHealthTask>("generateBuildHealth") {
projectHealthReports.setFrom(adviceResolver.internal)
projectHealthReports.setFrom(adviceResolver.internal.map { it.artifactsFor("json").artifactFiles })
dslKind.set(DslKind.from(buildFile))
dependencyMap.set(getExtension().dependenciesHandler.map)

Expand All @@ -111,5 +99,36 @@ internal class RootPlugin(private val project: Project) {
consoleReport.set(generateBuildHealthTask.flatMap { it.consoleOutput })
printBuildHealth.set(printBuildHealth())
}

// Add a dependency from the root project all projects (including itself).
val projectHealthPublisher = interProjectPublisher(
project = this,
artifact = DagpArtifacts.Kind.PROJECT_HEALTH
)
val resolvedDependenciesPublisher = interProjectPublisher(
project = this,
artifact = DagpArtifacts.Kind.RESOLVED_DEPS
)

allprojects.forEach { p ->
dependencies.run {
add(projectHealthPublisher.declarableName, project(p.path))
add(resolvedDependenciesPublisher.declarableName, project(p.path))
}
}
}

/** Only apply to all subprojects if user hasn't requested otherwise. See [shouldAutoApply]. */
private fun Project.conditionallyApplyToSubprojects() {
if (!shouldAutoApply()) {
logger.debug("Not applying plugin to all subprojects. User must apply to each manually")
return
}

logger.debug("Applying plugin to all subprojects")
subprojects {
logger.debug("Auto-applying to $path.")
apply(plugin = DEPENDENCY_ANALYSIS_PLUGIN)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ abstract class GenerateBuildHealthTask : DefaultTask() {
var processorDependencies = 0
val androidMetricsBuilder = AndroidScoreMetrics.Builder()

val projectAdvice: Set<ProjectAdvice> = projectHealthReports.files
val projectAdvice: Set<ProjectAdvice> = projectHealthReports.files.asSequence()
.map { it.fromJson<ProjectAdvice>() }
// we sort here because of the onEach below, where we stream the console output to disk
.sortedBy { it.projectPath }
Expand Down

0 comments on commit a4630ea

Please sign in to comment.