blob: 3553fcf9075dadc94e44a2f2e1b19606e6ea82b4 [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;
Andreas Lundblade85033c2016-03-22 13:14:12 +010044import com.sun.tools.javac.main.Main.Result;
Andreas Lundblad6238d402015-09-21 11:19:10 +020045import com.sun.tools.javac.util.Context;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020046import com.sun.tools.sjavac.JavacState;
Andreas Lundblad3a315932015-06-09 15:57:45 +020047import com.sun.tools.sjavac.Log;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020048import com.sun.tools.sjavac.Module;
49import com.sun.tools.sjavac.ProblemException;
50import com.sun.tools.sjavac.Source;
51import com.sun.tools.sjavac.Transformer;
Andreas Lundblad96074062014-10-07 21:15:10 +020052import com.sun.tools.sjavac.Util;
Andreas Lundblad6238d402015-09-21 11:19:10 +020053import com.sun.tools.sjavac.options.Option;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020054import com.sun.tools.sjavac.options.Options;
55import com.sun.tools.sjavac.options.SourceLocation;
Andreas Lundbladce4c4562014-08-13 14:44:59 +020056import com.sun.tools.sjavac.server.Sjavac;
Alan Bateman001ebb32016-03-17 19:04:28 +000057import java.io.UncheckedIOException;
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020058
Andreas Lundblad6238d402015-09-21 11:19:10 +020059import javax.tools.JavaFileManager;
60
Andreas Lundbladce4c4562014-08-13 14:44:59 +020061/**
62 * The sjavac implementation that interacts with javac and performs the actual
63 * compilation.
64 *
65 * <p><b>This is NOT part of any supported API.
66 * If you write code that depends on this, you do so at your own risk.
67 * This code and its internal interfaces are subject to change or
68 * deletion without notice.</b>
69 */
70public class SjavacImpl implements Sjavac {
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020071
72 @Override
Andreas Lundblade85033c2016-03-22 13:14:12 +010073 public Result compile(String[] args) {
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020074 Options options;
75 try {
76 options = Options.parseArgs(args);
77 } catch (IllegalArgumentException e) {
78 Log.error(e.getMessage());
Andreas Lundblade85033c2016-03-22 13:14:12 +010079 return Result.CMDERR;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020080 }
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020081
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020082 if (!validateOptions(options))
Andreas Lundblade85033c2016-03-22 13:14:12 +010083 return Result.CMDERR;
Andreas Lundblad8baafcf2014-06-17 14:01:27 +020084
Andreas Lundbladc685f352016-03-02 13:12:24 +010085 if (srcDstOverlap(options.getSources(), options.getDestDir())) {
Andreas Lundblade85033c2016-03-22 13:14:12 +010086 return Result.CMDERR;
Andreas Lundbladc685f352016-03-02 13:12:24 +010087 }
88
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020089 if (!createIfMissing(options.getDestDir()))
Andreas Lundblade85033c2016-03-22 13:14:12 +010090 return Result.ERROR;
Jonathan Gibbonsa75d2db2014-11-05 19:12:45 -080091
Andreas Lundblad6238d402015-09-21 11:19:10 +020092 Path stateDir = options.getStateDir();
93 if (stateDir != null && !createIfMissing(options.getStateDir()))
Andreas Lundblade85033c2016-03-22 13:14:12 +010094 return Result.ERROR;
Andreas Lundblad3a315932015-06-09 15:57:45 +020095
Andreas Lundblad3672dbc2015-08-25 15:14:41 +020096 Path gensrc = options.getGenSrcDir();
97 if (gensrc != null && !createIfMissing(gensrc))
Andreas Lundblade85033c2016-03-22 13:14:12 +010098 return Result.ERROR;
Jonathan Gibbonsa75d2db2014-11-05 19:12:45 -080099
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200100 Path hdrdir = options.getHeaderDir();
101 if (hdrdir != null && !createIfMissing(hdrdir))
Andreas Lundblade85033c2016-03-22 13:14:12 +0100102 return Result.ERROR;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200103
Andreas Lundblad6238d402015-09-21 11:19:10 +0200104 if (stateDir == null) {
105 // Prepare context. Direct logging to our byte array stream.
106 Context context = new Context();
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100107 StringWriter strWriter = new StringWriter();
108 PrintWriter printWriter = new PrintWriter(strWriter);
109 com.sun.tools.javac.util.Log.preRegister(context, printWriter);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200110 JavacFileManager.preRegister(context);
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200111
Andreas Lundblad6238d402015-09-21 11:19:10 +0200112 // Prepare arguments
113 String[] passThroughArgs = Stream.of(args)
114 .filter(arg -> !arg.startsWith(Option.SERVER.arg))
115 .toArray(String[]::new);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200116 // Compile
Andreas Lundblade85033c2016-03-22 13:14:12 +0100117 Result result = new Main("javac", printWriter).compile(passThroughArgs, context);
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100118
119 // Process compiler output (which is always errors)
120 printWriter.flush();
121 Util.getLines(strWriter.toString()).forEach(Log::error);
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200122
Andreas Lundblad6238d402015-09-21 11:19:10 +0200123 // Clean up
124 JavaFileManager fileManager = context.get(JavaFileManager.class);
125 if (fileManager instanceof JavacFileManager) {
Jonathan Gibbons31cdc1a2015-12-07 14:02:55 -0800126 try {
127 ((JavacFileManager) fileManager).close();
Alan Bateman001ebb32016-03-17 19:04:28 +0000128 } catch (IOException es) {
129 throw new UncheckedIOException(es);
Jonathan Gibbons31cdc1a2015-12-07 14:02:55 -0800130 }
Andreas Lundblad6238d402015-09-21 11:19:10 +0200131 }
Andreas Lundblade85033c2016-03-22 13:14:12 +0100132 return result;
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200133
Andreas Lundblad6238d402015-09-21 11:19:10 +0200134 } else {
135 // Load the prev build state database.
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100136 JavacState javac_state = JavacState.load(options);
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200137
Andreas Lundblad6238d402015-09-21 11:19:10 +0200138 // Setup the suffix rules from the command line.
139 Map<String, Transformer> suffixRules = new HashMap<>();
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200140
Andreas Lundblad6238d402015-09-21 11:19:10 +0200141 // Handling of .java-compilation
142 suffixRules.putAll(javac_state.getJavaSuffixRule());
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200143
Andreas Lundblad6238d402015-09-21 11:19:10 +0200144 // Handling of -copy and -tr
145 suffixRules.putAll(options.getTranslationRules());
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200146
Andreas Lundblad6238d402015-09-21 11:19:10 +0200147 // All found modules are put here.
148 Map<String,Module> modules = new HashMap<>();
149 // We start out in the legacy empty no-name module.
150 // As soon as we stumble on a module-info.java file we change to that module.
151 Module current_module = new Module("", "");
152 modules.put("", current_module);
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200153
Andreas Lundblad6238d402015-09-21 11:19:10 +0200154 try {
Andreas Lundbladb3455182016-01-08 17:14:10 +0100155 // Find all sources, use the suffix rules to know which files are sources.
156 Map<String,Source> sources = new HashMap<>();
157
158 // Find the files, this will automatically populate the found modules
159 // with found packages where the sources are found!
160 findSourceFiles(options.getSources(),
161 suffixRules.keySet(),
162 sources,
163 modules,
164 current_module,
165 options.isDefaultPackagePermitted(),
166 false);
167
168 if (sources.isEmpty()) {
169 Log.error("Found nothing to compile!");
Andreas Lundblade85033c2016-03-22 13:14:12 +0100170 return Result.ERROR;
Andreas Lundbladb3455182016-01-08 17:14:10 +0100171 }
172
173
174 // Create a map of all source files that are available for linking. Both -src and
175 // -sourcepath point to such files. It is possible to specify multiple
176 // -sourcepath options to enable different filtering rules. If the
177 // filters are the same for multiple sourcepaths, they may be concatenated
178 // using :(;). Before sending the list of sourcepaths to javac, they are
179 // all concatenated. The list created here is used by the SmartFileWrapper to
180 // make sure only the correct sources are actually available.
181 // We might find more modules here as well.
182 Map<String,Source> sources_to_link_to = new HashMap<>();
183
184 List<SourceLocation> sourceResolutionLocations = new ArrayList<>();
185 sourceResolutionLocations.addAll(options.getSources());
186 sourceResolutionLocations.addAll(options.getSourceSearchPaths());
187 findSourceFiles(sourceResolutionLocations,
188 Collections.singleton(".java"),
189 sources_to_link_to,
190 modules,
191 current_module,
192 options.isDefaultPackagePermitted(),
193 true);
194
195 // Add the set of sources to the build database.
196 javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
197 javac_state.now().checkInternalState("checking sources", false, sources);
198 javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
199 javac_state.setVisibleSources(sources_to_link_to);
200
201 int round = 0;
202 printRound(round);
203
204 // If there is any change in the source files, taint packages
205 // and mark the database in need of saving.
206 javac_state.checkSourceStatus(false);
207
208 // Find all existing artifacts. Their timestamp will match the last modified timestamps stored
209 // in javac_state, simply because loading of the JavacState will clean out all artifacts
210 // that do not match the javac_state database.
211 javac_state.findAllArtifacts();
212
213 // Remove unidentified artifacts from the bin, gensrc and header dirs.
214 // (Unless we allow them to be there.)
215 // I.e. artifacts that are not known according to the build database (javac_state).
216 // For examples, files that have been manually copied into these dirs.
217 // Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
218 // in javac_state) have already been removed when the javac_state was loaded.
219 if (!options.areUnidentifiedArtifactsPermitted()) {
220 javac_state.removeUnidentifiedArtifacts();
221 }
222 // Go through all sources and taint all packages that miss artifacts.
223 javac_state.taintPackagesThatMissArtifacts();
224
Andreas Lundblad7cf539d2015-11-03 21:29:46 +0100225 // Check recorded classpath public apis. Taint packages that depend on
226 // classpath classes whose public apis have changed.
227 javac_state.taintPackagesDependingOnChangedClasspathPackages();
228
229 // Now clean out all known artifacts belonging to tainted packages.
230 javac_state.deleteClassArtifactsInTaintedPackages();
231 // Copy files, for example property files, images files, xml files etc etc.
232 javac_state.performCopying(Util.pathToFile(options.getDestDir()), suffixRules);
233 // Translate files, for example compile properties or compile idls.
234 javac_state.performTranslation(Util.pathToFile(gensrc), suffixRules);
235 // Add any potentially generated java sources to the tobe compiled list.
236 // (Generated sources must always have a package.)
237 Map<String,Source> generated_sources = new HashMap<>();
Andreas Lundblad6238d402015-09-21 11:19:10 +0200238
Andreas Lundbladb3455182016-01-08 17:14:10 +0100239 Source.scanRoot(Util.pathToFile(options.getGenSrcDir()),
240 Util.set(".java"),
241 Collections.emptyList(),
242 Collections.emptyList(),
243 generated_sources,
244 modules,
245 current_module,
246 false,
247 true,
248 false);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200249 javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
250 // Recheck the the source files and their timestamps again.
251 javac_state.checkSourceStatus(true);
252
253 // Now do a safety check that the list of source files is identical
254 // to the list Make believes we are compiling. If we do not get this
255 // right, then incremental builds will fail with subtility.
256 // If any difference is detected, then we will fail hard here.
257 // This is an important safety net.
258 javac_state.compareWithMakefileList(Util.pathToFile(options.getSourceReferenceList()));
259
260 // Do the compilations, repeatedly until no tainted packages exist.
261 boolean again;
262 // Collect the name of all compiled packages.
263 Set<String> recently_compiled = new HashSet<>();
264 boolean[] rc = new boolean[1];
265
266 CompilationService compilationService = new CompilationService();
267 do {
268 if (round > 0)
269 printRound(round);
270 // Clean out artifacts in tainted packages.
271 javac_state.deleteClassArtifactsInTaintedPackages();
Andreas Lundbladb3455182016-01-08 17:14:10 +0100272 again = javac_state.performJavaCompilations(compilationService,
273 options,
274 recently_compiled,
275 rc);
Andreas Lundblad6238d402015-09-21 11:19:10 +0200276 if (!rc[0]) {
277 Log.debug("Compilation failed.");
278 break;
279 }
280 if (!again) {
281 Log.debug("Nothing left to do.");
282 }
283 round++;
284 } while (again);
285 Log.debug("No need to do another round.");
286
287 // Only update the state if the compile went well.
288 if (rc[0]) {
289 javac_state.save();
290 // Reflatten only the artifacts.
291 javac_state.now().flattenArtifacts(modules);
292 // Remove artifacts that were generated during the last compile, but not this one.
293 javac_state.removeSuperfluousArtifacts(recently_compiled);
294 }
295
Andreas Lundblade85033c2016-03-22 13:14:12 +0100296 return rc[0] ? Result.OK : Result.ERROR;
Andreas Lundblad6238d402015-09-21 11:19:10 +0200297 } catch (ProblemException e) {
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100298 // For instance make file list mismatch.
Andreas Lundblad6238d402015-09-21 11:19:10 +0200299 Log.error(e.getMessage());
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100300 Log.debug(e);
Andreas Lundblade85033c2016-03-22 13:14:12 +0100301 return Result.ERROR;
Andreas Lundblad6238d402015-09-21 11:19:10 +0200302 } catch (Exception e) {
Andreas Lundblad49850dd2016-02-29 13:24:01 +0100303 Log.error(e);
Andreas Lundblade85033c2016-03-22 13:14:12 +0100304 return Result.ERROR;
Andreas Lundblad6238d402015-09-21 11:19:10 +0200305 }
Andreas Lundblad0545e4b2014-10-07 21:21:42 +0200306 }
Andreas Lundblad8baafcf2014-06-17 14:01:27 +0200307 }
Fredrik Öhrströmc8256e42014-08-08 21:26:23 +0200308
309 @Override
Andreas Lundbladce4c4562014-08-13 14:44:59 +0200310 public void shutdown() {
311 // Nothing to clean up
Andreas Lundbladce4c4562014-08-13 14:44:59 +0200312 }
313
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200314 private static boolean validateOptions(Options options) {
Fredrik Öhrströmc8256e42014-08-08 21:26:23 +0200315
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200316 String err = null;
317
318 if (options.getDestDir() == null) {
319 err = "Please specify output directory.";
320 } else if (options.isJavaFilesAmongJavacArgs()) {
321 err = "Sjavac does not handle explicit compilation of single .java files.";
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200322 } else if (!options.getImplicitPolicy().equals("none")) {
323 err = "The only allowed setting for sjavac is -implicit:none";
Andreas Lundblad6238d402015-09-21 11:19:10 +0200324 } else if (options.getSources().isEmpty() && options.getStateDir() != null) {
325 err = "You have to specify -src when using --state-dir.";
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200326 } else if (options.getTranslationRules().size() > 1
327 && options.getGenSrcDir() == null) {
328 err = "You have translators but no gensrc dir (-s) specified!";
Andreas Lundblad3a315932015-06-09 15:57:45 +0200329 }
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200330
331 if (err != null)
332 Log.error(err);
333
334 return err == null;
335
336 }
337
Andreas Lundbladc685f352016-03-02 13:12:24 +0100338 private static boolean srcDstOverlap(List<SourceLocation> locs, Path dest) {
339 for (SourceLocation loc : locs) {
340 if (isOverlapping(loc.getPath(), dest)) {
341 Log.error("Source location " + loc.getPath() + " overlaps with destination " + dest);
342 return true;
343 }
344 }
345 return false;
346 }
347
348 private static boolean isOverlapping(Path p1, Path p2) {
349 p1 = p1.toAbsolutePath().normalize();
350 p2 = p2.toAbsolutePath().normalize();
351 return p1.startsWith(p2) || p2.startsWith(p1);
352 }
353
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200354 private static boolean createIfMissing(Path dir) {
355
356 if (Files.isDirectory(dir))
357 return true;
358
359 if (Files.exists(dir)) {
360 Log.error(dir + " is not a directory.");
361 return false;
362 }
363
364 try {
365 Files.createDirectories(dir);
366 } catch (IOException e) {
367 Log.error("Could not create directory: " + e.getMessage());
368 return false;
369 }
370
371 return true;
372 }
373
374 /** Find source files in the given source locations. */
375 public static void findSourceFiles(List<SourceLocation> sourceLocations,
376 Set<String> sourceTypes,
377 Map<String,Source> foundFiles,
378 Map<String, Module> foundModules,
379 Module currentModule,
380 boolean permitSourcesInDefaultPackage,
Andreas Lundbladb3455182016-01-08 17:14:10 +0100381 boolean inLinksrc)
382 throws IOException {
Andreas Lundblad3672dbc2015-08-25 15:14:41 +0200383
384 for (SourceLocation source : sourceLocations) {
385 source.findSourceFiles(sourceTypes,
386 foundFiles,
387 foundModules,
388 currentModule,
389 permitSourcesInDefaultPackage,
390 inLinksrc);
391 }
392 }
393
394 private static void printRound(int round) {
395 Log.debug("****************************************");
396 Log.debug("* Round " + round + " *");
397 Log.debug("****************************************");
Andreas Lundblad3a315932015-06-09 15:57:45 +0200398 }
Andreas Lundblad8baafcf2014-06-17 14:01:27 +0200399}