Skip to content

Commit

Permalink
Overhaul meta-build docs (#3415)
Browse files Browse the repository at this point in the history
Previous docs were kind of confusing, because they tried to demonstrate
two use cases at once:

* Sharing of library configs between your `build.sc` and application
code
* Sharing of source code between your `build.sc` and application code
(except it never really showed the latter, so it only showed
`mill-build/src` as a place you could put `.scala` files so your
`build.sc` could use them

This PR breaks it up into two separate examples: one example
illustrating how to share library configs, one example illustrating how
to share source files. The original example wasn't _long_, but it was
_messy_, and both new examples are much shorter and more focused on
exactly what they want to show
  • Loading branch information
lihaoyi authored Aug 23, 2024
1 parent 9253189 commit fe8c347
Show file tree
Hide file tree
Showing 14 changed files with 158 additions and 95 deletions.
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/Tasks.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,4 @@ tasks and outputs, you do so with your own evaluator command.

=== Using ScalaModule.run as a task

include::example/misc/5-module-run-task.adoc[]
include::example/tasks/11-module-run-task.adoc[]
12 changes: 8 additions & 4 deletions docs/modules/ROOT/pages/The_Mill_Meta_Build.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Meta-builds are recursive, which means, it can itself have a nested meta-builds,

To run a task on a meta-build, you specifying the `--meta-level` option to select the meta-build level.

== Example: Format the `build.sc`
== Autoformatting the `build.sc`

As an example of running a task on the meta-build, you can format the `build.sc` with Scalafmt.
Everything is already provided by Mill.
Expand All @@ -27,7 +27,7 @@ $ mill --meta-level 1 mill.scalalib.scalafmt.ScalafmtModule/reformatAll sources
* `mill.scalalib.scalafmt.ScalafmtModule/reformatAll` is a generic task to format scala source files with Scalafmt. It requires the targets that refer to the source files as argument
* `sources` this selects the `sources` targets of the meta-build, which at least contains the `build.sc`.

== Example: Find plugin updates
== Finding plugin updates

Mill plugins are defined as `ivyDeps` in the meta-build.
Hence, you can easily search for updates with the external `mill.scalalib.Dependency` module.
Expand All @@ -39,6 +39,10 @@ Found 1 dependency update for
de.tototec:de.tobiasroeser.mill.vcs.version_mill0.11_2.13 : 0.3.1-> 0.4.0
----

== Example: Customizing the Meta-Build
== Sharing Libraries between `build.sc` and Application Code

include::example/misc/4-mill-build-folder.adoc[]
include::example/misc/4-meta-build.adoc[]

== Sharing Source Code between `build.sc` and Application Code

include::example/misc/5-meta-shared-sources.adoc[]
86 changes: 86 additions & 0 deletions example/misc/4-meta-build/build.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import $meta._
import mill._, scalalib._
import scalatags.Text.all._

object foo extends RootModule with ScalaModule {
def scalaVersion = "2.13.4"
def ivyDeps = Agg(
ivy"com.lihaoyi::scalatags:${millbuild.DepVersions.scalatagsVersion}",
ivy"com.lihaoyi::os-lib:0.9.1"
)

def htmlSnippet = T{ h1("hello").toString }
def resources = T.sources{
os.write(T.dest / "snippet.txt", htmlSnippet())
super.resources() ++ Seq(PathRef(T.dest))
}
}

// This example illustrates usage of the `mill-build/` folder. Mill's `build.sc`
// file and it's `import $file` and `$ivy` are a shorthand syntax for defining
// a Mill `ScalaModule`, with sources and `ivyDeps` and so on, which is
// compiled and executed to perform your build. This "meta-build" module lives in
// `mill-build/`, and can be enabled via the `import $meta._` statement above.

/** See Also: mill-build/build.sc */
/** See Also: src/Foo.scala */

// Typically, if you wanted to use a library such as Scalatags in both your `build.sc`
// as well as your application code, you would need to duplicate the version of Scalatags
// used in both your `import $ivy` statement as well as your `def ivyDeps` statement.
// The meta-build lets us avoid this duplication: we use `generatedSources` in
// `mill-build/build.sc` to create a `DepVersions` object that the `build.sc` can use to pass the
// `scalatagsVersion` to the application without having to copy-paste the
// version and keep the two copies in sync.
//
// When we run the application, we can see both the `Build-time HTML snippet` and the
// `Run-time HTML snippet` being printed out, both using the same version of Scalatags
// but performing the rendering of the HTML in two different places:

/** Usage
> mill compile
...
> mill run
Build-time HTML snippet: <h1>hello</h1>
Run-time HTML snippet: <p>world</p>
> mill show assembly
".../out/assembly.dest/out.jar"
> ./out/assembly.dest/out.jar # mac/linux
Build-time HTML snippet: <h1>hello</h1>
Run-time HTML snippet: <p>world</p>
*/


// This example is trivial, but in larger builds there may be much more scenarios
// where you may want to keep the libraries used in your `build.sc` and the libraries
// used in your application code consistent. With the `mill-build/build.sc` configuration
// enabled by `import $meta._`, this becomes possible.
//
// You can customize the `mill-build/` module with more flexibility than is
// provided by `import $ivy` or `import $file`, overriding any tasks that are
// present on a typical `ScalaModule`: `scalacOptions`, `generatedSources`, etc.
// This is useful for large projects where the build itself is a non-trivial
// module which requires its own non-trivial customization.


// You can also run tasks on the meta-build by using the `--meta-level`
// cli option.

/** Usage
> mill --meta-level 1 show sources
[
.../build.sc",
.../mill-build/src"
]
> mill --meta-level 2 show sources
.../mill-build/build.sc"
*/
8 changes: 8 additions & 0 deletions example/misc/4-meta-build/src/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package foo
import scalatags.Text.all._
object Foo {
def main(args: Array[String]): Unit = {
println("Build-time HTML snippet: " + os.read(os.resource / "snippet.txt"))
println("Run-time HTML snippet: " + p("world"))
}
}
81 changes: 0 additions & 81 deletions example/misc/4-mill-build-folder/build.sc

This file was deleted.

9 changes: 0 additions & 9 deletions example/misc/4-mill-build-folder/src/Foo.scala

This file was deleted.

45 changes: 45 additions & 0 deletions example/misc/5-meta-shared-sources/build.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import $meta._
import mill._, scalalib._

object foo extends RootModule with ScalaModule {
def scalaVersion = millbuild.ScalaVersion.myScalaVersion
def sources = T.sources{
super.sources() ++ Seq(PathRef(millSourcePath / "mill-build" / "src"))
}
}

/** See Also: mill-build/build.sc */
/** See Also: mill-build/src/ScalaVersion.scala */
/** See Also: src/Foo.scala */

// This example shows another use of the Mill meta-build: because the meta-build
// is just a normal Scala module (with some special handling for the `.sc` file extension),
// your `build.sc` file can take normal Scala source files that are placed in `mill-build/src/`
// This allows us to share those sources with your application code by appropriately
// configuring your `def sources` in `build.sc` above.

/** Usage
> mill run
scalaVersion: 2.13.10
*/

// Here we only share a trivial `def myScalaVersion = "2.13.10"` definition between
// the `build.sc` and application code. But this ability to share arbitrary code between
// your application and your build opens up a lot of possibilities:
//
// * Run-time initialization logic can be placed in `mill-build/src/`, shared with the
// `build.sc` and used to perform build-time preprocessing, reducing the work needing
// to be done at application start and reducing startup latencies.
//
// * Build-time `build.sc` logic can be placed in `mill-build/src/` and shared with your
// run-time application code, allowing greater flexibility in e.g. exercising the shared
// logic in response to user input that's not available at build time.
//
// In general, the Mill meta-build with its `mill-build` folder is meant to blur the line
// between build-time logic and run-time logic. Often you want to do the same thing in both
// places: at build-time where possible to save time at runtime, and at runtime where
// necessary to make use of user input. With the Mill meta-build, you can write logic
// comprising the same source code and using the same libraries in both environments,
// giving you flexibility in where your logic ends up running
3 changes: 3 additions & 0 deletions example/misc/5-meta-shared-sources/mill-build/build.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import mill._

object millbuild extends MillBuildRootModule
7 changes: 7 additions & 0 deletions example/misc/5-meta-shared-sources/src/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package foo

object Foo {
def main(args: Array[String]): Unit = {
println("scalaVersion: " + millbuild.ScalaVersion.myScalaVersion)
}
}
File renamed without changes.

0 comments on commit fe8c347

Please sign in to comment.