CI: Major refactor, expanded coverage, and ~80% speedup (#2736)

* netbsd fail on error

- Group each BSD test in its own subshell. Without this, if an error
occurs, it exits, but we don't know "where" it failed
- added *usesh: true* to BSD for faster copying into the virtualbox VM

- Some test_suite_runners wheren't correctly printing. added
--no-terminal
- For testing I've changed *build-msvc* from Debug to RelWithDebInfo

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* separate runs

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* fix typo

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Split msvc-debug into separate workflow

Run msvc-debug only on maintainer pushes, keep CI fast and still catches
errors.

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Fix build and test errors on NetBSD

* Set proper sizes for various types used for threads
* Add num_cpu function
* Add sysctl constants
* Allow testproject to build on NetBSD
* Tweaks to the linker code so that it adds correct flags and finds dynamic linker on NetBSD

* bsd: force system linker test

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* system linker on openbsd

remove lld from netbsd

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* use cc linker in openbsd, and re-add lld for netbsd

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* openbsd: fixes

- implement num_cpu() for OPENBSD
- testproject: add openbsd
- linker.c: fix for openbsd
- bsd refactor main.yml and more fixes

* openbsd: fix for unit tests and test_suite_runner

openbsd now passes all tests except the 'dynlib-test' which yields
'relocation error' when running `cc test.c -L. -ladd -Wl,-rpath=.`

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* openbsd: guard/disable unit test test_ct_intlog2()

* build-msys2-clang: add all tests that build-msvc has

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Fix Windows import library generation by adding /IMPLIB support and properly declaring static_lib_name()

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* build-msys2-mingw: add all tests that build-msvc has and enable it

configure CMake to link LLVM and LLD statically

* Revert "Revert windows"

This reverts commit 197f82d829.

* win-llvm 21.1.8

uses the RelWithDebInfo+Assertions

* refactor(CI): Centralize test logic and fix Docker execution

This commit refactors the GitHub Actions workflow to improve
maintainability and correct a flaw in the Docker build.

- Ensures Docker based tests run *inside* the generated container, not
on the host. This also fixes environment isolation and user permission
errors.
- Centralizes all common test steps (examples, libs, unit tests, etc)
into a single, auto-detecting Bash script: `scripts/tools/ci_tests.sh`.
- Centralize all release artifact packaging into a single
`scripts/tools/package_build.sh`
- fixes using correct paths for test_suite_runner when running on
windows under bash.

- This significantly reduces YAML duplication, making `main.yml` more
readable and easier to maintain.

This one supersedes pull/2677, and includes the fixes for:

- fix openbsd and netbsd
- added win-llvm 21.1.8 and fixed CMakeLists.txt
- added full unit-tests and suite for build-msys2-mingw and
build-msys2-clang

* macos: add/fix build for llvm (19,20,21)

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* ci: run tests in temp dir to avoid workspace pollution

Executes ci_tests.sh in a disposable temporary directory by copying
resources/ and test/ folders, preventing build artifacts from cluttering
the source tree.

This makes it easy to run `scripts/tools/ci_tests.sh` locally on any
platform.

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* add ilammy/msvc-dev-cmd@ and Ninja

we can simplify the windows runner and run everything in bash

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* windows: drain subprocess stderr before join to avoid test_suite_runner hang

* move last test to `ci_tests.sh`

thus completing the centralization of tests in `ci_tests.sh`

* Fix Windows CI cache

Updates the cache key to include hashes of CMakeLists.txt and the
workflow file.

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* `package_build.sh` runs in temp dir to avoid workspace pollution

- add cleanup

* nix: use `ci_tests.sh`

* remove annoying build-mac warnings

This now checks if package already installed trying to prevent
annoying warnings in the github ci:

Fixes:
```
build-mac (Release, 19)curl 8.17.0 is already installed and up-to-date.
To reinstall 8.17.0, run: brew reinstall curl
```

* refactor test_suite_runner

- less allocations, cleaner code, more maintainable.
- now can be killed and the temp dirs are correctly cleaned up, and the
output is correct.

* main.yml: bsd: change to using rsync, is twice as fast.

* default to `--wincrt=dynamic` when `--sanitize=address` on windows

Default to dynamic, as static ASan is removed in LLVM 21+ for Windows
https://github.com/llvm/llvm-project/pull/107899

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Formatting (indent with tab, align with space). Fix K&R braces. Style: keep all cases multiline if one is.

---------

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
Co-authored-by: Alex Garrett <limit.ordinal17@gmail.com>
This commit is contained in:
Manu Linares
2026-01-13 21:16:06 -03:00
committed by GitHub
parent 15fc435d92
commit 04eb6fc451
20 changed files with 1516 additions and 1649 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.20)
set(C3_LLVM_MIN_VERSION 17)
set(C3_LLVM_MAX_VERSION 22)
set(C3_LLVM_DEFAULT_VERSION 19)
set(C3_LLVM_DEFAULT_VERSION 21)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
message(FATAL_ERROR "In-tree build detected, please build in a separate directory")
@@ -56,12 +56,30 @@ set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Use /MT or /MTd
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
if(MSVC)
message(STATUS "MSVC version ${MSVC_VERSION}")
add_compile_options(/utf-8)
if(C3_WITH_LLVM)
FetchContent_GetProperties(LLVM_Windows)
if(NOT LLVM_Windows_URL MATCHES "msvcrt")
set(MSVC_CRT_SUFFIX "")
message(STATUS "Detected STATIC LLVM (libcmt)")
else()
set(MSVC_CRT_SUFFIX "DLL")
message(STATUS "Detected DYNAMIC LLVM (msvcrt)")
endif()
endif()
# Force the Runtime to Release (/MT or /MD) even in Debug
# This is required to match our RelWithDebInfo LLVM builds
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
set_property(GLOBAL PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
add_compile_definitions(_ITERATOR_DEBUG_LEVEL=0)
# Suppresses the LNK4098 mismatch warning in Debug builds
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libcmtd /NODEFAULTLIB:msvcrtd")
else()
add_compile_options(-gdwarf-3 -fno-exceptions)
@@ -77,6 +95,7 @@ set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
set(C3_USE_TB OFF CACHE BOOL "Use TB")
set(C3_LLD_DIR "" CACHE STRING "Use custom LLD directory")
set(C3_LLD_INCLUDE_DIR "" CACHE STRING "Use custom LLD include directory")
set(C3_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
set(TCC_LIB_PATH "/usr/lib/tcc/libtcc1.a" CACHE STRING "Use custom libtcc1.a path")
@@ -142,11 +161,11 @@ if(C3_WITH_LLVM)
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_21_1_8/llvm-21.1.8-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt-dbg.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_21_1_8/llvm-21.1.8-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
@@ -273,7 +292,12 @@ if(C3_WITH_LLVM)
else()
message(STATUS "Looking for shared lld libraries in ${LLVM_LIBRARY_DIRS}")
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
#find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
if(UNIX AND NOT WIN32)
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} REQUIRED)
else()
find_library(LLVM NAMES libLLVM.a LLVM.lib PATHS ${LLVM_LIBRARY_DIRS} REQUIRED)
endif()
set(llvm_libs ${LLVM})
# These don't seem to be reliable on windows.
@@ -435,10 +459,18 @@ if(C3_WITH_LLVM)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
if (MSVC)
# Use the same detected CRT for the wrapper
set_target_properties(c3c_wrappers PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
set_target_properties(miniz PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
target_compile_options(c3c PRIVATE
"$<$<CONFIG:Debug>:/EHa>"
"$<$<CONFIG:Release>:/EHsc>")
endif()
if(C3_LLD_INCLUDE_DIR)
target_include_directories(c3c_wrappers PRIVATE ${C3_LLD_INCLUDE_DIR})
endif()
else()
target_sources(c3c PRIVATE src/utils/hostinfo.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
@@ -552,13 +584,21 @@ if(MSVC)
endif()
if(C3_WITH_LLVM)
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
# The the sanitizer libs are in the folder "lib/clang/21/lib/windows/" but use the find anyway
file(GLOB_RECURSE FOUND_ASAN_LIB "${llvm_dir}/*clang_rt.asan_dynamic-x86_64.lib")
if(FOUND_ASAN_LIB)
list(GET FOUND_ASAN_LIB 0 _asan_path)
get_filename_component(_asan_dir "${_asan_path}" DIRECTORY)
set(sanitizer_runtime_libraries
${clang_lib_dir}/clang_rt.asan-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
${_asan_dir}/clang_rt.asan_dynamic-x86_64.lib
${_asan_dir}/clang_rt.asan_dynamic-x86_64.dll
${_asan_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
message(STATUS "Found Sanitizer binaries at: ${_asan_dir}")
else()
message(WARNING "Could not find sanitizer runtime libraries in ${llvm_dir}")
endif()
endif()
else()
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)

View File

@@ -27,7 +27,8 @@ const CUInt SA_RESTART = env::LINUX ? 0x10000000 : 0x0002;
const CUInt SA_RESETHAND = env::LINUX ? 0x80000000 : 0x0004;
const CUInt SA_SIGINFO = env::LINUX ? 0x00000004 : 0x0040;
alias Sigset_t @if(env::DARWIN || env::BSD_FAMILY) = uint;
alias Sigset_t @if((env::DARWIN || env::BSD_FAMILY) && !env::NETBSD) = uint;
alias Sigset_t @if(env::NETBSD) = uint[4];
alias Sigset_t @if(env::LINUX) = ulong[16];
alias SigActionFunction = fn void(CInt, void*, void*);
@@ -65,7 +66,8 @@ struct Stack_t
extern fn CInt sigaltstack(Stack_t* ss, Stack_t* old_ss);
extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction) @if(!env::NETBSD);
extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction) @cname("__sigaction_siginfo") @if(env::NETBSD);
extern fn CInt sigemptyset(Sigset_t* set);
extern fn CInt sigemptyset(Sigset_t* set) @if(!env::NETBSD);
extern fn CInt sigemptyset(Sigset_t* set) @cname("__sigemptyset14") @if(env::NETBSD);
module libc::termios @if(env::LIBC &&& env::POSIX);

View File

@@ -32,3 +32,40 @@ fn uint num_cpu()
win32::getSystemInfo(&info);
return info.dwNumberOfProcessors;
}
module std::os @if(env::NETBSD);
import std::os::netbsd;
import libc;
fn uint num_cpu()
{
int[2] nm;
usz len = 4;
uint count;
nm = { netbsd::CTL_HW, netbsd::HW_NCPU };
libc::sysctl(&nm, 2, &count, &len, null, 0);
if (count < 1) count = 1;
return count;
}
module std::os @if(env::OPENBSD);
import std::os::openbsd;
import libc;
fn uint num_cpu()
{
int[2] nm;
usz len = 4;
uint count;
nm = { openbsd::CTL_HW, openbsd::HW_NCPUONLINE };
if (libc::sysctl(&nm, 2, &count, &len, null, 0) == 0 && count >= 1) {
return count;
}
nm = { openbsd::CTL_HW, openbsd::HW_NCPU };
libc::sysctl(&nm, 2, &count, &len, null, 0);
if (count < 1) count = 1;
return count;
}

View File

@@ -1 +1,33 @@
module std::os::netbsd @if(env::NETBSD);
const CTL_UNSPEC = 0; // unused
const CTL_KERN = 1; // "high kernel": proc, limits
const CTL_VM = 2; // virtual memory
const CTL_VFS = 3; // file system, mount type is next
const CTL_NET = 4; // network, see socket.h
const CTL_DEBUG = 5; // debugging parameters
const CTL_HW = 6; // generic CPU/io
const CTL_MACHDEP = 7; // machine dependent
const CTL_USER = 8; // user-level
const CTL_DDB = 9; // in-kernel debugger
const CTL_PROC = 10; // per-proc attr
const CTL_VENDOR = 11; // vendor-specific data
const CTL_EMUL = 12; // emulation-specific data
const CTL_SECURITY = 13; // security
const HW_MACHINE = 1; // string: machine class
const HW_MODEL = 2; // string: specific machine model
const HW_NCPU = 3; // int: number of cpus
const HW_BYTEORDER = 4; // int: machine byte order
const HW_PHYSMEM = 5; // int: total memory (bytes)
const HW_USERMEM = 6; // int: non-kernel memory (bytes)
const HW_PAGESIZE = 7; // int: software page size
const HW_DISKNAMES = 8; // string: disk drive names
const HW_IOSTATS = 9; // struct: iostats[]
const HW_MACHINE_ARCH = 10; // string: machine architecture
const HW_ALIGNBYTES = 11; // int: ALIGNBYTES for the kernel
const HW_CNMAGIC = 12; // string: console magic sequence(s)
const HW_PHYSMEM64 = 13; // quad: total memory (bytes)
const HW_USERMEM64 = 14; // quad: non-kernel memory (bytes)
const HW_IOSTATNAMES = 15; // string: iostat names
const HW_NCPUONLINE = 16; // number CPUs online

View File

@@ -88,7 +88,7 @@ extern fn void pthread_cleanup_push(PosixThreadFn routine, void* routine_arg);
extern fn int sched_yield();
module std::thread::os @if(env::POSIX && !env::LINUX);
module std::thread::os @if(env::POSIX && !env::LINUX && !env::NETBSD);
typedef Pthread_attr_t = ulong[8];
typedef Pthread_cond_t = ulong[6];
typedef Pthread_condattr_t = ulong[8];
@@ -111,3 +111,18 @@ typedef Pthread_once_t = int;
typedef Pthread_rwlock_t = ulong[7]; // 32 on 3bit
typedef Pthread_rwlockattr_t = uint;
typedef Pthread_sched_param = uint;
module std::thread::os @if(env::NETBSD);
typedef Pthread_attr_t = ulong[2];
typedef Pthread_cond_t = ulong[5];
typedef Pthread_condattr_t = ulong[2];
typedef Pthread_key_t = uint;
typedef Pthread_mutexattr_t = ulong[2];
typedef Pthread_rwlock_t = ulong[8];
typedef Pthread_rwlockattr_t = ulong[2];
typedef Pthread_sched_param = int;
typedef Pthread_mutex_t @if(env::X86_64)= ulong[6];
typedef Pthread_once_t @if(env::X86_64) = ulong[7];
typedef Pthread_mutex_t @if(!env::X86_64) = ulong[5];
typedef Pthread_once_t @if(!env::X86_64) = ulong[6];

View File

@@ -181,7 +181,12 @@ fn void? NativeConditionVariable.wait_until(&cond, NativeMutex* mtx, Time time)
case errno::OK:
return;
default:
$if(env::OPENBSD):
// TODO: Investigate why this doesn't work correctly on openbsd.
return thread::WAIT_TIMEOUT?;
$else
abort("pthread_cond_timedwait failed, invalid value");
$endif
}
}

