| /* 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 java.io.*; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| 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"; |
| public static final String DEFAULT_JAR = "jar"; |
| |
| //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<File> srcFiles, List<String> 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("\\\\", "\\\\\\\\") + "\""; |
| } |
| |
| static private String quoteNoEscapeFilename(String filename) { |
| // don't quote if there's no space, and don't quote on linux |
| return (!filename.contains(" ") || File.separatorChar == '/') ? filename : "\"" + filename + "\""; |
| } |
| |
| /** |
| * 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</code>s for all of the classes produced or null if an |
| * error occurred. |
| * |
| * @deprecated |
| */ |
| public static boolean externalCompile(List<File> srcFiles, File outdir, File[] cp, boolean debug) { |
| return externalCompile(srcFiles, outdir, cp, debug, DEFAULT_COMPILER, null, DEFAULT_MEM_START, DEFAULT_MEM_MAX, false, false); |
| } |
| |
| // KHK: temporary to avoid build break |
| public static boolean externalCompile(List<File> 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); |
| } |
| |
| /** |
| * 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</code>s for all of the classes produced or null if an |
| * error occurred. |
| */ |
| public static boolean externalCompile(List<File> srcFiles, File outdir, File[] cp, boolean debug, String javacPath, String genver, String memStart, String memMax, boolean quiet, boolean verbose) { |
| List<String> 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 (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 = File.createTempFile("javac", ""); |
| try (Writer fw = Files.newBufferedWriter(clFile.toPath(), StandardCharsets.ISO_8859_1)) { |
| Iterator<String> i = args.iterator(); |
| for (i.next(); i.hasNext(); ) { |
| String arg = i.next(); |
| fw.write(arg); |
| fw.write('\n'); |
| } |
| } |
| List<String> newargs = new ArrayList<>(); |
| newargs.add(args.get(0)); |
| |
| if (memStart != null && memStart.length() != 0) { |
| newargs.add("-J-Xms" + memStart); |
| } |
| if (memMax != null && memMax.length() != 0) { |
| 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) { |
| System.err.println(e.toString()); |
| System.err.println(e.getCause()); |
| e.printStackTrace(System.err); |
| return false; |
| } |
| |
| if (clFile != null) { |
| clFile.delete(); |
| } |
| |
| return true; |
| } |
| |
| public static File[] systemClasspath() { |
| List<File> cp = new ArrayList<>(); |
| 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]); |
| } |
| |
| /** |
| * @deprecated Use org.apache.xmlbeans.impl.common.JarHelper instead. |
| */ |
| public static boolean externalJar(File srcdir, File outfile) { |
| return externalJar(srcdir, outfile, DEFAULT_JAR, false, false); |
| } |
| |
| /** |
| * @deprecated Use org.apache.xmlbeans.impl.common.JarHelper instead. |
| */ |
| public static boolean externalJar(File srcdir, File outfile, String jarPath, boolean quiet, boolean verbose) { |
| List<String> args = new ArrayList<>(); |
| |
| File jar = findJavaTool(jarPath == null ? DEFAULT_JAR : jarPath); |
| assert (jar.exists()) : "jar not found " + jar; |
| args.add(jar.getAbsolutePath()); |
| |
| args.add("cf"); |
| args.add(quoteNoEscapeFilename(outfile.getAbsolutePath())); |
| |
| args.add("-C"); |
| args.add(quoteNoEscapeFilename(srcdir.getAbsolutePath())); |
| |
| args.add("."); |
| |
| try { |
| String[] strArgs = args.toArray(new String[0]); |
| |
| if (verbose) { |
| System.out.print("jar 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) { |
| e.printStackTrace(System.err); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * 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, StandardCharsets.ISO_8859_1)); |
| Thread readerThread = new Thread(() -> |
| reader.lines().forEach(s -> output.append(s).append("\n")) |
| ); |
| readerThread.start(); |
| return readerThread; |
| } |
| } |