blight
is a framework for wrapping and instrumenting build tools and build
systems. It contains:
- A collection of high-fidelity models for various common build tools (e.g. the C and C++ compilers, the standard linker, the preprocessor, etc.);
- A variety of "actions" that can be run on each build tool or specific
classes of tools (e.g. "whenever the build system invokes
$CC
, add this flag"); - Command-line wrappers (
blight-env
andblight-exec
) for instrumenting builds.
blight
is available on PyPI and is installable via pip
:
python -m pip install blight
Python 3.8 or newer is required.
blight
comes with two main entrypoints:
blight-exec
: directly execute a command within ablight
-instrumented environmentblight-env
: write ash
-compatible environment definition tostdout
, which the shell or other tools can consume to enter ablight
-instrumented environment
In most cases, you'll probably want blight-exec
. blight-env
can be thought
of as the "advanced" or "plumbing" interface.
Usage: blight-exec [OPTIONS] TARGET [ARGS]...
Options:
--guess-wrapped Attempt to guess the appropriate programs to wrap
--swizzle-path Wrap via PATH swizzling
--stub STUB Stub a command out while swizzling
--shim SHIM Add a custom shim while swizzling
--action ACTION Enable an action
--journal-path PATH The path to use for action journaling
--help Show this message and exit.
Usage: blight-env [OPTIONS]
Options:
--guess-wrapped Attempt to guess the appropriate programs to wrap
--swizzle-path Wrap via PATH swizzling
--stub TEXT Stub a command out while swizzling
--shim TEXT Add a custom shim while swizzling
--unset Unset the tool variables instead of setting them
--help Show this message and exit.
The easiest way to get started with blight
is to use blight-exec
with
--guess-wrapped
and --swizzle-path
. These flags tell blight
to configure
the environment with some common-sense defaults:
--guess-wrapped
: guess the appropriate underlying tools to invoke from the currentPATH
and other runtime environment;--swizzle-path
: rewrite thePATH
to put some common build tool shims first, e.g. redirectingcc
toblight-cc
.
For example, the following will run cc -v
under blight
's instrumentation,
with the Demo
action:
blight-exec --action Demo --swizzle-path --guess-wrapped -- cc -v
which should produce something like:
[demo] before-run: /usr/bin/cc
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: x86_64-apple-darwin22.2.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
[demo] after-run: /usr/bin/cc
We can also see the effect of --swizzle-path
by running which cc
under
blight
, and observing that it points to a temporary shim rather than the
normal cc
location:
$ blight-exec --swizzle-path --guess-wrapped -- which cc
/var/folders/zj/hy934vnj5xs68zv6w4b_f6s40000gn/T/tmp5uahp6tg@blight-swizzle@/cc
$ which cc
/usr/bin/cc
All the Demo
action does is print a message before and after each tool run,
allowing you to diagnose when a tool is or isn't correctly instrumented.
See the actions documentation below for information on
using and configuring more interesting actions.
Most make
-based builds use $(CC)
, $(CXX)
, etc., which means that they
should work out of the box with blight-exec
:
blight-exec --guess-wrapped -- make
In some cases, poorly written builds may hard-code cc
, clang
, gcc
, etc.
rather than using their symbolic counterparts. For these, you can use
--swizzle-path
to interpose shims that redirect those hardcoded tool
invocations back to blight
's wrappers:
blight-exec --guess-wrapped --swizzle-path -- make
See Taming an uncooperative build with shims and stubs for more advanced techniques for dealing with poorly written build systems.
Actions are where blight
really shines: they allow you to run arbitrary Python
code before and after each build tool invocation.
blight
comes with built-in actions, which are
documented here.
See each action's Python module for its documentation.
Actions can be enabled in two different ways:
-
With the
--action
flag, which can be passed multiple times. For example,--action SkipStrip --action Record
enables both theSkipStrip
andRecord
actions. -
With the
BLIGHT_ACTIONS
environment variable, which can take multiple actions delimited by:
. For example,BLIGHT_ACTIONS=SkipStrip:Record
is equivalent to--action SkipStrip --action Record
.
Actions are run in the order of specification with duplicates removed, meaning
that BLIGHT_ACTIONS=Foo:Bar:Foo
is equivalent to BLIGHT_ACTIONS=Foo:Bar
but not BLIGHT_ACTIONS=Bar:Foo
. This is important if actions have side
effects, which they may (such as modifying the tool's flags).
Some actions accept or require additional configuration, which is passed
through the BLIGHT_ACTION_{ACTION}
environment variable in key=value
format, where {ACTION}
is the uppercased name of the action.
For example, to configure Record
's output file:
BLIGHT_ACTION_RECORD="output=/tmp/output.jsonl"
There are two ways to get output from actions under blight
:
- Many actions support an
output
configuration value, which should be a filename to write to. This allows each action to write its own output file. blight
supports a "journaling" mode, in which all action outputs are written to a single file, keyed by action name.
The "journaling" mode is generally encouraged over individual outputs,
and can be enabled with either BLIGHT_JOURNAL_PATH=/path/to/output.jsonl
in the environment or blight-exec --journal-path /path/to/output.jsonl
.
blight-env
behaves exactly the same as blight-exec
, except that it
stops before actually executing anything. You can use it to set up an
environment for use across multiple build system runs.
By default, blight-env
will just export the appropriate environment
for replacing CC
, etc., with their blight
wrappers:
$ blight-env
export CC=blight-cc
export CXX=blight-c++
export CPP=blight-cpp
export LD=blight-ld
export AS=blight-as
export AR=blight-ar
export STRIP=blight-strip
export INSTALL=blight-install
--guess-wrapped
augments this by adding a best-guess underlying tool for
each wrapper:
$ blight-env --guess-wrapped
export BLIGHT_WRAPPED_CC=/usr/bin/cc
export BLIGHT_WRAPPED_CXX=/usr/bin/c++
export BLIGHT_WRAPPED_CPP=/usr/bin/cpp
export BLIGHT_WRAPPED_LD=/usr/bin/ld
export BLIGHT_WRAPPED_AS=/usr/bin/as
export BLIGHT_WRAPPED_AR=/usr/bin/ar
export BLIGHT_WRAPPED_STRIP=/usr/bin/strip
export BLIGHT_WRAPPED_INSTALL=/usr/bin/install
export CC=blight-cc
export CXX=blight-c++
export CPP=blight-cpp
export LD=blight-ld
export AS=blight-as
export AR=blight-ar
export STRIP=blight-strip
export INSTALL=blight-install
--guess-wrapped
also respects CC
, etc. in the environment:
$ CC=/some/custom/cc blight-env --guess-wrapped
export BLIGHT_WRAPPED_CC=/some/custom/cc
export BLIGHT_WRAPPED_CXX=/usr/bin/c++
export BLIGHT_WRAPPED_CPP=/usr/bin/cpp
export BLIGHT_WRAPPED_LD=/usr/bin/ld
export BLIGHT_WRAPPED_AS=/usr/bin/as
export BLIGHT_WRAPPED_AR=/usr/bin/ar
export BLIGHT_WRAPPED_STRIP=/usr/bin/strip
export BLIGHT_WRAPPED_INSTALL=/usr/bin/install
export CC=blight-cc
export CXX=blight-c++
export CPP=blight-cpp
export LD=blight-ld
export AS=blight-as
export AR=blight-ar
export STRIP=blight-strip
export INSTALL=blight-install
--swizzle-path
further modifies the environment by rewriting PATH
:
$ blight-env --guess-wrapped-swizzle-path
export BLIGHT_WRAPPED_CC=/usr/bin/cc
export BLIGHT_WRAPPED_CXX=/usr/bin/c++
export BLIGHT_WRAPPED_CPP=/usr/bin/cpp
export BLIGHT_WRAPPED_LD=/usr/bin/ld
export BLIGHT_WRAPPED_AS=/usr/bin/as
export BLIGHT_WRAPPED_AR=/usr/bin/ar
export BLIGHT_WRAPPED_STRIP=/usr/bin/strip
export BLIGHT_WRAPPED_INSTALL=/usr/bin/install
export PATH='/var/folders/zj/hy934vnj5xs68zv6w4b_f6s40000gn/T/tmpxh5ryu22@blight-swizzle@:/bin:/usr/bin:/usr/local/bin'
export CC=blight-cc
export CXX=blight-c++
export CPP=blight-cpp
export LD=blight-ld
export AS=blight-as
export AR=blight-ar
export STRIP=blight-strip
export INSTALL=blight-install
The swizzled addition can be identified by its @blight-swizzle@
directory name.
Sometimes build systems need more coaxing than just --guess-wrapped
and
--swizzle-path
. Common examples include:
- Hard-coding a particular tool or tool version rather than using the symbolic
name (e.g.
clang-7 example.c
instead of$(CC) example.c
); - Running lots of "junk" commands that can be suppressed (e.g. lots of
echo
invocations)
You can use shims and stubs to smooth out these cases:
- shims replace a command with a build tool that
blight
knows about, e.g.clang-3.8
withcc
. - stubs replace a command with an invocation of
true
, meaning that it does nothing and never fails.
Shims are specified with --shim cmd:tool
, while stubs are specified with
--stub cmd
. Both require --swizzle-path
, since the PATH
must be rewritten
to inject additional commands.
For example, to instrument a build system that hardcodes tcc
everywhere
and that spews way too much output with echo
:
blight-exec --guess-wrapped --swizzle-path --shim tcc:cc --stub echo -- make
- Wrapping
CC
,CXX
,CPP
,LD
,AS
,AR
,STRIP
, andINSTALL
. - Providing a visitor-style API for each of the above, pre- and post-execution.
- Providing a nice set of default actions.
- Being as non-invasive as possible.
- Using
LD_PRELOAD
to capture everyexec
in a build system, a la Bear. - Supporting
cl.exe
. - Detailed support for non C/C++ languages.
Check out our CONTRIBUTING.md!