Merge branch '0.13' of github.com:sbt/sbt into 0.13

This commit is contained in:
Jean-Rémi Desjardins 2014-09-16 09:20:21 -07:00
commit 2eb8fd1b8a
23 changed files with 483 additions and 111 deletions

View File

@ -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
------------- -------------

View File

@ -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 = (

View File

@ -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

View File

@ -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)

View File

@ -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) =>
@ -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 => ()
} }

View File

@ -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)
}
} }
} }

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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"

View File

@ -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[_]] =

View File

@ -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)

View File

@ -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]

View File

@ -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(

View File

@ -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 * ")}")
}

View File

@ -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>

View File

@ -0,0 +1 @@
> check

View File

@ -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 ()
}
} }
} }

View File

@ -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

View File

@ -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.")
}
}

View File

@ -0,0 +1,3 @@
> set every version := '"1.0"'
> trySetEvery