Reconfigure

One of the problems with a ./configure script. Is that we might not always remember to re-run it, this is pretty annoying. Your next build might not pick up dependencies, or worse, succeed but not have everything you wanted to include.

Ninja provides a solution to this in the form of generators.

A simple generator

As your project may have different needs, this is provided as sample code, that can be tweaked as needed

Add the ninja files cargo crate as a dependency

ninja-files-cargo = { version = "0.1.0" }

#![allow(unused)]

fn main() {
const BUILD_NINJA: &str = "build.ninja";
const CONFIGURE_TOML: &str = "util/configure/Cargo.toml";
const CONFIGURE_EXE: &str = "target/debug/configure";
const CONFIGURE_RULE: &str = "configure";

fn configure() -> FileBuilder {
    let configure_rule = {
        let command = CommandBuilder::new(CONFIGURE_EXE);
        RuleBuilder::new(command).generator(true)
    };

    let cargo = ninja_files_cargo::build(CONFIGURE_TOML, CONFIGURE_EXE);

    let configure_file = { BuildBuilder::new(CONFIGURE_RULE).explicit(CONFIGURE_EXE) };

    FileBuilder::new()
        .rule(CONFIGURE_RULE, configure_rule)
        .file(BUILD_NINJA, configure_file)
        .merge(&cargo)
}
}

This is a simple case of building a rule and a build target in ninja. The rule is just running our configure program (and this rule is defined in the configure program). The build rule is stating that build.ninja is dependent on the compiled configure program (the rust program, not the entrypoint in repo root) The configure program is built by the cargo crate. This is smart enough to know when the source .rs files have changed, and rebuild the configure program when needed

This means that the build ninja, is dependent on the rust program and is regenerated if the rust program changes. Very handy as you add in new rules

Merge with the other rules

The function is good and all, but how do we actually use it?. Luckily the FileBuilder type supports merge. We can merge multiple files together before finally rendering them.

Change the main.rs from installation to look like below

fn main() {
	let configure = configure();
    let ninja = FileBuilder::new().merge(&configure).build().unwrap();
    let file = std::fs::File::create("build.ninja").unwrap();
    let _ = write_ninja_file(&ninja, file).unwrap();
}

We will need to run ./configure or cargo run -p configure to get the build.ninja to have our new capabilty the first time, but after that any run of ninja will rebuild build.ninja if it needs to