Documentation
¶
Index ¶
- Variables
- func Compile(ctx context.Context, pkgdir, binfile string) error
- func GetNames(ctx context.Context) map[uintptr]string
- func GetVerbose(ctx context.Context) bool
- func ID(prefix string) string
- func Indentf(ctx context.Context, format string, args ...any)
- func Name(ctx context.Context, target Target) string
- func Run(ctx context.Context, targets ...Target) error
- func WithHashDB(ctx context.Context, db HashDB) context.Context
- func WithNames(ctx context.Context, names map[uintptr]string) context.Context
- func WithRunner(ctx context.Context, r *Runner) context.Context
- func WithVerbose(ctx context.Context, verbose bool) context.Context
- type Command
- type CommandErr
- type DirHasher
- type FilesCommand
- type HashDB
- type HashTarget
- type Main
- type Runner
- type Target
Constants ¶
This section is empty.
Variables ¶
var DefaultRunner = NewRunner()
DefaultRunner is a Runner used by default in Run.
Functions ¶
func Compile ¶ added in v0.8.0
Compile compiles a "driver binary" from a directory of user code and places the executable result in a given file. The driver converts command-line target names into the necessary Fab rule invocations.
When the driver exists, the "fab" command simply executes it. When it doesn't exist, the fab command compiles it and _then_ executes it.
The driver binary knows the "dir hash" of the Go files from which it was compiled. When the driver runs, it checks that the dir hash is still the same. If it's not, then the build rules have changed and the driver binary is out of date. In this case the driver recompiles, replaces, and reruns itself.
When Compile runs (including when the driver recompiles itself) the "go" program must exist in the user's PATH.
How it works:
- The user's code is loaded with packages.Load.
- The set of exported top-level identifiers is filtered to find those implementing the fab.Target interface.
- The user's code is then copied to a temp directory together with a main package (and main() function) that records the set of targets, the dir hash of the user's code, and the value of binfile.
- The go compiler is invoked to produce an executable, which is renamed into place as binfile.
func GetNames ¶ added in v0.6.0
GetNames returns the map[uintptr]string added to `ctx` with WithNames. The default, if WithNames was not used, is nil.
func GetVerbose ¶
GetVerbose returns the value of the verbose boolean added to `ctx` with WithVerbose. The default, if WithVerbose was not used, is false.
func Indentf ¶ added in v0.9.0
Indentf calls Runner.Indent with the given format and args if a Runner can be found in the given context. If one cannot, then the formatted string is simply printed (with a trailing newline added if needed).
func Name ¶ added in v0.6.0
Name returns a name for `target`. Normally this is just target.ID(). But if `ctx` has been decorated with a name map using WithNames and target's address is in it, then that name is used instead.
func Run ¶
Run runs the given targets with a Runner. If `ctx` contains a Runner (e.g., because this is a recursive call and the context has been decorated using WithRunner) then it uses that Runner, otherwise it uses DefaultRunner.
func WithHashDB ¶
WithHashDB decorates a context with a HashDB. Retrieve it with GetHashDB.
func WithNames ¶ added in v0.6.0
WithNames decorates a context with a map[uintptr]string. The keys are the addresses of Target objects and the values are their "pretty names." When available, these are shown to the user at runtime in preference to the value of Target.ID.
func WithRunner ¶
WithRunner decorates a context with a Runner. Retrieve it with GetRunner.
Types ¶
type Command ¶
type Command struct {
// Shell is the command to run,
// as a single string with command name and arguments together.
// It is parsed as if by a Unix shell,
// with quoting and so on,
// in order to produce the command name
// and a list of individual argument strings.
//
// To bypass this parsing behavior,
// you may specify Cmd and Args directly.
Shell string `json:"shell,omitempty"`
// Cmd is the command to invoke,
// either the path to a file,
// or an executable file found in some directory
// named in the PATH environment variable.
//
// Leave Cmd blank and specify Shell instead
// to get shell-like parsing of a command and its arguments.
Cmd string `json:"cmd,omitempty"`
// Args is the list of command-line arguments
// to pass to the command named in Cmd.
Args []string `json:"args,omitempty"`
// Stdout and Stderr tell where to send the command's output.
// If either or both is nil,
// that output is saved in case the subprocess encounters an error.
// Then the returned error is a CommandErr containing that output.
Stdout io.Writer `json:"-"`
Stderr io.Writer `json:"-"`
// Dir is the directory in which to run the command.
// The default is the value of GetDir(ctx) when the Run method is called.
Dir string `json:"dir,omitempty"`
// Env is a list of VAR=VALUE strings to add to the environment when the command runs.
Env []string `json:"env,omitempty"`
// contains filtered or unexported fields
}
Command is a target whose Run function executes a command in a subprocess.
type CommandErr ¶
CommandErr is a type of error that may be returned from Command.Run. If the Command's Stdout or Stderr field was nil, then that output from the subprocess is in CommandErr.Output and the underlying error is in CommandErr.Err.
type DirHasher ¶ added in v0.8.0
type DirHasher struct {
// contains filtered or unexported fields
}
DirHasher computes a hash for a set of files. Instantiate a DirHasher, add files to it by repeated calls to File, then get the result by calling Hash.
The zero value of DirHasher is not usable. Obtain one with NewDirHasher.
func NewDirHasher ¶ added in v0.8.0
func NewDirHasher() *DirHasher
NewDirHasher produces a new DirHasher.
type FilesCommand ¶
FilesCommand is a HashTarget. It contains a Command, a list of input files, and a list of expected output files.
type HashDB ¶
type HashDB interface {
// Has tells whether the database contains the given entry.
Has(context.Context, []byte) (bool, error)
// Add adds an entry to the database.
Add(context.Context, []byte) error
}
HashDB is the type of a database for storing hashes. It must permit concurrent operations safely. It may expire entries to save space.
type HashTarget ¶
HashTarget is a Target that knows how to produce a hash (or "digest") representing the complete state of the target: the inputs, the outputs, and the rules for turning one into the other. Any change in any of those should produce a distinct hash value.
When a HashTarget is executed by Runner.Run, its Run method is skipped and it succeeds trivially if the Runner can determine that the outputs are up to date with respect to the inputs and build rules. It does this by consulting a HashDB that is populated with the hashes of HashTargets whose Run methods succeeded in the past.
Using such a hash to decide whether a target's outputs are up to date is preferable to using file modification times (like Make does, for example). Those aren't always sufficient for this purpose, nor are they entirely reliable, considering the limited resolution of filesystem timestamps, the possibility of clock skew, etc.
type Main ¶ added in v0.11.0
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner is an object that knows how to run Targets without ever running the same Target twice.
A zero runner is not usable. Use NewRunner to obtain one instead.
func GetRunner ¶
GetRunner returns the value of the Runner added to `ctx` with WithRunner. The default, if WithRunner was not used, is nil.
func (*Runner) Indentf ¶ added in v0.8.0
Indentf formats and prints its arguments with leading indentation based on the nesting depth of the Runner. The nesting depth increases with each call to Runner.Run and decreases at the end of the call.
A newline is added to the end of the string if one is not already there.
func (*Runner) Run ¶
Run runs the given targets, skipping any that have already run.
THEORY OF OPERATION ¶
A Runner remembers which targets it has already run (whether in this call or any previous call to Run), distinguishing them by their ID() values.
A separate goroutine is created for each Target passed to Run. If the Runner has never yet run the Target, it does so, and caches the result (error or no error). If the Target did already run, the cached error value is used. If another goroutine concurrently requests the same Target, it blocks until the first one completes, then uses the first one's result.
As a special case, if the Target is a HashTarget and there is a HashDB attached to the context, then the HashTarget's hash is computed and sought in the HashDB. If it's found, the target's outputs are already up to date and its Run method can be skipped. Otherwise, Run is invoked and (if it succeeds) a new hash is computed for the target and added to the HashDB.
This function waits for all goroutines to complete. The return value may be an accumulation of multiple errors. These can be retrieved with multierr.Errors.
The runner is added to the context with WithRunner and can be retrieved with GetRunner. Calls to Run (the global function, not the Runner method) will use this Runner instead of DefaultRunner by finding it in the context.
type Target ¶
type Target interface {
// Run invokes the target's logic.
//
// Callers generally should not invoke a target's Run method.
// Instead, pass the target to a Runner's Run method,
// or to the global Run function.
// That will handle concurrency properly
// and make sure that the target is not rerun
// when it doesn't need to be.
Run(context.Context) error
// ID is a unique ID for the target.
ID() string
}
Target is the interface that Fab targets must implement.
func Deps ¶ added in v0.3.0
Deps wraps a target with a set of dependencies, making sure those run first.
It is equivalent to Seq(All(depTargets...), target).