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