Skip to content

Commit e879726

Browse files
committed
Converted the versionsProvidingConfiguration to be a configuration rather than a
string identifying the configuration. Added the ability to export all packages with the exception of named ones.
1 parent 57ecad4 commit e879726

File tree

8 files changed

+131
-24
lines changed

8 files changed

+131
-24
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ extraJavaModuleInfo {
7878
// "org.mycompany.server", "org.mycompany.client")
7979
// or simply export all packages
8080
// exportAllPackages()
81+
// or export all packages except specific named ones
82+
// exportAllPackagesExcept("org.mycompany.notgood1", "org.mycompany.notgood2")
8183

8284
requiresTransitive("org.apache.commons.logging")
8385
requires("java.sql")
@@ -216,7 +218,7 @@ This needs to be done in all subprojects. You use the `versionsProvidingConfigur
216218

217219
```kotlin
218220
extraJavaModuleInfo {
219-
versionsProvidingConfiguration = "mainRuntimeClasspath"
221+
versionsProvidingConfiguration = project.provider { project.configurations.named("mainRuntimeClasspath").get() }
220222
}
221223
```
222224

build.gradle.kts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
plugins {
22
id("groovy")
3+
`java-gradle-plugin`
34
id("org.gradlex.internal.plugin-publish-conventions") version "0.6"
45
}
56

@@ -15,7 +16,9 @@ java {
1516
dependencies {
1617
implementation("org.ow2.asm:asm:9.9")
1718

18-
testImplementation("org.spockframework:spock-core:2.3-groovy-4.0")
19+
testImplementation("org.spockframework:spock-core:2.3-groovy-4.0") {
20+
exclude(group = "org.codehaus.groovy")
21+
}
1922
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
2023
}
2124

@@ -33,21 +36,21 @@ pluginPublishConventions {
3336
}
3437
}
3538

36-
tasks.test {
37-
description = "Runs tests against the Gradle version the plugin is built with"
39+
tasks.withType<Test>().configureEach {
40+
group = "verification"
3841
classpath = sourceSets.test.get().runtimeClasspath
42+
testClassesDirs = sourceSets.test.get().output.classesDirs
3943
useJUnitPlatform()
4044
maxParallelForks = 4
4145
}
4246

47+
tasks.named("test") {
48+
description = "Runs tests against the Gradle version the plugin is built with"
49+
}
50+
4351
listOf("6.8.3", "6.9.4", "7.6.5", "8.14.2").forEach { gradleVersionUnderTest ->
4452
val testGradle = tasks.register<Test>("testGradle$gradleVersionUnderTest") {
45-
group = "verification"
4653
description = "Runs tests against Gradle $gradleVersionUnderTest"
47-
testClassesDirs = sourceSets.test.get().output.classesDirs
48-
classpath = sourceSets.test.get().runtimeClasspath
49-
useJUnitPlatform()
50-
maxParallelForks = 4
5154
systemProperty("gradleVersionUnderTest", gradleVersionUnderTest)
5255
if (gradleVersionUnderTest.startsWith("6")) {
5356
javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(11) }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public abstract class ExtraJavaModuleInfoPluginExtension {
5252
public abstract Property<Boolean> getFailOnModifiedDerivedModuleNames();
5353
public abstract Property<Boolean> getSkipLocalJars();
5454
public abstract Property<Boolean> getDeriveAutomaticModuleNamesFromFileNames();
55-
public abstract Property<String> getVersionsProvidingConfiguration();
55+
public abstract Property<Configuration> getVersionsProvidingConfiguration();
5656

5757
/**
5858
* Add full module information for a given Jar file.

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ private void addModuleDescriptor(File originalJar, File moduleJar, ModuleInfo mo
309309
byte[] existingModuleInfo = copyAndExtractProviders(inputStream, outputStream, moduleInfo.getRemovedPackages(), !moduleInfo.getMergedJars().isEmpty(), providers, packages);
310310
mergeJars(moduleInfo, outputStream, providers, packages);
311311
outputStream.putNextEntry(newReproducibleEntry("module-info.class"));
312+
313+
if (moduleInfo.exportAllPackages) {
314+
moduleInfo.exportAllPackagesExceptions.forEach(it -> packages.remove(packageToPath(it)));
315+
}
312316
outputStream.write(addModuleInfo(moduleInfo, providers, versionFromFilePath(originalJar.toPath()),
313317
moduleInfo.exportAllPackages ? packages : Collections.emptySet(),
314318
existingModuleInfo));
@@ -358,7 +362,7 @@ private byte[] copyAndExtractProviders(JarInputStream inputStream, JarOutputStre
358362
: entryName.substring(0, i)
359363
: "";
360364

361-
if (!removedPackages.contains(packagePath.replace('/', '.'))) {
365+
if (!removedPackages.contains(pathToPackage(packagePath))) {
362366
if (entryName.endsWith(".class") && !packagePath.isEmpty()) {
363367
packages.add(packagePath);
364368
}
@@ -424,20 +428,38 @@ public void visitEnd() {
424428
return classWriter.toByteArray();
425429
}
426430

431+
/**
432+
* Convert a Java package name to a path
433+
* @param packageName The package name
434+
* @return The package name converted to a path
435+
*/
436+
private static String packageToPath(String packageName) {
437+
return packageName.replace('.', '/');
438+
}
439+
440+
/**
441+
* Convert a path to a Java package name
442+
* @param path The path
443+
* @return The path converted to a package name
444+
*/
445+
private static String pathToPackage(String path) {
446+
return path.replace('/', '.');
447+
}
448+
427449
private void addModuleInfoEntires(ModuleInfo moduleInfo, Map<String, List<String>> providers, Set<String> autoExportedPackages, ModuleVisitor moduleVisitor) {
428450
for (String packageName : autoExportedPackages) {
429451
moduleVisitor.visitExport(packageName, 0);
430452
}
431453
for (Map.Entry<String, Set<String>> entry : moduleInfo.exports.entrySet()) {
432454
String packageName = entry.getKey();
433455
Set<String> modules = entry.getValue();
434-
moduleVisitor.visitExport(packageName.replace('.', '/'), 0, modules.toArray(new String[0]));
456+
moduleVisitor.visitExport(packageToPath(packageName), 0, modules.toArray(new String[0]));
435457
}
436458

437459
for (Map.Entry<String, Set<String>> entry : moduleInfo.opens.entrySet()) {
438460
String packageName = entry.getKey();
439461
Set<String> modules = entry.getValue();
440-
moduleVisitor.visitOpen(packageName.replace('.', '/'), 0, modules.toArray(new String[0]));
462+
moduleVisitor.visitOpen(packageToPath(packageName), 0, modules.toArray(new String[0]));
441463
}
442464

443465
if (moduleInfo.requireAllDefinedDependencies) {
@@ -482,7 +504,7 @@ private void addModuleInfoEntires(ModuleInfo moduleInfo, Map<String, List<String
482504
moduleVisitor.visitRequire(requireName, Opcodes.ACC_STATIC_PHASE | Opcodes.ACC_TRANSITIVE, null);
483505
}
484506
for (String usesName : moduleInfo.uses) {
485-
moduleVisitor.visitUse(usesName.replace('.', '/'));
507+
moduleVisitor.visitUse(packageToPath(usesName));
486508
}
487509
for (Map.Entry<String, List<String>> entry : providers.entrySet()) {
488510
String name = entry.getKey();
@@ -497,8 +519,8 @@ private void addModuleInfoEntires(ModuleInfo moduleInfo, Map<String, List<String
497519
}
498520
if (!implementations.isEmpty()) {
499521
moduleVisitor.visitProvide(
500-
name.replace('.', '/'),
501-
implementations.stream().map(impl -> impl.replace('.', '/')).toArray(String[]::new)
522+
packageToPath(name),
523+
implementations.stream().map(ExtraJavaModuleInfoTransform::packageToPath).toArray(String[]::new)
502524
);
503525
}
504526
}

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@
1919
import org.gradle.api.model.ObjectFactory;
2020
import org.jspecify.annotations.Nullable;
2121

22-
import java.util.Arrays;
23-
import java.util.LinkedHashMap;
24-
import java.util.LinkedHashSet;
25-
import java.util.Map;
26-
import java.util.Set;
22+
import java.util.*;
2723

2824
/**
2925
* Data class to hold the information that should be added as module-info.class to an existing Jar file.
@@ -43,6 +39,7 @@ public class ModuleInfo extends ModuleSpec {
4339
final Set<String> requiresStaticTransitive = new LinkedHashSet<>();
4440
final Map<String, Set<String>> ignoreServiceProviders = new LinkedHashMap<>();
4541
final Set<String> uses = new LinkedHashSet<>();
42+
final Set<String> exportAllPackagesExceptions = new LinkedHashSet<>();
4643

4744
boolean exportAllPackages;
4845
boolean requireAllDefinedDependencies;
@@ -135,7 +132,24 @@ public String getModuleVersion() {
135132
* Automatically export all packages of the Jar. Can be used instead of individual 'exports()' statements.
136133
*/
137134
public void exportAllPackages() {
135+
exportAllPackagesExcept(Collections.emptyList());
136+
}
137+
138+
/**
139+
* Automatically export all packages of the Jar. Can be used instead of individual 'exports()' statements.
140+
* @param exceptions A list of packages not to export
141+
*/
142+
public void exportAllPackagesExcept(String... exceptions) {
143+
exportAllPackagesExcept(Arrays.asList(exceptions));
144+
}
145+
146+
/**
147+
* Automatically export all packages of the Jar. Can be used instead of individual 'exports()' statements.
148+
* @param exceptions A list of packages not to export
149+
*/
150+
public void exportAllPackagesExcept(List<String> exceptions) {
138151
this.exportAllPackages = true;
152+
exportAllPackagesExceptions.addAll(exceptions);
139153
}
140154

141155
/**

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,13 @@ public class PublishedMetadata implements Serializable {
7676
}
7777

7878
@SuppressWarnings({"UnstableApiUsage", "unchecked"})
79-
private List<String> componentVariant(Provider<String> versionsProvidingConfiguration, Project project, String usage) {
79+
private List<String> componentVariant(Provider<Configuration> versionsProvidingConfiguration, Project project, String usage) {
8080
Configuration versionsSource;
8181
if (versionsProvidingConfiguration.isPresent()) {
82-
versionsSource = project.getConfigurations().named(versionsProvidingConfiguration.get()).get();
82+
versionsSource = versionsProvidingConfiguration.get();
83+
if (!versionsSource.isCanBeResolved()) {
84+
throw new IllegalArgumentException("Configuration '" + versionsSource.getName() + "' must be resolvable");
85+
}
8386
} else {
8487
// version provider is not configured, create on adhoc based on ALL classpaths of the project
8588
versionsSource = maybeCreateDefaultVersionSourcConfiguration(project.getConfigurations(), project.getObjects(),

src/test/groovy/org/gradlex/javamodule/moduleinfo/test/EdgeCasesFunctionalTest.groovy

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,69 @@ class EdgeCasesFunctionalTest extends Specification {
177177
run().task(':run').outcome == TaskOutcome.SUCCESS
178178
}
179179

180+
def "can automatically export all packages except specified of a legacy Jar"() {
181+
given:
182+
file("src/main/java/org/gradle/sample/app/Main.java") << """
183+
package org.gradle.sample.app;
184+
185+
import javax.json.JsonString;
186+
import javax.json.JsonValue;
187+
188+
public class Main {
189+
public static void main(String[] args) {
190+
JsonString jsonString = new JsonString() {
191+
@Override
192+
public boolean equals(Object obj) {
193+
return false;
194+
}
195+
@Override
196+
public CharSequence getChars() {
197+
return null;
198+
}
199+
@Override
200+
public String getString() {
201+
return null;
202+
}
203+
@Override
204+
public int hashCode() {
205+
return 0;
206+
}
207+
@Override
208+
public JsonValue.ValueType getValueType() {
209+
return null;
210+
}
211+
};
212+
}
213+
}
214+
"""
215+
file("src/main/java/module-info.java") << """
216+
module org.gradle.sample.app {
217+
exports org.gradle.sample.app;
218+
219+
requires org.glassfish.java.json;
220+
requires java.json;
221+
}
222+
"""
223+
buildFile << """
224+
dependencies {
225+
implementation("org.glassfish:jakarta.json:1.1.6")
226+
implementation("jakarta.json:jakarta.json-api:1.1.6")
227+
}
228+
229+
extraJavaModuleInfo {
230+
module("org.glassfish:jakarta.json", "org.glassfish.java.json") {
231+
exportAllPackagesExcept("javax.json", "javax.json.spi", "javax.json.stream")
232+
overrideModuleName()
233+
}
234+
knownModule("jakarta.json:jakarta.json-api", "java.json")
235+
}
236+
"""
237+
238+
expect:
239+
def result = failRun()
240+
result.output.matches(/(?s).*Package javax\.json[.a-z]* in both.*/)
241+
}
242+
180243
def "deriveAutomaticModuleNamesFromFileNames produces a build time error for invalid module names"() {
181244
given:
182245
buildFile << """

src/test/groovy/org/gradlex/javamodule/moduleinfo/test/RequireAllDefinedDependenciesFunctionalTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ class RequireAllDefinedDependenciesFunctionalTest extends Specification {
374374
given:
375375
def sharedBuildScript = """
376376
extraJavaModuleInfo {
377-
versionsProvidingConfiguration.set("mainRuntimeClasspath")
377+
versionsProvidingConfiguration = project.provider { project.configurations.named("mainRuntimeClasspath").get() }
378378
module(${libs.commonsHttpClient}, "org.apache.httpcomponents.httpclient")
379379
module(${libs.commonsLogging}, "org.apache.commons.logging")
380380
knownModule("commons-codec:commons-codec", "org.apache.commons.codec")

0 commit comments

Comments
 (0)