Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions conventions/src/main/groovy/config.docs.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
// https://github.com/grails-plugins/grails-plugin-template/tree/main/conventions
import grails.plugins.conventions.PluginDocsExtension

plugins {
// Gives the Groovydoc tasks their conventions (Gradle 9 requires the noIndex, noHelp, ... flags) and groovyRuntime
id 'groovy-base'
}

// Defaults follow the template naming convention (<name>-root, <name>, <name>-docs); override them with
// pluginDocs { pluginProject = ':my-plugin'; docsProject = ':my-docs' }
def docsExtension = extensions.create('pluginDocs', PluginDocsExtension)
Expand All @@ -12,6 +17,32 @@ docsExtension.docsProject.convention(":${project.name - 'root'}docs".toString())
def docProject = docsExtension.docsProject.map { project(it) }
def pluginProject = docsExtension.pluginProject.map { project(it) }

// Gradle 9 does not let a task resolve another project's configurations, so the aggregation does not read the plugin
// project's Groovydoc classpath: it resolves the plugin project (and the Groovy it is built with) through its own
def aggregateGroovydoc = configurations.dependencyScope('aggregateGroovydoc') {
description = 'Dependencies of the aggregated Groovy API documentation'
}
aggregateGroovydoc.configure {
def grailsVersion = providers.gradleProperty('grailsVersion').orElse(providers.provider {
throw new GradleException('config.docs needs grailsVersion in gradle.properties')
})
dependencies.addLater(grailsVersion.map {
project.dependencies.platform("org.apache.grails:grails-bom:${it}")
})
dependencies.add(project.dependencies.create('org.apache.groovy:groovy'))
dependencies.addLater(docsExtension.pluginProject.map { project.dependencies.project(path: it) })
}
def aggregateGroovydocClasspath = configurations.resolvable('aggregateGroovydocClasspath') {
description = 'Classpath of the aggregated Groovy API documentation'
extendsFrom(aggregateGroovydoc.get())
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, LibraryElements.JAR))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
}
}

tasks.register('cleanDocs', Delete) {
description = 'Deletes the documentation output'
group = 'documentation'
Expand All @@ -23,12 +54,12 @@ tasks.register('aggregateGroovyApiDoc', Groovydoc) {
description = 'Generates Groovy API Documentation for the plugin project under build/docs/gapi'
group = 'documentation'

// Only the sources are taken from the plugin project's Groovydoc task; the plugin jar is built through classpath
def upstream = pluginProject.flatMap {
it.tasks.named('groovydoc', Groovydoc)
} as Provider<Groovydoc>

dependsOn(tasks.named('cleanDocs'))
dependsOn(upstream)

access = GroovydocAccess.PROTECTED
includeAuthor = false
Expand All @@ -39,8 +70,8 @@ tasks.register('aggregateGroovyApiDoc', Groovydoc) {

source = { upstream.get().source }
destinationDir = rootProject.layout.buildDirectory.dir('docs/gapi').get().asFile
classpath = files({ upstream.get().classpath })
groovyClasspath = files({ upstream.get().groovyClasspath })
classpath = aggregateGroovydocClasspath.get()
groovyClasspath = groovyRuntime.inferGroovyClasspath(aggregateGroovydocClasspath.get())
}

tasks.register('docs') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package grails.plugins.conventions

import java.nio.file.Path

import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.gradle.util.GradleVersion
import spock.lang.Requires
import spock.lang.Specification
import spock.lang.TempDir

/**
* config.docs runs Groovydoc in the root project over the sources of the plugin project. Gradle 9 does not let a task
* resolve another project's configurations, so the aggregation must only resolve configurations of its own project.
*/
class PluginDocsSpec extends Specification {

@TempDir
Path projectDir

private void write(String path, String text) {
def file = projectDir.resolve(path).toFile()
file.parentFile.mkdirs()
file.text = text
}

@Requires({ Runtime.version().feature() >= data.javaVersion })
def 'config.docs aggregates the Groovydoc of a Grails #grailsVersion plugin project on Gradle #gradleVersion'() {
given: 'a root project with config.docs and a plugin project laid out like the template'
write('settings.gradle', '''
dependencyResolutionManagement {
repositories { mavenCentral() }
}
rootProject.name = 'sample-root'
include 'sample'
''')
write('gradle.properties', "grailsVersion=${grailsVersion}\n")
write('build.gradle', "plugins { id 'config.docs' }\n")
write('sample/build.gradle', """
plugins { id 'groovy' }
dependencies {
implementation platform('org.apache.grails:grails-bom:${grailsVersion}')
implementation 'org.apache.groovy:groovy'
}
""")
write('sample/src/main/groovy/sample/Greeter.groovy', '''
package sample
/** Greets people. */
class Greeter {
String greet(String name) { "Hello ${name}" }
}
''')

when:
def result = GradleRunner.create()
.withProjectDir(projectDir.toFile())
.withPluginClasspath()
.withGradleVersion(gradleVersion)
// consumers build in parallel (org.gradle.parallel=true), where a task only holds the lock of its own project
.withArguments('aggregateGroovyApiDoc', '--parallel', '--stacktrace')
.forwardOutput()
.build()

then:
result.task(':aggregateGroovyApiDoc').outcome == TaskOutcome.SUCCESS
projectDir.resolve('build/docs/gapi/sample/Greeter.html').toFile().file

and: 'only the sources are taken from the plugin project, its own Groovydoc is not generated'
result.task(':sample:groovydoc') == null

where:
gradleVersion | grailsVersion | javaVersion
GradleVersion.current().version | System.getProperty('grailsVersion') | 17
System.getProperty('grails8Gradle') | System.getProperty('grails8Version') | 21
}
}
Loading