diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala index 5e7ca55e0..73003bcb7 100644 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala @@ -216,6 +216,7 @@ private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine { private[sbt] def cachedResolutionResolveCache: CachedResolutionResolveCache private[sbt] def projectResolver: Option[ProjectResolver] private[sbt] def makeInstance: Ivy + private[sbt] val ignoreTransitiveForce: Boolean = true /** * This returns sbt's UpdateReport structure. @@ -329,6 +330,17 @@ private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine { survivor ++ evicted ++ (merged filter { m => m.evicted || m.problem.isDefined }) } } + /** + * resolves dependency resolution conflicts in which multiple candidates are found for organization+name combos. + * The main input is conflicts, which is a Vector of ModuleReport, which contains full info on the modulerevision, including its callers. + * Conflict resolution could be expensive, so this is first cached to `cachedResolutionResolveCache` if the conflict is between 2 modules. + * Otherwise, the default "latest" resolution takes the following precedence: + * 1. overrides passed in to `os`. + * 2. diretly forced dependency within the artificial module. + * 3. latest revision. + * Note transitively forced dependencies are not respected. This seems to be the case for stock Ivy's behavior as well, + * which may be because Ivy makes all Maven dependencies as forced="true". + */ def resolveConflict(rootModuleConf: String, conflicts: Vector[ModuleReport], os: Vector[IvyOverride], log: Logger): (Vector[ModuleReport], Vector[ModuleReport]) = { import org.apache.ivy.plugins.conflict.{ NoConflictManager, StrictConflictManager, LatestConflictManager } @@ -339,19 +351,26 @@ private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine { def useLatest(lcm: LatestConflictManager): (Vector[ModuleReport], Vector[ModuleReport], String) = (conflicts find { m => m.callers.exists { _.isDirectlyForceDependency } - } orElse (conflicts find { m => - m.callers.exists { _.isForceDependency } - })) match { + }) match { case Some(m) => - log.debug(s"- forced dependency: $m ${m.callers}") - (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some(lcm.toString)) }, lcm.toString) + log.debug(s"- directly forced dependency: $m ${m.callers}") + (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some("direct-force")) }, "direct-force") case None => - val strategy = lcm.getStrategy - val infos = conflicts map { ModuleReportArtifactInfo(_) } - Option(strategy.findLatest(infos.toArray, None.orNull)) match { - case Some(ModuleReportArtifactInfo(m)) => - (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some(lcm.toString)) }, lcm.toString) - case _ => (conflicts, Vector(), lcm.toString) + (conflicts find { m => + m.callers.exists { _.isForceDependency } + }) match { + // Ivy translates pom.xml dependencies to forced="true", so transitive force is broken. + case Some(m) if !ignoreTransitiveForce => + log.debug(s"- transitively forced dependency: $m ${m.callers}") + (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some("transitive-force")) }, "transitive-force") + case _ => + val strategy = lcm.getStrategy + val infos = conflicts map { ModuleReportArtifactInfo(_) } + Option(strategy.findLatest(infos.toArray, None.orNull)) match { + case Some(ModuleReportArtifactInfo(m)) => + (Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some(lcm.toString)) }, lcm.toString) + case _ => (conflicts, Vector(), lcm.toString) + } } } def doResolveConflict: (Vector[ModuleReport], Vector[ModuleReport], String) = diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 4f9ef4da3..40645e4fe 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -264,7 +264,7 @@ object Defaults extends BuildCommon { definedSbtPlugins <<= discoverPlugins, discoveredSbtPlugins <<= discoverSbtPluginNames, inTask(run)(runnerTask :: Nil).head, - selectMainClass := pickMainClass(discoveredMainClasses.value) orElse askForMainClass(discoveredMainClasses.value), + selectMainClass := mainClass.value orElse askForMainClass(discoveredMainClasses.value), mainClass in run := (selectMainClass in run).value, mainClass := pickMainClassOrWarn(discoveredMainClasses.value, streams.value.log), run <<= runTask(fullClasspath, mainClass in run, runner in run), diff --git a/notes/0.13.7.markdown b/notes/0.13.7.markdown index e99f764db..87ad134fb 100644 --- a/notes/0.13.7.markdown +++ b/notes/0.13.7.markdown @@ -39,6 +39,13 @@ ### Fixes with compatibility implications - Maven artifact dependencies will limit their transitive dependencies to `Compile` rather than *every configuration* if no `master` configuration is found. [#1586][1586] by [@jsuereth][@jsuereth] +- The new natural whitspace handling parser is unable to cope with certain classes of Scala syntax. In particular, top-level pattern matches, or multi-value defintions are no longer supported: + +```scala + val x, y = project // BAD + val x = project // + val y = project // GOOD +``` ### Improvements diff --git a/project/Sbt.scala b/project/Sbt.scala index 0c23ce6d2..d5c1f0539 100644 --- a/project/Sbt.scala +++ b/project/Sbt.scala @@ -17,7 +17,7 @@ object Sbt extends Build { s"all control/$task collections/$task io/$task completion/$task" def buildSettings = Seq( organization := "org.scala-sbt", - version := "0.13.7-SNAPSHOT", + version := "0.13.8-SNAPSHOT", publishArtifact in packageDoc := false, scalaVersion := "2.10.4", publishMavenStyle := false, diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution/multi.sbt index 5f1763f56..b5af8b348 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution/multi.sbt @@ -1,5 +1,4 @@ lazy val check = taskKey[Unit]("Runs the check") -lazy val check2 = taskKey[Unit]("Runs the check") def commonSettings: Seq[Def.Setting[_]] = Seq( @@ -37,20 +36,6 @@ lazy val c = project. // libraryDependencies := Seq(organization.value %% "a" % version.value) ) -// overrides cached -lazy val d = project. - settings(consolidatedResolutionSettings: _*). - settings( - dependencyOverrides += "commons-io" % "commons-io" % "2.0" - ) - -// overrides plain -lazy val e = project. - settings(commonSettings: _*). - settings( - dependencyOverrides += "commons-io" % "commons-io" % "2.0" - ) - lazy val root = (project in file(".")). settings( organization in ThisBuild := "org.example", @@ -64,13 +49,5 @@ lazy val root = (project in file(".")). "\n - a (cached) " + acp.toString + "\n - b (plain) " + bcp.toString + "\n - c (inter-project) " + ccp.toString) - }, - check2 := { - val dcp = (externalDependencyClasspath in Compile in d).value.sortBy {_.data.getName} - val ecp = (externalDependencyClasspath in Compile in e).value.sortBy {_.data.getName} - if (dcp == ecp) () - else sys.error("Different classpaths are found:" + - "\n - d (overrides + cached) " + dcp.toString + - "\n - e (overrides + plain) " + ecp.toString) } ) diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution/test b/sbt/src/sbt-test/dependency-management/cached-resolution/test index 66b1c7357..93a09a407 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution/test +++ b/sbt/src/sbt-test/dependency-management/cached-resolution/test @@ -11,5 +11,3 @@ > c/clean > check - -> check2 diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution2/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution2/multi.sbt index a3c3d5a87..380e0cc65 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution2/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution2/multi.sbt @@ -4,12 +4,6 @@ def commonSettings: Seq[Def.Setting[_]] = Seq( ivyPaths := new IvyPaths( (baseDirectory in ThisBuild).value, Some((baseDirectory in LocalRootProject).value / "ivy-cache")), dependencyCacheDirectory := (baseDirectory in LocalRootProject).value / "dependency", - libraryDependencies := Seq( - "org.springframework" % "spring-core" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"), - "org.springframework" % "spring-tx" % "3.1.2.RELEASE" force() exclude("org.springframework", "spring-asm"), - "org.springframework" % "spring-beans" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"), - "org.springframework" % "spring-context" % "3.1.2.RELEASE" force() exclude("org.springframework", "spring-asm") - ), scalaVersion := "2.10.4", resolvers += Resolver.sonatypeRepo("snapshots") ) @@ -20,17 +14,44 @@ def cachedResolutionSettings: Seq[Def.Setting[_]] = ) lazy val a = project. - settings(cachedResolutionSettings: _*) + settings(cachedResolutionSettings: _*). + settings( + libraryDependencies := Seq( + "org.springframework" % "spring-core" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"), + "org.springframework" % "spring-tx" % "3.1.2.RELEASE" force() exclude("org.springframework", "spring-asm"), + "org.springframework" % "spring-beans" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"), + "org.springframework" % "spring-context" % "3.1.2.RELEASE" force() exclude("org.springframework", "spring-asm") + ) + ) lazy val b = project. - settings(commonSettings: _*) + settings(commonSettings: _*). + settings( + libraryDependencies := Seq( + "org.springframework" % "spring-core" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"), + "org.springframework" % "spring-tx" % "3.1.2.RELEASE" force() exclude("org.springframework", "spring-asm"), + "org.springframework" % "spring-beans" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"), + "org.springframework" % "spring-context" % "3.1.2.RELEASE" force() exclude("org.springframework", "spring-asm") + ) + ) lazy val c = project. dependsOn(a). settings(cachedResolutionSettings: _*). settings( libraryDependencies := Seq( - "org.springframework" % "spring-core" % "4.0.3.RELEASE" exclude("org.springframework", "spring-asm") + // transitive force seems to be broken in ivy + // "org.springframework" % "spring-core" % "4.0.3.RELEASE" exclude("org.springframework", "spring-asm") + ) + ) + +lazy val d = project. + dependsOn(b). + settings(commonSettings: _*). + settings( + libraryDependencies := Seq( + // transitive force seems to be broken in ivy + // "org.springframework" % "spring-core" % "4.0.3.RELEASE" exclude("org.springframework", "spring-asm") ) ) @@ -44,29 +65,39 @@ lazy val root = (project in file(".")). val acp = (externalDependencyClasspath in Compile in a).value.sortBy {_.data.getName} val bcp = (externalDependencyClasspath in Compile in b).value.sortBy {_.data.getName} val ccp = (externalDependencyClasspath in Compile in c).value.sortBy {_.data.getName} + val dcp = (externalDependencyClasspath in Compile in d).value.sortBy {_.data.getName} if (!(acp exists {_.data.getName contains "spring-core-3.2.2.RELEASE"})) { - sys.error("spring-core-3.2.2 is not found") + sys.error("spring-core-3.2.2 is not found on a") } if (!(bcp exists {_.data.getName contains "spring-core-3.2.2.RELEASE"})) { - sys.error("spring-core-3.2.2 is not found") + sys.error("spring-core-3.2.2 is not found on b") } if (!(ccp exists {_.data.getName contains "spring-core-3.2.2.RELEASE"})) { - sys.error("spring-core-3.2.2 is not found") + sys.error("spring-core-3.2.2 is not found on c") + } + if (!(dcp exists {_.data.getName contains "spring-core-3.2.2.RELEASE"})) { + sys.error("spring-core-3.2.2 is not found on d\n" + dcp.toString) } if (!(acp exists {_.data.getName contains "spring-tx-3.1.2.RELEASE"})) { - sys.error("spring-tx-3.1.2 is not found") + sys.error("spring-tx-3.1.2 is not found on a") } if (!(bcp exists {_.data.getName contains "spring-tx-3.1.2.RELEASE"})) { - sys.error("spring-tx-3.1.2 is not found") + sys.error("spring-tx-3.1.2 is not found on b") } if (!(ccp exists {_.data.getName contains "spring-tx-3.1.2.RELEASE"})) { - sys.error("spring-tx-3.1.2 is not found") + sys.error("spring-tx-3.1.2 is not found on c") } - if (acp == bcp && acp == ccp) () + if (!(dcp exists {_.data.getName contains "spring-tx-3.1.2.RELEASE"})) { + sys.error("spring-tx-3.1.2 is not found on d") + } + if (acp == bcp) () else sys.error("Different classpaths are found:" + "\n - a (consolidated) " + acp.toString + - "\n - b (plain) " + bcp.toString + - "\n - c (inter-project) " + ccp.toString) + "\n - b (plain) " + bcp.toString) + if (ccp == dcp) () + else sys.error("Different classpaths are found:" + + "\n - c (consolidated) " + ccp.toString + + "\n - d (plain) " + dcp.toString) } ) diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution4/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution4/multi.sbt new file mode 100644 index 000000000..9e918f933 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/cached-resolution4/multi.sbt @@ -0,0 +1,48 @@ +lazy val check = taskKey[Unit]("Runs the check") + +def commonSettings: Seq[Def.Setting[_]] = + Seq( + ivyPaths := new IvyPaths( (baseDirectory in ThisBuild).value, Some((baseDirectory in LocalRootProject).value / "ivy-cache")), + dependencyCacheDirectory := (baseDirectory in LocalRootProject).value / "dependency", + libraryDependencies := Seq( + "net.databinder" %% "unfiltered-uploads" % "0.8.0", + "commons-io" % "commons-io" % "1.3", + "org.scala-refactoring" %% "org.scala-refactoring.library" % "0.6.2", + "org.scala-lang" % "scala-compiler" % scalaVersion.value + ), + scalaVersion := "2.11.2", + resolvers += Resolver.sonatypeRepo("snapshots") + ) + +def consolidatedResolutionSettings: Seq[Def.Setting[_]] = + commonSettings ++ Seq( + updateOptions := updateOptions.value.withConsolidatedResolution(true) + ) + +// overrides cached +lazy val a = project. + settings(consolidatedResolutionSettings: _*). + settings( + dependencyOverrides += "commons-io" % "commons-io" % "2.0" + ) + +// overrides plain +lazy val b = project. + settings(commonSettings: _*). + settings( + dependencyOverrides += "commons-io" % "commons-io" % "2.0" + ) + +lazy val root = (project in file(".")). + settings( + organization in ThisBuild := "org.example", + version in ThisBuild := "1.0", + check := { + val acp = (externalDependencyClasspath in Compile in a).value.sortBy {_.data.getName} + val bcp = (externalDependencyClasspath in Compile in b).value.sortBy {_.data.getName} + if (acp == bcp) () + else sys.error("Different classpaths are found:" + + "\n - a (overrides + cached) " + acp.toString + + "\n - b (overrides + plain) " + bcp.toString) + } + ) diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution4/test b/sbt/src/sbt-test/dependency-management/cached-resolution4/test new file mode 100644 index 000000000..15675b169 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/cached-resolution4/test @@ -0,0 +1 @@ +> check diff --git a/sbt/src/sbt-test/run/non-local-main/build.sbt b/sbt/src/sbt-test/run/non-local-main/build.sbt new file mode 100644 index 000000000..0010f0c27 --- /dev/null +++ b/sbt/src/sbt-test/run/non-local-main/build.sbt @@ -0,0 +1,22 @@ + + +lazy val main = project.settings( + organization := "org.scala-sbt.testsuite.example", + name := "has-main", + version := "1.0-SNAPSHOT" +) + +lazy val user = project.settings( + fullResolvers := fullResolvers.value.filterNot(_.name == "inter-project"), + libraryDependencies += (projectID in main).value, + mainClass in Compile := Some("Test") +) + +// NOTE - This will NOT work, as mainClass must be scoped by Compile (and optionally task) to function correctly). +lazy val user2 = project.settings( + fullResolvers := fullResolvers.value.filterNot(_.name == "inter-project"), + libraryDependencies += (projectID in main).value, + mainClass := Some("Test") +) + + diff --git a/sbt/src/sbt-test/run/non-local-main/main/src/main/scala/Test.scala b/sbt/src/sbt-test/run/non-local-main/main/src/main/scala/Test.scala new file mode 100644 index 000000000..e5ced234e --- /dev/null +++ b/sbt/src/sbt-test/run/non-local-main/main/src/main/scala/Test.scala @@ -0,0 +1,5 @@ +object Test { + def main(args: Array[String]): Unit = { + println("SUCCESS") + } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/run/non-local-main/test b/sbt/src/sbt-test/run/non-local-main/test new file mode 100644 index 000000000..f46eeb9d3 --- /dev/null +++ b/sbt/src/sbt-test/run/non-local-main/test @@ -0,0 +1,3 @@ +> main/publishLocal +> user/run +-> user2/run \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/Settings.scala b/util/collection/src/main/scala/sbt/Settings.scala index 9edc46ca7..bc4aca4ce 100644 --- a/util/collection/src/main/scala/sbt/Settings.scala +++ b/util/collection/src/main/scala/sbt/Settings.scala @@ -85,6 +85,10 @@ trait Init[Scope] { */ private[sbt] final def validated[T](key: ScopedKey[T], selfRefOk: Boolean): ValidationCapture[T] = new ValidationCapture(key, selfRefOk) + + @deprecated("0.13.7", "Use the version with default arguments and default paramter.") + final def derive[T](s: Setting[T], allowDynamic: Boolean, filter: Scope => Boolean, trigger: AttributeKey[_] => Boolean): Setting[T] = + derive(s, allowDynamic, filter, trigger, false) /** * Constructs a derived setting that will be automatically defined in every scope where one of its dependencies * is explicitly defined and the where the scope matches `filter`.