Crate ninja_writer

Source
Expand description

§ninja-writer

Build Badge Version Badge License Badge Issue Badge

Library for writing ninja build files with a focus on ergonomics and simplicity.

Since Rust requires a trait to be in scope to use, it is recommended to import * from the crate, so that all the traits are in scope.

use ninja_writer::*;

§Why another?

I found existing libraries poorly documented, and I want to explore and learn the syntax for ninja myself.

§Example

The Ninja struct is the main entry point for writing a ninja file. It is used to make top-level declarations, such as variables and rules. It implements Display so that it can be converted to a string, written to a file, etc.

Here’s a complete example of writing a ninja file that builds a simple C program. See Ninja for all the methods available.

use ninja_writer::*;

// Create writer
let ninja = Ninja::new();
// Create a variable
ninja.variable("cflags", "-Wall -Wextra -Werror");
// Create the cc rule
let cc = ninja.rule("cc", "gcc -MD -MF $depfile $cflags -c $in -o $out")
    .description("Compiling $out")
    .depfile("$out.d")
    .deps_gcc();
// Create the ld rule
let ld = ninja.rule("ld", "gcc -o $out $in")
    .description("Linking $out");

// Create build edges using the rules
cc.build(["foo.o"]).with(["foo.c"]);
cc.build(["bar.o"]).with(["bar.c"])
    .variable("cflags", "-Wall -DDEBUG");

ld.build(["app"]).with(["foo.o", "bar.o"]);

ninja.defaults(["app"]);

let ninja_file: String = ninja.to_string();
assert_eq!(ninja_file, r###"
cflags = -Wall -Wextra -Werror

rule cc
  command = gcc -MD -MF $depfile $cflags -c $in -o $out
  description = Compiling $out
  depfile = $out.d
  deps = gcc

rule ld
  command = gcc -o $out $in
  description = Linking $out

build foo.o: cc foo.c
build bar.o: cc bar.c
  cflags = -Wall -DDEBUG
build app: ld foo.o bar.o

default app
"###);

§Encoding

Because .to_string() is used to get the output, All inputs/outputs are expected to be UTF-8 encoded. Utilities like ToArg will panic for std types if the input is not valid UTF-8.

§Args and lists

All functions take implementation of ToArg as parameters. This trait is implemented for common Rust types like Path and String.

For functions that take a list of arguments (such as build), the types of the elements in the slice must be the same due to Rust’s type system restrictions.

// This won't compile
let args = [1, "bar"];

The args macro is provided to workaround this limitation.

§std feature

You can disable the std feature to make the library no_std compatible. I don’t know why you want to do that, but it’s here just in case.

§Thread safety

By default, the API is not thread-safe. However, you can enable the thread-safe feature, which uses Arc and RwLock to ensure thread safety.

Here’s an example of using 2 threads to configure 200 rules. (It’s highly theoretical. Rule has a more realistic example where multiple threads configure build edges on the same rule)

use ninja_writer::*;
use std::sync::Arc;

let ninja = Arc::new(Ninja::new());
let ninja1 = Arc::clone(&ninja);
let ninja2 = Arc::clone(&ninja);
let t1 = std::thread::spawn(move || {
    for i in 0..100 {
        ninja1.rule("example", "...");
    }
});
let t2 = std::thread::spawn(move || {
    for i in 0..100 {
        ninja2.rule("example", "...");
    }
});
t1.join().unwrap();
t2.join().unwrap();

assert_eq!(ninja.stmts.inner().len(), 200);

The example won’t compile unless you enable the thread-safe feature.

§Escaping

There is an escape function that can be used to escape strings according to the behavior of ninja.

use ninja_writer::escape;

assert_eq!(escape("foo"), "foo");
assert_eq!(escape("$foo"), "$$foo");
assert_eq!(escape("foo bar"), "foo bar");
assert_eq!(escape("foo: bar"), "foo: bar");

Since it’s only necessary to escape spaces in list of paths, you can use escape_path to do that:

use ninja_writer::escape_path;
assert_eq!(escape_path("foo bar"), "foo$ bar");

Similarly, escape_build can be used to escape both spaces and :s, for specifying outputs.

use ninja_writer::escape_build;
assert_eq!(escape_build("foo: bar"), "foo$:$ bar");

§Duplicated variables

Duplicates are not checked, since ninja allows it.

use ninja_writer::Ninja;

let mut ninja = Ninja::new();
ninja.variable("foo", "bar");
ninja.variable("foo", "bar again");

assert_eq!(ninja.to_string(), r###"
foo = bar
foo = bar again
"###);

§Order of statements

The order of statements is preserved. Ninja’s variables are expanded immediately except for in rules, so the order of statements does matter.

Macros§

  • Convert a mixed list of arguments types to a list of strings

Structs§

  • A build edge, as defined by the build keyword
  • Reference to a build statement
  • The main entry point for writing a ninja file.
  • A pool, as defined by the pool keyword
  • Reference to a pool statement
  • A rule, as defined by the rule keyword
  • Reference to a rule statement that can be used to create build edges using this rule
  • A variable declaration (name = value)

Traits§

  • Trait for implementing build-specific variables
  • Trait for implementing variables for rule and build
  • Convert something to an argument
  • Convienience trait to implement types that supports variables

Functions§

  • Escape a string for ninja, without escaping spaces or colons
  • Escape a string for ninja. Spaces and colons are escaped as well
  • Escape a string for ninja, including spaces in the string, but not colons