Clean up code
This commit is contained in:
+79
-49
@@ -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
|
||||||
|
|||||||
+74
-70
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user