# `Mojoler.Build`
[🔗](https://github.com/thanos/mojoler/blob/v0.1.0/lib/mojoler/build.ex#L1)

Decides whether a native build is required and publishes `.so` files by rename.

The rename is the publication step. A failed `mojo build` never reaches it,
so the previous libraries stay in place.

# `decision`

```elixir
@type decision() :: :noop | :loaded | :build
```

What the compiler should do with the current fingerprint and load state.

  * `:noop` — the fingerprint matches and every library file is present
  * `:loaded` — a rebuild is required, but this VM has already mapped a library
  * `:build` — compile into a staging directory, then publish

## Examples

    iex> Mojoler.Build.decide(true, true, true)
    :noop

# `any_loaded?`

```elixir
@spec any_loaded?([Mojoler.Nif.library()]) :: boolean()
```

Returns whether any name in `names` was marked loaded.

`names` are basenames such as `"libpoc.so"`. An empty list is `false`.

## Examples

    iex> Mojoler.Build.any_loaded?([])
    false
    iex> Mojoler.Nif.mark!("doctest-any-loaded")
    :ok
    iex> Mojoler.Build.any_loaded?(["doctest-any-loaded.so"])
    true

# `decide`

```elixir
@spec decide(boolean(), boolean(), boolean()) :: decision()
```

Chooses the next compile step.

`fingerprint_match?` is true when `priv/native/FINGERPRINT` equals the
current fingerprint. `loaded?` is true when this VM has marked any product
library. `outputs_exist?` is true when every expected `.so` is a regular file.
None of the arguments is optional.

## Examples

    iex> Mojoler.Build.decide(true, true, true)
    :noop

    iex> Mojoler.Build.decide(false, true, true)
    :loaded

    iex> Mojoler.Build.decide(false, false, false)
    :build

    iex> Mojoler.Build.decide(true, false, false)
    :build

# `fingerprint`

```elixir
@spec fingerprint(Path.t()) :: String.t()
```

Returns the sha256 fingerprint of the native inputs.

`root` is the project directory. The script hashes Mojo sources, the ABI
tools, the build scripts, `erl_nif.h`, the Mojo version, the compiler
triple, and the link flags. There is no optional argument.

## Returns

A 64-character hex string.

## Examples

    iex> fp = Mojoler.Build.fingerprint(Mojoler.Build.root())
    iex> byte_size(fp) == 64
    true

# `outputs_present?`

```elixir
@spec outputs_present?(Path.t()) :: boolean()
```

Returns whether `dir` contains every product library.

The expected names are `libpoc.so`, `libtwice.so`, `libterm.so`,
`libown.so`, `libsched.so`, `libdsl_arith.so`, `libdsl_bytes.so`,
and `libdsl_box.so`.

## Examples

    iex> Mojoler.Build.outputs_present?(Path.join(Mojoler.Build.root(), "priv/native"))
    true

# `publish!`

```elixir
@spec publish!(Path.t(), Path.t()) :: :ok | {:error, String.t()}
```

Moves `lib*.so` from `staging` to `dest`.

`staging` and `dest` are directories. When this VM has already loaded one
of those libraries, nothing is renamed.

Also copies `link-inputs.txt` when that file is present in `staging`.

## Returns

  * `:ok` after the renames
  * `{:error, message}` when a listed library is already loaded

`File.rename!/2` raises `File.Error` when the rename itself fails.

## Examples

    iex> parent = Path.join(System.tmp_dir!(), "mojoler-doctest-34")
    iex> staging = Path.join(parent, "staging")
    iex> dest = Path.join(parent, "dest")
    iex> File.mkdir_p!(staging)
    :ok
    iex> Mojoler.Build.publish!(staging, dest)
    :ok
    iex> is_list(File.rm_rf!(parent))
    true

# `root`

```elixir
@spec root() :: Path.t()
```

Returns the directory that contains this project's `mix.exs`.

## Examples

    iex> File.regular?(Path.join(Mojoler.Build.root(), "mix.exs"))
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
