1 /* 2 * Copyright (c) 1998, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javatest.regtest.exec; 27 28 import java.io.BufferedReader; 29 import java.io.BufferedWriter; 30 import java.io.ByteArrayOutputStream; 31 import java.io.File; 32 import java.io.FileNotFoundException; 33 import java.io.FileReader; 34 import java.io.FileWriter; 35 import java.io.IOException; 36 import java.io.PrintStream; 37 import java.io.PrintWriter; 38 import java.io.StringReader; 39 import java.lang.reflect.Constructor; 40 import java.lang.reflect.InvocationTargetException; 41 import java.lang.reflect.Method; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.Collections; 45 import java.util.Iterator; 46 import java.util.LinkedHashMap; 47 import java.util.LinkedHashSet; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.Set; 51 import java.util.concurrent.TimeUnit; 52 53 import com.sun.javatest.Status; 54 import com.sun.javatest.regtest.TimeoutHandler; 55 import com.sun.javatest.regtest.agent.CompileActionHelper; 56 import com.sun.javatest.regtest.agent.JDK_Version; 57 import com.sun.javatest.regtest.agent.SearchPath; 58 import com.sun.javatest.regtest.config.ExecMode; 59 import com.sun.javatest.regtest.config.JDK; 60 import com.sun.javatest.regtest.config.JDKOpts; 61 import com.sun.javatest.regtest.config.Locations; 62 import com.sun.javatest.regtest.config.Locations.LibLocn; 63 import com.sun.javatest.regtest.config.Modules; 64 import com.sun.javatest.regtest.config.ParseException; 65 import com.sun.javatest.regtest.exec.RegressionScript.PathKind; 66 import com.sun.javatest.regtest.util.StringUtils; 67 68 import static com.sun.javatest.regtest.agent.RStatus.createStatus; 69 import static com.sun.javatest.regtest.agent.RStatus.error; 70 import static com.sun.javatest.regtest.agent.RStatus.failed; 71 import static com.sun.javatest.regtest.agent.RStatus.normalize; 72 import static com.sun.javatest.regtest.agent.RStatus.passed; 73 74 /** 75 * This class implements the "compile" action as described by the JDK tag 76 * specification. It is also invoked implicitly as needed by the "build" 77 * action. 78 * 79 * @author Iris A Garcia 80 * @see Action 81 * @see com.sun.javatest.regtest.agent.MainActionHelper 82 */ 83 public class CompileAction extends Action { 84 public static final String NAME = "compile"; 85 86 /** 87 * {@inheritDoc} 88 * @return "compile" 89 */ 90 @Override 91 public String getName() { 92 return NAME; 93 } 94 95 /** 96 * A method used by sibling classes to run both the init() and run() 97 * method of CompileAction. 98 * 99 * @param destDir Where to place the compiled classes 100 * @param opts The options for the action. 101 * @param args The arguments for the actions. 102 * @param reason Indication of why this action was invoked. 103 * @param script The script. 104 * @return The result of the action. 105 * @throws TestRunException if an error occurs while executing this action 106 * @see #init 107 * @see #run 108 */ 109 Status compile(LibLocn libLocn, Map<String,String> opts, List<String> args, String reason, 110 RegressionScript script) throws TestRunException { 111 this.libLocn = libLocn; 112 init(opts, args, reason, script); 113 return run(); 114 } // compile() 115 116 /** 117 * This method does initial processing of the options and arguments for the 118 * action. Processing is determined by the requirements of run() and 119 * getSourceFiles(). If run will be called, script.hasEnv() will be true. 120 * If script.hasEnv() is false, there is no context available to determine 121 * any class directories. 122 * 123 * Verify that the options are valid for the "compile" action. 124 * 125 * Verify that there is at least one argument. Find the class names to 126 * compile (via presence of ".java") and modify to contain fully qualified 127 * path. 128 * 129 * If one of the JVM options is "-classpath" or "-cp", add the test classes 130 * and test sources directory to the provided path. 131 * 132 * @param opts The options for the action. 133 * @param args The arguments for the actions. 134 * @param reason Indication of why this action was invoked. 135 * @param script The script. 136 * @exception ParseException If the options or arguments are not expected 137 * for the action or are improperly formated. 138 */ 139 @Override 140 public void init(Map<String,String> opts, List<String> args, String reason, 141 RegressionScript script) 142 throws ParseException { 143 super.init(opts, args, reason, script); 144 145 if (reason.startsWith(SREASON_USER_SPECIFIED)) 146 addDebugOpts = true; 147 148 if (args.isEmpty()) 149 throw new ParseException(COMPILE_NO_CLASSNAME); 150 151 for (Map.Entry<String,String> e: opts.entrySet()) { 152 String optName = e.getKey(); 153 String optValue = e.getValue(); 154 155 switch (optName) { 156 case "fail": 157 reverseStatus = parseFail(optValue); 158 break; 159 case "timeout": 160 timeout = parseTimeout(optValue); 161 break; 162 case "ref": 163 ref = parseRef(optValue); 164 break; 165 case "process": 166 process = true; 167 break; 168 case "module": 169 module = parseModule(optValue); 170 modules = Collections.singleton(module); 171 break; 172 case "modules": 173 if (optValue != null) 174 throw new ParseException(COMPILE_MODULES_UEXPECT + optValue); 175 multiModule = true; 176 modules = new LinkedHashSet<>(); 177 break; 178 default: 179 throw new ParseException(COMPILE_BAD_OPT + optName); 180 } 181 } 182 183 if (module != null && multiModule) { 184 throw new ParseException("Bad combination of options: /module=" + module + ", /modules"); 185 } 186 187 if (module == null && !multiModule) 188 modules = Collections.<String>emptySet(); 189 190 if (timeout < 0) 191 timeout = script.getActionTimeout(-1); 192 193 // add absolute path name to all the .java files create appropriate 194 // class directories 195 Locations locations = script.locations; 196 if (libLocn == null) { 197 destDir = multiModule ? locations.absTestModulesDir() : locations.absTestClsDir(module); 198 } else { 199 destDir = (module == null) ? libLocn.absClsDir : new File(libLocn.absClsDir, module); 200 } 201 if (!script.isCheck()) 202 mkdirs(destDir); 203 204 boolean foundJavaFile = false; 205 boolean foundAsmFile = false; 206 207 for (int i = 0; i < args.size(); i++) { 208 // note: in the following code, some args are overrwritten in place 209 String currArg = args.get(i); 210 211 if (currArg.endsWith(".java")) { 212 foundJavaFile = true; 213 File sourceFile = new File(currArg.replace('/', File.separatorChar)); 214 if (!sourceFile.isAbsolute()) { 215 // User must have used @compile, so file must be 216 // in the same directory as the defining file. 217 if (multiModule) 218 addModule(currArg); 219 File absSourceFile = locations.absTestSrcFile(module, sourceFile); 220 if (!absSourceFile.exists()) 221 throw new ParseException(CANT_FIND_SRC + currArg); 222 args.set(i, absSourceFile.getPath()); 223 } 224 } else if (currArg.endsWith(".jasm") || currArg.endsWith("jcod")) { 225 if (module != null) { 226 throw new ParseException(COMPILE_OPT_DISALLOW); 227 } 228 foundAsmFile = true; 229 File sourceFile = new File(currArg.replace('/', File.separatorChar)); 230 if (!sourceFile.isAbsolute()) { 231 // User must have used @compile, so file must be 232 // in the same directory as the defining file. 233 if (multiModule) 234 addModule(currArg); 235 File absSourceFile = locations.absTestSrcFile(null, sourceFile); 236 if (!absSourceFile.exists()) 237 throw new ParseException(CANT_FIND_SRC + currArg); 238 args.set(i, absSourceFile.getPath()); 239 } 240 } 241 242 if (currArg.equals("-classpath") || currArg.equals("-cp") 243 || currArg.equals("--class-path") || currArg.startsWith("--class-path=")) { 244 if (module != null || multiModule) { 245 throw new ParseException(COMPILE_OPT_DISALLOW); 246 } 247 classpathp = true; 248 if (!currArg.startsWith("--class-path=")) { 249 i++; 250 } 251 } else if (currArg.equals("-sourcepath") 252 || currArg.equals("--source-path") || currArg.startsWith("--source-path=")) { 253 if (module != null || multiModule) { 254 throw new ParseException(COMPILE_OPT_DISALLOW); 255 } 256 sourcepathp = true; 257 if (!currArg.startsWith("--source-path=")) { 258 i++; 259 } 260 } else if (currArg.equals("-d")) { 261 throw new ParseException(COMPILE_OPT_DISALLOW); 262 } 263 } 264 265 if (!foundJavaFile && !process && !foundAsmFile) { 266 throw new ParseException(COMPILE_NO_DOT_JAVA); 267 } 268 if (foundAsmFile) { 269 if (sourcepathp || classpathp || process) { 270 throw new ParseException(COMPILE_OPT_DISALLOW); 271 } 272 if (reverseStatus || ref != null) { 273 throw new ParseException(COMPILE_OPT_DISALLOW); 274 } 275 } 276 } // init() 277 278 @Override 279 public Set<File> getSourceFiles() { 280 Set<File> files = new LinkedHashSet<>(); 281 282 for (String currArg : args) { 283 if (currArg.endsWith(".java") 284 || currArg.endsWith(".jasm") 285 || currArg.endsWith(".jcod")) { 286 files.add(new File(currArg)); 287 } 288 } 289 290 return files; 291 } 292 293 @Override 294 public Set<String> getModules() { 295 return modules; 296 } 297 298 /** 299 * The method that does the work of the action. The necessary work for the 300 * given action is defined by the tag specification. 301 * 302 * Invoke the compiler on the given arguments which may possibly include 303 * compiler options. Equivalent to "javac arg+". 304 * 305 * Each named class will be compiled if its corresponding class file doesn't 306 * exist or is older than its source file. The class name is fully 307 * qualified as necessary and the ".java" extension is added before 308 * compilation. 309 * 310 * Build is allowed to search anywhere in the library-list. Compile is 311 * allowed to search only in the directory containing the defining file of 312 * the test. Thus, compile will always make files absolute by adding the 313 * directory path of the defining file to the passed filename. 314 * Build must pass an absolute filename to handle files found in the 315 * library-list. 316 * 317 * @return The result of the action. 318 * @throws TestRunException If an unexpected error occurs while executing 319 * the action. 320 */ 321 @Override 322 public Status run() throws TestRunException { 323 startAction(true); 324 325 List<String> javacArgs = new ArrayList<>(); 326 List<String> jasmArgs = new ArrayList<>(); 327 List<String> jcodArgs = new ArrayList<>(); 328 boolean runJavac = process; 329 330 for (String currArg : args) { 331 if (currArg.endsWith(".java")) { 332 if (!(new File(currArg)).exists()) 333 throw new TestRunException(CANT_FIND_SRC + currArg); 334 javacArgs.add(currArg); 335 runJavac = true; 336 } else if (currArg.endsWith(".jasm")) { 337 jasmArgs.add(currArg); 338 } else if (currArg.endsWith(".jcod")) { 339 jcodArgs.add(currArg); 340 } else 341 javacArgs.add(currArg); 342 } 343 344 Status status; 345 346 if (script.isCheck()) { 347 status = passed(CHECK_PASS); 348 } else { 349 // run jasm and jcod first (if needed) in case the resulting class 350 // files will be required when compiling the .java files. 351 status = passed("Not yet run"); 352 if (status.isPassed() && !jasmArgs.isEmpty()) 353 status = jasm(jasmArgs); 354 if (status.isPassed() && !jcodArgs.isEmpty()) 355 status = jcod(jcodArgs); 356 if (status.isPassed() && runJavac) { 357 javacArgs = getJavacCommandArgs(javacArgs); 358 if (explicitAnnotationProcessingRequested(javacArgs) 359 && !getExtraModuleConfigOptions(Modules.Phase.DYNAMIC).isEmpty()) { 360 othervmOverrideReasons.add("additional runtime exports needed for annotation processing"); 361 } 362 switch (!othervmOverrideReasons.isEmpty() ? ExecMode.OTHERVM : script.getExecMode()) { 363 case AGENTVM: 364 showMode(ExecMode.AGENTVM); 365 status = runAgentJVM(javacArgs); 366 break; 367 case OTHERVM: 368 showMode(ExecMode.OTHERVM, othervmOverrideReasons); 369 status = runOtherJVM(javacArgs); 370 break; 371 default: 372 throw new AssertionError(); 373 } 374 } 375 } 376 377 endAction(status); 378 return status; 379 } // run() 380 381 //----------internal methods------------------------------------------------ 382 383 private Status jasm(List<String> files) { 384 return asmtools("jasm", files); 385 } 386 387 private Status jcod(List<String> files) { 388 return asmtools("jcoder", files); 389 } 390 391 private Status asmtools(String toolName, List<String> files) { 392 if (files.isEmpty()) 393 return Status.passed(toolName + ": no files"); 394 395 List<String> toolArgs = new ArrayList<>(); 396 toolArgs.add("-d"); 397 toolArgs.add(destDir.getPath()); 398 toolArgs.addAll(files); 399 try { 400 String toolClassName = "org.openjdk.asmtools." + toolName + ".Main"; 401 recorder.asmtools(toolClassName, toolArgs); 402 Class<?> toolClass = Class.forName(toolClassName); 403 Constructor<?> c = toolClass.getConstructor(new Class<?>[] { PrintStream.class, String.class }); 404 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 405 PrintStream ps = new PrintStream(baos); 406 try { 407 Object tool = c.newInstance(ps, toolName); 408 Method m = toolClass.getMethod("compile", new Class<?>[] { String[].class }); 409 Object r = m.invoke(tool, new Object[] { toolArgs.toArray(new String[0]) }); 410 if (r instanceof Boolean) { 411 boolean ok = (Boolean) r; 412 return ok ? Status.passed(toolName + " OK") : Status.failed(toolName + " failed"); 413 } else 414 return Status.error("unexpected result from " + toolName + ": " + r.toString()); 415 } finally { 416 try (PrintWriter out = section.createOutput(toolName)) { 417 out.write(baos.toString()); 418 } 419 } 420 } catch (ClassNotFoundException e) { 421 return Status.error("can't find " + toolName); 422 } catch (NoSuchMethodException e) { 423 return Status.error("error invoking " + toolName + ": " + e); 424 } catch (InstantiationException e) { 425 return Status.error("error invoking " + toolName + ": " + e); 426 } catch (IllegalAccessException e) { 427 return Status.error("error invoking " + toolName + ": " + e); 428 } catch (InvocationTargetException e) { 429 return Status.error("error invoking " + toolName + ": " + e); 430 } catch (IllegalArgumentException t) { 431 return Status.error("error invoking " + toolName + ": " + t); 432 } catch (SecurityException t) { 433 return Status.error("error invoking " + toolName + ": " + t); 434 } 435 } 436 437 /** 438 * Determine the arguments for the compilation. 439 * Three different types of compilation are supported. 440 * <ul> 441 * <li>Compilation of classes in the unnamed module. 442 * This is the default "classic" compilation. 443 * The output directory should be a package-oriented directory. 444 * Sources and classes for the unnamed module are put on the 445 * sourcepath and classpath. 446 * <li>Compilation of classes in a single named user module. 447 * This mode is indicated by the option /module=module-name 448 * where module-name is not the name of a system module. 449 * The output directory should be the appropriate subdirectory 450 * of a module-oriented directory. 451 * The output directory should appear on the classpath. 452 * Sources and classes for the unnamed module are <i>not</i> available. 453 * <li>Compilation of classes to patch those in a system module. 454 * This mode is indicated by the option /module=module-name 455 * where module-name is the name of a system module. 456 * <li>Compilation of classes in one or more named user modules. 457 * This mode is indicated by the option /modules. 458 * The output directory should be a module-oriented directory. 459 * Sources and classes for the unnamed module are put on the 460 * sourcepath and classpath. 461 * </ul> 462 */ 463 private List<String> getJavacCommandArgs(List<String> args) throws TestRunException { 464 Map<PathKind, SearchPath> compilePaths = script.getCompilePaths(libLocn, multiModule, module); 465 466 JDKOpts javacArgs = new JDKOpts(); 467 javacArgs.addAll(script.getTestCompilerOptions()); 468 469 if (isModuleOptionsAllowed(args)) { 470 javacArgs.addAll(getExtraModuleConfigOptions(Modules.Phase.STATIC)); 471 } 472 473 if (destDir != null) { 474 javacArgs.add("-d"); 475 javacArgs.add(destDir.toString()); 476 } 477 478 if (module != null && script.systemModules.contains(module) 479 && !script.useNewPatchModule()) { 480 javacArgs.add("-Xmodule:" + module); 481 } 482 483 // modulesourcepath and sourcepath are mutually exclusive 484 if (multiModule) { 485 javacArgs.addPath("--module-source-path", compilePaths.get(PathKind.MODULESOURCEPATH)); 486 } else if (module != null && script.useNewPatchModule()) { 487 // Note: any additional patches for this module will be 488 // automatically merged with this one. 489 javacArgs.addAll("--patch-module", module + "=" + compilePaths.get(PathKind.SOURCEPATH)); 490 } else { 491 javacArgs.addPath("--source-path", compilePaths.get(PathKind.SOURCEPATH)); 492 } 493 494 // Need to refine what it means to put absTestClsDir unconditionally on the compilePath 495 SearchPath cp = compilePaths.get(PathKind.CLASSPATH); 496 javacArgs.addPath("--class-path", cp); 497 498 javacArgs.addPath("--module-path", compilePaths.get(PathKind.MODULEPATH)); 499 500 SearchPath pp = compilePaths.get(PathKind.PATCHPATH); 501 javacArgs.addAllPatchModules(pp); // will merge as needed with any similar preceding options 502 if (pp != null && !pp.isEmpty() && cp != null && !cp.isEmpty()) { 503 // provide addReads from patch modules to unnamed module(s). 504 for (String s: getModules(pp)) { 505 javacArgs.add("--add-reads=" + s + "=ALL-UNNAMED"); 506 } 507 } 508 509 Set<String> userMods = getModules(compilePaths.get(PathKind.MODULEPATH)); 510 if (!userMods.isEmpty()) { 511 javacArgs.add("--add-modules"); 512 javacArgs.add(StringUtils.join(userMods, ",")); 513 } 514 515 javacArgs.addAll(args); 516 517 return javacArgs.toList(); 518 } 519 520 private boolean isModuleOptionsAllowed(List<String> args) { 521 Iterator<String> iter = args.iterator(); 522 while (iter.hasNext()) { 523 String option = iter.next(); 524 switch (option) { 525 case "-source": 526 case "-target": 527 case "--release": 528 if (iter.hasNext()) { 529 JDK_Version v = JDK_Version.forName(iter.next()); 530 return v.compareTo(JDK_Version.V9) >= 0; 531 } 532 break; 533 534 default: 535 if (option.startsWith("--release=")) { 536 JDK_Version v = JDK_Version.forName( 537 option.substring(option.indexOf("=") + 1)); 538 return v.compareTo(JDK_Version.V9) >= 0; 539 } 540 break; 541 } 542 } 543 return true; 544 } 545 546 private Status runOtherJVM(List<String> javacArgs) throws TestRunException { 547 Status status; 548 549 // Set test.src and test.classes for the benefit of annotation processors 550 Map<String, String> javacProps = script.getTestProperties(); 551 552 // CONSTRUCT THE COMMAND LINE 553 Map<String, String> env = new LinkedHashMap<>(); 554 env.putAll(script.getEnvVars()); 555 556 String javacCmd = script.getJavacProg(); 557 558 JDKOpts javacVMOpts = new JDKOpts(); 559 javacVMOpts.addAll(script.getTestVMOptions()); 560 if (addDebugOpts && script.getCompileJDK().equals(script.getTestJDK())) 561 javacVMOpts.addAll(script.getTestDebugOptions()); 562 563 if (explicitAnnotationProcessingRequested(javacArgs)) { 564 javacVMOpts.addAll(getExtraModuleConfigOptions(Modules.Phase.DYNAMIC)); 565 } 566 567 // WRITE ARGUMENT FILE 568 List<String> fullJavacArgs = javacArgs; 569 if (javacArgs.size() >= 10) { 570 File argFile = getArgFile(); 571 try (BufferedWriter w = new BufferedWriter(new FileWriter(argFile))) { 572 for (String arg: javacArgs) { 573 w.write(arg); 574 w.newLine(); 575 } 576 } catch (IOException e) { 577 return error(COMPILE_CANT_WRITE_ARGS); 578 } catch (SecurityException e) { 579 // shouldn't happen since JavaTestSecurityManager allows file ops 580 return error(COMPILE_SECMGR_FILEOPS); 581 } 582 javacArgs = Arrays.asList("@" + argFile); 583 } 584 585 List<String> command = new ArrayList<>(); 586 command.add(javacCmd); 587 for (String opt: javacVMOpts.toList()) 588 command.add("-J" + opt); 589 for (Map.Entry<String,String> e: javacProps.entrySet()) 590 command.add("-J-D" + e.getKey() + "=" + e.getValue()); 591 command.addAll(javacArgs); 592 593 if (showMode) 594 showMode("compile", ExecMode.OTHERVM, section); 595 if (showCmd) 596 showCmd("compile", command, section); 597 598 new ModuleConfig("Boot Layer (javac runtime environment)") 599 .setFromOpts(javacVMOpts) 600 .write(configWriter); 601 new ModuleConfig("javac compilation environment") 602 .setFromOpts(fullJavacArgs) 603 .write(configWriter); 604 recorder.javac(env, javacCmd, javacVMOpts.toList(), javacProps, javacArgs); 605 606 // PASS TO PROCESSCOMMAND 607 PrintStringWriter stdOut = new PrintStringWriter(); 608 PrintStringWriter stdErr = new PrintStringWriter(); 609 ProcessCommand cmd = new ProcessCommand() { 610 @Override 611 protected Status getStatus(int exitCode, Status logStatus) { 612 // logStatus is never used by javac, so ignore it 613 JDK_Version v = script.getCompileJDKVersion(); 614 return CompileActionHelper.getStatusForJavacExitCode(v, exitCode); 615 } 616 }; 617 618 TimeoutHandler timeoutHandler = 619 script.getTimeoutHandlerProvider().createHandler(script, section); 620 621 cmd.setExecDir(script.absTestScratchDir()) 622 .setCommand(command) 623 .setEnvironment(env) 624 .setStreams(stdOut, stdErr) 625 .setTimeout(timeout, TimeUnit.SECONDS) 626 .setTimeoutHandler(timeoutHandler); 627 628 status = normalize(cmd.exec()); 629 630 try (PrintWriter sysOut = section.createOutput("System.out")) { 631 sysOut.write(stdOut.getOutput()); 632 } 633 634 try (PrintWriter sysErr = section.createOutput("System.err")) { 635 sysErr.write(stdErr.getOutput()); 636 } 637 638 // EVALUATE THE RESULTS 639 status = checkReverse(status, reverseStatus); 640 641 // COMPARE OUTPUT TO GOLDENFILE IF REQUIRED 642 // tag-spec says that "standard error is redirected to standard out 643 // so that /ref can be used." Simulate this by concatenating streams. 644 if ((ref != null) && status.isPassed()) { 645 String combined = stdOut.getOutput() + stdErr.getOutput(); 646 status = checkGoldenFile(combined, status); 647 } 648 649 return status; 650 } // runOtherJVM() 651 652 private Status runAgentJVM(List<String> javacArgs) throws TestRunException { 653 // TAG-SPEC: "The source and class directories of a test are made 654 // available to main and applet actions via the system properties 655 // "test.src" and "test.classes", respectively" 656 Map<String, String> javacProps = script.getTestProperties(); 657 658 if (showMode) 659 showMode("compile", ExecMode.AGENTVM, section); 660 if (showCmd) 661 showCmd("compile", javacArgs, section); 662 663 String javacProg = script.getJavacProg(); 664 List<String> javacVMOpts = script.getTestVMJavaOptions(); 665 recorder.javac(script.getEnvVars(), javacProg, javacVMOpts, javacProps, javacArgs); 666 667 Agent agent; 668 try { 669 JDK jdk = script.getCompileJDK(); 670 SearchPath agentClasspath = new SearchPath(jdk.getJDKClassPath(), script.getJavaTestClassPath()); 671 List<String> vmOpts = addDebugOpts && jdk.equals(script.getTestJDK()) 672 ? join(script.getTestVMOptions(), script.getTestDebugOptions()) 673 : script.getTestVMOptions(); 674 agent = script.getAgent(jdk, agentClasspath, vmOpts); 675 section.getMessageWriter().println("Agent id: " + agent.getId()); 676 new ModuleConfig("Boot Layer (javac runtime environment)") 677 .setFromOpts(agent.vmOpts) 678 .write(configWriter); 679 } catch (Agent.Fault e) { 680 return error(AGENTVM_CANT_GET_VM + ": " + e.getCause()); 681 } 682 683 TimeoutHandler timeoutHandler = 684 script.getTimeoutHandlerProvider().createHandler(script, section); 685 686 Status status; 687 try { 688 new ModuleConfig("javac compilation environment") 689 .setFromOpts(javacArgs) 690 .write(configWriter); 691 status = agent.doCompileAction( 692 script.getTestResult().getTestName(), 693 javacProps, 694 javacArgs, 695 timeout, 696 timeoutHandler, 697 section); 698 } catch (Agent.Fault e) { 699 if (e.getCause() instanceof IOException) 700 status = error(String.format(AGENTVM_IO_EXCEPTION, e.getCause())); 701 else 702 status = error(String.format(AGENTVM_EXCEPTION, e.getCause())); 703 } 704 if (status.isError()) { 705 script.closeAgent(agent); 706 } 707 708 // EVALUATE THE RESULTS 709 status = checkReverse(status, reverseStatus); 710 711 // COMPARE OUTPUT TO GOLDENFILE IF REQUIRED 712 // tag-spec says that "standard error is redirected to standard out 713 // so that /ref can be used." Simulate this by concatenating streams. 714 if ((ref != null) && status.isPassed()) { 715 String outString = getOutput(OutputHandler.OutputKind.DIRECT); 716 String errString = getOutput(OutputHandler.OutputKind.DIRECT_LOG); 717 String stdOutString = getOutput(OutputHandler.OutputKind.STDOUT); 718 String stdErrString = getOutput(OutputHandler.OutputKind.STDERR); 719 String combined = (outString + errString + stdOutString + stdErrString); 720 status = checkGoldenFile(combined, status); 721 } 722 723 return status; 724 } // runAgentJVM() 725 726 private String getOutput(OutputHandler.OutputKind kind) { 727 String s = section.getOutput(kind.name); 728 return (s == null) ? "" : s; 729 } 730 731 // See JavaCompiler.explicitAnnotationProcessingRequested 732 private boolean explicitAnnotationProcessingRequested(List<String> javacArgs) { 733 for (String arg: javacArgs) { 734 if (arg.equals("-processor") 735 || arg.equals("-processorpath") 736 || arg.equals("-processormodulepath") 737 || arg.equals("-proc:only") 738 || arg.equals("-Xprint")) { 739 return true; 740 } 741 } 742 return false; 743 } 744 745 //----------internal methods------------------------------------------------ 746 747 /** 748 * This method parses the <em>ref</em> action option used by the compile 749 * action. It verifies that the indicated reference file exists in the 750 * directory containing the defining file of the test. 751 * 752 * @param value The proposed filename for the reference file. 753 * @return A string indicating the name of the reference file for the 754 * test. 755 * @exception ParseException If the passed filename is null, the empty 756 * string, or does not exist. 757 */ 758 private String parseRef(String value) throws ParseException { 759 if ((value == null) || (value.equals(""))) 760 throw new ParseException(COMPILE_NO_REF_NAME); 761 File refFile = new File(script.absTestSrcDir(), value); 762 if (!refFile.exists()) 763 throw new ParseException(COMPILE_CANT_FIND_REF + refFile); 764 return value; 765 } // parseRef() 766 767 private Status checkReverse(Status status, boolean reverseStatus) { 768 if (!status.isError()) { 769 boolean ok = status.isPassed(); 770 int st = status.getType(); 771 String sr; 772 if (ok && reverseStatus) { 773 sr = COMPILE_PASS_UNEXPECT; 774 st = Status.FAILED; 775 } else if (ok && !reverseStatus) { 776 sr = COMPILE_PASS; 777 } else if (!ok && reverseStatus) { 778 sr = COMPILE_FAIL_EXPECT; 779 st = Status.PASSED; 780 } else { /* !ok && !reverseStatus */ 781 sr = COMPILE_FAIL; 782 } 783 if ((st == Status.FAILED) && ! (status.getReason() == null) && 784 !status.getReason().equals(EXEC_PASS)) 785 sr += ": " + status.getReason(); 786 status = createStatus(st, sr); 787 } 788 return status; 789 } 790 791 /** 792 * Compare output against a reference file. 793 * @param status default result if no differences found 794 * @param actual the text to be compared against the reference file 795 * @return a status indicating the first difference, or the default status 796 * if no differences found 797 * @throws TestRunException if the reference file can't be found 798 */ 799 private Status checkGoldenFile(String actual, Status status) throws TestRunException { 800 File refFile = new File(script.absTestSrcDir(), ref); 801 try (BufferedReader r1 = new BufferedReader(new StringReader(actual)); 802 BufferedReader r2 = new BufferedReader(new FileReader(refFile)) ) { 803 int lineNum; 804 if ((lineNum = compareGoldenFile(r1, r2)) != 0) { 805 return failed(COMPILE_GOLD_FAIL + ref + 806 COMPILE_GOLD_LINE + lineNum); 807 } 808 return status; 809 } catch (FileNotFoundException e) { 810 throw new TestRunException(COMPILE_CANT_FIND_REF + refFile); 811 } catch (IOException e) { 812 throw new TestRunException(COMPILE_CANT_READ_REF + refFile); 813 } 814 } 815 816 /** 817 * Line by line comparison of compile output and a reference file. If no 818 * differences are found, then 0 is returned. Otherwise, the line number 819 * where differences are first detected is returned. 820 * 821 * @param r1 The first item for comparison. 822 * @param r2 The second item for comparison. 823 * @return 0 If no differences are returned. Otherwise, the line number 824 * where differences were first detected. 825 */ 826 private int compareGoldenFile(BufferedReader r1, BufferedReader r2) 827 throws TestRunException { 828 try { 829 int lineNum = 0; 830 for ( ; ; ) { 831 String s1 = r1.readLine(); 832 String s2 = r2.readLine(); 833 lineNum++; 834 835 if ((s1 == null) && (s2 == null)) 836 return 0; 837 if ((s1 == null) || (s2 == null) || !s1.equals(s2)) { 838 return lineNum; 839 } 840 } 841 } catch (IOException e) { 842 File refFile = new File(script.absTestSrcDir(), ref); 843 throw new TestRunException(COMPILE_GOLD_READ_PROB + refFile); 844 } 845 } // compareGoldenFile() 846 847 private void addModule(String file) { 848 int sep = file.indexOf('/'); 849 if (sep > 0) 850 modules.add(file.substring(0, sep)); 851 } 852 853 //----------member variables------------------------------------------------ 854 855 private LibLocn libLocn; 856 private File destDir; 857 858 private boolean reverseStatus = false; 859 private String ref = null; 860 private int timeout = -1; 861 private boolean classpathp = false; 862 private boolean sourcepathp = false; 863 private boolean process = false; 864 private String module = null; 865 private boolean multiModule = false; 866 private Set<String> modules; 867 private boolean addDebugOpts = false; 868 protected Set<String> othervmOverrideReasons = new LinkedHashSet<>(); 869 }