A build tool for C that interfaces directly with the system compiler.
You write a Barrfile, barr evaluates it, and invokes the compiler accordingly.
I built barr as a personal challenge to understand the build process and wanted a way to jump straight into coding without much manual setup and boilerplate.
-
Purpose-Built: It was designed to solve my workflow needs for C projects.
-
Learning Milestone: The codebase is preserved as a snapshot of my growth and understanding at the time of development.
-
Tested: Used daily for months in other projects. Reliable for my needs.
-
Active development is paused, and I do not provide ongoing support.
-
This project is considered feature-complete for my workflow.
-
Feel free to fork and adapt it to your needs.
Version: 1.0.1
LICENSE: Apache 2.0
- Tested on Arch, Mint, Ubuntu, Fedora
Platform: Linux
Dependencies:
- Before running bootstrap script, ensure the following are installed:
-
General:
git,libxxhash(Development Headers) -
Arch:
sudo pacman -S base-devel xxhash -
Ubuntu/Mint:
sudo apt install build-essential libxxhash-dev -
Fedora/RHEL:
sudo dnf install @development-tools xxhash-devel
-
git clone https://github.com/vsix8625/Build_Barrage.git
cd Build_Barrage
./scripts/build.shThe build.sh script is a bootstrap tool. It uses the system compiler to build a minimal version of barr.
Once the bootstrap is complete, barr will automatically rebuild itself to produce a final, optimized binary.
Once built, install it:
# Option A: copy to any directory in your $PATH
cp build/release/bin/barr ~/.local/bin/
# Option B: using barr install --prefix
# If you have in $PATH: $HOME/opt/usr/bin
barr install --prefix $HOME/opt/usr # will install to ~/opt/usr/bin
# Option C: system install
sudo ./build/release/bin/barr install # installs to /usr/local/binCreate a new project and get to a running binary:
barr new --project demo
cd demo
barr new --barrfile # creates a default Barrfile in cwd
barr new --main # creates a main.c with "Hello from barr" main function in src
barr build # read the Barrfile and build the sources
barr run # run the resulting binaryCommands can be chained with ... or ---, both separators are equivalent:
barr new --barrfile --- new --main --- new --pair util --- build --- runThe Barrfile is barr's build configuration — a declarative script with variable interpolation and a small set of built-in functions.
Variable conventions:
ALL_CAPS— reserved keys that control barr's behavior (compiler, flags, output dirs, etc.)BARR_prefix — read-only built-in variables (OS, detected compilers, paths)lowercase/camelCase— your own custom variables, free to define and reference
A freshly generated Barrfile looks like this:
print("Barrfile execution started");
TARGET = "myapp";
VERSION = "0.0.1";
TARGET_TYPE = "executable";
print("Building ${TARGET}-v${VERSION} | Type: ${TARGET_TYPE}");
COMPILER = "/usr/bin/clang";
LINKER = "lld";
BUILD_TYPE = "debug";
OUT_DIR = "build/${BUILD_TYPE}/${TARGET}";
std = "-std=c23";
CFLAGS = "${std} -Wall -Werror -Wextra -g";
CFLAGS_RELEASE = "${std} -Wall -O3";
DEFINES = "-DDEBUG";
# LFLAGS = "-lpthread";
# LIB_PATHS = "-L/usr/local/lib";
# GEN_COMPILE_COMMANDS = "on";
# EXCLUDE_PATTERNS = "build test"; # src/main.c is the default main entry
MAIN_ENTRY = "barr.c"; # file containing the entry point for your executable.File discovery:
barr scans the project root recursively for:
-
C/C++ Sources:
.c,.cpp,.cc,.cxx,.C. You can override this withGLOB_SOURCESorSOURCES. -
Headers:
.h,.hpp,.hh,.hxx. You can override this withAUTO_INCLUDE_DISCOVERY="off".
GLOB_SOURCES = "src engine/core"; # scan specific directories
SOURCES = "src/main.c src/util.c"; # explicit file list
AUTO_INCLUDE_DISCOVERY = "on" # controls header scanning (`on` [default], `append`, `off`).
INCLUDES = "-I. -Iinc -Isrc"; # defining this will set AUTO_INCLUDE_DISCOVERY to "off" unless it was set to "append"System packages via pkg-config:
pkgs = "xxhash zlib";
find_package("${pkgs}");Modules let you build a dependency before the main target:
add_module("engine", "engine", "required");
engine_includes = "-Iengine/include";
MODULES_INCLUDES = "${engine_includes}";Running arbitrary commands with run_cmd() — barr will ask for your approval before executing:
- You can skip confirmation prompt by passing
--trustto the command. - Example:
barr build --trust.
run_cmd("./tools/gen_headers.sh");Run barr status --vars to see all available built-in variables for your current project.
barr new --project <name> # new directory with barr initialized
barr new --barrfile # create default Barrfile in current dir
barr new --main # create src/main.c
barr new --file <name> [dir] # create a single file
barr new --pair <name> --dir <dir> # create <name>.c and <name>.h togetherThe --pair command also accepts --ext and --public:
barr new --pair renderer --dir graphics
# → graphics/renderer.c, graphics/renderer.h
barr new --pair graphics --ext .cpp --public true
# if --dir is not specified it writes to src by default
# → src/graphics.cpp, inc/graphics.hppbarr status # fast timestamp scan of source files
barr status --all # full file status with build summary
barr status --vars # list all Barrfile variables (built-in + user-defined)
barr status --binfo # show stored build info (name, type, version, paths, timestamp)barr debug --cache # show content hashes for all tracked source files
barr debug --fsinfo # directory summary: file count, sizes, executablesfo is an experimental file watcher bundled with barr. It monitors source file modification times and triggers barr build automatically when changes are detected.
Typical workflow — run these in two separate terminal panes:
# Pane 1: deploy fo in your project directory
barr fo deploy
# [barr_log]: Forward Observer deployed: 19114
# Pane 2: watch the live log (requires shell 'watch')
barr fo watch
# [19:53:36]: idle — no changes detected
# ...
# [19:56:16]: TRIGGERED — modified sources detected
# [19:56:16]: barr build exit: 0Save a source file and fo picks it up within its tick interval and rebuilds. Then barr run to test.
barr fo report # check fo status and last build timestamp
barr fo dismiss # dismiss (stop) the watcherfo is not required for normal use and is considered experimental.
barr tool (alias: -tl) launches external development tools against the last build binary.
No need to locate the path manually, barr resolves it from the build info automatically.
barr tool --gdb # Launch GDB with the last built binary loaded
barr tool --perf # Run perf record on the binary, then use perf report to inspect
barr tool --valgrind # Run Valgrind memcheck on the binary
barr tool --strace # Run strace -c and write syscall summary to a .txt file in the project root--gdb opens GDB with the binary loaded and ready to run. Useful for debug builds where symbols are present -g in CFLAGS.
--perf runs perf record against the binary. After it completes, inspect the results with:
perf report
# or
perf report --stdio > perf_report.txt # your file name--valgrind runs --tool=memcheck against the binary. Useful for catching heap errors and confirming clean exits.
--strace runs strace -c and writes the syscall summary to <target>_strace_syscall.txt in the project root rather than printing to terminal.
barr debug --cache # Show hashes for all tracked source files
barr debug --fsinfo # Directory summary: file count, sizes, executables barr install works for any project. It reads your build info and installs to the appropriate system directories under a prefix (default /usr/local):
- Executables →
<prefix>/bin/<name> - Shared libraries →
<prefix>/lib/<name>.so - Static libraries →
<prefix>/lib/<name>.a - Public headers — any
-Ipaths from yourCFLAGSare copied to<prefix>/include/<name>/
sudo barr install # installs to /usr/local
sudo barr uninstall # removes installed filesbarr tracks what it installed so uninstall knows exactly what to clean up.
barr config open # open the local Barrfile in $EDITOR (requires initialized project)build -b Build project (incremental)
rebuild -rb Clean and build
run -r Run latest binary
clean -c Remove build directory
init -I Initialize barr in current directory
deinit -D Remove barr from current directory
new -n Create project, files, or scaffolding
config -C Manage configuration
status -s Project status and variable inspection
fo Forward observer (file watcher)
debug -db Cache, filesystem, and build diagnostics
install -i Install project to system (executables → bin/, libraries → lib/, headers → include/)
uninstall -uni Remove installed files from system
barr help <command> # detailed options for any command-
Linuxonly: NomacOSorWindowssupport. -
Changing
BUILD_TYPEin the Barrfile does not flag the cache change for recompilation. A manual rebuild is advised. -
When running a target with
barr run(-r), flags intended for the executable may sometimes be interpreted by barr itself (e.g.,--verbose). Works in most cases and does not affect normal usage. -
barr statususes modification time for quick checks. In some cases (e.g., edit + undo + save), a file may appear “modified” even if content hasn’t changed. The actual build (barr build) uses content hashing and will correctly skip unchanged files. -
barr build --turbobatch build is experimental. Gains are situational: many small files with noglobalorstaticdata may benefit, but in real projects it often provides little advantage. -
Barrfilescript layer:- One
TARGETperBarrfile. - No multi-line strings.
- No conditional statements.
- Numbers should be enclosed in quotes:
- One
Example:
major = "0";
minor = "0";
patch = "12";
VERSION = "${major}.${minor}.${patch}";
file_write("#define FOO_VERSION_MAJOR ${major}","${config_file}","true");
file_write("#define FOO_VERSION_MINOR ${minor}","${config_file}","true");
file_write("#define FOO_VERSION_PATCH ${patch}","${config_file}","true");
file_write('#define FOO_VERSION_STRING "${major}.${minor}.${patch}"',"${config_file}","true");
# Failure to do so may result in truncated digits.
-
When writing
#definestatements or C-strings to a file, use single quotes for thefile_writefunction to wrap the double quotes.- Example:
file_write('#define TYPE "${VAR}"', "file.h", "true");# true for append file
- Example:
-
For C++ main.cpp has to be set with
MAIN_ENTRY=main.cppotherwisebarrexpects main.c. -
Barrfilemust contain at least one variable assignment (e.gTARGET). A file containing only comments or whitespace may trigger an arena allocation error. -
fois experimental. Treat it as a convenience tool, not a stable feature.
For a deeper dive into Barr’s compilation stages, modules, and internal workflow, see docs/technical.md.