Skip to content

Commit

Permalink
Show settings.gradle.dcl as a declarative document
Browse files Browse the repository at this point in the history
  • Loading branch information
h0tk3y committed Sep 30, 2024
1 parent 5beff3a commit bbb557a
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class GetResolvedDomAction implements BuildAction<ResolvedDomPrerequisites> {
Expand All @@ -37,10 +38,10 @@ private static Pair<File, List<File>> getDeclarativeBuildFiles(BuildController c
if (declarativeBuildFiles.isEmpty()) {
throw new RuntimeException("No declarative project file found");
}
return Pair.of(rootProjectDirectory, declarativeBuildFiles);
return Pair.of(rootProjectDirectory, declarativeBuildFiles);
}


private static final class ResolvedDomPrerequisitesImpl implements ResolvedDomPrerequisites {

private final InterpretationSequence settingsSequence;
Expand All @@ -51,7 +52,7 @@ private static final class ResolvedDomPrerequisitesImpl implements ResolvedDomPr
public ResolvedDomPrerequisitesImpl(
InterpretationSequence settingsSequence,
InterpretationSequence projectSequence,
File rootDir,
File rootDir,
List<File> declarativeBuildFiles
) {
this.settingsSequence = settingsSequence;
Expand Down Expand Up @@ -91,8 +92,11 @@ public File getSettingsFile() {
}

@Override
public List<File> getDeclarativeBuildFiles() {
return declarativeBuildFiles;
public List<File> getDeclarativeFiles() {
return Stream.concat(
declarativeBuildFiles.stream(),
getSettingsFile().canRead() ? Stream.of(getSettingsFile()) : Stream.empty()
).collect(Collectors.toList());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ public interface ResolvedDomPrerequisites extends Serializable {

File getSettingsFile();

List<File> getDeclarativeBuildFiles();
List<File> getDeclarativeFiles();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@ package org.gradle.client.core.gradle.dcl

import org.gradle.internal.declarativedsl.dom.DeclarativeDocument
import org.gradle.internal.declarativedsl.dom.DocumentResolution
import org.gradle.internal.declarativedsl.dom.UnresolvedBase
import org.gradle.internal.declarativedsl.dom.data.collectToMap
import org.gradle.internal.declarativedsl.dom.operations.overlay.DocumentOverlay.overlayResolvedDocuments
import org.gradle.internal.declarativedsl.dom.operations.overlay.DocumentOverlayResult
import org.gradle.internal.declarativedsl.dom.resolution.DocumentResolutionContainer
import org.gradle.internal.declarativedsl.dom.resolution.DocumentWithResolution
import org.gradle.internal.declarativedsl.evaluator.main.AnalysisDocumentUtils.resolvedDocument
import org.gradle.internal.declarativedsl.evaluator.main.AnalysisSequenceResult
import org.gradle.internal.declarativedsl.evaluator.runner.stepResultOrPartialResult
import org.gradle.internal.declarativedsl.language.SourceData
import org.gradle.internal.declarativedsl.language.SyntheticallyProduced
import java.util.*

fun DeclarativeDocument.relevantRange(): IntRange {
Expand Down Expand Up @@ -42,3 +51,109 @@ fun DeclarativeDocument.nodeAt(fileIdentifier: String, offset: Int): Declarative
}
return node
}

fun settingsWithNoOverlayOrigin(analysisSequenceResult: AnalysisSequenceResult): DocumentOverlayResult? {
val docs = analysisSequenceResult.stepResults.map { it.value.stepResultOrPartialResult.resolvedDocument() }
if (docs.isEmpty())
return null

return indexBasedOverlayResultFromDocuments(docs)
}


internal fun indexBasedOverlayResultFromDocuments(docs: List<DocumentWithResolution>): DocumentOverlayResult {
val emptyDoc = DocumentWithResolution(
object : DeclarativeDocument {
override val content: List<DeclarativeDocument.DocumentNode> = emptyList()
override val sourceData: SourceData = SyntheticallyProduced
},
indexBasedMultiResolutionContainer(emptyList())
)

val lastDocWithAllResolutionResults = DocumentWithResolution(
docs.last().document,
indexBasedMultiResolutionContainer(docs)
)

/**
* NB: No real overlay origin data is going to be present, as we are overlaying the doc with all the resolution
* results collected over the empty document.
*/
return overlayResolvedDocuments(emptyDoc, lastDocWithAllResolutionResults)
}

/**
* A resolution results container collected from multiple resolved instances of the same document (or multiple
* different instances of the same document, no referential equality required).
*
* The document parts are matched based on indices.
*
* If any of the [docs] is different from the others, the result is undefined (likely to be a broken container).
*/
internal fun indexBasedMultiResolutionContainer(docs: List<DocumentWithResolution>): DocumentResolutionContainer {
val indicesMaps: Map<DocumentWithResolution, Map<IntRange, DeclarativeDocument.Node>> = docs.associateWith {
buildMap {
fun visitValue(valueNode: DeclarativeDocument.ValueNode) {
put(valueNode.sourceData.indexRange, valueNode)
when (valueNode) {
is DeclarativeDocument.ValueNode.ValueFactoryNode -> valueNode.values.forEach(::visitValue)
is DeclarativeDocument.ValueNode.LiteralValueNode,
is DeclarativeDocument.ValueNode.NamedReferenceNode -> Unit
}
}

fun visitDocumentNode(documentNode: DeclarativeDocument.DocumentNode) {
put(documentNode.sourceData.indexRange, documentNode)
when (documentNode) {
is DeclarativeDocument.DocumentNode.ElementNode -> {
documentNode.elementValues.forEach(::visitValue)
documentNode.content.forEach(::visitDocumentNode)
}

is DeclarativeDocument.DocumentNode.PropertyNode -> visitValue(documentNode.value)
is DeclarativeDocument.DocumentNode.ErrorNode -> Unit
}
}

it.document.content.forEach(::visitDocumentNode)
}
}

/**
* The resolution containers work with node identities.
* Querying resolution results using nodes from a different document is prohibited.
* Given that all documents are the same, we can map the node indices and use them to find matching nodes in
* the documents that we are merging.
*/
return object : DocumentResolutionContainer {
inline fun <reified N : DeclarativeDocument.Node, reified T> retryOverContainers(
node: N,
noinline get: DocumentResolutionContainer.(N) -> T
) = docs.map { doc ->
val matchingNode = indicesMaps.getValue(doc)[node.sourceData.indexRange]
?: error("index not found in index map")
get(doc.resolutionContainer, matchingNode as N)
}.let { results ->
results.firstOrNull {
it !is DocumentResolution.UnsuccessfulResolution || !it.reasons.contains(UnresolvedBase)
} ?: results.first()
}

override fun data(node: DeclarativeDocument.DocumentNode.ElementNode) = retryOverContainers(node) { data(it) }
override fun data(node: DeclarativeDocument.DocumentNode.ErrorNode) = retryOverContainers(node) { data(it) }
override fun data(node: DeclarativeDocument.DocumentNode.PropertyNode) = retryOverContainers(node) { data(it) }
override fun data(node: DeclarativeDocument.ValueNode.LiteralValueNode) = retryOverContainers(node) { data(it) }
override fun data(node: DeclarativeDocument.ValueNode.NamedReferenceNode) =
retryOverContainers(node) { data(it) }

override fun data(node: DeclarativeDocument.ValueNode.ValueFactoryNode) = retryOverContainers(node) { data(it) }
}
}

internal fun DocumentResolutionContainer.isUnresolvedBase(node: DeclarativeDocument.Node): Boolean {
val resolution = when (node) {
is DeclarativeDocument.DocumentNode -> data(node)
is DeclarativeDocument.ValueNode -> data(node)
}
return resolution is DocumentResolution.UnsuccessfulResolution && resolution.reasons != listOf(UnresolvedBase)
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ object MutationUtils {
overlayResult: DocumentOverlayResult,
): NodeData<List<ApplicableMutation>> {
val overlayDocument = overlayResult.inputOverlay
val compatibleMutationsCatalog = DefaultMutationDefinitionCatalog().apply {
mutationCatalog.mutationDefinitionsById.values.forEach {
if (it.isCompatibleWithSchema(modelSchema)) {
val compatibleMutationsCatalog = DefaultMutationDefinitionCatalog().apply {
mutationCatalog.mutationDefinitionsById.values.forEach {
if (it.isCompatibleWithSchema(modelSchema)) {
registerMutationDefinition(it)
}
}
Expand Down Expand Up @@ -124,3 +124,9 @@ internal class OverlayRoutedNodeDataContainer<DNode, DElement : DNode, DProperty
is FromOverlay -> overlay.data(node)
}
}

internal class NoApplicableMutationsNodeData : NodeData<List<ApplicableMutation>> {
override fun data(node: DeclarativeDocument.DocumentNode.ElementNode) = emptyList<ApplicableMutation>()
override fun data(node: DeclarativeDocument.DocumentNode.ErrorNode) = emptyList<ApplicableMutation>()
override fun data(node: DeclarativeDocument.DocumentNode.PropertyNode) = emptyList<ApplicableMutation>()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package org.gradle.client.ui.connected.actions
import org.gradle.internal.declarativedsl.dom.DeclarativeDocument
import org.gradle.internal.declarativedsl.dom.DeclarativeDocument.DocumentNode.ElementNode
import org.gradle.internal.declarativedsl.dom.DeclarativeDocument.DocumentNode.PropertyNode
import org.gradle.internal.declarativedsl.dom.DocumentNodeContainer

val DeclarativeDocument.singleSoftwareTypeNode: ElementNode?
get() = content.filterIsInstance<ElementNode>().singleOrNull()

fun ElementNode.childElementNode(
fun DocumentNodeContainer.childElementNode(
name: String
): ElementNode? =
content.filterIsInstance<ElementNode>().singleOrNull() { it.name == name }

fun ElementNode.property(name: String): PropertyNode? =
fun DocumentNodeContainer.property(name: String): PropertyNode? =
content.filterIsInstance<PropertyNode>().singleOrNull { it.name == name }
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ val DataProperty.typeName: String
is DataTypeRef.Name -> propType.toHumanReadable()
}

val DataProperty.kotlinType: KClass<*>
get() = when (typeName) {
"String" -> String::class
"Int" -> Int::class
"Boolean" -> Boolean::class
else -> Any::class
}

fun DataTypeRef.toHumanReadable(): String =
when (this) {
is DataTypeRef.Name -> fqName.simpleName
Expand Down
Loading

0 comments on commit bbb557a

Please sign in to comment.