summaryrefslogblamecommitdiff
path: root/docs/pages/7 - Extending Mill.md
blob: 70ac24dad2b4c2197e849b2275be7f9192dfd162 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                              

                                              







                                                                                
                                                     
                                                                                
                                                                            


                                                                                
 






                                                                              



                                                                       





                                        
                                




                                                        
                                                                            






                                                                                
                                                                                


                                                                 
                              


                                                          
                                                           


                                                                                 
                                                                                   

   
                                                                              


                                                                                


                 


                                     









                                                                             

                               






                                                                              
                                     
                              
                             
                                                          
                                                             
















                                                                              
                                                      







                                                                         
                                                                 




                                          
                       

















                                                                                
                                                                 
 


                                                                                                                                       
 





                                                                                
                                     








                                                                 


                                                                                                                                        
 
There are many different ways of extending Mill, depending on how much
customization and flexibility you need. This page will go through your options
from the easiest/least-flexible to the hardest/most-flexible.

## Custom Targets & Commands

The simplest way of adding custom functionality to Mill is to define a custom
Target or Command:

```scala
def foo = T { ... }
def bar(x: Int, s: String) = T.command { ... }
```

These can depend on other Targets, contain arbitrary code, and be placed
top-level or within any module. If you have something you just want to *do* that
isn't covered by the built-in `ScalaModule`s/`ScalaJSModule`s, simply write a
custom Target (for cached computations) or Command (for un-cached actions) and
you're done.

For subprocess/filesystem operations, you can use the
[Ammonite-Ops](http://ammonite.io/#Ammonite-Ops) library that comes bundled with
Mill, or even plain `java.nio`/`java.lang.Process`. Each target gets its own
[T.ctx().dest](http://www.lihaoyi.com/mill/page/tasks#millutilctxdestctx) folder
that you can use to place files without worrying about colliding with other
targets.

This covers use cases like:

### Compile some Javascript with Webpack and put it in your runtime classpath:

```scala
def doWebpackStuff(sources: Seq[PathRef]): PathRef = ???

def javascriptSources = T.sources { millSourcePath / "js" }
def compiledJavascript = T { doWebpackStuff(javascriptSources()) }  
object foo extends ScalaModule {
  def runClasspath = T { super.runClasspath() ++ compiledJavascript() }
}
```

### Deploy your compiled assembly to AWS

```scala
object foo extends ScalaModule {

}

def deploy(assembly: PathRef, credentials: String) = ???

def deployFoo(credentials: String) = T.command { deployFoo(foo.assembly()) }
```


## Custom Workers

[Custom Targets & Commands](#custom-targets--commands) are re-computed from
scratch each time; sometimes you want to keep values around in-memory when using
`--watch` or the Build REPL. E.g. you may want to keep a webpack process running
so webpack's own internal caches are hot and compilation is fast:

```scala
def webpackWorker = T.worker {
  // Spawn a process using java.lang.Process and return it
}

def javascriptSources = T.sources { millSourcePath / "js" }

def doWebpackStuff(webpackProcess: Process, sources: Seq[PathRef]): PathRef = ???

def compiledJavascript = T { doWebpackStuff(webpackWorker(), javascriptSources()) }
```

Mill itself uses `T.worker`s for its built-in Scala support: we keep the Scala
compiler in memory between compilations, rather than discarding it each time, in
order to improve performance.

## Custom Modules

```scala
trait FooModule extends mill.Module {
  def bar = T { "hello" }
  def baz = T { "world" }
}
```

Custom modules are useful if you have a common set of tasks that you want to
re-used across different parts of your build. You simply define a `trait`
inheriting from `mill.Module`, and then use that `trait` as many times as you
want in various `object`s:

```scala
object foo1 extends FooModule
object foo2 extends FooModule {
  def qux = T { "I am Cow" }
}  
```

You can also define a `trait` extending the built-in `ScalaModule` if you have
common configuration you want to apply to all your `ScalaModule`s:

```scala
trait FooModule extends ScalaModule {
  def scalaVersion = "2.11.11"
  object test extends Tests {
    def ivyDeps = Agg(ivy"org.scalatest::scalatest:3.0.4")
    def testFrameworks = Seq("org.scalatest.tools.Framework")
  }
}
```

## import $file

If you want to define some functionality that can be used both inside and
outside the build, you can create a new `foo.sc` file next to your `build.sc`,
`import $file.foo`, and use it in your `build.sc` file:

```scala
// foo.sc
def fooValue() = 31337 
```
```scala
// build.sc
import $file.foo
def printFoo() = T.command { println(foo.fooValue()) }
```

Mill's `import $file` syntax supports the full functionality of
[Ammonite Scripts](http://ammonite.io/#ScalaScripts)

## import $ivy

If you want to pull in artifacts from the public repositories (e.g. Maven
Central) for use in your build, you can simply use `import $ivy`:

```scala
// build.sc
import $ivy.`com.lihaoyi::scalatags:0.6.2`

def generatedHtml = T {
  import scalatags.Text.all._
  html(
    head(),
    body(
      h1("Hello"),
      p("World")
    )
  ).render  
}
```

This creates the `generatedHtml` target which can then be used however you would
like: written to a file, further processed, etc.

If you want to publish re-usable libraries that *other* people can use in their
builds, simply publish your code as a library to maven central.

For more information, see Ammonite's
[Ivy Dependencies documentation](http://ammonite.io/#import$ivy).

## Evaluator Commands (experimental)

_Evaluator Command are experimental and suspected to change. See [issue #502](https://github.com/lihaoyi/mill/issues/502) for details._

You can define a command that takes in the current `Evaluator` as an argument,
which you can use to inspect the entire build, or run arbitrary tasks. For
example, here is the `mill.scalalib.GenIdea/idea` command which uses this to
traverse the module-tree and generate an Intellij project config for your build.

```scala
def idea(ev: Evaluator) = T.command {
  mill.scalalib.GenIdea(
    implicitly,
    ev.rootModule,
    ev.discover
  )
}
```

Many built-in tools are implemented as custom evaluator commands:
[all](http://www.lihaoyi.com/mill/#all), [inspect](http://www.lihaoyi.com/mill/#inspect),
[resolve](http://www.lihaoyi.com/mill/#resolve), [show](http://www.lihaoyi.com/mill/#show).
If you want a way to run Mill commands and programmatically manipulate the tasks and outputs, you do so with your own evaluator command.