Skip to content

Commit ea78804

Browse files
authored
Merge pull request #224 from wiwa/f/targetroot-javac-classdir
Allow javac plugin to infer targetroot from javac class output directory
2 parents ba89c92 + b9c1e0c commit ea78804

File tree

7 files changed

+132
-27
lines changed

7 files changed

+132
-27
lines changed

build.sbt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ commands +=
6464

6565
commands +=
6666
Command.command("checkAll") { s =>
67-
"scalafmtCheckAll" :: "scalafmtSbtCheck" :: "scalafixAll --check" ::
68-
"javafmtCheckAll" :: "publishLocal" :: "docs/docusaurusCreateSite" :: s
67+
"javafmtCheckAll" :: "scalafmtCheckAll" :: "scalafmtSbtCheck" ::
68+
"scalafixAll --check" :: "publishLocal" :: "docs/docusaurusCreateSite" ::
69+
s
6970
}
7071

7172
lazy val semanticdb = project

docs/manual-configuration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ compiler plugin. To do this you need to explicitly configure two directories:
6666
It's important that all of the source files that should be index live under
6767
this directory.
6868
- `-targetroot:PATH`: the absolute path to the directory where to generate
69-
SemanticDB file. This directory can be anywhere on your file system.
69+
SemanticDB file. This directory can be anywhere on your file system.
70+
Alternatively, pass in `-targetroot:javac-classes-directory`
71+
for the plugin to automatically use the `javac` output directory.
7072

7173
If you're using Gradle.
7274

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

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
package com.sourcegraph.semanticdb_javac;
22

3+
import java.io.ByteArrayOutputStream;
4+
import java.io.PrintStream;
35
import java.nio.file.Path;
46
import java.nio.file.Paths;
57
import java.util.ArrayList;
8+
import javax.tools.FileObject;
9+
import javax.tools.JavaFileManager;
10+
import javax.tools.JavaFileObject;
11+
12+
import com.sun.tools.javac.util.Context;
13+
14+
import static javax.tools.StandardLocation.CLASS_OUTPUT;
615

716
/** Settings that can be configured alongside the -Xplugin compiler option. */
817
public class SemanticdbJavacOptions {
918

10-
/** The directory to place */
19+
/** The directory to place META-INF and its .semanticdb files */
1120
public Path targetroot;
1221

1322
public Path sourceroot;
1423
public boolean includeText = false;
1524
public boolean verboseEnabled = false;
1625
public final ArrayList<String> errors;
1726

27+
public static String stubClassName = "META-INF-semanticdb-stub";
28+
1829
public SemanticdbJavacOptions() {
1930
errors = new ArrayList<>();
2031
}
@@ -26,11 +37,20 @@ public static String missingRequiredDirectoryOption(String option) {
2637
option, option);
2738
}
2839

