diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..9f949bb --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,21 @@ +version = 2.3.2 +maxColumn = 100 +project.git = true +project.excludeFilters = [ /sbt-test/, /input_sources/, /contraband-scala/ ] + +# http://docs.scala-lang.org/style/scaladoc.html recommends the JavaDoc style. +# scala/scala is written that way too https://github.com/scala/scala/blob/v2.12.2/src/library/scala/Predef.scala +docstrings = JavaDoc + +# This also seems more idiomatic to include whitespace in import x.{ yyy } +spaces.inImportCurlyBraces = true + +# This is more idiomatic Scala. +# http://docs.scala-lang.org/style/indentation.html#methods-with-numerous-arguments +align.openParenCallSite = false +align.openParenDefnSite = false + +# For better code clarity +danglingParentheses = true + +trailingCommas = preserve diff --git a/.travis.yml b/.travis.yml index 31dcec6..8dbf920 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,4 @@ env: - ADOPTOPENJDK=8 - ADOPTOPENJDK=11 -script: sbt -Dfile.encoding=UTF8 compilerInterface/mimaReportBinaryIssues headerCheck packageBin doc +script: sbt -Dfile.encoding=UTF8 compilerInterface/headerCheck jvmfmtCheck dummyBridge/test doc diff --git a/build.sbt b/build.sbt index dc22772..c55cea2 100644 --- a/build.sbt +++ b/build.sbt @@ -1,9 +1,11 @@ +import Dependencies._ import Util._ lazy val scala212 = "2.12.10" -ThisBuild / headerLicense := Some(HeaderLicense.Custom( - """Scala compiler interface +ThisBuild / headerLicense := Some( + HeaderLicense.Custom( + """Scala compiler interface | |Copyright Lightbend, Inc. and Mark Harrah | @@ -13,7 +15,8 @@ ThisBuild / headerLicense := Some(HeaderLicense.Custom( |See the NOTICE file distributed with this work for |additional information regarding copyright ownership. |""".stripMargin -)) + ) +) def commonSettings: Seq[Setting[_]] = Seq( Test / publishArtifact := false, @@ -26,7 +29,7 @@ def commonSettings: Seq[Setting[_]] = Seq( ) lazy val compilerInterfaceRoot = (project in file(".")) - .aggregate(compilerInterface) + .aggregate(compilerInterface, dummyBridge) .settings( publish / skip := true, crossScalaVersions := Vector(), @@ -40,11 +43,11 @@ lazy val compilerInterfaceRoot = (project in file(".")) | /_/ |welcome to the build for sbt/compiler-interface. |""".stripMargin + - (if (sys.props("java.specification.version") != "1.8") - s"""!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + (if (sys.props("java.specification.version") != "1.8") + s"""!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | Java version is ${sys.props("java.specification.version")}. We recommend 1.8. |!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""".stripMargin - else "") + else "") }, ) @@ -63,13 +66,24 @@ lazy val compilerInterface = (project in file("compiler-interface")) crossPaths := false, autoScalaLibrary := false, mimaPreviousArtifacts := Set( - "1.0.0", "1.0.1", "1.0.2", "1.0.3", "1.0.4", "1.0.5", - "1.1.0", "1.1.1", "1.1.2", "1.1.3", - "1.2.0", "1.2.1", "1.2.2", - ) map (version => - organization.value %% moduleName.value % version - cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) - ), + "1.0.0", + "1.0.1", + "1.0.2", + "1.0.3", + "1.0.4", + "1.0.5", + "1.1.0", + "1.1.1", + "1.1.2", + "1.1.3", + "1.2.0", + "1.2.1", + "1.2.2", + ) map ( + version => + organization.value %% moduleName.value % version + cross (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled) + ), mimaBinaryIssueFilters ++= { import com.typesafe.tools.mima.core._ import com.typesafe.tools.mima.core.ProblemFilters._ @@ -85,6 +99,33 @@ lazy val compilerInterface = (project in file("compiler-interface")) }, ) +lazy val dummyBridge = (project in file("dummy-bridge")) + .dependsOn(compilerInterface) + .settings( + name := "Dummy Compiler Bridge", + scalaVersion := "2.13.1", + publish / skip := true, + exportJars := true, + libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value, + testFrameworks += new TestFramework("verify.runner.Framework"), + libraryDependencies ++= Seq(verify % Test, sbtIo % Test), + // we need to fork because in unit tests we set usejavacp = true which means + // we are expecting all of our dependencies to be on classpath so Scala compiler + // can use them while constructing its own classpath for compilation + Test / fork := true, + // needed because we fork tests and tests are ran in parallel so we have multiple Scala + // compiler instances that are memory hungry + Test / javaOptions ++= { + val si = (Test / scalaInstance).value + val bridge = (Compile / packageBin).value + List( + "-Xmx1G", + "-Dtest.bridgejar=" + bridge.toString, + "-Dtest.sijars=" + si.allJars.toList.mkString(sys.props("path.separator")) + ) + }, + ) + ThisBuild / organization := "org.scala-sbt" ThisBuild / organizationName := "sbt" ThisBuild / organizationHomepage := Some(url("https://www.scala-sbt.org/")) @@ -100,11 +141,15 @@ ThisBuild / developers := List( ) ThisBuild / description := "a binary contract between Zinc and Scala compilers" -ThisBuild / licenses := List("Apache-2.0" -> new URL("http://www.apache.org/licenses/LICENSE-2.0.txt")) +ThisBuild / licenses := List( + "Apache-2.0" -> new URL("http://www.apache.org/licenses/LICENSE-2.0.txt") +) ThisBuild / homepage := Some(url("https://github.com/sbt/compiler-interface")) // Remove all additional repository other than Maven Central from POM -ThisBuild / pomIncludeRepository := { _ => false } +ThisBuild / pomIncludeRepository := { _ => + false +} ThisBuild / publishTo := { val nexus = "https://oss.sonatype.org/" if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots") diff --git a/compiler-interface/src/main/contraband-java/xsbti/compile/CompileOptions.java b/compiler-interface/src/main/contraband-java/xsbti/compile/CompileOptions.java index 5a649c4..277b1e6 100644 --- a/compiler-interface/src/main/contraband-java/xsbti/compile/CompileOptions.java +++ b/compiler-interface/src/main/contraband-java/xsbti/compile/CompileOptions.java @@ -13,40 +13,50 @@ public static CompileOptions create() { public static CompileOptions of() { return new CompileOptions(); } - public static CompileOptions create(java.io.File[] _classpath, java.io.File[] _sources, java.io.File _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order) { + public static CompileOptions create(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order) { return new CompileOptions(_classpath, _sources, _classesDirectory, _scalacOptions, _javacOptions, _maxErrors, _sourcePositionMapper, _order); } - public static CompileOptions of(java.io.File[] _classpath, java.io.File[] _sources, java.io.File _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order) { + public static CompileOptions of(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order) { return new CompileOptions(_classpath, _sources, _classesDirectory, _scalacOptions, _javacOptions, _maxErrors, _sourcePositionMapper, _order); } - public static CompileOptions create(java.io.File[] _classpath, java.io.File[] _sources, java.io.File _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory) { + public static CompileOptions create(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory) { return new CompileOptions(_classpath, _sources, _classesDirectory, _scalacOptions, _javacOptions, _maxErrors, _sourcePositionMapper, _order, _temporaryClassesDirectory); } - public static CompileOptions of(java.io.File[] _classpath, java.io.File[] _sources, java.io.File _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory) { + public static CompileOptions of(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory) { return new CompileOptions(_classpath, _sources, _classesDirectory, _scalacOptions, _javacOptions, _maxErrors, _sourcePositionMapper, _order, _temporaryClassesDirectory); } - private java.io.File[] classpath; - private java.io.File[] sources; - private java.io.File classesDirectory; + public static CompileOptions create(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory, java.util.Optional _converter, java.util.Optional _stamper) { + return new CompileOptions(_classpath, _sources, _classesDirectory, _scalacOptions, _javacOptions, _maxErrors, _sourcePositionMapper, _order, _temporaryClassesDirectory, _converter, _stamper); + } + public static CompileOptions of(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory, java.util.Optional _converter, java.util.Optional _stamper) { + return new CompileOptions(_classpath, _sources, _classesDirectory, _scalacOptions, _javacOptions, _maxErrors, _sourcePositionMapper, _order, _temporaryClassesDirectory, _converter, _stamper); + } + private xsbti.VirtualFile[] classpath; + private xsbti.VirtualFile[] sources; + private java.nio.file.Path classesDirectory; private String[] scalacOptions; private String[] javacOptions; private int maxErrors; private java.util.function.Function sourcePositionMapper; private xsbti.compile.CompileOrder order; - private java.util.Optional temporaryClassesDirectory; + private java.util.Optional temporaryClassesDirectory; + private java.util.Optional converter; + private java.util.Optional stamper; protected CompileOptions() { super(); - classpath = new java.io.File[0]; - sources = new java.io.File[0]; - classesDirectory = new java.io.File("classes"); + classpath = new xsbti.VirtualFile[0]; + sources = new xsbti.VirtualFile[0]; + classesDirectory = java.nio.file.Paths.get("classes"); scalacOptions = new String[0]; javacOptions = new String[0]; maxErrors = 100; sourcePositionMapper = new java.util.function.Function() { public xsbti.Position apply(xsbti.Position a) { return a; } }; order = xsbti.compile.CompileOrder.Mixed; temporaryClassesDirectory = java.util.Optional.empty(); + converter = java.util.Optional.empty(); + stamper = java.util.Optional.empty(); } - protected CompileOptions(java.io.File[] _classpath, java.io.File[] _sources, java.io.File _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order) { + protected CompileOptions(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order) { super(); classpath = _classpath; sources = _sources; @@ -57,8 +67,10 @@ protected CompileOptions(java.io.File[] _classpath, java.io.File[] _sources, jav sourcePositionMapper = _sourcePositionMapper; order = _order; temporaryClassesDirectory = java.util.Optional.empty(); + converter = java.util.Optional.empty(); + stamper = java.util.Optional.empty(); } - protected CompileOptions(java.io.File[] _classpath, java.io.File[] _sources, java.io.File _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory) { + protected CompileOptions(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory) { super(); classpath = _classpath; sources = _sources; @@ -69,22 +81,38 @@ protected CompileOptions(java.io.File[] _classpath, java.io.File[] _sources, jav sourcePositionMapper = _sourcePositionMapper; order = _order; temporaryClassesDirectory = _temporaryClassesDirectory; + converter = java.util.Optional.empty(); + stamper = java.util.Optional.empty(); + } + protected CompileOptions(xsbti.VirtualFile[] _classpath, xsbti.VirtualFile[] _sources, java.nio.file.Path _classesDirectory, String[] _scalacOptions, String[] _javacOptions, int _maxErrors, java.util.function.Function _sourcePositionMapper, xsbti.compile.CompileOrder _order, java.util.Optional _temporaryClassesDirectory, java.util.Optional _converter, java.util.Optional _stamper) { + super(); + classpath = _classpath; + sources = _sources; + classesDirectory = _classesDirectory; + scalacOptions = _scalacOptions; + javacOptions = _javacOptions; + maxErrors = _maxErrors; + sourcePositionMapper = _sourcePositionMapper; + order = _order; + temporaryClassesDirectory = _temporaryClassesDirectory; + converter = _converter; + stamper = _stamper; } /** * The classpath to use for compilation. * This will be modified according to the ClasspathOptions used to configure the ScalaCompiler. */ - public java.io.File[] classpath() { + public xsbti.VirtualFile[] classpath() { return this.classpath; } /** * All sources that should be recompiled. * This should include Scala and Java sources, which are identified by their extension. */ - public java.io.File[] sources() { + public xsbti.VirtualFile[] sources() { return this.sources; } - public java.io.File classesDirectory() { + public java.nio.file.Path classesDirectory() { return this.classesDirectory; } /** The options to pass to the Scala compiler other than the sources and classpath to use. */ @@ -110,35 +138,49 @@ public xsbti.compile.CompileOrder order() { * of any kind. The lifetime of these compilation products is short and the temporary * classes directory only needs to exist during one incremental compiler cycle. */ - public java.util.Optional temporaryClassesDirectory() { + public java.util.Optional temporaryClassesDirectory() { return this.temporaryClassesDirectory; } - public CompileOptions withClasspath(java.io.File[] classpath) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + /** FileConverter to convert between Path and VirtualFileRef. */ + public java.util.Optional converter() { + return this.converter; } - public CompileOptions withSources(java.io.File[] sources) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + /** ReadStamps to calculate timestamp or hash. */ + public java.util.Optional stamper() { + return this.stamper; } - public CompileOptions withClassesDirectory(java.io.File classesDirectory) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + public CompileOptions withClasspath(xsbti.VirtualFile[] classpath) { + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); + } + public CompileOptions withSources(xsbti.VirtualFile[] sources) { + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); + } + public CompileOptions withClassesDirectory(java.nio.file.Path classesDirectory) { + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); } public CompileOptions withScalacOptions(String[] scalacOptions) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); } public CompileOptions withJavacOptions(String[] javacOptions) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); } public CompileOptions withMaxErrors(int maxErrors) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); } public CompileOptions withSourcePositionMapper(java.util.function.Function sourcePositionMapper) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); } public CompileOptions withOrder(xsbti.compile.CompileOrder order) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); + } + public CompileOptions withTemporaryClassesDirectory(java.util.Optional temporaryClassesDirectory) { + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); + } + public CompileOptions withConverter(java.util.Optional converter) { + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); } - public CompileOptions withTemporaryClassesDirectory(java.util.Optional temporaryClassesDirectory) { - return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory); + public CompileOptions withStamper(java.util.Optional stamper) { + return new CompileOptions(classpath, sources, classesDirectory, scalacOptions, javacOptions, maxErrors, sourcePositionMapper, order, temporaryClassesDirectory, converter, stamper); } public boolean equals(Object obj) { if (this == obj) { @@ -147,13 +189,13 @@ public boolean equals(Object obj) { return false; } else { CompileOptions o = (CompileOptions)obj; - return java.util.Arrays.deepEquals(this.classpath(), o.classpath()) && java.util.Arrays.deepEquals(this.sources(), o.sources()) && this.classesDirectory().equals(o.classesDirectory()) && java.util.Arrays.deepEquals(this.scalacOptions(), o.scalacOptions()) && java.util.Arrays.deepEquals(this.javacOptions(), o.javacOptions()) && (this.maxErrors() == o.maxErrors()) && this.sourcePositionMapper().equals(o.sourcePositionMapper()) && this.order().equals(o.order()) && this.temporaryClassesDirectory().equals(o.temporaryClassesDirectory()); + return java.util.Arrays.deepEquals(this.classpath(), o.classpath()) && java.util.Arrays.deepEquals(this.sources(), o.sources()) && this.classesDirectory().equals(o.classesDirectory()) && java.util.Arrays.deepEquals(this.scalacOptions(), o.scalacOptions()) && java.util.Arrays.deepEquals(this.javacOptions(), o.javacOptions()) && (this.maxErrors() == o.maxErrors()) && this.sourcePositionMapper().equals(o.sourcePositionMapper()) && this.order().equals(o.order()) && this.temporaryClassesDirectory().equals(o.temporaryClassesDirectory()) && this.converter().equals(o.converter()) && this.stamper().equals(o.stamper()); } } public int hashCode() { - return 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "xsbti.compile.CompileOptions".hashCode()) + java.util.Arrays.deepHashCode(classpath())) + java.util.Arrays.deepHashCode(sources())) + classesDirectory().hashCode()) + java.util.Arrays.deepHashCode(scalacOptions())) + java.util.Arrays.deepHashCode(javacOptions())) + (new Integer(maxErrors())).hashCode()) + sourcePositionMapper().hashCode()) + order().hashCode()) + temporaryClassesDirectory().hashCode()); + return 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "xsbti.compile.CompileOptions".hashCode()) + java.util.Arrays.deepHashCode(classpath())) + java.util.Arrays.deepHashCode(sources())) + classesDirectory().hashCode()) + java.util.Arrays.deepHashCode(scalacOptions())) + java.util.Arrays.deepHashCode(javacOptions())) + (new Integer(maxErrors())).hashCode()) + sourcePositionMapper().hashCode()) + order().hashCode()) + temporaryClassesDirectory().hashCode()) + converter().hashCode()) + stamper().hashCode()); } public String toString() { - return "CompileOptions(" + "classpath: " + classpath() + ", " + "sources: " + sources() + ", " + "classesDirectory: " + classesDirectory() + ", " + "scalacOptions: " + scalacOptions() + ", " + "javacOptions: " + javacOptions() + ", " + "maxErrors: " + maxErrors() + ", " + "sourcePositionMapper: " + sourcePositionMapper() + ", " + "order: " + order() + ", " + "temporaryClassesDirectory: " + temporaryClassesDirectory() + ")"; + return "CompileOptions(" + "classpath: " + classpath() + ", " + "sources: " + sources() + ", " + "classesDirectory: " + classesDirectory() + ", " + "scalacOptions: " + scalacOptions() + ", " + "javacOptions: " + javacOptions() + ", " + "maxErrors: " + maxErrors() + ", " + "sourcePositionMapper: " + sourcePositionMapper() + ", " + "order: " + order() + ", " + "temporaryClassesDirectory: " + temporaryClassesDirectory() + ", " + "converter: " + converter() + ", " + "stamper: " + stamper() + ")"; } } diff --git a/compiler-interface/src/main/contraband-java/xsbti/compile/FileHash.java b/compiler-interface/src/main/contraband-java/xsbti/compile/FileHash.java index 93db6d1..90b7df4 100644 --- a/compiler-interface/src/main/contraband-java/xsbti/compile/FileHash.java +++ b/compiler-interface/src/main/contraband-java/xsbti/compile/FileHash.java @@ -6,27 +6,27 @@ package xsbti.compile; public final class FileHash implements java.io.Serializable { - public static FileHash create(java.io.File _file, int _hash) { + public static FileHash create(java.nio.file.Path _file, int _hash) { return new FileHash(_file, _hash); } - public static FileHash of(java.io.File _file, int _hash) { + public static FileHash of(java.nio.file.Path _file, int _hash) { return new FileHash(_file, _hash); } - private java.io.File file; + private java.nio.file.Path file; private int hash; - protected FileHash(java.io.File _file, int _hash) { + protected FileHash(java.nio.file.Path _file, int _hash) { super(); file = _file; hash = _hash; } - public java.io.File file() { + public java.nio.file.Path file() { return this.file; } public int hash() { return this.hash; } - public FileHash withFile(java.io.File file) { + public FileHash withFile(java.nio.file.Path file) { return new FileHash(file, hash); } public FileHash withHash(int hash) { diff --git a/compiler-interface/src/main/contraband-java/xsbti/compile/IncOptions.java b/compiler-interface/src/main/contraband-java/xsbti/compile/IncOptions.java index 9f619ce..751c6ce 100644 --- a/compiler-interface/src/main/contraband-java/xsbti/compile/IncOptions.java +++ b/compiler-interface/src/main/contraband-java/xsbti/compile/IncOptions.java @@ -70,6 +70,9 @@ public static boolean defaultLogRecompileOnMacro() { public static boolean defaultStrictMode() { return false; } + public static boolean defaultAllowMachinePath() { + return true; + } public static IncOptions create() { return new IncOptions(); } @@ -94,6 +97,12 @@ public static IncOptions create(int _transitiveStep, double _recompileAllFractio public static IncOptions of(int _transitiveStep, double _recompileAllFraction, boolean _relationsDebug, boolean _apiDebug, int _apiDiffContextSize, java.util.Optional _apiDumpDirectory, java.util.Optional _classfileManagerType, boolean _useCustomizedFileManager, java.util.Optional _recompileOnMacroDef, boolean _useOptimizedSealed, boolean _storeApis, boolean _enabled, java.util.Map _extra, boolean _logRecompileOnMacro, xsbti.compile.ExternalHooks _externalHooks, String[] _ignoredScalacOptions, boolean _strictMode) { return new IncOptions(_transitiveStep, _recompileAllFraction, _relationsDebug, _apiDebug, _apiDiffContextSize, _apiDumpDirectory, _classfileManagerType, _useCustomizedFileManager, _recompileOnMacroDef, _useOptimizedSealed, _storeApis, _enabled, _extra, _logRecompileOnMacro, _externalHooks, _ignoredScalacOptions, _strictMode); } + public static IncOptions create(int _transitiveStep, double _recompileAllFraction, boolean _relationsDebug, boolean _apiDebug, int _apiDiffContextSize, java.util.Optional _apiDumpDirectory, java.util.Optional _classfileManagerType, boolean _useCustomizedFileManager, java.util.Optional _recompileOnMacroDef, boolean _useOptimizedSealed, boolean _storeApis, boolean _enabled, java.util.Map _extra, boolean _logRecompileOnMacro, xsbti.compile.ExternalHooks _externalHooks, String[] _ignoredScalacOptions, boolean _strictMode, boolean _allowMachinePath) { + return new IncOptions(_transitiveStep, _recompileAllFraction, _relationsDebug, _apiDebug, _apiDiffContextSize, _apiDumpDirectory, _classfileManagerType, _useCustomizedFileManager, _recompileOnMacroDef, _useOptimizedSealed, _storeApis, _enabled, _extra, _logRecompileOnMacro, _externalHooks, _ignoredScalacOptions, _strictMode, _allowMachinePath); + } + public static IncOptions of(int _transitiveStep, double _recompileAllFraction, boolean _relationsDebug, boolean _apiDebug, int _apiDiffContextSize, java.util.Optional _apiDumpDirectory, java.util.Optional _classfileManagerType, boolean _useCustomizedFileManager, java.util.Optional _recompileOnMacroDef, boolean _useOptimizedSealed, boolean _storeApis, boolean _enabled, java.util.Map _extra, boolean _logRecompileOnMacro, xsbti.compile.ExternalHooks _externalHooks, String[] _ignoredScalacOptions, boolean _strictMode, boolean _allowMachinePath) { + return new IncOptions(_transitiveStep, _recompileAllFraction, _relationsDebug, _apiDebug, _apiDiffContextSize, _apiDumpDirectory, _classfileManagerType, _useCustomizedFileManager, _recompileOnMacroDef, _useOptimizedSealed, _storeApis, _enabled, _extra, _logRecompileOnMacro, _externalHooks, _ignoredScalacOptions, _strictMode, _allowMachinePath); + } private int transitiveStep; private double recompileAllFraction; private boolean relationsDebug; @@ -111,6 +120,7 @@ public static IncOptions of(int _transitiveStep, double _recompileAllFraction, b private xsbti.compile.ExternalHooks externalHooks; private String[] ignoredScalacOptions; private boolean strictMode; + private boolean allowMachinePath; protected IncOptions() { super(); transitiveStep = xsbti.compile.IncOptions.defaultTransitiveStep(); @@ -130,6 +140,7 @@ protected IncOptions() { externalHooks = xsbti.compile.IncOptions.defaultExternal(); ignoredScalacOptions = xsbti.compile.IncOptions.defaultIgnoredScalacOptions(); strictMode = xsbti.compile.IncOptions.defaultStrictMode(); + allowMachinePath = xsbti.compile.IncOptions.defaultAllowMachinePath(); } protected IncOptions(int _transitiveStep, double _recompileAllFraction, boolean _relationsDebug, boolean _apiDebug, int _apiDiffContextSize, java.util.Optional _apiDumpDirectory, java.util.Optional _classfileManagerType, boolean _useCustomizedFileManager, java.util.Optional _recompileOnMacroDef, boolean _useOptimizedSealed, boolean _storeApis, boolean _enabled, java.util.Map _extra, boolean _logRecompileOnMacro, xsbti.compile.ExternalHooks _externalHooks) { super(); @@ -150,6 +161,7 @@ protected IncOptions(int _transitiveStep, double _recompileAllFraction, boolean externalHooks = _externalHooks; ignoredScalacOptions = xsbti.compile.IncOptions.defaultIgnoredScalacOptions(); strictMode = xsbti.compile.IncOptions.defaultStrictMode(); + allowMachinePath = xsbti.compile.IncOptions.defaultAllowMachinePath(); } protected IncOptions(int _transitiveStep, double _recompileAllFraction, boolean _relationsDebug, boolean _apiDebug, int _apiDiffContextSize, java.util.Optional _apiDumpDirectory, java.util.Optional _classfileManagerType, boolean _useCustomizedFileManager, java.util.Optional _recompileOnMacroDef, boolean _useOptimizedSealed, boolean _storeApis, boolean _enabled, java.util.Map _extra, boolean _logRecompileOnMacro, xsbti.compile.ExternalHooks _externalHooks, String[] _ignoredScalacOptions) { super(); @@ -170,6 +182,7 @@ protected IncOptions(int _transitiveStep, double _recompileAllFraction, boolean externalHooks = _externalHooks; ignoredScalacOptions = _ignoredScalacOptions; strictMode = xsbti.compile.IncOptions.defaultStrictMode(); + allowMachinePath = xsbti.compile.IncOptions.defaultAllowMachinePath(); } protected IncOptions(int _transitiveStep, double _recompileAllFraction, boolean _relationsDebug, boolean _apiDebug, int _apiDiffContextSize, java.util.Optional _apiDumpDirectory, java.util.Optional _classfileManagerType, boolean _useCustomizedFileManager, java.util.Optional _recompileOnMacroDef, boolean _useOptimizedSealed, boolean _storeApis, boolean _enabled, java.util.Map _extra, boolean _logRecompileOnMacro, xsbti.compile.ExternalHooks _externalHooks, String[] _ignoredScalacOptions, boolean _strictMode) { super(); @@ -190,6 +203,28 @@ protected IncOptions(int _transitiveStep, double _recompileAllFraction, boolean externalHooks = _externalHooks; ignoredScalacOptions = _ignoredScalacOptions; strictMode = _strictMode; + allowMachinePath = xsbti.compile.IncOptions.defaultAllowMachinePath(); + } + protected IncOptions(int _transitiveStep, double _recompileAllFraction, boolean _relationsDebug, boolean _apiDebug, int _apiDiffContextSize, java.util.Optional _apiDumpDirectory, java.util.Optional _classfileManagerType, boolean _useCustomizedFileManager, java.util.Optional _recompileOnMacroDef, boolean _useOptimizedSealed, boolean _storeApis, boolean _enabled, java.util.Map _extra, boolean _logRecompileOnMacro, xsbti.compile.ExternalHooks _externalHooks, String[] _ignoredScalacOptions, boolean _strictMode, boolean _allowMachinePath) { + super(); + transitiveStep = _transitiveStep; + recompileAllFraction = _recompileAllFraction; + relationsDebug = _relationsDebug; + apiDebug = _apiDebug; + apiDiffContextSize = _apiDiffContextSize; + apiDumpDirectory = _apiDumpDirectory; + classfileManagerType = _classfileManagerType; + useCustomizedFileManager = _useCustomizedFileManager; + recompileOnMacroDef = _recompileOnMacroDef; + useOptimizedSealed = _useOptimizedSealed; + storeApis = _storeApis; + enabled = _enabled; + extra = _extra; + logRecompileOnMacro = _logRecompileOnMacro; + externalHooks = _externalHooks; + ignoredScalacOptions = _ignoredScalacOptions; + strictMode = _strictMode; + allowMachinePath = _allowMachinePath; } /** After which step include whole transitive closure of invalidated source files. */ public int transitiveStep() { @@ -288,56 +323,62 @@ public String[] ignoredScalacOptions() { public boolean strictMode() { return this.strictMode; } + public boolean allowMachinePath() { + return this.allowMachinePath; + } public IncOptions withTransitiveStep(int transitiveStep) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withRecompileAllFraction(double recompileAllFraction) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withRelationsDebug(boolean relationsDebug) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withApiDebug(boolean apiDebug) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withApiDiffContextSize(int apiDiffContextSize) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withApiDumpDirectory(java.util.Optional apiDumpDirectory) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withClassfileManagerType(java.util.Optional classfileManagerType) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withUseCustomizedFileManager(boolean useCustomizedFileManager) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withRecompileOnMacroDef(java.util.Optional recompileOnMacroDef) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withUseOptimizedSealed(boolean useOptimizedSealed) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withStoreApis(boolean storeApis) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withEnabled(boolean enabled) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withExtra(java.util.Map extra) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withLogRecompileOnMacro(boolean logRecompileOnMacro) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withExternalHooks(xsbti.compile.ExternalHooks externalHooks) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withIgnoredScalacOptions(String[] ignoredScalacOptions) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public IncOptions withStrictMode(boolean strictMode) { - return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode); + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); + } + public IncOptions withAllowMachinePath(boolean allowMachinePath) { + return new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, apiDumpDirectory, classfileManagerType, useCustomizedFileManager, recompileOnMacroDef, useOptimizedSealed, storeApis, enabled, extra, logRecompileOnMacro, externalHooks, ignoredScalacOptions, strictMode, allowMachinePath); } public boolean equals(Object obj) { if (this == obj) { @@ -346,13 +387,13 @@ public boolean equals(Object obj) { return false; } else { IncOptions o = (IncOptions)obj; - return (this.transitiveStep() == o.transitiveStep()) && (this.recompileAllFraction() == o.recompileAllFraction()) && (this.relationsDebug() == o.relationsDebug()) && (this.apiDebug() == o.apiDebug()) && (this.apiDiffContextSize() == o.apiDiffContextSize()) && this.apiDumpDirectory().equals(o.apiDumpDirectory()) && this.classfileManagerType().equals(o.classfileManagerType()) && (this.useCustomizedFileManager() == o.useCustomizedFileManager()) && this.recompileOnMacroDef().equals(o.recompileOnMacroDef()) && (this.useOptimizedSealed() == o.useOptimizedSealed()) && (this.storeApis() == o.storeApis()) && (this.enabled() == o.enabled()) && this.extra().equals(o.extra()) && (this.logRecompileOnMacro() == o.logRecompileOnMacro()) && this.externalHooks().equals(o.externalHooks()) && this.ignoredScalacOptions().equals(o.ignoredScalacOptions()) && (this.strictMode() == o.strictMode()); + return (this.transitiveStep() == o.transitiveStep()) && (this.recompileAllFraction() == o.recompileAllFraction()) && (this.relationsDebug() == o.relationsDebug()) && (this.apiDebug() == o.apiDebug()) && (this.apiDiffContextSize() == o.apiDiffContextSize()) && this.apiDumpDirectory().equals(o.apiDumpDirectory()) && this.classfileManagerType().equals(o.classfileManagerType()) && (this.useCustomizedFileManager() == o.useCustomizedFileManager()) && this.recompileOnMacroDef().equals(o.recompileOnMacroDef()) && (this.useOptimizedSealed() == o.useOptimizedSealed()) && (this.storeApis() == o.storeApis()) && (this.enabled() == o.enabled()) && this.extra().equals(o.extra()) && (this.logRecompileOnMacro() == o.logRecompileOnMacro()) && this.externalHooks().equals(o.externalHooks()) && this.ignoredScalacOptions().equals(o.ignoredScalacOptions()) && (this.strictMode() == o.strictMode()) && (this.allowMachinePath() == o.allowMachinePath()); } } public int hashCode() { - return 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "xsbti.compile.IncOptions".hashCode()) + (new Integer(transitiveStep())).hashCode()) + (new Double(recompileAllFraction())).hashCode()) + (new Boolean(relationsDebug())).hashCode()) + (new Boolean(apiDebug())).hashCode()) + (new Integer(apiDiffContextSize())).hashCode()) + apiDumpDirectory().hashCode()) + classfileManagerType().hashCode()) + (new Boolean(useCustomizedFileManager())).hashCode()) + recompileOnMacroDef().hashCode()) + (new Boolean(useOptimizedSealed())).hashCode()) + (new Boolean(storeApis())).hashCode()) + (new Boolean(enabled())).hashCode()) + extra().hashCode()) + (new Boolean(logRecompileOnMacro())).hashCode()) + externalHooks().hashCode()) + ignoredScalacOptions().hashCode()) + (new Boolean(strictMode())).hashCode()); + return 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "xsbti.compile.IncOptions".hashCode()) + (new Integer(transitiveStep())).hashCode()) + (new Double(recompileAllFraction())).hashCode()) + (new Boolean(relationsDebug())).hashCode()) + (new Boolean(apiDebug())).hashCode()) + (new Integer(apiDiffContextSize())).hashCode()) + apiDumpDirectory().hashCode()) + classfileManagerType().hashCode()) + (new Boolean(useCustomizedFileManager())).hashCode()) + recompileOnMacroDef().hashCode()) + (new Boolean(useOptimizedSealed())).hashCode()) + (new Boolean(storeApis())).hashCode()) + (new Boolean(enabled())).hashCode()) + extra().hashCode()) + (new Boolean(logRecompileOnMacro())).hashCode()) + externalHooks().hashCode()) + ignoredScalacOptions().hashCode()) + (new Boolean(strictMode())).hashCode()) + (new Boolean(allowMachinePath())).hashCode()); } public String toString() { - return "IncOptions(" + "transitiveStep: " + transitiveStep() + ", " + "recompileAllFraction: " + recompileAllFraction() + ", " + "relationsDebug: " + relationsDebug() + ", " + "apiDebug: " + apiDebug() + ", " + "apiDiffContextSize: " + apiDiffContextSize() + ", " + "apiDumpDirectory: " + apiDumpDirectory() + ", " + "classfileManagerType: " + classfileManagerType() + ", " + "useCustomizedFileManager: " + useCustomizedFileManager() + ", " + "recompileOnMacroDef: " + recompileOnMacroDef() + ", " + "useOptimizedSealed: " + useOptimizedSealed() + ", " + "storeApis: " + storeApis() + ", " + "enabled: " + enabled() + ", " + "extra: " + extra() + ", " + "logRecompileOnMacro: " + logRecompileOnMacro() + ", " + "externalHooks: " + externalHooks() + ", " + "ignoredScalacOptions: " + ignoredScalacOptions() + ", " + "strictMode: " + strictMode() + ")"; + return "IncOptions(" + "transitiveStep: " + transitiveStep() + ", " + "recompileAllFraction: " + recompileAllFraction() + ", " + "relationsDebug: " + relationsDebug() + ", " + "apiDebug: " + apiDebug() + ", " + "apiDiffContextSize: " + apiDiffContextSize() + ", " + "apiDumpDirectory: " + apiDumpDirectory() + ", " + "classfileManagerType: " + classfileManagerType() + ", " + "useCustomizedFileManager: " + useCustomizedFileManager() + ", " + "recompileOnMacroDef: " + recompileOnMacroDef() + ", " + "useOptimizedSealed: " + useOptimizedSealed() + ", " + "storeApis: " + storeApis() + ", " + "enabled: " + enabled() + ", " + "extra: " + extra() + ", " + "logRecompileOnMacro: " + logRecompileOnMacro() + ", " + "externalHooks: " + externalHooks() + ", " + "ignoredScalacOptions: " + ignoredScalacOptions() + ", " + "strictMode: " + strictMode() + ", " + "allowMachinePath: " + allowMachinePath() + ")"; } } diff --git a/compiler-interface/src/main/contraband-java/xsbti/compile/Setup.java b/compiler-interface/src/main/contraband-java/xsbti/compile/Setup.java index 2291e49..5db4d17 100644 --- a/compiler-interface/src/main/contraband-java/xsbti/compile/Setup.java +++ b/compiler-interface/src/main/contraband-java/xsbti/compile/Setup.java @@ -6,26 +6,37 @@ package xsbti.compile; /** Configures incremental recompilation. */ public final class Setup implements java.io.Serializable { - + public java.io.File cacheFile() { + return this.cachePath.toFile(); + } + public Setup withCacheFile(java.io.File cacheFile) { + return this.withCachePath(cacheFile.toPath()); + } public static Setup create(xsbti.compile.PerClasspathEntryLookup _perClasspathEntryLookup, boolean _skip, java.io.File _cacheFile, xsbti.compile.GlobalsCache _cache, xsbti.compile.IncOptions _incrementalCompilerOptions, xsbti.Reporter _reporter, java.util.Optional _progress, xsbti.T2[] _extra) { - return new Setup(_perClasspathEntryLookup, _skip, _cacheFile, _cache, _incrementalCompilerOptions, _reporter, _progress, _extra); + return new Setup(_perClasspathEntryLookup, _skip, _cacheFile.toPath(), _cache, _incrementalCompilerOptions, _reporter, _progress, _extra); } public static Setup of(xsbti.compile.PerClasspathEntryLookup _perClasspathEntryLookup, boolean _skip, java.io.File _cacheFile, xsbti.compile.GlobalsCache _cache, xsbti.compile.IncOptions _incrementalCompilerOptions, xsbti.Reporter _reporter, java.util.Optional _progress, xsbti.T2[] _extra) { - return new Setup(_perClasspathEntryLookup, _skip, _cacheFile, _cache, _incrementalCompilerOptions, _reporter, _progress, _extra); + return new Setup(_perClasspathEntryLookup, _skip, _cacheFile.toPath(), _cache, _incrementalCompilerOptions, _reporter, _progress, _extra); + } + public static Setup create(xsbti.compile.PerClasspathEntryLookup _perClasspathEntryLookup, boolean _skip, java.nio.file.Path _cachePath, xsbti.compile.GlobalsCache _cache, xsbti.compile.IncOptions _incrementalCompilerOptions, xsbti.Reporter _reporter, java.util.Optional _progress, xsbti.T2[] _extra) { + return new Setup(_perClasspathEntryLookup, _skip, _cachePath, _cache, _incrementalCompilerOptions, _reporter, _progress, _extra); + } + public static Setup of(xsbti.compile.PerClasspathEntryLookup _perClasspathEntryLookup, boolean _skip, java.nio.file.Path _cachePath, xsbti.compile.GlobalsCache _cache, xsbti.compile.IncOptions _incrementalCompilerOptions, xsbti.Reporter _reporter, java.util.Optional _progress, xsbti.T2[] _extra) { + return new Setup(_perClasspathEntryLookup, _skip, _cachePath, _cache, _incrementalCompilerOptions, _reporter, _progress, _extra); } private xsbti.compile.PerClasspathEntryLookup perClasspathEntryLookup; private boolean skip; - private java.io.File cacheFile; + private java.nio.file.Path cachePath; private xsbti.compile.GlobalsCache cache; private xsbti.compile.IncOptions incrementalCompilerOptions; private xsbti.Reporter reporter; private java.util.Optional progress; private xsbti.T2[] extra; - protected Setup(xsbti.compile.PerClasspathEntryLookup _perClasspathEntryLookup, boolean _skip, java.io.File _cacheFile, xsbti.compile.GlobalsCache _cache, xsbti.compile.IncOptions _incrementalCompilerOptions, xsbti.Reporter _reporter, java.util.Optional _progress, xsbti.T2[] _extra) { + protected Setup(xsbti.compile.PerClasspathEntryLookup _perClasspathEntryLookup, boolean _skip, java.nio.file.Path _cachePath, xsbti.compile.GlobalsCache _cache, xsbti.compile.IncOptions _incrementalCompilerOptions, xsbti.Reporter _reporter, java.util.Optional _progress, xsbti.T2[] _extra) { super(); perClasspathEntryLookup = _perClasspathEntryLookup; skip = _skip; - cacheFile = _cacheFile; + cachePath = _cachePath; cache = _cache; incrementalCompilerOptions = _incrementalCompilerOptions; reporter = _reporter; @@ -45,8 +56,8 @@ public boolean skip() { * This file can be removed to force a full recompilation. * The file should be unique and not shared between compilations. */ - public java.io.File cacheFile() { - return this.cacheFile; + public java.nio.file.Path cachePath() { + return this.cachePath; } public xsbti.compile.GlobalsCache cache() { return this.cache; @@ -66,28 +77,28 @@ public xsbti.T2[] extra() { return this.extra; } public Setup withPerClasspathEntryLookup(xsbti.compile.PerClasspathEntryLookup perClasspathEntryLookup) { - return new Setup(perClasspathEntryLookup, skip, cacheFile, cache, incrementalCompilerOptions, reporter, progress, extra); + return new Setup(perClasspathEntryLookup, skip, cachePath, cache, incrementalCompilerOptions, reporter, progress, extra); } public Setup withSkip(boolean skip) { - return new Setup(perClasspathEntryLookup, skip, cacheFile, cache, incrementalCompilerOptions, reporter, progress, extra); + return new Setup(perClasspathEntryLookup, skip, cachePath, cache, incrementalCompilerOptions, reporter, progress, extra); } - public Setup withCacheFile(java.io.File cacheFile) { - return new Setup(perClasspathEntryLookup, skip, cacheFile, cache, incrementalCompilerOptions, reporter, progress, extra); + public Setup withCachePath(java.nio.file.Path cachePath) { + return new Setup(perClasspathEntryLookup, skip, cachePath, cache, incrementalCompilerOptions, reporter, progress, extra); } public Setup withCache(xsbti.compile.GlobalsCache cache) { - return new Setup(perClasspathEntryLookup, skip, cacheFile, cache, incrementalCompilerOptions, reporter, progress, extra); + return new Setup(perClasspathEntryLookup, skip, cachePath, cache, incrementalCompilerOptions, reporter, progress, extra); } public Setup withIncrementalCompilerOptions(xsbti.compile.IncOptions incrementalCompilerOptions) { - return new Setup(perClasspathEntryLookup, skip, cacheFile, cache, incrementalCompilerOptions, reporter, progress, extra); + return new Setup(perClasspathEntryLookup, skip, cachePath, cache, incrementalCompilerOptions, reporter, progress, extra); } public Setup withReporter(xsbti.Reporter reporter) { - return new Setup(perClasspathEntryLookup, skip, cacheFile, cache, incrementalCompilerOptions, reporter, progress, extra); + return new Setup(perClasspathEntryLookup, skip, cachePath, cache, incrementalCompilerOptions, reporter, progress, extra); } public Setup withProgress(java.util.Optional progress) { - return new Setup(perClasspathEntryLookup, skip, cacheFile, cache, incrementalCompilerOptions, reporter, progress, extra); + return new Setup(perClasspathEntryLookup, skip, cachePath, cache, incrementalCompilerOptions, reporter, progress, extra); } public Setup withExtra(xsbti.T2[] extra) { - return new Setup(perClasspathEntryLookup, skip, cacheFile, cache, incrementalCompilerOptions, reporter, progress, extra); + return new Setup(perClasspathEntryLookup, skip, cachePath, cache, incrementalCompilerOptions, reporter, progress, extra); } public boolean equals(Object obj) { if (this == obj) { @@ -96,13 +107,13 @@ public boolean equals(Object obj) { return false; } else { Setup o = (Setup)obj; - return this.perClasspathEntryLookup().equals(o.perClasspathEntryLookup()) && (this.skip() == o.skip()) && this.cacheFile().equals(o.cacheFile()) && this.cache().equals(o.cache()) && this.incrementalCompilerOptions().equals(o.incrementalCompilerOptions()) && this.reporter().equals(o.reporter()) && this.progress().equals(o.progress()) && java.util.Arrays.deepEquals(this.extra(), o.extra()); + return this.perClasspathEntryLookup().equals(o.perClasspathEntryLookup()) && (this.skip() == o.skip()) && this.cachePath().equals(o.cachePath()) && this.cache().equals(o.cache()) && this.incrementalCompilerOptions().equals(o.incrementalCompilerOptions()) && this.reporter().equals(o.reporter()) && this.progress().equals(o.progress()) && java.util.Arrays.deepEquals(this.extra(), o.extra()); } } public int hashCode() { - return 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "xsbti.compile.Setup".hashCode()) + perClasspathEntryLookup().hashCode()) + (new Boolean(skip())).hashCode()) + cacheFile().hashCode()) + cache().hashCode()) + incrementalCompilerOptions().hashCode()) + reporter().hashCode()) + progress().hashCode()) + java.util.Arrays.deepHashCode(extra())); + return 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "xsbti.compile.Setup".hashCode()) + perClasspathEntryLookup().hashCode()) + (new Boolean(skip())).hashCode()) + cachePath().hashCode()) + cache().hashCode()) + incrementalCompilerOptions().hashCode()) + reporter().hashCode()) + progress().hashCode()) + java.util.Arrays.deepHashCode(extra())); } public String toString() { - return "Setup(" + "perClasspathEntryLookup: " + perClasspathEntryLookup() + ", " + "skip: " + skip() + ", " + "cacheFile: " + cacheFile() + ", " + "cache: " + cache() + ", " + "incrementalCompilerOptions: " + incrementalCompilerOptions() + ", " + "reporter: " + reporter() + ", " + "progress: " + progress() + ", " + "extra: " + extra() + ")"; + return "Setup(" + "perClasspathEntryLookup: " + perClasspathEntryLookup() + ", " + "skip: " + skip() + ", " + "cachePath: " + cachePath() + ", " + "cache: " + cache() + ", " + "incrementalCompilerOptions: " + incrementalCompilerOptions() + ", " + "reporter: " + reporter() + ", " + "progress: " + progress() + ", " + "extra: " + extra() + ")"; } } diff --git a/compiler-interface/src/main/contraband/incremental.json b/compiler-interface/src/main/contraband/incremental.json index 0f303a2..d76e0f2 100644 --- a/compiler-interface/src/main/contraband/incremental.json +++ b/compiler-interface/src/main/contraband/incremental.json @@ -12,8 +12,8 @@ "target": "Java", "type": "record", "doc": [ - "Constructs a minimal ClassfileManager that immediately deletes class files when requested.", - "This is the default classfile manager if no type is provided in incremental options." + "Constructs a minimal ClassfileManager that immediately deletes class files when requested.", + "This is the default classfile manager if no type is provided in incremental options." ] }, { @@ -27,8 +27,14 @@ "keeps the successfully generated class files from the new compilation." ], "fields": [ - { "name": "backupDirectory", "type": "java.io.File" }, - { "name": "logger", "type": "xsbti.Logger" } + { + "name": "backupDirectory", + "type": "java.io.File" + }, + { + "name": "logger", + "type": "xsbti.Logger" + } ] } ] @@ -194,6 +200,12 @@ "Can be useful for debugging incremental compilation issues." ], "since": "1.2.6" + }, + { + "name": "allowMachinePath", + "type": "boolean", + "default": "xsbti.compile.IncOptions.defaultAllowMachinePath()", + "since": "1.4.0" } ], "extra": [ @@ -257,6 +269,9 @@ "}", "public static boolean defaultStrictMode() {", " return false;", + "}", + "public static boolean defaultAllowMachinePath() {", + " return true;", "}" ] }, @@ -269,8 +284,8 @@ "fields": [ { "name": "classpath", - "type": "java.io.File*", - "default": "new java.io.File[0]", + "type": "xsbti.VirtualFile*", + "default": "new xsbti.VirtualFile[0]", "doc": [ "The classpath to use for compilation.", "This will be modified according to the ClasspathOptions used to configure the ScalaCompiler." @@ -279,8 +294,8 @@ }, { "name": "sources", - "type": "java.io.File*", - "default": "new java.io.File[0]", + "type": "xsbti.VirtualFile*", + "default": "new xsbti.VirtualFile[0]", "doc": [ "All sources that should be recompiled.", "This should include Scala and Java sources, which are identified by their extension." @@ -289,8 +304,8 @@ }, { "name": "classesDirectory", - "type": "java.io.File", - "default": "new java.io.File(\"classes\")", + "type": "java.nio.file.Path", + "default": "java.nio.file.Paths.get(\"classes\")", "since": "0.1.0" }, { @@ -328,7 +343,7 @@ }, { "name": "temporaryClassesDirectory", - "type": "java.util.Optional", + "type": "java.util.Optional", "default": "java.util.Optional.empty()", "doc": [ "Points to a temporary classes directory where the compiler can put compilation products", @@ -336,6 +351,20 @@ "classes directory only needs to exist during one incremental compiler cycle." ], "since": "1.3.0" + }, + { + "name": "converter", + "type": "java.util.Optional", + "default": "java.util.Optional.empty()", + "doc": "FileConverter to convert between Path and VirtualFileRef.", + "since": "1.4.0" + }, + { + "name": "stamper", + "type": "java.util.Optional", + "default": "java.util.Optional.empty()", + "doc": "ReadStamps to calculate timestamp or hash.", + "since": "1.4.0" } ] }, @@ -345,8 +374,14 @@ "target": "Java", "type": "record", "fields": [ - { "name": "file", "type": "java.io.File" }, - { "name": "hash", "type": "int" } + { + "name": "file", + "type": "java.nio.file.Path" + }, + { + "name": "hash", + "type": "int" + } ] }, { @@ -409,12 +444,30 @@ "type": "record", "doc": "This is used as part of CompileResult.", "fields": [ - { "name": "output", "type": "xsbti.compile.Output" }, - { "name": "options", "type": "xsbti.compile.MiniOptions" }, - { "name": "compilerVersion", "type": "String" }, - { "name": "order", "type": "xsbti.compile.CompileOrder" }, - { "name": "storeApis", "type": "boolean" }, - { "name": "extra", "type": "xsbti.T2*" } + { + "name": "output", + "type": "xsbti.compile.Output" + }, + { + "name": "options", + "type": "xsbti.compile.MiniOptions" + }, + { + "name": "compilerVersion", + "type": "String" + }, + { + "name": "order", + "type": "xsbti.compile.CompileOrder" + }, + { + "name": "storeApis", + "type": "boolean" + }, + { + "name": "extra", + "type": "xsbti.T2*" + } ] }, { @@ -424,9 +477,18 @@ "type": "record", "doc": "The result of running the incremental compilation.", "fields": [ - { "name": "analysis", "type": "xsbti.compile.CompileAnalysis" }, - { "name": "setup", "type": "xsbti.compile.MiniSetup" }, - { "name": "hasModified", "type": "boolean" } + { + "name": "analysis", + "type": "xsbti.compile.CompileAnalysis" + }, + { + "name": "setup", + "type": "xsbti.compile.MiniSetup" + }, + { + "name": "hasModified", + "type": "boolean" + } ] }, { @@ -436,8 +498,14 @@ "type": "record", "doc": "The previous source dependency analysis result from compilation.", "fields": [ - { "name": "analysis", "type": "java.util.Optional" }, - { "name": "setup", "type": "java.util.Optional" } + { + "name": "analysis", + "type": "java.util.Optional" + }, + { + "name": "setup", + "type": "java.util.Optional" + } ] }, { @@ -458,16 +526,22 @@ "doc": "If true, no sources are actually compiled and the Analysis from the previous compilation is returned." }, { - "name": "cacheFile", - "type": "java.io.File", + "name": "cachePath", + "type": "java.nio.file.Path", "doc": [ "The file used to cache information across compilations.", "This file can be removed to force a full recompilation.", "The file should be unique and not shared between compilations." ] }, - { "name": "cache", "type": "xsbti.compile.GlobalsCache" }, - { "name": "incrementalCompilerOptions", "type": "xsbti.compile.IncOptions" }, + { + "name": "cache", + "type": "xsbti.compile.GlobalsCache" + }, + { + "name": "incrementalCompilerOptions", + "type": "xsbti.compile.IncOptions" + }, { "name": "reporter", "type": "xsbti.Reporter", @@ -478,7 +552,24 @@ "type": "java.util.Optional", "doc": "Optionally provide progress indication." }, - { "name": "extra", "type": "xsbti.T2*" } + { + "name": "extra", + "type": "xsbti.T2*" + } + ], + "extra": [ + "public java.io.File cacheFile() {", + " return this.cachePath.toFile();", + "}", + "public Setup withCacheFile(java.io.File cacheFile) {", + " return this.withCachePath(cacheFile.toPath());", + "}", + "public static Setup create(xsbti.compile.PerClasspathEntryLookup _perClasspathEntryLookup, boolean _skip, java.io.File _cacheFile, xsbti.compile.GlobalsCache _cache, xsbti.compile.IncOptions _incrementalCompilerOptions, xsbti.Reporter _reporter, java.util.Optional _progress, xsbti.T2[] _extra) {", + " return new Setup(_perClasspathEntryLookup, _skip, _cacheFile.toPath(), _cache, _incrementalCompilerOptions, _reporter, _progress, _extra);", + "}", + "public static Setup of(xsbti.compile.PerClasspathEntryLookup _perClasspathEntryLookup, boolean _skip, java.io.File _cacheFile, xsbti.compile.GlobalsCache _cache, xsbti.compile.IncOptions _incrementalCompilerOptions, xsbti.Reporter _reporter, java.util.Optional _progress, xsbti.T2[] _extra) {", + " return new Setup(_perClasspathEntryLookup, _skip, _cacheFile.toPath(), _cache, _incrementalCompilerOptions, _reporter, _progress, _extra);", + "}" ] }, { @@ -586,4 +677,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/compiler-interface/src/main/java/xsbti/AnalysisCallback.java b/compiler-interface/src/main/java/xsbti/AnalysisCallback.java index 706462a..267376c 100644 --- a/compiler-interface/src/main/java/xsbti/AnalysisCallback.java +++ b/compiler-interface/src/main/java/xsbti/AnalysisCallback.java @@ -15,192 +15,172 @@ import xsbti.api.DependencyContext; import java.io.File; +import java.nio.file.Path; import java.util.EnumSet; public interface AnalysisCallback { - /** - * Set the source file mapped to a concrete {@link AnalysisCallback}. - * @param source Source file mapped to this instance of {@link AnalysisCallback}. - */ - void startSource(File source); - - /** - * Indicate that the class sourceClassName depends on the - * class onClassName. - * - * Note that only classes defined in source files included in the current - * compilation will passed to this method. Dependencies on classes generated - * by sources not in the current compilation will be passed as binary - * dependencies to the `binaryDependency` method. - * - * @param onClassName Class name being depended on. - * @param sourceClassName Dependent class name. - * @param context The kind of dependency established between - * onClassName and sourceClassName. - * - * @see xsbti.api.DependencyContext - */ - void classDependency(String onClassName, - String sourceClassName, - DependencyContext context); - - /** - * Indicate that the class fromClassName depends on a class - * named onBinaryClassName coming from class file or jar - * onBinaryEntry. - * - * @param onBinaryEntry A binary entry represents either the jar or the - * concrete class file from which the Scala compiler - * knows that onBinaryClassName comes from. - * @param onBinaryClassName Dependent binary name. - * Binary name with JVM-like representation. Inner classes - * are represented with '$'. For more information on the - * binary name format, check section 13.1 of the Java - * Language Specification. - * @param fromClassName Represent the class file name where - * onBinaryClassName is defined. - * Binary name with JVM-like representation. Inner classes - * are represented with '$'. For more information on the - * binary name format, check section 13.1 of the Java - * Language Specification. - * @param fromSourceFile Source file where onBinaryClassName - * is defined. - * @param context The kind of dependency established between - * onClassName and sourceClassName. - * - * @see xsbti.api.DependencyContext for more information on the context. - */ - void binaryDependency(File onBinaryEntry, - String onBinaryClassName, - String fromClassName, - File fromSourceFile, - DependencyContext context); - - /** - * Map the source class name (srcClassName) of a top-level - * Scala class coming from a given source file to a binary class name - * (binaryClassName) coming from a given class file. - * - * This relation indicates that classFile is the product of - * compilation from source. - * - * @param source File where srcClassName is defined. - * @param classFile File where binaryClassName is defined. This - * class file is the product of source. - * @param binaryClassName Binary name with JVM-like representation. Inner - * classes are represented with '$'. For more - * information on the binary name format, check - * section 13.1 of the Java Language Specification. - * @param srcClassName Class name as defined in source. - */ - void generatedNonLocalClass(File source, - File classFile, - String binaryClassName, - String srcClassName); - - /** - * Map the product relation between classFile and - * source to indicate that classFile is the - * product of compilation from source. - * - * @param source File that produced classFile. - * @param classFile File product of compilation of source. - */ - void generatedLocalClass(File source, File classFile); - - /** - * Register a public API entry coming from a given source file. - * - * @param sourceFile Source file where classApi comes from. - * @param classApi The extracted public class API. - */ - void api(File sourceFile, xsbti.api.ClassLike classApi); - - /** - * Register a class containing an entry point coming from a given source file. - * - * A class is an entry point if its bytecode contains a method with the - * following signature: - *
-     * public static void main(String[] args);
-     * 
- * - * @param sourceFile Source file where className is defined. - * @param className A class containing an entry point. - */ - void mainClass(File sourceFile, String className); - - /** - * Register the use of a name from a given source class name. - * - * @param className The source class name that uses name. - * @param name The source name used in className. - * @param useScopes Scopes(e.g. patmat, implicit) where name is used className. - */ - void usedName(String className, String name, EnumSet useScopes); - - /** - * Register a compilation problem. - * - * This error may have already been logged or not. Unreported problems may - * happen because the reporting option was not enabled via command-line. - * - * @param what The headline of the error. - * @param pos At a given source position. - * @param msg The in-depth description of the error. - * @param severity The severity of the error reported. - * @param reported Flag that confirms whether this error was reported or not. - */ - void problem(String what, - Position pos, - String msg, - Severity severity, - boolean reported); - - /** - * Communicate to the callback that the dependency phase has finished. - * - * For instance, you can use this method it to wait on asynchronous tasks. - */ - void dependencyPhaseCompleted(); - - /** - * Communicate to the callback that the API phase has finished. - * - * For instance, you can use this method it to wait on asynchronous tasks. - */ - void apiPhaseCompleted(); - - /** - * Return whether incremental compilation is enabled or not. - * - * This method is useful to know whether the incremental compilation - * phase defined by xsbt-analyzer should be added. - */ - boolean enabled(); - - /** - * Return class files in output jar at a given point in time. - * - * When straight-to-jar compilation is enabled, the following entrypoint - * in the analysis callback tells the compiler which classes can be found - * in the jar used as a compilation target (where all class files will be - * store). The entrypoint will return all the paths to class files in Zinc - * format, an example would be `xsbti/AnalysisCallback.class`. - * - * This entrypoint serves two main purposes: - * - * 1. Before the dependency phase is run, it returns the class files found - * in the jar previous to the current compilation. - * 2. After dependency has run, when called again, it returns the class - * files written by the compiler in genbcode. - * - * The second purpose is required because the compiler cannot communicate - * us via an internal programmatic API which files has written in genbcode - * and therefore we need to pay the price of opening the jar again to figure - * it out. If the compiler is to expose an entry point for this data, we - * can repurpose `classesInOutputJar` to only do 1). - */ - java.util.Set classesInOutputJar(); - + /** + * Set the source file mapped to a concrete {@link AnalysisCallback}. + * + * @param source Source file mapped to this instance of {@link AnalysisCallback}. + */ + void startSource(VirtualFile source); + // startSource needs to be VirtualFile because it needs to read the stamp. + + /** + * Indicate that the class sourceClassName depends on the class onClassName + * . + * + *

