Update build; improve caching; improve tests

This commit is contained in:
Tad Fisher
2020-11-22 16:22:22 -08:00
parent 2ad217b878
commit beb157cf18
83 changed files with 3732 additions and 1771 deletions

View File

@@ -0,0 +1,15 @@
package org.nixos.gradle2nix;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency;
import javax.annotation.Nullable;
/**
* Workarounds for APIs improperly marked with @NonNullApi.
*/
interface ApiHack {
static Dependency defaultExternalModuleDependency(String group, String name, @Nullable String version) {
return new DefaultExternalModuleDependency(group, name, version);
}
}

View File

@@ -6,6 +6,7 @@ import org.apache.ivy.plugins.parser.m2.PomReader
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser
import org.apache.ivy.plugins.repository.url.URLResource
import org.apache.ivy.plugins.resolver.ChainResolver
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ModuleIdentifier
import org.gradle.api.artifacts.ResolvedArtifact
@@ -23,12 +24,21 @@ import org.gradle.kotlin.dsl.withArtifacts
import org.gradle.maven.MavenModule
import org.gradle.maven.MavenPomArtifact
import org.gradle.util.GradleVersion
import java.io.File
internal class ConfigurationResolverFactory(repositories: RepositoryHandler) {
enum class ConfigurationScope {
BUILDSCRIPT,
PLUGIN,
PROJECT
}
internal class ConfigurationResolverFactory(
project: Project,
scope: ConfigurationScope,
repositories: RepositoryHandler
) {
private val ivySettings = IvySettings().apply {
defaultInit()
setDefaultRepositoryCacheBasedir(createTempDir("gradle2nix-cache").apply(File::deleteOnExit).absolutePath)
defaultRepositoryCacheManager = null
setDictatorResolver(ChainResolver().also { chain ->
chain.settings = this@apply
for (resolver in resolvers) chain.add(resolver)
@@ -38,7 +48,7 @@ internal class ConfigurationResolverFactory(repositories: RepositoryHandler) {
private val resolvers = repositories
.filterIsInstance<ResolutionAwareRepository>()
.filterNot { it.createResolver().isLocal }
.mapNotNull { it.repositoryResolver(ivySettings) }
.mapNotNull { it.repositoryResolver(project, scope, ivySettings) }
fun create(dependencies: DependencyHandler): ConfigurationResolver =
ConfigurationResolver(ivySettings, resolvers, dependencies)
@@ -49,11 +59,23 @@ internal class ConfigurationResolver(
private val resolvers: List<RepositoryResolver>,
private val dependencies: DependencyHandler
) {
private val failed = mutableListOf<ArtifactIdentifier>()
private val ivy = Ivy.newInstance(ivySettings)
val unresolved: List<ArtifactIdentifier> = failed.toList()
fun resolve(configuration: Configuration): List<DefaultArtifact> {
val resolved = configuration.resolvedConfiguration.lenientConfiguration
failed.addAll(resolved.unresolvedModuleDependencies.map {
DefaultArtifactIdentifier(
group = it.selector.group,
name = it.selector.name,
version = it.selector.version ?: "",
type = "module"
)
})
val topLevelMetadata = resolved.firstLevelModuleDependencies
.flatMap { resolveMetadata(it.moduleGroup, it.moduleName, it.moduleVersion) }
@@ -78,6 +100,7 @@ internal class ConfigurationResolver(
val sha256 = resolvedArtifact.file.sha256()
val artifacts = resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge()
if (artifacts.isEmpty()) failed.add(artifactId)
return artifacts + componentId.run { resolveMetadata(group, module, version) }
}
@@ -113,7 +136,9 @@ internal class ConfigurationResolver(
type = "pom"
)
val sha256 = resolvedPom.file.sha256()
resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge()
val artifacts = resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge()
if (artifacts.isEmpty()) failed.add(artifactId)
artifacts
}
}
@@ -140,7 +165,9 @@ internal class ConfigurationResolver(
extension = "xml"
)
val sha256 = resolvedDesc.file.sha256()
resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge()
val artifacts = resolvers.mapNotNull { it.resolve(artifactId, sha256) }.merge()
if (artifacts.isEmpty()) failed.add(artifactId)
artifacts
}
}
@@ -155,7 +182,9 @@ internal class ConfigurationResolver(
version = version,
type = "module"
)
return resolvers.mapNotNull { it.resolve(artifactId) }.merge()
val artifacts = resolvers.mapNotNull { it.resolve(artifactId) }.merge()
if (artifacts.isEmpty()) failed.add(artifactId)
return artifacts
}
private fun ResolvedArtifactResult.parentPom(): ResolvedArtifactResult? {

View File

@@ -18,7 +18,7 @@ import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.gradle.util.GradleVersion
import java.net.URL
import java.util.Locale
import java.util.*
@Suppress("unused")
open class Gradle2NixPlugin : Plugin<Gradle> {
@@ -127,7 +127,7 @@ private fun Project.buildGradle(): DefaultGradle =
}
private fun Project.buildPlugins(pluginRequests: List<PluginRequest>): List<DefaultArtifact> {
return objects.newInstance<PluginResolver>().resolve(pluginRequests).distinct().sorted()
return objects.newInstance<PluginResolver>(this).resolve(pluginRequests).distinct().sorted()
}
private fun Project.includedBuilds(): List<DefaultIncludedBuild> =
@@ -141,37 +141,64 @@ private fun Project.buildProject(
pluginArtifacts: List<DefaultArtifact>
): DefaultProject {
logger.lifecycle(" Subproject: $path")
val (buildscriptDependencies, buildscriptUnresolved) = buildscriptDependencies(pluginArtifacts)
if (buildscriptUnresolved.isNotEmpty()) {
logger.warn(buildString {
append(" Failed to resolve buildscript dependencies:\n")
for (id in buildscriptUnresolved) {
append(" - $id\n")
}
})
}
val (projectDependencies, projectUnresolved) = projectDependencies(explicitConfigurations)
if (projectUnresolved.isNotEmpty()) {
logger.warn(buildString {
append(" Failed to resolve project dependencies:\n")
for (id in projectUnresolved) {
append(" - $id\n")
}
})
}
return DefaultProject(
name = name,
version = version.toString(),
path = path,
projectDir = projectDir.toRelativeString(rootProject.projectDir),
buildscriptDependencies = buildscriptDependencies(pluginArtifacts),
projectDependencies = projectDependencies(explicitConfigurations),
buildscriptDependencies = buildscriptDependencies,
projectDependencies = projectDependencies,
children = explicitSubprojects.map {
it.buildProject(explicitConfigurations, emptyList(), pluginArtifacts)
}
)
}
private fun Project.buildscriptDependencies(pluginArtifacts: List<DefaultArtifact>): List<DefaultArtifact> {
val resolverFactory = ConfigurationResolverFactory(buildscript.repositories)
private fun Project.buildscriptDependencies(
pluginArtifacts: List<DefaultArtifact>
): Pair<List<DefaultArtifact>, List<ArtifactIdentifier>> {
val resolverFactory = ConfigurationResolverFactory(this, ConfigurationScope.BUILDSCRIPT, buildscript.repositories)
val resolver = resolverFactory.create(buildscript.dependencies)
val pluginIds = pluginArtifacts.map(DefaultArtifact::id)
return buildscript.configurations
.flatMap(resolver::resolve)
.distinct()
.filter { it.id !in pluginIds }
.sorted()
.sorted() to resolver.unresolved
}
private fun Project.projectDependencies(explicitConfigurations: List<String>): List<DefaultArtifact> {
val resolverFactory = ConfigurationResolverFactory(repositories)
private fun Project.projectDependencies(
explicitConfigurations: List<String>
): Pair<List<DefaultArtifact>, List<ArtifactIdentifier>> {
val resolverFactory = ConfigurationResolverFactory(this, ConfigurationScope.PROJECT, repositories)
val resolver = resolverFactory.create(dependencies)
return collectConfigurations(explicitConfigurations)
.flatMap(resolver::resolve)
.distinct()
.sorted()
.sorted() to resolver.unresolved
}
private fun Project.dependentSubprojects(explicitConfigurations: List<String>): Set<Project> {

View File

@@ -1,25 +1,28 @@
package org.nixos.gradle2nix
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
import org.gradle.api.Project
import org.gradle.plugin.management.PluginRequest
import org.gradle.plugin.use.internal.PluginDependencyResolutionServices
import javax.inject.Inject
internal open class PluginResolver @Inject constructor(
project: Project,
pluginDependencyResolutionServices: PluginDependencyResolutionServices
) {
private val configurations = pluginDependencyResolutionServices.configurationContainer
private val resolver = ConfigurationResolverFactory(
project,
ConfigurationScope.PLUGIN,
pluginDependencyResolutionServices.resolveRepositoryHandler
).create(pluginDependencyResolutionServices.dependencyHandler)
fun resolve(pluginRequests: List<PluginRequest>): List<DefaultArtifact> {
val markerDependencies = pluginRequests.map {
it.module?.let { selector ->
DefaultExternalModuleDependency(selector.group, selector.name, selector.version)
} ?: it.id.run {
DefaultExternalModuleDependency(id, "$id.gradle.plugin", it.version)
val markerDependencies = pluginRequests.map { request ->
request.module?.let { module ->
ApiHack.defaultExternalModuleDependency(module.group, module.name, module.version)
} ?: request.id.id.let { id ->
ApiHack.defaultExternalModuleDependency(id, "$id.gradle.plugin", request.version)
}
}
return resolver.resolve(configurations.detachedConfiguration(*markerDependencies.toTypedArray()))

View File

@@ -15,6 +15,7 @@ import org.apache.ivy.plugins.resolver.URLResolver
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader
import org.codehaus.plexus.util.ReaderFactory
import org.codehaus.plexus.util.xml.pull.XmlPullParserException
import org.gradle.api.Project
import org.gradle.api.artifacts.repositories.ArtifactRepository
import org.gradle.api.artifacts.repositories.IvyArtifactRepository
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
@@ -27,10 +28,14 @@ import org.apache.ivy.core.module.descriptor.Artifact as IvyArtifact
import org.apache.ivy.core.module.descriptor.DefaultArtifact as IvyDefaultArtifact
import org.apache.ivy.plugins.resolver.RepositoryResolver as IvyRepositoryResolver
internal fun ResolutionAwareRepository.repositoryResolver(ivySettings: IvySettings): RepositoryResolver? =
internal fun ResolutionAwareRepository.repositoryResolver(
project: Project,
scope: ConfigurationScope,
ivySettings: IvySettings
): RepositoryResolver? =
when(this) {
is MavenArtifactRepository -> MavenResolver(ivySettings, this)
is IvyArtifactRepository -> IvyResolver(ivySettings, this)
is MavenArtifactRepository -> MavenResolver(project, scope, ivySettings, this)
is IvyArtifactRepository -> IvyResolver(project, scope, ivySettings, this)
else -> null
}
@@ -49,6 +54,8 @@ internal sealed class RepositoryResolver {
}
internal class MavenResolver(
project: Project,
scope: ConfigurationScope,
ivySettings: IvySettings,
repository: MavenArtifactRepository
) : RepositoryResolver() {
@@ -58,7 +65,7 @@ internal class MavenResolver(
root = repository.url.toString()
isM2compatible = true
settings = ivySettings
setCache(cacheManager(ivySettings, repository).name)
setCache(cacheManager(project, scope, ivySettings, repository).name)
}
override fun resolve(artifactId: DefaultArtifactIdentifier, sha256: String?): DefaultArtifact? {
@@ -121,6 +128,8 @@ internal class MavenResolver(
}
internal class IvyResolver(
project: Project,
scope: ConfigurationScope,
ivySettings: IvySettings,
repository: IvyArtifactRepository
) : RepositoryResolver() {
@@ -132,7 +141,7 @@ internal class IvyResolver(
for (p in ivyResolver.ivyPatterns) addIvyPattern(p)
for (p in ivyResolver.artifactPatterns) addArtifactPattern(p)
settings = ivySettings
setCache(cacheManager(ivySettings, repository).name)
setCache(cacheManager(project, scope, ivySettings, repository).name)
}
override fun resolve(artifactId: DefaultArtifactIdentifier, sha256: String?): DefaultArtifact? {
@@ -149,11 +158,16 @@ internal class IvyResolver(
}
}
private fun cacheManager(ivySettings: IvySettings, repository: ArtifactRepository): RepositoryCacheManager {
private fun cacheManager(
project: Project,
scope: ConfigurationScope,
ivySettings: IvySettings,
repository: ArtifactRepository
): RepositoryCacheManager {
return DefaultRepositoryCacheManager(
"${repository.name}-cache",
"${scope.name.toLowerCase()}-${repository.name}-cache",
ivySettings,
createTempDir("gradle2nix-${repository.name}-cache")
project.buildDir.resolve("tmp/gradle2nix/${scope.name.toLowerCase()}/${repository.name}")
).also {
ivySettings.addRepositoryCacheManager(it)
}