jdaugherty commented on code in PR #15948:
URL: https://github.com/apache/grails-core/pull/15948#discussion_r3608663437


##########
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/commands/GrailsCliGradlePlugin.groovy:
##########
@@ -0,0 +1,435 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.gradle.plugin.commands
+
+import java.util.jar.JarFile
+
+import groovy.transform.CompileDynamic
+import groovy.transform.CompileStatic
+
+import org.gradle.api.NamedDomainObjectProvider
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.DependencySet
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier
+import org.gradle.api.artifacts.result.ResolvedArtifactResult
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.JavaExec
+import org.gradle.api.tasks.SourceSet
+import org.gradle.api.tasks.TaskContainer
+import org.gradle.api.tasks.TaskProvider
+
+import grails.util.Environment
+import grails.util.GrailsNameUtils
+import org.grails.gradle.plugin.core.GrailsExtension
+import org.grails.gradle.plugin.core.GrailsGradlePlugin
+import org.grails.gradle.plugin.util.ClasspathUtils
+import org.grails.gradle.plugin.util.SourceSets
+import org.grails.io.support.FactoriesLoaderSupport
+import org.grails.build.parsing.CommandLineParser
+
+/**
+ * Configures the CLI tier of a Grails application or plugin project: the 
{@code grailsCli}
+ * configurations (with automatic discovery of companion {@code -cli} 
artifacts advertised by the
+ * dependency graph), the per-command tasks, the generic {@code 
runCommand}/{@code runScript}
+ * tasks, and the interactive {@code console}/{@code shell} tasks. Applied 
automatically by the
+ * Grails Gradle plugin.
+ *
+ * @since 8.0
+ */
+@CompileStatic
+class GrailsCliGradlePlugin implements Plugin<Project> {
+
+    public static final String APPLICATION_CONTEXT_COMMAND_CLASS = 
'org.apache.grails.core.cli.ApplicationCommand'
+
+    /**
+     * The dependency bucket carrying CLI-only dependencies (Grails commands 
and their libraries):
+     * compile-visible so `grails-app/commands` sources compile against the 
cli-only contract, on
+     * the command-runner classpath, but never on `runtimeClasspath`, 
`bootRun`, or packaged
+     * artifacts.
+     */
+    public static final String GRAILS_CLI_CONFIGURATION = 'grailsCli'
+
+    /** The resolvable view of {@link #GRAILS_CLI_CONFIGURATION} used by the 
command-runner tasks */
+    public static final String GRAILS_CLI_CLASSPATH_CONFIGURATION = 
'grailsCliClasspath'
+
+    /**
+     * Set this project property to {@code false} to stop the plugin from 
auto-provisioning the
+     * CLI tier onto {@link #GRAILS_CLI_CONFIGURATION}; equivalent to
+     * {@code grails { cliAutoProvision = false }}.
+     */
+    public static final String GRAILS_CLI_AUTO_PROVISION_PROPERTY = 
'grailsCliAutoProvision'
+
+    /** The internal probe configuration used to detect companion `-cli` 
modules */
+    public static final String GRAILS_CLI_DETECT_CONFIGURATION = 
'grailsCliDetect'
+
+    @Override
+    void apply(Project project) {
+        // self-sufficient when applied standalone: the auto-provisioning 
behavior is configured
+        // through the `grails` extension, normally registered by the Grails 
Gradle plugin
+        if (project.extensions.findByName('grails') == null) {
+            project.extensions.create('grails', GrailsExtension, project)
+        }
+
+        configureGrailsCliConfiguration(project)
+
+        configureConsoleTask(project)
+
+        configureApplicationCommands(project)
+
+        configureRunScript(project)
+
+        configureRunCommand(project)
+    }
+
+    /**
+     * Registers the `grailsCli` dependency bucket and its resolvable 
`grailsCliClasspath` view.
+     * `grailsCli` carries the CLI tier — command companion artifacts 
(`<artifactId>-cli`) and the
+     * libraries they need. It extends the compile classpaths (the same wiring 
`compileOnly` uses)
+     * so `grails-app/commands` sources compile against the cli-only contract, 
while staying off
+     * `runtimeClasspath` — and therefore out of `bootRun`, `bootJar`, and 
`bootWar`.
+     */
+    protected void configureGrailsCliConfiguration(Project project) {
+        ConfigurationContainer configurations = project.configurations
+        if (configurations.names.contains(GRAILS_CLI_CONFIGURATION)) {
+            return
+        }
+
+        Configuration grailsCli = 
configurations.create(GRAILS_CLI_CONFIGURATION)
+        grailsCli.canBeResolved = false
+        grailsCli.canBeConsumed = false
+        grailsCli.description = 'CLI-only dependencies (Grails commands and 
the libraries they need); compile-visible and on the command-runner classpath, 
but never on runtimeClasspath, bootRun, or packaged artifacts.'
+
+        // compile visibility for grails-app/commands sources plus the TEST 
classpaths (tests
+        // exercise commands inside the test JVM), while the main 
runtimeClasspath — and therefore
+        // bootRun, bootJar, and bootWar — never sees the cli tier; matching 
configurations that
+        // appear later (e.g. the integrationTest pair) are included as they 
are created
+        configurations.matching { Configuration it ->
+            it.name in ['compileClasspath', 'testCompileClasspath', 
'testRuntimeClasspath',
+                        'integrationTestCompileClasspath', 
'integrationTestRuntimeClasspath']
+        }.configureEach { Configuration it ->
+            it.extendsFrom(grailsCli)
+        }
+
+        Configuration grailsCliClasspath = 
configurations.create(GRAILS_CLI_CLASSPATH_CONFIGURATION)
+        grailsCliClasspath.extendsFrom(grailsCli)
+        grailsCliClasspath.canBeResolved = true
+        grailsCliClasspath.canBeConsumed = false
+        grailsCliClasspath.description = 'Resolvable view of grailsCli used by 
the command-runner tasks.'
+
+        Configuration grailsCliDetect = 
configurations.create(GRAILS_CLI_DETECT_CONFIGURATION)
+        grailsCliDetect.canBeResolved = true
+        grailsCliDetect.canBeConsumed = false
+        grailsCliDetect.visible = false
+        grailsCliDetect.description = 'Internal probe used to discover 
companion -cli artifacts advertised by dependencies.'
+        for (String bucket : ['api', 'implementation', 'runtimeOnly']) {
+            configurations.matching { Configuration it -> it.name == bucket 
}.configureEach { Configuration it ->
+                grailsCliDetect.extendsFrom(it)
+            }
+        }
+
+        // computed when the configuration is first resolved, so every 
dependency (and the
+        // extension configuration) declared by the build script is visible
+        grailsCli.withDependencies { DependencySet dependencies ->
+            autoProvisionCliDependencies(project, dependencies)
+        }
+    }
+
+    /**
+     * Auto-provisions the CLI tier onto {@code grailsCli}: the command 
contract and runner, plus
+     * every companion {@code -cli} artifact advertised by a dependency of the 
application. A
+     * module advertises its companion through the {@code Grails-Cli-Artifact} 
manifest attribute
+     * of its runtime jar (stamped by the framework's cli-artifact build 
convention; third-party
+     * plugins set it on their jar task). Discovery walks the full dependency 
graph (including
+     * transitive plugins) through a lenient resolution of an internal probe 
configuration.
+     * Disable with {@code grails { cliAutoProvision = false }}.
+     */
+    @CompileDynamic
+    protected void autoProvisionCliDependencies(Project project, DependencySet 
dependencies) {
+        GrailsExtension grails = project.extensions.getByType(GrailsExtension)
+        if (!grails.cliAutoProvision.get()) {
+            return
+        }
+
+        // command authoring and execution work out of the box: the command 
contract + the runner
+        
dependencies.add(project.dependencies.create('org.apache.grails:grails-core-cli'))
+        
dependencies.add(project.dependencies.create('org.apache.grails:grails-console'))
+
+        Configuration probe = 
project.configurations.getByName(GRAILS_CLI_DETECT_CONFIGURATION)
+        Set<String> companions = [] as Set
+        def lenientArtifacts = probe.incoming.artifactView { it.lenient(true) 
}.artifacts

Review Comment:
   I'm going to resolve this since this issue already exists with the existing 
implementation - this isn't new to this change. The ticket was created to 
address that issue.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to