blob: 4c39a70f7791d6da32f1bc83984f6870d868e2f6 [file] [log] [blame]
Jonathan Gibbons444268e2014-07-14 17:25:53 -07001/*
Andreas Lundblad5cd3c7c2016-01-24 11:32:38 +01002 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
Jonathan Gibbons444268e2014-07-14 17:25:53 -07003 * 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 */
Vassili Igouchkine9c330782015-12-14 20:18:19 +010025
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020026package com.sun.tools.sjavac.comp;
27
Jonathan Gibbonsa75d2db2014-11-05 19:12:45 -080028import java.io.IOException;
Andreas Lundbladab159bb2015-09-04 13:24:15 +020029import java.io.PrintWriter;
Andreas Lundblad49850dd2016-02-29 13:24:01 +010030import java.io.StringWriter;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020031import java.nio.file.Files;
32import java.nio.file.Path;
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.HashMap;
36import java.util.HashSet;
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020037import java.util.List;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020038import java.util.Map;
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020039import java.util.Set;
Andreas Lundblad6238d402015-09-21 11:19:10 +020040import java.util.stream.Stream;
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020041
Andreas Lundblad6238d402015-09-21 11:19:10 +020042import com.sun.tools.javac.file.JavacFileManager;
43import com.sun.tools.javac.main.Main;
44import com.sun.tools.javac.util.Context;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020045import com.sun.tools.sjavac.JavacState;
Andreas Lundblad3a315932015-06-09 15:57:45 +020046import com.sun.tools.sjavac.Log;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020047import com.sun.tools.sjavac.Module;
48import com.sun.tools.sjavac.ProblemException;
49import com.sun.tools.sjavac.Source;
50import com.sun.tools.sjavac.Transformer;
Andreas Lundblad96074062014-10-07 21:15:10 +020051import com.sun.tools.sjavac.Util;
Andreas Lundblad6238d402015-09-21 11:19:10 +020052import com.sun.tools.sjavac.options.Option;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020053import com.sun.tools.sjavac.options.Options;
54import com.sun.tools.sjavac.options.SourceLocation;
Andreas Lundbladce4c4562014-08-13 14:44:59 +020055import com.sun.tools.sjavac.server.Sjavac;
Alan Bateman001ebb32016-03-17 19:04:28 +000056import java.io.UncheckedIOException;
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020057
Andreas Lundblad6238d402015-09-21 11:19:10 +020058import javax.tools.JavaFileManager;
59
Andreas Lundbladce4c4562014-08-13 14:44:59 +020060/**
61 * The sjavac implementation that interacts with javac and performs the actual
62 * compilation.
63 *
64 * <p><b>This is NOT part of any supported API.
65 * If you write code that depends on this, you do so at your own risk.
66 * This code and its internal interfaces are subject to change or
67 * deletion without notice.</b>
68 */
69public class SjavacImpl implements Sjavac {
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020070
71 @Override
Andreas Lundblad49850dd2016-02-29 13:24:01 +010072 public int compile(String[] args) {
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020073 Options options;
74 try {
75 options = Options.parseArgs(args);
76 } catch (IllegalArgumentException e) {
77 Log.error(e.getMessage());
Andreas Lundbladab159bb2015-09-04 13:24:15 +020078 return RC_FATAL;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020079 }
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020080
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020081 if (!validateOptions(options))
Andreas Lundbladab159bb2015-09-04 13:24:15 +020082 return RC_FATAL;
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020083
Andreas Lundbladc685f352016-03-02 13:12:24 +010084 if (srcDstOverlap(options.getSources(), options.getDestDir())) {
85 return RC_FATAL;
86 }
87
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020088 if (!createIfMissing(options.getDestDir()))
Andreas Lundbladab159bb2015-09-04 13:24:15 +020089 return RC_FATAL;
Jonathan Gibbonsa75d2db2014-11-05 19:12:45 -080090
Andreas Lundblad6238d402015-09-21 11:19:10 +020091 Path stateDir = options.getStateDir();
92 if (stateDir != null && !createIfMissing(options.getStateDir()))
Andreas Lundbladab159bb2015-09-04 13:24:15 +020093 return RC_FATAL;
Andreas Lundblad3a315932015-06-09 15:57:45 +020094
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020095 Path gensrc = options.getGenSrcDir();
96 if (gensrc != null && !createIfMissing(gensrc))
Andreas Lundbladab159bb2015-09-04 13:24:15 +020097 return RC_FATAL;
Jonathan Gibbonsa75d2db2014-11-05 19:12:45 -080098
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020099 Path hdrdir = options.getHeaderDir();
100 if (hdrdir != null && !createIfMissing(hdrdir))
Andreas Lundbladab159bb2015-09-04 13:24:15 +0200101 return RC_FATAL;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200102
Andreas Lundblad6238d402015-09-21 11:19:10 +0200103 if (stateDir == null) {
104 // Prepare context. Direct logging to our byte array stream.
105 Context context = new Context();
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100106 StringWriter strWriter = new StringWriter();
107 PrintWriter printWriter = new PrintWriter(strWriter);
108 com.sun.tools.javac.util.Log.preRegister(context, printWriter);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200109 JavacFileManager.preRegister(context);
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200110
Andreas Lundblad6238d402015-09-21 11:19:10 +0200111 // Prepare arguments
112 String[] passThroughArgs = Stream.of(args)
113 .filter(arg -> !arg.startsWith(Option.SERVER.arg))
114 .toArray(String[]::new);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200115 // Compile
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100116 Main.Result result = new Main("javac", printWriter).compile(passThroughArgs, context);
117
118 // Process compiler output (which is always errors)
119 printWriter.flush();
120 Util.getLines(strWriter.toString()).forEach(Log::error);
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200121
Andreas Lundblad6238d402015-09-21 11:19:10 +0200122 // Clean up
123 JavaFileManager fileManager = context.get(JavaFileManager.class);
124 if (fileManager instanceof JavacFileManager) {
Jonathan Gibbons31cdc1a2015-12-07 14:02:55 -0800125 try {
126 ((JavacFileManager) fileManager).close();
Alan Bateman001ebb32016-03-17 19:04:28 +0000127 } catch (IOException es) {
128 throw new UncheckedIOException(es);
Jonathan Gibbons31cdc1a2015-12-07 14:02:55 -0800129 }
Andreas Lundblad6238d402015-09-21 11:19:10 +0200130 }
131 return result.exitCode;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200132
Andreas Lundblad6238d402015-09-21 11:19:10 +0200133 } else {
134 // Load the prev build state database.
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100135 JavacState javac_state = JavacState.load(options);
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200136
Andreas Lundblad6238d402015-09-21 11:19:10 +0200137 // Setup the suffix rules from the command line.
138 Map<String, Transformer> suffixRules = new HashMap<>();
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200139
Andreas Lundblad6238d402015-09-21 11:19:10 +0200140 // Handling of .java-compilation
141 suffixRules.putAll(javac_state.getJavaSuffixRule());
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200142
Andreas Lundblad6238d402015-09-21 11:19:10 +0200143 // Handling of -copy and -tr
144 suffixRules.putAll(options.getTranslationRules());
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200145
Andreas Lundblad6238d402015-09-21 11:19:10 +0200146 // All found modules are put here.
147 Map<String,Module> modules = new HashMap<>();
148 // We start out in the legacy empty no-name module.
149 // As soon as we stumble on a module-info.java file we change to that module.
150 Module current_module = new Module("", "");
151 modules.put("", current_module);
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200152
Andreas Lundblad6238d402015-09-21 11:19:10 +0200153 try {
Andreas Lundbladb3455182016-01-08 17:14:10 +0100154 // Find all sources, use the suffix rules to know which files are sources.
155 Map<String,Source> sources = new HashMap<>();
156
157 // Find the files, this will automatically populate the found modules
158 // with found packages where the sources are found!
159 findSourceFiles(options.getSources(),
160 suffixRules.keySet(),
161 sources,
162 modules,
163 current_module,
164 options.isDefaultPackagePermitted(),
165 false);
166
167 if (sources.isEmpty()) {
168 Log.error("Found nothing to compile!");
169 return RC_FATAL;
170 }
171
172
173 // Create a map of all source files that are available for linking. Both -src and
174 // -sourcepath point to such files. It is possible to specify multiple
175 // -sourcepath options to enable different filtering rules. If the
176 // filters are the same for multiple sourcepaths, they may be concatenated
177 // using :(;). Before sending the list of sourcepaths to javac, they are
178 // all concatenated. The list created here is used by the SmartFileWrapper to
179 // make sure only the correct sources are actually available.
180 // We might find more modules here as well.
181 Map<String,Source> sources_to_link_to = new HashMap<>();
182
183 List<SourceLocation> sourceResolutionLocations = new ArrayList<>();
184 sourceResolutionLocations.addAll(options.getSources());
185 sourceResolutionLocations.addAll(options.getSourceSearchPaths());
186 findSourceFiles(sourceResolutionLocations,
187 Collections.singleton(".java"),
188 sources_to_link_to,
189 modules,
190 current_module,
191 options.isDefaultPackagePermitted(),
192 true);
193
194 // Add the set of sources to the build database.
195 javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
196 javac_state.now().checkInternalState("checking sources", false, sources);
197 javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
198 javac_state.setVisibleSources(sources_to_link_to);
199
200 int round = 0;
201 printRound(round);
202
203 // If there is any change in the source files, taint packages
204 // and mark the database in need of saving.
205 javac_state.checkSourceStatus(false);
206
207 // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
208 // in javac_state, simply because loading of the JavacState will clean out all artifacts
209 // that do not match the javac_state database.
210 javac_state.findAllArtifacts();
211
212 // Remove unidentified artifacts from the bin, gensrc and header dirs.
213 // (Unless we allow them to be there.)
214 // I.e. artifacts that are not known according to the build database (javac_state).
215 // For examples, files that have been manually copied into these dirs.
216 // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
217 // in javac_state) have already been removed when the javac_state was loaded.
218 if (!options.areUnidentifiedArtifactsPermitted()) {
219 javac_state.removeUnidentifiedArtifacts();
220 }
221 // Go through all sources and taint all packages that miss artifacts.
222 javac_state.taintPackagesThatMissArtifacts();
223
Andreas Lundblad7cf539d2015-11-03 21:29:46 +0100224 // Check recorded classpath public apis. Taint packages that depend on
225 // classpath classes whose public apis have changed.
226 javac_state.taintPackagesDependingOnChangedClasspathPackages();
227
228 // Now clean out all known artifacts belonging to tainted packages.
229 javac_state.deleteClassArtifactsInTaintedPackages();
230 // Copy files, for example property files, images files, xml files etc etc.
231 javac_state.performCopying(Util.pathToFile(options.getDestDir()), suffixRules);
232 // Translate files, for example compile properties or compile idls.
233 javac_state.performTranslation(Util.pathToFile(gensrc), suffixRules);
234 // Add any potentially generated java sources to the tobe compiled list.
235 // (Generated sources must always have a package.)
236 Map<String,Source> generated_sources = new HashMap<>();
Andreas Lundblad6238d402015-09-21 11:19:10 +0200237
Andreas Lundbladb3455182016-01-08 17:14:10 +0100238 Source.scanRoot(Util.pathToFile(options.getGenSrcDir()),
239 Util.set(".java"),
240 Collections.emptyList(),
241 Collections.emptyList(),
242 generated_sources,
243 modules,
244 current_module,
245 false,
246 true,
247 false);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200248 javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
249 // Recheck the the source files and their timestamps again.
250 javac_state.checkSourceStatus(true);
251
252 // Now do a safety check that the list of source files is identical
253 // to the list Make believes we are compiling. If we do not get this
254 // right, then incremental builds will fail with subtility.
255 // If any difference is detected, then we will fail hard here.
256 // This is an important safety net.
257 javac_state.compareWithMakefileList(Util.pathToFile(options.getSourceReferenceList()));
258
259 // Do the compilations, repeatedly until no tainted packages exist.
260 boolean again;
261 // Collect the name of all compiled packages.
262 Set<String> recently_compiled = new HashSet<>();
263 boolean[] rc = new boolean[1];
264
265 CompilationService compilationService = new CompilationService();
266 do {
267 if (round > 0)
268 printRound(round);
269 // Clean out artifacts in tainted packages.
270 javac_state.deleteClassArtifactsInTaintedPackages();
Andreas Lundbladb3455182016-01-08 17:14:10 +0100271 again = javac_state.performJavaCompilations(compilationService,
272 options,
273 recently_compiled,
274 rc);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200275 if (!rc[0]) {
276 Log.debug("Compilation failed.");
277 break;
278 }
279 if (!again) {
280 Log.debug("Nothing left to do.");
281 }
282 round++;
283 } while (again);
284 Log.debug("No need to do another round.");
285
286 // Only update the state if the compile went well.
287 if (rc[0]) {
288 javac_state.save();
289 // Reflatten only the artifacts.
290 javac_state.now().flattenArtifacts(modules);
291 // Remove artifacts that were generated during the last compile, but not this one.
292 javac_state.removeSuperfluousArtifacts(recently_compiled);
293 }
294
295 return rc[0] ? RC_OK : RC_FATAL;
296 } catch (ProblemException e) {
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100297 // For instance make file list mismatch.
Andreas Lundblad6238d402015-09-21 11:19:10 +0200298 Log.error(e.getMessage());
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100299 Log.debug(e);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200300 return RC_FATAL;
301 } catch (Exception e) {
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100302 Log.error(e);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200303 return RC_FATAL;
304 }
Andreas Lundblad0545e4b2014-10-07 21:21:42 +0200305 }
Andreas Lundblad8baafcf2014-06-17 14:01:27 +0200306 }
Fredrik Öhrströmc8256e42014-08-08 21:26:23 +0200307
308 @Override
Andreas Lundbladce4c4562014-08-13 14:44:59 +0200309 public void shutdown() {
310 // Nothing to clean up
Andreas Lundbladce4c4562014-08-13 14:44:59 +0200311 }
312
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200313 private static boolean validateOptions(Options options) {
Fredrik Öhrströmc8256e42014-08-08 21:26:23 +0200314
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200315 String err = null;
316
317 if (options.getDestDir() == null) {
318 err = "Please specify output directory.";
319 } else if (options.isJavaFilesAmongJavacArgs()) {
320 err = "Sjavac does not handle explicit compilation of single .java files.";
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200321 } else if (!options.getImplicitPolicy().equals("none")) {
322 err = "The only allowed setting for sjavac is -implicit:none";
Andreas Lundblad6238d402015-09-21 11:19:10 +0200323 } else if (options.getSources().isEmpty() && options.getStateDir() != null) {
324 err = "You have to specify -src when using --state-dir.";
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200325 } else if (options.getTranslationRules().size() > 1
326 && options.getGenSrcDir() == null) {
327 err = "You have translators but no gensrc dir (-s) specified!";
Andreas Lundblad3a315932015-06-09 15:57:45 +0200328 }
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200329
330 if (err != null)
331 Log.error(err);
332
333 return err == null;
334
335 }
336
Andreas Lundbladc685f352016-03-02 13:12:24 +0100337 private static boolean srcDstOverlap(List<SourceLocation> locs, Path dest) {
338 for (SourceLocation loc : locs) {
339 if (isOverlapping(loc.getPath(), dest)) {
340 Log.error("Source location " + loc.getPath() + " overlaps with destination " + dest);
341 return true;
342 }
343 }
344 return false;
345 }
346
347 private static boolean isOverlapping(Path p1, Path p2) {
348 p1 = p1.toAbsolutePath().normalize();
349 p2 = p2.toAbsolutePath().normalize();
350 return p1.startsWith(p2) || p2.startsWith(p1);
351 }
352
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200353 private static boolean createIfMissing(Path dir) {
354
355 if (Files.isDirectory(dir))
356 return true;
357
358 if (Files.exists(dir)) {
359 Log.error(dir + " is not a directory.");
360 return false;
361 }
362
363 try {
364 Files.createDirectories(dir);
365 } catch (IOException e) {
366 Log.error("Could not create directory: " + e.getMessage());
367 return false;
368 }
369
370 return true;
371 }
372
373 /** Find source files in the given source locations. */
374 public static void findSourceFiles(List<SourceLocation> sourceLocations,
375 Set<String> sourceTypes,
376 Map<String,Source> foundFiles,
377 Map<String, Module> foundModules,
378 Module currentModule,
379 boolean permitSourcesInDefaultPackage,
Andreas Lundbladb3455182016-01-08 17:14:10 +0100380 boolean inLinksrc)
381 throws IOException {
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200382
383 for (SourceLocation source : sourceLocations) {
384 source.findSourceFiles(sourceTypes,
385 foundFiles,
386 foundModules,
387 currentModule,
388 permitSourcesInDefaultPackage,
389 inLinksrc);
390 }
391 }
392
393 private static void printRound(int round) {
394 Log.debug("****************************************");
395 Log.debug("* Round " + round + " *");
396 Log.debug("****************************************");
Andreas Lundblad3a315932015-06-09 15:57:45 +0200397 }
Andreas Lundblad8baafcf2014-06-17 14:01:27 +0200398}