mirror of https://github.com/sbt/sbt.git
Merge branch '0.13' of github.com:sbt/sbt into 0.13
This commit is contained in:
commit
2eb8fd1b8a
|
|
@ -84,6 +84,22 @@ Whether implementing a new feature, fixing a bug, or modifying documentation, pl
|
||||||
Binary compatible changes will be backported to a previous series (currently, 0.12.x) at the time of the next stable release.
|
Binary compatible changes will be backported to a previous series (currently, 0.12.x) at the time of the next stable release.
|
||||||
See below for instructions on building sbt from source.
|
See below for instructions on building sbt from source.
|
||||||
|
|
||||||
|
All pull requests are required to include a "Notes" file which documents the change. This file should reside in the
|
||||||
|
directory:
|
||||||
|
|
||||||
|
<sbt root>
|
||||||
|
notes/
|
||||||
|
<target release>/
|
||||||
|
<your-change-name>.md
|
||||||
|
|
||||||
|
Notes files should have the following contents:
|
||||||
|
|
||||||
|
* Bullet item description under one of the following sections:
|
||||||
|
- `### Bug fixes`
|
||||||
|
- `### Improvements`
|
||||||
|
- `### Fixes with compatibility implications`
|
||||||
|
* Complete section describing new features.
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -175,9 +175,67 @@ trait Relations {
|
||||||
* Relation between source files and _unqualified_ term and type names used in given source file.
|
* Relation between source files and _unqualified_ term and type names used in given source file.
|
||||||
*/
|
*/
|
||||||
private[inc] def names: Relation[File, String]
|
private[inc] def names: Relation[File, String]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists of all the pairs (header, relation) that sbt knows of.
|
||||||
|
* Used by TextAnalysisFormat to persist relations.
|
||||||
|
* This cannot be stored as a Map because the order is important.
|
||||||
|
*/
|
||||||
|
private[inc] def allRelations: List[(String, Relation[File, _])]
|
||||||
}
|
}
|
||||||
|
|
||||||
object Relations {
|
object Relations {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents all the relations that sbt knows of along with a way to recreate each
|
||||||
|
* of their elements from their string representation.
|
||||||
|
*/
|
||||||
|
private[inc] val existingRelations = {
|
||||||
|
val string2File: String => File = new File(_)
|
||||||
|
List(
|
||||||
|
("products", string2File),
|
||||||
|
("binary dependencies", string2File),
|
||||||
|
("direct source dependencies", string2File),
|
||||||
|
("direct external dependencies", identity[String] _),
|
||||||
|
("public inherited source dependencies", string2File),
|
||||||
|
("public inherited external dependencies", identity[String] _),
|
||||||
|
("member reference internal dependencies", string2File),
|
||||||
|
("member reference external dependencies", identity[String] _),
|
||||||
|
("inheritance internal dependencies", string2File),
|
||||||
|
("inheritance external dependencies", identity[String] _),
|
||||||
|
("class names", identity[String] _),
|
||||||
|
("used names", identity[String] _))
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Reconstructs a Relations from a list of Relation
|
||||||
|
* The order in which the relations are read matters and is defined by `existingRelations`.
|
||||||
|
*/
|
||||||
|
def construct(nameHashing: Boolean, relations: List[Relation[_, _]]) =
|
||||||
|
relations match {
|
||||||
|
case p :: bin :: di :: de :: pii :: pie :: mri :: mre :: ii :: ie :: cn :: un :: Nil =>
|
||||||
|
val srcProd = p.asInstanceOf[Relation[File, File]]
|
||||||
|
val binaryDep = bin.asInstanceOf[Relation[File, File]]
|
||||||
|
val directSrcDeps = makeSource(di.asInstanceOf[Relation[File, File]], de.asInstanceOf[Relation[File, String]])
|
||||||
|
val publicInheritedSrcDeps = makeSource(pii.asInstanceOf[Relation[File, File]], pie.asInstanceOf[Relation[File, String]])
|
||||||
|
val memberRefSrcDeps = makeSourceDependencies(mri.asInstanceOf[Relation[File, File]], mre.asInstanceOf[Relation[File, String]])
|
||||||
|
val inheritanceSrcDeps = makeSourceDependencies(ii.asInstanceOf[Relation[File, File]], ie.asInstanceOf[Relation[File, String]])
|
||||||
|
val classes = cn.asInstanceOf[Relation[File, String]]
|
||||||
|
val names = un.asInstanceOf[Relation[File, String]]
|
||||||
|
|
||||||
|
// we don't check for emptiness of publicInherited/inheritance relations because
|
||||||
|
// we assume that invariant that says they are subsets of direct/memberRef holds
|
||||||
|
assert(nameHashing || (memberRefSrcDeps == emptySourceDependencies), "When name hashing is disabled the `memberRef` relation should be empty.")
|
||||||
|
assert(!nameHashing || (directSrcDeps == emptySource), "When name hashing is enabled the `direct` relation should be empty.")
|
||||||
|
|
||||||
|
if (nameHashing)
|
||||||
|
Relations.make(srcProd, binaryDep, memberRefSrcDeps, inheritanceSrcDeps, classes, names)
|
||||||
|
else {
|
||||||
|
assert(names.all.isEmpty, s"When `nameHashing` is disabled `names` relation should be empty: $names")
|
||||||
|
Relations.make(srcProd, binaryDep, directSrcDeps, publicInheritedSrcDeps, classes)
|
||||||
|
}
|
||||||
|
case _ => throw new java.io.IOException(s"Expected to read ${existingRelations.length} relations but read ${relations.length}.")
|
||||||
|
}
|
||||||
|
|
||||||
/** Tracks internal and external source dependencies for a specific dependency type, such as direct or inherited.*/
|
/** Tracks internal and external source dependencies for a specific dependency type, such as direct or inherited.*/
|
||||||
final class Source private[sbt] (val internal: Relation[File, File], val external: Relation[File, String]) {
|
final class Source private[sbt] (val internal: Relation[File, File], val external: Relation[File, String]) {
|
||||||
def addInternal(source: File, dependsOn: Iterable[File]): Source = new Source(internal + (source, dependsOn), external)
|
def addInternal(source: File, dependsOn: Iterable[File]): Source = new Source(internal + (source, dependsOn), external)
|
||||||
|
|
@ -403,6 +461,23 @@ private class MRelationsDefaultImpl(srcProd: Relation[File, File], binaryDep: Re
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def allRelations = {
|
||||||
|
val rels = List(
|
||||||
|
srcProd,
|
||||||
|
binaryDep,
|
||||||
|
direct.internal,
|
||||||
|
direct.external,
|
||||||
|
publicInherited.internal,
|
||||||
|
publicInherited.external,
|
||||||
|
Relations.emptySourceDependencies.internal, // Default implementation doesn't provide memberRef source deps
|
||||||
|
Relations.emptySourceDependencies.external, // Default implementation doesn't provide memberRef source deps
|
||||||
|
Relations.emptySourceDependencies.internal, // Default implementation doesn't provide inheritance source deps
|
||||||
|
Relations.emptySourceDependencies.external, // Default implementation doesn't provide inheritance source deps
|
||||||
|
classes,
|
||||||
|
Relation.empty[File, String]) // Default implementation doesn't provide used names relation
|
||||||
|
Relations.existingRelations map (_._1) zip rels
|
||||||
|
}
|
||||||
|
|
||||||
override def hashCode = (srcProd :: binaryDep :: direct :: publicInherited :: classes :: Nil).hashCode
|
override def hashCode = (srcProd :: binaryDep :: direct :: publicInherited :: classes :: Nil).hashCode
|
||||||
|
|
||||||
override def toString = (
|
override def toString = (
|
||||||
|
|
@ -490,6 +565,23 @@ private class MRelationsNameHashing(srcProd: Relation[File, File], binaryDep: Re
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def allRelations = {
|
||||||
|
val rels = List(
|
||||||
|
srcProd,
|
||||||
|
binaryDep,
|
||||||
|
Relations.emptySource.internal, // NameHashing doesn't provide direct dependencies
|
||||||
|
Relations.emptySource.external, // NameHashing doesn't provide direct dependencies
|
||||||
|
Relations.emptySource.internal, // NameHashing doesn't provide public inherited dependencies
|
||||||
|
Relations.emptySource.external, // NameHashing doesn't provide public inherited dependencies
|
||||||
|
memberRef.internal,
|
||||||
|
memberRef.external,
|
||||||
|
inheritance.internal,
|
||||||
|
inheritance.external,
|
||||||
|
classes,
|
||||||
|
names)
|
||||||
|
Relations.existingRelations map (_._1) zip rels
|
||||||
|
}
|
||||||
|
|
||||||
override def hashCode = (srcProd :: binaryDep :: memberRef :: inheritance :: classes :: Nil).hashCode
|
override def hashCode = (srcProd :: binaryDep :: memberRef :: inheritance :: classes :: Nil).hashCode
|
||||||
|
|
||||||
override def toString = (
|
override def toString = (
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,9 @@ object AnalysisTest extends Properties("Analysis") {
|
||||||
// Merge and split large, generated examples.
|
// Merge and split large, generated examples.
|
||||||
// Mustn't shrink, as the default Shrink[Int] doesn't respect the lower bound of choose(), which will cause
|
// Mustn't shrink, as the default Shrink[Int] doesn't respect the lower bound of choose(), which will cause
|
||||||
// a divide-by-zero error masking the original error.
|
// a divide-by-zero error masking the original error.
|
||||||
property("Complex Merge and Split") = forAllNoShrink(genAnalysis, choose(1, 10)) { (analysis: Analysis, numSplits: Int) =>
|
// Note that the generated Analyses have nameHashing = false (Grouping of Analyses with name hashing enabled
|
||||||
|
// is not supported right now)
|
||||||
|
property("Complex Merge and Split") = forAllNoShrink(genAnalysis(nameHashing = false), choose(1, 10)) { (analysis: Analysis, numSplits: Int) =>
|
||||||
val grouped: Map[Int, Analysis] = analysis.groupBy({ f: File => abs(f.hashCode()) % numSplits })
|
val grouped: Map[Int, Analysis] = analysis.groupBy({ f: File => abs(f.hashCode()) % numSplits })
|
||||||
def getGroup(i: Int): Analysis = grouped.getOrElse(i, Analysis.empty(false))
|
def getGroup(i: Int): Analysis = grouped.getOrElse(i, Analysis.empty(false))
|
||||||
val splits = (Range(0, numSplits) map getGroup).toList
|
val splits = (Range(0, numSplits) map getGroup).toList
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,18 @@ object TestCaseGenerators {
|
||||||
|
|
||||||
// Ensure that we generate unique class names and file paths every time.
|
// Ensure that we generate unique class names and file paths every time.
|
||||||
// Using repeated strings may lead to all sorts of undesirable interactions.
|
// Using repeated strings may lead to all sorts of undesirable interactions.
|
||||||
val used = scala.collection.mutable.Set.empty[String]
|
val used1 = scala.collection.mutable.Set.empty[String]
|
||||||
|
val used2 = scala.collection.mutable.Set.empty[String]
|
||||||
|
|
||||||
def unique[T](g: Gen[T]) = g retryUntil { o: T => used.add(o.toString) }
|
// When using `retryUntil`, the condition is actually tested twice (see implementation in ScalaCheck),
|
||||||
|
// which is why we need to insert twice the element.
|
||||||
|
// If the element is present in both sets, then it has already been used.
|
||||||
|
def unique[T](g: Gen[T]) = g retryUntil { o: T =>
|
||||||
|
if (used1.add(o.toString))
|
||||||
|
true
|
||||||
|
else
|
||||||
|
used2.add(o.toString)
|
||||||
|
}
|
||||||
|
|
||||||
def identifier: Gen[String] = sized { size =>
|
def identifier: Gen[String] = sized { size =>
|
||||||
resize(Math.max(size, 3), Gen.identifier)
|
resize(Math.max(size, 3), Gen.identifier)
|
||||||
|
|
@ -134,6 +143,18 @@ object TestCaseGenerators {
|
||||||
external <- someOf(src.external.all.toList)
|
external <- someOf(src.external.all.toList)
|
||||||
} yield Relations.makeSource(Relation.empty ++ internal, Relation.empty ++ external)
|
} yield Relations.makeSource(Relation.empty ++ internal, Relation.empty ++ external)
|
||||||
|
|
||||||
|
def genRSourceDependencies(srcs: List[File]): Gen[Relations.SourceDependencies] = for {
|
||||||
|
internal <- listOfN(srcs.length, someOf(srcs))
|
||||||
|
external <- genStringRelation(srcs)
|
||||||
|
} yield Relations.makeSourceDependencies(
|
||||||
|
Relation.reconstruct((srcs zip (internal map { _.toSet }) map { case (a, b) => (a, b - a) }).toMap),
|
||||||
|
external)
|
||||||
|
|
||||||
|
def genSubRSourceDependencies(src: Relations.SourceDependencies): Gen[Relations.SourceDependencies] = for {
|
||||||
|
internal <- someOf(src.internal.all.toList)
|
||||||
|
external <- someOf(src.external.all.toList)
|
||||||
|
} yield Relations.makeSourceDependencies(Relation.empty ++ internal, Relation.empty ++ external)
|
||||||
|
|
||||||
def genRelations: Gen[Relations] = for {
|
def genRelations: Gen[Relations] = for {
|
||||||
numSrcs <- choose(0, maxSources)
|
numSrcs <- choose(0, maxSources)
|
||||||
srcs <- listOfN(numSrcs, genFile)
|
srcs <- listOfN(numSrcs, genFile)
|
||||||
|
|
@ -145,8 +166,19 @@ object TestCaseGenerators {
|
||||||
|
|
||||||
} yield Relations.make(srcProd, binaryDep, direct, publicInherited, classes)
|
} yield Relations.make(srcProd, binaryDep, direct, publicInherited, classes)
|
||||||
|
|
||||||
def genAnalysis: Gen[Analysis] = for {
|
def genRelationsNameHashing: Gen[Relations] = for {
|
||||||
rels <- genRelations
|
numSrcs <- choose(0, maxSources)
|
||||||
|
srcs <- listOfN(numSrcs, genFile)
|
||||||
|
srcProd <- genFileRelation(srcs)
|
||||||
|
binaryDep <- genFileRelation(srcs)
|
||||||
|
memberRef <- genRSourceDependencies(srcs)
|
||||||
|
inheritance <- genSubRSourceDependencies(memberRef)
|
||||||
|
classes <- genStringRelation(srcs)
|
||||||
|
names <- genStringRelation(srcs)
|
||||||
|
} yield Relations.make(srcProd, binaryDep, memberRef, inheritance, classes, names)
|
||||||
|
|
||||||
|
def genAnalysis(nameHashing: Boolean): Gen[Analysis] = for {
|
||||||
|
rels <- if (nameHashing) genRelationsNameHashing else genRelations
|
||||||
stamps <- genStamps(rels)
|
stamps <- genStamps(rels)
|
||||||
apis <- genAPIs(rels)
|
apis <- genAPIs(rels)
|
||||||
} yield new MAnalysis(stamps, apis, rels, SourceInfos.empty, Compilations.empty)
|
} yield new MAnalysis(stamps, apis, rels, SourceInfos.empty, Compilations.empty)
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,16 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ExtractDependenciesByMemberRefTraverser extends ExtractDependenciesTraverser {
|
private class ExtractDependenciesByMemberRefTraverser extends ExtractDependenciesTraverser {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = collection.mutable.Set.empty[Tree]
|
||||||
|
|
||||||
override def traverse(tree: Tree): Unit = {
|
override def traverse(tree: Tree): Unit = {
|
||||||
tree match {
|
tree match {
|
||||||
case Import(expr, selectors) =>
|
case Import(expr, selectors) =>
|
||||||
|
|
@ -118,13 +128,13 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
||||||
case select: Select =>
|
case select: Select =>
|
||||||
addDependency(select.symbol)
|
addDependency(select.symbol)
|
||||||
/*
|
/*
|
||||||
* Idents are used in number of situations:
|
* Idents are used in number of situations:
|
||||||
* - to refer to local variable
|
* - to refer to local variable
|
||||||
* - to refer to a top-level package (other packages are nested selections)
|
* - 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;
|
* - to refer to a term defined in the same package as an enclosing class;
|
||||||
* this looks fishy, see this thread:
|
* this looks fishy, see this thread:
|
||||||
* https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion
|
* https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion
|
||||||
*/
|
*/
|
||||||
case ident: Ident =>
|
case ident: Ident =>
|
||||||
addDependency(ident.symbol)
|
addDependency(ident.symbol)
|
||||||
case typeTree: TypeTree =>
|
case typeTree: TypeTree =>
|
||||||
|
|
@ -136,13 +146,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
||||||
deps.foreach(addDependency)
|
deps.foreach(addDependency)
|
||||||
case Template(parents, self, body) =>
|
case Template(parents, self, body) =>
|
||||||
traverseTrees(body)
|
traverseTrees(body)
|
||||||
/*
|
case MacroExpansionOf(original) if inspectedOriginalTrees.add(original) =>
|
||||||
* Some macros appear to contain themselves as original tree
|
|
||||||
* In this case, we don't need to inspect the original tree because
|
|
||||||
* we already inspected its expansion, which is equal.
|
|
||||||
* See https://issues.scala-lang.org/browse/SI-8486
|
|
||||||
*/
|
|
||||||
case MacroExpansionOf(original) if original != tree =>
|
|
||||||
this.traverse(original)
|
this.traverse(original)
|
||||||
case other => ()
|
case other => ()
|
||||||
}
|
}
|
||||||
|
|
@ -191,4 +195,4 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
||||||
// for Scala 2.8 and 2.9 this method is provided through SymbolCompat
|
// for Scala 2.8 and 2.9 this method is provided through SymbolCompat
|
||||||
sym.enclosingTopLevelClass
|
sym.enclosingTopLevelClass
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +122,13 @@ object TextAnalysisFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
def write(out: Writer, relations: Relations) {
|
def write(out: Writer, relations: Relations) {
|
||||||
def writeRelation[T](header: String, rel: Relation[File, T])(implicit ord: Ordering[T]) {
|
// This ordering is used to persist all values in order. Since all values will be
|
||||||
|
// persisted using their string representation, it makes sense to sort them using
|
||||||
|
// their string representation.
|
||||||
|
val toStringOrd = new Ordering[Any] {
|
||||||
|
def compare(a: Any, b: Any) = a.toString compare b.toString
|
||||||
|
}
|
||||||
|
def writeRelation[T](header: String, rel: Relation[File, T]) {
|
||||||
writeHeader(out, header)
|
writeHeader(out, header)
|
||||||
writeSize(out, rel.size)
|
writeSize(out, rel.size)
|
||||||
// We sort for ease of debugging and for more efficient reconstruction when reading.
|
// We sort for ease of debugging and for more efficient reconstruction when reading.
|
||||||
|
|
@ -131,38 +137,15 @@ object TextAnalysisFormat {
|
||||||
rel.forwardMap.toSeq.sortBy(_._1).foreach {
|
rel.forwardMap.toSeq.sortBy(_._1).foreach {
|
||||||
case (k, vs) =>
|
case (k, vs) =>
|
||||||
val kStr = k.toString
|
val kStr = k.toString
|
||||||
vs.toSeq.sorted foreach { v =>
|
vs.toSeq.sorted(toStringOrd) foreach { v =>
|
||||||
out.write(kStr); out.write(" -> "); out.write(v.toString); out.write("\n")
|
out.write(kStr); out.write(" -> "); out.write(v.toString); out.write("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val nameHashing = relations.nameHashing
|
relations.allRelations.foreach {
|
||||||
writeRelation(Headers.srcProd, relations.srcProd)
|
case (header, rel) => writeRelation(header, rel)
|
||||||
writeRelation(Headers.binaryDep, relations.binaryDep)
|
}
|
||||||
|
|
||||||
val direct = if (nameHashing) Relations.emptySource else relations.direct
|
|
||||||
val publicInherited = if (nameHashing)
|
|
||||||
Relations.emptySource else relations.publicInherited
|
|
||||||
|
|
||||||
val memberRef = if (nameHashing)
|
|
||||||
relations.memberRef else Relations.emptySourceDependencies
|
|
||||||
val inheritance = if (nameHashing)
|
|
||||||
relations.inheritance else Relations.emptySourceDependencies
|
|
||||||
val names = if (nameHashing) relations.names else Relation.empty[File, String]
|
|
||||||
|
|
||||||
writeRelation(Headers.directSrcDep, direct.internal)
|
|
||||||
writeRelation(Headers.directExternalDep, direct.external)
|
|
||||||
writeRelation(Headers.internalSrcDepPI, publicInherited.internal)
|
|
||||||
writeRelation(Headers.externalDepPI, publicInherited.external)
|
|
||||||
|
|
||||||
writeRelation(Headers.memberRefInternalDep, memberRef.internal)
|
|
||||||
writeRelation(Headers.memberRefExternalDep, memberRef.external)
|
|
||||||
writeRelation(Headers.inheritanceInternalDep, inheritance.internal)
|
|
||||||
writeRelation(Headers.inheritanceExternalDep, inheritance.external)
|
|
||||||
|
|
||||||
writeRelation(Headers.classes, relations.classes)
|
|
||||||
writeRelation(Headers.usedNames, names)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def read(in: BufferedReader, nameHashing: Boolean): Relations = {
|
def read(in: BufferedReader, nameHashing: Boolean): Relations = {
|
||||||
|
|
@ -186,56 +169,9 @@ object TextAnalysisFormat {
|
||||||
Relation.reconstruct(forward.toMap)
|
Relation.reconstruct(forward.toMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
def readFileRelation(expectedHeader: String) = readRelation(expectedHeader, { new File(_) })
|
val relations = Relations.existingRelations map { case (header, fun) => readRelation(header, fun) }
|
||||||
def readStringRelation(expectedHeader: String) = readRelation(expectedHeader, identity[String])
|
|
||||||
|
|
||||||
val srcProd = readFileRelation(Headers.srcProd)
|
Relations.construct(nameHashing, relations)
|
||||||
val binaryDep = readFileRelation(Headers.binaryDep)
|
|
||||||
|
|
||||||
import sbt.inc.Relations.{
|
|
||||||
Source,
|
|
||||||
SourceDependencies,
|
|
||||||
makeSourceDependencies,
|
|
||||||
emptySource,
|
|
||||||
makeSource,
|
|
||||||
emptySourceDependencies
|
|
||||||
}
|
|
||||||
val directSrcDeps: Source = {
|
|
||||||
val internalSrcDep = readFileRelation(Headers.directSrcDep)
|
|
||||||
val externalDep = readStringRelation(Headers.directExternalDep)
|
|
||||||
makeSource(internalSrcDep, externalDep)
|
|
||||||
}
|
|
||||||
val publicInheritedSrcDeps: Source = {
|
|
||||||
val internalSrcDepPI = readFileRelation(Headers.internalSrcDepPI)
|
|
||||||
val externalDepPI = readStringRelation(Headers.externalDepPI)
|
|
||||||
makeSource(internalSrcDepPI, externalDepPI)
|
|
||||||
}
|
|
||||||
val memberRefSrcDeps: SourceDependencies = {
|
|
||||||
val internalMemberRefDep = readFileRelation(Headers.memberRefInternalDep)
|
|
||||||
val externalMemberRefDep = readStringRelation(Headers.memberRefExternalDep)
|
|
||||||
makeSourceDependencies(internalMemberRefDep, externalMemberRefDep)
|
|
||||||
}
|
|
||||||
val inheritanceSrcDeps: SourceDependencies = {
|
|
||||||
val internalInheritanceDep = readFileRelation(Headers.inheritanceInternalDep)
|
|
||||||
val externalInheritanceDep = readStringRelation(Headers.inheritanceExternalDep)
|
|
||||||
makeSourceDependencies(internalInheritanceDep, externalInheritanceDep)
|
|
||||||
}
|
|
||||||
// we don't check for emptiness of publicInherited/inheritance relations because
|
|
||||||
// we assume that invariant that says they are subsets of direct/memberRef holds
|
|
||||||
assert(nameHashing || (memberRefSrcDeps == emptySourceDependencies),
|
|
||||||
"When name hashing is disabled the `memberRef` relation should be empty.")
|
|
||||||
assert(!nameHashing || (directSrcDeps == emptySource),
|
|
||||||
"When name hashing is enabled the `direct` relation should be empty.")
|
|
||||||
val classes = readStringRelation(Headers.classes)
|
|
||||||
val names = readStringRelation(Headers.usedNames)
|
|
||||||
|
|
||||||
if (nameHashing)
|
|
||||||
Relations.make(srcProd, binaryDep, memberRefSrcDeps, inheritanceSrcDeps, classes, names)
|
|
||||||
else {
|
|
||||||
assert(names.all.isEmpty, "When `nameHashing` is disabled `names` relation " +
|
|
||||||
s"should be empty: $names")
|
|
||||||
Relations.make(srcProd, binaryDep, directSrcDeps, publicInheritedSrcDeps, classes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
package sbt
|
||||||
|
package inc
|
||||||
|
|
||||||
|
import java.io.{ BufferedReader, File, StringReader, StringWriter }
|
||||||
|
import scala.math.abs
|
||||||
|
import org.scalacheck._
|
||||||
|
import Gen._
|
||||||
|
import Prop._
|
||||||
|
|
||||||
|
object TextAnalysisFormatTest extends Properties("TextAnalysisFormat") {
|
||||||
|
|
||||||
|
val nameHashing = true
|
||||||
|
val dummyOutput = new xsbti.compile.SingleOutput { def outputDirectory: java.io.File = new java.io.File("dummy") }
|
||||||
|
val commonSetup = new CompileSetup(dummyOutput, new CompileOptions(Nil, Nil), "2.10.4", xsbti.compile.CompileOrder.Mixed, nameHashing)
|
||||||
|
val commonHeader = """format version: 5
|
||||||
|
|output mode:
|
||||||
|
|1 items
|
||||||
|
|0 -> single
|
||||||
|
|output directories:
|
||||||
|
|1 items
|
||||||
|
|output dir -> dummy
|
||||||
|
|compile options:
|
||||||
|
|0 items
|
||||||
|
|javac options:
|
||||||
|
|0 items
|
||||||
|
|compiler version:
|
||||||
|
|1 items
|
||||||
|
|0 -> 2.10.4
|
||||||
|
|compile order:
|
||||||
|
|1 items
|
||||||
|
|0 -> Mixed
|
||||||
|
|name hashing:
|
||||||
|
|1 items
|
||||||
|
|0 -> true""".stripMargin
|
||||||
|
|
||||||
|
property("Write and read empty Analysis") = {
|
||||||
|
|
||||||
|
val writer = new StringWriter
|
||||||
|
val analysis = Analysis.empty(nameHashing)
|
||||||
|
TextAnalysisFormat.write(writer, analysis, commonSetup)
|
||||||
|
|
||||||
|
val result = writer.toString
|
||||||
|
|
||||||
|
result.startsWith(commonHeader)
|
||||||
|
val reader = new BufferedReader(new StringReader(result))
|
||||||
|
|
||||||
|
val (readAnalysis, readSetup) = TextAnalysisFormat.read(reader)
|
||||||
|
|
||||||
|
analysis == readAnalysis
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
property("Write and read simple Analysis") = {
|
||||||
|
|
||||||
|
import TestCaseGenerators._
|
||||||
|
|
||||||
|
def f(s: String) = new File(s)
|
||||||
|
val aScala = f("A.scala")
|
||||||
|
val bScala = f("B.scala")
|
||||||
|
val aSource = genSource("A" :: "A$" :: Nil).sample.get
|
||||||
|
val bSource = genSource("B" :: "B$" :: Nil).sample.get
|
||||||
|
val cSource = genSource("C" :: Nil).sample.get
|
||||||
|
val exists = new Exists(true)
|
||||||
|
val sourceInfos = SourceInfos.makeInfo(Nil, Nil)
|
||||||
|
|
||||||
|
var analysis = Analysis.empty(nameHashing)
|
||||||
|
analysis = analysis.addProduct(aScala, f("A.class"), exists, "A")
|
||||||
|
analysis = analysis.addProduct(aScala, f("A$.class"), exists, "A$")
|
||||||
|
analysis = analysis.addSource(aScala, aSource, exists, Nil, Nil, sourceInfos)
|
||||||
|
analysis = analysis.addBinaryDep(aScala, f("x.jar"), "x", exists)
|
||||||
|
analysis = analysis.addExternalDep(aScala, "C", cSource, inherited = false)
|
||||||
|
|
||||||
|
val writer = new StringWriter
|
||||||
|
|
||||||
|
TextAnalysisFormat.write(writer, analysis, commonSetup)
|
||||||
|
|
||||||
|
val result = writer.toString
|
||||||
|
|
||||||
|
result.startsWith(commonHeader)
|
||||||
|
val reader = new BufferedReader(new StringReader(result))
|
||||||
|
|
||||||
|
val (readAnalysis, readSetup) = TextAnalysisFormat.read(reader)
|
||||||
|
|
||||||
|
compare(analysis, readAnalysis)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
property("Write and read complex Analysis") = forAllNoShrink(TestCaseGenerators.genAnalysis(nameHashing)) { analysis: Analysis =>
|
||||||
|
val writer = new StringWriter
|
||||||
|
|
||||||
|
TextAnalysisFormat.write(writer, analysis, commonSetup)
|
||||||
|
|
||||||
|
val result = writer.toString
|
||||||
|
|
||||||
|
result.startsWith(commonHeader)
|
||||||
|
val reader = new BufferedReader(new StringReader(result))
|
||||||
|
|
||||||
|
val (readAnalysis, readSetup) = TextAnalysisFormat.read(reader)
|
||||||
|
|
||||||
|
compare(analysis, readAnalysis)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two analyses with useful labelling when they aren't equal.
|
||||||
|
private[this] def compare(left: Analysis, right: Analysis): Prop =
|
||||||
|
s" LEFT: $left" |:
|
||||||
|
s"RIGHT: $right" |:
|
||||||
|
s"STAMPS EQUAL: ${left.stamps == right.stamps}" |:
|
||||||
|
s"APIS EQUAL: ${left.apis == right.apis}" |:
|
||||||
|
s"RELATIONS EQUAL: ${left.relations == right.relations}" |:
|
||||||
|
"UNEQUAL" |:
|
||||||
|
(left == right)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
package org.apache.ivy.plugins.parser.m2
|
||||||
|
|
||||||
|
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It turns out there was a very subtle, and evil, issue sitting the Ivy/maven configuration, and it
|
||||||
|
* related to dependency mapping. A mapping of `foo->bar(*)` means that the local configuration
|
||||||
|
* `foo` depends on the remote configuration `bar`, if it exists, or *ALL CONFIGURATIONS* if `bar`
|
||||||
|
* does not exist. Since the default Ivy configuration mapping was using the random `master`
|
||||||
|
* configuration, which AFAICT is NEVER specified, just an assumed default, this would cause leaks
|
||||||
|
* between maven + ivy projects.
|
||||||
|
*
|
||||||
|
* i.e. if a maven POM depends on a module denoted by an ivy.xml file, then you'd wind up accidentally
|
||||||
|
* bleeding ALL the ivy module's configurations into the maven module's configurations.
|
||||||
|
*
|
||||||
|
* This fix works around the issue, by assuming that if there is no `master` configuration, than the
|
||||||
|
* maven default of `compile` is intended. As sbt forces generated `ivy.xml` files to abide by
|
||||||
|
* maven conventions, this works in all of our test cases. The only scenario where it wouldn't work
|
||||||
|
* is those who have custom ivy.xml files *and* have pom.xml files which rely on those custom ivy.xml files,
|
||||||
|
* a very unlikely situation where the workaround is: "define a master configuration".
|
||||||
|
*
|
||||||
|
* Also see: http://ant.apache.org/ivy/history/2.3.0/ivyfile/dependency.html
|
||||||
|
* and: http://svn.apache.org/repos/asf/ant/ivy/core/tags/2.3.0/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
object ReplaceMavenConfigurationMappings {
|
||||||
|
|
||||||
|
val REPLACEMENT_MAVEN_MAPPINGS = {
|
||||||
|
// Here we copy paste from Ivy
|
||||||
|
val REPLACEMENT_MAPPINGS = new java.util.HashMap[String, PomModuleDescriptorBuilder.ConfMapper]
|
||||||
|
|
||||||
|
// NOTE - This code is copied from org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder
|
||||||
|
// except with altered default configurations...
|
||||||
|
REPLACEMENT_MAPPINGS.put("compile", new PomModuleDescriptorBuilder.ConfMapper {
|
||||||
|
def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean) {
|
||||||
|
if (isOptional) {
|
||||||
|
dd.addDependencyConfiguration("optional", "compile(*)")
|
||||||
|
// FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there.
|
||||||
|
dd.addDependencyConfiguration("optional", "master(compile)")
|
||||||
|
} else {
|
||||||
|
dd.addDependencyConfiguration("compile", "compile(*)")
|
||||||
|
// FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there.
|
||||||
|
dd.addDependencyConfiguration("compile", "master(compile)")
|
||||||
|
dd.addDependencyConfiguration("runtime", "runtime(*)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
REPLACEMENT_MAPPINGS.put("provided", new PomModuleDescriptorBuilder.ConfMapper {
|
||||||
|
def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean) {
|
||||||
|
if (isOptional) {
|
||||||
|
dd.addDependencyConfiguration("optional", "compile(*)")
|
||||||
|
dd.addDependencyConfiguration("optional", "provided(*)")
|
||||||
|
dd.addDependencyConfiguration("optional", "runtime(*)")
|
||||||
|
// FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there.
|
||||||
|
dd.addDependencyConfiguration("optional", "master(compile)")
|
||||||
|
} else {
|
||||||
|
dd.addDependencyConfiguration("provided", "compile(*)")
|
||||||
|
dd.addDependencyConfiguration("provided", "provided(*)")
|
||||||
|
dd.addDependencyConfiguration("provided", "runtime(*)")
|
||||||
|
// FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there.
|
||||||
|
dd.addDependencyConfiguration("provided", "master(compile)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
REPLACEMENT_MAPPINGS.put("runtime", new PomModuleDescriptorBuilder.ConfMapper {
|
||||||
|
def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean) {
|
||||||
|
if (isOptional) {
|
||||||
|
dd.addDependencyConfiguration("optional", "compile(*)")
|
||||||
|
dd.addDependencyConfiguration("optional", "provided(*)")
|
||||||
|
// FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there.
|
||||||
|
dd.addDependencyConfiguration("optional", "master(compile)")
|
||||||
|
} else {
|
||||||
|
dd.addDependencyConfiguration("runtime", "compile(*)")
|
||||||
|
dd.addDependencyConfiguration("runtime", "runtime(*)")
|
||||||
|
// FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there.
|
||||||
|
dd.addDependencyConfiguration("runtime", "master(compile)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
REPLACEMENT_MAPPINGS.put("test", new PomModuleDescriptorBuilder.ConfMapper {
|
||||||
|
def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean) {
|
||||||
|
dd.addDependencyConfiguration("test", "runtime(*)")
|
||||||
|
// FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there.
|
||||||
|
dd.addDependencyConfiguration("test", "master(compile)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
REPLACEMENT_MAPPINGS.put("system", new PomModuleDescriptorBuilder.ConfMapper {
|
||||||
|
def addMappingConfs(dd: DefaultDependencyDescriptor, isOptional: Boolean) {
|
||||||
|
// FIX - Here we take a mroe conservative approach of depending on the compile configuration if master isn't there.
|
||||||
|
dd.addDependencyConfiguration("system", "master(compile)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
REPLACEMENT_MAPPINGS
|
||||||
|
}
|
||||||
|
|
||||||
|
def init(): Unit = {
|
||||||
|
// Here we mutate a static final field, because we have to AND because it's evil.
|
||||||
|
try {
|
||||||
|
val map = PomModuleDescriptorBuilder.MAVEN2_CONF_MAPPING.asInstanceOf[java.util.Map[String, PomModuleDescriptorBuilder.ConfMapper]]
|
||||||
|
map.clear()
|
||||||
|
map.putAll(REPLACEMENT_MAVEN_MAPPINGS)
|
||||||
|
} catch {
|
||||||
|
case e: Exception =>
|
||||||
|
// TODO - Log that Ivy may not be configured correctly and you could have maven/ivy issues.
|
||||||
|
throw new RuntimeException("FAILURE to install Ivy maven hooks. Your ivy-maven interaction may suffer resolution errors", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ import org.apache.ivy.core.module.id.ModuleRevisionId
|
||||||
import org.apache.ivy.core.module.descriptor.{ DefaultArtifact, DefaultExtendsDescriptor, DefaultModuleDescriptor, ModuleDescriptor }
|
import org.apache.ivy.core.module.descriptor.{ DefaultArtifact, DefaultExtendsDescriptor, DefaultModuleDescriptor, ModuleDescriptor }
|
||||||
import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DependencyDescriptor }
|
import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DependencyDescriptor }
|
||||||
import org.apache.ivy.plugins.parser.{ ModuleDescriptorParser, ModuleDescriptorParserRegistry, ParserSettings }
|
import org.apache.ivy.plugins.parser.{ ModuleDescriptorParser, ModuleDescriptorParserRegistry, ParserSettings }
|
||||||
import org.apache.ivy.plugins.parser.m2.{ PomModuleDescriptorBuilder, PomModuleDescriptorParser }
|
import org.apache.ivy.plugins.parser.m2.{ ReplaceMavenConfigurationMappings, PomModuleDescriptorBuilder, PomModuleDescriptorParser }
|
||||||
import org.apache.ivy.plugins.repository.Resource
|
import org.apache.ivy.plugins.repository.Resource
|
||||||
import org.apache.ivy.plugins.namespace.NamespaceTransformer
|
import org.apache.ivy.plugins.namespace.NamespaceTransformer
|
||||||
import org.apache.ivy.util.extendable.ExtendableItem
|
import org.apache.ivy.util.extendable.ExtendableItem
|
||||||
|
|
@ -27,6 +27,10 @@ final class CustomPomParser(delegate: ModuleDescriptorParser, transform: (Module
|
||||||
override def getMetadataArtifact(mrid: ModuleRevisionId, res: Resource) = delegate.getMetadataArtifact(mrid, res)
|
override def getMetadataArtifact(mrid: ModuleRevisionId, res: Resource) = delegate.getMetadataArtifact(mrid, res)
|
||||||
}
|
}
|
||||||
object CustomPomParser {
|
object CustomPomParser {
|
||||||
|
|
||||||
|
// Evil hackery to override the default maven pom mappings.
|
||||||
|
ReplaceMavenConfigurationMappings.init()
|
||||||
|
|
||||||
/** The key prefix that indicates that this is used only to store extra information and is not intended for dependency resolution.*/
|
/** The key prefix that indicates that this is used only to store extra information and is not intended for dependency resolution.*/
|
||||||
val InfoKeyPrefix = "info."
|
val InfoKeyPrefix = "info."
|
||||||
val ApiURLKey = "info.apiURL"
|
val ApiURLKey = "info.apiURL"
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ private[sbt] object SettingCompletions {
|
||||||
{
|
{
|
||||||
import extracted._
|
import extracted._
|
||||||
val r = relation(extracted.structure, true)
|
val r = relation(extracted.structure, true)
|
||||||
val allDefs = r._1s.toSeq
|
val allDefs = Def.flattenLocals(Def.compiled(extracted.structure.settings, true)(structure.delegates, structure.scopeLocal, implicitly[Show[ScopedKey[_]]])).map(_._1)
|
||||||
val projectScope = Load.projectScope(currentRef)
|
val projectScope = Load.projectScope(currentRef)
|
||||||
def resolve(s: Setting[_]): Seq[Setting[_]] = Load.transformSettings(projectScope, currentRef.build, rootProject, s :: Nil)
|
def resolve(s: Setting[_]): Seq[Setting[_]] = Load.transformSettings(projectScope, currentRef.build, rootProject, s :: Nil)
|
||||||
def rescope[T](setting: Setting[T]): Seq[Setting[_]] =
|
def rescope[T](setting: Setting[T]): Seq[Setting[_]] =
|
||||||
|
|
@ -353,4 +353,4 @@ private[sbt] object SettingCompletions {
|
||||||
classOf[Long],
|
classOf[Long],
|
||||||
classOf[String]
|
classOf[String]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,6 @@
|
||||||
[@puffnfresh]: https://github.com/puffnfresh
|
[@puffnfresh]: https://github.com/puffnfresh
|
||||||
[@rtyley]: https://github.com/rtyley
|
[@rtyley]: https://github.com/rtyley
|
||||||
|
|
||||||
### Changes since 0.13.6-M1
|
|
||||||
|
|
||||||
- Fixes `NullPointerException` during `update`. [#1484][1484] by [@eed3si9n][@eed3si9n]
|
|
||||||
|
|
||||||
### Fixes with compatibility implications
|
### Fixes with compatibility implications
|
||||||
|
|
||||||
- Maven Central Repository, Java.net Maven 2 Repository, Typesafe Repository, and sbt Plugin repository now defaults to HTTPS. (See below)
|
- Maven Central Repository, Java.net Maven 2 Repository, Typesafe Repository, and sbt Plugin repository now defaults to HTTPS. (See below)
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
[1586]: https://github.com/sbt/sbt/pull/1586
|
||||||
|
[@jsuereth]: https://github.com/jsuereth
|
||||||
|
|
||||||
|
|
||||||
|
### Fixes with compatibility implications
|
||||||
|
|
||||||
|
* Maven artifact dependencies now limit their transitive dependencies to "compile" rather than "every configuration"
|
||||||
|
if no `master` configuration is found. [#1586][1586] by [@jsuereth][@jsuereth]
|
||||||
|
|
@ -17,7 +17,7 @@ object Sbt extends Build {
|
||||||
s"all control/$task collections/$task io/$task completion/$task"
|
s"all control/$task collections/$task io/$task completion/$task"
|
||||||
def buildSettings = Seq(
|
def buildSettings = Seq(
|
||||||
organization := "org.scala-sbt",
|
organization := "org.scala-sbt",
|
||||||
version := "0.13.6-SNAPSHOT",
|
version := "0.13.7-SNAPSHOT",
|
||||||
publishArtifact in packageDoc := false,
|
publishArtifact in packageDoc := false,
|
||||||
scalaVersion := "2.10.4",
|
scalaVersion := "2.10.4",
|
||||||
publishMavenStyle := false,
|
publishMavenStyle := false,
|
||||||
|
|
@ -65,6 +65,7 @@ object Sbt extends Build {
|
||||||
commands += Command.command("release-sbt") { state =>
|
commands += Command.command("release-sbt") { state =>
|
||||||
// TODO - Any sort of validation
|
// TODO - Any sort of validation
|
||||||
"checkCredentials" ::
|
"checkCredentials" ::
|
||||||
|
"conscript-configs" ::
|
||||||
"publishSigned" ::
|
"publishSigned" ::
|
||||||
"publishLauncher" ::
|
"publishLauncher" ::
|
||||||
"release-libs-211" ::
|
"release-libs-211" ::
|
||||||
|
|
@ -153,7 +154,7 @@ object Sbt extends Build {
|
||||||
// Defines the data structures for representing file fingerprints and relationships and the overall source analysis
|
// Defines the data structures for representing file fingerprints and relationships and the overall source analysis
|
||||||
lazy val compileIncrementalSub = testedBaseProject(compilePath / "inc", "Incremental Compiler") dependsOn (apiSub, ioSub, logSub, classpathSub, relationSub)
|
lazy val compileIncrementalSub = testedBaseProject(compilePath / "inc", "Incremental Compiler") dependsOn (apiSub, ioSub, logSub, classpathSub, relationSub)
|
||||||
// Persists the incremental data structures using SBinary
|
// Persists the incremental data structures using SBinary
|
||||||
lazy val compilePersistSub = baseProject(compilePath / "persist", "Persist") dependsOn (compileIncrementalSub, apiSub) settings (sbinary)
|
lazy val compilePersistSub = testedBaseProject(compilePath / "persist", "Persist") dependsOn (compileIncrementalSub, apiSub, compileIncrementalSub % "test->test") settings (sbinary)
|
||||||
// sbt-side interface to compiler. Calls compiler-side interface reflectively
|
// sbt-side interface to compiler. Calls compiler-side interface reflectively
|
||||||
lazy val compilerSub = testedBaseProject(compilePath, "Compile") dependsOn (launchInterfaceSub, interfaceSub % "compile;test->test", logSub, ioSub, classpathSub,
|
lazy val compilerSub = testedBaseProject(compilePath, "Compile") dependsOn (launchInterfaceSub, interfaceSub % "compile;test->test", logSub, ioSub, classpathSub,
|
||||||
logSub % "test->test", launchSub % "test->test", apiSub % "test") settings (compilerSettings: _*)
|
logSub % "test->test", launchSub % "test->test", apiSub % "test") settings (compilerSettings: _*)
|
||||||
|
|
@ -315,7 +316,7 @@ object Sbt extends Build {
|
||||||
autoScalaLibrary := false,
|
autoScalaLibrary := false,
|
||||||
description := "sbt application launcher",
|
description := "sbt application launcher",
|
||||||
publishLauncher <<= Release.deployLauncher,
|
publishLauncher <<= Release.deployLauncher,
|
||||||
packageBin in Compile <<= (proguard in Proguard, Transform.conscriptConfigs).map((x, y) => x)
|
packageBin in Compile <<= proguard in Proguard
|
||||||
)
|
)
|
||||||
|
|
||||||
def interfaceSettings = javaOnly ++ Seq(
|
def interfaceSettings = javaOnly ++ Seq(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
val repoFile = file("mvn-repo")
|
||||||
|
|
||||||
|
resolvers += "bad-mvn-repo" at repoFile.toURI.toURL.toString
|
||||||
|
|
||||||
|
libraryDependencies += "bad" % "mvn" % "1.0"
|
||||||
|
|
||||||
|
TaskKey[Unit]("check") := {
|
||||||
|
val cp = (fullClasspath in Compile).value
|
||||||
|
def isTestJar(n: String): Boolean =
|
||||||
|
(n contains "scalacheck") ||
|
||||||
|
(n contains "specs2")
|
||||||
|
val testLibs = cp map (_.data.getName) filter isTestJar
|
||||||
|
assert(testLibs.isEmpty, s"Compile Classpath has test libs:\n * ${testLibs.mkString("\n * ")}")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>bad</groupId>
|
||||||
|
<artifactId>mvn</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>1.0</version>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.scala-sbt</groupId>
|
||||||
|
<artifactId>completion</artifactId>
|
||||||
|
<version>0.13.5</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
> check
|
||||||
|
|
@ -16,9 +16,14 @@ object TestProject extends Build
|
||||||
private def check(transitive: Boolean) =
|
private def check(transitive: Boolean) =
|
||||||
(dependencyClasspath in Compile) map { downloaded =>
|
(dependencyClasspath in Compile) map { downloaded =>
|
||||||
val jars = downloaded.size
|
val jars = downloaded.size
|
||||||
if(transitive)
|
if(transitive) {
|
||||||
if(jars <= 2) error("Transitive dependencies not downloaded") else ()
|
if (jars <= 2)
|
||||||
else
|
sys.error(s"Transitive dependencies not downloaded, found:\n * ${downloaded.mkString("\n * ")}")
|
||||||
if(jars > 2) error("Transitive dependencies downloaded (" + downloaded.files.mkString(", ") + ")") else ()
|
else ()
|
||||||
|
} else {
|
||||||
|
if (jars > 2)
|
||||||
|
sys.error(s"Transitive dependencies not downloaded, found:\n * ${downloaded.mkString("\n * ")}")
|
||||||
|
else ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
> debug
|
||||||
# load the project definition with transitive dependencies enabled
|
# load the project definition with transitive dependencies enabled
|
||||||
# and check that they are not downloaded
|
# and check that they are not downloaded
|
||||||
#$ pause
|
#$ pause
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
val a = project.settings(version := "2.8.1")
|
||||||
|
|
||||||
|
val trySetEvery = taskKey[Unit]("Tests \"set every\"")
|
||||||
|
|
||||||
|
trySetEvery := {
|
||||||
|
val s = state.value
|
||||||
|
val extracted = Project.extract(s)
|
||||||
|
import extracted._
|
||||||
|
val allProjs = structure.allProjectRefs
|
||||||
|
val Some(aProj) = allProjs.find(_.project == "a")
|
||||||
|
val aVer = (version in aProj get structure.data).get
|
||||||
|
if (aVer != "1.0") {
|
||||||
|
println("Version of project a: " + aVer + ", expected: 1.0")
|
||||||
|
error("\"set every\" did not change the version of all projects.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
> set every version := '"1.0"'
|
||||||
|
> trySetEvery
|
||||||
|
|
||||||
Loading…
Reference in New Issue