Add plugin for creating MRJARs (#104883)
This commit adds an elasticsearch gradle plugin which sets up a project to build an MRJAR. By applying the plugin, the src dir is checked for any directories matching mainXX where XX is the java version number. A source set is automatically setup, an appropriate compiler tied to that source set, and it's output placed in the correct part of the final jar. Additionally, the sourceset allows use of preview features in that verison of Java, and the preview bits are stripped from the resulting class files.
This commit is contained in:
parent
9c086de30e
commit
d5e727e362
|
@ -139,6 +139,10 @@ gradlePlugin {
|
|||
id = 'elasticsearch.java-module'
|
||||
implementationClass = 'org.elasticsearch.gradle.internal.ElasticsearchJavaModulePathPlugin'
|
||||
}
|
||||
mrjar {
|
||||
id = 'elasticsearch.mrjar'
|
||||
implementationClass = 'org.elasticsearch.gradle.internal.MrjarPlugin'
|
||||
}
|
||||
releaseTools {
|
||||
id = 'elasticsearch.release-tools'
|
||||
implementationClass = 'org.elasticsearch.gradle.internal.release.ReleaseToolsPlugin'
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal;
|
||||
|
||||
import org.elasticsearch.gradle.util.GradleUtils;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaLibraryPlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.compile.CompileOptions;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.jvm.tasks.Jar;
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion;
|
||||
import org.gradle.jvm.toolchain.JavaToolchainService;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.V_PREVIEW;
|
||||
|
||||
public class MrjarPlugin implements Plugin<Project> {
|
||||
|
||||
private static final Pattern MRJAR_SOURCESET_PATTERN = Pattern.compile("main(\\d{2})");
|
||||
|
||||
private final JavaToolchainService javaToolchains;
|
||||
|
||||
@Inject
|
||||
MrjarPlugin(JavaToolchainService javaToolchains) {
|
||||
this.javaToolchains = javaToolchains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPluginManager().apply(JavaLibraryPlugin.class);
|
||||
var javaExtension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
|
||||
var srcDir = project.getProjectDir().toPath().resolve("src");
|
||||
try (var subdirStream = Files.list(srcDir)) {
|
||||
for (Path sourceset : subdirStream.toList()) {
|
||||
assert Files.isDirectory(sourceset);
|
||||
String sourcesetName = sourceset.getFileName().toString();
|
||||
Matcher sourcesetMatcher = MRJAR_SOURCESET_PATTERN.matcher(sourcesetName);
|
||||
if (sourcesetMatcher.matches()) {
|
||||
int javaVersion = Integer.parseInt(sourcesetMatcher.group(1));
|
||||
addMrjarSourceset(project, javaExtension, sourcesetName, javaVersion);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMrjarSourceset(Project project, JavaPluginExtension javaExtension, String sourcesetName, int javaVersion) {
|
||||
SourceSet sourceSet = javaExtension.getSourceSets().maybeCreate(sourcesetName);
|
||||
GradleUtils.extendSourceSet(project, SourceSet.MAIN_SOURCE_SET_NAME, sourcesetName);
|
||||
|
||||
project.getTasks().withType(Jar.class).named(JavaPlugin.JAR_TASK_NAME).configure(jarTask -> {
|
||||
jarTask.into("META-INF/versions/" + javaVersion, copySpec -> copySpec.from(sourceSet.getOutput()));
|
||||
jarTask.manifest(manifest -> { manifest.attributes(Map.of("Multi-Release", "true")); });
|
||||
});
|
||||
|
||||
project.getTasks().withType(JavaCompile.class).named(sourceSet.getCompileJavaTaskName()).configure(compileTask -> {
|
||||
compileTask.getJavaCompiler()
|
||||
.set(javaToolchains.compilerFor(spec -> { spec.getLanguageVersion().set(JavaLanguageVersion.of(javaVersion)); }));
|
||||
compileTask.setSourceCompatibility(Integer.toString(javaVersion));
|
||||
CompileOptions compileOptions = compileTask.getOptions();
|
||||
compileOptions.getRelease().set(javaVersion);
|
||||
compileOptions.getCompilerArgs().add("--enable-preview");
|
||||
compileOptions.getCompilerArgs().add("-Xlint:-preview");
|
||||
|
||||
compileTask.doLast(t -> { stripPreviewFromFiles(compileTask.getDestinationDirectory().getAsFile().get().toPath()); });
|
||||
});
|
||||
}
|
||||
|
||||
private static void stripPreviewFromFiles(Path compileDir) {
|
||||
try (Stream<Path> fileStream = Files.walk(compileDir)) {
|
||||
fileStream.filter(p -> p.toString().endsWith(".class")).forEach(MrjarPlugin::maybeStripPreview);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void maybeStripPreview(Path file) {
|
||||
ClassWriter classWriter = null;
|
||||
try (var in = Files.newInputStream(file)) {
|
||||
ClassReader classReader = new ClassReader(in);
|
||||
ClassNode classNode = new ClassNode();
|
||||
classReader.accept(classNode, 0);
|
||||
|
||||
if ((classNode.version & V_PREVIEW) != 0) {
|
||||
classNode.version = classNode.version & ~V_PREVIEW;
|
||||
classWriter = new ClassWriter(0);
|
||||
classNode.accept(classWriter);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
|
||||
if (classWriter != null) {
|
||||
try (var out = Files.newOutputStream(file)) {
|
||||
out.write(classWriter.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new org.gradle.api.UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
[versions]
|
||||
asm = "9.4"
|
||||
asm = "9.6"
|
||||
jackson = "2.15.0"
|
||||
junit5 = "5.8.1"
|
||||
spock = "2.1-groovy-3.0"
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
apply plugin: 'elasticsearch.publish'
|
||||
apply plugin: 'elasticsearch.mrjar'
|
||||
|
||||
dependencies {
|
||||
// This dependency is used only by :libs:core for null-checking interop with other tools
|
||||
|
|
Loading…
Reference in New Issue