Skip to content

Commit a8d3580

Browse files
committed
community build: add automatic dependency override handling
Introduce a new sbt plugin: `sbt-community-build`, which tracks the modules published during a community build run, and injects dependency overrides to force version alignment.
1 parent 5eb3258 commit a8d3580

File tree

26 files changed

+489
-79
lines changed

26 files changed

+489
-79
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ jobs:
283283
run: cp -vf .github/workflows/repositories /root/.sbt/ ; true
284284

285285
- name: Test
286-
run: ./project/scripts/sbt sbt-dotty/scripted
286+
run: ./project/scripts/sbt "sbt-dotty/scripted; sbt-community-build/scripted"
287287

288288
test_java8:
289289
runs-on: [self-hosted, Linux]

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ vscode-dotty/.vscode-test
8484
community-build/scala3-bootstrapped.version
8585
community-build/sbt-dotty-sbt
8686
community-build/sbt-scalajs-sbt
87+
community-build/dotty-community-build-deps
8788

8889
# Vulpix output files
8990
*.check.out

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ val `scaladoc-js` = Build.`scaladoc-js`
2626
val `scala3-bench-run` = Build.`scala3-bench-run`
2727
val dist = Build.dist
2828
val `community-build` = Build.`community-build`
29+
val `sbt-community-build` = Build.`sbt-community-build`
2930

3031
val sjsSandbox = Build.sjsSandbox
3132
val sjsJUnitTests = Build.sjsJUnitTests

community-build/src/scala/dotty/communitybuild/projects.scala

Lines changed: 8 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,6 @@ def exec(projectDir: Path, binary: String, arguments: String*): Int =
2727
exitCode
2828

2929

30-
/** Versions of published projects, needs to be updated when a project in the build is updated.
31-
*
32-
* TODO: instead of harcoding these numbers, we could get them from the
33-
* projects themselves. This likely requires injecting a custom task in the
34-
* projects to output the version number to a file.
35-
*/
36-
object Versions:
37-
val cats = "2.3.1-SNAPSHOT"
38-
val catsMtl = "1.1+DOTTY-SNAPSHOT"
39-
val coop = "1.0+DOTTY-SNAPSHOT"
40-
val discipline = "1.1.3-SNAPSHOT"
41-
val disciplineMunit = "1.0.3+DOTTY-SNAPSHOT"
42-
val disciplineSpecs2 = "1.1.3-SNAPSHOT"
43-
val izumiReflect = "1.0.0-SNAPSHOT"
44-
val scalacheck = "1.15.2-SNAPSHOT"
45-
val scalatest = "3.2.3"
46-
val munit = "0.7.19+DOTTY-SNAPSHOT"
47-
val scodecBits = "1.1+DOTTY-SNAPSHOT"
48-
val simulacrumScalafix = "0.5.1-SNAPSHOT"
49-
val scalaCollectionCompat = "2.3.0+DOTTY-SNAPSHOT"
50-
5130
sealed trait CommunityProject:
5231
private var published = false
5332

@@ -115,56 +94,8 @@ final case class SbtCommunityProject(
11594
) extends CommunityProject:
11695
override val binaryName: String = "sbt"
11796

