Skip to content

Commit

Permalink
Merge pull request #224 from wiwa/f/targetroot-javac-classdir
Browse files Browse the repository at this point in the history
Allow javac plugin to infer targetroot from javac class output directory
  • Loading branch information
olafurpg authored May 19, 2021
2 parents ba89c92 + b9c1e0c commit ea78804
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 27 deletions.
5 changes: 3 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ commands +=

commands +=
Command.command("checkAll") { s =>
"scalafmtCheckAll" :: "scalafmtSbtCheck" :: "scalafixAll --check" ::
"javafmtCheckAll" :: "publishLocal" :: "docs/docusaurusCreateSite" :: s
"javafmtCheckAll" :: "scalafmtCheckAll" :: "scalafmtSbtCheck" ::
"scalafixAll --check" :: "publishLocal" :: "docs/docusaurusCreateSite" ::
s
}

lazy val semanticdb = project
Expand Down
4 changes: 3 additions & 1 deletion docs/manual-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ compiler plugin. To do this you need to explicitly configure two directories:
It's important that all of the source files that should be index live under
this directory.
- `-targetroot:PATH`: the absolute path to the directory where to generate
SemanticDB file. This directory can be anywhere on your file system.
SemanticDB file. This directory can be anywhere on your file system.
Alternatively, pass in `-targetroot:javac-classes-directory`
for the plugin to automatically use the `javac` output directory.

If you're using Gradle.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
package com.sourcegraph.semanticdb_javac;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;

import com.sun.tools.javac.util.Context;

import static javax.tools.StandardLocation.CLASS_OUTPUT;

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

/** The directory to place */
/** The directory to place META-INF and its .semanticdb files */
public Path targetroot;

public Path sourceroot;
public boolean includeText = false;
public boolean verboseEnabled = false;
public final ArrayList<String> errors;

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

public SemanticdbJavacOptions() {
errors = new ArrayList<>();
}
Expand All @@ -26,11 +37,20 @@ public static String missingRequiredDirectoryOption(String option) {
option, option);
}

