Skip to content

Commit 3baac8a

Browse files
committed
Add 'disable(...)' and 'enable(...)' options
1 parent 05ea60c commit 3baac8a

File tree

5 files changed

+241
-57
lines changed

5 files changed

+241
-57
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Extra Java Module Info Gradle Plugin - Changelog
22

33
## Version 1.11
4+
* [New] [#134](https://github.com/gradlex-org/extra-java-module-info/pull/134) - Add 'disable(...)' and 'enable(...)' options to control when the plugin is active
45
* [New] [#161](https://github.com/gradlex-org/extra-java-module-info/pull/161) - Add 'skipLocalJars' option
56
* [New] [#106](https://github.com/gradlex-org/extra-java-module-info/pull/106) - Actionable error message when plugin is used at configuration time
67

README.md

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -126,51 +126,29 @@ Sample uses Gradle's Kotlin DSL (`build.gradle.kts` file). The Groovy DSL syntax
126126

127127
# FAQ
128128

129-
## How do I deactivate the plugin functionality for a certain classpath?
129+
## How do I (de)activate the plugin functionality for a certain classpath?
130130

131-
This can be useful for the test classpath if it should be used for unit testing on the classpath (rather than the module path).
132-
If you use the [shadow plugin](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) and [encounter this issue](https://github.com/gradlex-org/extra-java-module-info/issues/7),
133-
you can deactivate it for the runtime classpath as the module information is irrelevant for a fat Jar in any case.
131+
The plugin activates Jar transformation functionality individually for each
132+
[resolvable configuration](https://docs.gradle.org/current/userguide/declaring_configurations.html#sec:configuration-flags-roles)
133+
(.e.g `compileClasspath`, `runtimeClasspath`, `testRuntimeClasspath`).
134+
When any of these configurations are used, for example by the `compileJava` task, the transformed Jars will be used
135+
instead of the original ones. By default, the plugin activates for all configurations that are linked to a _source set_,
136+
which are: `<sourcSet>CompileClasspath`, `<sourcSet>RuntimeClasspath` and `<sourceSet>AnnotationProcessor`.
134137

135-
**Kotlin DSL**
136-
```
137-
// Disable for a single Classpath (Configuration)
138-
configurations {
139-
runtimeClasspath { // testRuntimeClasspath, testCompileClasspath, ...
140-
attributes { attribute(Attribute.of("javaModule", Boolean::class.javaObjectType), false) }
141-
}
142-
}
143-
144-
// Disable for all 'annotationProcessor' paths
145-
sourceSets.all {
146-
configurations.getByName(annotationProcessorConfigurationName) {
147-
attributes { attribute(Attribute.of("javaModule", Boolean::class.javaObjectType), false) }
148-
}
149-
}
150-
```
138+
You can customize this as follows:
151139

152-
**Groovy DSL**
153-
```
154-
// Disable for a single Classpath (Configuration)
155-
configurations {
156-
runtimeClasspath { // testRuntimeClasspath, testCompileClasspath, ...
157-
attributes { attribute(Attribute.of("javaModule", Boolean), false) }
158-
}
159-
}
160-
161-
// Disable for all 'annotationProcessor' paths
162-
sourceSets.all {
163-
configurations.getByName(annotationProcessorConfigurationName) {
164-
attributes { attribute(Attribute.of("javaModule", Boolean), false) }
165-
}
166-
}
167-
```
140+
- Completely deactivate the plugin for a source set:<br/>
141+
`extraJavaModuleInfo { deactivate(sourceSets.test) }`
142+
- Deactivate the plugin for a selected configuration:<br/>
143+
`extraJavaModuleInfo { deactivate(configurations.annotationProcessor) }`
144+
- Activate the plugin for a custom configuration that is not linked to a source set:<br/>
145+
`extraJavaModuleInfo { activate(configurations["myCustomPath"]) }`
168146

169147
## How do I deactivate the plugin functionality for my own Jars?
170148

171149
A major use case of the plugin is to transform Jars from 3rd party repositories that you do not control.
172150
By default, however, the plugin looks at all Jars on the module paths – including the Jars Gradle builds from you own modules.
173-
This is working well in most cases. The jars are analyzed and the plugin detects that they are infact modules and does not modify them.
151+
This is working well in most cases. The jars are analyzed and the plugin detects that they are in fact modules and does not modify them.
174152
You can still optimize the plugin execution to completely skip analysis of locally-built Jars by setting `skipLocalJars = true`.
175153

176154
## How do I add `provides ... with ...` declarations to the `module-info.class` descriptor?

src/main/java/org/gradlex/javamodule/moduleinfo/ExtraJavaModuleInfoPlugin.java

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.gradle.api.Project;
2222
import org.gradle.api.Transformer;
2323
import org.gradle.api.artifacts.Configuration;
24-
import org.gradle.api.artifacts.ConfigurationContainer;
2524
import org.gradle.api.artifacts.component.ComponentIdentifier;
2625
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
2726
import org.gradle.api.artifacts.dsl.DependencyHandler;
@@ -59,6 +58,7 @@
5958
import static org.gradle.api.attributes.Usage.JAVA_RUNTIME;
6059
import static org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE;
6160
import static org.gradle.api.plugins.JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME;
61+
import static org.gradlex.javamodule.moduleinfo.ExtraJavaModuleInfoPluginExtension.JAVA_MODULE_ATTRIBUTE;
6262

6363
/**
6464
* Entry point of the plugin.
@@ -161,50 +161,41 @@ private void configureTransform(Project project, ExtraJavaModuleInfoPluginExtens
161161
});
162162

163163
Attribute<String> artifactType = Attribute.of("artifactType", String.class);
164-
Attribute<Boolean> javaModule = Attribute.of("javaModule", Boolean.class);
165164

166165
project.getExtensions().getByType(SourceSetContainer.class).all(sourceSet -> {
167-
Configuration runtimeClasspath = project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName());
168-
Configuration compileClasspath = project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName());
169-
Configuration annotationProcessor = project.getConfigurations().getByName(sourceSet.getAnnotationProcessorConfigurationName());
170-
Configuration runtimeElements = project.getConfigurations().findByName(sourceSet.getRuntimeElementsConfigurationName());
171-
Configuration apiElements = project.getConfigurations().findByName(sourceSet.getApiElementsConfigurationName());
172-
173-
// compile, runtime and annotation processor classpaths express that they only accept modules by requesting the javaModule=true attribute
174-
runtimeClasspath.getAttributes().attribute(javaModule, true);
175-
compileClasspath.getAttributes().attribute(javaModule, true);
176-
annotationProcessor.getAttributes().attribute(javaModule, true);
166+
// by default, activate plugin for all source sets
167+
extension.activate(sourceSet);
177168

178169
// outgoing variants may express that they already provide a modular Jar and can hence skip the transform altogether
170+
Configuration runtimeElements = project.getConfigurations().findByName(sourceSet.getRuntimeElementsConfigurationName());
171+
Configuration apiElements = project.getConfigurations().findByName(sourceSet.getApiElementsConfigurationName());
179172
if (GradleVersion.current().compareTo(GradleVersion.version("7.4")) >= 0) {
180173
if (runtimeElements != null) {
181-
runtimeElements.getOutgoing().getAttributes().attributeProvider(javaModule, extension.getSkipLocalJars());
174+
runtimeElements.getOutgoing().getAttributes().attributeProvider(JAVA_MODULE_ATTRIBUTE, extension.getSkipLocalJars());
182175
}
183176
if (apiElements != null) {
184-
apiElements.getOutgoing().getAttributes().attributeProvider(javaModule, extension.getSkipLocalJars());
177+
apiElements.getOutgoing().getAttributes().attributeProvider(JAVA_MODULE_ATTRIBUTE, extension.getSkipLocalJars());
185178
}
186179
} else {
187180
project.afterEvaluate(p -> {
188181
if (runtimeElements != null) {
189-
runtimeElements.getOutgoing().getAttributes().attribute(javaModule, extension.getSkipLocalJars().get());
182+
runtimeElements.getOutgoing().getAttributes().attribute(JAVA_MODULE_ATTRIBUTE, extension.getSkipLocalJars().get());
190183
}
191184
if (apiElements != null) {
192-
apiElements.getOutgoing().getAttributes().attribute(javaModule, extension.getSkipLocalJars().get());
185+
apiElements.getOutgoing().getAttributes().attribute(JAVA_MODULE_ATTRIBUTE, extension.getSkipLocalJars().get());
193186
}
194187
});
195188
}
196189
});
197190

198191
// Jars may be transformed (or merged into) Module Jars
199-
registerTransform("jar", project, extension, javaModulesMergeJars, artifactType, javaModule);
192+
registerTransform("jar", project, extension, javaModulesMergeJars, artifactType, JAVA_MODULE_ATTRIBUTE);
200193
// Classpath entries may also be zip files that may be merged into Module Jars (from the docs: "Class paths to the .jar, .zip or .class files)"
201-
registerTransform("zip", project, extension, javaModulesMergeJars, artifactType, javaModule);
194+
registerTransform("zip", project, extension, javaModulesMergeJars, artifactType, JAVA_MODULE_ATTRIBUTE);
202195
}
203196

204197
private void registerTransform(String fileExtension, Project project, ExtraJavaModuleInfoPluginExtension extension, Configuration javaModulesMergeJars, Attribute<String> artifactType, Attribute<Boolean> javaModule) {
205198
DependencyHandler dependencies = project.getDependencies();
206-
ConfigurationContainer configurations = project.getConfigurations();
207-
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
208199

209200
// all Jars have a javaModule=false attribute by default; the transform also recognizes modules and returns them without modification
210201
dependencies.getArtifactTypes().maybeCreate(fileExtension).getAttributes().attribute(javaModule, false);

src/main/java/org/gradlex/javamodule/moduleinfo/ExtraJavaModuleInfoPluginExtension.java

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@
1717
package org.gradlex.javamodule.moduleinfo;
1818

1919
import org.gradle.api.Action;
20+
import org.gradle.api.NamedDomainObjectProvider;
21+
import org.gradle.api.artifacts.Configuration;
22+
import org.gradle.api.artifacts.ConfigurationContainer;
2023
import org.gradle.api.artifacts.MinimalExternalModuleDependency;
24+
import org.gradle.api.attributes.Attribute;
2125
import org.gradle.api.model.ObjectFactory;
2226
import org.gradle.api.provider.MapProperty;
2327
import org.gradle.api.provider.Property;
2428
import org.gradle.api.provider.Provider;
29+
import org.gradle.api.tasks.SourceSet;
2530

2631
import javax.annotation.Nullable;
2732
import javax.inject.Inject;
@@ -33,10 +38,14 @@
3338
*/
3439
@SuppressWarnings("unused")
3540
public abstract class ExtraJavaModuleInfoPluginExtension {
41+
static Attribute<Boolean> JAVA_MODULE_ATTRIBUTE = Attribute.of("javaModule", Boolean.class);
3642

3743
@Inject
3844
protected abstract ObjectFactory getObjects();
3945

46+
@Inject
47+
protected abstract ConfigurationContainer getConfigurations();
48+
4049
public abstract MapProperty<String, ModuleSpec> getModuleSpecs();
4150
public abstract Property<Boolean> getFailOnMissingModuleInfo();
4251
public abstract Property<Boolean> getFailOnAutomaticModules();
@@ -209,4 +218,97 @@ public void knownModule(String coordinates, String moduleName) {
209218
public void knownModule(Provider<MinimalExternalModuleDependency> alias, String moduleName) {
210219
knownModule(alias.get().getModule().toString(), moduleName);
211220
}
221+
222+
/**
223+
* Activate the plugin's functionality for dependencies of all scopes of the given source set
224+
* (runtimeClasspath, compileClasspath, annotationProcessor).
225+
* Note that the plugin activates the functionality for all source sets by default.
226+
* Therefore, this method only has an effect for source sets for which a {@link #deactivate(SourceSet)}
227+
* has been performed.
228+
*
229+
* @param sourceSet the Source Set to activate (e.g. sourceSets.test)
230+
*/
231+
public void activate(SourceSet sourceSet) {
232+
Configuration runtimeClasspath = getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName());
233+
Configuration compileClasspath = getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName());
234+
Configuration annotationProcessor = getConfigurations().getByName(sourceSet.getAnnotationProcessorConfigurationName());
235+
236+
activate(runtimeClasspath);
237+
activate(compileClasspath);
238+
activate(annotationProcessor);
239+
}
240+
241+
/**
242+
* Activate the plugin's functionality for a single resolvable Configuration.
243+
* This is useful to use the plugins for scopes that are not tied to a Source Set,
244+
* for which the plugin does not activate automatically.
245+
*
246+
* @param resolvable a resolvable Configuration (e.g. configurations["customClasspath"])
247+
*/
248+
public void activate(Configuration resolvable) {
249+
resolvable.getAttributes().attribute(JAVA_MODULE_ATTRIBUTE, true);
250+
}
251+
252+
/**
253+
* Variant of {@link #activate(SourceSet)} and {@link #activate(Configuration)} that accepts either a
254+
* Provider of {@link SourceSet} or a Provider of {@link Configuration}. This is a convenience to use
255+
* notations like 'activate(sourceSets.main)' in Kotlin DSL.
256+
*
257+
* @param sourceSetOrResolvable the Source Set or Configuration to activate
258+
*/
259+
public void activate(NamedDomainObjectProvider<?> sourceSetOrResolvable) {
260+
Object realized = sourceSetOrResolvable.get();
261+
if (realized instanceof SourceSet) {
262+
activate((SourceSet) realized);
263+
} else if (realized instanceof Configuration) {
264+
activate((Configuration) realized);
265+
} else {
266+
throw new RuntimeException("Not SourceSet or Configuration: " + realized);
267+
}
268+
}
269+
270+
/**
271+
* Deactivate the plugin's functionality for dependencies of all scopes of the given source set
272+
* (runtimeClasspath, compileClasspath, annotationProcessor).
273+
*
274+
* @param sourceSet the Source Set to deactivate (e.g. sourceSets.test)
275+
*/
276+
public void deactivate(SourceSet sourceSet) {
277+
Configuration runtimeClasspath = getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName());
278+
Configuration compileClasspath = getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName());
279+
Configuration annotationProcessor = getConfigurations().getByName(sourceSet.getAnnotationProcessorConfigurationName());
280+
281+
deactivate(runtimeClasspath);
282+
deactivate(compileClasspath);
283+
deactivate(annotationProcessor);
284+
}
285+
286+
/**
287+
* Deactivate the plugin's functionality for a single resolvable Configuration.
288+
* This is useful if selected scopes do not use the Module Path and therefore
289+
* module information is not required.
290+
*
291+
* @param resolvable a resolvable Configuration (e.g. configurations.annotationProcessor)
292+
*/
293+
public void deactivate(Configuration resolvable) {
294+
resolvable.getAttributes().attribute(JAVA_MODULE_ATTRIBUTE, false);
295+
}
296+
297+
/**
298+
* Variant of {@link #deactivate(SourceSet)} and {@link #deactivate(Configuration)} that accepts either a
299+
* Provider of {@link SourceSet} or a Provider of {@link Configuration}. This is a convenience to use
300+
* notations like 'deactivate(sourceSets.test)' in Kotlin DSL.
301+
*
302+
* @param sourceSetOrResolvable the Source Set or Configuration to activate
303+
*/
304+
public void deactivate(NamedDomainObjectProvider<?> sourceSetOrResolvable) {
305+
Object realized = sourceSetOrResolvable.get();
306+
if (realized instanceof SourceSet) {
307+
deactivate((SourceSet) realized);
308+
} else if (realized instanceof Configuration) {
309+
deactivate((Configuration) realized);
310+
} else {
311+
throw new RuntimeException("Not SourceSet or Configuration: " + realized);
312+
}
313+
}
212314
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package org.gradlex.javamodule.moduleinfo.test
2+
3+
import org.gradlex.javamodule.moduleinfo.test.fixture.GradleBuild
4+
import spock.lang.Specification
5+
6+
class PluginActivationFunctionalTest extends Specification {
7+
8+
@Delegate
9+
GradleBuild build = new GradleBuild()
10+
11+
def setup() {
12+
settingsFile << 'rootProject.name = "test-project"'
13+
buildFile << '''
14+
plugins {
15+
id("java-library")
16+
id("org.gradlex.extra-java-module-info")
17+
}
18+
val customPath = configurations.create("customPath")
19+
dependencies {
20+
implementation("commons-cli:commons-cli:1.4")
21+
annotationProcessor("commons-cli:commons-cli:1.4")
22+
customPath("commons-cli:commons-cli:1.4")
23+
}
24+
extraJavaModuleInfo { module("commons-cli:commons-cli", "org.apache.commons.cli") }
25+
tasks.register("printRuntimeCP") {
26+
inputs.files(configurations.runtimeClasspath)
27+
doLast { print("RCP: "); println(inputs.files.files.map { it.name }) }
28+
}
29+
tasks.register("printCompileCP") {
30+
inputs.files(configurations.compileClasspath)
31+
doLast { print("CCP: "); println(inputs.files.files.map { it.name }) }
32+
}
33+
tasks.register("printAP") {
34+
inputs.files(configurations.annotationProcessor)
35+
doLast { print("AP: "); println(inputs.files.files.map { it.name }) }
36+
}
37+
tasks.register("printCustom") {
38+
inputs.files(configurations["customPath"])
39+
doLast { print("CUS: "); println(inputs.files.files.map { it.name }) }
40+
}
41+
'''
42+
}
43+
44+
def "plugin is a activated by default for all configurations of a source set"() {
45+
when:
46+
def result = task('printRuntimeCP', 'printCompileCP', 'printAP', '-q')
47+
48+
then:
49+
result.output.contains('RCP: [commons-cli-1.4-module.jar]')
50+
result.output.contains('CCP: [commons-cli-1.4-module.jar]')
51+
result.output.contains('AP: [commons-cli-1.4-module.jar]')
52+
}
53+
54+
def "plugin can be deactivated for a source set"() {
55+
given:
56+
buildFile << 'extraJavaModuleInfo { deactivate(sourceSets.main) }'
57+
58+
when:
59+
def result = task('printRuntimeCP', 'printCompileCP', 'printAP', '-q')
60+
61+
then:
62+
result.output.contains('RCP: [commons-cli-1.4.jar]')
63+
result.output.contains('CCP: [commons-cli-1.4.jar]')
64+
result.output.contains('AP: [commons-cli-1.4.jar]')
65+
}
66+
67+
def "plugin can be deactivated and later re-activated for a source set"() {
68+
given:
69+
buildFile << 'extraJavaModuleInfo { deactivate(sourceSets.main.get()) }\n'
70+
buildFile << 'extraJavaModuleInfo { activate(sourceSets.main) }\n'
71+
72+
when:
73+
def result = task('printRuntimeCP', 'printCompileCP', 'printAP', '-q')
74+
75+
then:
76+
result.output.contains('RCP: [commons-cli-1.4-module.jar]')
77+
result.output.contains('CCP: [commons-cli-1.4-module.jar]')
78+
result.output.contains('AP: [commons-cli-1.4-module.jar]')
79+
}
80+
81+
def "plugin can be deactivated for a single configuration"() {
82+
given:
83+
buildFile << 'extraJavaModuleInfo { deactivate(configurations.annotationProcessor) }'
84+
85+
when:
86+
def result = task('printRuntimeCP', 'printCompileCP', 'printAP', '-q')
87+
88+
then:
89+
result.output.contains('RCP: [commons-cli-1.4-module.jar]')
90+
result.output.contains('CCP: [commons-cli-1.4-module.jar]')
91+
result.output.contains('AP: [commons-cli-1.4.jar]')
92+
}
93+
94+
def "plugin is not active for custom configurations by default"() {
95+
when:
96+
def result = task('printCustom', '-q')
97+
98+
then:
99+
result.output.contains('CUS: [commons-cli-1.4.jar]')
100+
}
101+
102+
def "plugin can be activated for a single custom configuration"() {
103+
given:
104+
buildFile << 'extraJavaModuleInfo { activate(customPath) }'
105+
106+
when:
107+
def result = task('printCustom', '-q')
108+
109+
then:
110+
result.output.contains('CUS: [commons-cli-1.4-module.jar]')
111+
}
112+
}

0 commit comments

Comments
 (0)