diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 599ece90d..13db679a0 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -3,7 +3,7 @@ */ package sbt -import Keys._ +import Keys.{ version, _ } import sbt.internal.util.complete.{ DefaultParsers, Parser } import sbt.internal.util.AttributeKey import DefaultParsers._ @@ -18,6 +18,7 @@ import sbt.internal.CommandStrings.{ } import java.io.File +import sbt.internal.Act import sbt.internal.inc.ScalaInstance import sbt.io.IO import sbt.librarymanagement.CrossVersion @@ -125,10 +126,13 @@ object Cross { 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 // 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 => - crossVersions(x, proj) map { (proj.project, _) } + val projVersions = (projCrossVersions flatMap { + case (proj, versions) => versions map { proj.project -> _ } }).toList val verbose = if (args.verbose) "-v" else "" @@ -136,19 +140,51 @@ object Cross { if (projVersions.isEmpty) { state } else { - // Group all the projects by scala version - val allCommands = 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 ", " ", "")) + // Detect whether a task or command has been issued + val allCommands = Parser.parse(aggCommand, Act.aggregatedKeyParser(x)) match { + case Left(_) => + // It's definitely not a task, check if it's a valid command, because we don't want to emit the warning + // message below for typos. + val validCommand = Parser.parse(aggCommand, state.combinedParser).isRight + + val distinctCrossConfigs = projCrossVersions.map(_._2.toSet).distinct + if (validCommand && distinctCrossConfigs.size > 1) { + state.log.warn( + "Issuing a cross building command, but not all sub projects have the same cross build " + + "configuration. This could result in subprojects cross building against Scala versions that they are " + + "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) diff --git a/sbt/src/sbt-test/actions/cross-multiproject/build.sbt b/sbt/src/sbt-test/actions/cross-multiproject/build.sbt index 4d434aabd..117efa724 100644 --- a/sbt/src/sbt-test/actions/cross-multiproject/build.sbt +++ b/sbt/src/sbt-test/actions/cross-multiproject/build.sbt @@ -19,3 +19,5 @@ lazy val fooPlugin =(project in file("sbt-foo")). scalaVersion := "2.12.1", crossScalaVersions := Seq("2.12.1") ) + +addCommandAlias("build", "compile") diff --git a/sbt/src/sbt-test/actions/cross-multiproject/test b/sbt/src/sbt-test/actions/cross-multiproject/test index ea4079cf0..154e71c2c 100644 --- a/sbt/src/sbt-test/actions/cross-multiproject/test +++ b/sbt/src/sbt-test/actions/cross-multiproject/test @@ -36,3 +36,13 @@ $ exists lib/target/scala-2.11 # -$ exists lib/target/scala-2.12 # -$ exists sbt-foo/target/scala-2.12 # $ 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