blob: 21c95ebfd4d3840096a2270f2a5ade4761fb3f9c [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef NINJA_GRAPH_H_
#define NINJA_GRAPH_H_
#include <atomic>
#include <set>
#include <string>
#include <vector>
using namespace std;
#include "eval_env.h"
#include "timestamp.h"
#include "util.h"
struct BuildLog;
struct DiskInterface;
struct DepsLog;
struct Edge;
struct Node;
struct Pool;
struct State;
struct ThreadPool;
/// A reference to a path from the lexer. This path is unevaluated, stored in
/// mmap'ed memory, and is guaranteed to be followed by a terminating character
/// (e.g. whitespace, a colon or pipe, etc).
struct LexedPath {
StringPiece str_;
};
#ifdef _WIN32
/// On Windows, we record the '/'-vs-'\' slash direction in the first reference
/// to a path, which determines the path strings used in command-lines.
struct NodeSlashBits {
NodeSlashBits() {}
NodeSlashBits(uint64_t value) : slash_bits_(value) {}
uint64_t slash_bits() const { return slash_bits_; }
private:
uint64_t slash_bits_ = 0;
};
#else
/// By default, '\' is not recognized as a path separator, and slash_bits is
/// always 0.
struct NodeSlashBits {
NodeSlashBits() {}
NodeSlashBits(uint64_t /*value*/) {}
uint64_t slash_bits() const { return 0; }
};
#endif // _WIN32
/// Record the earliest reference to the node, which is needed for two uses:
///
/// - To verify that a node exists before a "default" declaration references
/// it.
/// - On Windows, the earliest reference to the node determines the slash_bits
/// value for decanonicalizing the node's path.
///
/// On Windows, atomic operations on this struct probably use a spin lock, but
/// it can be configured to use a DWCAS (e.g. -mcx16 with Clang/libc++). On
/// other targets, the empty base class occupies 0 bytes, and atomic operations
/// will be lock-free.
struct NodeFirstReference : NodeSlashBits {
NodeFirstReference() {}
NodeFirstReference(DeclIndex loc, uint64_t slash_bits)
: NodeSlashBits(slash_bits), loc_(loc) {}
DeclIndex dfs_location() const { return loc_; }
private:
DeclIndex loc_ = kLastDeclIndex;
};
inline bool operator<(const NodeFirstReference& x,
const NodeFirstReference& y) {
if (x.dfs_location() < y.dfs_location()) return true;
if (x.dfs_location() > y.dfs_location()) return false;
return x.slash_bits() < y.slash_bits();
}
/// Information about a node in the dependency graph: the file, whether
/// it's dirty, mtime, etc.
struct Node {
Node(const HashedStrView& path, uint64_t initial_slash_bits)
: path_(path),
first_reference_({ kLastDeclIndex, initial_slash_bits }) {}
~Node();
/// Precompute the node's Stat() call from a worker thread with exclusive
/// access to this node. Returns false on error.
bool PrecomputeStat(DiskInterface* disk_interface, string* err);
/// After the dependency scan is complete, reset the precomputed mtime so it
/// can't affect later StatIfNecessary() calls.
void ClearPrecomputedStat() {
precomputed_mtime_ = -1;
}
/// Return false on error.
/// Uses stat() or lstat() as appropriate.
bool Stat(DiskInterface* disk_interface, string* err);
/// Only use when lstat() is desired (output files)
bool LStat(DiskInterface* disk_interface, bool* is_dir, string* err);
/// Return false on error.
bool StatIfNecessary(DiskInterface* disk_interface, string* err) {
if (status_known())
return true;
if (precomputed_mtime_ >= 0) {
mtime_ = precomputed_mtime_;
return true;
}
return Stat(disk_interface, err);
}
/// Mark as not-yet-stat()ed and not dirty.
void ResetState() {
mtime_ = -1;
precomputed_mtime_ = -1;
dirty_ = false;
precomputed_dirtiness_ = false;
}
/// Mark the Node as already-stat()ed and missing.
void MarkMissing() {
mtime_ = 0;
}
bool exists() const {
return mtime_ != 0;
}
bool status_known() const {
return mtime_ != -1;
}
const std::string& path() const { return path_.str(); }
const HashedStr& path_hashed() const { return path_; }
/// Get |path()| but use slash_bits to convert back to original slash styles.
string PathDecanonicalized() const {
return PathDecanonicalized(path_.str(), slash_bits());
}
static string PathDecanonicalized(const string& path,
uint64_t slash_bits);
TimeStamp mtime() const { return mtime_; }
bool dirty() const { return dirty_; }
void set_dirty(bool dirty) { dirty_ = dirty; }
void MarkDirty() { dirty_ = true; }
bool precomputed_dirtiness() const { return precomputed_dirtiness_; }
void set_precomputed_dirtiness(bool value) { precomputed_dirtiness_ = value; }
Edge* in_edge() const { return in_edge_; }
void set_in_edge(Edge* edge) { in_edge_ = edge; }
int id() const { return id_; }
void set_id(int id) { id_ = id; }
// Thread-safe properties.
DeclIndex dfs_location() const {
return first_reference_.load().dfs_location();
}
uint64_t slash_bits() const {
return first_reference_.load().slash_bits();
}
void UpdateFirstReference(DeclIndex dfs_location, uint64_t slash_bits) {
AtomicUpdateMinimum(&first_reference_, { dfs_location, slash_bits });
}
// Thread-safe properties.
bool has_out_edge() const;
std::vector<Edge*> GetOutEdges() const;
std::vector<Edge*> GetValidationOutEdges() const;
void AddOutEdge(Edge* edge);
void AddValidationOutEdge(Edge* edge);
/// Add an out-edge from the dependency scan. This function differs from
/// AddOutEdge in several ways:
/// - It uses a simple vector, which is faster for single-threaded use.
/// - It's not thread-safe.
/// - It preserves edge order. Edges added with AddOutEdge come from the
/// manifest and are ordered by their position within the manifest
/// (represented with the edge ID). Dep scan edges, on the other hand,
/// are ordered by a DFS walk from target nodes to their dependencies.
/// (I'm not sure whether this order is practically important.)
void AddOutEdgeDepScan(Edge* edge) { dep_scan_out_edges_.push_back(edge); }
void Dump(const char* prefix="") const;
// Used in the inputs debug tool.
bool InputsChecked() const { return inputs_checked_; }
void MarkInputsChecked() { inputs_checked_ = true; }
private:
const HashedStr path_;
/// Possible values of mtime_:
/// -1: file hasn't been examined
/// 0: we looked, and file doesn't exist
/// >0: actual file's mtime
TimeStamp mtime_ = -1;
/// If this value is >= 0, it represents a precomputed mtime for the node.
TimeStamp precomputed_mtime_ = -1;
/// Dirty is true when the underlying file is out-of-date.
/// But note that Edge::outputs_ready_ is also used in judging which
/// edges to build.
bool dirty_ = false;
/// Set to true once the node's stat and command-hash info have been
/// precomputed.
bool precomputed_dirtiness_ = false;
/// A dense integer id for the node, assigned and used by DepsLog.
int id_ = -1;
std::atomic<NodeFirstReference> first_reference_;
Edge* in_edge_ = nullptr;
struct EdgeList {
EdgeList(Edge* edge=nullptr, EdgeList* next=nullptr)
: edge(edge), next(next) {}
Edge* edge = nullptr;
EdgeList* next = nullptr;
};
/// All Edges that use this Node as an input. The order of this list is
/// non-deterministic. An accessor function sorts it each time it's used.
std::atomic<EdgeList*> out_edges_ { nullptr };
std::atomic<EdgeList*> validation_out_edges_ { nullptr };
std::vector<Edge*> dep_scan_out_edges_;
/// Stores if this node's inputs have been already computed. Used in the
/// inputs debug tool.
bool inputs_checked_ = false;
};
struct EdgeEval {
enum EvalPhase { kParseTime, kFinalScope };
enum EscapeKind { kShellEscape, kDoNotEscape };
EdgeEval(Edge* edge, EvalPhase eval_phase, EscapeKind escape)
: edge_(edge),
eval_phase_(eval_phase),
escape_in_out_(escape) {}
/// Looks up the variable and appends its value to the output buffer. Returns
/// false on error (i.e. a cycle in rule variable expansion).
bool EvaluateVariable(std::string* out_append, const HashedStrView& var,
std::string* err);
/// There are only a small number of bindings allowed on a rule. If we recurse
/// enough times, we're guaranteed to repeat a variable.
static constexpr int kEvalRecursionLimit = 16;
private:
Edge* edge_ = nullptr;
EvalPhase eval_phase_ = kFinalScope;
/// The kind of escaping to do on $in and $out path variables.
EscapeKind escape_in_out_ = kShellEscape;
int recursion_count_ = 0;
StringPiece recursion_vars_[kEvalRecursionLimit];
void AppendPathList(std::string* out_append,
std::vector<Node*>::iterator begin,
std::vector<Node*>::iterator end,
char sep);
};
/// An edge in the dependency graph; links between Nodes using Rules.
struct Edge {
enum VisitMark {
VisitNone,
VisitInStack,
VisitDone
};
struct DepScanInfo {
bool valid = false;
bool restat = false;
bool generator = false;
bool deps = false;
bool depfile = false;
bool phony_output = false;
uint64_t command_hash = 0;
};
/// Return true if all inputs' in-edges are ready.
bool AllInputsReady() const;
/// Expand all variables in a command and return it as a string.
/// If incl_rsp_file is enabled, the string will also contain the
/// full contents of a response file (if applicable)
bool EvaluateCommand(std::string* out_append, bool incl_rsp_file,
std::string* err);
/// Convenience method. This method must not be called from a worker thread,
/// because it could abort with a fatal error. (For consistency with other
/// Get*/Evaluate* methods, a better name might be GetCommand.)
std::string EvaluateCommand(bool incl_rsp_file = false);
/// Attempts to evaluate info needed for scanning dependencies.
bool PrecomputeDepScanInfo(std::string* err);
/// Returns dependency-scanning info or exits with a fatal error. These
/// methods must not be called until after the manifest has been loaded.
const DepScanInfo& ComputeDepScanInfo();
uint64_t GetCommandHash() { return ComputeDepScanInfo().command_hash; }
bool IsRestat() { return ComputeDepScanInfo().restat; }
bool IsGenerator() { return ComputeDepScanInfo().generator; }
bool IsPhonyOutput() { return ComputeDepScanInfo().phony_output; }
bool UsesDepsLog() { return ComputeDepScanInfo().deps; }
bool UsesDepfile() { return ComputeDepScanInfo().depfile; }
/// Appends the value of |key| to the output buffer. On error, returns false,
/// and the content of the output buffer is unspecified.
bool EvaluateVariable(std::string* out_append, const HashedStrView& key,
std::string* err,
EdgeEval::EvalPhase phase=EdgeEval::kFinalScope,
EdgeEval::EscapeKind escape=EdgeEval::kShellEscape);
private:
std::string GetBindingImpl(const HashedStrView& key,
EdgeEval::EvalPhase phase,
EdgeEval::EscapeKind escape);
public:
/// Convenience method for EvaluateVariable. On failure, it issues a fatal
/// error. This function must not be called from a worker thread because:
/// - Error reporting should be deterministic, and
/// - Fatal() destructs static globals, which a worker thread could be using.
std::string GetBinding(const HashedStrView& key);
/// Like GetBinding("depfile"), but without shell escaping.
string GetUnescapedDepfile();
/// Like GetBinding("rspfile"), but without shell escaping.
string GetUnescapedRspfile();
void Dump(const char* prefix="") const;
/// Temporary fields used only during manifest parsing.
struct DeferredPathList {
enum Type {
INPUT = 0,
OUTPUT = 1,
VALIDATION = 2,
};
DeferredPathList(const char* lexer_pos=nullptr,
Type type = INPUT, int count=0)
: lexer_pos(lexer_pos), type(type), count(count) {}
const char* lexer_pos = nullptr;
Type type;
int count = 0;
};
struct {
StringPiece rule_name;
size_t rule_name_diag_pos = 0;
size_t final_diag_pos = 0;
std::vector<DeferredPathList> deferred_path_lists;
} parse_state_;
RelativePosition pos_;
const Rule* rule_ = nullptr;
Pool* pool_ = nullptr;
vector<Node*> inputs_;
vector<Node*> outputs_;
vector<Node*> validations_;
std::vector<std::pair<HashedStr, std::string>> unevaled_bindings_;
VisitMark mark_ = VisitNone;
bool precomputed_dirtiness_ = false;
size_t id_ = 0;
bool outputs_ready_ = false;
bool deps_missing_ = false;
bool phony_from_depfile_ = false;
DepScanInfo dep_scan_info_;
DeclIndex dfs_location() const { return pos_.dfs_location(); }
const Rule& rule() const { return *rule_; }
Pool* pool() const { return pool_; }
int weight() const { return 1; }
bool outputs_ready() const { return outputs_ready_; }
// There are three types of inputs.
// 1) explicit deps, which show up as $in on the command line;
// 2) implicit deps, which the target depends on implicitly (e.g. C headers),
// and changes in them cause the target to rebuild;
// 3) order-only deps, which are needed before the target builds but which
// don't cause the target to rebuild.
// These are stored in inputs_ in that order, and we keep counts of
// #2 and #3 when we need to access the various subsets.
int explicit_deps_ = 0;
int implicit_deps_ = 0;
int order_only_deps_ = 0;
bool is_implicit(size_t index) {
return index >= inputs_.size() - order_only_deps_ - implicit_deps_ &&
!is_order_only(index);
}
bool is_order_only(size_t index) {
return index >= inputs_.size() - order_only_deps_;
}
// There are two types of outputs.
// 1) explicit outs, which show up as $out on the command line;
// 2) implicit outs, which the target generates but are not part of $out.
// These are stored in outputs_ in that order, and we keep a count of
// #2 to use when we need to access the various subsets.
int explicit_outs_ = 0;
int implicit_outs_ = 0;
bool is_implicit_out(size_t index) const {
return index >= outputs_.size() - implicit_outs_;
}
int validation_deps_ = 0;
bool is_phony() const;
bool use_console() const;
bool maybe_phonycycle_diagnostic() const;
/// Search for a binding on this edge and append its value to the output
/// string. Does not search the enclosing scope. Use other functions to
/// include ancestor scopes and rule bindings.
bool EvaluateVariableSelfOnly(std::string* out_append,
const HashedStrView& var) const;
};
struct EdgeCmp {
bool operator()(const Edge* a, const Edge* b) const {
return a->id_ < b->id_;
}
};
typedef set<Edge*, EdgeCmp> EdgeSet;
/// ImplicitDepLoader loads implicit dependencies, as referenced via the
/// "depfile" attribute in build files.
struct ImplicitDepLoader {
ImplicitDepLoader(State* state, DepsLog* deps_log,
DiskInterface* disk_interface)
: state_(state), disk_interface_(disk_interface), deps_log_(deps_log) {}
/// Load implicit dependencies for \a edge.
/// @return false on error (without filling \a err if info is just missing
// or out of date).
bool LoadDeps(Edge* edge, string* err);
DepsLog* deps_log() const {
return deps_log_;
}
private:
/// Load implicit dependencies for \a edge from a depfile attribute.
/// @return false on error (without filling \a err if info is just missing).
bool LoadDepFile(Edge* edge, const string& path, string* err);
/// Load implicit dependencies for \a edge from the DepsLog.
/// @return false on error (without filling \a err if info is just missing).
bool LoadDepsFromLog(Edge* edge, string* err);
/// Preallocate \a count spaces in the input array on \a edge, returning
/// an iterator pointing at the first new space.
vector<Node*>::iterator PreallocateSpace(Edge* edge, int count);
/// If we don't have a edge that generates this input already,
/// create one; this makes us not abort if the input is missing,
/// but instead will rebuild in that circumstance.
void CreatePhonyInEdge(Node* node);
State* state_;
DiskInterface* disk_interface_;
DepsLog* deps_log_;
};
/// DependencyScan manages the process of scanning the files in a graph
/// and updating the dirty/outputs_ready state of all the nodes and edges.
struct DependencyScan {
DependencyScan(State* state, BuildLog* build_log, DepsLog* deps_log,
DiskInterface* disk_interface, bool missing_phony_is_err)
: build_log_(build_log),
disk_interface_(disk_interface),
dep_loader_(state, deps_log, disk_interface),
missing_phony_is_err_(missing_phony_is_err) {}
/// Used for tests.
bool RecomputeDirty(Node* node, std::vector<Node*>* validation_nodes,
std::string* err) {
std::vector<Node*> nodes = {node};
return RecomputeNodesDirty(nodes, validation_nodes, err);
}
/// Update the |dirty_| state of the given nodes by transitively inspecting
/// their input edges.
/// Examine inputs, outputs, and command lines to judge whether an edge
/// needs to be re-run, and update outputs_ready_ and each outputs' |dirty_|
/// state accordingly.
/// Appends any validation nodes found to the nodes parameter.
/// Returns false on failure.
bool RecomputeNodesDirty(const std::vector<Node*>& initial_nodes,
std::vector<Node*>* validation_nodes,
std::string* err);
/// Recompute whether any output of the edge is dirty, if so sets |*dirty|.
/// Returns false on failure.
bool RecomputeOutputsDirty(Edge* edge, Node* most_recent_input,
bool* dirty, string* err);
BuildLog* build_log() const {
return build_log_;
}
void set_build_log(BuildLog* log) {
build_log_ = log;
}
DepsLog* deps_log() const {
return dep_loader_.deps_log();
}
private:
/// Find the transitive closure of edges and nodes that the given node depends
/// on. Each Node and Edge is guaranteed to appear at most once in an output
/// vector. The returned lists are not guaranteed to be a superset or a subset
/// of the nodes and edges that RecomputeNodesDirty will initialize.
void CollectPrecomputeLists(Node* node, std::vector<Node*>* nodes,
std::vector<Edge*>* edges);
bool PrecomputeNodesDirty(const std::vector<Node*>& nodes,
const std::vector<Edge*>& edges,
ThreadPool* thread_pool, std::string* err);
bool RecomputeNodeDirty(Node* node, vector<Node*>* stack,
vector<Node*>* validation_nodes, string* err);
bool VerifyDAG(Node* node, vector<Node*>* stack, string* err);
/// Recompute whether a given single output should be marked dirty.
/// Returns true if so.
bool RecomputeOutputDirty(Edge* edge, Node* most_recent_input,
uint64_t command_hash, Node* output);
BuildLog* build_log_;
DiskInterface* disk_interface_;
ImplicitDepLoader dep_loader_;
bool missing_phony_is_err_;
};
#endif // NINJA_GRAPH_H_