Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translate the sbt-bridge to Java. #5596

Merged
merged 6 commits into from Jan 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion build.sbt
Expand Up @@ -8,7 +8,6 @@ val `dotty-compiler-bootstrapped` = Build.`dotty-compiler-bootstrapped`
val `dotty-library` = Build.`dotty-library`
val `dotty-library-bootstrapped` = Build.`dotty-library-bootstrapped`
val `dotty-sbt-bridge` = Build.`dotty-sbt-bridge`
val `dotty-sbt-bridge-bootstrapped` = Build.`dotty-sbt-bridge-bootstrapped`
val `dotty-language-server` = Build.`dotty-language-server`
val `dotty-bench` = Build.`dotty-bench`
val `dotty-bench-bootstrapped` = Build.`dotty-bench-bootstrapped`
Expand Down
@@ -0,0 +1,8 @@
package dotty.tools
package dotc
package reporting

/**
* This class mixes in a few standard traits, so that it is easier to extend from Java.
*/
abstract class AbstractReporter extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with MessageRendering
Expand Up @@ -13,7 +13,7 @@ import diagnostic.messages.{ Error, ConditionalWarning }
class ConsoleReporter(
reader: BufferedReader = Console.in,
writer: PrintWriter = new PrintWriter(Console.err, true)
) extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with MessageRendering {
) extends AbstractReporter {

import MessageContainer._

Expand Down
124 changes: 26 additions & 98 deletions project/Build.scala
Expand Up @@ -134,7 +134,7 @@ object Build {
"-language:existentials,higherKinds,implicitConversions"
),

javacOptions ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
javacOptions in (Compile, compile) ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),

// Change this to true if you want to bootstrap using a published non-bootstrapped compiler
bootstrapFromPublishedJars := false,
Expand Down Expand Up @@ -209,10 +209,14 @@ object Build {
testOptions in Test += Tests.Argument(TestFrameworks.JUnit, "-a", "-v")
)