29-
public static SemanticdbJavacOptions parse(String[] args) {
40+
public static String JAVAC_CLASSES_DIR_ARG = "javac-classes-directory";
41+
42+
public static SemanticdbJavacOptions parse(String[] args, Context ctx) {
3043
SemanticdbJavacOptions result = new SemanticdbJavacOptions();
44+
boolean useJavacClassesDir = false;
3145
for (String arg : args) {
3246
if (arg.startsWith("-targetroot:")) {
33-
result.targetroot = Paths.get(arg.substring("-targetroot:".length()));
47+
String argValue = arg.substring("-targetroot:".length());
48+
if (argValue.equals(JAVAC_CLASSES_DIR_ARG)) {
49+
useJavacClassesDir = true;
50+
result.targetroot = getJavacClassesDir(result, ctx);
51+
} else {
52+
result.targetroot = Paths.get(argValue);
53+
}
3454
} else if (arg.startsWith("-sourceroot:")) {
3555
result.sourceroot = Paths.get(arg.substring("-sourceroot:".length())).normalize();
3656
} else if (arg.equals("-text:on")) {
@@ -47,12 +67,32 @@ public static SemanticdbJavacOptions parse(String[] args) {
4767
result.errors.add(String.format("unknown flag '%s'\n", arg));
4868
}
4969
}
50-
if (result.targetroot == null) {
70+
if (result.targetroot == null && !useJavacClassesDir) {
5171
result.errors.add(missingRequiredDirectoryOption("targetroot"));
5272
}
5373
if (result.sourceroot == null) {
5474
result.errors.add(missingRequiredDirectoryOption("sourceroot"));
5575
}
5676
return result;
5777
}
78+
79+
private static Path getJavacClassesDir(SemanticdbJavacOptions result, Context ctx) {
80+
// I'm not aware of a better way to get the class output directory from javac
81+
Path outputDir = null;
82+
try {
83+
JavaFileManager fm = ctx.get(JavaFileManager.class);
84+
FileObject outputDirStub =
85+
fm.getJavaFileForOutput(CLASS_OUTPUT, stubClassName, JavaFileObject.Kind.CLASS, null);
86+
outputDir = Paths.get(outputDirStub.toUri()).toAbsolutePath().getParent();
87+
} catch (Exception e) {
88+
ByteArrayOutputStream out = new ByteArrayOutputStream();
89+
e.printStackTrace(new PrintStream(out));
90+
String errorMsg =
91+
String.format(
92+
"exception while processing SemanticDB option '-targetroot:%s'\n%s",
93+
JAVAC_CLASSES_DIR_ARG, out.toString());
94+
result.errors.add(errorMsg);
95+
}
96+
return outputDir;
97+
}
5898
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.sourcegraph.semanticdb_javac;
22

3-
import com.sun.source.util.*;
3+
import com.sun.source.util.JavacTask;
4+
import com.sun.source.util.Plugin;
45
import com.sun.tools.javac.api.BasicJavacTask;
56
import com.sun.tools.javac.model.JavacTypes;
7+
import com.sun.tools.javac.util.Context;
68

79
/** Entrypoint of the semanticdb-javac compiler plugin. */
810
public class SemanticdbPlugin implements Plugin {
@@ -14,10 +16,12 @@ public String getName() {
1416

1517
@Override
1618
public void init(JavacTask task, String... args) {
19+
Context ctx = ((BasicJavacTask) task).getContext();
20+
1721
SemanticdbReporter reporter = new SemanticdbReporter();
18-
SemanticdbJavacOptions options = SemanticdbJavacOptions.parse(args);
22+
SemanticdbJavacOptions options = SemanticdbJavacOptions.parse(args, ctx);
1923
GlobalSymbolsCache globals = new GlobalSymbolsCache(options);
20-
JavacTypes javacTypes = JavacTypes.instance(((BasicJavacTask) task).getContext());
24+
JavacTypes javacTypes = JavacTypes.instance(ctx);
2125
if (!options.errors.isEmpty()) {
2226
for (String error : options.errors) {
2327
reporter.error(error);
Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,39 @@
11
package tests;
22

3+
import java.net.URI;
4+
import java.nio.file.Path;
5+
import java.util.ArrayList;
6+
import java.util.List;
37
import javax.tools.FileObject;
48
import javax.tools.ForwardingJavaFileManager;
59
import javax.tools.JavaFileObject;
610
import javax.tools.StandardJavaFileManager;
7-
import java.net.URI;
8-
import java.util.ArrayList;
9-
import java.util.List;
11+
12+
import com.sourcegraph.semanticdb_javac.SemanticdbJavacOptions;
1013

1114
public class SimpleFileManager
12-
extends ForwardingJavaFileManager<StandardJavaFileManager> {
15+
extends ForwardingJavaFileManager<StandardJavaFileManager> {
1316

14-
public List<SimpleClassFile> compiled = new ArrayList<>();
15-
protected SimpleFileManager(StandardJavaFileManager fileManager) {
16-
super(fileManager);
17-
}
17+
public final List<SimpleClassFile> compiled = new ArrayList<>();
18+
public final Path targetroot;
19+
20+
protected SimpleFileManager(StandardJavaFileManager fileManager, Path targetroot) {
21+
super(fileManager);
22+
this.targetroot = targetroot;
23+
}
1824

19-
// standard constructors/getters
25+
// standard constructors/getters
2026

21-
@Override
22-
public JavaFileObject getJavaFileForOutput(Location location,
23-
String className, JavaFileObject.Kind kind, FileObject sibling) {
24-
SimpleClassFile result = new SimpleClassFile(
25-
URI.create("string://" + className));
26-
compiled.add(result);
27-
return result;
27+
@Override
28+
public JavaFileObject getJavaFileForOutput(Location location,
29+
String className, JavaFileObject.Kind kind, FileObject sibling) {
30+
URI uri = targetroot.resolve(className).toUri();
31+
SimpleClassFile result = new SimpleClassFile(uri);
32+
if (!className.equals(SemanticdbJavacOptions.stubClassName)) {
33+
compiled.add(result);
2834
}
35+
return result;
36+
}
2937

3038
}
3139

tests/unit/src/main/scala/tests/TestCompiler.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ class TestCompiler(
2828

2929
private val compiler = ToolProvider.getSystemJavaCompiler
3030
private val fileManager =
31-
new SimpleFileManager(compiler.getStandardFileManager(null, null, null))
31+
new SimpleFileManager(
32+
compiler.getStandardFileManager(null, null, null),
33+
targetroot
34+
)
3235

3336
def this(targetroot: Path) {
3437
this(TestCompiler.PROCESSOR_PATH, Nil, targetroot)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package tests
2+
3+
import java.nio.file.Files
4+
import java.nio.file.Paths
5+
6+
import scala.meta.inputs.Input
7+
8+
import munit.FunSuite
9+
10+
class JavacClassesDirectorySuite extends FunSuite with TempDirectories {
11+
val sourceroot = new DirectoryFixture()
12+
13+
override def munitFixtures: Seq[Fixture[_]] =
14+
super.munitFixtures ++ List(sourceroot)
15+
16+
test("targetroot:javac-classes-directory") {
17+
val compiler =
18+
new TestCompiler(
19+
classpath = TestCompiler.PROCESSOR_PATH,
20+
options = Nil,
21+
targetroot = sourceroot(),
22+
sourceroot = sourceroot()
23+
)
24+
val compileResult = compiler.compile(
25+
List(
26+
Input.VirtualFile(
27+
"example/Example.java",
28+
"""package example;
29+
|public class Example{}""".stripMargin
30+
)
31+
),
32+
List(
33+
s"-Xplugin:semanticdb -sourceroot:${sourceroot()} -targetroot:javac-classes-directory",
34+
"-d",
35+
sourceroot().toString
36+
)
37+
)
38+
assert(clue(compileResult).isSuccess)
39+
val semanticdbPath = Paths
40+
.get("META-INF")
41+
.resolve("semanticdb")
42+
.resolve("example")
43+
.resolve("Example.java.semanticdb")
44+
assert(Files.isRegularFile(clue(sourceroot().resolve(semanticdbPath))))
45+
}
46+
47+
}

0 commit comments

Comments
 (0)