1 /* 2 * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package toolbox; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.io.PrintWriter; 29 import java.nio.file.Path; 30 import java.nio.file.Paths; 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.stream.Collectors; 38 import java.util.stream.Stream; 39 import javax.tools.JavaCompiler; 40 import javax.tools.JavaFileManager; 41 import javax.tools.JavaFileObject; 42 import javax.tools.StandardJavaFileManager; 43 import javax.tools.StandardLocation; 44 45 import com.sun.tools.javac.api.JavacTaskImpl; 46 import com.sun.tools.javac.api.JavacTool; 47 48 /** 49 * A task to configure and run the Java compiler, javac. 50 */ 51 public class JavacTask extends AbstractTask<JavacTask> { 52 private boolean includeStandardOptions; 53 private List<Path> classpath; 54 private List<Path> sourcepath; 55 private Path outdir; 56 private List<String> options; 57 private List<String> classes; 58 private List<String> files; 59 private List<JavaFileObject> fileObjects; 60 private JavaFileManager fileManager; 61 62 private JavaCompiler compiler; 63 private StandardJavaFileManager internalFileManager; 64 65 /** 66 * Creates a task to execute {@code javac} using API mode. 67 * @param toolBox the {@code ToolBox} to use 68 */ 69 public JavacTask(ToolBox toolBox) { 70 super(toolBox, Task.Mode.API); 71 } 72 73 /** 74 * Creates a task to execute {@code javac} in a specified mode. 75 * @param toolBox the {@code ToolBox} to use 76 * @param mode the mode to be used 77 */ 78 public JavacTask(ToolBox toolBox, Task.Mode mode) { 79 super(toolBox, mode); 80 } 81 82 /** 83 * Sets the classpath. 84 * @param classpath the classpath 85 * @return this task object 86 */ 87 public JavacTask classpath(String classpath) { 88 this.classpath = Stream.of(classpath.split(File.pathSeparator)) 89 .filter(s -> !s.isEmpty()) 90 .map(s -> Paths.get(s)) 91 .collect(Collectors.toList()); 92 return this; 93 } 94 95 /** 96 * Sets the classpath. 97 * @param classpath the classpath 98 * @return this task object 99 */ 100 public JavacTask classpath(Path... classpath) { 101 this.classpath = Arrays.asList(classpath); 102 return this; 103 } 104 105 /** 106 * Sets the classpath. 107 * @param classpath the classpath 108 * @return this task object 109 */ 110 public JavacTask classpath(List<Path> classpath) { 111 this.classpath = classpath; 112 return this; 113 } 114 115 /** 116 * Sets the sourcepath. 117 * @param sourcepath the sourcepath 118 * @return this task object 119 */ 120 public JavacTask sourcepath(String sourcepath) { 121 this.sourcepath = Stream.of(sourcepath.split(File.pathSeparator)) 122 .filter(s -> !s.isEmpty()) 123 .map(s -> Paths.get(s)) 124 .collect(Collectors.toList()); 125 return this; 126 } 127 128 /** 129 * Sets the sourcepath. 130 * @param sourcepath the sourcepath 131 * @return this task object 132 */ 133 public JavacTask sourcepath(Path... sourcepath) { 134 this.sourcepath = Arrays.asList(sourcepath); 135 return this; 136 } 137 138 /** 139 * Sets the sourcepath. 140 * @param sourcepath the sourcepath 141 * @return this task object 142 */ 143 public JavacTask sourcepath(List<Path> sourcepath) { 144 this.sourcepath = sourcepath; 145 return this; 146 } 147 148 /** 149 * Sets the output directory. 150 * @param outdir the output directory 151 * @return this task object 152 */ 153 public JavacTask outdir(String outdir) { 154 this.outdir = Paths.get(outdir); 155 return this; 156 } 157 158 /** 159 * Sets the output directory. 160 * @param outdir the output directory 161 * @return this task object 162 */ 163 public JavacTask outdir(Path outdir) { 164 this.outdir = outdir; 165 return this; 166 } 167 168 /** 169 * Sets the options. 170 * @param options the options 171 * @return this task object 172 */ 173 public JavacTask options(String... options) { 174 this.options = Arrays.asList(options); 175 return this; 176 } 177 178 /** 179 * Sets the classes to be analyzed. 180 * @param classes the classes 181 * @return this task object 182 */ 183 public JavacTask classes(String... classes) { 184 this.classes = Arrays.asList(classes); 185 return this; 186 } 187 188 /** 189 * Sets the files to be compiled or analyzed. 190 * @param files the files 191 * @return this task object 192 */ 193 public JavacTask files(String... files) { 194 this.files = Arrays.asList(files); 195 return this; 196 } 197 198 /** 199 * Sets the files to be compiled or analyzed. 200 * @param files the files 201 * @return this task object 202 */ 203 public JavacTask files(Path... files) { 204 this.files = Stream.of(files) 205 .map(Path::toString) 206 .collect(Collectors.toList()); 207 return this; 208 } 209 210 /** 211 * Sets the files to be compiled or analyzed. 212 * @param files the files 213 * @return this task object 214 */ 215 public JavacTask files(List<Path> files) { 216 this.files = files.stream() 217 .map(Path::toString) 218 .collect(Collectors.toList()); 219 return this; 220 } 221 222 /** 223 * Sets the sources to be compiled or analyzed. 224 * Each source string is converted into an in-memory object that 225 * can be passed directly to the compiler. 226 * @param sources the sources 227 * @return this task object 228 */ 229 public JavacTask sources(String... sources) { 230 fileObjects = Stream.of(sources) 231 .map(s -> new ToolBox.JavaSource(s)) 232 .collect(Collectors.toList()); 233 return this; 234 } 235 236 /** 237 * Sets the file manager to be used by this task. 238 * @param fileManager the file manager 239 * @return this task object 240 */ 241 public JavacTask fileManager(JavaFileManager fileManager) { 242 this.fileManager = fileManager; 243 return this; 244 } 245 246 /** 247 * {@inheritDoc} 248 * @return the name "javac" 249 */ 250 @Override 251 public String name() { 252 return "javac"; 253 } 254 255 /** 256 * Calls the compiler with the arguments as currently configured. 257 * @return a Result object indicating the outcome of the compilation 258 * and the content of any output written to stdout, stderr, or the 259 * main stream by the compiler. 260 * @throws TaskError if the outcome of the task is not as expected. 261 */ 262 @Override 263 public Task.Result run() { 264 if (mode == Task.Mode.EXEC) 265 return runExec(); 266 267 AbstractTask.WriterOutput direct = new AbstractTask.WriterOutput(); 268 // The following are to catch output to System.out and System.err, 269 // in case these are used instead of the primary (main) stream 270 AbstractTask.StreamOutput sysOut = new AbstractTask.StreamOutput(System.out, System::setOut); 271 AbstractTask.StreamOutput sysErr = new AbstractTask.StreamOutput(System.err, System::setErr); 272 int rc; 273 Map<Task.OutputKind, String> outputMap = new HashMap<>(); 274 try { 275 switch (mode == null ? Task.Mode.API : mode) { 276 case API: 277 rc = runAPI(direct.pw); 278 break; 279 case CMDLINE: 280 rc = runCommand(direct.pw); 281 break; 282 default: 283 throw new IllegalStateException(); 284 } 285 } catch (IOException e) { 286 toolBox.out.println("Exception occurred: " + e); 287 rc = 99; 288 } finally { 289 outputMap.put(Task.OutputKind.STDOUT, sysOut.close()); 290 outputMap.put(Task.OutputKind.STDERR, sysErr.close()); 291 outputMap.put(Task.OutputKind.DIRECT, direct.close()); 292 } 293 return checkExit(new Task.Result(toolBox, this, rc, outputMap)); 294 } 295 296 private int runAPI(PrintWriter pw) throws IOException { 297 try { 298 // if (compiler == null) { 299 // TODO: allow this to be set externally 300 // compiler = ToolProvider.getSystemJavaCompiler(); 301 compiler = JavacTool.create(); 302 // } 303 304 if (fileManager == null) 305 fileManager = internalFileManager = compiler.getStandardFileManager(null, null, null); 306 if (outdir != null) 307 setLocationFromPaths(StandardLocation.CLASS_OUTPUT, Collections.singletonList(outdir)); 308 if (classpath != null) 309 setLocationFromPaths(StandardLocation.CLASS_PATH, classpath); 310 if (sourcepath != null) 311 setLocationFromPaths(StandardLocation.SOURCE_PATH, sourcepath); 312 List<String> allOpts = new ArrayList<>(); 313 if (options != null) 314 allOpts.addAll(options); 315 316 Iterable<? extends JavaFileObject> allFiles = joinFiles(files, fileObjects); 317 JavaCompiler.CompilationTask task = compiler.getTask(pw, 318 fileManager, 319 null, // diagnostic listener; should optionally collect diags 320 allOpts, 321 classes, 322 allFiles); 323 return ((JavacTaskImpl) task).doCall().exitCode; 324 } finally { 325 if (internalFileManager != null) 326 internalFileManager.close(); 327 } 328 } 329 330 private void setLocationFromPaths(StandardLocation location, List<Path> files) throws IOException { 331 if (!(fileManager instanceof StandardJavaFileManager)) 332 throw new IllegalStateException("not a StandardJavaFileManager"); 333 ((StandardJavaFileManager) fileManager).setLocationFromPaths(location, files); 334 } 335 336 private int runCommand(PrintWriter pw) { 337 List<String> args = getAllArgs(); 338 String[] argsArray = args.toArray(new String[args.size()]); 339 return com.sun.tools.javac.Main.compile(argsArray, pw); 340 } 341 342 private Task.Result runExec() { 343 List<String> args = new ArrayList<>(); 344 Path javac = toolBox.getJDKTool("javac"); 345 args.add(javac.toString()); 346 if (includeStandardOptions) { 347 args.addAll(toolBox.split(System.getProperty("test.tool.vm.opts"), " +")); 348 args.addAll(toolBox.split(System.getProperty("test.compiler.opts"), " +")); 349 } 350 args.addAll(getAllArgs()); 351 352 String[] argsArray = args.toArray(new String[args.size()]); 353 ProcessBuilder pb = getProcessBuilder(); 354 pb.command(argsArray); 355 try { 356 return runProcess(toolBox, this, pb.start()); 357 } catch (IOException | InterruptedException e) { 358 throw new Error(e); 359 } 360 } 361 362 private List<String> getAllArgs() { 363 List<String> args = new ArrayList<>(); 364 if (options != null) 365 args.addAll(options); 366 if (outdir != null) { 367 args.add("-d"); 368 args.add(outdir.toString()); 369 } 370 if (classpath != null) { 371 args.add("-classpath"); 372 args.add(toSearchPath(classpath)); 373 } 374 if (sourcepath != null) { 375 args.add("-sourcepath"); 376 args.add(toSearchPath(sourcepath)); 377 } 378 if (classes != null) 379 args.addAll(classes); 380 if (files != null) 381 args.addAll(files); 382 383 return args; 384 } 385 386 private String toSearchPath(List<Path> files) { 387 return files.stream() 388 .map(Path::toString) 389 .collect(Collectors.joining(File.pathSeparator)); 390 } 391 392 private Iterable<? extends JavaFileObject> joinFiles( 393 List<String> files, List<JavaFileObject> fileObjects) { 394 if (files == null) 395 return fileObjects; 396 if (internalFileManager == null) 397 internalFileManager = compiler.getStandardFileManager(null, null, null); 398 Iterable<? extends JavaFileObject> filesAsFileObjects = 399 internalFileManager.getJavaFileObjectsFromStrings(files); 400 if (fileObjects == null) 401 return filesAsFileObjects; 402 List<JavaFileObject> combinedList = new ArrayList<>(); 403 for (JavaFileObject o : filesAsFileObjects) 404 combinedList.add(o); 405 combinedList.addAll(fileObjects); 406 return combinedList; 407 } 408 }