1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
package eval
import (
"crypto/sha256"
)
// Store abstracts the Nix store operations needed during evaluation.
// The default (in-memory) implementation computes paths from hashes alone
// and never touches the real /nix/store. A database-backed implementation
// can additionally persist derivations for post-hoc inspection.
type Store interface {
// AllHashTraces returns the full trace map collected during evaluation.
// Returns nil if tracing was not enabled.
AllHashTraces() map[string]*HashTrace
// AddPathToStore NAR-hashes the file or directory at fsKey in fsys and
// returns the computed source store path (e.g. /nix/store/xxx-name).
// Mirrors C++ Store::copyPathToStore.
AddPathToStore(name string, fsys NixFS, fsKey, nixPath string) (string, error)
// AddTextToStore computes the text store path for the given file content
// and reference store paths. Mirrors C++ Store::addTextToStore.
AddTextToStore(name, content string, refs []string) (string, error)
// AddFixedOutputPath computes the store path for a fixed-output derivation.
// algo is e.g. "sha256", method is "flat" or "recursive", hash is raw bytes.
// Mirrors C++ Store::makeFixedOutputPath.
AddFixedOutputPath(name, algo, method string, hash []byte) (string, error)
// RecordDerivation is called once per derivation after its drv path is
// computed. Implementations may persist it for later inspection.
// drv is the fully-populated Derivation; drvPath is its store path.
RecordDerivation(drvPath string, drv *Derivation)
// GetDerivation returns the Derivation previously recorded for drvPath,
// or nil if not known. Used by hashDerivationModulo to recurse into inputs.
GetDerivation(drvPath string) *Derivation
// ATermFor returns the ATerm for the given drv path, or ("", false) if
// not recorded. Used by gonix-eval --print-drv.
ATermFor(drvPath string) (string, bool)
// SetTracing enables hash-trace collection. When enabled,
// ComputeInputAddressedOutputsTraced is used and traces are stored in
// the in-memory trace map for later flushing (e.g. to the DB).
SetTracing(enabled bool)
// IsTracing reports whether hash-trace collection is currently enabled.
IsTracing() bool
// StoreHashTrace stores a HashTrace under drvPath. Called by the evaluator
// after ComputeInputAddressedOutputsTraced.
StoreHashTrace(drvPath string, t *HashTrace)
// moduloCache returns the session-level memo map used by
// hashDerivationModulo.
moduloCache() map[string]map[string][]byte
// traceCache returns the session-level trace map, or nil if tracing is off.
traceCache() map[string]*HashTrace
}
// Fetcher is an optional interface that Store implementations may provide
// to support builtins.fetchTarball, builtins.fetchurl, and similar fetcher
// primops. The evaluator computes the expected fixed-output store path from
// the content hash and delegates the actual fetch/substitution to the Fetcher.
//
// Implementations should:
// 1. Check whether storePath already exists on disk (fast path).
// 2. If not, fetch/build the content and place it at storePath.
//
// The MemStore does not implement Fetcher — fetching requires a real store.
// DaemonStore implements Fetcher by synthesizing a builtin:fetchurl derivation
// and asking the daemon to build it via BuildDerivation (op 36).
type Fetcher interface {
// FetchTarball ensures storePath exists by fetching url and unpacking the
// tarball. hash is the expected raw SHA-256 bytes (recursive NAR hash).
// name is the store-path name component (e.g. "source").
FetchTarball(storePath, url, name string, hash []byte) error
// FetchURL ensures storePath exists by fetching url as a flat file (no
// unpacking). hash is the expected raw SHA-256 bytes (flat file hash).
// name is the store-path name component.
FetchURL(storePath, url, name string, hash []byte) error
}
// MemStore is the default in-memory Store implementation. It computes all
// store paths from hashes (never touches /nix/store) and records derivations
// for hashDerivationModulo and --print-drv.
type MemStore struct {
aterms map[string]string // drvPath → ATerm string
drvs map[string]*Derivation // drvPath → Derivation (for modulo recursion)
modulo map[string]map[string][]byte // drvPath → outputName → modulo hash (memoised)
tracing bool
traces map[string]*HashTrace // drvPath → HashTrace (only when tracing)
// RecordDerivationHook, if non-nil, is called after every RecordDerivation.
// Use this to side-channel derivation data to an external store (e.g. SQLite).
RecordDerivationHook func(drvPath string, drv *Derivation)
}
// NewMemStore creates a new MemStore.
func NewMemStore() *MemStore {
return &MemStore{
aterms: make(map[string]string),
drvs: make(map[string]*Derivation),
modulo: make(map[string]map[string][]byte),
}
}
func (m *MemStore) AddPathToStore(name string, fsys NixFS, fsKey, nixPath string) (string, error) {
narHash, err := NARHashPath(fsys, fsKey, nixPath, nil)
if err != nil {
real := fsys.RealPath(fsKey, false)
return "", evalErrorf("hashing path '%s': %v", real, err)
}
return MakeSourcePath(name, narHash), nil
}
func (m *MemStore) AddTextToStore(name, content string, refs []string) (string, error) {
return addTextStorePath(name, content, refs), nil
}
func (m *MemStore) AddFixedOutputPath(name, algo, method string, hash []byte) (string, error) {
return MakeFixedOutputPathGeneric(name, algo, method, hash), nil
}
func (m *MemStore) RecordDerivation(drvPath string, drv *Derivation) {
m.aterms[drvPath] = drv.Unparse(false)
m.drvs[drvPath] = drv
if m.RecordDerivationHook != nil {
m.RecordDerivationHook(drvPath, drv)
}
}
func (m *MemStore) GetDerivation(drvPath string) *Derivation {
return m.drvs[drvPath]
}
func (m *MemStore) ATermFor(drvPath string) (string, bool) {
a, ok := m.aterms[drvPath]
return a, ok
}
func (m *MemStore) SetTracing(enabled bool) {
m.tracing = enabled
if enabled && m.traces == nil {
m.traces = make(map[string]*HashTrace)
}
}
func (m *MemStore) IsTracing() bool { return m.tracing }
func (m *MemStore) StoreHashTrace(drvPath string, t *HashTrace) {
if m.traces != nil {
m.traces[drvPath] = t
}
}
func (m *MemStore) moduloCache() map[string]map[string][]byte {
return m.modulo
}
func (m *MemStore) traceCache() map[string]*HashTrace {
return m.traces
}
// AllHashTraces returns the full trace map (drvPath → HashTrace).
// Returns nil if tracing was not enabled. Used by DBStore.FlushTraces.
func (m *MemStore) AllHashTraces() map[string]*HashTrace {
return m.traces
}
// --- package-level helpers ---
func addTextStorePath(name, content string, refs []string) string {
h := sha256.Sum256([]byte(content))
return makeTextPath(name, h[:], refs)
}
|