Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow plugins { } in settings.gradle.something #28345

Merged
merged 4 commits into from Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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())