Skip to content

Commit 95d8f7d

Browse files
Add support for Annotation Processors in the Javac Extension (#3686)
2 parents 9b13d38 + 73d9619 commit 95d8f7d

8 files changed

Lines changed: 427 additions & 57 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ bin/
5151
.settings
5252
.project
5353
.classpath
54+
.factorypath
5455

5556
# Files generated by IntelliJ ANTLR plugin
5657
key.core/src/main/gen

key.core/src/main/java/de/uka/ilkd/key/settings/Configuration.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,6 @@ public ConfigurationWriter printComment(@Nullable String comment) {
508508
if (comment == null) {
509509
return this;
510510
}
511-
512511
if (comment.contains("\n")) {
513512
out.format("/* %s */\n", comment);
514513
} else {

key.ui/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ run {
8383

8484
// this can be used to solve a problem where the OS hangs during debugging of popup menus
8585
// (see https://docs.oracle.com/javase/10/troubleshoot/awt.htm#JSTGD425)
86-
jvmArgs += "-Dsun.awt.disablegrab=true"
86+
jvmArgs += "-Dsun.awt.disablegrab=true "
8787
}
8888

8989

key.ui/src/main/java/de/uka/ilkd/key/gui/plugins/javac/JavaCompilerCheckFacade.java

Lines changed: 176 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
* SPDX-License-Identifier: GPL-2.0-only */
44
package de.uka.ilkd.key.gui.plugins.javac;
55

6-
import java.io.File;
7-
import java.io.IOException;
8-
import java.io.OutputStream;
9-
import java.io.StringWriter;
6+
import java.io.*;
107
import java.net.URI;
118
import java.net.URISyntaxException;
129
import java.nio.charset.Charset;
1310
import java.nio.file.Files;
1411
import java.nio.file.Path;
12+
import java.nio.file.Paths;
1513
import java.util.*;
1614
import java.util.concurrent.CompletableFuture;
1715
import java.util.stream.Collectors;
@@ -20,8 +18,11 @@
2018
import de.uka.ilkd.key.gui.PositionedIssueString;
2119
import de.uka.ilkd.key.java.Position;
2220
import de.uka.ilkd.key.parser.Location;
23-
import de.uka.ilkd.key.proof.init.ProblemInitializer;
21+
import de.uka.ilkd.key.settings.Configuration;
2422

23+
import org.key_project.util.Streams;
24+
25+
import org.antlr.v4.runtime.CharStreams;
2526
import org.jspecify.annotations.NonNull;
2627
import org.slf4j.Logger;
2728
import org.slf4j.LoggerFactory;
@@ -33,8 +34,6 @@
3334
* <p>
3435
* For setting up <code>javac</code> it uses the KeY project information about the bootpath and
3536
* classpath.
36-
* Any issues found in the compilation are reported to a provided listener of type
37-
* {@link ProblemInitializer.ProblemInitializerListener}.
3837
* <p>
3938
* Checking the target Java code can be enabled / disabled by providing the property
4039
* <code>-PKEY_JAVAC_DISABLE=true</code> / <code>-PKEY_JAVAC_DISABLE=false</code> on startup of KeY.
@@ -43,24 +42,146 @@
4342
* @version 1 (14.10.22)
4443
*/
4544
public class JavaCompilerCheckFacade {
45+
private JavaCompilerCheckFacade() {
46+
/* This utility class should not be instantiated */
47+
}
48+
4649
private static final Logger LOGGER = LoggerFactory.getLogger(JavaCompilerCheckFacade.class);
4750

51+
/**
52+
* This main method be exclusively used by `checkExternally` to perform javac checks
53+
*/
54+
public static void main(String[] args) {
55+
try (var input = new InputStreamReader(System.in)) {
56+
Configuration params = Configuration.load(CharStreams.fromReader(input));
57+
58+
var settings = new JavacSettings();
59+
settings.readSettings(params);
60+
61+
List<PositionedIssueString> result = check(
62+
Paths.get(params.getString("bootClassPath")),
63+
params.getStringList("classPath").stream().map(Paths::get).toList(),
64+
Paths.get(params.getString("javaPath")), settings).get();
65+
66+
var out = new Configuration();
67+
out.set("messages",
68+
result.stream().map(it -> {
69+
var map = Map.of(
70+
"message", it.text,
71+
"kind", it.getKind().toString(),
72+
"line", it.location.getPosition().line(),
73+
"column", it.location.getPosition().column(),
74+
"additionalInfo", it.getAdditionalInfo());
75+
if (it.location.fileUri() != null) {
76+
map = new TreeMap<>(map);
77+
map.put("fileUri", it.location.fileUri().toString());
78+
}
79+
return map;
80+
}).toList());
81+
82+
// send the data over stderr to not interfere with the log messages
83+
Writer writer = new OutputStreamWriter(System.err);
84+
out.save(writer, null);
85+
writer.close();
86+
} catch (Exception e) {
87+
LOGGER.error("Error during execution.", e);
88+
}
89+
}
90+
91+
/**
92+
* initiates the compilation check on the target Java source (the Java program to be verified)
93+
* in a separate process (with another java runtime)
94+
*
95+
* @param bootClassPath the {@link Path} referring to the path containing the core Java classes
96+
* @param classPath the {@link List} of {@link Path}s referring to the directory that make up
97+
* the target Java programs classpath
98+
* @param javaPath the {@link Path} to the source of the target Java program
99+
* @param settings the {@link JavacSettings} that describe what other options the compiler
100+
* should be called with
101+
* @return future providing the list of diagnostics
102+
*/
103+
public static @NonNull CompletableFuture<List<PositionedIssueString>> checkExternally(
104+
Path bootClassPath, List<Path> classPath, Path javaPath,
105+
JavacSettings settings) {
106+
if (Boolean.getBoolean("KEY_JAVAC_DISABLE")) {
107+
LOGGER.info("Javac check is disabled by system property -PKEY_JAVAC_DISABLE");
108+
return CompletableFuture.completedFuture(Collections.emptyList());
109+
}
110+
LOGGER.info(
111+
"External Javac check is triggered. To disable use property -PKEY_JAVAC_DISABLE=true");
112+
var params = new Configuration();
113+
params.set("bootClassPath", Objects.toString(bootClassPath));
114+
params.set("classPath",
115+
classPath.stream().map(Path::toAbsolutePath).map(Path::toString).toList());
116+
params.set("javaPath", Objects.toString(javaPath));
117+
settings.writeSettings(params);
118+
119+
String classpath = System.getProperty("java.class.path");
120+
String path =
121+
Paths.get(System.getProperty("java.home"), "bin", "java").toAbsolutePath().toString();
122+
ProcessBuilder processBuilder =
123+
new ProcessBuilder(path,
124+
"--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
125+
"--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
126+
"--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
127+
"--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
128+
"--add-exports", "jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
129+
"--add-exports", "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
130+
"--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
131+
"--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
132+
"--add-opens", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
133+
"-cp",
134+
classpath,
135+
JavaCompilerCheckFacade.class.getCanonicalName());
136+
return CompletableFuture.supplyAsync(() -> {
137+
try {
138+
var process = processBuilder.start();
139+
params.save(process.outputWriter(), null);
140+
process.outputWriter().close();
141+
142+
// the KeY logging messages from `process` (currently not used)
143+
String logs = Streams.toString(process.getInputStream());
144+
Configuration messages =
145+
Configuration.load(CharStreams.fromStream(process.getErrorStream()));
146+
process.waitFor();
147+
148+
return messages.getList("messages").stream()
149+
.map(msgObj -> {
150+
Configuration msg = (Configuration) msgObj;
151+
return new PositionedIssueString(
152+
msg.getString("message"),
153+
new Location(
154+
msg.exists("fileUri")
155+
? URI.create(msg.getString("fileUri"))
156+
: null,
157+
msg.getInt("line") == -1 || msg.getInt("column") == -1
158+
? Position.UNDEFINED
159+
: Position.newOneBased(
160+
msg.getInt("line"),
161+
msg.getInt("column"))),
162+
msg.getString("additionalInfo"),
163+
PositionedIssueString.Kind.valueOf(msg.getString("kind")));
164+
}).toList();
165+
} catch (IOException | InterruptedException e) {
166+
throw new RuntimeException(e);
167+
}
168+
});
169+
}
170+
48171
/**
49172
* initiates the compilation check on the target Java source (the Java program to be verified)
50-
* and
51-
* reports any issues to the provided <code>listener</code>
52173
*
53-
* @param listener the {@link ProblemInitializer.ProblemInitializerListener} to be informed
54-
* about any issues found in the target Java program
55-
* @param bootClassPath the {@link File} referring to the path containing the core Java classes
56-
* @param classPath the {@link List} of {@link File}s referring to the directory that make up
174+
* @param bootClassPath the {@link Path} referring to the path containing the core Java classes
175+
* @param classPath the {@link List} of {@link Path}s referring to the directory that make up
57176
* the target Java programs classpath
58-
* @param javaPath the {@link String} with the path to the source of the target Java program
177+
* @param javaPath the {@link Path} to the source of the target Java program
178+
* @param settings the {@link JavacSettings} that describe what other options the compiler
179+
* should be called with
59180
* @return future providing the list of diagnostics
60181
*/
61182
public static @NonNull CompletableFuture<List<PositionedIssueString>> check(
62-
ProblemInitializer.ProblemInitializerListener listener,
63-
Path bootClassPath, List<Path> classPath, Path javaPath) {
183+
Path bootClassPath, List<Path> classPath, Path javaPath,
184+
JavacSettings settings) {
64185
if (Boolean.getBoolean("KEY_JAVAC_DISABLE")) {
65186
LOGGER.info("Javac check is disabled by system property -PKEY_JAVAC_DISABLE");
66187
return CompletableFuture.completedFuture(Collections.emptyList());
@@ -72,7 +193,6 @@ public class JavaCompilerCheckFacade {
72193

73194
if (compiler == null) {
74195
LOGGER.info("Javac is not available in current java runtime. Javac check skipped");
75-
listener.reportStatus(null, "No javac compiler found. Java check disabled.");
76196
return CompletableFuture.completedFuture(Collections.emptyList());
77197
}
78198

@@ -86,17 +206,47 @@ public class JavaCompilerCheckFacade {
86206

87207
// gather configured bootstrap classpath and regular classpath
88208
List<String> options = new ArrayList<>();
209+
210+
if (settings.getUseProcessors()) {
211+
String newlineClassPath = settings.getClassPaths();
212+
List<Path> processorClassPath =
213+
Arrays.asList(newlineClassPath.split(System.lineSeparator()))
214+
.stream()
215+
.filter(s -> !s.isBlank())
216+
.map(Paths::get)
217+
.toList();
218+
219+
if (!processorClassPath.isEmpty()) {
220+
classPath = new ArrayList<>(classPath);
221+
classPath.addAll(processorClassPath);
222+
}
223+
224+
List<String> processors =
225+
Arrays.asList(settings.getProcessors().split(System.lineSeparator()))
226+
.stream()
227+
.filter(s -> !s.isBlank())
228+
.toList();
229+
230+
if (!processors.isEmpty()) {
231+
options.add("-processor");
232+
options.add(processors.stream().collect(Collectors.joining(",")));
233+
}
234+
}
235+
89236
if (bootClassPath != null) {
90-
options.add("-bootclasspath");
91-
options.add(bootClassPath.toAbsolutePath().toString());
237+
// options.add("-bootclasspath");
238+
// options.add(bootClassPath.toAbsolutePath().toString());
239+
LOGGER.info("The \"bootclasspath\" Option is set but not supported.");
92240
}
241+
93242
if (classPath != null && !classPath.isEmpty()) {
94243
options.add("-classpath");
95244
options.add(
96245
classPath.stream().map(Path::toAbsolutePath)
97246
.map(Objects::toString)
98247
.collect(Collectors.joining(":")));
99248
}
249+
100250
ArrayList<Path> files = new ArrayList<>();
101251
if (Files.isDirectory(javaPath)) {
102252
try (var s = Files.walk(javaPath)) {
@@ -113,6 +263,10 @@ public class JavaCompilerCheckFacade {
113263
Iterable<? extends JavaFileObject> compilationUnits =
114264
fileManager.getJavaFileObjects(files.toArray(new Path[0]));
115265

266+
LOGGER.info(
267+
"running Javac check with following\n\toptions: {},\n\tclasses: {},\n\tcompilation units: {},",
268+
options, classes, compilationUnits);
269+
116270
JavaCompiler.CompilationTask task = compiler.getTask(output, fileManager, diagnostics,
117271
options, classes, compilationUnits);
118272

@@ -127,7 +281,9 @@ public class JavaCompilerCheckFacade {
127281
it -> new PositionedIssueString(
128282
it.getMessage(Locale.ENGLISH),
129283
new Location(
130-
fileManager.asPath(it.getSource()).toFile().toPath().toUri(),
284+
it.getSource() == null
285+
? null
286+
: fileManager.asPath(it.getSource()).toFile().toPath().toUri(),
131287
it.getPosition() != Diagnostic.NOPOS
132288
? Position.newOneBased((int) it.getLineNumber(),
133289
(int) it.getColumnNumber())

key.ui/src/main/java/de/uka/ilkd/key/gui/plugins/javac/JavacExtension.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import de.uka.ilkd.key.gui.extension.api.KeYGuiExtension;
2323
import de.uka.ilkd.key.gui.fonticons.IconFontProvider;
2424
import de.uka.ilkd.key.gui.fonticons.MaterialDesignRegular;
25+
import de.uka.ilkd.key.gui.settings.SettingsProvider;
2526
import de.uka.ilkd.key.proof.JavaModel;
2627
import de.uka.ilkd.key.proof.Node;
2728
import de.uka.ilkd.key.proof.Proof;
@@ -30,7 +31,7 @@
3031
import org.slf4j.LoggerFactory;
3132

3233
/**
33-
* Extensions provides Javac checks for recent-loaded Java files.
34+
* Extension provides Javac checks for recent-loaded Java files.
3435
* <p>
3536
* Provides an entry in the status line for access.
3637
*
@@ -43,7 +44,7 @@
4344
experimental = false)
4445
public class JavacExtension
4546
implements KeYGuiExtension, KeYGuiExtension.StatusLine, KeYGuiExtension.Startup,
46-
KeYSelectionListener {
47+
KeYSelectionListener, KeYGuiExtension.Settings {
4748
/**
4849
* Color used for the label if javac didn't produce any diagnostics.
4950
*/
@@ -147,14 +148,21 @@ private void loadProof(Proof selectedProof) throws RuntimeException {
147148

148149
Path bootClassPath = jm.getBootClassPath() != null ? jm.getBootClassPath() : null;
149150
List<Path> classpath = jm.getClassPath();
151+
150152
Path javaPath = jm.getModelDir();
151153

152154
lblStatus.setForeground(Color.black);
153155
lblStatus.setText("Javac runs");
154156
lblStatus.setIcon(ICON_WAIT.get(16));
155157

156-
CompletableFuture<List<PositionedIssueString>> task =
157-
JavaCompilerCheckFacade.check(mediator.getUI(), bootClassPath, classpath, javaPath);
158+
JavacSettings settings = JavacSettingsProvider.getJavacSettings();
159+
CompletableFuture<List<PositionedIssueString>> task = settings.getUseProcessors()
160+
? JavaCompilerCheckFacade.checkExternally(bootClassPath,
161+
classpath,
162+
javaPath,
163+
settings)
164+
: JavaCompilerCheckFacade.check(bootClassPath, classpath,
165+
javaPath, settings);
158166
try {
159167
task.thenAccept(it -> SwingUtilities.invokeLater(() -> {
160168
lblStatus.setText("Javac finished");
@@ -227,6 +235,10 @@ public void selectedNodeChanged(KeYSelectionEvent<Node> e) {
227235
public void selectedProofChanged(KeYSelectionEvent<Proof> e) {
228236
loadProof(e.getSource().getSelectedProof());
229237
}
238+
239+
public SettingsProvider getSettings() {
240+
return new JavacSettingsProvider();
241+
}
230242
}
231243

232244

0 commit comments

Comments
 (0)