Skip to content

Commit 1c3091e

Browse files
committed
Refactor BuildSpecTask to GenerateSpecTask
Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com>
1 parent fa585b9 commit 1c3091e

File tree

6 files changed

+128
-79
lines changed

6 files changed

+128
-79
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ The `nextflowPlugin` block supports the following configuration options:
4646
- **`requirePlugins`** (optional) - List of plugin dependencies that must be present
4747
- **`extensionPoints`** (optional) - List of extension point class names provided by the plugin
4848
- **`useDefaultDependencies`** (optional, default: `true`) - Whether to automatically add default dependencies required for Nextflow plugin development
49+
- **`generateSpec`** (optional, default: `true`) - Whether to generate a plugin spec file during the build. Set to `false` to skip spec file generation
4950

5051
### Registry Configuration
5152

src/main/groovy/io/nextflow/gradle/BuildSpecTask.groovy

Lines changed: 0 additions & 70 deletions
This file was deleted.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package io.nextflow.gradle
2+
3+
import org.gradle.api.file.RegularFileProperty
4+
import org.gradle.api.provider.ListProperty
5+
import org.gradle.api.tasks.Input
6+
import org.gradle.api.tasks.JavaExec
7+
import org.gradle.api.tasks.OutputFile
8+
9+
/**
10+
* Gradle task to generate the plugin specification file for a Nextflow plugin.
11+
*
12+
* <p>This task creates a JSON specification file (spec.json) that describes the plugin's
13+
* structure and capabilities. The spec file is used by Nextflow's plugin system to understand
14+
* what extension points and functionality the plugin provides.
15+
*
16+
* <p>This task extends {@link JavaExec} because it needs to execute Java code from the
17+
* Nextflow core library (specifically {@code nextflow.plugin.spec.PluginSpecWriter}) to
18+
* generate the spec file. The JavaExec task type provides the necessary infrastructure to:
19+
* <ul>
20+
* <li>Set up a Java process with the correct classpath</li>
21+
* <li>Execute a main class with arguments</li>
22+
* <li>Handle the execution lifecycle and error reporting</li>
23+
* </ul>
24+
*
25+
* <p>The task automatically checks if the configured Nextflow version supports plugin specs
26+
* (version 25.09.0 or later). For earlier versions, it creates an empty spec file to maintain
27+
* compatibility.
28+
*
29+
* <p>The generated spec file is placed at {@code build/resources/main/META-INF/spec.json}
30+
* and is included in the plugin's JAR file.
31+
*
32+
* @author Ben Sherman <bentshermann@gmail.com>
33+
*/
34+
abstract class GenerateSpecTask extends JavaExec {
35+
36+
/**
37+
* List of fully qualified class names that represent extension points provided by this plugin.
38+
* These classes extend or implement Nextflow extension point interfaces.
39+
*/
40+
@Input
41+
final ListProperty<String> extensionPoints
42+
43+
/**
44+
* The output file where the plugin specification JSON will be written.
45+
* Defaults to {@code build/resources/main/META-INF/spec.json}.
46+
*/
47+
@OutputFile
48+
final RegularFileProperty specFile
49+
50+
/**
51+
* Constructor that configures the task to execute the PluginSpecWriter from Nextflow core.
52+
* Sets up the classpath, main class, and arguments needed to generate the spec file.
53+
*/
54+
GenerateSpecTask() {
55+
extensionPoints = project.objects.listProperty(String)
56+
extensionPoints.convention(project.provider {
57+
project.extensions.getByType(NextflowPluginConfig).extensionPoints
58+
})
59+
60+
specFile = project.objects.fileProperty()
61+
specFile.convention(project.layout.buildDirectory.file("resources/main/META-INF/spec.json"))
62+
63+
getMainClass().set('nextflow.plugin.spec.PluginSpecWriter')
64+
65+
project.afterEvaluate {
66+
setClasspath(project.sourceSets.getByName('specFile').runtimeClasspath)
67+
setArgs([specFile.get().asFile.toString()] + extensionPoints.get())
68+
}
69+
70+
doFirst {
71+
specFile.get().asFile.parentFile.mkdirs()
72+
}
73+
}
74+
75+
/**
76+
* Executes the task to generate the plugin spec file.
77+
* Checks if the Nextflow version supports plugin specs (>= 25.09.0).
78+
* For unsupported versions, creates an empty spec file instead.
79+
*/
80+
@Override
81+
void exec() {
82+
def config = project.extensions.getByType(NextflowPluginConfig)
83+
if (!isVersionSupported(config.nextflowVersion)) {
84+
createEmptySpecFile()
85+
return
86+
}
87+
super.exec()
88+
}
89+
90+
/**
91+
* Determines whether the given Nextflow version supports plugin specifications.
92+
* Plugin specs are supported in Nextflow version 25.09.0 and later.
93+
*
94+
* @param nextflowVersion the Nextflow version string (e.g., "25.09.0-edge")
95+
* @return true if the version supports plugin specs, false otherwise
96+
*/
97+
private boolean isVersionSupported(String nextflowVersion) {
98+
try {
99+
def parts = nextflowVersion.split(/\./, 3)
100+
if (parts.length < 3)
101+
return false
102+
def major = Integer.parseInt(parts[0])
103+
def minor = Integer.parseInt(parts[1])
104+
return major >= 25 && minor >= 9
105+
} catch (Exception e) {
106+
project.logger.warn("Unable to parse Nextflow version '${nextflowVersion}', assuming plugin spec is not supported: ${e.message}")
107+
return false
108+
}
109+
}
110+
111+
/**
112+
* Creates an empty spec file for backward compatibility with Nextflow versions
113+
* that don't support plugin specifications.
114+
*/
115+
private void createEmptySpecFile() {
116+
specFile.get().asFile.text = ''
117+
}
118+
}

