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
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
20 changes: 20 additions & 0 deletions .github/scripts/check-c.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Compile every production translation unit; warnings are CI failures.
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
compiler=${CC:-gcc}
flags=(-std=c23 -O1 -Wall -Wextra -Werror -Iinclude -Isrc -Ilib/include -Ilib/src)
case "${1:-warnings}" in
warnings) ;;
analyze) flags+=(-fanalyzer -fno-analyzer-state-merge) ;;
*) echo "Usage: bash .github/scripts/check-c.sh [warnings|analyze]" >&2; exit 2 ;;
esac
mkdir -p build/ci
object_dir=$(mktemp -d "$PWD/build/ci/check-c.XXXXXX")
trap 'rm -rf "$object_dir"' EXIT
mapfile -d '' sources < <(git ls-files -z -- 'src/*.c' 'lib/src/*.c')
(( ${#sources[@]} > 0 ))
for source in "${sources[@]}"; do
echo "Checking $source"
"$compiler" "${flags[@]}" -c "$source" -o "$object_dir/${source//\//_}.o"
done
13 changes: 13 additions & 0 deletions .github/scripts/configure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Configure the local build entry point for CI or an equivalent local run.
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
compiler_path=$(command -v "${CC:-gcc}")
case "$(basename "$compiler_path")" in
gcc*) backend=GCC ;;
clang*) backend=CLANG ;;
*) echo "CC must select GCC or Clang" >&2; exit 2 ;;
esac
sed -i '/^compiler=/d; /^compiler_path=/d; /^sanitize=/d' build.conf
printf '\ncompiler=%s\ncompiler_path=%s\nsanitize=1\n' "$backend" "$compiler_path" >> build.conf
"$compiler_path" --version
20 changes: 20 additions & 0 deletions .github/scripts/install-tools.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Pinned CLI releases, verified before executing; no GitHub secrets required.
set -euo pipefail
destination=${1:?Pass an installation directory}
mkdir -p "$destination"
destination=$(cd "$destination" && pwd)
download_dir=$(mktemp -d)
trap 'rm -rf "$download_dir"' EXIT
install_release() {
local url=$1 checksum=$2 executable=$3
curl --fail --location --silent --show-error --retry 3 "$url" -o "$download_dir/archive.tar.gz"
printf '%s %s\n' "$checksum" "$download_dir/archive.tar.gz" | sha256sum --check
tar -xzf "$download_dir/archive.tar.gz" -C "$destination" "$executable"
}
install_release \
https://github.com/rhysd/actionlint/releases/download/v1.7.7/actionlint_1.7.7_linux_amd64.tar.gz \
023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757 actionlint
install_release \
https://github.com/gitleaks/gitleaks/releases/download/v8.24.2/gitleaks_8.24.2_linux_x64.tar.gz \
fa0500f6b7e41d28791ebc680f5dd9899cd42b58629218a5f041efa899151a8e gitleaks
160 changes: 160 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
name: CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

defaults:
run:
shell: bash

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
ASAN_OPTIONS: detect_leaks=1:halt_on_error=1
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1

jobs:
build-test:
name: Build and test (${{ matrix.cc }})
runs-on: ubuntu-24.04
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
cc: [gcc-14, clang-19]
env:
CC: ${{ matrix.cc }}
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Install both supported toolchains
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y gcc-14 clang-19 llvm-19 libclang-rt-19-dev binutils pkg-config
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
sudo update-alternatives --set gcc /usr/bin/gcc-14
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 190
sudo update-alternatives --set clang /usr/bin/clang-19
- name: Verify compiler versions used by generated builds
run: |
test "$(gcc -dumpversion | cut -d. -f1)" = 14
test "$(clang -dumpversion | cut -d. -f1)" = 19
- name: Configure the selected compiler
run: bash .github/scripts/configure.sh
- name: Reject compiler warnings
run: bash .github/scripts/check-c.sh
- name: Bootstrap the real build
run: sh bootstrap.sh --no-test
- name: Run all unit and scenario tests with sanitizers
run: ./make test -j 2 2>&1 | tee build/ci-tests.log
- name: Build and run the example
working-directory: examples/demo
run: |
../../build/output/forge .
./make -j 2
./output/app | tee demo.log
grep -Fx 'hello from forge demo' demo.log
- name: Validate installation and Debian package
run: |
./make install "$RUNNER_TEMP/forge-install"
"$RUNNER_TEMP/forge-install/bin/forge" --version
./make gen_deb
dpkg-deb --info build/deb/forge_*.deb
dpkg-deb --contents build/deb/forge_*.deb
- name: Keep diagnostics after a failure
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: diagnostics-${{ matrix.cc }}
path: |
build/ci-tests.log
build/**/*.d
build/**/*.meta
test/edge_*/build.conf
test/edge_*/build.c
if-no-files-found: ignore
retention-days: 7

static-analysis:
name: GCC static analysis
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Install analyzer dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y gcc-14
- name: Check production code for unsafe memory and resource use
env:
CC: gcc-14
run: |
mkdir -p build
bash .github/scripts/check-c.sh analyze 2>&1 | tee build/ci-analyzer.log
- name: Keep analyzer diagnostics after a failure
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: analyzer-diagnostics
path: build/ci-analyzer.log
if-no-files-found: ignore
retention-days: 7

