package nixbuild import ( "context" "encoding/json" "errors" "io" "iter" ) type ( // DerivationMap is the output of `nix derivation show`. DerivationMap map[string]*Derivation // Derivation is a description of a derivation. Derivation struct { // Args are arguments passed to Builder. Args []string `json:"args"` // Builder is the store path of the program that builds the [Derivation]. Builder string `json:"builder"` // System is the value of pkgs.system during evaluation. System string `json:"system"` // Name is the user-facing name of a derivation, as seen in the nix store path suffix. Name string `json:"name"` Environment json.RawMessage `json:"env"` InputDerivations InputDerivationsMap `json:"inputDrvs"` InputSources []string `json:"inputSrcs"` Outputs OutputsMap `json:"outputs"` } // OutputsMap is an output name to [Output] map. OutputsMap map[string]Output // Output is an output of a [Derivation]. Output struct { Path string `json:"path"` } // InputDerivationsMap is a store path to metadata map. InputDerivationsMap map[string]InputDerivation // InputDerivation contains input derivation metadata. InputDerivation struct { DynamicOutputs json.RawMessage `json:"dynamicOutputs"` Outputs []string `json:"outputs"` } ) // DerivationShow returns a [DerivationMap] describing all entries yielded by installables. func DerivationShow(ctx context.Context, installables iter.Seq[string]) (DerivationMap, error) { c, cancel := context.WithCancel(ctx) defer cancel() cmd := Nix(c, CommandDerivation, CommandDerivationShow, FlagStdin) ir, iw := io.Pipe() cmd.Stdin = ir or, ow := io.Pipe() cmd.Stdout = ow if Stderr != nil { cmd.Stderr = Stderr } cmd.Cancel = func() error { _ = ow.Close(); return cmd.Process.Kill() } if err := cmd.Start(); err != nil { return nil, err } done := make(chan error) var v DerivationMap go func() { done <- json.NewDecoder(or).Decode(&v) }() if _, err := WriteStdin(iw, installables); err != nil { return nil, errors.Join(err, cmd.Wait()) } if err := iw.Close(); err != nil { return nil, errors.Join(err, cmd.Wait()) } if err := cmd.Wait(); err != nil { // deferred cancel closes pipe return nil, err } err := <-done return v, err }