Added dependency scanning, added default runtime configurations

This commit is contained in:
nathan 2020-08-07 15:58:30 +02:00
parent fe6051c8f4
commit 8cf9f90805
3 changed files with 174 additions and 27 deletions

View File

@ -0,0 +1,102 @@
package dorkbox.gradle
import org.gradle.api.Project
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.ResolvedDependency
import java.io.File
object DependencyScanner {
/**
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun scan(project: Project,
configurationName: String,
projectDependencies: MutableList<Dependency>,
existingNames: MutableSet<String>): MutableList<Dependency> {
project.configurations.getByName(configurationName).resolvedConfiguration.firstLevelModuleDependencies.forEach { dep ->
// we know the FIRST series will exist
val makeDepTree = makeDepTree(dep, existingNames)
if (makeDepTree != null) {
// it's only null if we've ALREADY scanned it
projectDependencies.add(makeDepTree)
}
}
return projectDependencies
}
// how to resolve dependencies
// NOTE: it is possible, when we have a project DEPEND on an older version of that project (ie: bootstrapped from an older version)
// we can have infinite recursion.
// This is a problem, so we limit how much a dependency can show up the the tree
private fun makeDepTree(dep: ResolvedDependency, existingNames: MutableSet<String>): Dependency? {
val module = dep.module.id
val group = module.group
val name = module.name
val version = module.version
if (!existingNames.contains("$group:$name")) {
// println("Searching: $group:$name:$version")
val artifacts: List<DependencyInfo> = dep.moduleArtifacts.map { artifact: ResolvedArtifact ->
val artifactModule = artifact.moduleVersion.id
DependencyInfo(artifactModule.group, artifactModule.name, artifactModule.version, artifact.file.absoluteFile)
}
val children = mutableListOf<Dependency>()
dep.children.forEach {
existingNames.add("$group:$name")
val makeDep = makeDepTree(it, existingNames)
if (makeDep != null) {
children.add(makeDep)
}
}
return Dependency(group, name, version, artifacts, children.toList())
}
// we already have this dependency in our chain.
return null
}
/**
* Flatten the dependency children
*/
fun flattenDeps(dep: Dependency): List<Dependency> {
val flatDeps = mutableSetOf<Dependency>()
flattenDep(dep, flatDeps)
return flatDeps.toList()
}
private fun flattenDep(dep: Dependency, flatDeps: MutableSet<Dependency>) {
flatDeps.add(dep)
dep.children.forEach {
flattenDep(it, flatDeps)
}
}
data class Dependency(val group: String,
val name: String,
val version: String,
val artifacts: List<DependencyInfo>,
val children: List<Dependency>) {
fun mavenId(): String {
return "$group:$name:$version"
}
override fun toString(): String {
return mavenId()
}
}
data class DependencyInfo(val group: String, val name: String, val version: String, val file: File) {
val id: String
get() {
return "$group:$name:$version"
}
}
}

View File

@ -21,6 +21,7 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import java.util.*

View File

@ -3,9 +3,7 @@ package dorkbox.gradle
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.ResolvedDependency
import org.gradle.api.plugins.JavaPluginConvention
import java.io.File
import java.util.*
import kotlin.reflect.KMutableProperty
@ -13,6 +11,7 @@ import kotlin.reflect.KProperty
import kotlin.reflect.full.declaredMemberFunctions
import kotlin.reflect.full.declaredMemberProperties
open class StaticMethodsAndTools(private val project: Project) {
/**
* Maps the property (key/value) pairs of a property file onto the specified target object. Also maps fields in the targetObject to the
@ -113,40 +112,45 @@ open class StaticMethodsAndTools(private val project: Project) {
/**
* Resolves all child dependencies of the project
*
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun resolveDependencies(): List<ResolvedArtifact> {
val configuration = project.configurations.getByName("default") as Configuration
fun resolveDependencies(): List<DependencyScanner.Dependency> {
val projectDependencies = mutableListOf<DependencyScanner.Dependency>()
val existingNames = mutableSetOf<String>()
val includedDeps = mutableSetOf<ResolvedDependency>()
val depsToSearch = LinkedList<ResolvedDependency>()
depsToSearch.addAll(configuration.resolvedConfiguration.firstLevelModuleDependencies)
DependencyScanner.scan(project, "compileClasspath", projectDependencies, existingNames)
DependencyScanner.scan(project, "runtimeClasspath", projectDependencies, existingNames)
return includedDeps.flatMap {
it.moduleArtifacts
}
return projectDependencies
}
/**
* Resolves all child compile dependencies of the project
*
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun resolveCompileDependencies(): List<DependencyScanner.Dependency> {
val projectDependencies = mutableListOf<DependencyScanner.Dependency>()
val existingNames = mutableSetOf<String>()
DependencyScanner.scan(project, "compileClasspath", projectDependencies, existingNames)
return projectDependencies
}
/**
* Recursively resolves all dependencies of the project
* Resolves all child compile dependencies of the project
*
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun resolveAllDependencies(): List<ResolvedArtifact> {
val configuration = project.configurations.getByName("default") as Configuration
fun resolveRuntimeDependencies(): List<DependencyScanner.Dependency> {
val projectDependencies = mutableListOf<DependencyScanner.Dependency>()
val existingNames = mutableSetOf<String>()
val includedDeps = mutableSetOf<ResolvedDependency>()
val depsToSearch = LinkedList<ResolvedDependency>()
depsToSearch.addAll(configuration.resolvedConfiguration.firstLevelModuleDependencies)
DependencyScanner.scan(project, "runtimeClasspath", projectDependencies, existingNames)
while (depsToSearch.isNotEmpty()) {
val dep = depsToSearch.removeFirst()
includedDeps.add(dep)
depsToSearch.addAll(dep.children)
}
return includedDeps.flatMap {
it.moduleArtifacts
}
return projectDependencies
}
/**
@ -165,6 +169,46 @@ open class StaticMethodsAndTools(private val project: Project) {
it.testOutputDir = mainDir
}
}
// this has the side-effect of NOT creating the gradle directories....
// make sure that the source set directories all exist. THIS SHOULD NOT BE A PROBLEM!
project.afterEvaluate { prj ->
prj.allprojects.forEach { proj ->
val javaPlugin: JavaPluginConvention = proj.convention.getPlugin(JavaPluginConvention::class.java)
val sourceSets = javaPlugin.sourceSets
sourceSets.forEach { set ->
set.output.classesDirs.forEach { dir ->
if (!dir.exists()) {
dir.mkdirs()
}
}
}
}
}
}
/**
* Configure a default resolution strategy. While not necessary, this is used for enforcing sane project builds
*/
fun defaultResolutionStrategy() {
project.configurations.forEach { config ->
config.resolutionStrategy {
// fail eagerly on version conflict (includes transitive dependencies)
// e.g. multiple different versions of the same dependency (group and name are equal)
it.failOnVersionConflict()
// if there is a version we specified, USE THAT VERSION (over transitive versions)
it.preferProjectModules()
// cache dynamic versions for 10 minutes
it.cacheDynamicVersionsFor(10 * 60, "seconds")
// don't cache changing modules at all
it.cacheChangingModulesFor(0, "seconds")
}
}
}
private fun idea(project: Project, configure: org.gradle.plugins.ide.idea.model.IdeaModel.() -> Unit): Unit =