1 /* 2 * Copyright (c) 2014, 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 */ 25 26 package com.sun.tools.sjavac.client; 27 28 import java.io.IOException; 29 import java.io.PrintStream; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 40 import com.sun.tools.sjavac.JavacState; 41 import com.sun.tools.sjavac.Log; 42 import com.sun.tools.sjavac.Module; 43 import com.sun.tools.sjavac.ProblemException; 44 import com.sun.tools.sjavac.Source; 45 import com.sun.tools.sjavac.Transformer; 46 import com.sun.tools.sjavac.Util; 47 import com.sun.tools.sjavac.comp.PooledSjavac; 48 import com.sun.tools.sjavac.comp.SjavacImpl; 49 import com.sun.tools.sjavac.options.Options; 50 import com.sun.tools.sjavac.options.SourceLocation; 51 import com.sun.tools.sjavac.server.Sjavac; 52 53 /** 54 * <p><b>This is NOT part of any supported API. 55 * If you write code that depends on this, you do so at your own risk. 56 * This code and its internal interfaces are subject to change or 57 * deletion without notice.</b> 58 */ 59 public class ClientMain { 60 61 public static int run(String[] args) { 62 return run(args, System.out, System.err); 63 } 64 65 public static int run(String[] args, PrintStream out, PrintStream err) { 66 67 Log.initializeLog(out, err); 68 69 Options options; 70 try { 71 options = Options.parseArgs(args); 72 } catch (IllegalArgumentException e) { 73 Log.error(e.getMessage()); 74 return -1; 75 } 76 77 Log.setLogLevel(options.getLogLevel()); 78 79 if (!validateOptions(options)) 80 return -1; 81 82 if (!createIfMissing(options.getDestDir())) 83 return -1; 84 85 if (!createIfMissing(options.getStateDir())) 86 return -1; 87 88 Path gensrc = options.getGenSrcDir(); 89 if (gensrc != null && !createIfMissing(gensrc)) 90 return -1; 91 92 Path hdrdir = options.getHeaderDir(); 93 if (hdrdir != null && !createIfMissing(hdrdir)) 94 return -1; 95 96 Log.debug("=========================================================="); 97 Log.debug("Launching sjavac client with the following parameters:"); 98 Log.debug(" " + options.getStateArgsString()); 99 Log.debug("=========================================================="); 100 101 // Load the prev build state database. 102 JavacState javac_state = JavacState.load(options, out, err); 103 104 // Setup the suffix rules from the command line. 105 Map<String, Transformer> suffixRules = new HashMap<>(); 106 107 // Handling of .java-compilation 108 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 -1; 136 } 137 138 // Create a map of all source files that are available for linking. Both -src and 139 // -sourcepath point to such files. It is possible to specify multiple 140 // -sourcepath options to enable different filtering rules. If the 141 // filters are the same for multiple sourcepaths, they may be concatenated 142 // using :(;). Before sending the list of sourcepaths to javac, they are 143 // all concatenated. The list created here is used by the SmartFileWrapper to 144 // make sure only the correct sources are actually available. 145 // We might find more modules here as well. 146 Map<String,Source> sources_to_link_to = new HashMap<>(); 147 148 List<SourceLocation> sourceResolutionLocations = new ArrayList<>(); 149 sourceResolutionLocations.addAll(options.getSources()); 150 sourceResolutionLocations.addAll(options.getSourceSearchPaths()); 151 findSourceFiles(sourceResolutionLocations, 152 Collections.singleton(".java"), 153 sources_to_link_to, 154 modules, 155 current_module, 156 options.isDefaultPackagePermitted(), 157 true); 158 159 // Find all class files allowable for linking. 160 // And pickup knowledge of all modules found here. 161 // This cannot currently filter classes inside jar files. 162 // Map<String,Source> classes_to_link_to = new HashMap<String,Source>(); 163 // findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true); 164 165 // Find all module sources allowable for linking. 166 // Map<String,Source> modules_to_link_to = new HashMap<String,Source>(); 167 // findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true); 168 169 // Add the set of sources to the build database. 170 javac_state.now().flattenPackagesSourcesAndArtifacts(modules); 171 javac_state.now().checkInternalState("checking sources", false, sources); 172 javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to); 173 javac_state.setVisibleSources(sources_to_link_to); 174 175 int round = 0; 176 printRound(round); 177 178 // If there is any change in the source files, taint packages 179 // and mark the database in need of saving. 180 javac_state.checkSourceStatus(false); 181 182 // Find all existing artifacts. Their timestamp will match the last modified timestamps stored 183 // in javac_state, simply because loading of the JavacState will clean out all artifacts 184 // that do not match the javac_state database. 185 javac_state.findAllArtifacts(); 186 187 // Remove unidentified artifacts from the bin, gensrc and header dirs. 188 // (Unless we allow them to be there.) 189 // I.e. artifacts that are not known according to the build database (javac_state). 190 // For examples, files that have been manually copied into these dirs. 191 // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp 192 // in javac_state) have already been removed when the javac_state was loaded. 193 if (!options.areUnidentifiedArtifactsPermitted()) { 194 javac_state.removeUnidentifiedArtifacts(); 195 } 196 // Go through all sources and taint all packages that miss artifacts. 197 javac_state.taintPackagesThatMissArtifacts(); 198 199 // Check recorded classpath public apis. Taint packages that depend on 200 // classpath classes whose public apis have changed. 201 javac_state.taintPackagesDependingOnChangedClasspathPackages(); 202 203 // Now clean out all known artifacts belonging to tainted packages. 204 javac_state.deleteClassArtifactsInTaintedPackages(); 205 // Copy files, for example property files, images files, xml files etc etc. 206 javac_state.performCopying(Util.pathToFile(options.getDestDir()), suffixRules); 207 // Translate files, for example compile properties or compile idls. 208 javac_state.performTranslation(Util.pathToFile(gensrc), suffixRules); 209 // Add any potentially generated java sources to the tobe compiled list. 210 // (Generated sources must always have a package.) 211 Map<String,Source> generated_sources = new HashMap<>(); 212 213 try { 214 215 Source.scanRoot(Util.pathToFile(options.getGenSrcDir()), Util.set(".java"), null, null, null, null, 216 generated_sources, modules, current_module, false, true, false); 217 javac_state.now().flattenPackagesSourcesAndArtifacts(modules); 218 // Recheck the the source files and their timestamps again. 219 javac_state.checkSourceStatus(true); 220 221 // Now do a safety check that the list of source files is identical 222 // to the list Make believes we are compiling. If we do not get this 223 // right, then incremental builds will fail with subtility. 224 // If any difference is detected, then we will fail hard here. 225 // This is an important safety net. 226 javac_state.compareWithMakefileList(Util.pathToFile(options.getSourceReferenceList())); 227 228 // Do the compilations, repeatedly until no tainted packages exist. 229 boolean again; 230 // Collect the name of all compiled packages. 231 Set<String> recently_compiled = new HashSet<>(); 232 boolean[] rc = new boolean[1]; 233 boolean background = Util.extractBooleanOption("background", options.getServerConf(), true); 234 Sjavac sjavac; 235 // Create an sjavac implementation to be used for compilation 236 if (background) { 237 sjavac = new SjavacClient(options); 238 } else { 239 int poolsize = Util.extractIntOption("poolsize", options.getServerConf()); 240 if (poolsize <= 0) 241 poolsize = Runtime.getRuntime().availableProcessors(); 242 sjavac = new PooledSjavac(new SjavacImpl(), poolsize); 243 } 244 245 do { 246 if (round > 0) 247 printRound(round); 248 // Clean out artifacts in tainted packages. 249 javac_state.deleteClassArtifactsInTaintedPackages(); 250 again = javac_state.performJavaCompilations(sjavac, options, recently_compiled, rc); 251 if (!rc[0]) { 252 Log.debug("Compilation failed."); 253 break; 254 } 255 if (!again) { 256 Log.debug("Nothing left to do."); 257 } 258 round++; 259 } while (again); 260 Log.debug("No need to do another round."); 261 262 // Only update the state if the compile went well. 263 if (rc[0]) { 264 javac_state.save(); 265 // Reflatten only the artifacts. 266 javac_state.now().flattenArtifacts(modules); 267 // Remove artifacts that were generated during the last compile, but not this one. 268 javac_state.removeSuperfluousArtifacts(recently_compiled); 269 } 270 if (!background) 271 sjavac.shutdown(); 272 273 return rc[0] ? 0 : -1; 274 } catch (ProblemException e) { 275 Log.error(e.getMessage()); 276 return -1; 277 } catch (Exception e) { 278 e.printStackTrace(err); 279 return -1; 280 } 281 } 282 283 private static boolean validateOptions(Options options) { 284 285 String err = null; 286 287 if (options.getDestDir() == null) { 288 err = "Please specify output directory."; 289 } else if (options.isJavaFilesAmongJavacArgs()) { 290 err = "Sjavac does not handle explicit compilation of single .java files."; 291 } else if (options.getServerConf() == null) { 292 err = "No server configuration provided."; 293 } else if (!options.getImplicitPolicy().equals("none")) { 294 err = "The only allowed setting for sjavac is -implicit:none"; 295 } else if (options.getSources().isEmpty()) { 296 err = "You have to specify -src."; 297 } else if (options.getTranslationRules().size() > 1 298 && options.getGenSrcDir() == null) { 299 err = "You have translators but no gensrc dir (-s) specified!"; 300 } 301 302 if (err != null) 303 Log.error(err); 304 305 return err == null; 306 307 } 308 309 private static boolean createIfMissing(Path dir) { 310 311 if (Files.isDirectory(dir)) 312 return true; 313 314 if (Files.exists(dir)) { 315 Log.error(dir + " is not a directory."); 316 return false; 317 } 318 319 try { 320 Files.createDirectories(dir); 321 } catch (IOException e) { 322 Log.error("Could not create directory: " + e.getMessage()); 323 return false; 324 } 325 326 return true; 327 } 328 329 330 /** Find source files in the given source locations. */ 331 public static void findSourceFiles(List<SourceLocation> sourceLocations, 332 Set<String> sourceTypes, 333 Map<String,Source> foundFiles, 334 Map<String, Module> foundModules, 335 Module currentModule, 336 boolean permitSourcesInDefaultPackage, 337 boolean inLinksrc) { 338 339 for (SourceLocation source : sourceLocations) { 340 source.findSourceFiles(sourceTypes, 341 foundFiles, 342 foundModules, 343 currentModule, 344 permitSourcesInDefaultPackage, 345 inLinksrc); 346 } 347 } 348 349 private static void printRound(int round) { 350 Log.debug("****************************************"); 351 Log.debug("* Round " + round + " *"); 352 Log.debug("****************************************"); 353 } 354 355 }