Skip to content

Commit 0eabfe7

Browse files
authored
Merge pull request #2 from libplctag/prerelease
Adding code coverage.
2 parents 742f7a7 + 0164e2e commit 0eabfe7

File tree

9 files changed

+263
-32
lines changed

9 files changed

+263
-32
lines changed

.github/workflows/coverage.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: coverage
2+
3+
on:
4+
push:
5+
branches: [release]
6+
pull_request:
7+
8+
jobs:
9+
coverage:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Install dependencies
16+
run: |
17+
sudo apt-get update
18+
sudo apt-get install -y lcov cmake build-essential
19+
20+
- name: Configure
21+
run: |
22+
cmake -S . -B build -DENABLE_COVERAGE=ON
23+
24+
- name: Build
25+
run: cmake --build build
26+
27+
- name: Test
28+
run: ctest --test-dir build --output-on-failure
29+
30+
- name: Generate coverage report
31+
run: |
32+
lcov --capture --directory build --output-file coverage.info
33+
lcov --remove coverage.info '/usr/*' --output-file coverage.info
34+
genhtml coverage.info --output-directory docs/coverage --title "yafl Code Coverage"
35+
36+
- name: Generate coverage badge
37+
run: |
38+
chmod +x scripts/make_coverage_badge.sh
39+
scripts/make_coverage_badge.sh docs/coverage.svg coverage.info
40+
41+
- name: Create GitHub Pages configuration
42+
run: |
43+
touch docs/.nojekyll
44+
45+
- name: Commit and push coverage to repository
46+
if: github.event_name == 'push' && github.ref == 'refs/heads/release'
47+
run: |
48+
git config user.name "GitHub Actions"
49+
git config user.email "actions@github.com"
50+
git add -A docs/
51+
if git diff --quiet --cached; then
52+
echo "No coverage changes to commit"
53+
else
54+
git commit -m "Update code coverage report" && git push origin release
55+
fi

.github/workflows/release.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ on:
1414
- x86_64-unknown-linux-android
1515
- aarch64-apple-ios
1616
- arm-unknown-linux-gnueabihf
17-
- aarch64-unknown-linux-gnu
1817
- riscv64-unknown-linux-gnu
1918
- mipsel-unknown-linux-gnu
2019
- mips64el-unknown-linux-gnuabi64
@@ -53,7 +52,6 @@ jobs:
5352
"x86_64-unknown-linux-android",
5453
"aarch64-apple-ios",
5554
"arm-unknown-linux-gnueabihf",
56-
"aarch64-unknown-linux-gnu",
5755
"riscv64-unknown-linux-gnu",
5856
"mipsel-unknown-linux-gnu",
5957
"mips64el-unknown-linux-gnuabi64",

CMakeLists.txt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,7 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
6868

6969
option(ENABLE_COVERAGE "Enable code coverage generation" OFF)
7070
if(ENABLE_COVERAGE)
71-
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
72-
message(STATUS "Enabling code coverage")
73-
add_compile_options(--coverage)
74-
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
75-
endif()
71+
include(toolchains/coverage.cmake)
7672
endif()
7773

7874
# Note: Assembly language will be enabled after architecture detection

README.md

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,57 @@
11
# yafl - Portable Fiber/Coroutine Library
22

