This commit is contained in:
LordMZTE 2024-10-15 16:34:54 +02:00
commit bcd10b75d3
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
17 changed files with 1506 additions and 0 deletions

129
.clang-format Normal file
View file

@ -0,0 +1,129 @@
---
AccessModifierOffset: 0
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: None
AlignConsecutiveAssignments: None
AlignConsecutiveMacros: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: DontAlign
AlignOperands: DontAlign
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros: []
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: After
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakAfterJavaFieldAnnotations: true
#BreakArrays: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeConceptDeclarations: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
BreakStringLiterals: true
ColumnLimit: 90
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DeriveLineEnding: false
DerivePointerAlignment: false
DisableFormat: false # wtf
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: Always
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros: ["BOOST_FOREACH"]
IfMacros: []
IncludeBlocks: Regroup
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: Indent
IndentGotoLabels: true
IndentPPDirectives: BeforeHash
#IndentRequiresClause: false
IndentWidth: 4
IndentWrappedFunctionNames: false
#InsertBraces: false
InsertTrailingCommas: Wrapped
JavaScriptQuotes: Double
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: OuterScope
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
PackConstructorInitializers: NextLine
PointerAlignment: Left
QualifierAlignment: Left
ReferenceAlignment: Left
ReflowComments: true
#RemoveSemicolon: true
#RequiresClausePosition: OwnLine
#RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Always
SortIncludes: false
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: After
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatementsExceptControlMacros
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesInAngles: Never
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
Minimum: 0
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++20
StatementAttributeLikeMacros: []
StatementMacros: []
TabWidth: 4
TypenameMacros: []
UseCRLF: false # wtf
UseTab: Never
WhitespaceSensitiveMacros: ["BOOST_PP_STRINGSIZE"]

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
.gradle
.idea
build
gradle.properties
bin
.settings
.classpath
.project

54
build.gradle Normal file
View file