src/main/groovy/io/nextflow/gradle/NextflowPlugin.groovy

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class NextflowPlugin implements Plugin<Project> {
6262
}
6363

6464
// Create specFile source set early so configurations are available
65-
if( config.buildSpec ) {
65+
if( config.generateSpec ) {
6666
project.configurations.create('specFile')
6767
if (!project.sourceSets.findByName('specFile')) {
6868
project.sourceSets.create('specFile') { sourceSet ->
@@ -80,8 +80,8 @@ class NextflowPlugin implements Plugin<Project> {
8080
addDefaultDependencies(project, nextflowVersion)
8181
}
8282

83-
// dependencies for buildSpec task
84-
if( config.buildSpec ) {
83+
// dependencies for generateSpec task
84+
if( config.generateSpec ) {
8585
project.dependencies { deps ->
8686
deps.specFile "io.nextflow:nextflow:${nextflowVersion}"
8787
deps.specFile project.files(project.tasks.jar.archiveFile)
@@ -114,8 +114,8 @@ class NextflowPlugin implements Plugin<Project> {
114114
project.tasks.compileTestGroovy.dependsOn << extensionPointsTask
115115

116116
// buildSpec - generates the plugin spec file
117-
if( config.buildSpec ) {
118-
project.tasks.register('buildSpec', BuildSpecTask)
117+
if( config.generateSpec ) {
118+
project.tasks.register('buildSpec', GenerateSpecTask)
119119
project.tasks.buildSpec.dependsOn << [
120120
project.tasks.jar,
121121
project.tasks.compileSpecFileGroovy
@@ -129,7 +129,7 @@ class NextflowPlugin implements Plugin<Project> {
129129
project.tasks.classes
130130
]
131131
project.afterEvaluate {
132-
if( config.buildSpec )
132+
if( config.generateSpec )
133133
project.tasks.packagePlugin.dependsOn << project.tasks.buildSpec
134134
}
135135
project.tasks.assemble.dependsOn << project.tasks.packagePlugin

src/main/groovy/io/nextflow/gradle/NextflowPluginConfig.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import org.gradle.api.Project
1616
* publisher = 'nextflow'
1717
* className = 'com.example.ExamplePlugin'
1818
* useDefaultDependencies = false // optional, defaults to true
19-
* buildSpec = false // optional, defaults to true
19+
* generateSpec = false // optional, defaults to true
2020
* extensionPoints = [
2121
* 'com.example.ExampleFunctions'
2222
* ]
@@ -71,7 +71,7 @@ class NextflowPluginConfig {
7171
/**
7272
* Whether to generate a plugin spec (default: true)
7373
*/
74-
boolean buildSpec = true
74+
boolean generateSpec = true
7575

7676
/**
7777
* Configure registry publishing settings (optional)

src/test/groovy/io/nextflow/gradle/BuildSpecTaskTest.groovy renamed to src/test/groovy/io/nextflow/gradle/GenerateSpecTaskTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import spock.lang.Specification
66
*
77
* @author Ben Sherman <bentshermann@gmail.com>
88
*/
9-
class BuildSpecTaskTest extends Specification {
9+
class GenerateSpecTaskTest extends Specification {
1010

1111
def 'should determine whether Nextflow version is >=25.09.0-edge' () {
1212
given:

0 commit comments

Comments
 (0)