Locations.java revision 3980:f34b5b81ef55
14Srgrimes/* 24Srgrimes * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. 34Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 44Srgrimes * 54Srgrimes * This code is free software; you can redistribute it and/or modify it 64Srgrimes * under the terms of the GNU General Public License version 2 only, as 74Srgrimes * published by the Free Software Foundation. Oracle designates this 84Srgrimes * particular file as subject to the "Classpath" exception as provided 94Srgrimes * by Oracle in the LICENSE file that accompanied this code. 104Srgrimes * 114Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT 124Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 134Srgrimes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 144Srgrimes * version 2 for more details (a copy is included in the LICENSE file that 154Srgrimes * accompanied this code). 164Srgrimes * 174Srgrimes * You should have received a copy of the GNU General Public License version 184Srgrimes * 2 along with this work; if not, write to the Free Software Foundation, 194Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 204Srgrimes * 214Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 224Srgrimes * or visit www.oracle.com if you need additional information or have any 234Srgrimes * questions. 244Srgrimes */ 254Srgrimes 264Srgrimespackage com.sun.tools.javac.file; 274Srgrimes 284Srgrimesimport java.io.Closeable; 294Srgrimesimport java.io.File; 304Srgrimesimport java.io.FileNotFoundException; 314Srgrimesimport java.io.IOException; 324Srgrimesimport java.io.UncheckedIOException; 334Srgrimesimport java.net.URI; 344Srgrimesimport java.net.URL; 354Srgrimesimport java.net.URLClassLoader; 36619Srgrimesimport java.nio.file.DirectoryIteratorException; 3750477Speterimport java.nio.file.DirectoryStream; 384Srgrimesimport java.nio.file.FileSystem; 394Srgrimesimport java.nio.file.FileSystemNotFoundException; 403185Ssosimport java.nio.file.FileSystems; 4119173Sbdeimport java.nio.file.Files; 4219173Sbdeimport java.nio.file.InvalidPathException; 4319173Sbdeimport java.nio.file.Path; 4419173Sbdeimport java.nio.file.Paths; 453185Ssosimport java.nio.file.ProviderNotFoundException; 463185Ssosimport java.nio.file.spi.FileSystemProvider; 473185Ssosimport java.util.ArrayList; 483185Ssosimport java.util.Arrays; 492913Sacheimport java.util.Collection; 502913Sacheimport java.util.Collections; 5116299Spstimport java.util.EnumMap; 5233929Sphkimport java.util.EnumSet; 5313228Swollmanimport java.util.HashMap; 542056Swollmanimport java.util.HashSet; 552056Swollmanimport java.util.Iterator; 562056Swollmanimport java.util.LinkedHashMap; 572056Swollmanimport java.util.LinkedHashSet; 5831253Sbdeimport java.util.List; 5931253Sbdeimport java.util.Map; 6031253Sbdeimport java.util.Objects; 6115508Sbdeimport java.util.NoSuchElementException; 6249558Sphkimport java.util.Set; 6315508Sbdeimport java.util.regex.Matcher; 644180Sbdeimport java.util.regex.Pattern; 6515508Sbdeimport java.util.stream.Collectors; 6615508Sbdeimport java.util.stream.Stream; 6730805Sbde 682056Swollmanimport javax.lang.model.SourceVersion; 6926309Speterimport javax.tools.JavaFileManager; 7028551Sbdeimport javax.tools.JavaFileManager.Location; 7132054Sphkimport javax.tools.JavaFileObject; 7247588Sbdeimport javax.tools.StandardJavaFileManager; 7330805Sbdeimport javax.tools.StandardJavaFileManager.PathFactory; 7430805Sbdeimport javax.tools.StandardLocation; 7530805Sbde 7628921Sfsmpimport com.sun.tools.javac.code.Lint; 7726949Sfsmpimport com.sun.tools.javac.code.Lint.LintCategory; 7828921Sfsmpimport com.sun.tools.javac.main.Option; 7932054Sphkimport com.sun.tools.javac.resources.CompilerProperties.Errors; 8015508Sbdeimport com.sun.tools.javac.resources.CompilerProperties.Warnings; 812056Swollmanimport com.sun.tools.javac.util.DefinedBy; 822056Swollmanimport com.sun.tools.javac.util.DefinedBy.Api; 8347642Sdfrimport com.sun.tools.javac.util.JDK9Wrappers; 842056Swollmanimport com.sun.tools.javac.util.ListBuffer; 854Srgrimesimport com.sun.tools.javac.util.Log; 8645897Speterimport com.sun.tools.javac.jvm.ModuleNameReader; 8728487Sfsmpimport com.sun.tools.javac.util.Pair; 8850823Smdoddimport com.sun.tools.javac.util.StringUtils; 8950823Smdodd 9050823Smdoddimport static javax.tools.StandardLocation.PLATFORM_CLASS_PATH; 9150823Smdodd 9250823Smdoddimport static com.sun.tools.javac.main.Option.BOOT_CLASS_PATH; 9328921Sfsmpimport static com.sun.tools.javac.main.Option.DJAVA_ENDORSED_DIRS; 9429000Sfsmpimport static com.sun.tools.javac.main.Option.DJAVA_EXT_DIRS; 9529000Sfsmpimport static com.sun.tools.javac.main.Option.ENDORSEDDIRS; 9634058Steggeimport static com.sun.tools.javac.main.Option.EXTDIRS; 9734571Steggeimport static com.sun.tools.javac.main.Option.XBOOTCLASSPATH; 9834571Steggeimport static com.sun.tools.javac.main.Option.XBOOTCLASSPATH_APPEND; 9934058Steggeimport static com.sun.tools.javac.main.Option.XBOOTCLASSPATH_PREPEND; 10034058Stegge 10134571Stegge/** 10234571Stegge * This class converts command line arguments, environment variables and system properties (in 10334571Stegge * File.pathSeparator-separated String form) into a boot class path, user class path, and source 10428921Sfsmp * path (in {@code Collection<String>} form). 10528921Sfsmp * 1062873Sbde * <p> 1072873Sbde * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at 1082873Sbde * your own risk. This code and its internal interfaces are subject to change or deletion without 1092873Sbde * notice.</b> 1102873Sbde */ 1112913Sachepublic class Locations { 1122873Sbde 11315508Sbde /** 1144Srgrimes * The log to use for warning output 1154180Sbde */ 1164180Sbde private Log log; 1174180Sbde 1184180Sbde /** 1194180Sbde * Access to (possibly cached) file info 1204180Sbde */ 1214180Sbde private FSInfo fsInfo; 1224180Sbde 1234180Sbde /** 1244180Sbde * Whether to warn about non-existent path elements 12517236Sjoerg */ 12617231Sjoerg private boolean warn; 12733690Sphk 1284180Sbde private ModuleNameReader moduleNameReader; 12917231Sjoerg 1304180Sbde private PathFactory pathFactory = Paths::get; 13141787Smckay 13247588Sbde static final Path javaHome = FileSystems.getDefault().getPath(System.getProperty("java.home")); 13315045Sache static final Path thisSystemModules = javaHome.resolve("lib").resolve("modules"); 13446847Speter 13532052Sphk Map<Path, FileSystem> fileSystems = new LinkedHashMap<>(); 13632052Sphk List<Closeable> closeables = new ArrayList<>(); 13733690Sphk private Map<String,String> fsEnv = Collections.emptyMap(); 13833690Sphk 13933690Sphk Locations() { 14032052Sphk initHandlers(); 14132052Sphk } 14232005Sphk 14347592Sphk Path getPath(String first, String... more) { 14441787Smckay try { 1451390Ssos return pathFactory.getPath(first, more); 1464180Sbde } catch (InvalidPathException ipe) { 1475291Sbde throw new IllegalArgumentException(ipe); 1484180Sbde } 14919173Sbde } 15033690Sphk 15133690Sphk public void close() throws IOException { 15233690Sphk ListBuffer<IOException> list = new ListBuffer<>(); 1534180Sbde closeables.forEach(closeable -> { 1544180Sbde try { 1554180Sbde closeable.close(); 1564180Sbde } catch (IOException ex) { 1574180Sbde list.add(ex); 1584180Sbde } 15919173Sbde }); 16019173Sbde if (list.nonEmpty()) { 1614180Sbde IOException ex = new IOException(); 16215345Snate for (IOException e: list) 16333690Sphk ex.addSuppressed(e); 16417231Sjoerg throw ex; 16517231Sjoerg } 16617236Sjoerg } 16717236Sjoerg 16817236Sjoerg void update(Log log, boolean warn, FSInfo fsInfo) { 16917236Sjoerg this.log = log; 17017231Sjoerg this.warn = warn; 17117231Sjoerg this.fsInfo = fsInfo; 17217231Sjoerg } 17319173Sbde 17433690Sphk void setPathFactory(PathFactory f) { 1754180Sbde pathFactory = f; 17636719Sphk } 17736719Sphk 17821783Sbde boolean isDefaultBootClassPath() { 17917353Sbde BootClassPathLocationHandler h 18040610Sphk = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); 18133690Sphk return h.isDefault(); 18236741Sphk } 18336198Sphk 18433690Sphk /** 18533690Sphk * Split a search path into its elements. Empty path elements will be ignored. 18633690Sphk * 18733690Sphk * @param searchPath The search path to be split 18833690Sphk * @return The elements of the path 18940610Sphk */ 19033690Sphk private Iterable<Path> getPathEntries(String searchPath) { 19140610Sphk return getPathEntries(searchPath, null); 19233690Sphk } 19336741Sphk 19436198Sphk /** 19533690Sphk * Split a search path into its elements. If emptyPathDefault is not null, all empty elements in the 19633690Sphk * path, including empty elements at either end of the path, will be replaced with the value of 19733690Sphk * emptyPathDefault. 19833690Sphk * 19933690Sphk * @param searchPath The search path to be split 20040610Sphk * @param emptyPathDefault The value to substitute for empty path elements, or null, to ignore 20133690Sphk * empty path elements 20212724Sphk * @return The elements of the path 2033185Ssos */ 2042074Swollman private Iterable<Path> getPathEntries(String searchPath, Path emptyPathDefault) { 20539503Sbde ListBuffer<Path> entries = new ListBuffer<>(); 20647588Sbde for (String s: searchPath.split(Pattern.quote(File.pathSeparator), -1)) { 20747588Sbde if (s.isEmpty()) { 20839503Sbde if (emptyPathDefault != null) { 20947588Sbde entries.add(emptyPathDefault); 21039503Sbde } 21139503Sbde } else { 21239503Sbde try { 21347588Sbde entries.add(getPath(s)); 21447588Sbde } catch (IllegalArgumentException e) { 21539503Sbde if (warn) { 2161549Srgrimes log.warning(LintCategory.PATH, "invalid.path", s); 2171442Ssos } 21817236Sjoerg } 21917231Sjoerg } 2208448Sbde } 2211442Ssos return entries; 22217236Sjoerg } 22317231Sjoerg 2244180Sbde public void setMultiReleaseValue(String multiReleaseValue) { 2254180Sbde fsEnv = Collections.singletonMap("multi-release", multiReleaseValue); 22633309Sbde } 2271549Srgrimes 2288448Sbde /** 2291390Ssos * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths 2301442Ssos * can be expanded. 23117236Sjoerg */ 23217231Sjoerg private class SearchPath extends LinkedHashSet<Path> { 23348889Sbde 23448889Sbde private static final long serialVersionUID = 0; 23548889Sbde 2364180Sbde private boolean expandJarClassPaths = false; 2374180Sbde private final Set<Path> canonicalValues = new HashSet<>(); 2384180Sbde 2394180Sbde public SearchPath expandJarClassPaths(boolean x) { 24029000Sfsmp expandJarClassPaths = x; 2411442Ssos return this; 24217231Sjoerg } 24348889Sbde 2441442Ssos /** 24517236Sjoerg * What to use when path element is the empty string 24617231Sjoerg */ 2474180Sbde private Path emptyPathDefault = null; 2484180Sbde 24948889Sbde public SearchPath emptyPathDefault(Path x) { 25048889Sbde emptyPathDefault = x; 25148889Sbde return this; 2525291Sbde } 2534180Sbde 2544180Sbde public SearchPath addDirectories(String dirs, boolean warn) { 2554180Sbde boolean prev = expandJarClassPaths; 2564180Sbde expandJarClassPaths = true; 25729000Sfsmp try { 2584180Sbde if (dirs != null) { 25917231Sjoerg for (Path dir : getPathEntries(dirs)) { 26017231Sjoerg addDirectory(dir, warn); 26148889Sbde } 26248889Sbde } 2631442Ssos return this; 2641442Ssos } finally { 2651442Ssos expandJarClassPaths = prev; 26650823Smdodd } 26750823Smdodd } 26850823Smdodd 26950823Smdodd public SearchPath addDirectories(String dirs) { 27050823Smdodd return addDirectories(dirs, warn); 2711390Ssos } 2721390Ssos 27317231Sjoerg private void addDirectory(Path dir, boolean warn) { 27417236Sjoerg if (!Files.isDirectory(dir)) { 27517231Sjoerg if (warn) { 2761390Ssos log.warning(Lint.LintCategory.PATH, 2774180Sbde "dir.path.element.not.found", dir); 2781390Ssos } 27917231Sjoerg return; 28017231Sjoerg } 28117231Sjoerg 28217231Sjoerg try (Stream<Path> s = Files.list(dir)) { 28317231Sjoerg s.filter(Locations.this::isArchive) 28417231Sjoerg .forEach(dirEntry -> addFile(dirEntry, warn)); 28517236Sjoerg } catch (IOException ignore) { 28617236Sjoerg } 28717236Sjoerg } 28817231Sjoerg 28917236Sjoerg public SearchPath addFiles(String files, boolean warn) { 29017236Sjoerg if (files != null) { 29117236Sjoerg addFiles(getPathEntries(files, emptyPathDefault), warn); 29217236Sjoerg } 29317236Sjoerg return this; 29417236Sjoerg } 29517236Sjoerg 29617236Sjoerg public SearchPath addFiles(String files) { 29717236Sjoerg return addFiles(files, warn); 29817236Sjoerg } 29917236Sjoerg 30017236Sjoerg public SearchPath addFiles(Iterable<? extends Path> files, boolean warn) { 30117236Sjoerg if (files != null) { 30217236Sjoerg for (Path file : files) { 30317231Sjoerg addFile(file, warn); 3041442Ssos } 30517231Sjoerg } 30617231Sjoerg return this; 3071390Ssos } 3081390Ssos 3091390Ssos public SearchPath addFiles(Iterable<? extends Path> files) { 3101390Ssos return addFiles(files, warn); 3111390Ssos } 31217231Sjoerg 31317231Sjoerg public void addFile(Path file, boolean warn) { 31417231Sjoerg if (contains(file)) { 31517231Sjoerg // discard duplicates 31617236Sjoerg return; 31717236Sjoerg } 31817236Sjoerg 31917236Sjoerg if (!fsInfo.exists(file)) { 32017236Sjoerg /* No such file or directory exists */ 32117236Sjoerg if (warn) { 32217236Sjoerg log.warning(Lint.LintCategory.PATH, 32317236Sjoerg "path.element.not.found", file); 32417236Sjoerg } 32517236Sjoerg super.add(file); 32617231Sjoerg return; 3271390Ssos } 3281390Ssos 3291390Ssos Path canonFile = fsInfo.getCanonicalFile(file); 3301390Ssos if (canonicalValues.contains(canonFile)) { 3311390Ssos /* Discard duplicates and avoid infinite recursion */ 33217231Sjoerg return; 33317231Sjoerg } 33417236Sjoerg 33517236Sjoerg if (fsInfo.isFile(file)) { 33617236Sjoerg /* File is an ordinary file. */ 33717231Sjoerg if ( !file.getFileName().toString().endsWith(".jmod") 33817236Sjoerg && !file.endsWith("modules")) { 33917236Sjoerg if (!isArchive(file)) { 34017236Sjoerg /* Not a recognized extension; open it to see if 34117236Sjoerg it looks like a valid zip file. */ 34217236Sjoerg try { 34317236Sjoerg FileSystems.newFileSystem(file, null).close(); 34417236Sjoerg if (warn) { 34517231Sjoerg log.warning(Lint.LintCategory.PATH, 34617231Sjoerg "unexpected.archive.file", file); 3471390Ssos } 3481390Ssos } catch (IOException | ProviderNotFoundException e) { 3491390Ssos // FIXME: include e.getLocalizedMessage in warning 3501390Ssos if (warn) { 3511390Ssos log.warning(Lint.LintCategory.PATH, 35217231Sjoerg "invalid.archive.file", file); 35317231Sjoerg } 35417231Sjoerg return; 35517231Sjoerg } 35617236Sjoerg } else { 35717231Sjoerg if (fsInfo.getJarFSProvider() == null) { 3581390Ssos log.error(Errors.NoZipfsForArchive(file)); 3591390Ssos return ; 3603185Ssos } 3613185Ssos } 3623185Ssos } 3633185Ssos } 3643185Ssos 3653185Ssos /* Now what we have left is either a directory or a file name 3663185Ssos conforming to archive naming convention */ 3673185Ssos super.add(file); 3683185Ssos canonicalValues.add(canonFile); 3693185Ssos 3703185Ssos if (expandJarClassPaths && fsInfo.isFile(file) && !file.endsWith("modules")) { 3713185Ssos addJarClassPath(file, warn); 37224676Smckay } 37324676Smckay } 37424676Smckay 37524676Smckay // Adds referenced classpath elements from a jar's Class-Path 37624676Smckay // Manifest entry. In some future release, we may want to 37724676Smckay // update this code to recognize URLs rather than simple 37824676Smckay // filenames, but if we do, we should redo all path-related code. 37924676Smckay private void addJarClassPath(Path jarFile, boolean warn) { 3803185Ssos try { 38112724Sphk for (Path f : fsInfo.getJarClassPath(jarFile)) { 3823185Ssos addFile(f, warn); 3833185Ssos } 38424676Smckay } catch (IOException e) { 3853185Ssos log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e)); 3863185Ssos } 3871390Ssos } 38818297Sbde } 3895291Sbde 39018297Sbde /** 39118297Sbde * Base class for handling support for the representation of Locations. 39218297Sbde * 3933185Ssos * Locations are (by design) opaque handles that can easily be implemented 3945291Sbde * by enums like StandardLocation. Within JavacFileManager, each Location 3955291Sbde * has an associated LocationHandler, which provides much of the appropriate 3965291Sbde * functionality for the corresponding Location. 3975291Sbde * 3983185Ssos * @see #initHandlers 39918297Sbde * @see #getHandler 4003185Ssos */ 4011390Ssos protected abstract class LocationHandler { 40210268Sbde 4031390Ssos /** 40416428Sbde * @see JavaFileManager#handleOption 4051390Ssos */ 4061390Ssos abstract boolean handleOption(Option option, String value); 40716428Sbde 40829000Sfsmp /** 40916428Sbde * @see StandardJavaFileManager#hasLocation 41016428Sbde */ 41119173Sbde boolean isSet() { 41216428Sbde return (getPaths() != null); 4131390Ssos } 4141390Ssos 41516428Sbde /** 41628921Sfsmp * @see StandardJavaFileManager#getLocation 41716428Sbde */ 4181390Ssos abstract Collection<Path> getPaths(); 4191390Ssos 4201390Ssos /** 4212017Swollman * @see StandardJavaFileManager#setLocation 4221390Ssos */ 42315508Sbde abstract void setPaths(Iterable<? extends Path> files) throws IOException; 4241390Ssos 4251390Ssos /** 4261390Ssos * @see JavaFileManager#getLocationForModule(Location, String) 4271390Ssos */ 4281390Ssos Location getLocationForModule(String moduleName) throws IOException { 42922106Sbde return null; 4301390Ssos } 4311390Ssos 4321390Ssos /** 4331390Ssos * @see JavaFileManager#getLocationForModule(Location, JavaFileObject, String) 4341390Ssos */ 4351390Ssos Location getLocationForModule(Path dir) { 4361390Ssos return null; 4371390Ssos } 4381390Ssos 4391390Ssos /** 4401390Ssos * @see JavaFileManager#inferModuleName 4411390Ssos */ 4421390Ssos String inferModuleName() { 4431390Ssos return null; 4441390Ssos } 4451390Ssos 44621783Sbde /** 44721783Sbde * @see JavaFileManager#listLocationsForModules 44821783Sbde */ 44921783Sbde Iterable<Set<Location>> listLocationsForModules() throws IOException { 45021783Sbde return null; 45121783Sbde } 45221783Sbde } 4531390Ssos 4541390Ssos /** 4551390Ssos * A LocationHandler for a given Location, and associated set of options. 4561390Ssos */ 4571390Ssos private abstract class BasicLocationHandler extends LocationHandler { 4581390Ssos 45910268Sbde final Location location; 46022106Sbde final Set<Option> options; 4611390Ssos 46215508Sbde /** 4631390Ssos * Create a handler. The location and options provide a way to map from a location or an 4641390Ssos * option to the corresponding handler. 46522106Sbde * 46622106Sbde * @param location the location for which this is the handler 46722106Sbde * @param options the options affecting this location 46822106Sbde * @see #initHandlers 46922106Sbde */ 47022106Sbde protected BasicLocationHandler(Location location, Option... options) { 47122106Sbde this.location = location; 47222106Sbde this.options = options.length == 0 47322106Sbde ? EnumSet.noneOf(Option.class) 47422106Sbde : EnumSet.copyOf(Arrays.asList(options)); 47522106Sbde } 47622106Sbde } 47722106Sbde 47822106Sbde /** 47922106Sbde * General purpose implementation for output locations, such as -d/CLASS_OUTPUT and 48022106Sbde * -s/SOURCE_OUTPUT. All options are treated as equivalent (i.e. aliases.) 48122106Sbde * The value is a single file, possibly null. 48222106Sbde */ 48322106Sbde private class OutputLocationHandler extends BasicLocationHandler { 4841390Ssos 4851390Ssos private Path outputDir; 48610268Sbde private Map<String, Location> moduleLocations; 4871390Ssos private Map<Path, Location> pathLocations; 4881390Ssos 4891390Ssos OutputLocationHandler(Location location, Option... options) { 49021783Sbde super(location, options); 4911390Ssos } 49221783Sbde 49321783Sbde @Override 49421783Sbde boolean handleOption(Option option, String value) { 49521783Sbde if (!options.contains(option)) { 49621783Sbde return false; 49721783Sbde } 49821783Sbde 49921783Sbde // TODO: could/should validate outputDir exists and is a directory 50021783Sbde // need to decide how best to report issue for benefit of 50121783Sbde // direct API call on JavaFileManager.handleOption(specifies IAE) 50221783Sbde // vs. command line decoding. 50321783Sbde outputDir = (value == null) ? null : getPath(value); 5041390Ssos return true; 5051390Ssos } 5061390Ssos 5071390Ssos @Override 5081390Ssos Collection<Path> getPaths() { 5091390Ssos return (outputDir == null) ? null : Collections.singleton(outputDir); 5101390Ssos } 5111390Ssos 5121390Ssos @Override 5133185Ssos void setPaths(Iterable<? extends Path> files) throws IOException { 5141390Ssos if (files == null) { 5151390Ssos outputDir = null; 5161390Ssos } else { 5171390Ssos Iterator<? extends Path> pathIter = files.iterator(); 5181390Ssos if (!pathIter.hasNext()) { 5191390Ssos throw new IllegalArgumentException("empty path for directory"); 5208876Srgrimes } 5211390Ssos Path dir = pathIter.next(); 5221390Ssos if (pathIter.hasNext()) { 52317231Sjoerg throw new IllegalArgumentException("path too long for directory"); 5241390Ssos } 5258876Srgrimes if (!Files.exists(dir)) { 52617231Sjoerg throw new FileNotFoundException(dir + ": does not exist"); 52717231Sjoerg } else if (!Files.isDirectory(dir)) { 52817231Sjoerg throw new IOException(dir + ": not a directory"); 52917231Sjoerg } 53017231Sjoerg outputDir = dir; 53129000Sfsmp } 5321390Ssos moduleLocations = null; 5331390Ssos pathLocations = null; 53429000Sfsmp } 5351390Ssos 53617231Sjoerg @Override 53717231Sjoerg Location getLocationForModule(String name) { 5381390Ssos if (moduleLocations == null) { 5392873Sbde moduleLocations = new HashMap<>(); 5401390Ssos pathLocations = new HashMap<>(); 54117231Sjoerg } 54217231Sjoerg Location l = moduleLocations.get(name); 5431390Ssos if (l == null) { 5441390Ssos Path out = outputDir.resolve(name); 5452913Sache l = new ModuleLocationHandler(location.getName() + "[" + name + "]", 5462913Sache name, 5472913Sache Collections.singleton(out), 5482913Sache true, false); 54914943Sbde moduleLocations.put(name, l); 55014943Sbde pathLocations.put(out.toAbsolutePath(), l); 55114943Sbde } 55214943Sbde return l; 55314943Sbde } 55414943Sbde 55514943Sbde @Override 55614943Sbde Location getLocationForModule(Path dir) { 55714943Sbde return (pathLocations == null) ? null : pathLocations.get(dir); 55814943Sbde } 55914943Sbde 56014943Sbde private boolean listed; 56114943Sbde 56213445Sphk @Override 5635291Sbde Iterable<Set<Location>> listLocationsForModules() throws IOException { 5642913Sache if (!listed && outputDir != null) { 56533309Sbde try (DirectoryStream<Path> stream = Files.newDirectoryStream(outputDir)) { 5665291Sbde for (Path p : stream) { 56733309Sbde getLocationForModule(p.getFileName().toString()); 5685291Sbde } 56933309Sbde } 5702913Sache listed = true; 5712913Sache } 57213445Sphk if (moduleLocations == null) 5732913Sache return Collections.emptySet(); 5742913Sache Set<Location> locns = new LinkedHashSet<>(); 57513445Sphk moduleLocations.forEach((k, v) -> locns.add(v)); 5762913Sache return Collections.singleton(locns); 5772913Sache } 57815508Sbde } 57915508Sbde 58015508Sbde /** 58148160Sgreen * General purpose implementation for search path locations, 58215508Sbde * such as -sourcepath/SOURCE_PATH and -processorPath/ANNOTATION_PROCESSOR_PATH. 58315508Sbde * All options are treated as equivalent (i.e. aliases.) 58415508Sbde * The value is an ordered set of files and/or directories. 58523393Sbde */ 58623393Sbde private class SimpleLocationHandler extends BasicLocationHandler { 58715508Sbde 58815508Sbde protected Collection<Path> searchPath; 58915508Sbde 59015508Sbde SimpleLocationHandler(Location location, Option... options) { 59115508Sbde super(location, options); 59215508Sbde } 59315508Sbde 59415508Sbde @Override 59515508Sbde boolean handleOption(Option option, String value) { 59615508Sbde if (!options.contains(option)) { 59715508Sbde return false; 59815508Sbde } 59915508Sbde searchPath = value == null ? null 60015508Sbde : Collections.unmodifiableCollection(createPath().addFiles(value)); 60115508Sbde return true; 60215508Sbde } 60315508Sbde 60415508Sbde @Override 60515508Sbde Collection<Path> getPaths() { 60615508Sbde return searchPath; 60715508Sbde } 60815508Sbde 60915508Sbde @Override 61015508Sbde void setPaths(Iterable<? extends Path> files) { 61115508Sbde SearchPath p; 61215508Sbde if (files == null) { 61315508Sbde p = computePath(null); 61415508Sbde } else { 61515508Sbde p = createPath().addFiles(files); 61615508Sbde } 61715508Sbde searchPath = Collections.unmodifiableCollection(p); 61815508Sbde } 61932054Sphk 62048160Sgreen protected SearchPath computePath(String value) { 62148266Speter return createPath().addFiles(value); 62248266Speter } 62315508Sbde 62415508Sbde protected SearchPath createPath() { 62515508Sbde return new SearchPath(); 62615508Sbde } 62715508Sbde } 62815508Sbde 62915508Sbde /** 63015508Sbde * Subtype of SimpleLocationHandler for -classpath/CLASS_PATH. 63115508Sbde * If no value is given, a default is provided, based on system properties and other values. 63215508Sbde */ 63315508Sbde private class ClassPathLocationHandler extends SimpleLocationHandler { 63415508Sbde 63515508Sbde ClassPathLocationHandler() { 63615508Sbde super(StandardLocation.CLASS_PATH, Option.CLASS_PATH); 63715508Sbde } 63815508Sbde 63915508Sbde @Override 64015508Sbde Collection<Path> getPaths() { 64115508Sbde lazy(); 64215508Sbde return searchPath; 64315508Sbde } 64415508Sbde 64515508Sbde @Override 64615508Sbde protected SearchPath computePath(String value) { 64715508Sbde String cp = value; 64815508Sbde 64915508Sbde // CLASSPATH environment variable when run from `javac'. 65015508Sbde if (cp == null) { 65115508Sbde cp = System.getProperty("env.class.path"); 65215508Sbde } 65315508Sbde 65415508Sbde // If invoked via a java VM (not the javac launcher), use the 65515508Sbde // platform class path 65633690Sphk if (cp == null && System.getProperty("application.home") == null) { 65748160Sgreen cp = System.getProperty("java.class.path"); 65833690Sphk } 65933690Sphk 66033690Sphk // Default to current working directory. 66132005Sphk if (cp == null) { 66233929Sphk cp = "."; 66315508Sbde } 66415508Sbde 66515508Sbde return createPath().addFiles(cp); 66615508Sbde } 66723393Sbde 66823393Sbde @Override 66923393Sbde protected SearchPath createPath() { 67015508Sbde return new SearchPath() 67115508Sbde .expandJarClassPaths(true) // Only search user jars for Class-Paths 67215508Sbde .emptyPathDefault(getPath(".")); // Empty path elt ==> current directory 67315508Sbde } 67415508Sbde 67515508Sbde private void lazy() { 67616874Sbde if (searchPath == null) { 67733309Sbde setPaths(null); 67815508Sbde } 67915508Sbde } 68029000Sfsmp } 68115508Sbde 68233309Sbde /** 68333309Sbde * Custom subtype of LocationHandler for PLATFORM_CLASS_PATH. 68433309Sbde * Various options are supported for different components of the 68533309Sbde * platform class path. 68633309Sbde * Setting a value with setLocation overrides all existing option values. 68733309Sbde * Setting any option overrides any value set with setLocation, and 68833309Sbde * reverts to using default values for options that have not been set. 68928921Sfsmp * Setting -bootclasspath or -Xbootclasspath overrides any existing 69015508Sbde * value for -Xbootclasspath/p: and -Xbootclasspath/a:. 69115508Sbde */ 69215508Sbde private class BootClassPathLocationHandler extends BasicLocationHandler { 6935291Sbde 69452669Siwasaki private Collection<Path> searchPath; 69552669Siwasaki final Map<Option, String> optionValues = new EnumMap<>(Option.class); 69652669Siwasaki 69752669Siwasaki /** 69852669Siwasaki * Is the bootclasspath the default? 69952669Siwasaki */ 70052669Siwasaki private boolean isDefault; 70152669Siwasaki 70252669Siwasaki BootClassPathLocationHandler() { 70352669Siwasaki super(StandardLocation.PLATFORM_CLASS_PATH, 70452669Siwasaki Option.BOOT_CLASS_PATH, Option.XBOOTCLASSPATH, 70552669Siwasaki Option.XBOOTCLASSPATH_PREPEND, 70652669Siwasaki Option.XBOOTCLASSPATH_APPEND, 70752669Siwasaki Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS, 70852669Siwasaki Option.EXTDIRS, Option.DJAVA_EXT_DIRS); 70952669Siwasaki } 71052669Siwasaki 71152669Siwasaki boolean isDefault() { 71252669Siwasaki lazy(); 71352669Siwasaki return isDefault; 71452669Siwasaki } 71552669Siwasaki 71633690Sphk @Override 7175291Sbde boolean handleOption(Option option, String value) { 7185291Sbde if (!options.contains(option)) { 7191390Ssos return false; 7208876Srgrimes } 721798Swollman 72215508Sbde option = canonicalize(option); 72315508Sbde optionValues.put(option, value); 72432054Sphk if (option == BOOT_CLASS_PATH) { 72532054Sphk optionValues.remove(XBOOTCLASSPATH_PREPEND); 72632054Sphk optionValues.remove(XBOOTCLASSPATH_APPEND); 72732054Sphk } 72832054Sphk searchPath = null; // reset to "uninitialized" 72915508Sbde return true; 73015508Sbde } 73115508Sbde // where 73216874Sbde // TODO: would be better if option aliasing was handled at a higher 73315508Sbde // level 73415508Sbde private Option canonicalize(Option option) { 73515508Sbde switch (option) { 73615508Sbde case XBOOTCLASSPATH: 73715508Sbde return Option.BOOT_CLASS_PATH; 73818288Sbde case DJAVA_ENDORSED_DIRS: 73915508Sbde return Option.ENDORSEDDIRS; 74015508Sbde case DJAVA_EXT_DIRS: 74115508Sbde return Option.EXTDIRS; 74215508Sbde default: 74315508Sbde return option; 74415508Sbde } 74515508Sbde } 74615508Sbde 74715508Sbde @Override 74815508Sbde Collection<Path> getPaths() { 74915508Sbde lazy(); 75015508Sbde return searchPath; 75116300Spst } 75219173Sbde 75315508Sbde @Override 75415508Sbde void setPaths(Iterable<? extends Path> files) { 75515508Sbde if (files == null) { 75615508Sbde searchPath = null; // reset to "uninitialized" 75715508Sbde } else { 75823393Sbde isDefault = false; 75923393Sbde SearchPath p = new SearchPath().addFiles(files, false); 76023393Sbde searchPath = Collections.unmodifiableCollection(p); 76123393Sbde optionValues.clear(); 76232005Sphk } 76315508Sbde } 76415508Sbde 76515508Sbde SearchPath computePath() throws IOException { 76640610Sphk SearchPath path = new SearchPath(); 76740610Sphk 76815508Sbde String bootclasspathOpt = optionValues.get(BOOT_CLASS_PATH); 76932005Sphk String endorseddirsOpt = optionValues.get(ENDORSEDDIRS); 77032005Sphk String extdirsOpt = optionValues.get(EXTDIRS); 77116300Spst String xbootclasspathPrependOpt = optionValues.get(XBOOTCLASSPATH_PREPEND); 77219173Sbde String xbootclasspathAppendOpt = optionValues.get(XBOOTCLASSPATH_APPEND); 77332005Sphk path.addFiles(xbootclasspathPrependOpt); 77432005Sphk 77515508Sbde if (endorseddirsOpt != null) { 77615508Sbde path.addDirectories(endorseddirsOpt); 77732054Sphk } else { 77815508Sbde path.addDirectories(System.getProperty("java.endorsed.dirs"), false); 77915508Sbde } 78015508Sbde 78115508Sbde if (bootclasspathOpt != null) { 78215508Sbde path.addFiles(bootclasspathOpt); 78348160Sgreen } else { 78448160Sgreen // Standard system classes for this compiler's release. 78515508Sbde Collection<Path> systemClasses = systemClasses(); 78648160Sgreen if (systemClasses != null) { 78732054Sphk path.addFiles(systemClasses, false); 78823393Sbde } else { 78933690Sphk // fallback to the value of sun.boot.class.path 79016300Spst String files = System.getProperty("sun.boot.class.path"); 79115508Sbde path.addFiles(files, false); 79234617Sphk } 79334617Sphk } 79434617Sphk 79534617Sphk path.addFiles(xbootclasspathAppendOpt); 79634617Sphk 79734617Sphk // Strictly speaking, standard extensions are not bootstrap 79834617Sphk // classes, but we treat them identically, so we'll pretend 79934617Sphk // that they are. 80034617Sphk if (extdirsOpt != null) { 80134617Sphk path.addDirectories(extdirsOpt); 80234617Sphk } else { 80349186Smsmith // Add lib/jfxrt.jar to the search path 80449186Smsmith Path jfxrt = javaHome.resolve("lib/jfxrt.jar"); 80534617Sphk if (Files.exists(jfxrt)) { 80634617Sphk path.addFile(jfxrt, false); 80734617Sphk } 80834617Sphk path.addDirectories(System.getProperty("java.ext.dirs"), false); 80949186Smsmith } 81049186Smsmith 81134617Sphk isDefault = 81249186Smsmith (xbootclasspathPrependOpt == null) 81334617Sphk && (bootclasspathOpt == null) 81434617Sphk && (xbootclasspathAppendOpt == null); 81547592Sphk 81640610Sphk return path; 81740610Sphk } 81833690Sphk 81934617Sphk /** 82034617Sphk * Return a collection of files containing system classes. 8214Srgrimes * Returns {@code null} if not running on a modular image. 8224Srgrimes * 8232913Sache * @throws UncheckedIOException if an I/O errors occurs 82441787Smckay */ 82541787Smckay private Collection<Path> systemClasses() throws IOException { 8262913Sache // Return "modules" jimage file if available 8272913Sache if (Files.isRegularFile(thisSystemModules)) { 8283185Ssos return Collections.singleton(thisSystemModules); 8294Srgrimes } 8302913Sache 8312913Sache // Exploded module image 8322913Sache Path modules = javaHome.resolve("modules"); 8332913Sache if (Files.isDirectory(modules.resolve("java.base"))) { 83433690Sphk try (Stream<Path> listedModules = Files.list(modules)) { 8354Srgrimes return listedModules.collect(Collectors.toList()); 83632850Sphk } 83732850Sphk } 83833690Sphk 83933690Sphk // not a modular image that we know about 84033690Sphk return null; 84132850Sphk } 84232850Sphk 8431390Ssos private void lazy() { 84441787Smckay if (searchPath == null) { 8459202Srgrimes try { 8462913Sache searchPath = Collections.unmodifiableCollection(computePath()); 8474Srgrimes } catch (IOException e) { 84841787Smckay // TODO: need better handling here, e.g. javac Abort? 84941787Smckay throw new UncheckedIOException(e); 8502913Sache } 8514Srgrimes } 8522913Sache } 8533355Sache } 85441787Smckay 8553355Sache /** 8563355Sache * A LocationHander to represent modules found from a module-oriented 8572913Sache * location such as MODULE_SOURCE_PATH, UPGRADE_MODULE_PATH, 8583355Sache * SYSTEM_MODULES and MODULE_PATH. 8593355Sache * 8603355Sache * The Location can be specified to accept overriding classes from the 8612913Sache * {@code --patch-module <module>=<path> } parameter. 86241787Smckay */ 86341787Smckay private class ModuleLocationHandler extends LocationHandler implements Location { 86441787Smckay protected final String name; 86541787Smckay protected final String moduleName; 8662913Sache protected final Collection<Path> searchPath; 86741787Smckay protected final Collection<Path> searchPathWithOverrides; 8682913Sache protected final boolean output; 8692913Sache 87041787Smckay ModuleLocationHandler(String name, String moduleName, Collection<Path> searchPath, 8712913Sache boolean output, boolean allowOverrides) { 8722913Sache this.name = name; 8732913Sache this.moduleName = moduleName; 8742913Sache this.searchPath = searchPath; 87541787Smckay this.output = output; 87641787Smckay 8771390Ssos if (allowOverrides && patchMap != null) { 87815054Sache SearchPath mPatch = patchMap.get(moduleName); 8794Srgrimes if (mPatch != null) { 88034961Sphk SearchPath sp = new SearchPath(); 88133690Sphk sp.addAll(mPatch); 88233690Sphk sp.addAll(searchPath); 88333690Sphk searchPathWithOverrides = sp; 88433690Sphk } else { 88533690Sphk searchPathWithOverrides = searchPath; 88633690Sphk } 88733690Sphk } else { 88833690Sphk searchPathWithOverrides = searchPath; 8892913Sache } 8902913Sache } 8912913Sache 89241787Smckay @Override @DefinedBy(Api.COMPILER) 89341787Smckay public String getName() { 8944Srgrimes return name; 8954Srgrimes } 8964Srgrimes 89741787Smckay @Override @DefinedBy(Api.COMPILER) 8984Srgrimes public boolean isOutputLocation() { 8994180Sbde return output; 9004180Sbde } 9014Srgrimes 9022913Sache @Override // defined by LocationHandler 90311872Sphk boolean handleOption(Option option, String value) { 9044Srgrimes throw new UnsupportedOperationException(); 9053366Sache } 9063366Sache 9073366Sache @Override // defined by LocationHandler 9082913Sache Collection<Path> getPaths() { 90934961Sphk // For now, we always return searchPathWithOverrides. This may differ from the 9102913Sache // JVM behavior if there is a module-info.class to be found in the overriding 9114Srgrimes // classes. 9125291Sbde return searchPathWithOverrides; 9132913Sache } 9144Srgrimes 91541787Smckay @Override // defined by LocationHandler 9161390Ssos void setPaths(Iterable<? extends Path> files) throws IOException { 91715054Sache throw new UnsupportedOperationException(); 9182913Sache } 91913445Sphk 92013445Sphk @Override // defined by LocationHandler 92113445Sphk String inferModuleName() { 9222913Sache return moduleName; 92341787Smckay } 9242913Sache } 92513350Sache 92613350Sache /** 92713350Sache * A LocationHandler for simple module-oriented search paths, 92813350Sache * like UPGRADE_MODULE_PATH and MODULE_PATH. 9292913Sache */ 9302913Sache private class ModulePathLocationHandler extends SimpleLocationHandler { 93113453Sache private Map<String, ModuleLocationHandler> pathModules; 93213350Sache 93313445Sphk ModulePathLocationHandler(Location location, Option... options) { 9343355Sache super(location, options); 93513402Sbde } 93613402Sbde 9372913Sache @Override 93813402Sbde public boolean handleOption(Option option, String value) { 93913402Sbde if (!options.contains(option)) { 94013402Sbde return false; 94113402Sbde } 94213402Sbde setPaths(value == null ? null : getPathEntries(value)); 94313402Sbde return true; 94413402Sbde } 94513402Sbde 94613445Sphk @Override 94713445Sphk public Location getLocationForModule(String moduleName) { 9482913Sache initPathModules(); 9495291Sbde return pathModules.get(moduleName); 95015345Snate } 9514Srgrimes 9524Srgrimes @Override 95327560Sfsmp Iterable<Set<Location>> listLocationsForModules() { 9544Srgrimes if (searchPath == null) 9555291Sbde return Collections.emptyList(); 9564Srgrimes 9575291Sbde return ModulePathIterator::new; 9585291Sbde } 9594Srgrimes 9605291Sbde @Override 96126949Sfsmp void setPaths(Iterable<? extends Path> paths) { 96234571Stegge if (paths != null) { 96345900Speter for (Path p: paths) { 96425164Speter checkValidModulePathEntry(p); 9654Srgrimes } 96615345Snate } 96715345Snate super.setPaths(paths); 96815345Snate } 96915345Snate 97015345Snate private void initPathModules() { 97115345Snate if (pathModules != null) { 97215345Snate return; 97315345Snate } 97415345Snate 97515345Snate pathModules = new LinkedHashMap<>(); 97615345Snate 97715345Snate for (Set<Location> set : listLocationsForModules()) { 97815345Snate for (Location locn : set) { 97915345Snate if (locn instanceof ModuleLocationHandler) { 9804Srgrimes ModuleLocationHandler h = (ModuleLocationHandler) locn; 9815291Sbde pathModules.put(h.moduleName, h); 98226949Sfsmp } 98327563Sfsmp } 98438888Stegge } 98534571Stegge } 98634571Stegge 98734571Stegge private void checkValidModulePathEntry(Path p) { 98834571Stegge if (!Files.exists(p)) { 98934571Stegge // warning may be generated later 99034571Stegge return; 99134571Stegge } 99234571Stegge 99334571Stegge if (Files.isDirectory(p)) { 99434571Stegge // either an exploded module or a directory of modules 99534571Stegge return; 99627563Sfsmp } 99727563Sfsmp 99845897Speter String name = p.getFileName().toString(); 99945897Speter int lastDot = name.lastIndexOf("."); 100034571Stegge if (lastDot > 0) { 100134571Stegge switch (name.substring(lastDot)) { 100227696Sfsmp case ".jar": 100327563Sfsmp case ".jmod": 100445897Speter return; 100545897Speter } 10064Srgrimes } 100727696Sfsmp throw new IllegalArgumentException(p.toString()); 100825164Speter } 100926949Sfsmp 10105291Sbde class ModulePathIterator implements Iterator<Set<Location>> { 10115291Sbde Iterator<Path> pathIter = searchPath.iterator(); 10125291Sbde int pathIndex = 0; 101315345Snate Set<Location> next = null; 101415345Snate 101515345Snate @Override 101615345Snate public boolean hasNext() { 10175291Sbde if (next != null) 10185291Sbde return true; 10195291Sbde 102027520Sfsmp while (next == null) { 102126949Sfsmp if (pathIter.hasNext()) { 102238888Stegge Path path = pathIter.next(); 102327520Sfsmp if (Files.isDirectory(path)) { 102427563Sfsmp next = scanDirectory(path); 102528487Sfsmp } else { 102645897Speter next = scanFile(path); 102745897Speter } 102828487Sfsmp pathIndex++; 102927616Sfsmp } else 103027616Sfsmp return false; 103127616Sfsmp } 10322074Swollman return true; 103327616Sfsmp } 103427520Sfsmp 103515345Snate @Override 103634571Stegge public Set<Location> next() { 103734571Stegge hasNext(); 103834571Stegge if (next != null) { 103934571Stegge Set<Location> result = next; 104034571Stegge next = null; 104134571Stegge return result; 104235035Stegge } 104334571Stegge throw new NoSuchElementException(); 104434571Stegge } 104534571Stegge 104634571Stegge private Set<Location> scanDirectory(Path path) { 104734571Stegge Set<Path> paths = new LinkedHashSet<>(); 104834571Stegge Path moduleInfoClass = null; 104934571Stegge try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) { 105034571Stegge for (Path entry: stream) { 105145897Speter if (entry.endsWith("module-info.class")) { 105234571Stegge moduleInfoClass = entry; 105334571Stegge break; // no need to continue scanning 105434571Stegge } 105534571Stegge paths.add(entry); 105634571Stegge } 105734571Stegge } catch (DirectoryIteratorException | IOException ignore) { 105845897Speter log.error(Errors.LocnCantReadDirectory(path)); 105945897Speter return Collections.emptySet(); 106034571Stegge } 106134571Stegge 106234571Stegge if (moduleInfoClass != null) { 106334571Stegge // It's an exploded module directly on the module path. 106434571Stegge // We can't infer module name from the directory name, so have to 106534571Stegge // read module-info.class. 106634571Stegge try { 106734571Stegge String moduleName = readModuleName(moduleInfoClass); 106834571Stegge String name = location.getName() 106934571Stegge + "[" + pathIndex + ":" + moduleName + "]"; 10704Srgrimes ModuleLocationHandler l = new ModuleLocationHandler(name, moduleName, 10714Srgrimes Collections.singleton(path), false, true); 107234571Stegge return Collections.singleton(l); 107334571Stegge } catch (ModuleNameReader.BadClassFile e) { 107434571Stegge log.error(Errors.LocnBadModuleInfo(path)); 107534571Stegge return Collections.emptySet(); 107634571Stegge } catch (IOException e) { 107734571Stegge log.error(Errors.LocnCantReadFile(path)); 107834571Stegge return Collections.emptySet(); 107934571Stegge } 108034571Stegge } 108134571Stegge 108234571Stegge // A directory of modules 108334571Stegge Set<Location> result = new LinkedHashSet<>(); 108434571Stegge int index = 0; 108534571Stegge for (Path entry : paths) { 108634571Stegge Pair<String,Path> module = inferModuleName(entry); 108734571Stegge if (module == null) { 108834571Stegge // diagnostic reported if necessary; skip to next 108934571Stegge continue; 109034571Stegge } 109134571Stegge String moduleName = module.fst; 109234571Stegge Path modulePath = module.snd; 109334571Stegge String name = location.getName() 109434571Stegge + "[" + pathIndex + "." + (index++) + ":" + moduleName + "]"; 109534571Stegge ModuleLocationHandler l = new ModuleLocationHandler(name, moduleName, 109634571Stegge Collections.singleton(modulePath), false, true); 109734571Stegge result.add(l); 109834571Stegge } 109934571Stegge return result; 110034571Stegge } 110134571Stegge 110234571Stegge private Set<Location> scanFile(Path path) { 11034Srgrimes Pair<String,Path> module = inferModuleName(path); 11041549Srgrimes if (module == null) { 11051549Srgrimes // diagnostic reported if necessary 11065291Sbde return Collections.emptySet(); 11072074Swollman } 11085291Sbde String moduleName = module.fst; 11092074Swollman Path modulePath = module.snd; 11105291Sbde String name = location.getName() 11111549Srgrimes + "[" + pathIndex + ":" + moduleName + "]"; 111215508Sbde ModuleLocationHandler l = new ModuleLocationHandler(name, moduleName, 111315508Sbde Collections.singleton(modulePath), false, true); 111415508Sbde return Collections.singleton(l); 111515508Sbde } 111615508Sbde 111715508Sbde private Pair<String,Path> inferModuleName(Path p) { 111815508Sbde if (Files.isDirectory(p)) { 111915508Sbde if (Files.exists(p.resolve("module-info.class"))) { 112015508Sbde String name = p.getFileName().toString(); 112115508Sbde if (SourceVersion.isName(name)) 112215508Sbde return new Pair<>(name, p); 112315508Sbde } 112448888Sbde return null; 112517353Sbde } 112633690Sphk 112715508Sbde if (p.getFileName().toString().endsWith(".jar") && fsInfo.exists(p)) { 112815508Sbde FileSystemProvider jarFSProvider = fsInfo.getJarFSProvider(); 112940610Sphk if (jarFSProvider == null) { 113046054Sphk log.error(Errors.NoZipfsForArchive(p)); 113115508Sbde return null; 113215508Sbde } 113315508Sbde try (FileSystem fs = jarFSProvider.newFileSystem(p, fsEnv)) { 113415508Sbde Path moduleInfoClass = fs.getPath("module-info.class"); 113515508Sbde if (Files.exists(moduleInfoClass)) { 113648888Sbde String moduleName = readModuleName(moduleInfoClass); 113715508Sbde return new Pair<>(moduleName, p); 113815508Sbde } 113932054Sphk } catch (ModuleNameReader.BadClassFile e) { 114015508Sbde log.error(Errors.LocnBadModuleInfo(p)); 114115508Sbde return null; 114215508Sbde } catch (IOException e) { 114315508Sbde log.error(Errors.LocnCantReadFile(p)); 114448888Sbde return null; 114515508Sbde } 114632005Sphk 114748888Sbde //automatic module: 114833690Sphk String fn = p.getFileName().toString(); 114933690Sphk //from ModulePath.deriveModuleDescriptor: 115040610Sphk 115146054Sphk // drop .jar 115233690Sphk String mn = fn.substring(0, fn.length()-4); 115315508Sbde 115415508Sbde // find first occurrence of -${NUMBER}. or -${NUMBER}$ 115515508Sbde Matcher matcher = Pattern.compile("-(\\d+(\\.|$))").matcher(mn); 115632054Sphk if (matcher.find()) { 115748888Sbde int start = matcher.start(); 115833690Sphk 115936441Sphk mn = mn.substring(0, start); 116036719Sphk } 116133690Sphk 116236198Sphk // finally clean up the module name 116333690Sphk mn = mn.replaceAll("(\\.|\\d)*$", "") // remove trailing version 116433690Sphk .replaceAll("[^A-Za-z0-9]", ".") // replace non-alphanumeric 116533690Sphk .replaceAll("(\\.)(\\1)+", ".") // collapse repeating dots 116633690Sphk .replaceAll("^\\.", "") // drop leading dots 116733690Sphk .replaceAll("\\.$", ""); // drop trailing dots 116833690Sphk 116933690Sphk 117033690Sphk if (!mn.isEmpty()) { 117133690Sphk return new Pair<>(mn, p); 117233690Sphk } 117333690Sphk 117447588Sbde log.error(Errors.LocnCantGetModuleNameForJar(p)); 117547588Sbde return null; 117647588Sbde } 117747588Sbde 117847588Sbde if (p.getFileName().toString().endsWith(".jmod")) { 117947588Sbde try { 118047588Sbde // check if the JMOD file is valid 118147588Sbde JDK9Wrappers.JmodFile.checkMagic(p); 118247588Sbde 118347588Sbde // No JMOD file system. Use JarFileSystem to 118447588Sbde // workaround for now 118547588Sbde FileSystem fs = fileSystems.get(p); 118633690Sphk if (fs == null) { 118747588Sbde FileSystemProvider jarFSProvider = fsInfo.getJarFSProvider(); 118833690Sphk if (jarFSProvider == null) { 118933690Sphk log.error(Errors.LocnCantReadFile(p)); 119033690Sphk return null; 119133727Sjkh } 119233690Sphk fs = jarFSProvider.newFileSystem(p, Collections.emptyMap()); 119333690Sphk try { 119433690Sphk Path moduleInfoClass = fs.getPath("classes/module-info.class"); 119533690Sphk String moduleName = readModuleName(moduleInfoClass); 119636441Sphk Path modulePath = fs.getPath("classes"); 119736719Sphk fileSystems.put(p, fs); 119833690Sphk closeables.add(fs); 119936198Sphk fs = null; // prevent fs being closed in the finally clause 120033690Sphk return new Pair<>(moduleName, modulePath); 1201 } finally { 1202 if (fs != null) 1203 fs.close(); 1204 } 1205 } 1206 } catch (ModuleNameReader.BadClassFile e) { 1207 log.error(Errors.LocnBadModuleInfo(p)); 1208 } catch (IOException e) { 1209 log.error(Errors.LocnCantReadFile(p)); 1210 return null; 1211 } 1212 } 1213 1214 if (warn && false) { // temp disable, when enabled, massage examples.not-yet.txt suitably. 1215 log.warning(Warnings.LocnUnknownFileOnModulePath(p)); 1216 } 1217 return null; 1218 } 1219 1220 private String readModuleName(Path path) throws IOException, ModuleNameReader.BadClassFile { 1221 if (moduleNameReader == null) 1222 moduleNameReader = new ModuleNameReader(); 1223 return moduleNameReader.readModuleName(path); 1224 } 1225 } 1226 1227 } 1228 1229 private class ModuleSourcePathLocationHandler extends BasicLocationHandler { 1230 1231 private Map<String, Location> moduleLocations; 1232 private Map<Path, Location> pathLocations; 1233 1234 ModuleSourcePathLocationHandler() { 1235 super(StandardLocation.MODULE_SOURCE_PATH, 1236 Option.MODULE_SOURCE_PATH); 1237 } 1238 1239 @Override 1240 boolean handleOption(Option option, String value) { 1241 init(value); 1242 return true; 1243 } 1244 1245 void init(String value) { 1246 Collection<String> segments = new ArrayList<>(); 1247 for (String s: value.split(File.pathSeparator)) { 1248 expandBraces(s, segments); 1249 } 1250 1251 Map<String, Collection<Path>> map = new LinkedHashMap<>(); 1252 final String MARKER = "*"; 1253 for (String seg: segments) { 1254 int markStart = seg.indexOf(MARKER); 1255 if (markStart == -1) { 1256 add(map, getPath(seg), null); 1257 } else { 1258 if (markStart == 0 || !isSeparator(seg.charAt(markStart - 1))) { 1259 throw new IllegalArgumentException("illegal use of " + MARKER + " in " + seg); 1260 } 1261 Path prefix = getPath(seg.substring(0, markStart - 1)); 1262 Path suffix; 1263 int markEnd = markStart + MARKER.length(); 1264 if (markEnd == seg.length()) { 1265 suffix = null; 1266 } else if (!isSeparator(seg.charAt(markEnd)) 1267 || seg.indexOf(MARKER, markEnd) != -1) { 1268 throw new IllegalArgumentException("illegal use of " + MARKER + " in " + seg); 1269 } else { 1270 suffix = getPath(seg.substring(markEnd + 1)); 1271 } 1272 add(map, prefix, suffix); 1273 } 1274 } 1275 1276 moduleLocations = new LinkedHashMap<>(); 1277 pathLocations = new LinkedHashMap<>(); 1278 map.forEach((k, v) -> { 1279 String name = location.getName() + "[" + k + "]"; 1280 ModuleLocationHandler h = new ModuleLocationHandler(name, k, v, false, false); 1281 moduleLocations.put(k, h); 1282 v.forEach(p -> pathLocations.put(normalize(p), h)); 1283 }); 1284 } 1285 1286 private boolean isSeparator(char ch) { 1287 // allow both separators on Windows 1288 return (ch == File.separatorChar) || (ch == '/'); 1289 } 1290 1291 void add(Map<String, Collection<Path>> map, Path prefix, Path suffix) { 1292 if (!Files.isDirectory(prefix)) { 1293 if (warn) { 1294 String key = Files.exists(prefix) 1295 ? "dir.path.element.not.directory" 1296 : "dir.path.element.not.found"; 1297 log.warning(Lint.LintCategory.PATH, key, prefix); 1298 } 1299 return; 1300 } 1301 try (DirectoryStream<Path> stream = Files.newDirectoryStream(prefix, path -> Files.isDirectory(path))) { 1302 for (Path entry: stream) { 1303 Path path = (suffix == null) ? entry : entry.resolve(suffix); 1304 if (Files.isDirectory(path)) { 1305 String name = entry.getFileName().toString(); 1306 Collection<Path> paths = map.get(name); 1307 if (paths == null) 1308 map.put(name, paths = new ArrayList<>()); 1309 paths.add(path); 1310 } 1311 } 1312 } catch (IOException e) { 1313 // TODO? What to do? 1314 System.err.println(e); 1315 } 1316 } 1317 1318 private void expandBraces(String value, Collection<String> results) { 1319 int depth = 0; 1320 int start = -1; 1321 String prefix = null; 1322 String suffix = null; 1323 for (int i = 0; i < value.length(); i++) { 1324 switch (value.charAt(i)) { 1325 case '{': 1326 depth++; 1327 if (depth == 1) { 1328 prefix = value.substring(0, i); 1329 suffix = value.substring(getMatchingBrace(value, i) + 1); 1330 start = i + 1; 1331 } 1332 break; 1333 1334 case ',': 1335 if (depth == 1) { 1336 String elem = value.substring(start, i); 1337 expandBraces(prefix + elem + suffix, results); 1338 start = i + 1; 1339 } 1340 break; 1341 1342 case '}': 1343 switch (depth) { 1344 case 0: 1345 throw new IllegalArgumentException("mismatched braces"); 1346 1347 case 1: 1348 String elem = value.substring(start, i); 1349 expandBraces(prefix + elem + suffix, results); 1350 return; 1351 1352 default: 1353 depth--; 1354 } 1355 break; 1356 } 1357 } 1358 if (depth > 0) 1359 throw new IllegalArgumentException("mismatched braces"); 1360 results.add(value); 1361 } 1362 1363 int getMatchingBrace(String value, int offset) { 1364 int depth = 1; 1365 for (int i = offset + 1; i < value.length(); i++) { 1366 switch (value.charAt(i)) { 1367 case '{': 1368 depth++; 1369 break; 1370 1371 case '}': 1372 if (--depth == 0) 1373 return i; 1374 break; 1375 } 1376 } 1377 throw new IllegalArgumentException("mismatched braces"); 1378 } 1379 1380 @Override 1381 boolean isSet() { 1382 return (moduleLocations != null); 1383 } 1384 1385 @Override 1386 Collection<Path> getPaths() { 1387 throw new UnsupportedOperationException(); 1388 } 1389 1390 @Override 1391 void setPaths(Iterable<? extends Path> files) throws IOException { 1392 throw new UnsupportedOperationException(); 1393 } 1394 1395 @Override 1396 Location getLocationForModule(String name) { 1397 return (moduleLocations == null) ? null : moduleLocations.get(name); 1398 } 1399 1400 @Override 1401 Location getLocationForModule(Path dir) { 1402 return (pathLocations == null) ? null : pathLocations.get(dir); 1403 } 1404 1405 @Override 1406 Iterable<Set<Location>> listLocationsForModules() { 1407 if (moduleLocations == null) 1408 return Collections.emptySet(); 1409 Set<Location> locns = new LinkedHashSet<>(); 1410 moduleLocations.forEach((k, v) -> locns.add(v)); 1411 return Collections.singleton(locns); 1412 } 1413 1414 } 1415 1416 private class SystemModulesLocationHandler extends BasicLocationHandler { 1417 private Path systemJavaHome; 1418 private Path modules; 1419 private Map<String, ModuleLocationHandler> systemModules; 1420 1421 SystemModulesLocationHandler() { 1422 super(StandardLocation.SYSTEM_MODULES, Option.SYSTEM); 1423 systemJavaHome = Locations.javaHome; 1424 } 1425 1426 @Override 1427 boolean handleOption(Option option, String value) { 1428 if (!options.contains(option)) { 1429 return false; 1430 } 1431 1432 if (value == null) { 1433 systemJavaHome = Locations.javaHome; 1434 } else if (value.equals("none")) { 1435 systemJavaHome = null; 1436 } else { 1437 update(getPath(value)); 1438 } 1439 1440 modules = null; 1441 return true; 1442 } 1443 1444 @Override 1445 Collection<Path> getPaths() { 1446 return (systemJavaHome == null) ? null : Collections.singleton(systemJavaHome); 1447 } 1448 1449 @Override 1450 void setPaths(Iterable<? extends Path> files) throws IOException { 1451 if (files == null) { 1452 systemJavaHome = null; 1453 } else { 1454 Iterator<? extends Path> pathIter = files.iterator(); 1455 if (!pathIter.hasNext()) { 1456 throw new IllegalArgumentException("empty path for directory"); // TODO: FIXME 1457 } 1458 Path dir = pathIter.next(); 1459 if (pathIter.hasNext()) { 1460 throw new IllegalArgumentException("path too long for directory"); // TODO: FIXME 1461 } 1462 if (!Files.exists(dir)) { 1463 throw new FileNotFoundException(dir + ": does not exist"); 1464 } else if (!Files.isDirectory(dir)) { 1465 throw new IOException(dir + ": not a directory"); 1466 } 1467 update(dir); 1468 } 1469 } 1470 1471 private void update(Path p) { 1472 if (!isCurrentPlatform(p) && !Files.exists(p.resolve("lib").resolve("jrt-fs.jar")) && 1473 !Files.exists(systemJavaHome.resolve("modules"))) 1474 throw new IllegalArgumentException(p.toString()); 1475 systemJavaHome = p; 1476 modules = null; 1477 } 1478 1479 private boolean isCurrentPlatform(Path p) { 1480 try { 1481 return Files.isSameFile(p, Locations.javaHome); 1482 } catch (IOException ex) { 1483 throw new IllegalArgumentException(p.toString(), ex); 1484 } 1485 } 1486 1487 @Override 1488 Location getLocationForModule(String name) throws IOException { 1489 initSystemModules(); 1490 return systemModules.get(name); 1491 } 1492 1493 @Override 1494 Iterable<Set<Location>> listLocationsForModules() throws IOException { 1495 initSystemModules(); 1496 Set<Location> locns = new LinkedHashSet<>(); 1497 for (Location l: systemModules.values()) 1498 locns.add(l); 1499 return Collections.singleton(locns); 1500 } 1501 1502 private void initSystemModules() throws IOException { 1503 if (systemModules != null) { 1504 return; 1505 } 1506 1507 if (systemJavaHome == null) { 1508 systemModules = Collections.emptyMap(); 1509 return; 1510 } 1511 1512 if (modules == null) { 1513 try { 1514 URI jrtURI = URI.create("jrt:/"); 1515 FileSystem jrtfs; 1516 1517 if (isCurrentPlatform(systemJavaHome)) { 1518 jrtfs = FileSystems.getFileSystem(jrtURI); 1519 } else { 1520 try { 1521 Map<String, String> attrMap = 1522 Collections.singletonMap("java.home", systemJavaHome.toString()); 1523 jrtfs = FileSystems.newFileSystem(jrtURI, attrMap); 1524 } catch (ProviderNotFoundException ex) { 1525 URL javaHomeURL = systemJavaHome.resolve("jrt-fs.jar").toUri().toURL(); 1526 ClassLoader currentLoader = Locations.class.getClassLoader(); 1527 URLClassLoader fsLoader = 1528 new URLClassLoader(new URL[] {javaHomeURL}, currentLoader); 1529 1530 jrtfs = FileSystems.newFileSystem(jrtURI, Collections.emptyMap(), fsLoader); 1531 1532 closeables.add(fsLoader); 1533 } 1534 1535 closeables.add(jrtfs); 1536 } 1537 1538 modules = jrtfs.getPath("/modules"); 1539 } catch (FileSystemNotFoundException | ProviderNotFoundException e) { 1540 modules = systemJavaHome.resolve("modules"); 1541 if (!Files.exists(modules)) 1542 throw new IOException("can't find system classes", e); 1543 } 1544 } 1545 1546 systemModules = new LinkedHashMap<>(); 1547 try (DirectoryStream<Path> stream = Files.newDirectoryStream(modules, Files::isDirectory)) { 1548 for (Path entry : stream) { 1549 String moduleName = entry.getFileName().toString(); 1550 String name = location.getName() + "[" + moduleName + "]"; 1551 ModuleLocationHandler h = new ModuleLocationHandler(name, moduleName, 1552 Collections.singleton(entry), false, true); 1553 systemModules.put(moduleName, h); 1554 } 1555 } 1556 } 1557 } 1558 1559 Map<Location, LocationHandler> handlersForLocation; 1560 Map<Option, LocationHandler> handlersForOption; 1561 1562 void initHandlers() { 1563 handlersForLocation = new HashMap<>(); 1564 handlersForOption = new EnumMap<>(Option.class); 1565 1566 BasicLocationHandler[] handlers = { 1567 new BootClassPathLocationHandler(), 1568 new ClassPathLocationHandler(), 1569 new SimpleLocationHandler(StandardLocation.SOURCE_PATH, Option.SOURCE_PATH), 1570 new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, Option.PROCESSOR_PATH), 1571 new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, Option.PROCESSOR_MODULE_PATH), 1572 new OutputLocationHandler(StandardLocation.CLASS_OUTPUT, Option.D), 1573 new OutputLocationHandler(StandardLocation.SOURCE_OUTPUT, Option.S), 1574 new OutputLocationHandler(StandardLocation.NATIVE_HEADER_OUTPUT, Option.H), 1575 new ModuleSourcePathLocationHandler(), 1576 // TODO: should UPGRADE_MODULE_PATH be merged with SYSTEM_MODULES? 1577 new ModulePathLocationHandler(StandardLocation.UPGRADE_MODULE_PATH, Option.UPGRADE_MODULE_PATH), 1578 new ModulePathLocationHandler(StandardLocation.MODULE_PATH, Option.MODULE_PATH), 1579 new SystemModulesLocationHandler(), 1580 }; 1581 1582 for (BasicLocationHandler h : handlers) { 1583 handlersForLocation.put(h.location, h); 1584 for (Option o : h.options) { 1585 handlersForOption.put(o, h); 1586 } 1587 } 1588 } 1589 1590 private Map<String, SearchPath> patchMap; 1591 1592 boolean handleOption(Option option, String value) { 1593 switch (option) { 1594 case PATCH_MODULE: 1595 if (value == null) { 1596 patchMap = null; 1597 } else { 1598 // Allow an extended syntax for --patch-module consisting of a series 1599 // of values separated by NULL characters. This is to facilitate 1600 // supporting deferred file manager options on the command line. 1601 // See Option.PATCH_MODULE for the code that composes these multiple 1602 // values. 1603 for (String v : value.split("\0")) { 1604 int eq = v.indexOf('='); 1605 if (eq > 0) { 1606 String mName = v.substring(0, eq); 1607 SearchPath mPatchPath = new SearchPath() 1608 .addFiles(v.substring(eq + 1)); 1609 boolean ok = true; 1610 for (Path p : mPatchPath) { 1611 Path mi = p.resolve("module-info.class"); 1612 if (Files.exists(mi)) { 1613 log.error(Errors.LocnModuleInfoNotAllowedOnPatchPath(mi)); 1614 ok = false; 1615 } 1616 } 1617 if (ok) { 1618 if (patchMap == null) { 1619 patchMap = new LinkedHashMap<>(); 1620 } 1621 patchMap.put(mName, mPatchPath); 1622 } 1623 } else { 1624 // Should not be able to get here; 1625 // this should be caught and handled in Option.PATCH_MODULE 1626 log.error(Errors.LocnInvalidArgForXpatch(value)); 1627 } 1628 } 1629 } 1630 return true; 1631 default: 1632 LocationHandler h = handlersForOption.get(option); 1633 return (h == null ? false : h.handleOption(option, value)); 1634 } 1635 } 1636 1637 boolean hasLocation(Location location) { 1638 LocationHandler h = getHandler(location); 1639 return (h == null ? false : h.isSet()); 1640 } 1641 1642 Collection<Path> getLocation(Location location) { 1643 LocationHandler h = getHandler(location); 1644 return (h == null ? null : h.getPaths()); 1645 } 1646 1647 Path getOutputLocation(Location location) { 1648 if (!location.isOutputLocation()) { 1649 throw new IllegalArgumentException(); 1650 } 1651 LocationHandler h = getHandler(location); 1652 return ((OutputLocationHandler) h).outputDir; 1653 } 1654 1655 void setLocation(Location location, Iterable<? extends Path> files) throws IOException { 1656 LocationHandler h = getHandler(location); 1657 if (h == null) { 1658 if (location.isOutputLocation()) { 1659 h = new OutputLocationHandler(location); 1660 } else { 1661 h = new SimpleLocationHandler(location); 1662 } 1663 handlersForLocation.put(location, h); 1664 } 1665 h.setPaths(files); 1666 } 1667 1668 Location getLocationForModule(Location location, String name) throws IOException { 1669 LocationHandler h = getHandler(location); 1670 return (h == null ? null : h.getLocationForModule(name)); 1671 } 1672 1673 Location getLocationForModule(Location location, Path dir) { 1674 LocationHandler h = getHandler(location); 1675 return (h == null ? null : h.getLocationForModule(dir)); 1676 } 1677 1678 String inferModuleName(Location location) { 1679 LocationHandler h = getHandler(location); 1680 return (h == null ? null : h.inferModuleName()); 1681 } 1682 1683 Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException { 1684 LocationHandler h = getHandler(location); 1685 return (h == null ? null : h.listLocationsForModules()); 1686 } 1687 1688 protected LocationHandler getHandler(Location location) { 1689 Objects.requireNonNull(location); 1690 return (location instanceof LocationHandler) 1691 ? (LocationHandler) location 1692 : handlersForLocation.get(location); 1693 } 1694 1695 /** 1696 * Is this the name of an archive file? 1697 */ 1698 private boolean isArchive(Path file) { 1699 String n = StringUtils.toLowerCase(file.getFileName().toString()); 1700 return fsInfo.isFile(file) 1701 && (n.endsWith(".jar") || n.endsWith(".zip")); 1702 } 1703 1704 static Path normalize(Path p) { 1705 try { 1706 return p.toRealPath(); 1707 } catch (IOException e) { 1708 return p.toAbsolutePath().normalize(); 1709 } 1710 } 1711 1712} 1713