Skip to content

Commit 2c88fe1

Browse files
committed
Support 'require /*runtime*/' directives for runtimeOnly dependencies
1 parent 22ff749 commit 2c88fe1

File tree

5 files changed

+126
-5
lines changed

5 files changed

+126
-5
lines changed

src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesPlugin.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ private void setupForJavaProject(Project project, JavaModuleDependenciesExtensio
8383
process(ModuleInfo.Directive.REQUIRES_STATIC, sourceSet.getCompileOnlyConfigurationName(), sourceSet, project, javaModuleDependencies);
8484
process(ModuleInfo.Directive.REQUIRES_TRANSITIVE, sourceSet.getApiConfigurationName(), sourceSet, project, javaModuleDependencies);
8585
process(ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE, sourceSet.getCompileOnlyApiConfigurationName(), sourceSet, project, javaModuleDependencies);
86+
process(ModuleInfo.Directive.REQUIRES_RUNTIME, sourceSet.getRuntimeOnlyConfigurationName(), sourceSet, project, javaModuleDependencies);
8687
});
8788
setupReportTasks(project, javaModuleDependencies);
8889
setupMigrationTasks(project, javaModuleDependencies);
@@ -136,6 +137,7 @@ private void setupMigrationTasks(Project project, JavaModuleDependenciesExtensio
136137
t.getImplementationDependencies().convention(declaredDependencies(project, sourceSet.getImplementationConfigurationName()));
137138
t.getCompileOnlyApiDependencies().convention(declaredDependencies(project, sourceSet.getCompileOnlyApiConfigurationName()));
138139
t.getCompileOnlyDependencies().convention(declaredDependencies(project, sourceSet.getCompileOnlyConfigurationName()));
140+
t.getRuntimeOnlyDependencies().convention(declaredDependencies(project, sourceSet.getRuntimeOnlyConfigurationName()));
139141

140142
t.getModuleInfoFile().convention(project.getLayout().file(project.provider(() ->
141143
new File(sourceSet.getJava().getSrcDirs().iterator().next(), "module-info.java"))));

src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,23 @@ public enum Directive {
3030
REQUIRES,
3131
REQUIRES_TRANSITIVE,
3232
REQUIRES_STATIC,
33-
REQUIRES_STATIC_TRANSITIVE;
33+
REQUIRES_STATIC_TRANSITIVE,
34+
REQUIRES_RUNTIME;
3435

3536
public String literal() {
36-
return toString().toLowerCase().replace("_", " ");
37+
return toString().toLowerCase().replace("_", " ")
38+
.replace("runtime", RUNTIME_KEYWORD);
3739
}
3840
}
3941

42+
public static final String RUNTIME_KEYWORD = "/*runtime*/";
43+
4044
private String moduleName;
4145
private final List<String> requires = new ArrayList<>();
4246
private final List<String> requiresTransitive = new ArrayList<>();
4347
private final List<String> requiresStatic = new ArrayList<>();
4448
private final List<String> requiresStaticTransitive = new ArrayList<>();
49+
private final List<String> requiresRuntime = new ArrayList<>();
4550

4651
public ModuleInfo(String moduleInfoFileContent) {
4752
boolean insideComment = false;
@@ -63,6 +68,9 @@ public List<String> get(Directive directive) {
6368
if (directive == Directive.REQUIRES_STATIC_TRANSITIVE) {
6469
return requiresStaticTransitive;
6570
}
71+
if (directive == Directive.REQUIRES_RUNTIME) {
72+
return requiresRuntime;
73+
}
6674
return Collections.emptyList();
6775
}
6876

@@ -91,9 +99,10 @@ private boolean parse(String moduleLine, boolean insideComment) {
9199
}
92100

93101
List<String> tokens = Arrays.asList(moduleLine
94-
.replace(";","")
95-
.replace("{","")
96-
.replaceAll("/\\*.*?\\*/"," ")
102+
.replace(";", "")
103+
.replace("{", "")
104+
.replace(RUNTIME_KEYWORD, "runtime")
105+
.replaceAll("/\\*.*?\\*/", " ")
97106
.trim().split("\\s+"));
98107
int singleLineCommentStartIndex = tokens.indexOf("//");
99108
if (singleLineCommentStartIndex >= 0) {
@@ -110,6 +119,8 @@ private boolean parse(String moduleLine, boolean insideComment) {
110119
requiresTransitive.add(tokens.get(2));
111120
} else if (tokens.size() > 2 && tokens.contains("static")) {
112121
requiresStatic.add(tokens.get(2));
122+
} else if (tokens.size() > 2 && tokens.contains("runtime")) {
123+
requiresRuntime.add(tokens.get(2));
113124
} else {
114125
requires.add(tokens.get(1));
115126
}

src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleInfoGeneration.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public abstract class ModuleInfoGeneration extends DefaultTask {
5353
@Input
5454
public abstract ListProperty<String> getCompileOnlyDependencies();
5555

56+
@Input
57+
public abstract ListProperty<String> getRuntimeOnlyDependencies();
58+
5659
@Input
5760
public abstract MapProperty<String, String> getModuleNameToGA();
5861

@@ -86,6 +89,10 @@ public void generate() throws IOException {
8689
content.addAll(dependenciesToModuleDirectives(getCompileOnlyDependencies().get(), REQUIRES_STATIC));
8790
content.add("");
8891
}
92+
if (!getRuntimeOnlyDependencies().get().isEmpty()) {
93+
content.addAll(dependenciesToModuleDirectives(getRuntimeOnlyDependencies().get(), REQUIRES_RUNTIME));
94+
content.add("");
95+
}
8996
content.add("}");
9097

9198
Files.write(moduleInfo.toPath(), content);

src/test/groovy/org/gradlex/javamodule/dependencies/test/ModuleInfoParseTest.groovy

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo
44
import spock.lang.Specification
55

66
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES
7+
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME
78
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE
89
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC
910
import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE
@@ -25,6 +26,7 @@ class ModuleInfoParseTest extends Specification {
2526
moduleInfo.get(REQUIRES_TRANSITIVE) == ["foo.bar.la"]
2627
moduleInfo.get(REQUIRES_STATIC) == []
2728
moduleInfo.get(REQUIRES_STATIC_TRANSITIVE) == []
29+
moduleInfo.get(REQUIRES_RUNTIME) == []
2830
}
2931

3032
def "ignores single line comments late in line"() {
@@ -41,6 +43,7 @@ class ModuleInfoParseTest extends Specification {
4143
moduleInfo.get(REQUIRES_TRANSITIVE) == ["foo.bar.la"]
4244
moduleInfo.get(REQUIRES_STATIC) == []
4345
moduleInfo.get(REQUIRES_STATIC_TRANSITIVE) == []
46+
moduleInfo.get(REQUIRES_RUNTIME) == []
4447
}
4548

4649
def "ignores multi line comments"() {
@@ -59,6 +62,7 @@ class ModuleInfoParseTest extends Specification {
5962
moduleInfo.get(REQUIRES_TRANSITIVE) == []
6063
moduleInfo.get(REQUIRES_STATIC) == ["foo.bar.la"]
6164
moduleInfo.get(REQUIRES_STATIC_TRANSITIVE) == []
65+
moduleInfo.get(REQUIRES_RUNTIME) == []
6266
}
6367

6468
def "ignores multi line comments between keywords"() {
@@ -78,6 +82,23 @@ class ModuleInfoParseTest extends Specification {
7882
moduleInfo.get(REQUIRES_TRANSITIVE) == ["foo.bar.la"]
7983
moduleInfo.get(REQUIRES_STATIC) == ["foo.bar.lo"]
8084
moduleInfo.get(REQUIRES_STATIC_TRANSITIVE) == []
85+
moduleInfo.get(REQUIRES_RUNTIME) == []
86+
}
87+
88+
def "supports runtime dependencies through special keyword"() {
89+
given:
90+
def moduleInfo = new ModuleInfo('''
91+
module some.thing {
92+
requires /*runtime*/ foo.bar.lo;
93+
}
94+
''')
95+
96+
expect:
97+
moduleInfo.get(REQUIRES) == []
98+
moduleInfo.get(REQUIRES_TRANSITIVE) == []
99+
moduleInfo.get(REQUIRES_STATIC) == []
100+
moduleInfo.get(REQUIRES_STATIC_TRANSITIVE) == []
101+
moduleInfo.get(REQUIRES_RUNTIME) == ["foo.bar.lo"]
81102
}
82103

83104
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.gradlex.javamodule.dependencies.test
2+
3+
import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild
4+
import spock.lang.Specification
5+
6+
class RequiresRuntimeTest extends Specification {
7+
8+
@Delegate
9+
GradleBuild build = new GradleBuild()
10+
11+
def "can define runtime only dependencies in module-info"() {
12+
given:
13+
appBuildFile << '''
14+
dependencies.constraints {
15+
javaModuleDependencies {
16+
implementation(gav("org.slf4j", "2.0.3"))
17+
implementation(gav("org.slf4j.simple", "2.0.3"))
18+
}
19+
}
20+
'''
21+
appModuleInfoFile << '''
22+
module org.gradlex.test.app {
23+
requires org.slf4j;
24+
requires /*runtime*/ org.slf4j.simple;
25+
}
26+
'''
27+
28+
when:
29+
def rt = printRuntimeJars()
30+
31+
then:
32+
rt.output.contains('[slf4j-simple-2.0.3.jar, slf4j-api-2.0.3.jar]')
33+
34+
when:
35+
def cp = printCompileJars()
36+
37+
then:
38+
cp.output.contains('[slf4j-api-2.0.3.jar]')
39+
}
40+
41+
def "compiles with runtime only dependencies in module-info"() {
42+
given:
43+
appBuildFile << '''
44+
dependencies.constraints {
45+
javaModuleDependencies {
46+
implementation(gav("org.slf4j", "2.0.3"))
47+
implementation(gav("org.slf4j.simple", "2.0.3"))
48+
}
49+
}
50+
'''
51+
appModuleInfoFile << '''
52+
module org.gradlex.test.app {
53+
requires org.slf4j;
54+
requires /*runtime*/ org.slf4j.simple;
55+
56+
exports org.gradlex.test.app;
57+
}
58+
'''
59+
file("app/src/main/java/org/gradlex/test/app/Main.java") << """
60+
package org.gradlex.test.app;
61+
62+
import org.slf4j.Logger;
63+
import org.slf4j.LoggerFactory;
64+
65+
public class Main {
66+
private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
67+
68+
public static void main(String[] args) {
69+
LOGGER.info("Running application...");
70+
}
71+
}
72+
"""
73+
74+
when:
75+
def result = run()
76+
77+
then:
78+
result.output.contains("[main] INFO org.gradlex.test.app.Main - Running application...")
79+
}
80+
}

0 commit comments

Comments
 (0)