1 /*
2  * Copyright (c) 2014, 2018, 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 */
getImplicitPolicy()80     public String getImplicitPolicy() {
81         return implicitPolicy;
82     }
83 
84     /** Get the path for generated sources (or null if no such path is set) */
getGenSrcDir()85     public Path getGenSrcDir() {
86         return genSrcDir;
87     }
88 
89     /** Get the path for the destination directory */
getDestDir()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) */
getHeaderDir()95     public Path getHeaderDir() {
96         return headerDir;
97     }
98 
99     /** Get the path for the state directory, defaults to destDir. */
getStateDir()100     public Path getStateDir() {
101         return stateDir;
102     }
103 
104     /** Get all source locations for files to be compiled */
getSources()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      */
getSourceSearchPaths()113     public List<SourceLocation> getSourceSearchPaths() {
114         return sourceSearchPaths;
115     }
116 
117     /** Get all paths to search for classes in. */
getClassSearchPath()118     public List<SourceLocation> getClassSearchPath() {
119         return classSearchPaths;
120     }
121 
122     /** Get all paths to search for modules in. */
getModuleSearchPaths()123     public List<SourceLocation> getModuleSearchPaths() {
124         return moduleSearchPaths;
125     }
126 
127     /** Get the log level. */
getLogLevel()128     public String getLogLevel() {
129         return logLevel;
130     }
131 
132     /** Returns true iff the artifact is permitted in the output dir. */
isUnidentifiedArtifactPermitted(String f)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. */
areUnidentifiedArtifactsPermitted()139     public boolean areUnidentifiedArtifactsPermitted() {
140         return permitUnidentifiedArtifacts;
141     }
142 
143     /** Returns true iff sources in the default package should be permitted. */
isDefaultPackagePermitted()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) */
getSourceReferenceList()149     public Path getSourceReferenceList() {
150         return sourceReferenceList;
151     }
152 
153     /** Get the number of cores to be used by sjavac */
getNumCores()154     public int getNumCores() {
155         return numCores;
156     }
157 
158     /** Returns all arguments relevant to javac but irrelevant to sjavac. */
getJavacArgs()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      */
getTranslationRules()167     public Map<String, Transformer> getTranslationRules() {
168         return trRules;
169     }
170 
171     /** Return true iff a new server should be started */
startServerFlag()172     public boolean startServerFlag() {
173         return startServer;
174     }
175 
176     /** Return the server configuration string. */
getServerConf()177     public String getServerConf() {
178         return serverConf;
179     }
180 
181     /**
182      * Parses the given argument array and returns a corresponding Options
183      * instance.
184      */
parseArgs(String... args)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 */
isJavaFilesAmongJavacArgs()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      */
getStateArgsString()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. */
prepJavacArgs()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("--debug=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
concatenateSourceLocations(List<SourceLocation> locs)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         {
resetFilters()350             resetFilters();
351         }
352 
353         boolean headerProvided = false;
354         boolean genSrcProvided = false;
355         boolean stateProvided = false;
356 
357         @Override
reportError(String msg)358         public void reportError(String msg) {
359             throw new IllegalArgumentException(msg);
360         }
361 
362         @Override
sourceRoots(List<Path> paths)363         public void sourceRoots(List<Path> paths) {
364             sources.addAll(createSourceLocations(paths));
365         }
366 
367         @Override
exclude(String exclPattern)368         public void exclude(String exclPattern) {
369             exclPattern = Util.normalizeDriveLetter(exclPattern);
370             excludes.add(exclPattern);
371         }
372 
373         @Override
include(String inclPattern)374         public void include(String inclPattern) {
375             inclPattern = Util.normalizeDriveLetter(inclPattern);
376             includes.add(inclPattern);
377         }
378 
379         @Override
addTransformer(String suffix, Transformer tr)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
sourcepath(List<Path> paths)390         public void sourcepath(List<Path> paths) {
391             sourceSearchPaths.addAll(createSourceLocations(paths));
392         }
393 
394         @Override
modulepath(List<Path> paths)395         public void modulepath(List<Path> paths) {
396             moduleSearchPaths.addAll(createSourceLocations(paths));
397         }
398 
399         @Override
classpath(List<Path> paths)400         public void classpath(List<Path> paths) {
401             classSearchPaths.addAll(createSourceLocations(paths));
402         }
403 
404         @Override
numCores(int n)405         public void numCores(int n) {
406             numCores = n;
407         }
408 
409         @Override
logLevel(String level)410         public void logLevel(String level) {
411             logLevel = level;
412         }
413 
414         @Override
compareFoundSources(Path referenceList)415         public void compareFoundSources(Path referenceList) {
416             sourceReferenceList = referenceList;
417         }
418 
419         @Override
permitArtifact(String f)420         public void permitArtifact(String f) {
421             permitted_artifacts.add(f);
422         }
423 
424         @Override
permitUnidentifiedArtifacts()425         public void permitUnidentifiedArtifacts() {
426             permitUnidentifiedArtifacts = true;
427         }
428 
429         @Override
permitDefaultPackage()430         public void permitDefaultPackage() {
431             permitSourcesInDefaultPackage = true;
432         }
433 
434         @Override
serverConf(String conf)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
implicit(String policy)443         public void implicit(String policy) {
444             implicitPolicy = policy;
445         }
446 
447         @Override
startServerConf(String conf)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
javacArg(String... arg)458         public void javacArg(String... arg) {
459             javacArgs.addAll(Arrays.asList(arg));
460         }
461 
462         @Override
destDir(Path dir)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
generatedSourcesDir(Path dir)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
headerDir(Path dir)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
stateDir(Path dir)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 
createSourceLocations(List<Path> paths)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 
resetFilters()513         private void resetFilters() {
514             includes = new ArrayList<>();
515             excludes = new ArrayList<>();
516             includeFiles = new ArrayList<>();
517             excludeFiles = new ArrayList<>();
518         }
519     }
520 
521 }
522