mirror of https://github.com/sbt/sbt.git
Merge branch '0.13' into fix-1968
This commit is contained in:
commit
a8d644eb45
|
|
@ -89,10 +89,8 @@ object ClassToAPI {
|
|||
}
|
||||
|
||||
/** TODO: over time, ClassToAPI should switch the majority of access to the classfile parser */
|
||||
private[this] def classFileForClass(c: Class[_]): ClassFile = {
|
||||
val file = new java.io.File(IO.classLocationFile(c), s"${c.getName.replace('.', '/')}.class")
|
||||
classfile.Parser.apply(file)
|
||||
}
|
||||
private[this] def classFileForClass(c: Class[_]): ClassFile =
|
||||
classfile.Parser.apply(IO.classfileLocation(c))
|
||||
|
||||
private[this] def lzyS[T <: AnyRef](t: T): xsbti.api.Lazy[T] = lzy(t)
|
||||
def lzy[T <: AnyRef](t: => T): xsbti.api.Lazy[T] = xsbti.SafeLazy(t)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ import javax.tools.Diagnostic.NOPOS
|
|||
final class DiagnosticsReporter(reporter: Reporter) extends DiagnosticListener[JavaFileObject] {
|
||||
val END_OF_LINE_MATCHER = "(\r\n)|[\r]|[\n]"
|
||||
val EOL = System.getProperty("line.separator")
|
||||
|
||||
private[this] var errorEncountered = false
|
||||
def hasErrors: Boolean = errorEncountered
|
||||
|
||||
private def fixedDiagnosticMessage(d: Diagnostic[_ <: JavaFileObject]): String = {
|
||||
def getRawMessage = d.getMessage(null)
|
||||
def fixWarnOrErrorMessage = {
|
||||
|
|
@ -110,6 +114,7 @@ final class DiagnosticsReporter(reporter: Reporter) extends DiagnosticListener[J
|
|||
if (sourceUri.isDefined) s"${sourceUri.get}:${if (line.isDefined) line.get else -1}"
|
||||
else ""
|
||||
}
|
||||
if (severity == Severity.Error) errorEncountered = true
|
||||
reporter.log(pos, msg, severity)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ final class LocalJavaCompiler(compiler: javax.tools.JavaCompiler) extends JavaCo
|
|||
log.warn("Javac is running in 'local' mode. These flags have been removed:")
|
||||
log.warn(invalidOptions.mkString("\t", ", ", ""))
|
||||
}
|
||||
compiler.getTask(logWriter, fileManager, diagnostics, cleanedOptions.asJava, null, jfiles).call()
|
||||
val success = compiler.getTask(logWriter, fileManager, diagnostics, cleanedOptions.asJava, null, jfiles).call()
|
||||
|
||||
// The local compiler may report a successful compilation even though there are errors (e.g. encoding problems in the
|
||||
// source files). In a similar situation, command line javac reports a failed compilation. To have the local java compiler
|
||||
// stick to javac's behavior, we report a failed compilation if there have been errors.
|
||||
success && !diagnostics.hasErrors
|
||||
}
|
||||
}
|
||||
|
|
@ -536,14 +536,18 @@ private[sbt] object IvySbt {
|
|||
{
|
||||
import IvyRetrieve.toModuleID
|
||||
val dds = moduleID.getDependencies
|
||||
inconsistentDuplicateWarning(dds map { dd => toModuleID(dd.getDependencyRevisionId) })
|
||||
val deps = dds flatMap { dd =>
|
||||
val module = toModuleID(dd.getDependencyRevisionId)
|
||||
dd.getModuleConfigurations map (c => module.copy(configurations = Some(c)))
|
||||
}
|
||||
inconsistentDuplicateWarning(deps)
|
||||
}
|
||||
|
||||
def inconsistentDuplicateWarning(dependencies: Seq[ModuleID]): List[String] =
|
||||
{
|
||||
val warningHeader = "Multiple dependencies with the same organization/name but different versions. To avoid conflict, pick one version:"
|
||||
val out: mutable.ListBuffer[String] = mutable.ListBuffer()
|
||||
(dependencies groupBy { dep => (dep.organization, dep.name) }) foreach {
|
||||
(dependencies groupBy { dep => (dep.organization, dep.name, dep.configurations) }) foreach {
|
||||
case (k, vs) if vs.size > 1 =>
|
||||
val v0 = vs.head
|
||||
(vs find { _.revision != v0.revision }) foreach { v =>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class InconsistentDuplicateSpec extends Specification {
|
|||
|
||||
Duplicate with different version should
|
||||
be warned $warn1
|
||||
not be warned if in different configurations $nodupe2
|
||||
|
||||
Duplicate with same version should
|
||||
not be warned $nodupe1
|
||||
|
|
@ -25,4 +26,8 @@ class InconsistentDuplicateSpec extends Specification {
|
|||
|
||||
def nodupe1 =
|
||||
IvySbt.inconsistentDuplicateWarning(Seq(akkaActor230Test, akkaActor230)) must_== Nil
|
||||
|
||||
def nodupe2 =
|
||||
IvySbt.inconsistentDuplicateWarning(Seq(akkaActor214, akkaActor230Test)) must_== Nil
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
[@Duhemm]: http://github.com/Duhemm
|
||||
[1933]: https://github.com/sbt/sbt/issues/1933
|
||||
[2258]: https://github.com/sbt/sbt/pull/2258
|
||||
|
||||
### Fixes with compatibility implications
|
||||
|
||||
### Improvements
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fixes [#1933][1933] duplicate warnings for artifacts in distinct configurations [#2258][2258] by
|
||||
[@Duhemm][@Duhemm]
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
[@Duhemm]: http://github.com/Duhemm
|
||||
[2228]: https://github.com/sbt/sbt/issues/2228
|
||||
[2271]: https://github.com/sbt/sbt/pull/2271
|
||||
|
||||
### Fixes with compatibility implications
|
||||
|
||||
### Improvements
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Report java compilation as failed if the local java compiler reported compilation errors (issue [#2228][2228]) [#2271][2271] by [@Duhemm][@Duhemm]
|
||||
|
|
@ -10,7 +10,6 @@ import java.io.File
|
|||
private[sbt] trait ClassFile {
|
||||
val majorVersion: Int
|
||||
val minorVersion: Int
|
||||
val fileName: String
|
||||
val className: String
|
||||
val superClassName: String
|
||||
val interfaceNames: Array[String]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
package sbt
|
||||
package classfile
|
||||
|
||||
import java.net.URL
|
||||
import java.io.{ DataInputStream, File, InputStream }
|
||||
import scala.annotation.switch
|
||||
|
||||
|
|
@ -15,15 +16,19 @@ import scala.annotation.switch
|
|||
import Constants._
|
||||
|
||||
private[sbt] object Parser {
|
||||
def apply(file: File): ClassFile = Using.fileInputStream(file)(parse(file.getAbsolutePath)).right.get
|
||||
private def parse(fileName: String)(is: InputStream): Either[String, ClassFile] = Right(parseImpl(fileName, is))
|
||||
private def parseImpl(filename: String, is: InputStream): ClassFile =
|
||||
def apply(file: File): ClassFile =
|
||||
Using.fileInputStream(file)(parse(file.toString)).right.get
|
||||
|
||||
def apply(url: URL): ClassFile =
|
||||
Using.urlInputStream(url)(parse(url.toString)).right.get
|
||||
|
||||
private def parse(readableName: String)(is: InputStream): Either[String, ClassFile] = Right(parseImpl(readableName, is))
|
||||
private def parseImpl(readableName: String, is: InputStream): ClassFile =
|
||||
{
|
||||
val in = new DataInputStream(is)
|
||||
new ClassFile {
|
||||
assume(in.readInt() == JavaMagic, "Invalid class file: " + fileName)
|
||||
assume(in.readInt() == JavaMagic, "Invalid class file: " + readableName)
|
||||
|
||||
val fileName = filename
|
||||
new ClassFile {
|
||||
val minorVersion: Int = in.readUnsignedShort()
|
||||
val majorVersion: Int = in.readUnsignedShort()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package sbt
|
||||
package classfile
|
||||
|
||||
import util.Try
|
||||
|
||||
import org.scalacheck._
|
||||
import Prop._
|
||||
|
||||
object ParserSpecification extends Properties("Parser") {
|
||||
property("able to parse all relevant classes") =
|
||||
Prop.forAll(classes) { (c: Class[_]) =>
|
||||
Parser(IO.classfileLocation(c)) ne null
|
||||
}
|
||||
|
||||
implicit def classes: Gen[Class[_]] =
|
||||
Gen.oneOf(
|
||||
this.getClass,
|
||||
classOf[java.lang.Integer],
|
||||
classOf[java.util.AbstractMap.SimpleEntry[String, String]],
|
||||
classOf[String],
|
||||
classOf[Thread],
|
||||
classOf[Properties]
|
||||
)
|
||||
}
|
||||
|
|
@ -36,39 +36,43 @@ object IO {
|
|||
val utf8 = Charset.forName("UTF-8")
|
||||
|
||||
/**
|
||||
* Returns a URL for the directory or jar containing the the class file `cl`.
|
||||
* Returns a URL for the classfile containing the given class
|
||||
* If the location cannot be determined, an error is generated.
|
||||
*/
|
||||
def classLocation(cl: Class[_]): URL = {
|
||||
val codeSource = cl.getProtectionDomain.getCodeSource
|
||||
if (codeSource ne null) {
|
||||
codeSource.getLocation
|
||||
} else {
|
||||
// NB: This assumes that classes without code sources are System classes, and thus located in
|
||||
// jars. It assumes that `urlAsFile` will truncate to the containing jar file.
|
||||
val clsfile = s"${cl.getName.replace('.', '/')}.class"
|
||||
Option(ClassLoader.getSystemClassLoader.getResource(clsfile))
|
||||
.flatMap {
|
||||
urlAsFile
|
||||
}.getOrElse {
|
||||
sys.error("No class location for " + cl)
|
||||
}.toURI.toURL
|
||||
def classfileLocation(cl: Class[_]): URL = {
|
||||
val clsfile = s"${cl.getName.replace('.', '/')}.class"
|
||||
try {
|
||||
Stream(Option(cl.getClassLoader), Some(ClassLoader.getSystemClassLoader)).flatten.flatMap { classLoader =>
|
||||
Option(classLoader.getResource(clsfile))
|
||||
}.headOption.getOrElse {
|
||||
sys.error("No class location for " + cl)
|
||||
}
|
||||
} catch {
|
||||
case e =>
|
||||
e.printStackTrace()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory or jar file containing the the class file `cl`.
|
||||
* If the location cannot be determined or it is not a file, an error is generated.
|
||||
* Returns the directory or jar file containing the class file `cl`.
|
||||
* If the location cannot be determined or if it is not a file, an error is generated.
|
||||
* Note that Java standard library classes typically do not have a location associated with them.
|
||||
*/
|
||||
def classLocationFile(cl: Class[_]): File = toFile(classLocation(cl))
|
||||
def classLocationFile(cl: Class[_]): File = {
|
||||
val classURL =
|
||||
Option(cl.getProtectionDomain.getCodeSource).getOrElse {
|
||||
sys.error("No class location for " + cl)
|
||||
}.getLocation
|
||||
toFile(classURL)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URL for the directory or jar containing the class file for type `T` (as determined by an implicit Manifest).
|
||||
* If the location cannot be determined, an error is generated.
|
||||
* Note that Java standard library classes typically do not have a location associated with them.
|
||||
*/
|
||||
def classLocation[T](implicit mf: SManifest[T]): URL = classLocation(mf.runtimeClass)
|
||||
def classfileLocation[T](implicit mf: SManifest[T]): URL = classfileLocation(mf.runtimeClass)
|
||||
|
||||
/**
|
||||
* Returns the directory or jar file containing the the class file for type `T` (as determined by an implicit Manifest).
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ object IOSpecification extends Properties("IO") {
|
|||
Gen.oneOf(
|
||||
this.getClass,
|
||||
classOf[java.lang.Integer],
|
||||
classOf[java.util.AbstractMap.SimpleEntry[String, String]],
|
||||
classOf[String],
|
||||
classOf[Thread],
|
||||
classOf[Properties]
|
||||
|
|
|
|||
Loading…
Reference in New Issue