Skip to content

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_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 #

@[params]
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 #

@[params]
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 #

@[params]
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 #

@[params]
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.