118-
// A project in the community build can depend on an arbitrary version of
119-
// another project in the build, so we force the use of the version that is
120-
// actually in the community build.
121-
val dependencyOverrides = List(
122-
// dependencyOverrides doesn't seem to understand `%%%`
123-
s""""org.scalacheck" %% "scalacheck" % "${Versions.scalacheck}"""",
124-
s""""org.scalacheck" %% "scalacheck_sjs1" % "${Versions.scalacheck}"""",
125-
s""""org.scalatest" %% "scalatest" % "${Versions.scalatest}"""",
126-
s""""org.scalatest" %% "scalatest_sjs1" % "${Versions.scalatest}"""",
127-
s""""org.scalatestplus" %% "junit-4-13" % "${Versions.scalatest}.0"""",
128-
s""""org.scalameta" %% "munit" % "${Versions.munit}"""",
129-
s""""org.scalameta" %% "munit_sjs1" % "${Versions.munit}"""",
130-
s""""org.scalameta" %% "munit-scalacheck" % "${Versions.munit}"""",
131-
s""""org.scalameta" %% "munit-scalacheck_sjs1" % "${Versions.munit}"""",
132-
s""""org.scalameta" %% "junit-interface" % "${Versions.munit}"""",
133-
s""""org.scodec" %% "scodec-bits" % "${Versions.scodecBits}"""",
134-
s""""org.scodec" %% "scodec-bits_sjs1" % "${Versions.scodecBits}"""",
135-
s""""org.typelevel" %% "discipline-core" % "${Versions.discipline}"""",
136-
s""""org.typelevel" %% "discipline-core_sjs1" % "${Versions.discipline}"""",
137-
s""""org.typelevel" %% "discipline-munit" % "${Versions.disciplineMunit}"""",
138-
s""""org.typelevel" %% "discipline-munit_sjs1" % "${Versions.disciplineMunit}"""",
139-
s""""org.typelevel" %% "discipline-specs2" % "${Versions.disciplineSpecs2}"""",
140-
s""""org.typelevel" %% "discipline-specs2_sjs1" % "${Versions.disciplineSpecs2}"""",
141-
s""""org.typelevel" %% "simulacrum-scalafix-annotations" % "${Versions.simulacrumScalafix}"""",
142-
s""""org.typelevel" %% "simulacrum-scalafix-annotations_sjs1" % "${Versions.simulacrumScalafix}"""",
143-
s""""org.typelevel" %% "cats-core" % "${Versions.cats}"""",
144-
s""""org.typelevel" %% "cats-core_sjs1" % "${Versions.cats}"""",
145-
s""""org.typelevel" %% "cats-free" % "${Versions.cats}"""",
146-
s""""org.typelevel" %% "cats-free_sjs1" % "${Versions.cats}"""",
147-
s""""org.typelevel" %% "cats-kernel" % "${Versions.cats}"""",
148-
s""""org.typelevel" %% "cats-kernel_sjs1" % "${Versions.cats}"""",
149-
s""""org.typelevel" %% "cats-kernel-laws" % "${Versions.cats}"""",
150-
s""""org.typelevel" %% "cats-kernel-laws_sjs1" % "${Versions.cats}"""",
151-
s""""org.typelevel" %% "cats-laws" % "${Versions.cats}"""",
152-
s""""org.typelevel" %% "cats-laws_sjs1" % "${Versions.cats}"""",
153-
s""""org.typelevel" %% "cats-testkit" % "${Versions.cats}"""",
154-
s""""org.typelevel" %% "cats-testkit_sjs1" % "${Versions.cats}"""",
155-
s""""org.typelevel" %% "cats-mtl" % "${Versions.catsMtl}"""",
156-
s""""org.typelevel" %% "cats-mtl_sjs1" % "${Versions.catsMtl}"""",
157-
s""""org.typelevel" %% "cats-mtl-laws" % "${Versions.catsMtl}"""",
158-
s""""org.typelevel" %% "cats-mtl-laws_sjs1" % "${Versions.catsMtl}"""",
159-
s""""org.typelevel" %% "coop" % "${Versions.coop}"""",
160-
s""""org.typelevel" %% "coop_sjs1" % "${Versions.coop}"""",
161-
s""""dev.zio" %% "izumi-reflect" % "${Versions.izumiReflect}"""",
162-
s""""org.scala-lang.modules" %% "scala-collection-compat" % "${Versions.scalaCollectionCompat}"""",
163-
)
164-
16597
private val baseCommand =
16698
"clean; set logLevel in Global := Level.Error; set updateOptions in Global ~= (_.withLatestSnapshots(false)); "
167-
++ s"""set dependencyOverrides in ThisBuild ++= ${dependencyOverrides.mkString("Seq(", ", ", ")")}; """
16899
++ s"++$compilerVersion!; "
169100

