x.imp #
imp is a mischievous implicit build system meant to be used in V scripts
features
- dependencies derived from what flows take and give
- skips unchanged flows, by mtime or content hash
- pre/post hooks
- script and flow flags
- generated clean and run flows
- checks for binaries, V modules and C libraries
example usage
#!/usr/bin/env -S v run
import x.imp
const target = $if windows { 'hello.exe' } $else { 'hello' }
mut ctx := imp.new()
ctx.add_target(target)
ctx.add_flow(
id: 'build'
help: 'build the binary'
takes: ['src']
gives: [target]
flags: [imp.Flag{ id: 'prod', help: 'optimised build' }]
flow: fn (self imp.Flow) ! {
mut args := ['v', '-o', target]
if self.flag('prod') {
args << '-prod'
}
args << 'src'
imp.system_args(args)!
}
)
ctx.add_auto_flow(.clean)
ctx.add_auto_flow(.run)
ctx.default(['build'])
ctx.flow()
flow fields
| id | what is it |
|---|---|
name |
display name |
help |
help text |
takes |
input paths |
gives |
output paths |
check |
.mtime (default), .hash or .none |
pre |
[]imp.FlowDep run before |
post |
[]imp.FlowDep run after |
flags |
[]imp.Flag read with self.flag |
flow |
fn (self imp.Flow) ! |
hooks
| declaration | result |
|---|---|
imp.FlowDep{id: 'clean'} |
skipped along with its flow |
imp.FlowDep{id: 'clean', required: true} |
always runs, pre before the skip check |
A later post hook waits for the remaining post work of earlier hooks. A hook that needs an active flow waits for that flow to produce its outputs. Cycles of post hooks run each flow once; their pending work finishes before continuations waiting outside the cycle resume.
flags
| declaration | result |
|---|---|
flags: [imp.Flag{id: 'prod'}] |
self.flag('prod'), forces the flow to run |
ctx.flag(id: 'android') |
returns a bool, also readable with self.flag |
checks
imp.check_binary(['tar']) imp.check_v_modules(['ui2']) imp.check_system_c_links(['mpv/client.h'], flags: ['-lmpv'])
all return a bool
panic: true ends the build instead
fn check_binary #
fn check_binary(binaries []string, opts CheckOpts) bool
check_binary reports whether all the binaries are in the PATH.
fn check_system_c_links #
fn check_system_c_links(headers []string, opts LinkCheckOpts) bool
check_system_c_links reports whether a program including each of the headers compiles and links with the flags. Each header is tried in its own program. The program calls nothing from the header, so this shows that the flags resolve, such as -lmpv finding a library, and not that they are complete. The compiler is the one in $CC, or else the first of cc, gcc, clang and tcc in the PATH.
fn check_v_modules #
fn check_v_modules(modules []string, opts CheckOpts) bool
check_v_modules reports whether V can find all the modules, which may be given in dotted form. It searches vmodules, vlib, and ./modules.
fn new #
fn new() Context
new returns an empty build context.
fn system_args #
fn system_args(args []string) !
system_args runs a program with literal arguments and inherited standard streams. Unlike os.system_args, a failure is an error, so that propagating it with ! fails the flow.
fn AutoFlow.from #
fn AutoFlow.from[W](input W) !AutoFlow
from returns the AutoFlow value of an integer or of a field name, or an error if there is none.
fn Check.from #
fn Check.from[W](input W) !Check
from returns the Check value of an integer or of a field name, or an error if there is none.
type FlowFn #
type FlowFn = fn (self Flow) !
FlowFn is the body of a flow. Returning an error stops the build.
enum AutoFlow #
enum AutoFlow {
clean // deletes the targets
run // executes the single target
}
AutoFlow names a flow generated from the context targets.
enum Check #
enum Check {
mtime // skip when every output is newer than every input
hash // skip when inputs and set flags match the last successful run
none // never skip
}
Check selects how a flow decides whether it can be skipped.
struct CheckOpts #
struct CheckOpts {
pub:
panic bool // on failure, print the failed check and terminate all flows
}
CheckOpts configures a check.
struct Context #
struct Context {
mut:
targets []string
flows []Flow
autos []AutoFlow
defaults []string
enabled []string
flags []Flag
}
Context holds the targets, flows and flags of a build script.
fn (Context) add_target #
fn (mut c Context) add_target(path string)
add_target registers a build product, for the auto flows to act on.
fn (Context) add_flow #
fn (mut c Context) add_flow(flow Flow)
add_flow registers a flow. A duplicate id is a mistake in the build script.
fn (Context) flag #
fn (mut c Context) flag(flag Flag) bool
flag declares a script-level flag and reports whether it was given. It can be called before any flow exists, so the answer may shape the build itself, like the name of a target. Unlike a flag on a flow, it never defeats a skip.
fn (Context) add_auto_flow #
fn (mut c Context) add_auto_flow(kind AutoFlow)
add_auto_flow requests a flow generated from the targets. The flow is generated by flow(), so targets may be added after this call.
fn (Context) default #
fn (mut c Context) default(ids []string)
default sets the flows executed when the user names none. It must be called before flow(), which does not return control to the script on failure.
fn (Context) flow #
fn (mut c Context) flow()
flow executes the flows named on the command line, or the defaults. Given help, -h or --help first, it prints the flows and flags instead, unless the script has a flow called help, which then runs. On failure it reports the error and exits with status 1.
struct Flag #
struct Flag {
pub:
id string @[required]
help string
}
Flag is a boolean --id command line switch, declared by the flows reading it. It may appear anywhere on the command line, and reaches a declaring flow even when that flow only runs as a dependency. A set flag defeats the skip check of a declaring flow, as mtimes cannot tell that the flag changes output.
struct Flow #
struct Flow {
pub:
id string @[required]
name string // display name, derived from the id when unset
help string
takes []string // input files and directories
gives []string // output files and directories
check Check = .mtime
pre []FlowDep // hooks run before this flow
post []FlowDep // hooks run after this flow
flags []Flag // command line switches this flow reads with `flag`
flow FlowFn @[required]
mut:
enabled []string // the flags set on the command line
script_flags []string // the ids declared with `Context.flag`
}
Flow is a unit of work. It is also the parameter struct of add_flow.
fn (Flow) flag #
fn (f Flow) flag(id string) bool
flag reports whether --id was given. Reading a flag declared neither by the flow nor by Context.flag is a mistake in the build script.
fn (Flow) display_name #
fn (f Flow) display_name() string
display_name returns the name shown to the user.
struct FlowDep #
struct FlowDep {
pub:
id string @[required]
required bool
}
FlowDep is a hook on a flow. By default a hook is tied to its flow doing work, and is left out when the flow is skipped. A required hook always runs. A required pre hook runs ahead of the skip check, and can affect its result.
struct LinkCheckOpts #
struct LinkCheckOpts {
pub:
flags []string // compiler and linker flags, such as `-lmpv` or `-L/opt/lib`
panic bool // same as `CheckOpts.panic`
}
LinkCheckOpts configures check_system_c_links.