Putup uses Tup's Tupfile syntax. This document covers supported features and differences.
| Tup Command | Putup Equivalent | Status |
|---|---|---|
tup |
putup |
✅ Implemented |
tup upd |
putup |
✅ Implemented |
tup init |
putup configure |
✅ Creates tup.config and initializes project |
tup refactor |
putup parse |
✅ Implemented |
tup graph |
putup show graph |
✅ Implemented |
tup compiledb |
putup show compdb |
✅ Implemented |
tup monitor |
- | ❌ Not planned (no FUSE) |
tup stop |
- | ❌ Not planned (no monitor) |
tup scan |
- | ❌ Not implemented |
tup variant |
putup configure -B |
✅ Creates variant dir + tup.config |
tup generate |
putup show script |
✅ Implemented |
tup commandline |
- | ❌ Not implemented |
tup todo |
- | ❌ Not implemented |
tup varsed |
- | ❌ Not implemented |
tup options |
- | ❌ Not implemented |
tup dbconfig |
- | ❌ Not implemented |
See docs/reference.md for complete syntax documentation.
| Feature | Status |
|---|---|
Rules (: inputs |> command |> outputs) |
✅ Implemented |
| foreach rules | ✅ Implemented |
Variables (=, +=, :=) |
✅ Implemented |
Soft/weak set (?=, ??=) |
✅ Implemented (extension) |
Config variables (@(VAR)) |
✅ Implemented |
Node variables (&(VAR)) |
✅ Implemented |
Pattern flags (%f, %o, %B, etc.) |
✅ Implemented |
Bang macros (!name) |
✅ Implemented |
Conditionals (ifdef, ifeq, etc.) |
✅ Implemented |
Groups and bins ({group}, <group>) |
✅ Implemented |
Directives (include, export, import) |
✅ Implemented |
run directive |
❌ Not implemented |
preload directive |
|
error directive |
❌ Not implemented |
| Feature | Syntax | Status |
|---|---|---|
| Groups (bins) | {groupname} |
✅ |
| Order-only groups | <groupname> |
✅ |
| Cross-directory groups | dir/<groupname> |
✅ |
Groups (tup calls these "bins") collect outputs for use as inputs in other rules:
: foreach *.c |> $(CC) -c %f -o %o |> %B.o {objs}
: {objs} |> $(CC) -o %o %f |> program
Order-only groups are for cross-directory order-only dependencies:
: gen-headers.sh |> ./gen-headers.sh |> headers.h <gen>
: foo.c | <gen> |> $(CC) -c %f -o %o |> foo.o
Cross-directory groups reference groups defined in other Tupfiles:
# In src/Tupfile - reference group from include/generated/Tupfile
: foo.c | $(ROOT)/include/generated/<gen-headers> |> $(CC) -c %f -o %o |> foo.o
| Feature | Status |
|---|---|
| Subdirectory Tupfiles | ✅ |
| Cross-directory dependencies | ✅ |
| Demand-driven parsing | ✅ |
| Per-Tupfile variable scope | ✅ |
include_rules inheritance |
✅ |
| Circular dependency detection | ✅ |
Putup fully supports projects with Tupfiles in multiple subdirectories:
project/
├── Tuprules.tup # Shared macros
├── src/
│ └── Tupfile # Uses !cc from Tuprules.tup
└── tests/
└── Tupfile # Independent Tupfile
| Aspect | Tup | Putup |
|---|---|---|
| Primary method | FUSE interception | Index comparison |
| Fallback | mtime | mtime → size → SHA-256 |
| Implicit deps | Automatic via FUSE | Requires -MD flag |
Putup's change detection algorithm:
- If mtime differs from index → rebuild
- If mtime matches but size differs → rebuild
- If size matches → compute SHA-256, rebuild if different
Tup uses FUSE to intercept file accesses and automatically discover dependencies.
Putup requires explicit .d file generation:
CFLAGS += -MD # Generate foo.d alongside foo.o
: foreach *.c |> $(CC) $(CFLAGS) -c %f -o %o |> %B.o
Putup tracks all headers including system headers (/usr/include/*).
| Aspect | Tup | Putup |
|---|---|---|
| Format | SQLite database | Binary index file |
| Location | .tup/db |
.pup/index |
| Corruption recovery | SQLite tools | Delete and rebuild |
# Tup # Putup
.tup/ .pup/
├── db └── index
├── object/
└── ...
- Most Tupfiles work unchanged
- Replace
tupwithputupin scripts - Add
-MDto compiler flags for header tracking
Tup:
run ./generate-sources.sh
Workaround: Run the script manually before building, or use a rule:
: generate-sources.sh |> ./generate-sources.sh |> generated.c
Putup includes E2E tests that verify tup-compatible behavior:
./build/test/unit/putup_test '[e2e]'Test fixtures cover:
- Simple C compilation
- Multi-file projects
- Bang macros
- Groups (bins and order-only)
- Conditionals
- Incremental builds
- Variant builds
- Multi-variant parallel builds
- Multi-directory projects (tested with ctos - 75 Tupfiles, 681 commands)
Features in putup that extend beyond tup:
| Feature | Description |
|---|---|
| Conditional assignments | ?= (soft set) and ??= (weak set) operators |
| Path-based variants | putup build-debug instead of putup -B build-debug |
| Scoped builds | putup build-debug/src/lib builds only that subdirectory |
| Single output targets | putup build-debug/src/lib/foo.o rebuilds one specific output |
| Glob patterns | putup 'build-*' matches multiple variants |
| Multi-variant parallel | putup -B build-debug -B build-release or putup build-debug build-release |
| Auto-variant detection | Running putup from project root auto-detects all variants |
| Variant output prefix | Output lines are prefixed with [variant-name] for clarity |
| Show formats | putup show graph|script|compdb for different output formats |
| Content-based hashing | SHA-256 for precise change detection beyond mtime |
| Scoped tup.config | Per-subdirectory configs: nearest tup.config in parent chain is used |
| Configure command | putup configure runs config rules and ensures tup.config exists |
Affected: MSVC 19.38+ (Visual Studio 2022 17.8) with /Zc:nrvo /std:c++20 /O2
Status: Fixed. The root cause was DirNameKeyHash/DirNameKeyEqual functors holding raw StringPool* pointers into the same Graph object. When MSVC failed to apply NRVO on make_graph(), the move left dangling pointers. The DirNameKey hash map has been replaced with std::vector<SortedPairVec> dir_children (per-directory sorted arrays), eliminating the pointer coupling entirely. MSVC CI jobs remain disabled for other reasons (MinGW/GCC is the Windows CI target).
If you find a Tupfile that works with tup but not putup:
- Minimize the reproducer
- Check if the feature is listed as "not implemented"
- Open an issue with the Tupfile and expected behavior