/** * Tries to find automatically include paths for {@code jni.h} and {@code jni_md.h}, as well as * the link and library paths for the {@code jvm} library. * * @param properties the Properties containing the paths to update * @param header to request support for exporting callbacks via generated header file */ static void includeJavaPaths(ClassProperties properties, boolean header) { if (properties.getProperty("platform", "").startsWith("android")) { // Android includes its own jni.h file and doesn't have a jvm library return; } String platform = Loader.getPlatform(); final String jvmlink = properties.getProperty("platform.link.prefix", "") + "jvm" + properties.getProperty("platform.link.suffix", ""); final String jvmlib = properties.getProperty("platform.library.prefix", "") + "jvm" + properties.getProperty("platform.library.suffix", ""); final String[] jnipath = new String[2]; final String[] jvmpath = new String[2]; FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (new File(dir, "jni.h").exists()) { jnipath[0] = dir.getAbsolutePath(); } if (new File(dir, "jni_md.h").exists()) { jnipath[1] = dir.getAbsolutePath(); } if (new File(dir, jvmlink).exists()) { jvmpath[0] = dir.getAbsolutePath(); } if (new File(dir, jvmlib).exists()) { jvmpath[1] = dir.getAbsolutePath(); } return new File(dir, name).isDirectory(); } }; File javaHome = new File(System.getProperty("java.home")).getParentFile(); try { javaHome = javaHome.getCanonicalFile(); } catch (IOException e) { } ArrayList<File> dirs = new ArrayList<File>(Arrays.asList(javaHome.listFiles(filter))); while (!dirs.isEmpty()) { File d = dirs.remove(dirs.size() - 1); String dpath = d.getPath(); for (File f : d.listFiles(filter)) { try { f = f.getCanonicalFile(); } catch (IOException e) { } if (!dpath.startsWith(f.getPath())) { dirs.add(f); } } } if (jnipath[0] != null && jnipath[0].equals(jnipath[1])) { jnipath[1] = null; } else if (jnipath[0] == null) { String macpath = "/System/Library/Frameworks/JavaVM.framework/Headers/"; if (new File(macpath).isDirectory()) { jnipath[0] = macpath; } } if (jvmpath[0] != null && jvmpath[0].equals(jvmpath[1])) { jvmpath[1] = null; } properties.addAll("platform.includepath", jnipath); if (platform.equals(properties.getProperty("platform", platform))) { if (header) { // We only need libjvm for callbacks exported with the header file properties.get("platform.link").add(0, "jvm"); properties.addAll("platform.linkpath", jvmpath); } if (platform.startsWith("macosx")) { properties.addAll("platform.framework", "JavaVM"); } } }
/** * Launches and waits for the native compiler to produce a native shared library. * * @param sourceFilename the C++ source filename * @param outputFilename the output filename of the shared library * @param properties the Properties detailing the compiler options to use * @return the result of {@link Process#waitFor()} * @throws IOException * @throws InterruptedException */ int compile(String sourceFilename, String outputFilename, ClassProperties properties) throws IOException, InterruptedException { ArrayList<String> command = new ArrayList<String>(); includeJavaPaths(properties, header); String platform = Loader.getPlatform(); String compilerPath = properties.getProperty("platform.compiler"); command.add(compilerPath); { String p = properties.getProperty("platform.sysroot.prefix", ""); for (String s : properties.get("platform.sysroot")) { if (new File(s).isDirectory()) { if (p.endsWith(" ")) { command.add(p.trim()); command.add(s); } else { command.add(p + s); } } } } { String p = properties.getProperty("platform.includepath.prefix", ""); for (String s : properties.get("platform.includepath")) { if (new File(s).isDirectory()) { if (p.endsWith(" ")) { command.add(p.trim()); command.add(s); } else { command.add(p + s); } } } } command.add(sourceFilename); Collection<String> allOptions = properties.get("platform.compiler.*"); if (allOptions.isEmpty()) { allOptions.add("default"); } for (String s : allOptions) { if (s == null || s.length() == 0) { continue; } String p = "platform.compiler." + s; String options = properties.getProperty(p); if (options != null && options.length() > 0) { command.addAll(Arrays.asList(options.split(" "))); } else if (!"default".equals(s)) { logger.warn("Could not get the property named \"" + p + "\""); } } command.addAll(compilerOptions); String output = properties.getProperty("platform.compiler.output"); if (output != null && output.length() > 0) { command.addAll(Arrays.asList(output.split(" "))); } if (output == null || output.length() == 0 || output.endsWith(" ")) { command.add(outputFilename); } else { command.add(command.remove(command.size() - 1) + outputFilename); } { String p = properties.getProperty("platform.linkpath.prefix", ""); String p2 = properties.getProperty("platform.linkpath.prefix2"); for (String s : properties.get("platform.linkpath")) { if (new File(s).isDirectory()) { if (p.endsWith(" ")) { command.add(p.trim()); command.add(s); } else { command.add(p + s); } if (p2 != null) { if (p2.endsWith(" ")) { command.add(p2.trim()); command.add(s); } else { command.add(p2 + s); } } } } } { String p = properties.getProperty("platform.link.prefix", ""); String x = properties.getProperty("platform.link.suffix", ""); int i = command.size(); // to inverse order and satisfy typical compilers for (String s : properties.get("platform.link")) { String[] libnameversion = s.split("@"); if (libnameversion.length == 3 && libnameversion[1].length() == 0) { // Only use the version number when the user gave us a double @ s = libnameversion[0] + libnameversion[2]; } else { s = libnameversion[0]; } if (p.endsWith(" ") && x.startsWith(" ")) { command.add(i, p.trim()); command.add(i + 1, s); command.add(i + 2, x.trim()); } else if (p.endsWith(" ")) { command.add(i, p.trim()); command.add(i + 1, s + x); } else if (x.startsWith(" ")) { command.add(i, p + s); command.add(i + 1, x.trim()); } else { command.add(i, p + s + x); } } } { String p = properties.getProperty("platform.frameworkpath.prefix", ""); for (String s : properties.get("platform.frameworkpath")) { if (new File(s).isDirectory()) { if (p.endsWith(" ")) { command.add(p.trim()); command.add(s); } else { command.add(p + s); } } } } { String p = properties.getProperty("platform.framework.prefix", ""); String x = properties.getProperty("platform.framework.suffix", ""); for (String s : properties.get("platform.framework")) { if (p.endsWith(" ") && x.startsWith(" ")) { command.add(p.trim()); command.add(s); command.add(x.trim()); } else if (p.endsWith(" ")) { command.add(p.trim()); command.add(s + x); } else if (x.startsWith(" ")) { command.add(p + s); command.add(x.trim()); } else { command.add(p + s + x); } } } String text = ""; boolean windows = platform.startsWith("windows"); for (String s : command) { boolean hasSpaces = s.indexOf(" ") > 0; if (hasSpaces) { text += windows ? "\"" : "'"; } text += s; if (hasSpaces) { text += windows ? "\"" : "'"; } text += " "; } logger.info(text); ProcessBuilder pb = new ProcessBuilder(command); if (environmentVariables != null) { pb.environment().putAll(environmentVariables); } return pb.inheritIO().start().waitFor(); }