diff --git a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/AndroidABI.java b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/AndroidABI.java new file mode 100644 index 00000000..794b2934 --- /dev/null +++ b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/AndroidABI.java @@ -0,0 +1,20 @@ +package com.badlogic.gdx.jnigen.commons; + +public enum AndroidABI { + + ABI_ARMEABI_V7A("armeabi-v7a"), + ABI_x86("x86"), + ABI_x86_64("x86_64"), + ABI_ARM64_V8A("arm64-v8a"); + + private final String abiString; + + AndroidABI (String abiString) { + this.abiString = abiString; + } + + public String getAbiString () { + return abiString; + } +} + diff --git a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Architecture.java b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Architecture.java index 47b07a98..1d36de69 100644 --- a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Architecture.java +++ b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Architecture.java @@ -1,7 +1,21 @@ package com.badlogic.gdx.jnigen.commons; public enum Architecture { - x86, ARM, RISCV,LOONGARCH; + + x86("x86"), + ARM("Arm"), + RISCV("Riscv"), + LOONGARCH("LoongArch"); + + private final String displayName; + + Architecture (String displayName) { + this.displayName = displayName; + } + + public String getDisplayName () { + return displayName; + } public String toSuffix() { if (this == x86) return ""; diff --git a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/CompilerABIType.java b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/CompilerABIType.java new file mode 100644 index 00000000..291740dc --- /dev/null +++ b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/CompilerABIType.java @@ -0,0 +1,6 @@ +package com.badlogic.gdx.jnigen.commons; + +public enum CompilerABIType { + GCC_CLANG, + MSVC +} diff --git a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/HostDetection.java b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/HostDetection.java new file mode 100644 index 00000000..c41367d6 --- /dev/null +++ b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/HostDetection.java @@ -0,0 +1,79 @@ +package com.badlogic.gdx.jnigen.commons; + +public class HostDetection { + + static public Os os; + static public Architecture.Bitness bitness = Architecture.Bitness._32; + static public Architecture architecture = Architecture.x86; + + static { + if (System.getProperty("os.name").contains("Windows")) + os = Os.Windows; + else if (System.getProperty("os.name").contains("Linux")) + os = Os.Linux; + else if (System.getProperty("os.name").contains("Mac")) + os = Os.MacOsX; + + if (System.getProperty("os.arch").startsWith("arm") || System.getProperty("os.arch").startsWith("aarch64")) + architecture = Architecture.ARM; + else if (System.getProperty("os.arch").startsWith("riscv")) + architecture = Architecture.RISCV; + else if (System.getProperty("os.arch").startsWith("loongarch")) + architecture = Architecture.LOONGARCH; + + if (System.getProperty("os.arch").contains("64") || System.getProperty("os.arch").startsWith("armv8")) + bitness = Architecture.Bitness._64; + else if (System.getProperty("os.arch").contains("128")) + bitness = Architecture.Bitness._128; + + boolean isMOEiOS = System.getProperty("moe.platform.name") != null; + String vm = System.getProperty("java.runtime.name"); + if (vm != null && vm.contains("Android Runtime")) { + os = Os.Android; + bitness = Architecture.Bitness._32; + architecture = Architecture.x86; + } + if (isMOEiOS || (os != Os.Android && os != Os.Windows && os != Os.Linux && os != Os.MacOsX)) { + os = Os.IOS; + bitness = Architecture.Bitness._32; + architecture = Architecture.x86; + } + } + + /** + * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.Windows} instead. + */ + @Deprecated + static public boolean isWindows = os == Os.Windows; + /** + * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.Linux} instead. + */ + @Deprecated + static public boolean isLinux = os == Os.Linux; + /** + * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.MacOsX} instead. + */ + @Deprecated + static public boolean isMac = os == Os.MacOsX; + /** + * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.IOS} instead. + */ + @Deprecated + static public boolean isIos = os == Os.IOS; + /** + * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.Android} instead. + */ + @Deprecated + static public boolean isAndroid = os == Os.Android; + /** + * @deprecated Use {@link #architecture} as {@code SharedLibraryLoader.architecture == Architecture.ARM} instead. + */ + @Deprecated + static public boolean isARM = architecture == Architecture.ARM; + /** + * @deprecated Use {@link #bitness} as {@code SharedLibraryLoader.bitness == Architecture.Bitness._64} instead. + */ + @Deprecated + static public boolean is64Bit = bitness == Architecture.Bitness._64; + +} diff --git a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Os.java b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Os.java index acb576ec..5547703f 100644 --- a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Os.java +++ b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Os.java @@ -1,28 +1,47 @@ package com.badlogic.gdx.jnigen.commons; -/** The target operating system of a build target. */ + +/** + * The target operating system of a build target. + */ public enum Os { - Windows, Linux, MacOsX, Android, IOS; - - public String getJniPlatform () { - if (this == Os.Windows) return "win32"; - if (this == Os.Linux) return "linux"; - if (this == Os.MacOsX) return "mac"; - return ""; - } - - public String getLibPrefix () { - if (this == Os.Linux || this == Os.Android || this == Os.MacOsX) { - return "lib"; - } - return ""; - } - - public String getLibExtension () { - if (this == Os.Windows) return "dll"; - if (this == Os.Linux) return "so"; - if (this == Os.MacOsX) return "dylib"; - if (this == Os.Android) return "so"; - return ""; - } + + Windows(Platform.Desktop), + Linux(Platform.Desktop), + MacOsX(Platform.Desktop), + Android(Platform.Android), + IOS(Platform.IOS); + + private final Platform platform; + + Os (Platform platform) { + this.platform = platform; + } + + public Platform getPlatform () { + return platform; + } + + public String getJniPlatform () { + if (this == Os.Windows) return "win32"; + if (this == Os.Linux) return "linux"; + if (this == Os.MacOsX) return "mac"; + if (this == Os.IOS) return "mac"; + return ""; + } + + public String getLibPrefix () { + if (this == Os.Linux || this == Os.Android || this == Os.MacOsX) { + return "lib"; + } + return ""; + } + + public String getLibExtension () { + if (this == Os.Windows) return "dll"; + if (this == Os.Linux) return "so"; + if (this == Os.MacOsX) return "dylib"; + if (this == Os.Android) return "so"; + return ""; + } } diff --git a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Platform.java b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Platform.java new file mode 100644 index 00000000..387a694c --- /dev/null +++ b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/Platform.java @@ -0,0 +1,7 @@ +package com.badlogic.gdx.jnigen.commons; + +public enum Platform { + Desktop, + Android, + IOS +} diff --git a/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/TargetType.java b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/TargetType.java new file mode 100644 index 00000000..2f12778a --- /dev/null +++ b/compiling/gdx-jnigen-commons/src/main/java/com/badlogic/gdx/jnigen/commons/TargetType.java @@ -0,0 +1,23 @@ +package com.badlogic.gdx.jnigen.commons; + +public enum TargetType { + + SIMULATOR("simulator", "iphoneos"), + DEVICE("device", "iphonesimulator"); + + private final String targetTypeBuildDirName; + private final String platformName; + + TargetType (String device, String xcodeDeviceTypeArg) { + this.targetTypeBuildDirName = device; + this.platformName = xcodeDeviceTypeArg; + } + + public String getTargetTypeBuildDirName () { + return targetTypeBuildDirName; + } + + public String getPlatformName () { + return platformName; + } +} diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenBuildTargetTask.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenBuildTargetTask.java deleted file mode 100644 index 7b9f1259..00000000 --- a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenBuildTargetTask.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.badlogic.gdx.jnigen.gradle; - -import java.io.File; - -import javax.inject.Inject; - -import org.gradle.api.DefaultTask; -import org.gradle.api.tasks.TaskAction; - -import com.badlogic.gdx.jnigen.BuildExecutor; -import com.badlogic.gdx.jnigen.BuildTarget; - -/** - * @author Desu - */ -public class JnigenBuildTargetTask extends DefaultTask { - JnigenExtension ext; - BuildTarget target; - - @Inject - public JnigenBuildTargetTask(JnigenExtension ext, BuildTarget target) { - this.ext = ext; - this.target = target; - - setGroup("jnigen"); - setDescription("Executes all jnigen build script for build target " + target.getTargetFolder() + "."); - } - - @TaskAction - public void run() { - boolean verbose = getProject().findProperty("VERBOSE") != null; - if(!BuildExecutor.executeAnt(new File(ext.subProjectDir + ext.jniDir, target.getBuildFilename()).getPath(), - "-Drelease=" + ext.release, "clean", "postcompile", verbose ? "-v" : "")) { - throw new RuntimeException("Ant execution for " + target.getBuildFilename() + " failed."); - } - } -} \ No newline at end of file diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenBuildTask.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenBuildTask.java index de32b261..ba1f2a12 100644 --- a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenBuildTask.java +++ b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenBuildTask.java @@ -1,26 +1,70 @@ package com.badlogic.gdx.jnigen.gradle; -import javax.inject.Inject; - +import com.badlogic.gdx.jnigen.BuildConfig; +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.RobovmBuildConfig; +import com.badlogic.gdx.jnigen.build.PlatformBuilder; +import com.badlogic.gdx.jnigen.commons.Os; import org.gradle.api.DefaultTask; import org.gradle.api.tasks.TaskAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.util.Collections; /** * @author Desu */ -public class JnigenBuildTask extends DefaultTask { - JnigenExtension ext; - - @Inject - public JnigenBuildTask(JnigenExtension ext) { - this.ext = ext; - - setGroup("jnigen"); - setDescription("Executes all available jnigen build scripts for the current platform."); - } - - @TaskAction - public void run() { - // Empty task - } -} \ No newline at end of file +public class JnigenBuildTask extends DefaultTask { + + private static final Logger logger = LoggerFactory.getLogger(JnigenBuildTask.class); + + private final JnigenExtension ext; + + private BuildTarget buildTarget; + private Os osToBuild; + + @Inject + public JnigenBuildTask (JnigenExtension ext) { + this.ext = ext; + + setGroup("jnigen"); + setDescription("Builds native libraries"); + } + + public void setBuildTarget (BuildTarget buildTarget) { + this.buildTarget = buildTarget; + } + + + public void setOsToBuild (Os osToBuild) { + this.osToBuild = osToBuild; + } + + @TaskAction + public void run () { + RobovmBuildConfig robovmBuildConfig = new RobovmBuildConfig(); + if (ext.robovm != null) { + ext.robovm.execute(robovmBuildConfig); + } + + BuildConfig buildConfig = new BuildConfig(ext.sharedLibName, ext.subProjectDir + ext.temporaryDir, ext.subProjectDir + ext.libsDir, ext.subProjectDir + ext.jniDir, robovmBuildConfig, new FileDescriptor(ext.subProjectDir)); + + buildConfig.multiThreadedCompile = ext.multiThreadedCompile; + + //Build the build target! + //If we have no build target, its OS level build target build + + //If we have build target, its a specific build + + PlatformBuilder platformBuilder = new PlatformBuilder(); + + if (buildTarget != null) { + platformBuilder.build(buildTarget.os, buildConfig, Collections.singletonList(buildTarget)); + } else { + platformBuilder.build(osToBuild, buildConfig, ext.targets); + } + } +} diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenExtension.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenExtension.java index 7e65de33..1f513efe 100644 --- a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenExtension.java +++ b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenExtension.java @@ -1,369 +1,320 @@ package com.badlogic.gdx.jnigen.gradle; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; - -import javax.inject.Inject; - +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.RobovmBuildConfig; +import com.badlogic.gdx.jnigen.commons.*; import org.gradle.api.Action; import org.gradle.api.Project; -import org.gradle.api.Task; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.badlogic.gdx.jnigen.BuildTarget; -import com.badlogic.gdx.jnigen.commons.Architecture; -import com.badlogic.gdx.jnigen.commons.Os; +import javax.inject.Inject; +import java.io.File; +import java.util.*; +import java.util.function.Predicate; /** * @author Desu */ public class JnigenExtension { - private static final Logger log = LoggerFactory.getLogger(JnigenExtension.class); - - public static final Architecture.Bitness x32 = Architecture.Bitness._32; - public static final Architecture.Bitness x64 = Architecture.Bitness._64; - public static final Architecture.Bitness x128 = Architecture.Bitness._128; - public static final Architecture x86 = Architecture.x86; - public static final Architecture ARM = Architecture.ARM; - public static final Architecture RISCV = Architecture.RISCV; - public static final Architecture LOONGARCH = Architecture.LOONGARCH; - public static final Os Windows = Os.Windows; - public static final Os Linux = Os.Linux; - public static final Os MacOsX = Os.MacOsX; - public static final Os Android = Os.Android; - public static final Os IOS = Os.IOS; - - final Project project; - - /** - * Gradle Tasks are executed in the main project working directory. Supply - * actual subproject path where necessary. - */ - String subProjectDir; - - String sharedLibName = null; - String temporaryDir = "target"; - String libsDir = "libs"; - String jniDir = "jni"; - - /** - * If we should build with release flag set.
- * This strips debug symbols. - */ - boolean release = true; - - NativeCodeGeneratorConfig nativeCodeGeneratorConfig; - List targets = new ArrayList<>(); - Action all = null; - - Task jarAndroidNatives = null; - JnigenJarTask jarDesktopNatives = null; - - RoboVMXml robovm = new RoboVMXml(); + private static final Logger log = LoggerFactory.getLogger(JnigenExtension.class); + + public static final Architecture.Bitness x32 = Architecture.Bitness._32; + public static final Architecture.Bitness x64 = Architecture.Bitness._64; + public static final Architecture.Bitness x128 = Architecture.Bitness._128; + public static final Architecture x86 = Architecture.x86; + public static final Architecture ARM = Architecture.ARM; + public static final Architecture RISCV = Architecture.RISCV; + public static final Architecture LOONGARCH = Architecture.LOONGARCH; + + public static final CompilerABIType MSVC = CompilerABIType.MSVC; + public static final CompilerABIType GCC_CLANG = CompilerABIType.GCC_CLANG; + public static final Os Windows = Os.Windows; + public static final Os Linux = Os.Linux; + public static final Os MacOsX = Os.MacOsX; + public static final Os Android = Os.Android; + public static final Os IOS = Os.IOS; + + public static final AndroidABI ABI_ARMEABI_V7A = AndroidABI.ABI_ARMEABI_V7A; + public static final AndroidABI ABI_x86f = AndroidABI.ABI_x86; + public static final AndroidABI ABI_ARM64_V8A = AndroidABI.ABI_ARM64_V8A; + public static final AndroidABI ABI_x86_64 = AndroidABI.ABI_x86_64; + + public static final TargetType DEVICE = TargetType.DEVICE; + public static final TargetType SIMULATOR = TargetType.SIMULATOR; + + + final Project project; + + /** + * Gradle Tasks are executed in the main project working directory. Supply + * actual subproject path where necessary. + */ + String subProjectDir; + + String sharedLibName = null; + String temporaryDir = "build/jnigen/target"; + String libsDir = "build/jnigen/libs"; + String jniDir = "build/jnigen/jni"; + + /** + * If we should build with release flag set.
+ * This strips debug symbols. + */ + boolean release = true; + + boolean multiThreadedCompile = true; + + NativeCodeGeneratorConfig nativeCodeGeneratorConfig; + public List targets = new ArrayList<>(); + Action all = null; + + Action robovm; JnigenBindingGeneratorExtension generator = new JnigenBindingGeneratorExtension(); - @Inject - public JnigenExtension(Project project) { - this.project = project; - this.subProjectDir = project.getProjectDir().getAbsolutePath() + File.separator; - this.nativeCodeGeneratorConfig = new NativeCodeGeneratorConfig(project, subProjectDir); - } + @Inject + public JnigenExtension (Project project) { + this.project = project; + this.subProjectDir = project.getProjectDir().getAbsolutePath() + File.separator; + this.nativeCodeGeneratorConfig = new NativeCodeGeneratorConfig(project); + } - public void generator(Action container) { + public void generator(Action container) { container.execute(generator); } - public void nativeCodeGenerator(Action container) { - container.execute(nativeCodeGeneratorConfig); - } + public void nativeCodeGenerator (Action container) { + container.execute(nativeCodeGeneratorConfig); + } - public void all(Action container) { - this.all = container; - } + public void all (Action container) { + this.all = container; + } - public void robovm(Action action) { - action.execute(robovm); - } + public void robovm (Action robovmConfigContainer) { + this.robovm = robovmConfigContainer; + } - public void add(Os type) { - add(type, Architecture.Bitness._32); - } - public void add(Os type, Architecture.Bitness bitness) { - add(type, bitness, Architecture.x86); - } + public void addLinux (Architecture.Bitness bitness, Architecture architecture) { + addLinux(bitness, architecture, null); + } - public void add(Os type, Architecture.Bitness bitness, Architecture architecture) { - add(type, bitness, architecture, null); - } + public void addLinux (Architecture.Bitness bitness, Architecture architecture, Action container) { + add(Linux, bitness, architecture, CompilerABIType.GCC_CLANG, TargetType.DEVICE, null, container); + } - @Deprecated - public void add(Os type, boolean is64Bit) { - add(type, is64Bit, false, null); - } - @Deprecated - public void add(Os type, boolean is64Bit, boolean isARM) { - add(type, is64Bit, isARM, null); - } + public void addIOS () { + addIOS(x64, x86, SIMULATOR); + addIOS(x64, ARM, DEVICE); + addIOS(x64, ARM, SIMULATOR); + } - public void add(Os type, Action container) { - add(type, Architecture.Bitness._32, Architecture.x86, container); - } + public void addIOS (Action container) { + addIOS(x64, x86, SIMULATOR, container); + addIOS(x64, ARM, DEVICE, container); + addIOS(x64, ARM, SIMULATOR, container); + } - @Deprecated - public void add(Os type, boolean is64Bit, Action container) { - add(type, is64Bit, false, container); - } - public void add(Os type, Architecture.Bitness bitness, Action container) { - add(type, bitness, Architecture.x86, container); - } - @Deprecated - public void add(Os type, boolean is64Bit, boolean isARM, Action container) { - add(type, is64Bit ? Architecture.Bitness._64 : Architecture.Bitness._32, isARM ? Architecture.ARM : Architecture.x86, container); - } + public void addIOS (Architecture.Bitness bitness, Architecture architecture, TargetType targetType) { + addIOS(bitness, architecture, targetType, null); + } - public void add(Os type, Architecture.Bitness bitness, Architecture architecture, Action container) { - String name = type + architecture.toSuffix().toUpperCase() + bitness.toSuffix(); - - if(get(type, bitness, architecture) != null) - throw new RuntimeException("Attempt to add duplicate build target " + name); - if((type == Android || type == IOS) && bitness != Architecture.Bitness._32 && architecture != Architecture.x86) - throw new RuntimeException("Android and iOS must not have is64Bit or isARM or isRISCV."); - - BuildTarget target = BuildTarget.newDefaultTarget(type, bitness, architecture); - - if (all != null) - all.execute(target); - if (container != null) - container.execute(target); - - targets.add(target); - - Task jnigenTask = project.getTasks().getByName("jnigen"); - Task jnigenBuildTask = project.getTasks().getByName("jnigenBuild"); - Task builtTargetTask = project.getTasks().create("jnigenBuild" + name, - JnigenBuildTargetTask.class, this, target); - builtTargetTask.dependsOn(jnigenTask); - - if (!target.excludeFromMasterBuildFile && target.canBuild.getAsBoolean()) - jnigenBuildTask.dependsOn(builtTargetTask); - - if(type == Android) { - if(jarAndroidNatives == null) { - jarAndroidNatives = project.getTasks().create("jnigenJarNativesAndroid"); - jarAndroidNatives.setGroup("jnigen"); - jarAndroidNatives.setDescription("Assembles all jar archives containing the native libraries for Android."); - } - - String[] abis = target.androidABIs; - - // If we have an "all" abi, add tasks for all known abis. - if(Arrays.asList(abis).contains("all")) { - List tmp = new ArrayList<>(Arrays.asList(abis)); - tmp.remove("all"); - tmp.add("armeabi"); - tmp.add("armeabi-v7a"); - tmp.add("x86"); - tmp.add("x86_64"); - tmp.add("arm64-v8a"); - abis = tmp.toArray(new String[tmp.size()]); - } - - JnigenJarTask[] jarAndroidNativesABIs = new JnigenJarTask[abis.length]; - for(int i = 0; i < abis.length; i++) { - jarAndroidNativesABIs[i] = project.getTasks().create("jnigenJarNativesAndroid"+abis[i], JnigenJarTask.class, type); - jarAndroidNativesABIs[i].add(target, this, abis[i]); - - jarAndroidNatives.dependsOn(jarAndroidNativesABIs[i]); - } - } else if (type == IOS) { - JnigenGenerateRoboVMXml generateRoboVMXml = project.getTasks().create("jnigenGenerateRoboVMXml", - JnigenGenerateRoboVMXml.class, this); - - JnigenIOSJarTask jarIOSNatives = project.getTasks().create("jnigenJarNativesIOS", JnigenIOSJarTask.class); - jarIOSNatives.add(target, this); - - jarIOSNatives.dependsOn(generateRoboVMXml); - } - else { - if(jarDesktopNatives == null) - jarDesktopNatives = project.getTasks().create("jnigenJarNativesDesktop", JnigenJarTask.class, type); - jarDesktopNatives.add(target, this); - } - } + public void addIOS (Architecture.Bitness bitness, Architecture architecture, TargetType targetType, Action container) { + add(Os.IOS, bitness, architecture, CompilerABIType.GCC_CLANG, targetType, null, container); + } - public BuildTarget get(Os type) { - return get(type, Architecture.Bitness._32, Architecture.x86, null); - } + public void addMac (Architecture.Bitness bitness, Architecture architecture) { + addMac(bitness, architecture, null); + } - public BuildTarget get(Os type, Architecture.Bitness bitness) { - return get(type, bitness, Architecture.x86); - } + public void addMac (Architecture.Bitness bitness, Architecture architecture, Action container) { + add(Os.MacOsX, bitness, architecture, GCC_CLANG, TargetType.DEVICE, null, container); + } - @Deprecated - public BuildTarget get(Os type, boolean is64Bit) { - return get(type, is64Bit, false, null); - } + public void addWindows (Architecture.Bitness bitness, Architecture architecture) { + addWindows(bitness, architecture, GCC_CLANG, null); + } - @Deprecated - public BuildTarget get(Os type, boolean is64Bit, boolean isARM) { - return get(type, is64Bit, isARM, null); - } + public void addWindows (Architecture.Bitness bitness, Architecture architecture, Action container) { + addWindows(bitness, architecture, GCC_CLANG, null); + } - public BuildTarget get(Os type, Architecture.Bitness bitness, Architecture architecture) { - return get(type, bitness, architecture, null); - } + public void addWindows (Architecture.Bitness bitness, Architecture architecture, CompilerABIType compilerABIType) { + addWindows(bitness, architecture, compilerABIType, null); + } - public BuildTarget get(Os type, Action container) { - return get(type, Architecture.Bitness._32, Architecture.x86, container); - } + public void addWindows (Architecture.Bitness bitness, Architecture architecture, CompilerABIType compilerABIType, Action container) { + add(Os.Windows, bitness, architecture, compilerABIType, TargetType.DEVICE, null, container); + } - @Deprecated - public BuildTarget get(Os type, boolean is64Bit, Action container) { - return get(type, is64Bit, false, container); - } + public void addAndroid () { + addAndroid(null, null); + } - @Deprecated - public BuildTarget get(Os type, boolean is64Bit, boolean isARM, Action container) { - return get(type, is64Bit ? Architecture.Bitness._64 : Architecture.Bitness._32, isARM ? Architecture.ARM : Architecture.x86, container); - } + public void addAndroid (Action container) { + addAndroid(null, container); + } + + public void addAndroid (AndroidABI abi) { + addAndroid(abi, null); + } + + public void addAndroid (AndroidABI abi, Action container) { + if (abi == null) { + for (AndroidABI value : AndroidABI.values()) { + //Add them all! + addAndroid(value, container); + } + } else { + //bitness and arch doesn't mean anything for android + add(Os.Android, Architecture.Bitness._32, Architecture.x86, CompilerABIType.GCC_CLANG, DEVICE, abi, container); + } + } + + public void add (Os targetOs, Architecture.Bitness bitness, Architecture architecture, CompilerABIType abiType, TargetType targetType, AndroidABI androidABI, Action container) { + String name = targetOs + architecture.toSuffix().toUpperCase() + bitness.toSuffix(); + + if (get(targetOs, bitness, architecture, androidABI, targetType, container) != null) + throw new RuntimeException("Attempt to add duplicate build target " + name); + if ((targetOs == Android) && bitness != Architecture.Bitness._32 && architecture != Architecture.x86) + throw new RuntimeException("Android and iOS must not have is64Bit or isARM or isRISCV."); + + BuildTarget target = BuildTarget.newDefaultTarget(targetOs, bitness, architecture, abiType, targetType); + target.release = release; + + if (all != null) + all.execute(target); + if (container != null) + container.execute(target); + + if (target.excludeFromMasterBuildFile) { + return; + } + targets.add(target); + target.setAndroidOverrideABI(androidABI); + + checkForTasksToAdd(target); + + } + + private Set osLevelTargetsSeen = new HashSet<>(); + private Set platformLevelTargetsSeen = new HashSet<>(); + + private void checkForTasksToAdd (BuildTarget target) { + + Os os = target.os; + Platform platform = os.getPlatform(); + Architecture architecture = target.architecture; + Architecture.Bitness bitness = target.bitness; + + JnigenTask jnigenTask = (JnigenTask) project.getTasks().getByName("jnigen"); + + if (!osLevelTargetsSeen.contains(os)) { + osLevelTargetsSeen.add(os); + JnigenBuildTask jnigenBuildTask = project.getTasks().create("jnigenBuildAll" + os.name(), JnigenBuildTask.class, this); + jnigenBuildTask.setOsToBuild(os); + jnigenBuildTask.dependsOn(jnigenTask); + } + + if (target.os == Android) { + JnigenBuildTask jnigenBuildTask = project.getTasks().create("jnigenBuild" + os.name() + "_" + target.getTargetAndroidABI().getAbiString(), JnigenBuildTask.class, this); + jnigenBuildTask.setBuildTarget(target); + jnigenBuildTask.dependsOn(jnigenTask); + + //Add the package task, android does separate artifacts + + JnigenPackageTask jnigenPackageTask = project.getTasks().create("jnigenPackage" + platform.name() + "_" + target.getTargetAndroidABI().getAbiString(), JnigenPackageTask.class, this); + jnigenPackageTask.configure(target.getTargetAndroidABI(), platform); + + } else if (target.os == Os.IOS) { + //Nope! No platform specific builds for ios, because theyend up in framework. + //Don't want to have to do a separate task after for combining the framework + } else { + JnigenBuildTask jnigenBuildTask = project.getTasks().create("jnigenBuild" + os.name() + "_" + architecture.getDisplayName() + bitness.name(), JnigenBuildTask.class, this); + jnigenBuildTask.setBuildTarget(target); + jnigenBuildTask.dependsOn(jnigenTask); + } + + if (!platformLevelTargetsSeen.contains(platform)) { + platformLevelTargetsSeen.add(platform); + + JnigenPackageTask jnigenPackageTask = project.getTasks().create("jnigenPackageAll" + platform.name(), JnigenPackageTask.class, this); + jnigenPackageTask.configure(null, platform); + } + + } + + public BuildTarget get (Os type, Architecture.Bitness bitness, Architecture architecture, AndroidABI androidABI, TargetType targetType, Action container) { + for (BuildTarget target : targets) { + if ( + target.os == type && + target.bitness == bitness && + target.architecture == architecture && + target.getTargetAndroidABI() == androidABI && + target.targetType == targetType) { + if (container != null) + container.execute(target); + return target; + } + } + return null; + } + + public void each (Predicate condition, Action container) { + for (BuildTarget target : targets) { + if (condition.test(target)) + container.execute(target); + } + } + + class NativeCodeGeneratorConfig { + SourceSet sourceSet; + private String[] sourceDirs; + String[] includes = null; + String[] excludes = null; + + public NativeCodeGeneratorConfig (Project project) { + JavaPluginConvention javaPlugin = project.getConvention().getPlugin(JavaPluginConvention.class); + SourceSetContainer sourceSets = javaPlugin.getSourceSets(); + sourceSet = sourceSets.findByName("main"); + } + + @Override + public String toString () { + return "NativeCodeGeneratorConfig[sourceDir=`" + Arrays.toString(sourceDirs) + "`, sourceSet=`" + sourceSet + "`, jniDir=`" + + jniDir + "`, includes=`" + Arrays.toString(includes) + + "`, excludes=`" + Arrays.toString(excludes) + "`]"; + } + + /** + * This method is deprecated in favor of {@link NativeCodeGeneratorConfig#setSourceDirs(String[])} + */ + @Deprecated + public void setSourceDir (String sourceDir) { + this.sourceDirs = new String[]{sourceDir}; + } + + public void setSourceDirs (String[] sourceDirs) { + this.sourceDirs = sourceDirs; + } + + public String[] getSourceDirs () { + //If already set, use provided value + if (sourceDirs != null) { + return sourceDirs; + } + + sourceDirs = sourceSet.getJava().getSrcDirs().stream().map(File::getPath).toArray(String[]::new); + return sourceDirs; + } + } - public BuildTarget get(Os type, Architecture.Bitness bitness, Architecture architecture, Action container) { - for(BuildTarget target : targets) { - if(target.os == type && target.bitness == bitness && target.architecture == architecture) { - if(container != null) - container.execute(target); - return target; - } - } - return null; - } - - public void each(Predicate condition, Action container) { - for(BuildTarget target : targets) { - if(condition.test(target)) - container.execute(target); - } - } - class NativeCodeGeneratorConfig { - SourceSet sourceSet; - private String[] sourceDirs; - String jniDir = "jni"; - String[] includes = null; - String[] excludes = null; - - public NativeCodeGeneratorConfig(Project project, String subProjectDir) { - JavaPluginConvention javaPlugin = project.getConvention().getPlugin(JavaPluginConvention.class); - SourceSetContainer sourceSets = javaPlugin.getSourceSets(); - sourceSet = sourceSets.findByName("main"); - } - - @Override - public String toString() { - return "NativeCodeGeneratorConfig[sourceDir=`" + Arrays.toString(sourceDirs) + "`, sourceSet=`" + sourceSet + "`, jniDir=`" - + jniDir + "`, includes=`" + Arrays.toString(includes) - + "`, excludes=`" + Arrays.toString(excludes) + "`]"; - } - - /** - * This method is deprecated in favor of {@link NativeCodeGeneratorConfig#setSourceDirs(String[])} - */ - @Deprecated - public void setSourceDir(String sourceDir) { - this.sourceDirs = new String[]{sourceDir}; - } - - public void setSourceDirs(String[] sourceDirs) { - this.sourceDirs = sourceDirs; - } - - public String[] getSourceDirs() - { - //If already set, use provided value - if(sourceDirs != null) { - return sourceDirs; - } - - sourceDirs = sourceSet.getJava().getSrcDirs().stream().map(File::getPath).toArray(String[]::new); - return sourceDirs; - } - } - - class RoboVMXml { - /** - * Use an existing robovm.xml file instead of generating one. - */ - private File manualFile = null; - private List forceLinkClasses = new ArrayList<>(); - private List extraLibs = new ArrayList<>(); - private List extraXCFrameworks = new ArrayList<>(); - - public File getManualFile() { - return manualFile; - } - - public List getForceLinkClasses() { - return forceLinkClasses; - } - - public List getExtraLibs() { - return extraLibs; - } - - public List getExtraXCFrameworks() { - return extraXCFrameworks; - } - - public void manualFile(File manualFile) { - if (!forceLinkClasses.isEmpty() || !extraLibs.isEmpty()) - throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); - this.manualFile = manualFile; - } - - public void forceLinkClasses(String[] forceLinkClasses) { - if (manualFile != null) - throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); - this.forceLinkClasses.addAll(Arrays.asList(forceLinkClasses)); - } - - public void extraLib(String path) { - if (manualFile != null) - throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); - extraLibs.add(new RoboVMXmlLib(path, null)); - } - - public void extraLib(String path, String variant) { - if (manualFile != null) - throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); - extraLibs.add(new RoboVMXmlLib(path, variant)); - } - - public void extraXCFramework(String path) { - if (manualFile != null) - throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); - extraXCFrameworks.add(path); - } - - class RoboVMXmlLib { - String path; - String variant; - - public RoboVMXmlLib(String path, String variant) { - this.path = path; - this.variant = variant; - } - } - } } diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenGenerateRoboVMXml.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenGenerateRoboVMXml.java deleted file mode 100644 index 961e2f2d..00000000 --- a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenGenerateRoboVMXml.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.badlogic.gdx.jnigen.gradle; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.HashSet; - -import javax.inject.Inject; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import com.badlogic.gdx.jnigen.commons.Os; -import org.gradle.api.DefaultTask; -import org.gradle.api.tasks.TaskAction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import com.badlogic.gdx.jnigen.BuildTarget; -import com.badlogic.gdx.jnigen.gradle.JnigenExtension.RoboVMXml.RoboVMXmlLib; - -/** - * @author Desu - */ -public class JnigenGenerateRoboVMXml extends DefaultTask { - private static final Logger log = LoggerFactory.getLogger(JnigenGenerateRoboVMXml.class); - - JnigenExtension ext; - - @Inject - public JnigenGenerateRoboVMXml(JnigenExtension ext) { - this.ext = ext; - - setGroup("jnigen"); - setDescription("Generates robovm.xml file"); - } - - @TaskAction - public void run() { - BuildTarget target = ext.get(Os.IOS); - if (target == null) { - log.info("Nothing to do because no IOS BuildTarget"); - return; - } - - File buildDir = getProject().getBuildDir(); - if (!buildDir.exists()) - buildDir.mkdirs(); - - File robovmXml = new File(buildDir, "robovm.xml"); - - if (ext.robovm.getManualFile() != null) { - File manualFile = ext.robovm.getManualFile(); - if (!manualFile.exists()) { - throw new RuntimeException("Manual robovm.xml file does not exist: " + manualFile.getPath()); - } - - try { - Files.copy(manualFile.toPath(), robovmXml.toPath(), StandardCopyOption.REPLACE_EXISTING, - StandardCopyOption.COPY_ATTRIBUTES); - } catch (IOException e) { - throw new RuntimeException("Unable to copy manual robovm.xml file to temporary robovm.xml file", e); - } - return; - } - - File oldRobovmXml = new File(ext.subProjectDir + ext.jniDir + File.separatorChar + "robovm.xml"); - if (oldRobovmXml.exists()) { - throw new RuntimeException("Legacy " + oldRobovmXml.getPath() - + " file exists, please define `jnigen.robovm.manualFile = file('jni/robovm.xml')` or migrate to gradle declaration and delete this file."); - } - - try { - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - - Document doc = docBuilder.newDocument(); - Element config = doc.createElement("config"); - doc.appendChild(config); - - HashSet addedPaths = new HashSet<>(); - Element frameworkPaths = doc.createElement("frameworkPaths"); - config.appendChild(frameworkPaths); - - Element xcFrameworkPath = doc.createElement("path"); - xcFrameworkPath.setTextContent("libs"); - addedPaths.add("libs"); - - frameworkPaths.appendChild(xcFrameworkPath); - - Element frameworks = doc.createElement("frameworks"); - config.appendChild(frameworks); - - Element framework = doc.createElement("framework"); - framework.setTextContent(ext.sharedLibName); - frameworks.appendChild(framework); - - // Add any extra libraries we have declared - if (!ext.robovm.getExtraLibs().isEmpty()) { - Element libs = doc.createElement("libs"); - for (RoboVMXmlLib l : ext.robovm.getExtraLibs()) { - Element lib = doc.createElement("lib"); - if (l.variant != null) - lib.setAttribute("variant", l.variant); - lib.setTextContent(l.path); - libs.appendChild(lib); - } - config.appendChild(libs); - } - - // Add any extra xcFramework we have declared - // TODO: 03.05.23 Rework at somepoint, if xcframeworks tag merging works - if (!ext.robovm.getExtraXCFrameworks().isEmpty()) { - for (String path : ext.robovm.getExtraXCFrameworks()) { - String xcFrameworkName = path.substring(path.lastIndexOf('/') + 1); - xcFrameworkName = xcFrameworkName.substring(0, xcFrameworkName.lastIndexOf('.')); - String frameworkPath = path.substring(0, path.lastIndexOf('/')); - if (!addedPaths.contains(frameworkPath)) { - addedPaths.add(frameworkPath); - Element pathEl = doc.createElement("path"); - pathEl.setTextContent(frameworkPath); - frameworkPaths.appendChild(pathEl); - } - Element frameworkEl = doc.createElement("framework"); - frameworkEl.setTextContent(xcFrameworkName); - frameworks.appendChild(frameworkEl); - } - } - - // Add any forceLinkClasses definitions we have declared - if (!ext.robovm.getForceLinkClasses().isEmpty()) { - Element forceLinkClasses = doc.createElement("forceLinkClasses"); - - for (String p : ext.robovm.getForceLinkClasses()) { - Element pattern = doc.createElement("pattern"); - pattern.setTextContent(p); - forceLinkClasses.appendChild(pattern); - } - - config.appendChild(forceLinkClasses); - } - - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); - - FileOutputStream fos = new FileOutputStream(robovmXml); - StreamResult result = new StreamResult(fos); - transformer.setOutputProperty("omit-xml-declaration", "yes"); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); - transformer.transform(source, result); - fos.close(); - } catch (IOException | ParserConfigurationException | TransformerException e) { - throw new RuntimeException("Unable to create temporary robovm.xml file", e); - } - } -} diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenGithubActionBuilderTask.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenGithubActionBuilderTask.java new file mode 100644 index 00000000..6c6cc989 --- /dev/null +++ b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenGithubActionBuilderTask.java @@ -0,0 +1,46 @@ +package com.badlogic.gdx.jnigen.gradle; + +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.commons.Os; +import com.badlogic.gdx.jnigen.gradle.gha.GHABuilder; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.TaskAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; + +public class JnigenGithubActionBuilderTask extends DefaultTask { + + private static final Logger logger = LoggerFactory.getLogger(JnigenGithubActionBuilderTask.class); + + private final JnigenExtension ext; + + private BuildTarget buildTarget; + private String overrideABI; + private Os osToBuild; + + @Inject + public JnigenGithubActionBuilderTask (JnigenExtension ext) { + this.ext = ext; + + setGroup("jnigen"); + setDescription("Generate github action to build"); + } + + @TaskAction + public void run () { + File file = getProject().file(".github/workflows/jnigen.yaml"); + file.getParentFile().mkdirs(); + + + try { + GHABuilder ghaBuilder = new GHABuilder(file, ext); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenIOSJarTask.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenIOSJarTask.java deleted file mode 100644 index 5eafa97c..00000000 --- a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenIOSJarTask.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.badlogic.gdx.jnigen.gradle; - -import java.io.File; - -import com.badlogic.gdx.jnigen.BuildTarget; -import com.badlogic.gdx.jnigen.commons.Os; - -public class JnigenIOSJarTask extends JnigenJarTask { - public JnigenIOSJarTask() { - super(Os.IOS); - } - - public void add(BuildTarget target, JnigenExtension ext, String abi) { - String targetFolder = target.getTargetFolder(); - - String path = ext.subProjectDir + ext.libsDir + File.separatorChar + targetFolder; - from(path, (copySpec) -> { - copySpec.include("**/*.xcframework/"); - copySpec.include("**/*.framework/"); - copySpec.include("**/*.a"); - copySpec.into("META-INF/robovm/ios/libs"); - }); - - File robovmXml = new File(getProject().getBuildDir(), "robovm.xml"); - from(robovmXml, (copySpec) -> { - copySpec.into("META-INF/robovm/ios"); - copySpec.rename(".*", "robovm.xml"); - }); - } -} diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenJarTask.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenJarTask.java deleted file mode 100644 index f34ab241..00000000 --- a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenJarTask.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.badlogic.gdx.jnigen.gradle; - -import java.io.File; - -import javax.inject.Inject; - -import com.badlogic.gdx.jnigen.commons.Os; -import org.gradle.api.tasks.bundling.Jar; - -import com.badlogic.gdx.jnigen.BuildTarget; - -/** - * @author Desu - */ -public class JnigenJarTask extends Jar { - @Inject - public JnigenJarTask(Os os) { - switch(os) { - case Android: - getArchiveClassifier().set("natives-android"); - break; - case IOS: - getArchiveClassifier().set("natives-ios"); - break; - default: - getArchiveClassifier().set("natives-desktop"); - break; - } - - setGroup("jnigen"); - setDescription("Assembles a jar archive containing the native libraries."); - } - - public final void add(BuildTarget target, JnigenExtension ext) { - add(target, ext, null); - } - - public void add(BuildTarget target, JnigenExtension ext, String abi) { - String targetFolder = target.getTargetFolder(); - - if (abi != null && !abi.isEmpty()) { - targetFolder = abi; - getArchiveClassifier().set("natives-" + abi); - } - - String path = ext.subProjectDir + ext.libsDir + File.separatorChar + targetFolder + File.separatorChar; - from(path); - } -} diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenPackageTask.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenPackageTask.java new file mode 100644 index 00000000..c4a447c8 --- /dev/null +++ b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenPackageTask.java @@ -0,0 +1,125 @@ +package com.badlogic.gdx.jnigen.gradle; + +import com.badlogic.gdx.jnigen.BuildConfig; +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.RobovmBuildConfig; +import com.badlogic.gdx.jnigen.build.packaging.Packager; +import com.badlogic.gdx.jnigen.commons.AndroidABI; +import com.badlogic.gdx.jnigen.commons.Platform; +import org.gradle.api.DefaultTask; +import org.gradle.api.Task; +import org.gradle.api.specs.Spec; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.TaskAction; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Desu + */ +public class JnigenPackageTask extends DefaultTask { + + private static final Logger log = LoggerFactory.getLogger(JnigenPackageTask.class); + + private JnigenExtension ext; + + private Platform[] platformsToPackage; + @Nullable + private AndroidABI targetAndroidABI; + + private BuildConfig buildConfig; + private Packager packager; + + @Inject + public JnigenPackageTask (JnigenExtension ext) { + this.ext = ext; + + setGroup("jnigen"); + setDescription("Packages all native libraries into a single Platform artifact"); + } + + public void configure (AndroidABI androidABIPackageOverride, Platform... platformsToPackage) { + this.platformsToPackage = platformsToPackage; + this.targetAndroidABI = androidABIPackageOverride; + + + RobovmBuildConfig robovmBuildConfig = new RobovmBuildConfig(); + if (ext.robovm != null) { + ext.robovm.execute(robovmBuildConfig); + } + + buildConfig = new BuildConfig(ext.sharedLibName, ext.subProjectDir + ext.temporaryDir, ext.subProjectDir + ext.libsDir, ext.subProjectDir + ext.jniDir, robovmBuildConfig, new FileDescriptor(ext.subProjectDir)); + buildConfig.targetJarBaseName = ext.sharedLibName; + + packager = new Packager(); + + //add outputs manually here. This is only really required for publishing support + List outputs = getBuildOutputs(); + + for (String output : outputs) { + getOutputs().file(ext.subProjectDir + ext.libsDir + "/" + output); + } + + getOutputs().upToDateWhen(new Spec() { + @Override + public boolean isSatisfiedBy (Task task) { + return false; + } + }); + } + + private List getBuildOutputs () { + ArrayList outputs = new ArrayList<>(); + + for (Platform platform : platformsToPackage) { + if (platform == Platform.Android) { + if (targetAndroidABI != null) { + outputs.add(buildConfig.targetJarBaseName + "-natives-" + targetAndroidABI.getAbiString() + ".jar"); + } else { + log.trace("No target AndroidABI set"); + } + } else { + switch (platform) { + case Desktop: + outputs.add(buildConfig.targetJarBaseName + "-natives-" + "desktop" + ".jar"); + + break; + case IOS: + outputs.add(buildConfig.targetJarBaseName + "-natives-" + "ios" + ".jar"); + break; + } + } + } + + return outputs; + } + + + @TaskAction + public void run () { + + for (Platform platform : platformsToPackage) { + if (platform == Platform.Android && targetAndroidABI != null) { + List filteredForABI = ext.targets.stream() + .filter(target -> target.os.getPlatform() == Platform.Android && target.getTargetAndroidABI() == targetAndroidABI) + .collect(Collectors.toList()); + packager.packagePlatform(platform, buildConfig, filteredForABI); + } else { + packager.packagePlatform(platform, buildConfig, ext.targets); + } + } + } + + @Input + public String getSharedLibName () { + return ext.sharedLibName; + } + +} diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenPlugin.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenPlugin.java index 1c72352b..2cf10ad1 100644 --- a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenPlugin.java +++ b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenPlugin.java @@ -1,21 +1,105 @@ package com.badlogic.gdx.jnigen.gradle; +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.commons.AndroidABI; +import com.badlogic.gdx.jnigen.commons.Platform; +import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.invocation.Gradle; +import org.gradle.api.publish.PublicationContainer; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenArtifact; +import org.gradle.api.publish.maven.MavenPublication; + +import java.util.HashSet; +import java.util.Set; /** * @author Desu */ public class JnigenPlugin implements Plugin { - @Override - public void apply(Project project) { - JnigenExtension ext = project.getExtensions().create("jnigen", JnigenExtension.class, project); - JnigenTask jnigen = project.getTasks().create("jnigen", JnigenTask.class, ext); - jnigen.dependsOn(project.getTasks().getByName("classes")); + @Override + public void apply (Project project) { + JnigenExtension ext = project.getExtensions().create("jnigen", JnigenExtension.class, project); + JnigenTask jnigen = project.getTasks().create("jnigen", JnigenTask.class, ext); + jnigen.dependsOn(project.getTasks().getByName("classes")); + + project.getTasks().create("jnigenGHA", JnigenGithubActionBuilderTask.class, ext); + + project.getGradle().projectsEvaluated(new Action() { + @Override + public void execute (Gradle gradle) { + Set platformsConfigured = new HashSet<>(); + + for (BuildTarget target : ext.targets) { + platformsConfigured.add(target.os.getPlatform()); + } + + int size = platformsConfigured.size(); + Platform[] platformsToBuild = new Platform[size]; + int index = 0; + for (Platform platform : platformsConfigured) { + platformsToBuild[index++] = platform; + } + + JnigenPackageTask jnigenPackageTask = project.getTasks().create("jnigenPackageAll", JnigenPackageTask.class, ext); + jnigenPackageTask.configure(null, platformsToBuild); + + + //Maven publish + + if (!project.getPlugins().hasPlugin("maven-publish")) { + return; + } + + project.getPlugins().apply("maven-publish"); + project.getExtensions().configure(PublishingExtension.class, new Action() { + @Override + public void execute (PublishingExtension publishingExtension) { + publishingExtension.publications(new Action() { + @Override + public void execute (PublicationContainer publications) { + + publications.create("jniPlatform", MavenPublication.class, new Action() { + @Override + public void execute (MavenPublication mavenPublication) { + String archiveBaseName = jnigenPackageTask.getSharedLibName(); + mavenPublication.setArtifactId(archiveBaseName + "-platform"); + + safeAddMavenPublication("jnigenPackageAllDesktop", project, mavenPublication, "natives-desktop"); + + for (AndroidABI abi : AndroidABI.values()) { + safeAddMavenPublication("jnigenPackageAndroid_" + abi.getAbiString(), project, mavenPublication, "natives-" + abi.getAbiString()); + } + + safeAddMavenPublication("jnigenPackageAllIOS", project, mavenPublication, "natives-ios"); + + } + }); + + } + }); + } + }); + } + }); + } + + private void safeAddMavenPublication (String jniPackageTask, Project project, MavenPublication mavenPublication, String classifier) { + try { + JnigenPackageTask packageByName = (JnigenPackageTask)project.getTasks().getByName(jniPackageTask); + mavenPublication.artifact(packageByName.getOutputs().getFiles().getSingleFile(), new Action() { + @Override + public void execute (MavenArtifact mavenArtifact) { + mavenArtifact.setClassifier(classifier); + } + }); + } catch (Exception e) { + } + + } - project.getTasks().create("jnigenBuild", JnigenBuildTask.class, ext); - project.getTasks().create("jnigenGenerateBindings", JnigenGenerateBindingsTask.class, ext); - } } diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenTask.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenTask.java index bd12bff0..d84acf27 100644 --- a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenTask.java +++ b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/JnigenTask.java @@ -1,61 +1,59 @@ package com.badlogic.gdx.jnigen.gradle; -import javax.inject.Inject; - +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.NativeCodeGenerator; +import com.badlogic.gdx.jnigen.build.PlatformBuilder; import org.gradle.api.DefaultTask; import org.gradle.api.tasks.TaskAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.badlogic.gdx.jnigen.AntScriptGenerator; -import com.badlogic.gdx.jnigen.BuildConfig; -import com.badlogic.gdx.jnigen.BuildTarget; -import com.badlogic.gdx.jnigen.NativeCodeGenerator; - +import javax.inject.Inject; import java.util.Arrays; /** * @author Desu */ public class JnigenTask extends DefaultTask { - private static final Logger log = LoggerFactory.getLogger(JnigenTask.class); - - JnigenExtension ext; - - @Inject - public JnigenTask(JnigenExtension ext) { - this.ext = ext; - - setGroup("jnigen"); - setDescription("Generates jnigen native code files and build scripts."); - dependsOn(ext.nativeCodeGeneratorConfig.sourceSet.getRuntimeClasspath()); - } - - @TaskAction - public void run() { - if (ext.sharedLibName == null) { - log.error("sharedLibName must be defined"); - throw new RuntimeException("sharedLibName must be defined"); - } - - log.debug("subProjectDir " + ext.subProjectDir); - log.debug("sharedLibName " + ext.sharedLibName); - log.debug("nativeCodeGeneratorConfig " + ext.nativeCodeGeneratorConfig); - - Arrays.stream(ext.nativeCodeGeneratorConfig.getSourceDirs()) - .map(s -> s.startsWith(ext.subProjectDir) ? s : ext.subProjectDir + s) - .forEach(s -> { - try { - new NativeCodeGenerator().generate(s, - ext.nativeCodeGeneratorConfig.sourceSet.getRuntimeClasspath().getAsPath(), ext.subProjectDir + ext.nativeCodeGeneratorConfig.jniDir, - ext.nativeCodeGeneratorConfig.includes, ext.nativeCodeGeneratorConfig.excludes); - } catch (Exception e) { - throw new RuntimeException("NativeCodeGenerator threw exception", e); - } - }); - - BuildConfig buildConfig = new BuildConfig(ext.sharedLibName, ext.temporaryDir, ext.libsDir, - ext.subProjectDir + ext.jniDir); - new AntScriptGenerator().generate(buildConfig, ext.targets.toArray(new BuildTarget[0])); - } + + private static final Logger log = LoggerFactory.getLogger(JnigenTask.class); + + JnigenExtension ext; + + @Inject + public JnigenTask (JnigenExtension ext) { + this.ext = ext; + + setGroup("jnigen"); + setDescription("Generates jnigen native code files and build scripts."); + dependsOn(ext.nativeCodeGeneratorConfig.sourceSet.getRuntimeClasspath()); + } + + @TaskAction + public void run () { + if (ext.sharedLibName == null) { + log.error("sharedLibName must be defined"); + throw new RuntimeException("sharedLibName must be defined"); + } + + log.debug("subProjectDir {}", ext.subProjectDir); + log.debug("sharedLibName {}", ext.sharedLibName); + log.debug("nativeCodeGeneratorConfig {}", ext.nativeCodeGeneratorConfig); + + FileDescriptor headerDestination = new FileDescriptor(ext.subProjectDir).child(ext.jniDir); + + PlatformBuilder.copyHeaders(headerDestination); + + Arrays.stream(ext.nativeCodeGeneratorConfig.getSourceDirs()) + .map(s -> s.startsWith(ext.subProjectDir) ? s : ext.subProjectDir + s) + .forEach(s -> { + try { + new NativeCodeGenerator().generate(s, + ext.nativeCodeGeneratorConfig.sourceSet.getRuntimeClasspath().getAsPath(), ext.subProjectDir + ext.jniDir, + ext.nativeCodeGeneratorConfig.includes, ext.nativeCodeGeneratorConfig.excludes); + } catch (Exception e) { + throw new RuntimeException("NativeCodeGenerator threw exception", e); + } + }); + } } diff --git a/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/gha/GHABuilder.java b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/gha/GHABuilder.java new file mode 100644 index 00000000..87842538 --- /dev/null +++ b/compiling/gdx-jnigen-gradle/src/main/java/com/badlogic/gdx/jnigen/gradle/gha/GHABuilder.java @@ -0,0 +1,145 @@ +package com.badlogic.gdx.jnigen.gradle.gha; + +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.commons.CompilerABIType; +import com.badlogic.gdx.jnigen.commons.Os; +import com.badlogic.gdx.jnigen.gradle.JnigenExtension; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +public class GHABuilder { + + + public GHABuilder (File outputFile, JnigenExtension ext) throws IOException { + + FileWriter writer = new FileWriter(outputFile); + + String template = new FileDescriptor("com.badlogic.gdx.jnigen.gradle/gha-template.yaml", + FileDescriptor.FileType.Classpath).readString(); + + boolean needsLinux = needsLinux(ext); + boolean needsWindows = needsWindows(ext); + boolean needsMac = needsMac(ext); + + List jobsNeeded = new ArrayList<>(); + List jobs = new ArrayList<>(); + List artifactDownloads = new ArrayList<>(); + + List allTargets = new ArrayList<>(); + allTargets.addAll(ext.targets); + + if (needsLinux) { + String linuxTemplateString = new FileDescriptor("com.badlogic.gdx.jnigen.gradle/linux.yaml", FileDescriptor.FileType.Classpath).readString(); + linuxTemplateString = injectJobs(linuxTemplateString, Os.Linux, allTargets); + jobs.add(linuxTemplateString); + jobsNeeded.add("build-linux"); + + String downloadArtifactTemplate = new FileDescriptor("com.badlogic.gdx.jnigen.gradle/artifactdownload.yaml", FileDescriptor.FileType.Classpath).readString(); + downloadArtifactTemplate = downloadArtifactTemplate.replace("%tasknameos%", "linux"); + artifactDownloads.add(downloadArtifactTemplate); + } + if (needsMac) { + String macTemplateString = new FileDescriptor("com.badlogic.gdx.jnigen.gradle/mac.yaml", FileDescriptor.FileType.Classpath).readString(); + macTemplateString = injectJobs(macTemplateString, Os.MacOsX, allTargets); + jobs.add(macTemplateString); + jobsNeeded.add("build-mac"); + + String downloadArtifactTemplate = new FileDescriptor("com.badlogic.gdx.jnigen.gradle/artifactdownload.yaml", FileDescriptor.FileType.Classpath).readString(); + downloadArtifactTemplate = downloadArtifactTemplate.replace("%tasknameos%", "mac"); + artifactDownloads.add(downloadArtifactTemplate); + } + if (needsWindows) { + String windowsTemplateString = new FileDescriptor("com.badlogic.gdx.jnigen.gradle/windows.yaml", FileDescriptor.FileType.Classpath).readString(); + windowsTemplateString = injectJobs(windowsTemplateString, Os.Windows, allTargets); + jobs.add(windowsTemplateString); + jobsNeeded.add("build-windows"); + + String downloadArtifactTemplate = new FileDescriptor("com.badlogic.gdx.jnigen.gradle/artifactdownload.yaml", FileDescriptor.FileType.Classpath).readString(); + downloadArtifactTemplate = downloadArtifactTemplate.replace("%tasknameos%", "windows"); + artifactDownloads.add(downloadArtifactTemplate); + } + + if (!allTargets.isEmpty()) { + throw new RuntimeException("Left over targets"); + } + + String jobsNeededString = ""; + for (int i = 0; i < jobsNeeded.size(); i++) { + String item = jobsNeeded.get(i); + if (i != 0) { + jobsNeededString += ", " + item; + } else { + jobsNeededString += item; + } + } + + + String buffer = ""; + for (String job : jobs) { + buffer += job + "\n"; + } + + template = template.replace("%jobsMarker%", buffer); + template = template.replace("%jobNeeds%", jobsNeededString); + + buffer = ""; + for (String downloadArtifact: artifactDownloads) { + buffer += downloadArtifact + "\n"; + } + + template = template.replace("%downloadArtifacts%", buffer); + + writer.write(template); + writer.close(); + } + + private String injectJobs (String template, Os os, List targets) { + //Inject base job + String jniGenTasks = "\n" + + " - name: Initialize jnigen\n" + + " run: ./gradlew jnigen"; + + + HashSet osToBuild = new HashSet<>(); + + Iterator iterator = targets.iterator(); + while (iterator.hasNext()) { + BuildTarget next = iterator.next(); + if (next.canBuildOnHost(os)) { + osToBuild.add(next.os); + iterator.remove(); + } + } + + String buffer = ""; + for (Os targetOses : osToBuild) { + buffer += " jnigenBuildAll" + targetOses; + } + + jniGenTasks += "\n" + + " - name: Build natives \n" + + " run: ./gradlew" + buffer; + + return template.replace("%steps%", jniGenTasks); + } + + private boolean needsMac (JnigenExtension ext) { + return ext.targets.stream().anyMatch(it -> it.os == Os.MacOsX || it.os == Os.IOS); + } + + private boolean needsWindows (JnigenExtension ext) { + return ext.targets.stream().anyMatch(it -> it.os == Os.Windows && it.compilerABIType == CompilerABIType.MSVC); + } + + private boolean needsLinux (JnigenExtension ext) { + return ext.targets.stream().anyMatch(it -> it.os == Os.Android || it.os == Os.Linux || (it.os == Os.Windows && it.compilerABIType == CompilerABIType.GCC_CLANG)); + } + +} diff --git a/compiling/gdx-jnigen/build.gradle b/compiling/gdx-jnigen/build.gradle index c58ff6c5..92801925 100644 --- a/compiling/gdx-jnigen/build.gradle +++ b/compiling/gdx-jnigen/build.gradle @@ -1,6 +1,10 @@ dependencies { implementation "com.github.javaparser:javaparser-core:3.14.14" api project(':jnigen-commons') + + api 'ch.qos.logback:logback-core:1.4.14' + api 'ch.qos.logback:logback-classic:1.4.12' + api "org.slf4j:slf4j-api:2.0.16" } eclipse { diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AndroidNdkScriptGenerator.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AndroidNdkScriptGenerator.java deleted file mode 100644 index 753b6eb9..00000000 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AndroidNdkScriptGenerator.java +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************* - * Copyright 2011 See AUTHORS file. - * - * 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 com.badlogic.gdx.jnigen; - -import java.util.ArrayList; - -import com.badlogic.gdx.jnigen.FileDescriptor.FileType; -import com.badlogic.gdx.jnigen.commons.Os; - -public class AndroidNdkScriptGenerator { - public void generate (BuildConfig config, BuildTarget target) { - if (target.os != Os.Android) throw new IllegalArgumentException("target os must be Android"); - - // create all the directories for outputing object files, shared libs and natives jar as well as build scripts. - if (!config.libsDir.exists()) { - if (!config.libsDir.mkdirs()) - throw new RuntimeException("Couldn't create directory for shared library files in '" + config.libsDir + "'"); - } - if (!config.jniDir.exists()) { - if (!config.jniDir.mkdirs()) - throw new RuntimeException("Couldn't create native code directory '" + config.jniDir + "'"); - } - - ArrayList files = new ArrayList(); - - int idx = 0; - String[] includes = new String[target.cIncludes.length + target.cppIncludes.length]; - for (String include : target.cIncludes) - includes[idx++] = config.jniDir + "/" + include; - for (String include : target.cppIncludes) - includes[idx++] = config.jniDir + "/" + include; - - idx = 0; - String[] excludes = new String[target.cExcludes.length + target.cppExcludes.length + 1]; - for (String exclude : target.cExcludes) - excludes[idx++] = config.jniDir + "/" + exclude; - for (String exclude : target.cppExcludes) - excludes[idx++] = config.jniDir + "/" + exclude; - //Include jniDir since ** does not match absolute files that start with / - excludes[idx] = config.jniDir + "/**/target/*"; - - gatherSourceFiles(config.jniDir, includes, excludes, files); - - // create Application.mk file - String template = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/Application.mk.template", - FileType.Classpath).readString(); - - template = template.replace("%androidABIs%", String.join(" ", target.androidABIs)); - for(String extra : target.androidApplicationMk) - template += "\n" + extra; - - config.jniDir.child("Application.mk").writeString(template, false); - - // create Android.mk file - template = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/Android.mk.template", FileType.Classpath) - .readString(); - - StringBuilder srcFiles = new StringBuilder(); - for (int i = 0; i < files.size(); i++) { - if (i > 0) srcFiles.append("\t"); - srcFiles.append(files.get(i).path().replace('\\', '/').replace(config.jniDir.toString() + "/", "")); - if (i < files.size() - 1) - srcFiles.append("\\\n"); - else - srcFiles.append("\n"); - } - - StringBuilder headerDirs = new StringBuilder(); - for (String headerDir : target.headerDirs) { - headerDirs.append(headerDir); - headerDirs.append(" "); - } - - template = template.replace("%sharedLibName%", config.sharedLibName); - template = template.replace("%headerDirs%", headerDirs); - template = template.replace("%cFlags%", target.cFlags); - template = template.replace("%cppFlags%", target.cppFlags); - template = template.replace("%linkerFlags%", target.linkerFlags); - template = template.replace("%srcFiles%", srcFiles); - for(String extra : target.androidAndroidMk) - template += "\n" + extra; - - config.jniDir.child("Android.mk").writeString(template, false); - } - - private void gatherSourceFiles (FileDescriptor file, String[] includes, String[] excludes, ArrayList files) { - String fileName = file.path().replace('\\', '/'); - if (file.isDirectory()) { - if (match(fileName, excludes)) return; - for (FileDescriptor child : file.list()) { - gatherSourceFiles(child, includes, excludes, files); - } - } else { - if (match(fileName, includes) && !match(fileName, excludes)) files.add(file); - } - } - - private boolean match (String file, String[] patterns) { - return new AntPathMatcher().match(file, patterns); - } -} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AntPathMatcher.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AntPathMatcher.java index 80535720..9bb9ad9e 100644 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AntPathMatcher.java +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AntPathMatcher.java @@ -49,7 +49,7 @@ * @since 16.07.2003 */ public class AntPathMatcher { - public boolean isPattern (String str) { + public static boolean isPattern (String str) { return (str.indexOf('*') != -1 || str.indexOf('?') != -1); } @@ -71,7 +71,7 @@ public static String[] tokenizeToStringArray (String str, String delimiters, boo return tokens.toArray(new String[tokens.size()]); } - public boolean match (String file, String[] patterns) { + public static boolean match (String file, String[] patterns) { if (patterns == null || patterns.length == 0) return true; for (String pattern : patterns) { if (match(pattern, file)) { @@ -81,7 +81,7 @@ public boolean match (String file, String[] patterns) { return false; } - public boolean match (String pattern, String str) { + public static boolean match (String pattern, String str) { if (str.startsWith("/") != pattern.startsWith("/")) { return false; } @@ -200,7 +200,7 @@ public boolean match (String pattern, String str) { * @param pattern pattern to match against. Must not be null. * @param str string which must be matched against the pattern. Must not be null. * @return true if the string matches against the pattern, or false otherwise. */ - private boolean matchStrings (String pattern, String str) { + private static boolean matchStrings (String pattern, String str) { char[] patArr = pattern.toCharArray(); char[] strArr = str.toCharArray(); int patIdxStart = 0; diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AntScriptGenerator.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AntScriptGenerator.java deleted file mode 100644 index 4d6ca5c9..00000000 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/AntScriptGenerator.java +++ /dev/null @@ -1,248 +0,0 @@ -/******************************************************************************* - * Copyright 2011 See AUTHORS file. - * - * 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 com.badlogic.gdx.jnigen; - -import java.util.ArrayList; - -import com.badlogic.gdx.jnigen.FileDescriptor.FileType; -import com.badlogic.gdx.jnigen.commons.Os; - -/**

Generates Ant scripts for multiple native build targets based on the given {@link BuildConfig}. - * - * For each build target, an Ant build script is created that will compile C/C++ files to a shared library for a specific - * platform. A master build script is generated that will execute the build scripts for each platform and bundles their shared - * libraries into a Jar file containing all shared libraries for all desktop platform targets, and armeabi/ and armeabi-v7a/ - * folders containing the shard libraries for Android. The scripts can be executed from the command line or via the - * {@link BuildExecutor}. The resulting shared libraries can be loaded with the SharedLibraryLoader which will load - * the correct shared library from the natives jar/arm folders based on the platform the application is running on

- * - * A common use case looks like this: - * - *
- * BuildTarget win32 = BuildTarget.newDefaultTarget(Os.Windows, Architecture.Bitness._32);
- * BuildTarget win64 = BuildTarget.newDefaultTarget(Os.Windows, Architecture.Bitness._64);
- * BuildTarget linux32 = BuildTarget.newDefaultTarget(Os.Linux, Architecture.Bitness._32);
- * BuildTarget linux64 = BuildTarget.newDefaultTarget(Os.Linux, Architecture.Bitness._64);
- * BuildTarget mac = BuildTarget.newDefaultTarget(Os.MacOsX, Architecture.Bitness._32);
- * BuildTarget android = BuildTarget.newDefaultTarget(Os.Android, Architecture.Bitness._32);
- * BuildConfig config = new BuildConfig("mysharedlibrary");
- * 
- * new AntScriptGenerator().generate(config, win32, win64, linux32, linux64, mac, android);
- * BuildExecutor.executeAnt("jni/build.xml", "clean", "all", "-v");
- * 
- * // assuming the natives jar is on the classpath of the application 
- * new SharedLibraryLoader().load("mysharedlibrary)
- * 
- * - *

This will create the build scripts and execute the build of the shared libraries for each platform, provided that the compilers - * are available on the system. Mac OS X might have to be treated separately as there are no cross-compilers for it.

- * - *

The generator will also copy the necessary JNI headers to the jni/jni-headers folder for Windows, Linux and Mac OS X.

- * - * @author mzechner */ -public class AntScriptGenerator { - /** Creates a master build script and one build script for each target to generated native shared libraries. - * @param config the {@link BuildConfig} - * @param targets list of {@link BuildTarget} instances */ - public void generate (BuildConfig config, BuildTarget... targets) { - // create all the directories for outputing object files, shared libs and natives jar as well as build scripts. - if (!config.libsDir.exists()) { - if (!config.libsDir.mkdirs()) - throw new RuntimeException("Couldn't create directory for shared library files in '" + config.libsDir + "'"); - } - if (!config.jniDir.exists()) { - if (!config.jniDir.mkdirs()) - throw new RuntimeException("Couldn't create native code directory '" + config.jniDir + "'"); - } - - // copy jni headers - copyJniHeaders(config.jniDir.path()); - if (!config.jniDir.child("jnigen.h").exists()) { - new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/jnigen.h", FileType.Classpath).copyTo( - config.jniDir.child("jnigen.h")); - } - - // copy memcpy_wrap.c, needed if your build platform uses the latest glibc, e.g. Ubuntu 12.10 - if (!config.jniDir.child("memcpy_wrap.c").exists()) { - new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/memcpy_wrap.c", FileType.Classpath).copyTo(config.jniDir - .child("memcpy_wrap.c")); - } - - ArrayList buildFiles = new ArrayList(); - ArrayList libsDirs = new ArrayList(); - ArrayList sharedLibFiles = new ArrayList(); - - // generate an Ant build script for each target - for (BuildTarget target : targets) { - String buildFile = generateBuildTargetTemplate(config, target); - FileDescriptor libsDir = new FileDescriptor(getLibsDirectory(config, target)); - - if (!libsDir.exists()) { - if (!libsDir.mkdirs()) throw new RuntimeException("Couldn't create libs directory '" + libsDir + "'"); - } - - String buildFileName = target.getBuildFilename(); - config.jniDir.child(buildFileName).writeString(buildFile, false); - System.out.println("Wrote target '" + target.os + target.architecture.toSuffix() + target.bitness.toSuffix() + "' build script '" - + config.jniDir.child(buildFileName) + "'"); - if (target.os == Os.IOS) { - byte[] plist = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/Info.plist.template", FileType.Classpath) - .readBytes(); - config.jniDir.child("Info.plist").writeBytes(plist, false); - } - - if (!target.excludeFromMasterBuildFile) { - if (target.os != Os.MacOsX && target.os != Os.IOS) { - buildFiles.add(buildFileName); - } - - String sharedLibFilename = target.getSharedLibFilename(config.sharedLibName); - - if (target.os != Os.Android && target.os != Os.IOS) { - sharedLibFiles.add(sharedLibFilename); - libsDirs.add("../" + libsDir.path().replace('\\', '/')); - } - } - } - - // generate the master build script - String template = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/build.xml.template", FileType.Classpath) - .readString(); - StringBuilder clean = new StringBuilder(); - StringBuilder compile = new StringBuilder(); - StringBuilder pack = new StringBuilder(); - - for (int i = 0; i < buildFiles.size(); i++) { - clean.append("\t\t\n"); - compile.append("\t\t\n"); - } - for (int i = 0; i < libsDirs.size(); i++) { - pack.append("\t\t\t\n"); - } - - if (config.sharedLibs != null) { - for (String sharedLib : config.sharedLibs) { - pack.append("\t\t\t\n"); - } - } - - template = template.replace("%projectName%", config.sharedLibName + "-natives"); - template = template.replace("", clean.toString()); - template = template.replace("", compile.toString()); - template = template.replace("%packFile%", "../" + config.libsDir.path().replace('\\', '/') + "/" + config.sharedLibName - + "-natives.jar"); - template = template.replace("", pack); - - config.jniDir.child("build.xml").writeString(template, false); - System.out.println("Wrote master build script '" + config.jniDir.child("build.xml") + "'"); - } - - private void copyJniHeaders (String jniDir) { - final String pack = "com/badlogic/gdx/jnigen/resources/headers"; - String files[] = {"classfile_constants.h", "jawt.h", "jdwpTransport.h", "jni.h", "linux/jawt_md.h", "linux/jni_md.h", - "mac/jni_md.h", "win32/jawt_md.h", "win32/jni_md.h"}; - - for (String file : files) { - new FileDescriptor(pack, FileType.Classpath).child(file).copyTo( - new FileDescriptor(jniDir).child("jni-headers").child(file)); - } - } - - public static String getLibsDirectory (BuildConfig config, BuildTarget target) { - return config.libsDir.child(target.getTargetFolder()).path().replace('\\', '/'); - } - - private String generateBuildTargetTemplate (BuildConfig config, BuildTarget target) { - // special case for android - if (target.os == Os.Android) { - new AndroidNdkScriptGenerator().generate(config, target); - String template = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/build-android.xml.template", - FileType.Classpath).readString(); - template = template.replace("%precompile%", target.preCompileTask == null ? "" : target.preCompileTask); - template = template.replace("%postcompile%", target.postCompileTask == null ? "" : target.postCompileTask); - return template; - } - - // read template file from resources - String template = null; - if (target.os == Os.IOS) { - template = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/build-ios.xml.template", FileType.Classpath) - .readString(); - } else { - template = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/build-target.xml.template", FileType.Classpath) - .readString(); - } - - // generate shared lib filename and jni platform headers directory name - String libName = target.getSharedLibFilename(config.sharedLibName); - - // generate include and exclude fileset Ant description for C/C++ - // append memcpy_wrap.c to list of files to be build - StringBuilder cIncludes = new StringBuilder(); - cIncludes.append("\t\t\n"); - for (String cInclude : target.cIncludes) { - cIncludes.append("\t\t\n"); - } - StringBuilder cppIncludes = new StringBuilder(); - for (String cppInclude : target.cppIncludes) { - cppIncludes.append("\t\t\n"); - } - StringBuilder cExcludes = new StringBuilder(); - for (String cExclude : target.cExcludes) { - cExcludes.append("\t\t\n"); - } - StringBuilder cppExcludes = new StringBuilder(); - for (String cppExclude : target.cppExcludes) { - cppExcludes.append("\t\t\n"); - } - - // generate C/C++ header directories - StringBuilder headerDirs = new StringBuilder(); - for (String headerDir : target.headerDirs) { - headerDirs.append("\t\t\t\n"); - } - - // replace template vars with proper values - template = template.replace("%projectName%", config.sharedLibName + "-" + target.os + "-" + target.architecture.toSuffix() + target.bitness.name().substring(1)); - template = template.replace("%buildDir%", config.buildDir.child(target.getTargetFolder()).path().replace('\\', '/')); - template = template.replace("%libsDir%", "../" + getLibsDirectory(config, target)); - template = template.replace("%libName%", libName); - template = template.replace("%xcframeworkName%", config.sharedLibName); - template = template.replace("%xcframeworkBundleIdentifier%", target.xcframeworkBundleIdentifier == null ? ("gdx.jnigen." + config.sharedLibName) : target.xcframeworkBundleIdentifier); - template = template.replace("%minIOSVersion%", target.minIOSVersion); - template = template.replace("%jniPlatform%", target.os.getJniPlatform()); - template = template.replace("%cCompiler%", target.cCompiler); - template = template.replace("%cppCompiler%", target.cppCompiler); - template = template.replace("%archiver%", target.archiver); - template = template.replace("%compilerPrefix%", target.compilerPrefix); - template = template.replace("%compilerSuffix%", target.compilerSuffix); - template = template.replace("%cFlags%", target.cFlags); - template = template.replace("%cppFlags%", target.cppFlags); - template = template.replace("%linkerFlags%", target.linkerFlags); - template = template.replace("%archiverFlags%", target.archiverFlags); - template = template.replace("%libraries%", target.libraries); - template = template.replace("%cIncludes%", cIncludes.toString().trim()); - template = template.replace("%cExcludes%", cExcludes.toString().trim()); - template = template.replace("%cppIncludes%", cppIncludes.toString().trim()); - template = template.replace("%cppExcludes%", cppExcludes.toString().trim()); - template = template.replace("%headerDirs%", headerDirs.toString().trim()); - template = template.replace("%precompile%", target.preCompileTask == null ? "" : target.preCompileTask); - template = template.replace("%postcompile%", target.postCompileTask == null ? "" : target.postCompileTask); - - return template; - } -} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildConfig.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildConfig.java index 900c8d21..0775fa57 100644 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildConfig.java +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildConfig.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2011 See AUTHORS file. - * + * * 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. @@ -16,45 +16,108 @@ package com.badlogic.gdx.jnigen; -/**

Specifies the global properties of a native build.

- * +/** + *

Specifies the global properties of a native build.

+ *

* The shared library name will be used to generate the shared libraries. The build directory is used to store the object files * during compilation for each {@link BuildTarget}. It is relative to the jni directory which houses the C/C++ source code. The * libs directory is the final output directory where the natives jar and arm shared libraries will be written to. - * - * Used with {@link AntScriptGenerator} to generate the build scripts for build targets. - * @author mzechner */ + *

+ * + * @author mzechner + */ public class BuildConfig { - /** the name of the shared library, without prefix or suffix, e.g. 'gdx', 'bullet' **/ - public final String sharedLibName; - /** the directory to put the object files in **/ - public final FileDescriptor buildDir; - /** the directory to put the shared libraries and natives jar file in **/ - public final FileDescriptor libsDir; - /** the directory containing the native code **/ - public final FileDescriptor jniDir; - /** additional shared library files to be packed into the natives jar, relative to the jni dir **/ - public String[] sharedLibs; - - /** Creates a new BuildConfig. The build directory, the libs directory and the jni directory are assumed to be "target", "libs" - * and "jni". All paths are relative to the application's working directory. - * @param sharedLibName the shared library name, without prefix or suffix, e.g. 'gdx', 'bullet' */ - public BuildConfig (String sharedLibName) { - this.sharedLibName = sharedLibName; - this.buildDir = new FileDescriptor("target"); - this.libsDir = new FileDescriptor("libs"); - this.jniDir = new FileDescriptor("jni"); - } - - /** Creates a new BuildConfig. All paths are relative to the application's working directory. - * @param sharedLibName the shared library name, without prefix or suffix, e.g. 'gdx', 'bullet' - * @param temporaryDir - * @param libsDir - * @param jniDir */ - public BuildConfig (String sharedLibName, String temporaryDir, String libsDir, String jniDir) { - this.sharedLibName = sharedLibName; - this.buildDir = new FileDescriptor(temporaryDir); - this.libsDir = new FileDescriptor(libsDir); - this.jniDir = new FileDescriptor(jniDir); - } + /** + * the name of the shared library, without prefix or suffix, e.g. 'gdx', 'bullet' + **/ + public final String sharedLibName; + /** + * the directory to put the object files in + **/ + public final FileDescriptor buildDir; + /** + * the directory to put the shared libraries and natives jar file in + **/ + public final FileDescriptor libsDir; + /** + * the directory containing the native code + **/ + public final FileDescriptor jniDir; + + /** + * Directory in the root of the project, used for relative offsets + */ + public final FileDescriptor projectDir; + + /** + * additional shared library files to be packed into the natives jar, relative to the jni dir + **/ + public String[] sharedLibs; + + /** + * Customize the base name for the target packaged jars. targetJarName-classifier.jar + * Usually this is just left alone and passed through gradle + */ + public String targetJarBaseName; + + public RobovmBuildConfig robovmBuildConfig; + + public boolean multiThreadedCompile = true; + + /** + * Creates a new BuildConfig. The build directory, the libs directory and the jni directory are assumed to be "target", "libs" + * and "jni". All paths are relative to the application's working directory. + * + * @param sharedLibName the shared library name, without prefix or suffix, e.g. 'gdx', 'bullet' + */ + public BuildConfig (String sharedLibName, FileDescriptor projectDir) { + this.sharedLibName = sharedLibName; + this.buildDir = new FileDescriptor("build/jnigen/build"); + this.libsDir = new FileDescriptor("build/jnigen/libs"); + this.jniDir = new FileDescriptor("build/jnigen/jni"); + this.projectDir = projectDir; + this.robovmBuildConfig = new RobovmBuildConfig(); + this.targetJarBaseName = sharedLibName; + } + + /** + * Creates a new BuildConfig. All paths are relative to the application's working directory. + * + * @param sharedLibName the shared library name, without prefix or suffix, e.g. 'gdx', 'bullet' + * @param temporaryDir + * @param libsDir + * @param jniDir + */ + public BuildConfig (String sharedLibName, String temporaryDir, String libsDir, String jniDir, FileDescriptor projectDir) { + this.sharedLibName = sharedLibName; + this.buildDir = new FileDescriptor(temporaryDir); + this.libsDir = new FileDescriptor(libsDir); + this.jniDir = new FileDescriptor(jniDir); + this.projectDir = projectDir; + this.robovmBuildConfig = new RobovmBuildConfig(); + targetJarBaseName = sharedLibName; + } + + /** + * Creates a new BuildConfig. All paths are relative to the application's working directory. + * + * @param sharedLibName the shared library name, without prefix or suffix, e.g. 'gdx', 'bullet' + * @param temporaryDir + * @param libsDir + * @param jniDir + * @param robovmBuildConfig + */ + public BuildConfig (String sharedLibName, String temporaryDir, String libsDir, String jniDir, RobovmBuildConfig robovmBuildConfig, FileDescriptor projectDir) { + this.sharedLibName = sharedLibName; + this.buildDir = new FileDescriptor(temporaryDir); + this.libsDir = new FileDescriptor(libsDir); + this.jniDir = new FileDescriptor(jniDir); + this.projectDir = projectDir; + this.robovmBuildConfig = robovmBuildConfig; + this.targetJarBaseName = sharedLibName; + } + + public void setTargetJarBaseName (String targetJarBaseName) { + this.targetJarBaseName = targetJarBaseName; + } } diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildExecutor.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildExecutor.java deleted file mode 100644 index edde3015..00000000 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildExecutor.java +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * Copyright 2011 See AUTHORS file. - * - * 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 com.badlogic.gdx.jnigen; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** Executes an ant script and its targets or an Android NDK build. See {@link AntScriptGenerator}. - * @author mzechner */ -public class BuildExecutor { - /** Execute the Ant script file with the given parameters. - * @param buildFile - * @param params - * @return whether the Ant succeeded */ - public static boolean executeAnt (String buildFile, String... params) { - FileDescriptor build = new FileDescriptor(buildFile); - String ant = System.getProperty("os.name").contains("Windows") ? "ant.bat" : "ant"; - - List command = new ArrayList<>(); - command.add(ant); - command.add("-f"); - command.add(build.file.getAbsolutePath()); - command.addAll(Arrays.asList(params)); - - String[] args = command.toArray(new String[0]); - System.out.println("Executing '" + command + "'"); - return startProcess(build.parent().file(), args); - } - - /** Execute ndk-build in the given directory - * @param directory */ - public static void executeNdk (String directory) { - FileDescriptor build = new FileDescriptor(directory); - String command = "ndk-build"; - startProcess(build.file(), command); - } - - private static boolean startProcess (File directory, String... command) { - try { - final Process process = new ProcessBuilder(command) - .redirectErrorStream(true) - .directory(directory) - .start(); - - Thread t = new Thread(new Runnable() { - @Override - public void run () { - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - String line = null; - try { - while ((line = reader.readLine()) != null) { - // augment output with java file line references :D - printFileLineNumber(line); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void printFileLineNumber (String line) { - if (line.contains("warning") || line.contains("error")) { - try { - String fileName = getFileName(line); - String error = getError(line); - int lineNumber = getLineNumber(line) - 1; - if (fileName != null && lineNumber >= 0) { - FileDescriptor file = new FileDescriptor(fileName); - if (file.exists()) { - String[] content = file.readString().split("\n"); - if (lineNumber < content.length) { - for (int i = lineNumber; i >= 0; i--) { - String contentLine = content[i]; - if (contentLine.startsWith("//@line:")) { - int javaLineNumber = Integer.parseInt(contentLine.split(":")[1].trim()); - System.out.flush(); - if (line.contains("warning")) { - System.out.println("(" + file.nameWithoutExtension() + ".java:" - + (javaLineNumber + (lineNumber - i) - 1) + "): " + error + ", original: " + line); - System.out.flush(); - } else { - System.err.println("(" + file.nameWithoutExtension() + ".java:" - + (javaLineNumber + (lineNumber - i) - 1) + "): " + error + ", original: " + line); - System.err.flush(); - } - return; - } - } - } - } else { - System.out.println(line); - } - } - } catch (Throwable t) { - System.out.println(line); - // silent death... - } - } else { - System.out.println(line); - } - } - - private String getFileName (String line) { - Pattern pattern = Pattern.compile("(.*):([0-9])+:[0-9]+:"); - Matcher matcher = pattern.matcher(line); - matcher.find(); - String fileName = matcher.groupCount() >= 2 ? matcher.group(1).trim() : null; - if (fileName == null) return null; - int index = fileName.indexOf(" "); - if (index != -1) - return fileName.substring(index).trim(); - else - return fileName; - } - - private String getError (String line) { - Pattern pattern = Pattern.compile(":[0-9]+:[0-9]+:(.+)"); - Matcher matcher = pattern.matcher(line); - matcher.find(); - return matcher.groupCount() >= 1 ? matcher.group(1).trim() : null; - } - - private int getLineNumber (String line) { - Pattern pattern = Pattern.compile(":([0-9]+):[0-9]+:"); - Matcher matcher = pattern.matcher(line); - matcher.find(); - return matcher.groupCount() >= 1 ? Integer.parseInt(matcher.group(1)) : -1; - } - }); - t.setDaemon(true); - t.start(); - process.waitFor(); - return process.exitValue() == 0; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } -} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildTarget.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildTarget.java index 31896141..185189c7 100644 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildTarget.java +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/BuildTarget.java @@ -17,297 +17,505 @@ package com.badlogic.gdx.jnigen; -import com.badlogic.gdx.jnigen.commons.Architecture; -import com.badlogic.gdx.jnigen.commons.Os; +import com.badlogic.gdx.jnigen.commons.*; -import java.util.function.BooleanSupplier; +import java.io.File; -/** Defines the configuration for building a native shared library for a specific platform. Used with {@link AntScriptGenerator} - * to create Ant build files that invoke the compiler toolchain to create the shared libraries. */ +/** + * Defines the configuration for building a native shared library for a specific platform. + */ public class BuildTarget { - /** the target operating system **/ - public Os os; - /** whether this is a 32-bit, 64-bit or 128-bit build, not used for Android **/ - public Architecture.Bitness bitness; - /** whether this is an x86, ARM or RISC-V build, not used for Android **/ - public Architecture architecture = Architecture.x86; - /** the C files and directories to be included in the build, accepts Ant path format, must not be null **/ - public String[] cIncludes; - /** the C files and directories to be excluded from the build, accepts Ant path format, must not be null **/ - public String[] cExcludes; - /** the C++ files and directories to be included in the build, accepts Ant path format, must not be null **/ - public String[] cppIncludes; - /** the C++ files and directories to be excluded from the build, accepts Ant path format, must not be null **/ - public String[] cppExcludes; - /** the directories containing headers for the build, must not be null **/ - public String[] headerDirs; - /** the compiler to use when compiling c files. Usually gcc or clang, must not be null */ - public String cCompiler = "gcc"; - /** the compiler to use when compiling c++ files. Usually g++ or clang++, must not be null */ - public String cppCompiler = "g++"; - /** the command to use when archiving files. Usually ar, must not be null */ - public String archiver = "ar"; - /** prefix for the compiler (g++, gcc), useful for cross compilation, must not be null **/ - public String compilerPrefix = ""; - /** suffix for the compiler (g++, gcc), useful for cross compilation, must not be null **/ - public String compilerSuffix = ""; - /** the flags passed to the C compiler, must not be null **/ - public String cFlags; - /** the flags passed to the C++ compiler, must not be null **/ - public String cppFlags; - /** the flags passed to the linker, must not be null **/ - public String linkerFlags; - /** the flags passed to the archiver, must not be null **/ - public String archiverFlags = "rcs"; - /** the name of the generated build file for this target, defaults to "build-${target}(64)?.xml", must not be null **/ - public String buildFileName; - /** whether to exclude this build target from the master build file, useful for debugging **/ - public boolean excludeFromMasterBuildFile = false; - /** Ant XML executed in a target before compilation **/ - public String preCompileTask; - /** Ant Xml executed in a target after compilation **/ - public String postCompileTask; - /** the libraries to be linked to the output, specify via e.g. -ldinput -ldxguid etc. **/ - public String libraries; - /** The name used for folders for this specific target. Defaults to "${target}(64)" **/ - public String osFileName; - /** The name used for the library file. This is a full file name, including file extension. Default is platform specific. E.g. - * "lib{sharedLibName}64.so" **/ - public String libName; - /** Condition to check if build this target */ - public BooleanSupplier canBuild = () -> !System.getProperty("os.name").contains("Mac"); - - /** List of ABIs we wish to build for Android. Defaults to all available in current NDK. - * https://developer.android.com/ndk/guides/application_mk#app_abi **/ - public String[] androidABIs = {"all"}; - /** Extra lines which will be added to Android's Android.mk */ - public String[] androidAndroidMk = {}; - /** Extra lines which will be added to Android's Application.mk */ - public String[] androidApplicationMk = {}; - /** ios framework bundle identifier, if null an automatically generated bundle identifier will be used */ - public String xcframeworkBundleIdentifier = null; - /** Minimum supported iOS version, will default to iOS 12*/ - public String minIOSVersion = "12.0"; - - /** Creates a new build target. See members of this class for a description of the parameters. */ - public BuildTarget(Os targetType, Architecture.Bitness bitness, String[] cIncludes, String[] cExcludes, String[] cppIncludes, String[] cppExcludes, String[] headerDirs, String compilerPrefix, String cFlags, String cppFlags, String linkerFlags) { - if (targetType == null) throw new IllegalArgumentException("targetType must not be null"); - if (cIncludes == null) cIncludes = new String[0]; - if (cExcludes == null) cExcludes = new String[0]; - if (cppIncludes == null) cppIncludes = new String[0]; - if (cppExcludes == null) cppExcludes = new String[0]; - if (headerDirs == null) headerDirs = new String[0]; - if (compilerPrefix == null) compilerPrefix = ""; - if (cFlags == null) cFlags = ""; - if (cppFlags == null) cppFlags = ""; - if (linkerFlags == null) linkerFlags = ""; - - this.os = targetType; - this.bitness = bitness; - this.cIncludes = cIncludes; - this.cExcludes = cExcludes; - this.cppIncludes = cppIncludes; - this.cppExcludes = cppExcludes; - this.headerDirs = headerDirs; - this.compilerPrefix = compilerPrefix; - this.cFlags = cFlags; - this.cppFlags = cppFlags; - this.linkerFlags = linkerFlags; - this.libraries = ""; - } - - public String getBuildFilename () { - // Use specified buildFileName if it is user provided - if (buildFileName != null && !buildFileName.isEmpty()) - return buildFileName; - - return "build-" + os.toString().toLowerCase() + architecture.toSuffix() + bitness.name().substring(1) + ".xml"; - } - - public String getSharedLibFilename (String sharedLibName) { - // Use specified libName if it is user provided - if (libName != null && !libName.isEmpty()) - return libName; - - String suffix = os.getLibExtension().isEmpty() ? "" : "." + os.getLibExtension(); - - // generate shared lib prefix and suffix, determine jni platform headers directory - return os.getLibPrefix() + sharedLibName + architecture.toSuffix() + bitness.toSuffix() + suffix; - } - - public String getTargetFolder () { - // Use specified osFileName if it is user provided - if (osFileName != null && !osFileName.isEmpty()) - return osFileName; - - return os.toString().toLowerCase() + architecture.toSuffix() + bitness.name().substring(1); - } - - /** Creates a new default BuildTarget for the given OS, using common default values. - * @deprecated Use {@link #newDefaultTarget(Os, Architecture.Bitness) newDefaultTarget} method.*/ - @Deprecated - public static BuildTarget newDefaultTarget (Os type, boolean is64Bit) { - return newDefaultTarget(type, is64Bit, false); - } - - /** Creates a new default BuildTarget for the given OS, using common default values. */ - public static BuildTarget newDefaultTarget (Os type, Architecture.Bitness bitness) { - return newDefaultTarget(type, bitness, Architecture.x86); - } - - /** Creates a new default BuildTarget for the given OS, using common default values. - * @deprecated Use {@link #newDefaultTarget(Os, Architecture.Bitness, Architecture) newDefaultTarget} method.*/ - @Deprecated - public static BuildTarget newDefaultTarget (Os type, boolean is64Bit, boolean isARM) { - return newDefaultTarget(type, is64Bit ? Architecture.Bitness._64 : Architecture.Bitness._32, isARM ? Architecture.ARM : Architecture.x86); - } - - /** Creates a new default BuildTarget for the given OS, using common default values. */ - public static BuildTarget newDefaultTarget (Os type, Architecture.Bitness bitness, Architecture architecture) { - if (type == Os.Windows && architecture == Architecture.x86 && bitness == Architecture.Bitness._32) { - // Windows x86 32-Bit - return new BuildTarget(Os.Windows, Architecture.Bitness._32, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "i686-w64-mingw32-", "-c -Wall -O2 -mfpmath=sse -msse2 -fmessage-length=0 -m32", - "-c -Wall -O2 -mfpmath=sse -msse2 -fmessage-length=0 -m32", - "-Wl,--kill-at -shared -m32 -static -static-libgcc -static-libstdc++"); - } - - if (type == Os.Windows && architecture == Architecture.x86 && bitness == Architecture.Bitness._64) { - // Windows x86 64-Bit - return new BuildTarget(Os.Windows, Architecture.Bitness._64, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "x86_64-w64-mingw32-", "-c -Wall -O2 -mfpmath=sse -msse2 -fmessage-length=0 -m64", - "-c -Wall -O2 -mfpmath=sse -msse2 -fmessage-length=0 -m64", - "-Wl,--kill-at -shared -static -static-libgcc -static-libstdc++ -m64"); - } - - if (type == Os.Windows && architecture == Architecture.ARM && bitness == Architecture.Bitness._32) { - // Windows ARM 32-Bit - BuildTarget target = new BuildTarget(Os.Windows, Architecture.Bitness._32, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "armv7-w64-mingw32-", "-c -Wall -O2 -fmessage-length=0", - "-c -Wall -O2 -fmessage-length=0", - "-Wl,--kill-at -shared -static -static-libgcc -static-libstdc++"); - target.architecture = Architecture.ARM; - return target; - } - - if (type == Os.Windows && architecture == Architecture.ARM && bitness == Architecture.Bitness._64) { - // Windows ARM 64-Bit - BuildTarget target = new BuildTarget(Os.Windows, Architecture.Bitness._64, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "aarch64-w64-mingw32-", "-c -Wall -O2 -fmessage-length=0", - "-c -Wall -O2 -fmessage-length=0", - "-Wl,--kill-at -shared -static -static-libgcc -static-libstdc++"); - target.architecture = Architecture.ARM; - return target; - } - - if (type == Os.Linux && architecture == Architecture.LOONGARCH && bitness == Architecture.Bitness._64) { - // Linux LoongArch 64-Bit - BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._64, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "loongarch64-unknown-linux-gnu-", "-c -Wall -O2 -fmessage-length=0 -fPIC", - "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared"); - target.architecture = Architecture.LOONGARCH; - return target; - } - - if (type == Os.Linux && architecture == Architecture.RISCV && bitness == Architecture.Bitness._32) { - // Linux RISCV 32-Bit - BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._32, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "riscv32-linux-gnu-", "-c -Wall -O2 -fmessage-length=0 -fPIC", - "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared"); - target.architecture = Architecture.RISCV; - return target; - } - - if (type == Os.Linux && architecture == Architecture.RISCV && bitness == Architecture.Bitness._64) { - // Linux RISCV 64-Bit - BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._64, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "riscv64-linux-gnu-", "-c -Wall -O2 -fmessage-length=0 -fPIC", - "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared"); - target.architecture = Architecture.RISCV; - return target; - } - - if (type == Os.Linux && architecture == Architecture.ARM && bitness == Architecture.Bitness._32) { - // Linux ARM 32-Bit hardfloat - BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._32, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "arm-linux-gnueabihf-", "-c -Wall -O2 -fmessage-length=0 -fPIC", - "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared"); - target.architecture = Architecture.ARM; - return target; - } - - if (type == Os.Linux && architecture == Architecture.ARM && bitness == Architecture.Bitness._64) { - // Linux ARM 64-Bit - BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._64, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "aarch64-linux-gnu-", "-c -Wall -O2 -fmessage-length=0 -fPIC", - "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared"); - target.architecture = Architecture.ARM; - return target; - } - - if (type == Os.Linux && bitness == Architecture.Bitness._32) { - // Linux 32-Bit - return new BuildTarget(Os.Linux, Architecture.Bitness._32, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "", "-c -Wall -O2 -mfpmath=sse -msse -fmessage-length=0 -m32 -fPIC", - "-c -Wall -O2 -mfpmath=sse -msse -fmessage-length=0 -m32 -fPIC", "-shared -m32"); - } - - if (type == Os.Linux && bitness == Architecture.Bitness._64) { - // Linux 64-Bit - return new BuildTarget(Os.Linux, Architecture.Bitness._64, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "", "-c -Wall -O2 -mfpmath=sse -msse -fmessage-length=0 -m64 -fPIC", - "-c -Wall -O2 -mfpmath=sse -msse -fmessage-length=0 -m64 -fPIC", "-shared -m64 -Wl,-wrap,memcpy"); - } - - if (type == Os.MacOsX && bitness == Architecture.Bitness._32) { - throw new RuntimeException("macOS 32-bit not supported"); - } - - if (type == Os.MacOsX && bitness == Architecture.Bitness._64 && architecture == Architecture.ARM) { - // Mac OS aarch64 - BuildTarget mac = new BuildTarget(Os.MacOsX, Architecture.Bitness._64, new String[] {"**/*.c"}, new String[0], - new String[] {"**/*.cpp"}, new String[0], new String[0], "", - "-c -Wall -O2 -arch arm64 -DFIXED_POINT -fmessage-length=0 -fPIC -mmacosx-version-min=10.7 -stdlib=libc++", - "-c -Wall -O2 -arch arm64 -DFIXED_POINT -fmessage-length=0 -fPIC -mmacosx-version-min=10.7 -stdlib=libc++", - "-shared -arch arm64 -mmacosx-version-min=10.7 -stdlib=libc++"); - mac.cCompiler = "clang"; - mac.cppCompiler = "clang++"; - mac.canBuild = () -> System.getProperty("os.name").contains("Mac"); - mac.architecture = Architecture.ARM; - return mac; - } - - if (type == Os.MacOsX && bitness == Architecture.Bitness._64) { - // Mac OS x86_64 - BuildTarget mac = new BuildTarget(Os.MacOsX, Architecture.Bitness._64, new String[] {"**/*.c"}, new String[0], - new String[] {"**/*.cpp"}, new String[0], new String[0], "", - "-c -Wall -O2 -arch x86_64 -DFIXED_POINT -fmessage-length=0 -fPIC -mmacosx-version-min=10.7 -stdlib=libc++", - "-c -Wall -O2 -arch x86_64 -DFIXED_POINT -fmessage-length=0 -fPIC -mmacosx-version-min=10.7 -stdlib=libc++", - "-shared -arch x86_64 -mmacosx-version-min=10.7 -stdlib=libc++"); - mac.cCompiler = "clang"; - mac.cppCompiler = "clang++"; - mac.canBuild = () -> System.getProperty("os.name").contains("Mac"); - return mac; - } - - if (type == Os.Android) { - BuildTarget android = new BuildTarget(Os.Android, Architecture.Bitness._32, new String[] {"**/*.c"}, new String[0], - new String[] {"**/*.cpp"}, new String[0], new String[0], "", "-O2 -Wall -D__ANDROID__", "-O2 -Wall -D__ANDROID__", - "-lm -Wl,-z,max-page-size=0x4000"); - return android; - } - - if(type == Os.IOS) { - // iOS, x86_64 simulator, armv7, and arm64 compiled to fat static lib - BuildTarget ios = new BuildTarget(Os.IOS, Architecture.Bitness._32, new String[] {"**/*.c"}, new String[0], new String[] {"**/*.cpp"}, - new String[0], new String[0], "", "-c -Wall -O2 -stdlib=libc++", "-c -Wall -O2 -stdlib=libc++", - "-shared -stdlib=libc++"); - ios.cCompiler = "clang"; - ios.cppCompiler = "clang++"; - ios.canBuild = () -> System.getProperty("os.name").contains("Mac"); - return ios; - } - - throw new RuntimeException("Unknown target type"); - } + /** + * the target operating system + **/ + public Os os; + /** + * whether this is a 32-bit, 64-bit or 128-bit build, not used for Android + **/ + public Architecture.Bitness bitness; + /** + * whether this is an x86, ARM or RISC-V build, not used for Android + **/ + public Architecture architecture = Architecture.x86; + + /** + * Compiler ABI type, GCC/clang compatible or MSVC + **/ + public CompilerABIType compilerABIType = CompilerABIType.GCC_CLANG; + + /** + * the C files and directories to be included in the build, accepts Ant path format, must not be null + **/ + public String[] cIncludes; + /** + * the C files and directories to be excluded from the build, accepts Ant path format, must not be null + **/ + public String[] cExcludes; + /** + * the C++ files and directories to be included in the build, accepts Ant path format, must not be null + **/ + public String[] cppIncludes; + /** + * the C++ files and directories to be excluded from the build, accepts Ant path format, must not be null + **/ + public String[] cppExcludes; + /** + * the directories containing headers for the build, must not be null + **/ + public String[] headerDirs; + /** + * the compiler to use when compiling c files. Usually gcc or clang, must not be null + */ + public String cCompiler = "gcc"; + /** + * the compiler to use when compiling c++ files. Usually g++ or clang++, must not be null + */ + public String cppCompiler = "g++"; + /** + * the command to use when archiving files. Usually ar, must not be null + */ + public String archiver = "ar"; + /** + * prefix for the compiler (g++, gcc), useful for cross compilation, must not be null + **/ + public String compilerPrefix = ""; + /** + * suffix for the compiler (g++, gcc), useful for cross compilation, must not be null + **/ + public String compilerSuffix = ""; + /** + * the flags passed to the C compiler, must not be null + **/ + public String cFlags; + /** + * the flags passed to the C++ compiler, must not be null + **/ + public String cppFlags; + /** + * the flags passed to the linker, must not be null + **/ + public String linkerFlags; + /** + * Pre linker flags for msvc, required separately during linking to provide compiler flags + **/ + public String msvcPreLinkerFlags; + /** + * the flags passed to the archiver, must not be null + **/ + public String archiverFlags = "rcs"; + /** + * the name of the generated build file for this target, defaults to "build-${target}(64)?.xml", must not be null + **/ + public String buildFileName; + /** + * whether to exclude this build target from the master build file, useful for debugging + **/ + public boolean excludeFromMasterBuildFile = false; + /** + * Ant XML executed in a target before compilation + **/ + public String preCompileTask; + /** + * Ant Xml executed in a target after compilation + **/ + public String postCompileTask; + /** + * the libraries to be linked to the output, specify via e.g. -ldinput -ldxguid etc. + **/ + public String libraries; + /** + * The name used for folders for this specific target. Defaults to "${target}(64)" + **/ + public String osFileName; + /** + * The name used for the library file. This is a full file name, including file extension. Default is platform specific. E.g. + * "lib{sharedLibName}64.so" + **/ + public String libName; + + /** + * Extra lines which will be added to Android's Android.mk + */ + public String[] androidAndroidMk = {}; + /** + * Extra lines which will be added to Android's Application.mk + */ + public String[] androidApplicationMk = {}; + + /** + * Is the target a simulator + */ + public TargetType targetType = TargetType.DEVICE; + + /** + * If this is a release build or not + */ + public boolean release; + + /** + * Override for building abi on android + **/ + private AndroidABI targetAndroidABI; + + /** + * Creates a new build target. See members of this class for a description of the parameters. + */ + public BuildTarget (Os targetOs, Architecture.Bitness bitness, String[] cIncludes, String[] cExcludes, String[] cppIncludes, String[] cppExcludes, String[] headerDirs, String compilerPrefix, String cFlags, String cppFlags, String linkerFlags, String msvcPreLinkerFlags) { + if (targetOs == null) throw new IllegalArgumentException("targetOs must not be null"); + if (cIncludes == null) cIncludes = new String[0]; + if (cExcludes == null) cExcludes = new String[0]; + if (cppIncludes == null) cppIncludes = new String[0]; + if (cppExcludes == null) cppExcludes = new String[0]; + if (headerDirs == null) headerDirs = new String[0]; + if (compilerPrefix == null) compilerPrefix = ""; + if (cFlags == null) cFlags = ""; + if (cppFlags == null) cppFlags = ""; + if (linkerFlags == null) linkerFlags = ""; + if (msvcPreLinkerFlags == null) msvcPreLinkerFlags = ""; + + this.os = targetOs; + this.bitness = bitness; + this.cIncludes = cIncludes; + this.cExcludes = cExcludes; + this.cppIncludes = cppIncludes; + this.cppExcludes = cppExcludes; + this.headerDirs = headerDirs; + this.compilerPrefix = compilerPrefix; + this.cFlags = cFlags; + this.cppFlags = cppFlags; + this.linkerFlags = linkerFlags; + this.msvcPreLinkerFlags = msvcPreLinkerFlags; + this.libraries = ""; + } + + public String getBuildFilename () { + // Use specified buildFileName if it is user provided + if (buildFileName != null && !buildFileName.isEmpty()) + return buildFileName; + + return "build-" + os.toString().toLowerCase() + architecture.toSuffix() + bitness.name().substring(1) + ".xml"; + } + + public String getSharedLibFilename (String sharedLibName) { + // Use specified libName if it is user provided + if (libName != null && !libName.isEmpty()) + return libName; + + String suffix = os.getLibExtension().isEmpty() ? "" : "." + os.getLibExtension(); + + // generate shared lib prefix and suffix, determine jni platform headers directory + return os.getLibPrefix() + sharedLibName + architecture.toSuffix() + bitness.toSuffix() + suffix; + } + + public String getTargetFolder () { + // Use specified osFileName if it is user provided + if (osFileName != null && !osFileName.isEmpty()) + return osFileName; + + return os.toString().toLowerCase() + architecture.toSuffix() + bitness.name().substring(1); + } + + /** + * Creates a new default BuildTarget for the given OS, using common default values. + * + * @deprecated Use {@link #newDefaultTarget(Os, Architecture.Bitness) newDefaultTarget} method. + */ + @Deprecated + public static BuildTarget newDefaultTarget (Os osTarget, boolean is64Bit) { + return newDefaultTarget(osTarget, is64Bit, false); + } + + /** + * Creates a new default BuildTarget for the given OS, using common default values. + */ + public static BuildTarget newDefaultTarget (Os osTarget, Architecture.Bitness bitness) { + return newDefaultTarget(osTarget, bitness, Architecture.x86); + } + + /** + * Creates a new default BuildTarget for the given OS, using common default values. + * + * @deprecated Use {@link #newDefaultTarget(Os, Architecture.Bitness, Architecture) newDefaultTarget} method. + */ + @Deprecated + public static BuildTarget newDefaultTarget (Os osTarget, boolean is64Bit, boolean isARM) { + return newDefaultTarget(osTarget, is64Bit ? Architecture.Bitness._64 : Architecture.Bitness._32, isARM ? Architecture.ARM : Architecture.x86); + } + + public static BuildTarget newDefaultTarget (Os osTarget, Architecture.Bitness bitness, Architecture architecture) { + return newDefaultTarget(osTarget, bitness, architecture, CompilerABIType.GCC_CLANG); + } + + public static BuildTarget newDefaultTarget (Os osTarget, Architecture.Bitness bitness, Architecture architecture, CompilerABIType abiType) { + return newDefaultTarget(osTarget, bitness, architecture, abiType, TargetType.DEVICE); + } + + /** + * Creates a new default BuildTarget for the given OS, using common default values. + */ + public static BuildTarget newDefaultTarget (Os osTarget, Architecture.Bitness bitness, Architecture architecture, CompilerABIType abiType, TargetType targetType) { + + if (abiType == CompilerABIType.MSVC) { + if (osTarget == Os.Windows && architecture == Architecture.x86 && bitness == Architecture.Bitness._32) { + // Windows x86 32-Bit + BuildTarget target = new BuildTarget(Os.Windows, Architecture.Bitness._32, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "", "/O2 /W4 /fp:fast", + "/O2 /W4 /fp:fast", + "/DLL", "/MT"); + target.cCompiler = "cl.exe"; + target.cppCompiler = "cl.exe"; + target.compilerABIType = abiType; + return target; + } + + if (osTarget == Os.Windows && architecture == Architecture.x86 && bitness == Architecture.Bitness._64) { + // Windows x86 64-Bit + BuildTarget target = new BuildTarget(Os.Windows, Architecture.Bitness._64, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "", "/O2 /W4 /fp:fast", + "/O2 /W4 /fp:fast", + "", "/MT"); + target.compilerABIType = abiType; + target.cCompiler = "cl.exe"; + target.cppCompiler = "cl.exe"; + return target; + } + + if (osTarget == Os.Windows && architecture == Architecture.ARM && bitness == Architecture.Bitness._32) { + // Windows ARM 32-Bit + BuildTarget target = new BuildTarget(Os.Windows, Architecture.Bitness._32, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "", "/O2 /W4 /fp:fast", + "/O2 /W4 /fp:fast", + "", "/MT"); + target.cCompiler = "cl.exe"; + target.cppCompiler = "cl.exe"; + target.architecture = Architecture.ARM; + target.compilerABIType = abiType; + return target; + } + + if (osTarget == Os.Windows && architecture == Architecture.ARM && bitness == Architecture.Bitness._64) { + // Windows ARM 64-Bit + BuildTarget target = new BuildTarget(Os.Windows, Architecture.Bitness._64, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "", "/O2 /W4 /fp:fast", + "/O2 /W4 /fp:fast", + "", "/MT"); + target.cCompiler = "cl.exe"; + target.cppCompiler = "cl.exe"; + target.architecture = Architecture.ARM; + target.compilerABIType = abiType; + return target; + } + } else { + if (osTarget == Os.Windows && architecture == Architecture.x86 && bitness == Architecture.Bitness._32) { + // Windows x86 32-Bit + return new BuildTarget(Os.Windows, Architecture.Bitness._32, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "i686-w64-mingw32-", "-c -Wall -O2 -mfpmath=sse -msse2 -fmessage-length=0 -m32", + "-c -Wall -O2 -mfpmath=sse -msse2 -fmessage-length=0 -m32", + "-Wl,--kill-at -shared -m32 -static -static-libgcc -static-libstdc++", null); + } + + if (osTarget == Os.Windows && architecture == Architecture.x86 && bitness == Architecture.Bitness._64) { + // Windows x86 64-Bit + return new BuildTarget(Os.Windows, Architecture.Bitness._64, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "x86_64-w64-mingw32-", "-c -Wall -O2 -mfpmath=sse -msse2 -fmessage-length=0 -m64", + "-c -Wall -O2 -mfpmath=sse -msse2 -fmessage-length=0 -m64", + "-Wl,--kill-at -shared -static -static-libgcc -static-libstdc++ -m64", null); + } + + if (osTarget == Os.Windows && architecture == Architecture.ARM && bitness == Architecture.Bitness._32) { + // Windows ARM 32-Bit + BuildTarget target = new BuildTarget(Os.Windows, Architecture.Bitness._32, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "armv7-w64-mingw32-", "-c -Wall -O2 -fmessage-length=0", + "-c -Wall -O2 -fmessage-length=0", + "-Wl,--kill-at -shared -static -static-libgcc -static-libstdc++", null); + target.architecture = Architecture.ARM; + return target; + } + + if (osTarget == Os.Windows && architecture == Architecture.ARM && bitness == Architecture.Bitness._64) { + // Windows ARM 64-Bit + BuildTarget target = new BuildTarget(Os.Windows, Architecture.Bitness._64, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "aarch64-w64-mingw32-", "-c -Wall -O2 -fmessage-length=0", + "-c -Wall -O2 -fmessage-length=0", + "-Wl,--kill-at -shared -static -static-libgcc -static-libstdc++", null); + target.architecture = Architecture.ARM; + return target; + } + } + + if (osTarget == Os.Linux && architecture == Architecture.LOONGARCH && bitness == Architecture.Bitness._64) { + // Linux LoongArch 64-Bit + BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._64, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "loongarch64-unknown-linux-gnu-", "-c -Wall -O2 -fmessage-length=0 -fPIC", + "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared", null); + target.architecture = Architecture.LOONGARCH; + return target; + } + + if (osTarget == Os.Linux && architecture == Architecture.RISCV && bitness == Architecture.Bitness._32) { + // Linux RISCV 32-Bit + BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._32, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "riscv32-linux-gnu-", "-c -Wall -O2 -fmessage-length=0 -fPIC", + "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared", null); + target.architecture = Architecture.RISCV; + return target; + } + + if (osTarget == Os.Linux && architecture == Architecture.RISCV && bitness == Architecture.Bitness._64) { + // Linux RISCV 64-Bit + BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._64, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "riscv64-linux-gnu-", "-c -Wall -O2 -fmessage-length=0 -fPIC", + "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared", null); + target.architecture = Architecture.RISCV; + return target; + } + + if (osTarget == Os.Linux && architecture == Architecture.ARM && bitness == Architecture.Bitness._32) { + // Linux ARM 32-Bit hardfloat + BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._32, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "arm-linux-gnueabihf-", "-c -Wall -O2 -fmessage-length=0 -fPIC", + "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared", null); + target.architecture = Architecture.ARM; + return target; + } + + if (osTarget == Os.Linux && architecture == Architecture.ARM && bitness == Architecture.Bitness._64) { + // Linux ARM 64-Bit + BuildTarget target = new BuildTarget(Os.Linux, Architecture.Bitness._64, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "aarch64-linux-gnu-", "-c -Wall -O2 -fmessage-length=0 -fPIC", + "-c -Wall -O2 -fmessage-length=0 -fPIC", "-shared", null); + target.architecture = Architecture.ARM; + return target; + } + + if (osTarget == Os.Linux && bitness == Architecture.Bitness._32) { + // Linux 32-Bit + return new BuildTarget(Os.Linux, Architecture.Bitness._32, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "", "-c -Wall -O2 -mfpmath=sse -msse -fmessage-length=0 -m32 -fPIC", + "-c -Wall -O2 -mfpmath=sse -msse -fmessage-length=0 -m32 -fPIC", "-shared -m32", null); + } + + if (osTarget == Os.Linux && bitness == Architecture.Bitness._64) { + // Linux 64-Bit + return new BuildTarget(Os.Linux, Architecture.Bitness._64, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "", "-c -Wall -O2 -mfpmath=sse -msse -fmessage-length=0 -m64 -fPIC", + "-c -Wall -O2 -mfpmath=sse -msse -fmessage-length=0 -m64 -fPIC", "-shared -m64", null); + } + + if (osTarget == Os.MacOsX && bitness == Architecture.Bitness._32) { + throw new RuntimeException("macOS 32-bit not supported"); + } + + if (osTarget == Os.MacOsX && bitness == Architecture.Bitness._64 && architecture == Architecture.ARM) { + // Mac OS aarch64 + BuildTarget mac = new BuildTarget(Os.MacOsX, Architecture.Bitness._64, new String[]{""}, new String[0], + new String[]{""}, new String[0], new String[0], "", + "-c -Wall -O2 -arch arm64 -DFIXED_POINT -fmessage-length=0 -fPIC -mmacosx-version-min=10.7 -stdlib=libc++", + "-c -Wall -O2 -arch arm64 -DFIXED_POINT -fmessage-length=0 -fPIC -mmacosx-version-min=10.7 -stdlib=libc++", + "-shared -arch arm64 -mmacosx-version-min=10.7 -stdlib=libc++", null); + mac.cCompiler = "clang"; + mac.cppCompiler = "clang++"; + mac.architecture = Architecture.ARM; + return mac; + } + + if (osTarget == Os.MacOsX && bitness == Architecture.Bitness._64) { + // Mac OS x86_64 + BuildTarget mac = new BuildTarget(Os.MacOsX, Architecture.Bitness._64, new String[]{""}, new String[0], + new String[]{""}, new String[0], new String[0], "", + "-c -Wall -O2 -arch x86_64 -DFIXED_POINT -fmessage-length=0 -fPIC -mmacosx-version-min=10.7 -stdlib=libc++", + "-c -Wall -O2 -arch x86_64 -DFIXED_POINT -fmessage-length=0 -fPIC -mmacosx-version-min=10.7 -stdlib=libc++", + "-shared -arch x86_64 -mmacosx-version-min=10.7 -stdlib=libc++", null); + mac.cCompiler = "clang"; + mac.cppCompiler = "clang++"; + return mac; + } + + if (osTarget == Os.Android) { + BuildTarget android = new BuildTarget(Os.Android, Architecture.Bitness._32, new String[]{""}, new String[0], + new String[]{""}, new String[0], new String[0], "", "-O2 -Wall -D__ANDROID__", "-O2 -Wall -D__ANDROID__", + "-lm", null); + return android; + } + + if (osTarget == Os.IOS) { + // iOS, x86_64 simulator, armv7, and arm64 compiled to fat static lib + BuildTarget ios = new BuildTarget(Os.IOS, bitness, new String[]{""}, new String[0], new String[]{""}, + new String[0], new String[0], "", "-c -Wall -O2 -stdlib=libc++", "-c -Wall -O2 -stdlib=libc++", + "-shared -stdlib=libc++", null); + ios.cCompiler = "clang"; + ios.cppCompiler = "clang++"; + ios.targetType = targetType; + ios.architecture = architecture; + return ios; + } + + throw new RuntimeException("Unknown target type"); + } + + public File getTargetBinaryFile (BuildConfig config) { + File libsDirectory = config.libsDir.child(getTargetFolder()).file(); + + String sharedLibFilename = getSharedLibFilename(config.sharedLibName); + if (targetType == TargetType.SIMULATOR) { + sharedLibFilename += "-sim"; + } + + return new File(libsDirectory, sharedLibFilename); + } + + public AndroidABI getTargetAndroidABI () { + return targetAndroidABI; + } + + public void setAndroidOverrideABI (AndroidABI overrideAndroidABI) { + this.targetAndroidABI = overrideAndroidABI; + } + + @Override + public String toString () { + if (os == Os.Android) { + return "BuildTarget{" + + "os=" + os + + ", androidABI=" + targetAndroidABI + + '}'; + } else { + return "BuildTarget{" + + "os=" + os + + ", bitness=" + bitness + + ", architecture=" + architecture + + ", compilerABIType=" + compilerABIType + + '}'; + } + } + + public boolean canBuildOnHost (Os hostOS) { + switch (hostOS) { + case Windows: + //Android, Windows, WindowsMSCV + return os == Os.Windows || + os == Os.Android; + case Linux: + //Android, Windows, Linux + if (os == Os.Windows) { + //No MSVC support + if (compilerABIType == CompilerABIType.MSVC) { + return false; + } + return true; + } + + //The rest + return os == Os.Android || os == Os.Linux; + case MacOsX: + //Mac / iOS. Yes there are others, but Its not as trivial, so I think we should just handle Apple ecosphere + return os == Os.MacOsX || os == Os.IOS || os == Os.Android; + + case Android: + case IOS: + return false; + default: + throw new IllegalStateException("Unexpected value: " + hostOS); + } + } } diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/FileDescriptor.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/FileDescriptor.java index fc11eca4..ae980343 100644 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/FileDescriptor.java +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/FileDescriptor.java @@ -69,7 +69,7 @@ public FileDescriptor (File file) { this.type = FileType.Absolute; } - protected FileDescriptor (String fileName, FileType type) { + public FileDescriptor (String fileName, FileType type) { this.type = type; file = new File(fileName); } diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/JniGenSharedLibraryLoader.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/JniGenSharedLibraryLoader.java deleted file mode 100644 index 0fc92882..00000000 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/JniGenSharedLibraryLoader.java +++ /dev/null @@ -1,221 +0,0 @@ -/******************************************************************************* - * Copyright 2011 See AUTHORS file. - * - * 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 com.badlogic.gdx.jnigen; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashSet; -import java.util.Set; -import java.util.zip.CRC32; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** Loads shared libraries from a natives jar file (desktop) or arm folders (Android). For desktop projects, have the natives jar - * in the classpath, for Android projects put the shared libraries in the libs/armeabi and libs/armeabi-v7a folders. - * - * See {@link AntScriptGenerator}. - * - * This class is deprecated in favor of SharedLibraryLoader. - * - * @author mzechner */ -@Deprecated -public class JniGenSharedLibraryLoader { - private static Set loadedLibraries = new HashSet(); - private String nativesJar; - private SharedLibraryFinder libraryFinder; - - private ZipFile nativesZip = null; - - public JniGenSharedLibraryLoader () { - } - - /** Fetches the natives from the given natives jar file. Used for testing a shared lib on the fly, see MyJniClass. - * @param nativesJar */ - public JniGenSharedLibraryLoader (String nativesJar) { - this.nativesJar = nativesJar; - } - - /** Fetches the natives from the given natives jar file. Used for testing a shared lib on the fly, see MyJniClass. - * @param nativesJar - * @param libraryFinder A custom libraryfinder, which enables the use of different dynamic libs naming. */ - public JniGenSharedLibraryLoader (String nativesJar, SharedLibraryFinder libraryFinder) { - this.nativesJar = nativesJar; - this.libraryFinder = libraryFinder; - if (nativesJar != null) { - try { - nativesZip = new ZipFile(nativesJar); - } catch (IOException e) { - nativesZip = null; - } - } - } - - /** Setting a SharedLibraryFinder enables you to load libraries according to a nondefault natives jar layout or library names. - * @param libraryFinder */ - public void setSharedLibraryFinder (SharedLibraryFinder libraryFinder) { - this.libraryFinder = libraryFinder; - if (nativesJar != null) { - try { - nativesZip = new ZipFile(nativesJar); - } catch (IOException e) { - nativesZip = null; - } - } - } - - /** Returns a CRC of the remaining bytes in the stream. */ - public String crc (InputStream input) { - if (input == null) return "" + System.nanoTime(); // fallback - CRC32 crc = new CRC32(); - byte[] buffer = new byte[4096]; - try { - while (true) { - int length = input.read(buffer); - if (length == -1) break; - crc.update(buffer, 0, length); - } - } catch (Exception ex) { - try { - input.close(); - } catch (Exception ignored) { - } - } - return Long.toString(crc.getValue()); - } - - private boolean loadLibrary (String sharedLibName) { - if (sharedLibName == null) return false; - - String path = extractLibrary(sharedLibName); - if (path != null) System.load(path); - return path != null; - } - - private String extractLibrary (String sharedLibName) { - String srcCrc = crc(JniGenSharedLibraryLoader.class.getResourceAsStream("/" + sharedLibName)); - File nativesDir = new File(System.getProperty("java.io.tmpdir") + "/jnigen/" + srcCrc); - File nativeFile = new File(nativesDir, sharedLibName); - - String extractedCrc = null; - if (nativeFile.exists()) { - try { - extractedCrc = crc(new FileInputStream(nativeFile)); - } catch (FileNotFoundException ignored) { - } - } - - if (extractedCrc == null || !extractedCrc.equals(srcCrc)) { - try { - // Extract native from classpath to temp dir. - InputStream input = null; - if (nativesJar == null) - input = JniGenSharedLibraryLoader.class.getResourceAsStream("/" + sharedLibName); - else - input = getFromJar(nativesJar, sharedLibName); - if (input == null) return null; - nativeFile.getParentFile().mkdirs(); - FileOutputStream output = new FileOutputStream(nativeFile); - byte[] buffer = new byte[4096]; - while (true) { - int length = input.read(buffer); - if (length == -1) break; - output.write(buffer, 0, length); - } - input.close(); - output.close(); - } catch (IOException ex) { - ex.printStackTrace(); - throw new RuntimeException(ex); - } - } - return nativeFile.exists() ? nativeFile.getAbsolutePath() : null; - } - - private InputStream getFromJar (String jarFile, String sharedLibrary) throws IOException { - ZipFile file = new ZipFile(nativesJar); - ZipEntry entry = file.getEntry(sharedLibrary); - return file.getInputStream(entry); - } - - /** Loads a shared library with the given name for the platform the application is running on. The name should not contain a - * prefix (e.g. 'lib') or suffix (e.g. '.dll). - * @param sharedLibName */ - public synchronized void load (String sharedLibName) { - if (loadedLibraries.contains(sharedLibName)) return; - - boolean isWindows = System.getProperty("os.name").contains("Windows"); - boolean isLinux = System.getProperty("os.name").contains("Linux"); - boolean isMac = System.getProperty("os.name").contains("Mac"); - boolean isAndroid = false; - boolean is64Bit = System.getProperty("os.arch").contains("64") || System.getProperty("os.arch").startsWith("armv8"); - boolean isARM = System.getProperty("os.arch").equals("arm") || System.getProperty("os.arch").startsWith("aarch64"); - - String vm = System.getProperty("java.vm.name"); - if (vm != null && vm.contains("Dalvik")) { - isAndroid = true; - isWindows = false; - isLinux = false; - isMac = false; - is64Bit = false; - } - - boolean loaded = false; - if (isWindows) { - if (libraryFinder != null) - loaded = loadLibrary(libraryFinder.getSharedLibraryNameWindows(sharedLibName, is64Bit, nativesZip)); - else if (!is64Bit) - loaded = loadLibrary(sharedLibName + ".dll"); - else - loaded = loadLibrary(sharedLibName + "64.dll"); - } - if (isLinux) { - if (libraryFinder != null) - loaded = loadLibrary(libraryFinder.getSharedLibraryNameLinux(sharedLibName, is64Bit, isARM, nativesZip)); - else if (!is64Bit) { - if (isARM) - loaded = loadLibrary("lib" + sharedLibName + "arm.so"); - else - loaded = loadLibrary("lib" + sharedLibName + ".so"); - } else { - if (isARM) - loaded = loadLibrary("lib" + sharedLibName + "arm64.so"); - else - loaded = loadLibrary("lib" + sharedLibName + "64.so"); - } - } - if (isMac) { - if (libraryFinder != null) - loaded = loadLibrary(libraryFinder.getSharedLibraryNameMac(sharedLibName, is64Bit, nativesZip)); - else if (!is64Bit) - loaded = loadLibrary("lib" + sharedLibName + ".dylib"); - else - loaded = loadLibrary("lib" + sharedLibName + "64.dylib"); - } - if (isAndroid) { - if (libraryFinder != null) - System.loadLibrary(libraryFinder.getSharedLibraryNameAndroid(sharedLibName, nativesZip)); - else - System.loadLibrary(sharedLibName); - loaded = true; - } - if (loaded) loadedLibraries.add(sharedLibName); - } -} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/NativeCodeGenerator.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/NativeCodeGenerator.java index 75c79f1c..8da3b4b8 100644 --- a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/NativeCodeGenerator.java +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/NativeCodeGenerator.java @@ -170,7 +170,7 @@ * new NativeCodeGenerator().generate("src", "bin", "jni"); * * - *

To automatically compile and load the native code, see the classes {@link AntScriptGenerator}, {@link BuildExecutor} and + *

To automatically compile and load the native code, {@link BuildExecutor} and * SharedLibraryLoader classes.

* * @author mzechner */ @@ -184,7 +184,6 @@ public class NativeCodeGenerator { FileDescriptor jniDir; String[] includes; String[] excludes; - AntPathMatcher matcher = new AntPathMatcher(); JavaMethodParser javaMethodParser = new RobustJavaMethodParser(); CMethodParser cMethodParser = new JniHeaderCMethodParser(); CMethodParserResult cResult; @@ -246,13 +245,13 @@ private void processDirectory (FileDescriptor dir) throws Exception { for (FileDescriptor file : files) { if (file.isDirectory()) { if (file.path().contains(".svn")) continue; - if (excludes != null && matcher.match(file.path(), excludes)) continue; + if (excludes != null && AntPathMatcher.match(file.path(), excludes)) continue; processDirectory(file); } else { if (file.extension().equals("java")) { if (file.name().contains("NativeCodeGenerator")) continue; - if (includes != null && !matcher.match(file.path(), includes)) continue; - if (excludes != null && matcher.match(file.path(), excludes)) continue; + if (includes != null && !AntPathMatcher.match(file.path(), includes)) continue; + if (excludes != null && AntPathMatcher.match(file.path(), excludes)) continue; String className = getNativeClassFileName(file); FileDescriptor cppFile = new FileDescriptor(jniDir + "/" + className + ".cpp"); if (file.lastModified() < cppFile.lastModified()) { @@ -343,9 +342,9 @@ private void generateCppFile (ArrayList javaSegments, List forceLinkClasses = new ArrayList<>(); + private List extraLibs = new ArrayList<>(); + private List extraXCFrameworks = new ArrayList<>(); + + public File getManualFile () { + return manualFile; + } + + public List getForceLinkClasses () { + return forceLinkClasses; + } + + public List getExtraLibs () { + return extraLibs; + } + + public List getExtraXCFrameworks () { + return extraXCFrameworks; + } + + public void manualFile (File manualFile) { + if (!forceLinkClasses.isEmpty() || !extraLibs.isEmpty()) + throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); + this.manualFile = manualFile; + } + + public void forceLinkClasses (String[] forceLinkClasses) { + if (manualFile != null) + throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); + this.forceLinkClasses.addAll(Arrays.asList(forceLinkClasses)); + } + + public void extraLib (String path) { + if (manualFile != null) + throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); + extraLibs.add(new RoboVMXmlLib(path, null)); + } + + public void extraLib (String path, String variant) { + if (manualFile != null) + throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); + extraLibs.add(new RoboVMXmlLib(path, variant)); + } + + public void extraXCFramework (String path) { + if (manualFile != null) + throw new RuntimeException("robovm cannot use both manualFile and gradle overrides"); + extraXCFrameworks.add(path); + } + + public class RoboVMXmlLib { + public String path; + public String variant; + + public RoboVMXmlLib (String path, String variant) { + this.path = path; + this.variant = variant; + } + } +} \ No newline at end of file diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/IOSFrameworkBuilder.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/IOSFrameworkBuilder.java new file mode 100644 index 00000000..d4d79316 --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/IOSFrameworkBuilder.java @@ -0,0 +1,220 @@ +package com.badlogic.gdx.jnigen.build; + +import com.badlogic.gdx.jnigen.BuildConfig; +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.build.toolchains.IOSToolchain; +import com.badlogic.gdx.jnigen.commons.TargetType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class IOSFrameworkBuilder { + + private final Logger logger = LoggerFactory.getLogger(IOSFrameworkBuilder.class); + + private static class DeviceSimBuildWrapper { + + private final TargetType targetType; + + private final List deviceTargetToolchains; + + private FileDescriptor frameworkDirectory; + private FileDescriptor frameworkBinaryFile; + + public DeviceSimBuildWrapper (BuildConfig config, FileDescriptor iosFrameworkFolder, TargetType targetType, List deviceTargetToolchains) { + this.targetType = targetType; + this.deviceTargetToolchains = deviceTargetToolchains; + + this.frameworkDirectory = createFrameworkDirectory(iosFrameworkFolder, targetType, config.sharedLibName); + this.frameworkBinaryFile = this.frameworkDirectory.child(config.sharedLibName); + } + + private FileDescriptor createFrameworkDirectory (FileDescriptor parent, TargetType targetType, String sharedLibName) { + FileDescriptor frameworkFolder = parent.child(targetType.getTargetTypeBuildDirName()).child(sharedLibName + ".framework"); + frameworkFolder.mkdirs(); + return frameworkFolder; + } + } + + public static void createXCFramework (BuildConfig config, List buildTargets) { + List deviceTargetToolchains = new ArrayList<>(); + List simTargetToolchains = new ArrayList<>(); + + for (BuildTarget buildTarget : buildTargets) { + IOSToolchain iosToolchain = new IOSToolchain(); + iosToolchain.configure(buildTarget, config); + if (buildTarget.targetType == TargetType.DEVICE) { + deviceTargetToolchains.add(iosToolchain); + } else { + simTargetToolchains.add(iosToolchain); + } + } + + boolean hasSim = !simTargetToolchains.isEmpty(); + boolean hasDevice = !deviceTargetToolchains.isEmpty(); + + FileDescriptor iosFrameworkFolder = config.buildDir.child("iosframework"); + iosFrameworkFolder.mkdirs(); + + List deviceSimBuildWrappers = new ArrayList<>(); + if (hasSim) { + deviceSimBuildWrappers.add(new DeviceSimBuildWrapper(config, iosFrameworkFolder, TargetType.SIMULATOR, simTargetToolchains)); + } + if (hasDevice) { + deviceSimBuildWrappers.add(new DeviceSimBuildWrapper(config, iosFrameworkFolder, TargetType.DEVICE, deviceTargetToolchains)); + } + + RuntimeEnv env = new RuntimeEnv(); + File lipo = ToolFinder.getToolFile("lipo", env, true); + + File buildDir = config.buildDir.file(); + + for (DeviceSimBuildWrapper deviceSimBuildWrapper : deviceSimBuildWrappers) { + runLipoCommand(lipo, buildDir, deviceSimBuildWrapper); + } + + + byte[] plist = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/Info.plist.template", FileDescriptor.FileType.Classpath).readBytes(); + for (DeviceSimBuildWrapper deviceSimBuildWrapper : deviceSimBuildWrappers) { + writePlist(plist, deviceSimBuildWrapper); + } + + String xcBundleIdentifier = config.robovmBuildConfig.xcframeworkBundleIdentifier == null ? ("gdx.jnigen." + config.sharedLibName) : config.robovmBuildConfig.xcframeworkBundleIdentifier; + + for (DeviceSimBuildWrapper deviceSimBuildWrapper : deviceSimBuildWrappers) { + runPlistBuddyCommand(env, deviceSimBuildWrapper, xcBundleIdentifier, config); + } + + for (DeviceSimBuildWrapper deviceSimBuildWrapper : deviceSimBuildWrappers) { + runPlutilCommand(env, deviceSimBuildWrapper); + } + + File dsymutil = ToolFinder.getToolFile("dsymutil", env, true); + + for (DeviceSimBuildWrapper deviceSimBuildWrapper : deviceSimBuildWrappers) { + runDsymutilCommand(dsymutil, deviceSimBuildWrapper, config); + } + + File strip = ToolFinder.getToolFile("strip", env, true); + for (DeviceSimBuildWrapper deviceSimBuildWrapper : deviceSimBuildWrappers) { + runStripCommand(strip, deviceSimBuildWrapper, config); + } + + File outputFramework = config.libsDir.child(config.sharedLibName + ".xcframework").file(); + if (outputFramework.exists()) { + new FileDescriptor(outputFramework).deleteDirectory(); + } + + createXCFramework(env, buildDir, deviceSimBuildWrappers, config, outputFramework); + } + + + private static void runLipoCommand (File lipo, File workingDir, DeviceSimBuildWrapper wrapper) { + List args = new ArrayList<>(); + args.add("-create"); + args.add("-output"); + args.add(wrapper.frameworkBinaryFile.file().getAbsolutePath()); + for (IOSToolchain toolchain : wrapper.deviceTargetToolchains) { + args.add(toolchain.getTargetLibFile().getAbsolutePath()); + } + ToolchainExecutor.execute(lipo, workingDir, args, createCallback("Lipo")); + } + + private static void writePlist (byte[] plist, DeviceSimBuildWrapper wrapper) { + FileDescriptor devicePlist = wrapper.frameworkDirectory.child("Info.plist"); + devicePlist.writeBytes(plist, false); + } + + private static void runPlistBuddyCommand (RuntimeEnv env, DeviceSimBuildWrapper wrapper, String bundleIdentifier, BuildConfig config) { + File plistBuddy = ToolFinder.getToolFile("/usr/libexec/PlistBuddy", env, true); + List plistArgs = new ArrayList<>(); + plistArgs.add("-c"); + plistArgs.add("Set :CFBundleName " + config.sharedLibName); + plistArgs.add("-c"); + plistArgs.add("Set :CFBundleExecutable " + config.sharedLibName); + plistArgs.add("-c"); + plistArgs.add("Set :DTPlatformName " + wrapper.targetType.getPlatformName()); + plistArgs.add("-c"); + plistArgs.add("Set :CFBundleIdentifier " + bundleIdentifier); + plistArgs.add("-c"); + plistArgs.add("Set :MinimumOSVersion " + config.robovmBuildConfig.minIOSVersion); + plistArgs.add(wrapper.frameworkDirectory.child("Info.plist").file().getAbsolutePath()); + ToolchainExecutor.execute(plistBuddy, wrapper.frameworkDirectory.file().getParentFile(), plistArgs, createCallback("PlistBuddy " + wrapper.targetType.getPlatformName())); + } + + private static void runPlutilCommand (RuntimeEnv env, DeviceSimBuildWrapper wrapper) { + File plutil = ToolFinder.getToolFile("plutil", env, true); + List plutilArgs = new ArrayList<>(); + plutilArgs.add("-convert"); + plutilArgs.add("binary1"); + plutilArgs.add(wrapper.frameworkDirectory.child("Info.plist").file().getAbsolutePath()); + ToolchainExecutor.execute(plutil, wrapper.frameworkDirectory.file().getParentFile(), plutilArgs, createCallback("Plutil " + wrapper.targetType.getPlatformName())); + } + + private static void runDsymutilCommand (File dsymutil, DeviceSimBuildWrapper wrapper, BuildConfig config) { + List dsymutilArgs = new ArrayList<>(); + dsymutilArgs.add(wrapper.frameworkDirectory.child(config.sharedLibName).file().getAbsolutePath()); + dsymutilArgs.add("-o"); + FileDescriptor dsymFolder = wrapper.frameworkDirectory.parent().child(config.sharedLibName + ".framework.dSYM"); + dsymutilArgs.add(dsymFolder.file().getAbsolutePath()); + ToolchainExecutor.execute(dsymutil, wrapper.frameworkDirectory.file().getParentFile(), dsymutilArgs, createCallback("Dsym " + wrapper.targetType.getPlatformName())); + } + + private static void runStripCommand (File strip, DeviceSimBuildWrapper wrapper, BuildConfig config) { + List stripArgs = new ArrayList<>(); + stripArgs.add("-x"); + stripArgs.add(wrapper.frameworkDirectory.child(config.sharedLibName).file().getAbsolutePath()); + ToolchainExecutor.execute(strip, wrapper.frameworkDirectory.file().getParentFile(), stripArgs, createCallback("Strip " + wrapper.targetType.getPlatformName())); + } + + private static void createXCFramework (RuntimeEnv env, File workingDir, List wrappers, BuildConfig config, File output) { + File xcodeBuild = ToolFinder.getToolFile("xcodebuild", env, true); + List xcframeworkArgs = new ArrayList<>(); + xcframeworkArgs.add("-create-xcframework"); + + for (DeviceSimBuildWrapper wrapper : wrappers) { + xcframeworkArgs.add("-framework"); + xcframeworkArgs.add(wrapper.frameworkDirectory.file().getAbsolutePath()); + xcframeworkArgs.add("-debug-symbols"); + xcframeworkArgs.add(wrapper.frameworkDirectory.parent().child(config.sharedLibName + ".framework.dSYM").file().getAbsolutePath()); + } + + xcframeworkArgs.add("-output"); + xcframeworkArgs.add(output.getAbsolutePath()); + + ToolchainExecutor.execute(xcodeBuild, workingDir, xcframeworkArgs, createCallback("XCFramework generation")); + } + + private static ToolchainExecutor.ToolchainCallback createCallback (String identifier) { + + final Logger logger = LoggerFactory.getLogger(IOSFrameworkBuilder.class); + + return new ToolchainExecutor.ToolchainCallback() { + @Override + public void onInfoMessage (String message) { + logger.info(message); + } + + @Override + public void onErrorMessage (String message) { + logger.error(message); + } + + @Override + public void onSuccess () { + logger.info("{} complete", identifier); + } + + @Override + public void onFail (int statusCode) { + logger.error("Failure to {}, status code {}", identifier, statusCode); + throw new RuntimeException("Failure to " + identifier); + } + }; + } + +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/PlatformBuilder.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/PlatformBuilder.java new file mode 100644 index 00000000..8497b044 --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/PlatformBuilder.java @@ -0,0 +1,163 @@ +package com.badlogic.gdx.jnigen.build; + +import com.badlogic.gdx.jnigen.BuildConfig; +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.commons.CompilerABIType; +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.build.toolchains.*; +import com.badlogic.gdx.jnigen.commons.HostDetection; +import com.badlogic.gdx.jnigen.commons.Os; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.BiConsumer; + +/** + * Will build all targets given to it, each individual toolchain and approach will be decided here + */ +public class PlatformBuilder { + + private static final Logger logger = LoggerFactory.getLogger(PlatformBuilder.class); + + /** + * Supply all build targets from a configuration, we can sort and collect here. Saves us doing it in + * Gradle tasks, and lets us support this easily with the direct api calls too + *

+ * e.g. targetOSToBuildFor = android + * We filter out all the BuildTargets so we only have ones with Android os. We then pick the Android + * Toolchain and build everything + *

+ * If we want, we can just supply a single build at a time, this gives us backwards compatibility with old gradle tasks + *

+ * Desktop case, this would be called for Windows/Mac/Linux separately, but it would handle all archs in one + * + * @param targetOSToBuildFor + * @param config + * @param targets + */ + public void build (Os targetOSToBuildFor, BuildConfig config, List targets) { + try { + + List compatibleTargets = collectTargetsToBuild(targetOSToBuildFor, targets); + + //Check these are compatible with the host + checkTargetsCompatibleWithHost(compatibleTargets); + + if (compatibleTargets.isEmpty()) { + logger.info("No compatible targets found, nothing to build"); + return; + } + + //Lets sort these out and build + + HashMap> osSeparatedBuildTargets = new HashMap<>(); + for (BuildTarget compatibleTarget : compatibleTargets) { + if (!osSeparatedBuildTargets.containsKey(compatibleTarget.os)) { + osSeparatedBuildTargets.put(compatibleTarget.os, new ArrayList<>()); + } + if (compatibleTarget.excludeFromMasterBuildFile) { + logger.warn("Skipping build target because its marked as excluded from master build file. {}", compatibleTarget); + } else { + osSeparatedBuildTargets.get(compatibleTarget.os).add(compatibleTarget); + } + } + + + osSeparatedBuildTargets.forEach(new BiConsumer>() { + @Override + public void accept (Os os, List buildTargets) { + + if (buildTargets.size() > 1) { + //Make sure all abis are the same + CompilerABIType compilerABIType = buildTargets.get(0).compilerABIType; + + for (BuildTarget buildTarget : buildTargets) { + if (buildTarget.compilerABIType != compilerABIType) { + logger.error("Build target {} does not have the same compilerABI type as previous ABI {}.", buildTarget, compilerABIType); + throw new RuntimeException("Invalid configuration"); + } + } + } + + logger.info("Starting build for OS {}", os); + + for (BuildTarget buildTarget : buildTargets) { + logger.info("Starting build for Target {}", buildTarget); + + BaseToolchain toolchain = findToolchainForTarget(buildTarget); + toolchain.configure(buildTarget, config); + toolchain.build(); + } + + + if (os == Os.IOS) { + //special ios case to package on the platform into framework + IOSFrameworkBuilder.createXCFramework(config, buildTargets); + } + + } + }); + + } catch (Exception e) { + logger.error("Failure to build", e); + throw e; + } + } + + private BaseToolchain findToolchainForTarget (BuildTarget buildTarget) { + switch (buildTarget.os) { + case Windows: + if (buildTarget.compilerABIType == CompilerABIType.MSVC) { + return new MSVCToolchain(); + } else { + return new GNUToolchain(); + } + case IOS: + return new IOSToolchain(); + case Linux: + case MacOsX: + return new GNUToolchain(); + case Android: + return new AndroidToolchain(); + default: + throw new IllegalStateException("Unexpected value: " + buildTarget.os); + } + } + + + private void checkTargetsCompatibleWithHost (List compatibleTargets) { + for (BuildTarget compatibleTarget : compatibleTargets) { + if (!compatibleTarget.canBuildOnHost(HostDetection.os)) { + throw new RuntimeException("Build target is not compatible with the host OS. " + compatibleTarget.toString()); + } + } + } + + + private List collectTargetsToBuild (Os targetOSToBuildFor, List targets) { + ArrayList targetsThatMatchOS = new ArrayList<>(); + for (BuildTarget target : targets) { + if (target.os == targetOSToBuildFor) { + targetsThatMatchOS.add(target); + } + } + return targetsThatMatchOS; + } + + public static void copyHeaders (FileDescriptor jniDir) { + //copy headers to jni dir + final String pack = "com/badlogic/gdx/jnigen/resources/headers"; + String files[] = {"classfile_constants.h", "jawt.h", "jdwpTransport.h", "jni.h", "linux/jawt_md.h", "linux/jni_md.h", + "mac/jni_md.h", "win32/jawt_md.h", "win32/jni_md.h"}; + + for (String file : files) { + FileDescriptor source = new FileDescriptor(pack, FileDescriptor.FileType.Classpath).child(file); + FileDescriptor destination = jniDir.child("jni-headers").child(file); + destination.parent().mkdirs(); + source.copyTo(destination); + } + } +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/RuntimeEnv.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/RuntimeEnv.java new file mode 100644 index 00000000..bad8f52c --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/RuntimeEnv.java @@ -0,0 +1,47 @@ +package com.badlogic.gdx.jnigen.build; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.*; + +public class RuntimeEnv { + + private static final Logger logger = LoggerFactory.getLogger(RuntimeEnv.class); + + private Set paths = new HashSet<>(); + + public RuntimeEnv () { + addToPathFromSystemEnv("Path"); + addToPathFromSystemEnv("PATH"); + + addToPathFromSystemEnv("NDK_HOME"); + } + + public Set getPaths () { + return paths; + } + + private void addToPathFromSystemEnv (String envKey) { + String env = System.getenv(envKey); + addToPath(env); + } + + public void addToPath (String encodedPath) { + String[] split = splitToPaths(encodedPath); + if (split != null) { + paths.addAll(Arrays.asList(split)); + } + } + + public String[] splitToPaths (String encodedPath) { + if (encodedPath == null) { + logger.trace("Path is null {}", encodedPath); + return null; + } + + String[] split = encodedPath.split(File.pathSeparator); + return split; + } +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/ToolFinder.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/ToolFinder.java new file mode 100644 index 00000000..9ca1187d --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/ToolFinder.java @@ -0,0 +1,67 @@ +package com.badlogic.gdx.jnigen.build; + +import com.badlogic.gdx.jnigen.commons.HostDetection; +import com.badlogic.gdx.jnigen.commons.Os; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.Set; + +public class ToolFinder { + + private static final Logger logger = LoggerFactory.getLogger(ToolFinder.class); + + public static File getSDK (String pathName, boolean failOnNotFound) { + File absoluteFile = new File(pathName); + if (absoluteFile.exists()) { + return absoluteFile; + } else { + if (failOnNotFound) { + logger.error("Failure to find SDK {}. Make sure that the path is correct", pathName); + throw new RuntimeException("Tool was not found"); + } + } + return null; + } + + public static File getToolFile (String toolPathOrName, RuntimeEnv env, boolean failOnNotFound) { + return getToolFile(toolPathOrName, env, null, failOnNotFound); + } + + public static File getToolFile (String toolPathOrName, RuntimeEnv env, String optionalFallbackSuffix, boolean failOnNotFound) { + boolean found = false; + + File absoluteFile = new File(toolPathOrName); + if (absoluteFile.exists() && absoluteFile.isFile()) { + return absoluteFile; + } + + Set paths = env.getPaths(); + for (String path : paths) { + File file = new File(path, toolPathOrName); + if (file.exists() && file.isFile()) { + return file; + } + } + + if (failOnNotFound && !found && optionalFallbackSuffix != null) { + + //Lets do a last ditch for windows friendly support + if (HostDetection.os == Os.Windows && !toolPathOrName.endsWith(optionalFallbackSuffix)) { + return getToolFile(toolPathOrName + optionalFallbackSuffix, env, optionalFallbackSuffix, failOnNotFound); + } + + logger.error("Failure to find tool {}. Make sure that its available on your path", toolPathOrName); + throw new RuntimeException("Tool was not found"); + } + + if (failOnNotFound) { + logger.error("Failure to find tool {}. Make sure that its available on your path", toolPathOrName); + throw new RuntimeException("Tool was not found"); + } + + return null; + } + +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/ToolchainExecutor.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/ToolchainExecutor.java new file mode 100644 index 00000000..c020107d --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/ToolchainExecutor.java @@ -0,0 +1,68 @@ +package com.badlogic.gdx.jnigen.build; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.List; + +public class ToolchainExecutor { + private static final Logger logger = LoggerFactory.getLogger(ToolchainExecutor.class); + + public interface ToolchainCallback { + + void onInfoMessage (String message); + void onErrorMessage (String message); + + void onSuccess (); + void onFail (int statusCode); + } + + public static void execute (File executable, File workingDirectory, List args, ToolchainCallback callback) { + try { + ProcessBuilder processBuilder = new ProcessBuilder(executable.getAbsolutePath()); + processBuilder.directory(workingDirectory); + processBuilder.redirectErrorStream(true); + + processBuilder.command().addAll(args); + + StringBuilder builder = new StringBuilder(); + for (String arg : args) { + builder.append(arg); + builder.append(" "); + } + String argString = builder.toString().trim(); + + logger.info("Staring process: \n{} {}", executable.getAbsolutePath(), argString); + + Process process = processBuilder.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + callback.onInfoMessage(line); + } + int exitCode = process.waitFor(); + + // Handle errors (if any) + if (exitCode != 0) { + BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + String errorLine; + while ((errorLine = errorReader.readLine()) != null) { + callback.onErrorMessage(errorLine); + } + callback.onFail(exitCode); + } + + callback.onSuccess(); + + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/AndroidPackaging.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/AndroidPackaging.java new file mode 100644 index 00000000..2984125b --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/AndroidPackaging.java @@ -0,0 +1,45 @@ +package com.badlogic.gdx.jnigen.build.packaging; + +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.commons.AndroidABI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class AndroidPackaging extends PlatformPackager { + + + private static final Logger logger = LoggerFactory.getLogger(AndroidPackaging.class); + + @Override + protected void packagePlatformImpl () { + //Each one goes in its own package + for (BuildTarget abi : targetList) { + AndroidABI targetAndroidABI = abi.getTargetAndroidABI(); + + String abiString = targetAndroidABI.getAbiString(); + File outputJar = new File(buildConfig.libsDir.file().getAbsoluteFile(), buildConfig.targetJarBaseName + "-natives-" + abiString + ".jar"); + + File targetBinaryFolder = abi.getTargetBinaryFile(buildConfig).getParentFile(); + + File abiFile = new File(targetBinaryFolder, abiString + File.separator + "lib" + buildConfig.sharedLibName + ".so"); + + if (!abiFile.exists()) { + logger.warn("No build found for abi {}. If this is a specific abi, this could be due to building with a newer NDK", abi); + continue; + } + + try { + Util.JarFiles(outputJar, Collections.singletonList(abiFile)); + } catch (IOException e) { + logger.error("Exception when packing", e); + throw new RuntimeException(e); + } + + logger.info("Packed android platform to {}", outputJar); + } + } +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/DesktopPackaging.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/DesktopPackaging.java new file mode 100644 index 00000000..e909dadc --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/DesktopPackaging.java @@ -0,0 +1,36 @@ +package com.badlogic.gdx.jnigen.build.packaging; + +import com.badlogic.gdx.jnigen.BuildTarget; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class DesktopPackaging extends PlatformPackager { + + private static final Logger logger = LoggerFactory.getLogger(DesktopPackaging.class); + + @Override + protected void packagePlatformImpl () { + List targetBinaries = new ArrayList<>(); + for (BuildTarget buildTarget : targetList) { + targetBinaries.add(buildTarget.getTargetBinaryFile(buildConfig)); + } + + File outputJar = new File(buildConfig.libsDir.file().getAbsoluteFile(), buildConfig.targetJarBaseName + "-natives-desktop.jar"); + + try { + Util.JarFiles(outputJar, targetBinaries); + } catch (IOException e) { + logger.error("Exception when packing", e); + throw new RuntimeException(e); + } + + logger.info("Packed desktop platform to {}", outputJar); + + } + +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/IOSPackaging.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/IOSPackaging.java new file mode 100644 index 00000000..511b2951 --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/IOSPackaging.java @@ -0,0 +1,198 @@ +package com.badlogic.gdx.jnigen.build.packaging; + +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.RobovmBuildConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.HashSet; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +public class IOSPackaging extends PlatformPackager { + + private static final Logger logger = LoggerFactory.getLogger(IOSPackaging.class); + + @Override + protected void packagePlatformImpl () { + + File outputJar = new File(buildConfig.libsDir.file().getAbsoluteFile(), buildConfig.targetJarBaseName + "-natives-ios.jar"); + + //single input file + FileDescriptor frameworkFolder = buildConfig.libsDir.child(buildConfig.sharedLibName + ".xcframework"); + + generateRobovmXML(); + + try { + try (FileOutputStream fileOutputStream = new FileOutputStream(outputJar)) { + JarOutputStream jarOutputStream = new JarOutputStream(fileOutputStream); + + //add the framework + Path sourcePath = frameworkFolder.file().toPath(); + Files.walk(sourcePath).forEach(path -> { + try { + String entryName = sourcePath.relativize(path).toString().replace("\\", "/"); + JarEntry entry = new JarEntry("META-INF/robovm/ios/libs/" + buildConfig.sharedLibName + ".xcframework/" + entryName + (Files.isDirectory(path) ? "/" : "")); + jarOutputStream.putNextEntry(entry); + + // Only copy file contents for regular files + if (Files.isRegularFile(path)) { + Files.copy(path, jarOutputStream); + } + + jarOutputStream.closeEntry(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + //Add the robovm.xml + File robovmXMl = new File(buildConfig.buildDir.file(), "robovm.xml"); + JarEntry robovmEntry = new JarEntry("META-INF/robovm/ios/robovm.xml"); + + jarOutputStream.putNextEntry(robovmEntry); + Files.copy(robovmXMl.toPath(), jarOutputStream); + jarOutputStream.closeEntry(); + + jarOutputStream.close(); + } + } catch (IOException e) { + logger.error("Exception when packing", e); + throw new RuntimeException(e); + } + + logger.info("Packed ios platform to {}", outputJar); + + } + + private void generateRobovmXML () { + File buildDir = buildConfig.buildDir.file(); + File robovmXml = new File(buildDir, "robovm.xml"); + robovmXml.getParentFile().mkdirs(); + + RobovmBuildConfig robovmBuildConfig = buildConfig.robovmBuildConfig; + + if (robovmBuildConfig.getManualFile() != null) { + File manualFile = robovmBuildConfig.getManualFile(); + if (!manualFile.exists()) { + throw new RuntimeException("Manual robovm.xml file does not exist: " + manualFile.getPath()); + } + + try { + Files.copy(manualFile.toPath(), robovmXml.toPath(), StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.COPY_ATTRIBUTES); + } catch (IOException e) { + throw new RuntimeException("Unable to copy manual robovm.xml file to temporary robovm.xml file", e); + } + return; + } + + File oldRobovmXml = new File(buildConfig.jniDir.file(), "robovm.xml"); + if (oldRobovmXml.exists()) { + throw new RuntimeException("Legacy " + oldRobovmXml.getPath() + + " file exists, please define `jnigen.robovm.manualFile = file('jni/robovm.xml')` or migrate to gradle declaration and delete this file."); + } + + try { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + + Document doc = docBuilder.newDocument(); + Element config = doc.createElement("config"); + doc.appendChild(config); + + HashSet addedPaths = new HashSet<>(); + Element frameworkPaths = doc.createElement("frameworkPaths"); + config.appendChild(frameworkPaths); + + Element xcFrameworkPath = doc.createElement("path"); + xcFrameworkPath.setTextContent("libs"); + addedPaths.add("libs"); + + frameworkPaths.appendChild(xcFrameworkPath); + + Element frameworks = doc.createElement("frameworks"); + config.appendChild(frameworks); + + Element framework = doc.createElement("framework"); + framework.setTextContent(buildConfig.sharedLibName); + frameworks.appendChild(framework); + + // Add any extra libraries we have declared + if (!robovmBuildConfig.getExtraLibs().isEmpty()) { + Element libs = doc.createElement("libs"); + for (RobovmBuildConfig.RoboVMXmlLib l : robovmBuildConfig.getExtraLibs()) { + Element lib = doc.createElement("lib"); + if (l.variant != null) + lib.setAttribute("variant", l.variant); + lib.setTextContent(l.path); + libs.appendChild(lib); + } + config.appendChild(libs); + } + + // Add any extra xcFramework we have declared + // TODO: 03.05.23 Rework at somepoint, if xcframeworks tag merging works + if (!robovmBuildConfig.getExtraXCFrameworks().isEmpty()) { + for (String path : robovmBuildConfig.getExtraXCFrameworks()) { + String xcFrameworkName = path.substring(path.lastIndexOf('/') + 1); + xcFrameworkName = xcFrameworkName.substring(0, xcFrameworkName.lastIndexOf('.')); + String frameworkPath = path.substring(0, path.lastIndexOf('/')); + if (!addedPaths.contains(frameworkPath)) { + addedPaths.add(frameworkPath); + Element pathEl = doc.createElement("path"); + pathEl.setTextContent(frameworkPath); + frameworkPaths.appendChild(pathEl); + } + Element frameworkEl = doc.createElement("framework"); + frameworkEl.setTextContent(xcFrameworkName); + frameworks.appendChild(frameworkEl); + } + } + + // Add any forceLinkClasses definitions we have declared + if (!robovmBuildConfig.getForceLinkClasses().isEmpty()) { + Element forceLinkClasses = doc.createElement("forceLinkClasses"); + + for (String p : robovmBuildConfig.getForceLinkClasses()) { + Element pattern = doc.createElement("pattern"); + pattern.setTextContent(p); + forceLinkClasses.appendChild(pattern); + } + + config.appendChild(forceLinkClasses); + } + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(doc); + + FileOutputStream fos = new FileOutputStream(robovmXml); + StreamResult result = new StreamResult(fos); + transformer.setOutputProperty("omit-xml-declaration", "yes"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + transformer.transform(source, result); + fos.close(); + } catch (IOException | ParserConfigurationException | TransformerException e) { + throw new RuntimeException("Unable to create temporary robovm.xml file", e); + } + } +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/Packager.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/Packager.java new file mode 100644 index 00000000..9023e471 --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/Packager.java @@ -0,0 +1,63 @@ +package com.badlogic.gdx.jnigen.build.packaging; + +import com.badlogic.gdx.jnigen.BuildConfig; +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.commons.Platform; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +public class Packager { + + private static Logger logger = LoggerFactory.getLogger(Packager.class); + + /** + * Packager takes in all possible build targets, filters based on what is configured, and bundles into the platform + * specific archives + * + * @param platform + * @param config + * @param totalTargets + */ + public void packagePlatform (Platform platform, BuildConfig config, List totalTargets) { + + List targetList = gatherCompatibleBuildTargets(platform, totalTargets); + + if (targetList.isEmpty()) { + logger.warn("No build targets are compatible with this platform {}", platform); + } + + logger.info("Starting package for platform {}", platform); + + PlatformPackager packaging = getPackaging(platform); + packaging.startPackage(config, targetList); + + } + + private static List gatherCompatibleBuildTargets (Platform platform, List totalTargets) { + List targetList = new ArrayList<>(); + for (BuildTarget buildTarget : totalTargets) { + if (buildTarget.os.getPlatform() == platform) { + targetList.add(buildTarget); + } + } + return targetList; + } + + private PlatformPackager getPackaging (Platform platform) { + switch (platform) { + case Desktop: + return new DesktopPackaging(); + case Android: + return new AndroidPackaging(); + case IOS: + return new IOSPackaging(); + default: + throw new RuntimeException("Packaging for platform " + platform + " is not supported"); + } + } + + +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/PlatformPackager.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/PlatformPackager.java new file mode 100644 index 00000000..9556a4df --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/PlatformPackager.java @@ -0,0 +1,26 @@ +package com.badlogic.gdx.jnigen.build.packaging; + +import com.badlogic.gdx.jnigen.BuildConfig; +import com.badlogic.gdx.jnigen.BuildTarget; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public abstract class PlatformPackager { + + private static final Logger logger = LoggerFactory.getLogger(PlatformPackager.class); + + protected BuildConfig buildConfig; + protected List targetList; + + public void startPackage (BuildConfig config, List targetList) { + this.buildConfig = config; + this.targetList = targetList; + + packagePlatformImpl(); + } + + protected abstract void packagePlatformImpl (); + +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/Util.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/Util.java new file mode 100644 index 00000000..4e5ec864 --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/packaging/Util.java @@ -0,0 +1,29 @@ +package com.badlogic.gdx.jnigen.build.packaging; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +public class Util { + + + public static void JarFiles (File target, List filesToJar) throws IOException { + try (FileOutputStream fileOutputStream = new FileOutputStream(target)) { + JarOutputStream jarOutputStream = new JarOutputStream(fileOutputStream); + + for (File file : filesToJar) { + JarEntry jarEntry = new JarEntry(file.getName()); + + jarOutputStream.putNextEntry(jarEntry); + Files.copy(file.toPath(), jarOutputStream); + jarOutputStream.closeEntry(); + } + jarOutputStream.close(); + } + } +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/AndroidToolchain.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/AndroidToolchain.java new file mode 100644 index 00000000..b7eb62ff --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/AndroidToolchain.java @@ -0,0 +1,132 @@ +package com.badlogic.gdx.jnigen.build.toolchains; + +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.build.ToolFinder; +import com.badlogic.gdx.jnigen.build.ToolchainExecutor; +import com.badlogic.gdx.jnigen.commons.Os; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; + +public class AndroidToolchain extends BaseToolchain { + + + private static final Logger logger = LoggerFactory.getLogger(AndroidToolchain.class); + + private File ndkExecutable; + + @Override + public void checkForTools () { + String ndkHome = System.getenv("NDK_HOME"); + if (ndkHome == null) { + logger.warn("NDK_HOME not set, may not be able to find ndk-build unless you have set it on Path"); + } else { + ENV.addToPath(ndkHome); + } + + ndkExecutable = ToolFinder.getToolFile("ndk-build", ENV, ".cmd", true); + logger.info("Toolchain is valid.\nCompiler: {}", ndkExecutable); + } + + @Override + public void compileTasks () { + if (target.os != Os.Android) throw new IllegalArgumentException("target os must be Android"); + + // create all the directories for outputing object files, shared libs and natives jar as well as build scripts. + if (!config.libsDir.exists()) { + if (!config.libsDir.mkdirs()) + throw new RuntimeException("Couldn't create directory for shared library files in '" + config.libsDir + "'"); + } + if (!config.jniDir.exists()) { + if (!config.jniDir.mkdirs()) + throw new RuntimeException("Couldn't create native code directory '" + config.jniDir + "'"); + } + + + // create Application.mk file + String template = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/Application.mk.template", + FileDescriptor.FileType.Classpath).readString(); + + template = template.replace("%androidABIs%", target.getTargetAndroidABI().getAbiString()); + + for (String extra : target.androidApplicationMk) { + template += "\n" + extra; + } + + config.buildDir.child("android32").child("Application.mk").writeString(template, false); + + // create Android.mk file + template = new FileDescriptor("com/badlogic/gdx/jnigen/resources/scripts/Android.mk.template", FileDescriptor.FileType.Classpath) + .readString(); + + ArrayList allFiles = new ArrayList<>(); + allFiles.addAll(buildCFiles); + allFiles.addAll(buildCppFiles); + + + StringBuilder srcFiles = new StringBuilder(); + for (int i = 0; i < allFiles.size(); i++) { + if (i > 0) { + srcFiles.append("\t"); + } + srcFiles.append(allFiles.get(i).getAbsolutePath()); + if (i < allFiles.size() - 1) { + srcFiles.append("\\\n"); + } else { + srcFiles.append("\n"); + } + } + + StringBuilder headerDirs = new StringBuilder(); + for (String headerDir : target.headerDirs) { + headerDirs.append(convertToAbsoluteRelativeTo(config.projectDir, headerDir)); + headerDirs.append(" "); + } + headerDirs.append(convertToAbsoluteRelativeTo(config.jniDir, "jni-headers")); + headerDirs.append(" " + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/" + target.os.getJniPlatform())); + headerDirs.append(" " + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/" + "linux")); + headerDirs.append(" " + convertToAbsoluteRelativeTo(config.jniDir, ".")); + + template = template.replace("%sharedLibName%", config.sharedLibName); + template = template.replace("%headerDirs%", headerDirs); + template = template.replace("%cFlags%", target.cFlags); + template = template.replace("%cppFlags%", target.cppFlags); + template = template.replace("%linkerFlags%", target.linkerFlags); + template = template.replace("%libraries%", target.libraries); + template = template.replace("%srcFiles%", srcFiles); + for (String extra : target.androidAndroidMk) + template += "\n" + extra; + + config.buildDir.child("android32").child("Android.mk").writeString(template, false); + + ArrayList args = new ArrayList<>(); + if (config.multiThreadedCompile) { + args.add("-j" + Runtime.getRuntime().availableProcessors()); + } + args.add("NDK_PROJECT_PATH=" + buildDirectory.getAbsolutePath()); + args.add("NDK_APPLICATION_MK=" + new File(buildDirectory, "Application.mk").getAbsolutePath()); + args.add("APP_BUILD_SCRIPT=" + new File(buildDirectory, "Android.mk").getAbsolutePath()); + args.add("NDK_OUT=" + buildDirectory.getAbsolutePath()); + args.add("NDK_LIBS_OUT=" + libsDirectory.getAbsolutePath()); + ToolchainExecutor.execute(ndkExecutable, config.jniDir.file(), args, createToolChainCallback("Android compile")); + + } + + @Override + public void collectCompilationTasks () { + //Dont have independent compilation tasks, ndk handles it + } + + @Override + public void link () { + //NDK build handles + } + + @Override + public void strip () { + //NDK build handles + } + +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/BaseToolchain.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/BaseToolchain.java new file mode 100644 index 00000000..0ae81137 --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/BaseToolchain.java @@ -0,0 +1,324 @@ +package com.badlogic.gdx.jnigen.build.toolchains; + +import com.badlogic.gdx.jnigen.AntPathMatcher; +import com.badlogic.gdx.jnigen.BuildConfig; +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.FileDescriptor; +import com.badlogic.gdx.jnigen.build.RuntimeEnv; +import com.badlogic.gdx.jnigen.build.ToolchainExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public abstract class BaseToolchain { + + private static final Logger logger = LoggerFactory.getLogger(BaseToolchain.class); + + protected RuntimeEnv ENV = new RuntimeEnv(); + protected BuildConfig config; + protected BuildTarget target; + + private List sourceJniGeneratedCFiles; + private List sourceJniGeneratedCPPFiles; + + private List sourceIncludedCFiles; + private List sourceIncludedCPPFiles; + + protected List buildCFiles; + protected List buildCppFiles; + + protected File buildDirectory; + protected File libsDirectory; + protected File targetLibFile; + + protected List> compilationTasks; + + BaseToolchain () { + compilationTasks = new ArrayList<>(); + } + + public void configure (BuildTarget target, BuildConfig config) { + this.target = target; + this.config = config; + this.buildDirectory = config.buildDir.child(target.getTargetFolder()).file(); + this.libsDirectory = config.libsDir.child(target.getTargetFolder()).file(); + + this.targetLibFile = target.getTargetBinaryFile(config); + + } + + public abstract void checkForTools (); + + public void build () { + checkForTools(); + + collectCFiles(); + collectCPPFiles(); + cleanBuildDirectory(); + createBuildDirectory(); + + collectCompilationTasks(); + compileTasks(); + + link(); + strip(); + } + + protected void compileTasks () { + if (config.multiThreadedCompile) { + logger.info("Compiling in multithreaded mode"); + + int threads = Runtime.getRuntime().availableProcessors(); + ExecutorService executorService = Executors.newFixedThreadPool(threads); + + ArrayList> futures = new ArrayList<>(); + + for (Callable compilationTask : compilationTasks) { + Future submit = executorService.submit(compilationTask); + futures.add(submit); + } + + //Wait for all futures to complete + for (Future future : futures) { + try { + future.get(); + } catch (Exception e) { + logger.error("Failure to compile task", e); + throw new RuntimeException("Failure to compile task", e); + } + } + executorService.shutdown(); + + + } else { + logger.info("Compiling in single threaded mode"); + + for (Callable task : compilationTasks) { + try { + task.call(); + } catch (Exception e) { + logger.error("Failure to compile task", e); + throw new RuntimeException("Failure to compile task", e); + } + } + } + } + + public abstract void collectCompilationTasks (); + + public abstract void link (); + public abstract void strip (); + + private void collectCFiles () { + File jniDir = config.jniDir.file(); + if (!jniDir.exists()) { + throw new RuntimeException("Jni directory does not exist at path " + config.jniDir.file().getAbsolutePath()); + } + + String[] jniCIncludes = new String[]{"**/*.c"}; + String[] jniCExcludes = new String[]{}; + + String[] cIncludes = target.cIncludes; + String[] cExcludes = target.cExcludes; + + try { + sourceJniGeneratedCFiles = collectFiles(jniDir.toPath(), jniCIncludes, jniCExcludes); + sourceIncludedCFiles = collectFiles(config.projectDir.file().toPath(), cIncludes, cExcludes); + logger.info("Collected C files {}", sourceJniGeneratedCFiles); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void collectCPPFiles () { + File jniDir = config.jniDir.file(); + if (!jniDir.exists()) { + throw new RuntimeException("Jni directory does not exist at path " + config.jniDir.file().getAbsolutePath()); + } + + String[] jniCPPIncludes = new String[]{"**/*.cpp"}; + String[] jniCPPExcludes = new String[]{}; + + String[] cppIncludes = target.cppIncludes; + String[] cppExcludes = target.cppExcludes; + + try { + sourceJniGeneratedCPPFiles = collectFiles(jniDir.toPath(), jniCPPIncludes, jniCPPExcludes); + sourceIncludedCPPFiles = collectFiles(config.projectDir.file().toPath(), cppIncludes, cppExcludes); + logger.info("Collected CPP files {}", sourceJniGeneratedCPPFiles); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + private void cleanBuildDirectory () { + + logger.info("Cleaning build directory {} - abs {}", buildDirectory, buildDirectory.getAbsolutePath()); + logger.info("Cleaning libs directory {} - abs {}", libsDirectory, libsDirectory.getAbsolutePath()); + + logger.error("Not actually wiping directories yet"); + } + + private void createBuildDirectory () { + logger.info("Creating build directory and copying source includes"); + + buildDirectory.mkdirs(); + if (!buildDirectory.exists()) { + logger.error("Failure making file {}", buildDirectory.getAbsolutePath()); + throw new RuntimeException("Failure to create build dir"); + } + + try { + //copy the source files all over + buildCFiles = new ArrayList<>(); + buildCppFiles = new ArrayList<>(); + for (File collectedCFile : sourceJniGeneratedCFiles) { + File targetFile = new File(buildDirectory, collectedCFile.getName()); + buildCFiles.add(targetFile); + Files.copy(collectedCFile.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + for (File collectedCppFile : sourceJniGeneratedCPPFiles) { + File targetFile = new File(buildDirectory, collectedCppFile.getName()); + buildCppFiles.add(targetFile); + Files.copy(collectedCppFile.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + //for includes, we don't copy + buildCFiles.addAll(sourceIncludedCFiles); + buildCppFiles.addAll(sourceIncludedCPPFiles); + + } catch (IOException e) { + logger.error("Failure to copy files into build directory", e); + throw new RuntimeException("Failure to copy files into build directory"); + } + + } + + public static List collectFiles (Path rootDir, String[] includes, String[] excludes) throws IOException { + List matchedFiles = new ArrayList<>(); + + + Files.walkFileTree(rootDir, new FileVisitor() { + @Override + public FileVisitResult preVisitDirectory (Path dir, BasicFileAttributes attrs) { + if (dir == rootDir) { + return FileVisitResult.CONTINUE; + } + + + //Todo skip looking at directories we have no business looking at + Path relativePath = rootDir.relativize(dir); + String relativePathString = relativePath.toString(); + if (relativePathString.equalsIgnoreCase(".git")) { + return FileVisitResult.SKIP_SUBTREE; + } + if (relativePathString.equalsIgnoreCase(".gradle")) { + return FileVisitResult.SKIP_SUBTREE; + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile (Path file, BasicFileAttributes attrs) { + Path relativePath = rootDir.relativize(file); + + String relativePathString = relativePath.toString().replace("\\", "/"); + + + boolean isInclude = AntPathMatcher.match(relativePathString, includes); + + if (isInclude) { + boolean isExclude = false; + if (excludes.length > 0) { + isExclude = AntPathMatcher.match(relativePathString, excludes); + } + + if (!isExclude) { + logger.trace("Including file {}", file); + matchedFiles.add(file.toFile()); + } else { + logger.trace("Ignoring file as included, but marked by exclude {}", file); + } + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed (Path file, IOException exc) { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory (Path dir, IOException exc) { + return FileVisitResult.CONTINUE; + } + }); + + return matchedFiles; + } + + + + protected String convertToAbsoluteRelativeTo (FileDescriptor rootRelative, String path) { + //Path might be a relative path or an absolute, if its absolute just wrap, otherwise convert it to absolute + //based around our project directory + + File file = new File(path); + if (!file.isAbsolute()) { + file = new File(rootRelative.file(), path); + } else { + file = file.getAbsoluteFile(); + } + return file.getAbsolutePath(); + } + + protected Collection stringFlagsToArgs (String flagsEmbeddedinString) { + String[] split = flagsEmbeddedinString.trim().split("\\s+"); + ArrayList args = new ArrayList<>(Arrays.asList(split)); + return args; + } + + protected ToolchainExecutor.ToolchainCallback createToolChainCallback (String tag) { + return new ToolchainExecutor.ToolchainCallback() { + @Override + public void onInfoMessage (String message) { + System.out.println(message); + } + + @Override + public void onErrorMessage (String message) { + System.err.println(message); + } + + @Override + public void onSuccess () { + logger.info("{} complete", tag); + } + + @Override + public void onFail (int statusCode) { + logger.error("Failure to {} target {} with status {}", tag, target, statusCode); + throw new RuntimeException("Failed to " + tag); + } + }; + } + + public File getTargetLibFile () { + return targetLibFile; + } +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/GNUToolchain.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/GNUToolchain.java new file mode 100644 index 00000000..bb134dc3 --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/GNUToolchain.java @@ -0,0 +1,182 @@ +package com.badlogic.gdx.jnigen.build.toolchains; + +import com.badlogic.gdx.jnigen.build.ToolFinder; +import com.badlogic.gdx.jnigen.build.ToolchainExecutor; +import com.badlogic.gdx.jnigen.commons.Os; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +/** + * Toolchain that compiles c/c++ using gcc/g++/clang toolchains + */ +public class GNUToolchain extends BaseToolchain { + + private static final Logger logger = LoggerFactory.getLogger(GNUToolchain.class); + + private File cCompilerExecutable; + private File cppCompilerExecutable; + private File linkerExecutable; + private File stripperExecutable; + + @Override + public void checkForTools () { + String cCompiler = target.compilerPrefix + target.cCompiler + target.compilerSuffix; + String cppCompiler = target.compilerPrefix + target.cppCompiler + target.compilerSuffix; + String linker = target.compilerPrefix + target.cppCompiler + target.compilerSuffix; + String stripper = target.compilerPrefix + "strip" + target.compilerSuffix; + + //Check that these actually exist! + cCompilerExecutable = ToolFinder.getToolFile(cCompiler, ENV, ".exe", true); + cppCompilerExecutable = ToolFinder.getToolFile(cppCompiler, ENV, ".exe", true); + linkerExecutable = ToolFinder.getToolFile(linker, ENV, ".exe", true); + + //Don't strip on Mac or in non release mode + stripperExecutable = ToolFinder.getToolFile(stripper, ENV, ".exe", target.release || target.os == Os.MacOsX); + + logger.info("Toolchain is valid.\ncCompiler: {}\ncppCompiler: {}\nlinker: {}\nstripper: {}", cCompilerExecutable, cppCompilerExecutable, linkerExecutable, stripperExecutable); + } + + + @Override + public void collectCompilationTasks () { + collectCCompileTasks(); + collectCPPCompileTasks(); + } + + + private void collectCCompileTasks () { + if (buildCFiles.isEmpty()) { + logger.info("No c files, skipping c compilation"); + return; + } + + List args = new ArrayList<>(); + args.addAll(stringFlagsToArgs(target.cFlags)); + + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/")); + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/" + target.os.getJniPlatform())); + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, ".")); + for (String headerDir : target.headerDirs) { + args.add("-I" + convertToAbsoluteRelativeTo(config.projectDir, headerDir)); + } + + + for (File buildCFile : buildCFiles) { + ArrayList perFileArgs = new ArrayList(); + perFileArgs.addAll(args); + + perFileArgs.add(buildCFile.getAbsolutePath()); + perFileArgs.add("-o"); + perFileArgs.add(new File(buildDirectory, toObjectFile(buildCFile.getName())).getAbsolutePath()); + + compilationTasks.add(new Callable() { + @Override + public Void call () throws Exception { + logger.info("Compiling C File {}", buildCFile.getAbsolutePath()); + ToolchainExecutor.execute(cCompilerExecutable, config.jniDir.file(), perFileArgs, createToolChainCallback("C Compile - " + buildCFile.getName())); + return null; + } + }); + } + } + + private void collectCPPCompileTasks () { + if (buildCppFiles.isEmpty()) { + logger.info("No c files, skipping c compilation"); + return; + } + + List args = new ArrayList<>(); + args.addAll(stringFlagsToArgs(target.cppFlags)); + + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/")); + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/" + target.os.getJniPlatform())); + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, ".")); + for (String headerDir : target.headerDirs) { + args.add("-I" + convertToAbsoluteRelativeTo(config.projectDir, headerDir)); + } + + for (File buildCppFile : buildCppFiles) { + ArrayList perFileArgs = new ArrayList(); + perFileArgs.addAll(args); + + perFileArgs.add(buildCppFile.getAbsolutePath()); + perFileArgs.add("-o"); + perFileArgs.add(new File(buildDirectory, toObjectFile(buildCppFile.getName())).getAbsolutePath()); + + compilationTasks.add(new Callable() { + @Override + public Void call () throws Exception { + logger.info("Compiling CPP File {}", buildCppFile.getAbsolutePath()); + ToolchainExecutor.execute(cppCompilerExecutable, config.jniDir.file(), perFileArgs, createToolChainCallback("CPP Compile - " + buildCppFile.getName())); + return null; + } + }); + + } + + } + + private String toObjectFile (String name) { + String[] split = name.split("\\."); + return split[0] + ".o"; + } + + @Override + public void link () { + + libsDirectory.mkdirs(); + if (!libsDirectory.exists()) { + logger.error("Error creating libs dir {}", libsDirectory.getAbsolutePath()); + throw new RuntimeException("Error creating libs dir"); + } + + + ArrayList objFiles = new ArrayList<>(); + for (File cObjectFile : buildCFiles) { + objFiles.add(new File(buildDirectory, toObjectFile(cObjectFile.getName()))); + } + for (File cppObjhectFile : buildCppFiles) { + objFiles.add(new File(buildDirectory, toObjectFile(cppObjhectFile.getName()))); + } + + List args = new ArrayList<>(); + + args.addAll(stringFlagsToArgs(target.linkerFlags)); + args.add("-o"); + args.add(targetLibFile.getAbsolutePath()); + for (File objFile : objFiles) { + args.add(objFile.getAbsolutePath()); + } + if (!target.libraries.isEmpty()) { + args.addAll(stringFlagsToArgs(target.libraries)); + } + + ToolchainExecutor.execute(linkerExecutable, config.buildDir.file(), args, createToolChainCallback("Link")); + } + + @Override + public void strip () { + if (!target.release) { + logger.info("Skipping strip due to release property {}", target.release); + return; + } + if (target.os == Os.MacOsX) { + logger.info("Skipping strip due to target being {}", target.os); + return; + } + + //Otherwise we strip it ye boooi + + ArrayList args = new ArrayList<>(); + args.add("--strip-unneeded"); + args.add(targetLibFile.getAbsolutePath()); + + ToolchainExecutor.execute(stripperExecutable, buildDirectory, args, createToolChainCallback("Strip")); + } +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/IOSToolchain.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/IOSToolchain.java new file mode 100644 index 00000000..881811ef --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/IOSToolchain.java @@ -0,0 +1,227 @@ +package com.badlogic.gdx.jnigen.build.toolchains; + +import com.badlogic.gdx.jnigen.build.ToolFinder; +import com.badlogic.gdx.jnigen.build.ToolchainExecutor; +import com.badlogic.gdx.jnigen.commons.Architecture; +import com.badlogic.gdx.jnigen.commons.TargetType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +/** + * Toolchain that compiles c/c++ using gcc/g++/clang toolchains + */ +public class IOSToolchain extends BaseToolchain { + + private static final Logger logger = LoggerFactory.getLogger(GNUToolchain.class); + + private File cCompilerExecutable; + private File cppCompilerExecutable; + private File linkerExecutable; + private File iphoneSDK; + private File simSDK; + + @Override + public void checkForTools () { + String cCompiler = target.compilerPrefix + target.cCompiler + target.compilerSuffix; + String cppCompiler = target.compilerPrefix + target.cppCompiler + target.compilerSuffix; + + //Check that these actually exist! + cCompilerExecutable = ToolFinder.getToolFile(cCompiler, ENV, ".exe", true); + cppCompilerExecutable = ToolFinder.getToolFile(cppCompiler, ENV, ".exe", true); + linkerExecutable = cppCompilerExecutable; + + iphoneSDK = ToolFinder.getSDK("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/", true); + simSDK = ToolFinder.getSDK("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk", true); + + logger.info("Toolchain is valid.\ncCompiler: {}\ncppCompiler: {}\nlinker: {}\niOS SDK: {}\niOS Sim SDK: {}", cCompilerExecutable, cppCompilerExecutable, linkerExecutable, iphoneSDK, simSDK); + } + + + @Override + public void collectCompilationTasks () { + collectCCompileTasks(); + collectCPPCompileTasks(); + } + + private String getClangArch () { + if (target.architecture == Architecture.ARM) { + return "arm64"; + } + if (target.architecture == Architecture.x86) { + return "x86_64"; + } + throw new RuntimeException("Unsupported arch"); + } + + + private void collectCCompileTasks () { + if (buildCFiles.isEmpty()) { + logger.info("No c files, skipping c compilation"); + return; + } + + List args = new ArrayList<>(); + + args.add("-isysroot"); + if (target.targetType == TargetType.DEVICE) { + args.add(iphoneSDK.getAbsolutePath()); + } else { + args.add(simSDK.getAbsolutePath()); + } + + args.add("-arch"); + args.add(getClangArch()); + + String iosVesion = target.targetType == TargetType.DEVICE ? "-miphoneos-version-min" : "-mios-simulator-version-min"; + args.add(iosVesion + "=" + config.robovmBuildConfig.minIOSVersion); + + args.addAll(stringFlagsToArgs(target.cFlags)); + + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers")); + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/" + target.os.getJniPlatform())); + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, ".")); + for (String headerDir : target.headerDirs) { + args.add("-I" + convertToAbsoluteRelativeTo(config.projectDir, headerDir)); + } + + args.add("-g"); + + for (File buildCFile : buildCFiles) { + ArrayList perFileArgs = new ArrayList(); + perFileArgs.addAll(args); + + perFileArgs.add(buildCFile.getAbsolutePath()); + perFileArgs.add("-o"); + perFileArgs.add(new File(buildDirectory, toObjectFile(buildCFile.getName())).getAbsolutePath()); + + + compilationTasks.add(new Callable() { + @Override + public Void call () throws Exception { + logger.info("Compiling C File {}", buildCFile.getAbsolutePath()); + ToolchainExecutor.execute(cCompilerExecutable, config.jniDir.file(), perFileArgs, createToolChainCallback("C Compile - " + buildCFile.getName())); + return null; + } + }); + } + } + + private void collectCPPCompileTasks () { + if (buildCppFiles.isEmpty()) { + logger.info("No c files, skipping c compilation"); + return; + } + + List args = new ArrayList<>(); + + args.add("-isysroot"); + if (target.targetType == TargetType.DEVICE) { + args.add(iphoneSDK.getAbsolutePath()); + } else { + args.add(simSDK.getAbsolutePath()); + } + + args.add("-arch"); + args.add(getClangArch()); + + String iosVesion = target.targetType == TargetType.DEVICE ? "-miphoneos-version-min" : "-mios-simulator-version-min"; + args.add(iosVesion + "=" + config.robovmBuildConfig.minIOSVersion); + + args.addAll(stringFlagsToArgs(target.cppFlags)); + + + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers")); + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/" + target.os.getJniPlatform())); + args.add("-I" + convertToAbsoluteRelativeTo(config.jniDir, ".")); + for (String headerDir : target.headerDirs) { + args.add("-I" + convertToAbsoluteRelativeTo(config.projectDir, headerDir)); + } + + args.add("-g"); + + for (File buildCppFile : buildCppFiles) { + ArrayList perFileArgs = new ArrayList(); + perFileArgs.addAll(args); + + perFileArgs.add(buildCppFile.getAbsolutePath()); + perFileArgs.add("-o"); + perFileArgs.add(new File(buildDirectory, toObjectFile(buildCppFile.getName())).getAbsolutePath()); + + compilationTasks.add(new Callable() { + @Override + public Void call () throws Exception { + logger.info("Compiling CPP File {}", buildCppFile.getAbsolutePath()); + ToolchainExecutor.execute(cppCompilerExecutable, config.jniDir.file(), perFileArgs, createToolChainCallback("CPP Compile - " + buildCppFile.getName())); + return null; + } + }); + } + + } + + private String toObjectFile (String name) { + String[] split = name.split("\\."); + return split[0] + ".o"; + } + + @Override + public void link () { + + libsDirectory.mkdirs(); + if (!libsDirectory.exists()) { + logger.error("Error creating libs dir {}", libsDirectory.getAbsolutePath()); + throw new RuntimeException("Error creating libs dir"); + } + + + ArrayList objFiles = new ArrayList<>(); + for (File cObjectFile : buildCFiles) { + objFiles.add(new File(buildDirectory, toObjectFile(cObjectFile.getName()))); + } + for (File cppObjhectFile : buildCppFiles) { + objFiles.add(new File(buildDirectory, toObjectFile(cppObjhectFile.getName()))); + } + + List args = new ArrayList<>(); + + args.add("-isysroot"); + if (target.targetType == TargetType.DEVICE) { + args.add(iphoneSDK.getAbsolutePath()); + } else { + args.add(simSDK.getAbsolutePath()); + } + + args.add("-arch"); + args.add(getClangArch()); + + String iosVesion = target.targetType == TargetType.DEVICE ? "-miphoneos-version-min" : "-mios-simulator-version-min"; + args.add(iosVesion + "=" + config.robovmBuildConfig.minIOSVersion); + + args.addAll(stringFlagsToArgs(target.linkerFlags)); + args.add("-install_name"); + args.add("@rpath/" + config.sharedLibName + ".framework/" + config.sharedLibName); + + args.add("-o"); + args.add(targetLibFile.getAbsolutePath()); + for (File objFile : objFiles) { + args.add(objFile.getAbsolutePath()); + } + if (!target.libraries.isEmpty()) { + args.addAll(stringFlagsToArgs(target.libraries)); + } + + ToolchainExecutor.execute(linkerExecutable, config.buildDir.file(), args, createToolChainCallback("Link")); + } + + @Override + public void strip () { + + } + + +} diff --git a/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/MSVCToolchain.java b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/MSVCToolchain.java new file mode 100644 index 00000000..185416da --- /dev/null +++ b/compiling/gdx-jnigen/src/main/java/com/badlogic/gdx/jnigen/build/toolchains/MSVCToolchain.java @@ -0,0 +1,339 @@ +package com.badlogic.gdx.jnigen.build.toolchains; + +import com.badlogic.gdx.jnigen.build.ToolFinder; +import com.badlogic.gdx.jnigen.build.ToolchainExecutor; +import com.badlogic.gdx.jnigen.commons.Architecture; +import com.badlogic.gdx.jnigen.commons.HostDetection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +/** + * Toolchain that compiles c/c++ using msvc toolchain + */ +public class MSVCToolchain extends BaseToolchain { + + private static final Logger logger = LoggerFactory.getLogger(MSVCToolchain.class); + + private File cCompilerExecutable; + private File cppCompilerExecutable; + private File linkerExecutable; + private File cmdExecutable; + + private String msvcIncludePath; + private String msvcLibPath; + private String msvcPathPath; + private String[] msvcIncludeDirectories; + private String[] msvcLibDirectories; + + @Override + public void checkForTools () { + //Lets configure our environment with vcvarsall + ToolFinder.getToolFile("vcvarsall.bat", ENV, true); + cmdExecutable = ToolFinder.getToolFile("cmd.exe", ENV, true); + + executeAndExtractVcVarsAll(); + + + String cCompiler = target.compilerPrefix + target.cCompiler + target.compilerSuffix; + String cppCompiler = target.compilerPrefix + target.cppCompiler + target.compilerSuffix; + String linker = target.compilerPrefix + target.cppCompiler + target.compilerSuffix; + + //Check that these actually exist! + cCompilerExecutable = ToolFinder.getToolFile(cCompiler, ENV, true); + cppCompilerExecutable = ToolFinder.getToolFile(cppCompiler, ENV, true); + linkerExecutable = ToolFinder.getToolFile(linker, ENV, true); + + logger.info("Toolchain is valid.\ncCompiler: {}\ncppCompiler: {}\nlinker: {}", cCompilerExecutable, cppCompilerExecutable, linkerExecutable); + } + + private String getVcVarsAllTarget () { + boolean isHostARM = HostDetection.architecture == Architecture.ARM; + boolean isHostX86 = HostDetection.architecture == Architecture.x86; + + boolean isTargetARM = target.architecture == Architecture.ARM; + boolean isTargetX86 = target.architecture == Architecture.x86; + + boolean isHost32Bit = HostDetection.bitness == Architecture.Bitness._32; + boolean isHost64Bit = HostDetection.bitness == Architecture.Bitness._64; + + boolean isTarget32Bit = target.bitness == Architecture.Bitness._32; + boolean isTarget64Bit = target.bitness == Architecture.Bitness._64; + + if (isHostX86 && isHost64Bit) { + if (isTargetX86 && isTarget32Bit) { + return "x86"; + } else if (isTargetX86 && isTarget64Bit) { + return "x64"; + } else if (isTargetARM && isTarget32Bit) { + return "amd64_arm"; + } else if (isTargetARM && isTarget64Bit) { + return "amd64_arm64"; + } + } else if (isHostX86 && isHost32Bit) { + if (isTargetX86 && isTarget32Bit) { + return "x86"; + } else if (isTargetX86 && isTarget64Bit) { + return "x64"; + } else if (isTargetARM && isTarget32Bit) { + return "x86_arm"; + } else if (isTargetARM && isTarget64Bit) { + return "x86_arm64"; + } + } else if (isHostARM && isHost32Bit) { + if (isTargetX86 && isTarget32Bit) { + return "x86"; + } else if (isTargetX86 && isTarget64Bit) { + return "x64"; + } else if (isTargetARM && isTarget32Bit) { + return "arm"; + } else if (isTargetARM && isTarget64Bit) { + return "arm64"; + } + } else if (isHostARM && isHost64Bit) { + if (isTargetX86 && isTarget32Bit) { + return "x86"; + } else if (isTargetX86 && isTarget64Bit) { + return "x64"; + } else if (isTargetARM && isTarget32Bit) { + return "arm"; + } else if (isTargetARM && isTarget64Bit) { + return "arm64"; + } + } + throw new UnsupportedOperationException("Unsupported host or target architecture combination"); + + } + + + private void executeAndExtractVcVarsAll () { + ArrayList args = new ArrayList<>(); + + final String vcVarsAllTarget = getVcVarsAllTarget(); + + args.add("/c"); + args.add("call"); + args.add("vcvarsall.bat"); + args.add(vcVarsAllTarget); + args.add("&&"); + args.add("set"); + + List pathLines = new ArrayList<>(); + + ToolchainExecutor.execute(cmdExecutable, config.jniDir.file(), args, new ToolchainExecutor.ToolchainCallback() { + @Override + public void onInfoMessage (String message) { + if (message.contains("=")) { + if (message.startsWith("INCLUDE=")) { + msvcIncludePath = message.split("=")[1]; + } + if (message.startsWith("LIB=")) { + msvcLibPath = message.split("=")[1]; + System.out.println(); + } + if (message.startsWith("Path=")) { + msvcPathPath = message.split("=")[1]; + } + pathLines.add(message); + } + } + + @Override + public void onErrorMessage (String message) { + System.err.println(message); + } + + @Override + public void onSuccess () { + logger.info("Completed vcvarsall"); + } + + @Override + public void onFail (int statusCode) { + logger.error("Failure to execute vcvarsall with target {} - status {}", vcVarsAllTarget, statusCode); + throw new RuntimeException("Failed to execute vcvarsall"); + } + }); + + if (msvcIncludePath == null) { + throw new RuntimeException("MSVC InludePath is null"); + } + if (msvcLibPath == null) { + throw new RuntimeException("MSVC Lib path is null"); + } + if (msvcPathPath == null) { + throw new RuntimeException("MSVC Path is null"); + } + + ENV.addToPath(msvcPathPath); + + msvcIncludeDirectories = ENV.splitToPaths(msvcIncludePath); + if (msvcIncludeDirectories == null) { + logger.error("MSVC include path cannot be parsed {}", msvcIncludePath); + throw new RuntimeException("Failure to parse MSVC include path"); + } + + msvcLibDirectories = ENV.splitToPaths(msvcLibPath); + if (msvcLibDirectories == null) { + logger.error("MSVC lib directories path cannot be parsed {}", msvcLibPath); + throw new RuntimeException("Failure to parse MSVC lib path"); + } + + } + + @Override + public void collectCompilationTasks () { + collectCCompileTasks(); + collectCPPCompileTasks(); + } + + private void collectCCompileTasks () { + if (buildCFiles.isEmpty()) { + logger.info("No c files, skipping c compilation"); + return; + } + + List args = new ArrayList<>(); + args.addAll(stringFlagsToArgs(target.cFlags)); + for (String msvcIncludeDirectory : msvcIncludeDirectories) { + args.add("/I" + msvcIncludeDirectory); + } + args.add("/I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers")); + args.add("/I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/" + target.os.getJniPlatform())); + args.add("/I" + convertToAbsoluteRelativeTo(config.jniDir, ".")); + for (String headerDir : target.headerDirs) { + args.add("/I" + convertToAbsoluteRelativeTo(config.projectDir, headerDir)); + } + + for (File buildCFile : buildCFiles) { + ArrayList perFileArgs = new ArrayList(); + perFileArgs.addAll(args); + + perFileArgs.add(buildCFile.getAbsolutePath()); + perFileArgs.add("/Fo:" + new File(buildDirectory, toObjectFile(buildCFile.getName())).getAbsolutePath()); + perFileArgs.add("/c"); + perFileArgs.add("/EHsc"); + + compilationTasks.add(new Callable() { + @Override + public Void call () throws Exception { + logger.info("Compiling C File {}", buildCFile.getAbsolutePath()); + ToolchainExecutor.execute(cCompilerExecutable, config.jniDir.file(), perFileArgs, createToolChainCallback("C Compile - " + buildCFile.getName())); + return null; + } + }); + } + } + + private void collectCPPCompileTasks () { + if (buildCppFiles.isEmpty()) { + logger.info("No cpp files, skipping cpp compilation"); + return; + } + + List args = new ArrayList<>(); + args.addAll(stringFlagsToArgs(target.cppFlags)); + for (String msvcIncludeDirectory : msvcIncludeDirectories) { + args.add("/I" + msvcIncludeDirectory); + } + args.add("/I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers")); + args.add("/I" + convertToAbsoluteRelativeTo(config.jniDir, "jni-headers/" + target.os.getJniPlatform())); + args.add("/I" + convertToAbsoluteRelativeTo(config.jniDir, ".")); + for (String headerDir : target.headerDirs) { + args.add("/I" + convertToAbsoluteRelativeTo(config.projectDir, headerDir)); + } + + for (File buildCppFile : buildCppFiles) { + ArrayList perFileArgs = new ArrayList(); + perFileArgs.addAll(args); + + perFileArgs.add(buildCppFile.getAbsolutePath()); + perFileArgs.add("/Fo:" + new File(buildDirectory, toObjectFile(buildCppFile.getName())).getAbsolutePath()); + perFileArgs.add("/c"); + perFileArgs.add("/EHsc"); + + compilationTasks.add(new Callable() { + @Override + public Void call () throws Exception { + logger.info("Compiling CPP File {}", buildCppFile.getAbsolutePath()); + ToolchainExecutor.execute(cppCompilerExecutable, config.jniDir.file(), perFileArgs, createToolChainCallback("CPP Compile - " + buildCppFile.getName())); + return null; + } + }); + } + + } + + private String toObjectFile (String name) { + String[] split = name.split("\\."); + return split[0] + ".obj"; + } + + @Override + public void link () { + + libsDirectory.mkdirs(); + if (!libsDirectory.exists()) { + logger.error("Error creating libs dir {}", libsDirectory.getAbsolutePath()); + throw new RuntimeException("Error creating libs dir"); + } + + + ArrayList objFiles = new ArrayList<>(); + for (File cObjectFile : buildCFiles) { + objFiles.add(new File(buildDirectory, toObjectFile(cObjectFile.getName()))); + } + for (File cppObjhectFile : buildCppFiles) { + objFiles.add(new File(buildDirectory, toObjectFile(cppObjhectFile.getName()))); + } + + List args = new ArrayList<>(); + + if (target.release) { + args.add("/LD"); + } else { + args.add("/LDd"); + args.add("/Zi"); + } + + if (!target.msvcPreLinkerFlags.isEmpty()) { + args.addAll(stringFlagsToArgs(target.msvcPreLinkerFlags)); + } + + args.add("/Fe" + targetLibFile.getName()); + for (File objFile : objFiles) { + args.add(objFile.getAbsolutePath()); + } + + args.add("/link"); + + if (target.release) { + args.add("/RELEASE"); + args.add("/NOCOFFGRPINFO"); + } else { + args.add("/DEBUG"); + } + + for (String msvcLibDirectory : msvcLibDirectories) { + args.add("/LIBPATH:" + msvcLibDirectory); + } + args.addAll(stringFlagsToArgs(target.linkerFlags)); + if (!target.libraries.isEmpty()) { + args.addAll(stringFlagsToArgs(target.libraries)); + } + + logger.info("Linking Target {}", target); + + ToolchainExecutor.execute(linkerExecutable, libsDirectory, args, createToolChainCallback("Link")); + } + + @Override + public void strip () { + //Handled by linker + } + +} diff --git a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/Android.mk.template b/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/Android.mk.template index de60a273..5bcaf470 100644 --- a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/Android.mk.template +++ b/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/Android.mk.template @@ -6,8 +6,9 @@ LOCAL_C_INCLUDES := %headerDirs% LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%) %cFlags% LOCAL_CPPFLAGS := $(LOCAL_C_INCLUDES:%=-I%) %cppFlags% -LOCAL_LDLIBS := %linkerFlags% +LOCAL_LDFLAGS := %linkerFlags% +LOCAL_LDLIBS := %libraries% LOCAL_ARM_MODE := arm - -LOCAL_SRC_FILES := %srcFiles% + +LOCAL_SRC_FILES := %srcFiles% include $(BUILD_SHARED_LIBRARY) diff --git a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-android.xml.template b/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-android.xml.template deleted file mode 100644 index a60327e0..00000000 --- a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-android.xml.template +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - %precompile% - - - - ndk_home: ${env.NDK_HOME} - - - - - %postcompile% - - - - - - - - - - - - diff --git a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-ios.xml.template b/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-ios.xml.template deleted file mode 100755 index 0d83f7f9..00000000 --- a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-ios.xml.template +++ /dev/null @@ -1,357 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %cIncludes% - %cExcludes% - - - - - - - - %cppIncludes% - %cppExcludes% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %headerDirs% - - - - - - - - - - - - - - - %headerDirs% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %headerDirs% - - - - - - - - - - - - - - - %headerDirs% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %headerDirs% - - - - - - - - - - - - - - - %headerDirs% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %postcompile% - - diff --git a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-target.xml.template b/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-target.xml.template deleted file mode 100644 index 0f7223d8..00000000 --- a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build-target.xml.template +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - %cIncludes% - %cExcludes% - - - - - - - - %cppIncludes% - %cppExcludes% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %precompile% - - - - - - - - - - - - - - - - - - - - - - - - - %headerDirs% - - - - - - - - - - - - - - %headerDirs% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %postcompile% - - diff --git a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build.xml.template b/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build.xml.template deleted file mode 100644 index 642ba7cc..00000000 --- a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/build.xml.template +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/jnigen.h b/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/jnigen.h deleted file mode 100644 index 8cc5a20e..00000000 --- a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/jnigen.h +++ /dev/null @@ -1,138 +0,0 @@ -#include -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include -#include -#include - -// Helper macro for platform-specific thread attachment -// Stolen from https://github.com/rednblackgames/gdx-miniaudio -#ifdef __ANDROID__ -#define THREAD_ATTACH_MACRO gJVM->AttachCurrentThread(&env, NULL); -#else -#define THREAD_ATTACH_MACRO gJVM->AttachCurrentThread((void**)&env, NULL); -#endif - -#define ATTACH_ENV() \ - bool _hadToAttach = false; \ - JNIEnv* env; \ - if (gJVM->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_EDETACHED) { \ - THREAD_ATTACH_MACRO \ - _hadToAttach = true; \ - } - -#define DETACH_ENV() \ - if (_hadToAttach) { \ - gJVM->DetachCurrentThread(); \ - } - -#define HANDLE_JAVA_EXCEPTION_START() try { - -#define HANDLE_JAVA_EXCEPTION_END() } catch (const JavaExceptionMarker& e) { env->Throw(e.javaExc); } \ - catch (const std::exception& ex) { env->ThrowNew(cxxExceptionClass, ex.what()); } \ - catch (...) { env->ThrowNew(cxxExceptionClass, "An unknown error occurred"); } - -class JavaExceptionMarker : public std::runtime_error { - public: - jthrowable javaExc; - JavaVM* gJVM; - JavaExceptionMarker(JavaVM* passedJVM, jthrowable exc, const std::string& message) : std::runtime_error(message) { - gJVM = passedJVM; - ATTACH_ENV() // TODO: We could save this by doing this in the caller, but that seems overoptimization? - javaExc = (jthrowable)env->NewGlobalRef(exc); - DETACH_ENV() - } - - ~JavaExceptionMarker() throw() { - ATTACH_ENV() // TODO: Figure out, whether this is an issue during full-crash - env->DeleteGlobalRef(javaExc); - DETACH_ENV() - } -}; - -typedef enum _native_type_id { - VOID_TYPE = 1, - INT_TYPE, - FLOAT_TYPE, - DOUBLE_TYPE, - STRUCT_TYPE, - UNION_TYPE, - POINTER_TYPE -} native_type_id; - -typedef struct _native_type { - native_type_id type; - int size; - bool sign; - // If we are a struct/union - int field_count; - _native_type** fields; -} native_type; - -static inline void set_native_type(native_type* nat_type, native_type_id id, size_t size, bool sign) { - nat_type->type = id; - nat_type->size = size; - nat_type->sign = sign; -} - -#define IS_SIGNED_TYPE(type) (((type)-1) < 0) - -#ifdef __cplusplus -template -struct native_type_traits; - -template<> -struct native_type_traits { - static void get(native_type* nat_type) { - set_native_type(nat_type, DOUBLE_TYPE, sizeof(double), true); - } -}; - -template<> -struct native_type_traits { - static void get(native_type* nat_type) { - set_native_type(nat_type, FLOAT_TYPE, sizeof(float), true); - } -}; - -template -struct native_type_traits { - static void get(native_type* nat_type) { - set_native_type(nat_type, INT_TYPE, sizeof(T), std::is_signed::value); - } -}; - -template -void get_native_type(native_type* nat_type) { - native_type_traits::get(nat_type); -} - -#define GET_NATIVE_TYPE(type, nat_type) get_native_type(nat_type) - -#else // C code - -#define GET_NATIVE_TYPE(type, nat_type) \ - _Generic((type){0}, \ - double: set_native_type(nat_type, DOUBLE_TYPE, 8, true), \ - float: set_native_type(nat_type, FLOAT_TYPE, 4, true), \ - default: set_native_type(nat_type, INT_TYPE, sizeof(type), IS_SIGNED_TYPE(type)) \ - ) - -#endif //__cplusplus - -#define CHECK_AND_THROW_C_TYPE(env, type, value, argument_index, returnAction) \ - bool _signed##argument_index = IS_SIGNED_TYPE(type); \ - int _size##argument_index = sizeof(type); \ - if (!CHECK_BOUNDS_FOR_NUMBER(value, _size##argument_index, _signed##argument_index)) { \ - char buffer[1024]; \ - snprintf(buffer, sizeof(buffer), "Value %" PRId64 " is out of bound for size %d and signess %d on argument %d", (jlong)value, _size##argument_index, _signed##argument_index, argument_index); \ - env->ThrowNew(illegalArgumentExceptionClass, buffer); \ - returnAction; \ - } - -#define CHECK_BOUNDS_FOR_NUMBER(value, size, is_signed) \ - ((size) == 8 ? true : \ - (!(is_signed)) ? ((uint64_t)(value) < (1ULL << ((size) << 3))) : \ - ((int64_t)(value) >= (-1LL << (((size) << 3) - 1)) && (int64_t)(value) <= ((1LL << (((size) << 3) - 1)) - 1))) diff --git a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/memcpy_wrap.c b/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/memcpy_wrap.c deleted file mode 100644 index 70a3021c..00000000 --- a/compiling/gdx-jnigen/src/main/resources/com/badlogic/gdx/jnigen/resources/scripts/memcpy_wrap.c +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __ANDROID__ -#ifdef __linux__ -#ifdef __x86_64__ - -#include -#include -#include - -__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); - -void *__wrap_memcpy(void * destination, const void * source, size_t num) -{ - return memcpy(destination, source, num); -} - -#endif -#endif -#endif \ No newline at end of file diff --git a/compiling/gdx-jnigen/src/main/resources/logback.xml b/compiling/gdx-jnigen/src/main/resources/logback.xml new file mode 100644 index 00000000..97d1b4f2 --- /dev/null +++ b/compiling/gdx-jnigen/src/main/resources/logback.xml @@ -0,0 +1,11 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n + + + + + + + \ No newline at end of file diff --git a/gdx-jnigen-generator-test/build.gradle b/gdx-jnigen-generator-test/build.gradle index 29ad3df3..3a243d57 100644 --- a/gdx-jnigen-generator-test/build.gradle +++ b/gdx-jnigen-generator-test/build.gradle @@ -25,7 +25,7 @@ task copyTestFiles(type: Copy) { test { // TODO: Figure out how to force build natives? - dependsOn(":jnigen-generator-test:jnigenJarNativesDesktop") +// dependsOn(":jnigen-generator-test:jnigenJarNativesDesktop") useJUnitPlatform() outputs.upToDateWhen {false} } @@ -49,19 +49,19 @@ jnigen { options = ["-I" + file("src/test/resources/").absolutePath] } - add(Linux, x32, ARM) - add(Linux, x64, x86) - add(Linux, x64, ARM) - add(Linux, x64, RISCV) - add(MacOsX, x64, x86) - add(MacOsX, x64, ARM) - add(Windows, x32, x86) - add(Windows, x64, x86) + addLinux(x32, ARM) + addLinux(x64, x86) + addLinux(x64, ARM) + addLinux(x64, RISCV) + addMac(x64, x86) + addMac(x64, ARM) + addWindows(x32, x86) + addWindows(x64, x86) //add(Windows, x64, ARM) - add(Android) { + addAndroid() { linkerFlags += " -stdlib=libc++\nAPP_STL := c++_shared " } - add(IOS) + addIOS() } tasks.named("jnigen").get().dependsOn(compileTestJava) diff --git a/gdx-jnigen-loader/src/main/java/com/badlogic/gdx/jnigen/loader/SharedLibraryLoader.java b/gdx-jnigen-loader/src/main/java/com/badlogic/gdx/jnigen/loader/SharedLibraryLoader.java index 9a8dae1c..27b236fa 100644 --- a/gdx-jnigen-loader/src/main/java/com/badlogic/gdx/jnigen/loader/SharedLibraryLoader.java +++ b/gdx-jnigen-loader/src/main/java/com/badlogic/gdx/jnigen/loader/SharedLibraryLoader.java @@ -17,6 +17,7 @@ package com.badlogic.gdx.jnigen.loader; import com.badlogic.gdx.jnigen.commons.Architecture; +import com.badlogic.gdx.jnigen.commons.HostDetection; import com.badlogic.gdx.jnigen.commons.Os; import java.io.Closeable; @@ -40,80 +41,6 @@ * @author Nathan Sweet */ public class SharedLibraryLoader { - static public Os os; - static public Architecture.Bitness bitness = Architecture.Bitness._32; - static public Architecture architecture = Architecture.x86; - - static { - if (System.getProperty("os.name").contains("Windows")) - os = Os.Windows; - else if (System.getProperty("os.name").contains("Linux")) - os = Os.Linux; - else if (System.getProperty("os.name").contains("Mac")) - os = Os.MacOsX; - - if (System.getProperty("os.arch").startsWith("arm") || System.getProperty("os.arch").startsWith("aarch64")) - architecture = Architecture.ARM; - else if (System.getProperty("os.arch").startsWith("riscv")) - architecture = Architecture.RISCV; - else if (System.getProperty("os.arch").startsWith("loongarch")) - architecture = Architecture.LOONGARCH; - - if (System.getProperty("os.arch").contains("64") || System.getProperty("os.arch").startsWith("armv8")) - bitness = Architecture.Bitness._64; - else if (System.getProperty("os.arch").contains("128")) - bitness = Architecture.Bitness._128; - - boolean isMOEiOS = System.getProperty("moe.platform.name") != null; - String vm = System.getProperty("java.runtime.name"); - if (vm != null && vm.contains("Android Runtime")) { - os = Os.Android; - bitness = Architecture.Bitness._32; - architecture = Architecture.x86; - } - if (isMOEiOS || (os != Os.Android && os != Os.Windows && os != Os.Linux && os != Os.MacOsX)) { - os = Os.IOS; - bitness = Architecture.Bitness._32; - architecture = Architecture.x86; - } - } - - /** - * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.Windows} instead. - */ - @Deprecated - static public boolean isWindows = os == Os.Windows; - /** - * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.Linux} instead. - */ - @Deprecated - static public boolean isLinux = os == Os.Linux; - /** - * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.MacOsX} instead. - */ - @Deprecated - static public boolean isMac = os == Os.MacOsX; - /** - * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.IOS} instead. - */ - @Deprecated - static public boolean isIos = os == Os.IOS; - /** - * @deprecated Use {@link #os} as {@code SharedLibraryLoader.os == Os.Android} instead. - */ - @Deprecated - static public boolean isAndroid = os == Os.Android; - /** - * @deprecated Use {@link #architecture} as {@code SharedLibraryLoader.architecture == Architecture.ARM} instead. - */ - @Deprecated - static public boolean isARM = architecture == Architecture.ARM; - /** - * @deprecated Use {@link #bitness} as {@code SharedLibraryLoader.bitness == Architecture.Bitness._64} instead. - */ - @Deprecated - static public boolean is64Bit = bitness == Architecture.Bitness._64; - static private final HashSet loadedLibraries = new HashSet<>(); static private final Random random = new Random(); @@ -152,29 +79,29 @@ public String crc (InputStream input) { /** Maps a platform independent library name to a platform dependent name. */ public String mapLibraryName (String libraryName) { - if (os == Os.Android) + if (HostDetection.os == Os.Android) return libraryName; - return os.getLibPrefix() + libraryName + architecture.toSuffix() + bitness.toSuffix() + "." + os.getLibExtension(); + return HostDetection.os.getLibPrefix() + libraryName + HostDetection.architecture.toSuffix() + HostDetection.bitness.toSuffix() + "." + HostDetection.os.getLibExtension(); } /** Loads a shared library for the platform the application is running on. * @param libraryName The platform independent library name. If not contain a prefix (eg lib) or suffix (eg .dll). */ public void load (String libraryName) { // in case of iOS, it's unnecessary to dlopen - if (os == Os.IOS) return; + if (HostDetection.os == Os.IOS) return; synchronized (SharedLibraryLoader.class) { if (isLoaded(libraryName)) return; String platformName = mapLibraryName(libraryName); try { - if (os == Os.Android) + if (HostDetection.os == Os.Android) System.loadLibrary(platformName); else loadFile(platformName); setLoaded(libraryName); } catch (Throwable ex) { throw new SharedLibraryLoadRuntimeException("Couldn't load shared library '" + platformName + "' for target: " - + (os == Os.Android ? "Android" : (System.getProperty("os.name") + ", " + architecture.name() + ", " + bitness.name().substring(1) + "-bit")), + + (HostDetection.os == Os.Android ? "Android" : (System.getProperty("os.name") + ", " + HostDetection.architecture.name() + ", " + HostDetection.bitness.name().substring(1) + "-bit")), ex); } } diff --git a/gdx-jnigen-runtime/build.gradle b/gdx-jnigen-runtime/build.gradle index 80210efd..e75499bf 100644 --- a/gdx-jnigen-runtime/build.gradle +++ b/gdx-jnigen-runtime/build.gradle @@ -244,16 +244,16 @@ jnigen { libraries += " ${file("build/libffi-build/${combined}/lib/libffi.a").absolutePath} " } - add(Linux, x32, ARM) - add(Linux, x64, x86) - add(Linux, x64, ARM) - add(Linux, x64, RISCV) - add(MacOsX, x64, x86) - add(MacOsX, x64, ARM) - add(Windows, x32, x86) - add(Windows, x64, x86) + addLinux(x32, ARM) + addLinux(x64, x86) + addLinux(x64, ARM) + addLinux(x64, RISCV) + addMac(x64, x86) + addMac(x64, ARM) + addWindows(x32, x86) + addWindows(x64, x86) //add(Windows, x64, ARM) - add(Android) { + addAndroid() { androidApplicationMk += "APP_PLATFORM := android-19\nAPP_STRIP_MODE := none" linkerFlags += " -stdlib=libc++\nLOCAL_WHOLE_STATIC_LIBRARIES := static_libffi\nAPP_STL := c++_shared" androidAndroidMk += [ @@ -264,7 +264,7 @@ jnigen { "include \$(PREBUILT_STATIC_LIBRARY)" ] } - add(IOS) { + addIOS() { // Gnu11 is the only one that compiles for whatever reason cFlags += " -std=gnu11 -fexceptions " cIncludes += ["**/*.S"] @@ -275,16 +275,16 @@ jnigen { } } -tasks.named("jnigenBuildIOS").get().dependsOn(copyIOSFiles) -tasks.named("jnigenBuildAndroid").get().finalizedBy(extractAndroidCXXLibs) +//tasks.named("jnigenBuildIOS").get().dependsOn(copyIOSFiles) +//tasks.named("jnigenBuildAndroid").get().finalizedBy(extractAndroidCXXLibs) artifacts { - archives jnigenJarNativesDesktop +// archives jnigenJarNativesDesktop ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'].each { id -> - archives "jnigenJarNativesAndroid${id}" { } +// archives "jnigenJarNativesAndroid${id}" { } } - archives jnigenJarNativesIOS +// archives jnigenJarNativesIOS } eclipse { diff --git a/publish.gradle b/publish.gradle index 493b00df..eb232521 100644 --- a/publish.gradle +++ b/publish.gradle @@ -38,12 +38,12 @@ subprojects from components.java if (project.getPluginManager().hasPlugin("com.badlogicgames.jnigen.jnigen-gradle")) { - artifact jnigenJarNativesDesktop { } - ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'].each { id -> - artifact "jnigenJarNativesAndroid${id}" { } - } - - artifact jnigenJarNativesIOS { } +// artifact jnigenJarNativesDesktop { } +// ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'].each { id -> +// artifact "jnigenJarNativesAndroid${id}" { } +// } +// +// artifact jnigenJarNativesIOS { } } versionMapping {