- Configurable rules via
~/.bake.toml
- CI/CD integration with check mode
- Extensible plugin architecture
- Rich terminal output with progress indicators
- Syntax validation before and after formatting
- Smart .PHONY detection with automatic insertion
- Suppress formatting with special comments
- Tabs for recipes: Recipe lines use tabs instead of spaces
- Assignment operators: Normalized spacing around
:=
,=
,+=
,?=
- Target colons: Consistent spacing around target dependency colons
- Trailing whitespace: Removes unnecessary trailing spaces
- Backslash normalization: Proper spacing around backslash continuations
- Smart joining: Consolidates simple continuations while preserving complex structures
- Grouping: Consolidates multiple
.PHONY
declarations - Auto-insertion: Automatically detects and inserts
.PHONY
declarations when missing (opt-in) - Dynamic enhancement: Enhances existing
.PHONY
declarations with additional detected phony targets - Rule-based analysis: Uses command analysis to determine if targets are phony
- Minimal changes: Only modifies
.PHONY
lines, preserves file structure
pip install mbake
- Open VSCode
- Go to Extensions (Ctrl+Shift+X)
- Search for "mbake Makefile Formatter"
- Click Install
git clone https://github.com/ebodshojaei/bake.git
cd mbake
pip install -e .
git clone https://github.com/ebodshojaei/bake.git
cd mbake
pip install -e ".[dev]"
For system package managers and AUR packagers, mbake supports configurable command names to avoid namespace conflicts.
For AUR packagers: The default behavior already avoids conflicts with ruby-bake
- no additional configuration needed!
bake
to mbake
for consistency.
- Python module:
bake
→mbake
(for consistency with command name) - Command name: Still
mbake
(unchanged) - Configuration: Still
~/.bake.toml
(unchanged)
-
Update the package:
pip install --upgrade mbake
-
If you have shell aliases, they will continue working:
# Your existing alias will still work alias bake='mbake' bake --version # ✅ Still works
-
If you have Python scripts that import from
bake
, update them:# Old (v1.2.x) from bake.config import Config # New (v1.3.x) from mbake.config import Config
-
If you have CI/CD scripts, update import statements:
# Old (v1.2.x) python -c "from bake.cli import main; main()" # New (v1.3.x) python -c "from mbake.cli import main; main()"
- CLI commands: All commands work exactly the same
- Configuration files: No changes needed
- Shell aliases: Continue working without modification
- Python imports: Require updating to use
mbake
module
mbake uses a subcommand-based CLI. All commands support both bake
and mbake
aliases.
# Check version
mbake --version
# Set up your preferred command name (optional)
mbake setup-command mbake # or 'bake' (creates alias) or 'both' (creates alias)
# Initialize configuration (optional)
mbake init
# Format a Makefile
mbake format Makefile
# Validate Makefile syntax
mbake validate Makefile
# Initialize configuration file with defaults
bake init
# Initialize with custom path or force overwrite
bake init --config /path/to/config.toml --force
# Show current configuration
bake config
# Show configuration file path
bake config --path
# Use custom configuration file
bake config --config /path/to/config.toml
# Format a single Makefile
bake format Makefile
# Format multiple files
bake format Makefile src/Makefile tests/*.mk
# Check if files need formatting (CI/CD mode)
bake format --check Makefile
# Show diff of changes without modifying files
bake format --diff Makefile
# Format with verbose output
bake format --verbose Makefile
# Create backup before formatting
bake format --backup Makefile
# Validate syntax after formatting
bake format --validate Makefile
# Use custom configuration
bake format --config /path/to/config.toml Makefile
# Validate single file
bake validate Makefile
# Validate multiple files
bake validate Makefile src/Makefile tests/*.mk
# Validate with verbose output
bake validate --verbose Makefile
# Use custom configuration
bake validate --config /path/to/config.toml Makefile
bake validate
: Checks if Makefile will execute correctly using GNUmake
(syntax validation)bake format --check
: Checks if Makefile follows formatting rules (style validation)
Both are useful! Use validate
for syntax errors, format --check
for style issues.
# Check current version and for updates
bake --version
# Check for updates only (without updating)
bake update --check
# Update to latest version
bake update
# Update with confirmation prompt bypass
bake update --yes
# Force update even if already up to date
bake update --force
# Install completion for current shell
bake --install-completion
# Show completion script (for manual installation)
bake --show-completion
mbake works with sensible defaults. Generate a configuration file with:
bake init
# Global settings
debug = false
verbose = false
# Error message formatting
gnu_error_format = true
wrap_error_messages = false
[formatter]
# Spacing settings - enable proper spacing
space_around_assignment = true
space_before_colon = false
space_after_colon = true
# Line continuation settings
normalize_line_continuations = true
max_line_length = 120
# PHONY settings
group_phony_declarations = false
phony_at_top = false
auto_insert_phony_declarations = false
# General settings - enable proper formatting
remove_trailing_whitespace = true
ensure_final_newline = true
normalize_empty_lines = true
max_consecutive_empty_lines = 2
fix_missing_recipe_tabs = true
# Conditional formatting settings (Default disabled)
indent_nested_conditionals = false
# Indentation settings
tab_width = 2
mbake includes intelligent .PHONY
detection that automatically identifies and manages phony targets.
Detection uses dynamic analysis of recipe commands rather than hardcoded target names:
- Command Analysis: Examines what each target's recipe actually does
- File Creation Detection: Identifies if commands create files with the target name
- Pattern Recognition: Understands compilation patterns, redirections, and common tools
# These are detected as phony because they manage containers, not files
up:
docker compose up -d
down:
docker compose down -v
logs:
docker compose logs -f
# These are detected as phony because they don't create files with their names
test:
npm test
lint:
eslint src/
deploy:
ssh user@server 'systemctl restart myapp'
# NOT phony - creates myapp.o file
myapp.o: myapp.c
gcc -c myapp.c -o myapp.o
# Phony - removes files, doesn't create "clean"
clean:
rm -f *.o myapp
Enable auto-insertion in your ~/.bake.toml
:
[formatter]
auto_insert_phony_declarations = true
Default (Conservative):
- Groups existing
.PHONY
declarations - No automatic insertion or enhancement
- Backwards compatible
Enhanced (auto_insert_phony_declarations = true):
- Automatically inserts
.PHONY
when missing - Enhances existing
.PHONY
with detected targets - Uses dynamic analysis for accurate detection
Input (no .PHONY
):
setup:
docker compose up -d
npm install
test:
npm test
clean:
docker compose down -v
rm -rf node_modules
Output (with auto-insertion enabled):
setup:
docker compose up -d
npm install
test:
npm test
clean:
docker compose down -v
rm -rf node_modules
Before:
# Inconsistent spacing and indentation
CC:=gcc
CFLAGS= -Wall -g
SOURCES=main.c \
utils.c \
helper.c
.PHONY: clean
all: $(TARGET)
$(CC) $(CFLAGS) -o $@ $^
.PHONY: install
clean:
rm -f *.o
After:
# Clean, consistent formatting
CC := gcc
CFLAGS = -Wall -g
SOURCES = main.c \
utils.c \
helper.c
.PHONY: clean
all: $(TARGET)
$(CC) $(CFLAGS) -o $@ $^
.PHONY: install
clean:
rm -f *.o
Before (with auto_insert_phony_declarations = true
):
# Docker development workflow
setup:
docker compose down -v
docker compose up -d
@echo "Services ready!"
build:
docker compose build --no-cache
test:
docker compose exec app npm test
clean:
docker compose down -v
docker system prune -af
After:
# Docker development workflow
.PHONY: clean setup test
setup:
docker compose down -v
docker compose up -d
@echo "Services ready!"
build:
docker compose build --no-cache
test:
docker compose exec app npm test
clean:
docker compose down -v
docker system prune -af
Disable formatting within a region using special comments that switch formatting in a delimited range.
Use # bake-format off
to disable formatting for the lines until the next #bake-format on
, which re-enables formatting.
# bake-format off
NO_FORMAT_1= \
1 \
45678 \
#bake-format on
# bake-format off : optional comment
NO_FORMAT_2= \
1 \
45678 \
#bake-format on
Use mbake in continuous integration:
# GitHub Actions example
- name: Check Makefile formatting
run: |
pip install mbake
bake format --check Makefile
Exit codes:
0
- No formatting needed or formatting successful1
- Files need formatting (--check mode) or validation failed2
- Error occurred
git clone https://github.com/ebodshojaei/bake.git
cd mbake
pip install -e ".[dev]"
# Run all tests
pytest
# Run with coverage
pytest --cov=bake --cov-report=html
# Run specific test file
pytest tests/test_formatter.py -v
# Format code
black bake tests
# Lint code
ruff check bake tests
# Type checking
mypy bake
mbake follows a modular, plugin-based architecture:
bake/
├── __init__.py # Package initialization
├── cli.py # Command-line interface with subcommands
├── config.py # Configuration management
├── core/
│ ├── formatter.py # Main formatting engine
│ └── rules/ # Individual formatting rules
│ ├── tabs.py # Tab/indentation handling
│ ├── spacing.py # Spacing normalization
│ ├── continuation.py # Line continuation formatting
│ └── phony.py # .PHONY declaration management
└── plugins/
└── base.py # Plugin interface
Extend the FormatterPlugin
base class:
from bake.plugins.base import FormatterPlugin, FormatResult
class MyCustomRule(FormatterPlugin):
def __init__(self):
super().__init__("my_rule", priority=50)
def format(self, lines: List[str], config: dict) -> FormatResult:
# Your formatting logic here
return FormatResult(
lines=modified_lines,
changed=True,
errors=[],
warnings=[]
)
Contributions are welcome! Read the Contributing Guide for details on development process, submitting pull requests, and reporting issues.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Make your changes
- Add tests for new functionality
- Run the test suite (
pytest
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Minimal changes: Only modify what needs to be fixed, preserve file structure
- Predictable behavior: Consistent formatting rules across all Makefiles
- Fast execution: Efficient processing of large Makefiles
- Reliable validation: Ensure formatted Makefiles have correct syntax
- Developer-friendly: Rich CLI with helpful error messages and progress indicators