170101
override val testCommand =
@@ -186,7 +117,8 @@ final case class SbtCommunityProject(
186117
case _ => Nil
187118
extraSbtArgs ++ sbtProps ++ List(
188119
"-sbt-version", "1.4.7",
189-
"-Dsbt.supershell=false",
120+
"-Dsbt.supershell=false",
121+
s"-Ddotty.communitybuild.dir=$communitybuildDir",
190122
s"--addPluginSbtFile=$sbtPluginFilePath"
191123
)
192124

@@ -423,17 +355,15 @@ object projects:
423355
lazy val munit = SbtCommunityProject(
424356
project = "munit",
425357
sbtTestCommand = "testsJVM/test;testsJS/test;",
426-
// Hardcode the version to avoid having to deal with something set by sbt-dynver
427-
sbtPublishCommand = s"""set every version := "${Versions.munit}"; munitJVM/publishLocal; munitJS/publishLocal; munitScalacheckJVM/publishLocal; munitScalacheckJS/publishLocal; junit/publishLocal""",
358+
sbtPublishCommand = "munitJVM/publishLocal; munitJS/publishLocal; munitScalacheckJVM/publishLocal; munitScalacheckJS/publishLocal; junit/publishLocal",
428359
sbtDocCommand = "junit/doc; munitJVM/doc",
429360
dependencies = List(scalacheck)
430361
)
431362

432363
lazy val scodecBits = SbtCommunityProject(
433364
project = "scodec-bits",
434365
sbtTestCommand = "coreJVM/test;coreJS/test",
435-
// Hardcode the version to avoid having to deal with something set by sbt-git
436-
sbtPublishCommand = s"""set every version := "${Versions.scodecBits}"; coreJVM/publishLocal;coreJS/publishLocal""",
366+
sbtPublishCommand = "coreJVM/publishLocal;coreJS/publishLocal",
437367
sbtDocCommand = "coreJVM/doc",
438368
dependencies = List(munit)
439369
)
@@ -509,7 +439,7 @@ object projects:
509439
lazy val scalaCollectionCompat = SbtCommunityProject(
510440
project = "scala-collection-compat",
511441
sbtTestCommand = "compat30/test",
512-
sbtPublishCommand = s"""set every version := "${Versions.scalaCollectionCompat}"; compat30/publishLocal""",
442+
sbtPublishCommand = "compat30/publishLocal",
513443
)
514444

515445
lazy val verify = SbtCommunityProject(
@@ -528,7 +458,7 @@ object projects:
528458
lazy val disciplineMunit = SbtCommunityProject(
529459
project = "discipline-munit",
530460
sbtTestCommand = "test",
531-
sbtPublishCommand = s"""set every version := "${Versions.disciplineMunit}";coreJVM/publishLocal;coreJS/publishLocal""",
461+
sbtPublishCommand = "coreJVM/publishLocal;coreJS/publishLocal",
532462
dependencies = List(discipline, munit)
533463
)
534464

@@ -555,14 +485,14 @@ object projects:
555485
lazy val catsMtl = SbtCommunityProject(
556486
project = "cats-mtl",
557487
sbtTestCommand = "testsJVM/test;testsJS/test",
558-
sbtPublishCommand = s"""set every version := "${Versions.catsMtl}";coreJVM/publishLocal;coreJS/publishLocal;lawsJVM/publishLocal;lawsJS/publishLocal""",
488+
sbtPublishCommand = "coreJVM/publishLocal;coreJS/publishLocal;lawsJVM/publishLocal;lawsJS/publishLocal",
559489
dependencies = List(cats, disciplineMunit)
560490
)
561491

562492
lazy val coop = SbtCommunityProject(
563493
project = "coop",
564494
sbtTestCommand = "test",
565-
sbtPublishCommand = s"""set every version := "${Versions.coop}";coreJVM/publishLocal;coreJS/publishLocal""",
495+
sbtPublishCommand = "coreJVM/publishLocal;coreJS/publishLocal",
566496
dependencies = List(cats, catsMtl)
567497
)
568498

project/Build.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ object Build {
101101
if (isRelease) baseSbtDottyVersion else baseSbtDottyVersion + "-SNAPSHOT"
102102
}
103103

104+
val sbtCommunityBuildVersion = "0.1.0-SNAPSHOT"
105+
104106
val agentOptions = List(
105107
// "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"
106108
// "-agentpath:/home/dark/opt/yjp-2013-build-13072/bin/linux-x86-64/libyjpagent.so"
@@ -1272,6 +1274,45 @@ object Build {
12721274
}.dependsOn(compile in Compile).evaluated
12731275
)
12741276

1277+
lazy val `sbt-community-build` = project.in(file("sbt-community-build")).
1278+
enablePlugins(SbtPlugin).
1279+
settings(commonSettings).
1280+
settings(
1281+
name := "sbt-community-build",
1282+
version := sbtCommunityBuildVersion,
1283+
organization := "ch.epfl.lamp",
1284+
sbtTestDirectory := baseDirectory.value / "sbt-test",
1285+
scriptedLaunchOpts ++= Seq(
1286+
"-Dplugin.version=" + version.value,
1287+
"-Dplugin.scalaVersion=" + dottyVersion,
1288+
"-Dplugin.scalaJSVersion=" + scalaJSVersion,
1289+
"-Dplugin.sbtDottyVersion=" + sbtDottyVersion,
1290+
"-Ddotty.communitybuild.dir=" + baseDirectory.value / "target",
1291+
"-Dsbt.boot.directory=" + ((baseDirectory in ThisBuild).value / ".sbt-scripted").getAbsolutePath // Workaround sbt/sbt#3469
1292+
),
1293+
// Pass along ivy home and repositories settings to sbt instances run from the tests
1294+
scriptedLaunchOpts ++= {
1295+
val repositoryPath = (io.Path.userHome / ".sbt" / "repositories").absolutePath
1296+
s"-Dsbt.repository.config=$repositoryPath" ::
1297+
ivyPaths.value.ivyHome.map("-Dsbt.ivy.home=" + _.getAbsolutePath).toList
1298+
},
1299+
scriptedBufferLog := true,
1300+
scriptedBatchExecution := true,
1301+
scripted := scripted.dependsOn(
1302+
publishLocal in `scala3-sbt-bridge`,
1303+
publishLocal in `scala3-interfaces`,
1304+
publishLocal in `scala3-compiler-bootstrapped`,
1305+
publishLocal in `scala3-library-bootstrapped`,
1306+
publishLocal in `scala3-library-bootstrappedJS`,
1307+
publishLocal in `tasty-core-bootstrapped`,
1308+
publishLocal in `scala3-staging`,
1309+
publishLocal in `scala3-tasty-inspector`,
1310+
publishLocal in `scaladoc`,
1311+
publishLocal in `scala3-bootstrapped`,
1312+
publishLocal in `sbt-dotty`,
1313+
).evaluated
1314+
)
1315+
12751316
val prepareCommunityBuild = taskKey[Unit]("Publish local the compiler and the sbt plugin. Also store the versions of the published local artefacts in two files, community-build/{scala3-bootstrapped.version,sbt-dotty-sbt}.")
12761317

12771318
lazy val `community-build` = project.in(file("community-build")).
@@ -1289,13 +1330,16 @@ object Build {
12891330
(publishLocal in `sbt-dotty`).value
12901331
(publishLocal in `scala3-bootstrapped`).value
12911332
(publishLocal in `scala3-library-bootstrappedJS`).value
1333+
(publishLocal in `sbt-community-build`).value
12921334
// (publishLocal in `scala3-staging`).value
12931335
val pluginText =
12941336
s"""updateOptions in Global ~= (_.withLatestSnapshots(false))
12951337
|addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "$sbtDottyVersion")
1338+
|addSbtPlugin("ch.epfl.lamp" % "sbt-community-build" % "$sbtCommunityBuildVersion")
12961339
|addSbtPlugin("org.scala-js" % "sbt-scalajs" % "$scalaJSVersion")""".stripMargin
12971340
IO.write(baseDirectory.value / "sbt-dotty-sbt", pluginText)
12981341
IO.write(baseDirectory.value / "scala3-bootstrapped.version", dottyVersion)
1342+
IO.delete(baseDirectory.value / "dotty-community-build-deps") // delete any stale deps file
12991343
},
13001344
testOptions in Test += Tests.Argument(
13011345
TestFrameworks.JUnit,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
ThisBuild / scalaVersion := sys.props("plugin.scalaVersion")
2+
ThisBuild / organization := "org.example"
3+
4+
lazy val a = project
5+
.settings(
6+
name := "a",
7+
version := "0.4.1-SNAPSHOT",
8+
libraryDependencies := Seq(), // don't depend on scala-library
9+
)
10+
11+
lazy val b = project
12+
.settings(onlyThisTestResolverSettings)
13+
.settings(
14+
name := "b",
15+
libraryDependencies := Seq(organization.value %% "a" % "0.4.0-SNAPSHOT"),
16+
)
17+
18+
lazy val c = project
19+
.settings(onlyThisTestResolverSettings)
20+
.settings(
21+
name := "c",
22+
libraryDependencies := Seq(), // don't depend on scala-library
23+
).dependsOn(b)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import sbt._
2+
import Keys._
3+
4+
object ThisTestPlugin extends AutoPlugin {
5+
override def requires = plugins.IvyPlugin
6+
override def trigger = allRequirements
7+
8+
val thisTestIvyHome = settingKey[File]("Ivy home directory for artifacts published by this test")
9+
val thisTestResolver = settingKey[Resolver]("Resolver for artifacts published by this test")
10+
val deleteDepsFile = taskKey[Unit]("Deletes the dotty-community-build-deps dependency tracking file")
11+
12+
override val projectSettings = Seq(
13+
publishLocalConfiguration := publishLocalConfiguration.value.withResolverName("this-test")
14+
)
15+
16+
override val buildSettings = defaultThisTestSettings ++ Seq(
17+
resolvers += thisTestResolver.value
18+
)
19+
20+
def defaultThisTestSettings: Seq[Setting[_]] = {
21+
Seq(
22+
thisTestIvyHome := (LocalRootProject / target).value / "ivy-cache",
23+
thisTestResolver := Resolver.file("this-test", thisTestIvyHome.value / "local")(Resolver.ivyStylePatterns),
24+
deleteDepsFile := IO.delete(file(sys.props("dotty.communitybuild.dir")) / "dotty-community-build-deps"),
25+
)
26+
}
27+
28+
object autoImport {
29+
def onlyThisTestResolverSettings: Seq[Setting[_]] = Seq(
30+
externalResolvers := thisTestResolver.value :: Nil
31+
)
32+
}
33+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
addSbtPlugin("ch.epfl.lamp" % "sbt-community-build" % sys.props("plugin.version"))
2+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.sbtDottyVersion"))
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
> deleteDepsFile
2+
> reload
3+
4+
> a/publishLocal
5+
6+
-> c/update
7+
8+
> reload
9+
10+
> c/update
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
ThisBuild / scalaVersion := sys.props("plugin.scalaVersion")
2+
ThisBuild / organization := "org.example"
3+
4+
lazy val a = project
5+
.settings(
6+
name := "a",
7+
version := "0.2.1-SNAPSHOT",
8+
libraryDependencies := Seq(), // don't depend on scala-library
9+
)
10+
11+
lazy val b = project
12+
.settings(
13+
name := "b",
14+
version := "1.2.1-SNAPSHOT",
15+
libraryDependencies := Seq(), // don't depend on scala-library
16+
)
17+
18+
lazy val c = project
19+
.settings(onlyThisTestResolverSettings)
20+
.settings(
21+
name := "c",
22+
libraryDependencies := Seq(
23+
organization.value %% "a" % "0.2.0-SNAPSHOT",
24+
organization.value %% "b" % "1.2.0-SNAPSHOT",
25+
),
26+
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import sbt._
2+
import Keys._
3+
4+
object ThisTestPlugin extends AutoPlugin {
5+
override def requires = plugins.IvyPlugin
6+
override def trigger = allRequirements
7+
8+
val thisTestIvyHome = settingKey[File]("Ivy home directory for artifacts published by this test")
9+
val thisTestResolver = settingKey[Resolver]("Resolver for artifacts published by this test")
10+
val deleteDepsFile = taskKey[Unit]("Deletes the dotty-community-build-deps dependency tracking file")
11+
12+
override val projectSettings = Seq(
13+
publishLocalConfiguration := publishLocalConfiguration.value.withResolverName("this-test")
14+
)
15+
16+
override val buildSettings = defaultThisTestSettings ++ Seq(
17+
resolvers += thisTestResolver.value
18+
)
19+
20+
def defaultThisTestSettings: Seq[Setting[_]] = {
21+
Seq(
22+
thisTestIvyHome := (LocalRootProject / target).value / "ivy-cache",
23+
thisTestResolver := Resolver.file("this-test", thisTestIvyHome.value / "local")(Resolver.ivyStylePatterns),
24+
deleteDepsFile := IO.delete(file(sys.props("dotty.communitybuild.dir")) / "dotty-community-build-deps"),
25+
)
26+
}
27+
28+
object autoImport {
29+
def onlyThisTestResolverSettings: Seq[Setting[_]] = Seq(
30+
externalResolvers := thisTestResolver.value :: Nil
31+
)
32+
}
33+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
addSbtPlugin("ch.epfl.lamp" % "sbt-community-build" % sys.props("plugin.version"))
2+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.sbtDottyVersion"))
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
> deleteDepsFile
2+
> reload
3+
4+
> a/publishLocal
5+
> b/publishLocal
6+
7+
-> c/update
8+
9+
> reload
10+
11+
> c/update

0 commit comments

Comments
 (0)