终于,一个Makefile格式化工具(晚了50年)
Mbake – A Makefile formatter and linter, that only took 50 years

原始链接: https://github.com/EbodShojaei/bake

`mbake` 是一款 Makefile 格式化工具,旨在提高一致性和效率。它通过 `~/.bake.toml` 文件使用可配置的规则集来强制执行样式指南,包括缩进、空格和 `.PHONY` 声明管理。它具有 CI/CD 集成和检查模式。 其主要功能包括智能 `.PHONY` 检测,该功能会动态分析配方以识别和管理虚目标。该格式化工具还支持自动插入缺失的 `.PHONY` 声明。它具有可扩展的插件架构。 `mbake` 提供用于格式化、验证、配置和更新的子命令。它优先考虑对文件的最小改动以保持文件结构,并具有快速执行和可靠验证的特点。 对于开发,插件架构允许自定义格式化规则。为开发者提供了贡献指南,以便他们可以 fork、分支、添加测试、提交更改、推送和打开拉取请求。

这个Hacker News帖子讨论了Mbake,一个用Python编写的新的Makefile格式化工具和lint工具。评论涵盖了从关于合并.PHONY目标的技术讨论,到关于Python是否适合此类工具的辩论。 一些用户质疑Python的性能和依赖管理,而另一些用户则为其庞大的社区和易于上手的特点进行辩护。讨论涉及Go、Rust和Perl等替代语言,一些人认为编译型语言由于安装和性能方面的考虑优于解释型语言。 几位评论者建议将Mbake打包成一个pre-commit钩子,并询问内联忽略规则。一些人强调Makefile旨在用于依赖管理,而不是任务运行,而另一些人则赞赏Make的普遍性和声明式特性。该帖子还介绍了Tup和checkmake等替代工具。总的来说,评论展示了对Makefile工具以及不同编程语言权衡的各种观点。
相关文章

原文
  • 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

  • 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

  1. Open VSCode
  2. Go to Extensions (Ctrl+Shift+X)
  3. Search for "mbake Makefile Formatter"
  4. 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]"

mbake uses a subcommand-based CLI. All commands support both bake and mbake aliases.

# Check version
bake --version

# Initialize configuration (optional)
bake init

# Format a Makefile
bake format Makefile

# Validate Makefile syntax
bake 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
# 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:

[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

# Global settings
debug = false
verbose = false

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

Build/Development Targets

# 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'

File vs Phony Target Detection

# 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):

.PHONY: clean setup test

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: all clean install

all: $(TARGET)
 $(CC) $(CFLAGS) -o $@ $^

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: build 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

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 successful
  • 1 - Files need formatting (--check mode) or validation failed
  • 2 - Error occurred

git clone https://github.com/ebodshojaei/bake.git
cd mbake
pip install -e ".[dev]"
pre-commit install
# 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.

Quick Start for Contributors

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for new functionality
  5. Run the test suite (pytest)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. 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
联系我们 contact @ memedata.com