Skip to content

Commit eef8f3c

Browse files
authored
Merge pull request #602 from sourcegraph/bazel-auto-generated
Special handling of Bazel with source generators
2 parents 7573b40 + ad2e52f commit eef8f3c

File tree

7 files changed

+156
-35
lines changed

7 files changed

+156
-35
lines changed

examples/bazel-example/WORKSPACE

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ maven_install(
6969
artifacts = [
7070
"com.google.protobuf:protobuf-java:3.15.6", # Required dependency by scip-java.
7171
"com.google.protobuf:protobuf-java-util:3.15.6", # Required dependency by scip-java.
72-
"com.google.guava:guava:31.0-jre", # Not required dependency, only used by tests.
72+
# These dependencies are only required for the tests
73+
"com.google.guava:guava:31.0-jre",
74+
"com.google.auto.value:auto-value:1.5.3",
7375
],
7476
repositories = [
7577
"https://repo1.maven.org/maven2",
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
import com.google.auto.value.AutoValue;
3+
4+
@AutoValue
5+
abstract class Animal {
6+
static Animal create(String name, int numberOfLegs) {
7+
return new AutoValue_Animal(name, numberOfLegs);
8+
}
9+
10+
abstract String name();
11+
abstract int numberOfLegs();
12+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
load("@scip_java//semanticdb-javac:defs.bzl", "java_library")
2+
load("@rules_java//java:defs.bzl", "java_plugin")
3+
4+
package(
5+
default_visibility = ["//visibility:public"],
6+
)
7+
8+
java_library(
9+
name = "animal",
10+
srcs = ["Animal.java"],
11+
deps = [
12+
":autovalue",
13+
],
14+
)
15+
16+
java_plugin(
17+
name = "autovalue_plugin",
18+
processor_class = "com.google.auto.value.processor.AutoValueProcessor",
19+
deps = [
20+
"@maven//:com_google_auto_value_auto_value",
21+
],
22+
)
23+
24+
java_library(
25+
name = "autovalue",
26+
exported_plugins = [
27+
":autovalue_plugin",
28+
],
29+
neverlink = 1,
30+
exports = [
31+
"@maven//:com_google_auto_value_auto_value",
32+
],
33+
)
34+

semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbJavacOptions.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.sun.tools.javac.util.Context;
1313

1414
import static javax.tools.StandardLocation.CLASS_OUTPUT;
15+
import static javax.tools.StandardLocation.SOURCE_OUTPUT;
1516

1617
/** Settings that can be configured alongside the -Xplugin compiler option. */
1718
public class SemanticdbJavacOptions {
@@ -25,6 +26,7 @@ public class SemanticdbJavacOptions {
2526
public final ArrayList<String> errors;
2627
public boolean alreadyReportedErrors = false;
2728
public UriScheme uriScheme = UriScheme.DEFAULT;
29+
public Path generatedTargetRoot;
2830

2931
public static String stubClassName = "META-INF-semanticdb-stub";
3032

@@ -43,13 +45,14 @@ public static String missingRequiredDirectoryOption(String option) {
4345

4446
public static SemanticdbJavacOptions parse(String[] args, Context ctx) {
4547
SemanticdbJavacOptions result = new SemanticdbJavacOptions();
48+
4649
boolean useJavacClassesDir = false;
4750
for (String arg : args) {
4851
if (arg.startsWith("-targetroot:")) {
4952
String argValue = arg.substring("-targetroot:".length());
5053
if (argValue.equals(JAVAC_CLASSES_DIR_ARG)) {
5154
useJavacClassesDir = true;
52-
result.targetroot = getJavacClassesDir(result, ctx);
55+
result.targetroot = getJavacClassesDir(result, ctx).classes;
5356
} else {
5457
result.targetroot = Paths.get(argValue);
5558
}
@@ -60,7 +63,9 @@ public static SemanticdbJavacOptions parse(String[] args, Context ctx) {
6063
} else if (arg.equals("-build-tool:bazel")) {
6164
result.uriScheme = UriScheme.BAZEL;
6265
useJavacClassesDir = true;
63-
result.targetroot = getJavacClassesDir(result, ctx);
66+
TargetPaths paths = getJavacClassesDir(result, ctx);
67+
result.targetroot = paths.classes;
68+
result.generatedTargetRoot = paths.sources;
6469
} else if (arg.equals("-text:on")) {
6570
result.includeText = true;
6671
} else if (arg.equals("-text:off")) {
@@ -79,9 +84,11 @@ public static SemanticdbJavacOptions parse(String[] args, Context ctx) {
7984
result.errors.add(missingRequiredDirectoryOption("targetroot"));
8085
}
8186
if (!isSourcerootDefined(result)) {
82-
// When using -build-tool:bazel, the sourceroot is automatically inferred from the first
87+
// When using -build-tool:bazel, the sourceroot is automatically inferred from
88+
// the first
8389
// compilation unit.
84-
// See `SemanticdbTaskListener.inferBazelSourceroot()` for the method that infers the
90+
// See `SemanticdbTaskListener.inferBazelSourceroot()` for the method that
91+
// infers the
8592
// sourceroot.
8693
result.errors.add(missingRequiredDirectoryOption("sourceroot"));
8794
}
@@ -95,14 +102,18 @@ private static boolean isSourcerootDefined(SemanticdbJavacOptions options) {
95102
return options.sourceroot != null;
96103
}
97104

98-
private static Path getJavacClassesDir(SemanticdbJavacOptions result, Context ctx) {
105+
private static TargetPaths getJavacClassesDir(SemanticdbJavacOptions result, Context ctx) {
99106
// I'm not aware of a better way to get the class output directory from javac
100-
Path outputDir = null;
107+
Path classOutputDir = null;
108+
Path sourceOutputDir = null;
101109
try {
102110
JavaFileManager fm = ctx.get(JavaFileManager.class);
103-
FileObject outputDirStub =
111+
FileObject sourceOutputDirStub =
112+
fm.getJavaFileForOutput(SOURCE_OUTPUT, stubClassName, JavaFileObject.Kind.SOURCE, null);
113+
FileObject clasSOutputDirStub =
104114
fm.getJavaFileForOutput(CLASS_OUTPUT, stubClassName, JavaFileObject.Kind.CLASS, null);
105-
outputDir = Paths.get(outputDirStub.toUri()).toAbsolutePath().getParent();
115+
classOutputDir = Paths.get(clasSOutputDirStub.toUri()).toAbsolutePath().getParent();
116+
sourceOutputDir = Paths.get(sourceOutputDirStub.toUri()).toAbsolutePath().getParent();
106117
} catch (Exception e) {
107118
ByteArrayOutputStream out = new ByteArrayOutputStream();
108119
e.printStackTrace(new PrintStream(out));
@@ -112,6 +123,6 @@ private static Path getJavacClassesDir(SemanticdbJavacOptions result, Context ct
112123
JAVAC_CLASSES_DIR_ARG, out.toString());
113124
result.errors.add(errorMsg);
114125
}
115-
return outputDir;
126+
return new TargetPaths(classOutputDir, sourceOutputDir);
116127
}
117128
}

semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbReporter.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.sun.source.tree.CompilationUnitTree;
44
import com.sun.source.tree.Tree;
55
import com.sun.source.util.Trees;
6+
import com.sun.source.util.TaskEvent;
67

78
import javax.tools.Diagnostic;
89
import java.io.ByteArrayOutputStream;
@@ -30,8 +31,29 @@ public void exception(Throwable e, Tree tree, CompilationUnitTree root) {
3031
trees.printMessage(Diagnostic.Kind.ERROR, baos.toString(), tree, root);
3132
}
3233

34+
public void exception(Throwable e, TaskEvent task) {
35+
this.exception(e, task.getCompilationUnit(), task.getCompilationUnit());
36+
}
37+
38+
public void info(String message, TaskEvent e) {
39+
trees.printMessage(
40+
Diagnostic.Kind.NOTE,
41+
"semanticdb-javac: " + message,
42+
e.getCompilationUnit(),
43+
e.getCompilationUnit());
44+
}
45+
46+
public void error(String message, TaskEvent e) {
47+
trees.printMessage(
48+
Diagnostic.Kind.ERROR,
49+
"semanticdb-javac: " + message,
50+
e.getCompilationUnit(),
51+
e.getCompilationUnit());
52+
}
53+
3354
public void error(String message, Tree tree, CompilationUnitTree root) {
34-
// NOTE(olafur): ideally, this message should be reported as a compiler diagnostic, but I dind't
55+
// NOTE(olafur): ideally, this message should be reported as a compiler
56+
// diagnostic, but I dind't
3557
// find
3658
// the reporter API so the message goes to stderr instead for now.
3759
trees.printMessage(

semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbTaskListener.java

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public final class SemanticdbTaskListener implements TaskListener {
2525
private final SemanticdbReporter reporter;
2626
private final JavacTypes javacTypes;
2727
private final Trees trees;
28+
private boolean sourceGeneratorsMessageIsLogged = false;
2829

2930
public SemanticdbTaskListener(
3031
SemanticdbJavacOptions options,
@@ -50,11 +51,7 @@ public void finished(TaskEvent e) {
5051
if (!options.alreadyReportedErrors) {
5152
options.alreadyReportedErrors = true;
5253
for (String error : options.errors) {
53-
trees.printMessage(
54-
Diagnostic.Kind.ERROR,
55-
"semanticdb-javac: " + error,
56-
e.getCompilationUnit(),
57-
e.getCompilationUnit());
54+
reporter.error(error, e);
5855
}
5956
}
6057
return;
@@ -63,9 +60,12 @@ public void finished(TaskEvent e) {
6360
try {
6461
onFinishedAnalyze(e);
6562
} catch (Throwable ex) {
66-
// Catch exceptions because we don't want to stop the compilation even if this plugin has a
67-
// bug. We report the full stack trace because it's helpful for bug reports. Exceptions
68-
// should only happen in *exceptional* situations and they should be reported upstream.
63+
// Catch exceptions because we don't want to stop the compilation even if this
64+
// plugin has a
65+
// bug. We report the full stack trace because it's helpful for bug reports.
66+
// Exceptions
67+
// should only happen in *exceptional* situations and they should be reported
68+
// upstream.
6969
Throwable throwable = ex;
7070
if (e.getSourceFile() != null) {
7171
throwable =
@@ -79,13 +79,15 @@ public void finished(TaskEvent e) {
7979

8080
private void onFinishedAnalyze(TaskEvent e) {
8181
Result<Path, String> path = semanticdbOutputPath(options, e);
82-
if (path.isOk()) {
83-
Semanticdb.TextDocument textDocument =
84-
new SemanticdbVisitor(task, globals, e, options, javacTypes)
85-
.buildTextDocument(e.getCompilationUnit());
86-
writeSemanticdb(e, path.getOrThrow(), textDocument);
87-
} else {
88-
reporter.error(path.getErrorOrThrow(), e.getCompilationUnit(), e.getCompilationUnit());
82+
if (path != null) {
83+
if (path.isOk()) {
84+
Semanticdb.TextDocument textDocument =
85+
new SemanticdbVisitor(task, globals, e, options, javacTypes)
86+
.buildTextDocument(e.getCompilationUnit());
87+
writeSemanticdb(e, path.getOrThrow(), textDocument);
88+
} else {
89+
reporter.error(path.getErrorOrThrow(), e);
90+
}
8991
}
9092
}
9193

@@ -112,12 +114,17 @@ public static Path absolutePathFromUri(SemanticdbJavacOptions options, JavaFileO
112114
throw new IllegalArgumentException("unsupported URI: " + uri);
113115
}
114116
} else if (options.uriScheme == UriScheme.BAZEL) {
115-
String toString = file.toString();
116-
// This solution is hacky, and it would be very nice to use a dedicated API instead.
117-
// The Bazel Java compiler constructs `SimpleFileObject/DirectoryFileObject` with a
118-
// "user-friendly" name that points to the original source file and an underlying/actual
119-
// file path in a temporary directory. We're constrained by having to use only public APIs of
120-
// the Java compiler and `toString()` seems to be the only way to access the user-friendly
117+
String toString = file.toString().replace(":", "/");
118+
// This solution is hacky, and it would be very nice to use a dedicated API
119+
// instead.
120+
// The Bazel Java compiler constructs `SimpleFileObject/DirectoryFileObject`
121+
// with a
122+
// "user-friendly" name that points to the original source file and an
123+
// underlying/actual
124+
// file path in a temporary directory. We're constrained by having to use only
125+
// public APIs of
126+
// the Java compiler and `toString()` seems to be the only way to access the
127+
// user-friendly
121128
// path.
122129
String[] knownBazelToStringPatterns =
123130
new String[] {"SimpleFileObject[", "DirectoryFileObject["};
@@ -146,11 +153,11 @@ private void inferBazelSourceroot(JavaFileObject file) {
146153
// /private/var/tmp/com/example/Hello.java
147154
//
148155
// We infer sourceroot by iterating the names of both files in reverse order
149-
// and stop at the first entry where the two paths are different. For the
156+
// and stop at the first entry where the two paths are different. For the
150157
// example above, we compare "Hello.java", then "example", then "com", and
151158
// when we reach "repo" != "tmp" then we guess that "/home/repo" is the
152-
// sourceroot. This logic is brittle and it would be nice to use more
153-
// dedicated APIs, but Bazel actively makes an effort to sandbox
159+
// sourceroot. This logic is brittle and it would be nice to use more
160+
// dedicated APIs, but Bazel actively makes an effort to sandbox
154161
// compilation and hide access to the original workspace, which is why we
155162
// resort to solutions like this.
156163
int relativePathDepth = 0;
@@ -184,6 +191,26 @@ private Result<Path, String> semanticdbOutputPath(SemanticdbJavacOptions options
184191
.resolveSibling(filename);
185192
return Result.ok(semanticdbOutputPath);
186193
} else {
194+
195+
if (options.uriScheme == UriScheme.BAZEL && options.generatedTargetRoot != null) {
196+
try {
197+
if (absolutePath.toRealPath().startsWith(options.generatedTargetRoot)) {
198+
if (!sourceGeneratorsMessageIsLogged) {
199+
sourceGeneratorsMessageIsLogged = true;
200+
reporter.info(
201+
"Usage of source generators detected - scip-java does not produce SemanticDB files for generated files.\n"
202+
+ "This message is logged only once",
203+
e);
204+
}
205+
206+
return null;
207+
}
208+
} catch (IOException exc) {
209+
reporter.exception(exc, e);
210+
return null;
211+
}
212+
}
213+
187214
return Result.error(
188215
String.format(
189216
"sourceroot '%s does not contain path '%s'. To fix this problem, update the -sourceroot flag to "
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.sourcegraph.semanticdb_javac;
2+
3+
import java.nio.file.Path;
4+
5+
public class TargetPaths {
6+
public Path classes;
7+
public Path sources;
8+
9+
public TargetPaths(Path classesDir, Path sourcesDir) {
10+
classes = classesDir;
11+
sources = sourcesDir;
12+
}
13+
}

0 commit comments

Comments
 (0)