/*
* Copyright 2015 Cel Skeggs.
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.deployment;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.stream.Stream;
import javax.lang.model.SourceVersion;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
* Provides utilities for compiling Java sources into class files.
*
* @author skeggsc
*/
public class DepJava {
private static final SourceVersion JAVA_SOURCE_VERSION = SourceVersion.RELEASE_8;
private static final String JAVA_SOURCE_VERSION_OPTION = "1.8";
private static final String JAVA_TARGET_VERSION_OPTION = "1.8";
/**
* Compiles the source files in <code>folder</code> into a new artifact. The
* files contained in <code>classpath</code> will be used when resolving
* references in the Java class files, but will not be included in the
* generated artifact.
*
* The Java class library is automatically included in the classpath.
*
* @param folder the folder containing the source files.
* @param classpath the folders containing the classes that can be linked to
* from the compiled code.
* @return the generated Artifact of the compiled classes.
* @throws IOException if something around finding source files or storing
* created files fails.
*/
public static Artifact build(File folder, File... classpath) throws IOException {
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
if (javac == null) {
throw new RuntimeException("No java compiler available!");
}
if (!javac.getSourceVersions().contains(JAVA_SOURCE_VERSION)) {
throw new IllegalArgumentException("Source version not supported by local compiler: " + JAVA_SOURCE_VERSION);
}
StandardJavaFileManager fileManager = javac.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(walkSourceFiles(folder));
File destdir = Files.createTempDirectory("jclasses").toFile();
Runtime.getRuntime().addShutdownHook(new DirectoryDeleterThread(destdir));
JavaCompiler.CompilationTask task = javac.getTask(null, null, null, Arrays.asList("-classpath", classpathToOption(destdir, classpath), "-d", destdir.getAbsolutePath(), "-source", JAVA_SOURCE_VERSION_OPTION, "-target", JAVA_TARGET_VERSION_OPTION, "-g"), null, compilationUnits);
if (!task.call()) {
throw new RuntimeException("Could not complete compilation! See output for details.");
}
return new Folder(destdir);
}
private static File[] walkSourceFiles(File folder) throws IOException {
try (Stream<Path> stream = Files.walk(folder.toPath())) {
return stream.map(x -> x.toFile()).filter(t -> t.getName().endsWith(".java") && !t.isDirectory()).toArray(len -> new File[len]);
}
}
private static String classpathToOption(File first, File... classpath) {
if (!first.exists()) {
throw new IllegalArgumentException("Classpath element does not exist: " + first);
}
StringBuilder cp = new StringBuilder(first.getAbsolutePath());
for (File elem : classpath) {
if (!elem.exists()) {
throw new IllegalArgumentException("Classpath element does not exist: " + elem);
}
cp.append(File.pathSeparatorChar);
cp.append(elem.getAbsolutePath());
}
return cp.toString();
}
}