diff --git a/build.gradle b/build.gradle index 6ec6843..d59ab86 100644 --- a/build.gradle +++ b/build.gradle @@ -5,12 +5,12 @@ subprojects { subproject -> java { toolchain { - languageVersion = JavaLanguageVersion.of(11) + languageVersion = JavaLanguageVersion.of(17) } } int javaVersion - if(subproject.path == ':osgi-app') { + if(subproject.path == ':osgi-app' || ':multi-release-jar') { javaVersion = 11 } else { javaVersion = 8 diff --git a/gradle.properties b/gradle.properties index c86adc1..1e9e746 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ woggioniMavenRepositoryUrl=https://mvn.woggioni.net/ -lys.catalog.version=2023.06.11 +lys.catalog.version=2023.07.08 -version.myGradlePlugins=2023.06.13 +version.myGradlePlugins=2023.07.09 version.gradle=7.6 version.felix.config.admin=1.9.26 version.felix=7.0.5 diff --git a/multi-release-jar/build.gradle b/multi-release-jar/build.gradle index b2cf532..9bce826 100644 --- a/multi-release-jar/build.gradle +++ b/multi-release-jar/build.gradle @@ -1,6 +1,6 @@ plugins { id 'maven-publish' - id 'groovy-gradle-plugin' + id 'java-gradle-plugin' } gradlePlugin { diff --git a/multi-release-jar/src/main/groovy/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPlugin.groovy b/multi-release-jar/src/main/groovy/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPlugin.groovy deleted file mode 100644 index b050eb9..0000000 --- a/multi-release-jar/src/main/groovy/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPlugin.groovy +++ /dev/null @@ -1,198 +0,0 @@ -package net.woggioni.gradle.multi.release.jar - -import org.gradle.api.GradleException -import org.gradle.api.JavaVersion -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.attributes.LibraryElements -import org.gradle.api.attributes.java.TargetJvmVersion -import org.gradle.api.file.FileCollection -import org.gradle.api.file.SourceDirectorySet -import org.gradle.api.internal.plugins.DslObject -import org.gradle.api.model.ObjectFactory -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.* -import org.gradle.api.tasks.compile.JavaCompile -import org.gradle.jvm.tasks.Jar -import org.gradle.process.CommandLineArgumentProvider - -import java.lang.module.ModuleDescriptor -import java.util.jar.JarFile -import java.util.stream.Collectors -import java.util.zip.ZipFile - -import static org.gradle.api.attributes.LibraryElements.JAR -import static org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE - -class MultiReleaseJarPlugin implements Plugin { - - private static void jpmsModuleName(File file) { - JarFile jarFile = new JarFile(file).with { - if (it.isMultiRelease()) { - new JarFile( - file, - false, - ZipFile.OPEN_READ, - Runtime.version() - ) - } else { - it - } - } - String automaticModuleName = jarFile.manifest?.with {it.mainAttributes.getValue("Automatic-Module-Name") } - def moduleInfoEntry = jarFile.getJarEntry("module-info.class") - moduleInfoEntry - ?.with(jarFile.&getInputStream) - ?.withCloseable(ModuleDescriptor.&read) ?: automaticModuleName - ModuleDescriptor.read() - } - -// @Canonical - static class CompilerArgumentProvider implements CommandLineArgumentProvider { - - private final Project project - - private final ObjectFactory objects - - @Input - final Provider jpmsModuleName - - private final Map> patchModules - - @InputFiles - @CompileClasspath - final FileCollection sourceSetOutput - -// @InputFiles -// FileCollection getPatchModules() { -// return project.files(patchModules.entrySet().stream().flatMap { -// it.getValue().get().stream() -// }.toArray(String::new)) -// } - - CompilerArgumentProvider( - Project project, - ObjectFactory objects, - Provider jpmsModuleName, - Map> patchModules, - FileCollection sourceSetOutput) { - this.project = project - this.objects = objects - this.jpmsModuleName = jpmsModuleName - this.patchModules = patchModules - this.sourceSetOutput = sourceSetOutput - } - - @Override - Iterable asArguments() { - Map> patchModules = new HashMap<>(patchModules) - - String name = jpmsModuleName.get() - if(name) { - patchModules.computeIfAbsent(name) { - objects.listProperty(String.class).convention(new ArrayList()) - }.addAll(sourceSetOutput.collect { it.toString() }) - } else { - throw new GradleException("Missing property 'jpms.module.name'") - } - String sep = System.getProperty('path.separator') - List result = new ArrayList<>() - for(Map.Entry> entry : patchModules.entrySet()) { - String arg = entry.getValue().get().stream().collect(Collectors.joining(sep)) - result += '--patch-module' - result += "${entry.getKey()}=${arg}" - } - result - } - } - - @Override - void apply(Project project) { - project.pluginManager.apply(JavaPlugin) - MultiReleaseJarPluginExtension mrjpe = new MultiReleaseJarPluginExtension(project.objects) - project.extensions.add('multiReleaseJar', mrjpe) - JavaPluginExtension javaPluginExtension = project.extensions.findByType(JavaPluginExtension.class) - JavaVersion binaryVersion = javaPluginExtension.targetCompatibility ?: javaPluginExtension.toolchain?.with { - it.languageVersion.get() - } ?: JavaVersion.current() - if(binaryVersion > JavaVersion.VERSION_1_8) { - Configuration compileClasspathConfiguration = project.configurations.compileClasspath - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { - attributes { - attribute(LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.class, JAR)) - } - } - - SourceSet mainSourceSet = (project.sourceSets.main as SourceSet) - JavaCompile compileJavaTask = project.tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile).get() - compileJavaTask.configure { - options.release.set(JavaVersion.VERSION_1_8.majorVersion.toInteger()) - } - Jar jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar).get() - jarTask.configure { - manifest.attributes('Multi-Release': 'true') - } - ArrayList compileOutputs = new ArrayList<>() - compileOutputs << compileJavaTask.outputs.files - ArrayList sourcePaths = new ArrayList<>() - sourcePaths << mainSourceSet.java.sourceDirectories - Arrays.stream(JavaVersion.values()).filter { - it > JavaVersion.VERSION_1_8 && it <= binaryVersion - }.forEach {javaVersion -> - SourceDirectorySet sourceDirectorySet = - project.objects.sourceDirectorySet("java${javaVersion.majorVersion}", javaVersion.toString()) - sourceDirectorySet.with { - srcDir(new File(project.projectDir, "src/${mainSourceSet.name}/${sourceDirectorySet.name}")) - destinationDirectory.set(new File(project.buildDir, "classes/${mainSourceSet.name}/${sourceDirectorySet.name}")) - sourcePaths << sourceDirectories - } - new DslObject(mainSourceSet).getConvention().getPlugins().put(sourceDirectorySet.name, sourceDirectorySet) - mainSourceSet.getExtensions().add(SourceDirectorySet.class, sourceDirectorySet.name, sourceDirectorySet) - TaskProvider compileTask = project.tasks.register(JavaPlugin.COMPILE_JAVA_TASK_NAME + javaVersion.majorVersion, JavaCompile, { javaCompileTask -> - javaCompileTask.options.release.set(javaVersion.majorVersion.toInteger()) - javaCompileTask.classpath = compileClasspathConfiguration + compileOutputs.stream().reduce { fc1, fc2 -> fc1 + fc2 }.get() - javaCompileTask.options.compilerArgumentProviders.add( - new CompilerArgumentProvider( - project, - project.objects, - project.provider { - project.hasProperty("jpms.module.name") ? - project.property("jpms.module.name") : null - }, - mrjpe.patchModules, - mainSourceSet.output - ) - ) - javaCompileTask.source = sourceDirectorySet - javaCompileTask.destinationDirectory.set(sourceDirectorySet.destinationDirectory) - javaCompileTask.options.annotationProcessorPath = mainSourceSet.annotationProcessorPath - javaCompileTask.modularity.inferModulePath = javaPluginExtension.modularity.inferModulePath - javaCompileTask.options.sourcepath = sourcePaths.stream().reduce { fc1, fc2 -> fc1 + fc2 }.get() - javaCompileTask.javaCompiler = compileJavaTask.javaCompiler - }) - compileOutputs << compileTask.get().outputs.files - sourceDirectorySet.compiledBy(compileTask, { it.getDestinationDirectory()}) - jarTask.configure { - from(compileTask.get().destinationDirectory) { - into("META-INF/versions/${javaVersion.majorVersion}") - } - } - - } - SourceSet testSourceSet = (project.sourceSets.test as SourceSet) - testSourceSet.compileClasspath += compileOutputs.stream().reduce { fc1, fc2 -> fc1 + fc2 }.get() - testSourceSet.runtimeClasspath += compileOutputs.stream().reduce { fc1, fc2 -> fc1 + fc2 }.get() - - ["apiElements", "runtimeElements"].forEach { String name -> - Configuration conf = project.configurations.getByName(name) - conf.attributes { - attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, compileJavaTask.options.release.get()) - } - } - } - } -} diff --git a/multi-release-jar/src/main/java/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPlugin.java b/multi-release-jar/src/main/java/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPlugin.java new file mode 100644 index 0000000..82b59f9 --- /dev/null +++ b/multi-release-jar/src/main/java/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPlugin.java @@ -0,0 +1,219 @@ +package net.woggioni.gradle.multi.release.jar; + +import lombok.Getter; +import org.gradle.api.GradleException; +import org.gradle.api.JavaVersion; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.attributes.LibraryElements; +import org.gradle.api.attributes.java.TargetJvmVersion; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.SourceDirectorySet; +import org.gradle.api.internal.plugins.DslObject; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.CompileClasspath; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.compile.AbstractCompile; +import org.gradle.api.tasks.compile.JavaCompile; +import org.gradle.jvm.tasks.Jar; +import org.gradle.jvm.toolchain.JavaLanguageVersion; +import org.gradle.jvm.toolchain.JavaToolchainSpec; +import org.gradle.process.CommandLineArgumentProvider; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static org.gradle.api.attributes.LibraryElements.JAR; +import static org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE; + +public class MultiReleaseJarPlugin implements Plugin { + +// @SneakyThrows +// private static void jpmsModuleName(File file) { +// JarFile jarFile = new JarFile(file); +// if (jarFile.isMultiRelease()) { +// jarFile = new JarFile( +// file, +// false, +// ZipFile.OPEN_READ, +// Runtime.version() +// ); +// } +// String automaticModuleName = Optional.ofNullable(jarFile.getManifest()) +// .map(Manifest::getMainAttributes) +// .map(mainAttr -> mainAttr.getValue("Automatic-Module-Name")) +// .orElse(null); +// Optional moduleInfoEntry = Optional.ofNullable(jarFile.getJarEntry("module-info.class")); +// moduleInfoEntry +// .map(jarFile::getInputStream) +// .map(ModuleDescriptor::read) +// .orElse(automaticModuleName); +// } + + static class CompilerArgumentProvider implements CommandLineArgumentProvider { + + @Getter(onMethod_ = {@Input}) + private final Provider jpmsModuleName; + + @Getter(onMethod_ = {@Input}) + private final MapProperty> patchModules; + + @Getter(onMethod_ = {@InputFiles, @CompileClasspath}) + private final FileCollection sourceSetOutput; + + public CompilerArgumentProvider( + Provider jpmsModuleName, + MapProperty> patchModules, + FileCollection sourceSetOutput) { + this.jpmsModuleName = jpmsModuleName; + this.patchModules = patchModules; + this.sourceSetOutput = sourceSetOutput; + } + + @Override + public Iterable asArguments() { + Map> patchModules = new TreeMap<>(this.patchModules.get()); + + if (jpmsModuleName.isPresent()) { + String name = jpmsModuleName.get(); + patchModules.computeIfAbsent(name, k -> new ArrayList<>()) + .addAll( + StreamSupport.stream(sourceSetOutput.spliterator(), false) + .map(File::toString) + .collect(Collectors.toList()) + ); + } else { + throw new GradleException("Missing property 'jpms.module.name'"); + } + String sep = System.getProperty("path.separator"); + List result = new ArrayList<>(); + for (Map.Entry> entry : patchModules.entrySet()) { + String arg = String.join(sep, entry.getValue()); + result.add("--patch-module"); + result.add(entry.getKey() + "=" + arg); + } + return result; + } + } + + @Override + public void apply(Project project) { + project.getPluginManager().apply(JavaPlugin.class); + MultiReleaseJarPluginExtension mrjpe = project.getObjects().newInstance(MultiReleaseJarPluginExtension.class); + project.getExtensions().add("multiReleaseJar", mrjpe); + JavaPluginExtension javaPluginExtension = project.getExtensions().findByType(JavaPluginExtension.class); + SourceSetContainer ssc = javaPluginExtension.getSourceSets(); + JavaVersion binaryVersion = Optional.ofNullable(javaPluginExtension.getTargetCompatibility()) + .or(() -> Optional.ofNullable(javaPluginExtension.getToolchain()) + .map(JavaToolchainSpec::getLanguageVersion) + .filter(Property::isPresent) + .map(Property::get) + .map(JavaLanguageVersion::asInt) + .map(JavaVersion::toVersion) + ).orElseGet(JavaVersion::current); + if (Comparator.naturalOrder().compare(binaryVersion, JavaVersion.VERSION_1_8) > 0) { + Configuration compileClasspathConfiguration = project.getConfigurations() + .getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME); + project.getConfigurations().named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME, cfg -> { + cfg.attributes(attr -> { + attr.attribute(LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, JAR)); + }); + }); + + SourceSet mainSourceSet = ssc.getByName(SourceSet.MAIN_SOURCE_SET_NAME); + + JavaCompile compileJavaTask = project.getTasks() + .named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile.class, (JavaCompile t) -> { + t.getOptions().getRelease().set(Integer.parseInt(JavaVersion.VERSION_1_8.getMajorVersion())); + }).get(); + Jar jarTask = project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class, (Jar t) -> { + Map attrs = new HashMap<>(); + attrs.put("Multi-Release", "true"); + t.getManifest().attributes(attrs); + }).get(); + SourceDirectorySet java = mainSourceSet.getJava(); + ArrayList compileOutputs = new ArrayList<>(); + compileOutputs.add(compileJavaTask.getOutputs().getFiles()); + ArrayList sourcePaths = new ArrayList<>(); + sourcePaths.add(java.getSourceDirectories()); + Comparator cmp = Comparator.naturalOrder(); + Arrays.stream(JavaVersion.values()) + .filter((JavaVersion jv) -> cmp.compare(jv, JavaVersion.VERSION_1_8) > 0 && cmp.compare(jv, binaryVersion) <= 0) + .forEach(javaVersion -> { + SourceDirectorySet sourceDirectorySet = project.getObjects() + .sourceDirectorySet("java" + javaVersion.getMajorVersion(), javaVersion.toString()); + sourceDirectorySet.srcDir(new File(project.getProjectDir(), + "src/" + mainSourceSet.getName() + "/" + sourceDirectorySet.getName())); + sourceDirectorySet.getDestinationDirectory().set( + new File(project.getBuildDir(), + "classes/" + mainSourceSet.getName() + "/" + sourceDirectorySet.getName()) + ); + sourcePaths.add(sourceDirectorySet.getSourceDirectories()); + new DslObject(mainSourceSet).getConvention().getPlugins().put(sourceDirectorySet.getName(), sourceDirectorySet); + mainSourceSet.getExtensions().add(SourceDirectorySet.class, sourceDirectorySet.getName(), sourceDirectorySet); + TaskProvider compileTask = + project.getTasks().register(JavaPlugin.COMPILE_JAVA_TASK_NAME + javaVersion.getMajorVersion(), JavaCompile.class, + (JavaCompile javaCompileTask) -> { + javaCompileTask.getOptions().getRelease().set(Integer.parseInt(javaVersion.getMajorVersion())); + javaCompileTask.setClasspath(compileClasspathConfiguration.plus( + compileOutputs.stream().reduce(project.getObjects().fileCollection(), FileCollection::plus))); + javaCompileTask.getOptions().getCompilerArgumentProviders().add( + new CompilerArgumentProvider( + project.provider(() -> Optional.of("jpms.module.name") + .filter(project::hasProperty) + .map(project::property) + .map(Object::toString) + .orElse(null)), + mrjpe.getPatchModules(), + mainSourceSet.getOutput() + ) + ); + javaCompileTask.source(sourceDirectorySet); + javaCompileTask.getDestinationDirectory().set(sourceDirectorySet.getDestinationDirectory()); + javaCompileTask.getOptions().setAnnotationProcessorPath(mainSourceSet.getAnnotationProcessorPath()); + javaCompileTask.getModularity().getInferModulePath().set(javaPluginExtension.getModularity().getInferModulePath()); + javaCompileTask.getOptions().setSourcepath(sourcePaths.stream().reduce(project.files(), FileCollection::plus)); + javaCompileTask.getJavaCompiler().set(compileJavaTask.getJavaCompiler()); + }); + compileOutputs.add(compileTask.get().getOutputs().getFiles()); + sourceDirectorySet.compiledBy(compileTask, AbstractCompile::getDestinationDirectory); + jarTask.from(compileTask.get().getDestinationDirectory(), copySpec -> { + copySpec.into("META-INF/versions/" + javaVersion.getMajorVersion()); + }); + }); + + SourceSet testSourceSet = ssc.getByName(SourceSet.TEST_SOURCE_SET_NAME); + testSourceSet.setCompileClasspath( + testSourceSet.getCompileClasspath().plus(compileOutputs.stream().reduce(project.files(), FileCollection::plus)) + ); + testSourceSet.setRuntimeClasspath( + testSourceSet.getRuntimeClasspath().plus(compileOutputs.stream().reduce(project.files(), FileCollection::plus)) + ); + Arrays.asList("apiElements", "runtimeElements").forEach((String name) -> { + Configuration conf = project.getConfigurations().getByName(name); + conf.attributes(attrs -> { + attrs.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, + compileJavaTask.getOptions().getRelease().get()); + }); + }); + } + } +} diff --git a/multi-release-jar/src/main/java/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPluginExtension.java b/multi-release-jar/src/main/java/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPluginExtension.java index 08d2132..e0adf4d 100644 --- a/multi-release-jar/src/main/java/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPluginExtension.java +++ b/multi-release-jar/src/main/java/net/woggioni/gradle/multi/release/jar/MultiReleaseJarPluginExtension.java @@ -1,33 +1,41 @@ package net.woggioni.gradle.multi.release.jar; +import org.gradle.api.Project; import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Provider; import javax.inject.Inject; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; -import java.util.Map; +import java.util.List; public class MultiReleaseJarPluginExtension { - - private final ObjectFactory objects; - private final Map> patchModules; - + private final Provider> listCtor; + private final MapProperty> patchModules; @Inject - public MultiReleaseJarPluginExtension(ObjectFactory objects) { - this.objects = objects; - patchModules = new HashMap<>(); + public MultiReleaseJarPluginExtension(Project project, ObjectFactory objects) { + patchModules = objects.mapProperty(String.class, + (Class>) ((List) new ArrayList()).getClass()); + listCtor = project.provider(ArrayList::new); + } + + private static List listAdd(List l, T el) { + l.add(el); + return l; } public void patchModule(String moduleName, Provider path) { - this.patchModules - .computeIfAbsent(moduleName, key -> objects.listProperty(String.class) - .convention(new ArrayList<>())) - .add(path); + Provider> listProvider = this.patchModules.getting(moduleName); + if(listProvider.isPresent()) { + patchModules.put(moduleName, listProvider.zip(path, MultiReleaseJarPluginExtension::listAdd)); + } else { + patchModules.put(moduleName, listCtor.zip(path, MultiReleaseJarPluginExtension::listAdd)); + } } - public Map> getPatchModules() { + public MapProperty> getPatchModules() { return patchModules; } }