2 unstable releases
0.2.0 | Dec 10, 2023 |
---|---|
0.1.2 | Dec 8, 2023 |
0.1.1 |
|
0.1.0 |
|
#359 in Build Utils
56KB
885 lines
ninja-writer
Library for writing ninja build files with a focus on ergonomics and simplicity.
Why another?
I found existing libraries poorly documented, and I want to explore and learn the syntax for ninja myself.
Usage
See documentation.
Contribution
Contributions are welcomed. Please open issues and PRs :)
lib.rs
:
ninja-writer
Library for writing ninja build files with a focus on ergonomics and simplicity.
However, one slight negative is that Rust requires a trait to be in scope to use.
Therefore, 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
"###);
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");
Arg lists
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 foo = "foo".to_string();
let args = [foo, "bar"];
You can either call .as_ref()
on each element to convert them to &str
s,
or define a simple macro to do this for you to avoid sprinkling .as_ref()
everywhere.
macro_rules! refs {
($($x:expr),* $(,)?) => {
vec![$($x.as_ref()),*]
}
}
This can be useful if you have custom types that implement AsRef<str>
.
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.