Skip to content

Commit

Permalink
Add example/basic/2-custom-build-logic (#2559)
Browse files Browse the repository at this point in the history
This is a minimal example meant to demonstrate how including custom
logic in your Mill build works. This is notable enough that it's worth
calling out up-front, rather than waiting until the more detailed
discussion of the same topics in `example/scalabuilds/2-custom-tasks`
  • Loading branch information
lihaoyi committed May 30, 2023
1 parent bf2699c commit ef6dfa9
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/Builtin_Commands.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ It prompts you to enter project name and creates a folder with that name.
You can use it to quickly generate a starter project.
There are lots of templates out there for many frameworks and tools!

include::example/basic/3-builtin-commands.adoc[]
include::example/basic/4-builtin-commands.adoc[]

== visualize

Expand Down
6 changes: 5 additions & 1 deletion docs/modules/ROOT/pages/Intro_to_Mill.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@ Mill build tool works.

include::example/basic/1-simple-scala.adoc[]

== Custom Build Logic

include::example/basic/2-custom-build-logic.adoc[]

== Multi-Module Project

include::example/basic/2-multi-module.adoc[]
include::example/basic/3-multi-module.adoc[]


== Watch and Re-evaluate
Expand Down
61 changes: 61 additions & 0 deletions example/basic/2-custom-build-logic/build.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Mill makes it very easy to customize your build graph, overriding portions
// of it with custom logic. In this example, we override the JVM `resources` of
// our `ScalaModule` - normally the `resources/` folder - to instead contain a
// single generated text file containing the line count of all the source files
// in that module
import mill._, scalalib._

object foo extends RootModule with ScalaModule {
def scalaVersion = "2.13.8"

/** Total number of lines in module's source files */
def lineCount = T{
allSourceFiles().map(f => os.read.lines(f.path).size).sum
}

/** Generate resources using lineCount of sources */
override def resources = T{
os.write(T.dest / "line-count.txt", "" + lineCount())
Seq(PathRef(T.dest))
}
}

/** Usage
> ./mill run
...
Line Count: 11
> ./mill show lineCount
11
> ./mill inspect lineCount
lineCount(build.sc:7)
Total number of lines in module's source files
Inputs:
allSourceFiles
*/

// Above, `def lineCount` is a new build target we define, which makes use of
// `allSourceFiles` (an existing target) and is in-turn used in our override of
// `resources` (also an existing target). This generated file can then be
// loaded and used at runtime, as see in the output of `./mill run`
//
// While this is a toy example, it shows how easy it is to customize your Mill
// build to include the kinds of custom logic common in the build config of
// most real-world projects.
//
// This customization is done in a principled fashion familiar to most
// programmers - object-orienting overrides - rather than ad-hoc
// monkey-patching or mutation common in other build tools. You never have
// "spooky action at a distance" affecting your build / graph definition, and
// your IDE can always help you find the final override of any particular build
// target as well as where any overriden implementations may be defined.
//
// Lastly, custom user-defined targets in Mill benefit from all the same things
// that built-in targets do: caching, parallelism (with the `-j`/`--jobs`
// flag), inspectability via `show`/`inspect`, and so on.
//
// While these things may not matter for such a simple example that runs
// quickly, they ensure that custom build logic remains performant and
// maintainable even as the complexity of your project grows.
11 changes: 11 additions & 0 deletions example/basic/2-custom-build-logic/src/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package foo

object Foo{
def main(args: Array[String]): Unit = {
val lineCount = scala.io.Source
.fromResource("line-count.txt")
.mkString

println(s"Line Count: $lineCount")
}
}
16 changes: 16 additions & 0 deletions example/basic/2-custom-build-logic/test/src/FooTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package foo
import utest._
object FooTests extends TestSuite {
def tests = Tests {
test("simple") {
val result = Foo.generateHtml("hello")
assert(result == "<h1>hello</h1>")
result
}
test("escaping") {
val result = Foo.generateHtml("<hello>")
assert(result == "<h1>&lt;hello&gt;</h1>")
result
}
}
}
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions main/util/src/mill/util/Util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ object Util {
.stripSuffix("**/")
.stripSuffix("*/")
.dropWhile(_.isWhitespace)
.reverse
.dropWhile(_.isWhitespace)
.reverse
).toArray
.dropWhile(_.isEmpty)
.reverse
Expand Down

0 comments on commit ef6dfa9

Please sign in to comment.