View File

@@ -39,13 +39,26 @@ in llvmPackages.stdenv.mkDerivation (_:
local FILE_NAMES="$(find src -type f)"
substituteInPlace $FILE_NAMES --replace-quiet "__DATE__" "\"$(date '+%b %d %Y')\""
substituteInPlace $FILE_NAMES --replace-quiet "__TIME__" "\"$(date '+%T')\""
patchShebangs scripts/tools/ci_tests.sh
# Skip library tests (dynlib/staticlib).
substituteInPlace scripts/tools/ci_tests.sh \
--replace-fail "run_dynlib_tests() {" "run_dynlib_tests() { return 0;" \
--replace-fail "run_staticlib_tests() {" "run_staticlib_tests() { return 0;"
# Remove '--linker=builtin' from run_testproject so it uses the working system linker.
substituteInPlace scripts/tools/ci_tests.sh \
--replace-fail 'ARGS="$ARGS --linker=builtin"' 'ARGS="$ARGS"'
'';
cmakeBuildType = if debug then "Debug" else "Release";
# Only set LLVM_CRT_LIBRARY_DIR for Darwin.
cmakeFlags = [
"-DC3_ENABLE_CLANGD_LSP=${if debug then "ON" else "OFF"}"
"-DC3_LLD_DIR=${llvmPackages.lld.lib}/lib"
] ++ lib.optionals llvmPackages.stdenv.hostPlatform.isDarwin [
"-DLLVM_CRT_LIBRARY_DIR=${llvmPackages.compiler-rt}/lib/darwin"
];
@@ -74,11 +87,8 @@ in llvmPackages.stdenv.mkDerivation (_:
runHook preCheck
local BUILD_DIR=$(pwd)
cd ../resources/testproject
../../build/c3c build --trust=full
cd ../../test
../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c ./test_suite
export SKIP_NETWORK_TESTS=1
../scripts/tools/ci_tests.sh $(pwd)/c3c
cd $BUILD_DIR
runHook postCheck

View File

@@ -1,21 +1,15 @@
module game_of_life;
import std::io;
import libc;
extern fn void usleep(int time);
import std::io, std::thread, std::math;
struct GameBoard
{
int h;
int w;
int h, w;
char* world;
char* temp;
}
fn void GameBoard.show(GameBoard *board)
{
io::printf("\e[H");
char* current = board.world;
for (int y = 0; y < board.h; y++)
@@ -27,7 +21,7 @@ fn void GameBoard.show(GameBoard *board)
}
io::printf("\e[E");
}
libc::fflush(libc::stdout());
(void)io::stdout().flush();
}
fn void GameBoard.evolve(GameBoard *board)
@@ -41,9 +35,9 @@ fn void GameBoard.evolve(GameBoard *board)
{
for (int x1 = x - 1; x1 <= x + 1; x1++)
{
int actualX = (x1 + board.w) % board.w;
int actualY = (y1 + board.h) % board.h;
if (board.world[actualX + actualY * board.w]) n++;
int actual_x = (x1 + board.w) % board.w;
int actual_y = (y1 + board.h) % board.h;
if (board.world[actual_x + actual_y * board.w]) n++;
}
}
if (board.world[x + y * board.w]) n--;
@@ -56,31 +50,24 @@ fn void GameBoard.evolve(GameBoard *board)
}
}
fn int main(int c, char** v)
fn int main(String[] v)
{
int w = 0;
int h = 0;
if (c > 1) w = libc::atoi(v[1]);
if (c > 2) h = libc::atoi(v[2]);
if (w <= 0) w = 30;
if (h <= 0) h = 30;
int w = 30;
int h = 30;
if (v.len > 1) w = v[1].to_int() ?? w;
if (v.len > 2) h = v[2].to_int() ?? h;
GameBoard board;
board.w = w;
board.h = h;
board.world = malloc((ulong)(h * w));
board.temp = malloc((ulong)(h * w));
GameBoard board = { .w = w, .h = h, .world = malloc((usz)h * w), .temp = malloc((usz)h * w) };
for (int i = h * w - 1; i >= 0; i--)
{
board.world[i] = libc::rand() % 10 == 0 ? 1 : 0;
board.world[i] = rand(10) == 0 ? 1 : 0;
}
for (int j = 0; j < 1000; j++)
{
board.show();
board.evolve();
usleep(200000);
thread::sleep_ms(200);
}
return 1;
}

