Profpatsch/nix/runGoCommand

runGoCommand(7)

runGoCommand - a Nix derivation whose builder is a Go program

depot.nix.runGoCommand "name" ./builder.go {
  input = somePackage;
  flag = "x";
}

runGoCommand is the Go counterpart of pkgs.runCommand: instead of a bash script run under stdenv's setup, the build step is a Go program. The file is compiled for the build machine with buildGo(7) and becomes the derivation's builder itself. There is no shell, no setup script, no hooks and no implicit PATH.

It exists because builders that assemble file trees — copy this, write that, generate a config, refuse if a store path leaks — grow into shell scripts whose quoting, globbing and error handling are each a bug waiting to happen. In Go they are typed, testable, and fail loudly.

name

The derivation's name.

builder

The Go file, package main. It is the whole builder: one file, importing only the standard library and drv.

derivationArgs

Passed to derivation() as they are. As with any derivation, each attribute becomes an environment variable of the builder: a package becomes its store path, a list its elements separated by spaces. The builder reads them with drv.Env() and drv.EnvList().

name, builder, args and system are reserved. system in particular is derivation()'s build platform, so an input called system would silently select another platform; it is an evaluation error instead.

Every builder can import drv, a small stdlib-only helper package (drv/drv.go). Its functions do not return errors: a builder has nothing to recover to, so any failure prints a message and exits non-zero, which fails the build.

Out()

$out.

Env(name)

A required variable; missing is fatal.

EnvList(name)

A list attribute, split.

Run(prog, args...)

Run a program, output to the build log. prog must be a path; pass the package in derivationArgs and join /bin/foo.

RunIn(dir, prog, args...)

Run() in a directory.

Output(prog, args...)

Run and return stdout.

Pipe(r, w, prog, args...)

Run with stdin and stdout attached.

Mkdir(paths...)

Write(path, content, mode)

WriteShebang(path, interpreter, lines...)

An executable script: #!interpreter (arguments included, e.g. "/bin/execlineb -P"), then the lines, each newline-terminated.

Touch(path)

Symlink(target, link)

CopyFile(src, dst, mode)

Mode 0 keeps the source's mode, owner-writable.

CopyTree(src, dst)

Recursive, symlinks preserved, everything owner-writable.

Glob(pattern)

Check(err, context...)

Fatalf(format, args...)

// builder.go
package main

import "drv"

func main() {
	out := drv.Out()
	drv.Mkdir(out + "/bin")
	drv.CopyFile(drv.Env("hello")+"/bin/hello", out+"/bin/hello", 0)
	drv.Run(out+"/bin/hello", "-g", "built by a Go builder")
}

depot.nix.runGoCommand "example" ./builder.go {
  hello = pkgs.hello;
}

buildGo(7), //nix/runExecline