Merge pull request #3446 from jroper/cross-build-commands

[1.0.x] Add support for cross building commands
This commit is contained in:
eugene yokota 2017-08-23 18:38:25 -04:00 committed by GitHub
commit eb4312e6fa
3 changed files with 64 additions and 16 deletions

View File

@ -3,7 +3,7 @@
*/ */
package sbt package sbt
import Keys._ import Keys.{ version, _ }
import sbt.internal.util.complete.{ DefaultParsers, Parser } import sbt.internal.util.complete.{ DefaultParsers, Parser }
import sbt.internal.util.AttributeKey import sbt.internal.util.AttributeKey
import DefaultParsers._ import DefaultParsers._
@ -18,6 +18,7 @@ import sbt.internal.CommandStrings.{
} }
import java.io.File import java.io.File
import sbt.internal.Act
import sbt.internal.inc.ScalaInstance import sbt.internal.inc.ScalaInstance
import sbt.io.IO import sbt.io.IO
import sbt.librarymanagement.CrossVersion import sbt.librarymanagement.CrossVersion
@ -125,10 +126,13 @@ object Cross {
case Left(cmd) => (resolveAggregates(x), cmd) case Left(cmd) => (resolveAggregates(x), cmd)
} }
val projCrossVersions = aggs map { proj =>
proj -> crossVersions(x, proj)
}
// if we support scalaVersion, projVersions should be cached somewhere since // if we support scalaVersion, projVersions should be cached somewhere since
// running ++2.11.1 is at the root level is going to mess with the scalaVersion for the aggregated subproj // running ++2.11.1 is at the root level is going to mess with the scalaVersion for the aggregated subproj
val projVersions = (aggs flatMap { proj => val projVersions = (projCrossVersions flatMap {
crossVersions(x, proj) map { (proj.project, _) } case (proj, versions) => versions map { proj.project -> _ }
}).toList }).toList
val verbose = if (args.verbose) "-v" else "" val verbose = if (args.verbose) "-v" else ""
@ -136,19 +140,51 @@ object Cross {
if (projVersions.isEmpty) { if (projVersions.isEmpty) {
state state
} else { } else {
// Group all the projects by scala version // Detect whether a task or command has been issued
val allCommands = projVersions.groupBy(_._2).mapValues(_.map(_._1)).toSeq.flatMap { val allCommands = Parser.parse(aggCommand, Act.aggregatedKeyParser(x)) match {
case (version, Seq(project)) => case Left(_) =>
// If only one project for a version, issue it directly // It's definitely not a task, check if it's a valid command, because we don't want to emit the warning
Seq(s"$SwitchCommand $verbose $version $project/$aggCommand") // message below for typos.
case (version, projects) if aggCommand.contains(" ") => val validCommand = Parser.parse(aggCommand, state.combinedParser).isRight
// If the command contains a space, then the all command won't work because it doesn't support issuing
// commands with spaces, so revert to running the command on each project one at a time val distinctCrossConfigs = projCrossVersions.map(_._2.toSet).distinct
s"$SwitchCommand $verbose $version" :: projects.map(project => s"$project/$aggCommand") if (validCommand && distinctCrossConfigs.size > 1) {
case (version, projects) => state.log.warn(
// First switch scala version, then use the all command to run the command on each project concurrently "Issuing a cross building command, but not all sub projects have the same cross build " +
Seq(s"$SwitchCommand $verbose $version", "configuration. This could result in subprojects cross building against Scala versions that they are " +
projects.map(_ + "/" + aggCommand).mkString("all ", " ", "")) "not compatible with. Try issuing cross building command with tasks instead, since sbt will be able " +
"to ensure that cross building is only done using configured project and Scala version combinations " +
"that are configured.")
state.log.debug("Scala versions configuration is:")
projCrossVersions.foreach {
case (project, versions) => state.log.debug(s"$project: $versions")
}
}
// Execute using a blanket switch
projCrossVersions.toMap.apply(currentRef).flatMap { version =>
// Force scala version
Seq(s"$SwitchCommand $verbose $version!", aggCommand)
}
case Right(_) =>
// We have a key, we're likely to be able to cross build this using the per project behaviour.
// Group all the projects by scala version
projVersions.groupBy(_._2).mapValues(_.map(_._1)).toSeq.flatMap {
case (version, Seq(project)) =>
// If only one project for a version, issue it directly
Seq(s"$SwitchCommand $verbose $version $project/$aggCommand")
case (version, projects) if aggCommand.contains(" ") =>
// If the command contains a space, then the `all` command won't work because it doesn't support issuing
// commands with spaces, so revert to running the command on each project one at a time
s"$SwitchCommand $verbose $version" :: projects.map(project =>
s"$project/$aggCommand")
case (version, projects) =>
// First switch scala version, then use the all command to run the command on each project concurrently
Seq(s"$SwitchCommand $verbose $version",
projects.map(_ + "/" + aggCommand).mkString("all ", " ", ""))
}
} }
allCommands.toList ::: CrossRestoreSessionCommand :: captureCurrentSession(state, x) allCommands.toList ::: CrossRestoreSessionCommand :: captureCurrentSession(state, x)

View File

@ -19,3 +19,5 @@ lazy val fooPlugin =(project in file("sbt-foo")).
scalaVersion := "2.12.1", scalaVersion := "2.12.1",
crossScalaVersions := Seq("2.12.1") crossScalaVersions := Seq("2.12.1")
) )
addCommandAlias("build", "compile")

View File

@ -36,3 +36,13 @@ $ exists lib/target/scala-2.11
# -$ exists lib/target/scala-2.12 # -$ exists lib/target/scala-2.12
# -$ exists sbt-foo/target/scala-2.12 # -$ exists sbt-foo/target/scala-2.12
# $ exists sbt-foo/target/scala-2.11 # $ exists sbt-foo/target/scala-2.11
> clean
# Test legacy cross build with command support
> + build
# Uses the root project scala version config, which is only configured to build for Scala 2.12
$ exists lib/target/scala-2.12
-$ exists lib/target/scala-2.11
$ exists sbt-foo/target/scala-2.12
-$ exists sbt-foo/target/scala-2.11