src/share/classes/com/sun/tools/sjavac/Main.java

changeset 0
959103a6100f
child 2525
2eb010b6cb22
equal deleted inserted replaced
-1:000000000000 0:959103a6100f
1 /*
2 * Copyright (c) 2012, 2013, 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;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.PrintStream;
31 import java.util.*;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import com.sun.tools.sjavac.server.JavacServer;
36
37 /**
38 * The main class of the smart javac wrapper tool.
39 *
40 * <p><b>This is NOT part of any supported API.
41 * If you write code that depends on this, you do so at your own
42 * risk. This code and its internal interfaces are subject to change
43 * or deletion without notice.</b></p>
44 */
45 public class Main {
46
47 /* This is a smart javac wrapper primarily used when building the OpenJDK,
48 though other projects are welcome to use it too. But please be aware
49 that it is not an official api and will change in the future.
50 (We really mean it!)
51
52 Goals:
53
54 ** Create a state file, containing information about the build, so
55 that incremental builds only rebuild what is necessary. Also the
56 state file can be used by make/ant to detect when to trigger
57 a call to the smart javac wrapper.
58
59 This file is called bin/javac_state (assuming that you specified "-d bin")
60 Thus the simplest makefile is:
61
62 SJAVAC=java -cp .../tools.jar com.sun.tools.sjavac.Main
63 SRCS=$(shell find src -name "*.java")
64 bin/javac_state : $(SRCS)
65 $(SJAVAC) src -d bin
66
67 This makefile will run very fast and detect properly when Java code needs to
68 be recompiled. The smart javac wrapper will then use the information in java_state
69 to do an efficient incremental compile.
70
71 Previously it was near enough impossible to write an efficient makefile for Java
72 with support for incremental builds and dependency tracking.
73
74 ** Separate java sources to be compiled from java
75 sources used >only< for linking. The options:
76
77 "dir" points to root dir with sources to be compiled
78 "-sourcepath dir" points to root dir with sources used only for linking
79 "-classpath dir" points to dir with classes used only for linking (as before)
80
81 ** Use all cores for compilation by default.
82 "-j 4" limit the number of cores to 4.
83 For the moment, the sjavac server additionally limits the number of cores to three.
84 This will improve in the future when more sharing is performed between concurrent JavaCompilers.
85
86 ** Basic translation support from other sources to java, and then compilation of the generated java.
87 This functionality might be moved into annotation processors instead.
88 Again this is driven by the OpenJDK sources where properties and a few other types of files
89 are converted into Java sources regularily. The javac_state embraces copy and tr, and perform
90 incremental recompiles and copying for these as well. META-INF will be a special copy rule
91 that will copy any files found below any META-INF dir in src to the bin/META-INF dir.
92 "-copy .gif"
93 "-copy META-INF"
94 "-tr .prop=com.sun.tools.javac.smart.CompileProperties
95 "-tr .propp=com.sun.tools.javac.smart.CompileProperties,java.util.ListResourceBundle
96 "-tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle
97
98 ** Control which classes in the src,sourcepath and classpath that javac is allowed to see.
99 Again, this is necessary to deal with the source code structure of the OpenJDK which is
100 intricate (read messy).
101
102 "-i tools.*" to include the tools package and all its subpackages in the build.
103 "-x tools.net.aviancarrier.*" to exclude the aviancarrier package and all its sources and subpackages.
104 "-x tools.net.drums" to exclude the drums package only, keep its subpackages.
105 "-xf tools/net/Bar.java" // Do not compile this file...
106 "-xf *Bor.java" // Do not compile Bor.java wherever it is found, BUT do compile ABor.java!
107 "-if tools/net/Bor.java" // Only compile this file...odd, but sometimes used.
108
109 ** The smart javac wrapper is driven by the modification time on the source files and compared
110 to the modification times written into the javac_state file.
111
112 It does not compare the modification time of the source with the modification time of the artifact.
113 However it will detect if the modification time of an artifact has changed compared to the java_state,
114 and this will trigger a delete of the artifact and a subsequent recompile of the source.
115
116 The smart javac wrapper is not a generic makefile/ant system. Its purpose is to compile java source
117 as the final step before the output dir is finalized and immediately jared, or jmodded. The output
118 dir should be considered opaque. Do not write into the outputdir yourself!
119 Any artifacts found in the outputdir that javac_state does not know of, will be deleted!
120 This can however be prevented, using the switch --permit-unidentified-artifacts
121 This switch is necessary when build the OpenJDK because its makefiles still write directly to
122 the output classes dirs.
123
124 Any makefile/ant rules that want to put contents into the outputdir should put the content
125 in one of several source roots. Static content that is under version control, can be put in the same source
126 code tree as the Java sources. Dynamic content that is generated by make/ant on the fly, should
127 be put in a separate gensrc_stuff root. The smart javac wrapper call will then take the arguments:
128 "gensrc_stuff src -d bin"
129
130 The command line:
131 java -cp tools.jar com.sun.tools.sjavac.Main \
132 -i "com.bar.*" -x "com.bar.foo.*" \
133 first_root \
134 -i "com.bar.foo.*" \
135 second_root \
136 -x "org.net.*" \
137 -sourcepath link_root_sources \
138 -classpath link_root_classes \
139 -d bin
140
141 Will compile all sources for package com.bar and its subpackages, found below first_root,
142 except the package com.bar.foo (and its subpackages), for which the sources are picked
143 from second_root instead. It will link against classes in link_root_classes and against
144 sources in link_root_sources, but will not see (try to link against) sources matching org.net.*
145 but will link against org.net* classes (if they exist) in link_root_classes.
146
147 (If you want a set of complex filter rules to be applied to several source directories, without
148 having to repeat the the filter rules for each root. You can use the explicit -src option. For example:
149 sjavac -x "com.foo.*" -src root1:root2:root3 )
150
151 The resulting classes are written into bin.
152 */
153
154 // This is the final destination for classes and copied files.
155 private File bin_dir;
156 // This is where the annotation process will put generated sources.
157 private File gensrc_dir;
158 // This is where javac -h puts the generated c-header files.
159 private File header_dir;
160
161 // This file contains the list of sources genereated by the makefile.
162 // We double check that our calculated list of sources matches this list,
163 // if not, then we terminate with an error!
164 private File makefile_source_list;
165 // The challenging task to manage an incremental build is done by javac_state.
166 private JavacState javac_state;
167
168 // The suffix rules tells you for example, that .java files should be compiled,
169 // and .html files should be copied and .properties files be translated.
170 Map<String,Transformer> suffix_rules;
171
172 public static void main(String... args) {
173 if (args.length > 0 && args[0].startsWith("--startserver:")) {
174 if (args.length>1) {
175 Log.error("When spawning a background server, only a single --startserver argument is allowed.");
176 return;
177 }
178 // Spawn a background server.
179 int rc = JavacServer.startServer(args[0], System.err);
180 System.exit(rc);
181 }
182 Main main = new Main();
183 int rc = main.go(args, System.out, System.err);
184 // Remove the portfile, but only if this background=false was used.
185 JavacServer.cleanup(args);
186 System.exit(rc);
187 }
188
189 private void printHelp() {
190 System.out.println("Usage: sjavac <options>\n"+
191 "where required options are:\n"+
192 "dir Compile all sources in dir recursively\n"+
193 "-d dir Store generated classes here and the javac_state file\n"+
194 "--server:portfile=/tmp/abc Use a background sjavac server\n\n"+
195 "All other arguments as javac, except -implicit:none which is forced by default.\n"+
196 "No java source files can be supplied on the command line, nor can an @file be supplied.\n\n"+
197 "Warning!\n"+
198 "This tool might disappear at any time, and its command line options might change at any time!");
199 }
200
201 public int go(String[] args, PrintStream out, PrintStream err) {
202 try {
203 if (args.length == 0 || findJavaSourceFiles(args) || findAtFile(args) || null==Util.findServerSettings(args)) {
204 printHelp();
205 return 0;
206 }
207
208 Log.setLogLevel(findLogLevel(args), out, err);
209 String server_settings = Util.findServerSettings(args);
210 args = verifyImplicitOption(args);
211 // Find the source root directories, and add the -src option before these, if not there already.
212 args = addSrcBeforeDirectories(args);
213 // Check that there is at least one -src supplied.
214 checkSrcOption(args);
215 // Check that there is one -d supplied.
216 bin_dir = findDirectoryOption(args,"-d","output", true, false, true);
217 gensrc_dir = findDirectoryOption(args,"-s","gensrc", false, false, true);
218 header_dir = findDirectoryOption(args,"-h","headers", false, false, true);
219 makefile_source_list = findFileOption(args,"--compare-found-sources","makefile source list", false);
220
221 // Load the prev build state database.
222 javac_state = JavacState.load(args, bin_dir, gensrc_dir, header_dir,
223 findBooleanOption(args, "--permit-unidentified-artifacts"), out, err);
224
225 // Setup the suffix rules from the command line.
226 suffix_rules = javac_state.getJavaSuffixRule();
227 findTranslateOptions(args, suffix_rules);
228 if (suffix_rules.keySet().size() > 1 && gensrc_dir == null) {
229 Log.error("You have translators but no gensrc dir (-s) specified!");
230 return -1;
231 }
232 findCopyOptions(args, suffix_rules);
233
234 // All found modules are put here.
235 Map<String,Module> modules = new HashMap<String,Module>();
236 // We start out in the legacy empty no-name module.
237 // As soon as we stumble on a module-info.java file we change to that module.
238 Module current_module = new Module("", "");
239 modules.put("", current_module);
240
241 // Find all sources, use the suffix rules to know which files are sources.
242 Map<String,Source> sources = new HashMap<String,Source>();
243 // Find the files, this will automatically populate the found modules
244 // with found packages where the sources are found!
245 findFiles(args, "-src", suffix_rules.keySet(), sources, modules, current_module, false);
246
247 if (sources.isEmpty()) {
248 Log.error("Found nothing to compile!");
249 return -1;
250 }
251
252 // Create a map of all source files that are available for linking. Both -src and
253 // -sourcepath point to such files. It is possible to specify multiple
254 // -sourcepath options to enable different filtering rules. If the
255 // filters are the same for multiple sourcepaths, they may be concatenated
256 // using :(;). Before sending the list of sourcepaths to javac, they are
257 // all concatenated. The list created here is used by the SmartFileWrapper to
258 // make sure only the correct sources are actually available.
259 // We might find more modules here as well.
260 Map<String,Source> sources_to_link_to = new HashMap<String,Source>();
261 findFiles(args, "-src", Util.set(".java"), sources_to_link_to, modules, current_module, true);
262 findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, true);
263 // Rewrite the -src option to make it through to the javac instances.
264 rewriteOptions(args, "-src", "-sourcepath");
265
266 // Find all class files allowable for linking.
267 // And pickup knowledge of all modules found here.
268 // This cannot currently filter classes inside jar files.
269 // Map<String,Source> classes_to_link_to = new HashMap<String,Source>();
270 // findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);
271
272 // Find all module sources allowable for linking.
273 // Map<String,Source> modules_to_link_to = new HashMap<String,Source>();
274 // findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);
275
276 // Add the set of sources to the build database.
277 javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
278 javac_state.now().checkInternalState("checking sources", false, sources);
279 javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
280 javac_state.setVisibleSources(sources_to_link_to);
281
282 // If there is any change in the source files, taint packages
283 // and mark the database in need of saving.
284 javac_state.checkSourceStatus(false);
285
286 // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
287 // in javac_state, simply because loading of the JavacState will clean out all artifacts
288 // that do not match the javac_state database.
289 javac_state.findAllArtifacts();
290
291 // Remove unidentified artifacts from the bin, gensrc and header dirs.
292 // (Unless we allow them to be there.)
293 // I.e. artifacts that are not known according to the build database (javac_state).
294 // For examples, files that have been manually copied into these dirs.
295 // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
296 // in javac_state) have already been removed when the javac_state was loaded.
297 if (!findBooleanOption(args, "--permit-unidentified-artifacts")) {
298 javac_state.removeUnidentifiedArtifacts();
299 }
300 // Go through all sources and taint all packages that miss artifacts.
301 javac_state.taintPackagesThatMissArtifacts();
302
303 // Now clean out all known artifacts belonging to tainted packages.
304 javac_state.deleteClassArtifactsInTaintedPackages();
305 // Copy files, for example property files, images files, xml files etc etc.
306 javac_state.performCopying(bin_dir, suffix_rules);
307 // Translate files, for example compile properties or compile idls.
308 javac_state.performTranslation(gensrc_dir, suffix_rules);
309 // Add any potentially generated java sources to the tobe compiled list.
310 // (Generated sources must always have a package.)
311 Map<String,Source> generated_sources = new HashMap<String,Source>();
312 Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null,
313 generated_sources, modules, current_module, false, true, false);
314 javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
315 // Recheck the the source files and their timestamps again.
316 javac_state.checkSourceStatus(true);
317
318 // Now do a safety check that the list of source files is identical
319 // to the list Make believes we are compiling. If we do not get this
320 // right, then incremental builds will fail with subtility.
321 // If any difference is detected, then we will fail hard here.
322 // This is an important safety net.
323 javac_state.compareWithMakefileList(makefile_source_list);
324
325 // Do the compilations, repeatedly until no tainted packages exist.
326 boolean again;
327 // Collect the name of all compiled packages.
328 Set<String> recently_compiled = new HashSet<String>();
329 boolean[] rc = new boolean[1];
330 do {
331 // Clean out artifacts in tainted packages.
332 javac_state.deleteClassArtifactsInTaintedPackages();
333 again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc);
334 if (!rc[0]) break;
335 } while (again);
336 // Only update the state if the compile went well.
337 if (rc[0]) {
338 javac_state.save();
339 // Reflatten only the artifacts.
340 javac_state.now().flattenArtifacts(modules);
341 // Remove artifacts that were generated during the last compile, but not this one.
342 javac_state.removeSuperfluousArtifacts(recently_compiled);
343 }
344 return rc[0] ? 0 : -1;
345 } catch (ProblemException e) {
346 Log.error(e.getMessage());
347 return -1;
348 } catch (Exception e) {
349 e.printStackTrace(err);
350 return -1;
351 }
352 }
353
354 /**
355 * Are java source files passed on the command line?
356 */
357 private boolean findJavaSourceFiles(String[] args) {
358 String prev = "";
359 for (String s : args) {
360 if (s.endsWith(".java") && !prev.equals("-xf") && !prev.equals("-if")) {
361 return true;
362 }
363 prev = s;
364 }
365 return false;
366 }
367
368 /**
369 * Is an at file passed on the command line?
370 */
371 private boolean findAtFile(String[] args) {
372 for (String s : args) {
373 if (s.startsWith("@")) {
374 return true;
375 }
376 }
377 return false;
378 }
379
380 /**
381 * Find the log level setting.
382 */
383 private String findLogLevel(String[] args) {
384 for (String s : args) {
385 if (s.startsWith("--log=") && s.length()>6) {
386 return s.substring(6);
387 }
388 if (s.equals("-verbose")) {
389 return "info";
390 }
391 }
392 return "info";
393 }
394
395 /**
396 * Remove smart javac wrapper arguments, before feeding
397 * the args to the plain javac.
398 */
399 static String[] removeWrapperArgs(String[] args) {
400 String[] out = new String[args.length];
401 // The first source path index is remembered
402 // here. So that all following can be concatenated to it.
403 int source_path = -1;
404 // The same for class path.
405 int class_path = -1;
406 // And module path.
407 int module_path = -1;
408 int j = 0;
409 for (int i = 0; i<args.length; ++i) {
410 if (args[i].equals("-src") ||
411 args[i].equals("-x") ||
412 args[i].equals("-i") ||
413 args[i].equals("-xf") ||
414 args[i].equals("-if") ||
415 args[i].equals("-copy") ||
416 args[i].equals("-tr") ||
417 args[i].equals("-j")) {
418 // Just skip it and skip following value
419 i++;
420 } else if (args[i].startsWith("--server:")) {
421 // Just skip it.
422 } else if (args[i].startsWith("--log=")) {
423 // Just skip it.
424 } else if (args[i].equals("--permit-unidentified-artifacts")) {
425 // Just skip it.
426 } else if (args[i].equals("--permit-sources-without-package")) {
427 // Just skip it.
428 } else if (args[i].equals("--compare-found-sources")) {
429 // Just skip it and skip verify file name
430 i++;
431 } else if (args[i].equals("-sourcepath")) {
432 if (source_path == -1) {
433 source_path = j;
434 out[j] = args[i];
435 out[j+1] = args[i+1];
436 j+=2;
437 i++;
438 } else {
439 // Skip this and its argument, but
440 // append argument to found sourcepath.
441 out[source_path+1] = out[source_path+1]+File.pathSeparatorChar+args[i+1];
442 i++;
443 }
444 } else if (args[i].equals("-classpath") || args[i].equals("-cp")) {
445 if (class_path == -1) {
446 class_path = j;
447 out[j] = args[i];
448 out[j+1] = args[i+1];
449 j+=2;
450 i++;
451 } else {
452 // Skip this and its argument, but
453 // append argument to found sourcepath.
454 out[class_path+1] = out[class_path+1]+File.pathSeparatorChar+args[i+1];
455 i++;
456 }
457 } else if (args[i].equals("-modulepath")) {
458 if (module_path == -1) {
459 module_path = j;
460 out[j] = args[i];
461 out[j+1] = args[i+1];
462 j+=2;
463 i++;
464 } else {
465 // Skip this and its argument, but
466 // append argument to found sourcepath.
467 out[module_path+1] = out[module_path+1]+File.pathSeparatorChar+args[i+1];
468 i++;
469 }
470 } else {
471 // Copy argument.
472 out[j] = args[i];
473 j++;
474 }
475 }
476 String[] ret = new String[j];
477 System.arraycopy(out, 0, ret, 0, j);
478 return ret;
479 }
480
481 /**
482 * Make sure directory exist, create it if not.
483 */
484 private static boolean makeSureExists(File dir) {
485 // Make sure the dest directories exist.
486 if (!dir.exists()) {
487 if (!dir.mkdirs()) {
488 Log.error("Could not create the directory "+dir.getPath());
489 return false;
490 }
491 }
492 return true;
493 }
494
495 /**
496 * Verify that a package pattern is valid.
497 */
498 private static void checkPattern(String s) throws ProblemException {
499 // Package names like foo.bar.gamma are allowed, and
500 // package names suffixed with .* like foo.bar.* are
501 // also allowed.
502 Pattern p = Pattern.compile("[a-zA-Z_]{1}[a-zA-Z0-9_]*(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*(\\.\\*)?+");
503 Matcher m = p.matcher(s);
504 if (!m.matches()) {
505 throw new ProblemException("The string \""+s+"\" is not a proper package name pattern.");
506 }
507 }
508
509 /**
510 * Verify that a translate pattern is valid.
511 */
512 private static void checkTranslatePattern(String s) throws ProblemException {
513 // .prop=com.sun.tools.javac.smart.CompileProperties
514 // .idl=com.sun.corba.CompileIdl
515 // .g3=antlr.CompileGrammar,debug=true
516 Pattern p = Pattern.compile(
517 "\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*=[a-z_]{1}[a-z0-9_]*(\\.[a-z_]{1}[a-z0-9_]*)*"+
518 "(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)(,.*)?");
519 Matcher m = p.matcher(s);
520 if (!m.matches()) {
521 throw new ProblemException("The string \""+s+"\" is not a proper translate pattern.");
522 }
523 }
524
525 /**
526 * Verify that a copy pattern is valid.
527 */
528 private static void checkCopyPattern(String s) throws ProblemException {
529 // .gif
530 // .html
531 Pattern p = Pattern.compile(
532 "\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*");
533 Matcher m = p.matcher(s);
534 if (!m.matches()) {
535 throw new ProblemException("The string \""+s+"\" is not a proper suffix.");
536 }
537 }
538
539 /**
540 * Verify that a source file name is valid.
541 */
542 private static void checkFilePattern(String s) throws ProblemException {
543 // File names like foo/bar/gamma/Bar.java are allowed,
544 // as well as /bar/jndi.properties as well as,
545 // */bar/Foo.java
546 Pattern p = null;
547 if (File.separatorChar == '\\') {
548 p = Pattern.compile("\\*?(.+\\\\)*.+");
549 }
550 else if (File.separatorChar == '/') {
551 p = Pattern.compile("\\*?(.+/)*.+");
552 } else {
553 throw new ProblemException("This platform uses the unsupported "+File.separatorChar+
554 " as file separator character. Please add support for it!");
555 }
556 Matcher m = p.matcher(s);
557 if (!m.matches()) {
558 throw new ProblemException("The string \""+s+"\" is not a proper file name.");
559 }
560 }
561
562 /**
563 * Scan the arguments to find an option is used.
564 */
565 private static boolean hasOption(String[] args, String option) {
566 for (String a : args) {
567 if (a.equals(option)) return true;
568 }
569 return false;
570 }
571
572 /**
573 * Check if -implicit is supplied, if so check that it is none.
574 * If -implicit is not supplied, supply -implicit:none
575 * Only implicit:none is allowed because otherwise the multicore compilations
576 * and dependency tracking will be tangled up.
577 */
578 private static String[] verifyImplicitOption(String[] args)
579 throws ProblemException {
580
581 boolean foundImplicit = false;
582 for (String a : args) {
583 if (a.startsWith("-implicit:")) {
584 foundImplicit = true;
585 if (!a.equals("-implicit:none")) {
586 throw new ProblemException("The only allowed setting for sjavac is -implicit:none, it is also the default.");
587 }
588 }
589 }
590 if (foundImplicit) {
591 return args;
592 }
593 // -implicit:none not found lets add it.
594 String[] newargs = new String[args.length+1];
595 System.arraycopy(args,0, newargs, 0, args.length);
596 newargs[args.length] = "-implicit:none";
597 return newargs;
598 }
599
600 /**
601 * Rewrite a single option into something else.
602 */
603 private static void rewriteOptions(String[] args, String option, String new_option) {
604 for (int i=0; i<args.length; ++i) {
605 if (args[i].equals(option)) {
606 args[i] = new_option;
607 }
608 }
609 }
610
611 /**
612 * Scan the arguments to find an option that specifies a directory.
613 * Create the directory if necessary.
614 */
615 private static File findDirectoryOption(String[] args, String option, String name, boolean needed, boolean allow_dups, boolean create)
616 throws ProblemException, ProblemException {
617 File dir = null;
618 for (int i = 0; i<args.length; ++i) {
619 if (args[i].equals(option)) {
620 if (dir != null) {
621 throw new ProblemException("You have already specified the "+name+" dir!");
622 }
623 if (i+1 >= args.length) {
624 throw new ProblemException("You have to specify a directory following "+option+".");
625 }
626 if (args[i+1].indexOf(File.pathSeparatorChar) != -1) {
627 throw new ProblemException("You must only specify a single directory for "+option+".");
628 }
629 dir = new File(args[i+1]);
630 if (!dir.exists()) {
631 if (!create) {
632 throw new ProblemException("This directory does not exist: "+dir.getPath());
633 } else
634 if (!makeSureExists(dir)) {
635 throw new ProblemException("Cannot create directory "+dir.getPath());
636 }
637 }
638 if (!dir.isDirectory()) {
639 throw new ProblemException("\""+args[i+1]+"\" is not a directory.");
640 }
641 }
642 }
643 if (dir == null && needed) {
644 throw new ProblemException("You have to specify "+option);
645 }
646 try {
647 if (dir != null)
648 return dir.getCanonicalFile();
649 } catch (IOException e) {
650 throw new ProblemException(""+e);
651 }
652 return null;
653 }
654
655 /**
656 * Option is followed by path.
657 */
658 private static boolean shouldBeFollowedByPath(String o) {
659 return o.equals("-s") ||
660 o.equals("-h") ||
661 o.equals("-d") ||
662 o.equals("-sourcepath") ||
663 o.equals("-classpath") ||
664 o.equals("-cp") ||
665 o.equals("-bootclasspath") ||
666 o.equals("-src");
667 }
668
669 /**
670 * Add -src before source root directories if not already there.
671 */
672 private static String[] addSrcBeforeDirectories(String[] args) {
673 List<String> newargs = new ArrayList<String>();
674 for (int i = 0; i<args.length; ++i) {
675 File dir = new File(args[i]);
676 if (dir.exists() && dir.isDirectory()) {
677 if (i == 0 || !shouldBeFollowedByPath(args[i-1])) {
678 newargs.add("-src");
679 }
680 }
681 newargs.add(args[i]);
682 }
683 return newargs.toArray(new String[0]);
684 }
685
686 /**
687 * Check the -src options.
688 */
689 private static void checkSrcOption(String[] args)
690 throws ProblemException {
691 Set<File> dirs = new HashSet<File>();
692 for (int i = 0; i<args.length; ++i) {
693 if (args[i].equals("-src")) {
694 if (i+1 >= args.length) {
695 throw new ProblemException("You have to specify a directory following -src.");
696 }
697 StringTokenizer st = new StringTokenizer(args[i+1], File.pathSeparator);
698 while (st.hasMoreElements()) {
699 File dir = new File(st.nextToken());
700 if (!dir.exists()) {
701 throw new ProblemException("This directory does not exist: "+dir.getPath());
702 }
703 if (!dir.isDirectory()) {
704 throw new ProblemException("\""+dir.getPath()+"\" is not a directory.");
705 }
706 if (dirs.contains(dir)) {
707 throw new ProblemException("The src directory \""+dir.getPath()+"\" is specified more than once!");
708 }
709 dirs.add(dir);
710 }
711 }
712 }
713 if (dirs.isEmpty()) {
714 throw new ProblemException("You have to specify -src.");
715 }
716 }
717
718 /**
719 * Scan the arguments to find an option that specifies a file.
720 */
721 private static File findFileOption(String[] args, String option, String name, boolean needed)
722 throws ProblemException, ProblemException {
723 File file = null;
724 for (int i = 0; i<args.length; ++i) {
725 if (args[i].equals(option)) {
726 if (file != null) {
727 throw new ProblemException("You have already specified the "+name+" file!");
728 }
729 if (i+1 >= args.length) {
730 throw new ProblemException("You have to specify a file following "+option+".");
731 }
732 file = new File(args[i+1]);
733 if (file.isDirectory()) {
734 throw new ProblemException("\""+args[i+1]+"\" is not a file.");
735 }
736 if (!file.exists() && needed) {
737 throw new ProblemException("The file \""+args[i+1]+"\" does not exist.");
738 }
739
740 }
741 }
742 if (file == null && needed) {
743 throw new ProblemException("You have to specify "+option);
744 }
745 return file;
746 }
747
748 /**
749 * Look for a specific switch, return true if found.
750 */
751 public static boolean findBooleanOption(String[] args, String option) {
752 for (int i = 0; i<args.length; ++i) {
753 if (args[i].equals(option)) return true;
754 }
755 return false;
756 }
757
758 /**
759 * Scan the arguments to find an option that specifies a number.
760 */
761 public static int findNumberOption(String[] args, String option) {
762 int rc = 0;
763 for (int i = 0; i<args.length; ++i) {
764 if (args[i].equals(option)) {
765 if (args.length > i+1) {
766 rc = Integer.parseInt(args[i+1]);
767 }
768 }
769 }
770 return rc;
771 }
772
773 /**
774 * Scan the arguments to find the option (-tr) that setup translation rules to java source
775 * from different sources. For example: .properties are translated using CompileProperties
776 * The found translators are stored as suffix rules.
777 */
778 private static void findTranslateOptions(String[] args, Map<String,Transformer> suffix_rules)
779 throws ProblemException, ProblemException {
780
781 for (int i = 0; i<args.length; ++i) {
782 if (args[i].equals("-tr")) {
783 if (i+1 >= args.length) {
784 throw new ProblemException("You have to specify a translate rule following -tr.");
785 }
786 String s = args[i+1];
787 checkTranslatePattern(s);
788 int ep = s.indexOf("=");
789 String suffix = s.substring(0,ep);
790 String classname = s.substring(ep+1);
791 if (suffix_rules.get(suffix) != null) {
792 throw new ProblemException("You have already specified a "+
793 "rule for the suffix "+suffix);
794 }
795 if (s.equals(".class")) {
796 throw new ProblemException("You cannot have a translator for .class files!");
797 }
798 if (s.equals(".java")) {
799 throw new ProblemException("You cannot have a translator for .java files!");
800 }
801 String extra = null;
802 int exp = classname.indexOf(",");
803 if (exp != -1) {
804 extra = classname.substring(exp+1);
805 classname = classname.substring(0,exp);
806 }
807 try {
808 Class<?> cl = Class.forName(classname);
809 Transformer t = (Transformer)cl.newInstance();
810 t.setExtra(extra);
811 suffix_rules.put(suffix, t);
812 }
813 catch (Exception e) {
814 throw new ProblemException("Cannot use "+classname+" as a translator!");
815 }
816 }
817 }
818 }
819
820 /**
821 * Scan the arguments to find the option (-copy) that setup copying rules into the bin dir.
822 * For example: -copy .html
823 * The found copiers are stored as suffix rules as well. No translation is done, just copying.
824 */
825 private void findCopyOptions(String[] args, Map<String,Transformer> suffix_rules)
826 throws ProblemException, ProblemException {
827
828 for (int i = 0; i<args.length; ++i) {
829 if (args[i].equals("-copy")) {
830 if (i+1 >= args.length) {
831 throw new ProblemException("You have to specify a translate rule following -tr.");
832 }
833 String s = args[i+1];
834 checkCopyPattern(s);
835 if (suffix_rules.get(s) != null) {
836 throw new ProblemException("You have already specified a "+
837 "rule for the suffix "+s);
838 }
839 if (s.equals(".class")) {
840 throw new ProblemException("You cannot have a copy rule for .class files!");
841 }
842 if (s.equals(".java")) {
843 throw new ProblemException("You cannot have a copy rule for .java files!");
844 }
845 suffix_rules.put(s, javac_state.getCopier());
846 }
847 }
848 }
849
850 /**
851 * Rewrite a / separated path into \ separated, but only
852 * if we are running on a platform were File.separatorChar=='\', ie winapi.
853 */
854 private String fixupSeparator(String p) {
855 if (File.separatorChar == '/') return p;
856 return p.replaceAll("/", "\\\\");
857 }
858
859 /**
860 * Scan the arguments for -i -x -xf -if followed by the option
861 * -src, -sourcepath, -modulepath or -classpath and produce a map of all the
862 * files to referenced for that particular option.
863 *
864 * Store the found sources and the found modules in the supplied maps.
865 */
866 private boolean findFiles(String[] args, String option, Set<String> suffixes,
867 Map<String,Source> found_files, Map<String, Module> found_modules,
868 Module current_module, boolean inLinksrc)
869 throws ProblemException, ProblemException
870 {
871 // Track which source roots, source path roots and class path roots have been added.
872 Set<File> roots = new HashSet<File>();
873 // Track the current set of package includes,excludes as well as excluded source files,
874 // to be used in the next -src/-sourcepath/-classpath
875 List<String> includes = new LinkedList<String>();
876 List<String> excludes = new LinkedList<String>();
877 List<String> excludefiles = new LinkedList<String>();
878 List<String> includefiles = new LinkedList<String>();
879 // This include is used to find all modules in the source.
880 List<String> moduleinfo = new LinkedList<String>();
881 moduleinfo.add("module-info.java");
882
883 for (int i = 0; i<args.length; ++i) {
884 if (args[i].equals("-i")) {
885 if (i+1 >= args.length) {
886 throw new ProblemException("You have to specify a package pattern following -i");
887 }
888 String incl = args[i+1];
889 checkPattern(incl);
890 includes.add(incl);
891 }
892 if (args[i].equals("-x")) {
893 if (i+1 >= args.length) {
894 throw new ProblemException("You have to specify a package pattern following -x");
895 }
896 String excl = args[i+1];
897 checkPattern(excl);
898 excludes.add(excl);
899 }
900 if (args[i].equals("-xf")) {
901 if (i+1 >= args.length) {
902 throw new ProblemException("You have to specify a file following -xf");
903 }
904 String exclf = args[i+1];
905 checkFilePattern(exclf);
906 exclf = Util.normalizeDriveLetter(exclf);
907 excludefiles.add(fixupSeparator(exclf));
908 }
909 if (args[i].equals("-if")) {
910 if (i+1 >= args.length) {
911 throw new ProblemException("You have to specify a file following -xf");
912 }
913 String inclf = args[i+1];
914 checkFilePattern(inclf);
915 inclf = Util.normalizeDriveLetter(inclf);
916 includefiles.add(fixupSeparator(inclf));
917 }
918 if (args[i].equals(option)) {
919 if (i+1 >= args.length) {
920 throw new ProblemException("You have to specify a directory following "+option);
921 }
922 String[] root_dirs = args[i+1].split(File.pathSeparator);
923 for (String r : root_dirs) {
924 File root = new File(r);
925 if (!root.isDirectory()) {
926 throw new ProblemException("\""+r+"\" is not a directory.");
927 }
928 try {
929 root = root.getCanonicalFile();
930 } catch (IOException e) {
931 throw new ProblemException(""+e);
932 }
933 if (roots.contains(root)) {
934 throw new ProblemException("\""+r+"\" has already been used for "+option);
935 }
936 if (root.equals(bin_dir)) {
937 throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d");
938 }
939 if (root.equals(gensrc_dir)) {
940 throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s");
941 }
942 if (root.equals(header_dir)) {
943 throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -h");
944 }
945 roots.add(root);
946 Source.scanRoot(root, suffixes, excludes, includes, excludefiles, includefiles,
947 found_files, found_modules, current_module,
948 findBooleanOption(args, "--permit-sources-without-package"),
949 false, inLinksrc);
950 }
951 }
952 if (args[i].equals("-src") ||
953 args[i].equals("-sourcepath") ||
954 args[i].equals("-modulepath") ||
955 args[i].equals("-classpath") ||
956 args[i].equals("-cp"))
957 {
958 // Reset the includes,excludes and excludefiles after they have been used.
959 includes = new LinkedList<String>();
960 excludes = new LinkedList<String>();
961 excludefiles = new LinkedList<String>();
962 includefiles = new LinkedList<String>();
963 }
964 }
965 return true;
966 }
967
968 }
969

mercurial