public static SemanticdbJavacOptions parse(String[] args) {
public static String JAVAC_CLASSES_DIR_ARG = "javac-classes-directory";

public static SemanticdbJavacOptions parse(String[] args, Context ctx) {
SemanticdbJavacOptions result = new SemanticdbJavacOptions();
boolean useJavacClassesDir = false;
for (String arg : args) {
if (arg.startsWith("-targetroot:")) {
result.targetroot = Paths.get(arg.substring("-targetroot:".length()));
String argValue = arg.substring("-targetroot:".length());
if (argValue.equals(JAVAC_CLASSES_DIR_ARG)) {
useJavacClassesDir = true;
result.targetroot = getJavacClassesDir(result, ctx);
} else {
result.targetroot = Paths.get(argValue);
}
} else if (arg.startsWith("-sourceroot:")) {
result.sourceroot = Paths.get(arg.substring("-sourceroot:".length())).normalize();
} else if (arg.equals("-text:on")) {
Expand All @@ -47,12 +67,32 @@ public static SemanticdbJavacOptions parse(String[] args) {
result.errors.add(String.format("unknown flag '%s'\n", arg));
}
}
if (result.targetroot == null) {
if (result.targetroot == null && !useJavacClassesDir) {
result.errors.add(missingRequiredDirectoryOption("targetroot"));
}
if (result.sourceroot == null) {
result.errors.add(missingRequiredDirectoryOption("sourceroot"));
}
return result;
}

private static Path getJavacClassesDir(SemanticdbJavacOptions result, Context ctx) {
// I'm not aware of a better way to get the class output directory from javac
Path outputDir = null;
try {
JavaFileManager fm = ctx.get(JavaFileManager.class);
FileObject outputDirStub =
fm.getJavaFileForOutput(CLASS_OUTPUT, stubClassName, JavaFileObject.Kind.CLASS, null);
outputDir = Paths.get(outputDirStub.toUri()).toAbsolutePath().getParent();
} catch (Exception e) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(out));
String errorMsg =
String.format(
"exception while processing SemanticDB option '-targetroot:%s'\n%s",
JAVAC_CLASSES_DIR_ARG, out.toString());
result.errors.add(errorMsg);
}
return outputDir;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.sourcegraph.semanticdb_javac;

import com.sun.source.util.*;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.tools.javac.api.BasicJavacTask;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.util.Context;

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

@Override
public void init(JavacTask task, String... args) {
Context ctx = ((BasicJavacTask) task).getContext();

SemanticdbReporter reporter = new SemanticdbReporter();
SemanticdbJavacOptions options = SemanticdbJavacOptions.parse(args);
SemanticdbJavacOptions options = SemanticdbJavacOptions.parse(args, ctx);
GlobalSymbolsCache globals = new GlobalSymbolsCache(options);
JavacTypes javacTypes = JavacTypes.instance(((BasicJavacTask) task).getContext());
JavacTypes javacTypes = JavacTypes.instance(ctx);
if (!options.errors.isEmpty()) {
for (String error : options.errors) {
reporter.error(error);
Expand Down
40 changes: 24 additions & 16 deletions tests/unit/src/main/scala/tests/SimpleFileManager.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
package tests;

import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import com.sourcegraph.semanticdb_javac.SemanticdbJavacOptions;

public class SimpleFileManager
extends ForwardingJavaFileManager<StandardJavaFileManager> {
extends ForwardingJavaFileManager<StandardJavaFileManager> {

public List<SimpleClassFile> compiled = new ArrayList<>();
protected SimpleFileManager(StandardJavaFileManager fileManager) {
super(fileManager);
}
public final List<SimpleClassFile> compiled = new ArrayList<>();
public final Path targetroot;

protected SimpleFileManager(StandardJavaFileManager fileManager, Path targetroot) {
super(fileManager);
this.targetroot = targetroot;
}

// standard constructors/getters
// standard constructors/getters

@Override
public JavaFileObject getJavaFileForOutput(Location location,
String className, JavaFileObject.Kind kind, FileObject sibling) {
SimpleClassFile result = new SimpleClassFile(
URI.create("string://" + className));
compiled.add(result);
return result;
@Override
public JavaFileObject getJavaFileForOutput(Location location,
String className, JavaFileObject.Kind kind, FileObject sibling) {
URI uri = targetroot.resolve(className).toUri();
SimpleClassFile result = new SimpleClassFile(uri);
if (!className.equals(SemanticdbJavacOptions.stubClassName)) {
compiled.add(result);
}
return result;
}

}

5 changes: 4 additions & 1 deletion tests/unit/src/main/scala/tests/TestCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ class TestCompiler(

private val compiler = ToolProvider.getSystemJavaCompiler
private val fileManager =
new SimpleFileManager(compiler.getStandardFileManager(null, null, null))
new SimpleFileManager(
compiler.getStandardFileManager(null, null, null),
targetroot
)

def this(targetroot: Path) {
this(TestCompiler.PROCESSOR_PATH, Nil, targetroot)
Expand Down
47 changes: 47 additions & 0 deletions tests/unit/src/test/scala/tests/JavacClassesDirectorySuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package tests

import java.nio.file.Files
import java.nio.file.Paths

import scala.meta.inputs.Input

import munit.FunSuite

class JavacClassesDirectorySuite extends FunSuite with TempDirectories {
val sourceroot = new DirectoryFixture()

override def munitFixtures: Seq[Fixture[_]] =
super.munitFixtures ++ List(sourceroot)

test("targetroot:javac-classes-directory") {
val compiler =
new TestCompiler(
classpath = TestCompiler.PROCESSOR_PATH,
options = Nil,
targetroot = sourceroot(),
sourceroot = sourceroot()
)
val compileResult = compiler.compile(
List(
Input.VirtualFile(
"example/Example.java",
"""package example;
|public class Example{}""".stripMargin
)
),
List(
s"-Xplugin:semanticdb -sourceroot:${sourceroot()} -targetroot:javac-classes-directory",
"-d",
sourceroot().toString
)
)
assert(clue(compileResult).isSuccess)
val semanticdbPath = Paths
.get("META-INF")
.resolve("semanticdb")
.resolve("example")
.resolve("Example.java.semanticdb")
assert(Files.isRegularFile(clue(sourceroot().resolve(semanticdbPath))))
}

}

0 comments on commit ea78804

Please sign in to comment.