Skip to content

Commit

Permalink
New graph-support lib.
Browse files Browse the repository at this point in the history
  • Loading branch information
autonomousapps committed Sep 28, 2022
1 parent e7c4b83 commit 463ad17
Show file tree
Hide file tree
Showing 15 changed files with 139 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ class ConventionPlugin : Plugin<Project> {
val signing = extensions.getByType(SigningExtension::class.java)
val publishing = extensions.getByType(PublishingExtension::class.java)

val publishToMavenCentral = tasks.register("publishToMavenCentral")
val publishToMavenCentral = tasks.register("publishToMavenCentral") {
it.notCompatibleWithConfigurationCache("Publishing is not compatible")
}

extensions.configure(JavaPluginExtension::class.java) { j ->
j.withJavadocJar()
Expand Down Expand Up @@ -97,7 +99,7 @@ class ConventionPlugin : Plugin<Project> {
finalizedBy(promoteTask)
doLast {
if (isSnapshot.get()) {
logger.quiet("Browse files at https://oss.sonatype.org/content/repositories/snapshots/com/autonomousapps")
logger.quiet("Browse files at https://oss.sonatype.org/content/repositories/snapshots/com/autonomousapps/")
} else {
logger.quiet("After publishing to Sonatype, visit https://oss.sonatype.org to close and release from staging")
}
Expand Down
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ dependencies {
api(libs.javax.inject)
api(libs.moshi.core)
api(libs.moshix.sealed.runtime)

implementation(project(":graph-support"))
implementation(libs.kotlin.stdlib.jdk8)
implementation(libs.moshi.kotlin)
implementation(libs.moshix.sealed.reflect)
Expand Down
47 changes: 47 additions & 0 deletions graph-support/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
plugins {
id("org.jetbrains.kotlin.jvm")
id("convention")
id("com.autonomousapps.dependency-analysis")
}

group = "com.autonomousapps"
version = "0.1"

kotlin {
explicitApi()
}

dagp {
version(version)
pom {
name.set("Graph Support Library")
description.set("A graph support library for the JVM")
url.set("https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin")
inceptionYear.set("2022")
}
publishTaskDescription("Publishes to Maven Central and promotes.")
}

// We only use the Jupiter platform (JUnit 5)
configurations.all {
exclude(mapOf("group" to "junit", "module" to "junit"))
exclude(mapOf("group" to "org.junit.vintage", "module" to "junit-vintage-engine"))
}

dependencies {
api(libs.guava) {
because("Graphs")
}

testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.api)
testImplementation(libs.junit.params)
testImplementation(libs.truth)
testRuntimeOnly(libs.junit.engine)

testImplementation(libs.truth)
}

tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.autonomousapps.internal.graph
package com.autonomousapps.graph

import com.autonomousapps.internal.unsafeLazy
import com.google.common.graph.ElementOrder
import com.google.common.graph.Graph
import com.google.common.graph.GraphBuilder
Expand All @@ -13,7 +12,7 @@ import com.google.common.graph.GraphBuilder
* @see <a href="http://www.hipersoft.rice.edu/grads/publications/dom14.pdf">A Simple, Fast Dominance Algorithm.</a>
*/
@Suppress("UnstableApiUsage") // Guava graphs
internal class DominanceTree<N : Any>(
public class DominanceTree<N : Any>(
private val backingGraph: Graph<N>,
private val root: N
) {
Expand Down Expand Up @@ -78,7 +77,7 @@ internal class DominanceTree<N : Any>(
return left
}

val dominanceGraph: Graph<N> by unsafeLazy {
public val dominanceGraph: Graph<N> by lazy(LazyThreadSafetyMode.NONE) {
val builder = GraphBuilder.directed()
.allowsSelfLoops(false)
.incidentEdgeOrder(ElementOrder.stable<N>())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package com.autonomousapps.internal.graph

import org.gradle.kotlin.dsl.support.appendReproducibleNewLine
package com.autonomousapps.graph

@Suppress("UnstableApiUsage") // Guava
internal class DominanceTreeWriter<N : Any>(
public class DominanceTreeWriter<N : Any>(
private val root: N,
private val tree: DominanceTree<N>,
private val nodeWriter: NodeWriter<N>,
) {

private val builder = StringBuilder()
val string: String get() = builder.toString()
public val string: String get() = builder.toString()

init {
compute()
Expand All @@ -20,7 +18,7 @@ internal class DominanceTreeWriter<N : Any>(
val visiting = linkedMapOf<N, MutableSet<N>>()

// start by printing root node
builder.appendReproducibleNewLine(nodeWriter.toString(root))
builder.appendLine(nodeWriter.toString(root))

fun dfs(node: N) {
val subs = tree.dominanceGraph.successors(node).run { nodeWriter.comparator()?.let { sortedWith(it) } ?: this }
Expand All @@ -45,7 +43,7 @@ internal class DominanceTreeWriter<N : Any>(
}
}

builder.appendReproducibleNewLine(nodeWriter.toString(sub))
builder.appendLine(nodeWriter.toString(sub))

dfs(sub)

Expand All @@ -57,12 +55,12 @@ internal class DominanceTreeWriter<N : Any>(
dfs(root)
}

internal interface NodeWriter<N : Any> {
public interface NodeWriter<N : Any> {
/** A [Comparator] for sorting nodes of type [N]. May be null if you don't care about print order. */
fun comparator(): Comparator<N>?
public fun comparator(): Comparator<N>?

/** String representation of [node] that will be printed to console. */
fun toString(node: N): String
public fun toString(node: N): String
}

private companion object {
Expand Down
32 changes: 32 additions & 0 deletions graph-support/src/main/kotlin/com/autonomousapps/graph/Graphs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.autonomousapps.graph

import com.google.common.graph.Graph
import com.google.common.graph.Graphs as GuavaGraphs

@Suppress("UnstableApiUsage") // Guava graphs
public object Graphs {

public fun <N : Any> Graph<N>.reachableNodes(node: N, excludeSelf: Boolean = true): Set<N> {
val reachable = GuavaGraphs.reachableNodes(this, node)
return if (excludeSelf) {
reachable.filterNotTo(HashSet()) { it == node }
} else {
reachable
}
}

public fun <N : Any> Graph<N>.parents(node: N): Set<N> = predecessors(node)

public fun <N : Any> Graph<N>.children(node: N): Set<N> = successors(node)

/**
* Returns an ordered list of nodes if there is a path from [source] to [target]. If there is no path, returns an
* empty list.
*/
public fun <N : Any> Graph<N>.shortestPath(source: N, target: N): Iterable<N> {
val path = ShortestPath(this, source)
return path.pathTo(target)
}

public fun <N : Any> Graph<N>.topological(source: N): Iterable<N> = Topological(this, source).order
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.autonomousapps.internal.graph
package com.autonomousapps.graph

import com.google.common.graph.EndpointPair
import com.google.common.graph.Graph
import java.util.*

/** With thanks to Algorithms, 4th Ed. */
@Suppress("UnstableApiUsage") // Guava graphs
internal class ShortestPath<N>(
public class ShortestPath<N>(
graph: Graph<N>,
private val source: N
) where N : Any {
Expand All @@ -29,12 +29,12 @@ internal class ShortestPath<N>(
}
}

fun hasPathTo(other: N): Boolean {
public fun hasPathTo(other: N): Boolean {
val dist = distTo[other] ?: return false
return dist < Float.MAX_VALUE
}

fun pathTo(other: N): Iterable<N> {
public fun pathTo(other: N): Iterable<N> {
if (!hasPathTo(other)) return emptyList()

// Flatten the list of edges into a list of nodes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.autonomousapps.internal.graph
package com.autonomousapps.graph

import com.google.common.graph.Graph
import com.google.common.graph.Traverser
import java.util.*

/** With thanks to Algorithms, 4th Ed. See p582 for the explanation for why we want the reverse postorder. */
@Suppress("UnstableApiUsage") // Guava graphs
internal class Topological<N>(
public class Topological<N>(
graph: Graph<N>,
source: N
) where N : Any {

val order: Iterable<N>
public val order: Iterable<N>

init {
val postorder: Iterable<N> = Traverser.forGraph(graph).depthFirstPostOrder(source)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.autonomousapps.internal.graph
@file:Suppress("UnstableApiUsage") // Guava

import com.autonomousapps.test.graphOf
package com.autonomousapps.graph

import com.google.common.graph.ElementOrder
import com.google.common.graph.GraphBuilder
import com.google.common.graph.ImmutableGraph
import com.google.common.truth.Truth.assertThat
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test

@Suppress("UnstableApiUsage") // Guava
internal class GraphsTest {

@Nested inner class Topological {
Expand Down Expand Up @@ -125,3 +128,15 @@ internal class GraphsTest {
}
}
}

private fun <N : Any> graphOf(vararg pairs: Pair<N, N>): ImmutableGraph<N> {
val builder: ImmutableGraph.Builder<N> = GraphBuilder.directed()
.allowsSelfLoops(false)
.incidentEdgeOrder(ElementOrder.stable<N>())
.immutable()

pairs.forEach { (from, to) ->
builder.putEdge(from, to)
}
return builder.build()
}
4 changes: 4 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pluginManagement {
}
gradlePluginPortal()
mavenCentral()

// snapshots are permitted, but only for dependencies I own
maven {
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
content {
Expand Down Expand Up @@ -41,6 +43,7 @@ dependencyResolutionManagement {
if (providers.systemProperty("local").isPresent) {
mavenLocal()
}
// snapshots are permitted, but only for dependencies I own
maven {
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
content {
Expand Down Expand Up @@ -68,6 +71,7 @@ gradleEnterprise {

rootProject.name = "dependency-analysis-gradle-plugin"

include(":graph-support")
include(":testkit")
include(":testkit-truth")

Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/com/autonomousapps/internal/Bundles.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.autonomousapps.internal

import com.autonomousapps.extension.DependenciesHandler.SerializableBundles
import com.autonomousapps.internal.graph.Graphs.children
import com.autonomousapps.internal.graph.Graphs.reachableNodes
import com.autonomousapps.graph.Graphs.children
import com.autonomousapps.graph.Graphs.reachableNodes
import com.autonomousapps.model.Advice
import com.autonomousapps.model.Coordinates
import com.autonomousapps.model.Coordinates.Companion.copy
Expand Down
33 changes: 0 additions & 33 deletions src/main/kotlin/com/autonomousapps/internal/graph/Graphs.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.autonomousapps.internal.reason

import com.autonomousapps.internal.graph.Graphs.shortestPath
import com.autonomousapps.graph.Graphs.shortestPath
import com.autonomousapps.internal.utils.Colors
import com.autonomousapps.internal.utils.Colors.colorize
import com.autonomousapps.internal.utils.lowercase
Expand Down Expand Up @@ -56,10 +56,12 @@ internal class DependencyAdviceExplainer(
"There is no advice regarding this dependency. It was removed because it matched a $bundle rule for " +
"${printableIdentifier(trace.parent).colorize(Colors.BOLD)}, which is already declared."
}

is BundleTrace.UsedChild -> {
"There is no advice regarding this dependency. It was removed because it matched a $bundle rule for " +
"${printableIdentifier(trace.child).colorize(Colors.BOLD)}, which is declared and used."
}

else -> error("Trace was $trace, which makes no sense in this context")
}
} else if (wasFiltered) {
Expand All @@ -69,6 +71,7 @@ internal class DependencyAdviceExplainer(
"There is no advice regarding this dependency."
}
}

advice.isAdd() -> {
val trace = findTrace()
if (trace != null) {
Expand All @@ -80,13 +83,16 @@ internal class DependencyAdviceExplainer(
"You have been advised to add this dependency to '${advice.toConfiguration!!.colorize(Colors.GREEN)}'."
}
}

advice.isRemove() || advice.isProcessor() -> {
"You have been advised to remove this dependency from '${advice.fromConfiguration!!.colorize(Colors.RED)}'."
}

advice.isChange() || advice.isRuntimeOnly() || advice.isCompileOnly() -> {
"You have been advised to change this dependency to '${advice.toConfiguration!!.colorize(Colors.GREEN)}' " +
"from '${advice.fromConfiguration!!.colorize(Colors.YELLOW)}'."
}

else -> error("Unknown advice type: $advice")
}

Expand Down
Loading

0 comments on commit 463ad17

Please sign in to comment.