ninja_writer/rule.rs
1//! Implementation of the `rule` keyword
2
3use alloc::boxed::Box;
4use alloc::string::String;
5use core::fmt::{Display, Formatter, Result};
6use core::ops::Deref;
7
8use crate::stmt::{Stmt, StmtRef};
9use crate::util::{AddOnlyVec, Indented, RefCounted};
10use crate::{Build, BuildRef, Ninja, Pool, ToArg, Variable, Variables};
11
12/// A rule, as defined by the `rule` keyword
13///
14/// See <https://ninja-build.org/manual.html#_rules>
15///
16/// # Creating a rule
17/// The most common way to create a rule is with the [`Ninja::rule`](crate::Ninja::rule) method,
18/// which creates the rule, adds it to the ninja file, then returns a reference for configuration
19/// ```rust
20/// use ninja_writer::*;
21///
22/// let ninja = Ninja::new();
23/// let rule = ninja.rule("cc", "gcc -MD -MF $out.d -c $in -o $out")
24/// .depfile("$out.d")
25/// .deps_gcc()
26/// .description("Compiling $out");
27/// rule.build(["foo.o"]).with(["foo.c"]);
28///
29/// assert_eq!(ninja.to_string(), r###"
30/// rule cc
31/// command = gcc -MD -MF $out.d -c $in -o $out
32/// depfile = $out.d
33/// deps = gcc
34/// description = Compiling $out
35///
36/// build foo.o: cc foo.c
37/// "###);
38/// ```
39///
40/// # Using owned `Rule` type
41/// If you prefer, you can create a rule with the [`Rule::new`](crate::Rule::new) method,
42/// then add it to the ninja file with [`Rule::add_to`](crate::Rule::add_to)
43/// to starting adding build edges. This may be useful if you want to configure a rule
44/// in a function, since you don't need to deal with explicit lifetimes.
45/// ```rust
46/// use ninja_writer::*;
47///
48/// fn make_rule() -> Rule {
49/// Rule::new("cc", "gcc -MD -MF $out.d -c $in -o $out")
50/// .depfile("$out.d")
51/// .deps_gcc()
52/// .description("Compiling $out")
53/// }
54///
55/// # fn main() {
56/// // in the main function ...
57/// let ninja = Ninja::new();
58/// // `add_to` returns a reference, similar to `ninja.rule()`
59/// let rule = make_rule().add_to(&ninja);
60/// rule.build(["foo.o"]).with(["foo.c"]);
61/// assert_eq!(ninja.to_string(), r###"
62/// rule cc
63/// command = gcc -MD -MF $out.d -c $in -o $out
64/// depfile = $out.d
65/// deps = gcc
66/// description = Compiling $out
67///
68/// build foo.o: cc foo.c
69/// "###);
70/// # }
71///
72/// ```
73///
74/// # Thread safety
75/// Enable the `thread-safe` flag to make the API thread-safe.
76/// Here is an example of adding build edges to the same rule from
77/// multiple threads. Note that the example will not compile without the `thread-safe` feature.
78/// ```rust
79/// # #[cfg(feature = "thread-safe")]
80/// # {
81/// use ninja_writer::*;
82///
83/// let ninja = Ninja::new();
84///
85/// // The type of `rule` below is `RuleRef`
86/// // which implement `clone` to clone the underlying ref-counted pointer
87/// let rule = ninja.rule("cc", "gcc -c $in -o $out");
88/// let rule2 = rule.clone();
89///
90/// assert_eq!(ninja.to_string(), r###"
91/// rule cc
92/// command = gcc -c $in -o $out
93/// "###);
94/// assert_eq!(ninja.stmts.inner().len(), 1);
95///
96/// let t1 = std::thread::spawn(move || {
97/// for _ in 0..100 {
98/// rule.build(["foo1"]).with(["bar1"]);
99/// }
100/// });
101///
102/// let t2 = std::thread::spawn(move || {
103/// for _ in 0..100 {
104/// rule2.build(["foo2"]).with(["bar2"]);
105/// }
106/// });
107///
108/// t1.join().unwrap();
109/// t2.join().unwrap();
110/// assert_eq!(ninja.stmts.inner().len(), 201);
111/// # }
112/// ```
113///
114#[derive(Debug)]
115pub struct Rule {
116 /// The rule name as in `rule <name>`
117 ///
118 /// This is ref-counted so that it can be copied not-too-costly to build edges.
119 pub name: RefCounted<String>,
120
121 /// The list of variables, as an indented block
122 ///
123 /// See <https://ninja-build.org/manual.html#ref_rule>
124 pub variables: AddOnlyVec<Variable>,
125}
126
127/// Trait for implementing variables for `rule` and `build`
128pub trait RuleVariables: Variables {
129 /// Set the depfile for this `rule` or `build` to explicitly support C/C++ header
130 /// dependencies
131 ///
132 /// # Example
133 /// ```rust
134 /// use ninja_writer::*;
135 ///
136 /// let ninja = Ninja::new();
137 /// ninja.rule("cc", "gcc -c $in -o $out")
138 /// .depfile("$out.d");
139 ///
140 /// assert_eq!(ninja.to_string(), r###"
141 /// rule cc
142 /// command = gcc -c $in -o $out
143 /// depfile = $out.d
144 /// "###);
145 /// ```
146 #[inline]
147 fn depfile(self, depfile: impl ToArg) -> Self {
148 self.variable("depfile", depfile)
149 }
150
151 /// Set `deps = gcc` for this `rule` or `build`
152 ///
153 /// # Example
154 /// ```rust
155 /// use ninja_writer::*;
156 ///
157 /// let ninja = Ninja::new();
158 /// ninja.rule("cc", "gcc -c $in -o $out")
159 /// .deps_gcc();
160 ///
161 /// assert_eq!(ninja.to_string(), r###"
162 /// rule cc
163 /// command = gcc -c $in -o $out
164 /// deps = gcc
165 /// "###);
166 /// ```
167 #[inline]
168 fn deps_gcc(self) -> Self {
169 self.variable("deps", "gcc")
170 }
171
172 /// Set `deps = msvc` for this rule (or build) and the `msvc_deps_prefix` variable
173 ///
174 /// # Example
175 /// ```rust
176 /// use ninja_writer::*;
177 ///
178 /// let ninja = Ninja::new();
179 /// ninja.rule("cl", "cl /c $in /Fo$out")
180 /// .deps_msvc_prefix("Note: including file: ");
181 ///
182 /// assert_eq!(ninja.to_string(), format!(r###"
183 /// rule cl
184 /// command = cl /c $in /Fo$out
185 /// deps = msvc
186 /// msvc_deps_prefix = Note: including file: {}"###, "\n"));
187 /// ```
188 fn deps_msvc_prefix(self, msvc_deps_prefix: impl ToArg) -> Self {
189 self.deps_msvc()
190 .variable("msvc_deps_prefix", msvc_deps_prefix)
191 }
192
193 /// Set `deps = msvc` for this `rule` or `build` without `msvc_deps_prefix`
194 ///
195 /// # Example
196 /// ```rust
197 /// use ninja_writer::*;
198 ///
199 /// let ninja = Ninja::new();
200 /// ninja.rule("cl", "cl /c $in /Fo$out")
201 /// .deps_msvc();
202 ///
203 /// assert_eq!(ninja.to_string(), r###"
204 /// rule cl
205 /// command = cl /c $in /Fo$out
206 /// deps = msvc
207 /// "###);
208 /// ```
209 #[inline]
210 fn deps_msvc(self) -> Self {
211 self.variable("deps", "msvc")
212 }
213
214 /// Set the description of the rule to be printed during the build
215 ///
216 /// # Example
217 /// ```rust
218 /// use ninja_writer::*;
219 ///
220 /// let ninja = Ninja::new();
221 /// ninja.rule("cc", "gcc -c $in -o $out")
222 /// .description("Compiling $out");
223 ///
224 /// assert_eq!(ninja.to_string(), r###"
225 /// rule cc
226 /// command = gcc -c $in -o $out
227 /// description = Compiling $out
228 /// "###);
229 /// ```
230 #[inline]
231 fn description(self, desc: impl ToArg) -> Self {
232 self.variable("description", desc)
233 }
234
235 /// Indicate the rule is used to re-invoke the generator
236 ///
237 /// # Example
238 /// ```rust
239 /// use ninja_writer::*;
240 ///
241 /// let ninja = Ninja::new();
242 /// let configure = ninja.rule("configure", "cargo run --manifest-path ./configure/Cargo.toml -- $out")
243 /// .generator();
244 ///
245 /// configure.build(["build.ninja"]);
246 ///
247 /// assert_eq!(ninja.to_string(), r###"
248 /// rule configure
249 /// command = cargo run --manifest-path ./configure/Cargo.toml -- $out
250 /// generator = 1
251 ///
252 /// build build.ninja: configure
253 /// "###);
254 /// ```
255 #[inline]
256 fn generator(self) -> Self {
257 self.variable("generator", "1")
258 }
259
260 /// Specify `restat = 1` for the `rule` or `build`
261 ///
262 /// # Example
263 /// ```rust
264 /// use ninja_writer::*;
265 ///
266 /// let ninja = Ninja::new();
267 /// ninja.rule("example", "...")
268 /// .restat();
269 ///
270 /// assert_eq!(ninja.to_string(), r###"
271 /// rule example
272 /// command = ...
273 /// restat = 1
274 /// "###);
275 #[inline]
276 fn restat(self) -> Self {
277 self.variable("restat", "1")
278 }
279
280 /// Specify `rspfile` and `rspfile_content` variables for this `rule` or `build`
281 ///
282 /// # Example
283 /// ```rust
284 /// use ninja_writer::*;
285 ///
286 /// let ninja = Ninja::new();
287 /// ninja.rule("example", "...")
288 /// .rspfile("foo", "bar");
289 ///
290 /// assert_eq!(ninja.to_string(), r###"
291 /// rule example
292 /// command = ...
293 /// rspfile = foo
294 /// rspfile_content = bar
295 /// "###);
296 /// ```
297 fn rspfile(self, rspfile: impl ToArg, rspfile_content: impl ToArg) -> Self {
298 self.variable("rspfile", rspfile)
299 .variable("rspfile_content", rspfile_content)
300 }
301
302 /// Set `pool = console` for this `rule` or `build`
303 ///
304 /// See <https://ninja-build.org/manual.html#_the_literal_console_literal_pool>
305 ///
306 /// # Example
307 /// ```rust
308 /// use ninja_writer::*;
309 ///
310 /// let ninja = Ninja::new();
311 /// ninja.rule("example", "...").pool_console();
312 ///
313 /// assert_eq!(ninja.to_string(), r###"
314 /// rule example
315 /// command = ...
316 /// pool = console
317 /// "###);
318 /// ```
319 fn pool_console(self) -> Self {
320 self.variable("pool", "console")
321 }
322
323 /// Set the pool for this `rule` or `build`
324 ///
325 /// # Example
326 /// ```rust
327 /// use ninja_writer::*;
328 ///
329 /// let ninja = Ninja::new();
330 /// let pool = ninja.pool("expensive", 4);
331 /// let rule = ninja.rule("cc", "gcc -c $in -o $out").pool(pool);
332 /// rule.build(["foo.o"]).with(["foo.c"]);
333 ///
334 /// assert_eq!(ninja.to_string(), r###"
335 /// pool expensive
336 /// depth = 4
337 ///
338 /// rule cc
339 /// command = gcc -c $in -o $out
340 /// pool = expensive
341 ///
342 /// build foo.o: cc foo.c
343 /// "###);
344 /// ```
345 #[inline]
346 fn pool(self, pool: impl AsRef<Pool>) -> Self {
347 self.variable("pool", &pool.as_ref().name)
348 }
349}
350
351/// Reference to a rule statement that can be used to create build edges
352/// using this rule
353#[derive(Debug, Clone)]
354pub struct RuleRef(pub(crate) StmtRef);
355
356impl Deref for RuleRef {
357 type Target = Rule;
358 fn deref(&self) -> &Self::Target {
359 match self.0.deref().deref() {
360 Stmt::Rule(r) => r,
361 // safety: RuleRef is only constructable within this crate
362 _ => unreachable!(),
363 }
364 }
365}
366
367impl AsRef<Rule> for RuleRef {
368 fn as_ref(&self) -> &Rule {
369 self.deref()
370 }
371}
372
373impl RuleRef {
374 /// Create a build edge using this rule and the explicit outputs, then add it to
375 /// the ninja file provided.
376 ///
377 /// # Example
378 /// See [`Rule`]
379 pub fn build(&self, outputs: impl IntoIterator<Item = impl ToArg>) -> BuildRef {
380 let build = Build::new(self.deref(), outputs);
381 BuildRef(self.0.add(Stmt::Build(Box::new(build))))
382 }
383}
384
385impl Rule {
386 /// Create a new rule with the given name and command
387 pub fn new(name: impl ToArg, command: impl ToArg) -> Self {
388 let s = Self {
389 name: RefCounted::new(name.to_arg()),
390 variables: AddOnlyVec::new(),
391 };
392 s.variable("command", command)
393 }
394
395 /// Add the rule to a ninja file and return a [`RuleRef`] for further configuration
396 pub fn add_to(self, ninja: &Ninja) -> RuleRef {
397 RuleRef(ninja.add_stmt(Stmt::Rule(self)))
398 }
399}
400
401impl Variables for Rule {
402 #[inline]
403 fn add_variable_internal(&self, v: Variable) {
404 self.variables.add(v);
405 }
406}
407
408impl RuleVariables for Rule {}
409
410impl Variables for RuleRef {
411 fn add_variable_internal(&self, v: Variable) {
412 self.deref().add_variable_internal(v)
413 }
414}
415impl RuleVariables for RuleRef {}
416
417impl Display for Rule {
418 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
419 writeln!(f, "rule {}", self.name)?;
420 for variable in self.variables.inner().iter() {
421 Indented(variable).fmt(f)?;
422 writeln!(f)?;
423 }
424 Ok(())
425 }
426}