- 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
[formatter]
# Indentation settings
use_tabs = true
tab_width = 4
# Spacing settings
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 = true
phony_at_top = true
auto_insert_phony_declarations = false
# General settings
remove_trailing_whitespace = true
ensure_final_newline = true
normalize_empty_lines = true
max_consecutive_empty_lines = 2
fix_missing_recipe_tabs = true
# Global settings
debug = false
verbose = false
# Error message formatting
gnu_error_format = true # Use GNU standard error format (file:line: Error: message)
wrap_error_messages = false # Wrap long error messages (can interfere with IDE parsing)
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