org.apache.xmlbeans.impl.tool.CodeGenUtil Maven / Gradle / Ivy
The newest version!
/* Copyright 2004 The Apache Software Foundation
*
* 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 org.apache.xmlbeans.impl.tool;
import org.apache.xmlbeans.SystemProperties;
import org.apache.xmlbeans.impl.common.IOUtil;
import org.apache.xmlbeans.impl.util.ExceptionUtil;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class CodeGenUtil {
public static final String DEFAULT_MEM_START = "8m";
public static final String DEFAULT_MEM_MAX = "256m";
public static final String DEFAULT_COMPILER = "javac";
//workaround for Sun bug # 4723726
public static URI resolve(URI base, URI child) {
URI ruri = base.resolve(child);
//fix up normalization bug
if ("file".equals(ruri.getScheme()) && !child.equals(ruri)) {
if (base.getPath().startsWith("//") && !ruri.getPath().startsWith("//")) {
String path = "///".concat(ruri.getPath());
try {
ruri = new URI("file", null, path, ruri.getQuery(), ruri.getFragment());
} catch (URISyntaxException ignored) {
}
}
}
return ruri;
}
static void addAllJavaFiles(List srcFiles, List args) {
for (File f : srcFiles) {
if (f.isDirectory()) {
File[] files = f.listFiles(
file -> (file.isFile() && file.getName().endsWith(".java")) || file.isDirectory()
);
if (files != null) {
addAllJavaFiles(Arrays.asList(files), args);
}
} else {
args.add(quoteAndEscapeFilename(f.getAbsolutePath()));
}
}
}
static private String quoteAndEscapeFilename(String filename) {
// don't quote if there's no space
if (!filename.contains(" ")) {
return filename;
}
// bizarre. javac expects backslash escaping if we quote the classpath
// bizarre also. replaceAll expects replacement backslashes to be double escaped.
return "\"" + filename.replaceAll("\\\\", "\\\\\\\\") + "\"";
}
/**
* Invokes javac on the generated source files in order to turn them
* into binary files in the output directory. This will return a list of
* {@code GenFile}s for all of the classes produced or null if an
* error occurred.
*
* @deprecated
*/
public static boolean externalCompile(List srcFiles, File outdir, File[] cp, boolean debug) {
return externalCompile(srcFiles, outdir, cp, debug, DEFAULT_COMPILER, null, DEFAULT_MEM_START, DEFAULT_MEM_MAX,
false, false, null);
}
/**
* Invokes javac on the generated source files in order to turn them
* into binary files in the output directory. This will return a list of
* {@code GenFile}s for all of the classes produced or null if an
* error occurred.
*
* @deprecated
*/
public static boolean externalCompile(List srcFiles, File outdir, File[] cp, boolean debug, String javacPath, String memStart, String memMax,
boolean quiet, boolean verbose) {
return externalCompile(srcFiles, outdir, cp, debug, javacPath, null, memStart, memMax, quiet, verbose, null);
}
/**
* Invokes javac on the generated source files in order to turn them
* into binary files in the output directory. This will return a list of
* {@code GenFile}s for all of the classes produced or null if an
* error occurred.
*
* @deprecated
*/
public static boolean externalCompile(List srcFiles, File outdir, File[] cp, boolean debug, String javacPath, String memStart, String memMax,
boolean quiet, boolean verbose, String sourceCodeEncoding) {
return externalCompile(srcFiles, outdir, cp, debug, javacPath, null, memStart, memMax, quiet, verbose, sourceCodeEncoding);
}
/**
* Invokes javac on the generated source files in order to turn them
* into binary files in the output directory. This will return a list of
* {@code GenFile}s for all of the classes produced or null if an
* error occurred.
*
* @deprecated
*/
public static boolean externalCompile(List srcFiles, File outdir, File[] cp, boolean debug, String javacPath, String genver, String memStart, String memMax,
boolean quiet, boolean verbose) {
return externalCompile(srcFiles, outdir, cp, debug, javacPath, genver, memStart, memMax, quiet, verbose, null);
}
/**
* Invokes javac on the generated source files in order to turn them
* into binary files in the output directory. This will return a list of
* {@code GenFile}s for all of the classes produced or null if an
* error occurred.
*/
public static boolean externalCompile(List srcFiles, File outdir, File[] cp, boolean debug, String javacPath, String genver, String memStart, String memMax,
boolean quiet, boolean verbose, String sourceCodeEncoding) {
List args = new ArrayList<>();
File javac = findJavaTool(javacPath == null ? DEFAULT_COMPILER : javacPath);
assert (javac.exists()) : "compiler not found " + javac;
args.add(javac.getAbsolutePath());
if (outdir == null) {
outdir = new File(".");
} else {
args.add("-d");
args.add(quoteAndEscapeFilename(outdir.getAbsolutePath()));
}
if (cp == null) {
cp = systemClasspath();
}
if(sourceCodeEncoding != null && !sourceCodeEncoding.isEmpty()) {
args.add("-encoding");
args.add(sourceCodeEncoding);
}
if (cp.length > 0) {
StringBuilder classPath = new StringBuilder();
// Add the output directory to the classpath. We do this so that
// javac will be able to find classes that were compiled
// previously but are not in the list of sources this time.
classPath.append(outdir.getAbsolutePath());
// Add everything on our classpath.
for (File file : cp) {
classPath.append(File.pathSeparator);
classPath.append(file.getAbsolutePath());
}
args.add("-classpath");
// bizarre. javac expects backslash escaping if we quote the classpath
args.add(quoteAndEscapeFilename(classPath.toString()));
}
if (genver == null) {
genver = "1.8";
}
args.add("-source");
args.add(genver);
args.add("-target");
args.add(genver);
args.add(debug ? "-g" : "-g:none");
if (verbose) {
args.add("-verbose");
}
addAllJavaFiles(srcFiles, args);
File clFile = null;
try {
clFile = Files.createTempFile(IOUtil.getTempDir(), "javac", ".tmp").toFile();
try (Writer fw = Files.newBufferedWriter(clFile.toPath(), Charset.defaultCharset())) {
Iterator i = args.iterator();
for (i.next(); i.hasNext(); ) {
String arg = i.next();
fw.write(arg);
fw.write('\n');
}
}
List newargs = new ArrayList<>();
newargs.add(args.get(0));
if (memStart != null && !memStart.isEmpty()) {
newargs.add("-J-Xms" + memStart);
}
if (memMax != null && !memMax.isEmpty()) {
newargs.add("-J-Xmx" + memMax);
}
newargs.add("@" + clFile.getAbsolutePath());
args = newargs;
} catch (Exception e) {
System.err.println("Could not create command-line file for javac");
}
try {
String[] strArgs = args.toArray(new String[0]);
if (verbose) {
System.out.print("compile command:");
for (String strArg : strArgs) {
System.out.print(" " + strArg);
}
System.out.println();
}
final Process proc = Runtime.getRuntime().exec(strArgs);
StringBuilder errorBuffer = new StringBuilder();
StringBuilder outputBuffer = new StringBuilder();
Thread out = copy(proc.getInputStream(), outputBuffer);
Thread err = copy(proc.getErrorStream(), errorBuffer);
proc.waitFor();
if (verbose || proc.exitValue() != 0) {
if (outputBuffer.length() > 0) {
System.out.println(outputBuffer.toString());
System.out.flush();
}
if (errorBuffer.length() > 0) {
System.err.println(errorBuffer.toString());
System.err.flush();
}
if (proc.exitValue() != 0) {
return false;
}
}
} catch (Throwable e) {
if (ExceptionUtil.isFatal(e)) {
ExceptionUtil.rethrow(e);
}
System.err.println(e.toString());
System.err.println(e.getCause());
e.printStackTrace(System.err);
return false;
} finally {
if (!debug && clFile != null) {
clFile.delete();
}
}
return true;
}
public static File[] systemClasspath() {
List cp = new ArrayList<>();
CodeSource cs = CodeGenUtil.class.getProtectionDomain().getCodeSource();
if (cs != null) {
cp.add(new File(cs.getLocation().getPath()));
} else {
System.err.println("Can't determine path of xmlbeans-*.jar - specify classpath explicitly!");
}
String jcp = SystemProperties.getProperty("java.class.path");
if (jcp != null) {
String[] systemcp = jcp.split(File.pathSeparator);
for (String s : systemcp) {
cp.add(new File(s));
}
}
return cp.toArray(new File[0]);
}
/**
* Look for tool in current directory and ${JAVA_HOME}/../bin and
* try with .exe file extension.
*/
private static File findJavaTool(String tool) {
File toolFile = new File(tool);
if (toolFile.isFile()) {
return toolFile;
}
File result = new File(tool + ".exe");
if (result.isFile()) {
return result;
}
String home = SystemProperties.getProperty("java.home");
String sep = File.separator;
result = new File(home + sep + ".." + sep + "bin", tool);
if (result.isFile()) {
return result;
}
result = new File(result.getPath() + ".exe");
if (result.isFile()) {
return result;
}
result = new File(home + sep + "bin", tool);
if (result.isFile()) {
return result;
}
result = new File(result.getPath() + ".exe");
if (result.isFile()) {
return result;
}
// just return the original toolFile and hope that it is on the PATH.
return toolFile;
}
/**
* Reads the given input stream into the given buffer until there is
* nothing left to read.
*/
private static Thread copy(InputStream stream, final StringBuilder output) {
final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset()));
Thread readerThread = new Thread(() ->
reader.lines().forEach(s -> output.append(s).append("\n"))
);
readerThread.start();
return readerThread;
}
}