diff --git a/gradle.properties b/gradle.properties index fff5d6001..56651d6da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version = 4.0.0 +version = 4.0.1 group = org.asciidoctor sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -12,7 +12,7 @@ project_vcs = https://github.com/asciidoctor/asciidoctor-gradle-plugin.g cglibVersion = 3.3.0 jsoupVersion = 1.13.1 spockVersion = 2.3-groovy-3.0 -grolifantVersion = 2.2.1 +grolifantVersion = 2.2.3 jacocoVersion = 0.8.6 codenarcVersion = 3.3.0 nodejsGradleVersion = 2.2.0 diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index bb5783b14..a70090f17 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -48,7 +48,7 @@ tasks.named('jar', Jar) { ext { pomConfig = { name project.name - description project.project_description +// description project.project_description url project.project_website inceptionYear '2013' licenses { @@ -115,9 +115,9 @@ ext { } publishing.publications.withType(MavenPublication).configureEach { - pom.withXml { - asNode().appendNode('description', project.project_description) - } +// pom.withXml { +// asNode().appendNode('description', project.project_description) +// } } pluginBundle { diff --git a/jvm/src/gradleTest/complex-jvm-setup/build.gradle b/jvm/src/gradleTest/complex-jvm-setup/build.gradle index 1166125b9..00e43ed59 100644 --- a/jvm/src/gradleTest/complex-jvm-setup/build.gradle +++ b/jvm/src/gradleTest/complex-jvm-setup/build.gradle @@ -9,7 +9,7 @@ repositories { asciidoctorj { modules { - diagram.version '1.5.16' + diagram.version '1.5.16' } logLevel 'INFO' } diff --git a/jvm/src/gradleTest/extension-in-subproject/extension/build.gradle b/jvm/src/gradleTest/extension-in-subproject/extension/build.gradle index 6dc123a25..6832f2c40 100644 --- a/jvm/src/gradleTest/extension-in-subproject/extension/build.gradle +++ b/jvm/src/gradleTest/extension-in-subproject/extension/build.gradle @@ -2,7 +2,6 @@ plugins { id 'groovy' } - dependencies { compileOnly "org.codehaus.groovy:groovy:${System.getProperty('GROOVY_VERSION')}" compileOnly "org.asciidoctor:asciidoctorj:${System.getProperty('ASCIIDOCTORJ_VERSION')}" diff --git a/jvm/src/intTest/groovy/org/asciidoctor/gradle/jvm/ExtensionsFunctionalSpec.groovy b/jvm/src/intTest/groovy/org/asciidoctor/gradle/jvm/ExtensionsFunctionalSpec.groovy index d4e3d43e8..420a1e447 100644 --- a/jvm/src/intTest/groovy/org/asciidoctor/gradle/jvm/ExtensionsFunctionalSpec.groovy +++ b/jvm/src/intTest/groovy/org/asciidoctor/gradle/jvm/ExtensionsFunctionalSpec.groovy @@ -46,7 +46,7 @@ class ExtensionsFunctionalSpec extends FunctionalSpecification { void 'Extension can be applied via closure (#model)'() { given: getBuildFile( - model.processMode, model.version, """ + model.processMode, model.version, """ asciidoctor { asciidoctorj { docExtensions { @@ -84,12 +84,95 @@ class ExtensionsFunctionalSpec extends FunctionalSpecification { model << AsciidoctorjVersionProcessModeGenerator.get() } + @SuppressWarnings('GStringExpressionWithinString') + void 'Extension can be applied via closure (Gradle #gradle)'() { + given: + final model = AsciidoctorjVersionProcessModeGenerator.get().first() + getBuildFile( + model.processMode, model.version, """ + asciidoctor { + asciidoctorj { + docExtensions { + block(name: "BIG", contexts: [":paragraph"]) { + parent, reader, attributes -> + def upperLines = reader.readLines()*.toUpperCase() + .inject("") {a, b -> a + '\\\\n' + b} + + createBlock(parent, "paragraph", [upperLines], attributes, [:]) + } + block("small") { + parent, reader, attributes -> + def lowerLines = reader.readLines()*.toLowerCase() + .inject("") {a, b -> a + '\\\\n' + b} + + createBlock(parent, "paragraph", [lowerLines], attributes, [:]) + } + } + } + } + """.stripIndent()) + + GradleRunner runner = getGradleRunner(DEFAULT_ARGS).withGradleVersion(gradle) + + when: + runner.build() + File resultFile = new File(buildDir, "docs/asciidoc/${ASCIIDOC_INLINE_EXTENSIONS_FILE.replaceFirst('asciidoc', 'html')}") + + then: 'content is generated as HTML and XML' + resultFile.exists() + resultFile.text.contains('WRITE THIS IN UPPERCASE') + resultFile.text.contains('and write this in lowercase') + + where: + gradle << ['8.3', '8.1.1', '7.6.1', '7.0.2'] + } + + @SuppressWarnings('GStringExpressionWithinString') + void 'Extension cannot be applied via closure if Gradle #gradle'() { + given: + final model = AsciidoctorjVersionProcessModeGenerator.get().first() + getBuildFile( + model.processMode, model.version, """ + asciidoctor { + asciidoctorj { + docExtensions { + block(name: "BIG", contexts: [":paragraph"]) { + parent, reader, attributes -> + def upperLines = reader.readLines()*.toUpperCase() + .inject("") {a, b -> a + '\\\\n' + b} + + createBlock(parent, "paragraph", [upperLines], attributes, [:]) + } + block("small") { + parent, reader, attributes -> + def lowerLines = reader.readLines()*.toLowerCase() + .inject("") {a, b -> a + '\\\\n' + b} + + createBlock(parent, "paragraph", [lowerLines], attributes, [:]) + } + } + } + } + """.stripIndent()) + + GradleRunner runner = getGradleRunner(DEFAULT_ARGS).withGradleVersion(gradle) + + when: + final result = runner.buildAndFail() + + then: + result.output.contains('Closures are not supported on Gradle 8.4+ due to Gradle instrumentation issues. Place the content in a string or load from it from a file instead.') + + where: + gradle << ['8.4', '8.5'] + } + @Unroll @Timeout(value = 90) void 'Extension can be applied from a string (#model)'() { given: getBuildFile( - model.processMode, model.version, """ + model.processMode, model.version, """ asciidoctor { asciidoctorj { @@ -132,12 +215,61 @@ block('small') { model << AsciidoctorjVersionProcessModeGenerator.get() } + @Unroll + @Timeout(value = 90) + void 'Extension can be applied from a string (Gradle #gradle)'() { + given: + final model = AsciidoctorjVersionProcessModeGenerator.get().first() + getBuildFile( + model.processMode, model.version, """ +asciidoctor { + + asciidoctorj { + docExtensions ''' +block(name: 'BIG', contexts: [':paragraph']) { + parent, reader, attributes -> + def upperLines = reader.readLines() + .collect {it.toUpperCase()} + .inject('') {a, b -> "\${a}\\\n\${b}"} + + createBlock(parent, "paragraph", [upperLines], attributes, [:]) +} +block('small') { + parent, reader, attributes -> + def lowerLines = reader.readLines() + .collect {it.toLowerCase()} + .inject('') {a, b -> "\${a}\\\n\${b}"} + + createBlock(parent, 'paragraph', [lowerLines], attributes, [:]) +} +''' + } +} +""") + GradleRunner runner = getGradleRunner(DEFAULT_ARGS) + if (model.processMode != 'JAVA_EXEC') { + runner.withDebug(false) + } + + when: + runner.build() + File resultFile = new File(buildDir, "docs/asciidoc/${ASCIIDOC_INLINE_EXTENSIONS_FILE.replaceFirst('asciidoc', 'html')}") + + then: 'content is generated as HTML and XML' + resultFile.exists() + resultFile.text.contains('WRITE THIS IN UPPERCASE') + resultFile.text.contains('and write this in lowercase') + + where: + gradle << ['8.4', '8.3', '8.1.1', '7.6.1', '7.0.2'] + } + @Timeout(value = 90) @Unroll void 'Extension can be applied from file (#model)'() { given: getBuildFile( - model.processMode, model.version, """ + model.processMode, model.version, """ asciidoctor { asciidoctorj { docExtensions file('src/docs/asciidoc/blockMacro.groovy') @@ -170,7 +302,7 @@ asciidoctor { given: 'A build file that declares extensions' getBuildFile( - model.processMode, model.version, ''' + model.processMode, model.version, ''' asciidoctorj { docExtensions { postprocessor { document, output -> @@ -208,7 +340,7 @@ asciidoctor { void 'Fail build if extension fails to compile (#model)'() { given: getBuildFile( - model.processMode, model.version, """ + model.processMode, model.version, """ asciidoctor { asciidoctorj { docExtensions ''' @@ -241,22 +373,22 @@ asciidoctor { given: String extDSL = '''asciidoctorj.docExtensions file('src/docs/asciidoc/blockMacro.groovy')''' getBuildFile( - processMode, version, """ + processMode, version, """ ${extScope == GLOBAL ? extDSL : ''} asciidoctor { ${extScope == LOCAL ? extDSL : ''} } """, - verScope == GLOBAL + verScope == GLOBAL ) GradleRunner runner = getGradleRunner(DEFAULT_ARGS) when: runner.build() File resultFile = new File( - buildDir, - 'docs/asciidoc/' + ASCIIDOC_INLINE_EXTENSIONS_FILE.replaceFirst('asciidoc', 'html') + buildDir, + 'docs/asciidoc/' + ASCIIDOC_INLINE_EXTENSIONS_FILE.replaceFirst('asciidoc', 'html') ) then: 'content is generated as HTML and XML' @@ -270,14 +402,14 @@ asciidoctor { and: extScope | verScope - LOCAL | LOCAL - LOCAL | GLOBAL - GLOBAL | LOCAL - GLOBAL | GLOBAL + LOCAL | LOCAL + LOCAL | GLOBAL + GLOBAL | LOCAL + GLOBAL | GLOBAL } File getBuildFile( - final String processMode, final String version, final String extraContent, boolean configureGlobally = false) { + final String processMode, final String version, final String extraContent, boolean configureGlobally = false) { String versionConfig = """ asciidoctorj { diff --git a/jvm/src/main/groovy/org/asciidoctor/gradle/internal/AsciidoctorWorkerParameters.groovy b/jvm/src/main/groovy/org/asciidoctor/gradle/internal/AsciidoctorWorkerParameters.groovy index 55d529322..8ed726db0 100644 --- a/jvm/src/main/groovy/org/asciidoctor/gradle/internal/AsciidoctorWorkerParameters.groovy +++ b/jvm/src/main/groovy/org/asciidoctor/gradle/internal/AsciidoctorWorkerParameters.groovy @@ -18,13 +18,14 @@ package org.asciidoctor.gradle.internal import org.ysb33r.grolifant.api.remote.worker.SerializableWorkerAppParameters /** - * Parameters for serializing ASciidoctor jobs to workers. + * Parameters for serializing Asciidoctor jobs to workers. * * @author Schalk W> Cronjé * * @since 4.0 */ class AsciidoctorWorkerParameters implements SerializableWorkerAppParameters { + private static final long serialVersionUID = 1251694026305095019 /** * Whether to attempt conversions in parallel inside the worker. diff --git a/jvm/src/main/groovy/org/asciidoctor/gradle/internal/ExecutorConfiguration.groovy b/jvm/src/main/groovy/org/asciidoctor/gradle/internal/ExecutorConfiguration.groovy index fd3c7d62b..80c829ecd 100644 --- a/jvm/src/main/groovy/org/asciidoctor/gradle/internal/ExecutorConfiguration.groovy +++ b/jvm/src/main/groovy/org/asciidoctor/gradle/internal/ExecutorConfiguration.groovy @@ -29,6 +29,7 @@ import java.util.regex.Pattern @SuppressWarnings(['CloneableWithoutClone']) @TupleConstructor class ExecutorConfiguration implements Serializable, Cloneable { + private static final long serialVersionUID = -2024L File sourceDir File outputDir File projectDir diff --git a/jvm/src/main/groovy/org/asciidoctor/gradle/internal/ExecutorConfigurationContainer.groovy b/jvm/src/main/groovy/org/asciidoctor/gradle/internal/ExecutorConfigurationContainer.groovy index 2e642f54d..d62f816b4 100644 --- a/jvm/src/main/groovy/org/asciidoctor/gradle/internal/ExecutorConfigurationContainer.groovy +++ b/jvm/src/main/groovy/org/asciidoctor/gradle/internal/ExecutorConfigurationContainer.groovy @@ -17,7 +17,8 @@ package org.asciidoctor.gradle.internal import groovy.transform.CompileStatic -/** Contains a number of executor configurations. +/** + * Contains a number of executor configurations. * * @since 2.0.0 * @@ -25,6 +26,7 @@ import groovy.transform.CompileStatic */ @CompileStatic class ExecutorConfigurationContainer implements Serializable { + private static final long serialVersionUID = -2024L final List configurations ExecutorConfigurationContainer(Iterable list) { diff --git a/jvm/src/main/groovy/org/asciidoctor/gradle/internal/JavaExecUtils.groovy b/jvm/src/main/groovy/org/asciidoctor/gradle/internal/JavaExecUtils.groovy index 9d69b824d..c89e01247 100644 --- a/jvm/src/main/groovy/org/asciidoctor/gradle/internal/JavaExecUtils.groovy +++ b/jvm/src/main/groovy/org/asciidoctor/gradle/internal/JavaExecUtils.groovy @@ -22,8 +22,6 @@ import org.asciidoctor.gradle.remote.AsciidoctorJavaExec import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.FileCollection -import org.gradle.api.invocation.Gradle -import org.gradle.util.GradleVersion import org.ysb33r.grolifant.api.core.ProjectOperations import java.util.regex.Pattern @@ -45,16 +43,10 @@ class JavaExecUtils { */ public static final String JRUBY_COMPLETE_DEPENDENCY = 'org.jruby:jruby-complete' - /** The name of the Guava JAR used internally by Gradle. - * - */ - private static final FilenameFilter INTERNAL_GUAVA_PATTERN = internalGuavaPattern() - /** Get the classpath that needs to be passed to the external Java process. * * @param project Current Gradle project * @param asciidoctorClasspath External asciidoctor dependencies - * @param addInternalGuava Set to {@code true} to add internal Guava to classpath * @return A computed classpath that can be given to an external Java process. * * @deprecated @@ -62,28 +54,25 @@ class JavaExecUtils { @Deprecated static FileCollection getJavaExecClasspath( final Project project, - final FileCollection asciidoctorClasspath, - boolean addInternalGuava = false + final FileCollection asciidoctorClasspath ) { File entryPoint = getClassLocation(AsciidoctorJavaExec) File groovyJar = getClassLocation(GroovyObject) FileCollection fc = project.files(entryPoint, groovyJar, asciidoctorClasspath) - addInternalGuava ? project.files(fc, getInternalGuavaLocation(project.gradle)) : fc + fc } /** Get the classpath that needs to be passed to the external Java process. * * @param project Current Gradle project * @param asciidoctorClasspath External asciidoctor dependencies - * @param addInternalGuava Set to {@code true} to add internal Guava to classpath * @return A computed classpath that can be given to an external Java process. */ static FileCollection getJavaExecClasspath( final ProjectOperations po, - final FileCollection asciidoctorClasspath, - boolean addInternalGuava = false + final FileCollection asciidoctorClasspath ) { File entryPoint = getClassLocation(AsciidoctorJavaExec) File groovyJar = getClassLocation(GroovyObject) @@ -91,9 +80,6 @@ class JavaExecUtils { final fc = po.fsOperations.emptyFileCollection() fc.from(entryPoint, groovyJar) - if (addInternalGuava) { - fc.from(getInternalGuavaLocation(po)) - } fc + asciidoctorClasspath } @@ -153,43 +139,23 @@ class JavaExecUtils { getClassLocation(GroovyObject) } - /** Locate the internal Guava JAR from the Gradle distribution - * - * @param gradle Gradle instance - * @return Return Guava location. Never {@code null} - * @throw InternalGuavaLocationException - */ - static File getInternalGuavaLocation(ProjectOperations po) { - File[] files = new File(po.gradleUserHomeDir.get(), 'lib').listFiles(INTERNAL_GUAVA_PATTERN) - - if (!files) { - throw new InternalGuavaLocationException('Cannot locate a Guava JAR in the Gradle distribution') - } else if (files.size() > 1) { - throw new InternalGuavaLocationException( - "Found more than one Guava JAR in the Gradle distribution: ${files*.name}" - ) + static File getInternalGradleLibraryLocation(ProjectOperations po, final Pattern libraryPattern) { + final filter = new FilenameFilter() { + @Override + boolean accept(File dir, String name) { + name.matches(libraryPattern) + } } - files[0] - } - /** Locate the internal Guava JAR from the Gradle distribution - * - * @param gradle Gradle instance - * @return Return Guava location. Never {@code null} - * @throw InternalGuavaLocationException - * - * @deprecated - */ - @Deprecated - @SuppressWarnings('DuplicateStringLiteral') - static File getInternalGuavaLocation(Gradle gradle) { - File[] files = new File(gradle.gradleHomeDir, 'lib').listFiles(INTERNAL_GUAVA_PATTERN) + File[] files = new File(po.gradleHomeDir.get(), 'lib').listFiles(filter) if (!files) { - throw new InternalGuavaLocationException('Cannot locate a Guava JAR in the Gradle distribution') + throw new InternalGradleLibraryLocationException( + "Cannot locate a library in the Gradle distribution using ${libraryPattern}" + ) } else if (files.size() > 1) { - throw new InternalGuavaLocationException( - "Found more than one Guava JAR in the Gradle distribution: ${files*.name}" + throw new InternalGradleLibraryLocationException( + "Found more than one library matching ${libraryPattern} in the Gradle distribution: ${files*.name}" ) } files[0] @@ -197,25 +163,9 @@ class JavaExecUtils { /** Thrown when an internal Guava JAR cannot be located. * - * @since 3.0 + * @since 4.0 */ @InheritConstructors - static class InternalGuavaLocationException extends RuntimeException { - } - - private static FilenameFilter internalGuavaPattern() { - Pattern filter - if (GradleVersion.current() >= GradleVersion.version('5.0')) { - filter = ~/guava-([\d.]+)-[jre|android]+.jar/ - } else { - filter = ~/guava-jdk5-([\d.]+).jar/ - } - - new FilenameFilter() { - @Override - boolean accept(File dir, String name) { - name.matches(filter) - } - } + static class InternalGradleLibraryLocationException extends RuntimeException { } } diff --git a/jvm/src/main/groovy/org/asciidoctor/gradle/jvm/AbstractAsciidoctorTask.groovy b/jvm/src/main/groovy/org/asciidoctor/gradle/jvm/AbstractAsciidoctorTask.groovy index a9f306320..2d7c3c387 100644 --- a/jvm/src/main/groovy/org/asciidoctor/gradle/jvm/AbstractAsciidoctorTask.groovy +++ b/jvm/src/main/groovy/org/asciidoctor/gradle/jvm/AbstractAsciidoctorTask.groovy @@ -52,6 +52,7 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar import org.gradle.process.JavaForkOptions import org.gradle.workers.WorkerExecutor +import org.ysb33r.grolifant.api.core.LegacyLevel import org.ysb33r.grolifant.api.core.jvm.ExecutionMode import org.ysb33r.grolifant.api.core.jvm.JavaForkOptionsWithEnvProvider import org.ysb33r.grolifant.api.core.jvm.worker.WorkerAppParameterFactory @@ -66,6 +67,7 @@ import static org.asciidoctor.gradle.base.internal.AsciidoctorAttributes.prepare import static org.asciidoctor.gradle.base.internal.AsciidoctorAttributes.resolveAsCacheable import static org.asciidoctor.gradle.base.internal.AsciidoctorAttributes.resolveAsSerializable import static org.asciidoctor.gradle.internal.JavaExecUtils.getExecConfigurationDataFile +import static org.asciidoctor.gradle.internal.JavaExecUtils.getInternalGradleLibraryLocation import static org.gradle.api.tasks.PathSensitivity.RELATIVE /** @@ -426,8 +428,7 @@ class AbstractAsciidoctorTask extends AbstractJvmModelExecTask getSerializableAsciidoctorJExtensions() { + asciidoctorJExtensions.findAll { !(it instanceof Dependency) }.collect { + getSerializableAsciidoctorJExtension(it) + } + } + + private Object getSerializableAsciidoctorJExtension(Object ext) { + switch (ext) { + case CharSequence: + return projectOperations.stringTools.stringize(ext) + case Provider: + return getSerializableAsciidoctorJExtension(((Provider) ext).get()) + default: + return ext + } + } + private Map prepareWorkspacesByLanguage() { languagesAsOptionals.collectEntries { Optional lang -> Workspace workspace = prepareWorkspace(lang) @@ -652,7 +670,6 @@ class AbstractAsciidoctorTask extends AbstractJvmModelExecTask> - mapping } @@ -695,8 +712,7 @@ class AbstractAsciidoctorTask extends AbstractJvmModelExecTask closurePaths) { + // Jumping through hoops to make docExtensions based upon closures to work. + closurePaths.add(getClassLocation(org.gradle.internal.scripts.ScriptOrigin)) + if (LegacyLevel.PRE_8_4 && !LegacyLevel.PRE_7_6) { + closurePaths.add(getClassLocation(org.gradle.api.GradleException)) + } + if (LegacyLevel.PRE_8_4 && !LegacyLevel.PRE_8_3) { + closurePaths.add(getInternalGradleLibraryLocation( + projectOperations, + ~/gradle-internal-instrumentation-api-([\d.]+).jar/ + )) + closurePaths.add(getInternalGradleLibraryLocation( + projectOperations, + ~/gradle-instrumentation-declarations-([\d.]+).jar/ + )) + } + if (!LegacyLevel.PRE_8_1 && LegacyLevel.PRE_8_4) { + closurePaths.add(getClassLocation(kotlin.io.FilesKt)) + closurePaths.add(getClassLocation(org.gradle.internal.lazy.Lazy)) + closurePaths.add(getInternalGradleLibraryLocation(projectOperations, ~/fastutil-([\d.]+)-min.jar/)) + } + } + // TODO: Try to do this without a detached configuration private FileCollection jrubyLessConfiguration(List deps) { Configuration cfg = detachedConfigurationCreator.apply(deps) diff --git a/jvm/src/main/groovy/org/asciidoctor/gradle/jvm/AsciidoctorJExtension.groovy b/jvm/src/main/groovy/org/asciidoctor/gradle/jvm/AsciidoctorJExtension.groovy index 8d986c72a..afe8fa25f 100644 --- a/jvm/src/main/groovy/org/asciidoctor/gradle/jvm/AsciidoctorJExtension.groovy +++ b/jvm/src/main/groovy/org/asciidoctor/gradle/jvm/AsciidoctorJExtension.groovy @@ -28,6 +28,8 @@ import org.gradle.api.* import org.gradle.api.artifacts.* import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.logging.LogLevel +import org.gradle.api.provider.Provider +import org.ysb33r.grolifant.api.core.LegacyLevel import java.util.concurrent.Callable import java.util.function.BiConsumer @@ -64,6 +66,9 @@ class AsciidoctorJExtension extends AbstractImplementationEngineExtension { private static final String ASCIIDOCTOR_DEPENDENCY_PROPERTY_NAME = 'asciidoctorj' private static final String CONFIGURATION_NAME = "__\$\$${NAME}\$\$__" + @SuppressWarnings(['SpaceAfterOpeningBrace', 'SpaceBeforeClosingBrace']) + private static final Closure EMPTY_CONFIGURATOR = {} + // TODO: Kill this off // private static final boolean GUAVA_REQUIRED_FOR_EXTERNALS = !LegacyLevel.PRE_4_8 @@ -185,22 +190,126 @@ class AsciidoctorJExtension extends AbstractImplementationEngineExtension { * Defines extensions to be registered. The given parameters should * either contain Asciidoctor Groovy DSL closures or files * with content conforming to the Asciidoctor Groovy DSL. + *

