\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 {