ninja_writer/lib.rs
1//! # ninja-writer
2//! 
3//! 
4//! 
5//! 
6//!
7//! Library for writing [ninja](https://ninja-build.org) build files with a focus on
8//! ergonomics and simplicity.
9//!
10//! Since Rust requires a trait to be in scope to use,
11//! it is recommended to import `*` from the crate, so that all the traits
12//! are in scope.
13//! ```rust
14//! use ninja_writer::*;
15//! ```
16//!
17//! ## Why another?
18//! I found existing libraries poorly documented, and I want to explore
19//! and learn the syntax for ninja myself.
20//!
21//! ## Example
22//! The [`Ninja`] struct is the main entry point for writing a ninja file.
23//! It is used to make top-level declarations, such as variables and rules.
24//! It implements [`Display`](core::fmt::Display) so that it can be converted to a string, written to a file, etc.
25//!
26//! Here's a complete example of writing a ninja file that builds a simple C program.
27//! See [`Ninja`] for all the methods available.
28//!
29//! ```rust
30//! use ninja_writer::*;
31//!
32//! // Create writer
33//! let ninja = Ninja::new();
34//! // Create a variable
35//! ninja.variable("cflags", "-Wall -Wextra -Werror");
36//! // Create the cc rule
37//! let cc = ninja.rule("cc", "gcc -MD -MF $depfile $cflags -c $in -o $out")
38//! .description("Compiling $out")
39//! .depfile("$out.d")
40//! .deps_gcc();
41//! // Create the ld rule
42//! let ld = ninja.rule("ld", "gcc -o $out $in")
43//! .description("Linking $out");
44//!
45//! // Create build edges using the rules
46//! cc.build(["foo.o"]).with(["foo.c"]);
47//! cc.build(["bar.o"]).with(["bar.c"])
48//! .variable("cflags", "-Wall -DDEBUG");
49//!
50//! ld.build(["app"]).with(["foo.o", "bar.o"]);
51//!
52//! ninja.defaults(["app"]);
53//!
54//! let ninja_file: String = ninja.to_string();
55//! assert_eq!(ninja_file, r###"
56//! cflags = -Wall -Wextra -Werror
57//!
58//! rule cc
59//! command = gcc -MD -MF $depfile $cflags -c $in -o $out
60//! description = Compiling $out
61//! depfile = $out.d
62//! deps = gcc
63//!
64//! rule ld
65//! command = gcc -o $out $in
66//! description = Linking $out
67//!
68//! build foo.o: cc foo.c
69//! build bar.o: cc bar.c
70//! cflags = -Wall -DDEBUG
71//! build app: ld foo.o bar.o
72//!
73//! default app
74//! "###);
75//! ```
76//!
77//! ## Encoding
78//! Because `.to_string()` is used to get the output, All inputs/outputs are expected to be UTF-8
79//! encoded. Utilities like [`ToArg`] will panic for `std` types if the input is not valid UTF-8.
80//!
81//! ## Args and lists
82//! All functions take implementation of [`ToArg`] as parameters.
83//! This trait is implemented for common Rust types like [`Path`](std::path::Path) and
84//! [`String`].
85//!
86//! For functions that take a list of arguments (such as [`build`](RuleRef::build)),
87//! the types of the elements in the slice must be the same due to Rust's type system restrictions.
88//! ```compile_fail
89//! // This won't compile
90//! let args = [1, "bar"];
91//! ```
92//! The [`args`] macro is provided to workaround this limitation.
93//!
94//!
95//! ## `std` feature
96//! You can disable the `std` feature to make the library `no_std` compatible. I don't know why you
97//! want to do that, but it's here just in case.
98//!
99//! ## Thread safety
100//! By default, the API is not thread-safe. However, you can enable the `thread-safe` feature,
101//! which uses `Arc` and `RwLock` to ensure thread safety.
102//!
103//! Here's an example of using 2 threads to configure 200 rules.
104//! (It's highly theoretical. [`Rule`] has a more realistic example
105//! where multiple threads configure build edges on the same rule)
106//! ```rust
107//! # #[cfg(feature = "thread-safe")]
108//! # {
109//! use ninja_writer::*;
110//! use std::sync::Arc;
111//!
112//! let ninja = Arc::new(Ninja::new());
113//! let ninja1 = Arc::clone(&ninja);
114//! let ninja2 = Arc::clone(&ninja);
115//! let t1 = std::thread::spawn(move || {
116//! for i in 0..100 {
117//! ninja1.rule("example", "...");
118//! }
119//! });
120//! let t2 = std::thread::spawn(move || {
121//! for i in 0..100 {
122//! ninja2.rule("example", "...");
123//! }
124//! });
125//! t1.join().unwrap();
126//! t2.join().unwrap();
127//!
128//! assert_eq!(ninja.stmts.inner().len(), 200);
129//! # }
130//! ```
131//! The example won't compile unless you enable the `thread-safe` feature.
132//!
133//! ## Escaping
134//! There is an [`escape`] function that can be used to escape strings
135//! according to [the behavior](https://ninja-build.org/manual.html#ref_lexer) of ninja.
136//! ```rust
137//! use ninja_writer::escape;
138//!
139//! assert_eq!(escape("foo"), "foo");
140//! assert_eq!(escape("$foo"), "$$foo");
141//! assert_eq!(escape("foo bar"), "foo bar");
142//! assert_eq!(escape("foo: bar"), "foo: bar");
143//! ```
144//! Since it's only necessary to escape spaces in list of paths, you can use [`escape_path`] to do that:
145//! ```rust
146//! use ninja_writer::escape_path;
147//! assert_eq!(escape_path("foo bar"), "foo$ bar");
148//! ```
149//! Similarly, [`escape_build`] can be used to escape both spaces and `:`s, for
150//! specifying outputs.
151//! ```rust
152//! use ninja_writer::escape_build;
153//! assert_eq!(escape_build("foo: bar"), "foo$:$ bar");
154//! ```
155//!
156//! ## Duplicated variables
157//! Duplicates are not checked, since ninja allows it.
158//! ```rust
159//! use ninja_writer::Ninja;
160//!
161//! let mut ninja = Ninja::new();
162//! ninja.variable("foo", "bar");
163//! ninja.variable("foo", "bar again");
164//!
165//! assert_eq!(ninja.to_string(), r###"
166//! foo = bar
167//! foo = bar again
168//! "###);
169//! ```
170//!
171//! ## Order of statements
172//! The order of statements is preserved. Ninja's variables are expanded
173//! immediately except for in rules, so the order of statements does matter.
174
175#![cfg_attr(not(feature = "std"), no_std)]
176
177extern crate alloc;
178
179#[doc(hidden)]
180pub mod arg;
181#[doc(hidden)]
182pub mod build;
183#[doc(hidden)]
184pub mod ninja;
185#[doc(hidden)]
186pub mod pool;
187#[doc(hidden)]
188pub mod rule;
189#[doc(hidden)]
190pub mod stmt;
191#[doc(hidden)]
192pub mod util;
193#[doc(hidden)]
194pub mod variable;
195
196// Re-exports
197pub use arg::ToArg;
198pub use build::{Build, BuildRef, BuildVariables};
199pub use ninja::Ninja;
200pub use pool::{Pool, PoolRef};
201pub use rule::{Rule, RuleRef, RuleVariables};
202pub use util::{escape, escape_build, escape_path};
203pub use variable::{Variable, Variables};