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 } |