/*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.sjavac.options;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.StringJoiner;
import com.sun.tools.sjavac.Transformer;
import com.sun.tools.sjavac.Util;
Instances of this class represent values for sjavac command line options.
This is NOT part of any supported API.
If you write code that depends on this, you do so at your own risk.
This code and its internal interfaces are subject to change or
deletion without notice.
/**
* Instances of this class represent values for sjavac command line options.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class Options {
// Output directories
private Path destDir, genSrcDir, headerDir, stateDir;
// Input directories
private List<SourceLocation> sources = new ArrayList<>();
private List<SourceLocation> sourceSearchPaths = new ArrayList<>();
private List<SourceLocation> classSearchPaths = new ArrayList<>();
private List<SourceLocation> moduleSearchPaths = new ArrayList<>();
private String logLevel = "info";
private Set<String> permitted_artifacts = new HashSet<>();
private boolean permitUnidentifiedArtifacts = false;
private boolean permitSourcesInDefaultPackage = false;
private Path sourceReferenceList;
private int numCores = 4;
private String implicitPolicy = "none";
private List<String> javacArgs = new ArrayList<>();
private Map<String, Transformer> trRules = new HashMap<>();
private boolean startServer = false;
// Server configuration string
private String serverConf;
Get the policy for implicit classes /** Get the policy for implicit classes */
public String getImplicitPolicy() {
return implicitPolicy;
}
Get the path for generated sources (or null if no such path is set) /** Get the path for generated sources (or null if no such path is set) */
public Path getGenSrcDir() {
return genSrcDir;
}
Get the path for the destination directory /** Get the path for the destination directory */
public Path getDestDir() {
return destDir;
}
Get the path for the header directory (or null if no such path is set) /** Get the path for the header directory (or null if no such path is set) */
public Path getHeaderDir() {
return headerDir;
}
Get the path for the state directory, defaults to destDir. /** Get the path for the state directory, defaults to destDir. */
public Path getStateDir() {
return stateDir;
}
Get all source locations for files to be compiled /** Get all source locations for files to be compiled */
public List<SourceLocation> getSources() {
return sources;
}
Get all paths to search for classes in .java format. (Java-files in
found here should not be compiled.
/**
* Get all paths to search for classes in .java format. (Java-files in
* found here should not be compiled.
*/
public List<SourceLocation> getSourceSearchPaths() {
return sourceSearchPaths;
}
Get all paths to search for classes in. /** Get all paths to search for classes in. */
public List<SourceLocation> getClassSearchPath() {
return classSearchPaths;
}
Get all paths to search for modules in. /** Get all paths to search for modules in. */
public List<SourceLocation> getModuleSearchPaths() {
return moduleSearchPaths;
}
Get the log level. /** Get the log level. */
public String getLogLevel() {
return logLevel;
}
Returns true iff the artifact is permitted in the output dir. /** Returns true iff the artifact is permitted in the output dir. */
public boolean isUnidentifiedArtifactPermitted(String f) {
return permitted_artifacts.contains(f);
}
Returns true iff artifacts in the output directories should be kept,
even if they would not be generated in a clean build. /** Returns true iff artifacts in the output directories should be kept,
* even if they would not be generated in a clean build. */
public boolean areUnidentifiedArtifactsPermitted() {
return permitUnidentifiedArtifacts;
}
Returns true iff sources in the default package should be permitted. /** Returns true iff sources in the default package should be permitted. */
public boolean isDefaultPackagePermitted() {
return permitSourcesInDefaultPackage;
}
Get the path to the list of reference sources (or null if none is set) /** Get the path to the list of reference sources (or null if none is set) */
public Path getSourceReferenceList() {
return sourceReferenceList;
}
Get the number of cores to be used by sjavac /** Get the number of cores to be used by sjavac */
public int getNumCores() {
return numCores;
}
Returns all arguments relevant to javac but irrelevant to sjavac. /** Returns all arguments relevant to javac but irrelevant to sjavac. */
public List<String> getJavacArgs() {
return javacArgs;
}
Get a map which maps suffixes to transformers (for example ".java" -> CompileJavaPackages) /**
* Get a map which maps suffixes to transformers (for example
* ".java" {@literal ->} CompileJavaPackages)
*/
public Map<String, Transformer> getTranslationRules() {
return trRules;
}
Return true iff a new server should be started /** Return true iff a new server should be started */
public boolean startServerFlag() {
return startServer;
}
Return the server configuration string. /** Return the server configuration string. */
public String getServerConf() {
return serverConf;
}
Parses the given argument array and returns a corresponding Options
instance.
/**
* Parses the given argument array and returns a corresponding Options
* instance.
*/
public static Options parseArgs(String... args) {
Options options = new Options();
options.new ArgDecoderOptionHelper().traverse(args);
return options;
}
Returns true iff a .java file is among the javac arguments /** Returns true iff a .java file is among the javac arguments */
public boolean isJavaFilesAmongJavacArgs() {
for (String javacArg : javacArgs)
if (javacArg.endsWith(".java"))
return true;
return false;
}
Returns a string representation of the options that affect the result of
the compilation. (Used for saving the state of the options used in a
previous compile.)
/**
* Returns a string representation of the options that affect the result of
* the compilation. (Used for saving the state of the options used in a
* previous compile.)
*/
public String getStateArgsString() {
// Local utility class for collecting the arguments
class StateArgs {
private List<String> args = new ArrayList<>();
void addArg(Option opt) {
args.add(opt.arg);
}
void addArg(Option opt, Object val) {
addArg(opt);
args.add(val.toString());
}
void addSourceLocations(Option opt, List<SourceLocation> locs) {
for (SourceLocation sl : locs) {
for (String pkg : sl.includes) addArg(Option.I, pkg);
for (String pkg : sl.excludes) addArg(Option.X, pkg);
addArg(opt, sl.getPath());
}
}
String getResult() {
return String.join(" ", args);
}
public void addAll(Collection<String> toAdd) {
args.addAll(toAdd);
}
}
StateArgs args = new StateArgs();
// Directories
if (genSrcDir != null)
args.addArg(Option.S, genSrcDir.normalize());
if (headerDir != null)
args.addArg(Option.H, headerDir.normalize());
if (destDir != null)
args.addArg(Option.D, destDir.normalize());
if (stateDir != null)
args.addArg(Option.STATE_DIR, stateDir.normalize());
// Source roots
args.addSourceLocations(Option.SRC, sources);
args.addSourceLocations(Option.SOURCE_PATH, sourceSearchPaths);
args.addSourceLocations(Option.CLASS_PATH, classSearchPaths);
args.addSourceLocations(Option.MODULE_PATH, moduleSearchPaths);
// Boolean options
if (permitSourcesInDefaultPackage)
args.addArg(Option.PERMIT_SOURCES_WITHOUT_PACKAGE);
for (String f : permitted_artifacts) {
args.addArg(Option.PERMIT_ARTIFACT, f);
}
if (permitUnidentifiedArtifacts)
args.addArg(Option.PERMIT_UNIDENTIFIED_ARTIFACTS);
// Translation rules
for (Map.Entry<String, Transformer> tr : trRules.entrySet()) {
String val = tr.getKey() + "=" + tr.getValue().getClass().getName();
args.addArg(Option.TR, val);
}
// Javac args
args.addAll(javacArgs);
return args.getResult();
}
Extract the arguments to be passed on to javac. /** Extract the arguments to be passed on to javac. */
public String[] prepJavacArgs() {
List<String> args = new ArrayList<>();
// Output directories
args.add("-d");
args.add(destDir.toString());
if (getGenSrcDir() != null) {
args.add("-s");
args.add(genSrcDir.toString());
}
if (headerDir != null) {
args.add("-h");
args.add(headerDir.toString());
}
// Prep sourcepath
List<SourceLocation> sourcepath = new ArrayList<>();
sourcepath.addAll(sources);
sourcepath.addAll(sourceSearchPaths);
if (sourcepath.size() > 0) {
args.add("-sourcepath");
args.add(concatenateSourceLocations(sourcepath));
}
// Prep classpath
if (classSearchPaths.size() > 0) {
args.add("-classpath");
args.add(concatenateSourceLocations(classSearchPaths));
}
// Enable dependency generation
args.add("--debug=completionDeps=source,class");
// This can't be anything but 'none'. Enforced by sjavac main method.
args.add("-implicit:" + implicitPolicy);
// If this option is not used, Object for instance is erroneously
// picked up from PLATFORM_CLASS_PATH instead of CLASS_PATH.
//
// Discussing this further led to the decision of letting bootclasspath
// be a dummy (empty) directory when building the JDK.
//args.add("-XXuserPathsFirst");
// Append javac-options (i.e. pass through options not recognized by
// sjavac to javac.)
args.addAll(javacArgs);
return args.toArray(new String[args.size()]);
}
// Helper method to join a list of source locations separated by
// File.pathSeparator
private static String concatenateSourceLocations(List<SourceLocation> locs) {
StringJoiner joiner = new StringJoiner(java.io.File.pathSeparator);
for (SourceLocation loc : locs) {
joiner.add(loc.getPath().toString());
}
return joiner.toString();
}
// OptionHelper that records the traversed options in this Options instance.
private class ArgDecoderOptionHelper extends OptionHelper {
List<String> includes, excludes, includeFiles, excludeFiles;
{
resetFilters();
}
boolean headerProvided = false;
boolean genSrcProvided = false;
boolean stateProvided = false;
@Override
public void reportError(String msg) {
throw new IllegalArgumentException(msg);
}
@Override
public void sourceRoots(List<Path> paths) {
sources.addAll(createSourceLocations(paths));
}
@Override
public void exclude(String exclPattern) {
exclPattern = Util.normalizeDriveLetter(exclPattern);
excludes.add(exclPattern);
}
@Override
public void include(String inclPattern) {
inclPattern = Util.normalizeDriveLetter(inclPattern);
includes.add(inclPattern);
}
@Override
public void addTransformer(String suffix, Transformer tr) {
if (trRules.containsKey(suffix)) {
reportError("More than one transformer specified for " +
"suffix " + suffix + ".");
return;
}
trRules.put(suffix, tr);
}
@Override
public void sourcepath(List<Path> paths) {
sourceSearchPaths.addAll(createSourceLocations(paths));
}
@Override
public void modulepath(List<Path> paths) {
moduleSearchPaths.addAll(createSourceLocations(paths));
}
@Override
public void classpath(List<Path> paths) {
classSearchPaths.addAll(createSourceLocations(paths));
}
@Override
public void numCores(int n) {
numCores = n;
}
@Override
public void logLevel(String level) {
logLevel = level;
}
@Override
public void compareFoundSources(Path referenceList) {
sourceReferenceList = referenceList;
}
@Override
public void permitArtifact(String f) {
permitted_artifacts.add(f);
}
@Override
public void permitUnidentifiedArtifacts() {
permitUnidentifiedArtifacts = true;
}
@Override
public void permitDefaultPackage() {
permitSourcesInDefaultPackage = true;
}
@Override
public void serverConf(String conf) {
if (serverConf != null)
reportError("Can not specify more than one server configuration.");
else
serverConf = conf;
}
@Override
public void implicit(String policy) {
implicitPolicy = policy;
}
@Override
public void startServerConf(String conf) {
if (serverConf != null)
reportError("Can not specify more than one server configuration.");
else {
startServer = true;
serverConf = conf;
}
}
@Override
public void javacArg(String... arg) {
javacArgs.addAll(Arrays.asList(arg));
}
@Override
public void destDir(Path dir) {
if (destDir != null) {
reportError("Destination directory already specified.");
return;
}
destDir = dir.toAbsolutePath();
}
@Override
public void generatedSourcesDir(Path dir) {
if (genSrcProvided) {
reportError("Directory for generated sources already specified.");
return;
}
genSrcProvided = true;
genSrcDir = dir.toAbsolutePath();
}
@Override
public void headerDir(Path dir) {
if (headerProvided) {
reportError("Header directory already specified.");
return;
}
headerProvided = true;
headerDir = dir.toAbsolutePath();
}
@Override
public void stateDir(Path dir) {
if (stateProvided) {
reportError("State directory already specified.");
return;
}
stateProvided = true;
stateDir = dir.toAbsolutePath();
}
private List<SourceLocation> createSourceLocations(List<Path> paths) {
List<SourceLocation> result = new ArrayList<>();
for (Path path : paths) {
result.add(new SourceLocation(
path,
includes,
excludes));
}
resetFilters();
return result;
}
private void resetFilters() {
includes = new ArrayList<>();
excludes = new ArrayList<>();
includeFiles = new ArrayList<>();
excludeFiles = new ArrayList<>();
}
}
}