Ophestra 003ad4e04e
cmd/nixbuild: implement dependency resolver
This replaces the bash implementation of this hack.
2025-07-19 00:21:57 +09:00

143 lines
4.2 KiB
Go

package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"os/signal"
"slices"
"syscall"
"git.gensokyo.uk/yonah/nixbuild"
"hakurei.app/command"
)
type commandHandlerError string
func (c commandHandlerError) Error() string { return string(c) }
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
var (
flagNixOS bool
flagVerbose bool
flagJSON bool
)
c := command.New(os.Stderr, log.Printf, "nixbuild", func(args []string) error {
log.SetFlags(0)
log.SetPrefix("nixbuild: ")
nixbuild.Stdout = os.Stdout
if flagVerbose {
nixbuild.Stderr = os.Stderr
}
return nil
}).
Flag(&flagNixOS, "nixos", command.BoolFlag(false), "Interpret input as NixOS flake installable").
Flag(&flagVerbose, "v", command.BoolFlag(false), "Connect nix stderr").
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable")
c.Command("show", command.UsageInternal, func(args []string) error {
if len(args) < 1 {
return commandHandlerError("show requires at least 1 argument")
}
if drv, err := nixbuild.DerivationShow(ctx, slices.Values(args)); err != nil {
return commandHandlerError(fmt.Sprintf("cannot show: %v", err))
} else {
log.Printf("got %d derivations:\n%#v", len(drv), drv)
return nil
}
})
c.Command("build", command.UsageInternal, func(args []string) error {
if len(args) < 1 {
return commandHandlerError("build requires at least 1 argument")
}
if err := nixbuild.Build(ctx, slices.Values(args)); err != nil {
return commandHandlerError(fmt.Sprintf("cannot build: %v", err))
}
return nil
})
var (
resolveFlagOut string
)
c.Command("resolve", "Resolve possible build-time dependencies", func(args []string) error {
var installable string
if err := formatInstallable("resolve", &installable, flagNixOS, args); err != nil {
return err
}
log.Printf("evaluating %s", installable)
var installables []string
if v, err := nixbuild.EvalInstantiated(ctx, installable); err != nil {
return commandHandlerError(fmt.Sprintf("cannot evaluate for instantiated derivations: %v", err))
} else {
installables = v
}
log.Println("building instantiated derivations")
if err := nixbuild.Build(ctx, slices.Values(installables)); err != nil {
return commandHandlerError(fmt.Sprintf("cannot build: %v", err))
}
var collective []string
log.Println("collecting store paths")
if derivations, err := nixbuild.DerivationShow(ctx, slices.Values(installables)); err != nil {
return commandHandlerError(fmt.Sprintf("cannot show: %v", err))
} else {
collective = nixbuild.CollectFromDerivations(derivations)
}
f, err := os.Create(resolveFlagOut)
if err != nil {
return commandHandlerError(fmt.Sprintf("cannot create store paths file: %v", err))
}
if flagJSON {
if err = json.NewEncoder(f).Encode(collective); err != nil {
return commandHandlerError(fmt.Sprintf("cannot serialise JSON: %v", err))
}
} else {
if _, err = nixbuild.WriteStdin(f, slices.Values(collective)); err != nil {
return commandHandlerError(fmt.Sprintf("cannot write store path list: %v", err))
}
}
return nil
}).
Flag(&resolveFlagOut, "o", command.StringFlag("store-paths"), "Path to write collected store paths")
c.Command("instantiated", "Evaluate an installable and output all derivations instantiated during evaluation", func(args []string) error {
var installable string
if err := formatInstallable("instantiated", &installable, flagNixOS, args); err != nil {
return err
}
if v, err := nixbuild.EvalInstantiated(ctx, installable); err != nil {
return commandHandlerError(fmt.Sprintf("cannot evaluate for instantiated derivations: %v", err))
} else if flagJSON {
if err = json.NewEncoder(os.Stdout).Encode(v); err != nil {
return commandHandlerError(fmt.Sprintf("cannot serialise JSON: %v", err))
}
return nil
} else {
_, _ = nixbuild.WriteStdin(os.Stdout, slices.Values(v))
return nil
}
})
c.MustParse(os.Args[1:], func(err error) {
var ce commandHandlerError
if errors.As(err, &ce) {
log.Fatal(ce.Error())
return
}
log.Fatalf("cannot parse command: %v", err)
})
}