Skip to content

Commit

Permalink
Allow plugins { } in settings.gradle.something (#28345)
Browse files Browse the repository at this point in the history
  • Loading branch information
h0tk3y committed Mar 6, 2024
2 parents ecf8d06 + 7ca05a0 commit 59bc291
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 107 deletions.
Expand Up @@ -17,11 +17,47 @@
package org.gradle.internal.declarativedsl.analysis

import org.gradle.internal.declarativedsl.language.DataStatement
import org.gradle.internal.declarativedsl.language.FunctionArgument
import org.gradle.internal.declarativedsl.language.FunctionCall


fun interface AnalysisStatementFilter {
fun shouldAnalyzeStatement(statement: DataStatement, scopes: List<AnalysisScopeView>): Boolean

companion object {
val isConfiguringCall: AnalysisStatementFilter = AnalysisStatementFilter { statement, _ ->
statement is FunctionCall && statement.args.singleOrNull() is FunctionArgument.Lambda
}


val isTopLevelElement: AnalysisStatementFilter = AnalysisStatementFilter { _, scopes ->
scopes.last().receiver is ObjectOrigin.TopLevelReceiver
}


fun isCallNamed(name: String): AnalysisStatementFilter = AnalysisStatementFilter { statement, _ ->
statement is FunctionCall && statement.name == name
}
}
}


val analyzeEverything: AnalysisStatementFilter = AnalysisStatementFilter { _, _ -> true }


fun AnalysisStatementFilter.and(other: AnalysisStatementFilter): AnalysisStatementFilter = AnalysisStatementFilter { statement, scopes ->
shouldAnalyzeStatement(statement, scopes) && other.shouldAnalyzeStatement(statement, scopes)
}


fun AnalysisStatementFilter.or(other: AnalysisStatementFilter): AnalysisStatementFilter = AnalysisStatementFilter { statement, scopes ->
shouldAnalyzeStatement(statement, scopes) || other.shouldAnalyzeStatement(statement, scopes)
}


val analyzeEverything = AnalysisStatementFilter { _, _ -> true }
fun AnalysisStatementFilter.implies(other: AnalysisStatementFilter) = this.not().or(other)


fun AnalysisStatementFilter.not(): AnalysisStatementFilter = AnalysisStatementFilter { statement, scopes ->
!shouldAnalyzeStatement(statement, scopes)
}
@@ -1,5 +1,5 @@
/*
* Copyright 2023 the original author or authors.
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -14,7 +14,7 @@
* limitations under the License.
*/

package org.gradle.internal.declarativedsl
package org.gradle.internal.declarativedsl.settings


import org.gradle.integtests.fixtures.AbstractIntegrationSpec
Expand Down Expand Up @@ -85,4 +85,71 @@ class DeclarativeDslProjectSettingsIntegrationSpec extends AbstractIntegrationSp
"language feature" | "@A dependencies { }" | "2:13: unsupported language feature: AnnotationUsage"
"semantic" | "x = 1" | "2:13: unresolved reference 'x'"
}

def 'can apply settings plugins'() {
given:
file("included-settings-plugin/build.gradle") << """
plugins {
id('java-gradle-plugin')
}
gradlePlugin {
plugins {
create("settingsPlugin") {
id = "com.example.restricted.settings"
implementationClass = "com.example.restricted.RestrictedSettingsPlugin"
}
}
}
"""

file("included-settings-plugin/src/main/java/com/example/restricted/Extension.java") << """
package com.example.restricted;
import org.gradle.declarative.dsl.model.annotations.Restricted;
import org.gradle.api.provider.Property;
import javax.inject.Inject;
@Restricted
public abstract class Extension {
@Restricted
public abstract Property<String> getId();
}
"""

file("included-settings-plugin/src/main/java/com/example/restricted/RestrictedSettingsPlugin.java") << """
package com.example.restricted;
import org.gradle.api.Plugin;
import org.gradle.api.initialization.Settings;
public class RestrictedSettingsPlugin implements Plugin<Settings> {
@Override
public void apply(Settings target) {
Extension restricted = target.getExtensions().create("restricted", Extension.class);
target.getGradle().settingsEvaluated(settings -> {
System.out.println("id = " + restricted.getId().get());
});
}
}
"""

file("settings.gradle.something") << """
pluginManagement {
includeBuild("included-settings-plugin")
}
plugins {
id("com.example.restricted.settings")
}
restricted {
id = "test"
}
"""

expect:
succeeds("help")
outputContains("id = test")
}
}
Expand Up @@ -30,3 +30,19 @@ interface InterpretationSequenceStep<R : Any> {
fun topLevelReceiver(): R
fun whenEvaluated(resultReceiver: R)
}


