ninja_build/graph.rs
1// Copyright 2011 Google Inc. All Rights Reserved.
2// Copyright 2017 The Ninja-rs Project Developers. All Rights Reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16
17use std::rc::Rc;
18use std::cell::{Cell, RefCell};
19use std::borrow::Cow;
20use std::ops::Range;
21
22use super::state::{Pool, State, NodeState};
23use super::build_log::BuildLog;
24use super::deps_log::DepsLog;
25use super::disk_interface::DiskInterface;
26use super::state::{PHONY_RULE, CONSOLE_POOL};
27use super::eval_env::{Env, Rule, BindingEnv};
28use super::timestamp::TimeStamp;
29use super::utils::WINDOWS_PATH;
30use super::utils::{decanonicalize_path, pathbuf_from_bytes};
31use super::utils::{ExtendFromEscapedSlice, RangeContains};
32
33#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
34pub struct NodeIndex(pub(crate) usize);
35
36#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
37pub struct EdgeIndex(pub(crate) usize);
38
39/// Information about a node in the dependency graph: the file, whether
40/// it's dirty, mtime, etc.
41pub struct Node {
42 path: Vec<u8>,
43
44 /// Set bits starting from lowest for backslashes that were normalized to
45 /// forward slashes by CanonicalizePath. See |PathDecanonicalized|.
46 slash_bits: u64,
47
48 /// The Edge that produces this Node, or NULL when there is no
49 /// known edge to produce it.
50 in_edge: Option<EdgeIndex>,
51
52 /// All Edges that use this Node as an input.
53 out_edges: Vec<EdgeIndex>,
54
55 /// A dense integer id for the node, assigned and used by DepsLog.
56 id: isize,
57
58 /// Possible values of mtime_:
59 /// -1: file hasn't been examined
60 /// 0: we looked, and file doesn't exist
61 /// >0: actual file's mtime
62 mtime: TimeStamp,
63
64 /// Dirty is true when the underlying file is out-of-date.
65 /// But note that Edge::outputs_ready_ is also used in judging which
66 /// edges to build.
67 dirty: bool,
68}
69
70impl Node {
71 pub fn new(path: &[u8], slash_bits: u64) -> Self {
72 Node {
73 path: path.to_owned(),
74 slash_bits,
75 in_edge: None,
76 out_edges: Vec::new(),
77 id: -1,
78 mtime: TimeStamp(-1),
79 dirty: false,
80 }
81 }
82
83 pub fn id(&self) -> isize {
84 self.id
85 }
86
87 pub fn set_id(&mut self, id: isize) {
88 self.id = id;
89 }
90
91 pub fn path(&self) -> &[u8] {
92 &self.path
93 }
94
95 pub fn slash_bits(&self) -> u64 {
96 self.slash_bits
97 }
98
99 pub fn mtime(&self) -> TimeStamp {
100 self.mtime
101 }
102
103 pub fn is_dirty(&self) -> bool {
104 self.dirty
105 }
106
107 pub fn set_dirty(&mut self, dirty: bool) {
108 self.dirty = dirty;
109 }
110
111 pub fn mark_dirty(&mut self) {
112 self.dirty = true;
113 }
114
115 /// Mark as not-yet-stat()ed and not dirty.
116 pub fn reset_state(&mut self) {
117 self.mtime = TimeStamp(-1);
118 self.dirty = false;
119 }
120
121 /// Mark the Node as already-stat()ed and missing.
122 pub fn mark_missing(&mut self) {
123 self.mtime = TimeStamp(0);
124 }
125
126 pub fn exists(&self) -> bool {
127 self.mtime.0 != 0
128 }
129
130 pub fn status_known(&self) -> bool {
131 self.mtime.0 != -1
132 }
133
134 pub fn in_edge(&self) -> Option<EdgeIndex> {
135 self.in_edge
136 }
137
138 pub fn set_in_edge(&mut self, edge: Option<EdgeIndex>) {
139 self.in_edge = edge;
140 }
141
142 pub fn out_edges(&self) -> &[EdgeIndex] {
143 &self.out_edges
144 }
145
146 pub fn add_out_edge(&mut self, edge: EdgeIndex) {
147 self.out_edges.push(edge);
148 }
149
150 /// Get |path()| but use slash_bits to convert back to original slash styles.
151 pub fn path_decanonicalized(&self) -> Vec<u8> {
152 decanonicalize_path(&self.path, self.slash_bits)
153 }
154
155 /// Return false on error.
156 pub fn stat(&mut self, disk_interface: &DiskInterface) -> Result<(), String> {
157 self.mtime = TimeStamp(-1);
158
159 let pathbuf = pathbuf_from_bytes(self.path.clone()).map_err(|e| {
160 format!("invalid utf-8 pathname: {}", String::from_utf8_lossy(&e))
161 })?;
162
163 self.mtime = disk_interface.stat(&pathbuf)?;
164 Ok(())
165 }
166
167 /// Return false on error.
168 pub fn stat_if_necessary(&mut self, disk_interface: &DiskInterface) -> Result<(), String> {
169 if self.status_known() {
170 return Ok(());
171 }
172
173 self.stat(disk_interface)
174 }
175}
176
177/*
178
179struct Node {
180
181 void Dump(const char* prefix="") const;
182
183private:
184};
185*/
186
187#[derive(Clone, Copy)]
188pub enum EdgeVisitMark {
189 VisitNone,
190 VisitInStack,
191 VisitDone,
192}
193
194/// An edge in the dependency graph; links between Nodes using Rules.
195pub struct Edge {
196 rule: Rc<Rule>,
197 pub pool: Rc<RefCell<Pool>>,
198 pub inputs: Vec<NodeIndex>,
199 pub outputs: Vec<NodeIndex>,
200 pub env: Rc<RefCell<BindingEnv>>,
201 pub mark: EdgeVisitMark,
202 pub outputs_ready: bool,
203 pub deps_missing: bool,
204 pub implicit_deps: usize,
205 pub order_only_deps: usize,
206 pub implicit_outs: usize,
207}
208
209impl Edge {
210 pub fn new(rule: Rc<Rule>, pool: Rc<RefCell<Pool>>, env: Rc<RefCell<BindingEnv>>) -> Self {
211 Edge {
212 rule,
213 pool,
214 inputs: Vec::new(),
215 outputs: Vec::new(),
216 env,
217 mark: EdgeVisitMark::VisitNone,
218 outputs_ready: false,
219 deps_missing: false,
220 implicit_deps: 0,
221 order_only_deps: 0,
222 implicit_outs: 0,
223 }
224 }
225
226 pub fn rule(&self) -> &Rc<Rule> {
227 &self.rule
228 }
229
230 pub fn pool(&self) -> &Rc<RefCell<Pool>> {
231 &self.pool
232 }
233
234 pub fn weight(&self) -> usize {
235 1
236 }
237
238 pub fn outputs_ready(&self) -> bool {
239 self.outputs_ready
240 }
241
242 // There are three types of inputs.
243 // 1) explicit deps, which show up as $in on the command line;
244 // 2) implicit deps, which the target depends on implicitly (e.g. C headers),
245 // and changes in them cause the target to rebuild;
246 // 3) order-only deps, which are needed before the target builds but which
247 // don't cause the target to rebuild.
248 // These are stored in inputs_ in that order, and we keep counts of
249 // #2 and #3 when we need to access the various subsets.
250 pub fn explicit_deps_range(&self) -> Range<usize> {
251 0..(self.inputs.len() - self.implicit_deps - self.order_only_deps)
252 }
253
254 pub fn implicit_deps_range(&self) -> Range<usize> {
255 (self.inputs.len() - self.implicit_deps - self.order_only_deps)..
256 (self.inputs.len() - self.order_only_deps)
257 }
258
259 pub fn non_order_only_deps_range(&self) -> Range<usize> {
260 0..(self.inputs.len() - self.order_only_deps)
261 }
262
263 pub fn order_only_deps_range(&self) -> Range<usize> {
264 (self.inputs.len() - self.order_only_deps)..(self.inputs.len())
265 }
266
267 // There are two types of outputs.
268 // 1) explicit outs, which show up as $out on the command line;
269 // 2) implicit outs, which the target generates but are not part of $out.
270 // These are stored in outputs_ in that order, and we keep a count of
271 // #2 to use when we need to access the various subsets.
272 pub fn explicit_outs_range(&self) -> Range<usize> {
273 0..(self.outputs.len() - self.implicit_outs)
274 }
275
276 pub fn implicit_outs_range(&self) -> Range<usize> {
277 (self.outputs.len() - self.implicit_outs)..(self.outputs.len())
278 }
279
280 /// Returns the shell-escaped value of |key|.
281 pub fn get_binding(&self, node_state: &NodeState, key: &[u8]) -> Cow<[u8]> {
282 let env = EdgeEnv::new(self, node_state, EdgeEnvEscapeKind::ShellEscape);
283 env.lookup_variable(key).into_owned().into()
284 }
285
286 pub fn get_binding_bool(&self, node_state: &NodeState, key: &[u8]) -> bool {
287 !self.get_binding(node_state, key).is_empty()
288 }
289
290 /// Like GetBinding("depfile"), but without shell escaping.
291 pub fn get_unescaped_depfile(&self, node_state: &NodeState) -> Cow<[u8]> {
292 let env = EdgeEnv::new(self, node_state, EdgeEnvEscapeKind::DoNotEscape);
293 env.lookup_variable(b"depfile").into_owned().into()
294 }
295
296 /// Like GetBinding("rspfile"), but without shell escaping.
297 pub fn get_unescaped_rspfile(&self, node_state: &NodeState) -> Cow<[u8]> {
298 let env = EdgeEnv::new(self, node_state, EdgeEnvEscapeKind::DoNotEscape);
299 env.lookup_variable(b"rspfile").into_owned().into()
300 }
301
302 pub fn is_phony(&self) -> bool {
303 self.rule.as_ref() as *const Rule == PHONY_RULE.with(|x| x.as_ref() as *const Rule)
304 }
305
306 pub fn use_console(&self) -> bool {
307 &*self.pool().borrow() as *const Pool == CONSOLE_POOL.with(|x| &*x.borrow() as *const Pool)
308 }
309
310 /// Expand all variables in a command and return it as a string.
311 /// If incl_rsp_file is enabled, the string will also contain the
312 /// full contents of a response file (if applicable)
313 pub fn evaluate_command(&self, node_state: &NodeState) -> Vec<u8> {
314 self.evaluate_command_with_rsp_file(node_state, false)
315 }
316
317 pub fn evaluate_command_with_rsp_file(
318 &self,
319 node_state: &NodeState,
320 incl_rsp_file: bool,
321 ) -> Vec<u8> {
322 let mut command = self.get_binding(node_state, b"command").into_owned();
323 if incl_rsp_file {
324 let rspfile_content = self.get_binding(node_state, b"rspfile_content");
325 if !rspfile_content.is_empty() {
326 command.extend_from_slice(b";rspfile=");
327 command.extend_from_slice(rspfile_content.as_ref());
328 }
329 }
330 command
331 }
332
333 pub fn maybe_phonycycle_diagnostic(&self) -> bool {
334 // CMake 2.8.12.x and 3.0.x produced self-referencing phony rules
335 // of the form "build a: phony ... a ...". Restrict our
336 // "phonycycle" diagnostic option to the form it used.
337 self.is_phony() && self.outputs.len() == 1 && self.implicit_outs == 0 &&
338 self.implicit_deps == 0
339 }
340
341 /// Return true if all inputs' in-edges are ready.
342 pub fn all_inputs_ready(&self, state: &State) -> bool {
343 for input_idx in self.inputs.iter() {
344 let input = state.node_state.get_node(*input_idx);
345 if let Some(in_edge) = input.in_edge() {
346 if !state.edge_state.get_edge(in_edge).outputs_ready() {
347 return false;
348 }
349 }
350 }
351 return true;
352 }
353
354 pub fn dump(&self) {
355 unimplemented!();
356 }
357}
358
359
360/*
361
362struct Edge {
363
364 void Dump(const char* prefix="") const;
365};
366
367*/
368/// ImplicitDepLoader loads implicit dependencies, as referenced via the
369/// "depfile" attribute in build files.
370struct ImplicitDepLoader<'b, 'c> {
371 disk_interface: &'c DiskInterface,
372 deps_log: &'b DepsLog,
373}
374
375impl<'b, 'c> ImplicitDepLoader<'b, 'c> {
376 pub fn new(deps_log: &'b DepsLog, disk_interface: &'c DiskInterface) -> Self {
377 ImplicitDepLoader {
378 deps_log,
379 disk_interface,
380 }
381 }
382
383 pub fn deps_log(&self) -> &'b DepsLog {
384 self.deps_log
385 }
386
387 /// Load implicit dependencies for \a edge.
388 /// @return false on error (without filling \a err if info is just missing
389 // or out of date).
390 pub fn load_deps(&self, state: &State, edge_idx: EdgeIndex) -> Result<bool, String> {
391 let edge = state.edge_state.get_edge(edge_idx);
392 let deps_type = edge.get_binding(&state.node_state, b"deps");
393 if !deps_type.is_empty() {
394 return self.load_deps_from_log(state, edge_idx);
395 }
396
397 let depfile = edge.get_unescaped_depfile(&state.node_state);
398 if !depfile.is_empty() {
399 return self.load_dep_file(state, edge_idx, &depfile);
400 }
401
402 // No deps to load.
403 return Ok(true);
404 }
405
406 /// Load implicit dependencies for \a edge from a depfile attribute.
407 /// @return false on error (without filling \a err if info is just missing).
408 pub fn load_dep_file(
409 &self,
410 state: &State,
411 edge_idx: EdgeIndex,
412 path: &[u8],
413 ) -> Result<bool, String> {
414 unimplemented!{}
415 }
416
417 /// Load implicit dependencies for \a edge from the DepsLog.
418 /// @return false on error (without filling \a err if info is just missing).
419 pub fn load_deps_from_log(&self, state: &State, edge_idx: EdgeIndex) -> Result<bool, String> {
420 return Ok(false);
421 unimplemented!{}
422 }
423}
424
425/*
426struct ImplicitDepLoader {
427 ImplicitDepLoader(State* state, DepsLog* deps_log,
428 DiskInterface* disk_interface)
429 : state_(state), disk_interface_(disk_interface), deps_log_(deps_log) {}
430
431 /// Load implicit dependencies for \a edge.
432 /// @return false on error (without filling \a err if info is just missing
433 // or out of date).
434 bool LoadDeps(Edge* edge, string* err);
435
436 DepsLog* deps_log() const {
437 return deps_log_;
438 }
439
440 private:
441
442 /// Preallocate \a count spaces in the input array on \a edge, returning
443 /// an iterator pointing at the first new space.
444 vector<Node*>::iterator PreallocateSpace(Edge* edge, int count);
445
446 /// If we don't have a edge that generates this input already,
447 /// create one; this makes us not abort if the input is missing,
448 /// but instead will rebuild in that circumstance.
449 void CreatePhonyInEdge(Node* node);
450
451 State* state_;
452 DiskInterface* disk_interface_;
453 DepsLog* deps_log_;
454};
455
456*/
457
458/// DependencyScan manages the process of scanning the files in a graph
459/// and updating the dirty/outputs_ready state of all the nodes and edges.
460pub struct DependencyScan<'s, 'a, 'b, 'c>
461where
462 's: 'a,
463{
464 build_log: Option<&'a BuildLog<'s>>,
465 deps_log: &'b DepsLog,
466 disk_interface: &'c DiskInterface,
467 dep_loader: ImplicitDepLoader<'b, 'c>,
468}
469
470impl<'s, 'a, 'b, 'c> DependencyScan<'s, 'a, 'b, 'c>
471where
472 's: 'a,
473{
474 pub fn new(
475 build_log: &'a BuildLog<'s>,
476 deps_log: &'b DepsLog,
477 disk_interface: &'c DiskInterface,
478 ) -> Self {
479 DependencyScan {
480 build_log: Some(build_log),
481 deps_log,
482 disk_interface,
483 dep_loader: ImplicitDepLoader::new(deps_log, disk_interface),
484 }
485 }
486
487 pub fn build_log(&self) -> Option<&'a BuildLog<'s>> {
488 self.build_log.clone()
489 }
490
491 pub fn deps_log(&self) -> &'b DepsLog {
492 self.dep_loader.deps_log()
493 }
494
495 /// Update the |dirty_| state of the given node by inspecting its input edge.
496 /// Examine inputs, outputs, and command lines to judge whether an edge
497 /// needs to be re-run, and update outputs_ready_ and each outputs' |dirty_|
498 /// state accordingly.
499 /// Returns false on failure.
500 pub fn recompute_dirty(&self, state: &mut State, node_idx: NodeIndex) -> Result<(), String> {
501 let mut stack = Vec::new();
502 return self.recompute_dirty_inner(state, node_idx, &mut stack);
503 }
504
505 fn recompute_dirty_inner(
506 &self,
507 state: &mut State,
508 node_idx: NodeIndex,
509 stack: &mut Vec<NodeIndex>,
510 ) -> Result<(), String> {
511 match state.node_state.get_node(node_idx).in_edge.as_ref() {
512 None => {
513 let node = state.node_state.get_node_mut(node_idx);
514 // If we already visited this leaf node then we are done.
515 if node.status_known() {
516 return Ok(());
517 };
518 // This node has no in-edge; it is dirty if it is missing.
519 node.stat_if_necessary(self.disk_interface)?;
520 if !node.exists() {
521 explain!(
522 "{} has no in-edge and is missing",
523 String::from_utf8_lossy(node.path())
524 );
525 }
526 let dirty = !node.exists();
527 node.set_dirty(dirty);
528 Ok(())
529 }
530 Some(&edge_idx) => {
531 // If we already finished this edge then we are done.
532 match state.edge_state.get_edge(edge_idx).mark {
533 EdgeVisitMark::VisitDone => {
534 return Ok(());
535 }
536 _ => {}
537 };
538
539 // If we encountered this edge earlier in the call stack we have a cycle.
540 self.verify_dag(state, node_idx, stack)?;
541
542 let mut dirty = false;
543 let mut outputs_ready = true;
544 let mut deps_missing = false;
545
546 // Mark the edge temporarily while in the call stack.
547 state.edge_state.get_edge_mut(edge_idx).mark = EdgeVisitMark::VisitInStack;
548
549 stack.push(node_idx);
550 // Load output mtimes so we can compare them to the most recent input below.
551 for o_idx in state.edge_state.get_edge(edge_idx).outputs.iter().cloned() {
552 state.node_state.get_node_mut(o_idx).stat_if_necessary(
553 self.disk_interface,
554 )?;
555 }
556
557 if !self.dep_loader.load_deps(state, edge_idx)? {
558 // Failed to load dependency info: rebuild to regenerate it.
559 // LoadDeps() did EXPLAIN() already, no need to do it here.
560 dirty = true;
561 deps_missing = true;
562 }
563
564 let mut most_recent_input = None;
565 {
566 let (order_only_range, inputs) = {
567 let edge = state.edge_state.get_edge(edge_idx);
568 (edge.order_only_deps_range(), edge.inputs.clone())
569 };
570
571 for (i, i_idx) in inputs.into_iter().enumerate() {
572 // Visit this input.
573 self.recompute_dirty_inner(state, i_idx, stack)?;
574
575 // If an input is not ready, neither are our outputs.
576 if let Some(in_edge) = state.node_state.get_node(i_idx).in_edge() {
577 if !state.edge_state.get_edge(in_edge).outputs_ready {
578 outputs_ready = false;
579 }
580 }
581
582 if !order_only_range.contains_stable(i) {
583 // If a regular input is dirty (or missing), we're dirty.
584 // Otherwise consider mtime.
585 let i_node = state.node_state.get_node(i_idx);
586 if i_node.is_dirty() {
587 explain!("{} is dirty", String::from_utf8_lossy(i_node.path()));
588 dirty = true;
589 } else {
590 if most_recent_input
591 .as_ref()
592 .map(|&prev_idx| {
593 let prev_node = state.node_state.get_node(prev_idx);
594 i_node.mtime() > prev_node.mtime()
595 })
596 .unwrap_or(true)
597 {
598 most_recent_input = Some(i_idx);
599 }
600 }
601 }
602 }
603 }
604 // We may also be dirty due to output state: missing outputs, out of
605 // date outputs, etc. Visit all outputs and determine whether they're dirty.
606 if !dirty {
607 dirty = self.recompute_outputs_dirty(
608 state,
609 edge_idx,
610 most_recent_input,
611 )?;
612 }
613
614 if dirty {
615 let edge = state.edge_state.get_edge(edge_idx);
616
617 // Finally, visit each output and update their dirty state if necessary.
618 for o_idx in edge.outputs.iter().cloned() {
619 state.node_state.get_node_mut(o_idx).mark_dirty();
620 }
621
622 // If an edge is dirty, its outputs are normally not ready. (It's
623 // possible to be clean but still not be ready in the presence of
624 // order-only inputs.)
625 // But phony edges with no inputs have nothing to do, so are always
626 // ready.
627
628 if !(edge.is_phony() && edge.inputs.is_empty()) {
629 outputs_ready = false;
630 }
631 }
632
633 let edge = state.edge_state.get_edge_mut(edge_idx);
634 edge.deps_missing = deps_missing;
635 edge.outputs_ready = outputs_ready;
636
637 // Mark the edge as finished during this walk now that it will no longer
638 // be in the call stack.
639 edge.mark = EdgeVisitMark::VisitDone;
640 debug_assert!(stack.last() == Some(&node_idx));
641 stack.pop();
642 Ok(())
643 }
644 }
645 }
646
647 fn verify_dag(
648 &self,
649 state: &State,
650 node_idx: NodeIndex,
651 stack: &mut Vec<NodeIndex>,
652 ) -> Result<(), String> {
653 let edge_idx = state.node_state.get_node(node_idx).in_edge().unwrap();
654
655 // If we have no temporary mark on the edge then we do not yet have a cycle.
656 match state.edge_state.get_edge(edge_idx).mark {
657 EdgeVisitMark::VisitInStack => {}
658 _ => {
659 return Ok(());
660 }
661 };
662
663 // We have this edge earlier in the call stack. Find it.
664 let mut start = 0;
665 while start < stack.len() {
666 let item = stack[start];
667 if state.node_state.get_node(item).in_edge() == Some(edge_idx) {
668 break;
669 }
670 start += 1;
671 }
672 assert!(start < stack.len());
673
674 // Make the cycle clear by reporting its start as the node at its end
675 // instead of some other output of the starting edge. For example,
676 // running 'ninja b' on
677 // build a b: cat c
678 // build c: cat a
679 // should report a -> c -> a instead of b -> c -> a.
680 stack[start] = node_idx;
681
682 // Construct the error message rejecting the cycle.
683 let mut err = "dependency cycle: ".to_owned();
684 for iter_idx in stack[start..].iter() {
685 err += String::from_utf8_lossy(state.node_state.get_node(*iter_idx).path()).as_ref();
686 err += " -> ";
687 }
688
689 err += String::from_utf8_lossy(state.node_state.get_node(node_idx).path()).as_ref();
690
691 if start + 1 == stack.len() &&
692 state
693 .edge_state
694 .get_edge(edge_idx)
695 .maybe_phonycycle_diagnostic()
696 {
697 // The manifest parser would have filtered out the self-referencing
698 // input if it were not configured to allow the error.
699 err += " [-w phonycycle=err]";
700 }
701
702 return Err(err);
703 }
704
705 /// Recompute whether any output of the edge is dirty, if so sets |*dirty|.
706 /// Returns false on failure.
707 fn recompute_outputs_dirty(
708 &self,
709 state: &State,
710 edge_idx: EdgeIndex,
711 most_recent_input: Option<NodeIndex>,
712 ) -> Result<bool, String> {
713 let edge = state.edge_state.get_edge(edge_idx);
714 let command = edge.evaluate_command_with_rsp_file(&state.node_state, true);
715 for output in edge.outputs.iter() {
716 if self.recompute_output_dirty(state, edge_idx, most_recent_input, &command, *output) {
717 return Ok(true);
718 }
719 }
720 return Ok(false);
721 }
722
723 /// Recompute whether a given single output should be marked dirty.
724 /// Returns true if so.
725 fn recompute_output_dirty(
726 &self,
727 state: &State,
728 edge_idx: EdgeIndex,
729 most_recent_input: Option<NodeIndex>,
730 command: &[u8],
731 output_node_idx: NodeIndex,
732 ) -> bool {
733
734 let edge = state.edge_state.get_edge(edge_idx);
735 let output = state.node_state.get_node(output_node_idx);
736 let mut entry = None;
737
738 if edge.is_phony() {
739 // Phony edges don't write any output. Outputs are only dirty if
740 // there are no inputs and we're missing the output.
741 if edge.inputs.is_empty() && !output.exists() {
742 explain!(
743 "output {} of phony edge with no inputs doesn't exist",
744 String::from_utf8_lossy(output.path())
745 );
746
747 return true;
748 }
749 return false;
750 }
751
752 // Dirty if we're missing the output.
753 if !output.exists() {
754 explain!(
755 "output {} doesn't exist",
756 String::from_utf8_lossy(output.path())
757 );
758 return true;
759 }
760
761 // Dirty if the output is older than the input.
762 if let Some(ref most_recent_input_idx) = most_recent_input {
763 let most_recent_input = state.node_state.get_node(*most_recent_input_idx);
764 if output.mtime() < most_recent_input.mtime() {
765 let mut output_mtime = output.mtime();
766
767 // If this is a restat rule, we may have cleaned the output with a restat
768 // rule in a previous run and stored the most recent input mtime in the
769 // build log. Use that mtime instead, so that the file will only be
770 // considered dirty if an input was modified since the previous run.
771 let mut used_restat = false;
772
773 if edge.get_binding_bool(&state.node_state, b"restat") {
774 if let Some(build_log) = self.build_log() {
775 if entry.is_none() {
776 entry = build_log.lookup_by_output(output.path());
777 }
778 if let Some(found_entry) = entry {
779 output_mtime = found_entry.mtime;
780 used_restat = true;
781 }
782 }
783 }
784
785
786 if output_mtime < most_recent_input.mtime() {
787 explain!(
788 "{}output {} older than most recent input {} ({} vs {})",
789 if used_restat { "restat of " } else { "" },
790 String::from_utf8_lossy(output.path()),
791 String::from_utf8_lossy(most_recent_input.path()),
792 output_mtime,
793 most_recent_input.mtime
794 );
795 return true;
796 }
797 }
798 }
799
800 if let Some(build_log) = self.build_log() {
801 let generator = edge.get_binding_bool(&state.node_state, b"generator");
802 if entry.is_none() {
803 entry = build_log.lookup_by_output(output.path());
804 }
805 if let Some(found_entry) = entry {
806 unimplemented!()
807 /*
808 if (!generator &&
809 BuildLog::LogEntry::HashCommand(command) != entry->command_hash) {
810 // May also be dirty due to the command changing since the last build.
811 // But if this is a generator rule, the command changing does not make us
812 // dirty.
813 EXPLAIN("command line changed for %s", output->path().c_str());
814 return true;
815 }
816 if (most_recent_input && entry->mtime < most_recent_input->mtime()) {
817 // May also be dirty due to the mtime in the log being older than the
818 // mtime of the most recent input. This can occur even when the mtime
819 // on disk is newer if a previous run wrote to the output file but
820 // exited with an error or was interrupted.
821 EXPLAIN("recorded mtime of %s older than most recent input %s (%d vs %d)",
822 output->path().c_str(), most_recent_input->path().c_str(),
823 entry->mtime, most_recent_input->mtime());
824 return true;
825 }
826*/
827 }
828
829 if entry.is_none() && !generator {
830 explain!(
831 "command line not found in log for {}",
832 String::from_utf8_lossy(output.path())
833 );
834 return true;
835 }
836 }
837
838 return false;
839 }
840}
841
842/*
843
844struct DependencyScan {
845 DependencyScan(State* state, BuildLog* build_log, DepsLog* deps_log,
846 DiskInterface* disk_interface)
847 : build_log_(build_log),
848 disk_interface_(disk_interface),
849 dep_loader_(state, deps_log, disk_interface) {}
850
851 BuildLog* build_log() const {
852 return build_log_;
853 }
854 void set_build_log(BuildLog* log) {
855 build_log_ = log;
856 }
857
858 DepsLog* deps_log() const {
859 return dep_loader_.deps_log();
860 }
861
862 private:
863 bool RecomputeDirty(Node* node, vector<Node*>* stack, string* err);
864 bool VerifyDAG(Node* node, vector<Node*>* stack, string* err);
865
866
867
868 BuildLog* build_log_;
869 DiskInterface* disk_interface_;
870 ImplicitDepLoader dep_loader_;
871};
872
873#endif // NINJA_GRAPH_H_
874
875*/
876
877
878
879/*
880
881bool DependencyScan::RecomputeOutputDirty(Edge* edge,
882 Node* most_recent_input,
883 const string& command,
884 Node* output) {
885}
886
887*/
888
889#[derive(Clone, Copy)]
890enum EdgeEnvEscapeKind {
891 ShellEscape,
892 DoNotEscape,
893}
894
895/// An Env for an Edge, providing $in and $out.
896struct EdgeEnv<'a, 'b> {
897 lookups: RefCell<Vec<Vec<u8>>>,
898 edge: &'a Edge,
899 node_state: &'b NodeState,
900 escape_in_out: EdgeEnvEscapeKind,
901 recursive: Cell<bool>,
902}
903
904impl<'a, 'b> EdgeEnv<'a, 'b> {
905 pub fn new(edge: &'a Edge, node_state: &'b NodeState, escape: EdgeEnvEscapeKind) -> Self {
906 EdgeEnv {
907 lookups: RefCell::new(Vec::new()),
908 edge,
909 node_state,
910 escape_in_out: escape,
911 recursive: Cell::new(false),
912 }
913 }
914
915 /// Given a span of Nodes, construct a list of paths suitable for a command
916 /// line.
917 pub fn make_path_list(
918 node_state: &NodeState,
919 nodes: &[NodeIndex],
920 sep: u8,
921 escape_in_out: EdgeEnvEscapeKind,
922 ) -> Vec<u8> {
923
924 let mut result = Vec::new();
925 for node_idx in nodes {
926 if !result.is_empty() {
927 result.push(sep);
928 };
929 let node = node_state.get_node(*node_idx);
930 let path = node.path_decanonicalized();
931 match escape_in_out {
932 EdgeEnvEscapeKind::ShellEscape => {
933 if WINDOWS_PATH {
934 result.extend_from_win32_escaped_slice(&path);
935 } else {
936 result.extend_from_shell_escaped_slice(&path);
937 }
938 }
939 EdgeEnvEscapeKind::DoNotEscape => {
940 result.extend_from_slice(&path);
941 }
942 }
943 }
944 result
945 }
946}
947
948impl<'a, 'b> Env for EdgeEnv<'a, 'b> {
949 fn lookup_variable(&self, var: &[u8]) -> Cow<[u8]> {
950 if var == b"in".as_ref() || var == b"in_newline".as_ref() {
951 let sep = if var == b"in".as_ref() { b' ' } else { b'\n' };
952 let explicit_deps_range = self.edge.explicit_deps_range();
953 return Cow::Owned(EdgeEnv::make_path_list(
954 self.node_state,
955 &self.edge.inputs[explicit_deps_range],
956 sep,
957 self.escape_in_out,
958 ));
959 } else if var == b"out".as_ref() {
960 let explicit_outs_range = self.edge.explicit_outs_range();
961 return Cow::Owned(EdgeEnv::make_path_list(
962 self.node_state,
963 &self.edge.outputs[explicit_outs_range],
964 b' ',
965 self.escape_in_out,
966 ));
967 }
968
969 if self.recursive.get() {
970 let lookups = self.lookups.borrow();
971 let mut lookups_iter = lookups.iter().skip_while(|v| var != v as &[u8]).peekable();
972 if lookups_iter.peek().is_some() {
973 let mut cycle = Vec::new();
974 for it in lookups_iter {
975 cycle.extend_from_slice(&it);
976 cycle.extend_from_slice(b" -> ");
977 }
978 cycle.extend_from_slice(var);
979 fatal!(
980 "cycle in rule variables: {}",
981 String::from_utf8_lossy(&cycle)
982 )
983 }
984 }
985
986 // See notes on BindingEnv::LookupWithFallback.
987 let eval = self.edge.rule.get_binding(var);
988 if self.recursive.get() && eval.is_some() {
989 self.lookups.borrow_mut().push(var.to_owned());
990 }
991
992 // In practice, variables defined on rules never use another rule variable.
993 // For performance, only start checking for cycles after the first lookup.
994 self.recursive.set(true);
995 return Cow::Owned(
996 self.edge
997 .env
998 .borrow()
999 .lookup_with_fallback(var, eval, self)
1000 .into_owned(),
1001 );
1002 }
1003}
1004/*
1005
1006void Edge::Dump(const char* prefix) const {
1007 printf("%s[ ", prefix);
1008 for (vector<Node*>::const_iterator i = inputs_.begin();
1009 i != inputs_.end() && *i != NULL; ++i) {
1010 printf("%s ", (*i)->path().c_str());
1011 }
1012 printf("--%s-> ", rule_->name().c_str());
1013 for (vector<Node*>::const_iterator i = outputs_.begin();
1014 i != outputs_.end() && *i != NULL; ++i) {
1015 printf("%s ", (*i)->path().c_str());
1016 }
1017 if (pool_) {
1018 if (!pool_->name().empty()) {
1019 printf("(in pool '%s')", pool_->name().c_str());
1020 }
1021 } else {
1022 printf("(null pool?)");
1023 }
1024 printf("] 0x%p\n", this);
1025}
1026
1027
1028void Node::Dump(const char* prefix) const {
1029 printf("%s <%s 0x%p> mtime: %d%s, (:%s), ",
1030 prefix, path().c_str(), this,
1031 mtime(), mtime() ? "" : " (:missing)",
1032 dirty() ? " dirty" : " clean");
1033 if (in_edge()) {
1034 in_edge()->Dump("in-edge: ");
1035 } else {
1036 printf("no in-edge\n");
1037 }
1038 printf(" out edges:\n");
1039 for (vector<Edge*>::const_iterator e = out_edges().begin();
1040 e != out_edges().end() && *e != NULL; ++e) {
1041 (*e)->Dump(" +- ");
1042 }
1043}
1044
1045bool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) {
1046 string deps_type = edge->GetBinding("deps");
1047 if (!deps_type.empty())
1048 return LoadDepsFromLog(edge, err);
1049
1050 string depfile = edge->GetUnescapedDepfile();
1051 if (!depfile.empty())
1052 return LoadDepFile(edge, depfile, err);
1053
1054 // No deps to load.
1055 return true;
1056}
1057
1058bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path,
1059 string* err) {
1060 METRIC_RECORD("depfile load");
1061 // Read depfile content. Treat a missing depfile as empty.
1062 string content;
1063 switch (disk_interface_->ReadFile(path, &content, err)) {
1064 case DiskInterface::Okay:
1065 break;
1066 case DiskInterface::NotFound:
1067 err->clear();
1068 break;
1069 case DiskInterface::OtherError:
1070 *err = "loading '" + path + "': " + *err;
1071 return false;
1072 }
1073 // On a missing depfile: return false and empty *err.
1074 if (content.empty()) {
1075 EXPLAIN("depfile '%s' is missing", path.c_str());
1076 return false;
1077 }
1078
1079 DepfileParser depfile;
1080 string depfile_err;
1081 if (!depfile.Parse(&content, &depfile_err)) {
1082 *err = path + ": " + depfile_err;
1083 return false;
1084 }
1085
1086 uint64_t unused;
1087 if (!CanonicalizePath(const_cast<char*>(depfile.out_.str_),
1088 &depfile.out_.len_, &unused, err)) {
1089 *err = path + ": " + *err;
1090 return false;
1091 }
1092
1093 // Check that this depfile matches the edge's output, if not return false to
1094 // mark the edge as dirty.
1095 Node* first_output = edge->outputs_[0];
1096 StringPiece opath = StringPiece(first_output->path());
1097 if (opath != depfile.out_) {
1098 EXPLAIN("expected depfile '%s' to mention '%s', got '%s'", path.c_str(),
1099 first_output->path().c_str(), depfile.out_.AsString().c_str());
1100 return false;
1101 }
1102
1103 // Preallocate space in edge->inputs_ to be filled in below.
1104 vector<Node*>::iterator implicit_dep =
1105 PreallocateSpace(edge, depfile.ins_.size());
1106
1107 // Add all its in-edges.
1108 for (vector<StringPiece>::iterator i = depfile.ins_.begin();
1109 i != depfile.ins_.end(); ++i, ++implicit_dep) {
1110 uint64_t slash_bits;
1111 if (!CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits,
1112 err))
1113 return false;
1114
1115 Node* node = state_->GetNode(*i, slash_bits);
1116 *implicit_dep = node;
1117 node->AddOutEdge(edge);
1118 CreatePhonyInEdge(node);
1119 }
1120
1121 return true;
1122}
1123
1124bool ImplicitDepLoader::LoadDepsFromLog(Edge* edge, string* err) {
1125 // NOTE: deps are only supported for single-target edges.
1126 Node* output = edge->outputs_[0];
1127 DepsLog::Deps* deps = deps_log_->GetDeps(output);
1128 if (!deps) {
1129 EXPLAIN("deps for '%s' are missing", output->path().c_str());
1130 return false;
1131 }
1132
1133 // Deps are invalid if the output is newer than the deps.
1134 if (output->mtime() > deps->mtime) {
1135 EXPLAIN("stored deps info out of date for '%s' (%d vs %d)",
1136 output->path().c_str(), deps->mtime, output->mtime());
1137 return false;
1138 }
1139
1140 vector<Node*>::iterator implicit_dep =
1141 PreallocateSpace(edge, deps->node_count);
1142 for (int i = 0; i < deps->node_count; ++i, ++implicit_dep) {
1143 Node* node = deps->nodes[i];
1144 *implicit_dep = node;
1145 node->AddOutEdge(edge);
1146 CreatePhonyInEdge(node);
1147 }
1148 return true;
1149}
1150
1151vector<Node*>::iterator ImplicitDepLoader::PreallocateSpace(Edge* edge,
1152 int count) {
1153 edge->inputs_.insert(edge->inputs_.end() - edge->order_only_deps_,
1154 (size_t)count, 0);
1155 edge->implicit_deps_ += count;
1156 return edge->inputs_.end() - edge->order_only_deps_ - count;
1157}
1158
1159void ImplicitDepLoader::CreatePhonyInEdge(Node* node) {
1160 if (node->in_edge())
1161 return;
1162
1163 Edge* phony_edge = state_->AddEdge(&State::kPhonyRule);
1164 node->set_in_edge(phony_edge);
1165 phony_edge->outputs_.push_back(node);
1166
1167 // RecomputeDirty might not be called for phony_edge if a previous call
1168 // to RecomputeDirty had caused the file to be stat'ed. Because previous
1169 // invocations of RecomputeDirty would have seen this node without an
1170 // input edge (and therefore ready), we have to set outputs_ready_ to true
1171 // to avoid a potential stuck build. If we do call RecomputeDirty for
1172 // this node, it will simply set outputs_ready_ to the correct value.
1173 phony_edge->outputs_ready_ = true;
1174}
1175
1176*/
1177
1178/*
1179// Copyright 2011 Google Inc. All Rights Reserved.
1180//
1181// Licensed under the Apache License, Version 2.0 (the "License");
1182// you may not use this file except in compliance with the License.
1183// You may obtain a copy of the License at
1184//
1185// http://www.apache.org/licenses/LICENSE-2.0
1186//
1187// Unless required by applicable law or agreed to in writing, software
1188// distributed under the License is distributed on an "AS IS" BASIS,
1189// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1190// See the License for the specific language governing permissions and
1191// limitations under the License.
1192
1193#include "graph.h"
1194#include "build.h"
1195
1196#include "test.h"
1197
1198struct GraphTest : public StateTestWithBuiltinRules {
1199 GraphTest() : scan_(&state_, NULL, NULL, &fs_) {}
1200
1201 VirtualFileSystem fs_;
1202 DependencyScan scan_;
1203};
1204
1205TEST_F(GraphTest, MissingImplicit) {
1206 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1207"build out: cat in | implicit\n"));
1208 fs_.Create("in", "");
1209 fs_.Create("out", "");
1210
1211 string err;
1212 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
1213 ASSERT_EQ("", err);
1214
1215 // A missing implicit dep *should* make the output dirty.
1216 // (In fact, a build will fail.)
1217 // This is a change from prior semantics of ninja.
1218 EXPECT_TRUE(GetNode("out")->dirty());
1219}
1220
1221TEST_F(GraphTest, ModifiedImplicit) {
1222 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1223"build out: cat in | implicit\n"));
1224 fs_.Create("in", "");
1225 fs_.Create("out", "");
1226 fs_.Tick();
1227 fs_.Create("implicit", "");
1228
1229 string err;
1230 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
1231 ASSERT_EQ("", err);
1232
1233 // A modified implicit dep should make the output dirty.
1234 EXPECT_TRUE(GetNode("out")->dirty());
1235}
1236
1237TEST_F(GraphTest, FunkyMakefilePath) {
1238 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1239"rule catdep\n"
1240" depfile = $out.d\n"
1241" command = cat $in > $out\n"
1242"build out.o: catdep foo.cc\n"));
1243 fs_.Create("foo.cc", "");
1244 fs_.Create("out.o.d", "out.o: ./foo/../implicit.h\n");
1245 fs_.Create("out.o", "");
1246 fs_.Tick();
1247 fs_.Create("implicit.h", "");
1248
1249 string err;
1250 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
1251 ASSERT_EQ("", err);
1252
1253 // implicit.h has changed, though our depfile refers to it with a
1254 // non-canonical path; we should still find it.
1255 EXPECT_TRUE(GetNode("out.o")->dirty());
1256}
1257
1258TEST_F(GraphTest, ExplicitImplicit) {
1259 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1260"rule catdep\n"
1261" depfile = $out.d\n"
1262" command = cat $in > $out\n"
1263"build implicit.h: cat data\n"
1264"build out.o: catdep foo.cc || implicit.h\n"));
1265 fs_.Create("implicit.h", "");
1266 fs_.Create("foo.cc", "");
1267 fs_.Create("out.o.d", "out.o: implicit.h\n");
1268 fs_.Create("out.o", "");
1269 fs_.Tick();
1270 fs_.Create("data", "");
1271
1272 string err;
1273 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
1274 ASSERT_EQ("", err);
1275
1276 // We have both an implicit and an explicit dep on implicit.h.
1277 // The implicit dep should "win" (in the sense that it should cause
1278 // the output to be dirty).
1279 EXPECT_TRUE(GetNode("out.o")->dirty());
1280}
1281
1282TEST_F(GraphTest, ImplicitOutputParse) {
1283 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1284"build out | out.imp: cat in\n"));
1285
1286 Edge* edge = GetNode("out")->in_edge();
1287 EXPECT_EQ(2, edge->outputs_.size());
1288 EXPECT_EQ("out", edge->outputs_[0]->path());
1289 EXPECT_EQ("out.imp", edge->outputs_[1]->path());
1290 EXPECT_EQ(1, edge->implicit_outs_);
1291 EXPECT_EQ(edge, GetNode("out.imp")->in_edge());
1292}
1293
1294TEST_F(GraphTest, ImplicitOutputMissing) {
1295 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1296"build out | out.imp: cat in\n"));
1297 fs_.Create("in", "");
1298 fs_.Create("out", "");
1299
1300 string err;
1301 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
1302 ASSERT_EQ("", err);
1303
1304 EXPECT_TRUE(GetNode("out")->dirty());
1305 EXPECT_TRUE(GetNode("out.imp")->dirty());
1306}
1307
1308TEST_F(GraphTest, ImplicitOutputOutOfDate) {
1309 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1310"build out | out.imp: cat in\n"));
1311 fs_.Create("out.imp", "");
1312 fs_.Tick();
1313 fs_.Create("in", "");
1314 fs_.Create("out", "");
1315
1316 string err;
1317 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
1318 ASSERT_EQ("", err);
1319
1320 EXPECT_TRUE(GetNode("out")->dirty());
1321 EXPECT_TRUE(GetNode("out.imp")->dirty());
1322}
1323
1324TEST_F(GraphTest, ImplicitOutputOnlyParse) {
1325 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1326"build | out.imp: cat in\n"));
1327
1328 Edge* edge = GetNode("out.imp")->in_edge();
1329 EXPECT_EQ(1, edge->outputs_.size());
1330 EXPECT_EQ("out.imp", edge->outputs_[0]->path());
1331 EXPECT_EQ(1, edge->implicit_outs_);
1332 EXPECT_EQ(edge, GetNode("out.imp")->in_edge());
1333}
1334
1335TEST_F(GraphTest, ImplicitOutputOnlyMissing) {
1336 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1337"build | out.imp: cat in\n"));
1338 fs_.Create("in", "");
1339
1340 string err;
1341 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), &err));
1342 ASSERT_EQ("", err);
1343
1344 EXPECT_TRUE(GetNode("out.imp")->dirty());
1345}
1346
1347TEST_F(GraphTest, ImplicitOutputOnlyOutOfDate) {
1348 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1349"build | out.imp: cat in\n"));
1350 fs_.Create("out.imp", "");
1351 fs_.Tick();
1352 fs_.Create("in", "");
1353
1354 string err;
1355 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), &err));
1356 ASSERT_EQ("", err);
1357
1358 EXPECT_TRUE(GetNode("out.imp")->dirty());
1359}
1360
1361TEST_F(GraphTest, PathWithCurrentDirectory) {
1362 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1363"rule catdep\n"
1364" depfile = $out.d\n"
1365" command = cat $in > $out\n"
1366"build ./out.o: catdep ./foo.cc\n"));
1367 fs_.Create("foo.cc", "");
1368 fs_.Create("out.o.d", "out.o: foo.cc\n");
1369 fs_.Create("out.o", "");
1370
1371 string err;
1372 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
1373 ASSERT_EQ("", err);
1374
1375 EXPECT_FALSE(GetNode("out.o")->dirty());
1376}
1377
1378TEST_F(GraphTest, RootNodes) {
1379 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1380"build out1: cat in1\n"
1381"build mid1: cat in1\n"
1382"build out2: cat mid1\n"
1383"build out3 out4: cat mid1\n"));
1384
1385 string err;
1386 vector<Node*> root_nodes = state_.RootNodes(&err);
1387 EXPECT_EQ(4u, root_nodes.size());
1388 for (size_t i = 0; i < root_nodes.size(); ++i) {
1389 string name = root_nodes[i]->path();
1390 EXPECT_EQ("out", name.substr(0, 3));
1391 }
1392}
1393
1394TEST_F(GraphTest, VarInOutPathEscaping) {
1395 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1396"build a$ b: cat no'space with$ space$$ no\"space2\n"));
1397
1398 Edge* edge = GetNode("a b")->in_edge();
1399#if _WIN32
1400 EXPECT_EQ("cat no'space \"with space$\" \"no\\\"space2\" > \"a b\"",
1401 edge->EvaluateCommand());
1402#else
1403 EXPECT_EQ("cat 'no'\\''space' 'with space$' 'no\"space2' > 'a b'",
1404 edge->EvaluateCommand());
1405#endif
1406}
1407
1408// Regression test for https://github.com/ninja-build/ninja/issues/380
1409TEST_F(GraphTest, DepfileWithCanonicalizablePath) {
1410 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1411"rule catdep\n"
1412" depfile = $out.d\n"
1413" command = cat $in > $out\n"
1414"build ./out.o: catdep ./foo.cc\n"));
1415 fs_.Create("foo.cc", "");
1416 fs_.Create("out.o.d", "out.o: bar/../foo.cc\n");
1417 fs_.Create("out.o", "");
1418
1419 string err;
1420 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
1421 ASSERT_EQ("", err);
1422
1423 EXPECT_FALSE(GetNode("out.o")->dirty());
1424}
1425
1426// Regression test for https://github.com/ninja-build/ninja/issues/404
1427TEST_F(GraphTest, DepfileRemoved) {
1428 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1429"rule catdep\n"
1430" depfile = $out.d\n"
1431" command = cat $in > $out\n"
1432"build ./out.o: catdep ./foo.cc\n"));
1433 fs_.Create("foo.h", "");
1434 fs_.Create("foo.cc", "");
1435 fs_.Tick();
1436 fs_.Create("out.o.d", "out.o: foo.h\n");
1437 fs_.Create("out.o", "");
1438
1439 string err;
1440 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
1441 ASSERT_EQ("", err);
1442 EXPECT_FALSE(GetNode("out.o")->dirty());
1443
1444 state_.Reset();
1445 fs_.RemoveFile("out.o.d");
1446 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
1447 ASSERT_EQ("", err);
1448 EXPECT_TRUE(GetNode("out.o")->dirty());
1449}
1450
1451// Check that rule-level variables are in scope for eval.
1452TEST_F(GraphTest, RuleVariablesInScope) {
1453 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1454"rule r\n"
1455" depfile = x\n"
1456" command = depfile is $depfile\n"
1457"build out: r in\n"));
1458 Edge* edge = GetNode("out")->in_edge();
1459 EXPECT_EQ("depfile is x", edge->EvaluateCommand());
1460}
1461
1462// Check that build statements can override rule builtins like depfile.
1463TEST_F(GraphTest, DepfileOverride) {
1464 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1465"rule r\n"
1466" depfile = x\n"
1467" command = unused\n"
1468"build out: r in\n"
1469" depfile = y\n"));
1470 Edge* edge = GetNode("out")->in_edge();
1471 EXPECT_EQ("y", edge->GetBinding("depfile"));
1472}
1473
1474// Check that overridden values show up in expansion of rule-level bindings.
1475TEST_F(GraphTest, DepfileOverrideParent) {
1476 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1477"rule r\n"
1478" depfile = x\n"
1479" command = depfile is $depfile\n"
1480"build out: r in\n"
1481" depfile = y\n"));
1482 Edge* edge = GetNode("out")->in_edge();
1483 EXPECT_EQ("depfile is y", edge->GetBinding("command"));
1484}
1485
1486// Verify that building a nested phony rule prints "no work to do"
1487TEST_F(GraphTest, NestedPhonyPrintsDone) {
1488 AssertParse(&state_,
1489"build n1: phony \n"
1490"build n2: phony n1\n"
1491 );
1492 string err;
1493 EXPECT_TRUE(scan_.RecomputeDirty(GetNode("n2"), &err));
1494 ASSERT_EQ("", err);
1495
1496 Plan plan_;
1497 EXPECT_TRUE(plan_.AddTarget(GetNode("n2"), &err));
1498 ASSERT_EQ("", err);
1499
1500 EXPECT_EQ(0, plan_.command_edge_count());
1501 ASSERT_FALSE(plan_.more_to_do());
1502}
1503
1504TEST_F(GraphTest, PhonySelfReferenceError) {
1505 ManifestParserOptions parser_opts;
1506 parser_opts.phony_cycle_action_ = kPhonyCycleActionError;
1507 AssertParse(&state_,
1508"build a: phony a\n",
1509 parser_opts);
1510
1511 string err;
1512 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err));
1513 ASSERT_EQ("dependency cycle: a -> a [-w phonycycle=err]", err);
1514}
1515
1516TEST_F(GraphTest, DependencyCycle) {
1517 AssertParse(&state_,
1518"build out: cat mid\n"
1519"build mid: cat in\n"
1520"build in: cat pre\n"
1521"build pre: cat out\n");
1522
1523 string err;
1524 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), &err));
1525 ASSERT_EQ("dependency cycle: out -> mid -> in -> pre -> out", err);
1526}
1527
1528TEST_F(GraphTest, CycleInEdgesButNotInNodes1) {
1529 string err;
1530 AssertParse(&state_,
1531"build a b: cat a\n");
1532 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err));
1533 ASSERT_EQ("dependency cycle: a -> a", err);
1534}
1535
1536TEST_F(GraphTest, CycleInEdgesButNotInNodes2) {
1537 string err;
1538 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1539"build b a: cat a\n"));
1540 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err));
1541 ASSERT_EQ("dependency cycle: a -> a", err);
1542}
1543
1544TEST_F(GraphTest, CycleInEdgesButNotInNodes3) {
1545 string err;
1546 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1547"build a b: cat c\n"
1548"build c: cat a\n"));
1549 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err));
1550 ASSERT_EQ("dependency cycle: a -> c -> a", err);
1551}
1552
1553TEST_F(GraphTest, CycleInEdgesButNotInNodes4) {
1554 string err;
1555 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1556"build d: cat c\n"
1557"build c: cat b\n"
1558"build b: cat a\n"
1559"build a e: cat d\n"
1560"build f: cat e\n"));
1561 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("f"), &err));
1562 ASSERT_EQ("dependency cycle: a -> d -> c -> b -> a", err);
1563}
1564
1565// Verify that cycles in graphs with multiple outputs are handled correctly
1566// in RecomputeDirty() and don't cause deps to be loaded multiple times.
1567TEST_F(GraphTest, CycleWithLengthZeroFromDepfile) {
1568 AssertParse(&state_,
1569"rule deprule\n"
1570" depfile = dep.d\n"
1571" command = unused\n"
1572"build a b: deprule\n"
1573 );
1574 fs_.Create("dep.d", "a: b\n");
1575
1576 string err;
1577 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err));
1578 ASSERT_EQ("dependency cycle: b -> b", err);
1579
1580 // Despite the depfile causing edge to be a cycle (it has outputs a and b,
1581 // but the depfile also adds b as an input), the deps should have been loaded
1582 // only once:
1583 Edge* edge = GetNode("a")->in_edge();
1584 EXPECT_EQ(1, edge->inputs_.size());
1585 EXPECT_EQ("b", edge->inputs_[0]->path());
1586}
1587
1588// Like CycleWithLengthZeroFromDepfile but with a higher cycle length.
1589TEST_F(GraphTest, CycleWithLengthOneFromDepfile) {
1590 AssertParse(&state_,
1591"rule deprule\n"
1592" depfile = dep.d\n"
1593" command = unused\n"
1594"rule r\n"
1595" command = unused\n"
1596"build a b: deprule\n"
1597"build c: r b\n"
1598 );
1599 fs_.Create("dep.d", "a: c\n");
1600
1601 string err;
1602 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err));
1603 ASSERT_EQ("dependency cycle: b -> c -> b", err);
1604
1605 // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
1606 // but c's in_edge has b as input but the depfile also adds |edge| as
1607 // output)), the deps should have been loaded only once:
1608 Edge* edge = GetNode("a")->in_edge();
1609 EXPECT_EQ(1, edge->inputs_.size());
1610 EXPECT_EQ("c", edge->inputs_[0]->path());
1611}
1612
1613// Like CycleWithLengthOneFromDepfile but building a node one hop away from
1614// the cycle.
1615TEST_F(GraphTest, CycleWithLengthOneFromDepfileOneHopAway) {
1616 AssertParse(&state_,
1617"rule deprule\n"
1618" depfile = dep.d\n"
1619" command = unused\n"
1620"rule r\n"
1621" command = unused\n"
1622"build a b: deprule\n"
1623"build c: r b\n"
1624"build d: r a\n"
1625 );
1626 fs_.Create("dep.d", "a: c\n");
1627
1628 string err;
1629 EXPECT_FALSE(scan_.RecomputeDirty(GetNode("d"), &err));
1630 ASSERT_EQ("dependency cycle: b -> c -> b", err);
1631
1632 // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
1633 // but c's in_edge has b as input but the depfile also adds |edge| as
1634 // output)), the deps should have been loaded only once:
1635 Edge* edge = GetNode("a")->in_edge();
1636 EXPECT_EQ(1, edge->inputs_.size());
1637 EXPECT_EQ("c", edge->inputs_[0]->path());
1638}
1639
1640#ifdef _WIN32
1641TEST_F(GraphTest, Decanonicalize) {
1642 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1643"build out\\out1: cat src\\in1\n"
1644"build out\\out2/out3\\out4: cat mid1\n"
1645"build out3 out4\\foo: cat mid1\n"));
1646
1647 string err;
1648 vector<Node*> root_nodes = state_.RootNodes(&err);
1649 EXPECT_EQ(4u, root_nodes.size());
1650 EXPECT_EQ(root_nodes[0]->path(), "out/out1");
1651 EXPECT_EQ(root_nodes[1]->path(), "out/out2/out3/out4");
1652 EXPECT_EQ(root_nodes[2]->path(), "out3");
1653 EXPECT_EQ(root_nodes[3]->path(), "out4/foo");
1654 EXPECT_EQ(root_nodes[0]->PathDecanonicalized(), "out\\out1");
1655 EXPECT_EQ(root_nodes[1]->PathDecanonicalized(), "out\\out2/out3\\out4");
1656 EXPECT_EQ(root_nodes[2]->PathDecanonicalized(), "out3");
1657 EXPECT_EQ(root_nodes[3]->PathDecanonicalized(), "out4\\foo");
1658}
1659#endif
1660
1661*/