Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul meta-build docs #3415

Merged
merged 2 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
}
Loading