From 40beb50e8f20bf1814f497f847d1b242906e1e98 Mon Sep 17 00:00:00 2001 From: Bernardo Meurer Costa Date: Tue, 28 Oct 2025 02:49:16 +0000 Subject: [PATCH] refactor(libstore): integrate DependencyGraph with store path scanning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds buildStorePathGraphFromScan() to bridge the generic DependencyGraph infrastructure with Nix's store path reference scanning. This enables building annotated dependency graphs that track which files create which dependencies. ## New API: buildStorePathGraphFromScan() **Purpose**: Convert file-level scan results into a StorePath-level graph **How it works**: 1. Calls scanForReferencesDeep() to find which files contain references 2. For each file that references another store path: - Adds edge: rootStorePath → referencedStorePath - Attaches FileListEdgeProperty with the file path 3. Returns DependencyGraph **Key benefit**: The graph carries file-level provenance. When you see an edge A→B, you can query which files in A reference B. Essential for: - why-depends output showing exact file locations - Future: detailed cycle error messages ## API Changes **scanForReferencesDeep() callback signature**: ```cpp // Before: std::function // After: std::function ``` Pass by const-ref to avoid copying large result structures. ## Usage Example ```cpp auto graph = buildStorePathGraphFromScan( *accessor, CanonPath("/nix/store/abc-foo"), storePathAbc, candidateRefs ); // Query file annotations auto edgeProp = graph.getEdgeProperty(storePathAbc, storePathDef); // edgeProp->files contains files that created this edge ``` ## Implementation Details - Uses DependencyGraph's edge properties feature - FileListEdgeProperty defined in dependency-graph.hh - Merges duplicate edges (multiple files → same path) - Debug logging for discovered edges - Zero overhead if file-level detail not needed --- .../include/nix/store/path-references.hh | 25 ++++++++++++++- src/libstore/path-references.cc | 31 +++++++++++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/libstore/include/nix/store/path-references.hh b/src/libstore/include/nix/store/path-references.hh index 6aa506da4..cfaa76f3b 100644 --- a/src/libstore/include/nix/store/path-references.hh +++ b/src/libstore/include/nix/store/path-references.hh @@ -3,10 +3,12 @@ #include "nix/store/references.hh" #include "nix/store/path.hh" +#include "nix/store/dependency-graph.hh" #include "nix/util/source-accessor.hh" #include #include +#include namespace nix { @@ -59,7 +61,7 @@ void scanForReferencesDeep( SourceAccessor & accessor, const CanonPath & rootPath, const StorePathSet & refs, - std::function callback); + std::function callback); /** * Scan a store path tree and return which references appear in which files. @@ -78,4 +80,25 @@ void scanForReferencesDeep( std::map scanForReferencesDeep(SourceAccessor & accessor, const CanonPath & rootPath, const StorePathSet & refs); +/** + * Build a StorePath-level dependency graph from file scanning. + * + * This scans the given path for references and builds a graph where: + * - Nodes are StorePaths + * - Edges represent dependencies between StorePaths + * - Edge properties store the files that created each dependency + * + * This unified approach allows both cycle detection and why-depends to share + * the same graph-building logic while maintaining file-level information for + * detailed error messages embedded directly in the graph. + * + * @param accessor Source accessor to read the tree + * @param rootPath Root path to scan + * @param rootStorePath The StorePath that rootPath belongs to + * @param refs Set of store paths to search for + * @return StorePathGraphWithFiles where edge properties contain file lists + */ +DependencyGraph buildStorePathGraphFromScan( + SourceAccessor & accessor, const CanonPath & rootPath, const StorePath & rootStorePath, const StorePathSet & refs); + } // namespace nix diff --git a/src/libstore/path-references.cc b/src/libstore/path-references.cc index 3d783bbe4..60cdcfb80 100644 --- a/src/libstore/path-references.cc +++ b/src/libstore/path-references.cc @@ -1,4 +1,5 @@ #include "nix/store/path-references.hh" +#include "nix/store/dependency-graph.hh" #include "nix/util/hash.hh" #include "nix/util/archive.hh" #include "nix/util/source-accessor.hh" @@ -62,7 +63,7 @@ void scanForReferencesDeep( SourceAccessor & accessor, const CanonPath & rootPath, const StorePathSet & refs, - std::function callback) + std::function callback) { // Recursive tree walker auto walk = [&](this auto & self, const CanonPath & path) -> void { @@ -137,11 +138,35 @@ scanForReferencesDeep(SourceAccessor & accessor, const CanonPath & rootPath, con { std::map results; - scanForReferencesDeep(accessor, rootPath, refs, [&](FileRefScanResult result) { - results[std::move(result.filePath)] = std::move(result.foundRefs); + scanForReferencesDeep(accessor, rootPath, refs, [&](const FileRefScanResult & result) { + results[result.filePath] = result.foundRefs; }); return results; } +DependencyGraph buildStorePathGraphFromScan( + SourceAccessor & accessor, const CanonPath & rootPath, const StorePath & rootStorePath, const StorePathSet & refs) +{ + DependencyGraph graph; + + scanForReferencesDeep(accessor, rootPath, refs, [&](const FileRefScanResult & result) { + // All files in this scan belong to rootStorePath + for (const auto & foundRef : result.foundRefs) { + // Add StorePath -> StorePath edge with file metadata + FileListEdgeProperty edgeProp; + edgeProp.files.push_back(result.filePath); + graph.addEdge(rootStorePath, foundRef, std::move(edgeProp)); + + debug( + "buildStorePathGraphFromScan: %s (in %s) → %s", + rootStorePath.to_string(), + result.filePath.abs(), + foundRef.to_string()); + } + }); + + return graph; +} + } // namespace nix