mbu/README.md
2018-03-18 17:35:39 +02:00

149 lines
4.6 KiB
Markdown

# MBU: Mix Build Utilities
**Hex.pm:** [hex.pm/packages/mbu](https://hex.pm/packages/mbu)
**Hexdocs:** [hexdocs.pm/mbu](https://hexdocs.pm/mbu)
_MBU_ is a collection of utility functions and scripts to turn Mix into a build tool
like Make. Sort of. With it, you can write tasks that build parts of your system,
be it front end or back end, without having to leave the safety of Elixir.
Shortly put, MBU allows you to write Mix tasks that depend on other tasks and contains
helper functions and macros to make writing them easier. See the basic usage section
for code examples.
## Basic Usage
A typical MBU task looks like this:
```elixir
defmodule Mix.Task.Build.Css do
use MBU.BuildTask
import MBU.TaskUtils
@deps [
"build.scss",
"build.assets"
]
task _args do
exec("css-processor", ["--output", "dist/css"]) |> listen()
end
end
```
There are a few parts to note here:
* With MBU, you don't call `use Mix.Task`, instead you call `use MBU.BuildTask` that will
insert the `task` macro and internally call `use Mix.Task`.
* You can define dependencies to your task with the `@deps` list. They are executed in
parallel before your task is run. If you need to run a task without running the
dependencies, you can use `MBU.TaskUtils.run_task/2` and give it the `deps: false`
option.
* The task is enclosed in the `task` macro that handles running the dependencies and
logging debug output. This is compared to the `run/1` function of an ordinary Mix task.
* You can execute programs easily with the `MBU.TaskUtils.exec/2` function that is in the `MBU.TaskUtils`
module. It starts the program and returns a program spec that can be given to `MBU.TaskUtils.listen/2`.
The listen function listens to output from the program and prints it on the screen.
MBU also has watch support both for watches builtin to commands and custom watches:
```elixir
defmodule Mix.Task.Watch.Css do
use MBU.BuildTask
import MBU.TaskUtils
@deps [
"build.css"
]
task _args do
[
# Builtin watch
exec("css-processor", ["--output", "dist-css", "-w"]),
# Custom watch
watch("CopyAssets", "/path/to/assets", fn _events -> File.cp_r!("from", "to") end)
]
|> listen(watch: true)
end
end
```
As you can see, there are two types of watches here. The `css-processor` command has its
own watch, activated with a command line flag `-w`. The second watch is a custom watch,
useful for when CLI tools don't have watch support or when you want to run custom Elixir
code. The `MBU.TaskUtils.watch/3` function takes in the watch name (for logging), directory
to watch and a callback function that is called for change events.
Here, the listening function is given an argument `watch: true`. The arguments makes it
listen to the user's keyboard input and if the user presses the enter key, the watches and
programs are stopped. Otherwise you would have to kill the task to stop them.
## Automatic directory handling
Often build tasks output files into some temporary directory for further processing by subsequent
tasks. This may result in boilerplate code defining the output path, like so:
```elixir
defmodule Mix.Task.Build.Js do
use MBU.BuildTask
def out_path() do
Path.join([tmp_path(), "some", "output", "path"])
end
task _ do
# Ensure output path exists
File.mkdir_p!(out_path())
exec("css-processor", ["--output", out_path()]) |> listen()
end
end
```
To avoid needless boilerplate, MBU has automatic directory handling (off by default). To use it,
turn it on in config:
```elixir
config :mbu,
# Autogenerate `out_path/0` based on task name
auto_paths: true,
# Automatically create output path when task runs
create_out_paths: true,
# Path to directory where task output is stored
tmp_path: Path.expand("_tmp")
```
Now you can simplify the above task to:
```elixir
defmodule Mix.Task.Build.Js do
use MBU.BuildTask
task _ do
exec("css-processor", ["--output", out_path()]) |> listen()
end
end
```
The `out_path/0` function is autogenerated for the task and it can also be referenced in other tasks.
The path autogeneration and creation configs can be overridden per task, by giving arguments to the
`use` statement: `use MBU.BuildTask, auto_path: false, create_out_path: true`.
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `mbu` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[{:mbu, "~> 3.0.0"}]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/mbu](https://hexdocs.pm/mbu).