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 }