treewide: use internal pipe for nix command

This essentially does the same thing underneath the hood but the API is less painful to use, and it makes more sense in this use case.
This commit is contained in:
Ophestra 2025-07-15 01:14:27 +09:00
parent b79eeea0db
commit 61c6b5d78e
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
2 changed files with 18 additions and 33 deletions

View File

@ -2,6 +2,7 @@ package nixbuild
import ( import (
"context" "context"
"io"
"iter" "iter"
"strings" "strings"
) )
@ -24,12 +25,10 @@ func Build(ctx context.Context, installables iter.Seq[string]) error {
cmd.Args = append(cmd.Args, FlagVerbose) cmd.Args = append(cmd.Args, FlagVerbose)
} }
stdin, err := cmd.StdinPipe() ir, iw := io.Pipe()
if err != nil { cmd.Stdin = ir
return err
}
if err = cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return err return err
} }
@ -38,12 +37,12 @@ func Build(ctx context.Context, installables iter.Seq[string]) error {
// this is just what nix requires now :c // this is just what nix requires now :c
drv += "^*" drv += "^*"
} }
if _, err = stdin.Write([]byte(drv + "\n")); err != nil { if _, err := iw.Write([]byte(drv + "\n")); err != nil {
return err return err
} }
} }
if err = stdin.Close(); err != nil { if err := iw.Close(); err != nil {
return err return err
} }

View File

@ -8,10 +8,9 @@ import (
"iter" "iter"
"os/exec" "os/exec"
"path" "path"
"runtime"
"slices" "slices"
"strings" "strings"
"syscall" "sync"
) )
const ( const (
@ -132,16 +131,18 @@ func (d *InstantiatedDecoder) Decode() ([]string, error) {
type InstantiatedEvaluator struct { type InstantiatedEvaluator struct {
// underlying nix program // underlying nix program
cmd *exec.Cmd cmd *exec.Cmd
// kills the nix command
cancel context.CancelFunc
// populated by Close // populated by Close
waitErr error waitErr error
// synchronises access to waitErr
waitMu sync.RWMutex
*InstantiatedDecoder *InstantiatedDecoder
} }
// Err returns the first error encountered by the [InstantiatedEvaluator]. // Err blocks until process exit and returns the first error encountered by the [InstantiatedEvaluator].
func (e *InstantiatedEvaluator) Err() error { func (e *InstantiatedEvaluator) Err() error {
e.waitMu.RLock()
defer e.waitMu.RUnlock()
return errors.Join(e.waitErr, e.InstantiatedDecoder.Err()) return errors.Join(e.waitErr, e.InstantiatedDecoder.Err())
} }
@ -157,41 +158,26 @@ func NewInstantiatedEvaluator(ctx context.Context, installable string) (*Instant
// increase verbosity so nix outputs 'instantiated' messages // increase verbosity so nix outputs 'instantiated' messages
FlagPrintBuildLogs, FlagDebug, FlagPrintBuildLogs, FlagDebug,
), ),
cancel: cancel,
} }
e.cmd.Stdout = Stdout e.cmd.Stdout = Stdout
// verbose output ends up on stderr in the current nix implementation // verbose output ends up on stderr in the current nix implementation
if stderr, err := e.cmd.StderrPipe(); err != nil { er, ew := io.Pipe()
cancel() e.cmd.Stderr = ew
return nil, err e.InstantiatedDecoder = NewInstantiatedDecoder(er)
} else {
e.InstantiatedDecoder = NewInstantiatedDecoder(stderr)
}
if err := e.cmd.Start(); err != nil { if err := e.cmd.Start(); err != nil {
cancel() cancel()
// have finalizer take care of the pipe // have finalizer take care of the pipe
return nil, err return nil, err
} }
e.waitMu.Lock()
go func() { e.waitErr = e.cmd.Wait(); cancel(); _ = ew.Close(); e.waitMu.Unlock() }()
runtime.SetFinalizer(e, (*InstantiatedEvaluator).Close)
return e, nil return e, nil
} }
// Close releases system resources held by [InstantiatedEvaluator].
func (e *InstantiatedEvaluator) Close() error {
if e.cmd.ProcessState != nil {
return syscall.EBADE
}
e.cancel()
e.waitErr = e.cmd.Wait()
runtime.SetFinalizer(e, nil)
return e.Err()
}
// EvalInstantiated calls the underlying [InstantiatedDecoder.Decode] of a new [InstantiatedEvaluator]. // EvalInstantiated calls the underlying [InstantiatedDecoder.Decode] of a new [InstantiatedEvaluator].
func EvalInstantiated(ctx context.Context, installable string) ([]string, error) { func EvalInstantiated(ctx context.Context, installable string) ([]string, error) {
evaluator, err := NewInstantiatedEvaluator(ctx, installable) evaluator, err := NewInstantiatedEvaluator(ctx, installable)
@ -199,5 +185,5 @@ func EvalInstantiated(ctx context.Context, installable string) ([]string, error)
return nil, err return nil, err
} }
instantiated, _ := evaluator.Decode() // error joined by Close instantiated, _ := evaluator.Decode() // error joined by Close
return instantiated, evaluator.Close() return instantiated, evaluator.Err()
} }