

























package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
)
const store = "/nix/store"
type drv struct {
Args []string `json:"args"`
Builder string `json:"builder"`
Env map[string]string `json:"env"`
Inputs struct {
Drvs map[string]any `json:"drvs"`
} `json:"inputs"`
Outputs map[string]struct {
Path string `json:"path"`
} `json:"outputs"`
}
func exists(path string) bool { _, err := os.Stat(path); return err == nil }
// storePath makes a store path absolute; Nix's JSON uses bare basenames.
func storePath(p string) string {
if strings.HasPrefix(p, "/") {
return p
}
return store + "/" + p
}
// loadDrv shells out to Nix to turn a .drv into JSON, then decodes it.
func loadDrv(path string) (error, drv) {
data, err := exec.Command("nix", "--extra-experimental-features", "nix-command",
"derivation", "show", path).Output()
if err != nil {
return err, drv{}
}
var doc struct {
Derivations map[string]drv `json:"derivations"`
}
if err := json.Unmarshal(data, &doc); err != nil {
return err, drv{}
}
for _, d := range doc.Derivations {
return nil, d // exactly one entry: the derivation we asked for
}
panic("no derivation found for " + path)
}
// realise ensures the derivation's output exists, building its inputs first,
// and returns the default output's store path.
func realise(path string) (error, string) {
err, d := loadDrv(path)
if err != nil {
return err, ""
}
out := storePath(d.Outputs["out"].Path)
if exists(out) {
return nil, out // already built (this also memoises shared dependencies)
}
for dep := range d.Inputs.Drvs {
realise(storePath(dep)) // recurse: dependencies before dependents
}
fmt.Fprintln(os.Stderr, "building", out)
tmp, err := os.MkdirTemp("", "simple-nix-")
if (err != nil) {
return err, ""
}
defer os.RemoveAll(tmp)
// The build's entire environment: a few fixed vars, the derivation's own
// attributes, and one var per output (this is where $out comes from).
// These fixed variables and their values are specified by the Nix manual:
// https://github.com/NixOS/nix/blob/f8bb823a23bf6d62f4c8feb792a77702d7a49fe1/doc/manual/source/store/building.md?plain=1#L154
env := map[string]string{
"PATH": "/path-not-set", "HOME": "/homeless-shelter",
"NIX_STORE": store, "NIX_BUILD_TOP": tmp,
"TMPDIR": tmp, "TEMPDIR": tmp, "TMP": tmp, "TEMP": tmp,
}
for k, v := range d.Env {
env[k] = v
}
for name, o := range d.Outputs {
env[name] = storePath(o.Path)
}
cmd := exec.Command(d.Builder, d.Args...)
cmd.Dir, cmd.Stdout, cmd.Stderr = tmp, os.Stderr, os.Stderr
for k, v := range env {
cmd.Env = append(cmd.Env, k+"="+v)
}
if err := cmd.Run(); err != nil {
return err, ""
}
if !exists(out) {
panic(fmt.Sprintf("builder did not produce %s", out))
}
return nil, out
}
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: simple-nix <file.drv> ...")
os.Exit(2)
}
for _, arg := range os.Args[1:] {
fmt.Println(realise(arg))
}
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。