langtools/src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/SjavacImpl.java
changeset 32335 7df616378cf3
parent 31115 8d8e98052d5d
child 32542 f4e4f4c4f9f4
equal deleted inserted replaced
32334:fd65e32e16b3 32335:7df616378cf3
    22  * or visit www.oracle.com if you need additional information or have any
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 package com.sun.tools.sjavac.comp;
    25 package com.sun.tools.sjavac.comp;
    26 
    26 
    27 import java.io.File;
    27 import static com.sun.tools.sjavac.server.CompilationResult.ERROR_FATAL;
       
    28 import static java.nio.charset.StandardCharsets.UTF_8;
       
    29 
       
    30 import java.io.ByteArrayOutputStream;
    28 import java.io.IOException;
    31 import java.io.IOException;
    29 import java.io.PrintWriter;
    32 import java.io.PrintStream;
    30 import java.io.StringWriter;
    33 import java.nio.file.Files;
    31 import java.net.URI;
    34 import java.nio.file.Path;
    32 import java.util.Arrays;
    35 import java.util.ArrayList;
    33 import java.util.Iterator;
    36 import java.util.Collections;
       
    37 import java.util.HashMap;
       
    38 import java.util.HashSet;
    34 import java.util.List;
    39 import java.util.List;
       
    40 import java.util.Map;
    35 import java.util.Set;
    41 import java.util.Set;
    36 
    42 
    37 import javax.tools.JavaFileObject;
    43 import com.sun.tools.sjavac.JavacState;
    38 import javax.tools.StandardJavaFileManager;
       
    39 import javax.tools.StandardLocation;
       
    40 import javax.tools.ToolProvider;
       
    41 
       
    42 import com.sun.tools.javac.api.JavacTaskImpl;
       
    43 import com.sun.tools.javac.api.JavacTool;
       
    44 import com.sun.tools.javac.util.Context;
       
    45 import com.sun.tools.javac.util.Dependencies;
       
    46 import com.sun.tools.javac.util.ListBuffer;
       
    47 import com.sun.tools.javac.util.Options;
       
    48 import com.sun.tools.sjavac.Log;
    44 import com.sun.tools.sjavac.Log;
       
    45 import com.sun.tools.sjavac.Module;
       
    46 import com.sun.tools.sjavac.ProblemException;
       
    47 import com.sun.tools.sjavac.Source;
       
    48 import com.sun.tools.sjavac.Transformer;
    49 import com.sun.tools.sjavac.Util;
    49 import com.sun.tools.sjavac.Util;
    50 import com.sun.tools.sjavac.comp.dependencies.NewDependencyCollector;
    50 import com.sun.tools.sjavac.options.Options;
    51 import com.sun.tools.sjavac.comp.dependencies.PublicApiCollector;
    51 import com.sun.tools.sjavac.options.SourceLocation;
    52 import com.sun.tools.sjavac.server.CompilationResult;
    52 import com.sun.tools.sjavac.server.CompilationResult;
    53 import com.sun.tools.sjavac.server.Sjavac;
    53 import com.sun.tools.sjavac.server.Sjavac;
    54 import com.sun.tools.sjavac.server.SysInfo;
       
    55 
    54 
    56 /**
    55 /**
    57  * The sjavac implementation that interacts with javac and performs the actual
    56  * The sjavac implementation that interacts with javac and performs the actual
    58  * compilation.
    57  * compilation.
    59  *
    58  *
    63  *  deletion without notice.</b>
    62  *  deletion without notice.</b>
    64  */
    63  */
    65 public class SjavacImpl implements Sjavac {
    64 public class SjavacImpl implements Sjavac {
    66 
    65 
    67     @Override
    66     @Override
    68     public SysInfo getSysInfo() {
    67     public CompilationResult compile(String[] args) {
    69         return new SysInfo(Runtime.getRuntime().availableProcessors(),
    68 
    70                            Runtime.getRuntime().maxMemory());
    69         ByteArrayOutputStream outBaos = new ByteArrayOutputStream();
    71     }
    70         ByteArrayOutputStream errBaos = new ByteArrayOutputStream();
    72 
    71         PrintStream out = new PrintStream(outBaos);
    73     @Override
    72         PrintStream err = new PrintStream(errBaos);
    74     public CompilationResult compile(String protocolId,
    73 
    75                                      String invocationId,
    74         Options options;
    76                                      String[] args,
    75         try {
    77                                      List<File> explicitSources,
    76             options = Options.parseArgs(args);
    78                                      Set<URI> sourcesToCompile,
    77         } catch (IllegalArgumentException e) {
    79                                      Set<URI> visibleSources) {
    78             Log.error(e.getMessage());
    80 
    79             return new CompilationResult(ERROR_FATAL);
    81         JavacTool compiler = (JavacTool) ToolProvider.getSystemJavaCompiler();
    80         }
    82         try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
    81 
    83             SmartFileManager sfm = new SmartFileManager(fm);
    82         Log.setLogLevel(options.getLogLevel());
    84             Context context = new Context();
    83 
    85 
    84         if (!validateOptions(options))
    86             Dependencies.GraphDependencies.preRegister(context);
    85             return new CompilationResult(ERROR_FATAL);
    87 
    86 
    88             // Now setup the actual compilation
    87         if (!createIfMissing(options.getDestDir()))
    89             CompilationResult compilationResult = new CompilationResult(0);
    88             return new CompilationResult(ERROR_FATAL);
    90 
    89 
    91             // First deal with explicit source files on cmdline and in at file
    90         if (!createIfMissing(options.getStateDir()))
    92             ListBuffer<JavaFileObject> explicitJFOs = new ListBuffer<>();
    91             return new CompilationResult(ERROR_FATAL);
    93             for (JavaFileObject jfo : fm.getJavaFileObjectsFromFiles(explicitSources)) {
    92 
    94                 explicitJFOs.append(SmartFileManager.locWrap(jfo, StandardLocation.SOURCE_PATH));
    93         Path gensrc = options.getGenSrcDir();
       
    94         if (gensrc != null && !createIfMissing(gensrc))
       
    95             return new CompilationResult(ERROR_FATAL);
       
    96 
       
    97         Path hdrdir = options.getHeaderDir();
       
    98         if (hdrdir != null && !createIfMissing(hdrdir))
       
    99             return new CompilationResult(ERROR_FATAL);
       
   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 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);
    95             }
   252             }
    96             // Now deal with sources supplied as source_to_compile
   253 
    97             ListBuffer<File> sourcesToCompileFiles = new ListBuffer<>();
   254             return new CompilationResult(rc[0] ? 0 : ERROR_FATAL,
    98             for (URI u : sourcesToCompile)
   255                                           new String(outBaos.toByteArray(), UTF_8),
    99                 sourcesToCompileFiles.append(new File(u));
   256                                           new String(errBaos.toByteArray(), UTF_8));
   100 
   257         } catch (ProblemException e) {
   101             for (JavaFileObject jfo : fm.getJavaFileObjectsFromFiles(sourcesToCompileFiles))
   258             Log.error(e.getMessage());
   102                 explicitJFOs.append(SmartFileManager.locWrap(jfo, StandardLocation.SOURCE_PATH));
   259             return new CompilationResult(ERROR_FATAL,
   103 
   260                                           new String(outBaos.toByteArray(), UTF_8),
   104             // Create a new logger
   261                                           new String(errBaos.toByteArray(), UTF_8));
   105             StringWriter stdoutLog = new StringWriter();
   262         } catch (Exception e) {
   106             StringWriter stderrLog = new StringWriter();
   263             e.printStackTrace(err);
   107             PrintWriter stdout = new PrintWriter(stdoutLog);
   264             return new CompilationResult(ERROR_FATAL,
   108             PrintWriter stderr = new PrintWriter(stderrLog);
   265                                           new String(outBaos.toByteArray(), UTF_8),
   109             com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK;
   266                                           new String(errBaos.toByteArray(), UTF_8));
   110             PublicApiCollector pubApiCollector = new PublicApiCollector(context, explicitJFOs);
       
   111             PathAndPackageVerifier papVerifier = new PathAndPackageVerifier();
       
   112             NewDependencyCollector depsCollector = new NewDependencyCollector(context, explicitJFOs);
       
   113             try {
       
   114                 if (explicitJFOs.size() > 0) {
       
   115                     sfm.setVisibleSources(visibleSources);
       
   116                     sfm.cleanArtifacts();
       
   117                     sfm.setLog(stdout);
       
   118 
       
   119                     // Do the compilation!
       
   120                     JavacTaskImpl task =
       
   121                             (JavacTaskImpl) compiler.getTask(stderr,
       
   122                                                              sfm,
       
   123                                                              null,
       
   124                                                              Arrays.asList(args),
       
   125                                                              null,
       
   126                                                              explicitJFOs,
       
   127                                                              context);
       
   128                     sfm.setSymbolFileEnabled(!Options.instance(context).isSet("ignore.symbol.file"));
       
   129                     task.addTaskListener(depsCollector);
       
   130                     task.addTaskListener(pubApiCollector);
       
   131                     task.addTaskListener(papVerifier);
       
   132                     logJavacInvocation(args);
       
   133                     rc = task.doCall();
       
   134                     Log.debug("javac returned with code " + rc);
       
   135                     sfm.flush();
       
   136                 }
       
   137             } catch (Exception e) {
       
   138                 Log.error(Util.getStackTrace(e));
       
   139                 stderrLog.append(Util.getStackTrace(e));
       
   140                 rc = com.sun.tools.javac.main.Main.Result.ERROR;
       
   141             }
       
   142 
       
   143             compilationResult.packageArtifacts = sfm.getPackageArtifacts();
       
   144 
       
   145             if (papVerifier.errorsDiscovered())
       
   146                 rc = com.sun.tools.javac.main.Main.Result.ERROR;
       
   147 
       
   148             compilationResult.packageDependencies = depsCollector.getDependencies(false);
       
   149             compilationResult.packageCpDependencies = depsCollector.getDependencies(true);
       
   150 
       
   151             compilationResult.packagePubapis = pubApiCollector.getPubApis(true);     // pubApis.getPubapis(explicitJFOs, true);
       
   152             compilationResult.dependencyPubapis = pubApiCollector.getPubApis(false); // pubApis.getPubapis(explicitJFOs, false);
       
   153             compilationResult.stdout = stdoutLog.toString();
       
   154             compilationResult.stderr = stderrLog.toString();
       
   155             compilationResult.returnCode = rc.exitCode;
       
   156 
       
   157             return compilationResult;
       
   158         } catch (IOException e) {
       
   159             throw new Error(e);
       
   160         }
   267         }
   161     }
   268     }
   162 
   269 
   163     @Override
   270     @Override
   164     public void shutdown() {
   271     public void shutdown() {
   165         // Nothing to clean up
   272         // Nothing to clean up
   166         // ... maybe we should wait for any current request to finish?
   273     }
   167     }
   274 
   168 
   275     private static boolean validateOptions(Options options) {
   169     @Override
   276 
   170     public String serverSettings() {
   277         String err = null;
   171         return "";
   278 
   172     }
   279         if (options.getDestDir() == null) {
   173 
   280             err = "Please specify output directory.";
   174     private void logJavacInvocation(String[] args) {
   281         } else if (options.isJavaFilesAmongJavacArgs()) {
   175         Log.debug("Invoking javac with args");
   282             err = "Sjavac does not handle explicit compilation of single .java files.";
   176         Iterator<String> argIter = Arrays.asList(args).iterator();
   283         } else if (options.getServerConf() == null) {
   177         while (argIter.hasNext()) {
   284             err = "No server configuration provided.";
   178             String arg = argIter.next();
   285         } else if (!options.getImplicitPolicy().equals("none")) {
   179             String line = "    " + arg;
   286             err = "The only allowed setting for sjavac is -implicit:none";
   180             if (arg.matches("\\-(d|cp|classpath|sourcepath|source|target)")
   287         } else if (options.getSources().isEmpty()) {
   181                     && argIter.hasNext()) {
   288             err = "You have to specify -src.";
   182                 line += " " + argIter.next();
   289         } else if (options.getTranslationRules().size() > 1
   183             }
   290                 && options.getGenSrcDir() == null) {
   184             Log.debug(line);
   291             err = "You have translators but no gensrc dir (-s) specified!";
   185         }
   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("****************************************");
   186     }
   344     }
   187 }
   345 }