Skip to content

Commit

Permalink
Add support for partially-shared src dirs
Browse files Browse the repository at this point in the history
  • Loading branch information
armanbilge committed Mar 21, 2022
1 parent fd991e8 commit b61c500
Show file tree
Hide file tree
Showing 14 changed files with 175 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import sbtcrossproject.{crossProject, CrossType}

lazy val foo = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.settings(scalaVersion := "2.11.11")
.jsSettings(scalaJSUseMainModuleInitializer := true)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object JSOrJVM {
def check = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object JSOrNative {
def check = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object JVMOrNative {
def check = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object Platform {
def isJVM = false
def isJS = true
def isNative = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object JVMOrNative {
def check = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object JSOrNative {
def check = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object Platform {
def isJVM = true
def isJS = false
def isNative = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object JSOrJVM {
def check = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object Platform {
def isJVM = false
def isJS = false
def isNative = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Platform._
object Foo {
def main(args: Array[String]): Unit = {
assert(List(isJVM, isJS, isNative).count(identity) == 1)

if (isJVM)
assert(JSOrJVM.check && JVMOrNative.check && !JSOrNative.check)
if (isJS)
assert(JSOrJVM.check && !JVMOrNative.check && JSOrNative.check)
if (isNative)
assert(!JSOrJVM.check && JVMOrNative.check && JSOrNative.check)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
> fooJVM/run
> fooJS/run
> fooNative/run
107 changes: 85 additions & 22 deletions sbt-crossproject/src/main/scala/sbtcrossproject/CrossProject.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import sbtcrossproject._

import scala.language.implicitConversions

import scala.collection.immutable.HashMap
import scala.collection.immutable.ListSet

import sbt._
import Keys._

Expand Down Expand Up @@ -202,16 +205,17 @@ object CrossProject {
).settings(
CrossPlugin.autoImport.crossProjectPlatform := platform,
name := id, // #80
sharedSrc,
sharedResources
sharedSrc(platform),
sharedResources(platform)
)
)
}.toMap

new CrossProject(id, crossType, projects)
}

private def sharedSrcSettings(crossType: CrossType): Seq[Setting[_]] = {
private def sharedSrcSettings(
crossType: CrossType): Map[Platform, Seq[Setting[_]]] = {
def makeCrossSources(sharedSrcDir: Option[File],
scalaBinaryVersion: String,
cross: Boolean): Seq[File] = {
Expand All @@ -234,30 +238,89 @@ object CrossProject {
}
}

Seq(
unmanagedSourceDirectories in Compile ++= {
makeCrossSources(crossType.sharedSrcDir(baseDirectory.value, "main"),
scalaBinaryVersion.value,
crossPaths.value)
},
unmanagedSourceDirectories in Test ++= {
makeCrossSources(crossType.sharedSrcDir(baseDirectory.value, "test"),
scalaBinaryVersion.value,
crossPaths.value)
def makeSharedSettings(
key: SettingKey[Seq[File]],
config: String): Seq[(Platform, Seq[Setting[_]])] = {
val partiallyShared = makePartiallySharedSettings(platforms) {
platformSubset =>
Seq(
key ++= makeCrossSources(
crossType.partiallySharedSrcDir(baseDirectory.value,
platformSubset,
config),
scalaBinaryVersion.value,
crossPaths.value)
)
}
)

val shared = Seq(
key ++= makeCrossSources(
crossType.sharedSrcDir(baseDirectory.value, config),
scalaBinaryVersion.value,
crossPaths.value)
)

partiallyShared ++ platforms.map(_ -> shared) // add shared to each platform
}

val compileSettings =
makeSharedSettings(unmanagedSourceDirectories in Compile, "main")
val testSettings =
makeSharedSettings(unmanagedSourceDirectories in Test, "test")

(compileSettings ++ testSettings) // group-reduce the settings per-platform
.groupBy(_._1)
.map(kv => kv._1 -> kv._2.flatMap(_._2))
}

private def sharedResourcesSettings(
crossType: CrossType): Seq[Setting[_]] = {
Seq(
unmanagedResourceDirectories in Compile ++= {
crossType.sharedResourcesDir(baseDirectory.value, "main")
},
unmanagedResourceDirectories in Test ++= {
crossType.sharedResourcesDir(baseDirectory.value, "test")
crossType: CrossType): Map[Platform, Seq[Setting[_]]] = {
def makeSharedSettings(
key: SettingKey[Seq[File]],
config: String): Seq[(Platform, Seq[Setting[_]])] = {
val partiallyShared = makePartiallySharedSettings(platforms) {
platformSubset =>
Seq(
key ++= crossType
.partiallySharedResourcesDir(baseDirectory.value,
platformSubset,
config)
.toSeq
)
}

val shared = Seq(
key ++= crossType
.sharedResourcesDir(baseDirectory.value, config)
.toSeq
)

partiallyShared ++ platforms.map(_ -> shared) // add shared to each platform
}

val compileSettings =
makeSharedSettings(unmanagedResourceDirectories in Compile, "main")
val testSettings =
makeSharedSettings(unmanagedResourceDirectories in Test, "test")

(compileSettings ++ testSettings) // group-reduce the settings per-platform
.groupBy(_._1)
.map(kv => kv._1 -> kv._2.flatMap(_._2))
}

private def makePartiallySharedSettings(platforms: Seq[Platform])(
mkSettings: Seq[Platform] => Seq[Setting[_]])
: Seq[(Platform, Seq[Setting[_]])] = {
platforms.toSet
.subsets()
.filter(_.size > 1) // skip the empty+singleton subsets
.filterNot(_.size == platforms.size) // skip the all-platform subset
.toSeq
.map(_.toSeq.sortBy(_.identifier)) // use a consistent ordering
.flatMap { platformSubset => // make the settings for this subset of platforms
val settings = mkSettings(platformSubset)
platformSubset.map(_ -> settings).toMap
}
)
}

}
Expand Down
36 changes: 36 additions & 0 deletions sbt-crossproject/src/main/scala/sbtcrossproject/CrossType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,32 @@ abstract class CrossType {
*/
def sharedSrcDir(projectBase: File, conf: String): Option[File]

/** The location of a partially shared source directory (if it exists)
* @param projectBase the base directory of a (true sbt) Project
* @param platforms non-empty seq of JSPlatform, JVMPlatform, NativePlatform, ...
* @param conf name of sub-directory for the configuration (typically "main"
* or "test")
*/
def partiallySharedSrcDir(projectBase: File,
platforms: Seq[Platform],
conf: String): Option[File] = None

/** The location of a shared resources directory (if it exists)
* @param projectBase the base directory of a (true sbt) Project
* @param conf name of sub-directory for the configuration (typically "main"
* or "test")
*/
def sharedResourcesDir(projectBase: File, conf: String): Option[File] = None

/** The location of a partially shared resources directory (if it exists)
* @param projectBase the base directory of a (true sbt) Project
* @param conf name of sub-directory for the configuration (typically "main"
* or "test")
*/
def partiallySharedResourcesDir(projectBase: File,
platforms: Seq[Platform],
conf: String): Option[File] = None

}

object CrossType {
Expand All @@ -69,9 +88,26 @@ object CrossType {
def sharedSrcDir(projectBase: File, conf: String): Option[File] =
Some(projectBase.getParentFile / "shared" / "src" / conf / "scala")

override def partiallySharedSrcDir(projectBase: File,
platforms: Seq[Platform],
conf: String): Option[File] = {
val dir = platforms.map(_.identifier).mkString("-")
Some(projectBase.getParentFile / dir / "src" / conf / "scala")
}

override def sharedResourcesDir(projectBase: File,
conf: String): Option[File] =
Some(projectBase.getParentFile / "shared" / "src" / conf / "resources")

override def partiallySharedResourcesDir(projectBase: File,
platforms: Seq[Platform],
conf: String): Option[File] =
if (platforms.isEmpty) {
None
} else {
val dir = platforms.map(_.identifier).mkString("-")
Some(projectBase.getParentFile / dir / "src" / conf / "resources")
}
}

/**
Expand Down

0 comments on commit b61c500

Please sign in to comment.