Clean up code

This commit is contained in:
s809
2025-01-26 17:52:30 +05:00
parent 4a72e02550
commit 23a1f0b025
3 changed files with 153 additions and 119 deletions
+79 -49
View File
@@ -105,6 +105,76 @@ forgix {
} }
class NativeTransformer implements Transformer {
private boolean enabled = false
private final HashMap<String, String> replacements = new HashMap()
private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
private var nativeRelocator
NativeTransformer() {
try {
int exitCode = Runtime.getRuntime().exec(new String[]{"python", "--version"}).waitFor();
if (exitCode == 0) {
enabled = true
}
} catch (IOException e) {
println(e)
}
}
void relocateNative(String target, String replacement) {
if (replacement.length() > target.length()) {
throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}")
}
replacements.put(target, replacement)
}
@Override
boolean canTransformResource(@Nonnull FileTreeElement element) {
return replacements.keySet().stream().anyMatch {
element.name.startsWith(it as String)
}
}
@Override
void transform(@Nonnull TransformerContext context) {
println("Transforming $context.path...")
byte[] content = context.is.readAllBytes()
if (nativeRelocator == null) {
nativeRelocator = new NativeRelocator()
}
try {
Map.Entry<String, String> pathReplacement = replacements.entrySet().stream().filter {
context.path.startsWith(it.key as String)
}.findFirst().orElseThrow()
String path = context.path.replace(pathReplacement.key as String, pathReplacement.value as String)
content = nativeRelocator.processBinary(path, content, replacements)
rewrittenFiles.put(path, content)
}
catch (Throwable e) {
throw new GradleException("Failed to relocate", e)
}
}
@Override
boolean hasTransformedResource() { return !rewrittenFiles.isEmpty() }
@Override
void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {
for (Map.Entry<String, byte[]> rewrittenFile : rewrittenFiles.entrySet()) {
os.putNextEntry(new ZipEntry(rewrittenFile.key))
os.write(rewrittenFile.value)
}
}
}
subprojects { p -> subprojects { p ->
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")" // Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
// Useful later on so we dont have duplicated code // Useful later on so we dont have duplicated code
@@ -317,57 +387,17 @@ subprojects { p ->
relocate "org.slf4j", "${librariesLocation}.slf4j" relocate "org.slf4j", "${librariesLocation}.slf4j"
// Sqlite Database // Sqlite Database
relocate("org.xerial", "dh_sqlite.org.xerial") // librariesLocation isn't used because it's too long for replacing paths in native libraries
relocate("org.sqlite", "dh_sqlite") { relocate "org.xerial", "dh_sqlite.org.xerial"
exclude("org/sqlite/native/**") relocate "org.sqlite", "dh_sqlite", {
exclude "org/sqlite/native/**"
} }
relocate("jdbc:sqlite", "jdbc:dh_sqlite") relocate "jdbc:sqlite", "jdbc:dh_sqlite"
// Should be pretty much enough for Linux & Windows transform(NativeTransformer) {
// Probably won't work for Mac, because it requires signing native libs relocateNative "org/sqlite", "dh_sqlite"
transform(new Transformer() { relocateNative "org_sqlite", "dh_1sqlite"
final String name = "RelocateSqliteNatives" }
private HashMap<String, byte[]> rewrittenFiles = new HashMap()
@Override
boolean canTransformResource(@Nonnull FileTreeElement element) {
System.out.println("canTransformResource " + element.name)
return element.name.contains("sqlitejdbc")
}
@Override
void transform(@Nonnull TransformerContext context) {
System.out.println("transform " + context.path)
String path = context.path.replace("org/sqlite", "dh_sqlite")
byte[] content = context.is.readAllBytes()
try {
content = RelocateNatives.processNative(path, content)
}
catch (Throwable e) {
throw new GradleException("Failed to relocate", e)
}
rewrittenFiles.put(path, content)
}
@Override
boolean hasTransformedResource() {
System.out.println("hasTransformedResource")
return true
}
@Override
void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {
System.out.println("modifyOutputStream")
for (Map.Entry<String, byte[]> rewrittenFile : rewrittenFiles.entrySet()) {
os.putNextEntry(new ZipEntry(rewrittenFile.key))
os.write(rewrittenFile.value)
}
}
})
// JOML // JOML
@@ -1,52 +1,21 @@
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
class RelocateNatives class NativeRelocator
{ {
private static boolean prepared = false;
private static final Path rootDirectory = Path.of(System.getProperty("user.dir"), "relocate_natives"); private static final Path rootDirectory = Path.of(System.getProperty("user.dir"), "relocate_natives");
private static final Path cacheRoot = rootDirectory.resolve("cache"); private static final Path cacheRoot = rootDirectory.resolve("cache");
@SuppressWarnings({"ResultOfMethodCallIgnored", "BusyWait"}) NativeRelocator() throws Exception
private static CompletableFuture<Void> readOutputStreams(Process process)
{ {
return CompletableFuture.runAsync(() -> { if (rootDirectory.resolve(".venv").toFile().exists())
try
{
while (process.isAlive() || process.getInputStream().available() > 0 || process.getErrorStream().available() > 0)
{
if (process.getInputStream().available() > 0)
{
byte[] data = new byte[process.getInputStream().available()];
process.getInputStream().read(data);
System.out.write(data);
}
if (process.getErrorStream().available() > 0)
{
byte[] data = new byte[process.getErrorStream().available()];
process.getErrorStream().read(data);
System.err.write(data);
}
Thread.sleep(100);
}
}
catch (Exception ignored)
{
}
});
}
private static void ensurePrepared() throws Exception
{
if (prepared)
{ {
return; return;
} }
prepared = true;
ProcessBuilder processBuilder = new ProcessBuilder(); ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.directory(rootDirectory.toFile()); processBuilder.directory(rootDirectory.toFile());
@@ -77,39 +46,40 @@ class RelocateNatives
} }
} }
public static byte[] updateUsingLief(Path outputFilePath, byte[] content) throws Exception
private static CompletableFuture<Void> readOutputStreams(Process process)
{ {
ensurePrepared(); return CompletableFuture.runAsync(() -> {
try
ProcessBuilder processBuilder = new ProcessBuilder(); {
processBuilder.directory(rootDirectory.toFile()); while (process.isAlive() || process.getInputStream().available() > 0 || process.getErrorStream().available() > 0)
{
processBuilder.command( if (process.getInputStream().available() > 0)
rootDirectory.resolve(".venv/Scripts").toFile().exists() {
? rootDirectory.resolve(".venv/Scripts/python.exe").toString() byte[] data = new byte[process.getInputStream().available()];
: rootDirectory.resolve(".venv/bin/python").toString(), //noinspection ResultOfMethodCallIgnored
"./process.py", process.getInputStream().read(data);
outputFilePath.toString() System.out.write(data);
); }
if (process.getErrorStream().available() > 0)
Process process = processBuilder.start(); {
CompletableFuture<Void> outputFuture = readOutputStreams(process); byte[] data = new byte[process.getErrorStream().available()];
//noinspection ResultOfMethodCallIgnored
process.getOutputStream().write(content); process.getErrorStream().read(data);
process.getOutputStream().close(); System.err.write(data);
}
int exitCode = process.waitFor();
outputFuture.get(); //noinspection BusyWait
Thread.sleep(100);
if (exitCode != 0) }
{ }
throw new Exception("Process failed: " + exitCode); catch (Throwable ignored)
} {
}
return Files.readAllBytes(outputFilePath); });
} }
private static void replaceInByteArray(byte[] byteArray, String target, String replacement) private void replaceInNullTerminatedStrings(byte[] byteArray, String target, String replacement)
{ {
if (target.length() < replacement.length()) if (target.length() < replacement.length())
{ {
@@ -147,9 +117,40 @@ class RelocateNatives
} }
} }
public static byte[] processNative(String path, byte[] content) throws Exception public byte[] fixModifiedBinary(Path outputFilePath, byte[] content) throws Exception
{ {
Path outputFilePath = cacheRoot.resolve(path); ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.directory(rootDirectory.toFile());
processBuilder.command(
rootDirectory.resolve(".venv/Scripts").toFile().exists()
? rootDirectory.resolve(".venv/Scripts/python.exe").toString()
: rootDirectory.resolve(".venv/bin/python").toString(),
"./fix_modified_binary.py",
outputFilePath.toString()
);
Process process = processBuilder.start();
CompletableFuture<Void> outputFuture = readOutputStreams(process);
process.getOutputStream().write(content);
process.getOutputStream().close();
int exitCode = process.waitFor();
outputFuture.get();
if (exitCode != 0)
{
throw new Exception("Process failed: " + exitCode);
}
return Files.readAllBytes(outputFilePath);
}
public byte[] processBinary(String outputPath, byte[] content, Map<String, String> replacements) throws Exception
{
Path outputFilePath = cacheRoot.resolve(outputPath);
//noinspection ResultOfMethodCallIgnored
outputFilePath.getParent().toFile().mkdirs(); outputFilePath.getParent().toFile().mkdirs();
if (outputFilePath.toFile().exists()) if (outputFilePath.toFile().exists())
@@ -157,9 +158,12 @@ class RelocateNatives
return Files.readAllBytes(outputFilePath); return Files.readAllBytes(outputFilePath);
} }
replaceInByteArray(content, "org_sqlite", "dh_1sqlite"); for (Map.Entry<String, String> replacement : replacements.entrySet())
replaceInByteArray(content, "org/sqlite", "dh_sqlite"); {
return updateUsingLief(outputFilePath, content); this.replaceInNullTerminatedStrings(content, replacement.getKey(), replacement.getValue());
}
return this.fixModifiedBinary(outputFilePath, content);
} }
} }