+ * If you use this method, then Gradle JARs will be leaked on to the classpath. This might not be what you + * want. + *

+ * @param Closures of Asciidoctor Groovy DSL extensions. * - * @since 2.2.0 + * @since 4.0.0 + */ + void docExtensions(Closure... exts) { + if (LegacyLevel.PRE_8_4) { + addExtensions(exts as List) + } else { + throw new GradleException( + 'Closures are not supported on Gradle 8.4+ due to Gradle instrumentation issues. ' + + 'Place the content in a string or load from it from a file instead.' + ) + } + } + + /** + * Defines extensions to be registered. The given parameters should + * either contain Asciidoctor Groovy DSL closures or files + * with content conforming to the Asciidoctor Groovy DSL. + * + * @param Files of Groovy code of Asciidoctor Groovy DSL extensions. + * + * @since 4.0.0 + */ + void docExtensions(File... exts) { + addExtensions(exts as List) + } + + /** + * Defines extensions to be registered. The given parameters should + * either contain Asciidoctor Groovy DSL closures or files + * with content conforming to the Asciidoctor Groovy DSL. + * + * @param Strings of Groovy code of Asciidoctor Groovy DSL extensions. + * + * @since 4.0.0 + */ + void docExtensions(String... exts) { + addExtensions(exts as List) + } + + /** + * Defines extensions to be registered. The given parameters should + * either contain Asciidoctor Groovy DSL closures or files + * with content conforming to the Asciidoctor Groovy DSL. + * + * @param Provider to strings or files of Groovy code of Asciidoctor Groovy DSL extensions + * + * @since 4.0.0 */ - void docExtensions(Object... exts) { - addExtensions(exts as List) + void docExtensions(Provider... exts) { + addExtensions(exts as List) + } + + /** + * Defines extensions to be registered. The given parameters should + * either contain Asciidoctor Groovy DSL closures or files + * with content conforming to the Asciidoctor Groovy DSL. + * + *

+ * THis method is specifically useful for project dependencies. + *

+ * @param Dependencies containing Asciidoctor extensions + * + * @since 4.0.0 + */ + void docExtensions(Dependency... exts) { + addExtensions(exts as List) + } + + /** + * Defines extensions to be registered. The given parameters should + * either contain Asciidoctor Groovy DSL closures or files + * with content conforming to the Asciidoctor Groovy DSL. + * + *

+ * THis method is specifically useful for project dependencies. + *

+ * @param Dependencies containing Asciidoctor extensions + * + * @since 4.0.0 + */ + void docExtensions(Project... exts) { + addExtensions(exts as List) + } + + /** + * Defines extensions to be registered. The given parameters should + * either contain Asciidoctor Groovy DSL closures or files + * with content conforming to the Asciidoctor Groovy DSL. + * + * @param External dependency definitions using standard Gradle dependency notation. + * + * @since 4.0.0 + */ + void docExtensionsFromExternal(String... exts) { + addExtensions(Transform.toList(exts as List) { + dependencyCreator.apply(it.toString(), EMPTY_CONFIGURATOR) + } as List) } /** * Clears the existing list of extensions and replace with a new set. * - * If this is declared on a task extension all extention from the global + * If this is declared on a task extension all extension from the global * project extension will be ignored. * * @since 2.2.0 */ void setDocExtensions(Iterable newExtensions) { + if (!LegacyLevel.PRE_8_4 && newExtensions.find { it instanceof Closure }) { + throw new GradleException( + 'Closures are no longer supported on Gradle 8.4+ due to Gradle instrumentation issues. ' + + 'Place content in a string or load from it from a file instead.' + ) + } asciidoctorExtensions.clear() addExtensions(newExtensions as List) onlyTaskExtensions = true @@ -261,58 +370,6 @@ class AsciidoctorJExtension extends AbstractImplementationEngineExtension { ~/include file not found/ } -// /* ------------------------- -// tag::extension-property[] -// gemPaths:: One or more gem installation directories (separated by the system path separator). -// Use `gemPaths` to append. Use `setGemPaths` or `gemPaths=['path1','path2']` to overwrite. -// Use `asGemPath` to obtain a path string, separated by platform-specific separator. -// Type: `FileCollection`, but any collection of objects convertible with `project.files` can be passed -// Default: empty -// end::extension-property[] -// ------------------------- */ -// -// /** Returns the list of paths to be used for {@code GEM_HOME} -// * -// */ -// FileCollection getGemPaths() { -// if (!task || onlyTaskGems) { -// projectOperations.fsOperations.files(this.gemPaths) -// } else { -// projectOperations.fsOperations.files(this.gemPaths).from(extFromProject.gemPaths) -// } -// } -// -// /** Sets a new list of GEM paths to be used. -// * -// * @param paths Paths resolvable by {@ocde project.files} -// */ -// void setGemPaths(Iterable paths) { -// this.gemPaths.clear() -// this.gemPaths.addAll(paths) -// -// if (task) { -// this.onlyTaskGems = true -// } -// } -// -// /** Adds more paths for discovering GEMs. -// * -// * @param f Path objects that can be be converted with {@code project.file}. -// */ -// void gemPaths(Object... f) { -// this.gemPaths.addAll(f) -// } -// -// /** -// * Returns the list of paths to be used for GEM installations in a format that is -// * suitable for assignment to {@code GEM_HOME} -// * -// * Calling this will cause gemPath to be resolved immediately. -// */ -// String asGemPath() { -// getGemPaths().files*.toString().join(OS.pathSeparator) -// } - /* ------------------------- tag::extension-property[] jrubyVersion:: Minimum version of JRuby to be used. @@ -576,31 +633,6 @@ class AsciidoctorJExtension extends AbstractImplementationEngineExtension { this.version = v } -// /** Whether the Guava JAR that ships with the Gradle distribution should be injected into the -// * classpath for external AsciidoctorJ processes. -// * -// * If not set previously via {@link #setInjectInternalGuavaJar} then a default version depending of the version of -// * the Gradle distribution will be used. -// * -// * @return {@code true} if JAR should be injected. -// */ -// boolean getInjectInternalGuavaJar() { -// if (task) { -// this.injectGuavaJar == null ? extFromProject.injectInternalGuavaJar : this.injectGuavaJar -// } else { -// this.injectGuavaJar == null ? GUAVA_REQUIRED_FOR_EXTERNALS : this.injectGuavaJar -// } -// } - -// /** Whether the Guava JAR that ships with the Gradle distribution should be injected into the -// * classpath for external AsciidoctorJ processes. -// * -// * @param inject {@code true} if JAR should be injected. -// */ -// void setInjectInternalGuavaJar(boolean inject) { -// this.injectGuavaJar = inject -// } - /** * Returns a runConfiguration of the configured AsciidoctorJ dependencies. * diff --git a/jvm/src/test/groovy/org/asciidoctor/gradle/internal/JavaExecUtilsSpec.groovy b/jvm/src/test/groovy/org/asciidoctor/gradle/internal/JavaExecUtilsSpec.groovy deleted file mode 100644 index 6d4521b6d..000000000 --- a/jvm/src/test/groovy/org/asciidoctor/gradle/internal/JavaExecUtilsSpec.groovy +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013-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. - * You may obtain a copy of the License at - * - * http://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.asciidoctor.gradle.internal - -import org.gradle.api.invocation.Gradle -import spock.lang.Specification -import spock.lang.TempDir - -import static org.asciidoctor.gradle.internal.JavaExecUtils.getInternalGuavaLocation - -class JavaExecUtilsSpec extends Specification { - - @TempDir - File temporaryFolder - - void 'Throw exception if internal Guava cannot be found'() { - setup: - def gradle = Stub(Gradle) - gradle.gradleHomeDir >> temporaryFolder - - when: - getInternalGuavaLocation(gradle) - - then: - def e = thrown(JavaExecUtils.InternalGuavaLocationException) - e.message.contains('Cannot locate a Guava JAR in the Gradle distribution') - } - - void 'Throw exception if multiple internal Guava JARs are found'() { - setup: - def gradle = Stub(Gradle) - gradle.gradleHomeDir >> temporaryFolder - new File(temporaryFolder, 'lib').mkdirs() - new File(temporaryFolder, 'lib/guava-0.0-android.jar').text = '' - new File(temporaryFolder, 'lib/guava-0.1-android.jar').text = '' - - when: - getInternalGuavaLocation(gradle) - - then: - def e = thrown(JavaExecUtils.InternalGuavaLocationException) - e.message.contains('Found more than one Guava JAR in the Gradle distribution') - } - - void 'detect jre variant of guava'() { - setup: - def gradle = Stub(Gradle) - gradle.gradleHomeDir >> temporaryFolder - new File(temporaryFolder, 'lib').mkdirs() - new File(temporaryFolder, 'lib/guavasomething.jar') - def guavaJar = new File(temporaryFolder, 'lib/guava-30.0-jre.jar') - guavaJar.text = '' - - when: - def location = JavaExecUtils.getInternalGuavaLocation(gradle) - - then: - location == guavaJar - } -}