Modules live under modules/. Each sub-directory there has a
corresponding mill module definition in build.sc (but for integration).
Most of the code currently lives in the build module.
The cli module depends on build, gets
packaged as a native-image executable, and distributed as scala-cli binary.
The other modules are either:
- integration tests
- utility modules, that
buildeither:- depends on
- fetches at run-time.
These are:
runner: simple app that starts a main class, catches any exception it throws and pretty-prints it.test-runner: finds test frameworks, test suites, and runs themtasty-lib: edits file names in.tastyfiles
The tests live either in:
build: unit testsintegration: integration tests
Run unit tests with
./mill 'build[_].test'Run integration tests with a JVM-based scala-cli with
./mill integration.test.jvmRun integration tests with a native-image-based scala-cli with
./mill integration.test.nativeWe roughly go from user inputs to byte code through 3 classes:
Inputs: ADT for input files / directories.Sources: processed sources, ready to be passed to scalacBuild: compilation result: success or failure.
Most commands
- take the arguments passed on the command-line: we have an
Array[String] - check whether each of them is a
.scalafile, an.scfile, a directory, …: we get anInputsinstance - reads the directories, the
.scala/.scfiles: we get aSourcesinstance - compile those sources: we get a
Buildinstance - do something with the build output (run it, run tests, package it, …)
In watch mode, we loop over the last 3 steps (Inputs is computed only once, the rest is re-computed upon file change).
Some input files cannot be passed as is to scalac, if they are scripts (.sc files), which contain top-level statements
Scripts get wrapped. If the script a/b/foo.sc contains
val n = 2we compile it as
package a.b
object foo {
val n = 2
def main(args: Array[String]): Unit = ()
}Basically,
- its directory dictates its package
- we put its sources as is in an object
- we add a
mainmethod
The source generation changes:
- file names, which now correspond to the directory where we write generated sources
- positions, when we wrap code (for
.scfiles)
As a consequence, some build outputs contains wrong paths or positions:
- diagnostics (warning and error messages) contain file paths and positions, used in reporting
- byte code contains file names and line numbers, that are used in stack traces
- semantic DBs contain relative file paths and positions, used by IDEs
- TASTy files contain relative file paths, used in pretty stack traces
We post-process those build outputs, to adjust positions and file paths of the generated sources: various "mappings" are computed out of the generated sources list, and are used to adjust:
- diagnostics: done in memory, right before printing diagnostics
- byte code: done using the ASM library
- semantic DBs: we parse the semantic DBs, edit them in memory, and write them back on disk
- TASTy files: we partly parse them in memory, edit names that contain source file paths, and write them back on disk
- Version Synchronization:
scalajs-cliwill be published with the same version as Scala.js version, for example1.13.0. - Updates & Fixes: For any subsequent fixes or patches in
scalajs-cli, we will append a numeric value to the end, like1.13.0.1. - GitHub Uploads
- Native Launchers: With the patch release of
scalajs-cli, native launchers are automatically uploaded to both versions, for example1.13.0.1and1.13.0tags on GitHub. - For instance: For release
1.13.0.2, the launchers are uploaded to tags1.13.0.2and1.13.0.
- Native Launchers: With the patch release of
- ScalaCli dependency to
scalajs-cli:- For Coursier to retrieve the most recent scalajs-cli for a specific Scala.js version, the version is set
as
org.virtuslab:scalajscli_2.13:{Scala.js version}+. For exampleorg.virtuslab:scalajscli_2.13:1.13.0+. - Native Version Download:
- The native version is downloaded from the Scala.js version tag. If there are updates or fixes to the
native
scalajs-clilaunchers, the updated launchers are uploaded to the1.13.0tag during the1.13.0.1publishing.
- The native version is downloaded from the Scala.js version tag. If there are updates or fixes to the
native
- For Coursier to retrieve the most recent scalajs-cli for a specific Scala.js version, the version is set
as