diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/resolution/OciImageSpecResolution.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/resolution/OciImageSpecResolution.kt index ac3fa8ce..785c6d42 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/resolution/OciImageSpecResolution.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/resolution/OciImageSpecResolution.kt @@ -1,8 +1,6 @@ package io.github.sgtsilvio.gradle.oci.internal.resolution -import io.github.sgtsilvio.gradle.oci.attributes.OCI_IMAGE_INDEX_PLATFORM_ATTRIBUTE import io.github.sgtsilvio.gradle.oci.attributes.OCI_IMAGE_REFERENCE_SPECS_ATTRIBUTE -import io.github.sgtsilvio.gradle.oci.attributes.PLATFORM_ATTRIBUTE import io.github.sgtsilvio.gradle.oci.internal.gradle.VariantSelector import io.github.sgtsilvio.gradle.oci.internal.gradle.toVariantSelector import io.github.sgtsilvio.gradle.oci.metadata.DEFAULT_OCI_IMAGE_REFERENCE_SPEC @@ -10,84 +8,13 @@ import io.github.sgtsilvio.gradle.oci.metadata.OciImageReferenceSpec import io.github.sgtsilvio.gradle.oci.metadata.toOciImageReferenceSpec import io.github.sgtsilvio.gradle.oci.platform.Platform import io.github.sgtsilvio.gradle.oci.platform.PlatformSelector -import io.github.sgtsilvio.gradle.oci.platform.toPlatform import org.gradle.api.artifacts.result.ResolvedComponentResult import org.gradle.api.artifacts.result.ResolvedDependencyResult import org.gradle.api.artifacts.result.ResolvedVariantResult -internal fun resolveOciVariantGraph(rootComponent: ResolvedComponentResult): List { - // the first variant is the resolvable configuration, but only if it declares at least one dependency - val rootVariant = rootComponent.variants.firstOrNull() ?: return emptyList() - val variantToNode = HashMap() - // rootNodesToDependencySelectors is linked to preserve the dependency order - val rootNodesToSelectors = LinkedHashMap>() - for (dependency in rootComponent.getDependenciesForVariant(rootVariant)) { - if (dependency.isConstraint) continue - if (dependency !is ResolvedDependencyResult) throw ResolutionException() - val node = resolveOciVariantNode(dependency.selected, dependency.resolvedVariant, variantToNode) - rootNodesToSelectors.getOrPut(node) { HashSet() } += dependency.requested.toVariantSelector() - } - return rootNodesToSelectors.map { (node, selectors) -> OciVariantGraphRoot(node, selectors) } -} - -private fun resolveOciVariantNode( - component: ResolvedComponentResult, - variant: ResolvedVariantResult, - variantToNode: HashMap, -): OciVariantNode { - if (variant in variantToNode) { - return variantToNode[variant] ?: throw IllegalStateException("cycle in dependencies graph") - } - variantToNode[variant] = null - // platformToDependenciesAndSupportedPlatforms is linked to preserve the platform order - val platformToDependenciesAndSupportedPlatforms = - LinkedHashMap, PlatformSet>>() - val platforms = variant.attributes.getAttribute(PLATFORM_ATTRIBUTE)?.decodePlatforms() ?: setOf(null) - for (platform in platforms) { - val supportedPlatforms = if (platform == null) PlatformSet(true) else PlatformSet(platform) - platformToDependenciesAndSupportedPlatforms[platform] = Pair(ArrayList(), supportedPlatforms) - } - for (dependency in component.getDependenciesForVariant(variant)) { - if (dependency.isConstraint) continue - if (dependency !is ResolvedDependencyResult) throw ResolutionException() - val dependencyPlatforms = - dependency.requested.attributes.getAttribute(OCI_IMAGE_INDEX_PLATFORM_ATTRIBUTE)?.decodePlatforms() - ?: platforms - val node = resolveOciVariantNode(dependency.selected, dependency.resolvedVariant, variantToNode) - for (dependencyPlatform in dependencyPlatforms) { - val (dependencies, supportedPlatforms) = platformToDependenciesAndSupportedPlatforms[dependencyPlatform] - ?: throw IllegalStateException("dependency can not be defined for more platforms than component") // TODO message - dependencies += node - supportedPlatforms.intersect(node.supportedPlatforms) - } - } - // platformToDependencies is linked to preserve the platform order - val platformToDependencies = LinkedHashMap>() - val platformSet = PlatformSet(false) - for ((platform, dependenciesAndSupportedPlatforms) in platformToDependenciesAndSupportedPlatforms) { - val (dependencies, supportedPlatforms) = dependenciesAndSupportedPlatforms - platformToDependencies[platform] = dependencies - platformSet.union(supportedPlatforms) - } - val node = OciVariantNode(variant, platformToDependencies, platformSet) - variantToNode[variant] = node - return node -} - -internal class OciVariantNode( - val variant: ResolvedVariantResult, - val platformToDependencies: Map>, - val supportedPlatforms: PlatformSet, -) - -internal class OciVariantGraphRoot(val node: OciVariantNode, val variantSelectors: Set) - -// return value is linked to preserve the platform order -private fun String.decodePlatforms() = split(';').mapTo(LinkedHashSet()) { it.toPlatform() } - // TODO new file from here? -internal fun List.selectPlatforms( +internal fun OciVariantGraph.selectPlatforms( platformSelector: PlatformSelector?, ): List>> { val selectedPlatformsGraph = ArrayList>>(size) diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/resolution/OciVariantGraph.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/resolution/OciVariantGraph.kt new file mode 100644 index 00000000..19458357 --- /dev/null +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/resolution/OciVariantGraph.kt @@ -0,0 +1,83 @@ +package io.github.sgtsilvio.gradle.oci.internal.resolution + +import io.github.sgtsilvio.gradle.oci.attributes.OCI_IMAGE_INDEX_PLATFORM_ATTRIBUTE +import io.github.sgtsilvio.gradle.oci.attributes.PLATFORM_ATTRIBUTE +import io.github.sgtsilvio.gradle.oci.internal.gradle.VariantSelector +import io.github.sgtsilvio.gradle.oci.internal.gradle.toVariantSelector +import io.github.sgtsilvio.gradle.oci.platform.Platform +import io.github.sgtsilvio.gradle.oci.platform.toPlatform +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.artifacts.result.ResolvedVariantResult + +internal typealias OciVariantGraph = List + +internal class OciVariantGraphRoot(val node: OciVariantGraphNode, val variantSelectors: Set) + +internal class OciVariantGraphNode( + val variant: ResolvedVariantResult, + val platformToDependencies: Map>, + val supportedPlatforms: PlatformSet, +) + +internal fun resolveOciVariantGraph(rootComponent: ResolvedComponentResult): OciVariantGraph { + // the first variant is the resolvable configuration, but only if it declares at least one dependency + val rootVariant = rootComponent.variants.firstOrNull() ?: return emptyList() + val variantToNode = HashMap() + // rootNodesToDependencySelectors is linked to preserve the dependency order + val rootNodesToSelectors = LinkedHashMap>() + for (dependency in rootComponent.getDependenciesForVariant(rootVariant)) { + if (dependency.isConstraint) continue + if (dependency !is ResolvedDependencyResult) throw ResolutionException() + val node = resolveOciVariantGraphNode(dependency.selected, dependency.resolvedVariant, variantToNode) + rootNodesToSelectors.getOrPut(node) { HashSet() } += dependency.requested.toVariantSelector() + } + return rootNodesToSelectors.map { (node, selectors) -> OciVariantGraphRoot(node, selectors) } +} + +private fun resolveOciVariantGraphNode( + component: ResolvedComponentResult, + variant: ResolvedVariantResult, + variantToNode: HashMap, +): OciVariantGraphNode { + if (variant in variantToNode) { + return variantToNode[variant] ?: throw IllegalStateException("cycle in dependencies graph") + } + variantToNode[variant] = null + // platformToDependenciesAndSupportedPlatforms is linked to preserve the platform order + val platformToDependenciesAndSupportedPlatforms = + LinkedHashMap, PlatformSet>>() + val platforms = variant.attributes.getAttribute(PLATFORM_ATTRIBUTE)?.decodePlatforms() ?: setOf(null) + for (platform in platforms) { + val supportedPlatforms = if (platform == null) PlatformSet(true) else PlatformSet(platform) + platformToDependenciesAndSupportedPlatforms[platform] = Pair(ArrayList(), supportedPlatforms) + } + for (dependency in component.getDependenciesForVariant(variant)) { + if (dependency.isConstraint) continue + if (dependency !is ResolvedDependencyResult) throw ResolutionException() + val dependencyPlatforms = + dependency.requested.attributes.getAttribute(OCI_IMAGE_INDEX_PLATFORM_ATTRIBUTE)?.decodePlatforms() + ?: platforms + val node = resolveOciVariantGraphNode(dependency.selected, dependency.resolvedVariant, variantToNode) + for (dependencyPlatform in dependencyPlatforms) { + val (dependencies, supportedPlatforms) = platformToDependenciesAndSupportedPlatforms[dependencyPlatform] + ?: throw IllegalStateException("dependency can not be defined for more platforms than component") // TODO message + dependencies += node + supportedPlatforms.intersect(node.supportedPlatforms) + } + } + // platformToDependencies is linked to preserve the platform order + val platformToDependencies = LinkedHashMap>() + val platformSet = PlatformSet(false) + for ((platform, dependenciesAndSupportedPlatforms) in platformToDependenciesAndSupportedPlatforms) { + val (dependencies, supportedPlatforms) = dependenciesAndSupportedPlatforms + platformToDependencies[platform] = dependencies + platformSet.union(supportedPlatforms) + } + val node = OciVariantGraphNode(variant, platformToDependencies, platformSet) + variantToNode[variant] = node + return node +} + +// return value is linked to preserve the platform order +private fun String.decodePlatforms() = split(';').mapTo(LinkedHashSet()) { it.toPlatform() }