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}