instantiated: embed decoder in evaluator
This makes it easier to directly parse nix output via the decoder interface.
This commit is contained in:
parent
c1cdd0b559
commit
d470e770ff
@ -6,9 +6,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"iter"
|
"iter"
|
||||||
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -61,7 +64,7 @@ type InstantiatedDecoder struct {
|
|||||||
scanner *bufio.Scanner
|
scanner *bufio.Scanner
|
||||||
}
|
}
|
||||||
|
|
||||||
// Err returns the first error encountered by the InstantiatedDecoder.
|
// Err returns the first error encountered by the [InstantiatedDecoder].
|
||||||
func (d *InstantiatedDecoder) Err() error { return errors.Join(d.err, d.scanner.Err()) }
|
func (d *InstantiatedDecoder) Err() error { return errors.Join(d.err, d.scanner.Err()) }
|
||||||
|
|
||||||
// NewInstantiatedDecoder returns a [InstantiatedDecoder] struct for collecting derivations from r.
|
// NewInstantiatedDecoder returns a [InstantiatedDecoder] struct for collecting derivations from r.
|
||||||
@ -123,34 +126,78 @@ func (d *InstantiatedDecoder) Decode() ([]string, error) {
|
|||||||
return slices.Compact(instantiated), d.Err()
|
return slices.Compact(instantiated), d.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvalInstantiated evaluates the installable and returns all derivations instantiated during the evaluation.
|
// InstantiatedEvaluator evaluates a nix installable and collects its instantiated derivations via [InstantiatedDecoder].
|
||||||
// This interprets verbose output of `nix build` and is certainly not the correct way to do it, but so far it works
|
// This interprets verbose output of `nix build` and is certainly not the correct way to do it, but so far its results
|
||||||
// significantly better than `nix-store -qR` and there does not appear to be a better way.
|
// are significantly more complete than `nix-store -qR` and there does not appear to be a better way.
|
||||||
func EvalInstantiated(ctx context.Context, installable string) ([]string, error) {
|
type InstantiatedEvaluator struct {
|
||||||
|
// underlying nix program
|
||||||
|
cmd *exec.Cmd
|
||||||
|
// kills the nix command
|
||||||
|
cancel context.CancelFunc
|
||||||
|
// populated by Close
|
||||||
|
waitErr error
|
||||||
|
|
||||||
|
*InstantiatedDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the first error encountered by the [InstantiatedEvaluator].
|
||||||
|
func (e *InstantiatedEvaluator) Err() error {
|
||||||
|
return errors.Join(e.waitErr, e.InstantiatedDecoder.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInstantiatedEvaluator initialises an [InstantiatedEvaluator] struct and its underlying nix process.
|
||||||
|
func NewInstantiatedEvaluator(ctx context.Context, installable string) (*InstantiatedEvaluator, error) {
|
||||||
c, cancel := context.WithCancel(ctx)
|
c, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
e := &InstantiatedEvaluator{
|
||||||
|
cmd: Nix(c, "build", installable,
|
||||||
cmd := Nix(c, "build", installable,
|
// 'instantiated' messages are only emitted when actually evaluating something
|
||||||
// 'instantiated' messages are only emitted when actually evaluating something
|
"--option", "eval-cache", "false",
|
||||||
"--option", "eval-cache", "false",
|
// do not actually build anything
|
||||||
// do not actually build anything
|
"--dry-run",
|
||||||
"--dry-run",
|
// increase verbosity so nix outputs 'instantiated' messages
|
||||||
// increase verbosity so nix outputs 'instantiated' messages
|
"-Lvvv",
|
||||||
"-Lvvv",
|
),
|
||||||
)
|
cancel: cancel,
|
||||||
|
|
||||||
cmd.Stdout = Stdout
|
|
||||||
stderr, err := cmd.StderrPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
e.cmd.Stdout = Stdout
|
||||||
|
|
||||||
|
// verbose output ends up on stderr in the current nix implementation
|
||||||
|
if stderr, err := e.cmd.StderrPipe(); err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
e.InstantiatedDecoder = NewInstantiatedDecoder(stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.cmd.Start(); err != nil {
|
||||||
|
cancel()
|
||||||
// have finalizer take care of the pipe
|
// have finalizer take care of the pipe
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiated, decodeErr := NewInstantiatedDecoder(stderr).Decode()
|
runtime.SetFinalizer(e, (*InstantiatedEvaluator).Close)
|
||||||
waitErr := cmd.Wait()
|
return e, nil
|
||||||
return instantiated, errors.Join(decodeErr, waitErr)
|
}
|
||||||
|
|
||||||
|
// 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].
|
||||||
|
func EvalInstantiated(ctx context.Context, installable string) ([]string, error) {
|
||||||
|
evaluator, err := NewInstantiatedEvaluator(ctx, installable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
instantiated, _ := evaluator.Decode() // error joined by Close
|
||||||
|
return instantiated, evaluator.Close()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user