This project explains how to invoke the MDoc Markdown pre-processor that transforms Markdown code fences tagged with
mdoc
. It shows how to set up the class path so that one can process Scala 3 code. To do this case must be taken
in replacing MDoc's Scala 3 compiler dependencies.
We should invoke the MDoc pre-processor using only the minimum MDoc required libraries. Here we show how to download and call MDoc with these libraries. Care must be taken to set up the compiler libraries correctly otherwise compilation errors will occur.
To compile the documentation with MDoc execute this command:
./mill -i tutorial.mdoc
or this command:
./mill -i --watch tutorial.mdoc
The code shown here was "lifted" from the Mill-MDoc plugin. This was born of an attempt to correct the
"bug" in the mill-mdoc plugin. The def mdoc : T[PathRef]
shown below is an altered version of the plugin's.
For information on the diagnosis see issue #702
MDoc has its own dependencies on the Scala compiler and uses those. To use a later version of Scala 3, we need to download that specific version of the compiler. If we don't, a compilation error will occur because earlier versions of the compiler cannot process the newer format. Here is an example of the error:
[40/40] tutorial.mdoc
info: Compiling 1 file to /home/user/VSCodeProjects/mdocMill/out/tutorial/mdoc.dest
error:
test.md:2 (mdoc generated code)
package scala.compiletime does not have a member method summonFrom
object MdocSession extends _root_.mdoc.internal.document.DocumentBuilder {
...
We must therefore first exclude the Scala 3 compiler related libraries that are dependencies of MDoc. Then we add the required version of the Scala aversion, whicc is the one we are using in our sourcecode. Here is the relevant code snippet of the build script:
def mdocDep: Task[Agg[Dep]] = T.task{
Agg(
ivy"org.scalameta::mdoc:${scalaMdocVersion()}"
.exclude("org.scala-lang" -> "scala3-compiler_3")
.exclude("org.scala-lang" -> "scala3-library_3"),
ivy"org.scala-lang::scala3-compiler:${scalaVersion()}"
)
}
def mDocLibs = T{ resolveDeps(mdocDep) }
There are several ways in which to exclude dependent libraries (see the documentation). They can be done:
- By organization and name;
- By organization only];
- By name only.
To find out what to exclude we looked at the dependencies in the Maven repository here. We note that the scala3-library_3
is a dependency of the compiler, and it is therefore not strictly necessary to exclude that. We then add the scala3-compiler_3
using a version of our choice.
Once we have the libraries for MDoc we can invoke it. The relevant code snippet is shown below:
def mdoc : T[PathRef] = T {
val rp = mDocLibs().map(_.path)
val cp = runClasspath().map(_.path)
// println(toArgumentDebug(rp))
// println(toArgument(cp))
// Set-up parameters to execute MDoc
val dir = T.dest.toIO.getAbsolutePath
val dirParams = mdocSources().map(pr => Seq(
"--classpath", toArgument(cp),
"--in", pr.path.toIO.getAbsolutePath,
"--out", dir)
).iterator.flatten.toSeq
// Execute MDoc, only pass the MDoc class path to execute the MDoc compiler
Jvm.runLocal("mdoc.Main", rp, dirParams)
PathRef(T.dest)
}
Note that the Jvm.runLocal("mdoc.Main", rp, dirParams)
uses the MDoc libraries and the MDoc compiler compiles the code
using the project's runClasspath()
. If this is different from the compileClasspath()
, then the compilation
dependencies should be used.
We did alternative tests to download directly via Coursier. We can do this, but we need to select the correct binary
compatible library. For 2.12 we add no extension, for 2.13 we add _2.13 and for 3 we add _3. This allows using
libraries that are published for various versions of Scala. However, using Coursier's ResolutionParams
does
not work.
To see how Mill parsers and changes the library names see Mill's
ZincWorkerAPI.Util.scalaBinaryVersion(scalaVersion: String)
scalalib.Lib.depToDependency
scalalib.Dep.toDependency
scalalib.CoursierModule.resolveDeps
on how Mill parsers and sets the correct library name using the expected conventions. Here are the references:
Here is the failed attempt:
def mDocLibsTest =
T{
import coursier._
import coursier.params._
val params = ResolutionParams()
// .withScalaVersion(ScalaVersion)
.withScalaVersion("3.1.3") // Download ok with :: but gets version 2.13?
// .withScalaVersion("2.13.8") // Download ok with ::
// For Scala 2 set explicitly Ok
// val files = Fetch().addDependencies(dep"org.scalameta::mdoc:2.3.3").run()
// For Scala 3 set explicitly Ok
// val files = Fetch().addDependencies(dep"org.scalameta:mdoc_3:2.3.3").run()
val files = Fetch()
.addDependencies(dep"org.scalameta::mdoc:2.3.3") // uses the latest Scala 2 version
.withResolutionParams(params)
.run()
val pathRefs = files.map(f => PathRef(os.Path(f)))
printf(pathRefs.mkString(","))
Agg(pathRefs : _*)
}