Finished dev [unoptimized + debuginfo] target(s) in 30.82s
```
I removed a bunch of the output, but it all boils down to the same.
You can re-run these commands and you should end up with the same binary as the one Cargo puts into `target/debug/`.
You could put them all in a Makefile and use that to re-run them, but you would have gained nothing.
Changing your own code or adding, removing or updating dependencies won't be picked up unless you carefully specify all these dependencies in the Makefile
and update them when something changes.
We can automate that part.
Lately (again) I've been also reading about other build systems, such as [Ninja][ninja-essay] and was intrigued to rebuild that.
I haven't done that yet, but I decided to generate Ninja configuration to see how it behaves.
Let's combine these two things.
## The basic manual version
Let's work from a basic binary crate, with no dependencies and a single file.
```
$ cargo new --bin hello-world
Created binary (application) `hello-world` package
$ cat
fn main() {
println!("Hello, world!");
}
```
Building just that is easy:
```
$ rustc src/main.rs -o hello-world
$ ./hello-world
Hello, world!
```
If we rerun `rustc` it will re-build the code and generate a new file `hello-world` (that should be the same as the one from the first invocation).
That's a bit of extra work; if nothing changed we shouldn't need to rebuild.
Let's create a Ninja configuration file `build.ninja`:
```
# The build rule to invoke rustc.
# $in and $out are variables provided by ninja.
rule rustc
command = rustc $in -o $out
description = RUSTC $out
# Specifying dependencies:
# For `hello-world` to be build we invoke the above `rustc` rule with `src/main.rs` as $in
build hello-world: rustc src/main.rs
```
With [Ninja] installed we can build and run this:
```
$ ninja
[1/1] RUSTC hello-world
$ ./hello-world
Hello, world!
```
Re-running Ninja won't build it again:
```
$ ninja
ninja: no work to do.
```
Ninja knows that nothing changed and thus skips extra work.
If we modify the source it rebuilds:
```
$ echo >> src/main.rs
$ ninja
[1/1] RUSTC hello-world
$ ./hello-world
Hello, world!
```
We don't gain anything if we keep continuing writing a Ninja build configuration from hand.
Especially not once we add more files and some external crates.
## Bygge - build your project
Instead of writing out the whole Ninja file,
or coming up with some rules for Make that expand to the right files,
we can generate the build configuration once and then not touch it again until something changes.
That's exactly what Ninja was designed for:
> it is designed to have its input files generated by a higher-level build system, [...]
>
> _(from the [Ninja Website][ninja])_
After some more exploration with Cargo and the command lines it produces and the files it creates I managed to more-or-less auto-generate a Ninja configuration for a crate.
I've published this experiment as [bygge], the crate to build your crates (and itself).
### What it does
`bygge create` generates a Ninja build configuration (in `build.ninja` by default),
listing all the targets a binary crate depends on, including all crate dependencies.
`ninja` can then take this configuration and assemble the final binary.
The result should be about the same as an invocation of `cargo build`.
### What it doesn't
`bygge` is and never will be an alternative to Cargo.
Cargo is a full-fledged build system, aware of different build targets, allowing to enable features per dependency,
easily cross-compile to different targets and run the built programs as well as tests and generate documentation.
`bygge` ... builds.
### Features
* Builds cargo dependencies as listed in a project's Cargo.toml
* Can build only crates with a single binary target
* Runs on (at least) macOS and Linux
* No support for `build.rs` files
* No support for linking non-Rust libraries
* Hard-coded Cargo features for its dependencies
* Uses `cargo fetch` to download your project's dependencies
### Building bygge
Bygge is able to create a Ninja build configuration to build itself.
But first you need a compiled `bygge`.
It comes with a pre-generated configuration for that, that only works on my machine unless you change the paths to the local checkouts of the dependencies.
```
$ ninja -f manual.ninja
[29/29] RUSTC build/bygge
```
It builds a `build/bygge` file that is able to create the build configuration and run Ninja:
```
$ build/bygge create
==> Creating build file: build.ninja
==> Package: bygge
$ build/bygge build
[29/29] RUSTC build/bygge
$ build/bygge -V
bygge v0.1.0
```
You can also build it using `cargo build` and use `cargo run create` (or `target/debug/bygge create`) to create the `build.ninja` file.
And there you have it: a 300-line Ninja build configuration to build bygge.