Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,8 @@ jobs:
chmod +x "$wrapper"
CFBOX="$wrapper" bash tests/integration/run_all.sh

# ── QEMU system-mode emulation tests (main / release-* only) ─────
# ── QEMU system-mode emulation tests ─────
qemu-system-test:
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release-')
needs: cross-compile
runs-on: ubuntu-latest
strategy:
Expand Down
15 changes: 13 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,22 @@ include(cmake/third_party/CPM.cmake)
# ── Compiler flags ────────────────────────────────────────────
include(cmake/compile/CompilerFlag.cmake)

# ── Applet sources (shared between main exe and tests) ────────
file(GLOB_RECURSE CFBOX_APPLET_SOURCES CONFIGURE_DEPENDS src/applets/*.cpp)
# ── Applet configuration ─────────────────────────────────────
include(cmake/Config.cmake)

# ── Applet sources (conditional on config) ───────────────────
set(CFBOX_APPLET_SOURCES)
foreach(applet IN LISTS CFBOX_APPLETS)
string(TOUPPER "${applet}" APPLET_UPPER)
if(CFBOX_ENABLE_${APPLET_UPPER})
list(APPEND CFBOX_APPLET_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/applets/${applet}.cpp)
endif()
endforeach()

# ── Main executable ───────────────────────────────────────────
add_executable(cfbox src/main.cpp ${CFBOX_APPLET_SOURCES})
target_include_directories(cfbox PUBLIC include)
target_include_directories(cfbox PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include)
target_link_libraries(cfbox PRIVATE cfbox_compiler_flags)

# ── GTest via CPM (FetchContent) ──────────────────────────────
Expand All @@ -39,6 +49,7 @@ if(GTest_ADDED)
if(CFBOX_TEST_SOURCES)
add_executable(cfbox_tests ${CFBOX_TEST_SOURCES} ${CFBOX_APPLET_SOURCES})
target_include_directories(cfbox_tests PUBLIC include)
target_include_directories(cfbox_tests PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include)
target_link_libraries(cfbox_tests PRIVATE
cfbox_compiler_flags
GTest::gtest_main
Expand Down
38 changes: 31 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ Release builds use `-O2` by default and enable LTO. For size-optimized builds, a
## Running Tests

```bash
# Unit tests (108 GTest cases)
# Unit tests (149 GTest cases)
ctest --test-dir build --output-on-failure

# Integration tests (16 shell scripts comparing against GNU coreutils)
# Integration tests (17 shell scripts comparing against GNU coreutils)
bash tests/integration/run_all.sh
```

Expand Down Expand Up @@ -138,12 +138,36 @@ The CI pipeline ([ci.yml](.github/workflows/ci.yml)) runs on every push/PR to `m

1. Create `src/applets/<name>.cpp` with signature `auto <name>_main(int argc, char* argv[]) -> int`.
- Add a comment header listing supported flags and known differences from GNU.
2. Declare the function in [applets.hpp](include/cfbox/applets.hpp).
3. Add one entry to `APPLET_REGISTRY` in [applets.hpp](include/cfbox/applets.hpp).
4. Add GTest unit tests in `tests/unit/test_<name>.cpp` (see [test_capture.hpp](tests/unit/test_capture.hpp) for stdout capture and `TempDir` utilities).
5. Add shell integration tests in `tests/integration/test_<name>.sh` following the pattern in existing scripts.
- Add a `constexpr cfbox::help::HelpEntry HELP` constant in the anonymous namespace.
- Handle `--help` / `--version` right after `args::parse()`:
```cpp
if (parsed.has_long("help")) { cfbox::help::print_help(HELP); return 0; }
if (parsed.has_long("version")) { cfbox::help::print_version(HELP); return 0; }
```
2. Declare the function in [applets.hpp](include/cfbox/applets.hpp), guarded by `#if CFBOX_ENABLE_<UPPER>`.
3. Add one entry to `APPLET_REGISTRY` in [applets.hpp](include/cfbox/applets.hpp), also guarded by `#if CFBOX_ENABLE_<UPPER>`.
4. Add the applet name to the `CFBOX_APPLETS` list in [cmake/Config.cmake](cmake/Config.cmake).
5. Add a `#cmakedefine01 CFBOX_ENABLE_<UPPER>` line to [include/cfbox/applet_config.hpp.in](include/cfbox/applet_config.hpp.in).
6. Add GTest unit tests in `tests/unit/test_<name>.cpp` (see [test_capture.hpp](tests/unit/test_capture.hpp) for stdout capture and `TempDir` utilities). Guard the test file with `#if CFBOX_ENABLE_<UPPER>`.
7. Add shell integration tests in `tests/integration/test_<name>.sh` following the pattern in existing scripts.

> **Note:** The `init` applet is special — it runs as PID 1 in QEMU system-mode tests. Regular applets should not need special PID 1 handling.
> **Note:** The `init` applet is special — it runs as PID 1 in QEMU system-mode tests and uses manual `argv` scanning instead of `args::parse()`. Regular applets should not need special PID 1 handling.

## Build Configuration

CFBox supports per-applet configuration via CMake options:

```bash
# Disable individual applets
cmake -DCFBOX_ENABLE_GREP=OFF -DCFBOX_ENABLE_SED=OFF ..

# Use preset profiles
cmake -DCFBOX_PROFILE=minimal .. # Only core file operations
cmake -DCFBOX_PROFILE=embedded .. # Everything except optional text processing
cmake -DCFBOX_PROFILE=desktop .. # All applets enabled (default)
```

Available profiles: `minimal` (echo, cat, ls, cp, mv, rm, mkdir, grep), `embedded` (all except sort/uniq/sed), `desktop`/`full` (all enabled).

## Submitting Changes

Expand Down
52 changes: 30 additions & 22 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ A minimalist BusyBox alternative written in modern C++23.
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![C++23](https://img.shields.io/badge/C++23-00599C?logo=cplusplus)](https://en.cppreference.com/w/cpp/23)
[![CMake](https://img.shields.io/badge/CMake-3.26+-064F8C?logo=cmake)](https://cmake.org/)
[![Tests](https://img.shields.io/badge/Tests-124_passing-brightgreen)](tests/)
[![Tests](https://img.shields.io/badge/Tests-149_passing-brightgreen)](tests/)
[![Applets](https://img.shields.io/badge/Applets-17-brightgreen)](src/applets/)

## Overview

CFBox is a single-executable Unix utility collection distributed via symbolic links. All development is complete — 17 applets implemented and tested, with a CI pipeline covering native builds, cross-compilation, and QEMU user/system-mode testing across 5 stages.
CFBox is a single-executable Unix utility collection distributed via symbolic links. 17 applets implemented and tested, with a CI pipeline covering native builds, cross-compilation, and QEMU user/system-mode testing across 5 stages. Features configurable CMake builds (per-applet toggles), GNU-style long options, and colored help output.

**Design philosophy:** Simplicity first — Modern C++ (`std::expected`) — Embedded-friendly (cross-compilation, static linking)

Expand All @@ -25,8 +25,8 @@ cmake -B build
cmake --build build

# Test
ctest --test-dir build --output-on-failure # 108 GTest unit tests
bash tests/integration/run_all.sh # 16 integration test scripts
ctest --test-dir build --output-on-failure # 149 GTest unit tests
bash tests/integration/run_all.sh # 17 integration test scripts

# Run via subcommand
./build/cfbox echo "Hello, World!"
Expand All @@ -42,7 +42,7 @@ echo "Hello, World!" # now calls cfbox via symlink

| Applet | Supported Flags / Features |
|--------|----------------------------|
| `echo` | `-n` (no trailing newline), `-e` (interpret escape sequences) |
| `echo` | `-n` (no trailing newline), `-e` (interpret escape sequences), all applets support `--help` / `--version` |
| `printf` | Format strings (`%s` `%d` `%f` `%c` `%%`), format reuse |
| `cat` | `-n` (number lines), `-b` (number non-blank), `-A` (show non-printing), stdin passthrough |
| `head` | `-n N` (first N lines), `-c N` (first N bytes), multi-file headers |
Expand All @@ -57,16 +57,16 @@ echo "Hello, World!" # now calls cfbox via symlink

| Applet | Supported Flags / Features |
|--------|----------------------------|
| `mkdir` | `-p` (create parents), `-m MODE` (permissions) |
| `rm` | `-r` (recursive), `-f` (force), `-i` (interactive), `/` safety check |
| `cp` | `-r` (recursive), `-p` (preserve permissions), multi-file to directory |
| `mkdir` | `-p`/`--parents` (create parents), `-m`/`--mode MODE` (permissions) |
| `rm` | `-r`/`--recursive` (recursive), `-f`/`--force` (force), `-i` (interactive), `/` safety check |
| `cp` | `-r`/`--recursive` (recursive), `-p`/`--preserve` (preserve permissions), multi-file to directory |
| `mv` | `-f` (force overwrite), cross-filesystem fallback (copy + remove) |

### Directory & Search

| Applet | Supported Flags / Features |
|--------|----------------------------|
| `ls` | `-a` (show hidden), `-l` (long format), `-h` (human-readable sizes) |
| `ls` | `-a`/`--all` (show hidden), `-l`/`--long` (long format), `-h`/`--human-readable` (human-readable sizes) |
| `find` | `-name PATTERN` (glob), `-type [f\|d\|l]`, `-maxdepth N`, `-exec CMD {} ;` |

### System
Expand Down Expand Up @@ -97,22 +97,30 @@ echo "Hello, World!" # now calls cfbox via symlink
cfbox/
├── CMakeLists.txt
├── cmake/
│ ├── compile/CompilerFlag.cmake # Compiler warnings & optimization flags
│ ├── third_party/CPM.cmake # CPM dependency manager
│ └── toolchain/ # Cross-compilation toolchains
│ ├── Config.cmake # Per-applet configuration (CFBOX_ENABLE_xxx options)
│ ├── compile/CompilerFlag.cmake # Compiler warnings & optimization flags
│ ├── third_party/CPM.cmake # CPM dependency manager
│ └── toolchain/ # Cross-compilation toolchains
├── configs/
│ └── qemu-virt-aarch64.config # Minimal QEMU aarch64 kernel config
├── document/ # Detailed documentation
├── include/cfbox/ # Public headers
│ └── qemu-virt-aarch64.config # Minimal QEMU aarch64 kernel config
├── document/ # Detailed documentation
├── include/cfbox/
│ ├── applet_config.hpp.in # CMake-generated config (version + enable flags)
│ ├── applet.hpp / applets.hpp # Registry & dispatch
│ ├── args.hpp # Short + long option argument parser
│ ├── help.hpp # --help / --version help system
│ ├── term.hpp # ANSI colored output (NO_COLOR support)
│ ├── utf8.hpp # Unicode-aware width/count utilities
│ └── ... # error.hpp, io.hpp, fs_util.hpp, escape.hpp
├── src/
│ ├── main.cpp # Dispatch entry
│ └── applets/ # 17 command implementations
│ ├── main.cpp # Dispatch entry
│ └── applets/ # 17 command implementations
├── tests/
│ ├── unit/ # GTest unit tests (108 cases)
│ └── integration/ # Shell integration tests (16 scripts)
├── scripts/ # Build, test, install scripts
├── .github/workflows/ci.yml # CI pipeline
└── CONTRIBUTING.md # Contributing guide
│ ├── unit/ # GTest unit tests (149 cases)
│ └── integration/ # Shell integration tests (17 scripts)
├── scripts/ # Build, test, install scripts
├── .github/workflows/ci.yml # CI pipeline
└── CONTRIBUTING.md # Contributing guide
```

## Contributing
Expand Down
52 changes: 30 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![C++23](https://img.shields.io/badge/C++23-00599C?logo=cplusplus)](https://en.cppreference.com/w/cpp/23)
[![CMake](https://img.shields.io/badge/CMake-3.26+-064F8C?logo=cmake)](https://cmake.org/)
[![Tests](https://img.shields.io/badge/Tests-124_passing-brightgreen)](tests/)
[![Tests](https://img.shields.io/badge/Tests-149_passing-brightgreen)](tests/)
[![Applets](https://img.shields.io/badge/Applets-17-brightgreen)](src/applets/)

## 概述

CFBox 是一个单一可执行文件的 Unix 工具集,通过符号链接分发。全部开发已完成,17 个 applet 已实现并通过测试,CI 流水线覆盖原生构建、交叉编译、QEMU 用户/系统模式 5 种测试场景。
CFBox 是一个单一可执行文件的 Unix 工具集,通过符号链接分发。17 个 applet 已实现并通过测试,CI 流水线覆盖原生构建、交叉编译、QEMU 用户/系统模式 5 种测试场景。支持 CMake 配置化构建(per-applet 开关)、GNU 风格长选项、彩色帮助输出

**设计理念:** 简洁优先 — 现代C++(`std::expected`) — 嵌入式友好(交叉编译、静态链接)

Expand All @@ -25,8 +25,8 @@ cmake -B build
cmake --build build

# 测试
ctest --test-dir build --output-on-failure # 108 个 GTest 单元测试
bash tests/integration/run_all.sh # 16 套集成测试脚本
ctest --test-dir build --output-on-failure # 149 个 GTest 单元测试
bash tests/integration/run_all.sh # 17 套集成测试脚本

# 通过子命令运行
./build/cfbox echo "Hello, World!"
Expand All @@ -42,7 +42,7 @@ echo "Hello, World!" # 通过符号链接调用 cfbox

| 命令 | 支持的标志 / 功能 |
|------|-------------------|
| `echo` | `-n`(不换行),`-e`(解释转义序列) |
| `echo` | `-n`(不换行),`-e`(解释转义序列),所有 applet 支持 `--help` / `--version` |
| `printf` | 格式字符串(`%s` `%d` `%f` `%c` `%%`),格式重用 |
| `cat` | `-n`(显示行号),`-b`(非空行编号),`-A`(显示不可打印字符),stdin 透传 |
| `head` | `-n N`(前 N 行),`-c N`(前 N 字节),多文件头部 |
Expand All @@ -57,16 +57,16 @@ echo "Hello, World!" # 通过符号链接调用 cfbox

| 命令 | 支持的标志 / 功能 |
|------|-------------------|
| `mkdir` | `-p`(递归创建父目录),`-m MODE`(设置权限) |
| `rm` | `-r`(递归删除),`-f`(强制),`-i`(交互确认),`/` 安全检查 |
| `cp` | `-r`(递归复制),`-p`(保留权限),多文件到目录 |
| `mkdir` | `-p`/`--parents`(递归创建父目录),`-m`/`--mode MODE`(设置权限) |
| `rm` | `-r`/`--recursive`(递归删除),`-f`/`--force`(强制),`-i`(交互确认),`/` 安全检查 |
| `cp` | `-r`/`--recursive`(递归复制),`-p`/`--preserve`(保留权限),多文件到目录 |
| `mv` | `-f`(强制覆盖),跨文件系统回退(复制 + 删除) |

### 目录与搜索

| 命令 | 支持的标志 / 功能 |
|------|-------------------|
| `ls` | `-a`(显示隐藏文件),`-l`(长格式),`-h`(人类可读大小) |
| `ls` | `-a`/`--all`(显示隐藏文件),`-l`/`--long`(长格式),`-h`/`--human-readable`(人类可读大小) |
| `find` | `-name 模式`(glob 匹配),`-type [f\|d\|l]`,`-maxdepth N`,`-exec 命令 {} ;` |

### 系统
Expand Down Expand Up @@ -97,22 +97,30 @@ echo "Hello, World!" # 通过符号链接调用 cfbox
cfbox/
├── CMakeLists.txt
├── cmake/
│ ├── compile/CompilerFlag.cmake # 编译器警告与优化标志
│ ├── third_party/CPM.cmake # CPM 依赖管理
│ └── toolchain/ # 交叉编译工具链
│ ├── Config.cmake # Per-applet 配置(CFBOX_ENABLE_xxx 选项)
│ ├── compile/CompilerFlag.cmake # 编译器警告与优化标志
│ ├── third_party/CPM.cmake # CPM 依赖管理
│ └── toolchain/ # 交叉编译工具链
├── configs/
│ └── qemu-virt-aarch64.config # QEMU aarch64 最小内核配置
├── document/ # 详细文档
├── include/cfbox/ # 公共头文件
│ └── qemu-virt-aarch64.config # QEMU aarch64 最小内核配置
├── document/ # 详细文档
├── include/cfbox/
│ ├── applet_config.hpp.in # CMake 生成的配置(版本号 + 启用开关)
│ ├── applet.hpp / applets.hpp # 注册表与分发
│ ├── args.hpp # 短选项 + 长选项参数解析器
│ ├── help.hpp # --help / --version 帮助系统
│ ├── term.hpp # ANSI 彩色输出(NO_COLOR 支持)
│ ├── utf8.hpp # Unicode 感知的宽度/计数工具
│ └── ... # error.hpp, io.hpp, fs_util.hpp, escape.hpp
├── src/
│ ├── main.cpp # 分发入口
│ └── applets/ # 17 个命令实现
│ ├── main.cpp # 分发入口
│ └── applets/ # 17 个命令实现
├── tests/
│ ├── unit/ # GTest 单元测试(108 个用例)
│ └── integration/ # Shell 集成测试(16 个脚本)
├── scripts/ # 构建、测试、安装脚本
├── .github/workflows/ci.yml # CI 流水线
└── CONTRIBUTING.md # 贡献指南
│ ├── unit/ # GTest 单元测试(149 个用例)
│ └── integration/ # Shell 集成测试(17 个脚本)
├── scripts/ # 构建、测试、安装脚本
├── .github/workflows/ci.yml # CI 流水线
└── CONTRIBUTING.md # 贡献指南
```

## 贡献
Expand Down
Loading
Loading