3-
![x86_64-pc-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-linux-gnu.yml/badge.svg)
4-
![aarch64-pc-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-linux-gnu.yml/badge.svg)
5-
![x86_64-apple-darwin](https://github.com/libplctag/yafl/actions/workflows/x86_64-apple-darwin.yml/badge.svg)
6-
![aarch64-apple-darwin](https://github.com/libplctag/yafl/actions/workflows/aarch64-apple-darwin.yml/badge.svg)
7-
![x86_64-pc-windows-msvc](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-windows-msvc.yml/badge.svg)
8-
![aarch64-pc-windows-msvc](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-windows-msvc.yml/badge.svg)
9-
![x86_64-pc-windows-gnu](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-windows-gnu.yml/badge.svg)
10-
![aarch64-pc-windows-gnu](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-windows-gnu.yml/badge.svg)
11-
![x86_64-unknown-linux-android](https://github.com/libplctag/yafl/actions/workflows/x86_64-unknown-linux-android.yml/badge.svg)
12-
![aarch64-apple-ios](https://github.com/libplctag/yafl/actions/workflows/aarch64-apple-ios.yml/badge.svg)
13-
![arm-unknown-linux-gnueabihf](https://github.com/libplctag/yafl/actions/workflows/arm-unknown-linux-gnueabihf.yml/badge.svg)
14-
![aarch64-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/aarch64-unknown-linux-gnu.yml/badge.svg)
15-
![riscv64-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/riscv64-unknown-linux-gnu.yml/badge.svg)
16-
![mipsel-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/mipsel-unknown-linux-gnu.yml/badge.svg)
17-
![mips64el-unknown-linux-gnuabi64](https://github.com/libplctag/yafl/actions/workflows/mips64el-unknown-linux-gnuabi64.yml/badge.svg)
18-
![powerpc64le-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/powerpc64le-unknown-linux-gnu.yml/badge.svg)
19-
![s390x-ibm-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/s390x-ibm-linux-gnu.yml/badge.svg)
20-
![sparc64-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/sparc64-unknown-linux-gnu.yml/badge.svg)
21-
![powerpc-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/powerpc-unknown-linux-gnu.yml/badge.svg)
22-
![i386-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/i386-unknown-linux-gnu.yml/badge.svg)
23-
[![codecov](https://codecov.io/gh/libplctag/yafl/graph/badge.svg)](https://codecov.io/gh/libplctag/yafl)
24-
25-
A portable, low-level C11 fiber/coroutine library derived from Boost.Context, designed to replace the deprecated `ucontext` API.
3+
## Build Status
4+
5+
| Platform | Status |
6+
| -------- | ------ |
7+
| x86_64-pc-linux-gnu | [![x86_64-pc-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-linux-gnu.yml) |
8+
| aarch64-pc-linux-gnu | [![aarch64-pc-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-linux-gnu.yml) |
9+
| x86_64-apple-darwin | [![x86_64-apple-darwin](https://github.com/libplctag/yafl/actions/workflows/x86_64-apple-darwin.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/x86_64-apple-darwin.yml) |
10+
| aarch64-apple-darwin | [![aarch64-apple-darwin](https://github.com/libplctag/yafl/actions/workflows/aarch64-apple-darwin.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/aarch64-apple-darwin.yml) |
11+
| x86_64-pc-windows-msvc | [![x86_64-pc-windows-msvc](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-windows-msvc.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-windows-msvc.yml) |
12+
| aarch64-pc-windows-msvc | [![aarch64-pc-windows-msvc](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-windows-msvc.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-windows-msvc.yml) |
13+
| x86_64-pc-windows-gnu | [![x86_64-pc-windows-gnu](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-windows-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/x86_64-pc-windows-gnu.yml) |
14+
| aarch64-pc-windows-gnu | [![aarch64-pc-windows-gnu](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-windows-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/aarch64-pc-windows-gnu.yml) |
15+
| x86_64-unknown-linux-android | [![x86_64-unknown-linux-android](https://github.com/libplctag/yafl/actions/workflows/x86_64-unknown-linux-android.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/x86_64-unknown-linux-android.yml) |
16+
| aarch64-apple-ios | [![aarch64-apple-ios](https://github.com/libplctag/yafl/actions/workflows/aarch64-apple-ios.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/aarch64-apple-ios.yml) |
17+
| arm-unknown-linux-gnueabihf | [![arm-unknown-linux-gnueabihf](https://github.com/libplctag/yafl/actions/workflows/arm-unknown-linux-gnueabihf.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/arm-unknown-linux-gnueabihf.yml) |
18+
| riscv64-unknown-linux-gnu | [![riscv64-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/riscv64-unknown-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/riscv64-unknown-linux-gnu.yml) |
19+
| mipsel-unknown-linux-gnu | [![mipsel-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/mipsel-unknown-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/mipsel-unknown-linux-gnu.yml) |
20+
| mips64el-unknown-linux-gnuabi64 | [![mips64el-unknown-linux-gnuabi64](https://github.com/libplctag/yafl/actions/workflows/mips64el-unknown-linux-gnuabi64.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/mips64el-unknown-linux-gnuabi64.yml) |
21+
| powerpc64le-unknown-linux-gnu | [![powerpc64le-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/powerpc64le-unknown-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/powerpc64le-unknown-linux-gnu.yml) |
22+
| s390x-ibm-linux-gnu | [![s390x-ibm-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/s390x-ibm-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/s390x-ibm-linux-gnu.yml) |
23+
| sparc64-unknown-linux-gnu | [![sparc64-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/sparc64-unknown-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/sparc64-unknown-linux-gnu.yml) |
24+
| powerpc-unknown-linux-gnu | [![powerpc-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/powerpc-unknown-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/powerpc-unknown-linux-gnu.yml) |
25+
| i386-unknown-linux-gnu | [![i386-unknown-linux-gnu](https://github.com/libplctag/yafl/actions/workflows/i386-unknown-linux-gnu.yml/badge.svg)](https://github.com/libplctag/yafl/actions/workflows/i386-unknown-linux-gnu.yml) |
26+
27+
[![Coverage](https://libplctag.github.io/libyafl/coverage/coverage.svg)](https://libplctag.github.io/libyafl/coverage/index.html)
28+
29+
A portable, low-level C11 fiber/coroutine library derived from Boost.Context.
30+
31+
## Why Another Fiber Library?
32+
33+
I wanted to use coroutines/fibers for another project and searched on GitHub and elsewhere and did not find anything that was:
34+
35+
- Portable across many OSes, architectures and compilers. Aiming for RTOS environments as well.
36+
- Every release tested across a matrix of architectures and operating systems and compilers.
37+
- Only C11 and assembly.
38+
- CMake build system or easy creation of CMake build configuration.
39+
- Minimal functionality. Just fibers.
40+
- Support for guard pages etc. on operating systems that support them and malloc on platforms that do not.
41+
- The ability to measure stack usage. This is critical for embedded projects.
42+
- No dependency on POSIX ucontext.
43+
- No other dependencies.
44+
- Appears to be maintained.
45+
46+
Boost.Context is very close but aimed at C++ and in one case seems to require C++ code. However, the project has large set of assembly for a wide variety of processor architectures. I decided it would make a good test of using an LLM to wrap/refactor some code.
47+
48+
The C code and documentation was mostly generated by Claude Code. The assembly is from the Boost.Context project and was written by humans. I have touched/edited almost every file at some point.
2649

2750
## Features
2851

2952
- **Pure C11 implementation** - No C++ dependencies
3053
- **Asymmetric coroutines (fibers)** - Simplified suspend/resume model
31-
- **Guard pages** - Memory-efficient mmap-based stack with automatic overflow detection
54+
- **Guard pages** - Memory-efficient mmap-based stack with automatic overflow detection (crashing your program!)
3255
- **Stack watermark checking** - Measure maximum stack usage
3356
- **16-byte stack alignment** - ABI-compliant on x86_64 and ARM64
3457
- **Page-aware allocation** - Handles 4KB (Linux/Windows) and 16KB (macOS ARM) pages

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.81.0-0
1+
0.1.0

docs/.gitkeep

Whitespace-only changes.

scripts/generate_coverage.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# Color codes for output
5+
RED='\033[0;31m'
6+
GREEN='\033[0;32m'
7+
YELLOW='\033[1;33m'
8+
NC='\033[0m' # No Color
9+
10+
# Script directory
11+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
13+
14+
echo -e "${YELLOW}=== YAFL Code Coverage Generator ===${NC}"
15+
echo
16+
17+
# Check for lcov
18+
if ! command -v lcov &> /dev/null; then
19+
echo -e "${RED}Error: lcov is not installed${NC}"
20+
echo "Install it with:"
21+
echo " macOS: brew install lcov"
22+
echo " Linux: sudo apt-get install lcov"
23+
exit 1
24+
fi
25+
26+
# Check for cmake
27+
if ! command -v cmake &> /dev/null; then
28+
echo -e "${RED}Error: cmake is not installed${NC}"
29+
exit 1
30+
fi
31+
32+
# Check for genhtml (comes with lcov)
33+
if ! command -v genhtml &> /dev/null; then
34+
echo -e "${RED}Error: genhtml is not installed${NC}"
35+
exit 1
36+
fi
37+
38+
cd "$PROJECT_DIR"
39+
40+
echo -e "${GREEN}Step 1: Configuring CMake with coverage enabled...${NC}"
41+
cmake -S . -B build -DENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug
42+
echo
43+
44+
echo -e "${GREEN}Step 2: Building project...${NC}"
45+
cmake --build build
46+
echo
47+
48+
echo -e "${GREEN}Step 3: Running tests...${NC}"
49+
ctest --test-dir build --output-on-failure
50+
echo
51+
52+
echo -e "${GREEN}Step 4: Capturing coverage data...${NC}"
53+
lcov --capture --directory build --output-file coverage.info
54+
echo
55+
56+
echo -e "${GREEN}Step 5: Filtering system files from coverage...${NC}"
57+
lcov --remove coverage.info '/usr/*' --output-file coverage.info
58+
echo
59+
60+
echo -e "${GREEN}Step 6: Generating HTML report...${NC}"
61+
genhtml coverage.info --output-directory docs/coverage --title "yafl Code Coverage"
62+
echo
63+
64+
echo -e "${GREEN}Step 7: Generating coverage badge...${NC}"
65+
"$SCRIPT_DIR/make_coverage_badge.sh" docs/coverage.svg coverage.info
66+
echo
67+
68+
echo -e "${GREEN}=== Coverage Generation Complete ===${NC}"
69+
echo
70+
echo -e "Coverage report: ${YELLOW}docs/coverage/index.html${NC}"
71+
echo -e "Coverage badge: ${YELLOW}docs/coverage.svg${NC}"
72+
echo
73+
echo "To view the report, open:"
74+
echo " file://$(cd "$PROJECT_DIR" && pwd)/docs/coverage/index.html"
75+
echo
76+
echo -e "${YELLOW}Note:${NC} Clean the build directory with:"
77+
echo " rm -rf build coverage.info docs/coverage docs/coverage.svg"

scripts/make_coverage_badge.sh

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# Usage: make_coverage_badge.sh <output_svg_path> [coverage_info_path]
5+
OUTPUT_SVG="${1:-coverage.svg}"
6+
COVERAGE_INFO="${2:-coverage.info}"
7+
8+
# Check if coverage.info exists
9+
if [ ! -f "$COVERAGE_INFO" ]; then
10+
echo "Error: Coverage file not found: $COVERAGE_INFO"
11+
exit 1
12+
fi
13+
14+
# Extract coverage percentage
15+
COVERAGE=$(lcov --summary "$COVERAGE_INFO" | grep "lines" | awk '{print $2}' | sed 's/%//')
16+
if [ -z "$COVERAGE" ]; then
17+
echo "Error: Could not extract coverage from $COVERAGE_INFO"
18+
exit 1
19+
fi
20+
21+
COVERAGE_INT=${COVERAGE%.*}
22+
23+
# Determine color based on coverage percentage
24+
if [ "$COVERAGE_INT" -ge 90 ]; then
25+
COLOR="brightgreen"
26+
elif [ "$COVERAGE_INT" -ge 75 ]; then
27+
COLOR="yellow"
28+
else
29+
COLOR="red"
30+
fi
31+
32+
# Ensure output directory exists
33+
OUTPUT_DIR=$(dirname "$OUTPUT_SVG")
34+
if [ "$OUTPUT_DIR" != "." ]; then
35+
mkdir -p "$OUTPUT_DIR"
36+
fi
37+
38+
# Generate SVG badge
39+
cat > "$OUTPUT_SVG" <<EOF
40+
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="20">
41+
<linearGradient id="b" x2="0" y2="100%">
42+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
43+
<stop offset="1" stop-opacity=".1"/>
44+
</linearGradient>
45+
<mask id="a">
46+
<rect width="120" height="20" rx="3" fill="#fff"/>
47+
</mask>
48+
<g mask="url(#a)">
49+
<rect width="70" height="20" fill="#555"/>
50+
<rect x="70" width="50" height="20" fill="${COLOR}"/>
51+
<rect width="120" height="20" fill="url(#b)"/>
52+
</g>
53+
<g fill="#fff" text-anchor="middle"
54+
font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
55+
font-size="11">
56+
<text x="35" y="14">coverage</text>
57+
<text x="95" y="14">${COVERAGE}%</text>
58+
</g>
59+
</svg>
60+
EOF
61+
62+
echo "Generated coverage badge: $OUTPUT_SVG ($COVERAGE%)"

toolchains/coverage.cmake

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
option(ENABLE_COVERAGE "Enable code coverage" OFF)
2+
3+
if(ENABLE_COVERAGE)
4+
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
5+
message(STATUS "Enabling GCC coverage")
6+
add_compile_options(-O0 -g --coverage)
7+
add_link_options(--coverage)
8+
9+
elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang")
10+
message(STATUS "Enabling Clang coverage with llvm-cov")
11+
# Use GCC-style coverage for lcov compatibility
12+
add_compile_options(-O0 -g --coverage)
13+
add_link_options(--coverage)
14+
15+
elseif(MSVC)
16+
message(WARNING "Coverage enabled, but MSVC coverage is not supported in CI")
17+
else()
18+
message(WARNING "Code coverage not configured for this compiler: ${CMAKE_C_COMPILER_ID}")
19+
endif()
20+
endif()

0 commit comments

Comments
 (0)