SjavacImpl.java revision 3012:adba44f6b471
127837Sdavidn/*
266830Sobrien * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
366830Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
466830Sobrien *
566830Sobrien * This code is free software; you can redistribute it and/or modify it
666830Sobrien * under the terms of the GNU General Public License version 2 only, as
766830Sobrien * published by the Free Software Foundation.  Oracle designates this
866830Sobrien * particular file as subject to the "Classpath" exception as provided
966830Sobrien * by Oracle in the LICENSE file that accompanied this code.
1066830Sobrien *
1166830Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT
1266830Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1366830Sobrien * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1466830Sobrien * version 2 for more details (a copy is included in the LICENSE file that
1566830Sobrien * accompanied this code).
1666830Sobrien *
1766830Sobrien * You should have received a copy of the GNU General Public License version
1866830Sobrien * 2 along with this work; if not, write to the Free Software Foundation,
1966830Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2066830Sobrien *
2166830Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2266830Sobrien * or visit www.oracle.com if you need additional information or have any
2366830Sobrien * questions.
2466830Sobrien */
2566830Sobrienpackage com.sun.tools.sjavac.comp;
2666830Sobrien
2750472Speterimport static com.sun.tools.sjavac.server.CompilationResult.ERROR_FATAL;
2866830Sobrienimport static java.nio.charset.StandardCharsets.UTF_8;
2927837Sdavidn
3051231Ssheldonhimport java.io.ByteArrayOutputStream;
3127837Sdavidnimport java.io.IOException;
3227837Sdavidnimport java.io.PrintStream;
3327837Sdavidnimport java.nio.file.Files;
3427837Sdavidnimport java.nio.file.Path;
3527837Sdavidnimport java.util.ArrayList;
3627837Sdavidnimport java.util.Collections;
3727837Sdavidnimport java.util.HashMap;
3827837Sdavidnimport java.util.HashSet;
3927837Sdavidnimport java.util.List;
4027837Sdavidnimport java.util.Map;
4127837Sdavidnimport java.util.Set;
4251231Ssheldonh
4327837Sdavidnimport com.sun.tools.sjavac.JavacState;
4451231Ssheldonhimport com.sun.tools.sjavac.Log;
4527837Sdavidnimport com.sun.tools.sjavac.Module;
46117324Smtmimport com.sun.tools.sjavac.ProblemException;
4762640Stgimport com.sun.tools.sjavac.Source;
48117324Smtmimport com.sun.tools.sjavac.Transformer;
4998189Sgordonimport com.sun.tools.sjavac.Util;
5096830Sgordonimport com.sun.tools.sjavac.options.Options;
5196830Sgordonimport com.sun.tools.sjavac.options.SourceLocation;
5296830Sgordonimport com.sun.tools.sjavac.server.CompilationResult;
5396830Sgordonimport com.sun.tools.sjavac.server.Sjavac;
5496830Sgordon
5596830Sgordon/**
5696830Sgordon * The sjavac implementation that interacts with javac and performs the actual
5796830Sgordon * compilation.
5896830Sgordon *
5996830Sgordon *  <p><b>This is NOT part of any supported API.
6096830Sgordon *  If you write code that depends on this, you do so at your own risk.
6196830Sgordon *  This code and its internal interfaces are subject to change or
62117324Smtm *  deletion without notice.</b>
63117324Smtm */
64117324Smtmpublic class SjavacImpl implements Sjavac {
6563307Smarkm
66117324Smtm    @Override
67117324Smtm    public CompilationResult compile(String[] args) {
68117324Smtm
69130151Sschweikh        ByteArrayOutputStream outBaos = new ByteArrayOutputStream();
70136615Sschweikh        ByteArrayOutputStream errBaos = new ByteArrayOutputStream();
71136615Sschweikh        PrintStream out = new PrintStream(outBaos);
72117324Smtm        PrintStream err = new PrintStream(errBaos);
73117324Smtm
74117324Smtm        Options options;
75117324Smtm        try {
76117324Smtm            options = Options.parseArgs(args);
77117324Smtm        } catch (IllegalArgumentException e) {
78117324Smtm            Log.error(e.getMessage());
7963307Smarkm            return new CompilationResult(ERROR_FATAL);
80117324Smtm        }
81117324Smtm
82117324Smtm        Log.setLogLevel(options.getLogLevel());
83138847Srse
84138847Srse        if (!validateOptions(options))
8553550Sdillon            return new CompilationResult(ERROR_FATAL);
86153027Sdougb
87153027Sdougb        if (!createIfMissing(options.getDestDir()))
88153027Sdougb            return new CompilationResult(ERROR_FATAL);
89153027Sdougb
90153027Sdougb        if (!createIfMissing(options.getStateDir()))
91153027Sdougb            return new CompilationResult(ERROR_FATAL);
92153027Sdougb
93117324Smtm        Path gensrc = options.getGenSrcDir();
94131135Smtm        if (gensrc != null && !createIfMissing(gensrc))
95131135Smtm            return new CompilationResult(ERROR_FATAL);
96117324Smtm
9727837Sdavidn        Path hdrdir = options.getHeaderDir();
98117324Smtm        if (hdrdir != null && !createIfMissing(hdrdir))
99117324Smtm            return new CompilationResult(ERROR_FATAL);
100117324Smtm
101117324Smtm        // Load the prev build state database.
102117324Smtm        JavacState javac_state = JavacState.load(options, out, err);
10327837Sdavidn
10486856Sdarrenr        // Setup the suffix rules from the command line.
10585219Sdarrenr        Map<String, Transformer> suffixRules = new HashMap<>();
10686856Sdarrenr
10727837Sdavidn        // Handling of .java-compilation
10827837Sdavidn        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);
252            }
253
254            return new CompilationResult(rc[0] ? 0 : ERROR_FATAL,
255                                          new String(outBaos.toByteArray(), UTF_8),
256                                          new String(errBaos.toByteArray(), UTF_8));
257        } catch (ProblemException e) {
258            Log.error(e.getMessage());
259            return new CompilationResult(ERROR_FATAL,
260                                          new String(outBaos.toByteArray(), UTF_8),
261                                          new String(errBaos.toByteArray(), UTF_8));
262        } catch (Exception e) {
263            e.printStackTrace(err);
264            return new CompilationResult(ERROR_FATAL,
265                                          new String(outBaos.toByteArray(), UTF_8),
266                                          new String(errBaos.toByteArray(), UTF_8));
267        }
268    }
269
270    @Override
271    public void shutdown() {
272        // Nothing to clean up
273    }
274
275    private static boolean validateOptions(Options options) {
276
277        String err = null;
278
279        if (options.getDestDir() == null) {
280            err = "Please specify output directory.";
281        } else if (options.isJavaFilesAmongJavacArgs()) {
282            err = "Sjavac does not handle explicit compilation of single .java files.";
283        } else if (options.getServerConf() == null) {
284            err = "No server configuration provided.";
285        } else if (!options.getImplicitPolicy().equals("none")) {
286            err = "The only allowed setting for sjavac is -implicit:none";
287        } else if (options.getSources().isEmpty()) {
288            err = "You have to specify -src.";
289        } else if (options.getTranslationRules().size() > 1
290                && options.getGenSrcDir() == null) {
291            err = "You have translators but no gensrc dir (-s) specified!";
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("****************************************");
344    }
345}
346