SjavacImpl.java revision 3012:adba44f6b471
127837Sdavidn/* 266830Sobrien * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 366830Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 466830Sobrien * 566830Sobrien * This code is free software; you can redistribute it and/or modify it 666830Sobrien * under the terms of the GNU General Public License version 2 only, as 766830Sobrien * published by the Free Software Foundation. Oracle designates this 866830Sobrien * particular file as subject to the "Classpath" exception as provided 966830Sobrien * by Oracle in the LICENSE file that accompanied this code. 1066830Sobrien * 1166830Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT 1266830Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1366830Sobrien * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1466830Sobrien * version 2 for more details (a copy is included in the LICENSE file that 1566830Sobrien * accompanied this code). 1666830Sobrien * 1766830Sobrien * You should have received a copy of the GNU General Public License version 1866830Sobrien * 2 along with this work; if not, write to the Free Software Foundation, 1966830Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2066830Sobrien * 2166830Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2266830Sobrien * or visit www.oracle.com if you need additional information or have any 2366830Sobrien * questions. 2466830Sobrien */ 2566830Sobrienpackage com.sun.tools.sjavac.comp; 2666830Sobrien 2750472Speterimport static com.sun.tools.sjavac.server.CompilationResult.ERROR_FATAL; 2866830Sobrienimport static java.nio.charset.StandardCharsets.UTF_8; 2927837Sdavidn 3051231Ssheldonhimport java.io.ByteArrayOutputStream; 3127837Sdavidnimport java.io.IOException; 3227837Sdavidnimport java.io.PrintStream; 3327837Sdavidnimport java.nio.file.Files; 3427837Sdavidnimport java.nio.file.Path; 3527837Sdavidnimport java.util.ArrayList; 3627837Sdavidnimport java.util.Collections; 3727837Sdavidnimport java.util.HashMap; 3827837Sdavidnimport java.util.HashSet; 3927837Sdavidnimport java.util.List; 4027837Sdavidnimport java.util.Map; 4127837Sdavidnimport java.util.Set; 4251231Ssheldonh 4327837Sdavidnimport com.sun.tools.sjavac.JavacState; 4451231Ssheldonhimport com.sun.tools.sjavac.Log; 4527837Sdavidnimport com.sun.tools.sjavac.Module; 46117324Smtmimport com.sun.tools.sjavac.ProblemException; 4762640Stgimport com.sun.tools.sjavac.Source; 48117324Smtmimport com.sun.tools.sjavac.Transformer; 4998189Sgordonimport com.sun.tools.sjavac.Util; 5096830Sgordonimport com.sun.tools.sjavac.options.Options; 5196830Sgordonimport com.sun.tools.sjavac.options.SourceLocation; 5296830Sgordonimport com.sun.tools.sjavac.server.CompilationResult; 5396830Sgordonimport com.sun.tools.sjavac.server.Sjavac; 5496830Sgordon 5596830Sgordon/** 5696830Sgordon * The sjavac implementation that interacts with javac and performs the actual 5796830Sgordon * compilation. 5896830Sgordon * 5996830Sgordon * <p><b>This is NOT part of any supported API. 6096830Sgordon * If you write code that depends on this, you do so at your own risk. 6196830Sgordon * This code and its internal interfaces are subject to change or 62117324Smtm * deletion without notice.</b> 63117324Smtm */ 64117324Smtmpublic class SjavacImpl implements Sjavac { 6563307Smarkm 66117324Smtm @Override 67117324Smtm public CompilationResult compile(String[] args) { 68117324Smtm 69130151Sschweikh ByteArrayOutputStream outBaos = new ByteArrayOutputStream(); 70136615Sschweikh ByteArrayOutputStream errBaos = new ByteArrayOutputStream(); 71136615Sschweikh PrintStream out = new PrintStream(outBaos); 72117324Smtm PrintStream err = new PrintStream(errBaos); 73117324Smtm 74117324Smtm Options options; 75117324Smtm try { 76117324Smtm options = Options.parseArgs(args); 77117324Smtm } catch (IllegalArgumentException e) { 78117324Smtm Log.error(e.getMessage()); 7963307Smarkm return new CompilationResult(ERROR_FATAL); 80117324Smtm } 81117324Smtm 82117324Smtm Log.setLogLevel(options.getLogLevel()); 83138847Srse 84138847Srse if (!validateOptions(options)) 8553550Sdillon return new CompilationResult(ERROR_FATAL); 86153027Sdougb 87153027Sdougb if (!createIfMissing(options.getDestDir())) 88153027Sdougb return new CompilationResult(ERROR_FATAL); 89153027Sdougb 90153027Sdougb if (!createIfMissing(options.getStateDir())) 91153027Sdougb return new CompilationResult(ERROR_FATAL); 92153027Sdougb 93117324Smtm Path gensrc = options.getGenSrcDir(); 94131135Smtm if (gensrc != null && !createIfMissing(gensrc)) 95131135Smtm return new CompilationResult(ERROR_FATAL); 96117324Smtm 9727837Sdavidn Path hdrdir = options.getHeaderDir(); 98117324Smtm if (hdrdir != null && !createIfMissing(hdrdir)) 99117324Smtm return new CompilationResult(ERROR_FATAL); 100117324Smtm 101117324Smtm // Load the prev build state database. 102117324Smtm JavacState javac_state = JavacState.load(options, out, err); 10327837Sdavidn 10486856Sdarrenr // Setup the suffix rules from the command line. 10585219Sdarrenr Map<String, Transformer> suffixRules = new HashMap<>(); 10686856Sdarrenr 10727837Sdavidn // Handling of .java-compilation 10827837Sdavidn suffixRules.putAll(javac_state.getJavaSuffixRule()); 109 110 // Handling of -copy and -tr 111 suffixRules.putAll(options.getTranslationRules()); 112 113 // All found modules are put here. 114 Map<String,Module> modules = new HashMap<>(); 115 // We start out in the legacy empty no-name module. 116 // As soon as we stumble on a module-info.java file we change to that module. 117 Module current_module = new Module("", ""); 118 modules.put("", current_module); 119 120 // Find all sources, use the suffix rules to know which files are sources. 121 Map<String,Source> sources = new HashMap<>(); 122 123 // Find the files, this will automatically populate the found modules 124 // with found packages where the sources are found! 125 findSourceFiles(options.getSources(), 126 suffixRules.keySet(), 127 sources, 128 modules, 129 current_module, 130 options.isDefaultPackagePermitted(), 131 false); 132 133 if (sources.isEmpty()) { 134 Log.error("Found nothing to compile!"); 135 return new CompilationResult(CompilationResult.ERROR_FATAL, 136 new String(outBaos.toByteArray(), UTF_8), 137 new String(errBaos.toByteArray(), UTF_8)); 138 } 139 140 141 // Create a map of all source files that are available for linking. Both -src and 142 // -sourcepath point to such files. It is possible to specify multiple 143 // -sourcepath options to enable different filtering rules. If the 144 // filters are the same for multiple sourcepaths, they may be concatenated 145 // using :(;). Before sending the list of sourcepaths to javac, they are 146 // all concatenated. The list created here is used by the SmartFileWrapper to 147 // make sure only the correct sources are actually available. 148 // We might find more modules here as well. 149 Map<String,Source> sources_to_link_to = new HashMap<>(); 150 151 List<SourceLocation> sourceResolutionLocations = new ArrayList<>(); 152 sourceResolutionLocations.addAll(options.getSources()); 153 sourceResolutionLocations.addAll(options.getSourceSearchPaths()); 154 findSourceFiles(sourceResolutionLocations, 155 Collections.singleton(".java"), 156 sources_to_link_to, 157 modules, 158 current_module, 159 options.isDefaultPackagePermitted(), 160 true); 161 162 // Add the set of sources to the build database. 163 javac_state.now().flattenPackagesSourcesAndArtifacts(modules); 164 javac_state.now().checkInternalState("checking sources", false, sources); 165 javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to); 166 javac_state.setVisibleSources(sources_to_link_to); 167 168 int round = 0; 169 printRound(round); 170 171 // If there is any change in the source files, taint packages 172 // and mark the database in need of saving. 173 javac_state.checkSourceStatus(false); 174 175 // Find all existing artifacts. Their timestamp will match the last modified timestamps stored 176 // in javac_state, simply because loading of the JavacState will clean out all artifacts 177 // that do not match the javac_state database. 178 javac_state.findAllArtifacts(); 179 180 // Remove unidentified artifacts from the bin, gensrc and header dirs. 181 // (Unless we allow them to be there.) 182 // I.e. artifacts that are not known according to the build database (javac_state). 183 // For examples, files that have been manually copied into these dirs. 184 // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp 185 // in javac_state) have already been removed when the javac_state was loaded. 186 if (!options.areUnidentifiedArtifactsPermitted()) { 187 javac_state.removeUnidentifiedArtifacts(); 188 } 189 // Go through all sources and taint all packages that miss artifacts. 190 javac_state.taintPackagesThatMissArtifacts(); 191 192 // Check recorded classpath public apis. Taint packages that depend on 193 // classpath classes whose public apis have changed. 194 javac_state.taintPackagesDependingOnChangedClasspathPackages(); 195 196 // Now clean out all known artifacts belonging to tainted packages. 197 javac_state.deleteClassArtifactsInTaintedPackages(); 198 // Copy files, for example property files, images files, xml files etc etc. 199 javac_state.performCopying(Util.pathToFile(options.getDestDir()), suffixRules); 200 // Translate files, for example compile properties or compile idls. 201 javac_state.performTranslation(Util.pathToFile(gensrc), suffixRules); 202 // Add any potentially generated java sources to the tobe compiled list. 203 // (Generated sources must always have a package.) 204 Map<String,Source> generated_sources = new HashMap<>(); 205 206 try { 207 208 Source.scanRoot(Util.pathToFile(options.getGenSrcDir()), Util.set(".java"), null, null, null, null, 209 generated_sources, modules, current_module, false, true, false); 210 javac_state.now().flattenPackagesSourcesAndArtifacts(modules); 211 // Recheck the the source files and their timestamps again. 212 javac_state.checkSourceStatus(true); 213 214 // Now do a safety check that the list of source files is identical 215 // to the list Make believes we are compiling. If we do not get this 216 // right, then incremental builds will fail with subtility. 217 // If any difference is detected, then we will fail hard here. 218 // This is an important safety net. 219 javac_state.compareWithMakefileList(Util.pathToFile(options.getSourceReferenceList())); 220 221 // Do the compilations, repeatedly until no tainted packages exist. 222 boolean again; 223 // Collect the name of all compiled packages. 224 Set<String> recently_compiled = new HashSet<>(); 225 boolean[] rc = new boolean[1]; 226 227 CompilationService compilationService = new CompilationService(); 228 do { 229 if (round > 0) 230 printRound(round); 231 // Clean out artifacts in tainted packages. 232 javac_state.deleteClassArtifactsInTaintedPackages(); 233 again = javac_state.performJavaCompilations(compilationService, options, recently_compiled, rc); 234 if (!rc[0]) { 235 Log.debug("Compilation failed."); 236 break; 237 } 238 if (!again) { 239 Log.debug("Nothing left to do."); 240 } 241 round++; 242 } while (again); 243 Log.debug("No need to do another round."); 244 245 // Only update the state if the compile went well. 246 if (rc[0]) { 247 javac_state.save(); 248 // Reflatten only the artifacts. 249 javac_state.now().flattenArtifacts(modules); 250 // Remove artifacts that were generated during the last compile, but not this one. 251 javac_state.removeSuperfluousArtifacts(recently_compiled); 252 } 253 254 return new CompilationResult(rc[0] ? 0 : ERROR_FATAL, 255 new String(outBaos.toByteArray(), UTF_8), 256 new String(errBaos.toByteArray(), UTF_8)); 257 } catch (ProblemException e) { 258 Log.error(e.getMessage()); 259 return new CompilationResult(ERROR_FATAL, 260 new String(outBaos.toByteArray(), UTF_8), 261 new String(errBaos.toByteArray(), UTF_8)); 262 } catch (Exception e) { 263 e.printStackTrace(err); 264 return new CompilationResult(ERROR_FATAL, 265 new String(outBaos.toByteArray(), UTF_8), 266 new String(errBaos.toByteArray(), UTF_8)); 267 } 268 } 269 270 @Override 271 public void shutdown() { 272 // Nothing to clean up 273 } 274 275 private static boolean validateOptions(Options options) { 276 277 String err = null; 278 279 if (options.getDestDir() == null) { 280 err = "Please specify output directory."; 281 } else if (options.isJavaFilesAmongJavacArgs()) { 282 err = "Sjavac does not handle explicit compilation of single .java files."; 283 } else if (options.getServerConf() == null) { 284 err = "No server configuration provided."; 285 } else if (!options.getImplicitPolicy().equals("none")) { 286 err = "The only allowed setting for sjavac is -implicit:none"; 287 } else if (options.getSources().isEmpty()) { 288 err = "You have to specify -src."; 289 } else if (options.getTranslationRules().size() > 1 290 && options.getGenSrcDir() == null) { 291 err = "You have translators but no gensrc dir (-s) specified!"; 292 } 293 294 if (err != null) 295 Log.error(err); 296 297 return err == null; 298 299 } 300 301 private static boolean createIfMissing(Path dir) { 302 303 if (Files.isDirectory(dir)) 304 return true; 305 306 if (Files.exists(dir)) { 307 Log.error(dir + " is not a directory."); 308 return false; 309 } 310 311 try { 312 Files.createDirectories(dir); 313 } catch (IOException e) { 314 Log.error("Could not create directory: " + e.getMessage()); 315 return false; 316 } 317 318 return true; 319 } 320 321 /** Find source files in the given source locations. */ 322 public static void findSourceFiles(List<SourceLocation> sourceLocations, 323 Set<String> sourceTypes, 324 Map<String,Source> foundFiles, 325 Map<String, Module> foundModules, 326 Module currentModule, 327 boolean permitSourcesInDefaultPackage, 328 boolean inLinksrc) { 329 330 for (SourceLocation source : sourceLocations) { 331 source.findSourceFiles(sourceTypes, 332 foundFiles, 333 foundModules, 334 currentModule, 335 permitSourcesInDefaultPackage, 336 inLinksrc); 337 } 338 } 339 340 private static void printRound(int round) { 341 Log.debug("****************************************"); 342 Log.debug("* Round " + round + " *"); 343 Log.debug("****************************************"); 344 } 345} 346