// Settings used for projects compiled only with Scala 2
lazy val commonScala2Settings = commonSettings ++ Seq(
// Settings used for projects compiled only with Java
lazy val commonJavaSettings = commonSettings ++ Seq(
version := dottyVersion,
scalaVersion := scalacVersion
scalaVersion := scalacVersion,
// Do not append Scala versions to the generated artifacts
crossPaths := false,
// Do not depend on the Scala library
autoScalaLibrary := false
)

// Settings used when compiling dotty using Scala 2
Expand All @@ -226,17 +230,9 @@ object Build {
version := dottyVersion,
scalaVersion := dottyNonBootstrappedVersion,

// Avoid having to run `dotty-sbt-bridge/publishLocal` before compiling a bootstrapped project
scalaCompilerBridgeSource :=
(dottyOrganization %% "dotty-sbt-bridge" % dottyVersion)
.artifacts(Artifact.sources("dotty-sbt-bridge").withUrl(
// We cannot use the `packageSrc` task because a setting cannot depend
// on a task. Instead, we make `compile` below depend on the bridge `packageSrc`
Some((artifactPath in (`dotty-sbt-bridge`, Compile, packageSrc)).value.toURI.toURL))),
compile in Compile := (compile in Compile)
.dependsOn(packageSrc in (`dotty-sbt-bridge`, Compile))
.dependsOn(compile in (`dotty-sbt-bridge`, Compile))
.value,
scalaCompilerBridgeBinaryJar := {
Some((packageBin in (`dotty-sbt-bridge`, Compile)).value)
},

// Use the same name as the non-bootstrapped projects for the artifacts
moduleName ~= { _.stripSuffix("-bootstrapped") },
Expand Down Expand Up @@ -359,13 +355,6 @@ object Build {
// currently refers to dotty in its scripted task and "aggregate" does not take by-name
// parameters: https://github.com/sbt/sbt/issues/2200
lazy val dottySbtBridgeRef = LocalProject("dotty-sbt-bridge")
// Same thing for the bootstrapped version
lazy val dottySbtBridgeBootstrappedRef = LocalProject("dotty-sbt-bridge-bootstrapped")

def dottySbtBridgeReference(implicit mode: Mode): LocalProject = mode match {
case NonBootstrapped => dottySbtBridgeRef
case _ => dottySbtBridgeBootstrappedRef
}

// The root project:
// - aggregates other projects so that "compile", "test", etc are run on all projects at once.
Expand All @@ -375,15 +364,7 @@ object Build {
lazy val `dotty-bootstrapped` = project.asDottyRoot(Bootstrapped)

lazy val `dotty-interfaces` = project.in(file("interfaces")).
settings(commonScala2Settings). // Java-only project, so this is fine
settings(
// Do not append Scala versions to the generated artifacts
crossPaths := false,
// Do not depend on the Scala library
autoScalaLibrary := false,
//Remove javac invalid options in Compile doc
javacOptions in (Compile, doc) --= Seq("-Xlint:unchecked", "-Xlint:deprecation")
)
settings(commonJavaSettings)

private lazy val dottydocClasspath = Def.task {
val jars = (packageAll in `dotty-compiler`).value
Expand Down Expand Up @@ -798,67 +779,19 @@ object Build {
case Bootstrapped => `dotty-library-bootstrapped`
}

// until sbt/sbt#2402 is fixed (https://github.com/sbt/sbt/issues/2402)
lazy val cleanSbtBridge = TaskKey[Unit]("cleanSbtBridge", "delete dotty-sbt-bridge cache")

def cleanSbtBridgeImpl(): Unit = {
val home = System.getProperty("user.home")
val sbtOrg = "org.scala-sbt"
val bridgePattern = s"*dotty-sbt-bridge*$dottyVersion*"

IO.delete((file(home) / ".sbt" / "1.0" / "zinc" / sbtOrg * bridgePattern).get)
IO.delete((file(home) / ".sbt" / "boot" * "scala-*" / sbtOrg / "sbt" * "*" * bridgePattern).get)
}

lazy val dottySbtBridgeSettings = Seq(
cleanSbtBridge := {
cleanSbtBridgeImpl()
},
compile in Compile := {
val log = streams.value.log
val prev = (previousCompile in Compile).value.analysis.orElse(null)
val cur = (compile in Compile).value
if (prev != cur) {
log.info("Cleaning the dotty-sbt-bridge cache because it was recompiled.")
cleanSbtBridgeImpl()
}
cur
},
description := "sbt compiler bridge for Dotty",
resolvers += Resolver.typesafeIvyRepo("releases"), // For org.scala-sbt:api
libraryDependencies ++= Seq(
Dependencies.`compiler-interface` % Provided,
(Dependencies.`zinc-api-info` % Test).withDottyCompat(scalaVersion.value)
),
// The sources should be published with crossPaths := false since they
// need to be compiled by the project using the bridge.
crossPaths := false,

// Don't publish any binaries for the bridge because of the above
publishArtifact in (Compile, packageBin) := false,

fork in Test := true,
parallelExecution in Test := false
)
lazy val `dotty-sbt-bridge` = project.in(file("sbt-bridge")).
dependsOn(dottyCompiler(NonBootstrapped) % Provided).
dependsOn(dottyDoc(NonBootstrapped) % Provided).
settings(commonJavaSettings).
settings(
description := "sbt compiler bridge for Dotty",
libraryDependencies ++= Seq(
Dependencies.`compiler-interface` % Provided,
(Dependencies.`zinc-api-info` % Test).withDottyCompat(scalaVersion.value)
),

lazy val `dotty-sbt-bridge` = project.in(file("sbt-bridge")).asDottySbtBridge(NonBootstrapped)
lazy val `dotty-sbt-bridge-bootstrapped` = project.in(file("sbt-bridge")).asDottySbtBridge(Bootstrapped)
.settings(
// Tweak -Yscala2-unpickler to allow some sbt dependencies used in tests
/*
scalacOptions in Test := {
val oldOptions = (scalacOptions in Test).value
val i = oldOptions.indexOf("-Yscala2-unpickler")
assert(i != -1)
val oldValue = oldOptions(i + 1)

val attList = (dependencyClasspath in Test).value
val sbtIo = findLib(attList, "org.scala-sbt/io")
val zincApiInfo = findLib(attList, "zinc-apiinfo")

oldOptions.updated(i + 1, s"$sbtIo:$zincApiInfo:$oldValue")
}
*/
fork in Test := true,
parallelExecution in Test := false
)

lazy val `dotty-language-server` = project.in(file("language-server")).
Expand Down Expand Up @@ -1017,7 +950,7 @@ object Build {
scriptedLaunchOpts ++= ivyPaths.value.ivyHome.map("-Dsbt.ivy.home=" + _.getAbsolutePath).toList,
scriptedBufferLog := true,
scripted := scripted.dependsOn(
publishLocal in `dotty-sbt-bridge-bootstrapped`,
publishLocal in `dotty-sbt-bridge`,
publishLocal in `dotty-interfaces`,
publishLocal in `dotty-compiler-bootstrapped`,
publishLocal in `dotty-library-bootstrapped`,
Expand Down Expand Up @@ -1285,7 +1218,7 @@ object Build {

// FIXME: we do not aggregate `bin` because its tests delete jars, thus breaking other tests
def asDottyRoot(implicit mode: Mode): Project = project.withCommonSettings.
aggregate(`dotty-interfaces`, dottyLibrary, dottyCompiler, dottyDoc, dottySbtBridgeReference).
aggregate(`dotty-interfaces`, dottyLibrary, dottyCompiler, dottyDoc, `dotty-sbt-bridge`).
bootstrappedAggregate(`scala-library`, `scala-compiler`, `scala-reflect`, scalap, `dotty-language-server`).
dependsOn(dottyCompiler).
dependsOn(dottyLibrary).
Expand All @@ -1309,11 +1242,6 @@ object Build {
dependsOn(dottyCompiler, dottyCompiler % "test->test").
settings(dottyDocSettings)

def asDottySbtBridge(implicit mode: Mode): Project = project.withCommonSettings.
dependsOn(dottyCompiler % Provided).
dependsOn(dottyDoc % Provided).
settings(dottySbtBridgeSettings)

def asDottyBench(implicit mode: Mode): Project = project.withCommonSettings.
dependsOn(dottyCompiler).
settings(commonBenchmarkSettings).
Expand Down
74 changes: 74 additions & 0 deletions sbt-bridge/src/xsbt/CachedCompilerImpl.java
@@ -0,0 +1,74 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package xsbt;

import xsbti.AnalysisCallback;
import xsbti.Logger;
import xsbti.Reporter;
import xsbti.Severity;
import xsbti.compile.*;

import java.io.File;

import dotty.tools.dotc.core.Contexts.Context;
import dotty.tools.dotc.core.Contexts.ContextBase;
import dotty.tools.dotc.Main;
import dotty.tools.dotc.interfaces.*;

import java.net.URLClassLoader;

public class CachedCompilerImpl implements CachedCompiler {
private final String[] args;
private final Output output;
private final String[] outputArgs;

public CachedCompilerImpl(String[] args, Output output) {
super();
this.args = args;
this.output = output;

if (!(output instanceof SingleOutput))
throw new IllegalArgumentException("output should be a SingleOutput, was a " + output.getClass().getName());

this.outputArgs =
new String[] { "-d", ((SingleOutput) output).getOutputDirectory().getAbsolutePath().toString() };
}

public String[] commandArguments(File[] sources) {
String[] sortedSourcesAbsolute = new String[sources.length];
for (int i = 0; i < sources.length; i++)
sortedSourcesAbsolute[i] = sources[i].getAbsolutePath();
java.util.Arrays.sort(sortedSourcesAbsolute);

// Concatenate outputArgs, args and sortedSourcesAbsolute
String[] result = new String[outputArgs.length + args.length + sortedSourcesAbsolute.length];
int j = 0;
for (int i = 0; i < outputArgs.length; i++, j++)
result[j] = outputArgs[i];
for (int i = 0; i < args.length; i++, j++)
result[j] = args[i];
for (int i = 0; i < sortedSourcesAbsolute.length; i++, j++)
result[j] = sortedSourcesAbsolute[i];

return result;
}

synchronized public void run(File[] sources, DependencyChanges changes, AnalysisCallback callback, Logger log, Reporter delegate, CompileProgress progress) {
log.debug(() -> {
String msg = "Calling Dotty compiler with arguments (CompilerInterface):";
for (String arg : args)
msg = msg + "\n\t" + arg;
return msg;
});

Context ctx = new ContextBase().initialCtx().fresh()
.setSbtCallback(callback)
.setReporter(new DelegatingReporter(delegate));

dotty.tools.dotc.reporting.Reporter reporter = Main.process(commandArguments(sources), ctx);
if (reporter.hasErrors()) {
throw new InterfaceCompileFailed(args, new Problem[0]);
}
}
}