From 1c968c1e365468d6c2b864ac02b8c482693d5f3d Mon Sep 17 00:00:00 2001 From: Ophestra Date: Mon, 14 Jul 2025 22:10:48 +0900 Subject: [PATCH] build: wrap nix build These things currently require manual testing unfortunately since writing a nix stub would take a long time. --- build.go | 51 ++++++++++++++++++++++++++++++++++++++++++++ cmd/nixbuild/main.go | 20 +++++++++++++---- format.go | 7 ++++++ 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 build.go diff --git a/build.go b/build.go new file mode 100644 index 0000000..bef9f14 --- /dev/null +++ b/build.go @@ -0,0 +1,51 @@ +package nixbuild + +import ( + "context" + "iter" + "strings" +) + +// Build builds all entries yielded by installables. +func Build(ctx context.Context, installables iter.Seq[string]) error { + c, cancel := context.WithCancel(ctx) + defer cancel() + + cmd := Nix(c, CommandBuild, + FlagKeepGoing, FlagNoLink, FlagStdin) + if Stdout != nil { + cmd.Stdout = Stdout + cmd.Args = append(cmd.Args, FlagPrintBuildLogs) + } else { + cmd.Args = append(cmd.Args, FlagQuiet) + } + if Stderr != nil { + cmd.Stderr = Stderr + cmd.Args = append(cmd.Args, FlagVerbose) + } + + stdin, err := cmd.StdinPipe() + if err != nil { + return err + } + + if err = cmd.Start(); err != nil { + return err + } + + for drv := range installables { + if strings.HasSuffix(drv, ".drv") { + // this is just what nix requires now :c + drv += "^*" + } + if _, err = stdin.Write([]byte(drv + "\n")); err != nil { + return err + } + } + + if err = stdin.Close(); err != nil { + return err + } + + return cmd.Wait() +} diff --git a/cmd/nixbuild/main.go b/cmd/nixbuild/main.go index f903f4e..b9a7eeb 100644 --- a/cmd/nixbuild/main.go +++ b/cmd/nixbuild/main.go @@ -8,6 +8,7 @@ import ( "log" "os" "os/signal" + "slices" "strings" "syscall" @@ -20,6 +21,9 @@ 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 @@ -38,7 +42,18 @@ func main() { Flag(&flagVerbose, "v", command.BoolFlag(false), "Connect nix stderr"). Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable") - c.Command("instantiated", "Evaluate an installable and output all derivations it instantiated", func(args []string) error { + c.Command("build", "Build a list of installables", 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 + }) + + c.Command("instantiated", "Evaluate an installable and output all derivations instantiated during evaluation", func(args []string) error { if len(args) != 1 { return commandHandlerError("instantiated requires exactly 1 argument") } @@ -60,9 +75,6 @@ func main() { } } - ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) - defer stop() - if v, err := nixbuild.EvalInstantiated(ctx, installable); err != nil { return commandHandlerError(fmt.Sprintf("cannot evaluate for instantiated derivations: %v", err)) } else if flagJSON { diff --git a/format.go b/format.go index c12661a..f0c5afc 100644 --- a/format.go +++ b/format.go @@ -48,6 +48,13 @@ const ( // FlagVersion show version information. FlagVersion = "--version" + // FlagKeepGoing keep going in case of failed builds, to the greatest extent possible. + // That is, if building an input of some derivation fails, Nix will still build the other inputs, + // but not the derivation itself. + // Without this option, Nix stops if any build fails (except for builds of substitutes), + // possibly killing builds in progress (in case of parallel or distributed builds). + FlagKeepGoing = "--keep-going" + // OptionEvalCache whether to use the flake evaluation cache. // Certain commands won’t have to evaluate when invoked for the second time with a particular version of a flake. // Intermediate results are not cached.