Map

forgezero

Toolbox toolboxgoassemblybuild-toolnasmgasfasmcwatchlistGoMIT โ†ณ show in map Markdown
title
forgezero
type
toolbox
summary
Makefile-free build tool wrapping NASM/GAS/FASM/GCC behind one strict-by-default command
tags
go, assembly, build-tool, nasm, gas, fasm, c, watchlist
language
Go
license
MIT
created
2026-05-19
updated
2026-07-22

Single-binary build tool for assembly and C/C++ that wraps NASM, GAS, FASM, GCC, Clang, and LD behind one fz command. Drops Makefiles entirely: point it at a file or directory, it picks the right backend by extension and links a binary. The target audience is people writing .asm and bare-metal C โ€” the kernel/bootloader/firmware crowd that doesn't want to hand-wire nasm -felf64 + ld -T linker.ld every iteration.

Tracked here because the defaults are unusually opinionated for a build tool and the v1.9 feature set is broader than the star count suggests. On the watchlist โ€” see "Status" below.

Design choices worth noting

Strict-by-default C. Every .c file gets -Wall -Wextra -Werror -Wpedantic -Wshadow -Wconversion with no escape hatch. Same flags for C++ (-Wpedantic applies to ISO C++ too). Warnings stop the build. No -Wno-anything.

Sanitizers on by default. -fsanitize=address,undefined for every C/C++ build unless -sanitize=false. -strict adds -fsanitize-address-use-after-return=always (Clang-only) and -fsanitize-address-use-after-scope. Most build tools leave sanitizers off โ€” this one flips it, which is a strong correctness bet but bad for release builds (you have to remember to disable).

Three linking modes. -mode auto tries gcc โ†’ gcc -no-pie โ†’ ld in sequence. -mode c forces gcc/clang (for libc programs). -mode raw invokes ld directly with no C runtime โ€” the kernel/bootloader path, paired with -T <script> and -Ttext <addr> to control layout. Sanitizer flag conflicts mean -type static doesn't compose with -mode raw.

Pre-link symbol check. Before linking, fz runs nm (fallback: objdump, readelf) across every object and refuses to link if any global symbol is defined twice. Catches the classic "two _starts" or duplicate-function case before the linker emits its own cryptic error. Disable with -no-symbol-check if you're relying on weak symbols.

Build cache keyed on (source SHA-256, debug, mode, target). Stored in .fz_cache/. The target component means changing -target arm-linux-gnueabihf properly invalidates the cache instead of reusing native objects.

Multi-level config merging. /etc/fz/fz.yaml (system) โ†’ ~/.config/fz/fz.yaml (user) โ†’ .fz.yaml (project) โ†’ CLI flags. Each layer overrides the previous. The system-level layer is the unusual one โ€” most tools stop at user-level.

Object-name collision avoidance. src/sub/hello.asm becomes src_sub_hello_asm.o instead of hello.o, so multi-directory projects with same-base-name files (e.g. several utils.c) compile cleanly. Was a v1.8 fix after the obvious bug landed.

CLI surface

fz -asm hello.asm                          # single file
fz -cc main.cpp                            # C or C++
fz -dir ./src                              # recursive scan + link
fz -dir ./src -type static -lib mylib      # produces libmylib.a
fz -cc main.c -target arm-linux-gnueabihf  # cross-compile
fz -compile-commands                       # emit compile_commands.json for clangd
fz -dir ./src -watch                       # fsnotify, 500ms debounce
fz -dir ./src -json                        # CI-shaped JSON report
fz -shell                                  # REPL
fz -init                                   # scaffolds .fz.yaml, .fzignore, README.md

-j N for parallel compile, -j 0 for ncores. Cross-compile resolves prefixed tools (<triple>-gcc, <triple>-g++, <triple>-ar) on PATH and exits with code 2 if missing.

Supported extensions: .asm (NASM), .s / .S (GAS, the latter C-preprocessed), .fasm (separate flatassembler.net install), .c, .cpp / .cc / .cxx. Everything else silently skipped.

What's interesting vs. what's not

The genuinely-new pieces are the strict-warnings-and-sanitizers-by-default posture and the symbol-pre-check before linking. Everything else (build cache, watch mode, JSON output, compile_commands generation, REPL, parallel builds) is reasonable but well-trodden โ€” the value is bundling them with assembler support, where most modern build tooling assumes C/C++/Rust.

The other way to get rid of the Makefile is to not write a build tool at all. ultimate-gojust covers the same ground for Go โ€” build with version injection, cross-compile, test, lint, watch, migrations โ€” as a single Justfile you copy into the project, with nothing to install beyond just. The tradeoff is the usual one: fz can enforce its defaults because it owns the compiler invocation, while a Justfile is text anyone can edit past.

The cross-compile story is GNU-triple-prefix detection, not a clang -target invocation. So riscv64-linux-gnu-gcc must already be on PATH; fz doesn't ship a toolchain. That makes it thin glue over distro packaging, which is fine but means cross-compilation breaks the moment your distro lacks the package.

Status

Two-month-old repo (created 2026-03-15), single author (alexvoste), 5 stars, MIT, repo just transferred to forgezero-cli org. Despite the v1.9.0 tag, this is firmly pre-adoption. Aggressive release cadence (1.5 โ†’ 1.9 in two months) suggests one person iterating fast, not a stable surface.

macOS support is "in progress." Windows support is "experimental" and requires MSYS2 + manual NASM install + Clang gymnastics for sanitizers. So the real target is Linux today.

On the watchlist โ€” re-check 2026-08-19. Want to see: a second contributor, macOS/Windows reaching parity, the strict-defaults / no-escape-hatch stance surviving real user pushback, and whether the project finds an audience beyond the "I write OS kernels for fun" niche it currently caters to.

Repo

github.com/forgezero-cli/ForgeZero โ€” Go, MIT, 5 stars, v1.9.0 (2026-05-18).