/**
* Implements a straightforward interpretation sequence step that uses the specified [topLevelReceiver]
* and produces an evaluation schema with [buildEvaluationSchema] immediately before the step runs.
*/
internal
class SimpleInterpretationSequenceStep<T : Any>(
override val stepIdentifier: String,
private val topLevelReceiver: T,
private val buildEvaluationSchema: () -> EvaluationSchema
) : InterpretationSequenceStep<T> {
override fun evaluationSchemaForStep(): EvaluationSchema = buildEvaluationSchema()
override fun topLevelReceiver(): T = topLevelReceiver
override fun whenEvaluated(resultReceiver: T) = Unit
}
Expand Up @@ -46,7 +46,6 @@ import org.gradle.internal.declarativedsl.objectGraph.ReflectionContext
import org.gradle.internal.declarativedsl.objectGraph.reflect
import org.gradle.internal.declarativedsl.parsing.DefaultLanguageTreeBuilder
import org.gradle.internal.declarativedsl.parsing.parse
import org.gradle.internal.declarativedsl.plugins.PluginsTopLevelReceiver


interface DeclarativeKotlinScriptEvaluator {
Expand All @@ -73,21 +72,10 @@ interface DeclarativeKotlinScriptEvaluator {
class ScriptPluginEvaluationContext(
val targetScope: ClassLoaderScope
) : EvaluationContext

object PluginsDslEvaluationContext : EvaluationContext
}
}


/**
* A default implementation of a declarative DSL script evaluator, for use when no additional information needs to be provided at the use site.
* TODO: The consumers should get an instance properly injected instead.
*/
val defaultDeclarativeKotlinScriptEvaluator: DeclarativeKotlinScriptEvaluator by lazy {
DefaultDeclarativeKotlinScriptEvaluator(DefaultInterpretationSchemaBuilder())
}


internal
class DefaultDeclarativeKotlinScriptEvaluator(
private val schemaBuilder: InterpretationSchemaBuilder
Expand Down Expand Up @@ -180,13 +168,15 @@ class DefaultDeclarativeKotlinScriptEvaluator(
target: Any,
scriptSource: ScriptSource,
evaluationContext: DeclarativeKotlinScriptEvaluator.EvaluationContext
) = when (target) {
is Settings -> RestrictedScriptContext.SettingsScript
is Project -> {
require(evaluationContext is ScriptPluginEvaluationContext) { "declarative DSL for projects is only supported in script plugins" }
RestrictedScriptContext.ProjectScript(evaluationContext.targetScope, scriptSource)
}
is PluginsTopLevelReceiver -> RestrictedScriptContext.PluginsBlock
): RestrictedScriptContext = when (target) {
is Settings -> RestrictedScriptContext.SettingsScript(requirePluginContext(evaluationContext).targetScope, scriptSource)
is Project -> RestrictedScriptContext.ProjectScript(requirePluginContext(evaluationContext).targetScope, scriptSource)
else -> RestrictedScriptContext.UnknownScript
}

private
fun requirePluginContext(evaluationContext: DeclarativeKotlinScriptEvaluator.EvaluationContext): ScriptPluginEvaluationContext {
require(evaluationContext is ScriptPluginEvaluationContext) { "this target is not supported outside script plugins" }
return evaluationContext
}
}
Expand Up @@ -16,16 +16,12 @@

package org.gradle.internal.declarativedsl.evaluator

import org.gradle.api.initialization.Settings
import org.gradle.api.internal.SettingsInternal
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.internal.declarativedsl.evaluationSchema.EvaluationSchema
import org.gradle.internal.declarativedsl.evaluationSchema.InterpretationSequence
import org.gradle.internal.declarativedsl.evaluationSchema.InterpretationSequenceStep
import org.gradle.internal.declarativedsl.evaluator.InterpretationSchemaBuildingResult.InterpretationSequenceAvailable
import org.gradle.internal.declarativedsl.evaluator.InterpretationSchemaBuildingResult.SchemaNotBuilt
import org.gradle.internal.declarativedsl.plugins.schemaForPluginsBlock
import org.gradle.internal.declarativedsl.project.projectInterpretationSequence
import org.gradle.internal.declarativedsl.settings.settingsEvaluationSchema
import org.gradle.internal.declarativedsl.settings.settingsInterpretationSequence


internal
Expand All @@ -36,22 +32,13 @@ class DefaultInterpretationSchemaBuilder : InterpretationSchemaBuilder {
): InterpretationSchemaBuildingResult =
when (scriptContext) {
is RestrictedScriptContext.UnknownScript -> SchemaNotBuilt
RestrictedScriptContext.PluginsBlock -> simpleInterpretation("plugins", EvaluationSchema(schemaForPluginsBlock), targetInstance)
is RestrictedScriptContext.SettingsScript -> simpleInterpretation("settings", settingsEvaluationSchema(targetInstance as Settings), targetInstance)
is RestrictedScriptContext.ProjectScript ->
InterpretationSequenceAvailable(projectInterpretationSequence(targetInstance as ProjectInternal, scriptContext.targetScope, scriptContext.scriptSource))
}