View File

@@ -7,6 +7,9 @@
"targets": {
"macos-x64": {},
"macos-aarch64": {},
"netbsd-x64": {},
"netbsd-aarch64": {},
"openbsd-x64": {},
"linux-x64": {
"cflags": "-fPIE"
},

View File

@@ -6,6 +6,9 @@
"targets": {
"macos-x64": {},
"macos-aarch64": {},
"netbsd-x64": {},
"netbsd-aarch64": {},
"openbsd-x64": {},
"linux-x64": {
"cflags-override": "-fPIE"
},

223
scripts/tools/ci_tests.sh Executable file
View File

@@ -0,0 +1,223 @@
#!/usr/bin/env bash
# Usage: ./ci_tests.sh <path_to_c3c_binary> [optional_os_mode_override]
if [ $# -lt 1 ]; then
echo "Usage: ./ci_tests.sh <path_to_c3c_binary> [os_mode]"
exit 1
fi
set -ex
# --- Setup Paths & Environment ---
# Resolve Script and Real Root Directory
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REAL_ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
C3C_BIN="$(cygpath -m "$(realpath "$1")")"
else
C3C_BIN="$(realpath "$1")"
fi
# Detect OS
SYSTEM_NAME="$(uname -s)"
if [ -n "$2" ]; then
OS_MODE="$2"
else
case "$SYSTEM_NAME" in
CYGWIN*|MINGW*|MSYS*) OS_MODE="windows" ;;
Darwin*) OS_MODE="mac" ;;
Linux*) OS_MODE="linux" ;;
*BSD) OS_MODE="bsd" ;;
*) OS_MODE="linux" ;;
esac
fi
echo ">>> Running CI Tests using C3C at: $C3C_BIN"
echo ">>> OS Mode: $OS_MODE (Detected System: $SYSTEM_NAME)"
# --- Create Disposable Workspace ---
# Create temp directory
WORK_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'c3_ci_tests')
echo ">>> Setting up workspace in: $WORK_DIR"
cleanup() {
echo ">>> Cleaning up..."
cd "$REAL_ROOT_DIR" || cd ..
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
# Copy necessary test data to the temp directory
cp -r "$REAL_ROOT_DIR/resources" "$WORK_DIR/resources"
cp -r "$REAL_ROOT_DIR/test" "$WORK_DIR/test"
# ROOT_DIR points to the temp workspace.
ROOT_DIR="$WORK_DIR"
# Move to the temp resources dir to match original script behavior
cd "$ROOT_DIR/resources"
# --- Tests ---
run_examples() {
echo "--- Running Standard Examples ---"
"$C3C_BIN" compile examples/base64.c3
"$C3C_BIN" compile examples/binarydigits.c3
"$C3C_BIN" compile examples/brainfk.c3
"$C3C_BIN" compile examples/factorial_macro.c3
"$C3C_BIN" compile examples/fasta.c3
"$C3C_BIN" compile examples/gameoflife.c3
"$C3C_BIN" compile examples/hash.c3
"$C3C_BIN" compile-only examples/levenshtein.c3
"$C3C_BIN" compile examples/load_world.c3
"$C3C_BIN" compile-only examples/map.c3
"$C3C_BIN" compile examples/mandelbrot.c3
"$C3C_BIN" compile examples/plus_minus.c3
"$C3C_BIN" compile examples/nbodies.c3
"$C3C_BIN" compile examples/spectralnorm.c3
"$C3C_BIN" compile examples/swap.c3
"$C3C_BIN" compile examples/contextfree/boolerr.c3
"$C3C_BIN" compile examples/contextfree/dynscope.c3
"$C3C_BIN" compile examples/contextfree/guess_number.c3
"$C3C_BIN" compile examples/contextfree/multi.c3
"$C3C_BIN" compile examples/contextfree/cleanup.c3
"$C3C_BIN" compile-run examples/hello_world_many.c3
"$C3C_BIN" compile-run examples/time.c3
"$C3C_BIN" compile-run examples/fannkuch-redux.c3
"$C3C_BIN" compile-run examples/contextfree/boolerr.c3
"$C3C_BIN" compile-run examples/load_world.c3
"$C3C_BIN" compile-run examples/process.c3
"$C3C_BIN" compile-run examples/ls.c3
"$C3C_BIN" compile-run examples/args.c3 -- foo -bar "baz baz"
if [[ "$OS_MODE" == "linux" ]]; then
"$C3C_BIN" compile-run --linker=builtin linux_stack.c3 || echo "Warning: linux_stack builtin linker skipped"
"$C3C_BIN" compile-run linux_stack.c3
fi
"$C3C_BIN" compile --no-entry --test -g --threads 1 --target macos-x64 examples/constants.c3
}
run_cli_tests() {
echo "--- Running CLI Tests (init/vendor) ---"
# Test init
"$C3C_BIN" init-lib mylib
"$C3C_BIN" init myproject
rm -rf mylib.c3l myproject
# Test vendor-fetch
if [ -n "$SKIP_NETWORK_TESTS" ]; then
echo "Skipping vendor-fetch (network tests disabled)"
else
echo "Testing vendor-fetch..."
cd "$ROOT_DIR/resources"
"$C3C_BIN" vendor-fetch raylib
if [ -f "/etc/alpine-release" ] || [[ "$SYSTEM_NAME" == "OpenBSD" ]] || [[ "$SYSTEM_NAME" == "NetBSD" ]]; then
echo "Skipping raylib_arkanoid (vendor raylib doesn't support this platform)"
return
fi
"$C3C_BIN" compile --lib raylib --print-linking examples/raylib/raylib_arkanoid.c3
fi
}
run_dynlib_tests() {
echo "--- Running Dynamic Lib Tests ---"
# Skip openbsd, idk
if [[ "$SYSTEM_NAME" == *"OpenBSD"* ]]; then return; fi
cd "$ROOT_DIR/resources/examples/dynlib-test"
"$C3C_BIN" -vv dynamic-lib add.c3
if [[ "$OS_MODE" == "windows" ]]; then
"$C3C_BIN" -vv compile-run test.c3 -l ./add.lib
elif [[ "$OS_MODE" == "mac" ]]; then
"$C3C_BIN" -vv compile-run test.c3 -l ./add.dylib
else
if [ -f add.so ]; then mv add.so libadd.so; fi
cc test.c -L. -ladd -Wl,-rpath=.
./a.out
"$C3C_BIN" compile-run test.c3 -L . -l add -z -Wl,-rpath=.
fi
}
run_staticlib_tests() {
echo "--- Running Static Lib Tests ---"
cd "$ROOT_DIR/resources/examples/staticlib-test"
if [[ "$OS_MODE" == "windows" ]]; then
"$C3C_BIN" -vv static-lib add.c3
"$C3C_BIN" -vv compile-run test.c3 -l ./add.lib
else
"$C3C_BIN" -vv static-lib add.c3 -o libadd
if [[ "$SYSTEM_NAME" == *"NetBSD"* ]]; then ranlib libadd.a; fi
OUTPUT_BIN="a.out"
if [[ "$SYSTEM_NAME" == *"OpenBSD"* ]]; then
cc test.c -L. -ladd -lexecinfo -lm -lpthread -o "$OUTPUT_BIN"
elif [[ "$SYSTEM_NAME" == "Linux" ]]; then
# Fix: Linux (and i mean specifically the docker container run) needs dl (for backtrace)
# math and pthread linked manually for static libs
cc test.c -L. -ladd -ldl -lm -lpthread -o "$OUTPUT_BIN"
else
# Mac / NetBSD
cc test.c -L. -ladd -o "$OUTPUT_BIN"
fi
./"$OUTPUT_BIN"
"$C3C_BIN" compile-run test.c3 -L . -l add
fi
}
run_testproject() {
echo "--- Running Test Project ---"
cd "$ROOT_DIR/resources/testproject"
ARGS="--trust=full"
if [[ "$OS_MODE" == "linux" || "$OS_MODE" == "mac" ]]; then
ARGS="$ARGS --linker=builtin"
if [ -f "/etc/alpine-release" ]; then
ARGS="$ARGS --linux-libc=musl"
fi
fi
"$C3C_BIN" run -vv $ARGS
"$C3C_BIN" clean
if [[ "$OS_MODE" == "windows" ]]; then
echo "Running Test Project (hello_world_win32)..."
"$C3C_BIN" -vv --emit-llvm run hello_world_win32 $ARGS
"$C3C_BIN" clean
"$C3C_BIN" -vv build hello_world_win32_lib $ARGS
fi
}
run_wasm_compile() {
echo "--- Running WASM Compile Check ---"
cd "$ROOT_DIR/resources/testfragments"
"$C3C_BIN" compile --target wasm32 -g0 --no-entry -Os wasm4.c3
}
run_unit_tests() {
echo "--- Running Unit Tests ---"
cd "$ROOT_DIR/test"
"$C3C_BIN" compile-test unit -O1 -D SLOW_TESTS
echo "--- Running Test Suite Runner ---"
"$C3C_BIN" compile-run -O1 src/test_suite_runner.c3 -- "$C3C_BIN" test_suite/ --no-terminal
}
# --- Execution ---
run_examples
run_cli_tests
run_dynlib_tests
run_staticlib_tests
run_testproject
run_wasm_compile
run_unit_tests

74
scripts/tools/package_build.sh Executable file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Usage: ./package_build.sh <c3c_path> <output_name> <format: tar|zip>
if [ $# -lt 2 ]; then
echo "Usage: ./package_build.sh <path_to_c3c_binary> <output_name> <format: tar|zip>"
exit 1
fi
set -ex
C3C_BIN="$(realpath "$1")"
OUT_NAME="$2"
FORMAT="$3"
BUILD_DIR="$(dirname "$C3C_BIN")" # Assuming c3c is in build/ or bin/
# Go to repo root
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
cd "$ROOT_DIR" || exit 1
echo ">>> Packaging $OUT_NAME.$FORMAT from $C3C_BIN"
# Create temp directory
WORK_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'c3_package')
echo ">>> Setting up packaging workspace in: $WORK_DIR"
cleanup() {
echo ">>> Cleaning up..."
cd "$ROOT_DIR" || cd ..
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
mkdir -p "$WORK_DIR/c3"
cd "$WORK_DIR" || exit 1
# Copy common files
cp -r "$ROOT_DIR/lib" c3/
cp "$ROOT_DIR/README.md" c3/
cp "$ROOT_DIR/releasenotes.md" c3/
cp "$ROOT_DIR/msvc_build_libraries.py" c3/
# Copy binaries
cp "$C3C_BIN" c3/
if [[ -f "$BUILD_DIR/c3c.pdb" ]]; then cp "$BUILD_DIR/c3c.pdb" c3/; fi
if [[ -d "$BUILD_DIR/c3c_rt" ]]; then
echo "Found c3c_rt directory at $BUILD_DIR/c3c_rt"
cp -r "$BUILD_DIR/c3c_rt" c3/
else
echo "Warning: c3c_rt not found in $BUILD_DIR"
fi
# Compress
if [[ "$FORMAT" == "zip" ]]; then
if command -v zip &> /dev/null; then
# Standard Unix zip
zip -r "$OUT_NAME.zip" c3
elif command -v 7z &> /dev/null; then
# for Windows/7-Zip fallback
echo "Info: 'zip' command not found. Using '7z'..."
7z a -tzip "$OUT_NAME.zip" c3
else
echo "Error: Neither 'zip' nor '7z' found."
exit 1
fi
mv "$OUT_NAME.zip" "$ROOT_DIR/"
else
tar -czf "$OUT_NAME.tar.gz" c3
mv "$OUT_NAME.tar.gz" "$ROOT_DIR/"
fi
echo ">>> Package created: $OUT_NAME.$FORMAT"

View File

@@ -237,7 +237,7 @@ static const char *dynamic_lib_name(void)
}
}
static const char *static_lib_name(void)
const char *static_lib_name(void)
{
const char *name = build_base_name();
@@ -1296,7 +1296,13 @@ static void check_address_sanitizer_options(BuildTarget *target)
WinCrtLinking crt_linking = target->win.crt_linking;
if (crt_linking == WIN_CRT_DEFAULT)
{
error_exit("Please specify `static` or `dynamic` for `wincrt` when using address sanitizer.");
// Default to dynamic, as static ASan is removed in LLVM 21+ for Windows
target->win.crt_linking = WIN_CRT_DYNAMIC;
crt_linking = WIN_CRT_DYNAMIC;
}
else if (crt_linking == WIN_CRT_STATIC)
{
error_exit("Address sanitizer on Windows no longer supports static CRT linking (`--wincrt=static`). Please use `dynamic`.");
}
if (crt_linking == WIN_CRT_STATIC_DEBUG || crt_linking == WIN_CRT_DYNAMIC_DEBUG)

View File

@@ -2353,6 +2353,7 @@ void global_context_add_type(Type *type);
void global_context_add_decl(Decl *type_decl);
void linking_add_link(Linking *linker, const char *link);
const char *static_lib_name(void);
Module *compiler_find_or_create_module(Path *module_name);
Module *global_context_find_module(const char *name);

View File

@@ -184,6 +184,7 @@ static void linker_setup_windows(const char ***args_ref, Linker linker_type, con
{
if (crt_linking == WIN_CRT_STATIC)
{
// This path is now unreachable due to the check in compiler.c
add_concat_file_arg(compiler_path, "c3c_rt/clang_rt.asan-x86_64.lib");
}
else
@@ -589,63 +590,72 @@ static void linker_setup_bsd(const char ***args_ref, Linker linker_type, bool is
{
if (compiler.linking.link_math) linking_add_link(&compiler.linking, "m");
linking_add_link(&compiler.linking, "pthread");
linking_add_link(&compiler.linking, "execinfo"); // for backtrace
if (compiler.build.debug_info == DEBUG_INFO_FULL)
{
add_plain_arg("-rdynamic");
}
linking_add_link(&compiler.linking, "execinfo");
if (compiler.build.debug_info == DEBUG_INFO_FULL) add_plain_arg("-rdynamic");
return;
}
if (is_no_pie(compiler.platform.reloc_model)) add_plain_arg("-no-pie");
if (is_pie(compiler.platform.reloc_model)) add_plain_arg("-pie");
add_plain_arg("--eh-frame-hdr");
if (!link_libc()) return;
const char *crt_dir = find_bsd_crt();
if (!crt_dir)
if (!crt_dir) error_exit("Failed to find the C runtime at link time.");
if (strip_unused() && compiler.build.type == TARGET_TYPE_EXECUTABLE) add_plain_arg("--gc-sections");
bool is_openbsd = compiler.platform.os == OS_TYPE_OPENBSD;
bool is_netbsd = compiler.platform.os == OS_TYPE_NETBSD;
bool is_pie_pic_mode = is_pie_pic(compiler.platform.reloc_model);
if (is_openbsd)
{
error_exit("Failed to find the C runtime at link time.");
if (!is_dylib) add_concat_file_arg(crt_dir, "crt0.o");
add_concat_file_arg(crt_dir, "crtbegin.o");
add_concat_file_arg(crt_dir, "crtend.o");
}
if (strip_unused() && compiler.build.type == TARGET_TYPE_EXECUTABLE)
else
{
add_plain_arg("--gc-sections");
}
if (is_pie_pic(compiler.platform.reloc_model))
if (!is_openbsd) add_concat_file_arg(crt_dir, "crti.o");
if (is_dylib || is_pie_pic_mode)
{
if (!is_dylib) add_plain_arg("-pie");
add_concat_file_arg(crt_dir, "crti.o");
if (!is_dylib && compiler.platform.os != OS_TYPE_NETBSD)
{
add_concat_file_arg(crt_dir, "Scrt1.o");
}
if (!is_dylib && is_pie(compiler.platform.reloc_model)) add_plain_arg("-pie");
if (!is_dylib) add_concat_file_arg(crt_dir, is_netbsd ? "crt0.o" : "Scrt1.o");
add_concat_file_arg(crt_dir, "crtbeginS.o");
add_concat_file_arg(crt_dir, "crtendS.o");
}
else
{
const char *crt_o = compiler.platform.os == OS_TYPE_NETBSD ? "crt0.o" : "crt1.o";
add_concat_file_arg(crt_dir, "crti.o");
if (!is_dylib) add_concat_file_arg(crt_dir, crt_o);
if (!is_dylib) add_concat_file_arg(crt_dir, is_netbsd ? "crt0.o" : "crt1.o");
add_concat_file_arg(crt_dir, "crtbegin.o");
add_concat_file_arg(crt_dir, "crtend.o");
}
add_concat_file_arg(crt_dir, "crtn.o");
if (!is_openbsd) add_concat_file_arg(crt_dir, "crtn.o");
}
add_concat_quote_arg("-L", crt_dir);
add_plain_arg("--dynamic-linker=/libexec/ld-elf.so.1");
if (compiler.platform.os == OS_TYPE_NETBSD)
add_plain_arg("-L/usr/lib/");
switch (compiler.platform.os)
{
case OS_TYPE_NETBSD:
add_plain_arg("--dynamic-linker=/usr/libexec/ld.elf_so");
if (is_dylib)
{
/* The following two flags are needed to work around ld-elf.so not being able
* to handle more than two PT_LOAD segments. */
add_plain_arg("--no-rosegment");
add_plain_arg("-znorelro");
}
break;
case OS_TYPE_OPENBSD:
add_plain_arg("--dynamic-linker=/usr/libexec/ld.so");
break;
default:
add_plain_arg("--dynamic-linker=/libexec/ld-elf.so.1");
}
linking_add_link(&compiler.linking, "c");
if (compiler.linking.link_math) linking_add_link(&compiler.linking, "m");
if (is_openbsd)
{
linking_add_link(&compiler.linking, "execinfo");
}
else
{
linking_add_link(&compiler.linking, "gcc");
linking_add_link(&compiler.linking, "gcc_s");
add_plain_arg("-L/usr/lib/");
}
if (compiler.linking.link_math) linking_add_link(&compiler.linking, "m");
add_plain_arg("-m");
add_plain_arg(ld_target(compiler.platform.arch));
}
@@ -716,6 +726,7 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns
if (is_dylib)
{
add_plain_arg("/DLL");
add_concat_quote_arg("/IMPLIB:", static_lib_name());
}
else
{

View File

@@ -627,8 +627,7 @@ char *file_append_path(const char *path, const char *name)
}
#ifdef _MSC_VER
extern int _getdrive(void);
extern int _chdrive(int drive);
#include <direct.h>
#endif
void file_copy_file(const char *src_path, const char *dst_path, bool overwrite)

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,8 @@
module game_of_life;
extern fn void printf(char *fmt, ...);
extern fn int atoi(char *val);
extern void *__stdoutp;
extern fn void fflush(void *std);
extern fn int rand();
extern fn void* malloc(usz size);
extern fn void usleep(int time);
import libc;
import std::io;
import std::thread;
struct GameBoard
@@ -19,19 +15,18 @@ struct GameBoard
fn void GameBoard.show(GameBoard *board)
{
printf("\e[H");
libc::printf("\e[H");
char* current = board.world;
for (int y = 0; y < board.h; y++)
{
for (int x = 0; x < board.w; x++)
{
printf(*current ? "\e[07m \e[m" : " ");
libc::printf(*current ? "\e[07m \e[m" : " ");
current++;
}
printf("\e[E");
libc::printf("\e[E");
}
fflush(__stdoutp);
libc::fflush(libc::stdout());
}
fn void GameBoard.evolve(GameBoard *board)
@@ -60,31 +55,30 @@ fn void GameBoard.evolve(GameBoard *board)
}
}
fn int main(int c, char** v)
{
int w = 0;
int h = 0;
if (c > 1) w = atoi(v[1]);
if (c > 2) h = atoi(v[2]);
if (c > 1) w = libc::atoi(v[1]);
if (c > 2) h = libc::atoi(v[2]);
if (w <= 0) w = 30;
if (h <= 0) h = 30;
GameBoard board;
board.w = w;
board.h = h;
board.world = malloc((isz)h * w);
board.temp = malloc((isz)h * w);
board.world = libc::malloc((isz)h * w);
board.temp = libc::malloc((isz)h * w);
for (int i = h * w - 1; i >= 0; i--)
{
board.world[i] = rand() % 10 == 0 ? 1 : 0;
board.world[i] = libc::rand() % 10 == 0 ? 1 : 0;
}
for (int j = 0; j < 1000; j++)
{
board.show();
board.evolve();
usleep(200000);
std::thread::sleep_ms(200);
}
return 1;
}

View File

@@ -457,7 +457,7 @@ fn void test_log() @test
}
}
fn void test_ct_intlog2() @test @if($feature(SLOW_TESTS))
fn void test_ct_intlog2() @test @if($feature(SLOW_TESTS) && !env::OPENBSD)
{
uint128 actual, expected;
$for var $x = 0; $x <= 128; ++$x :