@ -0,0 +1,54 @@
plugins {
id "java"
id "groovy"
id "java-gradle-plugin"
id "com.gradle.plugin-publish" version "0.10.0"
id "maven-publish"
}
group "net.anvilcraft"
version "0.1.0"
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile group: "junit", name: "junit", version: "4.12"
implementation gradleApi()
implementation "org.ow2.asm:asm:9.7.1"
implementation "org.ow2.asm:asm-commons:9.7.1"
implementation "commons-io:commons-io:2.7"
}
sourceSets {
main {
java {
srcDirs = []
}
groovy {
srcDirs = ["src/main/java" ]
}
}
}
pluginBundle {
website = "https://git.tilera.org/Anvilcraft/gradlehaxe"
vcsUrl = "https://git.tilera.org/Anvilcraft/gradlehaxe"
tags = ["haxe"]
}
gradlePlugin {
plugins {
gradlehaxe {
id = "net.anvilcraft.gradlehaxe"
displayName = "GradleHaxe"
description = "Haxe compatiblity for Gradle"
implementationClass = "net.anvilcraft.gradlehaxe.BuilderPlugin"
}
}
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
gradlew vendored Executable file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

2
settings.gradle Normal file
View file

@ -0,0 +1,2 @@
rootProject.name = 'modpackbuilder'

View file

@ -0,0 +1,56 @@
package net.anvilcraft.gradlehaxe;
import javax.inject.Inject;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.internal.JvmPluginsHelper;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.TaskProvider;
import net.anvilcraft.gradlehaxe.compile.CompileHaxeTask;
public class BuilderPlugin implements Plugin<Project> {
private final ObjectFactory objects;
@Inject
public BuilderPlugin(ObjectFactory objects) {
this.objects = objects;
}
@Override
public void apply(Project proj) {
proj.getPluginManager().apply(JavaBasePlugin.class);
proj.getExtensions().getByType(SourceSetContainer.class).all(ss -> {
SourceDirectorySet haxeSrc
= this.objects.sourceDirectorySet("haxe", ss.getName() + " Haxe Source.");
haxeSrc.srcDir("src/" + ss.getName() + "/haxe");
ss.getExtensions().add("haxe", haxeSrc);
TaskProvider<CompileHaxeTask> taskProv = proj.getTasks().register(
ss.getCompileTaskName("haxe"),
CompileHaxeTask.class,
task -> {
task.setSource(haxeSrc);
task.getSS().set(ss);
task.getConventionMapping().map("classpath", ss::getCompileClasspath);
}
);
JvmPluginsHelper.configureOutputDirectoryForSourceSet(
ss, haxeSrc, proj, taskProv, taskProv.map(CompileHaxeTask::getOptions)
);
proj.getTasks().named(ss.getClassesTaskName(), t -> t.dependsOn(taskProv));
proj.getTasks()
.register("generateHXML_" + ss.getName(), GenerateHXMLTask.class);
ss.getExtensions().create("haxeopts", HaxeSourceSetExtension.class);
});
}
}

View file

@ -0,0 +1,28 @@
package net.anvilcraft.gradlehaxe;
import java.io.FileWriter;
import java.io.IOException;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.TaskAction;
public abstract class GenerateHXMLTask extends DefaultTask {
@TaskAction
public void action() {
String ssName = this.getName().replace("generateHXML_", "");
SourceSet ss = this.getProject()
.getExtensions()
.getByType(SourceSetContainer.class)
.getByName(ssName);
HaxeArguments args = new HaxeArguments(ss, this.getProject(), this.getTemporaryDir() + "/build.jar");
try (FileWriter fw = new FileWriter("build.hxml")) {
args.writeHXML(fw);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,93 @@
package net.anvilcraft.gradlehaxe;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSet;
import org.gradle.jvm.toolchain.JavaLanguageVersion;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.jvm.toolchain.JavaToolchainService;
public class HaxeArguments {
public String outpath;
public String classpath;
public ArrayList<String> includedPackages = new ArrayList<>();
public ArrayList<String> libs = new ArrayList<>();
public HaxeArguments(SourceSet ss, Project proj, String outpath) {
this.classpath = "src/" + ss.getName() + "/haxe";
this.outpath = outpath;
this.includedPackages.addAll(
ss.getExtensions().getByType(HaxeSourceSetExtension.class).includedPackages
);
ss.getCompileClasspath()
.getFiles()
.stream()
.filter(f -> f.getPath().endsWith(".jar"))
.map(File::getPath)
.forEach(this.libs::add);
;
JavaPluginExtension java
= proj.getExtensions().getByType(JavaPluginExtension.class);
if (java.getTargetCompatibility() == JavaVersion.VERSION_1_8) {
JavaToolchainService javaToolchain
= proj.getExtensions().getByType(JavaToolchainService.class);
Provider<JavaLauncher> launcher = javaToolchain.launcherFor(
spec -> spec.getLanguageVersion().set(JavaLanguageVersion.of(8))
);
this.libs.add(
launcher.get().getMetadata().getInstallationPath() + "/jre/lib/jce.jar"
);
}
}
public void writeHXML(Writer w) throws IOException {
w.write("-cp " + this.classpath + "\n");
for (String p : this.includedPackages) {
w.write("--macro \"include('" + p + "')\"\n");
}
w.write("--jvm " + this.outpath + "\n");
w.write("\n");
for (String lib : this.libs) {
w.write("--java-lib-extern " + lib + "\n");
}
}
public String[] toCommand() {
String[] ret
= new String[this.includedPackages.size() * 2 + this.libs.size() * 2 + 5];
int i = 0;
ret[i++] = "haxe";
ret[i++] = "-cp";
ret[i++] = this.classpath;
ret[i++] = "--jvm";
ret[i++] = this.outpath;
for (String p : this.includedPackages) {
ret[i++] = "--macro";
ret[i++] = "include('" + p + "')";
}
for (String lib : this.libs) {
ret[i++] = "--java-lib-extern";
ret[i++] = lib;
}
assert i == ret.length;
return ret;
}
}

View file

@ -0,0 +1,17 @@
package net.anvilcraft.gradlehaxe;
import java.util.HashSet;
import java.util.Set;
public class HaxeSourceSetExtension {
public final Set<String> includedPackages = new HashSet<>();
public String remappedHaxePackageName = null;
public void includePackage(String pkg) {
this.includedPackages.add(pkg);
}
public void remapHaxePackage(String name) {
this.remappedHaxePackageName = name;
}
}

View file

@ -0,0 +1,89 @@
package net.anvilcraft.gradlehaxe.compile;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.jar.JarFile;
import org.apache.commons.io.FileUtils;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.compile.AbstractCompile;
import org.gradle.api.tasks.compile.CompileOptions;
import org.gradle.tooling.BuildException;
import net.anvilcraft.gradlehaxe.HaxeArguments;
import net.anvilcraft.gradlehaxe.HaxeSourceSetExtension;
import net.anvilcraft.gradlehaxe.util.JarRelocatorTask;
import net.anvilcraft.gradlehaxe.util.RelocatingRemapper;
import net.anvilcraft.gradlehaxe.util.Relocation;
public abstract class CompileHaxeTask extends AbstractCompile {
public CompileOptions options;
public CompileHaxeTask() {
this.options = this.getProject().getObjects().newInstance(
CompileOptions.class, new Object[0]
);
}
@TaskAction
public void action() {
String buildJar = this.getTemporaryDir() + "/build.jar";
HaxeArguments args
= new HaxeArguments(this.getSS().get(), this.getProject(), buildJar);
try {
Process proc = Runtime.getRuntime().exec(args.toCommand());
if (proc.waitFor() != 0) {
StringBuilder sb = new StringBuilder();
sb.append("Haxe failed.\nstdout:\n");
copyToSB(proc.getInputStream(), sb);
sb.append("\nstderr:\n");
copyToSB(proc.getErrorStream(), sb);
throw new BuildException(sb.toString(), null);
}
Path dest = this.getDestinationDirectory().getAsFile().get().toPath();
// Delete, then re-create destination directory to remove potential excess classes.
FileUtils.cleanDirectory(dest.toFile());
String relocatedHaxePkg = this.getSS()
.get()
.getExtensions()
.getByType(HaxeSourceSetExtension.class)
.remappedHaxePackageName;
if (relocatedHaxePkg != null) {
ArrayList<Relocation> relocations = new ArrayList<Relocation>();
relocations.add(new Relocation("haxe", relocatedHaxePkg));
new JarRelocatorTask(
new RelocatingRemapper(relocations),
dest,
new JarFile(buildJar)
).processEntries();
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
public abstract Property<SourceSet> getSS();
public CompileOptions getOptions() {
return this.options;
}
private void copyToSB(InputStream is, StringBuilder sb) throws IOException {
byte[] buf = new byte[4096];
int read;
while ((read = is.read(buf)) > 0) {
sb.append(new String(buf, 0, read));
}
}
}

View file

@ -0,0 +1,163 @@
/*
* Copyright Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.anvilcraft.gradlehaxe.util;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
/**
* A task that copies {@link JarEntry jar entries} from a {@link JarFile jar input} to a
* {@link JarOutputStream jar output}, applying the relocations defined by a
* {@link RelocatingRemapper}.
*/
public class JarRelocatorTask {
/**
* META-INF/*.SF
* META-INF/*.DSA
* META-INF/*.RSA
* META-INF/SIG-*
*
* <a
* href="https://docs.oracle.com/en/java/javase/11/docs/specs/jar/jar.html#signed-jar-file">Specification</a>
*/
private static final Pattern SIGNATURE_FILE_PATTERN
= Pattern.compile("META-INF/(?:[^/]+\\.(?:DSA|RSA|SF)|SIG-[^/]+)");
private final RelocatingRemapper remapper;
private final Path dirOut;
private final JarFile jarIn;
private final Set<String> resources = new HashSet<>();
public JarRelocatorTask(RelocatingRemapper remapper, Path dirOut, JarFile jarIn) {
this.remapper = remapper;
this.dirOut = dirOut;
this.jarIn = jarIn;
}
public void processEntries() throws IOException {
for (Enumeration<JarEntry> entries = this.jarIn.entries();
entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
// The 'INDEX.LIST' file is an optional file, containing information about the
// packages defined in a jar. Instead of relocating the entries in it, we
// delete it, since it is optional anyway.
//
// We don't process directory entries, and instead opt to recreate them when
// adding classes/resources.
String name = entry.getName();
if (name.equals("META-INF/INDEX.LIST") || name.equals("META-INF/MANIFEST.MF")
|| entry.isDirectory()) {
continue;
}
// Signatures will become invalid after remapping, so we delete them to avoid
// making the output useless
if (SIGNATURE_FILE_PATTERN.matcher(name).matches()) {
continue;
}
try (InputStream entryIn = this.jarIn.getInputStream(entry)) {
processEntry(entry, entryIn);
}
}
}
private void processEntry(JarEntry entry, InputStream entryIn) throws IOException {
String name = entry.getName();
String mappedName = this.remapper.map(name);
// ensure the parent directory structure exists for the entry.
processDirectory(mappedName, true);
if (name.endsWith(".class")) {
processClass(name, entryIn);
} else if (!this.resources.contains(mappedName)) {
processResource(mappedName, entryIn, entry.getTime());
}
}
private void processDirectory(String name, boolean parentsOnly) throws IOException {
int index = name.lastIndexOf('/');
if (index != -1) {
String parentDirectory = name.substring(0, index);
if (!this.resources.contains(parentDirectory)) {
processDirectory(parentDirectory, false);
}
}
if (parentsOnly) {
return;
}
this.dirOut.resolve(name).toFile().mkdirs();
this.resources.add(name);
}
private void processResource(String name, InputStream entryIn, long lastModified)
throws IOException {
try (
FileOutputStream fos
= new FileOutputStream(this.dirOut.resolve(name).toFile())
) {
IOUtils.copy(entryIn, fos);
}
this.resources.add(name);
}
private void processClass(String name, InputStream entryIn) throws IOException {
ClassReader classReader = new ClassReader(entryIn);
ClassWriter classWriter = new ClassWriter(0);
RelocatingClassVisitor classVisitor
= new RelocatingClassVisitor(classWriter, this.remapper, name);
try {
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
} catch (Throwable e) {
throw new RuntimeException("Error processing class " + name, e);
}
byte[] renamedClass = classWriter.toByteArray();
// Need to take the .class off for remapping evaluation
String mappedName = this.remapper.map(name.substring(0, name.indexOf('.')));
// Now we put it back on so the class file is written out with the right
// extension.
try (
FileOutputStream fos
= new FileOutputStream(this.dirOut.resolve(mappedName + ".class").toFile())
) {
fos.write(renamedClass);
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.anvilcraft.gradlehaxe.util;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper;
/**
* A {@link ClassVisitor} that relocates types and names with a {@link
* RelocatingRemapper}.
*/
final class RelocatingClassVisitor extends ClassRemapper {
private final String packageName;
RelocatingClassVisitor(ClassWriter writer, RelocatingRemapper remapper, String name) {
super(Opcodes.ASM9, writer, remapper);
this.packageName = name.substring(0, name.lastIndexOf('/') + 1);
}
@Override
public void visitSource(String source, String debug) {
if (source == null) {
super.visitSource(null, debug);
return;
}
// visit source file name
String name = this.packageName + source;
String mappedName = super.remapper.map(name);
String mappedFileName = mappedName.substring(mappedName.lastIndexOf('/') + 1);
super.visitSource(mappedFileName, debug);
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.anvilcraft.gradlehaxe.util;
import org.objectweb.asm.commons.Remapper;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Remaps class names and types using defined {@link Relocation} rules.
*/
public class RelocatingRemapper extends Remapper {
private static final Pattern CLASS_PATTERN = Pattern.compile("(\\[*)?L(.+);");
// https://docs.oracle.com/javase/10/docs/specs/jar/jar.html#multi-release-jar-files
private static final Pattern VERSION_PATTERN
= Pattern.compile("^(META-INF/versions/\\d+/)(.*)$");
private final Collection<Relocation> rules;
public RelocatingRemapper(Collection<Relocation> rules) {
this.rules = rules;
}
public Collection<Relocation> getRules() {
return this.rules;
}
@Override
public String map(String name) {
String relocatedName = relocate(name, false);
if (relocatedName != null) {
return relocatedName;
}
return super.map(name);
}
@Override
public Object mapValue(Object object) {
if (object instanceof String) {
String relocatedName = relocate((String) object, true);
if (relocatedName != null) {
return relocatedName;
}
}
return super.mapValue(object);
}
private String relocate(String name, boolean isStringValue) {
String prefix = "";
String suffix = "";
if (isStringValue) {
Matcher m = CLASS_PATTERN.matcher(name);
if (m.matches()) {
prefix = m.group(1) + "L";
name = m.group(2);
suffix = ";";
}
}
Matcher m = VERSION_PATTERN.matcher(name);
if (m.matches()) {
prefix = m.group(1);
name = m.group(2);
}
for (Relocation r : this.rules) {
if (isStringValue && r.canRelocateClass(name)) {
return prefix + r.relocateClass(name) + suffix;
} else if (r.canRelocatePath(name)) {
return prefix + r.relocatePath(name) + suffix;
}
}
return null;
}
}

View file

@ -0,0 +1,151 @@
/*
* Copyright Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.anvilcraft.gradlehaxe.util;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* A relocation rule
*/
public class Relocation {
private final String pattern;
private final String relocatedPattern;
private final String pathPattern;
private final String relocatedPathPattern;
private final Set<String> includes;
private final Set<String> excludes;
/**
* Creates a new relocation
*
* @param pattern the pattern to match
* @param relocatedPattern the pattern to relocate to
* @param includes a collection of patterns which this rule should specifically
* include
* @param excludes a collection of patterns which this rule should specifically
* exclude
*/
public Relocation(
String pattern,
String relocatedPattern,
Collection<String> includes,
Collection<String> excludes
) {
this.pattern = pattern.replace('/', '.');
this.pathPattern = pattern.replace('.', '/');
this.relocatedPattern = relocatedPattern.replace('/', '.');
this.relocatedPathPattern = relocatedPattern.replace('.', '/');
if (includes != null && !includes.isEmpty()) {
this.includes = normalizePatterns(includes);
this.includes.addAll(includes);
} else {
this.includes = null;
}
if (excludes != null && !excludes.isEmpty()) {
this.excludes = normalizePatterns(excludes);
this.excludes.addAll(excludes);
} else {
this.excludes = null;
}
}
/**
* Creates a new relocation with no specific includes or excludes
*
* @param pattern the pattern to match
* @param relocatedPattern the pattern to relocate to
*/
public Relocation(String pattern, String relocatedPattern) {
this(
pattern,
relocatedPattern,
Collections.<String>emptyList(),
Collections.<String>emptyList()
);
}
private boolean isIncluded(String path) {
if (this.includes == null) {
return true;
}
for (String include : this.includes) {
if (SelectorUtils.matchPath(include, path, true)) {
return true;
}
}
return false;
}
private boolean isExcluded(String path) {
if (this.excludes == null) {
return false;
}
for (String exclude : this.excludes) {
if (SelectorUtils.matchPath(exclude, path, true)) {
return true;
}
}
return false;
}
boolean canRelocatePath(String path) {
if (path.endsWith(".class")) {
path = path.substring(0, path.length() - 6);
}
if (!isIncluded(path) || isExcluded(path)) {
return false;
}
return path.startsWith(this.pathPattern)
|| path.startsWith("/" + this.pathPattern);
}
boolean canRelocateClass(String clazz) {
return clazz.indexOf('/') == -1 && canRelocatePath(clazz.replace('.', '/'));
}
String relocatePath(String path) {
return path.replaceFirst(this.pathPattern, this.relocatedPathPattern);
}
String relocateClass(String clazz) {
return clazz.replaceFirst(this.pattern, this.relocatedPattern);
}
private static Set<String> normalizePatterns(Collection<String> patterns) {
Set<String> normalized = new LinkedHashSet<>();
for (String pattern : patterns) {
String classPattern = pattern.replace('.', '/');
normalized.add(classPattern);
if (classPattern.endsWith("/*")) {
String packagePattern
= classPattern.substring(0, classPattern.lastIndexOf('/'));
normalized.add(packagePattern);
}
}
return normalized;
}
}

View file

@ -0,0 +1,396 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.codehaus.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "Ant" and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact codehaus@codehaus.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.codehaus.org/>.
*/
package net.anvilcraft.gradlehaxe.util;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/**
* This is a stripped down version of org.codehaus.plexus.util.SelectorUtils for
* use in {@link Relocation}.
*
* @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
* @author Magesh Umasankar
* @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
*/
final class SelectorUtils {
private static final String PATTERN_HANDLER_PREFIX = "[";
private static final String PATTERN_HANDLER_SUFFIX = "]";
private static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX;
private static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX;
private static boolean isAntPrefixedPattern(String pattern) {
return pattern.length()
> (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
&& pattern.startsWith(ANT_HANDLER_PREFIX)
&& pattern.endsWith(PATTERN_HANDLER_SUFFIX);
}
// When str starts with a File.separator, pattern has to start with a File.separator.
// When pattern starts with a File.separator, str has to start with a File.separator.
private static boolean
separatorPatternStartSlashMismatch(String pattern, String str, String separator) {
return str.startsWith(separator) != pattern.startsWith(separator);
}
public static boolean matchPath(String pattern, String str, boolean isCaseSensitive) {
return matchPath(pattern, str, File.separator, isCaseSensitive);
}
private static boolean
matchPath(String pattern, String str, String separator, boolean isCaseSensitive) {
if (isRegexPrefixedPattern(pattern)) {
pattern = pattern.substring(
REGEX_HANDLER_PREFIX.length(),
pattern.length() - PATTERN_HANDLER_SUFFIX.length()
);
return str.matches(pattern);
} else {
if (isAntPrefixedPattern(pattern)) {
pattern = pattern.substring(
ANT_HANDLER_PREFIX.length(),
pattern.length() - PATTERN_HANDLER_SUFFIX.length()
);
}
return matchAntPathPattern(pattern, str, separator, isCaseSensitive);
}
}
private static boolean isRegexPrefixedPattern(String pattern) {
return pattern.length()
> (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
&& pattern.startsWith(REGEX_HANDLER_PREFIX)
&& pattern.endsWith(PATTERN_HANDLER_SUFFIX);
}
private static boolean matchAntPathPattern(
String pattern, String str, String separator, boolean isCaseSensitive
) {
if (separatorPatternStartSlashMismatch(pattern, str, separator)) {
return false;
}
String[] patDirs = tokenizePathToString(pattern, separator);
String[] strDirs = tokenizePathToString(str, separator);
return matchAntPathPattern(patDirs, strDirs, isCaseSensitive);
}
private static boolean
matchAntPathPattern(String[] patDirs, String[] strDirs, boolean isCaseSensitive) {
int patIdxStart = 0;
int patIdxEnd = patDirs.length - 1;
int strIdxStart = 0;
int strIdxEnd = strDirs.length - 1;
// up to first '**'
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
String patDir = patDirs[patIdxStart];
if (patDir.equals("**")) {
break;
}
if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
return false;
}
patIdxStart++;
strIdxStart++;
}
if (strIdxStart > strIdxEnd) {
// String is exhausted
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (!patDirs[i].equals("**")) {
return false;
}
}
return true;
} else {
if (patIdxStart > patIdxEnd) {
// String not exhausted, but pattern is. Failure.
return false;
}
}
// up to last '**'
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
String patDir = patDirs[patIdxEnd];
if (patDir.equals("**")) {
break;
}
if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
return false;
}
patIdxEnd--;
strIdxEnd--;
}
if (strIdxStart > strIdxEnd) {
// String is exhausted
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (!patDirs[i].equals("**")) {
return false;
}
}
return true;
}
while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
int patIdxTmp = -1;
for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
if (patDirs[i].equals("**")) {
patIdxTmp = i;
break;
}
}
if (patIdxTmp == patIdxStart + 1) {
// '**/**' situation, so skip one
patIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
int patLength = (patIdxTmp - patIdxStart - 1);
int strLength = (strIdxEnd - strIdxStart + 1);
int foundIdx = -1;
strLoop:
for (int i = 0; i <= strLength - patLength; i++) {
for (int j = 0; j < patLength; j++) {
String subPat = patDirs[patIdxStart + j + 1];
String subStr = strDirs[strIdxStart + i + j];
if (!match(subPat, subStr, isCaseSensitive)) {
continue strLoop;
}
}
foundIdx = strIdxStart + i;
break;
}
if (foundIdx == -1) {
return false;
}
patIdxStart = patIdxTmp;
strIdxStart = foundIdx + patLength;
}
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (!patDirs[i].equals("**")) {
return false;
}
}
return true;
}
private static boolean match(String pattern, String str, boolean isCaseSensitive) {
char[] patArr = pattern.toCharArray();
char[] strArr = str.toCharArray();
return match(patArr, strArr, isCaseSensitive);
}
private static boolean match(char[] patArr, char[] strArr, boolean isCaseSensitive) {
int patIdxStart = 0;
int patIdxEnd = patArr.length - 1;
int strIdxStart = 0;
int strIdxEnd = strArr.length - 1;
char ch;
boolean containsStar = false;
for (char aPatArr : patArr) {
if (aPatArr == '*') {
containsStar = true;
break;
}
}
if (!containsStar) {
// No '*'s, so we make a shortcut
if (patIdxEnd != strIdxEnd) {
return false; // Pattern and string do not have the same size
}
for (int i = 0; i <= patIdxEnd; i++) {
ch = patArr[i];
if (ch != '?' && !equals(ch, strArr[i], isCaseSensitive)) {
return false; // Character mismatch
}
}
return true; // String matches against pattern
}
if (patIdxEnd == 0) {
return true; // Pattern contains only '*', which matches anything
}
// Process characters before first star
while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
if (ch != '?' && !equals(ch, strArr[strIdxStart], isCaseSensitive)) {
return false; // Character mismatch
}
patIdxStart++;
strIdxStart++;
}
if (strIdxStart > strIdxEnd) {
// All characters in the string are used. Check if only '*'s are
// left in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
// Process characters after last star
while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
if (ch != '?' && !equals(ch, strArr[strIdxEnd], isCaseSensitive)) {
return false; // Character mismatch
}
patIdxEnd--;
strIdxEnd--;
}
if (strIdxStart > strIdxEnd) {
// All characters in the string are used. Check if only '*'s are
// left in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
// process pattern between stars. padIdxStart and patIdxEnd point
// always to a '*'.
while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
int patIdxTmp = -1;
for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
if (patArr[i] == '*') {
patIdxTmp = i;
break;
}
}
if (patIdxTmp == patIdxStart + 1) {
// Two stars next to each other, skip the first one.
patIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
int patLength = (patIdxTmp - patIdxStart - 1);
int strLength = (strIdxEnd - strIdxStart + 1);
int foundIdx = -1;
strLoop:
for (int i = 0; i <= strLength - patLength; i++) {
for (int j = 0; j < patLength; j++) {
ch = patArr[patIdxStart + j + 1];
if (ch != '?'
&& !equals(ch, strArr[strIdxStart + i + j], isCaseSensitive)) {
continue strLoop;
}
}
foundIdx = strIdxStart + i;
break;
}
if (foundIdx == -1) {
return false;
}
patIdxStart = patIdxTmp;
strIdxStart = foundIdx + patLength;
}
// All characters in the string are used. Check if only '*'s are left
// in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
/**
* Tests whether two characters are equal.
*/
private static boolean equals(char c1, char c2, boolean isCaseSensitive) {
if (c1 == c2) {
return true;
}
if (!isCaseSensitive) {
// NOTE: Try both upper case and lower case as done by
// String.equalsIgnoreCase()
if (Character.toUpperCase(c1) == Character.toUpperCase(c2)
|| Character.toLowerCase(c1) == Character.toLowerCase(c2)) {
return true;
}
}
return false;
}
private static String[] tokenizePathToString(String path, String separator) {
List<String> ret = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(path, separator);
while (st.hasMoreTokens()) {
ret.add(st.nextToken());
}
return ret.toArray(new String[ret.size()]);
}
/**
* Private Constructor
*/
private SelectorUtils() {}
}