This is an open implementation of make, striving to be simple, portable, and fast.
To avoid clashing with your system's make, this project is built as omake by default, but if
this project is ever used as the default implementation of make in a system, then it should be
named make (to follow the same convention as bmake and gmake).
You can install with Cargo: cargo install omake. In the future, I may consider packaging this
project for other repos such as Homebrew or the AUR.
This project is in its infancy, so I may find out later that some or all of the project goals are impossible to achieve. Regardless, in order of importance, here are the project goals:
- Portable makefiles and makefiles generated by tools like CMake should behave correctly.
- Support as many commonly-used BSD and GNU
makeextensions as possible. - Be capable of building the Linux kernel.
- Be really fast.
- If we decide to implement new extensions, they should be opt-in to retain backwards compatibility. We should avoid this unless there are serious performance improvements.
- Possibly the hardest: don't turn into a backwards-incompatible competing standard. As uninspired
as it may seem, I just want an implementation of
makethat works on Linux and FreeBSD (and macOS); that's it.
1.0 release will probably happen when this project can build the Linux kernel.
Note that due to implementation details (and especially during the initial development phase this project is in), it's possible certain features are inadvertently added. Users should probably not rely on those and they may even qualify as bugs. I hope to get everything ironed out before the 1.0 release to avoid (as stated in Goal #6) building an incompatible competing standard. There are already other build systems, I don't actually want to make another one.
Working list of things that I plan on leaving out of this implementation intentionally:
- Remaking makefiles from RCS/SCCS. I see no need to support this.
To build and run:
cargo build
cargo run -- -f Makefile # run against a makefile (args after `--`)The toolchain is pinned to nightly via rust-toolchain.toml (only so rustfmt can wrap over-long
comments) and installs automatically; the code itself still builds on stable.
Like the BSD makes, omake will grow a few OS-specific paths — chiefly the system makefile
directory where sys.mk and the built-in rules live (bmake's _PATH_DEFSYSPATH, /usr/share/mk
on NetBSD and FreeBSD). Rather than hard-code one OS, these are centralized as build-time constants,
each overridable through an OMAKE_* environment variable read at compile time with option_env!
and defaulting to NetBSD's value:
// One module holds these; NetBSD defaults, overridable at build time.
pub const DEFAULT_SYSPATH: &str = match option_env!("OMAKE_DEFAULT_SYSPATH") {
Some(path) => path,
None => "/usr/share/mk",
};A packager targeting a different layout sets the override in the build environment (or pins it in
.cargo/config.toml):
OMAKE_DEFAULT_SYSPATH=/opt/pkg/share/mk cargo build --releasePlanned constants and their NetBSD defaults: system makefile dir /usr/share/mk, system makefile
name sys.mk, object-dir prefix /usr/obj, default shell dir /bin, temp dir /tmp. These are
tracked in TODO.md; none are wired up yet (omake does not read sys.mk today).
cargo fmt # nightly rustfmt, wraps comments at 100 columns
cargo clippy --all-targets # the baseline is zero warnings — keep it clean
cargo test # fast unit + system tests, no external dependenciessrc/lib/— the library (parsing + execution). Start here:makefile.rs— the parser and theMakefiletype (directives, includes, conditionals,.for).makefile/rule_map.rs— rules, the build graph, recipe execution, implicit/pattern rules.expand.rs— variable/function expansion ($(...), GNU functions, BSD:modifiers).vars.rs— the variable store and origins;makeflags.rs—MAKEFLAGS(de)serialization;compat.rs— the--compat={gnu,bsd}diagnostic-output emulation;glob.rs,logger.rs.
src/bin/main/— the CLI:args.rs(clap) and_main.rs(wiring, the restart loop).tests/—system_tests.rs+tests/scenarios/(directory-per-scenario), andtests/conformance/(the GNU/bmake suites; see its README).TODO.md(gitignored) tracks the remaining work, split into GNU and BSD sections.
Dependencies are kept minimal on purpose.
Unit tests live alongside the code where they are feasible. The bulk of coverage is the system
test suite under tests/scenarios/: each scenario is a directory with a makefile, a mod.rs, and
any fixture files. mod.rs invokes the system_test_cases! macro, which runs the built omake
against that makefile with the given arguments and checks stdout/stderr — and the resulting
directory contents — against the expected values. These are fast and need no external tools:
cargo test # unit + system testsBeyond the hand-written scenarios, tests/conformance/ runs omake against the real upstream test
suites of GNU make and BSD make (vendored on demand at a pinned commit). Both do exact golden-file
comparison, so the runners use --compat={gnu,bsd} to match that make's diagnostic output and
measure real behavioral gaps rather than format noise. Current pass rates:
| Suite | Version | omake --compat |
|---|---|---|
| GNU make | 4.4.1 | 717 / 1337 |
| BSD make | 20230711 | 117 / 396 |
The scores are pinned as a regression gate: two #[ignore]d tests shell out to the runners, so the
normal cargo test loop stays fast and dependency-free. To also run the gate, un-ignore them:
cargo test -- --include-ignored # also runs the conformance regression gate (~6 min)See tests/conformance/README.md for the runner scripts, their
prerequisites (perl, bmake, first-time network), how to read the results, and why a plain-omake
score would be lower but meaningless (output-format noise, not mis-run makefiles).