Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ auto get_phi_node(Graph&, id) -> PhiNode*; // nullptr for non-phi ID
auto add_condition_node(Graph&, ConditionNode) -> Result<NodeId>;
auto add_phi_node(Graph&, PhiNode) -> Result<NodeId>;
auto resolve_phi_node(Graph const&, phi_id) -> NodeId; // Returns active output
auto is_guard_satisfied(Graph const&, CommandNode const&) -> bool;
auto is_guard_satisfied(Graph const&, NodeId) -> bool;

// String access helpers (resolve StringId -> string_view)
auto get_name(Graph const&, id) -> std::string_view;
Expand Down Expand Up @@ -1830,7 +1830,7 @@ foo.c ─────┬─→ [cmd1: gcc -g] ─→ app@DEBUG=y ──┐

```cpp
// When building job list, evaluate guards via helper
auto guard_active = is_guard_satisfied(graph, *cmd);
auto guard_active = is_guard_satisfied(graph, cmd_id);

// is_guard_satisfied() checks ALL guards:
for (auto const& guard : cmd.guards) {
Expand Down
46 changes: 39 additions & 7 deletions include/pup/graph/dag.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,53 @@ auto add_command_node(Graph& graph, CommandNode node) -> Result<NodeId>;
[[nodiscard]]
auto add_edge(Graph& graph, NodeId from, NodeId to, LinkType type = LinkType::Normal) -> Result<void>;

/// Get a file node by ID - returns nullptr for command IDs
/// Get the type of a node (File, Generated, Ghost, Directory, Group, Command, etc.)
[[nodiscard]]
auto get_file_node(Graph const& graph, NodeId id) -> FileNode const*;
auto get_node_type(Graph const& graph, NodeId id) -> NodeType;

/// Get a command node by ID - returns nullptr for file IDs
/// Get the parent directory node of a file node
[[nodiscard]]
auto get_command_node(Graph const& graph, NodeId id) -> CommandNode const*;
auto get_parent_dir(Graph const& graph, NodeId id) -> NodeId;

/// Get the type of a node (File, Generated, Ghost, Directory, Group, Command, etc.)
/// Get the flags of a file node
[[nodiscard]]
auto get_node_type(Graph const& graph, NodeId id) -> NodeType;
auto get_node_flags(Graph const& graph, NodeId id) -> NodeFlags;

/// Get the content hash of a file node
[[nodiscard]]
auto get_content_hash(Graph const& graph, NodeId id) -> Hash256;

/// Get the input operand NodeIds of a command node
[[nodiscard]]
auto get_command_inputs(Graph const& graph, NodeId id) -> Vec<NodeId> const&;

/// Get the output operand NodeIds of a command node
[[nodiscard]]
auto get_command_outputs(Graph const& graph, NodeId id) -> Vec<NodeId> const&;

/// Get the output action of a command node (Normal or InjectImplicitDeps)
[[nodiscard]]
auto get_output_action(Graph const& graph, NodeId id) -> OutputAction;

/// Get the exported environment variables of a command node
[[nodiscard]]
auto get_exported_vars(Graph const& graph, NodeId id) -> SortedIdVec const&;

/// Get the parent command for InjectImplicitDeps commands
[[nodiscard]]
auto get_parent_command(Graph const& graph, NodeId id) -> NodeId;

/// Get the display StringId of a command node (raw interned ID, not resolved to string_view)
[[nodiscard]]
auto get_display_id(Graph const& graph, NodeId id) -> StringId;

/// Check if a command captures stdout (generated rule with Stdout output type)
[[nodiscard]]
auto is_stdout_capture(Graph const& graph, NodeId id) -> bool;

/// Check if all guards on a command are satisfied
[[nodiscard]]
auto is_guard_satisfied(Graph const& graph, CommandNode const& cmd) -> bool;
auto is_guard_satisfied(Graph const& graph, NodeId id) -> bool;

/// Find a node by parent directory and basename (tup-style lookup)
[[nodiscard]]
Expand Down
53 changes: 21 additions & 32 deletions src/cli/cmd_build.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,23 +461,21 @@ auto serialize_graph_nodes(
if (pup::node_id::is_command(id)) {
continue;
}
auto const* node = pup::graph::get_file_node(g, id);
if (!node) {
continue;
}

if (node->type == pup::NodeType::File || node->type == pup::NodeType::Generated) {
auto type = pup::graph::get_node_type(g, id);

if (type == pup::NodeType::File || type == pup::NodeType::Generated) {
auto node_path = pup::graph::get_full_path(g, id, state.path_cache);
if (node_path.empty()) {
continue;
}

auto fs_path = node_path;
if (node->type == pup::NodeType::Generated) {
if (type == pup::NodeType::Generated) {
fs_path = strip_build_root_prefix(fs_path, pup::graph::get_build_root_name(g));
}

auto file_path = pup::global_pool().get((node->type == pup::NodeType::Generated) ? pup::path::join(output_root, fs_path) : pup::path::join(source_root, node_path));
auto file_path = pup::global_pool().get((type == pup::NodeType::Generated) ? pup::path::join(output_root, fs_path) : pup::path::join(source_root, node_path));

auto content_hash = pup::Hash256 {};
auto file_size = std::uint64_t { 0 };
Expand All @@ -501,10 +499,10 @@ auto serialize_graph_nodes(
auto& pool = pup::global_pool();
auto entry = pup::index::FileEntry {
.id = id,
.parent_id = node->parent_dir,
.parent_id = pup::graph::get_parent_dir(g, id),
.src_id = 0,
.type = node->type,
.flags = node->flags,
.type = type,
.flags = pup::graph::get_node_flags(g, id),
.name = pool.intern(pup::graph::get_name(g, id)),
.path = pool.intern(node_path),
.size = file_size,
Expand All @@ -513,16 +511,16 @@ auto serialize_graph_nodes(
};
index.add_file(std::move(entry));
path_id_insert(path_to_id, pool.intern(node_path), id);
} else if (node->type == pup::NodeType::Directory || node->type == pup::NodeType::GeneratedDir) {
} else if (type == pup::NodeType::Directory || type == pup::NodeType::GeneratedDir) {
auto node_path = pup::graph::get_full_path(g, id, state.path_cache);
auto& pool = pup::global_pool();

auto entry = pup::index::FileEntry {
.id = id,
.parent_id = node->parent_dir,
.parent_id = pup::graph::get_parent_dir(g, id),
.src_id = 0,
.type = node->type,
.flags = node->flags,
.type = type,
.flags = pup::graph::get_node_flags(g, id),
.name = pool.intern(pup::graph::get_name(g, id)),
.path = pool.intern(node_path),
.size = 0,
Expand All @@ -533,21 +531,21 @@ auto serialize_graph_nodes(
if (!node_path.empty()) {
path_id_insert(path_to_id, pool.intern(node_path), id);
}
} else if (node->type == pup::NodeType::Variable
|| node->type == pup::NodeType::Group
|| node->type == pup::NodeType::Ghost) {
} else if (type == pup::NodeType::Variable
|| type == pup::NodeType::Group
|| type == pup::NodeType::Ghost) {
// These node types must be in index to maintain consecutive ID sequence
auto& pool = pup::global_pool();
auto entry = pup::index::FileEntry {
.id = id,
.parent_id = node->parent_dir,
.parent_id = pup::graph::get_parent_dir(g, id),
.src_id = 0,
.type = node->type,
.flags = node->flags,
.type = type,
.flags = pup::graph::get_node_flags(g, id),
.name = pool.intern(pup::graph::get_name(g, id)),
.path = pup::StringId::Empty,
.size = 0,
.content_hash = (node->type == pup::NodeType::Variable) ? node->content_hash : pup::Hash256 {},
.content_hash = (type == pup::NodeType::Variable) ? pup::graph::get_content_hash(g, id) : pup::Hash256 {},
};
index.add_file(std::move(entry));
}
Expand All @@ -569,13 +567,9 @@ auto serialize_command_nodes(
if (!pup::node_id::is_command(id)) {
continue;
}
auto const* cmd = pup::graph::get_command_node(g, id);
if (!cmd) {
continue;
}

auto inputs = cmd->inputs;
auto outputs = cmd->outputs;
auto inputs = pup::graph::get_command_inputs(g, id);
auto outputs = pup::graph::get_command_outputs(g, id);
auto& pool = pup::global_pool();

auto source_dir_sv = pup::graph::get_source_dir(g, id);
Expand Down Expand Up @@ -873,11 +867,6 @@ auto detect_new_commands(
if (!pup::node_id::is_command(id)) {
continue;
}
auto const* node = pup::graph::get_command_node(g, id);
if (!node) {
continue;
}

auto cmd_str_id = pup::graph::expand_instruction(g, id, state.path_cache);
auto found = idx.find_command_by_command(pup::global_pool().get(cmd_str_id));
if (!found) {
Expand Down
10 changes: 6 additions & 4 deletions src/cli/cmd_configure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,16 @@ auto configure_single_variant(
// Filter config commands by scope if specified
auto config_commands = pup::NodeIdMap32 {};
for (auto const& cfg : configs) {
auto const* node = pup::graph::get_command_node(ctx.graph().graph, cfg.cmd_id);
auto source_dir_sv = node ? pup::graph::get_source_dir(ctx.graph().graph, cfg.cmd_id) : std::string_view {};
if (!scopes.empty() && node && !pup::is_path_in_any_scope(source_dir_sv, scopes)) {
auto source_dir_sv = pup::graph::get_source_dir(ctx.graph().graph, cfg.cmd_id);
if (!scopes.empty() && !pup::is_path_in_any_scope(source_dir_sv, scopes)) {
continue;
}
config_commands.set(cfg.cmd_id, 1);
if (opts.verbose) {
auto display_sv = node ? pup::graph::get_display_str(ctx.graph().graph, cfg.cmd_id) : std::string_view { "<unknown>" };
auto display_sv = pup::graph::get_display_str(ctx.graph().graph, cfg.cmd_id);
if (display_sv.empty()) {
display_sv = "<unknown>";
}
auto output_path_sv = pool.get(cfg.output_path);
printf("[%.*s] Config rule: %.*s -> %.*s\n", static_cast<int>(variant_name.size()), variant_name.data(), static_cast<int>(display_sv.size()), display_sv.data(), static_cast<int>(output_path_sv.size()), output_path_sv.data());
}
Expand Down
10 changes: 4 additions & 6 deletions src/cli/cmd_parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,10 @@ auto parse_single_variant(Options const& opts, std::string_view variant_name) ->
printf("[%.*s] Commands:\n", static_cast<int>(variant_name.size()), variant_name.data());
auto cache = pup::graph::PathCache {};
for (auto id : commands) {
if (pup::graph::get_command_node(ctx.graph().graph, id)) {
auto display_sv = pup::graph::get_display_str(ctx.graph().graph, id);
auto cmd_str_id = pup::graph::expand_instruction(ctx.graph().graph, id, cache, pool.get(ctx.layout().source_root), pool.get(ctx.layout().config_root));
auto label = display_sv.empty() ? pool.get(cmd_str_id) : display_sv;
printf("[%.*s] %.*s\n", static_cast<int>(variant_name.size()), variant_name.data(), static_cast<int>(label.size()), label.data());
}
auto display_sv = pup::graph::get_display_str(ctx.graph().graph, id);
auto cmd_str_id = pup::graph::expand_instruction(ctx.graph().graph, id, cache, pool.get(ctx.layout().source_root), pool.get(ctx.layout().config_root));
auto label = display_sv.empty() ? pool.get(cmd_str_id) : display_sv;
printf("[%.*s] %.*s\n", static_cast<int>(variant_name.size()), variant_name.data(), static_cast<int>(label.size()), label.data());
}
}

Expand Down
26 changes: 5 additions & 21 deletions src/cli/cmd_show.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,7 @@ auto cmd_export_script(Options const& opts, std::string_view variant_name) -> in
if (!node_id::is_command(id)) {
continue;
}
auto const* node = graph::get_command_node(ctx.graph().graph, id);
if (!node) {
continue;
}

if (node->output_action == graph::OutputAction::InjectImplicitDeps) {
if (graph::get_output_action(ctx.graph().graph, id) == graph::OutputAction::InjectImplicitDeps) {
continue;
}

Expand Down Expand Up @@ -248,12 +243,10 @@ auto cmd_export_graph(Options const& opts, std::string_view variant_name) -> int
if (opts.verbose) {
printf("[%.*s] Commands:\n", static_cast<int>(variant_name.size()), variant_name.data());
for (auto id : commands) {
if (graph::get_command_node(ctx.graph().graph, id)) {
auto display_sv = graph::get_display_str(ctx.graph().graph, id);
auto cmd_str_id = graph::expand_instruction(ctx.graph().graph, id);
auto display = display_sv.empty() ? global_pool().get(cmd_str_id) : display_sv;
printf("[%.*s] %.*s\n", static_cast<int>(variant_name.size()), variant_name.data(), static_cast<int>(display.size()), display.data());
}
auto display_sv = graph::get_display_str(ctx.graph().graph, id);
auto cmd_str_id = graph::expand_instruction(ctx.graph().graph, id);
auto display = display_sv.empty() ? global_pool().get(cmd_str_id) : display_sv;
printf("[%.*s] %.*s\n", static_cast<int>(variant_name.size()), variant_name.data(), static_cast<int>(display.size()), display.data());
}
}
return EXIT_SUCCESS;
Expand All @@ -268,10 +261,6 @@ auto cmd_export_graph(Options const& opts, std::string_view variant_name) -> int

auto get_label = [&]() -> std::string_view {
if (node_id::is_command(id)) {
auto const* cmd = graph::get_command_node(ctx.graph().graph, id);
if (!cmd) {
return {};
}
auto display_sv = graph::get_display_str(ctx.graph().graph, id);
auto cmd_str_id = graph::expand_instruction(ctx.graph().graph, id);
return display_sv.empty() ? pool.get(cmd_str_id) : display_sv;
Expand Down Expand Up @@ -348,11 +337,6 @@ auto cmd_export_compdb(Options const& opts, std::string_view variant_name) -> in
auto first = true;

for (auto id : commands) {
auto const* node = graph::get_command_node(ctx.graph().graph, id);
if (!node) {
continue;
}

auto source_file = std::string_view {};
for (auto input_id : graph::get_inputs(ctx.graph().graph, id)) {
auto input_path_id = graph::get_full_path(ctx.graph().graph, input_id);
Expand Down
5 changes: 0 additions & 5 deletions src/cli/config_commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ auto find_config_commands(
if (!node_id::is_command(id)) {
continue;
}
auto const* node = graph::get_command_node(g, id);
if (!node) {
continue;
}

for (auto output_id : graph::get_outputs(g, id)) {
auto path = graph::get_full_path(g, output_id, state.path_cache);
if (is_config_output(path)) {
Expand Down
27 changes: 8 additions & 19 deletions src/exec/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,11 +609,6 @@ auto Scheduler::build_job_list(
if (!node_id::is_command(id)) {
continue;
}
auto const* node = graph::get_command_node(g, id);
if (!node) {
continue;
}

// Compute working directory: source_dir for subdirectory Tupfiles.
// Commands run from the Tupfile's SOURCE directory so that relative paths
// and TUP_VARIANT_OUTPUTDIR work correctly. Output paths are already
Expand All @@ -627,29 +622,23 @@ auto Scheduler::build_job_list(
}

// Check if this is a generated rule that captures stdout
auto capture_stdout = false;
auto inject_implicit = false;
auto parent_cmd = INVALID_NODE_ID;
if (node->generated_output && node->generated_output->type == graph::GeneratedOutput::Type::Stdout) {
capture_stdout = true;
if (node->output_action == graph::OutputAction::InjectImplicitDeps) {
inject_implicit = true;
parent_cmd = node->parent_command;
}
}
auto capture_stdout = graph::is_stdout_capture(g, id);
auto inject_implicit = capture_stdout && graph::get_output_action(g, id) == graph::OutputAction::InjectImplicitDeps;
auto parent_cmd = inject_implicit ? graph::get_parent_command(g, id) : INVALID_NODE_ID;

// Expand command from instruction pattern + operands
auto cmd_id = graph::expand_instruction(g, id, cache, source_root_sv, config_root_sv);
auto display_id = node->display;
auto display_id = graph::get_display_id(g, id);

auto const& exported_raw = graph::get_exported_vars(g, id);
auto exported_ids = Vec<StringId> {};
exported_ids.reserve(node->exported_vars.size());
for (auto raw_id : node->exported_vars) {
exported_ids.reserve(exported_raw.size());
for (auto raw_id : exported_raw) {
exported_ids.push_back(make_string_id(raw_id));
}

// Evaluate guards - command only executes if ALL guards are satisfied
auto guard_active = graph::is_guard_satisfied(g, *node);
auto guard_active = graph::is_guard_satisfied(g, id);

auto job = BuildJob {
.id = id,
Expand Down
Loading
Loading