Options.java revision 3034:c8206f440046
1/*
2 * Copyright (c) 2014, 2015, 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;
37
38import com.sun.tools.sjavac.Transformer;
39import 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 */
49public 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;
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" {@literal ->} 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        // Output directories
291        args.add("-d");
292        args.add(destDir.toString());
293
294        if (getGenSrcDir() != null) {
295            args.add("-s");
296            args.add(genSrcDir.toString());
297        }
298
299        if (headerDir != null) {
300            args.add("-h");
301            args.add(headerDir.toString());
302        }
303
304        // Prep sourcepath
305        List<SourceLocation> sourcepath = new ArrayList<>();
306        sourcepath.addAll(sources);
307        sourcepath.addAll(sourceSearchPaths);
308        if (sourcepath.size() > 0) {
309            args.add("-sourcepath");
310            args.add(concatenateSourceLocations(sourcepath));
311        }
312
313        // Prep classpath
314        if (classSearchPaths.size() > 0) {
315            args.add("-classpath");
316            args.add(concatenateSourceLocations(classSearchPaths));
317        }
318
319        // Enable dependency generation
320        args.add("-XDcompletionDeps=source,class");
321
322        // This can't be anything but 'none'. Enforced by sjavac main method.
323        args.add("-implicit:" + implicitPolicy);
324
325        // If this option is not used, Object for instance is erroneously
326        // picked up from PLATFORM_CLASS_PATH instead of CLASS_PATH.
327        //
328        // Discussing this further led to the decision of letting bootclasspath
329        // be a dummy (empty) directory when building the JDK.
330        //args.add("-XXuserPathsFirst");
331
332        // Append javac-options (i.e. pass through options not recognized by
333        // sjavac to javac.)
334        args.addAll(javacArgs);
335
336        return args.toArray(new String[args.size()]);
337    }
338
339    // Helper method to join a list of source locations separated by
340    // File.pathSeparator
341    private static String concatenateSourceLocations(List<SourceLocation> locs) {
342        String s = "";
343        for (SourceLocation loc : locs)
344            s += (s.isEmpty() ? "" : java.io.File.pathSeparator) + loc.getPath();
345        return s;
346    }
347
348    // OptionHelper that records the traversed options in this Options instance.
349    private class ArgDecoderOptionHelper extends OptionHelper {
350
351        List<String> includes, excludes, includeFiles, excludeFiles;
352        {
353            resetFilters();
354        }
355
356        boolean headerProvided = false;
357        boolean genSrcProvided = false;
358        boolean stateProvided = false;
359
360        @Override
361        public void reportError(String msg) {
362            throw new IllegalArgumentException(msg);
363        }
364
365        @Override
366        public void sourceRoots(List<Path> paths) {
367            sources.addAll(createSourceLocations(paths));
368        }
369
370        @Override
371        public void exclude(String exclPattern) {
372            exclPattern = Util.normalizeDriveLetter(exclPattern);
373            excludes.add(exclPattern);
374        }
375
376        @Override
377        public void include(String inclPattern) {
378            inclPattern = Util.normalizeDriveLetter(inclPattern);
379            includes.add(inclPattern);
380        }
381
382        @Override
383        public void excludeFile(String exclFilePattern) {
384            exclFilePattern = Util.normalizeDriveLetter(exclFilePattern);
385            excludeFiles.add(exclFilePattern);
386        }
387
388        @Override
389        public void includeFile(String inclFilePattern) {
390            inclFilePattern = Util.normalizeDriveLetter(inclFilePattern);
391            includeFiles.add(inclFilePattern);
392        }
393
394        @Override
395        public void addTransformer(String suffix, Transformer tr) {
396            if (trRules.containsKey(suffix)) {
397                reportError("More than one transformer specified for " +
398                            "suffix " + suffix + ".");
399                return;
400            }
401            trRules.put(suffix, tr);
402        }
403
404        @Override
405        public void sourcepath(List<Path> paths) {
406            sourceSearchPaths.addAll(createSourceLocations(paths));
407        }
408
409        @Override
410        public void modulepath(List<Path> paths) {
411            moduleSearchPaths.addAll(createSourceLocations(paths));
412        }
413
414        @Override
415        public void classpath(List<Path> paths) {
416            classSearchPaths.addAll(createSourceLocations(paths));
417        }
418
419        @Override
420        public void numCores(int n) {
421            numCores = n;
422        }
423
424        @Override
425        public void logLevel(String level) {
426            logLevel = level;
427        }
428
429        @Override
430        public void compareFoundSources(Path referenceList) {
431            sourceReferenceList = referenceList;
432        }
433
434        @Override
435        public void permitArtifact(String f) {
436            permitted_artifacts.add(f);
437        }
438
439        @Override
440        public void permitUnidentifiedArtifacts() {
441            permitUnidentifiedArtifacts = true;
442        }
443
444        @Override
445        public void permitDefaultPackage() {
446            permitSourcesInDefaultPackage = true;
447        }
448
449        @Override
450        public void serverConf(String conf) {
451            if (serverConf != null)
452                reportError("Can not specify more than one server configuration.");
453            else
454                serverConf = conf;
455        }
456
457        @Override
458        public void implicit(String policy) {
459            implicitPolicy = policy;
460        }
461
462        @Override
463        public void startServerConf(String conf) {
464            if (serverConf != null)
465                reportError("Can not specify more than one server configuration.");
466            else {
467                startServer = true;
468                serverConf = conf;
469            }
470        }
471
472        @Override
473        public void javacArg(String... arg) {
474            javacArgs.addAll(Arrays.asList(arg));
475        }
476
477        @Override
478        public void destDir(Path dir) {
479            if (destDir != null) {
480                reportError("Destination directory already specified.");
481                return;
482            }
483            destDir = dir.toAbsolutePath();
484        }
485
486        @Override
487        public void generatedSourcesDir(Path dir) {
488            if (genSrcProvided) {
489                reportError("Directory for generated sources already specified.");
490                return;
491            }
492            genSrcProvided = true;
493            genSrcDir = dir.toAbsolutePath();
494        }
495
496        @Override
497        public void headerDir(Path dir) {
498            if (headerProvided) {
499                reportError("Header directory already specified.");
500                return;
501            }
502            headerProvided = true;
503            headerDir = dir.toAbsolutePath();
504        }
505
506        @Override
507        public void stateDir(Path dir) {
508            if (stateProvided) {
509                reportError("State directory already specified.");
510                return;
511            }
512            stateProvided = true;
513            stateDir = dir.toAbsolutePath();
514        }
515
516        private List<SourceLocation> createSourceLocations(List<Path> paths) {
517            List<SourceLocation> result = new ArrayList<>();
518            for (Path path : paths) {
519                result.add(new SourceLocation(
520                        path,
521                        includes,
522                        excludes,
523                        includeFiles,
524                        excludeFiles));
525            }
526            resetFilters();
527            return result;
528        }
529
530        private void resetFilters() {
531            includes = new ArrayList<>();
532            excludes = new ArrayList<>();
533            includeFiles = new ArrayList<>();
534            excludeFiles = new ArrayList<>();
535        }
536    }
537
538}
539