Locations.java revision 3837:1089212e3333
1/* 2 * Copyright (c) 2003, 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.javac.file; 27 28import java.io.Closeable; 29import java.io.File; 30import java.io.FileNotFoundException; 31import java.io.IOException; 32import java.io.UncheckedIOException; 33import java.net.URI; 34import java.net.URL; 35import java.net.URLClassLoader; 36import java.nio.file.DirectoryIteratorException; 37import java.nio.file.DirectoryStream; 38import java.nio.file.FileSystem; 39import java.nio.file.FileSystemNotFoundException; 40import java.nio.file.FileSystems; 41import java.nio.file.Files; 42import java.nio.file.InvalidPathException; 43import java.nio.file.Path; 44import java.nio.file.Paths; 45import java.nio.file.ProviderNotFoundException; 46import java.util.ArrayList; 47import java.util.Arrays; 48import java.util.Collection; 49import java.util.Collections; 50import java.util.EnumMap; 51import java.util.EnumSet; 52import java.util.HashMap; 53import java.util.HashSet; 54import java.util.Iterator; 55import java.util.LinkedHashMap; 56import java.util.LinkedHashSet; 57import java.util.List; 58import java.util.Map; 59import java.util.Objects; 60import java.util.NoSuchElementException; 61import java.util.Set; 62import java.util.regex.Matcher; 63import java.util.regex.Pattern; 64import java.util.stream.Collectors; 65import java.util.stream.Stream; 66 67import javax.lang.model.SourceVersion; 68import javax.tools.JavaFileManager; 69import javax.tools.JavaFileManager.Location; 70import javax.tools.JavaFileObject; 71import javax.tools.StandardJavaFileManager; 72import javax.tools.StandardJavaFileManager.PathFactory; 73import javax.tools.StandardLocation; 74 75import com.sun.tools.javac.code.Lint; 76import com.sun.tools.javac.code.Lint.LintCategory; 77import com.sun.tools.javac.main.Option; 78import com.sun.tools.javac.resources.CompilerProperties.Errors; 79import com.sun.tools.javac.resources.CompilerProperties.Warnings; 80import com.sun.tools.javac.util.DefinedBy; 81import com.sun.tools.javac.util.DefinedBy.Api; 82import com.sun.tools.javac.util.JDK9Wrappers; 83import com.sun.tools.javac.util.ListBuffer; 84import com.sun.tools.javac.util.Log; 85import com.sun.tools.javac.jvm.ModuleNameReader; 86import com.sun.tools.javac.util.Pair; 87import com.sun.tools.javac.util.StringUtils; 88 89import static javax.tools.StandardLocation.PLATFORM_CLASS_PATH; 90 91import static com.sun.tools.javac.main.Option.BOOT_CLASS_PATH; 92import static com.sun.tools.javac.main.Option.DJAVA_ENDORSED_DIRS; 93import static com.sun.tools.javac.main.Option.DJAVA_EXT_DIRS; 94import static com.sun.tools.javac.main.Option.ENDORSEDDIRS; 95import static com.sun.tools.javac.main.Option.EXTDIRS; 96import static com.sun.tools.javac.main.Option.XBOOTCLASSPATH; 97import static com.sun.tools.javac.main.Option.XBOOTCLASSPATH_APPEND; 98import static com.sun.tools.javac.main.Option.XBOOTCLASSPATH_PREPEND; 99 100/** 101 * This class converts command line arguments, environment variables and system properties (in 102 * File.pathSeparator-separated String form) into a boot class path, user class path, and source 103 * path (in {@code Collection<String>} form). 104 * 105 * <p> 106 * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at 107 * your own risk. This code and its internal interfaces are subject to change or deletion without 108 * notice.</b> 109 */ 110public class Locations { 111 112 /** 113 * The log to use for warning output 114 */ 115 private Log log; 116 117 /** 118 * Access to (possibly cached) file info 119 */ 120 private FSInfo fsInfo; 121 122 /** 123 * Whether to warn about non-existent path elements 124 */ 125 private boolean warn; 126 127 private ModuleNameReader moduleNameReader; 128 129 private PathFactory pathFactory = Paths::get; 130 131 static final Path javaHome = FileSystems.getDefault().getPath(System.getProperty("java.home")); 132 static final Path thisSystemModules = javaHome.resolve("lib").resolve("modules"); 133 134 Map<Path, FileSystem> fileSystems = new LinkedHashMap<>(); 135 List<Closeable> closeables = new ArrayList<>(); 136 private Map<String,String> fsEnv = Collections.emptyMap(); 137 138 Locations() { 139 initHandlers(); 140 } 141 142 Path getPath(String first, String... more) { 143 try { 144 return pathFactory.getPath(first, more); 145 } catch (InvalidPathException ipe) { 146 throw new IllegalArgumentException(ipe); 147 } 148 } 149 150 public void close() throws IOException { 151 ListBuffer<IOException> list = new ListBuffer<>(); 152 closeables.forEach(closeable -> { 153 try { 154 closeable.close(); 155 } catch (IOException ex) { 156 list.add(ex); 157 } 158 }); 159 if (list.nonEmpty()) { 160 IOException ex = new IOException(); 161 for (IOException e: list) 162 ex.addSuppressed(e); 163 throw ex; 164 } 165 } 166 167 void update(Log log, boolean warn, FSInfo fsInfo) { 168 this.log = log; 169 this.warn = warn; 170 this.fsInfo = fsInfo; 171 } 172 173 void setPathFactory(PathFactory f) { 174 pathFactory = f; 175 } 176 177 boolean isDefaultBootClassPath() { 178 BootClassPathLocationHandler h 179 = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); 180 return h.isDefault(); 181 } 182 183 /** 184 * Split a search path into its elements. Empty path elements will be ignored. 185 * 186 * @param searchPath The search path to be split 187 * @return The elements of the path 188 */ 189 private Iterable<Path> getPathEntries(String searchPath) { 190 return getPathEntries(searchPath, null); 191 } 192 193 /** 194 * Split a search path into its elements. If emptyPathDefault is not null, all empty elements in the 195 * path, including empty elements at either end of the path, will be replaced with the value of 196 * emptyPathDefault. 197 * 198 * @param searchPath The search path to be split 199 * @param emptyPathDefault The value to substitute for empty path elements, or null, to ignore 200 * empty path elements 201 * @return The elements of the path 202 */ 203 private Iterable<Path> getPathEntries(String searchPath, Path emptyPathDefault) { 204 ListBuffer<Path> entries = new ListBuffer<>(); 205 for (String s: searchPath.split(Pattern.quote(File.pathSeparator), -1)) { 206 if (s.isEmpty()) { 207 if (emptyPathDefault != null) { 208 entries.add(emptyPathDefault); 209 } 210 } else { 211 try { 212 entries.add(getPath(s)); 213 } catch (IllegalArgumentException e) { 214 if (warn) { 215 log.warning(LintCategory.PATH, "invalid.path", s); 216 } 217 } 218 } 219 } 220 return entries; 221 } 222 223 public void setMultiReleaseValue(String multiReleaseValue) { 224 fsEnv = Collections.singletonMap("multi-release", multiReleaseValue); 225 } 226 227 /** 228 * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths 229 * can be expanded. 230 */ 231 private class SearchPath extends LinkedHashSet<Path> { 232 233 private static final long serialVersionUID = 0; 234 235 private boolean expandJarClassPaths = false; 236 private final Set<Path> canonicalValues = new HashSet<>(); 237 238 public SearchPath expandJarClassPaths(boolean x) { 239 expandJarClassPaths = x; 240 return this; 241 } 242 243 /** 244 * What to use when path element is the empty string 245 */ 246 private Path emptyPathDefault = null; 247 248 public SearchPath emptyPathDefault(Path x) { 249 emptyPathDefault = x; 250 return this; 251 } 252 253 public SearchPath addDirectories(String dirs, boolean warn) { 254 boolean prev = expandJarClassPaths; 255 expandJarClassPaths = true; 256 try { 257 if (dirs != null) { 258 for (Path dir : getPathEntries(dirs)) { 259 addDirectory(dir, warn); 260 } 261 } 262 return this; 263 } finally { 264 expandJarClassPaths = prev; 265 } 266 } 267 268 public SearchPath addDirectories(String dirs) { 269 return addDirectories(dirs, warn); 270 } 271 272 private void addDirectory(Path dir, boolean warn) { 273 if (!Files.isDirectory(dir)) { 274 if (warn) { 275 log.warning(Lint.LintCategory.PATH, 276 "dir.path.element.not.found", dir); 277 } 278 return; 279 } 280 281 try (Stream<Path> s = Files.list(dir)) { 282 s.filter(Locations.this::isArchive) 283 .forEach(dirEntry -> addFile(dirEntry, warn)); 284 } catch (IOException ignore) { 285 } 286 } 287 288 public SearchPath addFiles(String files, boolean warn) { 289 if (files != null) { 290 addFiles(getPathEntries(files, emptyPathDefault), warn); 291 } 292 return this; 293 } 294 295 public SearchPath addFiles(String files) { 296 return addFiles(files, warn); 297 } 298 299 public SearchPath addFiles(Iterable<? extends Path> files, boolean warn) { 300 if (files != null) { 301 for (Path file : files) { 302 addFile(file, warn); 303 } 304 } 305 return this; 306 } 307 308 public SearchPath addFiles(Iterable<? extends Path> files) { 309 return addFiles(files, warn); 310 } 311 312 public void addFile(Path file, boolean warn) { 313 if (contains(file)) { 314 // discard duplicates 315 return; 316 } 317 318 if (!fsInfo.exists(file)) { 319 /* No such file or directory exists */ 320 if (warn) { 321 log.warning(Lint.LintCategory.PATH, 322 "path.element.not.found", file); 323 } 324 super.add(file); 325 return; 326 } 327 328 Path canonFile = fsInfo.getCanonicalFile(file); 329 if (canonicalValues.contains(canonFile)) { 330 /* Discard duplicates and avoid infinite recursion */ 331 return; 332 } 333 334 if (fsInfo.isFile(file)) { 335 /* File is an ordinary file. */ 336 if ( !file.getFileName().toString().endsWith(".jmod") 337 && !file.endsWith("modules")) { 338 if (!isArchive(file)) { 339 /* Not a recognized extension; open it to see if 340 it looks like a valid zip file. */ 341 try { 342 FileSystems.newFileSystem(file, null).close(); 343 if (warn) { 344 log.warning(Lint.LintCategory.PATH, 345 "unexpected.archive.file", file); 346 } 347 } catch (IOException | ProviderNotFoundException e) { 348 // FIXME: include e.getLocalizedMessage in warning 349 if (warn) { 350 log.warning(Lint.LintCategory.PATH, 351 "invalid.archive.file", file); 352 } 353 return; 354 } 355 } else { 356 if (fsInfo.getJarFSProvider() == null) { 357 log.error(Errors.NoZipfsForArchive(file)); 358 return ; 359 } 360 } 361 } 362 } 363 364 /* Now what we have left is either a directory or a file name 365 conforming to archive naming convention */ 366 super.add(file); 367 canonicalValues.add(canonFile); 368 369 if (expandJarClassPaths && fsInfo.isFile(file) && !file.endsWith("modules")) { 370 addJarClassPath(file, warn); 371 } 372 } 373 374 // Adds referenced classpath elements from a jar's Class-Path 375 // Manifest entry. In some future release, we may want to 376 // update this code to recognize URLs rather than simple 377 // filenames, but if we do, we should redo all path-related code. 378 private void addJarClassPath(Path jarFile, boolean warn) { 379 try { 380 for (Path f : fsInfo.getJarClassPath(jarFile)) { 381 addFile(f, warn); 382 } 383 } catch (IOException e) { 384 log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e)); 385 } 386 } 387 } 388 389 /** 390 * Base class for handling support for the representation of Locations. 391 * 392 * Locations are (by design) opaque handles that can easily be implemented 393 * by enums like StandardLocation. Within JavacFileManager, each Location 394 * has an associated LocationHandler, which provides much of the appropriate 395 * functionality for the corresponding Location. 396 * 397 * @see #initHandlers 398 * @see #getHandler 399 */ 400 protected abstract class LocationHandler { 401 402 /** 403 * @see JavaFileManager#handleOption 404 */ 405 abstract boolean handleOption(Option option, String value); 406 407 /** 408 * @see StandardJavaFileManager#hasLocation 409 */ 410 boolean isSet() { 411 return (getPaths() != null); 412 } 413 414 /** 415 * @see StandardJavaFileManager#getLocation 416 */ 417 abstract Collection<Path> getPaths(); 418 419 /** 420 * @see StandardJavaFileManager#setLocation 421 */ 422 abstract void setPaths(Iterable<? extends Path> files) throws IOException; 423 424 /** 425 * @see JavaFileManager#getLocationForModule(Location, String) 426 */ 427 Location getLocationForModule(String moduleName) throws IOException { 428 return null; 429 } 430 431 /** 432 * @see JavaFileManager#getLocationForModule(Location, JavaFileObject, String) 433 */ 434 Location getLocationForModule(Path dir) { 435 return null; 436 } 437 438 /** 439 * @see JavaFileManager#inferModuleName 440 */ 441 String inferModuleName() { 442 return null; 443 } 444 445 /** 446 * @see JavaFileManager#listLocationsForModules 447 */ 448 Iterable<Set<Location>> listLocationsForModules() throws IOException { 449 return null; 450 } 451 } 452 453 /** 454 * A LocationHandler for a given Location, and associated set of options. 455 */ 456 private abstract class BasicLocationHandler extends LocationHandler { 457 458 final Location location; 459 final Set<Option> options; 460 461 /** 462 * Create a handler. The location and options provide a way to map from a location or an 463 * option to the corresponding handler. 464 * 465 * @param location the location for which this is the handler 466 * @param options the options affecting this location 467 * @see #initHandlers 468 */ 469 protected BasicLocationHandler(Location location, Option... options) { 470 this.location = location; 471 this.options = options.length == 0 472 ? EnumSet.noneOf(Option.class) 473 : EnumSet.copyOf(Arrays.asList(options)); 474 } 475 } 476 477 /** 478 * General purpose implementation for output locations, such as -d/CLASS_OUTPUT and 479 * -s/SOURCE_OUTPUT. All options are treated as equivalent (i.e. aliases.) 480 * The value is a single file, possibly null. 481 */ 482 private class OutputLocationHandler extends BasicLocationHandler { 483 484 private Path outputDir; 485 private Map<String, Location> moduleLocations; 486 private Map<Path, Location> pathLocations; 487 488 OutputLocationHandler(Location location, Option... options) { 489 super(location, options); 490 } 491 492 @Override 493 boolean handleOption(Option option, String value) { 494 if (!options.contains(option)) { 495 return false; 496 } 497 498 // TODO: could/should validate outputDir exists and is a directory 499 // need to decide how best to report issue for benefit of 500 // direct API call on JavaFileManager.handleOption(specifies IAE) 501 // vs. command line decoding. 502 outputDir = (value == null) ? null : getPath(value); 503 return true; 504 } 505 506 @Override 507 Collection<Path> getPaths() { 508 return (outputDir == null) ? null : Collections.singleton(outputDir); 509 } 510 511 @Override 512 void setPaths(Iterable<? extends Path> files) throws IOException { 513 if (files == null) { 514 outputDir = null; 515 } else { 516 Iterator<? extends Path> pathIter = files.iterator(); 517 if (!pathIter.hasNext()) { 518 throw new IllegalArgumentException("empty path for directory"); 519 } 520 Path dir = pathIter.next(); 521 if (pathIter.hasNext()) { 522 throw new IllegalArgumentException("path too long for directory"); 523 } 524 if (!Files.exists(dir)) { 525 throw new FileNotFoundException(dir + ": does not exist"); 526 } else if (!Files.isDirectory(dir)) { 527 throw new IOException(dir + ": not a directory"); 528 } 529 outputDir = dir; 530 } 531 moduleLocations = null; 532 pathLocations = null; 533 } 534 535 @Override 536 Location getLocationForModule(String name) { 537 if (moduleLocations == null) { 538 moduleLocations = new HashMap<>(); 539 pathLocations = new HashMap<>(); 540 } 541 Location l = moduleLocations.get(name); 542 if (l == null) { 543 Path out = outputDir.resolve(name); 544 l = new ModuleLocationHandler(location.getName() + "[" + name + "]", 545 name, 546 Collections.singleton(out), 547 true, false); 548 moduleLocations.put(name, l); 549 pathLocations.put(out.toAbsolutePath(), l); 550 } 551 return l; 552 } 553 554 @Override 555 Location getLocationForModule(Path dir) { 556 return pathLocations.get(dir); 557 } 558 559 private boolean listed; 560 561 @Override 562 Iterable<Set<Location>> listLocationsForModules() throws IOException { 563 if (!listed && outputDir != null) { 564 try (DirectoryStream<Path> stream = Files.newDirectoryStream(outputDir)) { 565 for (Path p : stream) { 566 getLocationForModule(p.getFileName().toString()); 567 } 568 } 569 listed = true; 570 } 571 if (moduleLocations == null) 572 return Collections.emptySet(); 573 Set<Location> locns = new LinkedHashSet<>(); 574 moduleLocations.forEach((k, v) -> locns.add(v)); 575 return Collections.singleton(locns); 576 } 577 } 578 579 /** 580 * General purpose implementation for search path locations, 581 * such as -sourcepath/SOURCE_PATH and -processorPath/ANNOTATION_PROCESSOR_PATH. 582 * All options are treated as equivalent (i.e. aliases.) 583 * The value is an ordered set of files and/or directories. 584 */ 585 private class SimpleLocationHandler extends BasicLocationHandler { 586 587 protected Collection<Path> searchPath; 588 589 SimpleLocationHandler(Location location, Option... options) { 590 super(location, options); 591 } 592 593 @Override 594 boolean handleOption(Option option, String value) { 595 if (!options.contains(option)) { 596 return false; 597 } 598 searchPath = value == null ? null 599 : Collections.unmodifiableCollection(createPath().addFiles(value)); 600 return true; 601 } 602 603 @Override 604 Collection<Path> getPaths() { 605 return searchPath; 606 } 607 608 @Override 609 void setPaths(Iterable<? extends Path> files) { 610 SearchPath p; 611 if (files == null) { 612 p = computePath(null); 613 } else { 614 p = createPath().addFiles(files); 615 } 616 searchPath = Collections.unmodifiableCollection(p); 617 } 618 619 protected SearchPath computePath(String value) { 620 return createPath().addFiles(value); 621 } 622 623 protected SearchPath createPath() { 624 return new SearchPath(); 625 } 626 } 627 628 /** 629 * Subtype of SimpleLocationHandler for -classpath/CLASS_PATH. 630 * If no value is given, a default is provided, based on system properties and other values. 631 */ 632 private class ClassPathLocationHandler extends SimpleLocationHandler { 633 634 ClassPathLocationHandler() { 635 super(StandardLocation.CLASS_PATH, Option.CLASS_PATH); 636 } 637 638 @Override 639 Collection<Path> getPaths() { 640 lazy(); 641 return searchPath; 642 } 643 644 @Override 645 protected SearchPath computePath(String value) { 646 String cp = value; 647 648 // CLASSPATH environment variable when run from `javac'. 649 if (cp == null) { 650 cp = System.getProperty("env.class.path"); 651 } 652 653 // If invoked via a java VM (not the javac launcher), use the 654 // platform class path 655 if (cp == null && System.getProperty("application.home") == null) { 656 cp = System.getProperty("java.class.path"); 657 } 658 659 // Default to current working directory. 660 if (cp == null) { 661 cp = "."; 662 } 663 664 return createPath().addFiles(cp); 665 } 666 667 @Override 668 protected SearchPath createPath() { 669 return new SearchPath() 670 .expandJarClassPaths(true) // Only search user jars for Class-Paths 671 .emptyPathDefault(getPath(".")); // Empty path elt ==> current directory 672 } 673 674 private void lazy() { 675 if (searchPath == null) { 676 setPaths(null); 677 } 678 } 679 } 680 681 /** 682 * Custom subtype of LocationHandler for PLATFORM_CLASS_PATH. 683 * Various options are supported for different components of the 684 * platform class path. 685 * Setting a value with setLocation overrides all existing option values. 686 * Setting any option overrides any value set with setLocation, and 687 * reverts to using default values for options that have not been set. 688 * Setting -bootclasspath or -Xbootclasspath overrides any existing 689 * value for -Xbootclasspath/p: and -Xbootclasspath/a:. 690 */ 691 private class BootClassPathLocationHandler extends BasicLocationHandler { 692 693 private Collection<Path> searchPath; 694 final Map<Option, String> optionValues = new EnumMap<>(Option.class); 695 696 /** 697 * Is the bootclasspath the default? 698 */ 699 private boolean isDefault; 700 701 BootClassPathLocationHandler() { 702 super(StandardLocation.PLATFORM_CLASS_PATH, 703 Option.BOOT_CLASS_PATH, Option.XBOOTCLASSPATH, 704 Option.XBOOTCLASSPATH_PREPEND, 705 Option.XBOOTCLASSPATH_APPEND, 706 Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS, 707 Option.EXTDIRS, Option.DJAVA_EXT_DIRS); 708 } 709 710 boolean isDefault() { 711 lazy(); 712 return isDefault; 713 } 714 715 @Override 716 boolean handleOption(Option option, String value) { 717 if (!options.contains(option)) { 718 return false; 719 } 720 721 option = canonicalize(option); 722 optionValues.put(option, value); 723 if (option == BOOT_CLASS_PATH) { 724 optionValues.remove(XBOOTCLASSPATH_PREPEND); 725 optionValues.remove(XBOOTCLASSPATH_APPEND); 726 } 727 searchPath = null; // reset to "uninitialized" 728 return true; 729 } 730 // where 731 // TODO: would be better if option aliasing was handled at a higher 732 // level 733 private Option canonicalize(Option option) { 734 switch (option) { 735 case XBOOTCLASSPATH: 736 return Option.BOOT_CLASS_PATH; 737 case DJAVA_ENDORSED_DIRS: 738 return Option.ENDORSEDDIRS; 739 case DJAVA_EXT_DIRS: 740 return Option.EXTDIRS; 741 default: 742 return option; 743 } 744 } 745 746 @Override 747 Collection<Path> getPaths() { 748 lazy(); 749 return searchPath; 750 } 751 752 @Override 753 void setPaths(Iterable<? extends Path> files) { 754 if (files == null) { 755 searchPath = null; // reset to "uninitialized" 756 } else { 757 isDefault = false; 758 SearchPath p = new SearchPath().addFiles(files, false); 759 searchPath = Collections.unmodifiableCollection(p); 760 optionValues.clear(); 761 } 762 } 763 764 SearchPath computePath() throws IOException { 765 SearchPath path = new SearchPath(); 766 767 String bootclasspathOpt = optionValues.get(BOOT_CLASS_PATH); 768 String endorseddirsOpt = optionValues.get(ENDORSEDDIRS); 769 String extdirsOpt = optionValues.get(EXTDIRS); 770 String xbootclasspathPrependOpt = optionValues.get(XBOOTCLASSPATH_PREPEND); 771 String xbootclasspathAppendOpt = optionValues.get(XBOOTCLASSPATH_APPEND); 772 path.addFiles(xbootclasspathPrependOpt); 773 774 if (endorseddirsOpt != null) { 775 path.addDirectories(endorseddirsOpt); 776 } else { 777 path.addDirectories(System.getProperty("java.endorsed.dirs"), false); 778 } 779 780 if (bootclasspathOpt != null) { 781 path.addFiles(bootclasspathOpt); 782 } else { 783 // Standard system classes for this compiler's release. 784 Collection<Path> systemClasses = systemClasses(); 785 if (systemClasses != null) { 786 path.addFiles(systemClasses, false); 787 } else { 788 // fallback to the value of sun.boot.class.path 789 String files = System.getProperty("sun.boot.class.path"); 790 path.addFiles(files, false); 791 } 792 } 793 794 path.addFiles(xbootclasspathAppendOpt); 795 796 // Strictly speaking, standard extensions are not bootstrap 797 // classes, but we treat them identically, so we'll pretend 798 // that they are. 799 if (extdirsOpt != null) { 800 path.addDirectories(extdirsOpt); 801 } else { 802 // Add lib/jfxrt.jar to the search path 803 Path jfxrt = javaHome.resolve("lib/jfxrt.jar"); 804 if (Files.exists(jfxrt)) { 805 path.addFile(jfxrt, false); 806 } 807 path.addDirectories(System.getProperty("java.ext.dirs"), false); 808 } 809 810 isDefault = 811 (xbootclasspathPrependOpt == null) 812 && (bootclasspathOpt == null) 813 && (xbootclasspathAppendOpt == null); 814 815 return path; 816 } 817 818 /** 819 * Return a collection of files containing system classes. 820 * Returns {@code null} if not running on a modular image. 821 * 822 * @throws UncheckedIOException if an I/O errors occurs 823 */ 824 private Collection<Path> systemClasses() throws IOException { 825 // Return "modules" jimage file if available 826 if (Files.isRegularFile(thisSystemModules)) { 827 return Collections.singleton(thisSystemModules); 828 } 829 830 // Exploded module image 831 Path modules = javaHome.resolve("modules"); 832 if (Files.isDirectory(modules.resolve("java.base"))) { 833 try (Stream<Path> listedModules = Files.list(modules)) { 834 return listedModules.collect(Collectors.toList()); 835 } 836 } 837 838 // not a modular image that we know about 839 return null; 840 } 841 842 private void lazy() { 843 if (searchPath == null) { 844 try { 845 searchPath = Collections.unmodifiableCollection(computePath()); 846 } catch (IOException e) { 847 // TODO: need better handling here, e.g. javac Abort? 848 throw new UncheckedIOException(e); 849 } 850 } 851 } 852 } 853 854 /** 855 * A LocationHander to represent modules found from a module-oriented 856 * location such as MODULE_SOURCE_PATH, UPGRADE_MODULE_PATH, 857 * SYSTEM_MODULES and MODULE_PATH. 858 * 859 * The Location can be specified to accept overriding classes from the 860 * {@code --patch-module <module>=<path> } parameter. 861 */ 862 private class ModuleLocationHandler extends LocationHandler implements Location { 863 protected final String name; 864 protected final String moduleName; 865 protected final Collection<Path> searchPath; 866 protected final Collection<Path> searchPathWithOverrides; 867 protected final boolean output; 868 869 ModuleLocationHandler(String name, String moduleName, Collection<Path> searchPath, 870 boolean output, boolean allowOverrides) { 871 this.name = name; 872 this.moduleName = moduleName; 873 this.searchPath = searchPath; 874 this.output = output; 875 876 if (allowOverrides && patchMap != null) { 877 SearchPath mPatch = patchMap.get(moduleName); 878 if (mPatch != null) { 879 SearchPath sp = new SearchPath(); 880 sp.addAll(mPatch); 881 sp.addAll(searchPath); 882 searchPathWithOverrides = sp; 883 } else { 884 searchPathWithOverrides = searchPath; 885 } 886 } else { 887 searchPathWithOverrides = searchPath; 888 } 889 } 890 891 @Override @DefinedBy(Api.COMPILER) 892 public String getName() { 893 return name; 894 } 895 896 @Override @DefinedBy(Api.COMPILER) 897 public boolean isOutputLocation() { 898 return output; 899 } 900 901 @Override // defined by LocationHandler 902 boolean handleOption(Option option, String value) { 903 throw new UnsupportedOperationException(); 904 } 905 906 @Override // defined by LocationHandler 907 Collection<Path> getPaths() { 908 // For now, we always return searchPathWithOverrides. This may differ from the 909 // JVM behavior if there is a module-info.class to be found in the overriding 910 // classes. 911 return searchPathWithOverrides; 912 } 913 914 @Override // defined by LocationHandler 915 void setPaths(Iterable<? extends Path> files) throws IOException { 916 throw new UnsupportedOperationException(); 917 } 918 919 @Override // defined by LocationHandler 920 String inferModuleName() { 921 return moduleName; 922 } 923 } 924 925 /** 926 * A LocationHandler for simple module-oriented search paths, 927 * like UPGRADE_MODULE_PATH and MODULE_PATH. 928 */ 929 private class ModulePathLocationHandler extends SimpleLocationHandler { 930 private Map<String, ModuleLocationHandler> pathModules; 931 932 ModulePathLocationHandler(Location location, Option... options) { 933 super(location, options); 934 } 935 936 @Override 937 public boolean handleOption(Option option, String value) { 938 if (!options.contains(option)) { 939 return false; 940 } 941 setPaths(value == null ? null : getPathEntries(value)); 942 return true; 943 } 944 945 @Override 946 public Location getLocationForModule(String moduleName) { 947 initPathModules(); 948 return pathModules.get(moduleName); 949 } 950 951 @Override 952 Iterable<Set<Location>> listLocationsForModules() { 953 if (searchPath == null) 954 return Collections.emptyList(); 955 956 return ModulePathIterator::new; 957 } 958 959 @Override 960 void setPaths(Iterable<? extends Path> paths) { 961 if (paths != null) { 962 for (Path p: paths) { 963 checkValidModulePathEntry(p); 964 } 965 } 966 super.setPaths(paths); 967 } 968 969 private void initPathModules() { 970 if (pathModules != null) { 971 return; 972 } 973 974 pathModules = new LinkedHashMap<>(); 975 976 for (Set<Location> set : listLocationsForModules()) { 977 for (Location locn : set) { 978 if (locn instanceof ModuleLocationHandler) { 979 ModuleLocationHandler h = (ModuleLocationHandler) locn; 980 pathModules.put(h.moduleName, h); 981 } 982 } 983 } 984 } 985 986 private void checkValidModulePathEntry(Path p) { 987 if (Files.isDirectory(p)) { 988 // either an exploded module or a directory of modules 989 return; 990 } 991 992 String name = p.getFileName().toString(); 993 int lastDot = name.lastIndexOf("."); 994 if (lastDot > 0) { 995 switch (name.substring(lastDot)) { 996 case ".jar": 997 case ".jmod": 998 return; 999 } 1000 } 1001 throw new IllegalArgumentException(p.toString()); 1002 } 1003 1004 class ModulePathIterator implements Iterator<Set<Location>> { 1005 Iterator<Path> pathIter = searchPath.iterator(); 1006 int pathIndex = 0; 1007 Set<Location> next = null; 1008 1009 @Override 1010 public boolean hasNext() { 1011 if (next != null) 1012 return true; 1013 1014 while (next == null) { 1015 if (pathIter.hasNext()) { 1016 Path path = pathIter.next(); 1017 if (Files.isDirectory(path)) { 1018 next = scanDirectory(path); 1019 } else { 1020 next = scanFile(path); 1021 } 1022 pathIndex++; 1023 } else 1024 return false; 1025 } 1026 return true; 1027 } 1028 1029 @Override 1030 public Set<Location> next() { 1031 hasNext(); 1032 if (next != null) { 1033 Set<Location> result = next; 1034 next = null; 1035 return result; 1036 } 1037 throw new NoSuchElementException(); 1038 } 1039 1040 private Set<Location> scanDirectory(Path path) { 1041 Set<Path> paths = new LinkedHashSet<>(); 1042 Path moduleInfoClass = null; 1043 try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) { 1044 for (Path entry: stream) { 1045 if (entry.endsWith("module-info.class")) { 1046 moduleInfoClass = entry; 1047 break; // no need to continue scanning 1048 } 1049 paths.add(entry); 1050 } 1051 } catch (DirectoryIteratorException | IOException ignore) { 1052 log.error(Errors.LocnCantReadDirectory(path)); 1053 return Collections.emptySet(); 1054 } 1055 1056 if (moduleInfoClass != null) { 1057 // It's an exploded module directly on the module path. 1058 // We can't infer module name from the directory name, so have to 1059 // read module-info.class. 1060 try { 1061 String moduleName = readModuleName(moduleInfoClass); 1062 String name = location.getName() 1063 + "[" + pathIndex + ":" + moduleName + "]"; 1064 ModuleLocationHandler l = new ModuleLocationHandler(name, moduleName, 1065 Collections.singleton(path), false, true); 1066 return Collections.singleton(l); 1067 } catch (ModuleNameReader.BadClassFile e) { 1068 log.error(Errors.LocnBadModuleInfo(path)); 1069 return Collections.emptySet(); 1070 } catch (IOException e) { 1071 log.error(Errors.LocnCantReadFile(path)); 1072 return Collections.emptySet(); 1073 } 1074 } 1075 1076 // A directory of modules 1077 Set<Location> result = new LinkedHashSet<>(); 1078 int index = 0; 1079 for (Path entry : paths) { 1080 Pair<String,Path> module = inferModuleName(entry); 1081 if (module == null) { 1082 // diagnostic reported if necessary; skip to next 1083 continue; 1084 } 1085 String moduleName = module.fst; 1086 Path modulePath = module.snd; 1087 String name = location.getName() 1088 + "[" + pathIndex + "." + (index++) + ":" + moduleName + "]"; 1089 ModuleLocationHandler l = new ModuleLocationHandler(name, moduleName, 1090 Collections.singleton(modulePath), false, true); 1091 result.add(l); 1092 } 1093 return result; 1094 } 1095 1096 private Set<Location> scanFile(Path path) { 1097 Pair<String,Path> module = inferModuleName(path); 1098 if (module == null) { 1099 // diagnostic reported if necessary 1100 return Collections.emptySet(); 1101 } 1102 String moduleName = module.fst; 1103 Path modulePath = module.snd; 1104 String name = location.getName() 1105 + "[" + pathIndex + ":" + moduleName + "]"; 1106 ModuleLocationHandler l = new ModuleLocationHandler(name, moduleName, 1107 Collections.singleton(modulePath), false, true); 1108 return Collections.singleton(l); 1109 } 1110 1111 private Pair<String,Path> inferModuleName(Path p) { 1112 if (Files.isDirectory(p)) { 1113 if (Files.exists(p.resolve("module-info.class"))) { 1114 String name = p.getFileName().toString(); 1115 if (SourceVersion.isName(name)) 1116 return new Pair<>(name, p); 1117 } 1118 return null; 1119 } 1120 1121 if (p.getFileName().toString().endsWith(".jar") && fsInfo.exists(p)) { 1122 URI uri = URI.create("jar:" + p.toUri()); 1123 try (FileSystem fs = FileSystems.newFileSystem(uri, fsEnv, null)) { 1124 Path moduleInfoClass = fs.getPath("module-info.class"); 1125 if (Files.exists(moduleInfoClass)) { 1126 String moduleName = readModuleName(moduleInfoClass); 1127 return new Pair<>(moduleName, p); 1128 } 1129 } catch (ModuleNameReader.BadClassFile e) { 1130 log.error(Errors.LocnBadModuleInfo(p)); 1131 return null; 1132 } catch (IOException e) { 1133 log.error(Errors.LocnCantReadFile(p)); 1134 return null; 1135 } catch (ProviderNotFoundException e) { 1136 log.error(Errors.NoZipfsForArchive(p)); 1137 return null; 1138 } 1139 1140 //automatic module: 1141 String fn = p.getFileName().toString(); 1142 //from ModulePath.deriveModuleDescriptor: 1143 1144 // drop .jar 1145 String mn = fn.substring(0, fn.length()-4); 1146 1147 // find first occurrence of -${NUMBER}. or -${NUMBER}$ 1148 Matcher matcher = Pattern.compile("-(\\d+(\\.|$))").matcher(mn); 1149 if (matcher.find()) { 1150 int start = matcher.start(); 1151 1152 mn = mn.substring(0, start); 1153 } 1154 1155 // finally clean up the module name 1156 mn = mn.replaceAll("(\\.|\\d)*$", "") // remove trailing version 1157 .replaceAll("[^A-Za-z0-9]", ".") // replace non-alphanumeric 1158 .replaceAll("(\\.)(\\1)+", ".") // collapse repeating dots 1159 .replaceAll("^\\.", "") // drop leading dots 1160 .replaceAll("\\.$", ""); // drop trailing dots 1161 1162 1163 if (!mn.isEmpty()) { 1164 return new Pair<>(mn, p); 1165 } 1166 1167 log.error(Errors.LocnCantGetModuleNameForJar(p)); 1168 return null; 1169 } 1170 1171 if (p.getFileName().toString().endsWith(".jmod")) { 1172 try { 1173 // check if the JMOD file is valid 1174 JDK9Wrappers.JmodFile.checkMagic(p); 1175 1176 // No JMOD file system. Use JarFileSystem to 1177 // workaround for now 1178 FileSystem fs = fileSystems.get(p); 1179 if (fs == null) { 1180 URI uri = URI.create("jar:" + p.toUri()); 1181 fs = FileSystems.newFileSystem(uri, Collections.emptyMap(), null); 1182 try { 1183 Path moduleInfoClass = fs.getPath("classes/module-info.class"); 1184 String moduleName = readModuleName(moduleInfoClass); 1185 Path modulePath = fs.getPath("classes"); 1186 fileSystems.put(p, fs); 1187 closeables.add(fs); 1188 fs = null; // prevent fs being closed in the finally clause 1189 return new Pair<>(moduleName, modulePath); 1190 } finally { 1191 if (fs != null) 1192 fs.close(); 1193 } 1194 } 1195 } catch (ModuleNameReader.BadClassFile e) { 1196 log.error(Errors.LocnBadModuleInfo(p)); 1197 } catch (IOException | ProviderNotFoundException e) { 1198 log.error(Errors.LocnCantReadFile(p)); 1199 return null; 1200 } 1201 } 1202 1203 if (warn && false) { // temp disable, when enabled, massage examples.not-yet.txt suitably. 1204 log.warning(Warnings.LocnUnknownFileOnModulePath(p)); 1205 } 1206 return null; 1207 } 1208 1209 private String readModuleName(Path path) throws IOException, ModuleNameReader.BadClassFile { 1210 if (moduleNameReader == null) 1211 moduleNameReader = new ModuleNameReader(); 1212 return moduleNameReader.readModuleName(path); 1213 } 1214 } 1215 1216 } 1217 1218 private class ModuleSourcePathLocationHandler extends BasicLocationHandler { 1219 1220 private Map<String, Location> moduleLocations; 1221 private Map<Path, Location> pathLocations; 1222 1223 ModuleSourcePathLocationHandler() { 1224 super(StandardLocation.MODULE_SOURCE_PATH, 1225 Option.MODULE_SOURCE_PATH); 1226 } 1227 1228 @Override 1229 boolean handleOption(Option option, String value) { 1230 init(value); 1231 return true; 1232 } 1233 1234 void init(String value) { 1235 Collection<String> segments = new ArrayList<>(); 1236 for (String s: value.split(File.pathSeparator)) { 1237 expandBraces(s, segments); 1238 } 1239 1240 Map<String, Collection<Path>> map = new LinkedHashMap<>(); 1241 final String MARKER = "*"; 1242 for (String seg: segments) { 1243 int markStart = seg.indexOf(MARKER); 1244 if (markStart == -1) { 1245 add(map, getPath(seg), null); 1246 } else { 1247 if (markStart == 0 || !isSeparator(seg.charAt(markStart - 1))) { 1248 throw new IllegalArgumentException("illegal use of " + MARKER + " in " + seg); 1249 } 1250 Path prefix = getPath(seg.substring(0, markStart - 1)); 1251 Path suffix; 1252 int markEnd = markStart + MARKER.length(); 1253 if (markEnd == seg.length()) { 1254 suffix = null; 1255 } else if (!isSeparator(seg.charAt(markEnd)) 1256 || seg.indexOf(MARKER, markEnd) != -1) { 1257 throw new IllegalArgumentException("illegal use of " + MARKER + " in " + seg); 1258 } else { 1259 suffix = getPath(seg.substring(markEnd + 1)); 1260 } 1261 add(map, prefix, suffix); 1262 } 1263 } 1264 1265 moduleLocations = new LinkedHashMap<>(); 1266 pathLocations = new LinkedHashMap<>(); 1267 map.forEach((k, v) -> { 1268 String name = location.getName() + "[" + k + "]"; 1269 ModuleLocationHandler h = new ModuleLocationHandler(name, k, v, false, false); 1270 moduleLocations.put(k, h); 1271 v.forEach(p -> pathLocations.put(normalize(p), h)); 1272 }); 1273 } 1274 1275 private boolean isSeparator(char ch) { 1276 // allow both separators on Windows 1277 return (ch == File.separatorChar) || (ch == '/'); 1278 } 1279 1280 void add(Map<String, Collection<Path>> map, Path prefix, Path suffix) { 1281 if (!Files.isDirectory(prefix)) { 1282 if (warn) { 1283 String key = Files.exists(prefix) 1284 ? "dir.path.element.not.directory" 1285 : "dir.path.element.not.found"; 1286 log.warning(Lint.LintCategory.PATH, key, prefix); 1287 } 1288 return; 1289 } 1290 try (DirectoryStream<Path> stream = Files.newDirectoryStream(prefix, path -> Files.isDirectory(path))) { 1291 for (Path entry: stream) { 1292 Path path = (suffix == null) ? entry : entry.resolve(suffix); 1293 if (Files.isDirectory(path)) { 1294 String name = entry.getFileName().toString(); 1295 Collection<Path> paths = map.get(name); 1296 if (paths == null) 1297 map.put(name, paths = new ArrayList<>()); 1298 paths.add(path); 1299 } 1300 } 1301 } catch (IOException e) { 1302 // TODO? What to do? 1303 System.err.println(e); 1304 } 1305 } 1306 1307 private void expandBraces(String value, Collection<String> results) { 1308 int depth = 0; 1309 int start = -1; 1310 String prefix = null; 1311 String suffix = null; 1312 for (int i = 0; i < value.length(); i++) { 1313 switch (value.charAt(i)) { 1314 case '{': 1315 depth++; 1316 if (depth == 1) { 1317 prefix = value.substring(0, i); 1318 suffix = value.substring(getMatchingBrace(value, i) + 1); 1319 start = i + 1; 1320 } 1321 break; 1322 1323 case ',': 1324 if (depth == 1) { 1325 String elem = value.substring(start, i); 1326 expandBraces(prefix + elem + suffix, results); 1327 start = i + 1; 1328 } 1329 break; 1330 1331 case '}': 1332 switch (depth) { 1333 case 0: 1334 throw new IllegalArgumentException("mismatched braces"); 1335 1336 case 1: 1337 String elem = value.substring(start, i); 1338 expandBraces(prefix + elem + suffix, results); 1339 return; 1340 1341 default: 1342 depth--; 1343 } 1344 break; 1345 } 1346 } 1347 if (depth > 0) 1348 throw new IllegalArgumentException("mismatched braces"); 1349 results.add(value); 1350 } 1351 1352 int getMatchingBrace(String value, int offset) { 1353 int depth = 1; 1354 for (int i = offset + 1; i < value.length(); i++) { 1355 switch (value.charAt(i)) { 1356 case '{': 1357 depth++; 1358 break; 1359 1360 case '}': 1361 if (--depth == 0) 1362 return i; 1363 break; 1364 } 1365 } 1366 throw new IllegalArgumentException("mismatched braces"); 1367 } 1368 1369 @Override 1370 boolean isSet() { 1371 return (moduleLocations != null); 1372 } 1373 1374 @Override 1375 Collection<Path> getPaths() { 1376 throw new UnsupportedOperationException(); 1377 } 1378 1379 @Override 1380 void setPaths(Iterable<? extends Path> files) throws IOException { 1381 throw new UnsupportedOperationException(); 1382 } 1383 1384 @Override 1385 Location getLocationForModule(String name) { 1386 return (moduleLocations == null) ? null : moduleLocations.get(name); 1387 } 1388 1389 @Override 1390 Location getLocationForModule(Path dir) { 1391 return (pathLocations == null) ? null : pathLocations.get(dir); 1392 } 1393 1394 @Override 1395 Iterable<Set<Location>> listLocationsForModules() { 1396 if (moduleLocations == null) 1397 return Collections.emptySet(); 1398 Set<Location> locns = new LinkedHashSet<>(); 1399 moduleLocations.forEach((k, v) -> locns.add(v)); 1400 return Collections.singleton(locns); 1401 } 1402 1403 } 1404 1405 private class SystemModulesLocationHandler extends BasicLocationHandler { 1406 private Path systemJavaHome; 1407 private Path modules; 1408 private Map<String, ModuleLocationHandler> systemModules; 1409 1410 SystemModulesLocationHandler() { 1411 super(StandardLocation.SYSTEM_MODULES, Option.SYSTEM); 1412 systemJavaHome = Locations.javaHome; 1413 } 1414 1415 @Override 1416 boolean handleOption(Option option, String value) { 1417 if (!options.contains(option)) { 1418 return false; 1419 } 1420 1421 if (value == null) { 1422 systemJavaHome = Locations.javaHome; 1423 } else if (value.equals("none")) { 1424 systemJavaHome = null; 1425 } else { 1426 update(getPath(value)); 1427 } 1428 1429 modules = null; 1430 return true; 1431 } 1432 1433 @Override 1434 Collection<Path> getPaths() { 1435 return (systemJavaHome == null) ? null : Collections.singleton(systemJavaHome); 1436 } 1437 1438 @Override 1439 void setPaths(Iterable<? extends Path> files) throws IOException { 1440 if (files == null) { 1441 systemJavaHome = null; 1442 } else { 1443 Iterator<? extends Path> pathIter = files.iterator(); 1444 if (!pathIter.hasNext()) { 1445 throw new IllegalArgumentException("empty path for directory"); // TODO: FIXME 1446 } 1447 Path dir = pathIter.next(); 1448 if (pathIter.hasNext()) { 1449 throw new IllegalArgumentException("path too long for directory"); // TODO: FIXME 1450 } 1451 if (!Files.exists(dir)) { 1452 throw new FileNotFoundException(dir + ": does not exist"); 1453 } else if (!Files.isDirectory(dir)) { 1454 throw new IOException(dir + ": not a directory"); 1455 } 1456 update(dir); 1457 } 1458 } 1459 1460 private void update(Path p) { 1461 if (!isCurrentPlatform(p) && !Files.exists(p.resolve("lib").resolve("jrt-fs.jar")) && 1462 !Files.exists(systemJavaHome.resolve("modules"))) 1463 throw new IllegalArgumentException(p.toString()); 1464 systemJavaHome = p; 1465 modules = null; 1466 } 1467 1468 private boolean isCurrentPlatform(Path p) { 1469 try { 1470 return Files.isSameFile(p, Locations.javaHome); 1471 } catch (IOException ex) { 1472 throw new IllegalArgumentException(p.toString(), ex); 1473 } 1474 } 1475 1476 @Override 1477 Location getLocationForModule(String name) throws IOException { 1478 initSystemModules(); 1479 return systemModules.get(name); 1480 } 1481 1482 @Override 1483 Iterable<Set<Location>> listLocationsForModules() throws IOException { 1484 initSystemModules(); 1485 Set<Location> locns = new LinkedHashSet<>(); 1486 for (Location l: systemModules.values()) 1487 locns.add(l); 1488 return Collections.singleton(locns); 1489 } 1490 1491 private void initSystemModules() throws IOException { 1492 if (systemModules != null) { 1493 return; 1494 } 1495 1496 if (systemJavaHome == null) { 1497 systemModules = Collections.emptyMap(); 1498 return; 1499 } 1500 1501 if (modules == null) { 1502 try { 1503 URI jrtURI = URI.create("jrt:/"); 1504 FileSystem jrtfs; 1505 1506 if (isCurrentPlatform(systemJavaHome)) { 1507 jrtfs = FileSystems.getFileSystem(jrtURI); 1508 } else { 1509 try { 1510 Map<String, String> attrMap = 1511 Collections.singletonMap("java.home", systemJavaHome.toString()); 1512 jrtfs = FileSystems.newFileSystem(jrtURI, attrMap); 1513 } catch (ProviderNotFoundException ex) { 1514 URL javaHomeURL = systemJavaHome.resolve("jrt-fs.jar").toUri().toURL(); 1515 ClassLoader currentLoader = Locations.class.getClassLoader(); 1516 URLClassLoader fsLoader = 1517 new URLClassLoader(new URL[] {javaHomeURL}, currentLoader); 1518 1519 jrtfs = FileSystems.newFileSystem(jrtURI, Collections.emptyMap(), fsLoader); 1520 1521 closeables.add(fsLoader); 1522 } 1523 1524 closeables.add(jrtfs); 1525 } 1526 1527 modules = jrtfs.getPath("/modules"); 1528 } catch (FileSystemNotFoundException | ProviderNotFoundException e) { 1529 modules = systemJavaHome.resolve("modules"); 1530 if (!Files.exists(modules)) 1531 throw new IOException("can't find system classes", e); 1532 } 1533 } 1534 1535 systemModules = new LinkedHashMap<>(); 1536 try (DirectoryStream<Path> stream = Files.newDirectoryStream(modules, Files::isDirectory)) { 1537 for (Path entry : stream) { 1538 String moduleName = entry.getFileName().toString(); 1539 String name = location.getName() + "[" + moduleName + "]"; 1540 ModuleLocationHandler h = new ModuleLocationHandler(name, moduleName, 1541 Collections.singleton(entry), false, true); 1542 systemModules.put(moduleName, h); 1543 } 1544 } 1545 } 1546 } 1547 1548 Map<Location, LocationHandler> handlersForLocation; 1549 Map<Option, LocationHandler> handlersForOption; 1550 1551 void initHandlers() { 1552 handlersForLocation = new HashMap<>(); 1553 handlersForOption = new EnumMap<>(Option.class); 1554 1555 BasicLocationHandler[] handlers = { 1556 new BootClassPathLocationHandler(), 1557 new ClassPathLocationHandler(), 1558 new SimpleLocationHandler(StandardLocation.SOURCE_PATH, Option.SOURCE_PATH), 1559 new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, Option.PROCESSOR_PATH), 1560 new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, Option.PROCESSOR_MODULE_PATH), 1561 new OutputLocationHandler(StandardLocation.CLASS_OUTPUT, Option.D), 1562 new OutputLocationHandler(StandardLocation.SOURCE_OUTPUT, Option.S), 1563 new OutputLocationHandler(StandardLocation.NATIVE_HEADER_OUTPUT, Option.H), 1564 new ModuleSourcePathLocationHandler(), 1565 // TODO: should UPGRADE_MODULE_PATH be merged with SYSTEM_MODULES? 1566 new ModulePathLocationHandler(StandardLocation.UPGRADE_MODULE_PATH, Option.UPGRADE_MODULE_PATH), 1567 new ModulePathLocationHandler(StandardLocation.MODULE_PATH, Option.MODULE_PATH), 1568 new SystemModulesLocationHandler(), 1569 }; 1570 1571 for (BasicLocationHandler h : handlers) { 1572 handlersForLocation.put(h.location, h); 1573 for (Option o : h.options) { 1574 handlersForOption.put(o, h); 1575 } 1576 } 1577 } 1578 1579 private Map<String, SearchPath> patchMap; 1580 1581 boolean handleOption(Option option, String value) { 1582 switch (option) { 1583 case PATCH_MODULE: 1584 if (value == null) { 1585 patchMap = null; 1586 } else { 1587 // Allow an extended syntax for --patch-module consisting of a series 1588 // of values separated by NULL characters. This is to facilitate 1589 // supporting deferred file manager options on the command line. 1590 // See Option.PATCH_MODULE for the code that composes these multiple 1591 // values. 1592 for (String v : value.split("\0")) { 1593 int eq = v.indexOf('='); 1594 if (eq > 0) { 1595 String mName = v.substring(0, eq); 1596 SearchPath mPatchPath = new SearchPath() 1597 .addFiles(v.substring(eq + 1)); 1598 boolean ok = true; 1599 for (Path p : mPatchPath) { 1600 Path mi = p.resolve("module-info.class"); 1601 if (Files.exists(mi)) { 1602 log.error(Errors.LocnModuleInfoNotAllowedOnPatchPath(mi)); 1603 ok = false; 1604 } 1605 } 1606 if (ok) { 1607 if (patchMap == null) { 1608 patchMap = new LinkedHashMap<>(); 1609 } 1610 patchMap.put(mName, mPatchPath); 1611 } 1612 } else { 1613 // Should not be able to get here; 1614 // this should be caught and handled in Option.PATCH_MODULE 1615 log.error(Errors.LocnInvalidArgForXpatch(value)); 1616 } 1617 } 1618 } 1619 return true; 1620 default: 1621 LocationHandler h = handlersForOption.get(option); 1622 return (h == null ? false : h.handleOption(option, value)); 1623 } 1624 } 1625 1626 boolean hasLocation(Location location) { 1627 LocationHandler h = getHandler(location); 1628 return (h == null ? false : h.isSet()); 1629 } 1630 1631 Collection<Path> getLocation(Location location) { 1632 LocationHandler h = getHandler(location); 1633 return (h == null ? null : h.getPaths()); 1634 } 1635 1636 Path getOutputLocation(Location location) { 1637 if (!location.isOutputLocation()) { 1638 throw new IllegalArgumentException(); 1639 } 1640 LocationHandler h = getHandler(location); 1641 return ((OutputLocationHandler) h).outputDir; 1642 } 1643 1644 void setLocation(Location location, Iterable<? extends Path> files) throws IOException { 1645 LocationHandler h = getHandler(location); 1646 if (h == null) { 1647 if (location.isOutputLocation()) { 1648 h = new OutputLocationHandler(location); 1649 } else { 1650 h = new SimpleLocationHandler(location); 1651 } 1652 handlersForLocation.put(location, h); 1653 } 1654 h.setPaths(files); 1655 } 1656 1657 Location getLocationForModule(Location location, String name) throws IOException { 1658 LocationHandler h = getHandler(location); 1659 return (h == null ? null : h.getLocationForModule(name)); 1660 } 1661 1662 Location getLocationForModule(Location location, Path dir) { 1663 LocationHandler h = getHandler(location); 1664 return (h == null ? null : h.getLocationForModule(dir)); 1665 } 1666 1667 String inferModuleName(Location location) { 1668 LocationHandler h = getHandler(location); 1669 return (h == null ? null : h.inferModuleName()); 1670 } 1671 1672 Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException { 1673 LocationHandler h = getHandler(location); 1674 return (h == null ? null : h.listLocationsForModules()); 1675 } 1676 1677 protected LocationHandler getHandler(Location location) { 1678 Objects.requireNonNull(location); 1679 return (location instanceof LocationHandler) 1680 ? (LocationHandler) location 1681 : handlersForLocation.get(location); 1682 } 1683 1684 /** 1685 * Is this the name of an archive file? 1686 */ 1687 private boolean isArchive(Path file) { 1688 String n = StringUtils.toLowerCase(file.getFileName().toString()); 1689 return fsInfo.isFile(file) 1690 && (n.endsWith(".jar") || n.endsWith(".zip")); 1691 } 1692 1693 static Path normalize(Path p) { 1694 try { 1695 return p.toRealPath(); 1696 } catch (IOException e) { 1697 return p.toAbsolutePath().normalize(); 1698 } 1699 } 1700 1701} 1702