Options.java revision 3643:589ff4d43428
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
26package com.sun.tools.sjavac.options;
27
28import java.nio.file.Path;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Collection;
32import java.util.HashMap;
33import java.util.List;
34import java.util.Map;
35import java.util.Set;
36import java.util.HashSet;
37import java.util.StringJoiner;
38
39import com.sun.tools.sjavac.Transformer;
40import 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 */
50public 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("--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
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}
522