private
fun simpleInterpretation(id: String, schema: EvaluationSchema, target: Any) =
InterpretationSequenceAvailable(
InterpretationSequence(
listOf(object : InterpretationSequenceStep<Any> {
override val stepIdentifier: String = id
override fun evaluationSchemaForStep(): EvaluationSchema = schema
override fun topLevelReceiver(): Any = target
override fun whenEvaluated(resultReceiver: Any) = Unit
})
is RestrictedScriptContext.SettingsScript -> InterpretationSequenceAvailable(
settingsInterpretationSequence(targetInstance as SettingsInternal, scriptContext.targetScope, scriptContext.scriptSource)
)

is RestrictedScriptContext.ProjectScript -> InterpretationSequenceAvailable(
projectInterpretationSequence(targetInstance as ProjectInternal, scriptContext.targetScope, scriptContext.scriptSource)
)
)
}
}
Expand Up @@ -44,8 +44,10 @@ sealed interface RestrictedScriptContext {
val scriptSource: ScriptSource
}

object SettingsScript : RestrictedScriptContext
object PluginsBlock : RestrictedScriptContext
data class SettingsScript(
override val targetScope: ClassLoaderScope,
override val scriptSource: ScriptSource
) : ScriptDependentContext

class ProjectScript(
override val targetScope: ClassLoaderScope,
Expand Down
Expand Up @@ -21,12 +21,14 @@ import org.gradle.api.internal.initialization.ScriptHandlerFactory
import org.gradle.api.internal.plugins.PluginManagerInternal
import org.gradle.groovy.scripts.ScriptSource
import org.gradle.internal.declarativedsl.analysis.AnalysisStatementFilter
import org.gradle.internal.declarativedsl.analysis.ObjectOrigin
import org.gradle.internal.declarativedsl.analysis.AnalysisStatementFilter.Companion.isCallNamed
import org.gradle.internal.declarativedsl.analysis.AnalysisStatementFilter.Companion.isConfiguringCall
import org.gradle.internal.declarativedsl.analysis.AnalysisStatementFilter.Companion.isTopLevelElement
import org.gradle.internal.declarativedsl.analysis.and
import org.gradle.internal.declarativedsl.analysis.implies
import org.gradle.internal.declarativedsl.analysis.not
import org.gradle.internal.declarativedsl.evaluationSchema.EvaluationSchema
import org.gradle.internal.declarativedsl.evaluationSchema.InterpretationSequenceStep
import org.gradle.internal.declarativedsl.language.DataStatement
import org.gradle.internal.declarativedsl.language.FunctionArgument
import org.gradle.internal.declarativedsl.language.FunctionCall
import org.gradle.internal.service.ServiceRegistry
import org.gradle.plugin.management.internal.DefaultPluginRequest
import org.gradle.plugin.management.internal.PluginRequestInternal
Expand All @@ -37,15 +39,15 @@ import org.gradle.plugin.use.internal.PluginRequestApplicator

internal
class PluginsInterpretationSequenceStep<T>(
override val stepIdentifier: String = "plugins",
private val target: T,
private val targetScope: ClassLoaderScope,
private val scriptSource: ScriptSource,
private val getTargetServices: (T) -> ServiceRegistry,
override val stepIdentifier: String = "plugins",
) : InterpretationSequenceStep<PluginsTopLevelReceiver> {
override fun evaluationSchemaForStep(): EvaluationSchema = EvaluationSchema(
schemaForPluginsBlock,
analysisStatementFilter = analyzeTopLevelPluginsBlockOnly
analysisStatementFilter = isTopLevelPluginsBlock
)

override fun topLevelReceiver() = PluginsTopLevelReceiver()
Expand All @@ -65,14 +67,13 @@ class PluginsInterpretationSequenceStep<T>(
}


private
val isPluginConfiguringCall: AnalysisStatementFilter = isConfiguringCall.and(isCallNamed("plugins"))


internal
val analyzeTopLevelPluginsBlockOnly = AnalysisStatementFilter { statement, scopes ->
if (scopes.last().receiver is ObjectOrigin.TopLevelReceiver) {
isPluginsCall(statement)
} else true
}
val isTopLevelPluginsBlock: AnalysisStatementFilter = isTopLevelElement.implies(isPluginConfiguringCall)


internal
fun isPluginsCall(statement: DataStatement) =
statement is FunctionCall && statement.name == "plugins" && statement.args.size == 1 && statement.args.single() is FunctionArgument.Lambda
val ignoreTopLevelPluginsBlock: AnalysisStatementFilter = isTopLevelElement.implies(isPluginConfiguringCall.not())

0 comments on commit 59bc291

Please sign in to comment.