1 /* 2 * Copyright (c) 2014, 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.tools.sjavac.options; 27 28 import java.nio.file.Path; 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.Collection; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 import java.util.HashSet; 37 38 import com.sun.tools.sjavac.Transformer; 39 import com.sun.tools.sjavac.Util; 40 41 /** 42 * Instances of this class represent values for sjavac command line options. 43 * 44 * <p><b>This is NOT part of any supported API. 45 * If you write code that depends on this, you do so at your own risk. 46 * This code and its internal interfaces are subject to change or 47 * deletion without notice.</b> 48 */ 49 public class Options { 50 51 // Output directories 52 private Path destDir, genSrcDir, headerDir, stateDir; 53 54 // Input directories 55 private List<SourceLocation> sources = new ArrayList<>(); 56 private List<SourceLocation> sourceSearchPaths = new ArrayList<>(); 57 private List<SourceLocation> classSearchPaths = new ArrayList<>(); 58 private List<SourceLocation> moduleSearchPaths = new ArrayList<>(); 59 60 private String logLevel = "info"; 61 62 private Set<String> permitted_artifacts = new HashSet<>(); 63 private boolean permitUnidentifiedArtifacts = false; 64 private boolean permitSourcesInDefaultPackage = false; 65 66 private Path sourceReferenceList; 67 private int numCores = 4; 68 private String implicitPolicy = "none"; 69 private List<String> javacArgs = new ArrayList<>(); 70 71 private Map<String, Transformer> trRules = new HashMap<>(); 72 73 private boolean startServer = false; 74 75 // Server configuration string 76 private String serverConf; 77 78 /** Get the policy for implicit classes */ 79 public String getImplicitPolicy() { 80 return implicitPolicy; 81 } 82 83 /** Get the path for generated sources (or null if no such path is set) */ 84 public Path getGenSrcDir() { 85 return genSrcDir; 86 } 87 88 /** Get the path for the destination directory */ 89 public Path getDestDir() { 90 return destDir; 91 } 92 93 /** Get the path for the header directory (or null if no such path is set) */ 94 public Path getHeaderDir() { 95 return headerDir; 96 } 97 98 /** Get the path for the state directory, defaults to destDir. */ 99 public Path getStateDir() { 100 return stateDir != null ? stateDir : destDir; 101 } 102 103 /** Get all source locations for files to be compiled */ 104 public List<SourceLocation> getSources() { 105 return sources; 106 } 107 108 /** 109 * Get all paths to search for classes in .java format. (Java-files in 110 * found here should not be compiled. 111 */ 112 public List<SourceLocation> getSourceSearchPaths() { 113 return sourceSearchPaths; 114 } 115 116 /** Get all paths to search for classes in. */ 117 public List<SourceLocation> getClassSearchPath() { 118 return classSearchPaths; 119 } 120 121 /** Get all paths to search for modules in. */ 122 public List<SourceLocation> getModuleSearchPaths() { 123 return moduleSearchPaths; 124 } 125 126 /** Get the log level. */ 127 public String getLogLevel() { 128 return logLevel; 129 } 130 131 /** Returns true iff the artifact is permitted in the output dir. */ 132 public boolean isUnidentifiedArtifactPermitted(String f) { 133 return permitted_artifacts.contains(f); 134 } 135 136 /** Returns true iff artifacts in the output directories should be kept, 137 * even if they would not be generated in a clean build. */ 138 public boolean areUnidentifiedArtifactsPermitted() { 139 return permitUnidentifiedArtifacts; 140 } 141 142 /** Returns true iff sources in the default package should be permitted. */ 143 public boolean isDefaultPackagePermitted() { 144 return permitSourcesInDefaultPackage; 145 } 146 147 /** Get the path to the list of reference sources (or null if none is set) */ 148 public Path getSourceReferenceList() { 149 return sourceReferenceList; 150 } 151 152 /** Get the number of cores to be used by sjavac */ 153 public int getNumCores() { 154 return numCores; 155 } 156 157 /** Returns all arguments relevant to javac but irrelevant to sjavac. */ 158 public List<String> getJavacArgs() { 159 return javacArgs; 160 } 161 162 /** 163 * Get a map which maps suffixes to transformers (for example 164 * ".java" -> CompileJavaPackages) 165 */ 166 public Map<String, Transformer> getTranslationRules() { 167 return trRules; 168 } 169 170 /** Return true iff a new server should be started */ 171 public boolean startServerFlag() { 172 return startServer; 173 } 174 175 /** Return the server configuration string. */ 176 public String getServerConf() { 177 return serverConf; 178 } 179 180 /** 181 * Parses the given argument array and returns a corresponding Options 182 * instance. 183 */ 184 public static Options parseArgs(String... args) { 185 Options options = new Options(); 186 options.new ArgDecoderOptionHelper().traverse(args); 187 return options; 188 } 189 190 /** Returns true iff a .java file is among the javac arguments */ 191 public boolean isJavaFilesAmongJavacArgs() { 192 for (String javacArg : javacArgs) 193 if (javacArg.endsWith(".java")) 194 return true; 195 return false; 196 } 197 198 /** 199 * Returns a string representation of the options that affect the result of 200 * the compilation. (Used for saving the state of the options used in a 201 * previous compile.) 202 */ 203 public String getStateArgsString() { 204 205 // Local utility class for collecting the arguments 206 class StateArgs { 207 208 private List<String> args = new ArrayList<>(); 209 210 void addArg(Option opt) { 211 args.add(opt.arg); 212 } 213 214 void addArg(Option opt, Object val) { 215 addArg(opt); 216 args.add(val.toString()); 217 } 218 219 void addSourceLocations(Option opt, List<SourceLocation> locs) { 220 for (SourceLocation sl : locs) { 221 for (String pkg : sl.includes) addArg(Option.I, pkg); 222 for (String pkg : sl.excludes) addArg(Option.X, pkg); 223 for (String f : sl.excludedFiles) addArg(Option.XF, f); 224 for (String f : sl.includedFiles) addArg(Option.IF, f); 225 addArg(opt, sl.getPath()); 226 } 227 } 228 229 String getResult() { 230 String result = ""; 231 for (String s : args) 232 result += s + " "; 233 return result.trim(); 234 } 235 236 public void addAll(Collection<String> toAdd) { 237 args.addAll(toAdd); 238 } 239 } 240 241 StateArgs args = new StateArgs(); 242 243 // Directories 244 if (genSrcDir != null) 245 args.addArg(Option.S, genSrcDir.normalize()); 246 247 if (headerDir != null) 248 args.addArg(Option.H, headerDir.normalize()); 249 250 if (destDir != null) 251 args.addArg(Option.D, destDir.normalize()); 252 253 if (stateDir != null) 254 args.addArg(Option.STATE_DIR, stateDir.normalize()); 255 256 // Source roots 257 args.addSourceLocations(Option.SRC, sources); 258 args.addSourceLocations(Option.SOURCEPATH, sourceSearchPaths); 259 args.addSourceLocations(Option.CLASSPATH, classSearchPaths); 260 args.addSourceLocations(Option.MODULEPATH, moduleSearchPaths); 261 262 // Boolean options 263 if (permitSourcesInDefaultPackage) 264 args.addArg(Option.PERMIT_SOURCES_WITHOUT_PACKAGE); 265 266 for (String f : permitted_artifacts) { 267 args.addArg(Option.PERMIT_ARTIFACT, f); 268 } 269 270 if (permitUnidentifiedArtifacts) 271 args.addArg(Option.PERMIT_UNIDENTIFIED_ARTIFACTS); 272 273 // Translation rules 274 for (Map.Entry<String, Transformer> tr : trRules.entrySet()) { 275 String val = tr.getKey() + "=" + tr.getValue().getClass().getName(); 276 args.addArg(Option.TR, val); 277 } 278 279 // Javac args 280 args.addAll(javacArgs); 281 282 return args.getResult(); 283 } 284 285 286 /** Extract the arguments to be passed on to javac. */ 287 public String[] prepJavacArgs() { 288 List<String> args = new ArrayList<>(); 289 290 //args.add("-doe"); 291 292 // Output directories 293 args.add("-d"); 294 args.add(destDir.toString()); 295 296 if (getGenSrcDir() != null) { 297 args.add("-s"); 298 args.add(genSrcDir.toString()); 299 } 300 301 if (headerDir != null) { 302 args.add("-h"); 303 args.add(headerDir.toString()); 304 } 305 306 // Prep sourcepath 307 List<SourceLocation> sourcepath = new ArrayList<>(); 308 sourcepath.addAll(sources); 309 sourcepath.addAll(sourceSearchPaths); 310 if (sourcepath.size() > 0) { 311 args.add("-sourcepath"); 312 args.add(concatenateSourceLocations(sourcepath)); 313 } 314 315 // Prep classpath 316 if (classSearchPaths.size() > 0) { 317 args.add("-classpath"); 318 args.add(concatenateSourceLocations(classSearchPaths)); 319 } 320 321 // This can't be anything but 'none'. Enforced by sjavac main method. 322 args.add("-implicit:" + implicitPolicy); 323 324 // If this option is not used, Object for instance is picked up from 325 // PLATFORM_CLASS_PATH. 326 // 327 // Turns out that bootclasspath = classpath, in the JDK build so it's 328 // still actually the correct file that is looked up. However, we don't 329 // want non-JDK-builds to track files on the bootclasspath, so we enable 330 // this option, to allow us to ignore dependencies on bootclasspath. 331 // 332 // It may make more sense to use an empty bootclasspath when building 333 // the JDK. 334 //args.add("-XXuserPathsFirst"); 335 336 // Append javac-options (i.e. pass through options not recognized by 337 // sjavac to javac.) 338 args.addAll(javacArgs); 339 340 return args.toArray(new String[args.size()]); 341 } 342 343 // Helper method to join a list of source locations separated by 344 // File.pathSeparator 345 private static String concatenateSourceLocations(List<SourceLocation> locs) { 346 String s = ""; 347 for (SourceLocation loc : locs) 348 s += (s.isEmpty() ? "" : java.io.File.pathSeparator) + loc.getPath(); 349 return s; 350 } 351 352 // OptionHelper that records the traversed options in this Options instance. 353 private class ArgDecoderOptionHelper extends OptionHelper { 354 355 List<String> includes, excludes, includeFiles, excludeFiles; 356 { 357 resetFilters(); 358 } 359 360 boolean headerProvided = false; 361 boolean genSrcProvided = false; 362 boolean stateProvided = false; 363 364 @Override 365 public void reportError(String msg) { 366 throw new IllegalArgumentException(msg); 367 } 368 369 @Override 370 public void sourceRoots(List<Path> paths) { 371 sources.addAll(createSourceLocations(paths)); 372 } 373 374 @Override 375 public void exclude(String exclPattern) { 376 exclPattern = Util.normalizeDriveLetter(exclPattern); 377 excludes.add(exclPattern); 378 } 379 380 @Override 381 public void include(String inclPattern) { 382 inclPattern = Util.normalizeDriveLetter(inclPattern); 383 includes.add(inclPattern); 384 } 385 386 @Override 387 public void excludeFile(String exclFilePattern) { 388 exclFilePattern = Util.normalizeDriveLetter(exclFilePattern); 389 excludeFiles.add(exclFilePattern); 390 } 391 392 @Override 393 public void includeFile(String inclFilePattern) { 394 inclFilePattern = Util.normalizeDriveLetter(inclFilePattern); 395 includeFiles.add(inclFilePattern); 396 } 397 398 @Override 399 public void addTransformer(String suffix, Transformer tr) { 400 if (trRules.containsKey(suffix)) { 401 reportError("More than one transformer specified for " + 402 "suffix " + suffix + "."); 403 return; 404 } 405 trRules.put(suffix, tr); 406 } 407 408 @Override 409 public void sourcepath(List<Path> paths) { 410 sourceSearchPaths.addAll(createSourceLocations(paths)); 411 } 412 413 @Override 414 public void modulepath(List<Path> paths) { 415 moduleSearchPaths.addAll(createSourceLocations(paths)); 416 } 417 418 @Override 419 public void classpath(List<Path> paths) { 420 classSearchPaths.addAll(createSourceLocations(paths)); 421 } 422 423 @Override 424 public void numCores(int n) { 425 numCores = n; 426 } 427 428 @Override 429 public void logLevel(String level) { 430 logLevel = level; 431 } 432 433 @Override 434 public void compareFoundSources(Path referenceList) { 435 sourceReferenceList = referenceList; 436 } 437 438 @Override 439 public void permitArtifact(String f) { 440 permitted_artifacts.add(f); 441 } 442 443 @Override 444 public void permitUnidentifiedArtifacts() { 445 permitUnidentifiedArtifacts = true; 446 } 447 448 @Override 449 public void permitDefaultPackage() { 450 permitSourcesInDefaultPackage = true; 451 } 452 453 @Override 454 public void serverConf(String conf) { 455 if (serverConf != null) 456 reportError("Can not specify more than one server configuration."); 457 else 458 serverConf = conf; 459 } 460 461 @Override 462 public void implicit(String policy) { 463 implicitPolicy = policy; 464 } 465 466 @Override 467 public void startServerConf(String conf) { 468 if (serverConf != null) 469 reportError("Can not specify more than one server configuration."); 470 else { 471 startServer = true; 472 serverConf = conf; 473 } 474 } 475 476 @Override 477 public void javacArg(String... arg) { 478 javacArgs.addAll(Arrays.asList(arg)); 479 } 480 481 @Override 482 public void destDir(Path dir) { 483 if (destDir != null) { 484 reportError("Destination directory already specified."); 485 return; 486 } 487 destDir = dir.toAbsolutePath(); 488 } 489 490 @Override 491 public void generatedSourcesDir(Path dir) { 492 if (genSrcProvided) { 493 reportError("Directory for generated sources already specified."); 494 return; 495 } 496 genSrcProvided = true; 497 genSrcDir = dir.toAbsolutePath(); 498 } 499 500 @Override 501 public void headerDir(Path dir) { 502 if (headerProvided) { 503 reportError("Header directory already specified."); 504 return; 505 } 506 headerProvided = true; 507 headerDir = dir.toAbsolutePath(); 508 } 509 510 @Override 511 public void stateDir(Path dir) { 512 if (stateProvided) { 513 reportError("State directory already specified."); 514 return; 515 } 516 stateProvided = true; 517 stateDir = dir.toAbsolutePath(); 518 } 519 520 private List<SourceLocation> createSourceLocations(List<Path> paths) { 521 List<SourceLocation> result = new ArrayList<>(); 522 for (Path path : paths) { 523 result.add(new SourceLocation( 524 path, 525 includes, 526 excludes, 527 includeFiles, 528 excludeFiles)); 529 } 530 resetFilters(); 531 return result; 532 } 533 534 private void resetFilters() { 535 includes = new ArrayList<>(); 536 excludes = new ArrayList<>(); 537 includeFiles = new ArrayList<>(); 538 excludeFiles = new ArrayList<>(); 539 } 540 } 541 542 }