quality:
name: Workflow and secret checks
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
fetch-depth: 0
persist-credentials: false
- name: Install ShellCheck and pinned security tools
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y shellcheck
bash .github/scripts/install-tools.sh "$RUNNER_TEMP/ci-tools"
- name: Lint workflows and helper scripts
run: |
"$RUNNER_TEMP/ci-tools/actionlint" -color
shellcheck .github/scripts/*.sh
- name: Scan the complete Git history for credentials
run: '"$RUNNER_TEMP/ci-tools/gitleaks" git --redact --verbose'
- name: Reject whitespace errors in the proposed changes
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.sha }}
run: |
if test -n "$BASE_SHA" && git cat-file -e "$BASE_SHA^{commit}" 2>/dev/null; then
git diff --check "$BASE_SHA" "$HEAD_SHA"
else
git show --format= --check "$HEAD_SHA"
fi

ci:
name: CI
if: always()
needs: [build-test, static-analysis, quality]
runs-on: ubuntu-24.04
timeout-minutes: 2
steps:
- name: Require every check to pass
env:
BUILD_TEST: ${{ needs.build-test.result }}
STATIC_ANALYSIS: ${{ needs.static-analysis.result }}
QUALITY: ${{ needs.quality.result }}
run: |
test "$BUILD_TEST" = success
test "$STATIC_ANALYSIS" = success
test "$QUALITY" = success
51 changes: 51 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: CodeQL

on:
push:
branches: [main]
pull_request:
schedule:
- cron: '17 3 * * 1'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: true

jobs:
analyze:
name: CodeQL (C/C++)
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
security-events: write
actions: read
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y gcc-14 binutils
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
- name: Configure the build compiler
env:
CC: gcc
run: bash .github/scripts/configure.sh
- name: Initialize security and maintainability queries
uses: github/codeql-action/init@793b778da9c9275a0f276522b27cb5fc14bba279 # v4
with:
languages: c-cpp
build-mode: manual
queries: security-and-quality
- name: Build production code for analysis
run: sh bootstrap.sh --no-test
- name: Publish findings to code scanning and the pull request
uses: github/codeql-action/analyze@793b778da9c9275a0f276522b27cb5fc14bba279 # v4
with:
category: /language:c-cpp
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ itself.

## Quick Start

Pull requests run compiler, sanitizer, static-analysis and secret checks.
See [Continuous integration](docs/CI.md) for coverage and merge-protection setup.

```sh
./bootstrap.sh # bootstrap from zero: host forge + runtime library + full build + tests
./make # incremental build (re-running the ./make produced by "forge .")
Expand Down Expand Up @@ -225,4 +228,4 @@ requests.
- IDE project generation

These are not bugs - they are the current boundaries. Issues and
feature requests are welcome to help set priorities.
feature requests are welcome to help set priorities.
5 changes: 4 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

## 快速开始

PR 会自动执行编译、内存检查、静态分析和密钥扫描。
检查范围与分支保护设置见[持续集成说明](docs/CI.md)。

```sh
./bootstrap.sh # 从零自举:宿主 forge + 运行时库 + 全量构建 + 测试
./make # 增量构建(等同 forge . 生成的 make 再次运行)
Expand Down Expand Up @@ -172,4 +175,4 @@ Forge 当通用库嵌进无关应用--附加许可只针对构建脚本及其构
- 交叉编译矩阵
- IDE 工程生成

这些不是 bug,是当前边界。欢迎提 issue 讨论优先级。
这些不是 bug,是当前边界。欢迎提 issue 讨论优先级。
25 changes: 21 additions & 4 deletions build.c
Comment thread
minelogy-dev marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
/* Copyright (C) 2026 The Forge development team */
/* Additional permission under GPLv3+ §7 applies; see LICENSE. */

/* Keep targets and tests on the compiler selected in build.conf. */
#ifndef CONF_compiler
#define CONF_compiler GCC
#endif
#ifndef CONF_sanitize
#define CONF_sanitize 0
#endif
#define TOOLCHAIN set_toolchain(CONF_compiler)
#define LANGUAGE \
do { \
set_language(C); \
set_standard("c23"); \
add_define("_DEFAULT_SOURCE", "1"); \
if (CONF_sanitize) \
add(TARGET_OPTIONS, 4, "-g", "-fsanitize=address,undefined", \
"-fno-sanitize-recover=all", "-fno-omit-frame-pointer"); \
} while (0)
#include <build.h>

/* The version has a single source of truth: the version key in
Expand All @@ -24,7 +41,7 @@
function(build) {
parse_args(builtin_args);
target("forge") {
set_toolchain(GCC);
set_toolchain(CONF_compiler);
add_sources("src/forge.c");
add_sources("src/cli.c");
add_sources("src/parser.c");
Expand All @@ -45,7 +62,7 @@ add_define("FORGE_VERSION", "\"" FORGE_VERSION "\"");
return -1;
}
target("main") {
set_toolchain(GCC);
set_toolchain(CONF_compiler);
set_type(FORGE_STATIC_LIB);
/* A static library must NOT carry -flto: its objects are linked by
whichever compiler builds the consumer (e.g. clang, whose driver
Expand All @@ -63,7 +80,7 @@ add_define("FORGE_VERSION", "\"" FORGE_VERSION "\"");
return -1;
}
target("forge") {
set_toolchain(GCC);
set_toolchain(CONF_compiler);
set_type(FORGE_SHARED_LIB);
add_sources_r("lib/src");
add_include_path("lib/include");
Expand Down Expand Up @@ -377,4 +394,4 @@ function(gen_deb) {
fprintf(stderr, "forge: gen_deb is only supported on Linux\n");
return -1;
#endif
}
}
Loading
Loading