Commit 2f09dac4 authored by Stephanie Gawroriski's avatar Stephanie Gawroriski
Browse files

Add an alternative means of loading the emulator library that does not require extraction.

parent c192cdef
Pipeline #5265 failed with stages
in 2 minutes and 19 seconds
......@@ -38,7 +38,7 @@ jobs:
# According to documentation, Gradle on Windows has trouble and tries
# to make way too many workers which causes extreme resource.
# contention
command: gradlew build testHosted --no-daemon --max-workers=2 --console plain --continue
command: gradlew build testHosted --no-daemon --max-workers=2 --console plain --continue --parallel
- run:
shell: bash.exe
name: Save test results
......
......@@ -27,7 +27,10 @@ import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Supplier;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.jvm.tasks.Jar;
import org.gradle.nativeplatform.tasks.LinkSharedLibrary;
import org.gradle.process.JavaExecSpec;
import org.gradle.workers.WorkQueue;
import org.gradle.workers.WorkerExecutor;
......@@ -140,6 +143,11 @@ public class VMTestTaskAction
if (Boolean.getBoolean("java.awt.headless"))
sysProps.put("java.awt.headless", "true");
// Can we directly refer to the emulator library already?
Path emuLib = VMTestTaskAction.__findEmulatorLib(__task);
if (emuLib != null && Files.exists(emuLib))
sysProps.put("squirreljme.emulator.libpath", emuLib.toString());
// Execute the tests concurrently but up to the limit, as testing is
// very intense on CPU
int runCount = 0;
......@@ -457,6 +465,40 @@ public class VMTestTaskAction
}
}
/**
* Attempts to find the emulator library so that can be loaded directly
* instead of being extracted by each test process, if possible.
*
* @param __task The task running under.
* @return The path to the emulator library.
* @since 2020/12/01
*/
@SuppressWarnings("ConstantConditions")
private static Path __findEmulatorLib(Task __task)
throws NullPointerException
{
if (__task == null)
throw new NullPointerException("NARG");
// Figure out what the library is called
String libName = System.mapLibraryName("emulator-base");
// We need to look through the emulator base tasks to determine
// the library to select
Project emuBase = __task.getProject().getRootProject()
.findProject(":emulators:emulator-base");
// Is this valid?
Object raw = emuBase.getExtensions().getExtraProperties()
.get("libPathBase");
if (!(raw instanceof Path))
return null;
// Library is here?
return ((Path)raw).resolve(
System.mapLibraryName("emulator-base"));
}
/**
* Returns the simple duration of the test.
*
......
......@@ -48,12 +48,15 @@ dependencies
// We need the native library in the JAR before we can properly use it
// But we can compile the Java code just fine without it
boolean useDebugLib = false
jar {
//dependsOn "assembleRelease"
dependsOn "assembleDebug"
dependsOn (useDebugLib ? "assembleDebug" : "assembleRelease")
java.nio.file.Path libPath = buildDir.toPath().resolve("lib")
.resolve("main").resolve((useDebugLib ? "debug" : "release"))
project.ext.libPathBase = libPath
from buildDir.toPath().resolve("lib").resolve("main").
resolve("debug").toFile()
from libPath.toFile()
into "/"
}
......@@ -70,12 +73,17 @@ java
// Improve debugging with native code compiles, these are only used by the
// build system tests so these do not impact users at all
tasks.withType(CppCompile).configureEach() {
debuggable = true
optimized = false
tasks.withType(CppCompile).configureEach() {item ->
// Never do this for release!
if (item.name.toLowerCase().contains("release")) {
return
}
item.debuggable = true
item.optimized = false
// Additional compiler options for each compiler
compilerArgs.addAll toolChain.map { toolChain ->
item.compilerArgs.addAll toolChain.map { toolChain ->
// Microsoft Visual C++
if ("visualCpp".equalsIgnoreCase(toolChain.name)) {
return [
......@@ -103,11 +111,16 @@ tasks.withType(CppCompile).configureEach() {
}
// Ensure debugging information is used when linking
tasks.withType(LinkSharedLibrary).configureEach() {
debuggable = true
tasks.withType(LinkSharedLibrary).configureEach() { item ->
// Never do this for release!
if (item.name.toLowerCase().contains("release")) {
return
}
item.getDebuggable().set(true)
// Additional compiler options for each compiler
linkerArgs.addAll toolChain.map { toolChain ->
item.linkerArgs.addAll toolChain.map { toolChain ->
// Microsoft Visual C++
if ("visualCpp".equalsIgnoreCase(toolChain.name)) {
return [
......
......@@ -14,6 +14,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
/**
......@@ -23,7 +24,83 @@ import java.nio.file.StandardOpenOption;
*/
public final class NativeBinding
{
/** Property for pre-loading libraries. */
public static final String LIB_PRELOAD =
"squirreljme.emulator.libpath";
static
{
long loadNs = System.nanoTime();
try
{
// Try to use a preloaded library, otherwise load it in
Path libFile = NativeBinding.__checkPreload();
if (libFile == null)
libFile = NativeBinding.__libFromResources();
// Debug
System.err.printf("Java Over-Layer: Loading %s...%n", libFile);
// Try loading the library now
System.load(libFile.toString());
// Debug
System.err.printf("Java Over-Layer: Binding methods...%n");
// Bind methods
if (NativeBinding.__bindMethods() != 0)
throw new RuntimeException("Could not bind methods!");
// Debug
System.err.printf("Java Over-Layer: Methods bound!%n");
}
catch (IOException e)
{
throw new RuntimeException("Could not load library.", e);
}
// Track execution time
finally
{
System.err.printf("Java Over-Layer: Loading took %dms%n",
(System.nanoTime() - loadNs) / 1_000_000L);
}
}
/**
* Binds methods accordingly.
*
* @since 2020/02/26
*/
private static native int __bindMethods();
/**
* Checks to see if the preloaded library is available.
*
* @return The path to the library or {@code null} if not preloaded.
* @since 2020/12/01
*/
private static Path __checkPreload()
{
String libProp = System.getProperty(NativeBinding.LIB_PRELOAD);
if (libProp == null)
return null;
Path path = Paths.get(libProp);
if (Files.exists(path))
return path;
return null;
}
/**
* Tries to load the library from resources.
*
* @return The loaded library.
* @throws IOException On read/write errors.
* @since 2020/12/01
*/
private static Path __libFromResources()
throws IOException
{
// Find the library to load
String libName = System.mapLibraryName("emulator-base");
......@@ -31,6 +108,9 @@ public final class NativeBinding
// Debug
System.err.printf("Java Over-Layer: Locating %s...%n", libName);
// Timing for extraction
long startNs = System.nanoTime();
// Copy resource to the output
Path tempDir = null,
libFile = null;
......@@ -41,7 +121,6 @@ public final class NativeBinding
throw new RuntimeException(String.format(
"Library %s not found in resource.", libName));
// Store the library as a given file
tempDir = Files.createTempDirectory("squirreljme-lib");
libFile = tempDir.resolve(libName);
......@@ -56,7 +135,7 @@ public final class NativeBinding
StandardOpenOption.WRITE))
{
// Store here
byte[] buf = new byte[4096];
byte[] buf = new byte[262144];
for (;;)
{
int rc = in.read(buf);
......@@ -70,6 +149,17 @@ public final class NativeBinding
// Make sure it is on the disk
out.flush();
}
// Attempt cleanup at shutdown.
Runtime.getRuntime().addShutdownHook(
new PathCleanup(libFile, tempDir));
// Debug
System.err.printf("Java Over-Layer: Extracted to %s...%n",
libFile);
// Use this path
return libFile;
}
catch (IOException e)
{
......@@ -87,37 +177,14 @@ public final class NativeBinding
e.addSuppressed(f);
}
throw new RuntimeException("Could not copy native library.", e);
throw new IOException("Could not copy native library.", e);
}
// Debug
System.err.printf("Java Over-Layer: Extracting %s...%n", libFile);
// Attempt cleanup at shutdown.
Runtime.getRuntime().addShutdownHook(
new PathCleanup(libFile, tempDir));
// Debug
System.err.printf("Java Over-Layer: Loading %s...%n", libFile);
// Try loading the library now
System.load(libFile.toString());
// Debug
System.err.printf("Java Over-Layer: Binding methods...%n");
// Bind methods
if (NativeBinding.__bindMethods() != 0)
throw new RuntimeException("Could not bind methods!");
// Debug
System.err.printf("Java Over-Layer: Methods bound!%n");
// Track execution time
finally
{
System.err.printf("Java Over-Layer: Extraction took %dms%n",
(System.nanoTime() - startNs) / 1_000_000L);
}
}
/**
* Binds methods accordingly.
*
* @since 2020/02/26
*/
private static native int __bindMethods();
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment