Skip to content

Commit b50df89

Browse files
committed
Split the scalalib .sjsir files in a separate artifact scalajs-scalalib.
Previously, the `.sjsir` files of the scalalib (and libraryAux) were bundled inside scalajs-library.jar. With the plan to drop forward binary compatibility of the upstream scala-library.jar, this model will not work anymore. We now publish those `.sjsir` files in their own artifact `scalajs-scalalib.jar`. It is versioned *both* with the Scala version number and the Scala.js version number, in a way that will allow Ivy resolution to pick the right one. At the POM level, `scalajs-scalalib` depends on `scalajs-javalib`. `scalajs-library` depends on the other two. However, in terms of *actual* content dependencies, as is, `scalajs-scalalib` also depends on `scalajs-library`. If the former is present on a classpath but not (a recent enough version of) the latter, linking errors can appear. This should not be an issue because any real build that depends on `scalajs-scalalib` will also depend on `scalajs-library`. Moreover, if a more recent `scalajs-scalalib` is picked up by Ivy resolution that implicitly depends on a more recent `scalajs-library`, the library that introduces that dependency would also *explicitly* depend on the more recent `scalajs-library`, and so the latter would also be picked up by Ivy resolution. The sbt plugin explicitly adds a dependency on the `scalajs-scalalib` with a Scala/Scala.js version combination that matches `scalaVersion` and `scalaJSVersion`. This way, if it uses a Scala.js version that was built for Scala 2.13.12 but it itself uses Scala 2.13.15, it will get the back-published `scalajs-library` built for Scala 2.13.15.
1 parent 95346cf commit b50df89

File tree

4 files changed

+92
-39
lines changed

4 files changed

+92
-39
lines changed

build.sbt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ val sbtPlugin = Build.plugin
1414
val javalibintf = Build.javalibintf
1515
val javalibInternal = Build.javalibInternal
1616
val javalib = Build.javalib
17-
val scalalib = Build.scalalib
17+
val scalalibInternal = Build.scalalibInternal
1818
val libraryAux = Build.libraryAux
19+
val scalalib = Build.scalalib
1920
val library = Build.library
2021
val testInterface = Build.testInterface
2122
val testBridge = Build.testBridge

project/Build.scala

Lines changed: 79 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ object MyScalaJSPlugin extends AutoPlugin {
197197
*/
198198
libraryDependencies ~= { libDeps =>
199199
val blacklist =
200-
Set("scalajs-compiler", "scalajs-library", "scalajs-test-bridge")
200+
Set("scalajs-compiler", "scalajs-library", "scalajs-scalalib", "scalajs-test-bridge")
201201
libDeps.filterNot(dep => blacklist.contains(dep.name))
202202
},
203203

@@ -638,7 +638,7 @@ object Build {
638638
* - `"semver-spec"` for artifacts whose public API can only break in major releases (e.g., `library`)
639639
*
640640
* At the moment, we only set the version scheme for artifacts in the
641-
* "library ecosystem", i.e., scalajs-javalib scalajs-library,
641+
* "library ecosystem", i.e., scalajs-javalib, scalajs-scalalib, scalajs-library,
642642
* scalajs-test-interface, scalajs-junit-runtime and scalajs-test-bridge.
643643
* Artifacts of the "tools ecosystem" do not have a version scheme set, as
644644
* the jury is still out on what is the best way to specify them.
@@ -749,17 +749,17 @@ object Build {
749749
}
750750
}
751751

752-
/** Depends on library and, by artificial transitivity, on the javalib. */
752+
/** Depends on library and, by artificial transitivity, on the javalib and scalalib. */
753753
def dependsOnLibrary2_12: Project = {
754754
val library = LocalProject("library2_12")
755755

756756
// Add a real dependency on the library
757757
val project1 = project
758758
.dependsOn(library)
759759

760-
/* Because the javalib's exportsJar is false, but its actual products are
761-
* only in its jar, we must manually add the jar on the internal
762-
* classpath.
760+
/* Because the javalib's and scalalib's exportsJar is false, but their
761+
* actual products are only in their jar, we must manually add the jars
762+
* on the internal classpath.
763763
* Once published, only jars are ever used, so this is fine.
764764
*/
765765
if (isGeneratingForIDE) {
@@ -772,6 +772,12 @@ object Build {
772772
Test / internalDependencyClasspath +=
773773
(javalib / Compile / packageBin).value,
774774
)
775+
.settings(
776+
Compile / internalDependencyClasspath +=
777+
(scalalib.v2_12 / Compile / packageBin).value,
778+
Test / internalDependencyClasspath +=
779+
(scalalib.v2_12 / Compile / packageBin).value,
780+
)
775781
}
776782
}
777783

@@ -818,28 +824,36 @@ object Build {
818824
}
819825
}
820826

821-
/** Depends on library and, by transitivity, on the javalib. */
827+
/** Depends on library and, by artificial transitivity, on the javalib and scalalib. */
822828
def dependsOnLibrary: MultiScalaProject = {
823829
// Add a real dependency on the library
824830
val project1 = project
825831
.dependsOn(library)
826832

827-
/* Because the javalib's exportsJar is false, but its actual products are
828-
* only in its jar, we must manually add the jar on the internal
829-
* classpath.
833+
/* Because the javalib's and scalalib's exportsJar is false, but their
834+
* actual products are only in their jar, we must manually add the jars
835+
* on the internal classpath.
830836
* Once published, only jars are ever used, so this is fine.
831837
*/
832838
if (isGeneratingForIDE) {
833839
project1
834840
} else {
835-
// Actually add classpath dependencies on the javalib jar
841+
// Actually add classpath dependencies on the javalib and scalalib jars
836842
project1
837843
.settings(
838844
Compile / internalDependencyClasspath +=
839845
(javalib / Compile / packageBin).value,
840846
Test / internalDependencyClasspath +=
841847
(javalib / Compile / packageBin).value,
842848
)
849+
.zippedSettings(scalalib) { scalalib =>
850+
Def.settings(
851+
Compile / internalDependencyClasspath +=
852+
(scalalib / Compile / packageBin).value,
853+
Test / internalDependencyClasspath +=
854+
(scalalib / Compile / packageBin).value,
855+
)
856+
}
843857
}
844858
}
845859

@@ -919,7 +933,7 @@ object Build {
919933
linkerInterface, linkerInterfaceJS, linker, linkerJS,
920934
testAdapter,
921935
javalibintf,
922-
javalibInternal, javalib, scalalib, libraryAux, library,
936+
javalibInternal, javalib, scalalibInternal, libraryAux, scalalib, library,
923937
testInterface, jUnitRuntime, testBridge, jUnitPlugin, jUnitAsyncJS,
924938
jUnitAsyncJVM, jUnitTestOutputsJS, jUnitTestOutputsJVM,
925939
helloworld, reversi, testingExample, testSuite, testSuiteJVM,
@@ -1358,12 +1372,14 @@ object Build {
13581372
// JS libs
13591373
publishLocal in javalib,
13601374

1375+
publishLocal in scalalib.v2_12,
13611376
publishLocal in library.v2_12,
13621377
publishLocal in testInterface.v2_12,
13631378
publishLocal in testBridge.v2_12,
13641379
publishLocal in jUnitRuntime.v2_12,
13651380
publishLocal in irProjectJS.v2_12,
13661381

1382+
publishLocal in scalalib.v2_13,
13671383
publishLocal in library.v2_13,
13681384
publishLocal in testInterface.v2_13,
13691385
publishLocal in testBridge.v2_13,
@@ -1478,7 +1494,7 @@ object Build {
14781494
* copied from `javalibInternal`.
14791495
*
14801496
* This the "public" version of the javalib, as depended on by the `library`
1481-
* and published on Maven.
1497+
* and `scalalib`, and published on Maven.
14821498
*/
14831499
lazy val javalib: Project = Project(
14841500
id = "javalib", base = file("javalib-public")
@@ -1506,8 +1522,13 @@ object Build {
15061522
},
15071523
)
15081524

1509-
lazy val scalalib: MultiScalaProject = MultiScalaProject(
1510-
id = "scalalib", base = file("scalalib")
1525+
/** The project that actually compiles the `scalalib`, but which is not
1526+
* exposed.
1527+
*
1528+
* Instead, its products are copied in `scalalib`.
1529+
*/
1530+
lazy val scalalibInternal: MultiScalaProject = MultiScalaProject(
1531+
id = "scalalibInternal", base = file("scalalib")
15111532
).enablePlugins(
15121533
MyScalaJSPlugin
15131534
).settings(
@@ -1523,7 +1544,7 @@ object Build {
15231544
s"https://raw.githubusercontent.com/scala/scala/v${scalaVersion.value}/src/library/")
15241545
option ++ prev
15251546
},
1526-
name := "Scala library for Scala.js",
1547+
name := "scalajs-scalalib-internal",
15271548
publishArtifact in Compile := false,
15281549
NoIDEExport.noIDEExportSettings,
15291550
delambdafySetting,
@@ -1669,12 +1690,54 @@ object Build {
16691690
recompileAllOrNothingSettings,
16701691
).withScalaJSCompiler.dependsOnLibraryNoJar
16711692

1693+
/** An empty project, without source nor dependencies (other than the javalib),
1694+
* whose products are copied from `scalalibInternal` and `libraryAux`.
1695+
*
1696+
* This the "public" version of the scalalib, as depended on by the `library`
1697+
* and published on Maven.
1698+
*/
1699+
lazy val scalalib: MultiScalaProject = MultiScalaProject(
1700+
id = "scalalib", base = file("scalalib-public")
1701+
).dependsOn(
1702+
javalib,
1703+
).settings(
1704+
commonSettings,
1705+
name := "scalajs-scalalib",
1706+
publishSettings(Some(VersionScheme.BreakOnMajor)),
1707+
1708+
/* The scalalib has a special version number that encodes both the Scala
1709+
* version and the Scala.js version. This allows us to back-publish for
1710+
* newer versions of Scala and older versions of Scala.js. The Scala
1711+
* version comes first so that Ivy resolution will choose 2.13.20+1.15.0
1712+
* over 2.13.18+1.16.0. The former might not be as optimized as the
1713+
* latter, but at least it will contain all the binary API that might be
1714+
* required.
1715+
*/
1716+
version := scalaVersion.value + "+" + scalaJSVersion,
1717+
1718+
exportJars := false, // very important, otherwise there's a cycle with the `library`
1719+
).zippedSettings(Seq("scalalibInternal", "libraryAux"))(localProjects =>
1720+
inConfig(Compile)(Seq(
1721+
// Use the .sjsir files from scalalibInternal and libraryAux (but not the .class files)
1722+
Compile / packageBin / mappings := {
1723+
val scalalibInternalMappings = (localProjects(0) / packageBin / mappings).value
1724+
val libraryAuxMappings = (localProjects(1) / packageBin / mappings).value
1725+
val allMappings = scalalibInternalMappings ++ libraryAuxMappings
1726+
allMappings.filter(_._2.endsWith(".sjsir"))
1727+
},
1728+
))
1729+
)
1730+
16721731
lazy val library: MultiScalaProject = MultiScalaProject(
16731732
id = "library", base = file("library")
16741733
).enablePlugins(
16751734
MyScalaJSPlugin
16761735
).dependsOn(
1736+
// Project dependencies
16771737
javalibintf % Provided, javalib,
1738+
).dependsOn(
1739+
// MultiScalaProject dependencies
1740+
scalalib,
16781741
).settings(
16791742
commonSettings,
16801743
publishSettings(Some(VersionScheme.BreakOnMajor)),
@@ -1727,25 +1790,6 @@ object Build {
17271790
*/
17281791
dependencyClasspath in doc ++= exportedProducts.value,
17291792
))
1730-
).zippedSettings(Seq("scalalib", "libraryAux"))(localProjects =>
1731-
inConfig(Compile)(Seq(
1732-
/* Add the .sjsir files from other lib projects
1733-
* (but not .class files)
1734-
*/
1735-
mappings in packageBin := {
1736-
val libraryMappings = (mappings in packageBin).value
1737-
1738-
val filter = ("*.sjsir": NameFilter)
1739-
1740-
val otherProducts = (
1741-
(products in localProjects(0)).value ++
1742-
(products in localProjects(1)).value)
1743-
val otherMappings =
1744-
otherProducts.flatMap(base => Path.selectSubpaths(base, filter))
1745-
1746-
libraryMappings ++ otherMappings
1747-
},
1748-
))
17491793
).withScalaJSCompiler
17501794

17511795
// The Scala.js version of sbt-testing-interface

sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPluginInternal.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,8 @@ private[sbtplugin] object ScalaJSPluginInternal {
795795
scalaOrg %% "scala3-library_sjs1" % scalaV,
796796
/* scala3-library_sjs1 depends on some version of scalajs-library_2.13,
797797
* but we bump it to be at least scalaJSVersion.
798+
* (It will also depend on some version of scalajs-scalalib_2.13,
799+
* but we do not have to worry about that here.)
798800
*/
799801
"org.scala-js" % "scalajs-library_2.13" % scalaJSVersion,
800802
"org.scala-js" % "scalajs-test-bridge_2.13" % scalaJSVersion % "test"
@@ -803,6 +805,12 @@ private[sbtplugin] object ScalaJSPluginInternal {
803805
prev ++ Seq(
804806
compilerPlugin("org.scala-js" % "scalajs-compiler" % scalaJSVersion cross CrossVersion.full),
805807
"org.scala-js" %% "scalajs-library" % scalaJSVersion,
808+
/* scalajs-library depends on some version of scalajs-scalalib,
809+
* but we want to make sure to bump it to be at least the one
810+
* of our own `scalaVersion` (which would have back-published in
811+
* the meantime).
812+
*/
813+
"org.scala-js" %% "scalajs-scalalib" % s"$scalaV+$scalaJSVersion",
806814
"org.scala-js" %% "scalajs-test-bridge" % scalaJSVersion % "test"
807815
)
808816
}

scripts/publish.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ fi
1010
SUFFIXES="2_12 2_13"
1111

1212
JAVA_LIBS="javalibintf javalib"
13-
COMPILER="compiler jUnitPlugin"
13+
FULL_SCALA_LIBS="compiler jUnitPlugin scalalib"
1414
JS_LIBS="library irJS linkerInterfaceJS linkerJS testInterface testBridge jUnitRuntime"
1515
JVM_LIBS="ir linkerInterface linker testAdapter"
1616
SCALA_LIBS="$JS_LIBS $JVM_LIBS"
@@ -22,10 +22,10 @@ for p in $JAVA_LIBS; do
2222
done
2323
$CMD $ARGS
2424

25-
# Publish compiler
25+
# Publish artifacts built with the full Scala version
2626
for s in $SUFFIXES; do
2727
ARGS=""
28-
for p in $COMPILER; do
28+
for p in $FULL_SCALA_LIBS; do
2929
ARGS="$ARGS +$p$s/publishSigned"
3030
done
3131
$CMD $ARGS

0 commit comments

Comments
 (0)