SjavacImpl.java revision 3219:aacc4ceb35c9
1321936Shselasky/* 2321936Shselasky * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. 3321936Shselasky * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4321936Shselasky * 5321936Shselasky * This code is free software; you can redistribute it and/or modify it 6321936Shselasky * under the terms of the GNU General Public License version 2 only, as 7321936Shselasky * published by the Free Software Foundation. Oracle designates this 8321936Shselasky * particular file as subject to the "Classpath" exception as provided 9321936Shselasky * by Oracle in the LICENSE file that accompanied this code. 10321936Shselasky * 11321936Shselasky * This code is distributed in the hope that it will be useful, but WITHOUT 12321936Shselasky * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13321936Shselasky * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14321936Shselasky * version 2 for more details (a copy is included in the LICENSE file that 15321936Shselasky * accompanied this code). 16321936Shselasky * 17321936Shselasky * You should have received a copy of the GNU General Public License version 18321936Shselasky * 2 along with this work; if not, write to the Free Software Foundation, 19321936Shselasky * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20321936Shselasky * 21321936Shselasky * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22321936Shselasky * or visit www.oracle.com if you need additional information or have any 23321936Shselasky * questions. 24321936Shselasky */ 25321936Shselasky 26321936Shselaskypackage com.sun.tools.sjavac.comp; 27321936Shselasky 28321936Shselaskyimport java.io.IOException; 29321936Shselaskyimport java.io.PrintWriter; 30321936Shselaskyimport java.io.Writer; 31321936Shselaskyimport java.nio.file.Files; 32321936Shselaskyimport java.nio.file.Path; 33321936Shselaskyimport java.util.ArrayList; 34321936Shselaskyimport java.util.Collections; 35321936Shselaskyimport java.util.HashMap; 36321936Shselaskyimport java.util.HashSet; 37321936Shselaskyimport java.util.List; 38321936Shselaskyimport java.util.Map; 39321936Shselaskyimport java.util.Set; 40321936Shselaskyimport java.util.stream.Stream; 41321936Shselasky 42321936Shselaskyimport com.sun.tools.javac.file.JavacFileManager; 43321936Shselaskyimport com.sun.tools.javac.main.Main; 44321936Shselaskyimport com.sun.tools.javac.util.Context; 45321936Shselaskyimport com.sun.tools.sjavac.JavacState; 46321936Shselaskyimport com.sun.tools.sjavac.Log; 47325522Snpimport com.sun.tools.sjavac.Module; 48325522Snpimport com.sun.tools.sjavac.ProblemException; 49325522Snpimport com.sun.tools.sjavac.Source; 50321936Shselaskyimport com.sun.tools.sjavac.Transformer; 51325522Snpimport com.sun.tools.sjavac.Util; 52325522Snpimport com.sun.tools.sjavac.options.Option; 53325522Snpimport com.sun.tools.sjavac.options.Options; 54321936Shselaskyimport com.sun.tools.sjavac.options.SourceLocation; 55321936Shselaskyimport com.sun.tools.sjavac.server.Sjavac; 56321936Shselasky 57321936Shselaskyimport javax.tools.JavaFileManager; 58321936Shselasky 59321936Shselasky/** 60321936Shselasky * The sjavac implementation that interacts with javac and performs the actual 61321936Shselasky * compilation. 62321936Shselasky * 63321936Shselasky * <p><b>This is NOT part of any supported API. 64325522Snp * If you write code that depends on this, you do so at your own risk. 65325522Snp * This code and its internal interfaces are subject to change or 66325522Snp * deletion without notice.</b> 67325522Snp */ 68325522Snppublic class SjavacImpl implements Sjavac { 69325522Snp 70325522Snp @Override 71325522Snp public int compile(String[] args, Writer out, Writer err) { 72325522Snp Options options; 73325522Snp try { 74325522Snp options = Options.parseArgs(args); 75321936Shselasky } catch (IllegalArgumentException e) { 76321936Shselasky Log.error(e.getMessage()); 77321936Shselasky return RC_FATAL; 78321936Shselasky } 79321936Shselasky 80321936Shselasky Log.setLogLevel(options.getLogLevel()); 81321936Shselasky 82321936Shselasky if (!validateOptions(options)) 83325522Snp return RC_FATAL; 84325522Snp 85325522Snp if (!createIfMissing(options.getDestDir())) 86321936Shselasky return RC_FATAL; 87325522Snp 88325522Snp Path stateDir = options.getStateDir(); 89325522Snp if (stateDir != null && !createIfMissing(options.getStateDir())) 90325522Snp return RC_FATAL; 91325522Snp 92325522Snp Path gensrc = options.getGenSrcDir(); 93325522Snp if (gensrc != null && !createIfMissing(gensrc)) 94325522Snp return RC_FATAL; 95325522Snp 96325522Snp Path hdrdir = options.getHeaderDir(); 97325522Snp if (hdrdir != null && !createIfMissing(hdrdir)) 98325522Snp return RC_FATAL; 99325522Snp 100321936Shselasky if (stateDir == null) { 101321936Shselasky // Prepare context. Direct logging to our byte array stream. 102321936Shselasky Context context = new Context(); 103321936Shselasky PrintWriter writer = new PrintWriter(err); 104321936Shselasky com.sun.tools.javac.util.Log.preRegister(context, writer); 105321936Shselasky JavacFileManager.preRegister(context); 106321936Shselasky 107321936Shselasky // Prepare arguments 108321936Shselasky String[] passThroughArgs = Stream.of(args) 109321936Shselasky .filter(arg -> !arg.startsWith(Option.SERVER.arg)) 110321936Shselasky .toArray(String[]::new); 111321936Shselasky 112321936Shselasky // Compile 113321936Shselasky com.sun.tools.javac.main.Main compiler = new com.sun.tools.javac.main.Main("javac", writer); 114321936Shselasky Main.Result result = compiler.compile(passThroughArgs, context); 115321936Shselasky 116321936Shselasky // Clean up 117321936Shselasky JavaFileManager fileManager = context.get(JavaFileManager.class); 118321936Shselasky if (fileManager instanceof JavacFileManager) { 119321936Shselasky try { 120321936Shselasky ((JavacFileManager) fileManager).close(); 121321936Shselasky } catch (IOException e) { 122321936Shselasky return RC_FATAL; 123321936Shselasky } 124321936Shselasky } 125321936Shselasky return result.exitCode; 126321936Shselasky 127321936Shselasky } else { 128321936Shselasky // Load the prev build state database. 129321936Shselasky JavacState javac_state = JavacState.load(options, out, err); 130321936Shselasky 131321936Shselasky // Setup the suffix rules from the command line. 132321936Shselasky Map<String, Transformer> suffixRules = new HashMap<>(); 133321936Shselasky 134321936Shselasky // Handling of .java-compilation 135321936Shselasky suffixRules.putAll(javac_state.getJavaSuffixRule()); 136321936Shselasky 137321936Shselasky // Handling of -copy and -tr 138321936Shselasky suffixRules.putAll(options.getTranslationRules()); 139321936Shselasky 140321936Shselasky // All found modules are put here. 141321936Shselasky Map<String,Module> modules = new HashMap<>(); 142321936Shselasky // We start out in the legacy empty no-name module. 143321936Shselasky // As soon as we stumble on a module-info.java file we change to that module. 144321936Shselasky Module current_module = new Module("", ""); 145321936Shselasky modules.put("", current_module); 146321936Shselasky 147321936Shselasky try { 148321936Shselasky // Find all sources, use the suffix rules to know which files are sources. 149321936Shselasky Map<String,Source> sources = new HashMap<>(); 150321936Shselasky 151321936Shselasky // Find the files, this will automatically populate the found modules 152321936Shselasky // with found packages where the sources are found! 153321936Shselasky findSourceFiles(options.getSources(), 154321936Shselasky suffixRules.keySet(), 155321936Shselasky sources, 156321936Shselasky modules, 157321936Shselasky current_module, 158321936Shselasky options.isDefaultPackagePermitted(), 159321936Shselasky false); 160321936Shselasky 161321936Shselasky if (sources.isEmpty()) { 162321936Shselasky Log.error("Found nothing to compile!"); 163321936Shselasky return RC_FATAL; 164321936Shselasky } 165321936Shselasky 166321936Shselasky 167321936Shselasky // Create a map of all source files that are available for linking. Both -src and 168321936Shselasky // -sourcepath point to such files. It is possible to specify multiple 169321936Shselasky // -sourcepath options to enable different filtering rules. If the 170321936Shselasky // filters are the same for multiple sourcepaths, they may be concatenated 171321936Shselasky // using :(;). Before sending the list of sourcepaths to javac, they are 172321936Shselasky // all concatenated. The list created here is used by the SmartFileWrapper to 173321936Shselasky // make sure only the correct sources are actually available. 174321936Shselasky // We might find more modules here as well. 175321936Shselasky Map<String,Source> sources_to_link_to = new HashMap<>(); 176321936Shselasky 177321936Shselasky List<SourceLocation> sourceResolutionLocations = new ArrayList<>(); 178321936Shselasky sourceResolutionLocations.addAll(options.getSources()); 179321936Shselasky sourceResolutionLocations.addAll(options.getSourceSearchPaths()); 180321936Shselasky findSourceFiles(sourceResolutionLocations, 181321936Shselasky Collections.singleton(".java"), 182321936Shselasky sources_to_link_to, 183321936Shselasky modules, 184321936Shselasky current_module, 185321936Shselasky options.isDefaultPackagePermitted(), 186321936Shselasky true); 187321936Shselasky 188321936Shselasky // Add the set of sources to the build database. 189321936Shselasky javac_state.now().flattenPackagesSourcesAndArtifacts(modules); 190321936Shselasky javac_state.now().checkInternalState("checking sources", false, sources); 191321936Shselasky javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to); 192321936Shselasky javac_state.setVisibleSources(sources_to_link_to); 193321936Shselasky 194321936Shselasky int round = 0; 195321936Shselasky printRound(round); 196321936Shselasky 197321936Shselasky // If there is any change in the source files, taint packages 198321936Shselasky // and mark the database in need of saving. 199321936Shselasky javac_state.checkSourceStatus(false); 200321936Shselasky 201321936Shselasky // Find all existing artifacts. Their timestamp will match the last modified timestamps stored 202321936Shselasky // in javac_state, simply because loading of the JavacState will clean out all artifacts 203321936Shselasky // that do not match the javac_state database. 204321936Shselasky javac_state.findAllArtifacts(); 205321936Shselasky 206321936Shselasky // Remove unidentified artifacts from the bin, gensrc and header dirs. 207321936Shselasky // (Unless we allow them to be there.) 208321936Shselasky // I.e. artifacts that are not known according to the build database (javac_state). 209321936Shselasky // For examples, files that have been manually copied into these dirs. 210321936Shselasky // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp 211321936Shselasky // in javac_state) have already been removed when the javac_state was loaded. 212321936Shselasky if (!options.areUnidentifiedArtifactsPermitted()) { 213321936Shselasky javac_state.removeUnidentifiedArtifacts(); 214321936Shselasky } 215321936Shselasky // Go through all sources and taint all packages that miss artifacts. 216321936Shselasky javac_state.taintPackagesThatMissArtifacts(); 217321936Shselasky 218321936Shselasky // Check recorded classpath public apis. Taint packages that depend on 219321936Shselasky // classpath classes whose public apis have changed. 220321936Shselasky javac_state.taintPackagesDependingOnChangedClasspathPackages(); 221321936Shselasky 222321936Shselasky // Now clean out all known artifacts belonging to tainted packages. 223321936Shselasky javac_state.deleteClassArtifactsInTaintedPackages(); 224321936Shselasky // Copy files, for example property files, images files, xml files etc etc. 225321936Shselasky javac_state.performCopying(Util.pathToFile(options.getDestDir()), suffixRules); 226321936Shselasky // Translate files, for example compile properties or compile idls. 227321936Shselasky javac_state.performTranslation(Util.pathToFile(gensrc), suffixRules); 228321936Shselasky // Add any potentially generated java sources to the tobe compiled list. 229321936Shselasky // (Generated sources must always have a package.) 230321936Shselasky Map<String,Source> generated_sources = new HashMap<>(); 231321936Shselasky 232321936Shselasky Source.scanRoot(Util.pathToFile(options.getGenSrcDir()), 233321936Shselasky Util.set(".java"), 234321936Shselasky Collections.emptyList(), 235321936Shselasky Collections.emptyList(), 236321936Shselasky generated_sources, 237321936Shselasky modules, 238321936Shselasky current_module, 239321936Shselasky false, 240321936Shselasky true, 241321936Shselasky false); 242321936Shselasky javac_state.now().flattenPackagesSourcesAndArtifacts(modules); 243321936Shselasky // Recheck the the source files and their timestamps again. 244321936Shselasky javac_state.checkSourceStatus(true); 245321936Shselasky 246321936Shselasky // Now do a safety check that the list of source files is identical 247321936Shselasky // to the list Make believes we are compiling. If we do not get this 248321936Shselasky // right, then incremental builds will fail with subtility. 249321936Shselasky // If any difference is detected, then we will fail hard here. 250321936Shselasky // This is an important safety net. 251321936Shselasky javac_state.compareWithMakefileList(Util.pathToFile(options.getSourceReferenceList())); 252321936Shselasky 253321936Shselasky // Do the compilations, repeatedly until no tainted packages exist. 254321936Shselasky boolean again; 255321936Shselasky // Collect the name of all compiled packages. 256321936Shselasky Set<String> recently_compiled = new HashSet<>(); 257321936Shselasky boolean[] rc = new boolean[1]; 258321936Shselasky 259321936Shselasky CompilationService compilationService = new CompilationService(); 260321936Shselasky do { 261321936Shselasky if (round > 0) 262321936Shselasky printRound(round); 263321936Shselasky // Clean out artifacts in tainted packages. 264321936Shselasky javac_state.deleteClassArtifactsInTaintedPackages(); 265321936Shselasky again = javac_state.performJavaCompilations(compilationService, 266321936Shselasky options, 267321936Shselasky recently_compiled, 268321936Shselasky rc); 269321936Shselasky if (!rc[0]) { 270321936Shselasky Log.debug("Compilation failed."); 271321936Shselasky break; 272321936Shselasky } 273321936Shselasky if (!again) { 274321936Shselasky Log.debug("Nothing left to do."); 275321936Shselasky } 276321936Shselasky round++; 277321936Shselasky } while (again); 278321936Shselasky Log.debug("No need to do another round."); 279321936Shselasky 280321936Shselasky // Only update the state if the compile went well. 281321936Shselasky if (rc[0]) { 282321936Shselasky javac_state.save(); 283321936Shselasky // Reflatten only the artifacts. 284321936Shselasky javac_state.now().flattenArtifacts(modules); 285321936Shselasky // Remove artifacts that were generated during the last compile, but not this one. 286321936Shselasky javac_state.removeSuperfluousArtifacts(recently_compiled); 287321936Shselasky } 288321936Shselasky 289321936Shselasky return rc[0] ? RC_OK : RC_FATAL; 290321936Shselasky } catch (ProblemException e) { 291321936Shselasky Log.error(e.getMessage()); 292321936Shselasky return RC_FATAL; 293321936Shselasky } catch (Exception e) { 294321936Shselasky e.printStackTrace(new PrintWriter(err)); 295321936Shselasky return RC_FATAL; 296321936Shselasky } 297321936Shselasky } 298321936Shselasky } 299321936Shselasky 300321936Shselasky @Override 301321936Shselasky public void shutdown() { 302321936Shselasky // Nothing to clean up 303321936Shselasky } 304321936Shselasky 305321936Shselasky private static boolean validateOptions(Options options) { 306321936Shselasky 307321936Shselasky String err = null; 308321936Shselasky 309321936Shselasky if (options.getDestDir() == null) { 310321936Shselasky err = "Please specify output directory."; 311321936Shselasky } else if (options.isJavaFilesAmongJavacArgs()) { 312321936Shselasky err = "Sjavac does not handle explicit compilation of single .java files."; 313321936Shselasky } else if (!options.getImplicitPolicy().equals("none")) { 314321936Shselasky err = "The only allowed setting for sjavac is -implicit:none"; 315321936Shselasky } else if (options.getSources().isEmpty() && options.getStateDir() != null) { 316321936Shselasky err = "You have to specify -src when using --state-dir."; 317321936Shselasky } else if (options.getTranslationRules().size() > 1 318321936Shselasky && options.getGenSrcDir() == null) { 319321936Shselasky err = "You have translators but no gensrc dir (-s) specified!"; 320321936Shselasky } 321321936Shselasky 322321936Shselasky if (err != null) 323321936Shselasky Log.error(err); 324321936Shselasky 325321936Shselasky return err == null; 326321936Shselasky 327321936Shselasky } 328321936Shselasky 329321936Shselasky private static boolean createIfMissing(Path dir) { 330321936Shselasky 331321936Shselasky if (Files.isDirectory(dir)) 332321936Shselasky return true; 333321936Shselasky 334321936Shselasky if (Files.exists(dir)) { 335321936Shselasky Log.error(dir + " is not a directory."); 336321936Shselasky return false; 337321936Shselasky } 338321936Shselasky 339321936Shselasky try { 340321936Shselasky Files.createDirectories(dir); 341321936Shselasky } catch (IOException e) { 342321936Shselasky Log.error("Could not create directory: " + e.getMessage()); 343321936Shselasky return false; 344321936Shselasky } 345321936Shselasky 346321936Shselasky return true; 347321936Shselasky } 348321936Shselasky 349321936Shselasky /** Find source files in the given source locations. */ 350321936Shselasky public static void findSourceFiles(List<SourceLocation> sourceLocations, 351321936Shselasky Set<String> sourceTypes, 352321936Shselasky Map<String,Source> foundFiles, 353321936Shselasky Map<String, Module> foundModules, 354321936Shselasky Module currentModule, 355321936Shselasky boolean permitSourcesInDefaultPackage, 356321936Shselasky boolean inLinksrc) 357321936Shselasky throws IOException { 358321936Shselasky 359321936Shselasky for (SourceLocation source : sourceLocations) { 360321936Shselasky source.findSourceFiles(sourceTypes, 361321936Shselasky foundFiles, 362321936Shselasky foundModules, 363321936Shselasky currentModule, 364321936Shselasky permitSourcesInDefaultPackage, 365321936Shselasky inLinksrc); 366321936Shselasky } 367321936Shselasky } 368321936Shselasky 369321936Shselasky private static void printRound(int round) { 370321936Shselasky Log.debug("****************************************"); 371321936Shselasky Log.debug("* Round " + round + " *"); 372321936Shselasky Log.debug("****************************************"); 373321936Shselasky } 374321936Shselasky} 375321936Shselasky