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