惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
WordPress大学
WordPress大学
博客园_首页
罗磊的独立博客
D
Docker
N
Netflix TechBlog - Medium
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
D
DataBreaches.Net
I
InfoQ
L
LangChain Blog
GbyAI
GbyAI
V
V2EX
博客园 - 聂微东
P
Proofpoint News Feed
博客园 - 【当耐特】
腾讯CDC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
量子位
Martin Fowler
Martin Fowler
有赞技术团队
有赞技术团队
U
Unit 42
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏

Lobsters

CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data
nix-build in under 100 lines
fzakaria.com · 2026-06-22 · via Lobsters
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))
	}
}