Note that only classes defined in source files included in the current compilation will + * passed to this method. Dependencies on classes generated by sources not in the current + * compilation will be passed as binary dependencies to the `binaryDependency` method. + * + * @param onClassName Class name being depended on. + * @param sourceClassName Dependent class name. + * @param context The kind of dependency established between onClassName and + * sourceClassName. + * @see xsbti.api.DependencyContext + */ + void classDependency(String onClassName, String sourceClassName, DependencyContext context); + + /** + * Indicate that the class fromClassName depends on a class named + * onBinaryClassName coming from class file or jar onBinaryEntry. + * + * @param onBinaryEntry A binary entry represents either the jar or the concrete class file from + * which the Scala compiler knows that onBinaryClassName comes from. + * @param onBinaryClassName Dependent binary name. Binary name with JVM-like representation. Inner + * classes are represented with '$'. For more information on the binary name format, check + * section 13.1 of the Java Language Specification. + * @param fromClassName Represent the class file name where onBinaryClassName is + * defined. Binary name with JVM-like representation. Inner classes are represented with '$'. + * For more information on the binary name format, check section 13.1 of the Java Language + * Specification. + * @param fromSourceFile Source file where onBinaryClassName is defined. + * @param context The kind of dependency established between onClassName and + * sourceClassName. + * @see xsbti.api.DependencyContext for more information on the context. + */ + void binaryDependency( + Path onBinaryEntry, + String onBinaryClassName, + String fromClassName, + VirtualFileRef fromSourceFile, + DependencyContext context); + + /** + * Map the source class name (srcClassName) of a top-level Scala class coming from a + * given source file to a binary class name (binaryClassName) coming from a given + * class file. + * + *

This relation indicates that classFile is the product of compilation from + * source. + * + * @param source File where srcClassName is defined. + * @param classFile File where binaryClassName is defined. This class file is the + * product of source. + * @param binaryClassName Binary name with JVM-like representation. Inner classes are represented + * with '$'. For more information on the binary name format, check section 13.1 of the Java + * Language Specification. + * @param srcClassName Class name as defined in source. + */ + void generatedNonLocalClass( + VirtualFileRef source, Path classFile, String binaryClassName, String srcClassName); + + /** + * Map the product relation between classFile and source to indicate + * that classFile is the product of compilation from source. + * + * @param source File that produced classFile. + * @param classFile File product of compilation of source. + */ + void generatedLocalClass(VirtualFileRef source, Path classFile); + + /** + * Register a public API entry coming from a given source file. + * + * @param sourceFile Source file where classApi comes from. + * @param classApi The extracted public class API. + */ + void api(VirtualFileRef sourceFile, xsbti.api.ClassLike classApi); + + /** + * Register a class containing an entry point coming from a given source file. + * + *

A class is an entry point if its bytecode contains a method with the following signature: + * + *

+   * public static void main(String[] args);
+   * 
+ * + * @param sourceFile Source file where className is defined. + * @param className A class containing an entry point. + */ + void mainClass(VirtualFileRef sourceFile, String className); + + /** + * Register the use of a name from a given source class name. + * + * @param className The source class name that uses name. + * @param name The source name used in className. + * @param useScopes Scopes(e.g. patmat, implicit) where name is used className. + */ + void usedName(String className, String name, EnumSet useScopes); + + /** + * Register a compilation problem. + * + *

This error may have already been logged or not. Unreported problems may happen because the + * reporting option was not enabled via command-line. + * + * @param what The headline of the error. + * @param pos At a given source position. + * @param msg The in-depth description of the error. + * @param severity The severity of the error reported. + * @param reported Flag that confirms whether this error was reported or not. + */ + void problem(String what, Position pos, String msg, Severity severity, boolean reported); + + /** + * Communicate to the callback that the dependency phase has finished. + * + *

For instance, you can use this method it to wait on asynchronous tasks. + */ + void dependencyPhaseCompleted(); + + /** + * Communicate to the callback that the API phase has finished. + * + *

For instance, you can use this method it to wait on asynchronous tasks. + */ + void apiPhaseCompleted(); + + /** + * Return whether incremental compilation is enabled or not. + * + *

This method is useful to know whether the incremental compilation phase defined by + * xsbt-analyzer should be added. + */ + boolean enabled(); + + /** + * Return class files in output jar at a given point in time. + * + *

When straight-to-jar compilation is enabled, the following entrypoint in the analysis + * callback tells the compiler which classes can be found in the jar used as a compilation target + * (where all class files will be store). The entrypoint will return all the paths to class files + * in Zinc format, an example would be `xsbti/AnalysisCallback.class`. + * + *

This entrypoint serves two main purposes: + * + *

1. Before the dependency phase is run, it returns the class files found in the jar previous + * to the current compilation. 2. After dependency has run, when called again, it returns the + * class files written by the compiler in genbcode. + * + *

The second purpose is required because the compiler cannot communicate us via an internal + * programmatic API which files has written in genbcode and therefore we need to pay the price of + * opening the jar again to figure it out. If the compiler is to expose an entry point for this + * data, we can repurpose `classesInOutputJar` to only do 1). + */ + java.util.Set classesInOutputJar(); } diff --git a/compiler-interface/src/main/java/xsbti/ArtifactInfo.java b/compiler-interface/src/main/java/xsbti/ArtifactInfo.java index 31f5f65..b95cf69 100644 --- a/compiler-interface/src/main/java/xsbti/ArtifactInfo.java +++ b/compiler-interface/src/main/java/xsbti/ArtifactInfo.java @@ -12,19 +12,17 @@ package xsbti; -/** - * Define constants of Scala compiler useful for artifact resolution. - */ +/** Define constants of Scala compiler useful for artifact resolution. */ public final class ArtifactInfo { - /** Define the name of the Scala organization. */ - public static final String ScalaOrganization = "org.scala-lang"; + /** Define the name of the Scala organization. */ + public static final String ScalaOrganization = "org.scala-lang"; - /** Define the name used to identify the sbt organization. */ - public static final String SbtOrganization = "org.scala-sbt"; + /** Define the name used to identify the sbt organization. */ + public static final String SbtOrganization = "org.scala-sbt"; - /** Define the ID used to identify the Scala library. */ - public static final String ScalaLibraryID = "scala-library"; + /** Define the ID used to identify the Scala library. */ + public static final String ScalaLibraryID = "scala-library"; - /** Define the ID used to identify the Scala compiler. */ - public static final String ScalaCompilerID = "scala-compiler"; -} \ No newline at end of file + /** Define the ID used to identify the Scala compiler. */ + public static final String ScalaCompilerID = "scala-compiler"; +} diff --git a/compiler-interface/src/main/java/xsbti/BasicVirtualFileRef.java b/compiler-interface/src/main/java/xsbti/BasicVirtualFileRef.java new file mode 100644 index 0000000..5b4f6b5 --- /dev/null +++ b/compiler-interface/src/main/java/xsbti/BasicVirtualFileRef.java @@ -0,0 +1,74 @@ +/* + * Scala compiler interface + * + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbti; + +import java.util.Arrays; +import java.util.ArrayList; + +public class BasicVirtualFileRef implements VirtualFileRef { + private final String parent; + private final String name; + + protected BasicVirtualFileRef(String _id) { + int idx = _id.lastIndexOf('/'); + if (idx >= 0) { + parent = _id.substring(0, idx + 1); + } else { + parent = ""; + } + name = _id.substring(idx + 1); + } + + public String id() { + return parent + name; + } + + public String name() { + return name; + } + + public String[] names() { + if (parent == null || parent == "") { + return new String[] {name}; + } + String[] parts = parent.split("/"); + String[] results = new String[parts.length + 1]; + int i = 0; + for (i = 0; i < parts.length; i++) results[i] = parts[i]; + results[i] = name; + return results; + } + + public BasicVirtualFileRef withId(String id) { + return new BasicVirtualFileRef(id); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (!(obj instanceof VirtualFileRef)) { + return false; + } else { + VirtualFileRef o = (VirtualFileRef) obj; + return this.id().equals(o.id()); + } + } + + public int hashCode() { + return 37 * (37 * (17 + "xsbti.VirtualFileRef".hashCode()) + id().hashCode()); + } + + public String toString() { + return id(); + } +} diff --git a/compiler-interface/src/main/java/xsbti/CompileCancelled.java b/compiler-interface/src/main/java/xsbti/CompileCancelled.java index 62d390c..2e8877d 100644 --- a/compiler-interface/src/main/java/xsbti/CompileCancelled.java +++ b/compiler-interface/src/main/java/xsbti/CompileCancelled.java @@ -13,10 +13,10 @@ package xsbti; /** - * Represent the cancellation of a compilation run. This failure extends - * {@link RuntimeException} that you can catch at the use-site. + * Represent the cancellation of a compilation run. This failure extends {@link RuntimeException} + * that you can catch at the use-site. */ public abstract class CompileCancelled extends RuntimeException { - /** Return an array of the initial arguments of the compiler. */ - public abstract String[] arguments(); + /** Return an array of the initial arguments of the compiler. */ + public abstract String[] arguments(); } diff --git a/compiler-interface/src/main/java/xsbti/CompileFailed.java b/compiler-interface/src/main/java/xsbti/CompileFailed.java index 3961a7b..c91c540 100644 --- a/compiler-interface/src/main/java/xsbti/CompileFailed.java +++ b/compiler-interface/src/main/java/xsbti/CompileFailed.java @@ -13,13 +13,13 @@ package xsbti; /** - * Represent a failure occurred during compilation of Java or Scala sources. - * This failure extends {@link RuntimeException} that you can catch at the use-site. + * Represent a failure occurred during compilation of Java or Scala sources. This failure extends + * {@link RuntimeException} that you can catch at the use-site. */ public abstract class CompileFailed extends RuntimeException { - /** Return an array of the initial arguments of the compiler. */ - public abstract String[] arguments(); + /** Return an array of the initial arguments of the compiler. */ + public abstract String[] arguments(); - /** Return an array of {@link Problem} that reports on the found errors. */ - public abstract Problem[] problems(); -} \ No newline at end of file + /** Return an array of {@link Problem} that reports on the found errors. */ + public abstract Problem[] problems(); +} diff --git a/compiler-interface/src/main/java/xsbti/CompilerInterface1.java b/compiler-interface/src/main/java/xsbti/CompilerInterface1.java index a74d9c6..af277bc 100644 --- a/compiler-interface/src/main/java/xsbti/CompilerInterface1.java +++ b/compiler-interface/src/main/java/xsbti/CompilerInterface1.java @@ -15,25 +15,18 @@ import xsbti.compile.*; import java.io.File; -/** - * Compiler Interface as of Zinc 1.2.0. - */ +/** Compiler Interface as of Zinc 1.2.0. */ public interface CompilerInterface1 { /** Returns a new compiler used for caching. */ CachedCompiler newCompiler( - String[] options, - Output output, - Logger initialLog, - Reporter initialDelegate - ); + String[] options, Output output, Logger initialLog, Reporter initialDelegate); void run( - File[] sources, - DependencyChanges changes, - AnalysisCallback callback, - Logger log, - Reporter delegate, - CompileProgress progress, - CachedCompiler cached - ); + VirtualFile[] sources, + DependencyChanges changes, + AnalysisCallback callback, + Logger log, + Reporter delegate, + CompileProgress progress, + CachedCompiler cached); } diff --git a/compiler-interface/src/main/java/xsbti/ConsoleInterface1.java b/compiler-interface/src/main/java/xsbti/ConsoleInterface1.java index d3ce511..3590049 100644 --- a/compiler-interface/src/main/java/xsbti/ConsoleInterface1.java +++ b/compiler-interface/src/main/java/xsbti/ConsoleInterface1.java @@ -12,25 +12,19 @@ package xsbti; -/** - * Console Interface as of Zinc 1.2.0. - */ +/** Console Interface as of Zinc 1.2.0. */ public interface ConsoleInterface1 { void run( - String[] args, - String bootClasspathString, - String classpathString, - String initialCommands, - String cleanupCommands, - ClassLoader loader, - String[] bindNames, - Object[] bindValues, - Logger log - ); + String[] args, + String bootClasspathString, + String classpathString, + String initialCommands, + String cleanupCommands, + ClassLoader loader, + String[] bindNames, + Object[] bindValues, + Logger log); String[] commandArguments( - String[] args, - String bootClasspathString, - String classpathString, - Logger log); + String[] args, String bootClasspathString, String classpathString, Logger log); } diff --git a/compiler-interface/src/main/java/xsbti/FileConverter.java b/compiler-interface/src/main/java/xsbti/FileConverter.java new file mode 100644 index 0000000..eced8ef --- /dev/null +++ b/compiler-interface/src/main/java/xsbti/FileConverter.java @@ -0,0 +1,23 @@ +/* + * Scala compiler interface + * + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbti; + +import java.nio.file.Path; + +public interface FileConverter { + Path toPath(VirtualFileRef ref); + + VirtualFile toVirtualFile(VirtualFileRef ref); + + VirtualFile toVirtualFile(Path path); +} diff --git a/compiler-interface/src/main/java/xsbti/InteractiveConsoleFactory.java b/compiler-interface/src/main/java/xsbti/InteractiveConsoleFactory.java index c51c554..d390c12 100644 --- a/compiler-interface/src/main/java/xsbti/InteractiveConsoleFactory.java +++ b/compiler-interface/src/main/java/xsbti/InteractiveConsoleFactory.java @@ -22,6 +22,5 @@ InteractiveConsoleInterface createConsole( ClassLoader loader, String[] bindNames, Object[] bindValues, - Logger log - ); + Logger log); } diff --git a/compiler-interface/src/main/java/xsbti/InteractiveConsoleInterface.java b/compiler-interface/src/main/java/xsbti/InteractiveConsoleInterface.java index e9e98db..d9720dd 100644 --- a/compiler-interface/src/main/java/xsbti/InteractiveConsoleInterface.java +++ b/compiler-interface/src/main/java/xsbti/InteractiveConsoleInterface.java @@ -14,5 +14,6 @@ public interface InteractiveConsoleInterface { void reset(); + InteractiveConsoleResponse interpret(String line, boolean synthetic); } diff --git a/compiler-interface/src/main/java/xsbti/Logger.java b/compiler-interface/src/main/java/xsbti/Logger.java index cfca504..313016e 100644 --- a/compiler-interface/src/main/java/xsbti/Logger.java +++ b/compiler-interface/src/main/java/xsbti/Logger.java @@ -15,9 +15,13 @@ import java.util.function.Supplier; public interface Logger { - void error(Supplier msg); - void warn(Supplier msg); - void info(Supplier msg); - void debug(Supplier msg); - void trace(Supplier exception); + void error(Supplier msg); + + void warn(Supplier msg); + + void info(Supplier msg); + + void debug(Supplier msg); + + void trace(Supplier exception); } diff --git a/compiler-interface/src/main/java/xsbti/PathBasedFile.java b/compiler-interface/src/main/java/xsbti/PathBasedFile.java new file mode 100644 index 0000000..32207e5 --- /dev/null +++ b/compiler-interface/src/main/java/xsbti/PathBasedFile.java @@ -0,0 +1,19 @@ +/* + * Scala compiler interface + * + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbti; + +import java.nio.file.Path; + +public interface PathBasedFile extends VirtualFile { + Path toPath(); +} diff --git a/compiler-interface/src/main/java/xsbti/Position.java b/compiler-interface/src/main/java/xsbti/Position.java index 21ea176..268b108 100644 --- a/compiler-interface/src/main/java/xsbti/Position.java +++ b/compiler-interface/src/main/java/xsbti/Position.java @@ -15,24 +15,44 @@ import java.io.File; import java.util.Optional; -public interface Position -{ - Optional line(); - String lineContent(); - Optional offset(); - - // pointer to the column position of the error/warning - Optional pointer(); - Optional pointerSpace(); - - Optional sourcePath(); - Optional sourceFile(); - - // Default values to avoid breaking binary compatibility - default Optional startOffset() { return Optional.empty(); } - default Optional endOffset() { return Optional.empty(); } - default Optional startLine() { return Optional.empty(); } - default Optional startColumn() { return Optional.empty(); } - default Optional endLine() { return Optional.empty(); } - default Optional endColumn() { return Optional.empty(); } +public interface Position { + Optional line(); + + String lineContent(); + + Optional offset(); + + // pointer to the column position of the error/warning + Optional pointer(); + + Optional pointerSpace(); + + Optional sourcePath(); + + Optional sourceFile(); + + // Default values to avoid breaking binary compatibility + default Optional startOffset() { + return Optional.empty(); + } + + default Optional endOffset() { + return Optional.empty(); + } + + default Optional startLine() { + return Optional.empty(); + } + + default Optional startColumn() { + return Optional.empty(); + } + + default Optional endLine() { + return Optional.empty(); + } + + default Optional endColumn() { + return Optional.empty(); + } } diff --git a/compiler-interface/src/main/java/xsbti/Problem.java b/compiler-interface/src/main/java/xsbti/Problem.java index 98111e3..722bd3b 100644 --- a/compiler-interface/src/main/java/xsbti/Problem.java +++ b/compiler-interface/src/main/java/xsbti/Problem.java @@ -14,18 +14,21 @@ import java.util.Optional; -public interface Problem -{ - String category(); - Severity severity(); - String message(); - Position position(); +public interface Problem { + String category(); + + Severity severity(); + + String message(); + + Position position(); // Default value to avoid breaking binary compatibility /** - * If present, the string shown to the user when displaying this Problem. - * Otherwise, the Problem will be shown in an implementation-defined way - * based on the values of its other fields. + * If present, the string shown to the user when displaying this Problem. Otherwise, the Problem + * will be shown in an implementation-defined way based on the values of its other fields. */ - default Optional rendered() { return Optional.empty(); } + default Optional rendered() { + return Optional.empty(); + } } diff --git a/compiler-interface/src/main/java/xsbti/Reporter.java b/compiler-interface/src/main/java/xsbti/Reporter.java index bafd8ef..0768421 100644 --- a/compiler-interface/src/main/java/xsbti/Reporter.java +++ b/compiler-interface/src/main/java/xsbti/Reporter.java @@ -15,29 +15,28 @@ /** * Define an interface for a reporter of the compiler. * - * The reporter exposes compilation errors coming from either Scala or - * Java compilers. This includes messages with any level of severity: - * from error, to warnings and infos. + *

The reporter exposes compilation errors coming from either Scala or Java compilers. This + * includes messages with any level of severity: from error, to warnings and infos. */ public interface Reporter { - /** Reset logging and any accumulated error, warning, message or count. */ - void reset(); + /** Reset logging and any accumulated error, warning, message or count. */ + void reset(); - /** Check whether the logger has received any error since last reset. */ - boolean hasErrors(); + /** Check whether the logger has received any error since last reset. */ + boolean hasErrors(); - /** Check whether the logger has received any warning since last reset. */ - boolean hasWarnings(); + /** Check whether the logger has received any warning since last reset. */ + boolean hasWarnings(); - /** Log a summary of the received output since the last reset. */ - void printSummary(); + /** Log a summary of the received output since the last reset. */ + void printSummary(); - /** Return a list of warnings and errors since the last reset. */ - Problem[] problems(); + /** Return a list of warnings and errors since the last reset. */ + Problem[] problems(); - /** Log a message at a concrete position and with a concrete severity. */ - void log(Problem problem); + /** Log a message at a concrete position and with a concrete severity. */ + void log(Problem problem); - /** Report a comment. */ - void comment(Position pos, String msg); + /** Report a comment. */ + void comment(Position pos, String msg); } diff --git a/compiler-interface/src/main/java/xsbti/ScaladocInterface1.java b/compiler-interface/src/main/java/xsbti/ScaladocInterface1.java index 39057a8..40672ab 100644 --- a/compiler-interface/src/main/java/xsbti/ScaladocInterface1.java +++ b/compiler-interface/src/main/java/xsbti/ScaladocInterface1.java @@ -12,9 +12,7 @@ package xsbti; -/** - * Scaladoc Interface as of Zinc 1.2.0. - */ +/** Scaladoc Interface as of Zinc 1.2.0. */ public interface ScaladocInterface1 { void run(String[] args, Logger log, Reporter delegate); } diff --git a/compiler-interface/src/main/java/xsbti/Severity.java b/compiler-interface/src/main/java/xsbti/Severity.java index 58df702..a8a0a87 100644 --- a/compiler-interface/src/main/java/xsbti/Severity.java +++ b/compiler-interface/src/main/java/xsbti/Severity.java @@ -12,7 +12,8 @@ package xsbti; -public enum Severity -{ - Info, Warn, Error -} \ No newline at end of file +public enum Severity { + Info, + Warn, + Error +} diff --git a/compiler-interface/src/main/java/xsbti/T2.java b/compiler-interface/src/main/java/xsbti/T2.java index ac6bcda..b5a9806 100644 --- a/compiler-interface/src/main/java/xsbti/T2.java +++ b/compiler-interface/src/main/java/xsbti/T2.java @@ -13,8 +13,8 @@ package xsbti; /** Used to pass a pair of values. */ -public interface T2 -{ +public interface T2 { public A1 get1(); + public A2 get2(); } diff --git a/compiler-interface/src/main/java/xsbti/UseScope.java b/compiler-interface/src/main/java/xsbti/UseScope.java index 7417b3f..3be8000 100644 --- a/compiler-interface/src/main/java/xsbti/UseScope.java +++ b/compiler-interface/src/main/java/xsbti/UseScope.java @@ -15,23 +15,28 @@ /** * Defines the scope in which a name hash was captured. * - * The incremental compiler uses [[UseScope]] to determine some Scala semantics - * assumed in the presence of a name in a concrete position. For instance, - * [[PatMatTarget]] is used for names that appear as the target types of a - * pattern match. + *

The incremental compiler uses [[UseScope]] to determine some Scala semantics assumed in the + * presence of a name in a concrete position. For instance, [[PatMatTarget]] is used for names that + * appear as the target types of a pattern match. * - * The order of declaration of these is crucial. Don't change it. - * Don't add more than 6 scopes. Otherwise, change `Mapper` implementation. + *

The order of declaration of these is crucial. Don't change it. Don't add more than 6 scopes. + * Otherwise, change `Mapper` implementation. */ public enum UseScope { - /** Represent standard definition/usage. Default is present for each declaration and usage of given name. */ - Default, - /** Used to track changes in implicit definition. - * So far classes don't depend on this scope since implicit changes has it's own invalidation process */ - Implicit, - /** Used in optimized sealed class invalidation (when IncOptions.seOptimizedSealed is true). - * Only sealed classes/trait produce hash in this scope (for their names). - * Class have dependencies in PatMatTarget scope to all names used in type of pattern match target. */ - PatMatTarget + /** + * Represent standard definition/usage. Default is present for each declaration and usage of given + * name. + */ + Default, + /** + * Used to track changes in implicit definition. So far classes don't depend on this scope since + * implicit changes has it's own invalidation process + */ + Implicit, + /** + * Used in optimized sealed class invalidation (when IncOptions.seOptimizedSealed is true). Only + * sealed classes/trait produce hash in this scope (for their names). Class have dependencies in + * PatMatTarget scope to all names used in type of pattern match target. + */ + PatMatTarget } - diff --git a/compiler-interface/src/main/java/xsbti/VirtualDirectory.java b/compiler-interface/src/main/java/xsbti/VirtualDirectory.java new file mode 100644 index 0000000..4d07c86 --- /dev/null +++ b/compiler-interface/src/main/java/xsbti/VirtualDirectory.java @@ -0,0 +1,19 @@ +/* + * Scala compiler interface + * + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbti; + +public interface VirtualDirectory extends VirtualFileRef { + public VirtualFileWrite fileNamed(String name); + + public VirtualDirectory subdirectoryNamed(String name); +} diff --git a/compiler-interface/src/main/java/xsbti/VirtualFile.java b/compiler-interface/src/main/java/xsbti/VirtualFile.java new file mode 100644 index 0000000..d880760 --- /dev/null +++ b/compiler-interface/src/main/java/xsbti/VirtualFile.java @@ -0,0 +1,21 @@ +/* + * Scala compiler interface + * + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbti; + +import java.io.InputStream; + +public interface VirtualFile extends VirtualFileRef { + long contentHash(); + + InputStream input(); +} diff --git a/compiler-interface/src/main/java/xsbti/VirtualFileRef.java b/compiler-interface/src/main/java/xsbti/VirtualFileRef.java new file mode 100644 index 0000000..bd54d50 --- /dev/null +++ b/compiler-interface/src/main/java/xsbti/VirtualFileRef.java @@ -0,0 +1,44 @@ +/* + * Scala compiler interface + * + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbti; + +/* + * Represents a reference to a file-like object. + */ +public interface VirtualFileRef { + public static VirtualFileRef of(String id) { + return new BasicVirtualFileRef(id); + } + + /** + * Returns unique identifier for the file. For Java compilation it needs to contain the path + * structure matching the package name. + * + *

However, it must end with the same value as name(), Java files must end with ".java", and + * Scala files must end with ".scala". + */ + public String id(); + + /** + * Returns "file name" for the file. Java files must end with ".java", and Scala files must end + * with ".scala". + */ + public String name(); + + /* + * Returns "file name" for the file. + * Java files must end with ".java", + * and Scala files must end with ".scala". + */ + public String[] names(); +} diff --git a/compiler-interface/src/main/java/xsbti/VirtualFileWrite.java b/compiler-interface/src/main/java/xsbti/VirtualFileWrite.java new file mode 100644 index 0000000..bd61918 --- /dev/null +++ b/compiler-interface/src/main/java/xsbti/VirtualFileWrite.java @@ -0,0 +1,19 @@ +/* + * Scala compiler interface + * + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbti; + +import java.io.OutputStream; + +public interface VirtualFileWrite extends VirtualFile { + OutputStream output(); +} diff --git a/compiler-interface/src/main/java/xsbti/api/AbstractLazy.java b/compiler-interface/src/main/java/xsbti/api/AbstractLazy.java index 0332f6b..8187399 100644 --- a/compiler-interface/src/main/java/xsbti/api/AbstractLazy.java +++ b/compiler-interface/src/main/java/xsbti/api/AbstractLazy.java @@ -14,28 +14,26 @@ import java.io.ObjectStreamException; -public abstract class AbstractLazy implements Lazy, java.io.Serializable -{ - // `writeReplace` must be `protected`, so that the `Impl` subclass - // inherits the serialization override. - // - // (See source-dependencies/java-analysis-serialization-error, which would - // crash trying to serialize an AbstractLazy, because it pulled in an - // unserializable type eventually.) - protected Object writeReplace() throws ObjectStreamException - { - return new StrictLazy(get()); - } - private static final class StrictLazy implements Lazy, java.io.Serializable - { - private final T value; - StrictLazy(T t) - { - value = t; - } - public T get() - { - return value; - } - } -} \ No newline at end of file +public abstract class AbstractLazy implements Lazy, java.io.Serializable { + // `writeReplace` must be `protected`, so that the `Impl` subclass + // inherits the serialization override. + // + // (See source-dependencies/java-analysis-serialization-error, which would + // crash trying to serialize an AbstractLazy, because it pulled in an + // unserializable type eventually.) + protected Object writeReplace() throws ObjectStreamException { + return new StrictLazy(get()); + } + + private static final class StrictLazy implements Lazy, java.io.Serializable { + private final T value; + + StrictLazy(T t) { + value = t; + } + + public T get() { + return value; + } + } +} diff --git a/compiler-interface/src/main/java/xsbti/api/Lazy.java b/compiler-interface/src/main/java/xsbti/api/Lazy.java index cf50ae7..21cf1b2 100644 --- a/compiler-interface/src/main/java/xsbti/api/Lazy.java +++ b/compiler-interface/src/main/java/xsbti/api/Lazy.java @@ -12,7 +12,6 @@ package xsbti.api; -public interface Lazy -{ - T get(); -} \ No newline at end of file +public interface Lazy { + T get(); +} diff --git a/compiler-interface/src/main/java/xsbti/api/Modifiers.java b/compiler-interface/src/main/java/xsbti/api/Modifiers.java index f856174..e82263a 100644 --- a/compiler-interface/src/main/java/xsbti/api/Modifiers.java +++ b/compiler-interface/src/main/java/xsbti/api/Modifiers.java @@ -12,109 +12,132 @@ package xsbti.api; -public final class Modifiers implements java.io.Serializable -{ - private static final int AbstractBit = 0; - private static final int OverrideBit = 1; - private static final int FinalBit = 2; - private static final int SealedBit = 3; - private static final int ImplicitBit = 4; - private static final int LazyBit = 5; - private static final int MacroBit = 6; - private static final int SuperAccessorBit = 7; - - private static int flag(boolean set, int bit) - { - return set ? (1 << bit) : 0; - } - - public Modifiers(boolean isAbstract, boolean isOverride, boolean isFinal, boolean isSealed, boolean isImplicit, boolean isLazy, boolean isMacro, boolean isSuperAccessor) - { - this.flags = (byte)( - flag(isAbstract, AbstractBit) | - flag(isOverride, OverrideBit) | - flag(isFinal, FinalBit) | - flag(isSealed, SealedBit) | - flag(isImplicit, ImplicitBit) | - flag(isLazy, LazyBit) | - flag(isMacro, MacroBit) | - flag(isSuperAccessor, SuperAccessorBit) - ); - } - - /** - * Allow to set the modifiers from a flags byte where: - * - * 1. The first bit tells if has an abstract modifier. - * 2. The second bit tells if has an override modifier. - * 3. The third bit tells if has an final modifier. - * 4. The fourth bit tells if has an sealed modifier. - * 5. The fifth bit tells if has an implicit modifier. - * 6. The sixth bit tells if has an lazy modifier. - * 7. The seventh bit tells if has an macro modifier. - * 8. The eighth bit tells if has an super accessor modifier. - * - * This method is not part of the public API and it may be removed at any point. - * @param flags An instance of byte encoding the modifiers. - */ - protected Modifiers(byte flags) { - this.flags = flags; - } - - private final byte flags; - - private boolean flag(int bit) - { - return (flags & (1 << bit)) != 0; - } - - public final byte raw() - { - return flags; - } - - public final boolean isAbstract() - { - return flag(AbstractBit); - } - public final boolean isOverride() - { - return flag(OverrideBit); - } - public final boolean isFinal() - { - return flag(FinalBit); - } - public final boolean isSealed() - { - return flag(SealedBit); - } - public final boolean isImplicit() - { - return flag(ImplicitBit); - } - public final boolean isLazy() - { - return flag(LazyBit); - } - public final boolean isMacro() - { - return flag(MacroBit); - } - public final boolean isSuperAccessor() - { - return flag(SuperAccessorBit); - } - public boolean equals(Object o) - { - return (o instanceof Modifiers) && flags == ((Modifiers)o).flags; - } - public int hashCode() - { - return flags; - } - public String toString() - { - return "Modifiers(" + "isAbstract: " + isAbstract() + ", " + "isOverride: " + isOverride() + ", " + "isFinal: " + isFinal() + ", " + "isSealed: " + isSealed() + ", " + "isImplicit: " + isImplicit() + ", " + "isLazy: " + isLazy() + ", " + "isMacro: " + isMacro()+ ", isSuperAccessor:" + isSuperAccessor() + ")"; - } +public final class Modifiers implements java.io.Serializable { + private static final int AbstractBit = 0; + private static final int OverrideBit = 1; + private static final int FinalBit = 2; + private static final int SealedBit = 3; + private static final int ImplicitBit = 4; + private static final int LazyBit = 5; + private static final int MacroBit = 6; + private static final int SuperAccessorBit = 7; + + private static int flag(boolean set, int bit) { + return set ? (1 << bit) : 0; + } + + public Modifiers( + boolean isAbstract, + boolean isOverride, + boolean isFinal, + boolean isSealed, + boolean isImplicit, + boolean isLazy, + boolean isMacro, + boolean isSuperAccessor) { + this.flags = + (byte) + (flag(isAbstract, AbstractBit) + | flag(isOverride, OverrideBit) + | flag(isFinal, FinalBit) + | flag(isSealed, SealedBit) + | flag(isImplicit, ImplicitBit) + | flag(isLazy, LazyBit) + | flag(isMacro, MacroBit) + | flag(isSuperAccessor, SuperAccessorBit)); + } + + /** + * Allow to set the modifiers from a flags byte where: + * + *

1. The first bit tells if has an abstract modifier. 2. The second bit tells if has an + * override modifier. 3. The third bit tells if has an final modifier. 4. The fourth bit tells if + * has an sealed modifier. 5. The fifth bit tells if has an implicit modifier. 6. The sixth bit + * tells if has an lazy modifier. 7. The seventh bit tells if has an macro modifier. 8. The eighth + * bit tells if has an super accessor modifier. + * + *

This method is not part of the public API and it may be removed at any point. + * + * @param flags An instance of byte encoding the modifiers. + */ + protected Modifiers(byte flags) { + this.flags = flags; + } + + private final byte flags; + + private boolean flag(int bit) { + return (flags & (1 << bit)) != 0; + } + + public final byte raw() { + return flags; + } + + public final boolean isAbstract() { + return flag(AbstractBit); + } + + public final boolean isOverride() { + return flag(OverrideBit); + } + + public final boolean isFinal() { + return flag(FinalBit); + } + + public final boolean isSealed() { + return flag(SealedBit); + } + + public final boolean isImplicit() { + return flag(ImplicitBit); + } + + public final boolean isLazy() { + return flag(LazyBit); + } + + public final boolean isMacro() { + return flag(MacroBit); + } + + public final boolean isSuperAccessor() { + return flag(SuperAccessorBit); + } + + public boolean equals(Object o) { + return (o instanceof Modifiers) && flags == ((Modifiers) o).flags; + } + + public int hashCode() { + return flags; + } + + public String toString() { + return "Modifiers(" + + "isAbstract: " + + isAbstract() + + ", " + + "isOverride: " + + isOverride() + + ", " + + "isFinal: " + + isFinal() + + ", " + + "isSealed: " + + isSealed() + + ", " + + "isImplicit: " + + isImplicit() + + ", " + + "isLazy: " + + isLazy() + + ", " + + "isMacro: " + + isMacro() + + ", isSuperAccessor:" + + isSuperAccessor() + + ")"; + } } diff --git a/compiler-interface/src/main/java/xsbti/api/SafeLazy.java b/compiler-interface/src/main/java/xsbti/api/SafeLazy.java index bb39e0b..6ac3c79 100644 --- a/compiler-interface/src/main/java/xsbti/api/SafeLazy.java +++ b/compiler-interface/src/main/java/xsbti/api/SafeLazy.java @@ -17,10 +17,9 @@ /** * Implement a Scala `lazy val` in Java for the facing sbt interface. * - * It holds a reference to a thunk that is lazily evaluated and then - * its reference is clear to avoid memory leaks in memory-intensive code. - * It needs to be defined in [[xsbti]] or a subpackage, see [[xsbti.api.Lazy]] - * for similar definitions. + *

It holds a reference to a thunk that is lazily evaluated and then its reference is clear to + * avoid memory leaks in memory-intensive code. It needs to be defined in [[xsbti]] or a subpackage, + * see [[xsbti.api.Lazy]] for similar definitions. */ public final class SafeLazy { @@ -35,12 +34,13 @@ public static xsbti.api.Lazy apply(Supplier sbtThunk) { /** Return a sbt [[xsbti.api.Lazy]] from a strict value. */ public static xsbti.api.Lazy strict(T value) { // Convert strict parameter to sbt function returning it - return apply(new Supplier() { - @Override - public T get() { - return value; - } - }); + return apply( + new Supplier() { + @Override + public T get() { + return value; + } + }); } private static final class Impl extends xsbti.api.AbstractLazy { @@ -54,8 +54,8 @@ private static final class Impl extends xsbti.api.AbstractLazy { /** * Return cached result or force lazy evaluation. - * - * Don't call it in a multi-threaded environment. + * + *

Don't call it in a multi-threaded environment. */ public T get() { if (flag) return result; @@ -69,5 +69,3 @@ public T get() { } } } - - diff --git a/compiler-interface/src/main/java/xsbti/compile/CachedCompiler.java b/compiler-interface/src/main/java/xsbti/compile/CachedCompiler.java index ff2865f..b453653 100644 --- a/compiler-interface/src/main/java/xsbti/compile/CachedCompiler.java +++ b/compiler-interface/src/main/java/xsbti/compile/CachedCompiler.java @@ -15,36 +15,40 @@ import xsbti.AnalysisCallback; import xsbti.Logger; import xsbti.Reporter; +import xsbti.VirtualFile; import java.io.File; /** * Define the interface of a cached Scala compiler that can be run. * - * This cached compiler hides the implementation of a compiler by just - * defining two operations: {@link #commandArguments(File[])} and - * {@link #run(File[], DependencyChanges, AnalysisCallback, Logger, Reporter, CompileProgress)}. - * + *

This cached compiler hides the implementation of a compiler by just defining two operations: + * {@link #commandArguments(File[])} and */ public interface CachedCompiler { - /** - * Return an array of arguments that represent a command-line like - * equivalent of a call to the Scala compiler, but without the command itself. - * - * @param sources The source files that the compiler must compile. - * - * @return The array of arguments of the Scala compiler. - */ - String[] commandArguments(File[] sources); + /** + * Return an array of arguments that represent a command-line like equivalent of a call to the + * Scala compiler, but without the command itself. + * + * @param sources The source files that the compiler must compile. + * @return The array of arguments of the Scala compiler. + */ + String[] commandArguments(File[] sources); - /** - * Run the cached Scala compiler with inputs of incremental compilation. - * - * @param sources The source files to be compiled. - * @param changes The changes that have occurred since last compilation. - * @param callback The callback injected by the incremental compiler. - * @param logger The logger of the incremental compilation. - * @param delegate The reporter that informs on the compiler's output. - * @param progress The compiler progress associated with a Scala compiler. - */ - void run(File[] sources, DependencyChanges changes, AnalysisCallback callback, Logger logger, Reporter delegate, CompileProgress progress); + /** + * Run the cached Scala compiler with inputs of incremental compilation. + * + * @param sources The source files to be compiled. + * @param changes The changes that have occurred since last compilation. + * @param callback The callback injected by the incremental compiler. + * @param logger The logger of the incremental compilation. + * @param delegate The reporter that informs on the compiler's output. + * @param progress The compiler progress associated with a Scala compiler. + */ + void run( + VirtualFile[] sources, + DependencyChanges changes, + AnalysisCallback callback, + Logger logger, + Reporter delegate, + CompileProgress progress); } diff --git a/compiler-interface/src/main/java/xsbti/compile/CachedCompilerProvider.java b/compiler-interface/src/main/java/xsbti/compile/CachedCompilerProvider.java index 5b914cd..44063e9 100644 --- a/compiler-interface/src/main/java/xsbti/compile/CachedCompilerProvider.java +++ b/compiler-interface/src/main/java/xsbti/compile/CachedCompilerProvider.java @@ -15,13 +15,12 @@ import xsbti.Logger; import xsbti.Reporter; -/** - * Represent a provider that creates cached Scala compilers from a Scala instance. - */ +/** Represent a provider that creates cached Scala compilers from a Scala instance. */ public interface CachedCompilerProvider { - /** Return the Scala instance used to provide cached compilers. */ - ScalaInstance scalaInstance(); + /** Return the Scala instance used to provide cached compilers. */ + ScalaInstance scalaInstance(); - /** Return a new cached compiler from the usual compiler input. */ - CachedCompiler newCachedCompiler(String[] arguments, Output output, Logger log, Reporter reporter); + /** Return a new cached compiler from the usual compiler input. */ + CachedCompiler newCachedCompiler( + String[] arguments, Output output, Logger log, Reporter reporter); } diff --git a/compiler-interface/src/main/java/xsbti/compile/Changes.java b/compiler-interface/src/main/java/xsbti/compile/Changes.java index 7238bbb..8f2c7a7 100644 --- a/compiler-interface/src/main/java/xsbti/compile/Changes.java +++ b/compiler-interface/src/main/java/xsbti/compile/Changes.java @@ -15,37 +15,27 @@ import java.util.Set; /** - * Defines an interface to query for changes of certain items that have an effect on - * incremental compilation. - *

- * The common use case for this interface is to detect added, removed, unmodified and - * changed source files. + * Defines an interface to query for changes of certain items that have an effect on incremental + * compilation. + * + *

The common use case for this interface is to detect added, removed, unmodified and changed + * source files. * * @param The type parameter that defines the items that have changed. */ public interface Changes { - /** - * @return The added items in a certain environment. - */ - Set getAdded(); + /** @return The added items in a certain environment. */ + Set getAdded(); - /** - * @return The removed items in a certain environment. - */ - Set getRemoved(); + /** @return The removed items in a certain environment. */ + Set getRemoved(); - /** - * @return The changed items in a certain environment. - */ - Set getChanged(); + /** @return The changed items in a certain environment. */ + Set getChanged(); - /** - * @return The unmodified items in a certain environment. - */ - Set getUnmodified(); + /** @return The unmodified items in a certain environment. */ + Set getUnmodified(); - /** - * @return Whether there was a change at all. - */ - Boolean isEmpty(); + /** @return Whether there was a change at all. */ + Boolean isEmpty(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/ClassFileManager.java b/compiler-interface/src/main/java/xsbti/compile/ClassFileManager.java index 75d1547..4c7527d 100644 --- a/compiler-interface/src/main/java/xsbti/compile/ClassFileManager.java +++ b/compiler-interface/src/main/java/xsbti/compile/ClassFileManager.java @@ -12,47 +12,47 @@ package xsbti.compile; -import java.io.File; +import xsbti.VirtualFile; /** - * Represent the interface to manage the generated class files by the - * Scala or Java compilers. The class file manager is responsible for - * providing operations to users to allow them to have a fine-grained - * control over the generated class files and how they are generated/deleted. + * Represent the interface to manage the generated class files by the Scala or Java compilers. The + * class file manager is responsible for providing operations to users to allow them to have a + * fine-grained control over the generated class files and how they are generated/deleted. * - * This class is meant to be used once per compilation run. + *

This class is meant to be used once per compilation run. */ public interface ClassFileManager { - /** - * Handler of classes that deletes them prior to every compilation step. - * - * @param classes The generated class files must not exist if the method - * returns normally, as well as any empty ancestor - * directories of deleted files. - */ - void delete(File[] classes); + /** + * Handler of classes that deletes them prior to every compilation step. + * + * @param classes The generated class files must not exist if the method returns normally, as well + * as any empty ancestor directories of deleted files. + */ + void delete(VirtualFile[] classes); - /** Called once per compilation step with the class files generated during that step. */ - /** - * Handler of classes that decides where certain class files should be - * stored after every compilation step. - * - * This method is called once per compilation run with the class - * files generated by that concrete run. - * - * @param classes The generated class files by the immediate compilation run. - */ - void generated(File[] classes); + /** Called once per compilation step with the class files generated during that step. */ + /** + * Handler of classes that decides where certain class files should be stored after every + * compilation step. + * + *

This method is called once per compilation run with the class files generated by that + * concrete run. + * + * @param classes The generated class files by the immediate compilation run. + */ + void generated(VirtualFile[] classes); - /** Called once at the end of the whole compilation run, with `success` - * indicating whether compilation succeeded (true) or not (false). */ - /** - * Informs the class file manager whether the compilation run has succeeded. - * - * If it has not succeeded, the class file manager will handle the current - * generated and the previous class files as per the underlying algorithm. - * - * @param success Whether the compilation run has succeeded or not. - */ - void complete(boolean success); + /** + * Called once at the end of the whole compilation run, with `success` indicating whether + * compilation succeeded (true) or not (false). + */ + /** + * Informs the class file manager whether the compilation run has succeeded. + * + *

If it has not succeeded, the class file manager will handle the current generated and the + * previous class files as per the underlying algorithm. + * + * @param success Whether the compilation run has succeeded or not. + */ + void complete(boolean success); } diff --git a/compiler-interface/src/main/java/xsbti/compile/CompileAnalysis.java b/compiler-interface/src/main/java/xsbti/compile/CompileAnalysis.java index 80a00a3..2ca09a6 100644 --- a/compiler-interface/src/main/java/xsbti/compile/CompileAnalysis.java +++ b/compiler-interface/src/main/java/xsbti/compile/CompileAnalysis.java @@ -20,33 +20,33 @@ /** * Represents the analysis interface of an incremental compilation. - *

- * The analysis interface conforms the public API of the Analysis files that - * contain information about the incremental compilation of a project. + * + *

The analysis interface conforms the public API of the Analysis files that contain information + * about the incremental compilation of a project. */ public interface CompileAnalysis extends Serializable { - /** - * Returns a read-only stamps interface that allows users to map files to stamps. - * - * @return A read-only stamps interface to query for stamps. - * @see xsbti.compile.analysis.Stamp - */ - public ReadStamps readStamps(); + /** + * Returns a read-only stamps interface that allows users to map files to stamps. + * + * @return A read-only stamps interface to query for stamps. + * @see xsbti.compile.analysis.Stamp + */ + public ReadStamps readStamps(); - /** - * Returns a read-only source infos interface that allows users to get compiler - * information on every source file they wish to. - * - * @return A read-only source infos interface. - * @see xsbti.compile.analysis.SourceInfo - */ - public ReadSourceInfos readSourceInfos(); + /** + * Returns a read-only source infos interface that allows users to get compiler information on + * every source file they wish to. + * + * @return A read-only source infos interface. + * @see xsbti.compile.analysis.SourceInfo + */ + public ReadSourceInfos readSourceInfos(); - /** - * Returns a read-only interface to check information about the incremental compilations. - * - * @return A read-only compilation info interface. - * @see xsbti.compile.analysis.Compilation - */ - public ReadCompilations readCompilations(); + /** + * Returns a read-only interface to check information about the incremental compilations. + * + * @return A read-only compilation info interface. + * @see xsbti.compile.analysis.Compilation + */ + public ReadCompilations readCompilations(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/CompileOrder.java b/compiler-interface/src/main/java/xsbti/compile/CompileOrder.java index 5317b46..e4ca868 100644 --- a/compiler-interface/src/main/java/xsbti/compile/CompileOrder.java +++ b/compiler-interface/src/main/java/xsbti/compile/CompileOrder.java @@ -15,52 +15,47 @@ /** * Define the order in which Scala and Java compilation should happen. * - * This compilation order matters when the sources to be compiled are both - * Scala and Java sources, in which case the Zinc compiler needs a strategy - * to deal with the compilation order. Therefore, this setting has no effect - * if only Java or Scala sources are being compiled. + *

This compilation order matters when the sources to be compiled are both Scala and Java + * sources, in which case the Zinc compiler needs a strategy to deal with the compilation order. + * Therefore, this setting has no effect if only Java or Scala sources are being compiled. */ public enum CompileOrder { - /** - * Allow Scala sources to depend on Java sources and allow Java sources to - * depend on Scala sources. - * - * Under this mode, both Java and Scala sources can depend on each other. - * Java sources are also passed to the Scala compiler, which parses them, - * populates the symbol table and lifts them to Scala trees without - * generating class files for the Java trees. - * - * Then, the incremental compiler will add the generated Scala class files - * to the classpath of the Java compiler so that Java sources can depend - * on Scala sources. - * - * For more information on the way Mixed and ScalaThenJava mode behave, - * see this link. - */ - Mixed, + /** + * Allow Scala sources to depend on Java sources and allow Java sources to depend on Scala + * sources. + * + *

Under this mode, both Java and Scala sources can depend on each other. Java sources are also + * passed to the Scala compiler, which parses them, populates the symbol table and lifts them to + * Scala trees without generating class files for the Java trees. + * + *

Then, the incremental compiler will add the generated Scala class files to the classpath of + * the Java compiler so that Java sources can depend on Scala sources. + * + *

For more information on the way Mixed and ScalaThenJava mode behave, see this link. + */ + Mixed, - /** - * Allow Scala sources to depend on the Java sources, but it does not allow - * Java sources to depend on Scala sources. - * - * When mixed compilation is not required, it's generally more efficient - * {@link CompileOrder#JavaThenScala} than {@link CompileOrder#ScalaThenJava} - * because the Scala compiler will not parse Java sources, it will just - * unpickle the symbol information from class files. - */ - JavaThenScala, + /** + * Allow Scala sources to depend on the Java sources, but it does not allow Java sources to depend + * on Scala sources. + * + *

When mixed compilation is not required, it's generally more efficient {@link + * CompileOrder#JavaThenScala} than {@link CompileOrder#ScalaThenJava} because the Scala compiler + * will not parse Java sources, it will just unpickle the symbol information from class files. + */ + JavaThenScala, - /** - * Allow Java sources to depend on Scala sources, but it does not allow Java - * sources to depend on Scala sources. - * - * Because of the way the Scala compiler works with regard to Java sources, - * Zinc has to enforce this mode by removing Java sources from the actual - * source arguments that are passed to the Scala compiler, otherwise it'll - * behave exactly as {@link CompileOrder#Mixed}. - * - * For more information on the way Mixed and ScalaThenJava mode behave, - * see this link. - */ - ScalaThenJava -} \ No newline at end of file + /** + * Allow Java sources to depend on Scala sources, but it does not allow Java sources to depend on + * Scala sources. + * + *

Because of the way the Scala compiler works with regard to Java sources, Zinc has to enforce + * this mode by removing Java sources from the actual source arguments that are passed to the + * Scala compiler, otherwise it'll behave exactly as {@link CompileOrder#Mixed}. + * + *

For more information on the way Mixed and ScalaThenJava mode behave, see this link. + */ + ScalaThenJava +} diff --git a/compiler-interface/src/main/java/xsbti/compile/CompileProgress.java b/compiler-interface/src/main/java/xsbti/compile/CompileProgress.java index 328cf31..9cb9736 100755 --- a/compiler-interface/src/main/java/xsbti/compile/CompileProgress.java +++ b/compiler-interface/src/main/java/xsbti/compile/CompileProgress.java @@ -15,28 +15,26 @@ /** * An API for reporting when files are being compiled. * - * This design is tied to the Scala compiler but it is used - * by the Java compiler too, check the docs of the methods. + *

This design is tied to the Scala compiler but it is used by the Java compiler too, check the + * docs of the methods. */ public interface CompileProgress { - /** - * Start the progress of a concrete phase for the path of a given - * compilation unit. - * - * @param phase The phase of the compiler being run. - * @param unitPath The path of the compilation unit. It will be empty - * when a Java compiler is reporting the progress. - */ - void startUnit(String phase, String unitPath); + /** + * Start the progress of a concrete phase for the path of a given compilation unit. + * + * @param phase The phase of the compiler being run. + * @param unitPath The path of the compilation unit. It will be empty when a Java compiler is + * reporting the progress. + */ + void startUnit(String phase, String unitPath); - /** - * Advance the progress of the current unit. - * - * @param current The current progress. - * @param total The total of the progress that has to be achieved. - * - * @return Whether the user has cancelled compilation or not. - */ - boolean advance(int current, int total); -} \ No newline at end of file + /** + * Advance the progress of the current unit. + * + * @param current The current progress. + * @param total The total of the progress that has to be achieved. + * @return Whether the user has cancelled compilation or not. + */ + boolean advance(int current, int total); +} diff --git a/compiler-interface/src/main/java/xsbti/compile/CompilerBridgeProvider.java b/compiler-interface/src/main/java/xsbti/compile/CompilerBridgeProvider.java index 798e69f..1fad8ff 100644 --- a/compiler-interface/src/main/java/xsbti/compile/CompilerBridgeProvider.java +++ b/compiler-interface/src/main/java/xsbti/compile/CompilerBridgeProvider.java @@ -17,29 +17,30 @@ /** * Defines an interface for users to get the compiler bridge for a given Scala version. - *

- * The implementors of this interface will retrieve the compiler bridge following different - * mechanisms. By default, Zinc uses ivy to resolve the sources for a given Scala version, - * compile them and then define the sbt component, which is reused across different sbt projects. + * + *

The implementors of this interface will retrieve the compiler bridge following different + * mechanisms. For example, sbt implements this to use LM to resolve the sources for a given Scala + * version, which Zinc then compiles and defines an sbt component, which is reused across different + * sbt projects. */ public interface CompilerBridgeProvider { - /** - * Get the location of the compiled Scala compiler bridge for a concrete Scala version. - * - * @param scalaInstance The Scala instance for which the bridge should be compiled for. - * @param logger A logger. - * @return The jar or directory where the bridge sources have been compiled. - */ - File fetchCompiledBridge(ScalaInstance scalaInstance, Logger logger); + /** + * Get the location of the compiled Scala compiler bridge for a concrete Scala version. + * + * @param scalaInstance The Scala instance for which the bridge should be compiled for. + * @param logger A logger. + * @return The jar or directory where the bridge sources have been compiled. + */ + File fetchCompiledBridge(ScalaInstance scalaInstance, Logger logger); - /** - * Get the Scala instance for a given Scala version. - * - * @param scalaVersion The scala version we want the instance for. - * @param logger A logger. - * @return A scala instance, useful to get a compiled bridge. - * @see ScalaInstance - * @see CompilerBridgeProvider#fetchCompiledBridge - */ - ScalaInstance fetchScalaInstance(String scalaVersion, Logger logger); + /** + * Get the Scala instance for a given Scala version. + * + * @param scalaVersion The scala version we want the instance for. + * @param logger A logger. + * @return A scala instance, useful to get a compiled bridge. + * @see ScalaInstance + * @see CompilerBridgeProvider#fetchCompiledBridge + */ + ScalaInstance fetchScalaInstance(String scalaVersion, Logger logger); } diff --git a/compiler-interface/src/main/java/xsbti/compile/DefaultExternalHooks.java b/compiler-interface/src/main/java/xsbti/compile/DefaultExternalHooks.java index 7c07f07..2541a2c 100644 --- a/compiler-interface/src/main/java/xsbti/compile/DefaultExternalHooks.java +++ b/compiler-interface/src/main/java/xsbti/compile/DefaultExternalHooks.java @@ -15,35 +15,37 @@ import java.util.Optional; public class DefaultExternalHooks implements ExternalHooks { - private Optional lookup = Optional.empty(); - private Optional classFileManager = Optional.empty(); - - public DefaultExternalHooks(Optional lookup, Optional classFileManager) { - this.lookup = lookup; - this.classFileManager = classFileManager; - } - - @Override - public Optional getExternalLookup() { - return lookup; - } - - @Override - public Optional getExternalClassFileManager() { - return classFileManager; - } - - @Override - public ExternalHooks withExternalClassFileManager(ClassFileManager externalClassFileManager) { - Optional external = Optional.of(externalClassFileManager); - Optional mixedManager = classFileManager.isPresent() + private Optional lookup = Optional.empty(); + private Optional classFileManager = Optional.empty(); + + public DefaultExternalHooks( + Optional lookup, Optional classFileManager) { + this.lookup = lookup; + this.classFileManager = classFileManager; + } + + @Override + public Optional getExternalLookup() { + return lookup; + } + + @Override + public Optional getExternalClassFileManager() { + return classFileManager; + } + + @Override + public ExternalHooks withExternalClassFileManager(ClassFileManager externalClassFileManager) { + Optional external = Optional.of(externalClassFileManager); + Optional mixedManager = + classFileManager.isPresent() ? Optional.of(WrappedClassFileManager.of(classFileManager.get(), external)) : external; - return new DefaultExternalHooks(lookup, mixedManager); - } + return new DefaultExternalHooks(lookup, mixedManager); + } - @Override - public ExternalHooks withExternalLookup(ExternalHooks.Lookup externalLookup) { - return new DefaultExternalHooks(Optional.of(externalLookup), classFileManager); - } + @Override + public ExternalHooks withExternalLookup(ExternalHooks.Lookup externalLookup) { + return new DefaultExternalHooks(Optional.of(externalLookup), classFileManager); + } } diff --git a/compiler-interface/src/main/java/xsbti/compile/DefinesClass.java b/compiler-interface/src/main/java/xsbti/compile/DefinesClass.java index b4bbc60..b9e0901 100644 --- a/compiler-interface/src/main/java/xsbti/compile/DefinesClass.java +++ b/compiler-interface/src/main/java/xsbti/compile/DefinesClass.java @@ -15,17 +15,16 @@ /** * Determine whether a classpath entry contains a class. * - * The corresponding classpath entry is not exposed by this interface. + *

The corresponding classpath entry is not exposed by this interface. */ public interface DefinesClass { - /** - * Return true if the classpath entry contains the requested class. - * - * @param className Binary name with JVM-like representation. Inner classes - * are represented with '$'. For more information on the - * binary name format, check section 13.1 of the Java - * Language Specification. - * @return True if className is found in the classpath entry. - */ - boolean apply(String className); + /** + * Return true if the classpath entry contains the requested class. + * + * @param className Binary name with JVM-like representation. Inner classes are represented with + * '$'. For more information on the binary name format, check section 13.1 of the Java + * Language Specification. + * @return True if className is found in the classpath entry. + */ + boolean apply(String className); } diff --git a/compiler-interface/src/main/java/xsbti/compile/DependencyChanges.java b/compiler-interface/src/main/java/xsbti/compile/DependencyChanges.java index 53ff3be..95d5c2c 100644 --- a/compiler-interface/src/main/java/xsbti/compile/DependencyChanges.java +++ b/compiler-interface/src/main/java/xsbti/compile/DependencyChanges.java @@ -12,24 +12,22 @@ package xsbti.compile; -import java.io.File; +import xsbti.VirtualFileRef; -/** - * Define the changes that can occur to the dependencies of a given compilation run. - */ +/** Define the changes that can occur to the dependencies of a given compilation run. */ public interface DependencyChanges { - /** Check whether there have been any change in the compilation dependencies. */ - boolean isEmpty(); + /** Check whether there have been any change in the compilation dependencies. */ + boolean isEmpty(); - /** - * Return the modified binaries since the last compilation run. - * These modified binaries are either class files or jar files. - */ - File[] modifiedBinaries(); + /** + * Return the modified binaries since the last compilation run. These modified binaries are either + * class files or jar files. + */ + VirtualFileRef[] modifiedLibraries(); - /** - * Return the modified class names since the last compilation run. - * These class names are mapped to sources and not binaries. - */ - String[] modifiedClasses(); -} \ No newline at end of file + /** + * Return the modified class names since the last compilation run. These class names are mapped to + * sources and not binaries. + */ + String[] modifiedClasses(); +} diff --git a/compiler-interface/src/main/java/xsbti/compile/ExternalHooks.java b/compiler-interface/src/main/java/xsbti/compile/ExternalHooks.java index 927a2b5..6e01dc9 100644 --- a/compiler-interface/src/main/java/xsbti/compile/ExternalHooks.java +++ b/compiler-interface/src/main/java/xsbti/compile/ExternalHooks.java @@ -12,85 +12,85 @@ package xsbti.compile; -import java.io.File; import java.util.Optional; import java.util.Set; +import xsbti.VirtualFileRef; +import xsbti.VirtualFile; /** - * Defines hooks that can be user-defined to modify the behaviour of - * internal components of the incremental compiler. + * Defines hooks that can be user-defined to modify the behaviour of internal components of the + * incremental compiler. */ public interface ExternalHooks { - /** - * Defines an interface for a lookup mechanism. - */ - interface Lookup { - - /** - * Used to provide information from external tools into sbt (e.g. IDEs) - * - * @param previousAnalysis - * @return None if is unable to determine what was changed, changes otherwise - */ - Optional> getChangedSources(CompileAnalysis previousAnalysis); - - /** - * Used to provide information from external tools into sbt (e.g. IDEs) - * - * @param previousAnalysis - * @return None if is unable to determine what was changed, changes otherwise - */ - Optional> getChangedBinaries(CompileAnalysis previousAnalysis); - - /** - * Used to provide information from external tools into sbt (e.g. IDEs) - * - * @param previousAnalysis - * @return None if is unable to determine what was changed, changes otherwise - */ - Optional> getRemovedProducts(CompileAnalysis previousAnalysis); - - /** - * Used to provide information from external tools into sbt (e.g. IDEs) - * - * @return API changes - */ - boolean shouldDoIncrementalCompilation(Set changedClasses, CompileAnalysis previousAnalysis); - - Optional hashClasspath(File[] classpath); - } + /** Defines an interface for a lookup mechanism. */ + interface Lookup { /** - * Returns the implementation of a lookup mechanism to be used instead of - * the internal lookup provided by the default implementation. + * Used to provide information from external tools into sbt (e.g. IDEs) + * + * @param previousAnalysis + * @return None if is unable to determine what was changed, changes otherwise */ - Optional getExternalLookup(); + Optional> getChangedSources(CompileAnalysis previousAnalysis); /** - * Returns the implementation of a {@link ClassFileManager} to be used - * alongside the internal manager provided by the default implementation. - *

- * This class file manager is run after the internal - * {@link ClassFileManager} defined in {@link IncOptions}. + * Used to provide information from external tools into sbt (e.g. IDEs) + * + * @param previousAnalysis + * @return None if is unable to determine what was changed, changes otherwise */ - Optional getExternalClassFileManager(); + Optional> getChangedBinaries(CompileAnalysis previousAnalysis); /** - * Returns an instance of hooks that executes the external passed class file manager. - * - * If several class file manager are passed, they are aggregated and their execution happens - * in the order of invocations of this method. + * Used to provide information from external tools into sbt (e.g. IDEs) * - * @return An instance of {@link ExternalHooks} with the aggregated external class file manager. + * @param previousAnalysis + * @return None if is unable to determine what was changed, changes otherwise */ - ExternalHooks withExternalClassFileManager(ClassFileManager externalClassFileManager); + Optional> getRemovedProducts(CompileAnalysis previousAnalysis); /** - * Returns an instance of hooks with one lookup. + * Used to provide information from external tools into sbt (e.g. IDEs) * - * If used several times, only the last lookup instance will be used. - * - * @return An instance of {@link ExternalHooks} with the specified lookup. + * @return API changes */ - ExternalHooks withExternalLookup(Lookup externalLookup); + boolean shouldDoIncrementalCompilation( + Set changedClasses, CompileAnalysis previousAnalysis); + + Optional hashClasspath(VirtualFile[] classpath); + } + + /** + * Returns the implementation of a lookup mechanism to be used instead of the internal lookup + * provided by the default implementation. + */ + Optional getExternalLookup(); + + /** + * Returns the implementation of a {@link ClassFileManager} to be used alongside the internal + * manager provided by the default implementation. + * + *

This class file manager is run after the internal {@link ClassFileManager} defined in {@link + * IncOptions}. + */ + Optional getExternalClassFileManager(); + + /** + * Returns an instance of hooks that executes the external passed class file manager. + * + *

If several class file manager are passed, they are aggregated and their execution happens in + * the order of invocations of this method. + * + * @return An instance of {@link ExternalHooks} with the aggregated external class file manager. + */ + ExternalHooks withExternalClassFileManager(ClassFileManager externalClassFileManager); + + /** + * Returns an instance of hooks with one lookup. + * + *

If used several times, only the last lookup instance will be used. + * + * @return An instance of {@link ExternalHooks} with the specified lookup. + */ + ExternalHooks withExternalLookup(Lookup externalLookup); } diff --git a/compiler-interface/src/main/java/xsbti/compile/GlobalsCache.java b/compiler-interface/src/main/java/xsbti/compile/GlobalsCache.java index f9f1fba..de416a4 100644 --- a/compiler-interface/src/main/java/xsbti/compile/GlobalsCache.java +++ b/compiler-interface/src/main/java/xsbti/compile/GlobalsCache.java @@ -16,33 +16,31 @@ import xsbti.Reporter; /** - * Define operations that let us retrieve cached compiler instances - * for the current Java Virtual Machine. + * Define operations that let us retrieve cached compiler instances for the current Java Virtual + * Machine. */ public interface GlobalsCache { - /** - * Retrieve a {@link CachedCompiler}. - * - * @param args The arguments being passed on to the compiler. - * @param output The output instance where the compiler stored class files. - * @param forceNew Mark whether the previous cached compiler should be - * thrown away and a new one should be returned. - * @param provider The provider used for the cached compiler. - * @param logger The logger used to log compiler output. - * @param reporter The reporter used to report warnings and errors. - * - * @return An instance of the latest {@link CachedCompiler}. - */ - CachedCompiler apply(String[] args, - Output output, - boolean forceNew, - CachedCompilerProvider provider, - Logger logger, - Reporter reporter); + /** + * Retrieve a {@link CachedCompiler}. + * + * @param args The arguments being passed on to the compiler. + * @param output The output instance where the compiler stored class files. + * @param forceNew Mark whether the previous cached compiler should be thrown away and a new one + * should be returned. + * @param provider The provider used for the cached compiler. + * @param logger The logger used to log compiler output. + * @param reporter The reporter used to report warnings and errors. + * @return An instance of the latest {@link CachedCompiler}. + */ + CachedCompiler apply( + String[] args, + Output output, + boolean forceNew, + CachedCompilerProvider provider, + Logger logger, + Reporter reporter); - /** - * Clear the cache of {@link CachedCompiler cached compilers}. - */ - void clear(); + /** Clear the cache of {@link CachedCompiler cached compilers}. */ + void clear(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/IncToolOptionsUtil.java b/compiler-interface/src/main/java/xsbti/compile/IncToolOptionsUtil.java index 16a79c8..0b8050f 100644 --- a/compiler-interface/src/main/java/xsbti/compile/IncToolOptionsUtil.java +++ b/compiler-interface/src/main/java/xsbti/compile/IncToolOptionsUtil.java @@ -15,23 +15,22 @@ import java.util.Optional; /** - * Define a helper for {@link IncToolOptions} that provides information on - * default {@link ClassFileManager class file managers} used and gives - * default incremental compilation options to the user. + * Define a helper for {@link IncToolOptions} that provides information on default {@link + * ClassFileManager class file managers} used and gives default incremental compilation options to + * the user. * - * The default customized classfile manager and incremental options are empty - * options because these are disabled by default in Java compilers and Java doc. - * {@link IncToolOptions} are only supposed to be used for the Scala incremental - * compiler. + *

The default customized classfile manager and incremental options are empty options because + * these are disabled by default in Java compilers and Java doc. {@link IncToolOptions} are only + * supposed to be used for the Scala incremental compiler. */ public class IncToolOptionsUtil { /** - * Return whether the default {@link ClassFileManager} should be used or not. - * If true, {@link IncToolOptionsUtil#defaultClassFileManager()} is used. + * Return whether the default {@link ClassFileManager} should be used or not. If true, {@link + * IncToolOptionsUtil#defaultClassFileManager()} is used. * - * @return true if the class file manager defined in - * {@link IncToolOptionsUtil#defaultClassFileManager()} is used, false otherwise. + * @return true if the class file manager defined in {@link + * IncToolOptionsUtil#defaultClassFileManager()} is used, false otherwise. */ public static boolean defaultUseCustomizedFileManager() { return false; diff --git a/compiler-interface/src/main/java/xsbti/compile/IncrementalCompiler.java b/compiler-interface/src/main/java/xsbti/compile/IncrementalCompiler.java index 8344549..a538edd 100644 --- a/compiler-interface/src/main/java/xsbti/compile/IncrementalCompiler.java +++ b/compiler-interface/src/main/java/xsbti/compile/IncrementalCompiler.java @@ -15,103 +15,164 @@ import xsbti.Logger; import xsbti.Reporter; import xsbti.T2; +import xsbti.FileConverter; +import xsbti.VirtualFile; +import xsbti.compile.analysis.ReadStamps; import java.io.File; import java.util.Optional; +import java.nio.file.Path; /* -* This API is subject to change. -* -* It is the client's responsibility to: -* 1. Manage class loaders. Usually the client will want to: -* i. Keep the class loader used by the ScalaInstance warm. -* ii. Keep the class loader of the incremental recompilation classes (xsbti.compile) warm. -* iii. Share the class loader for Scala classes between the incremental compiler implementation and the ScalaInstance where possible (must be binary compatible) -* 2. Manage the compiler interface jar. The interface must be compiled against the exact Scala version used for compilation and a compatible Java version. -* 3. Manage compilation order between different compilations. -* i. Execute a compilation for each dependency, obtaining an Analysis for each. -* ii. Provide the Analysis from previous compilations to dependent compilations in the analysis map. -* 4. Provide an implementation of JavaCompiler for compiling Java sources. -* 5. Define a function that determines if a classpath entry contains a class (Setup.definesClass). -* i. This is provided by the client so that the client can cache this information across compilations when compiling multiple sets of sources. -* ii. The cache should be cleared for each new compilation run or else recompilation will not properly account for changes to the classpath. -* 6. Provide a cache directory. -* i. This directory is used by IncrementalCompiler to persist data between compilations. -* ii. It should be a different directory for each set of sources being compiled. -* 7. Manage parallel execution. -* i. Each compilation may be performed in a different thread as long as the dependencies have been compiled already. -* ii. Implementations of all types should be immutable and arrays treated as immutable. -* 8. Ensure general invariants: -* i. The implementations of all types are immutable, except for the already discussed Setup.definesClass. -* ii. Arrays are treated as immutable. -* iii. No value is ever null. -*/ + * This API is subject to change. + * + * It is the client's responsibility to: + * 1. Manage class loaders. Usually the client will want to: + * i. Keep the class loader used by the ScalaInstance warm. + * ii. Keep the class loader of the incremental recompilation classes (xsbti.compile) warm. + * iii. Share the class loader for Scala classes between the incremental compiler implementation and the ScalaInstance where possible (must be binary compatible) + * 2. Manage the compiler interface jar. The interface must be compiled against the exact Scala version used for compilation and a compatible Java version. + * 3. Manage compilation order between different compilations. + * i. Execute a compilation for each dependency, obtaining an Analysis for each. + * ii. Provide the Analysis from previous compilations to dependent compilations in the analysis map. + * 4. Provide an implementation of JavaCompiler for compiling Java sources. + * 5. Define a function that determines if a classpath entry contains a class (Setup.definesClass). + * i. This is provided by the client so that the client can cache this information across compilations when compiling multiple sets of sources. + * ii. The cache should be cleared for each new compilation run or else recompilation will not properly account for changes to the classpath. + * 6. Provide a cache directory. + * i. This directory is used by IncrementalCompiler to persist data between compilations. + * ii. It should be a different directory for each set of sources being compiled. + * 7. Manage parallel execution. + * i. Each compilation may be performed in a different thread as long as the dependencies have been compiled already. + * ii. Implementations of all types should be immutable and arrays treated as immutable. + * 8. Ensure general invariants: + * i. The implementations of all types are immutable, except for the already discussed Setup.definesClass. + * ii. Arrays are treated as immutable. + * iii. No value is ever null. + */ public interface IncrementalCompiler { - /** - * Performs an incremental compilation given an instance of {@link Inputs}. - * - * @param inputs An instance of {@link Inputs} that collect all the inputs - * required to run the compiler (from sources and classpath, - * to compilation order, previous results, current setup, etc). - * @param logger An instance of {@link Logger} that logs Zinc output. - * - * @return An instance of {@link CompileResult} that holds information - * about the results of the compilation. - */ - CompileResult compile(Inputs inputs, Logger logger); + /** + * Performs an incremental compilation given an instance of {@link Inputs}. + * + * @param inputs An instance of {@link Inputs} that collect all the inputs required to run the + * compiler (from sources and classpath, to compilation order, previous results, current + * setup, etc). + * @param logger An instance of {@link Logger} that logs Zinc output. + * @return An instance of {@link CompileResult} that holds information about the results of the + * compilation. + */ + CompileResult compile(Inputs inputs, Logger logger); + + /** + * Performs an incremental compilation given its configuration. + * + * @param scalaCompiler The Scala compiler to compile Scala sources. + * @param javaCompiler The Java compiler to compile Java sources. + * @param sources An array of Java and Scala source files to be compiled. + * @param classpath An array of files representing classpath entries. + * @param output An instance of {@link Output} to store the compiler outputs. + * @param globalsCache Directory where previous cached compilers are stored. + * @param scalacOptions An array of options/settings for the Scala compiler. + * @param javacOptions An array of options for the Java compiler. + * @param previousAnalysis Optional previous incremental compilation analysis. + * @param previousSetup Optional previous incremental compilation setup. + * @param perClasspathEntryLookup Lookup of data structures and operations for a given classpath + * entry. + * @param reporter An instance of {@link Reporter} to report compiler output. + * @param compileOrder The order in which Java and Scala sources should be compiled. + * @param skip Flag to ignore this compilation run and return previous one. + * @param progress An instance of {@link CompileProgress} to keep track of the current compilation + * progress. + * @param incrementalOptions An Instance of {@link IncOptions} that configures the incremental + * compiler behaviour. + * @param temporaryClassesDirectory A directory where incremental compiler can put temporary class + * files or jars. + * @param extra An array of sbt tuples with extra options. + * @param converter FileConverter to convert between Path and VirtualFileRef. + * @param stamper Stamper creates timestamp or hash. + * @param logger An instance of {@link Logger} that logs Zinc output. + * @return An instance of {@link CompileResult} that holds information about the results of the + * compilation. + */ + CompileResult compile( + ScalaCompiler scalaCompiler, + JavaCompiler javaCompiler, + VirtualFile[] sources, + VirtualFile[] classpath, + Output output, + GlobalsCache globalsCache, + String[] scalacOptions, + String[] javacOptions, + Optional previousAnalysis, + Optional previousSetup, + PerClasspathEntryLookup perClasspathEntryLookup, + Reporter reporter, + CompileOrder compileOrder, + // Has to be boxed to override in Scala, + // this is a bug of the Scala compiler 2.12 + java.lang.Boolean skip, + Optional progress, + IncOptions incrementalOptions, + Optional temporaryClassesDirectory, + T2[] extra, + FileConverter converter, + ReadStamps stamper, + Logger logger); - /** - * Performs an incremental compilation given its configuration. - * - * @param scalaCompiler The Scala compiler to compile Scala sources. - * @param javaCompiler The Java compiler to compile Java sources. - * @param sources An array of Java and Scala source files to be compiled. - * @param classpath An array of files representing classpath entries. - * @param output An instance of {@link Output} to store the compiler outputs. - * @param globalsCache Directory where previous cached compilers are stored. - * @param scalacOptions An array of options/settings for the Scala compiler. - * @param javacOptions An array of options for the Java compiler. - * @param previousAnalysis Optional previous incremental compilation analysis. - * @param previousSetup Optional previous incremental compilation setup. - * @param perClasspathEntryLookup Lookup of data structures and operations - * for a given classpath entry. - * @param reporter An instance of {@link Reporter} to report compiler output. - * @param compileOrder The order in which Java and Scala sources should - * be compiled. - * @param skip Flag to ignore this compilation run and return previous one. - * @param progress An instance of {@link CompileProgress} to keep track of - * the current compilation progress. - * @param incrementalOptions An Instance of {@link IncOptions} that - * configures the incremental compiler behaviour. - * @param temporaryClassesDirectory A directory where incremental compiler - * can put temporary class files or jars. - * @param extra An array of sbt tuples with extra options. - * @param logger An instance of {@link Logger} that logs Zinc output. - * - * - * @return An instance of {@link CompileResult} that holds information - * about the results of the compilation. - */ - CompileResult compile(ScalaCompiler scalaCompiler, - JavaCompiler javaCompiler, - File[] sources, - File[] classpath, - Output output, - GlobalsCache globalsCache, - String[] scalacOptions, - String[] javacOptions, - Optional previousAnalysis, - Optional previousSetup, - PerClasspathEntryLookup perClasspathEntryLookup, - Reporter reporter, - CompileOrder compileOrder, - // Has to be boxed to override in Scala, - // this is a bug of the Scala compiler 2.12 - java.lang.Boolean skip, - Optional progress, - IncOptions incrementalOptions, - Optional temporaryClassesDirectory, - T2[] extra, - Logger logger); + /** + * Performs an incremental compilation given its configuration. + * + * @param scalaCompiler The Scala compiler to compile Scala sources. + * @param javaCompiler The Java compiler to compile Java sources. + * @param sources An array of Java and Scala source files to be compiled. + * @param classpath An array of files representing classpath entries. + * @param output An instance of {@link Output} to store the compiler outputs. + * @param globalsCache Directory where previous cached compilers are stored. + * @param scalacOptions An array of options/settings for the Scala compiler. + * @param javacOptions An array of options for the Java compiler. + * @param previousAnalysis Optional previous incremental compilation analysis. + * @param previousSetup Optional previous incremental compilation setup. + * @param perClasspathEntryLookup Lookup of data structures and operations for a given classpath + * entry. + * @param reporter An instance of {@link Reporter} to report compiler output. + * @param compileOrder The order in which Java and Scala sources should be compiled. + * @param skip Flag to ignore this compilation run and return previous one. + * @param progress An instance of {@link CompileProgress} to keep track of the current compilation + * progress. + * @param incrementalOptions An Instance of {@link IncOptions} that configures the incremental + * compiler behaviour. + * @param temporaryClassesDirectory A directory where incremental compiler can put temporary class + * files or jars. + * @param extra An array of sbt tuples with extra options. + * @param converter FileConverter to convert between Path and VirtualFileRef. + * @param stamper Stamper creates timestamp or hash. + * @param logger An instance of {@link Logger} that logs Zinc output. + * @return An instance of {@link CompileResult} that holds information about the results of the + * compilation. + */ + CompileResult compile( + ScalaCompiler scalaCompiler, + JavaCompiler javaCompiler, + Path[] sources, + Path[] classpath, + Output output, + GlobalsCache globalsCache, + String[] scalacOptions, + String[] javacOptions, + Optional previousAnalysis, + Optional previousSetup, + PerClasspathEntryLookup perClasspathEntryLookup, + Reporter reporter, + CompileOrder compileOrder, + // Has to be boxed to override in Scala, + // this is a bug of the Scala compiler 2.12 + java.lang.Boolean skip, + Optional progress, + IncOptions incrementalOptions, + Optional temporaryClassesDirectory, + T2[] extra, + FileConverter converter, + ReadStamps stamper, + Logger logger); } diff --git a/compiler-interface/src/main/java/xsbti/compile/JavaTool.java b/compiler-interface/src/main/java/xsbti/compile/JavaTool.java index 6e3d65f..b81be20 100644 --- a/compiler-interface/src/main/java/xsbti/compile/JavaTool.java +++ b/compiler-interface/src/main/java/xsbti/compile/JavaTool.java @@ -14,33 +14,33 @@ import xsbti.Logger; import xsbti.Reporter; - -import java.io.File; +import xsbti.VirtualFile; /** - * Represent a bare metal interface around one of the java tools, either - * the Java compiler (javac) or the Javadoc generator. + * Represent a bare metal interface around one of the java tools, either the Java compiler (javac) + * or the Javadoc generator. * - * The main purpose of this interface is to abstract over the local invocation - * of the Java toolchain and forked invocation via process. + *

The main purpose of this interface is to abstract over the local invocation of the Java + * toolchain and forked invocation via process. */ public interface JavaTool { /** * Run a concrete Java tool (either Java compiler or Javadoc generator). * * @param sources The list of Java source files to compile. - * @param options The set of all the options to pass to the java compiler. - * These options also include JVM options like the classpath. - * @param incToolOptions The set of options to pass to the Java compiler - * for the incremental dependency analysis. + * @param options The set of all the options to pass to the java compiler. These options also + * include JVM options like the classpath. + * @param incToolOptions The set of options to pass to the Java compiler for the incremental + * dependency analysis. * @param reporter The reporter for semantic error messages. - * @param log The logger to dump output into. - * + * @param log The logger to dump output into. * @return true if no errors, false otherwise. */ - boolean run(File[] sources, - String[] options, - IncToolOptions incToolOptions, - Reporter reporter, - Logger log); + boolean run( + VirtualFile[] sources, + String[] options, + Output output, + IncToolOptions incToolOptions, + Reporter reporter, + Logger log); } diff --git a/compiler-interface/src/main/java/xsbti/compile/JavaTools.java b/compiler-interface/src/main/java/xsbti/compile/JavaTools.java index b14fee2..5cb194d 100644 --- a/compiler-interface/src/main/java/xsbti/compile/JavaTools.java +++ b/compiler-interface/src/main/java/xsbti/compile/JavaTools.java @@ -13,8 +13,8 @@ package xsbti.compile; /** - * Represent an interface of the toolchain of Java compilation that gives - * access javadoc generation and Java compilation. + * Represent an interface of the toolchain of Java compilation that gives access javadoc generation + * and Java compilation. */ public interface JavaTools { /** Return an implementation of the Java compiler (javac). */ diff --git a/compiler-interface/src/main/java/xsbti/compile/MultipleOutput.java b/compiler-interface/src/main/java/xsbti/compile/MultipleOutput.java index 9fab610..05bf6b5 100755 --- a/compiler-interface/src/main/java/xsbti/compile/MultipleOutput.java +++ b/compiler-interface/src/main/java/xsbti/compile/MultipleOutput.java @@ -12,31 +12,31 @@ package xsbti.compile; -import java.io.File; +import java.nio.file.Path; import java.util.Optional; /** * Represents a mapping of several outputs depending on the source directory. - *

- * This option is used only by the Scala compiler. + * + *

This option is used only by the Scala compiler. */ public interface MultipleOutput extends Output { - /** - * Return an array of the existent output groups. - *

- * Incremental compilation manages the class files in these directories, so - * don't play with them out of the Zinc API. Zinc already takes care of - * deleting classes before every compilation run. - */ - public OutputGroup[] getOutputGroups(); + /** + * Return an array of the existent output groups. + * + *

Incremental compilation manages the class files in these directories, so don't play with + * them out of the Zinc API. Zinc already takes care of deleting classes before every compilation + * run. + */ + public OutputGroup[] getOutputGroups(); - @Override - public default Optional getSingleOutput() { - return Optional.empty(); - } + @Override + public default Optional getSingleOutput() { + return Optional.empty(); + } - @Override - public default Optional getMultipleOutput() { - return Optional.of(getOutputGroups()); - } -} \ No newline at end of file + @Override + public default Optional getMultipleOutput() { + return Optional.of(getOutputGroups()); + } +} diff --git a/compiler-interface/src/main/java/xsbti/compile/Output.java b/compiler-interface/src/main/java/xsbti/compile/Output.java index 459432f..ad167e5 100755 --- a/compiler-interface/src/main/java/xsbti/compile/Output.java +++ b/compiler-interface/src/main/java/xsbti/compile/Output.java @@ -12,41 +12,40 @@ package xsbti.compile; -import java.io.File; +import java.nio.file.Path; import java.io.Serializable; import java.util.Optional; /** * Define an abstract interface that represents the output of the compilation. - *

- * Inheritors are {@link SingleOutput} with a global output directory and - * {@link MultipleOutput} that specifies the output directory per source file. - *

- * These two subclasses exist to satisfy the Scala compiler which accepts both - * single and multiple targets. These targets may depend on the sources to be - * compiled. - *

- * Note that Javac does not support multiple output and any attempt to use it - * will result in a runtime exception. - *

- * This class is used both as an input to the compiler and as an output of the - * {@link xsbti.compile.CompileAnalysis}. + * + *

Inheritors are {@link SingleOutput} with a global output directory and {@link MultipleOutput} + * that specifies the output directory per source file. + * + *

These two subclasses exist to satisfy the Scala compiler which accepts both single and + * multiple targets. These targets may depend on the sources to be compiled. + * + *

Note that Javac does not support multiple output and any attempt to use it will result in a + * runtime exception. + * + *

This class is used both as an input to the compiler and as an output of the {@link + * xsbti.compile.CompileAnalysis}. */ public interface Output extends Serializable { - /** - * Returns the multiple outputs passed or to be passed to the Scala compiler. - * If single output directory is used or Javac will consume this setting, - * it returns {@link java.util.Optional#EMPTY}. - * - * @see xsbti.compile.MultipleOutput - */ - public Optional getMultipleOutput(); + /** + * Returns the multiple outputs passed or to be passed to the Scala compiler. If single output + * directory is used or Javac will consume this setting, it returns {@link + * java.util.Optional#EMPTY}. + * + * @see xsbti.compile.MultipleOutput + */ + public Optional getMultipleOutput(); - /** - * Returns the single output passed or to be passed to the Scala or Java compiler. - * If multiple outputs are used, it returns {@link java.util.Optional#EMPTY}. - * - * @see xsbti.compile.SingleOutput - */ - public Optional getSingleOutput(); + /** + * Returns the single output passed or to be passed to the Scala or Java compiler. If multiple + * outputs are used, it returns {@link java.util.Optional#EMPTY}. + * + * @see xsbti.compile.SingleOutput + */ + public Optional getSingleOutput(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/OutputGroup.java b/compiler-interface/src/main/java/xsbti/compile/OutputGroup.java index f252324..de7441a 100644 --- a/compiler-interface/src/main/java/xsbti/compile/OutputGroup.java +++ b/compiler-interface/src/main/java/xsbti/compile/OutputGroup.java @@ -12,29 +12,26 @@ package xsbti.compile; -import java.io.File; +import java.nio.file.Path; import java.io.Serializable; -/** - * Define the interface of a group of outputs. - */ +/** Define the interface of a group of outputs. */ public interface OutputGroup extends Serializable { - /** - * Return the directory where source files are stored for this group. - *

- * Note that source directories should uniquely identify the group - * for a certain source file. - */ - public File getSourceDirectory(); + /** + * Return the directory where source files are stored for this group. + * + *

Note that source directories should uniquely identify the group for a certain source file. + */ + public Path getSourceDirectory(); - /** - * Return the directory where class files should be generated. - *

- * Incremental compilation manages the class files in this directory, so - * don't play with this directory out of the Zinc API. Zinc already takes - * care of deleting classes before every compilation run. - *

- * This directory must be exclusively used for one set of sources. - */ - public File getOutputDirectory(); + /** + * Return the directory where class files should be generated. + * + *

Incremental compilation manages the class files in this directory, so don't play with this + * directory out of the Zinc API. Zinc already takes care of deleting classes before every + * compilation run. + * + *

This directory must be exclusively used for one set of sources. + */ + public Path getOutputDirectory(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/PerClasspathEntryLookup.java b/compiler-interface/src/main/java/xsbti/compile/PerClasspathEntryLookup.java index 664cd4c..e7aec06 100644 --- a/compiler-interface/src/main/java/xsbti/compile/PerClasspathEntryLookup.java +++ b/compiler-interface/src/main/java/xsbti/compile/PerClasspathEntryLookup.java @@ -12,36 +12,36 @@ package xsbti.compile; -import java.io.File; +import xsbti.VirtualFile; import java.util.Optional; /** - * Define the interface to look up mapped data structures and query classpath - * entries This interface gives answers to classpath-related questions like: + * Define the interface to look up mapped data structures and query classpath entries This interface + * gives answers to classpath-related questions like: * - * - Is this class defined in this classpath entry? + *

- Is this class defined in this classpath entry? * - * This interface also allows you to get the relation between a given - * classpath entry and its existing {@link CompileAnalysis} instance. + *

This interface also allows you to get the relation between a given classpath entry and its + * existing {@link CompileAnalysis} instance. * - * The classpath entry can be either a JAR file or a given directory, - * as per the Java Language Specification. + *

The classpath entry can be either a JAR file or a given directory, as per the Java Language + * Specification. */ public interface PerClasspathEntryLookup { - /** - * Provide the {@link CompileAnalysis} mapped to a given classpath entry. - * - * @return An optional instance of {@link CompileAnalysis}. - */ - Optional analysis(File classpathEntry); + /** + * Provide the {@link CompileAnalysis} mapped to a given classpath entry. + * + * @return An optional instance of {@link CompileAnalysis}. + */ + Optional analysis(VirtualFile classpathEntry); - /** - * Provide an instance of {@link DefinesClass} that will allow you to - * check whether a given classpath entry contains a binary class name. - * - * @return Instance of {@link DefinesClass} that will allow you to query - * information for a given classpath entry. - */ - DefinesClass definesClass(File classpathEntry); + /** + * Provide an instance of {@link DefinesClass} that will allow you to check whether a given + * classpath entry contains a binary class name. + * + * @return Instance of {@link DefinesClass} that will allow you to query information for a given + * classpath entry. + */ + DefinesClass definesClass(VirtualFile classpathEntry); } diff --git a/compiler-interface/src/main/java/xsbti/compile/ScalaCompiler.java b/compiler-interface/src/main/java/xsbti/compile/ScalaCompiler.java index 9b74ec9..7d72d68 100644 --- a/compiler-interface/src/main/java/xsbti/compile/ScalaCompiler.java +++ b/compiler-interface/src/main/java/xsbti/compile/ScalaCompiler.java @@ -15,71 +15,65 @@ import xsbti.AnalysisCallback; import xsbti.Logger; import xsbti.Reporter; +import xsbti.VirtualFile; -import java.io.File; import java.util.Optional; -/** - * Represent the interface of a Scala compiler. - */ +/** Represent the interface of a Scala compiler. */ public interface ScalaCompiler { - /** - * Return the {@link ScalaInstance} used by this instance of the compiler. - */ - ScalaInstance scalaInstance(); + /** Return the {@link ScalaInstance} used by this instance of the compiler. */ + ScalaInstance scalaInstance(); - /** - * Return the {@link ClasspathOptions} used by this instance of the compiler. - */ - ClasspathOptions classpathOptions(); + /** Return the {@link ClasspathOptions} used by this instance of the compiler. */ + ClasspathOptions classpathOptions(); - /** - * Recompile the subset of sources impacted by the - * changes defined in changes and collect the new APIs. - * - * @param sources All the sources of the project. - * @param changes The changes that have been detected at the previous step. - * @param callback The callback to which the extracted information should be - * reported. - * @param log The logger in which the Scala compiler will log info. - * @param reporter The reporter to which errors and warnings should be - * reported during compilation. - * @param progress Where to report the file being currently compiled. - * @param compiler The actual compiler that will perform the compilation step. - */ - void compile(File[] sources, - DependencyChanges changes, - AnalysisCallback callback, - Logger log, - Reporter reporter, - CompileProgress progress, - CachedCompiler compiler); + /** + * Recompile the subset of sources impacted by the changes defined in changes + * and collect the new APIs. + * + * @param sources All the sources of the project. + * @param changes The changes that have been detected at the previous step. + * @param callback The callback to which the extracted information should be reported. + * @param log The logger in which the Scala compiler will log info. + * @param reporter The reporter to which errors and warnings should be reported during + * compilation. + * @param progress Where to report the file being currently compiled. + * @param compiler The actual compiler that will perform the compilation step. + */ + void compile( + VirtualFile[] sources, + DependencyChanges changes, + AnalysisCallback callback, + Logger log, + Reporter reporter, + CompileProgress progress, + CachedCompiler compiler); - /** - * Recompile the subset of sources impacted by the - * changes defined in changes and collect the new APIs. - * - * @param sources All the sources of the project. - * @param changes The changes that have been detected at the previous step. - * @param options The arguments to give to the Scala compiler. - * For more information, run `scalac -help`. - * @param output The location where generated class files should be put. - * @param callback The callback to which the extracted information should - * be reported. - * @param reporter The reporter to which errors and warnings should be - * reported during compilation. - * @param cache The cache from where we retrieve the compiler to use. - * @param log The logger in which the Scala compiler will log info. - * @param progressOpt The progress interface in which the Scala compiler - * will report on the file being compiled. - */ - void compile(File[] sources, - DependencyChanges changes, - String[] options, - Output output, - AnalysisCallback callback, - Reporter reporter, - GlobalsCache cache, - Logger log, - Optional progressOpt); + /** + * Recompile the subset of sources impacted by the changes defined in changes + * and collect the new APIs. + * + * @param sources All the sources of the project. + * @param changes The changes that have been detected at the previous step. + * @param options The arguments to give to the Scala compiler. For more information, run `scalac + * -help`. + * @param output The location where generated class files should be put. + * @param callback The callback to which the extracted information should be reported. + * @param reporter The reporter to which errors and warnings should be reported during + * compilation. + * @param cache The cache from where we retrieve the compiler to use. + * @param log The logger in which the Scala compiler will log info. + * @param progressOpt The progress interface in which the Scala compiler will report on the file + * being compiled. + */ + void compile( + VirtualFile[] sources, + DependencyChanges changes, + String[] options, + Output output, + AnalysisCallback callback, + Reporter reporter, + GlobalsCache cache, + Logger log, + Optional progressOpt); } diff --git a/compiler-interface/src/main/java/xsbti/compile/ScalaInstance.java b/compiler-interface/src/main/java/xsbti/compile/ScalaInstance.java index f526d0f..d1e492e 100644 --- a/compiler-interface/src/main/java/xsbti/compile/ScalaInstance.java +++ b/compiler-interface/src/main/java/xsbti/compile/ScalaInstance.java @@ -15,46 +15,57 @@ import java.io.File; /** - * A Scala instance encapsulates all the information that is bound to a concrete - * Scala version, like the ClassLoader or all the JARs required - * for Scala compilation: library jar, compiler jar and others. + * A Scala instance encapsulates all the information that is bound to a concrete Scala version, like + * the ClassLoader or all the JARs required for Scala compilation: library jar, compiler jar and + * others. * - * Both a `ClassLoader` and the jars are required because the compiler's - * boot classpath requires the location of the library and compiler jar - * on the classpath to compile any Scala program and macros. + *

Both a `ClassLoader` and the jars are required because the compiler's boot classpath requires + * the location of the library and compiler jar on the classpath to compile any Scala program and + * macros. * - * NOTE: A "jar" can actually be any valid classpath entry. + *

NOTE: A "jar" can actually be any valid classpath entry. */ public interface ScalaInstance { - /** - * Scala version for this {@link ScalaInstance}. - * - * It need not to be unique and can be dynamic (e.g. 2.10.0-SNAPSHOT). - */ - String version(); + /** + * Scala version for this {@link ScalaInstance}. + * + *

It need not to be unique and can be dynamic (e.g. 2.10.0-SNAPSHOT). + */ + String version(); - /** A class loader providing access to the classes and resources in the library and compiler jars. */ - ClassLoader loader(); + /** + * A class loader providing access to the classes and resources in all the jars of this Scala + * instance. + */ + ClassLoader loader(); - /** A class loader providing access to the classes and resources in the library. */ - ClassLoader loaderLibraryOnly(); + /** + * A class loader providing access to the classes and resources in the library jars of this Scala + * instance. + */ + ClassLoader loaderLibraryOnly(); - /** Only `jars` can be reliably provided for modularized Scala. */ - File libraryJar(); + /** Classpath entries that stores the Scala library classes. */ + File[] libraryJars(); - /** Only `jars` can be reliably provided for modularized Scala. */ - File compilerJar(); + /** @deprecated Use `libraryJars` instead (since 1.3.0). */ + @Deprecated + default File libraryJar() { + return libraryJars()[0]; + } - /** @deprecated Only `jars` can be reliably provided for modularized Scala (since 0.13.0). */ - @Deprecated - File[] otherJars(); + /** Classpath entry that stores the Scala compiler classes. */ + File compilerJar(); - /** All jar files provided by this Scala instance. */ - File[] allJars(); + /** All the jars except `libraryJars` and `compilerJar`. */ + File[] otherJars(); - /** - * The unique identifier for this Scala instance, usually obtained - * (but not necessarily) from `compiler.properties` files. - */ - String actualVersion(); + /** Classpath entries for the `loader`. */ + File[] allJars(); + + /** + * The unique identifier for this Scala instance, usually obtained (but not necessarily) from + * `compiler.properties` files. + */ + String actualVersion(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/SingleOutput.java b/compiler-interface/src/main/java/xsbti/compile/SingleOutput.java index 484fdc4..fd33a67 100755 --- a/compiler-interface/src/main/java/xsbti/compile/SingleOutput.java +++ b/compiler-interface/src/main/java/xsbti/compile/SingleOutput.java @@ -12,34 +12,34 @@ package xsbti.compile; -import java.io.File; +import java.nio.file.Path; import java.util.Optional; /** - * Represent a single output directory where the Zinc incremental compiler - * will store all the generated class files by Java and Scala sources. + * Represent a single output directory where the Zinc incremental compiler will store all the + * generated class files by Java and Scala sources. */ public interface SingleOutput extends Output { - /** - * Return the **directory or jar** where class files should be generated - * and written to. The method name is a misnamer since it can return a - * jar file when straight-to-jar compilation is enabled. - *

- * Incremental compilation manages the class files in this file, so don't - * play with this directory out of the Zinc API. Zinc already takes care - * of deleting classes before every compilation run. - *

- * This file or directory must be exclusively used for one set of sources. - */ - public File getOutputDirectory(); + /** + * Return the **directory or jar** where class files should be generated and written to. The + * method name is a misnamer since it can return a jar file when straight-to-jar compilation is + * enabled. + * + *

Incremental compilation manages the class files in this file, so don't play with this + * directory out of the Zinc API. Zinc already takes care of deleting classes before every + * compilation run. + * + *

This file or directory must be exclusively used for one set of sources. + */ + public Path getOutputDirectory(); - @Override - public default Optional getSingleOutput() { - return Optional.of(getOutputDirectory()); - } + @Override + public default Optional getSingleOutput() { + return Optional.of(getOutputDirectory()); + } - @Override - public default Optional getMultipleOutput() { - return Optional.empty(); - } -} \ No newline at end of file + @Override + public default Optional getMultipleOutput() { + return Optional.empty(); + } +} diff --git a/compiler-interface/src/main/java/xsbti/compile/WrappedClassFileManager.java b/compiler-interface/src/main/java/xsbti/compile/WrappedClassFileManager.java index b908181..d93eca4 100644 --- a/compiler-interface/src/main/java/xsbti/compile/WrappedClassFileManager.java +++ b/compiler-interface/src/main/java/xsbti/compile/WrappedClassFileManager.java @@ -12,32 +12,33 @@ package xsbti.compile; -import java.io.File; import java.util.Optional; +import xsbti.VirtualFile; /** - * Defines a classfile manager that composes the operation of two classfile manager, - * one being the internal classfile manager (the one used by the compiler) and the - * other one being the external classfile manager (a customizable, build tool-defined - * class file manager to control which class files should be notified/removed/generated - * aside from the ones covered by the internal classfile manager). + * Defines a classfile manager that composes the operation of two classfile manager, one being the + * internal classfile manager (the one used by the compiler) and the other one being the external + * classfile manager (a customizable, build tool-defined class file manager to control which class + * files should be notified/removed/generated aside from the ones covered by the internal classfile + * manager). */ public class WrappedClassFileManager implements ClassFileManager { private ClassFileManager internal; private Optional external; - public static WrappedClassFileManager of(ClassFileManager internal, Optional external) { - return new WrappedClassFileManager(internal, external); + public static WrappedClassFileManager of( + ClassFileManager internal, Optional external) { + return new WrappedClassFileManager(internal, external); } - protected WrappedClassFileManager(ClassFileManager internal, - Optional external) { + protected WrappedClassFileManager( + ClassFileManager internal, Optional external) { this.internal = internal; this.external = external; } @Override - public void delete(File[] classes) { + public void delete(VirtualFile[] classes) { // Avoid Java 8 syntax to accommodate Scala 2.10 if (external.isPresent()) { external.get().delete(classes); @@ -55,7 +56,7 @@ public void complete(boolean success) { } @Override - public void generated(File[] classes) { + public void generated(VirtualFile[] classes) { // Avoid Java 8 syntax to accommodate Scala 2.10 if (external.isPresent()) { external.get().generated(classes); diff --git a/compiler-interface/src/main/java/xsbti/compile/analysis/Compilation.java b/compiler-interface/src/main/java/xsbti/compile/analysis/Compilation.java index dc6c652..361d132 100644 --- a/compiler-interface/src/main/java/xsbti/compile/analysis/Compilation.java +++ b/compiler-interface/src/main/java/xsbti/compile/analysis/Compilation.java @@ -16,23 +16,21 @@ import java.io.Serializable; -/** - * Defines Zinc's compilation information. - */ +/** Defines Zinc's compilation information. */ public interface Compilation extends Serializable { - /** - * Returns the milliseconds since the last epoch in which the compilation started. - * - * @return Compilation start time in milliseconds. - */ - public long getStartTime(); + /** + * Returns the milliseconds since the last epoch in which the compilation started. + * + * @return Compilation start time in milliseconds. + */ + public long getStartTime(); - /** - * Returns the instance of {@link Output} used by the Zinc compiler that tells the - * user which directories are being used to store class files. - * - * @return An instance of {@link Output}. - */ - public Output getOutput(); + /** + * Returns the instance of {@link Output} used by the Zinc compiler that tells the user which + * directories are being used to store class files. + * + * @return An instance of {@link Output}. + */ + public Output getOutput(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/analysis/ReadCompilations.java b/compiler-interface/src/main/java/xsbti/compile/analysis/ReadCompilations.java index 0b8440b..89aeb3d 100644 --- a/compiler-interface/src/main/java/xsbti/compile/analysis/ReadCompilations.java +++ b/compiler-interface/src/main/java/xsbti/compile/analysis/ReadCompilations.java @@ -16,18 +16,18 @@ /** * Defines an interface to read information about Zinc's incremental compilations. - *

- * This API is useful to check how many times Zinc has compiled a set of sources and - * when that compilation took place. One can also use it to test Zinc's regressions. + * + *

This API is useful to check how many times Zinc has compiled a set of sources and when that + * compilation took place. One can also use it to test Zinc's regressions. */ public interface ReadCompilations extends Serializable { - /** - * Returns an array of {@link Compilation compilation instances} that provide - * information on Zinc's compilation runs. - *

- * Note that the array may be empty if Zinc has not compiled your project yet. - * - * @return An array of compilation information. - */ - public Compilation[] getAllCompilations(); + /** + * Returns an array of {@link Compilation compilation instances} that provide information on + * Zinc's compilation runs. + * + *

Note that the array may be empty if Zinc has not compiled your project yet. + * + * @return An array of compilation information. + */ + public Compilation[] getAllCompilations(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/analysis/ReadSourceInfos.java b/compiler-interface/src/main/java/xsbti/compile/analysis/ReadSourceInfos.java index 6d1ee74..a3bd391 100644 --- a/compiler-interface/src/main/java/xsbti/compile/analysis/ReadSourceInfos.java +++ b/compiler-interface/src/main/java/xsbti/compile/analysis/ReadSourceInfos.java @@ -12,26 +12,24 @@ package xsbti.compile.analysis; -import java.io.File; +import xsbti.VirtualFileRef; import java.util.Map; -/** - * Defines a read-only interface to get compiler information mapped to a source file. - */ +/** Defines a read-only interface to get compiler information mapped to a source file. */ public interface ReadSourceInfos { - /** - * Returns the {@link SourceInfo sourceInfo} associated with a source file. - * - * @param sourceFile The source info associated with a source file. - * @return A {@link SourceInfo sourceInfo}. - */ - public SourceInfo get(File sourceFile); + /** + * Returns the {@link SourceInfo sourceInfo} associated with a source file. + * + * @param sourceFile The source info associated with a source file. + * @return A {@link SourceInfo sourceInfo}. + */ + public SourceInfo get(VirtualFileRef sourceFile); - /** - * Returns a map of all source files with their corresponding source infos. - * - * @return A map of source files to source infos. - * @see SourceInfo - */ - public Map getAllSourceInfos(); + /** + * Returns a map of all source files with their corresponding source infos. + * + * @return A map of source files to source infos. + * @see SourceInfo + */ + public Map getAllSourceInfos(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/analysis/ReadStamps.java b/compiler-interface/src/main/java/xsbti/compile/analysis/ReadStamps.java index b573d71..ff9fc06 100644 --- a/compiler-interface/src/main/java/xsbti/compile/analysis/ReadStamps.java +++ b/compiler-interface/src/main/java/xsbti/compile/analysis/ReadStamps.java @@ -12,70 +12,62 @@ package xsbti.compile.analysis; +import xsbti.VirtualFileRef; +import xsbti.VirtualFile; import xsbti.api.DependencyContext; -import java.io.File; import java.util.Map; /** * A read-only interface to get the timestamps of the binaries, sources and compilation products. */ public interface ReadStamps { - /** - * Retrieves the stamp associated with a given class file. - * - * @param compilationProduct The product produced by the current compilation run of a source file. - * @return The stamp for a class file. - * @see xsbti.AnalysisCallback#generatedLocalClass(File, File) - * @see xsbti.AnalysisCallback#generatedNonLocalClass(File, File, String, String) - */ - public Stamp product(File compilationProduct); + /** + * Retrieves the stamp associated with a given class file. + * + * @param compilationProduct The product produced by the current compilation run of a source file. + * @return The stamp for a class file. + */ + public Stamp product(VirtualFileRef compilationProduct); - /** - * Retrieves the stamp associated with a given internal source. - *

- * Note that the internal source has to be a source under compilation. - * - * @param internalSource The source file under compilation. - * @return The stamp for the file. - * @see xsbti.AnalysisCallback#startSource(File) - */ - public Stamp source(File internalSource); + /** + * Retrieves the stamp associated with a given internal source. + * + *

Note that the internal source has to be a source under compilation. + * + * @param internalSource The source file under compilation. + * @return The stamp for the file. + */ + public Stamp source(VirtualFile internalSource); - /** - * Retrieves the stamp associated with a binary dependency (class file). - * - * @param binaryFile A class file that represents an external or internal dependency. - * @return The stamp for the file. - * @see xsbti.AnalysisCallback#binaryDependency(File, String, String, File, DependencyContext) - */ - public Stamp binary(File binaryFile); + /** + * Retrieves the stamp associated with a binary dependency (class file). + * + * @param libraryFile A class file that represents an external or internal dependency. + * @return The stamp for the file. + */ + public Stamp library(VirtualFileRef libraryFile); - /** - * Returns a map of all the stamps associated with binary files. - * - * @return A map of binary files to stamps. - * @see xsbti.compile.analysis.ReadStamps#binary(File) - */ - public Map getAllBinaryStamps(); + /** + * Returns a map of all the stamps associated with binary files. + * + * @return A map of binary files to stamps. + */ + public Map getAllLibraryStamps(); - /** - * Returns a map of all the stamps associated with source files. - * - * @return A map of source files to stamps. - * @see xsbti.compile.analysis.ReadStamps#source(File) - */ - public Map getAllSourceStamps(); - - /** - * Returns a map of all the stamps associated with product files. - *

- * Note that the returned map can be empty if no compilation has happened yet. - * - * @return A map of product files to stamps. - * (e.g. compile analysis is empty). - * @see xsbti.compile.analysis.ReadStamps#product(File) - */ - public Map getAllProductStamps(); + /** + * Returns a map of all the stamps associated with source files. + * + * @return A map of source files to stamps. + */ + public Map getAllSourceStamps(); + /** + * Returns a map of all the stamps associated with product files. + * + *

Note that the returned map can be empty if no compilation has happened yet. + * + * @return A map of product files to stamps. (e.g. compile analysis is empty). + */ + public Map getAllProductStamps(); } diff --git a/compiler-interface/src/main/java/xsbti/compile/analysis/SourceInfo.java b/compiler-interface/src/main/java/xsbti/compile/analysis/SourceInfo.java index 5c85acf..2879b48 100644 --- a/compiler-interface/src/main/java/xsbti/compile/analysis/SourceInfo.java +++ b/compiler-interface/src/main/java/xsbti/compile/analysis/SourceInfo.java @@ -14,33 +14,30 @@ import xsbti.Problem; -/** - * Defines the compiler information for a given compilation unit (source file). - */ +/** Defines the compiler information for a given compilation unit (source file). */ public interface SourceInfo { - /** - * Returns the reported problems by the Java or Scala compiler. - *

- * Note that a reported problem is a problem that has been shown to the end user. - * - * @return The compiler reported problems. - */ - public Problem[] getReportedProblems(); + /** + * Returns the reported problems by the Java or Scala compiler. + * + *

Note that a reported problem is a problem that has been shown to the end user. + * + * @return The compiler reported problems. + */ + public Problem[] getReportedProblems(); - /** - * Returns the unreported problems by the Java or Scala compiler. - *

- * Note that an unreported problem is a problem that has not been shown to the end user. - * - * @return The compiler reported problems. - */ - public Problem[] getUnreportedProblems(); + /** + * Returns the unreported problems by the Java or Scala compiler. + * + *

Note that an unreported problem is a problem that has not been shown to the end user. + * + * @return The compiler reported problems. + */ + public Problem[] getUnreportedProblems(); - /** - * Returns the main classes found in this compilation unit. - * - * @return The full name of the main classes, like "foo.bar.Main" - */ - public String[] getMainClasses(); + /** + * Returns the main classes found in this compilation unit. + * + * @return The full name of the main classes, like "foo.bar.Main" + */ + public String[] getMainClasses(); } - diff --git a/compiler-interface/src/main/java/xsbti/compile/analysis/Stamp.java b/compiler-interface/src/main/java/xsbti/compile/analysis/Stamp.java index bf352af..bc8ec11 100644 --- a/compiler-interface/src/main/java/xsbti/compile/analysis/Stamp.java +++ b/compiler-interface/src/main/java/xsbti/compile/analysis/Stamp.java @@ -12,42 +12,39 @@ package xsbti.compile.analysis; - import java.util.Optional; /** * A stamp defines certain properties or information on files. - *

- * Stamp properties are available depending on its associated file. * - * A stamp is empty when getHash and getModified return - * an empty {@link Optional optional}. This value is returned for files that have - * not been tracked by the incremental compiler. + *

Stamp properties are available depending on its associated file. + * + *

A stamp is empty when getHash and getModified return an empty {@link + * Optional optional}. This value is returned for files that have not been tracked by the + * incremental compiler. */ public interface Stamp { - /** - * Returns a unique identifier depending on the underlying data structures. - * - * @return A valid string-based representation for logical equality, not referential equality. - */ - public int getValueId(); + /** + * Returns a unique identifier depending on the underlying data structures. + * + * @return A valid string-based representation for logical equality, not referential equality. + */ + public int getValueId(); - /** - * @return A string-based and recoverable representation of the underlying stamp. - */ - public String writeStamp(); + /** @return A string-based and recoverable representation of the underlying stamp. */ + public String writeStamp(); - /** - * Get the hash of the file contents if the stamp supports it. - * - * @return An optional hash of the file contents. - */ - public Optional getHash(); + /** + * Get the hash of the file contents if the stamp supports it. + * + * @return An optional hash of the file contents. + */ + public Optional getHash(); - /** - * Get the last modified time (in milliseconds from Epoch) of a file if the stamp supports it. - * - * @return An optional last modified time. - */ - public Optional getLastModified(); + /** + * Get the last modified time (in milliseconds from Epoch) of a file if the stamp supports it. + * + * @return An optional last modified time. + */ + public Optional getLastModified(); } diff --git a/compiler-interface/src/test/scala/xsbti/TestCallback.scala b/compiler-interface/src/test/scala/xsbti/TestCallback.scala deleted file mode 100644 index f330529..0000000 --- a/compiler-interface/src/test/scala/xsbti/TestCallback.scala +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Scala compiler interface - * - * Copyright Lightbend, Inc. and Mark Harrah - * - * Licensed under Apache License 2.0 - * (http://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package xsbti - -import java.io.File -import java.util - -import xsbti.api.{ DependencyContext, ClassLike } - -import scala.collection.mutable.ArrayBuffer - -class TestCallback extends AnalysisCallback { - case class TestUsedName(name: String, scopes: util.EnumSet[UseScope]) - - val classDependencies = new ArrayBuffer[(String, String, DependencyContext)] - val binaryDependencies = new ArrayBuffer[(File, String, String, DependencyContext)] - val productClassesToSources = scala.collection.mutable.Map.empty[File, File] - val usedNamesAndScopes = - scala.collection.mutable.Map.empty[String, Set[TestUsedName]].withDefaultValue(Set.empty) - val classNames = - scala.collection.mutable.Map.empty[File, Set[(String, String)]].withDefaultValue(Set.empty) - val apis: scala.collection.mutable.Map[File, Set[ClassLike]] = scala.collection.mutable.Map.empty - - def usedNames = usedNamesAndScopes.mapValues(_.map(_.name)) - - def startSource(source: File): Unit = { - assert(!apis.contains(source), - s"The startSource can be called only once per source file: $source") - apis(source) = Set.empty - } - - def classDependency(onClassName: String, - sourceClassName: String, - context: DependencyContext): Unit = { - if (onClassName != sourceClassName) - classDependencies += ((onClassName, sourceClassName, context)) - () - } - def binaryDependency(onBinary: File, - onBinaryClassName: String, - fromClassName: String, - fromSourceFile: File, - context: DependencyContext): Unit = { - binaryDependencies += ((onBinary, onBinaryClassName, fromClassName, context)) - () - } - def generatedNonLocalClass(sourceFile: File, - classFile: File, - binaryClassName: String, - srcClassName: String): Unit = { - productClassesToSources += ((classFile, sourceFile)) - classNames(sourceFile) += ((srcClassName, binaryClassName)) - () - } - - def generatedLocalClass(sourceFile: File, classFile: File): Unit = { - productClassesToSources += ((classFile, sourceFile)) - () - } - - def usedName(className: String, name: String, scopes: util.EnumSet[UseScope]): Unit = - usedNamesAndScopes(className) += TestUsedName(name, scopes) - - def api(source: File, api: ClassLike): Unit = { - apis(source) += api - () - } - - def mainClass(source: File, className: String): Unit = () - - override def enabled(): Boolean = true - - def problem(category: String, - pos: xsbti.Position, - message: String, - severity: xsbti.Severity, - reported: Boolean): Unit = () - - override def dependencyPhaseCompleted(): Unit = {} - - override def apiPhaseCompleted(): Unit = {} - - override def classesInOutputJar(): util.Set[String] = java.util.Collections.emptySet() -} - -object TestCallback { - case class ExtractedClassDependencies(memberRef: Map[String, Set[String]], - inheritance: Map[String, Set[String]], - localInheritance: Map[String, Set[String]]) - object ExtractedClassDependencies { - def fromPairs( - memberRefPairs: Seq[(String, String)], - inheritancePairs: Seq[(String, String)], - localInheritancePairs: Seq[(String, String)] - ): ExtractedClassDependencies = { - ExtractedClassDependencies(pairsToMultiMap(memberRefPairs), - pairsToMultiMap(inheritancePairs), - pairsToMultiMap(localInheritancePairs)) - } - - private def pairsToMultiMap[A, B](pairs: Seq[(A, B)]): Map[A, Set[B]] = { - import scala.collection.mutable.{ HashMap, MultiMap } - val emptyMultiMap = new HashMap[A, scala.collection.mutable.Set[B]] with MultiMap[A, B] - val multiMap = pairs.foldLeft(emptyMultiMap) { - case (acc, (key, value)) => - acc.addBinding(key, value) - } - // convert all collections to immutable variants - multiMap.toMap.mapValues(_.toSet).withDefaultValue(Set.empty) - } - } -} diff --git a/dummy-bridge/src/main/scala-2.13/xsbt/Compat.scala b/dummy-bridge/src/main/scala-2.13/xsbt/Compat.scala new file mode 100644 index 0000000..d0e638a --- /dev/null +++ b/dummy-bridge/src/main/scala-2.13/xsbt/Compat.scala @@ -0,0 +1,43 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.io.PrintWriter +import java.nio.file.Path +import xsbti.compile.Output +import scala.tools.nsc.Settings +import scala.tools.nsc.interpreter.shell.ReplReporterImpl +import scala.reflect.io.AbstractFile + +abstract class Compat +object Compat { + type PlainNioFile = scala.reflect.io.PlainNioFile + + // IR is renanmed to Results + val Results = scala.tools.nsc.interpreter.Results + + // IMain in 2.13 accepts ReplReporter + def replReporter(settings: Settings, writer: PrintWriter) = + new ReplReporterImpl(settings, writer) + + def plainNioFile(path: Path): AbstractFile = new PlainNioFile(path) +} + +/** Defines compatibility utils for [[ZincCompiler]]. */ +trait ZincGlobalCompat { + protected def superDropRun(): Unit = () +} + +private trait CachedCompilerCompat { self: CachedCompiler0 => + def newCompiler(settings: Settings, reporter: DelegatingReporter, output: Output): ZincCompiler = + new ZincCompiler(settings, reporter, output) +} diff --git a/dummy-bridge/src/main/scala-2.13/xsbt/ConsoleInterface.scala b/dummy-bridge/src/main/scala-2.13/xsbt/ConsoleInterface.scala new file mode 100644 index 0000000..fbc4475 --- /dev/null +++ b/dummy-bridge/src/main/scala-2.13/xsbt/ConsoleInterface.scala @@ -0,0 +1,110 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import xsbti.Logger +import scala.tools.nsc.interpreter.IMain +import scala.tools.nsc.interpreter.shell.{ ILoop, ShellConfig, ReplReporterImpl } +import scala.tools.nsc.{ GenericRunnerCommand, Settings } + +class ConsoleInterface { + def commandArguments( + args: Array[String], + bootClasspathString: String, + classpathString: String, + log: Logger + ): Array[String] = + MakeSettings.sync(args, bootClasspathString, classpathString, log).recreateArgs.toArray[String] + + def run( + args: Array[String], + bootClasspathString: String, + classpathString: String, + initialCommands: String, + cleanupCommands: String, + loader: ClassLoader, + bindNames: Array[String], + bindValues: Array[Any], + log: Logger + ): Unit = { + lazy val interpreterSettings = MakeSettings.sync(args.toList, log) + val compilerSettings = MakeSettings.sync(args, bootClasspathString, classpathString, log) + + log.info(Message("Starting scala interpreter...")) + log.info(Message("")) + + val loop = new ILoop(ShellConfig(interpreterSettings)) { + override def createInterpreter(interpreterSettings: Settings) = { + if (loader ne null) { + val reporter = new ReplReporterImpl(interpreterSettings) + intp = new IMain(interpreterSettings, reporter) { + override protected def parentClassLoader = + if (loader eq null) super.parentClassLoader + else loader + } + intp.setContextClassLoader() + } else + super.createInterpreter(interpreterSettings) + + for ((id, value) <- bindNames zip bindValues) { + intp.beQuietDuring { + intp.bind(id, value.asInstanceOf[AnyRef].getClass.getName, value) + () + } + } + + if (!initialCommands.isEmpty) + intp.interpret(initialCommands) + + () + } + + override def closeInterpreter(): Unit = { + if (!cleanupCommands.isEmpty) + intp.interpret(cleanupCommands) + super.closeInterpreter() + } + } + + loop.run(compilerSettings) + () + } +} + +object MakeSettings { + def apply(args: List[String], log: Logger): Settings = { + val command = new GenericRunnerCommand(args, message => log.error(Message(message))) + if (command.ok) + command.settings + else + throw new InterfaceCompileFailed(Array(), Array(), command.usageMsg) + } + + def sync( + args: Array[String], + bootClasspathString: String, + classpathString: String, + log: Logger + ): Settings = { + val compilerSettings = sync(args.toList, log) + if (!bootClasspathString.isEmpty) + compilerSettings.bootclasspath.value = bootClasspathString + compilerSettings.classpath.value = classpathString + compilerSettings + } + + def sync(options: List[String], log: Logger): Settings = { + val settings = apply(options, log) + settings.Yreplsync.value = true + settings + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/API.scala b/dummy-bridge/src/main/scala/xsbt/API.scala new file mode 100644 index 0000000..eb2c76e --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/API.scala @@ -0,0 +1,203 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import scala.tools.nsc.Phase +import scala.tools.nsc.symtab.Flags +import xsbti.api._ +import xsbti.VirtualFile + +object API { + val name = "xsbt-api" +} + +final class API(val global: CallbackGlobal) extends Compat with GlobalHelpers with ClassName { + import global._ + + import scala.collection.mutable + private val nonLocalClassSymbolsInCurrentUnits = new mutable.HashSet[Symbol]() + + def newPhase(prev: Phase) = new ApiPhase(prev) + class ApiPhase(prev: Phase) extends GlobalPhase(prev) { + override def description = "Extracts the public API from source files." + def name = API.name + override def run(): Unit = { + val start = System.currentTimeMillis + super.run() + + // After processing all units, register generated classes + registerGeneratedClasses(nonLocalClassSymbolsInCurrentUnits.iterator) + nonLocalClassSymbolsInCurrentUnits.clear() + + callback.apiPhaseCompleted() + val stop = System.currentTimeMillis + debuglog("API phase took : " + ((stop - start) / 1000.0) + " s") + } + + def apply(unit: global.CompilationUnit): Unit = processUnit(unit) + private def processUnit(unit: CompilationUnit) = if (!unit.isJava) processScalaUnit(unit) + private def processScalaUnit(unit: CompilationUnit): Unit = { + val sourceFile: VirtualFile = unit.source.file match { + case v: VirtualFileWrap => v.underlying + } + debuglog("Traversing " + sourceFile) + callback.startSource(sourceFile) + val extractApi = new ExtractAPI[global.type](global, sourceFile) + val traverser = new TopLevelHandler(extractApi) + traverser.apply(unit.body) + + val extractUsedNames = new ExtractUsedNames[global.type](global) + extractUsedNames.extractAndReport(unit) + + val classApis = traverser.allNonLocalClasses + val mainClasses = traverser.mainClasses + + // Use of iterators make this code easier to profile + + val classApisIt = classApis.iterator + while (classApisIt.hasNext) { + callback.api(sourceFile, classApisIt.next()) + } + + val mainClassesIt = mainClasses.iterator + while (mainClassesIt.hasNext) { + callback.mainClass(sourceFile, mainClassesIt.next()) + } + + extractApi.allExtractedNonLocalSymbols.foreach { cs => + // Only add the class symbols defined in this compilation unit + if (cs.sourceFile != null) nonLocalClassSymbolsInCurrentUnits.+=(cs) + } + } + } + + private case class FlattenedNames(binaryName: String, className: String) + + /** + * Registers only non-local generated classes in the callback by extracting + * information about its names and using the names to generate class file paths. + * + * Mimics the previous logic that was present in `Analyzer`, despite the fact + * that now we construct the names that the compiler will give to every non-local + * class independently of genbcode. + * + * Why do we do this? The motivation is that we want to run the incremental algorithm + * independently of the compiler pipeline. This independence enables us to: + * + * 1. Offload the incremental compiler logic out of the primary pipeline and + * run the incremental phases concurrently. + * 2. Know before the compilation is completed whether another compilation will or + * will not be required. This is important to make incremental compilation work + * with pipelining and enables further optimizations; for example, we can start + * subsequent incremental compilations before (!) the initial compilation is done. + * This can buy us ~30-40% faster incremental compiler iterations. + * + * This method only takes care of non-local classes because local classes have no + * relevance in the correctness of the algorithm and can be registered after genbcode. + * Local classes are only used to construct the relations of products and to produce + * the list of generated files + stamps, but names referring to local classes **never** + * show up in the name hashes of classes' APIs, hence never considered for name hashing. + * + * As local class files are owned by other classes that change whenever they change, + * we could most likely live without adding their class files to the products relation + * and registering their stamps. However, to be on the safe side, we will continue to + * register the local products in `Analyzer`. + * + * @param allClassSymbols The class symbols found in all the compilation units. + */ + def registerGeneratedClasses(classSymbols: Iterator[Symbol]): Unit = { + classSymbols.foreach { symbol => + val sourceFile = symbol.sourceFile + val sourceJavaFile0 = + if (sourceFile == null) symbol.enclosingTopLevelClass.sourceFile + else sourceFile + val sourceJavaFile: VirtualFile = sourceJavaFile0 match { + case v: VirtualFileWrap => v.underlying + } + + def registerProductNames(names: FlattenedNames): Unit = { + // Guard against a local class in case it surreptitiously leaks here + if (!symbol.isLocalClass) { + val pathToClassFile = s"${names.binaryName}.class" + val classFile = { + JarUtils.outputJar match { + case Some(outputJar) => + new java.io.File(JarUtils.classNameInJar(outputJar, pathToClassFile)) + case None => + val outputDir = global.settings.outputDirs.outputDirFor(sourceFile).file + new java.io.File(outputDir, pathToClassFile) + } + } + val zincClassName = names.className + val srcClassName = classNameAsString(symbol) + callback.generatedNonLocalClass( + sourceJavaFile, + classFile.toPath, + zincClassName, + srcClassName + ) + } else () + } + + val names = FlattenedNames( + fullName(symbol, java.io.File.separatorChar, symbol.moduleSuffix, true), + fullName(symbol, '.', symbol.moduleSuffix, false) + ) + + registerProductNames(names) + + // Register the names of top-level module symbols that emit two class files + val isTopLevelUniqueModule = + symbol.owner.isPackageClass && symbol.isModuleClass && symbol.companionClass == NoSymbol + if (isTopLevelUniqueModule || symbol.isPackageObject) { + val names = FlattenedNames( + fullName(symbol, java.io.File.separatorChar, "", true), + fullName(symbol, '.', "", false) + ) + registerProductNames(names) + } + } + } + + private final class TopLevelHandler(extractApi: ExtractAPI[global.type]) + extends TopLevelTraverser { + def allNonLocalClasses: Set[ClassLike] = { + extractApi.allExtractedNonLocalClasses + } + + def mainClasses: Set[String] = extractApi.mainClasses + + def `class`(c: Symbol): Unit = { + extractApi.extractAllClassesOf(c.owner, c) + } + } + + private abstract class TopLevelTraverser extends Traverser { + def `class`(s: Symbol): Unit + override def traverse(tree: Tree): Unit = { + tree match { + case (_: ClassDef | _: ModuleDef) if isTopLevel(tree.symbol) => `class`(tree.symbol) + case _: PackageDef => + super.traverse(tree) + case _ => + } + } + def isTopLevel(sym: Symbol): Boolean = { + !ignoredSymbol(sym) && + sym.isStatic && + !sym.isImplClass && + !sym.hasFlag(Flags.JAVA) && + !sym.isNestedClass + } + } + +} diff --git a/dummy-bridge/src/main/scala/xsbt/Analyzer.scala b/dummy-bridge/src/main/scala/xsbt/Analyzer.scala new file mode 100644 index 0000000..02ecd9f --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/Analyzer.scala @@ -0,0 +1,104 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.nio.file.Path +import java.io.File +import xsbti.VirtualFile +import scala.tools.nsc.Phase +import scala.collection.JavaConverters._ + +object Analyzer { + def name = "xsbt-analyzer" +} + +final class Analyzer(val global: CallbackGlobal) extends LocateClassFile { + import global._ + + def newPhase(prev: Phase): Phase = new AnalyzerPhase(prev) + private class AnalyzerPhase(prev: Phase) extends GlobalPhase(prev) { + override def description = + "Finds concrete instances of provided superclasses, and application entry points." + def name = Analyzer.name + + /** + * When straight-to-jar compilation is enabled, returns the classes + * that are found in the jar of the last compilation. This method + * gets the existing classes from the analysis callback and adapts + * it for consumption in the compiler bridge. + * + * It's lazy because it triggers a read of the zip, which may be + * unnecessary if there are no local classes in a compilation unit. + */ + private lazy val classesWrittenByGenbcode: Set[String] = { + JarUtils.outputJar match { + case Some(jar) => + val classes = global.callback.classesInOutputJar().asScala + classes.map(JarUtils.classNameInJar(jar, _)).toSet + case None => Set.empty + } + } + + def apply(unit: CompilationUnit): Unit = { + if (!unit.isJava) { + val sourceFile0: VirtualFileWrap = unit.source.file match { + case v: VirtualFileWrap => v + } + val sourceFile: VirtualFile = sourceFile0.underlying + lazy val outputDir = settings.outputDirs.outputDirFor(sourceFile0).file + for (iclass <- unit.icode) { + val sym = iclass.symbol + def addGenerated(separatorRequired: Boolean): Unit = { + val locatedClass = { + JarUtils.outputJar match { + case Some(outputJar) => locateClassInJar(sym, outputJar, separatorRequired) + case None => locatePlainClassFile(sym, outputDir, separatorRequired) + } + } + + locatedClass.foreach { classFile => + assert(sym.isClass, s"${sym.fullName} is not a class") + // Use own map of local classes computed before lambdalift to ascertain class locality + if (localToNonLocalClass.isLocal(sym).getOrElse(true)) { + // Inform callback about local classes, non-local classes have been reported in API + callback.generatedLocalClass(sourceFile, classFile.toPath) + } + } + } + + if (sym.isModuleClass && !sym.isImplClass) { + if (isTopLevelModule(sym) && sym.companionClass == NoSymbol) + addGenerated(false) + addGenerated(true) + } else + addGenerated(false) + } + } + } + + private def locatePlainClassFile( + sym: Symbol, + outputDir: File, + separatorRequired: Boolean + ): Option[File] = { + val classFile = fileForClass(outputDir, sym, separatorRequired) + if (classFile.exists()) Some(classFile) else None + } + + private def locateClassInJar(sym: Symbol, jar: Path, sepRequired: Boolean): Option[File] = { + val classFile = pathToClassFile(sym, sepRequired) + val classInJar = JarUtils.classNameInJar(jar, classFile) + if (!classesWrittenByGenbcode.contains(classInJar)) None + else Some(new File(classInJar)) + } + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/CallbackGlobal.scala b/dummy-bridge/src/main/scala/xsbt/CallbackGlobal.scala new file mode 100644 index 0000000..d2caeac --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/CallbackGlobal.scala @@ -0,0 +1,274 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import xsbti.{ AnalysisCallback, Severity } +import xsbti.compile._ + +import scala.tools.nsc._ +import io.AbstractFile +import java.nio.file.{ Files, Path } + +import scala.reflect.io.PlainFile + +/** Defines the interface of the incremental compiler hiding implementation details. */ +sealed abstract class CallbackGlobal( + settings: Settings, + reporter: reporters.Reporter, + output: Output +) extends Global(settings, reporter) { + + def callback: AnalysisCallback + def findAssociatedFile(name: String): Option[(AbstractFile, Boolean)] + + def fullName( + symbol: Symbol, + separator: Char, + suffix: CharSequence, + includePackageObjectClassNames: Boolean + ): String + + lazy val outputDirs: Iterable[Path] = { + output match { + case single: SingleOutput => List(single.getOutputDirectory) + // Use Stream instead of List because Analyzer maps intensively over the directories + case multi: MultipleOutput => multi.getOutputGroups.toStream map (_.getOutputDirectory) + } + } + + lazy val JarUtils = new JarUtils(outputDirs) + + /** + * Defines the sbt phase in which the dependency analysis is performed. + * The reason why this is exposed in the callback global is because it's used + * in [[xsbt.LocalToNonLocalClass]] to make sure the we don't resolve local + * classes before we reach this phase. + */ + private[xsbt] val sbtDependency: SubComponent + + /** + * A map from local classes to non-local class that contains it. + * + * This map is used by both Dependency and Analyzer phase so it has to be + * exposed here. The Analyzer phase uses the cached lookups performed by + * the Dependency phase. By the time Analyzer phase is run (close to backend + * phases), original owner chains are lost so Analyzer phase relies on + * information saved before. + * + * The LocalToNonLocalClass duplicates the tracking that Scala compiler does + * internally for backed purposes (generation of EnclosingClass attributes) but + * that internal mapping doesn't have a stable interface we could rely on. + */ + private[xsbt] val localToNonLocalClass = new LocalToNonLocalClass[this.type](this) +} + +/** Defines the implementation of Zinc with all its corresponding phases. */ +sealed class ZincCompiler(settings: Settings, dreporter: DelegatingReporter, output: Output) + extends CallbackGlobal(settings, dreporter, output) + with ZincGlobalCompat { + + final class ZincRun(compileProgress: CompileProgress) extends Run { + override def informUnitStarting(phase: Phase, unit: CompilationUnit): Unit = + compileProgress.startUnit(phase.name, unit.source.path) + override def progress(current: Int, total: Int): Unit = + if (!compileProgress.advance(current, total)) cancel else () + } + + object dummy // temporary fix for #4426 + + /** Phase that analyzes the generated class files and maps them to sources. */ + object sbtAnalyzer extends { + val global: ZincCompiler.this.type = ZincCompiler.this + val phaseName = Analyzer.name + val runsAfter = List("jvm") + override val runsBefore = List("terminal") + val runsRightAfter = None + } with SubComponent { + val analyzer = new Analyzer(global) + def newPhase(prev: Phase) = analyzer.newPhase(prev) + def name = phaseName + } + + /** Phase that extracts dependency information */ + object sbtDependency extends { + val global: ZincCompiler.this.type = ZincCompiler.this + val phaseName = Dependency.name + val runsAfter = List(API.name) + override val runsBefore = List("refchecks") + // Keep API and dependency close to each other -- we may want to merge them in the future. + override val runsRightAfter = Some(API.name) + } with SubComponent { + val dependency = new Dependency(global) + def newPhase(prev: Phase) = dependency.newPhase(prev) + def name = phaseName + } + + /** + * Phase that walks the trees and constructs a representation of the public API. + * + * @note It extracts the API information after picklers to see the same symbol information + * irrespective of whether we typecheck from source or unpickle previously compiled classes. + */ + object apiExtractor extends { + val global: ZincCompiler.this.type = ZincCompiler.this + val phaseName = API.name + val runsAfter = List("typer") + override val runsBefore = List("erasure") + // TODO: Consider migrating to "uncurry" for `runsBefore`. + // TODO: Consider removing the system property to modify which phase is used for API extraction. + val runsRightAfter = Option(System.getProperty("sbt.api.phase")) orElse Some("pickler") + } with SubComponent { + val api = new API(global) + def newPhase(prev: Phase) = api.newPhase(prev) + def name = phaseName + } + + override lazy val phaseDescriptors = { + phasesSet += sbtAnalyzer + if (callback.enabled()) { + phasesSet += sbtDependency + phasesSet += apiExtractor + } + this.computePhaseDescriptors + } + + private final val fqnsToAssociatedFiles = perRunCaches.newMap[String, (AbstractFile, Boolean)]() + + /** + * Returns the associated file of a fully qualified name and whether it's on the classpath. + * Note that the abstract file returned must exist. + */ + def findAssociatedFile(fqn: String): Option[(AbstractFile, Boolean)] = { + def findOnPreviousCompilationProducts(name: String): Option[AbstractFile] = { + // This class file path is relative to the output jar/directory and computed from class name + val classFilePath = name.replace('.', '/') + ".class" + + JarUtils.outputJar match { + case Some(outputJar) => + if (!callback.classesInOutputJar().contains(classFilePath)) None + else { + /* + * Important implementation detail: `classInJar` has the format of `$JAR!$CLASS_REF` + * which is, of course, a path to a file that does not exist. This file path is + * interpreted especially by Zinc to decompose the format under straight-to-jar + * compilation. For this strategy to work, `PlainFile` must **not** check that + * this file does exist or not because, if it does, it will return `null` in + * `processExternalDependency` and the dependency will not be correctly registered. + * If scalac breaks this contract (the check for existence is done when creating + * a normal reflect file but not a plain file), Zinc will not work correctly. + */ + Some(new PlainFile(JarUtils.classNameInJar(outputJar, classFilePath))) + } + + case None => // The compiler outputs class files in a classes directory (the default) + // This lookup could be improved if a hint where to look is given. + outputDirs + .map(_.resolve(classFilePath)) + .find(Files.exists(_)) + .map(Compat.plainNioFile(_)) + } + } + + def findOnClassPath(name: String): Option[AbstractFile] = + classPath.findClass(name).flatMap(_.binary.asInstanceOf[Option[AbstractFile]]) + + fqnsToAssociatedFiles.get(fqn).orElse { + val newResult = findOnPreviousCompilationProducts(fqn) + .map(f => (f, true)) + .orElse(findOnClassPath(fqn).map(f => (f, false))) + newResult.foreach(res => fqnsToAssociatedFiles.put(fqn, res)) + newResult + } + } + + /** + * Replicate the behaviour of `fullName` with a few changes to the code to produce + * correct file-system compatible full names for non-local classes. It mimics the + * paths of the class files produced by genbcode. + * + * Changes compared to the normal version in the compiler: + * + * 1. It will use the encoded name instead of the normal name. + * 2. It will not skip the name of the package object class (required for the class file path). + * + * Note that using `javaBinaryName` is not useful for these symbols because we + * need the encoded names. Zinc keeps track of encoded names in both the binary + * names and the Zinc names. + * + * @param symbol The symbol for which we extract the full name. + * @param separator The separator that we will apply between every name. + * @param suffix The suffix to add at the end (in case it's a module). + * @param includePackageObjectClassNames Include package object class names or not. + * @return The full name. + */ + override def fullName( + symbol: Symbol, + separator: Char, + suffix: CharSequence, + includePackageObjectClassNames: Boolean + ): String = { + var b: java.lang.StringBuffer = null + def loop(size: Int, sym: Symbol): Unit = { + val symName = sym.name + // Use of encoded to produce correct paths for names that have symbols + val encodedName = symName.encoded + val nSize = encodedName.length - (if (symName.endsWith(nme.LOCAL_SUFFIX_STRING)) 1 else 0) + if (sym.isRoot || sym.isRootPackage || sym == NoSymbol || sym.owner.isEffectiveRoot) { + val capacity = size + nSize + b = new java.lang.StringBuffer(capacity) + b.append(chrs, symName.start, nSize) + } else { + val next = if (sym.owner.isPackageObjectClass) sym.owner else sym.effectiveOwner.enclClass + loop(size + nSize + 1, next) + // Addition to normal `fullName` to produce correct names for nested non-local classes + if (sym.isNestedClass) b.append(nme.MODULE_SUFFIX_STRING) else b.append(separator) + b.append(chrs, symName.start, nSize) + } + () + } + loop(suffix.length(), symbol) + b.append(suffix) + b.toString + } + + private[this] var callback0: AnalysisCallback = null + + /** Returns the active analysis callback, set by [[set]] and cleared by [[clear]]. */ + def callback: AnalysisCallback = callback0 + + final def set(callback: AnalysisCallback, dreporter: DelegatingReporter): Unit = { + this.callback0 = callback + reporter = dreporter + } + + final def clear(): Unit = { + callback0 = null + superDropRun() + reporter = null + this match { + case c: java.io.Closeable => c.close() + case _ => + } + } + + // Scala 2.10.x and later + private[xsbt] def logUnreportedWarnings(seq: Seq[(String, List[(Position, String)])]): Unit = { + for ((what, warnings) <- seq; (pos, msg) <- warnings) + yield callback.problem(what, DelegatingReporter.convert(pos), msg, Severity.Warn, false) + () + } +} + +import scala.reflect.internal.Positions +final class ZincCompilerRangePos(settings: Settings, dreporter: DelegatingReporter, output: Output) + extends ZincCompiler(settings, dreporter, output) + with Positions diff --git a/dummy-bridge/src/main/scala/xsbt/ClassName.scala b/dummy-bridge/src/main/scala/xsbt/ClassName.scala new file mode 100644 index 0000000..50e577f --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/ClassName.scala @@ -0,0 +1,103 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import scala.tools.nsc.Global + +/** + * Utility methods for creating (source|binary) class names for a Symbol. + */ +trait ClassName extends Compat { + val global: Global + import global._ + + /** + * Creates a flat (binary) name for a class symbol `s`. + */ + protected def flatname(s: Symbol, separator: Char) = + enteringPhase(currentRun.flattenPhase.next) { s fullName separator } + + /** + * Create a (source) name for a class symbol `s`. + */ + protected def className(s: Symbol): Name = pickledName(s) + + /** + * Create a String (source) name for a class symbol `s`. + */ + protected def classNameAsString(s: Symbol): String = pickledNameAsString(s) + + /** + * Given a class symbol `cls`, construct a name representing this constructor. + * For a class: + * + * a.b.Foo + * + * this is: + * + * a;b;Foo;init; + * + * The prefix is important to avoid name hashing all constructors together + * (see #97), the weird format is necessary to avoid scalac or zinc trying to + * interpret this name (in particular we should not use '.' and we should not + * use ''), we use ';' because it is one of the few characters that + * cannot appear in a valid JVM name. + */ + protected def constructorName(cls: Symbol): Name = + newTermName(constructorNameAsString(cls)) + + protected def constructorNameAsString(cls: Symbol): String = + cls.fullName(';') ++ ";init;" + + /** + * Mangle a JVM symbol name in a format better suited for internal uses by sbt. + */ + protected def mangledName(s: Symbol): Name = + if (s.name == nme.CONSTRUCTOR) + constructorName(s.enclClass) + else + s.name + + /** + * Create a (source) name for the class symbol `s` with a prefix determined by the class symbol `in`. + * + * If `s` represents a package object `pkg3`, then the returned name will be `pkg1.pkg2.pkg3.package`. + * If `s` represents a class `Foo` nested in package object `pkg3` then the returned name is `pkg1.pkg2.pk3.Foo`. + * + * Note that some objects with special access rights are encoded in names + * (like qualified privates `private[qualifier]`). In order to get the right + * original names, we need to use `unexpandedName`. + */ + protected def classNameAsSeenIn(in: Symbol, s: Symbol): String = + enteringPhase(currentRun.picklerPhase.next) { + if (in.isRoot || in.isRootPackage || in == NoSymbol || in.isEffectiveRoot) + s.simpleName.toString + else if (in.isPackageObjectOrClass) + in.owner.fullName + "." + s.unexpandedName + else + in.fullName + "." + s.unexpandedName + } + + private def pickledName(s: Symbol): Name = + enteringPhase(currentRun.picklerPhase.next) { s.fullNameAsName('.') } + + private def pickledNameAsString(s: Symbol): String = + enteringPhase(currentRun.picklerPhase.next) { s.fullName } + + protected def isTopLevelModule(sym: Symbol): Boolean = + enteringPhase(currentRun.picklerPhase.next) { + sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass + } + + protected def flatclassName(s: Symbol, sep: Char, dollarRequired: Boolean): String = + flatname(s, sep) + (if (dollarRequired) "$" else "") +} diff --git a/dummy-bridge/src/main/scala/xsbt/Command.scala b/dummy-bridge/src/main/scala/xsbt/Command.scala new file mode 100644 index 0000000..a4049c5 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/Command.scala @@ -0,0 +1,43 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import scala.tools.nsc.{ CompilerCommand, Settings } + +object Command { + + /** + * Construct a CompilerCommand using reflection, to be compatible with Scalac before and after + * r21274 + */ + def apply(arguments: List[String], settings: Settings): CompilerCommand = { + def constr(params: Class[_]*) = classOf[CompilerCommand].getConstructor(params: _*) + try { + constr(classOf[List[_]], classOf[Settings]).newInstance(arguments, settings) + } catch { + case _: NoSuchMethodException => + constr(classOf[List[_]], classOf[Settings], classOf[(_) => _], classOf[Boolean]) + .newInstance( + arguments, + settings, + (s: String) => throw new RuntimeException(s), + false.asInstanceOf[AnyRef] + ) + } + } + + def getWarnFatal(settings: Settings): Boolean = + settings.fatalWarnings.value + + def getNoWarn(settings: Settings): Boolean = + settings.nowarn.value +} diff --git a/dummy-bridge/src/main/scala/xsbt/CompilerInterface.scala b/dummy-bridge/src/main/scala/xsbt/CompilerInterface.scala new file mode 100644 index 0000000..68e9c84 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/CompilerInterface.scala @@ -0,0 +1,202 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import xsbti.{ AnalysisCallback, CompilerInterface1, Logger, Problem, Reporter, VirtualFile } +import xsbti.compile._ +import scala.tools.nsc.Settings +import scala.collection.mutable +import scala.reflect.io.AbstractFile +import Log.debug +import java.io.File + +final class CompilerInterface extends CompilerInterface1 { + def newCompiler( + options: Array[String], + output: Output, + initialLog: Logger, + initialDelegate: Reporter + ): CachedCompiler = + new CachedCompiler0(options, output, new WeakLog(initialLog, initialDelegate)) + + def run( + sources: Array[VirtualFile], + changes: DependencyChanges, + callback: AnalysisCallback, + log: Logger, + delegate: Reporter, + progress: CompileProgress, + cached: CachedCompiler + ): Unit = + cached.run(sources, changes, callback, log, delegate, progress) +} + +class InterfaceCompileFailed( + val arguments: Array[String], + val problems: Array[Problem], + override val toString: String +) extends xsbti.CompileFailed + +class InterfaceCompileCancelled(val arguments: Array[String], override val toString: String) + extends xsbti.CompileCancelled + +private final class WeakLog(private[this] var log: Logger, private[this] var delegate: Reporter) { + def apply(message: String): Unit = { + assert(log ne null, "Stale reference to logger") + log.error(Message(message)) + } + def logger: Logger = log + def reporter: Reporter = delegate + def clear(): Unit = { + log = null + delegate = null + } +} + +private final class CachedCompiler0(args: Array[String], output: Output, initialLog: WeakLog) + extends CachedCompiler + with CachedCompilerCompat + with java.io.Closeable { + + ///////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////// INITIALIZATION CODE //////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + + val settings = new Settings(s => initialLog(s)) + output match { + case multi: MultipleOutput => + for (out <- multi.getOutputGroups) + settings.outputDirs + .add( + out.getSourceDirectory.toAbsolutePath.toString, + out.getOutputDirectory.toAbsolutePath.toString + ) + case single: SingleOutput => + val outputFilepath = single.getOutputDirectory.toAbsolutePath + settings.outputDirs.setSingleOutput(outputFilepath.toString) + } + + val command = Command(args.toList, settings) + private[this] val dreporter = DelegatingReporter(settings, initialLog.reporter) + try { + if (!noErrors(dreporter)) { + dreporter.printSummary() + handleErrors(dreporter, initialLog.logger) + } + } finally initialLog.clear() + + /** Instance of the underlying Zinc compiler. */ + val compiler: ZincCompiler = newCompiler(command.settings, dreporter, output) + + ///////////////////////////////////////////////////////////////////////////////////////////////// + + def close(): Unit = { + compiler match { + case c: java.io.Closeable => c.close() + case _ => + } + } + + def noErrors(dreporter: DelegatingReporter) = !dreporter.hasErrors && command.ok + + def commandArguments(sources: Array[File]): Array[String] = + (command.settings.recreateArgs ++ sources.map(_.getAbsolutePath)).toArray[String] + + import scala.tools.nsc.Properties.versionString + def infoOnCachedCompiler(compilerId: String): String = + s"[zinc] Running cached compiler $compilerId for Scala compiler $versionString" + + def run( + sources: Array[VirtualFile], + changes: DependencyChanges, + callback: AnalysisCallback, + log: Logger, + delegate: Reporter, + progress: CompileProgress + ): Unit = synchronized { + debug(log, infoOnCachedCompiler(hashCode().toLong.toHexString)) + val dreporter = DelegatingReporter(settings, delegate) + try { + run(sources.toList, changes, callback, log, dreporter, progress) + } finally { + dreporter.dropDelegate() + } + } + + private def prettyPrintCompilationArguments(args: Array[String]) = + args.mkString("[zinc] The Scala compiler is invoked with:\n\t", "\n\t", "") + private val StopInfoError = "Compiler option supplied that disabled Zinc compilation." + private[this] def run( + sources: List[VirtualFile], + changes: DependencyChanges, + callback: AnalysisCallback, + log: Logger, + underlyingReporter: DelegatingReporter, + compileProgress: CompileProgress + ): Unit = { + + if (command.shouldStopWithInfo) { + underlyingReporter.info(null, command.getInfoMessage(compiler), true) + throw new InterfaceCompileFailed(args, Array(), StopInfoError) + } + + if (noErrors(underlyingReporter)) { + debug(log, prettyPrintCompilationArguments(args)) + compiler.set(callback, underlyingReporter) + val run = new compiler.ZincRun(compileProgress) + + val wrappedFiles = sources.map(new VirtualFileWrap(_)) + val sortedSourceFiles: List[AbstractFile] = + wrappedFiles.sortWith(_.underlying.id < _.underlying.id) + run.compileFiles(sortedSourceFiles) + processUnreportedWarnings(run) + underlyingReporter.problems.foreach(p => + callback.problem(p.category, p.position, p.message, p.severity, true) + ) + } + + underlyingReporter.printSummary() + if (!noErrors(underlyingReporter)) + handleErrors(underlyingReporter, log) + + // the case where we cancelled compilation _after_ some compilation errors got reported + // will be handled by line above so errors still will be reported properly just potentially not + // all of them (because we cancelled the compilation) + if (underlyingReporter.cancelled) + handleCompilationCancellation(underlyingReporter, log) + } + + def handleErrors(dreporter: DelegatingReporter, log: Logger): Nothing = { + debug(log, "Compilation failed (CompilerInterface)") + throw new InterfaceCompileFailed(args, dreporter.problems, "Compilation failed") + } + + def handleCompilationCancellation(dreporter: DelegatingReporter, log: Logger): Nothing = { + assert(dreporter.cancelled, "We should get here only if when compilation got cancelled") + debug(log, "Compilation cancelled (CompilerInterface)") + throw new InterfaceCompileCancelled(args, "Compilation has been cancelled") + } + + def processUnreportedWarnings(run: compiler.Run): Unit = { + // allConditionalWarnings and the ConditionalWarning class are only in 2.10+ + final class CondWarnCompat( + val what: String, + val warnings: mutable.ListBuffer[(compiler.Position, String)] + ) + implicit def compat(run: AnyRef): Compat = new Compat + final class Compat { def allConditionalWarnings = List[CondWarnCompat]() } + + val warnings = run.allConditionalWarnings + if (warnings.nonEmpty) + compiler.logUnreportedWarnings(warnings.map(cw => ("" /*cw.what*/, cw.warnings.toList))) + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/DelegatingReporter.scala b/dummy-bridge/src/main/scala/xsbt/DelegatingReporter.scala new file mode 100644 index 0000000..02f4ceb --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/DelegatingReporter.scala @@ -0,0 +1,209 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.io.File +import java.util.Optional + +import scala.reflect.internal.util.{ FakePos, NoPosition, Position } +// Left for compatibility +import Compat._ + +private object DelegatingReporter { + def apply(settings: scala.tools.nsc.Settings, delegate: xsbti.Reporter): DelegatingReporter = + new DelegatingReporter(Command.getWarnFatal(settings), Command.getNoWarn(settings), delegate) + + class PositionImpl( + sourcePath0: Option[String], + sourceFile0: Option[File], + line0: Option[Int], + lineContent0: String, + offset0: Option[Int], + pointer0: Option[Int], + pointerSpace0: Option[String], + startOffset0: Option[Int], + endOffset0: Option[Int], + startLine0: Option[Int], + startColumn0: Option[Int], + endLine0: Option[Int], + endColumn0: Option[Int] + ) extends xsbti.Position { + val line = o2oi(line0) + val lineContent = lineContent0 + val offset = o2oi(offset0) + val sourcePath = o2jo(sourcePath0) + val sourceFile = o2jo(sourceFile0) + val pointer = o2oi(pointer0) + val pointerSpace = o2jo(pointerSpace0) + override val startOffset = o2oi(startOffset0) + override val endOffset = o2oi(endOffset0) + override val startLine = o2oi(startLine0) + override val startColumn = o2oi(startColumn0) + override val endLine = o2oi(endLine0) + override val endColumn = o2oi(endColumn0) + override def toString = + (sourcePath0, line0) match { + case (Some(s), Some(l)) => s + ":" + l + case (Some(s), _) => s + ":" + case _ => "" + } + } + + object PositionImpl { + def empty: PositionImpl = + new PositionImpl(None, None, None, "", None, None, None, None, None, None, None, None, None) + } + + import java.lang.{ Integer => I } + private[xsbt] def o2oi(opt: Option[Int]): Optional[I] = { + opt match { + case Some(s) => Optional.ofNullable[I](s: I) + case None => Optional.empty[I] + } + } + + private[xsbt] def o2jo[A](o: Option[A]): Optional[A] = { + o match { + case Some(v) => Optional.ofNullable(v) + case None => Optional.empty[A]() + } + } + + private[xsbt] def convert(dirtyPos: Position): xsbti.Position = { + def cleanPos(pos: Position) = { + Option(pos) match { + case None | Some(NoPosition) => None + case Some(_: FakePos) => None + case _ => Option(pos.finalPosition) + } + } + + def makePosition(pos: Position): xsbti.Position = { + val src = pos.source + val sourcePath = src.file.path + val sourceFile = new File(src.file.path) + val line = pos.line + val lineContent = pos.lineContent.stripLineEnd + val offset = pos.point + + // Same logic as Position#line + def lineOf(offset: Int) = src.offsetToLine(offset) + 1 + def columnOf(offset: Int) = offset - src.lineToOffset(src.offsetToLine(offset)) + + val pointer = columnOf(offset) + val pointerSpace = lineContent.toList.take(pointer).map { + case '\t' => '\t' + case _ => ' ' + } + + val startOffset = if (pos.isRange) Some(pos.start) else None + val endOffset = if (pos.isRange) Some(pos.end) else None + val startLine = if (pos.isRange) Some(lineOf(pos.start)) else None + val startColumn = if (pos.isRange) Some(columnOf(pos.start)) else None + val endLine = + if (pos.isRange) + try { + Some(lineOf(pos.end)) + } catch { + // work around for https://github.com/scala/bug/issues/11865 by falling back to start pos + case _: ArrayIndexOutOfBoundsException => + startLine + } + else None + val endColumn = + if (pos.isRange) + try { + Some(columnOf(pos.end)) + } catch { + // work around for https://github.com/scala/bug/issues/11865 by falling back to start pos + case _: ArrayIndexOutOfBoundsException => + startColumn + } + else None + + new PositionImpl( + Option(sourcePath), + Option(sourceFile), + Option(line), + lineContent, + Option(offset), + Option(pointer), + Option(pointerSpace.mkString), + startOffset, + endOffset, + startLine, + startColumn, + endLine, + endColumn + ) + } + + cleanPos(dirtyPos) match { + case None => PositionImpl.empty + case Some(cleanPos) => makePosition(cleanPos) + } + } +} + +// Copyright 2002-2009 LAMP/EPFL +// Original author: Martin Odersky +// Based on scala.tools.nsc.reporters.{AbstractReporter, ConsoleReporter} +private final class DelegatingReporter( + warnFatal: Boolean, + noWarn: Boolean, + private[this] var delegate: xsbti.Reporter +) extends scala.tools.nsc.reporters.Reporter { + def dropDelegate(): Unit = { delegate = null } + def error(msg: String): Unit = error(FakePos("scalac"), msg) + def printSummary(): Unit = delegate.printSummary() + + def problems = delegate.problems + override def hasErrors = delegate.hasErrors + override def hasWarnings = delegate.hasWarnings + override def comment(pos: Position, msg: String): Unit = + delegate.comment(DelegatingReporter.convert(pos), msg) + override def reset(): Unit = { + super.reset() + delegate.reset() + } + + protected def info0(pos: Position, msg: String, rawSeverity: Severity, force: Boolean): Unit = { + val skip = rawSeverity == WARNING && noWarn + if (!skip) { + val severity = if (warnFatal && rawSeverity == WARNING) ERROR else rawSeverity + delegate.log(new CompileProblem(DelegatingReporter.convert(pos), msg, convert(severity))) + } + } + + import xsbti.Severity.{ Info, Warn, Error } + private[this] def convert(sev: Severity): xsbti.Severity = { + sev match { + case INFO => Info + case WARNING => Warn + case ERROR => Error + } + } + + // Define our own problem because the bridge should not depend on sbt util-logging. + import xsbti.{ Problem => XProblem, Position => XPosition, Severity => XSeverity } + private final class CompileProblem( + pos: XPosition, + msg: String, + sev: XSeverity + ) extends XProblem { + override val category = "" + override val position = pos + override val message = msg + override val severity = sev + override def toString = s"[$severity] $pos: $message" + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/Dependency.scala b/dummy-bridge/src/main/scala/xsbt/Dependency.scala new file mode 100644 index 0000000..0707e2d --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/Dependency.scala @@ -0,0 +1,458 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.nio.file.{ Path, Paths } +import xsbti.VirtualFile +import xsbti.api.DependencyContext +import DependencyContext._ + +import scala.tools.nsc.io.{ PlainFile, ZipArchive } +import scala.tools.nsc.Phase + +import java.util.{ HashSet => JavaSet } +import java.util.{ HashMap => JavaMap } + +object Dependency { + def name = "xsbt-dependency" +} + +/** + * Extracts dependency information from each compilation unit. + * + * This phase detects all the dependencies both at the term and type level. + * + * When dependency symbol is processed, it is mapped back to either source file where + * it's defined in (if it's available in current compilation run) or classpath entry + * where it originates from. The Symbol -> Classfile mapping is implemented by + * LocateClassFile that we inherit from. + */ +final class Dependency(val global: CallbackGlobal) extends LocateClassFile with GlobalHelpers { + import global._ + + def newPhase(prev: Phase): Phase = new DependencyPhase(prev) + private class DependencyPhase(prev: Phase) extends GlobalPhase(prev) { + override def description = "Extracts dependency information" + def name = Dependency.name + + override def run(): Unit = { + val start = System.currentTimeMillis + super.run() + callback.dependencyPhaseCompleted() + val stop = System.currentTimeMillis + debuglog("Dependency phase took : " + ((stop - start) / 1000.0) + " s") + } + + def apply(unit: CompilationUnit): Unit = { + if (!unit.isJava) { + // Process dependencies if name hashing is enabled, fail otherwise + val dependencyProcessor = new DependencyProcessor(unit) + val dependencyTraverser = new DependencyTraverser(dependencyProcessor) + // Traverse symbols in compilation unit and register all dependencies + dependencyTraverser.traverse(unit.body) + } + } + } + + private class DependencyProcessor(unit: CompilationUnit) { + private def firstClassOrModuleClass(tree: Tree): Option[Symbol] = { + tree foreach { + case classOrModule @ ((_: ClassDef) | (_: ModuleDef)) => + val sym = classOrModule.symbol + return Some(if (sym.isModule) sym.moduleClass else sym) + case _ => () + } + None + } + + private val sourceFile: VirtualFile = unit.source.file match { + case v: VirtualFileWrap => v.underlying + } + private val responsibleOfImports = firstClassOrModuleClass(unit.body) + private var orphanImportsReported = false + + /* + * Registers top level import dependencies as coming from a first top level + * class/trait/object declared in the compilation unit. Otherwise, issue warning. + */ + def processTopLevelImportDependency(dep: Symbol): Unit = { + if (!orphanImportsReported) { + responsibleOfImports match { + case Some(classOrModuleDef) => + memberRef(ClassDependency(classOrModuleDef, dep)) + case None => + reporter.warning(unit.position(0), Feedback.OrphanTopLevelImports) + orphanImportsReported = true + } + } + () + } + + // Define processor reusing `processDependency` definition + val memberRef = processDependency(DependencyByMemberRef, false) _ + val inheritance = processDependency(DependencyByInheritance, true) _ + val localInheritance = processDependency(LocalDependencyByInheritance, true) _ + + @deprecated("Use processDependency that takes allowLocal.", "1.1.0") + def processDependency(context: DependencyContext)(dep: ClassDependency): Unit = + processDependency(context, true)(dep) + + /* + * Handles dependency on given symbol by trying to figure out if represents a term + * that is coming from either source code (not necessarily compiled in this compilation + * run) or from class file and calls respective callback method. + */ + def processDependency(context: DependencyContext, allowLocal: Boolean)( + dep: ClassDependency + ): Unit = { + val fromClassName = classNameAsString(dep.from) + + def binaryDependency(file: Path, binaryClassName: String) = { + callback.binaryDependency(file, binaryClassName, fromClassName, sourceFile, context) + } + import scala.tools.nsc.io.AbstractFile + def processExternalDependency(binaryClassName: String, at: AbstractFile): Unit = { + at match { + case zipEntry: ZipArchive#Entry => + // The dependency comes from a JAR + for { + zip <- zipEntry.underlyingSource + jarFile <- Option(zip.file) + if !jarFile.isDirectory // workaround for JDK9 and Scala 2.10/2.11, see https://github.com/sbt/sbt/pull/3701 + } binaryDependency(jarFile.toPath, binaryClassName) + case pf: xsbt.Compat.PlainNioFile => + // The dependency comes from a class file + binaryDependency(Paths.get(pf.path), binaryClassName) + case pf: PlainFile => + // The dependency comes from a class file + binaryDependency(pf.file.toPath, binaryClassName) + case _ => + // On Scala 2.10 you get Internal error: comes from unknown origin null + // if you uncomment the following: + // reporter.error( + // NoPosition, + // s"Internal error: ${binaryClassName} comes from unknown origin ${at} (${at.getClass})" + // ) + } + } + + val targetSymbol = dep.to + val onSource = targetSymbol.sourceFile + if (onSource == null) { + // Ignore packages right away as they don't map to a class file/jar + if (targetSymbol.hasFlag(scala.tools.nsc.symtab.Flags.PACKAGE)) () + // Ignore `Any` which by default has no `associatedFile` + else if (targetSymbol == definitions.AnyClass) () + else { + classFile(targetSymbol) match { + case Some((at, binaryClassName)) => + // Associated file is set, so we know which classpath entry it came from + processExternalDependency(binaryClassName, at) + case None => + /* If there is no associated file, it's likely the compiler didn't set it correctly. + * This happens very rarely, see https://github.com/sbt/zinc/issues/559 as an example, + * but when it does we must ensure the incremental compiler tries its best no to lose + * any dependency. Therefore, we do a last-time effort to get the origin of the symbol + * by inspecting the classpath manually. + */ + val fqn = fullName(targetSymbol, '.', targetSymbol.moduleSuffix, false) + global.findAssociatedFile(fqn) match { + case Some((at, true)) => + processExternalDependency(fqn, at) + case Some((_, false)) | None => + // Study the possibility of warning or adding this to the zinc profiler so that + // if users reports errors, the lost dependencies are present in the zinc profiler + debuglog(Feedback.noOriginFileForExternalSymbol(targetSymbol)) + } + } + } + } else { + val onSourceFile: VirtualFile = onSource match { + case v: VirtualFileWrap => v.underlying + } + if (onSourceFile != sourceFile || allowLocal) { + // We cannot ignore dependencies coming from the same source file because + // the dependency info needs to propagate. See source-dependencies/trait-trait-211. + val onClassName = classNameAsString(dep.to) + callback.classDependency(onClassName, fromClassName, context) + } else () + } + } + } + + private case class ClassDependency(from: Symbol, to: Symbol) + + private final class DependencyTraverser(processor: DependencyProcessor) extends Traverser { + // are we traversing an Import node at the moment? + private var inImportNode = false + + // Define caches for dependencies that have already been processed + private val _memberRefCache = new JavaSet[ClassDependency]() + private val _inheritanceCache = new JavaSet[ClassDependency]() + private val _localInheritanceCache = new JavaSet[ClassDependency]() + private val _topLevelImportCache = new JavaSet[Symbol]() + + private var _currentDependencySource: Symbol = _ + private var _currentNonLocalClass: Symbol = _ + private var _isLocalSource: Boolean = false + + @inline def resolveNonLocalClass(from: Symbol): (Symbol, Boolean) = { + val fromClass = enclOrModuleClass(from) + if (fromClass == NoSymbol || fromClass.hasPackageFlag) (fromClass, false) + else { + val nonLocal = localToNonLocalClass.resolveNonLocal(fromClass) + (nonLocal, fromClass != nonLocal) + } + } + + /** + * Resolves dependency source (that is, the closest non-local enclosing + * class from a given `currentOwner` set by the `Traverser`). + * + * This method modifies the value of `_currentDependencySource`, + * `_currentNonLocalClass` and `_isLocalSource` and it is not modeled + * as a case class for performance reasons. + * + * The used caching strategy works as follows: + * 1. Return previous non-local class if owners are referentially equal. + * 2. Otherwise, check if they resolve to the same non-local class. + * 1. If they do, overwrite `_isLocalSource` and return + * `_currentNonLocalClass`. + * 2. Otherwise, overwrite all the pertinent fields to be consistent. + */ + private def resolveDependencySource: Symbol = { + if (_currentDependencySource == null) { + // First time we access it, initialize it + _currentDependencySource = currentOwner + val (nonLocalClass, isLocal) = resolveNonLocalClass(currentOwner) + _currentNonLocalClass = nonLocalClass + _isLocalSource = isLocal + nonLocalClass + } else { + // Check if cached is equally referential + if (_currentDependencySource == currentOwner) _currentNonLocalClass + else { + // Check they resolve to the same nonLocalClass. If so, spare writes. + val (nonLocalClass, isLocal) = resolveNonLocalClass(currentOwner) + if (_currentNonLocalClass == nonLocalClass) { + // Resolution can be the same, but the origin affects `isLocal` + _isLocalSource = isLocal + _currentNonLocalClass + } else { + _currentDependencySource = _currentDependencySource + _currentNonLocalClass = nonLocalClass + _isLocalSource = isLocal + _currentNonLocalClass + } + } + } + } + + /** + * Process a given ClassDependency and add it to the cache. + * + * This class dependency can be of three different types: + * 1. Member reference; + * 2. Local inheritance; or, + * 3. Inheritance. + */ + private def addClassDependency( + cache: JavaSet[ClassDependency], + process: ClassDependency => Unit, + fromClass: Symbol, + dep: Symbol + ): Unit = { + assert(fromClass.isClass, Feedback.expectedClassSymbol(fromClass)) + val depClass = enclOrModuleClass(dep) + val dependency = ClassDependency(fromClass, depClass) + if (!cache.contains(dependency) && + !depClass.isRefinementClass) { + process(dependency) + cache.add(dependency) + () + } + } + + def addTopLevelImportDependency(dep: global.Symbol): Unit = { + val depClass = enclOrModuleClass(dep) + if (!_topLevelImportCache.contains(depClass) && !dep.hasPackageFlag) { + processor.processTopLevelImportDependency(depClass) + _topLevelImportCache.add(depClass) + () + } + } + + private def addTreeDependency(tree: Tree): Unit = { + addDependency(tree.symbol) + val tpe = tree.tpe + if (!ignoredType(tpe)) { + addTypeDependencies(tpe) + } + () + } + + private def addDependency(dep: Symbol): Unit = { + val fromClass = resolveDependencySource + if (ignoredSymbol(fromClass) || fromClass.hasPackageFlag) { + if (inImportNode) addTopLevelImportDependency(dep) + else devWarning(Feedback.missingEnclosingClass(dep, currentOwner)) + } else { + addClassDependency(_memberRefCache, processor.memberRef, fromClass, dep) + } + } + + /** Define a type traverser to keep track of the type dependencies. */ + object TypeDependencyTraverser extends TypeDependencyTraverser { + type Handler = Symbol => Unit + // Type dependencies are always added to member references + val memberRefHandler = processor.memberRef + def createHandler(fromClass: Symbol): Handler = { (dep: Symbol) => + if (ignoredSymbol(fromClass) || fromClass.hasPackageFlag) { + if (inImportNode) addTopLevelImportDependency(dep) + else devWarning(Feedback.missingEnclosingClass(dep, currentOwner)) + } else { + addClassDependency(_memberRefCache, memberRefHandler, fromClass, dep) + } + } + + val cache = new JavaMap[Symbol, (Handler, JavaSet[Type])]() + private var handler: Handler = _ + private var visitedOwner: Symbol = _ + def setOwner(owner: Symbol) = { + if (visitedOwner != owner) { + cache.get(owner) match { + case null => + val newVisited = new JavaSet[Type]() + handler = createHandler(owner) + cache.put(owner, handler -> newVisited) + visited = newVisited + visitedOwner = owner + case (h, ts) => + visited = ts + handler = h + } + } + } + + override def addDependency(symbol: global.Symbol) = handler(symbol) + } + + def addTypeDependencies(tpe: Type): Unit = { + val fromClass = resolveDependencySource + TypeDependencyTraverser.setOwner(fromClass) + TypeDependencyTraverser.traverse(tpe) + } + + private def addInheritanceDependency(dep: Symbol): Unit = { + val fromClass = resolveDependencySource + if (_isLocalSource) { + addClassDependency(_localInheritanceCache, processor.localInheritance, fromClass, dep) + } else { + addClassDependency(_inheritanceCache, processor.inheritance, fromClass, dep) + } + } + + /* + * Some macros appear to contain themselves as original tree. + * We must check that we don't inspect the same tree over and over. + * See https://issues.scala-lang.org/browse/SI-8486 + * https://github.com/sbt/sbt/issues/1237 + * https://github.com/sbt/sbt/issues/1544 + */ + private val inspectedOriginalTrees = new JavaSet[Tree]() + + override def traverse(tree: Tree): Unit = tree match { + case Import(expr, selectors) => + inImportNode = true + traverse(expr) + selectors.foreach { + case ImportSelector(nme.WILDCARD, _, null, _) => + // in case of wildcard import we do not rely on any particular name being defined + // on `expr`; all symbols that are being used will get caught through selections + case ImportSelector(name: Name, _, _, _) => + def lookupImported(name: Name) = expr.symbol.info.member(name) + // importing a name means importing both a term and a type (if they exist) + val termSymbol = lookupImported(name.toTermName) + if (termSymbol.info != NoType) addDependency(termSymbol) + addDependency(lookupImported(name.toTypeName)) + } + inImportNode = false + /* + * Idents are used in number of situations: + * - to refer to local variable + * - to refer to a top-level package (other packages are nested selections) + * - to refer to a term defined in the same package as an enclosing class; + * this looks fishy, see this thread: + * https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion + */ + case id: Ident => addTreeDependency(id) + case sel @ Select(qual, _) => + traverse(qual); addTreeDependency(sel) + case sel @ SelectFromTypeTree(qual, _) => + traverse(qual); addTreeDependency(sel) + + case Template(parents, self, body) => + // use typeSymbol to dealias type aliases -- we want to track the dependency on the real class in the alias's RHS + def flattenTypeToSymbols(tp: Type): List[Symbol] = + if (tp eq null) Nil + else + tp match { + // rt.typeSymbol is redundant if we list out all parents, TODO: what about rt.decls? + case rt: RefinedType => rt.parents.flatMap(flattenTypeToSymbols) + case _ => List(tp.typeSymbol) + } + + val inheritanceTypes = parents.map(_.tpe).toSet + val inheritanceSymbols = inheritanceTypes.flatMap(flattenTypeToSymbols) + + debuglog( + "Parent types for " + tree.symbol + " (self: " + self.tpt.tpe + "): " + inheritanceTypes + " with symbols " + inheritanceSymbols + .map(_.fullName) + ) + + inheritanceSymbols.foreach { symbol => + addInheritanceDependency(symbol) + addDependency(symbol) + } + + inheritanceTypes.foreach(addTypeDependencies) + addTypeDependencies(self.tpt.tpe) + + traverseTrees(body) + + case Literal(value) if value.tag == ClazzTag => + addTypeDependencies(value.typeValue) + + /* Original type trees have to be traversed because typer is very + * aggressive when expanding explicit user-defined types. For instance, + * `Foo#B` will be expanded to `C` and the dependency on `Foo` will be + * lost. This makes sure that we traverse all the original prefixes. */ + case typeTree: TypeTree if !ignoredType(typeTree.tpe) => + val original = typeTree.original + if (original != null && !inspectedOriginalTrees.contains(original)) { + traverse(original) + inspectedOriginalTrees.add(original) + } + addTypeDependencies(typeTree.tpe) + case m @ MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => + traverse(original) + super.traverse(m) + case _: ClassDef | _: ModuleDef if !ignoredSymbol(tree.symbol) => + // make sure we cache lookups for all classes declared in the compilation unit; the recorded information + // will be used in Analyzer phase + val sym = if (tree.symbol.isModule) tree.symbol.moduleClass else tree.symbol + localToNonLocalClass.resolveNonLocal(sym) + super.traverse(tree) + case other => super.traverse(other) + } + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/ExtractAPI.scala b/dummy-bridge/src/main/scala/xsbt/ExtractAPI.scala new file mode 100644 index 0000000..62ddb21 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/ExtractAPI.scala @@ -0,0 +1,812 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.util.{ Arrays, Comparator } +import scala.tools.nsc.symtab.Flags +import xsbti.VirtualFile +import xsbti.api._ + +import scala.annotation.tailrec +import scala.tools.nsc.Global +import scala.PartialFunction.cond + +/** + * Extracts full (including private members) API representation out of Symbols and Types. + * + * API for each class is extracted separately. Inner classes are represented as an empty (without members) + * member of the outer class and as a separate class with full API representation. For example: + * + * class A { + * class B { + * def foo: Int = 123 + * } + * } + * + * Is represented as: + * + * // className = A + * class A { + * class B + * } + * // className = A.B + * class A.B { + * def foo: Int + * } + * + * Each compilation unit should be processed by a fresh instance of this class. + * + * NOTE: This class extract *full* API representation. In most of other places in the incremental compiler, + * only non-private (accessible from other compilation units) members are relevant. Other parts of the + * incremental compiler filter out private definitions before processing API structures. Check SameAPI for + * an example. + * + */ +class ExtractAPI[GlobalType <: Global]( + val global: GlobalType, + // Tracks the source file associated with the CompilationUnit currently being processed by the API phase. + // This is used when recording inheritance dependencies. + sourceFile: VirtualFile +) extends Compat + with ClassName + with GlobalHelpers { + + import global._ + + private def error(msg: String) = throw new RuntimeException(msg) + + // this cache reduces duplicate work both here and when persisting + // caches on other structures had minimal effect on time and cache size + // (tried: Definition, Modifier, Path, Id, String) + private[this] val typeCache = perRunCaches.newMap[(Symbol, Type), xsbti.api.Type]() + // these caches are necessary for correctness + private[this] val structureCache = perRunCaches.newMap[Symbol, xsbti.api.Structure]() + private[this] val classLikeCache = + perRunCaches.newMap[(Symbol, Symbol), xsbti.api.ClassLikeDef]() + private[this] val pending = perRunCaches.newSet[xsbti.api.Lazy[_]]() + + private[this] val emptyStringArray = Array.empty[String] + + private[this] val allNonLocalClassSymbols = perRunCaches.newSet[Symbol]() + private[this] val allNonLocalClassesInSrc = perRunCaches.newSet[xsbti.api.ClassLike]() + private[this] val _mainClasses = perRunCaches.newSet[String]() + + /** + * Implements a work-around for https://github.com/sbt/sbt/issues/823 + * + * The strategy is to rename all type variables bound by existential type to stable + * names by assigning to each type variable a De Bruijn-like index. As a result, each + * type variable gets name of this shape: + * + * "existential_${nestingLevel}_${i}" + * + * where `nestingLevel` indicates nesting level of existential types and `i` variable + * indicates position of type variable in given existential type. + * + * For example, let's assume we have the following classes declared: + * + * class A[T]; class B[T,U] + * + * and we have type A[_] that is expanded by Scala compiler into + * + * A[_$1] forSome { type _$1 } + * + * After applying our renaming strategy we get + * + * A[existential_0_0] forSome { type existential_0_0 } + * + * Let's consider a bit more complicated example which shows how our strategy deals with + * nested existential types: + * + * A[_ <: B[_, _]] + * + * which gets expanded into: + * + * A[_$1] forSome { + * type _$1 <: B[_$2, _$3] forSome { type _$2; type _$3 } + * } + * + * After applying our renaming strategy we get + * + * A[existential_0_0] forSome { + * type existential_0_0 <: B[existential_1_0, existential_1_1] forSome { + * type existential_1_0; type existential_1_1 + * } + * } + * + * Note how the first index (nesting level) is bumped for both existential types. + * + * This way, all names of existential type variables depend only on the structure of + * existential types and are kept stable. + * + * Both examples presented above used placeholder syntax for existential types but our + * strategy is applied uniformly to all existential types no matter if they are written + * using placeholder syntax or explicitly. + */ + private[this] object existentialRenamings { + private var nestingLevel: Int = 0 + import scala.collection.mutable.Map + private val renameTo: Map[Symbol, String] = Map.empty + + def leaveExistentialTypeVariables(typeVariables: Seq[Symbol]): Unit = { + nestingLevel -= 1 + assert(nestingLevel >= 0) + typeVariables.foreach(renameTo.remove) + } + def enterExistentialTypeVariables(typeVariables: Seq[Symbol]): Unit = { + nestingLevel += 1 + typeVariables.zipWithIndex foreach { + case (tv, i) => + val newName = "existential_" + nestingLevel + "_" + i + renameTo(tv) = newName + } + } + def renaming(symbol: Symbol): Option[String] = renameTo.get(symbol) + } + + /** + * Construct a lazy instance from a by-name parameter that will null out references to once + * the value is forced and therefore references to thunk's classes will be garbage collected. + */ + private def lzy[S <: AnyRef](s: => S): xsbti.api.Lazy[S] = { + val lazyImpl = xsbti.api.SafeLazy.apply(Message(s)) + pending += lazyImpl + lazyImpl + } + + /** + * Force all lazy structures. This is necessary so that we see the symbols/types at this phase and + * so that we don't hold on to compiler objects and classes + */ + @tailrec final def forceStructures(): Unit = + if (pending.isEmpty) + structureCache.clear() + else { + val toProcess = pending.toList + pending.clear() + toProcess foreach { _.get() } + forceStructures() + } + + private def thisPath(sym: Symbol) = path(pathComponents(sym, Constants.thisPath :: Nil)) + private def path(components: List[PathComponent]) = + xsbti.api.Path.of(components.toArray[PathComponent]) + private def pathComponents(sym: Symbol, postfix: List[PathComponent]): List[PathComponent] = { + if (sym == NoSymbol || sym.isRoot || sym.isEmptyPackageClass || sym.isRootPackage) postfix + else pathComponents(sym.owner, xsbti.api.Id.of(simpleName(sym)) :: postfix) + } + private def types(in: Symbol, t: List[Type]): Array[xsbti.api.Type] = + t.toArray[Type].map(processType(in, _)) + private def projectionType(in: Symbol, pre: Type, sym: Symbol) = { + if (pre == NoPrefix) { + if (sym.isLocalClass || sym.isRoot || sym.isRootPackage) Constants.emptyType + else if (sym.isTypeParameterOrSkolem || sym.isExistentiallyBound) reference(sym) + else { + // this appears to come from an existential type in an inherited member- not sure why isExistential is false here + /*println("Warning: Unknown prefixless type: " + sym + " in " + sym.owner + " in " + sym.enclClass) + println("\tFlags: " + sym.flags + ", istype: " + sym.isType + ", absT: " + sym.isAbstractType + ", alias: " + sym.isAliasType + ", nonclass: " + isNonClassType(sym))*/ + reference(sym) + } + } else if (sym.isRoot || sym.isRootPackage) Constants.emptyType + else xsbti.api.Projection.of(processType(in, pre), simpleName(sym)) + } + private def reference(sym: Symbol): xsbti.api.ParameterRef = + xsbti.api.ParameterRef.of(tparamID(sym)) + + // The compiler only pickles static annotations, so only include these in the API. + // This way, the API is not sensitive to whether we compiled from source or loaded from classfile. + // (When looking at the sources we see all annotations, but when loading from classes we only see the pickled (static) ones.) + private def mkAnnotations(in: Symbol, as: List[AnnotationInfo]): Array[xsbti.api.Annotation] = { + if (in == NoSymbol) ExtractAPI.emptyAnnotationArray + else + staticAnnotations(as) match { + case Nil => ExtractAPI.emptyAnnotationArray + case staticAs => + staticAs.map { a => + xsbti.api.Annotation.of( + processType(in, a.atp), + if (a.assocs.isEmpty) + Array(xsbti.api.AnnotationArgument.of("", a.args.mkString("(", ",", ")"))) // what else to do with a Tree? + else + a.assocs + .map { + case (name, value) => + xsbti.api.AnnotationArgument.of(name.toString, value.toString) + } + .toArray[xsbti.api.AnnotationArgument] + ) + }.toArray + } + } + + // HOT method, hand optimized to reduce allocations and needless creation of Names with calls to getterIn/setterIn + // on non-fields. + private def annotations(in: Symbol, s: Symbol): Array[xsbti.api.Annotation] = { + val saved = phase + phase = currentRun.typerPhase + try { + val base = if (s.hasFlag(Flags.ACCESSOR)) s.accessed else NoSymbol + val b = if (base == NoSymbol) s else base + // annotations from bean methods are not handled because: + // a) they are recorded as normal source methods anyway + // b) there is no way to distinguish them from user-defined methods + if (b.hasGetter) { + val annotations = collection.mutable.LinkedHashSet[xsbti.api.Annotation]() + def add(sym: Symbol) = { + val anns = mkAnnotations(in, sym.annotations) + var i = 0 + while (i < anns.length) { + annotations += anns(i) + i += 1 + } + } + add(b) + add(b.getterIn(b.enclClass)) + add(b.setterIn(b.enclClass)) + annotations.toArray.distinct + } else { + if (b.annotations.isEmpty) ExtractAPI.emptyAnnotationArray + else mkAnnotations(in, b.annotations) + } + } finally { + phase = saved + } + } + + private def viewer(s: Symbol) = (if (s.isModule) s.moduleClass else s).thisType + + private def defDef(in: Symbol, s: Symbol): xsbti.api.Def = { + def build( + t: Type, + typeParams: Array[xsbti.api.TypeParameter], + valueParameters: List[xsbti.api.ParameterList] + ): xsbti.api.Def = { + def parameterList(syms: List[Symbol]): xsbti.api.ParameterList = { + val isImplicitList = cond(syms) { case head :: _ => isImplicit(head) } + xsbti.api.ParameterList.of(syms.map(parameterS).toArray, isImplicitList) + } + t match { + case PolyType(typeParams0, base) => + assert(typeParams.isEmpty) + assert(valueParameters.isEmpty) + build(base, typeParameters(in, typeParams0), Nil) + case MethodType(params, resultType) => + build(resultType, typeParams, parameterList(params) :: valueParameters) + case NullaryMethodType(resultType) => + build(resultType, typeParams, valueParameters) + case returnType => + val retType = processType(in, dropConst(returnType)) + xsbti.api.Def.of( + simpleName(s), + getAccess(s), + getModifiers(s), + annotations(in, s), + typeParams, + valueParameters.reverse.toArray, + retType + ) + } + } + def parameterS(s: Symbol): xsbti.api.MethodParameter = { + val tp: global.Type = s.info + makeParameter(simpleName(s), tp, tp.typeSymbol, s) + } + + // paramSym is only for 2.8 and is to determine if the parameter has a default + def makeParameter( + name: String, + tpe: Type, + ts: Symbol, + paramSym: Symbol + ): xsbti.api.MethodParameter = { + import xsbti.api.ParameterModifier._ + val (t, special) = + if (ts == definitions.RepeatedParamClass) // || s == definitions.JavaRepeatedParamClass) + (tpe.typeArgs.head, Repeated) + else if (ts == definitions.ByNameParamClass) + (tpe.typeArgs.head, ByName) + else + (tpe, Plain) + xsbti.api.MethodParameter.of(name, processType(in, t), hasDefault(paramSym), special) + } + val t = viewer(in).memberInfo(s) + build(t, Array(), Nil) + } + private def hasDefault(s: Symbol) = s != NoSymbol && s.hasFlag(Flags.DEFAULTPARAM) + private def fieldDef[T]( + in: Symbol, + s: Symbol, + keepConst: Boolean, + create: ( + String, + xsbti.api.Access, + xsbti.api.Modifiers, + Array[xsbti.api.Annotation], + xsbti.api.Type + ) => T + ): T = { + val t = dropNullary(viewer(in).memberType(s)) + val t2 = if (keepConst) t else dropConst(t) + create(simpleName(s), getAccess(s), getModifiers(s), annotations(in, s), processType(in, t2)) + } + private def dropConst(t: Type): Type = t match { + case ConstantType(constant) => constant.tpe + case _ => t + } + private def dropNullary(t: Type): Type = t match { + case NullaryMethodType(un) => un + case _ => t + } + + private def typeDef(in: Symbol, s: Symbol): xsbti.api.TypeMember = { + val (typeParams, tpe) = + viewer(in).memberInfo(s) match { + case PolyType(typeParams0, base) => (typeParameters(in, typeParams0), base) + case t => (Array[xsbti.api.TypeParameter](), t) + } + val name = simpleName(s) + val access = getAccess(s) + val modifiers = getModifiers(s) + val as = annotations(in, s) + + if (s.isAliasType) + xsbti.api.TypeAlias.of(name, access, modifiers, as, typeParams, processType(in, tpe)) + else if (s.isAbstractType) { + val bounds = tpe.bounds + xsbti.api.TypeDeclaration.of( + name, + access, + modifiers, + as, + typeParams, + processType(in, bounds.lo), + processType(in, bounds.hi) + ) + } else + error("Unknown type member" + s) + } + + private def structure(info: Type, s: Symbol): xsbti.api.Structure = + structureCache.getOrElseUpdate(s, mkStructure(info, s)) + private def structureWithInherited(info: Type, s: Symbol): xsbti.api.Structure = + structureCache.getOrElseUpdate(s, mkStructureWithInherited(info, s)) + + private def removeConstructors(ds: List[Symbol]): List[Symbol] = ds filter { !_.isConstructor } + + /** + * Create structure as-is, without embedding ancestors + * + * (for refinement types, and ClassInfoTypes encountered outside of a definition???). + */ + private def mkStructure(info: Type, s: Symbol): xsbti.api.Structure = { + // We're not interested in the full linearization, so we can just use `parents`, + // which side steps issues with baseType when f-bounded existential types and refined types mix + // (and we get cyclic types which cause a stack overflow in showAPI). + val parentTypes = info.parents + val decls = info.decls.toList + val declsNoModuleCtor = if (s.isModuleClass) removeConstructors(decls) else decls + mkStructure(s, parentTypes, declsNoModuleCtor, Nil) + } + + /** + * Track all ancestors and inherited members for a class's API. + * + * A class's hash does not include hashes for its parent classes -- only the symbolic names -- + * so we must ensure changes propagate somehow. + * + * TODO: can we include hashes for parent classes instead? This seems a bit messy. + */ + private def mkStructureWithInherited(info: Type, s: Symbol): xsbti.api.Structure = { + val ancestorTypes0 = linearizedAncestorTypes(info) + val ancestorTypes = + if (s.isDerivedValueClass) { + val underlying = s.derivedValueClassUnbox.tpe.finalResultType + // The underlying type of a value class should be part of the name hash + // of the value class (see the test `value-class-underlying`), this is accomplished + // by adding the underlying type to the list of parent types. + underlying :: ancestorTypes0 + } else + ancestorTypes0 + val decls = info.decls.toList + val declsNoModuleCtor = if (s.isModuleClass) removeConstructors(decls) else decls + val declSet = decls.toSet + val inherited = info.nonPrivateMembers.toList.filterNot(declSet) // private members are not inherited + mkStructure(s, ancestorTypes, declsNoModuleCtor, inherited) + } + + // Note that the ordering of classes in `baseClasses` is important. + // It would be easier to just say `baseTypeSeq.toList.tail`, + // but that does not take linearization into account. + def linearizedAncestorTypes(info: Type): List[Type] = info.baseClasses.tail.map(info.baseType) + + private def mkStructure( + s: Symbol, + bases: List[Type], + declared: List[Symbol], + inherited: List[Symbol] + ): xsbti.api.Structure = { + xsbti.api.Structure.of( + lzy(types(s, bases)), + lzy(processDefinitions(s, declared)), + lzy(processDefinitions(s, inherited)) + ) + } + private def processDefinitions(in: Symbol, defs: List[Symbol]): Array[xsbti.api.ClassDefinition] = + sort(defs.toArray).flatMap((d: Symbol) => definition(in, d)) + private[this] def sort(defs: Array[Symbol]): Array[Symbol] = { + Arrays.sort(defs, sortClasses) + defs + } + + private def definition(in: Symbol, sym: Symbol): Option[xsbti.api.ClassDefinition] = { + def mkVar = Some(fieldDef(in, sym, keepConst = false, xsbti.api.Var.of(_, _, _, _, _))) + def mkVal = Some(fieldDef(in, sym, keepConst = true, xsbti.api.Val.of(_, _, _, _, _))) + if (isClass(sym)) + if (ignoreClass(sym)) { + allNonLocalClassSymbols.+=(sym); None + } else Some(classLike(in, sym)) + else if (sym.isNonClassType) + Some(typeDef(in, sym)) + else if (sym.isVariable) + if (isSourceField(sym)) mkVar else None + else if (sym.isStable) + if (isSourceField(sym)) mkVal else None + else if (sym.isSourceMethod && !sym.isSetter) + if (sym.isGetter) mkVar else Some(defDef(in, sym)) + else + None + } + private def ignoreClass(sym: Symbol): Boolean = + sym.isLocalClass || sym.isAnonymousClass || sym.fullName.endsWith(tpnme.LOCAL_CHILD.toString) + + // This filters private[this] vals/vars that were not in the original source. + // The getter will be used for processing instead. + private def isSourceField(sym: Symbol): Boolean = { + val getter = sym.getterIn(sym.enclClass) + // the check `getter eq sym` is a precaution against infinite recursion + // `isParamAccessor` does not exist in all supported versions of Scala, so the flag check is done directly + (getter == NoSymbol && !sym.hasFlag(Flags.PARAMACCESSOR)) || (getter eq sym) + } + private def getModifiers(s: Symbol): xsbti.api.Modifiers = { + import Flags._ + val absOver = s.hasFlag(ABSOVERRIDE) + val abs = s.hasFlag(ABSTRACT) || s.hasFlag(DEFERRED) || absOver + val over = s.hasFlag(OVERRIDE) || absOver + new xsbti.api.Modifiers( + abs, + over, + s.isFinal, + s.hasFlag(SEALED), + isImplicit(s), + s.hasFlag(LAZY), + s.hasFlag(MACRO), + s.hasFlag(SUPERACCESSOR) + ) + } + + private def isImplicit(s: Symbol) = s.hasFlag(Flags.IMPLICIT) + private def getAccess(c: Symbol): xsbti.api.Access = { + if (c.isPublic) Constants.public + else if (c.isPrivateLocal) Constants.privateLocal + else if (c.isProtectedLocal) Constants.protectedLocal + else { + val within = c.privateWithin + val qualifier = + if (within == NoSymbol) Constants.unqualified + else xsbti.api.IdQualifier.of(within.fullName) + if (c.hasFlag(Flags.PROTECTED)) xsbti.api.Protected.of(qualifier) + else xsbti.api.Private.of(qualifier) + } + } + + /** + * Replace all types that directly refer to the `forbidden` symbol by `NoType`. + * (a specialized version of substThisAndSym) + */ + class SuppressSymbolRef(forbidden: Symbol) extends TypeMap { + def apply(tp: Type) = + if (tp.typeSymbolDirect == forbidden) NoType + else mapOver(tp) + } + + private def processType(in: Symbol, t: Type): xsbti.api.Type = + typeCache.getOrElseUpdate((in, t), makeType(in, t)) + private def makeType(in: Symbol, t: Type): xsbti.api.Type = { + + val dealiased = t match { + case TypeRef(_, sym, _) if sym.isAliasType => t.dealias + case _ => t + } + + dealiased match { + case NoPrefix => Constants.emptyType + case ThisType(sym) => xsbti.api.Singleton.of(thisPath(sym)) + case SingleType(pre, sym) => projectionType(in, pre, sym) + case ConstantType(constant) => + xsbti.api.Constant.of(processType(in, constant.tpe), constant.stringValue) + + /* explaining the special-casing of references to refinement classes (https://support.typesafe.com/tickets/1882) + * + * goal: a representation of type references to refinement classes that's stable across compilation runs + * (and thus insensitive to typing from source or unpickling from bytecode) + * + * problem: the current representation, which corresponds to the owner chain of the refinement: + * 1. is affected by pickling, so typing from source or using unpickled symbols give different results (because the unpickler "localizes" owners -- this could be fixed in the compiler) + * 2. can't distinguish multiple refinements in the same owner (this is a limitation of SBT's internal representation and cannot be fixed in the compiler) + * + * potential solutions: + * - simply drop the reference: won't work as collapsing all refinement types will cause recompilation to be skipped when a refinement is changed to another refinement + * - represent the symbol in the api: can't think of a stable way of referring to an anonymous symbol whose owner changes when pickled + * + expand the reference to the corresponding refinement type: doing that recursively may not terminate, but we can deal with that by approximating recursive references + * (all we care about is being sound for recompilation: recompile iff a dependency changes, and this will happen as long as we have one unrolling of the reference to the refinement) + */ + case TypeRef(pre, sym, Nil) if sym.isRefinementClass => + // Since we only care about detecting changes reliably, we unroll a reference to a refinement class once. + // Recursive references are simply replaced by NoType -- changes to the type will be seen in the first unrolling. + // The API need not be type correct, so this truncation is acceptable. Most of all, the API should be compact. + val unrolling = pre.memberInfo(sym) // this is a refinement type + + // in case there are recursive references, suppress them -- does this ever happen? + // we don't have a test case for this, so warn and hope we'll get a contribution for it :-) + val withoutRecursiveRefs = new SuppressSymbolRef(sym).mapOver(unrolling) + if (unrolling ne withoutRecursiveRefs) + reporter.warning( + sym.pos, + "sbt-api: approximated refinement ref" + t + " (== " + unrolling + ") to " + withoutRecursiveRefs + "\nThis is currently untested, please report the code you were compiling." + ) + + structure(withoutRecursiveRefs, sym) + case tr @ TypeRef(pre, sym, args) => + val base = projectionType(in, pre, sym) + if (args.isEmpty) + if (isRawType(tr)) + processType(in, rawToExistential(tr)) + else + base + else + xsbti.api.Parameterized.of(base, types(in, args)) + case SuperType(thistpe: Type, supertpe: Type) => + reporter.warning( + NoPosition, + "sbt-api: Super type (not implemented): this=" + thistpe + ", super=" + supertpe + ) + Constants.emptyType + case at: AnnotatedType => + at.annotations match { + case Nil => processType(in, at.underlying) + case annots => + xsbti.api.Annotated.of(processType(in, at.underlying), mkAnnotations(in, annots)) + } + case rt: CompoundType => structure(rt, rt.typeSymbol) + case t: ExistentialType => makeExistentialType(in, t) + case NoType => + Constants.emptyType // this can happen when there is an error that will be reported by a later phase + case PolyType(typeParams, resultType) => + xsbti.api.Polymorphic.of(processType(in, resultType), typeParameters(in, typeParams)) + case NullaryMethodType(_) => + reporter.warning( + NoPosition, + "sbt-api: Unexpected nullary method type " + in + " in " + in.owner + ) + Constants.emptyType + case _ => + reporter.warning(NoPosition, "sbt-api: Unhandled type " + t.getClass + " : " + t) + Constants.emptyType + } + } + private def makeExistentialType(in: Symbol, t: ExistentialType): xsbti.api.Existential = { + val ExistentialType(typeVariables, qualified) = t + existentialRenamings.enterExistentialTypeVariables(typeVariables) + try { + val typeVariablesConverted = typeParameters(in, typeVariables) + val qualifiedConverted = processType(in, qualified) + xsbti.api.Existential.of(qualifiedConverted, typeVariablesConverted) + } finally { + existentialRenamings.leaveExistentialTypeVariables(typeVariables) + } + } + private def typeParameters(in: Symbol, s: Symbol): Array[xsbti.api.TypeParameter] = + typeParameters(in, s.typeParams) + private def typeParameters(in: Symbol, s: List[Symbol]): Array[xsbti.api.TypeParameter] = + s.map(typeParameter(in, _)).toArray[xsbti.api.TypeParameter] + private def typeParameter(in: Symbol, s: Symbol): xsbti.api.TypeParameter = { + val varianceInt = s.variance + import xsbti.api.Variance._ + val annots = annotations(in, s) + val variance = + if (varianceInt < 0) Contravariant else if (varianceInt > 0) Covariant else Invariant + viewer(in).memberInfo(s) match { + case TypeBounds(low, high) => + xsbti.api.TypeParameter.of( + tparamID(s), + annots, + typeParameters(in, s), + variance, + processType(in, low), + processType(in, high) + ) + case PolyType(typeParams, base) => + xsbti.api.TypeParameter.of( + tparamID(s), + annots, + typeParameters(in, typeParams), + variance, + processType(in, base.bounds.lo), + processType(in, base.bounds.hi) + ) + case x => error("Unknown type parameter info: " + x.getClass) + } + } + private def tparamID(s: Symbol): String = + existentialRenamings.renaming(s) match { + case Some(rename) => + // can't use debuglog because it doesn't exist in Scala 2.9.x + if (settings.debug.value) + log("Renaming existential type variable " + s.fullName + " to " + rename) + rename + case None => + s.fullName + } + + /* Representation for the self type of a class symbol `s`, or `emptyType` for an *unascribed* self variable (or no self variable at all). + Only the self variable's explicitly ascribed type is relevant for incremental compilation. */ + private def selfType(in: Symbol, s: Symbol): xsbti.api.Type = + // `sym.typeOfThis` is implemented as `sym.thisSym.info`, which ensures the *self* symbol is initialized (the type completer is run). + // We can safely avoid running the type completer for `thisSym` for *class* symbols where `thisSym == this`, + // as that invariant is established on completing the class symbol (`mkClassLike` calls `s.initialize` before calling us). + // Technically, we could even ignore a self type that's a supertype of the class's type, + // as it does not contribute any information relevant outside of the class definition. + if ((s.thisSym eq s) || (s.thisSym.tpeHK == s.tpeHK)) Constants.emptyType + else processType(in, s.typeOfThis) + + def extractAllClassesOf(in: Symbol, c: Symbol): Unit = { + classLike(in, c) + () + } + + def allExtractedNonLocalClasses: Set[ClassLike] = { + forceStructures() + allNonLocalClassesInSrc.toSet + } + + def allExtractedNonLocalSymbols: Set[Symbol] = allNonLocalClassSymbols.toSet + + def mainClasses: Set[String] = { + forceStructures() + _mainClasses.toSet + } + + private def classLike(in: Symbol, c: Symbol): ClassLikeDef = + classLikeCache.getOrElseUpdate((in, c), mkClassLike(in, c)) + private def mkClassLike(in: Symbol, c: Symbol): ClassLikeDef = { + // Normalize to a class symbol, and initialize it. + // (An object -- aka module -- also has a term symbol, + // but it's the module class that holds the info about its structure.) + val sym = (if (c.isModule) c.moduleClass else c).initialize + val defType = + if (sym.isTrait) DefinitionType.Trait + else if (sym.isModuleClass) { + if (sym.isPackageObjectClass) DefinitionType.PackageModule + else DefinitionType.Module + } else DefinitionType.ClassDef + val childrenOfSealedClass = sort(sym.children.toArray).map(c => processType(c, c.tpe)) + val topLevel = sym.owner.isPackageClass + val anns = annotations(in, c) + val modifiers = getModifiers(c) + val acc = getAccess(c) + val name = classNameAsSeenIn(in, c) + val tParams = typeParameters(in, sym) // look at class symbol + val selfType = lzy(this.selfType(in, sym)) + def constructClass(structure: xsbti.api.Lazy[Structure]): ClassLike = { + xsbti.api.ClassLike.of( + name, + acc, + modifiers, + anns, + defType, + selfType, + structure, + emptyStringArray, + childrenOfSealedClass, + topLevel, + tParams + ) // use original symbol (which is a term symbol when `c.isModule`) for `name` and other non-classy stuff + } + val info = viewer(in).memberInfo(sym) + val structure = lzy(structureWithInherited(info, sym)) + val classWithMembers = constructClass(structure) + + allNonLocalClassesInSrc += classWithMembers + allNonLocalClassSymbols += sym + + if (sym.isStatic && defType == DefinitionType.Module && definitions.hasJavaMainMethod(sym)) { + _mainClasses += name + } + + val classDef = xsbti.api.ClassLikeDef.of( + name, + acc, + modifiers, + anns, + tParams, + defType + ) // use original symbol (which is a term symbol when `c.isModule`) for `name` and other non-classy stuff + classDef + } + + // TODO: could we restrict ourselves to classes, ignoring the term symbol for modules, + // since everything we need to track about a module is in the module's class (`moduleSym.moduleClass`)? + private[this] def isClass(s: Symbol) = s.isClass || s.isModule + // necessary to ensure a stable ordering of classes in the definitions list: + // modules and classes come first and are sorted by name + // all other definitions come later and are not sorted + private[this] val sortClasses = new Comparator[Symbol] { + def compare(a: Symbol, b: Symbol) = { + val aIsClass = isClass(a) + val bIsClass = isClass(b) + if (aIsClass == bIsClass) + if (aIsClass) + if (a.isModule == b.isModule) + a.fullName.compareTo(b.fullName) + else if (a.isModule) + -1 + else + 1 + else + 0 // substantial performance hit if fullNames are compared here + else if (aIsClass) + -1 + else + 1 + } + } + private object Constants { + val local = xsbti.api.ThisQualifier.of() + val public = xsbti.api.Public.of() + val privateLocal = xsbti.api.Private.of(local) + val protectedLocal = xsbti.api.Protected.of(local) + val unqualified = xsbti.api.Unqualified.of() + val emptyPath = xsbti.api.Path.of(Array()) + val thisPath = xsbti.api.This.of() + val emptyType = xsbti.api.EmptyType.of() + } + + private def simpleName(s: Symbol): String = { + val n = s.unexpandedName + val n2 = if (n == nme.CONSTRUCTOR) constructorNameAsString(s.enclClass) else n.decode.toString + n2.trim + } + + private def staticAnnotations(annotations: List[AnnotationInfo]): List[AnnotationInfo] = + if (annotations == Nil) Nil + else { + // compat stub for 2.8/2.9 + class IsStatic(ann: AnnotationInfo) { + def isStatic: Boolean = + ann.atp.typeSymbol isNonBottomSubClass definitions.StaticAnnotationClass + } + implicit def compat(ann: AnnotationInfo): IsStatic = new IsStatic(ann) + + // scala/bug#11679 annotations of inherited members may be absent from the compile time classpath + // so avoid calling `isNonBottomSubClass` on these stub symbols which would trigger a fatal error. + annotations.filter(ann => !isStub(ann.atp.typeSymbol) && ann.isStatic) + } + + private def isStub(sym: Symbol): Boolean = sym match { + case _: StubSymbol => true + case _ => false + } +} + +object ExtractAPI { + private val emptyAnnotationArray = new Array[xsbti.api.Annotation](0) +} diff --git a/dummy-bridge/src/main/scala/xsbt/ExtractUsedNames.scala b/dummy-bridge/src/main/scala/xsbt/ExtractUsedNames.scala new file mode 100644 index 0000000..e80fe48 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/ExtractUsedNames.scala @@ -0,0 +1,360 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.util.{ HashMap => JavaMap } +import java.util.{ HashSet => JavaSet } +import java.util.EnumSet + +import xsbti.UseScope +// Left for compatibility +import Compat._ + +/** + * Extracts simple names used in given compilation unit. + * + * Extracts simple (unqualified) names mentioned in given in non-definition position by collecting + * all symbols associated with non-definition trees and extracting names from all collected symbols. + * Also extract the names of the types of non-definition trees (see source-dependencies/types-in-used-names-* + * and source-dependencies/as-seen-from-* for examples where this is required). + * + * If given symbol is mentioned both in definition and in non-definition position (e.g. in member + * selection) then that symbol is collected. It means that names of symbols defined and used in the + * same compilation unit are extracted. We've considered not extracting names of those symbols + * as an optimization strategy. It turned out that this is not correct. Check + * https://github.com/gkossakowski/sbt/issues/3 for an example of scenario where it matters. + * + * All extracted names are returned in _decoded_ form. This way we stay consistent with the rest + * of incremental compiler which works with names in decoded form. + * + * Names mentioned in Import nodes are handled properly but require some special logic for two + * reasons: + * + * 1. The `termSymbol` of Import nodes point to the symbol of the prefix it imports from + * (not the actual members that we import, that are represented as names). + * 2. ImportSelector is not subtype of Tree therefore is not processed by `Tree.foreach`. + * + * Another type of tree nodes that requires special handling is TypeTree. TypeTree nodes + * has a little bit odd representation: + * + * 1. TypeTree.hasSymbol always returns false even when TypeTree.symbol + * returns a symbol + * 2. The original tree from which given TypeTree was derived is stored + * in TypeTree.original but Tree.forech doesn't walk into original + * tree so we missed it + * + * The tree walking algorithm walks into TypeTree.original explicitly. + * + */ +class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) + extends Compat + with ClassName + with GlobalHelpers { + + import global._ + import JavaUtils._ + + private final class NamesUsedInClass { + // Default names and other scopes are separated for performance reasons + val defaultNames: JavaSet[Name] = new JavaSet[global.Name]() + val scopedNames: JavaMap[Name, EnumSet[UseScope]] = new JavaMap[Name, EnumSet[UseScope]]() + + // We have to leave with commas on ends + override def toString(): String = { + val builder = new StringBuilder(": ") + defaultNames.foreach { name => + builder.append(name.decoded.trim) + val otherScopes = scopedNames.get(name) + if (otherScopes != null) { + builder.append(" in [") + otherScopes.foreach(scope => builder.append(scope.name()).append(", ")) + builder.append("]") + } + builder.append(", ") + } + builder.toString() + } + } + + private def DefaultScopes = EnumSet.of(UseScope.Default) + private def PatmatScopes = EnumSet.of(UseScope.PatMatTarget) + + def extractAndReport(unit: CompilationUnit): Unit = { + val tree = unit.body + val traverser = new ExtractUsedNamesTraverser + traverser.traverse(tree) + + val namesUsedAtTopLevel = traverser.namesUsedAtTopLevel + val defaultNamesTopLevel = namesUsedAtTopLevel.defaultNames + val scopedNamesTopLevel = namesUsedAtTopLevel.scopedNames + + // Handle names used at top level that cannot be related to an owner + if (!defaultNamesTopLevel.isEmpty || !scopedNamesTopLevel.isEmpty) { + val responsible = firstClassOrModuleDef(tree) + responsible match { + case Some(classOrModuleDef) => + val sym = classOrModuleDef.symbol + val firstClassSymbol = enclOrModuleClass(sym) + val firstClassName = className(firstClassSymbol) + val namesInFirstClass = traverser.usedNamesFromClass(firstClassName) + val scopedNamesInFirstClass = namesInFirstClass.scopedNames + + namesInFirstClass.defaultNames.addAll(defaultNamesTopLevel) + scopedNamesTopLevel.foreach { (topLevelName, topLevelScopes) => + val existingScopes = scopedNamesInFirstClass.get(topLevelName) + if (existingScopes == null) + scopedNamesInFirstClass.put(topLevelName, topLevelScopes) + else existingScopes.addAll(topLevelScopes) + () + } + + case None => + reporter.warning(unit.position(0), Feedback.OrphanNames) + } + } + + debuglog { + val msg = s"The ${unit.source} contains the following used names:\n" + val builder = new StringBuilder(msg) + traverser.usedNamesFromClasses.foreach { (name, usedNames) => + builder + .append(name.toString.trim) + .append(": ") + .append(usedNames.toString()) + .append("\n") + () + } + builder.toString() + } + + // Handle names circumscribed to classes + traverser.usedNamesFromClasses.foreach { (rawClassName, usedNames) => + val className = rawClassName.toString.trim + usedNames.defaultNames.foreach { rawUsedName => + val useName = rawUsedName.decoded.trim + val existingScopes = usedNames.scopedNames.get(rawUsedName) + val useScopes = { + if (existingScopes == null) DefaultScopes + else { + existingScopes.add(UseScope.Default) + existingScopes + } + } + callback.usedName(className, useName, useScopes) + } + } + } + + private def firstClassOrModuleDef(tree: Tree): Option[Tree] = { + tree foreach { + case t @ ((_: ClassDef) | (_: ModuleDef)) => return Some(t) + case _ => () + } + None + } + + private class ExtractUsedNamesTraverser extends Traverser { + + val usedNamesFromClasses = new JavaMap[Name, NamesUsedInClass]() + val namesUsedAtTopLevel = new NamesUsedInClass + + override def traverse(tree: Tree): Unit = { + handleClassicTreeNode(tree) + processMacroExpansion(tree)(handleMacroExpansion) + super.traverse(tree) + } + + val addSymbol: (JavaSet[Name], Symbol) => Unit = { (names: JavaSet[Name], symbol: Symbol) => + // Synthetic names are no longer included. See https://github.com/sbt/sbt/issues/2537 + if (!ignoredSymbol(symbol) && !isEmptyName(symbol.name)) { + names.add(mangledName(symbol)) + () + } + } + + /** Returns mutable set with all names from given class used in current context */ + def usedNamesFromClass(className: Name): NamesUsedInClass = { + val names = usedNamesFromClasses.get(className) + if (names == null) { + val newOne = new NamesUsedInClass + usedNamesFromClasses.put(className, newOne) + newOne + } else names + } + + /* + * Some macros appear to contain themselves as original tree. + * We must check that we don't inspect the same tree over and over. + * See https://issues.scala-lang.org/browse/SI-8486 + * https://github.com/sbt/sbt/issues/1237 + * https://github.com/sbt/sbt/issues/1544 + */ + private val inspectedOriginalTrees = new JavaSet[Tree]() + private val inspectedTypeTrees = new JavaSet[Tree]() + + private val handleMacroExpansion: Tree => Unit = { original => + if (!inspectedOriginalTrees.contains(original)) { + inspectedOriginalTrees.add(original) + traverse(original) + } + } + + private object PatMatDependencyTraverser extends TypeDependencyTraverser { + override def addDependency(symbol: global.Symbol): Unit = { + if (!ignoredSymbol(symbol) && symbol.isSealed) { + val name = mangledName(symbol) + if (!isEmptyName(name)) { + val existingScopes = _currentScopedNamesCache.get(name) + if (existingScopes == null) + _currentScopedNamesCache.put(name, PatmatScopes) + else existingScopes.add(UseScope.PatMatTarget) + } + } + () + } + } + + private object TypeDependencyTraverser extends TypeDependencyTraverser { + private val ownersCache = new JavaMap[Symbol, JavaSet[Type]]() + private var nameCache: JavaSet[Name] = _ + private var ownerVisited: Symbol = _ + + def setCacheAndOwner(cache: JavaSet[Name], owner: Symbol): Unit = { + if (ownerVisited != owner) { + val ts = ownersCache.get(owner) + + if (ts == null) { + val newVisited = new JavaSet[Type]() + visited = newVisited + ownersCache.put(owner, newVisited) + } else { + visited = ts + } + + nameCache = cache + ownerVisited = owner + } + } + + override def addDependency(symbol: global.Symbol): Unit = + addSymbol(nameCache, symbol) + } + + private def handleClassicTreeNode(tree: Tree): Unit = tree match { + // Register names from pattern match target type in PatMatTarget scope + case ValDef(mods, _, tpt, _) if mods.isCase && mods.isSynthetic => + updateCurrentOwner() + PatMatDependencyTraverser.traverse(tpt.tpe) + case _: DefTree | _: Template => () + case Import(_, selectors: List[ImportSelector]) => + val names = getNamesOfEnclosingScope + def usedNameInImportSelector(name: Name): Unit = { + if (!isEmptyName(name) && (name != nme.WILDCARD) && !names.contains(name)) { + names.add(name) + () + } + } + selectors foreach { selector => + usedNameInImportSelector(selector.name) + usedNameInImportSelector(selector.rename) + } + /* Original type trees have to be traversed because typer is very + * aggressive when expanding explicit user-defined types. For instance, + * `Foo#B` will be expanded to `C` and the dependency on `Foo` will be + * lost. This makes sure that we traverse all the original prefixes. */ + case t: TypeTree if t.original != null => + val original = t.original + if (!inspectedTypeTrees.contains(original)) { + inspectedTypeTrees.add(original) + original.foreach(traverse) + } + + case t if t.hasSymbolField => + val symbol = t.symbol + if (symbol != rootMirror.RootPackage) { + addSymbol(getNamesOfEnclosingScope, t.symbol) + } + + val tpe = t.tpe + if (!ignoredType(tpe)) { + // Initialize _currentOwner if it's not + val cache = getNamesOfEnclosingScope + TypeDependencyTraverser.setCacheAndOwner(cache, _currentOwner) + TypeDependencyTraverser.traverse(tpe) + } + case _ => + } + + private var _currentOwner: Symbol = _ + private var _currentNonLocalClass: Symbol = _ + private var _currentNamesCache: JavaSet[Name] = _ + private var _currentScopedNamesCache: JavaMap[Name, EnumSet[UseScope]] = _ + + @inline private def resolveNonLocal(from: Symbol): Symbol = { + val fromClass = enclOrModuleClass(from) + if (ignoredSymbol(fromClass) || fromClass.hasPackageFlag) NoSymbol + else localToNonLocalClass.resolveNonLocal(fromClass) + } + + @inline private def namesInClass(nonLocalClass: Symbol): NamesUsedInClass = { + if (nonLocalClass == NoSymbol) namesUsedAtTopLevel + else usedNamesFromClass(ExtractUsedNames.this.className(nonLocalClass)) + } + + /** + * Updates caches for closest non-local class owner of a tree given + * `currentOwner`, defined and updated by `Traverser`. + * + * This method modifies the state associated with the names variable + * `_currentNamesCache` and `_currentScopedNamesCache`, which are composed + * by `_currentOwner` and and `_currentNonLocalClass`. + * + * * The used caching strategy works as follows: + * 1. Do nothing if owners are referentially equal. + * 2. Otherwise, check if they resolve to the same non-local class. + * 1. If they do, do nothing + * 2. Otherwise, overwrite all the pertinent fields to be consistent. + */ + private def updateCurrentOwner(): Unit = { + if (_currentOwner == null) { + // Set the first state for the enclosing non-local class + _currentOwner = currentOwner + _currentNonLocalClass = resolveNonLocal(currentOwner) + val usedInClass = namesInClass(_currentNonLocalClass) + _currentNamesCache = usedInClass.defaultNames + _currentScopedNamesCache = usedInClass.scopedNames + } else if (_currentOwner != currentOwner) { + val nonLocalClass = resolveNonLocal(currentOwner) + if (_currentNonLocalClass != nonLocalClass) { + _currentOwner = currentOwner + _currentNonLocalClass = nonLocalClass + val usedInClass = namesInClass(_currentNonLocalClass) + _currentNamesCache = usedInClass.defaultNames + _currentScopedNamesCache = usedInClass.scopedNames + } + } + } + + /** + * Return the names associated with the closest non-local class owner + * of a tree given `currentOwner`, defined and updated by `Traverser`. + * + * This method modifies the state associated with the names variable + * by calling `updateCurrentOwner()`. + */ + @inline + private def getNamesOfEnclosingScope: JavaSet[Name] = { + updateCurrentOwner() + _currentNamesCache + } + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/GlobalHelpers.scala b/dummy-bridge/src/main/scala/xsbt/GlobalHelpers.scala new file mode 100644 index 0000000..4f4e154 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/GlobalHelpers.scala @@ -0,0 +1,177 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import scala.tools.nsc.Global +import java.util.HashSet + +trait GlobalHelpers { self: Compat => + val global: Global + import global._ + + /** Return true if type shall be ignored, false otherwise. */ + @inline def ignoredType(tpe: Type) = { + tpe == null || + tpe == NoType || + tpe.typeSymbol == EmptyPackageClass + } + + /** Return true if symbol shall be ignored, false otherwise. */ + @inline def ignoredSymbol(symbol: Symbol) = { + symbol == null || + symbol == NoSymbol || + symbol == EmptyPackageClass + } + + /** Return true if name is empty, false otherwise. */ + def isEmptyName(name: Name): Boolean = { + name match { + case null | nme.EMPTY | nme.EMPTY_PACKAGE_NAME | tpnme.EMPTY | tpnme.EMPTY_PACKAGE_NAME => + true + case _ => false + } + } + + private[xsbt] abstract class TypeDependencyTraverser extends TypeTraverser { + def addDependency(symbol: Symbol): Unit + + /** Add type dependency ignoring packages and inheritance info from classes. */ + @inline private def addTypeSymbolDependency(symbol: Symbol): Unit = { + addDependency(symbol) + if (!symbol.isClass) { + traverse(symbol.info) + } + } + + /** Add type dependency *AND* traverse prefix iff is not a package. */ + @inline private def addTypeDependency(tpe: Type): Unit = { + val symbol = tpe.typeSymbolDirect + if (!symbol.hasPackageFlag) { + addTypeSymbolDependency(symbol) + traverse(tpe.prefix) + } + } + + // Define cache and populate it with known types at initialization time + protected var visited = new HashSet[Type]() + + /** Clear the cache after every `traverse` invocation at the call-site. */ + protected def reinitializeVisited(): Unit = visited.clear() + + /** + * Traverse the type and its info to track all type dependencies. + * + * Note that tpe cannot be either `NoSymbol` or `null`. + * Check that you don't pass those types at the call-site. + */ + override def traverse(tpe: Type): Unit = { + if ((tpe ne NoType) && !visited.contains(tpe)) { + visited.add(tpe) + tpe match { + case singleRef: SingleType => + addTypeDependency(singleRef) + + case typeRef: TypeRef => + // Traverse materialized type arguments + typeRef.typeArguments.foreach(traverse) + addTypeDependency(typeRef) + + case MethodType(_, _) => + // Traverse the types of method parameters definitions + tpe.params.foreach(param => traverse(param.tpe)) + // Traverse return type + traverse(tpe.resultType) + + case PolyType(_, _) => + // Traverse the symbols of poly types and their prefixes + tpe.typeParams.foreach { typeParam => + addTypeSymbolDependency(typeParam) + val prefix = typeParam.info.prefix + if (!prefix.typeSymbolDirect.hasPackageFlag) + traverse(prefix) + } + // Traverse return type + traverse(tpe.resultType) + + case TypeBounds(lo, hi) => + // Ignore default types for lo and hi bounds + if (!(lo == definitions.NothingTpe)) traverse(lo) + if (!(hi == definitions.AnyTpe)) traverse(hi) + + case RefinedType(parents, decls) => + parents.foreach(traverse) + decls.toIterator.foreach { decl => + if (decl.isType) addTypeSymbolDependency(decl) + else addDependency(decl) + } + + case ExistentialType(quantified, underlying) => + quantified.foreach(quantified => traverse(quantified.tpe)) + traverse(underlying) + + case ThisType(_) | ConstantType(_) => + traverse(tpe.underlying) + + case _ => + mapOver(tpe) + () + } + } + } + } + + /** Returns true if given tree contains macro attchment. In such case calls func on tree from attachment. */ + def processMacroExpansion(in: Tree)(func: Tree => Unit): Boolean = { + import analyzer._ // this is where MEA lives in 2.11.x + // Hotspot + var seen = false + in.attachments.all.foreach { + case _ if seen => + case macroAttachment: MacroExpansionAttachment => + func(macroAttachment.expandee) + seen = true + case _ => + } + seen + } + + object MacroExpansionOf { + def unapply(tree: Tree): Option[Tree] = { + import analyzer._ // this is where MEA lives in 2.11.x + tree.attachments.all.collect { + case att: MacroExpansionAttachment => att.expandee + }.headOption + } + } + + /** Return the enclosing class or the module class if it's a module. */ + def enclOrModuleClass(s: Symbol): Symbol = + if (s.isModule) s.moduleClass else s.enclClass + + /** Define common error messages for error reporting and assertions. */ + object Feedback { + val OrphanTopLevelImports = noTopLevelMember("top level imports") + val OrphanNames = noTopLevelMember("names") + + def noOriginFileForExternalSymbol(symbol: Symbol) = + s"The symbol $symbol comes from an unknown source or compiled source -- ignoring." + def expectedClassSymbol(culprit: Symbol): String = + s"The ${culprit.fullName} defined at ${culprit.fullLocationString} is not a class symbol." + def missingEnclosingClass(culprit: Symbol, owner: Symbol): String = + s"No enclosing class. Discarding dependency on $culprit (currentOwner = $owner)." + def noTopLevelMember(found: String) = s""" + |Found $found but no class, trait or object is defined in the compilation unit. + |The incremental compiler cannot record the dependency information in such case. + |Some errors like unused import referring to a non-existent class might not be reported. + """.stripMargin + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleFactory.scala b/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleFactory.scala new file mode 100644 index 0000000..b55567d --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleFactory.scala @@ -0,0 +1,39 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import xsbti.Logger + +class InteractiveConsoleFactory extends xsbti.InteractiveConsoleFactory { + def createConsole( + args: Array[String], + bootClasspathString: String, + classpathString: String, + initialCommands: String, + cleanupCommands: String, + loader: ClassLoader, + bindNames: Array[String], + bindValues: Array[AnyRef], + log: Logger + ): xsbti.InteractiveConsoleInterface = + new InteractiveConsoleInterface( + args, + bootClasspathString, + classpathString, + initialCommands, + cleanupCommands, + loader, + bindNames, + bindValues, + log + ) +} diff --git a/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleHelper.scala b/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleHelper.scala new file mode 100644 index 0000000..d1ff271 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleHelper.scala @@ -0,0 +1,24 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import Compat._ +import xsbti.InteractiveConsoleResult + +object InteractiveConsoleHelper { + implicit def toConsoleResult(ir: Results.Result): InteractiveConsoleResult = + ir match { + case Results.Success => InteractiveConsoleResult.Success + case Results.Incomplete => InteractiveConsoleResult.Incomplete + case Results.Error => InteractiveConsoleResult.Error + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleInterface.scala b/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleInterface.scala new file mode 100644 index 0000000..b0dc963 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleInterface.scala @@ -0,0 +1,94 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.io.{ PrintWriter, StringWriter } + +import scala.tools.nsc.interpreter.IMain +import scala.tools.nsc.{ GenericRunnerCommand, Settings } + +import xsbti.Logger + +import Compat._ +import InteractiveConsoleHelper._ + +class InteractiveConsoleInterface( + args: Array[String], + bootClasspathString: String, + classpathString: String, + initialCommands: String, + cleanupCommands: String, + loader: ClassLoader, + bindNames: Array[String], + bindValues: Array[AnyRef], + log: Logger +) extends xsbti.InteractiveConsoleInterface { + + lazy val interpreterSettings: Settings = InteractiveMakeSettings.sync(args.toList, onError) + + val useJavaCp = "-usejavacp" // we need rt.jar from JDK, so java classpath is required + + val compilerSettings: Settings = + InteractiveMakeSettings.sync(args :+ useJavaCp, bootClasspathString, classpathString, onError) + + val outWriter: StringWriter = new StringWriter + val poutWriter: PrintWriter = new PrintWriter(outWriter) + + val interpreter: IMain = + new IMain(compilerSettings, replReporter(compilerSettings, new PrintWriter(outWriter))) + + def interpret(line: String, synthetic: Boolean): InteractiveConsoleResponse = { + clearBuffer() + val r = interpreter.interpret(line, synthetic) + InteractiveConsoleResponse(r, outWriter.toString) + } + + def clearBuffer(): Unit = { + // errorWriter.getBuffer.setLength(0) + outWriter.getBuffer.setLength(0) + } + + def reset(): Unit = { + clearBuffer() + interpreter.reset() + } + + private def onError(str: String) = log error Message(str) +} + +object InteractiveMakeSettings { + def apply(args: List[String], onError: String => Unit): Settings = { + val command = new GenericRunnerCommand(args, onError) + if (command.ok) command.settings + // TODO: Provide better exception + else throw new Exception(command.usageMsg) + } + + def sync( + args: Array[String], + bootClasspathString: String, + classpathString: String, + onError: String => Unit + ): Settings = { + val compilerSettings = sync(args.toList, onError) + if (!bootClasspathString.isEmpty) + compilerSettings.bootclasspath.value = bootClasspathString + compilerSettings.classpath.value = classpathString + compilerSettings + } + + def sync(options: List[String], onError: String => Unit): Settings = { + val settings = apply(options, onError) + settings.Yreplsync.value = true + settings + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleResponse.scala b/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleResponse.scala new file mode 100644 index 0000000..064b26b --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/InteractiveConsoleResponse.scala @@ -0,0 +1,17 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import xsbti.InteractiveConsoleResult + +case class InteractiveConsoleResponse(result: InteractiveConsoleResult, output: String) + extends xsbti.InteractiveConsoleResponse diff --git a/dummy-bridge/src/main/scala/xsbt/JarUtils.scala b/dummy-bridge/src/main/scala/xsbt/JarUtils.scala new file mode 100644 index 0000000..220e77d --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/JarUtils.scala @@ -0,0 +1,49 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.io.File +import java.nio.file.Path + +/** + * This is a utility class that provides a set of functions that + * are used to implement straight to jar compilation. + * + * sbt.internal.inc.JarUtils is an object that has similar purpose and + * duplicates some of the code, as it is difficult to share it. Any change + * in the logic of this file must be applied to the other `JarUtils` too! + */ +final class JarUtils(outputDirs: Iterable[Path]) { + // This is an equivalent of asking if it runs on Windows where the separator is `\` + private val isSlashSeparator: Boolean = File.separatorChar == '/' + + /** + * The jar file that is used as output for classes. If the output is + * not set to a single .jar file, value of this field is None. + */ + val outputJar: Option[Path] = { + outputDirs match { + case Seq(file) if file.toString.endsWith(".jar") => Some(file) + case _ => None + } + } + + /** + * Creates an identifier for a class located inside a jar. + * + * It follows the format to encode inter-jar dependencies that + * is established in sbt.internal.inc.JarUtils.ClassInJar. + */ + def classNameInJar(jar: Path, classFilePath: String): String = { + s"$jar!${if (isSlashSeparator) classFilePath else classFilePath.replace('/', File.separatorChar)}" + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/JavaUtils.scala b/dummy-bridge/src/main/scala/xsbt/JavaUtils.scala new file mode 100644 index 0000000..11e6fca --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/JavaUtils.scala @@ -0,0 +1,35 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +private[xsbt] object JavaUtils { + implicit class JavaForEach[T](val iterable: java.lang.Iterable[T]) extends AnyVal { + + @inline + def foreach[U](op: T => U): Unit = { + val iterator = iterable.iterator() + while (iterator.hasNext) op(iterator.next()) + } + } + + implicit class JavaMapForEach[K, V](val map: java.util.Map[K, V]) extends AnyVal { + + @inline + def foreach[U](op: (K, V) => U): Unit = { + val iterator = map.keySet().iterator() + while (iterator.hasNext) { + val key = iterator.next() + op(key, map.get(key)) + } + } + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/LocalToNonLocalClass.scala b/dummy-bridge/src/main/scala/xsbt/LocalToNonLocalClass.scala new file mode 100644 index 0000000..ce398dd --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/LocalToNonLocalClass.scala @@ -0,0 +1,75 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import collection.mutable.Map + +/** + * A memoized lookup of an enclosing non local class. + * + * Let's consider an example of an owner chain: + * + * pkg1 <- pkg2 <- class A <- object B <- class C <- def foo <- class Foo <- class Bar + * + * For an object, we work with its `moduleClass` so we can refer to everything as classes. + * + * Classes A, B, C are non local so they are mapped to themselves. Classes Foo and Bar are local because + * they are defined within method `foo`. + * + * Let's define non local class more precisely. A non local class is a class that is owned by either a package + * or another non local class. This gives rise to a recursive definition of a non local class that is used in the + * implementation of the mapping. + * + * Thanks to memoization, the amortized cost of a lookup is O(1). We amortize over lookups of all class symbols + * in the current compilation run. + * + * Additionally, you can query whether a given class is local. Check `isLocal`'s documentation. + */ +class LocalToNonLocalClass[G <: CallbackGlobal](val global: G) { + import global._ + private val cache: Map[Symbol, Symbol] = perRunCaches.newMap() + + def resolveNonLocal(s: Symbol): Symbol = { + assert( + phase.id <= sbtDependency.ownPhase.id, + s"Tried to resolve ${s.fullName} to a non local classes but the resolution works up to sbtDependency phase. We're at ${phase.name}" + ) + resolveCached(s) + } + + /** + * Queries the cached information whether a class is a local class. If there's no cached information about + * the class None is returned. + * + * This method doesn't mutate the cache. + */ + def isLocal(s: Symbol): Option[Boolean] = { + assert(s.isClass, s"The ${s.fullName} is not a class.") + cache.get(s).map(_ != s) + } + + private def resolveCached(s: Symbol): Symbol = { + assert(s.isClass, s"The ${s.fullName} is not a class.") + cache.getOrElseUpdate(s, lookupNonLocal(s)) + } + + private def lookupNonLocal(s: Symbol): Symbol = { + if (s.owner.isPackageClass) s + else if (s.owner.isClass) { + val nonLocalForOwner = resolveCached(s.owner) + // the s is owned by a non local class so s is non local + if (nonLocalForOwner == s.owner) s + // otherwise the inner most non local class is the same as for its owner + else nonLocalForOwner + } else resolveCached(s.owner.enclClass) + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/LocateClassFile.scala b/dummy-bridge/src/main/scala/xsbt/LocateClassFile.scala new file mode 100644 index 0000000..08b1936 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/LocateClassFile.scala @@ -0,0 +1,53 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import scala.reflect.io.NoAbstractFile +import scala.tools.nsc.io.AbstractFile + +import java.io.File + +/** + * Contains utility methods for looking up class files corresponding to Symbols. + */ +abstract class LocateClassFile extends Compat with ClassName { + val global: CallbackGlobal + import global._ + + private[this] final val classSeparator = '.' + protected def classFile(sym: Symbol): Option[(AbstractFile, String)] = + // package can never have a corresponding class file; this test does not + // catch package objects (that do not have this flag set) + if (sym hasFlag scala.tools.nsc.symtab.Flags.PACKAGE) None + else { + val file = sym.associatedFile + + if (file == NoAbstractFile) { + if (isTopLevelModule(sym)) { + val linked = sym.companionClass + if (linked == NoSymbol) + None + else + classFile(linked) + } else + None + } else { + Some((file, flatname(sym, classSeparator) + sym.moduleSuffix)) + } + } + + protected def fileForClass(outputDirectory: File, s: Symbol, separatorRequired: Boolean): File = + new File(outputDirectory, flatclassName(s, File.separatorChar, separatorRequired) + ".class") + + protected def pathToClassFile(s: Symbol, separatorRequired: Boolean): String = + flatclassName(s, File.separatorChar, separatorRequired) + ".class" +} diff --git a/dummy-bridge/src/main/scala/xsbt/Log.scala b/dummy-bridge/src/main/scala/xsbt/Log.scala new file mode 100644 index 0000000..007ed14 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/Log.scala @@ -0,0 +1,18 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +object Log { + def debug(log: xsbti.Logger, msg: => String) = log.debug(Message(msg)) + def settingsError(log: xsbti.Logger): String => Unit = + s => log.error(Message(s)) +} diff --git a/dummy-bridge/src/main/scala/xsbt/Message.scala b/dummy-bridge/src/main/scala/xsbt/Message.scala new file mode 100644 index 0000000..0fb3ab2 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/Message.scala @@ -0,0 +1,20 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import java.util.function.Supplier + +object Message { + def apply[T](s: => T): Supplier[T] = new Supplier[T] { + override def get(): T = s + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/ScaladocInterface.scala b/dummy-bridge/src/main/scala/xsbt/ScaladocInterface.scala new file mode 100644 index 0000000..8f17f25 --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/ScaladocInterface.scala @@ -0,0 +1,81 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import xsbti.{ Logger, VirtualFile } +import scala.reflect.io.AbstractFile +import Log.debug + +class ScaladocInterface { + def run(sources: Array[VirtualFile], args: Array[String], log: Logger, delegate: xsbti.Reporter) = + (new Runner(sources, args, log, delegate)).run +} + +private class Runner( + sources: Array[VirtualFile], + args: Array[String], + log: Logger, + delegate: xsbti.Reporter +) { + import scala.tools.nsc.{ doc, Global, reporters } + import reporters.Reporter + val docSettings: doc.Settings = new doc.Settings(Log.settingsError(log)) + val command = Command(args.toList, docSettings) + val reporter = DelegatingReporter(docSettings, delegate) + def noErrors = !reporter.hasErrors && command.ok + + def run(): Unit = { + debug(log, "Calling Scaladoc with arguments:\n\t" + args.mkString("\n\t")) + if (noErrors) { + import doc._ // 2.8 trunk and Beta1-RC4 have doc.DocFactory. For other Scala versions, the next line creates forScope.DocFactory + val processor = new DocFactory(reporter, docSettings) + processor.document(command.files) + } + reporter.printSummary() + if (!noErrors) + throw new InterfaceCompileFailed( + args ++ sources.map(_.toString), + reporter.problems, + "Scaladoc generation failed" + ) + } + + object forScope { + class DocFactory(reporter: Reporter, docSettings: doc.Settings) { + object compiler extends Global(command.settings, reporter) { + // override def onlyPresentation = true + // override def forScaladoc = true + + // 2.8 source compatibility + class DefaultDocDriver { + assert(false) + def process(units: Iterator[CompilationUnit]) = error("for 2.8 compatibility only") + } + } + def document(ignore: Seq[String]): Unit = { + import compiler._ + val run = new Run + val wrappedFiles = sources.toList.map(new VirtualFileWrap(_)) + val sortedSourceFiles: List[AbstractFile] = + wrappedFiles.sortWith(_.underlying.id < _.underlying.id) + run.compileFiles(sortedSourceFiles) + val generator = { + new DefaultDocDriver { + lazy val global: compiler.type = compiler + lazy val settings = docSettings + } + } + generator.process(run.units) + } + } + } +} diff --git a/dummy-bridge/src/main/scala/xsbt/VirtualFileWrap.scala b/dummy-bridge/src/main/scala/xsbt/VirtualFileWrap.scala new file mode 100644 index 0000000..2e5911c --- /dev/null +++ b/dummy-bridge/src/main/scala/xsbt/VirtualFileWrap.scala @@ -0,0 +1,91 @@ +/* + * Zinc - The incremental compiler for Scala. + * Copyright Lightbend, Inc. and Mark Harrah + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package xsbt + +import xsbti.VirtualFile +import scala.reflect.io.AbstractFile +import java.io.{ File, InputStream, OutputStream } + +final class VirtualFileWrap(val underlying: VirtualFile) extends AbstractFile { + // scala.tools.nsc.CompilationUnits$CompilationUnit.(CompilationUnits.scala:161) + override def name: String = underlying.name + + // scala.tools.nsc.Global$Run.addUnit(Global.scala:1353) + override def path: String = underlying.id + + // at scala.tools.nsc.io.SourceReader.read(SourceReader.scala:62) + override def input: InputStream = underlying.input + + override def absolute: AbstractFile = { + ??? + // abstractFile.absolute + } + + // used only by Scala 2.10 + // https://github.com/scala/scala/blob/v2.10.7/src/compiler/scala/tools/nsc/Global.scala#L1726 + override def container: AbstractFile = { + new AbstractFile { + override def name: String = "temp" + def absolute: AbstractFile = ??? + def container: AbstractFile = ??? + def create(): Unit = ??? + def delete(): Unit = ??? + def file: File = ??? + def input: InputStream = ??? + def isDirectory: Boolean = true + def iterator: Iterator[AbstractFile] = ??? + def lastModified: Long = ??? + def lookupName(name: String, directory: Boolean): AbstractFile = ??? + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = ??? + def output: OutputStream = ??? + def path: String = ??? + } + } + + override def file: File = { + null + } + + override def create(): Unit = { + ??? + // abstractFile.create() + } + override def delete(): Unit = { + ??? + /// abstractFile.delete() + } + override def isDirectory: Boolean = { + ??? + // abstractFile.isDirectory + } + override def lastModified: Long = { + ??? + // abstractFile.lastModified + } + + override def output: OutputStream = { + ??? + // abstractFile.output + } + override def iterator: Iterator[AbstractFile] = { + ??? + // abstractFile.iterator + } + override def lookupName(name: String, directory: Boolean): AbstractFile = { + ??? + // abstractFile.lookupName(name, directory) + } + override def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = { + ??? + // abstractFile.lookupNameUnchecked(name, directory) + } +} diff --git a/dummy-bridge/src/test/scala/testpkg/CollectingReporter.scala b/dummy-bridge/src/test/scala/testpkg/CollectingReporter.scala new file mode 100644 index 0000000..b057705 --- /dev/null +++ b/dummy-bridge/src/test/scala/testpkg/CollectingReporter.scala @@ -0,0 +1,37 @@ +package testpkg + +import xsbti.{ Position, Severity } + +class CollectingReporter extends xsbti.Reporter { + val buffer = collection.mutable.ArrayBuffer.empty[xsbti.Problem] + + def reset(): Unit = { + // System.err.println(s"DEBUGME: Clearing errors: $buffer") + buffer.clear() + } + def hasErrors: Boolean = buffer.exists(_.severity == Severity.Error) + def hasWarnings: Boolean = buffer.exists(_.severity == Severity.Warn) + def printSummary(): Unit = () + def problems: Array[xsbti.Problem] = buffer.toArray + + def log(problem: xsbti.Problem): Unit = + log(problem.position, problem.message, problem.severity) + + /** Logs a message. */ + def log(pos: xsbti.Position, msg: String, sev: xsbti.Severity): Unit = { + object MyProblem extends xsbti.Problem { + def category: String = null + def severity: Severity = sev + def message: String = msg + def position: Position = pos + override def toString = s"$position:$severity: $message" + } + System.err.println(s"Logging: $MyProblem") + buffer.append(MyProblem) + } + + /** Reports a comment. */ + def comment(pos: xsbti.Position, msg: String): Unit = () + + override def toString = "CollectingReporter" +} diff --git a/dummy-bridge/src/test/scala/testpkg/CompilerInterfaceTest.scala b/dummy-bridge/src/test/scala/testpkg/CompilerInterfaceTest.scala new file mode 100644 index 0000000..fe85c4a --- /dev/null +++ b/dummy-bridge/src/test/scala/testpkg/CompilerInterfaceTest.scala @@ -0,0 +1,55 @@ +package testpkg + +import verify._ +import java.nio.file.{ Path, Paths } +import sbt.io.IO +import sbt.io.syntax._ +import xsbti.{ VirtualFile, VirtualFileRef } +import xsbti.compile.{ CompileProgress, DependencyChanges } + +object CompilerInterfaceTest extends BasicTestSuite { + + test("CompilerInterface1") { + IO.withTemporaryDirectory { tempDir => + val targetDir = tempDir / "target" / "classes" + IO.createDirectory(targetDir) + val output = new ConcreteSingleOutput(targetDir.toPath) + val log = TestConsoleLogger() + val reporter = new CollectingReporter + val callback = new TestCallback + val vFile: VirtualFile = + StringVirtualFile("src/A.scala", """package example + +object A { + val x = 1 +} +""") + val vs = Vector(vFile) + val bridge = ReflectionUtil.bridgeInstance("xsbt.CompilerInterface") + bridge match { + case intf: xsbti.CompilerInterface1 => + val scalaLibraryJar = ReflectionUtil.scalaLibraryJar + val cachedCompiler = + intf.newCompiler(Array("-classpath", scalaLibraryJar.toString), output, log, reporter) + cachedCompiler.run(vs.toArray, emptyChanges, callback, log, reporter, ignoreProgress) + assert((targetDir / "example" / "A.class").exists) + assert((targetDir / "example" / "A$.class").exists) + } + } + } + + val emptyChanges: DependencyChanges = new DependencyChanges { + override val modifiedLibraries = new Array[VirtualFileRef](0) + override val modifiedClasses = new Array[String](0) + override def isEmpty = true + } + + val ignoreProgress: CompileProgress = new CompileProgress { + def startUnit(phase: String, unitPath: String): Unit = () + def advance(current: Int, total: Int) = true + } +} + +final class ConcreteSingleOutput(val getOutputDirectory: Path) extends xsbti.compile.SingleOutput { + override def toString: String = s"SingleOutput($getOutputDirectory)" +} diff --git a/dummy-bridge/src/test/scala/testpkg/DualLoader.scala b/dummy-bridge/src/test/scala/testpkg/DualLoader.scala new file mode 100644 index 0000000..05a140a --- /dev/null +++ b/dummy-bridge/src/test/scala/testpkg/DualLoader.scala @@ -0,0 +1,115 @@ +package testpkg + +import java.net.URL +import java.util.Enumeration +import java.util.Collections + +/** A class loader that always fails to load classes and resources. */ +final class NullLoader extends ClassLoader { + override final def loadClass(className: String, resolve: Boolean): Class[_] = + throw new ClassNotFoundException("No classes can be loaded from the null loader") + override def getResource(name: String): URL = null + override def getResources(name: String): Enumeration[URL] = + Collections.enumeration(Collections.emptyList()) + override def toString = "NullLoader" +} + +/** Exception thrown when `loaderA` and `loaderB` load a different Class for the same name. */ +class DifferentLoaders(message: String, val loaderA: ClassLoader, val loaderB: ClassLoader) + extends ClassNotFoundException(message) + +/** + * A ClassLoader with two parents `parentA` and `parentB`. The predicates direct lookups towards one parent or the other. + * + * If `aOnlyClasses` returns `true` for a class name, class lookup delegates to `parentA` only. + * Otherwise, if `bOnlyClasses` returns `true` for a class name, class lookup delegates to `parentB` only. + * If both `aOnlyClasses` and `bOnlyClasses` are `false` for a given class name, both class loaders must load the same Class or + * a [[DifferentLoaders]] exception is thrown. + * + * If `aOnlyResources` is `true` for a resource path, lookup delegates to `parentA` only. + * Otherwise, if `bOnlyResources` is `true` for a resource path, lookup delegates to `parentB` only. + * If neither are `true` for a resource path and either `parentA` or `parentB` return a valid URL, that valid URL is returned. + */ +class DualLoader( + parentA: ClassLoader, + aOnlyClasses: String => Boolean, + aOnlyResources: String => Boolean, + parentB: ClassLoader, + bOnlyClasses: String => Boolean, + bOnlyResources: String => Boolean +) extends ClassLoader(new NullLoader) { + def this( + parentA: ClassLoader, + aOnly: String => Boolean, + parentB: ClassLoader, + bOnly: String => Boolean + ) = + this(parentA, aOnly, aOnly, parentB, bOnly, bOnly) + override final def loadClass(className: String, resolve: Boolean): Class[_] = { + val c = + if (aOnlyClasses(className)) + parentA.loadClass(className) + else if (bOnlyClasses(className)) + parentB.loadClass(className) + else { + val classA = parentA.loadClass(className) + val classB = parentB.loadClass(className) + if (classA.getClassLoader eq classB.getClassLoader) + classA + else + throw new DifferentLoaders( + "Parent class loaders returned different classes for '" + className + "'", + classA.getClassLoader, + classB.getClassLoader + ) + } + if (resolve) + resolveClass(c) + c + } + override def getResource(name: String): URL = { + if (aOnlyResources(name)) + parentA.getResource(name) + else if (bOnlyResources(name)) + parentB.getResource(name) + else { + val urlA = parentA.getResource(name) + val urlB = parentB.getResource(name) + if (urlA eq null) + urlB + else + urlA + } + } + override def getResources(name: String): Enumeration[URL] = { + if (aOnlyResources(name)) + parentA.getResources(name) + else if (bOnlyResources(name)) + parentB.getResources(name) + else { + val urlsA = parentA.getResources(name) + val urlsB = parentB.getResources(name) + if (!urlsA.hasMoreElements) + urlsB + else if (!urlsB.hasMoreElements) + urlsA + else + new DualEnumeration(urlsA, urlsB) + } + } + + override def toString = s"DualLoader(a = $parentA, b = $parentB)" +} + +/** Concatenates `a` and `b` into a single `Enumeration`.*/ +final class DualEnumeration[T](a: Enumeration[T], b: Enumeration[T]) extends Enumeration[T] { + // invariant: current.hasMoreElements or current eq b + private[this] var current = if (a.hasMoreElements) a else b + def hasMoreElements = current.hasMoreElements + def nextElement = { + val element = current.nextElement + if (!current.hasMoreElements) + current = b + element + } +} diff --git a/dummy-bridge/src/test/scala/testpkg/ReflectionUtil.scala b/dummy-bridge/src/test/scala/testpkg/ReflectionUtil.scala new file mode 100644 index 0000000..d76ee34 --- /dev/null +++ b/dummy-bridge/src/test/scala/testpkg/ReflectionUtil.scala @@ -0,0 +1,52 @@ +package testpkg + +import java.nio.file.Paths +import java.net.URLClassLoader +import java.lang.reflect.InvocationTargetException + +object ReflectionUtil { + + def bridgeInstance(bridgeClassName: String): AnyRef = { + val bridgeClass = getBridgeClass(bridgeClassName) + bridgeClass.getDeclaredConstructor().newInstance().asInstanceOf[AnyRef] + } + + def getBridgeClass(name: String) = + Class.forName(name, true, loader) + + def siJars = + sys + .props("test.sijars") + .split(sys.props("path.separator")) + .toList + .map(Paths.get(_)) + + def scalaLibraryJar = siJars.find(_.toString.contains("scala-library")).get + + lazy val scalaInstaceLoader = + new URLClassLoader(siJars.map(_.toUri.toURL).toArray) + + lazy val loader = { + val bridgeJar = Paths.get(sys.props("test.bridgejar")) + new URLClassLoader( + Array(bridgeJar.toUri.toURL), + createDualLoader(scalaInstaceLoader, getClass.getClassLoader) + ) + } + + def createDualLoader( + scalaLoader: ClassLoader, + sbtLoader: ClassLoader + ): ClassLoader = { + val xsbtiFilter = (name: String) => name.startsWith("xsbti.") + val notXsbtiFilter = (name: String) => !xsbtiFilter(name) + new DualLoader( + scalaLoader, + notXsbtiFilter, + _ => true, + sbtLoader, + xsbtiFilter, + _ => false + ) + } +} diff --git a/dummy-bridge/src/test/scala/testpkg/StringVirtualFile.scala b/dummy-bridge/src/test/scala/testpkg/StringVirtualFile.scala new file mode 100644 index 0000000..45e6b53 --- /dev/null +++ b/dummy-bridge/src/test/scala/testpkg/StringVirtualFile.scala @@ -0,0 +1,15 @@ +package testpkg + +import java.io.{ InputStream, ByteArrayInputStream } +import xsbti.{ BasicVirtualFileRef, VirtualFile } + +case class StringVirtualFile(path: String, content: String) + extends BasicVirtualFileRef(path) + with VirtualFile { + override def contentHash(): Long = content.hashCode + override def id(): String = path + override def name(): String = path.split("/").last + override def names(): Array[String] = path.split("/") + override def input(): InputStream = new ByteArrayInputStream(content.getBytes("UTF-8")) + override def toString(): String = s"StringVirtualFile($path, )" +} diff --git a/dummy-bridge/src/test/scala/testpkg/TestCallback.scala b/dummy-bridge/src/test/scala/testpkg/TestCallback.scala new file mode 100644 index 0000000..97998b5 --- /dev/null +++ b/dummy-bridge/src/test/scala/testpkg/TestCallback.scala @@ -0,0 +1,135 @@ +package testpkg + +import java.nio.file.Path +import java.util + +import xsbti._ +import xsbti.api.{ DependencyContext, ClassLike } + +import scala.collection.mutable.ArrayBuffer + +class TestCallback extends AnalysisCallback { + case class TestUsedName(name: String, scopes: util.EnumSet[UseScope]) + + val classDependencies = new ArrayBuffer[(String, String, DependencyContext)] + val binaryDependencies = + new ArrayBuffer[(Path, String, String, DependencyContext)] + val productClassesToSources = + scala.collection.mutable.Map.empty[Path, VirtualFileRef] + val usedNamesAndScopes = + scala.collection.mutable.Map.empty[String, Set[TestUsedName]].withDefaultValue(Set.empty) + val classNames = + scala.collection.mutable.Map + .empty[VirtualFileRef, Set[(String, String)]] + .withDefaultValue(Set.empty) + val apis: scala.collection.mutable.Map[VirtualFileRef, Set[ClassLike]] = + scala.collection.mutable.Map.empty + + def usedNames = usedNamesAndScopes.mapValues(_.map(_.name)) + + override def startSource(source: VirtualFile): Unit = { + assert( + !apis.contains(source), + s"The startSource can be called only once per source file: $source" + ) + apis(source) = Set.empty + } + + def classDependency( + onClassName: String, + sourceClassName: String, + context: DependencyContext + ): Unit = { + if (onClassName != sourceClassName) + classDependencies += ((onClassName, sourceClassName, context)) + () + } + + override def binaryDependency( + onBinary: Path, + onBinaryClassName: String, + fromClassName: String, + fromSourceFile: VirtualFileRef, + context: DependencyContext + ): Unit = { + binaryDependencies += ((onBinary, onBinaryClassName, fromClassName, context)) + () + } + + override def generatedNonLocalClass( + sourceFile: VirtualFileRef, + classFile: Path, + binaryClassName: String, + srcClassName: String + ): Unit = { + productClassesToSources += ((classFile, sourceFile)) + classNames(sourceFile) += ((srcClassName, binaryClassName)) + () + } + + override def generatedLocalClass( + sourceFile: VirtualFileRef, + classFile: Path + ): Unit = { + productClassesToSources += ((classFile, sourceFile)) + () + } + + def usedName(className: String, name: String, scopes: util.EnumSet[UseScope]): Unit = + usedNamesAndScopes(className) += TestUsedName(name, scopes) + + override def api(source: VirtualFileRef, api: ClassLike): Unit = { + apis(source) += api + () + } + + override def mainClass(source: VirtualFileRef, className: String): Unit = () + + override def enabled(): Boolean = true + + def problem( + category: String, + pos: xsbti.Position, + message: String, + severity: xsbti.Severity, + reported: Boolean + ): Unit = () + + override def dependencyPhaseCompleted(): Unit = {} + + override def apiPhaseCompleted(): Unit = {} + + override def classesInOutputJar(): util.Set[String] = java.util.Collections.emptySet() +} + +object TestCallback { + case class ExtractedClassDependencies( + memberRef: Map[String, Set[String]], + inheritance: Map[String, Set[String]], + localInheritance: Map[String, Set[String]] + ) + object ExtractedClassDependencies { + def fromPairs( + memberRefPairs: Seq[(String, String)], + inheritancePairs: Seq[(String, String)], + localInheritancePairs: Seq[(String, String)] + ): ExtractedClassDependencies = { + ExtractedClassDependencies( + pairsToMultiMap(memberRefPairs), + pairsToMultiMap(inheritancePairs), + pairsToMultiMap(localInheritancePairs) + ) + } + + private def pairsToMultiMap[A, B](pairs: Seq[(A, B)]): Map[A, Set[B]] = { + import scala.collection.mutable.{ HashMap, MultiMap } + val emptyMultiMap = new HashMap[A, scala.collection.mutable.Set[B]] with MultiMap[A, B] + val multiMap = pairs.foldLeft(emptyMultiMap) { + case (acc, (key, value)) => + acc.addBinding(key, value) + } + // convert all collections to immutable variants + multiMap.toMap.mapValues(_.toSet).toMap // .withDefaultValue(Set.empty) + } + } +} diff --git a/dummy-bridge/src/test/scala/testpkg/TestConsoleLogger.scala b/dummy-bridge/src/test/scala/testpkg/TestConsoleLogger.scala new file mode 100644 index 0000000..7babec3 --- /dev/null +++ b/dummy-bridge/src/test/scala/testpkg/TestConsoleLogger.scala @@ -0,0 +1,28 @@ +package testpkg + +import java.util.function.Supplier + +private[testpkg] object TestConsoleLogger { + def apply() = new TestConsoleLogger() +} + +// Remove dependencies to log4j to avoid mixup. +private[testpkg] class TestConsoleLogger extends xsbti.Logger { + final def debug(message: => String): Unit = + System.out.println(s"[debug] $message") + final def info(message: => String): Unit = + System.out.println(s"[info] $message") + final def warn(message: => String): Unit = + System.out.println(s"[warn] $message") + final def error(message: => String): Unit = + System.out.println(s"[error] $message") + + def trace(t: => Throwable): Unit = { + System.out.println(t.toString()) + } + def debug(msg: Supplier[String]): Unit = debug(msg.get) + def warn(msg: Supplier[String]): Unit = info(msg.get) + def info(msg: Supplier[String]): Unit = warn(msg.get) + def error(msg: Supplier[String]): Unit = error(msg.get) + def trace(msg: Supplier[Throwable]): Unit = trace(msg.get) +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala new file mode 100644 index 0000000..3fd2194 --- /dev/null +++ b/project/Dependencies.scala @@ -0,0 +1,7 @@ +import sbt._ + +object Dependencies { + val scalatest = "org.scalatest" %% "scalatest" % "3.0.8" + val verify = "com.eed3si9n.verify" %% "verify" % "0.2.0" + val sbtIo = "org.scala-sbt" %% "io" % "1.4.0-M2" +} diff --git a/project/plugins.sbt b/project/plugins.sbt index c963560..75f7636 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,3 +2,4 @@ addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.4.1") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.6.4") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.4.0") addSbtPlugin("com.dwijnand" % "sbt-dynver" % "3.3.0") +addSbtPlugin("com.swoval" % "sbt-jvm-format" % "0.3.1")