com.qwazr.compiler.JniCompiler Maven / Gradle / Ivy
/**
* Copyright 2015-2017 Emmanuel Keller / QWAZR
*
* 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.qwazr.compiler;
import com.qwazr.utils.StringUtils;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
class JniCompiler {
private final static Logger LOGGER = LoggerFactory.getLogger(JniCompiler.class);
private final static String COMPILER_NAME = "gcc";
private final static String JAVAH = "javah";
private static final String JAVA_HOME = SystemUtils.getJavaHome().getAbsolutePath();
private static final String ARCH_PATH;
static {
if (SystemUtils.IS_OS_MAC_OSX)
ARCH_PATH = "darwin";
else if (SystemUtils.IS_OS_WINDOWS)
ARCH_PATH = "win32";
else if (SystemUtils.IS_OS_LINUX)
ARCH_PATH = "linux";
else
ARCH_PATH = StringUtils.EMPTY;
}
private final File javaClassesDirectory;
private final File cppSourceDirectory;
private final File includeDirectory;
private final File includeArchDirectory;
JniCompiler(final File javaClassesDirectory, final File cppSourceDirectory) throws IOException {
this.javaClassesDirectory = javaClassesDirectory;
this.cppSourceDirectory = cppSourceDirectory;
LOGGER.info("Enable JNI/GCC compiler on: " + cppSourceDirectory + " / " + javaClassesDirectory);
final Path jniPath =
FileSystems.getDefault().getPath(JAVA_HOME).getParent().resolve("include").resolve("jni.h");
if (!Files.exists(jniPath))
throw new IOException("Cant't find JNI header: " + jniPath);
includeDirectory = jniPath.getParent().toFile();
includeArchDirectory = jniPath.getParent().resolve(ARCH_PATH).toFile();
LOGGER.info("JAVA include directory: " + includeDirectory.getAbsolutePath());
LOGGER.info("JAVA include arch directory: " + includeArchDirectory.getAbsolutePath());
}
void execute(int expectedCode, File directory, String... commands) throws IOException, InterruptedException {
final ProcessBuilder builder = new ProcessBuilder(commands);
builder.directory(directory);
builder.inheritIO();
final Process process = builder.start();
try {
final int exitCode = process.waitFor();
if (exitCode != expectedCode)
throw new IOException("Wrong exit code for " + commands[0] + ": " + exitCode);
} catch (InterruptedException e) {
process.destroy();
throw e;
}
}
void generateHeader(Class> nativeClass) throws IOException, InterruptedException {
execute(0, javaClassesDirectory, JAVAH, nativeClass.getName());
}
private void compileC(File sourceFile) throws IOException, InterruptedException {
execute(0, cppSourceDirectory, COMPILER_NAME, "-c", "-I" + includeDirectory.getAbsolutePath(),
"-I" + includeArchDirectory.getAbsolutePath(), "-I" + javaClassesDirectory.getAbsolutePath(),
sourceFile.getAbsolutePath());
}
void compile(Class> nativeClass) throws IOException, InterruptedException {
final String fileName = nativeClass.getName().replace('.', '_');
final File cFile = new File(cppSourceDirectory, fileName + ".c");
if (cFile.exists()) {
compileC(cFile);
return;
}
throw new NotImplementedException("Compilation not supported: " + fileName);
}
}