SjavacImpl.java revision 3195:88a874f33d6d
1129198Scognet/* 2129198Scognet * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3129198Scognet * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4129198Scognet * 5129198Scognet * This code is free software; you can redistribute it and/or modify it 6239268Sgonzo * under the terms of the GNU General Public License version 2 only, as 7253768Scognet * published by the Free Software Foundation. Oracle designates this 8239268Sgonzo * particular file as subject to the "Classpath" exception as provided 9239268Sgonzo * by Oracle in the LICENSE file that accompanied this code. 10239268Sgonzo * 11239268Sgonzo * This code is distributed in the hope that it will be useful, but WITHOUT 12239268Sgonzo * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13266374Sian * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14239268Sgonzo * version 2 for more details (a copy is included in the LICENSE file that 15239268Sgonzo * accompanied this code). 16239268Sgonzo * 17239268Sgonzo * You should have received a copy of the GNU General Public License version 18266203Sian * 2 along with this work; if not, write to the Free Software Foundation, 19239268Sgonzo * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20239268Sgonzo * 21239268Sgonzo * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22239268Sgonzo * or visit www.oracle.com if you need additional information or have any 23239268Sgonzo * questions. 24239268Sgonzo */ 25239268Sgonzo 26239268Sgonzopackage com.sun.tools.sjavac.comp; 27239268Sgonzo 28239268Sgonzoimport java.io.IOException; 29239268Sgonzoimport java.io.PrintWriter; 30239268Sgonzoimport java.io.Writer; 31239268Sgonzoimport java.nio.file.Files; 32239268Sgonzoimport java.nio.file.Path; 33239268Sgonzoimport java.util.ArrayList; 34239268Sgonzoimport java.util.Collections; 35239268Sgonzoimport java.util.HashMap; 36239268Sgonzoimport java.util.HashSet; 37253762Scognetimport java.util.List; 38253762Scognetimport java.util.Map; 39253762Scognetimport java.util.Set; 40129198Scognetimport java.util.stream.Stream; 41 42import com.sun.tools.javac.file.JavacFileManager; 43import com.sun.tools.javac.main.Main; 44import com.sun.tools.javac.util.Context; 45import com.sun.tools.sjavac.JavacState; 46import com.sun.tools.sjavac.Log; 47import com.sun.tools.sjavac.Module; 48import com.sun.tools.sjavac.ProblemException; 49import com.sun.tools.sjavac.Source; 50import com.sun.tools.sjavac.Transformer; 51import com.sun.tools.sjavac.Util; 52import com.sun.tools.sjavac.options.Option; 53import com.sun.tools.sjavac.options.Options; 54import com.sun.tools.sjavac.options.SourceLocation; 55import com.sun.tools.sjavac.server.Sjavac; 56 57import javax.tools.JavaFileManager; 58 59/** 60 * The sjavac implementation that interacts with javac and performs the actual 61 * compilation. 62 * 63 * <p><b>This is NOT part of any supported API. 64 * If you write code that depends on this, you do so at your own risk. 65 * This code and its internal interfaces are subject to change or 66 * deletion without notice.</b> 67 */ 68public class SjavacImpl implements Sjavac { 69 70 @Override 71 public int compile(String[] args, Writer out, Writer err) { 72 Options options; 73 try { 74 options = Options.parseArgs(args); 75 } catch (IllegalArgumentException e) { 76 Log.error(e.getMessage()); 77 return RC_FATAL; 78 } 79 80 Log.setLogLevel(options.getLogLevel()); 81 82 if (!validateOptions(options)) 83 return RC_FATAL; 84 85 if (!createIfMissing(options.getDestDir())) 86 return RC_FATAL; 87 88 Path stateDir = options.getStateDir(); 89 if (stateDir != null && !createIfMissing(options.getStateDir())) 90 return RC_FATAL; 91 92 Path gensrc = options.getGenSrcDir(); 93 if (gensrc != null && !createIfMissing(gensrc)) 94 return RC_FATAL; 95 96 Path hdrdir = options.getHeaderDir(); 97 if (hdrdir != null && !createIfMissing(hdrdir)) 98 return RC_FATAL; 99 100 if (stateDir == null) { 101 // Prepare context. Direct logging to our byte array stream. 102 Context context = new Context(); 103 PrintWriter writer = new PrintWriter(err); 104 com.sun.tools.javac.util.Log.preRegister(context, writer); 105 JavacFileManager.preRegister(context); 106 107 // Prepare arguments 108 String[] passThroughArgs = Stream.of(args) 109 .filter(arg -> !arg.startsWith(Option.SERVER.arg)) 110 .toArray(String[]::new); 111 112 // Compile 113 com.sun.tools.javac.main.Main compiler = new com.sun.tools.javac.main.Main("javac", writer); 114 Main.Result result = compiler.compile(passThroughArgs, context); 115 116 // Clean up 117 JavaFileManager fileManager = context.get(JavaFileManager.class); 118 if (fileManager instanceof JavacFileManager) { 119 try { 120 ((JavacFileManager) fileManager).close(); 121 } catch (IOException e) { 122 return RC_FATAL; 123 } 124 } 125 return result.exitCode; 126 127 } else { 128 // Load the prev build state database. 129 JavacState javac_state = JavacState.load(options, out, err); 130 131 // Setup the suffix rules from the command line. 132 Map<String, Transformer> suffixRules = new HashMap<>(); 133 134 // Handling of .java-compilation 135 suffixRules.putAll(javac_state.getJavaSuffixRule()); 136 137 // Handling of -copy and -tr 138 suffixRules.putAll(options.getTranslationRules()); 139 140 // All found modules are put here. 141 Map<String,Module> modules = new HashMap<>(); 142 // We start out in the legacy empty no-name module. 143 // As soon as we stumble on a module-info.java file we change to that module. 144 Module current_module = new Module("", ""); 145 modules.put("", current_module); 146 147 try { 148 // Find all sources, use the suffix rules to know which files are sources. 149 Map<String,Source> sources = new HashMap<>(); 150 151 // Find the files, this will automatically populate the found modules 152 // with found packages where the sources are found! 153 findSourceFiles(options.getSources(), 154 suffixRules.keySet(), 155 sources, 156 modules, 157 current_module, 158 options.isDefaultPackagePermitted(), 159 false); 160 161 if (sources.isEmpty()) { 162 Log.error("Found nothing to compile!"); 163 return RC_FATAL; 164 } 165 166 167 // Create a map of all source files that are available for linking. Both -src and 168 // -sourcepath point to such files. It is possible to specify multiple 169 // -sourcepath options to enable different filtering rules. If the 170 // filters are the same for multiple sourcepaths, they may be concatenated 171 // using :(;). Before sending the list of sourcepaths to javac, they are 172 // all concatenated. The list created here is used by the SmartFileWrapper to 173 // make sure only the correct sources are actually available. 174 // We might find more modules here as well. 175 Map<String,Source> sources_to_link_to = new HashMap<>(); 176 177 List<SourceLocation> sourceResolutionLocations = new ArrayList<>(); 178 sourceResolutionLocations.addAll(options.getSources()); 179 sourceResolutionLocations.addAll(options.getSourceSearchPaths()); 180 findSourceFiles(sourceResolutionLocations, 181 Collections.singleton(".java"), 182 sources_to_link_to, 183 modules, 184 current_module, 185 options.isDefaultPackagePermitted(), 186 true); 187 188 // Add the set of sources to the build database. 189 javac_state.now().flattenPackagesSourcesAndArtifacts(modules); 190 javac_state.now().checkInternalState("checking sources", false, sources); 191 javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to); 192 javac_state.setVisibleSources(sources_to_link_to); 193 194 int round = 0; 195 printRound(round); 196 197 // If there is any change in the source files, taint packages 198 // and mark the database in need of saving. 199 javac_state.checkSourceStatus(false); 200 201 // Find all existing artifacts. Their timestamp will match the last modified timestamps stored 202 // in javac_state, simply because loading of the JavacState will clean out all artifacts 203 // that do not match the javac_state database. 204 javac_state.findAllArtifacts(); 205 206 // Remove unidentified artifacts from the bin, gensrc and header dirs. 207 // (Unless we allow them to be there.) 208 // I.e. artifacts that are not known according to the build database (javac_state). 209 // For examples, files that have been manually copied into these dirs. 210 // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp 211 // in javac_state) have already been removed when the javac_state was loaded. 212 if (!options.areUnidentifiedArtifactsPermitted()) { 213 javac_state.removeUnidentifiedArtifacts(); 214 } 215 // Go through all sources and taint all packages that miss artifacts. 216 javac_state.taintPackagesThatMissArtifacts(); 217 218 // Check recorded classpath public apis. Taint packages that depend on 219 // classpath classes whose public apis have changed. 220 javac_state.taintPackagesDependingOnChangedClasspathPackages(); 221 222 // Now clean out all known artifacts belonging to tainted packages. 223 javac_state.deleteClassArtifactsInTaintedPackages(); 224 // Copy files, for example property files, images files, xml files etc etc. 225 javac_state.performCopying(Util.pathToFile(options.getDestDir()), suffixRules); 226 // Translate files, for example compile properties or compile idls. 227 javac_state.performTranslation(Util.pathToFile(gensrc), suffixRules); 228 // Add any potentially generated java sources to the tobe compiled list. 229 // (Generated sources must always have a package.) 230 Map<String,Source> generated_sources = new HashMap<>(); 231 232 Source.scanRoot(Util.pathToFile(options.getGenSrcDir()), 233 Util.set(".java"), 234 Collections.emptyList(), 235 Collections.emptyList(), 236 generated_sources, 237 modules, 238 current_module, 239 false, 240 true, 241 false); 242 javac_state.now().flattenPackagesSourcesAndArtifacts(modules); 243 // Recheck the the source files and their timestamps again. 244 javac_state.checkSourceStatus(true); 245 246 // Now do a safety check that the list of source files is identical 247 // to the list Make believes we are compiling. If we do not get this 248 // right, then incremental builds will fail with subtility. 249 // If any difference is detected, then we will fail hard here. 250 // This is an important safety net. 251 javac_state.compareWithMakefileList(Util.pathToFile(options.getSourceReferenceList())); 252 253 // Do the compilations, repeatedly until no tainted packages exist. 254 boolean again; 255 // Collect the name of all compiled packages. 256 Set<String> recently_compiled = new HashSet<>(); 257 boolean[] rc = new boolean[1]; 258 259 CompilationService compilationService = new CompilationService(); 260 do { 261 if (round > 0) 262 printRound(round); 263 // Clean out artifacts in tainted packages. 264 javac_state.deleteClassArtifactsInTaintedPackages(); 265 again = javac_state.performJavaCompilations(compilationService, 266 options, 267 recently_compiled, 268 rc); 269 if (!rc[0]) { 270 Log.debug("Compilation failed."); 271 break; 272 } 273 if (!again) { 274 Log.debug("Nothing left to do."); 275 } 276 round++; 277 } while (again); 278 Log.debug("No need to do another round."); 279 280 // Only update the state if the compile went well. 281 if (rc[0]) { 282 javac_state.save(); 283 // Reflatten only the artifacts. 284 javac_state.now().flattenArtifacts(modules); 285 // Remove artifacts that were generated during the last compile, but not this one. 286 javac_state.removeSuperfluousArtifacts(recently_compiled); 287 } 288 289 return rc[0] ? RC_OK : RC_FATAL; 290 } catch (ProblemException e) { 291 Log.error(e.getMessage()); 292 return RC_FATAL; 293 } catch (Exception e) { 294 e.printStackTrace(new PrintWriter(err)); 295 return RC_FATAL; 296 } 297 } 298 } 299 300 @Override 301 public void shutdown() { 302 // Nothing to clean up 303 } 304 305 private static boolean validateOptions(Options options) { 306 307 String err = null; 308 309 if (options.getDestDir() == null) { 310 err = "Please specify output directory."; 311 } else if (options.isJavaFilesAmongJavacArgs()) { 312 err = "Sjavac does not handle explicit compilation of single .java files."; 313 } else if (options.getServerConf() == null) { 314 err = "No server configuration provided."; 315 } else if (!options.getImplicitPolicy().equals("none")) { 316 err = "The only allowed setting for sjavac is -implicit:none"; 317 } else if (options.getSources().isEmpty() && options.getStateDir() != null) { 318 err = "You have to specify -src when using --state-dir."; 319 } else if (options.getTranslationRules().size() > 1 320 && options.getGenSrcDir() == null) { 321 err = "You have translators but no gensrc dir (-s) specified!"; 322 } 323 324 if (err != null) 325 Log.error(err); 326 327 return err == null; 328 329 } 330 331 private static boolean createIfMissing(Path dir) { 332 333 if (Files.isDirectory(dir)) 334 return true; 335 336 if (Files.exists(dir)) { 337 Log.error(dir + " is not a directory."); 338 return false; 339 } 340 341 try { 342 Files.createDirectories(dir); 343 } catch (IOException e) { 344 Log.error("Could not create directory: " + e.getMessage()); 345 return false; 346 } 347 348 return true; 349 } 350 351 /** Find source files in the given source locations. */ 352 public static void findSourceFiles(List<SourceLocation> sourceLocations, 353 Set<String> sourceTypes, 354 Map<String,Source> foundFiles, 355 Map<String, Module> foundModules, 356 Module currentModule, 357 boolean permitSourcesInDefaultPackage, 358 boolean inLinksrc) 359 throws IOException { 360 361 for (SourceLocation source : sourceLocations) { 362 source.findSourceFiles(sourceTypes, 363 foundFiles, 364 foundModules, 365 currentModule, 366 permitSourcesInDefaultPackage, 367 inLinksrc); 368 } 369 } 370 371 private static void printRound(int round) { 372 Log.debug("****************************************"); 373 Log.debug("* Round " + round + " *"); 374 Log.debug("****************************************"); 375 } 376} 377