diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 593e99611..5b412627f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,270 +5,158 @@ on: branches: [ master, dev, ci_testing, experiments ] pull_request: branches: [ master, dev ] + workflow_dispatch: env: - LLVM_RELEASE_VERSION_WINDOWS: 18 - LLVM_RELEASE_VERSION_MAC: 18 - LLVM_RELEASE_VERSION_LINUX: 19 - LLVM_RELEASE_VERSION_OPENBSD: 19 - LLVM_RELEASE_VERSION_UBUNTU22: 19 - LLVM_RELEASE_VERSION_ALPINEv3_22: 20 + LLVM_RELEASE_VERSION_WINDOWS: 21.1.8 + LLVM_RELEASE_VERSION_MAC: 21 + LLVM_RELEASE_VERSION_LINUX: 21 + LLVM_RELEASE_VERSION_LINUX_MUSL: 20 + LLVM_RELEASE_VERSION_OPENBSD: 20 + LLVM_RELEASE_VERSION_NETBSD: 19 LLVM_DEV_VERSION: 22 + jobs: build-msvc: runs-on: windows-latest + timeout-minutes: 30 strategy: - # Don't abort runners if a single one fails fail-fast: false matrix: build_type: [ Release, Debug ] - defaults: run: shell: cmd steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 + - uses: actions/cache@v5 + with: + path: build/_deps + key: ${{ runner.os }}-llvm-${{ env.LLVM_RELEASE_VERSION_WINDOWS }}-${{ matrix.build_type }}-${{ hashFiles('CMakeLists.txt', '.github/workflows/main.yml') }} - - name: CMake + # set up the environment for Ninja + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: CMake Build run: | - cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} cmake --build build --config ${{ matrix.build_type }} - - name: Compile and run some examples - run: | - cd resources - ..\build\${{ matrix.build_type }}\c3c.exe compile-run -L C:\ --print-linking examples\hello_world_many.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile-run --print-linking examples\time.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile-run --print-linking examples\fannkuch-redux.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\ls.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\process.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\args.c3 -- foo -bar "baz baz" - ..\build\${{ matrix.build_type }}\c3c.exe compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples\constants.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile-run msvc_stack.c3 + # We remove the GNU link tool so C3C picks up the MSVC link.exe + - name: Remove GNU Link + shell: bash + run: rm -f /usr/bin/link - - name: Build testproject - run: | - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 - cd resources/testproject - ..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32 --trust=full - dir out\llvm\windows-x64 - ..\..\build\${{ matrix.build_type }}\c3c.exe clean - dir out\llvm\windows-x64 + - name: Run Unified Tests + shell: bash + run: ./scripts/tools/ci_tests.sh "./build/c3c.exe" + - name: Cache MSVC SDK + id: cache-msvc-sdk + uses: actions/cache@v5 + with: + path: msvc_sdk + key: msvc-sdk-${{ runner.os }}-${{ hashFiles('msvc_build_libraries.py') }} - - name: Build testproject lib - run: | - cd resources/testproject - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 - ..\..\build\${{ matrix.build_type }}\c3c.exe -vvv build hello_world_win32_lib --trust=full + - if: steps.cache-msvc-sdk.outputs.cache-hit != 'true' + run: py msvc_build_libraries.py --accept-license - - name: Compile and run dynlib-test - run: | - cd resources/examples/dynlib-test - ..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv dynamic-lib add.c3 - ..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib + - name: Bundle (Windows) + shell: bash + run: ./scripts/tools/package_build.sh "./build/c3c.exe" "c3-windows-${{ matrix.build_type }}" "zip" - - name: Compile and run staticlib-test - run: | - cd resources/examples/staticlib-test - ..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv static-lib add.c3 - ..\..\..\build\${{ matrix.build_type }}\c3c.exe -vv compile-run test.c3 -l ./add.lib - - - name: Vendor-fetch - run: | - build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib - - - name: Try raylib5 - run: | - cd resources - ..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib - ..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking examples\raylib\raylib_arkanoid.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking examples\raylib\raylib_snake.c3 - ..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking examples\raylib\raylib_tetris.c3 - - - name: run compiler tests - run: | - cd test - ..\build\${{ matrix.build_type }}\c3c.exe compile-run -O0 src/test_suite_runner.c3 -- ..\build\${{ matrix.build_type }}\c3c.exe test_suite/ - - - name: Compile run unit tests - run: | - cd test - ..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1 -D SLOW_TESTS - - - name: Test python script - run: | - py msvc_build_libraries.py --accept-license - dir msvc_sdk - - - name: upload artifacts - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: c3-windows-${{ matrix.build_type }} - path: | - build\${{ matrix.build_type }}\c3c.exe - build\${{ matrix.build_type }}\c3c_rt + path: c3-windows-${{ matrix.build_type }}.zip build-msys2-mingw: runs-on: windows-latest - if: ${{ false }} + timeout-minutes: 30 strategy: - # Don't abort runners if a single one fails fail-fast: false matrix: build_type: [Release, Debug] - defaults: run: shell: msys2 {0} steps: - - uses: actions/checkout@v4 - + - uses: actions/checkout@v6 - uses: msys2/setup-msys2@v2 with: msystem: MINGW64 - update: true - install: git binutils mingw-w64-x86_64-clang mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python - - shell: msys2 {0} + update: false + install: git binutils mingw-w64-x86_64-clang mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python mingw-w64-x86_64-llvm mingw-w64-x86_64-llvm-libs + + - name: Install LLD run: | - pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-20.1.0-1-any.pkg.tar.zst - pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-20.1.0-1-any.pkg.tar.zst - - name: CMake + echo "Server = https://mirror.msys2.org/mingw/mingw64" > /etc/pacman.d/mirrorlist.mingw64 + pacman -Sy --noconfirm --needed \ + mingw-w64-x86_64-llvm \ + mingw-w64-x86_64-llvm-libs \ + mingw-w64-x86_64-lld \ + mingw-w64-x86_64-clang + + - name: CMake Build run: | - cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_LINKER=lld + cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_LINKER=lld -DC3_LINK_DYNAMIC=OFF -DLLVM_DIR=/mingw64/lib/cmake/llvm -DC3_LLD_DIR=/mingw64/lib/ cmake --build build - - - name: Compile and run some examples - run: | - cd resources - ../build/c3c compile-run --print-linking examples/hello_world_many.c3 - ../build/c3c compile-run --print-linking examples/process.c3 - ../build/c3c compile-run --print-linking examples/time.c3 - ../build/c3c compile-run --print-linking examples/fannkuch-redux.c3 - ../build/c3c compile-run --print-linking examples/contextfree/boolerr.c3 - ../build/c3c compile-run --print-linking examples/load_world.c3 - ../build/c3c compile-run --print-linking examples/args.c3 -- foo -bar "baz baz" - ../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3 - - - name: Build testproject - run: | - cd resources/testproject - ../../build/c3c run -vvv --trust=full - - - name: Vendor-fetch - run: | - ./build/c3c vendor-fetch raylib - - - name: Build testproject lib - run: | - cd resources/testproject - ../../build/c3c build hello_world_lib --cc cc -vvv --trust=full - - - name: run compiler tests - run: | - cd test - ../build/c3c.exe compile --target windows-x64 -O1 src/test_suite_runner.c3 - ./test_suite_runner.exe ../build/c3c.exe test_suite/ --no-terminal + - name: Run Unified Tests + run: ./scripts/tools/ci_tests.sh "./build/c3c" build-msys2-clang: runs-on: windows-latest - #if: ${{ false }} + timeout-minutes: 30 strategy: - # Don't abort runners if a single one fails fail-fast: false matrix: build_type: [Release, Debug] - defaults: run: shell: msys2 {0} steps: - - uses: actions/checkout@v4 - + - uses: actions/checkout@v6 - uses: msys2/setup-msys2@v2 with: msystem: CLANG64 update: false install: git binutils mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-python - - - name: CMake + - name: CMake Build run: | cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} cmake --build build - - - name: Compile and run some examples - run: | - cd resources - ../build/c3c compile-run examples/hello_world_many.c3 - ../build/c3c compile-run examples/time.c3 - ../build/c3c compile-run examples/fannkuch-redux.c3 - ../build/c3c compile-run examples/contextfree/boolerr.c3 - ../build/c3c compile-run examples/load_world.c3 - ../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz" - ../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3 - - name: Build testproject - run: | - cd resources/testproject - ../../build/c3c run -vvv --trust=full - - - name: Build testproject lib - run: | - cd resources/testproject - ../../build/c3c build hello_world_lib -vvv --trust=full - - - name: run compiler tests - run: | - cd test - ../build/c3c.exe compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c.exe test_suite/ --no-terminal + - name: Run Unified Tests + run: ./scripts/tools/ci_tests.sh "./build/c3c" build-linux: runs-on: ubuntu-22.04 strategy: - # Don't abort runners if a single one fails fail-fast: false matrix: build_type: [Release, Debug] llvm_version: [17, 18, 19, 20, 21, 22] - steps: - - uses: actions/checkout@v4 - - name: Install common deps - run: | - sudo apt-get update - sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev - + - uses: actions/checkout@v6 + - run: sudo apt-get update && sudo apt-get install -y zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev - name: Install Clang ${{matrix.llvm_version}} run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - if [[ "${{matrix.llvm_version}}" < 18 ]]; then - sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main" - sudo apt-get update - sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \ - clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \ - lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libmlir-${{matrix.llvm_version}} \ - libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools - else - if [[ "${{matrix.llvm_version}}" < "${{env.LLVM_DEV_VERSION}}" ]]; then - sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main" - sudo apt-get update - sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \ - clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \ - lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev - else - sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" - sudo apt-get install -y -t llvm-toolchain-focal libpolly-${{matrix.llvm_version}}-dev \ - clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \ - lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev - fi - fi - - name: CMake - if: matrix.llvm_version < 18 || matrix.llvm_version == env.LLVM_DEV_VERSION + REPO_URL="http://apt.llvm.org/focal/ llvm-toolchain-focal" + if [[ "${{matrix.llvm_version}}" != "${{env.LLVM_DEV_VERSION}}" ]]; then REPO_URL="$REPO_URL-${{matrix.llvm_version}}"; fi + sudo add-apt-repository "deb $REPO_URL main" + sudo apt-get update + PKGS="clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libpolly-${{matrix.llvm_version}}-dev" + if [[ "${{matrix.llvm_version}}" < 18 ]]; then PKGS="$PKGS libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools"; fi + sudo apt-get install -y $PKGS + - name: CMake Build run: | - cmake -B build \ - -G Ninja \ + C3_LLVM_VER=${{matrix.llvm_version}} + if [[ "${{matrix.llvm_version}}" -ge 18 && "${{matrix.llvm_version}}" != "${{env.LLVM_DEV_VERSION}}" ]]; then C3_LLVM_VER="${{matrix.llvm_version}}.1"; fi + + cmake -B build -G Ninja \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \ -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \ @@ -277,135 +165,23 @@ jobs: -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ -DLLVM_ENABLE_LIBXML2=OFF \ - -DC3_LLVM_VERSION=${{matrix.llvm_version}} - cmake --build build - - name: CMake18 - if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION - run: | - cmake -B build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ - -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \ - -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \ - -DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \ - -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ - -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ - -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ - -DLLVM_ENABLE_LIBXML2=OFF \ - -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 + -DC3_LLVM_VERSION=$C3_LLVM_VER cmake --build build + - name: Run Unified Tests + run: ./scripts/tools/ci_tests.sh "./build/c3c" - - name: Compile and run some examples - run: | - cd resources - ../build/c3c compile examples/base64.c3 - ../build/c3c compile examples/binarydigits.c3 - ../build/c3c compile examples/brainfk.c3 - ../build/c3c compile examples/factorial_macro.c3 - ../build/c3c compile examples/fasta.c3 - ../build/c3c compile examples/gameoflife.c3 - ../build/c3c compile examples/hash.c3 - ../build/c3c compile-only examples/levenshtein.c3 - ../build/c3c compile examples/load_world.c3 - ../build/c3c compile-only examples/map.c3 - ../build/c3c compile examples/mandelbrot.c3 - ../build/c3c compile examples/plus_minus.c3 - ../build/c3c compile examples/nbodies.c3 - ../build/c3c compile examples/spectralnorm.c3 - ../build/c3c compile examples/swap.c3 - ../build/c3c compile examples/contextfree/boolerr.c3 - ../build/c3c compile examples/contextfree/dynscope.c3 - ../build/c3c compile examples/contextfree/guess_number.c3 - ../build/c3c compile examples/contextfree/multi.c3 - ../build/c3c compile examples/contextfree/cleanup.c3 - ../build/c3c compile-run examples/hello_world_many.c3 - ../build/c3c compile-run examples/time.c3 - ../build/c3c compile-run examples/fannkuch-redux.c3 - ../build/c3c compile-run examples/contextfree/boolerr.c3 - ../build/c3c compile-run examples/load_world.c3 - ../build/c3c compile-run examples/process.c3 - ../build/c3c compile-run examples/ls.c3 - ../build/c3c compile-run --linker=builtin linux_stack.c3 - ../build/c3c compile-run linux_stack.c3 - ../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz" - - - name: Compile and run dynlib-test - run: | - cd resources/examples/dynlib-test - ../../../build/c3c -vv dynamic-lib add.c3 - mv add.so libadd.so - cc test.c -L. -ladd -Wl,-rpath=. - ./a.out - ../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=. - - - name: Compile and run staticlib-test - run: | - cd resources/examples/staticlib-test - ../../../build/c3c -vv static-lib add.c3 - mv add.a libadd.a - cc test.c -L. -ladd - ./a.out - ../../../build/c3c compile-run test.c3 -L . -l add - - - name: Compile run unit tests - run: | - cd test - ../build/c3c compile-test unit -D SLOW_TESTS - - - name: Build testproject - run: | - cd resources/testproject - ../../build/c3c run -vvv --trust=full - - - name: Test WASM - run: | - cd resources/testfragments - ../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3 - - - name: Install QEMU and Risc-V toolchain - run: | - sudo apt-get install opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf - - - name: Compile and run Baremetal Risc-V Example + - name: Embedded/QEMU Tests run: | + sudo apt-get install -y opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf cd resources/examples/embedded/riscv-qemu make C3C_PATH=../../../../build/ run - - name: Build testproject direct linker - run: | - cd resources/testproject - ../../build/c3c run -vvv --linker=builtin --trust=full - - - name: Init a library & a project - run: | - ./build/c3c init-lib mylib - ls mylib.c3l - ./build/c3c init myproject - ls myproject - - - name: Vendor-fetch - run: | - ./build/c3c vendor-fetch raylib - - - name: run compiler tests - run: | - cd test - ../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/ - - - name: bundle_output + - name: Bundle & Upload (Linux) if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX run: | - mkdir c3 - cp -r lib c3 - cp msvc_build_libraries.py c3 - cp build/c3c c3 - cp README.md c3 - cp releasenotes.md c3 - tar czf c3-linux-${{matrix.build_type}}.tar.gz c3 - - - name: upload artifacts + bash ./scripts/tools/package_build.sh "./build/c3c" "c3-linux-${{matrix.build_type}}" "tar" + - uses: actions/upload-artifact@v6 if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX - uses: actions/upload-artifact@v4 with: name: c3-linux-${{matrix.build_type}} path: c3-linux-${{matrix.build_type}}.tar.gz @@ -415,312 +191,182 @@ jobs: container: image: alpine:3.22 strategy: - # Don't abort runners if a single one fails fail-fast: false matrix: build_type: [Release, Debug] llvm_version: [18, 19, 20] - steps: - - uses: actions/checkout@v4 - - name: Install common deps + - uses: actions/checkout@v6 + - run: apk update && apk add zlib-dev zlib-static python3 samurai cmake curl-dev curl-static openssl-dev bash + - name: Install Clang + run: apk add "llvm${{matrix.llvm_version}}-dev" "llvm${{matrix.llvm_version}}-static" "llvm${{matrix.llvm_version}}-gtest" "llvm${{matrix.llvm_version}}-linker-tools" "lld${{matrix.llvm_version}}-dev" "clang${{matrix.llvm_version}}-dev" "clang${{matrix.llvm_version}}-static" + - name: CMake Build run: | - apk update - apk add zlib-dev zlib-static python3 samurai cmake curl-dev curl-static openssl-dev - - - name: Install Clang ${{matrix.llvm_version}} - run: | - apk add "llvm${{matrix.llvm_version}}-dev" "llvm${{matrix.llvm_version}}-static" \ - "llvm${{matrix.llvm_version}}-gtest" "llvm${{matrix.llvm_version}}-linker-tools" \ - "llvm${{matrix.llvm_version}}-test-utils" "llvm${{matrix.llvm_version}}-test-utils-pyc" \ - "lld${{matrix.llvm_version}}-dev" "clang${{matrix.llvm_version}}-dev" \ - "clang${{matrix.llvm_version}}-extra-tools" "clang${{matrix.llvm_version}}-static" - - name: CMake - if: matrix.llvm_version == env.LLVM_DEV_VERSION - run: | - cmake -B build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=${{matrix.build_type}}" \ - -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \ - -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \ - -DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \ - -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ - -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ - -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ - -DLLVM_ENABLE_LIBXML2=OFF \ - -DC3_LINK_DYNAMIC=ON \ - -DC3_LLVM_VERSION=${{matrix.llvm_version}} + C3_LLVM_VER=${{matrix.llvm_version}} + if [[ "${{matrix.llvm_version}}" -ge 18 && "${{matrix.llvm_version}}" != "${{env.LLVM_DEV_VERSION}}" ]]; then C3_LLVM_VER="${{matrix.llvm_version}}.1"; fi + cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} -DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} -DLLVM_ENABLE_LIBXML2=OFF -DC3_LINK_DYNAMIC=ON -DC3_LLVM_VERSION=$C3_LLVM_VER cmake --build build - - name: CMake_Stable - if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION - run: | - cmake -B build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ - -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \ - -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \ - -DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \ - -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ - -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ - -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ - -DLLVM_ENABLE_LIBXML2=OFF \ - -DC3_LINK_DYNAMIC=ON \ - -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 - cmake --build build - - - name: Compile and run some examples - run: | - export C3C="$(realpath ./build/c3c)" - cd resources - "$C3C" compile examples/base64.c3 - "$C3C" compile examples/binarydigits.c3 - "$C3C" compile examples/brainfk.c3 - "$C3C" compile examples/factorial_macro.c3 - "$C3C" compile examples/fasta.c3 - "$C3C" compile examples/gameoflife.c3 - "$C3C" compile examples/hash.c3 - "$C3C" compile-only examples/levenshtein.c3 - "$C3C" compile examples/load_world.c3 - "$C3C" compile-only examples/map.c3 - "$C3C" compile examples/mandelbrot.c3 - "$C3C" compile examples/plus_minus.c3 - "$C3C" compile examples/nbodies.c3 - "$C3C" compile examples/spectralnorm.c3 - "$C3C" compile examples/swap.c3 - "$C3C" compile examples/contextfree/boolerr.c3 - "$C3C" compile examples/contextfree/dynscope.c3 - "$C3C" compile examples/contextfree/guess_number.c3 - "$C3C" compile examples/contextfree/multi.c3 - "$C3C" compile examples/contextfree/cleanup.c3 - "$C3C" compile-run examples/hello_world_many.c3 - "$C3C" compile-run examples/time.c3 - "$C3C" compile-run examples/fannkuch-redux.c3 - "$C3C" compile-run examples/contextfree/boolerr.c3 - "$C3C" compile-run examples/load_world.c3 - "$C3C" compile-run examples/process.c3 - "$C3C" compile-run examples/ls.c3 - # "$C3C" compile-run --linker=builtin linux_stack.c3 # Program will hang due to incorrect linking to `/lib64/ld-linux-x86-64.so.2` instead of `/lib/ld-musl-x86_64.so.1` - "$C3C" compile-run linux_stack.c3 - "$C3C" compile-run examples/args.c3 -- foo -bar "baz baz" - - - name: Compile and run dynlib-test - run: | - export C3C="$(realpath ./build/c3c)" - cd resources/examples/dynlib-test - "$C3C" -vv dynamic-lib add.c3 - mv add.so libadd.so - cc test.c -L. -ladd -Wl,-rpath=. - ./a.out - "$C3C" compile-run test.c3 -L . -l add -z -Wl,-rpath=. - - - name: Compile and run staticlib-test - run: | - export C3C="$(realpath ./build/c3c)" - cd resources/examples/staticlib-test - "$C3C" -vv static-lib add.c3 - mv add.a libadd.a - cc test.c -L. -ladd - ./a.out - "$C3C" compile-run test.c3 -L . -l add - - - name: Compile run unit tests - run: | - export C3C="$(realpath ./build/c3c)" - cd test - "$C3C" compile-test unit -D SLOW_TESTS - - - name: Build testproject - run: | - export C3C="$(realpath ./build/c3c)" - cd resources/testproject - "$C3C" run -vvv --trust=full - - - name: Test WASM - run: | - export C3C="$(realpath ./build/c3c)" - cd resources/testfragments - "$C3C" compile --target wasm32 -g0 --no-entry -Os wasm4.c3 - - - name: Install QEMU and Risc-V toolchain - run: | - apk add qemu-dev qemu-system-riscv32 gcc-riscv-none-elf make - - - name: Compile and run Baremetal Risc-V Example - run: | - cd resources/examples/embedded/riscv-qemu - make C3C_PATH=../../../../build/ run - - - name: Build testproject direct linker - run: | - export C3C="$(realpath ./build/c3c)" - cd resources/testproject - "$C3C" run -vvv --linker=builtin --trust=full --linux-libc=musl - - - name: Init a library & a project - run: | - ./build/c3c init-lib mylib - ls mylib.c3l - ./build/c3c init myproject - ls myproject - - - name: Vendor-fetch - run: | - "$(realpath ./build/c3c)" vendor-fetch raylib55 - - - name: run compiler tests - run: | - export C3C="$(realpath ./build/c3c)" - cd test - "$C3C" compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/ - - - name: bundle_output - if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_ALPINEv3_22 - run: | - mkdir c3 - cp -r lib c3 - cp msvc_build_libraries.py c3 - cp build/c3c c3 - cp README.md c3 - cp releasenotes.md c3 - tar -czf c3-musl-${{matrix.build_type}}.tar.gz c3 - - - name: upload artifacts - if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_ALPINEv3_22 - uses: actions/upload-artifact@v4 + - name: Run Unified Tests + run: ./scripts/tools/ci_tests.sh "./build/c3c" + + - name: Bundle & Upload (Alpine) + if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX_MUSL + run: bash ./scripts/tools/package_build.sh "./build/c3c" "c3-linux-musl-${{matrix.build_type}}" "tar" + - uses: actions/upload-artifact@v6 + if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX_MUSL with: - name: c3-musl-${{matrix.build_type}} - path: c3-musl-${{matrix.build_type}}.tar.gz + name: c3-linux-musl-${{matrix.build_type}} + path: c3-linux-musl-${{matrix.build_type}}.tar.gz - build-linux-ubuntu22: - runs-on: ubuntu-22.04 + build-mac: + runs-on: macos-latest strategy: - # Don't abort runners if a single one fails fail-fast: false matrix: build_type: [Release, Debug] - llvm_version: [17, 18, 19, 20] + llvm_version: [17, 18, 19, 20, 21] steps: - - uses: actions/checkout@v4 - - name: Install common deps + - uses: actions/checkout@v6 + - name: Download LLVM run: | - sudo apt-get update - sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev - - - name: Install Clang ${{matrix.llvm_version}} - run: | - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - if [[ "${{matrix.llvm_version}}" < "${{env.LLVM_DEV_VERSION}}" ]]; then - sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main" + brew_install() { + for pkg in "$@"; do + brew list "$pkg" &>/dev/null || brew install "$pkg" + done + } + if [[ "${{ matrix.llvm_version }}" == "21" ]]; then + brew_install llvm lld ninja curl + echo "/opt/homebrew/opt/llvm/bin" >> $GITHUB_PATH + echo "/opt/homebrew/opt/lld/bin" >> $GITHUB_PATH else - sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" + brew_install llvm@${{ matrix.llvm_version }} ninja curl + echo "/opt/homebrew/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH + if [[ "${{ matrix.llvm_version }}" -ge 19 ]]; then + brew_install lld@${{ matrix.llvm_version }} + echo "/opt/homebrew/opt/lld@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH + fi fi - sudo apt-get update - sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev - sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools - sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev - - name: CMake Old - if: matrix.llvm_version < 18 || matrix.llvm_version == env.LLVM_DEV_VERSION + echo "CPATH=$(xcrun --show-sdk-path)/user/include" >> $GITHUB_ENV + - name: CMake Build run: | - cmake -B build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ - -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \ - -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \ - -DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \ - -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ - -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ - -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ - -DLLVM_ENABLE_LIBXML2=OFF \ - -DC3_LLVM_VERSION=${{matrix.llvm_version}} + C3_LLVM_VER=${{matrix.llvm_version}} + if [[ "${{matrix.llvm_version}}" -ge 18 ]]; then C3_LLVM_VER="${{matrix.llvm_version}}.1"; fi + if [[ "${{ matrix.llvm_version }}" == "21" ]]; then + C3_LLD_DIR="/opt/homebrew/opt/lld/lib" + C3_LLD_INCLUDE_DIR="/opt/homebrew/opt/lld/include" + elif [[ "${{ matrix.llvm_version }}" -ge 19 ]]; then + C3_LLD_DIR="/opt/homebrew/opt/lld@${{ matrix.llvm_version }}/lib" + C3_LLD_INCLUDE_DIR="/opt/homebrew/opt/lld@${{ matrix.llvm_version }}/include" + else + C3_LLD_DIR="" + C3_LLD_INCLUDE_DIR="" + fi + cmake -B build -G Ninja -DC3_LLVM_VERSION=$C3_LLVM_VER -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DC3_LLD_DIR=$C3_LLD_DIR -DC3_LLD_INCLUDE_DIR=$C3_LLD_INCLUDE_DIR cmake --build build - - name: CMake - if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION - run: | - cmake -B build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ - -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \ - -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \ - -DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \ - -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ - -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ - -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ - -DLLVM_ENABLE_LIBXML2=OFF \ - -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 - cmake --build build - - - name: Compile and run some examples - run: | - cd resources - ../build/c3c compile examples/gameoflife.c3 - ../build/c3c compile-only examples/levenshtein.c3 - ../build/c3c compile-only examples/map.c3 - ../build/c3c compile examples/mandelbrot.c3 - ../build/c3c compile examples/plus_minus.c3 - ../build/c3c compile examples/spectralnorm.c3 - ../build/c3c compile examples/swap.c3 - ../build/c3c compile examples/contextfree/guess_number.c3 - ../build/c3c compile-run examples/hash.c3 - ../build/c3c compile-run examples/nbodies.c3 - ../build/c3c compile-run examples/contextfree/boolerr.c3 - ../build/c3c compile-run examples/contextfree/dynscope.c3 - ../build/c3c compile-run examples/contextfree/multi.c3 - ../build/c3c compile-run examples/contextfree/cleanup.c3 - ../build/c3c compile-run examples/hello_world_many.c3 - ../build/c3c compile-run examples/time.c3 - ../build/c3c compile-run examples/fannkuch-redux.c3 - ../build/c3c compile-run examples/load_world.c3 - ../build/c3c compile-run examples/base64.c3 - ../build/c3c compile-run examples/binarydigits.c3 - ../build/c3c compile-run examples/brainfk.c3 - ../build/c3c compile-run examples/factorial_macro.c3 - ../build/c3c compile-run examples/fasta.c3 - ../build/c3c compile-run examples/process.c3 - ../build/c3c compile-run --linker=builtin linux_stack.c3 - ../build/c3c compile-run linux_stack.c3 - ../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz" - - - name: Compile run unit tests - run: | - cd test - ../build/c3c compile-test unit --sanitize=address -D SLOW_TESTS - - - name: Build testproject + - name: Run Unified Tests + run: ./scripts/tools/ci_tests.sh "./build/c3c" + + - name: Build Lib (Mac) run: | cd resources/testproject - ../../build/c3c run -vvv --trust=full + ../../build/c3c build hello_world_lib -vv --trust=full - - name: Build testproject direct linker - run: | - cd resources/testproject - ../../build/c3c run -vvv --linker=builtin --trust=full - - - name: Vendor-fetch - run: | - ./build/c3c vendor-fetch raylib - - - name: run compiler tests - run: | - cd test - ../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/ - - - name: bundle_output - if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU22 - run: | - mkdir c3 - cp -r lib c3 - cp README.md c3 - cp releasenotes.md c3 - cp msvc_build_libraries.py c3 - cp build/c3c c3 - tar czf c3-ubuntu-22-${{matrix.build_type}}.tar.gz c3 - - - name: upload artifacts - if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU22 - uses: actions/upload-artifact@v4 + - name: Bundle & Upload (Mac) + if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC + run: bash ./scripts/tools/package_build.sh "./build/c3c" "c3-macos-${{matrix.build_type}}" "zip" + - uses: actions/upload-artifact@v6 + if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC with: - name: c3-ubuntu-22-${{matrix.build_type}} - path: c3-ubuntu-22-${{matrix.build_type}}.tar.gz + name: c3-macos-${{matrix.build_type}} + path: c3-macos-${{matrix.build_type}}.zip + + build-openbsd: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + build_type: [Release, Debug] + version: ['7.8'] + steps: + - uses: actions/checkout@v6 + + - name: Build, Test and Package in OpenBSD + uses: vmactions/openbsd-vm@v1 + with: + sync: rsync + usesh: true + release: ${{ matrix.version }} + prepare: pkg_add ninja cmake llvm%${{ env.LLVM_RELEASE_VERSION_OPENBSD }} bash + # Combine all logic here so rsync copyback triggers at the end + run: | + export MALLOC_OPTIONS=j # Disable junk filling (it's faster) + cd "$GITHUB_WORKSPACE" + + # Build + cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} + cmake --build build + + # Run Unified Tests + chmod +x scripts/tools/ci_tests.sh + ulimit -d unlimited + ./scripts/tools/ci_tests.sh "./build/c3c" + + # Package + chmod +x scripts/tools/package_build.sh + ./scripts/tools/package_build.sh "./build/c3c" "c3-openbsd-${{matrix.build_type}}" "tar" + + - uses: actions/upload-artifact@v6 + with: + name: c3-openbsd-${{matrix.build_type}} + path: c3-openbsd-${{matrix.build_type}}.tar.gz + + build-netbsd: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + build_type: [Release, Debug] + version: ['10.1'] + steps: + - uses: actions/checkout@v6 + + - name: Build, Test and Package in NetBSD + uses: vmactions/netbsd-vm@v1 + with: + sync: rsync + usesh: true + release: ${{ matrix.version }} + prepare: | + export PATH="/usr/pkg/sbin:/usr/pkg/bin:$PATH" + export PKG_PATH="https://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/$(uname -m)/$(uname -r)/All/" + /usr/sbin/pkg_add pkgin + pkgin -y update + pkgin -y install cmake gcc14 ninja-build bash \ + 'llvm>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}' \ + 'clang>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}' \ + 'lld>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}' + run: | + export PATH="/usr/pkg/sbin:/usr/pkg/bin:$PATH" + export CC=clang + export CXX=clang++ + cd "$GITHUB_WORKSPACE" + + # Build + cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} + cmake --build build + + # Run Unified Tests + chmod +x scripts/tools/ci_tests.sh + ./scripts/tools/ci_tests.sh "./build/c3c" + + # Package + chmod +x scripts/tools/package_build.sh + ./scripts/tools/package_build.sh "./build/c3c" "c3-netbsd-${{matrix.build_type}}" "tar" + + - uses: actions/upload-artifact@v6 + with: + name: c3-netbsd-${{matrix.build_type}} + path: c3-netbsd-${{matrix.build_type}}.tar.gz build-with-docker: runs-on: ubuntu-22.04 @@ -730,494 +376,93 @@ jobs: ubuntu_version: [20.04, 22.04] build_type: [Release, Debug] llvm_version: [17, 18, 19] - steps: - - uses: actions/checkout@v4 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Make script executable - run: chmod +x ./build-with-docker.sh - - - name: Run build + - uses: actions/checkout@v6 + - uses: docker/setup-buildx-action@v3 + - name: Build run: | + chmod +x ./build-with-docker.sh LLVM_VERSION=${{ matrix.llvm_version }} UBUNTU_VERSION=${{ matrix.ubuntu_version }} CMAKE_BUILD_TYPE=${{ matrix.build_type }} ./build-with-docker.sh - - - name: Compile and run some examples + - name: Run Unified Tests in Docker run: | - cd resources - ../build/c3c compile examples/base64.c3 - ../build/c3c compile examples/binarydigits.c3 - ../build/c3c compile examples/brainfk.c3 - ../build/c3c compile examples/factorial_macro.c3 - ../build/c3c compile examples/fasta.c3 - ../build/c3c compile examples/gameoflife.c3 - ../build/c3c compile examples/hash.c3 - ../build/c3c compile-only examples/levenshtein.c3 - ../build/c3c compile examples/load_world.c3 - ../build/c3c compile-only examples/map.c3 - ../build/c3c compile examples/mandelbrot.c3 - ../build/c3c compile examples/plus_minus.c3 - ../build/c3c compile examples/nbodies.c3 - ../build/c3c compile examples/spectralnorm.c3 - ../build/c3c compile examples/swap.c3 - ../build/c3c compile examples/contextfree/boolerr.c3 - ../build/c3c compile examples/contextfree/dynscope.c3 - ../build/c3c compile examples/contextfree/guess_number.c3 - ../build/c3c compile examples/contextfree/multi.c3 - ../build/c3c compile examples/contextfree/cleanup.c3 - ../build/c3c compile-run examples/hello_world_many.c3 - ../build/c3c compile-run examples/time.c3 - ../build/c3c compile-run examples/fannkuch-redux.c3 - ../build/c3c compile-run examples/contextfree/boolerr.c3 - ../build/c3c compile-run examples/load_world.c3 - ../build/c3c compile-run examples/process.c3 - ../build/c3c compile-run examples/ls.c3 - ../build/c3c compile-run --linker=builtin linux_stack.c3 - ../build/c3c compile-run linux_stack.c3 - ../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz" - - - name: Compile run unit tests - run: | - cd test - ../build/c3c compile-test unit -D SLOW_TESTS - - - name: Build testproject - run: | - cd resources/testproject - ../../build/c3c run -vvv --trust=full - - - name: Test WASM - run: | - cd resources/testfragments - ../../build/c3c compile --reloc=none --target wasm32 -g0 --no-entry -Os wasm4.c3 - - - name: Build testproject direct linker - run: | - cd resources/testproject - ../../build/c3c run -vvv --linker=builtin --trust=full - - - name: Init a library & a project - run: | - ./build/c3c init-lib mylib - ls mylib.c3l - ./build/c3c init myproject - ls myproject - - - name: run compiler tests - run: | - cd test - ../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/ - - build-mac: - runs-on: macos-latest - strategy: - # Don't abort runners if a single one fails - fail-fast: false - matrix: - build_type: [Release, Debug] - llvm_version: [17, 18] - steps: - - uses: actions/checkout@v4 - - name: Download LLVM - run: | - brew install llvm@${{ matrix.llvm_version }} ninja curl - echo "/opt/homebrew/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH - TMP_PATH=$(xcrun --show-sdk-path)/user/include - echo "CPATH=$TMP_PATH" >> $GITHUB_ENV - - - name: CMake - if: matrix.llvm_version < 18 - run: | - cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - cmake --build build - - name: CMake18 - if: matrix.llvm_version >= 18 - run: | - cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - cmake --build build - - - name: Vendor-fetch - run: | - ./build/c3c vendor-fetch raylib - - - name: Compile and run some examples - run: | - cd resources - ../build/c3c compile-run examples/hello_world_many.c3 - ../build/c3c compile-run examples/time.c3 - ../build/c3c compile-run examples/fannkuch-redux.c3 - ../build/c3c compile-run examples/contextfree/boolerr.c3 - ../build/c3c compile-run examples/process.c3 - ../build/c3c compile-run examples/load_world.c3 - ../build/c3c compile-run -O5 examples/load_world.c3 - ../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz" - - - name: Compile and run dynlib-test - run: | - cd resources/examples/dynlib-test - ../../../build/c3c -vv dynamic-lib add.c3 - ../../../build/c3c compile-run test.c3 -l ./add.dylib - - - name: Compile run unit tests - run: | - cd test - ../build/c3c compile-test unit -O1 -D SLOW_TESTS - - - name: Test WASM - run: | - cd resources/testfragments - ../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3 - - - name: Build testproject - run: | - cd resources/testproject - ../../build/c3c run -vvv --trust=full - - - name: Build testproject direct linker - run: | - cd resources/testproject - ../../build/c3c run -vvv --linker=builtin --trust=full - - - name: Build testproject lib - run: | - cd resources/testproject - ../../build/c3c build hello_world_lib -vvv --trust=full - - - name: run compiler tests - run: | - cd test - ../build/c3c compile -O1 src/test_suite_runner.c3 - ./test_suite_runner ../build/c3c test_suite/ - - - name: run build test suite runner - run: | - cd test - ../build/c3c compile -O1 src/test_suite_runner.c3 - - - name: bundle_output - if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC - run: | - mkdir macos - cp -r lib macos - cp msvc_build_libraries.py macos - cp README.md macos - cp releasenotes.md macos - cp build/c3c macos - zip -r c3-macos-${{matrix.build_type}}.zip macos - - - name: upload artifacts - if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC - uses: actions/upload-artifact@v4 - with: - name: c3-macos-${{matrix.build_type}} - path: c3-macos-${{matrix.build_type}}.zip + chmod +x ./scripts/tools/ci_tests.sh + docker run --rm \ + -u 0 \ + -v "$(pwd):/home/c3c/source" \ + -w /home/c3c/source \ + c3c-builder \ + bash -c "./scripts/tools/ci_tests.sh ./bin/c3c" build-nix: runs-on: ubuntu-22.04 strategy: - # Don't abort runners if a single one fails fail-fast: false matrix: build_type: [ Release, Debug ] nixpkgs: [ Lock, Latest ] steps: - - uses: actions/checkout@v4 - - - name: Install Nix - uses: cachix/install-nix-action@v30 + - uses: actions/checkout@v6 + - uses: cachix/install-nix-action@v31 with: github_access_token: ${{ secrets.GITHUB_TOKEN }} - - - name: Update flake (if necessary) + - name: Update flake + if: matrix.nixpkgs == 'Latest' + run: nix flake update + - name: CMake build and run Unified Tests run: | - if [[ matrix.nixpkgs == "Latest" ]]; then - nix flake update - fi - nix flake info - - - name: Build and check - run: | - if [[ ${{ matrix.build_type }} = "Debug" ]]; then - nix build -L ".#c3c-debug-checks" - else - nix build -L ".#c3c-checks" - fi - - build-openbsd: - runs-on: ubuntu-latest - - strategy: - # Don't abort runners if a single one fails - fail-fast: false - matrix: - build_type: [Release, Debug] - - steps: - - uses: actions/checkout@v4 - - name: OpenBSD VM - uses: vmactions/openbsd-vm@v1.2.5 - with: - prepare: | - pkg_add cmake llvm-20.1.8p1 ninja - - run: | - echo "CMake" - cmake -B build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ - -DLLVM_ENABLE_LIBXML2=OFF \ - -DC3_LLVM_VERSION=${LLVM_RELEASE_VERSION_OPENBSD} - cmake --build build - echo "Compile and run some examples" - cd resources - ../build/c3c compile examples/base64.c3 - ../build/c3c compile examples/binarydigits.c3 - ../build/c3c compile examples/brainfk.c3 - ../build/c3c compile examples/factorial_macro.c3 - ../build/c3c compile examples/fasta.c3 - ../build/c3c compile examples/gameoflife.c3 - ../build/c3c compile examples/hash.c3 - ../build/c3c compile-only examples/levenshtein.c3 - ../build/c3c compile examples/load_world.c3 - ../build/c3c compile-only examples/map.c3 - ../build/c3c compile examples/mandelbrot.c3 - ../build/c3c compile examples/plus_minus.c3 - ../build/c3c compile examples/nbodies.c3 - ../build/c3c compile examples/spectralnorm.c3 - ../build/c3c compile examples/swap.c3 - ../build/c3c compile examples/contextfree/boolerr.c3 - ../build/c3c compile examples/contextfree/dynscope.c3 - ../build/c3c compile examples/contextfree/guess_number.c3 - ../build/c3c compile examples/contextfree/multi.c3 - ../build/c3c compile examples/contextfree/cleanup.c3 - ../build/c3c compile-run examples/hello_world_many.c3 - ../build/c3c compile-run examples/time.c3 - ../build/c3c compile-run examples/fannkuch-redux.c3 - ../build/c3c compile-run examples/contextfree/boolerr.c3 - ../build/c3c compile-run examples/load_world.c3 - ../build/c3c compile-run examples/process.c3 - ../build/c3c compile-run examples/ls.c3 - ../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz" - cd .. - echo "Compile and run dynlib-test" - cd resources/examples/dynlib-test - ../../../build/c3c -vv dynamic-lib add.c3 - mv add.so libadd.so - cc test.c -L. -ladd -Wl,-rpath=. - ./a.out - ../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=. - cd ../../../ - echo "Compile and run staticlib-test" - cd resources/examples/staticlib-test - ../../../build/c3c -vv static-lib add.c3 - mv add.a libadd.a - cc test.c -L. -ladd - ./a.out - ../../../build/c3c compile-run test.c3 -L . -l add - cd ../../../ - echo "Compile run unit tests" - cd test - ../build/c3c --max-mem 128 compile-test unit -D SLOW_TESTS - cd .. - echo "Build testproject" - cd resources/testproject - ../../build/c3c run -vvv --trust=full - cd ../../ - echo "Test WASM" - cd resources/testfragments - ../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3 - cd ../../ - echo "Build testproject direct linker" - cd resources/testproject - ../../build/c3c run -vvv --linker=builtin --trust=full - cd ../../ - echo "Init a library & a project" - ./build/c3c init-lib mylib - ls mylib.c3l - ./build/c3c init myproject - ls myproject - echo "run compiler tests" - cd test - ../build/c3c --max-mem 128 compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/ - cd .. - - - name: bundle_output - run: | - mkdir c3 - cp -r lib c3 - cp msvc_build_libraries.py c3 - cp build/c3c c3 - cp README.md c3 - cp releasenotes.md c3 - tar czf c3-openbsd-${{matrix.build_type}}.tar.gz c3 - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: c3-openbsd-${{matrix.build_type}} - path: c3-openbsd-${{matrix.build_type}}.tar.gz - - build-netbsd: - runs-on: ubuntu-latest - - strategy: - # Don't abort runners if a single one fails - fail-fast: false - matrix: - build_type: [Release, Debug] - - steps: - - uses: actions/checkout@v4 - - name: NetBSD VM - uses: vmactions/netbsd-vm@v1.2.6 - with: - prepare: | - /usr/sbin/pkg_add cmake llvm-19.1.7 lld-19.1.7nb1 ninja - - run: | - echo "CMake" - cmake -B build -S . - cmake --build build - echo "Compile and run some examples" - cd resources - ../build/c3c compile examples/base64.c3 - ../build/c3c compile examples/binarydigits.c3 - ../build/c3c compile examples/brainfk.c3 - ../build/c3c compile examples/factorial_macro.c3 - ../build/c3c compile examples/fasta.c3 - ../build/c3c compile examples/gameoflife.c3 - ../build/c3c compile examples/hash.c3 - ../build/c3c compile-only examples/levenshtein.c3 - ../build/c3c compile examples/load_world.c3 - ../build/c3c compile-only examples/map.c3 - ../build/c3c compile examples/mandelbrot.c3 - ../build/c3c compile examples/plus_minus.c3 - ../build/c3c compile examples/nbodies.c3 - ../build/c3c compile examples/spectralnorm.c3 - ../build/c3c compile examples/swap.c3 - ../build/c3c compile examples/contextfree/boolerr.c3 - ../build/c3c compile examples/contextfree/dynscope.c3 - ../build/c3c compile examples/contextfree/guess_number.c3 - ../build/c3c compile examples/contextfree/multi.c3 - ../build/c3c compile examples/contextfree/cleanup.c3 - ../build/c3c compile-run examples/hello_world_many.c3 - ../build/c3c compile-run examples/time.c3 - ../build/c3c compile-run examples/fannkuch-redux.c3 - ../build/c3c compile-run examples/contextfree/boolerr.c3 - ../build/c3c compile-run examples/load_world.c3 - ../build/c3c compile-run examples/process.c3 - ../build/c3c compile-run examples/ls.c3 - ../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz" - cd .. - echo "Compile and run dynlib-test" - cd resources/examples/dynlib-test - ../../../build/c3c -vv dynamic-lib add.c3 - mv add.so libadd.so - cc test.c -L. -ladd -Wl,-rpath=. - ./a.out - ../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=. - cd ../../../ - echo "Compile and run staticlib-test" - cd resources/examples/staticlib-test - ../../../build/c3c -vv static-lib add.c3 - mv add.a libadd.a - ranlib libadd.a - cc test.c -L. -ladd - ./a.out - ../../../build/c3c compile-run test.c3 -L . -l add - cd ../../../ - echo "Compile run unit tests" - cd test - ../build/c3c compile-test unit -D SLOW_TESTS - cd .. - echo "Build testproject" - cd resources/testproject - ../../build/c3c run -vvv --trust=full - cd ../../ - echo "Test WASM" - cd resources/testfragments - ../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3 - cd ../../ - echo "Build testproject direct linker" - cd resources/testproject - ../../build/c3c run -vvv --linker=builtin --trust=full - cd ../../ - echo "Init a library & a project" - ./build/c3c init-lib mylib - ls mylib.c3l - ./build/c3c init myproject - ls myproject - echo "run compiler tests" - cd test - ../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/ - cd .. - - - name: bundle_output - run: | - mkdir c3 - cp -r lib c3 - cp msvc_build_libraries.py c3 - cp build/c3c c3 - cp README.md c3 - cp releasenotes.md c3 - tar czf c3-netbsd-${{matrix.build_type}}.tar.gz c3 - - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: c3-netbsd-${{matrix.build_type}} - path: c3-netbsd-${{matrix.build_type}}.tar.gz + CHECK_NAME=".#c3c-checks" + if [[ ${{ matrix.build_type }} = "Debug" ]]; then CHECK_NAME=".#c3c-debug-checks"; fi + nix build -L "$CHECK_NAME" release: runs-on: ubuntu-22.04 - needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22] + needs: [build-msvc, build-linux, build-mac, build-linux-alpine, build-openbsd, build-netbsd] if: github.ref == 'refs/heads/master' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: actions/download-artifact@v4 - - run: cp -r lib c3-windows-Release - - run: cp -r lib c3-windows-Debug - - run: cp msvc_build_libraries.py c3-windows-Release - - run: cp msvc_build_libraries.py c3-windows-Debug - - run: cp README.md c3-windows-Release - - run: cp README.md c3-windows-Debug - - run: cp releasenotes.md c3-windows-Release - - run: cp releasenotes.md c3-windows-Debug - - run: zip -r c3-windows.zip c3-windows-Release - - run: zip -r c3-windows-debug.zip c3-windows-Debug - - run: mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux-Release/c3-linux.tar.gz - - run: mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz - - run: mv c3-openbsd-Release/c3-openbsd-Release.tar.gz c3-openbsd-Release/c3-openbsd.tar.gz - - run: mv c3-openbsd-Debug/c3-openbsd-Debug.tar.gz c3-openbsd-Debug/c3-openbsd-debug.tar.gz - - run: mv c3-ubuntu-22-Release/c3-ubuntu-22-Release.tar.gz c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz - - run: mv c3-ubuntu-22-Debug/c3-ubuntu-22-Debug.tar.gz c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz - - run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip - - run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip + + - name: Prepare Assets + run: | + # --- Release Assets --- + mv c3-windows-Release/c3-windows-Release.zip c3-windows.zip + mv c3-macos-Release/c3-macos-Release.zip c3-macos.zip + + mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux.tar.gz + mv c3-linux-musl-Release/c3-linux-musl-Release.tar.gz c3-linux-musl.tar.gz + mv c3-openbsd-Release/c3-openbsd-Release.tar.gz c3-openbsd.tar.gz || true + mv c3-netbsd-Release/c3-netbsd-Release.tar.gz c3-netbsd.tar.gz || true + + # --- Debug Assets --- + mv c3-windows-Debug/c3-windows-Debug.zip c3-windows-debug.zip + mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-debug.zip + + mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-debug.tar.gz + mv c3-linux-musl-Debug/c3-linux-musl-Debug.tar.gz c3-linux-musl-debug.tar.gz + mv c3-openbsd-Debug/c3-openbsd-Debug.tar.gz c3-openbsd-debug.tar.gz || true + mv c3-netbsd-Debug/c3-netbsd-Debug.tar.gz c3-netbsd-debug.tar.gz || true + - run: gh release delete latest-prerelease-tag --cleanup-tag -y || true - run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV - - id: create_release - uses: softprops/action-gh-release@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: softprops/action-gh-release@v2 with: tag_name: latest-prerelease-tag name: ${{ env.RELEASE_NAME }} - draft: false prerelease: true files: | c3-windows.zip - c3-windows-debug.zip - c3-linux-Release/c3-linux.tar.gz - c3-linux-Debug/c3-linux-debug.tar.gz - c3-openbsd-Release/c3-openbsd.tar.gz - c3-openbsd-Debug/c3-openbsd-debug.tar.gz - c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz - c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz - c3-macos-Release/c3-macos.zip - c3-macos-Debug/c3-macos-debug.zip + c3-macos.zip + c3-linux.tar.gz + c3-linux-musl.tar.gz + c3-openbsd.tar.gz + c3-netbsd.tar.gz + # --- Debug Artifacts (Uncomment to include) --- + # c3-windows-debug.zip + # c3-macos-debug.zip + # c3-linux-debug.tar.gz + # c3-linux-musl-debug.tar.gz + # c3-openbsd-debug.tar.gz + # c3-netbsd-debug.tar.gz diff --git a/CMakeLists.txt b/CMakeLists.txt index 922676abe..d949faa3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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$<$: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 "$<$:/EHa>" "$<$:/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) - 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) + # 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 + ${_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) diff --git a/lib/std/libc/os/posix.c3 b/lib/std/libc/os/posix.c3 index d0e5a9f08..f50174191 100644 --- a/lib/std/libc/os/posix.c3 +++ b/lib/std/libc/os/posix.c3 @@ -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); diff --git a/lib/std/os/cpu.c3 b/lib/std/os/cpu.c3 index 25c8627c0..0754210cc 100644 --- a/lib/std/os/cpu.c3 +++ b/lib/std/os/cpu.c3 @@ -31,4 +31,41 @@ fn uint num_cpu() Win32_SYSTEM_INFO info; win32::getSystemInfo(&info); return info.dwNumberOfProcessors; -} \ No newline at end of file +} + +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; +} diff --git a/lib/std/os/netbsd/general.c3 b/lib/std/os/netbsd/general.c3 index d63c936b6..385dc05fb 100644 --- a/lib/std/os/netbsd/general.c3 +++ b/lib/std/os/netbsd/general.c3 @@ -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 diff --git a/lib/std/os/posix/threads.c3 b/lib/std/os/posix/threads.c3 index c12b12a40..4a21c6a75 100644 --- a/lib/std/os/posix/threads.c3 +++ b/lib/std/os/posix/threads.c3 @@ -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]; diff --git a/lib/std/threads/os/thread_posix.c3 b/lib/std/threads/os/thread_posix.c3 index 4a6a32e26..81f1c1d0d 100644 --- a/lib/std/threads/os/thread_posix.c3 +++ b/lib/std/threads/os/thread_posix.c3 @@ -181,7 +181,12 @@ fn void? NativeConditionVariable.wait_until(&cond, NativeMutex* mtx, Time time) case errno::OK: return; default: - abort("pthread_cond_timedwait failed, invalid value"); + $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 } } diff --git a/nix/default.nix b/nix/default.nix index c8a589587..b75ea354c 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -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" ]; @@ -73,12 +86,9 @@ in llvmPackages.stdenv.mkDerivation (_: checkPhase = '' 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 diff --git a/resources/examples/gameoflife.c3 b/resources/examples/gameoflife.c3 index eb47f7a5d..f1e0f7bf1 100644 --- a/resources/examples/gameoflife.c3 +++ b/resources/examples/gameoflife.c3 @@ -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; } \ No newline at end of file diff --git a/resources/testproject/lib/clib.c3l/manifest.json b/resources/testproject/lib/clib.c3l/manifest.json index de3a8bff3..fd9b8ca5b 100644 --- a/resources/testproject/lib/clib.c3l/manifest.json +++ b/resources/testproject/lib/clib.c3l/manifest.json @@ -7,6 +7,9 @@ "targets": { "macos-x64": {}, "macos-aarch64": {}, + "netbsd-x64": {}, + "netbsd-aarch64": {}, + "openbsd-x64": {}, "linux-x64": { "cflags": "-fPIE" }, diff --git a/resources/testproject/lib/clib2.c3l/manifest.json b/resources/testproject/lib/clib2.c3l/manifest.json index 00c4ff735..7eec8c903 100644 --- a/resources/testproject/lib/clib2.c3l/manifest.json +++ b/resources/testproject/lib/clib2.c3l/manifest.json @@ -6,6 +6,9 @@ "targets": { "macos-x64": {}, "macos-aarch64": {}, + "netbsd-x64": {}, + "netbsd-aarch64": {}, + "openbsd-x64": {}, "linux-x64": { "cflags-override": "-fPIE" }, diff --git a/scripts/tools/ci_tests.sh b/scripts/tools/ci_tests.sh new file mode 100755 index 000000000..ec46d3d5f --- /dev/null +++ b/scripts/tools/ci_tests.sh @@ -0,0 +1,223 @@ +#!/usr/bin/env bash +# Usage: ./ci_tests.sh [optional_os_mode_override] + +if [ $# -lt 1 ]; then + echo "Usage: ./ci_tests.sh [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 diff --git a/scripts/tools/package_build.sh b/scripts/tools/package_build.sh new file mode 100755 index 000000000..7aec15246 --- /dev/null +++ b/scripts/tools/package_build.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Usage: ./package_build.sh + +if [ $# -lt 2 ]; then + echo "Usage: ./package_build.sh " + 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" \ No newline at end of file diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 4610f38e5..a81f5f649 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -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) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 939f20a90..ad3190a1f 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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); diff --git a/src/compiler/linker.c b/src/compiler/linker.c index b58577d24..cc5779b9b 100644 --- a/src/compiler/linker.c +++ b/src/compiler/linker.c @@ -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 (strip_unused() && compiler.build.type == TARGET_TYPE_EXECUTABLE) - { - add_plain_arg("--gc-sections"); - } - if (is_pie_pic(compiler.platform.reloc_model)) - { - 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"); - } - 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, "crt0.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"); - add_concat_quote_arg("-L", crt_dir); - add_plain_arg("--dynamic-linker=/libexec/ld-elf.so.1"); - if (compiler.platform.os == OS_TYPE_NETBSD) + else { - /* 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"); + if (!is_openbsd) add_concat_file_arg(crt_dir, "crti.o"); + if (is_dylib || is_pie_pic_mode) + { + 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 + { + 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"); + } + if (!is_openbsd) add_concat_file_arg(crt_dir, "crtn.o"); + } + add_concat_quote_arg("-L", crt_dir); + 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) + { + 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 (is_openbsd) + { + linking_add_link(&compiler.linking, "execinfo"); + } + else + { + linking_add_link(&compiler.linking, "gcc"); + linking_add_link(&compiler.linking, "gcc_s"); + } if (compiler.linking.link_math) linking_add_link(&compiler.linking, "m"); - linking_add_link(&compiler.linking, "gcc"); - linking_add_link(&compiler.linking, "gcc_s"); - - add_plain_arg("-L/usr/lib/"); 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 { @@ -932,8 +943,8 @@ static bool link_exe(const char *output_file, const char **files_to_link, unsign UNREACHABLE } #else - success = false; - error = "linking (.exe) is not implemented for C3C compiled without LLVM"; + success = false; + error = "linking (.exe) is not implemented for C3C compiled without LLVM"; #endif if (!success) { @@ -1163,9 +1174,9 @@ bool dynamic_lib_linker(const char *output_file, const char **files, unsigned fi return true; } bool success; - const char *error = NULL; + const char *error = NULL; #if LLVM_AVAILABLE - int count = assemble_link_arguments(args, vec_size(args)); + int count = assemble_link_arguments(args, vec_size(args)); switch (compiler.platform.object_format) { case OBJ_FORMAT_COFF: @@ -1184,8 +1195,8 @@ bool dynamic_lib_linker(const char *output_file, const char **files, unsigned fi UNREACHABLE } #else - success = false; - error = "linking not implemented for c3c compiled without llvm"; + success = false; + error = "linking not implemented for c3c compiled without llvm"; #endif if (!success) { @@ -1219,7 +1230,7 @@ bool static_lib_linker(const char *output_file, const char **files, unsigned fil } return llvm_ar(output_file, files, file_count, format); #else - return false; + return false; #endif } diff --git a/src/utils/file_utils.c b/src/utils/file_utils.c index a061ccfd5..d4954c6f9 100644 --- a/src/utils/file_utils.c +++ b/src/utils/file_utils.c @@ -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 #endif void file_copy_file(const char *src_path, const char *dst_path, bool overwrite) diff --git a/test/src/test_suite_runner.c3 b/test/src/test_suite_runner.c3 index 32ca44167..81d20eb7b 100644 --- a/test/src/test_suite_runner.c3 +++ b/test/src/test_suite_runner.c3 @@ -5,6 +5,8 @@ import std::thread, std::atomic; alias Pool = FixedThreadPool; +Atomic{bool} global_should_quit; + struct TestRunnerContext { Path compiler_path; @@ -13,6 +15,7 @@ struct TestRunnerContext Atomic{int} success_count; Atomic{int} failure_count; Path start_cwd; + Path test_root; bool no_terminal; bool only_skipped; String stdlib; @@ -25,138 +28,38 @@ struct TestRunnerContext struct TestOutput { DString buffer; - String filename; usz index; bool has_output; } - TestRunnerContext context; -fn int main(String[] args) +/* ----------------------------- Utilities ------------------------------ */ +fn void usage(String appname) @noreturn { - libc::signal(libc::SIGINT, fn void (CInt _signal) { - foreach (f : path::ls(mem, context.start_cwd)!!) - { - if (f.str_view().starts_with("_c3test_")) (void)path::rmtree(context.start_cwd.append(mem, f.str_view())); - } - os::exit(1); - }); - - String appname = args[0]; - if (args.len < 3) usage(appname); - - context.no_terminal = !io::stdout().isatty(); - context.start_cwd = path::tcwd()!!; - context.print_mutex.init()!!; - - Path? path = context.start_cwd.tappend(args[1]); - if (catch path) arg_error_exit(appname, "Invalid compiler path: %s", args[1]); - if (!path::is_file(path)) - { - error_exit("Error: Invalid path to compiler: %s (%s relative to %s)", path.path_string, args[1], context.start_cwd); - } - // Ok we're done. - context.compiler_path = path.absolute(tmem)!!; - - context.only_skipped = false; - - usz num_threads = os::num_cpu(); - - for (int i = 3; i < args.len; i++) - { - String arg = args[i]; - switch (arg) - { - case "--no-terminal": - context.no_terminal = true; - case "-s": - case "--skipped": - context.only_skipped = true; - case "--stdlib": - if (i == args.len - 1 || args[i + 1].starts_with("-")) - { - arg_error_exit(appname, "Expected --stdlib to be followed by the path to the alternative standard library."); - } - context.stdlib = args[i + 1]; - i++; - if (!os::native_is_dir(context.stdlib)) - { - error_exit(appname, "Stdlib directory '%s' cannot be found.", context.stdlib); - } - context.stdlib = context.start_cwd.tappend(context.stdlib).absolute(mem)!!.str_view(); - case "--threads": - if (i == args.len - 1 || args[i + 1].starts_with("-")) { - arg_error_exit(appname, "Expected --threads to be followed by the number of threads."); - } - num_threads = args[i + 1].to_int()!!; - i++; - default: - arg_error_exit(appname, "Unknown option '%s'.", args[i]); - } - } - if (context.only_skipped && args[3] != "-s" && args[3] != "--skipped") usage(appname); - - Path? file = args[2].to_tpath().absolute(tmem); - if (catch file) arg_error_exit(appname, "Invalid path: '%s'.", args[2]); - - PathList files_to_test; - files_to_test.init(tmem); - //defer files_to_test.free(); - - switch - { - case path::is_file(file): - files_to_test.push(file); - case path::is_dir(file): - collect_files(file, &files_to_test)!!; - default: - error_exit("Error: Path wasn't to directory or file: %s", file); - } - - context.total_test_files = files_to_test.len(); - - TestOutput[] test_outputs = mem::new_array(TestOutput, context.total_test_files); - //Initialize all the DStrings - for (usz i = 0; i < context.total_test_files; i++) - { - test_outputs[i].index = i; - test_outputs[i].buffer.init(mem); - } - defer - { - foreach (o : test_outputs) - { - o.buffer.free(); - o.filename.free(mem); - } - mem::free(test_outputs); - } - - context.pool.init(num_threads, context.total_test_files)!!; - defer context.pool.stop_and_destroy(); - for (usz i = 0; i < context.total_test_files; i++) - { - context.pool.push(&test_file_threaded, files_to_test[i], &test_outputs[i], i)!!; - } - context.pool.join(); - - int total = context.test_count.load(); - int success = context.success_count.load(); - int skipped = context.skip_count.load(); - int failed = context.failure_count.load(); - int to_run = total - skipped; - bool all_ok = (success == to_run) && (failed == 0); - - if (!context.no_terminal) io::print(all_ok ? (String)Ansi.GREEN : (String)Ansi.RED); - io::printfn("\nFound %d tests: %.1f%% (%d / %d) passed (%d skipped, %d failed).", - total, (100.0 * success) / math::max(1, to_run), success, to_run, skipped, failed); - if (!context.no_terminal) io::print(Ansi.RESET); - - return all_ok ? 0 : 1; + io::printfn("Usage: %s [options]", appname); + io::printn(); + io::printn("Options:"); + io::printn(" -s, --skipped only run skipped tests"); + io::printn(" --no-terminal disable color and dynamic status line"); + io::printn(" --stdlib override the path to stdlib"); + io::printfn(" --threads [N] run tests in N threads (default: %s)", os::num_cpu()); + os::exit(0); } +fn void arg_error_exit(String appname, String fmt, args...) @noreturn +{ + io::printfn(fmt, ...args); + usage(appname); +} +fn void error_exit(String fmt, args...) @noreturn +{ + io::printfn(fmt, ...args); + libc::exit(1); +} + +/* ----------------------- Run / File descriptor types ------------------- */ struct Error { int line; @@ -164,9 +67,6 @@ struct Error bool found; } -<* - Information about a given file that we're running -*> struct RunFile { String name; @@ -199,28 +99,21 @@ fn void RunFile.add_line(&self, String line) } } -fn void RunFile.dealloc(&self) +fn RunFile* create_run_file(Path test_dir, String filename, bool is_output) { - self.close(); - if (self.is_output) + RunFile* rf = mem::tnew(RunFile); + *rf = { .name = filename, .is_output = is_output }; + if (is_output) { - self.expected_lines.free(); + rf.expected_lines.init(tmem); } else { - self.warnings.free(); - self.errors.free(); + rf.warnings.init(tmem); + rf.errors.init(tmem); + rf.file = file::open_path(test_dir.tappend(filename), "wb")!!; } -} - -fn Ref{RunFile}? create_run_file(Path test_dir, String filename, bool is_output) -{ - if (is_output) - { - return ref::new{RunFile}({ .name = filename, .is_output = true }); - } - File file = file::open_path(test_dir.tappend(filename), "wb")!; - return ref::new{RunFile}({ .name = filename, .file = file, .is_output = false }); + return rf; } fn void RunFile.close(&self) @@ -236,29 +129,27 @@ struct RunSettings bool no_deprecation; List{String} opts; String arch; - List{Ref{RunFile}} input_files; - List{Ref{RunFile}} output_files; + List{RunFile*} input_files; + List{RunFile*} output_files; RunFile* current_file; } -<* - Check a line from stderr returned by the compiler -*> -fn bool find_and_mark_error(RunSettings* settings, String type, String file, String line_str, String _col, String message) => @pool() + +/* --------------------- Error parsing helpers -------------------------- */ +fn bool find_and_mark_error(RunSettings* settings, String type, String file, + String line_str, String _col, String message) => @pool() { int line_no = line_str.to_int()!!; - String basename = file.file_tbasename()!!; - foreach (f : settings.input_files) { - if (f.ptr.name != basename) continue; + if (f.name != basename) continue; List {Error}* list; switch (type) { case "Error": - list = &f.ptr.errors; + list = &f.errors; case "Warning": - list = &f.ptr.warnings; + list = &f.warnings; default: io::printfn("FAILED - Unknown error type '%s'", type); return false; @@ -273,344 +164,191 @@ fn bool find_and_mark_error(RunSettings* settings, String type, String file, Str return false; } -<* - Parse all stderr output -*> -fn bool parse_result(DString out, RunSettings settings, List{String}* cmdline, OutStream buffer_stream) +fn void print_errors(OutStream stream, String title, List{String} errors) +{ + if (errors.is_empty()) return; + io::fprintfn(stream, "\n %s:\n ------------------------------", title)!!; + foreach(e : errors) io::fprintn(stream, e)!!; +} + +fn bool parse_result(DString out, RunSettings settings, List{String}* cmdline, OutStream buffer_stream, CInt result) { List{String} unexpected_errors; - - foreach (line : out.str_view().tsplit("\n")) + Splitter lines = out.str_view().tokenize("\n"); + + while (try line = lines.next()) { if (!line) continue; - String[] parts = line.tsplit("|", 5, skip_empty: true); - if (parts.len != 5) + Splitter s = line.tokenize("|"); + + String? type_opt = s.next(); + String? file_opt = s.next(); + String? line_opt = s.next(); + String? col_opt = s.next(); + + if (try type = type_opt && try file = file_opt && + try line_str = line_opt && try col = col_opt && + s.current < line.len) + { + String msg = line[s.current..]; + + if (!find_and_mark_error(&settings, type, file, line_str, col, msg)) + { + unexpected_errors.push(string::tformat(" - %s at %s:%s: %s", + type, file.file_tbasename()!!, line_str, msg)); + } + } + else { io::fprintn(buffer_stream, "\nFAILED - Unexpected response from compiler:")!!; io::fprint(buffer_stream, "Compiler command line: ")!!; foreach(arg : *cmdline) io::fprintf(buffer_stream, "%s ", arg)!!; io::fprintn(buffer_stream)!!; - io::fprintn(buffer_stream, "Output: ----------------------------------------------------------")!!; + io::fprintn(buffer_stream, "Output: ------------------------------------------------------->>>")!!; io::fprintn(buffer_stream, out)!!; - io::fprintn(buffer_stream, "------------------------------------------------------------------")!!; + io::fprintn(buffer_stream, "<<<---------------------------------------------------------------")!!; return false; } - - if (!find_and_mark_error(&settings, ...parts[:5])) - { - unexpected_errors.push(string::tformat(" - %s at %s:%s: %s", parts[0], parts[1].file_tbasename()!!, parts[2], parts[4])); - } } List{String} missed_errors; List{String} missed_warnings; - foreach (file : settings.input_files) { - foreach (&err : file.ptr.errors) if (!err.found) missed_errors.push(string::tformat(" - %s:%d expected: \"%s\"", file.ptr.name, err.line, err.text)); - foreach (&warn : file.ptr.warnings) if (!warn.found) missed_warnings.push(string::tformat(" - %s:%d expected: \"%s\"", file.ptr.name, warn.line, warn.text)); + foreach (&err : file.errors) if (!err.found) + { + missed_errors.push(string::tformat(" - %s:%d expected: \"%s\"", file.name, err.line, err.text)); + } + foreach (&warn : file.warnings) if (!warn.found) + { + missed_warnings.push(string::tformat(" - %s:%d expected: \"%s\"", file.name, warn.line, warn.text)); + } } bool is_ok = unexpected_errors.is_empty() && missed_errors.is_empty() && missed_warnings.is_empty(); + if (is_ok && result != 0) + { + bool has_expected_errors = false; + foreach (file : settings.input_files) + { + if (!file.errors.is_empty()) + { + has_expected_errors = true; + break; + } + } + if (!has_expected_errors) + { + unexpected_errors.push(string::tformat(" - Compiler exited with error code %d but no errors were expected.", result)); + is_ok = false; + } + } + if (is_ok) return true; - io::fprintn(buffer_stream, "FAILED")!!; - - if (!unexpected_errors.is_empty()) - { - io::fprintn(buffer_stream, "\n Unexpected compilation errors:")!!; - io::fprintn(buffer_stream, " ------------------------------")!!; - foreach(e : unexpected_errors) io::fprintn(buffer_stream, e)!!; - } - - if (!missed_errors.is_empty()) - { - io::fprintn(buffer_stream, "\n Errors that never occurred:")!!; - io::fprintn(buffer_stream, " ---------------------------")!!; - foreach(e : missed_errors) io::fprintn(buffer_stream, e)!!; - } - - if (!missed_warnings.is_empty()) - { - io::fprintn(buffer_stream, "\n Warnings that never occurred:")!!; - io::fprintn(buffer_stream, " -----------------------------")!!; - foreach(w : missed_warnings) io::fprintn(buffer_stream, w)!!; - } - + print_errors(buffer_stream, "Unexpected compilation errors", unexpected_errors); + print_errors(buffer_stream, "Errors that never occurred", missed_errors); + print_errors(buffer_stream, "Warnings that never occurred", missed_warnings); io::fprintn(buffer_stream)!!; return false; } -<* - Parse trailing directives #warning and #error -*> -fn void parse_trailing_directive(int line_number, String line, RunSettings* settings, bool is_single) +/* --------------------- Directive parsing ----------------------------- */ +fn bool parse_trailing_directive(int line_number, String line, RunSettings* settings, bool is_single) { - if (settings.current_file.is_output) return; + if (settings.current_file.is_output) return true; usz index = line.rindex_of("// #")!! + 4; - line = line[index..].trim(); - switch + String clean_line = line[index..].trim(); + + Splitter s = clean_line.tokenize(":"); + String? key = s.next(); + + if (@catch(key) || s.current > clean_line.len) { - case line.starts_with("warning:"): - line = line[8..].trim(); - settings.current_file.warnings.push({ line_number, line, false }); - case line.starts_with("error:"): - line = line[6..].trim(); - settings.current_file.errors.push({ line_number, line, false }); - default: - error_exit("FAILED - Unknown trailing directive '%s'", line); - } -} - -<* - Parse header directives, #error, #safe, #debuginfo, #opt, #target, #deprecation, #file and #expect -*> -fn void parse_header_directive(int* line_no, String line, RunSettings* settings, bool is_single, Path test_dir) -{ - line = line[4..].trim(); - switch - { - case line.starts_with("error:"): - line = line[6..].trim(); - if (settings.current_file.is_output) return; - settings.current_file.errors.push({ *line_no, line, false }); - case line.starts_with("safe:"): - if (settings.current_file.is_output) return; - settings.safe = line[5..].trim() == "yes"; - case line.starts_with("debuginfo:"): - if (settings.current_file.is_output) return; - settings.debuginfo = line[10..].trim() == "yes"; - case line.starts_with("opt:"): - if (settings.current_file.is_output) return; - settings.opts.push(line[4..].trim()); - case line.starts_with("target:"): - if (settings.current_file.is_output) return; - settings.arch = line[7..].trim(); - case line.starts_with("deprecation:"): - if (settings.current_file.is_output) return; - settings.no_deprecation = line[12..].trim() == "no"; - case line.starts_with("file:"): - if (is_single) error_exit("FAILED - 'file' directive only allowed with .c3t"); - settings.current_file.close(); - line = line[5..].trim(); - Ref{RunFile} file = create_run_file(test_dir, line, false)!!; - settings.current_file = file.ptr; - *line_no = 1; - settings.input_files.push(file); - case line.starts_with("expect:"): - if (is_single) error_exit("FAILED - 'expect' directive only allowed with .c3t"); - line = line[7..].trim(); - if (settings.current_file) settings.current_file.close(); - Ref{RunFile} file = create_run_file(test_dir, line, true)!!; - settings.current_file = file.ptr; - settings.output_files.push(file); - default: - io::printfn("FAILED - Unknown header directive '%s'", line); - context.failure_count.add(1); - return; - } -} - -fn void test_file_threaded(any[] data) -{ - @pool_init(mem, 1024*1024) { - Path file_path = *anycast(data[0], Path)!!; - TestOutput* output = *anycast(data[1], TestOutput*)!!; - usz index = *anycast(data[2], usz)!!; - if (!test_file(file_path, output, index)) - { - context.failure_count.add(1); - if (!context.no_terminal) - { - Mutex.@in_lock(&context.print_mutex) - { - io::printn(); - io::printn("------------------------------------------------------------------"); - io::printf("- %d/%d %s: %s", index, context.failure_count.load(), file_path, output.buffer)!!; - io::printn("------------------------------------------------------------------"); - }; - } - } - if (context.no_terminal) - { - Mutex.@in_lock(&context.print_mutex) - { - io::printf("- %d/%d %s: ", context.test_count.load(), context.failure_count.load(), file_path)!!; - io::printn(output.buffer.str_view()); - }; - } - }; -} - -<* - Test a file -*> -fn bool test_file(Path file_path, TestOutput* output, usz index) -{ - context.test_count.add(1); - update_status(index, file_path, output); - bool single; - int thread_id = context.thread_id_counter.add(1); - $if env::WIN32: - Path test_dir = context.start_cwd.tappend(string::tformat("_c3test_%s", thread_id))!!; - $else - Path test_dir = path::temp_directory(tmem).tappend(string::tformat("_c3test_%s", thread_id))!!; - $endif - (void)path::rmtree(test_dir); - defer (void)path::rmtree(test_dir); - if (@catch(path::mkdir(test_dir))) - { - io::fprintfn((OutStream)&output.buffer, "FAILED - Failed to create temp test directory '%s'.", test_dir)!!; - os::exit(1); - } - switch (file_path.extension() ?? "") - { - case "c3": - single = true; - case "c3t": - single = false; - default: - io::fprintfn((OutStream)&output.buffer, "FAILED - Unexpected file name '%s', expected a file with a '.c3' or '.c3t' suffix.", file_path.str_view())!!; - return false; - } - File? f = file::open_path(file_path, "rb"); - if (catch f) - { - io::fprintfn((OutStream)&output.buffer, "FAILED - Failed to open '%s'.", file_path)!!; + io::printfn("FAILED - Unknown trailing directive '%s'", clean_line); return false; } - defer (void)f.close(); - if (context.only_skipped) - { - bool has_skip = false; - while (try line = io::treadline(&f)) - { - if (line.contains("#skip")) { has_skip = true; break; } - } - if (!has_skip) - { - context.success_count.add(1); - return true; - } - (void)f.seek(0); - } + String value = clean_line[s.current..].trim(); - RunSettings settings; - Ref{RunFile} rf = create_run_file(test_dir, file_path.basename()[..^(single ? 4 : 5)].tconcat(".c3"), false)!!; - settings.current_file = rf.ptr; - settings.input_files.push(rf); - int line_no = 1; - while (try line = io::treadline(&f)) + switch (key!!) { - if (line.starts_with("// #") || line.starts_with("/* #")) - { - if (line.contains("#skip")) - { - context.skip_count.add(1); - return true; - } - parse_header_directive(&line_no, line, &settings, single, test_dir); - continue; - } - else if (line.contains("// #")) - { - parse_trailing_directive(line_no, line, &settings, single); - } - settings.current_file.add_line(line); - line_no++; - } - settings.current_file.close(); - defer - { - foreach (&r : settings.input_files) r.release(); - foreach (&r : settings.output_files) r.release(); - } - - // Construct the compile line - List{String} cmdline; - cmdline.push(context.compiler_path.str_view()); - $if env::OPENBSD: - cmdline.push("--max-mem"); - cmdline.push("128"); - $endif - cmdline.push("compile-only"); - cmdline.push("--test"); - if (context.stdlib) - { - cmdline.push("--stdlib"); - cmdline.push(context.stdlib); - } - cmdline.push("--llvm-out"); - cmdline.push(test_dir.str_view()); - cmdline.push("--build-dir"); - cmdline.push(test_dir.tappend("build").str_view()!!); - foreach (file : settings.input_files) - { - cmdline.push(test_dir.tappend(file.ptr.name).str_view()!!); - } - if (!single) cmdline.push("--emit-llvm"); - cmdline.push(settings.debuginfo ? "-g" : "-g0"); - if (settings.arch) - { - cmdline.push("--target"); - cmdline.push(settings.arch); - } - cmdline.push("-O0"); - if (settings.no_deprecation) cmdline.push("--silence-deprecation"); - cmdline.push(settings.safe ? "--safe=yes" : "--safe=no"); - foreach (opt : settings.opts) - { - cmdline.push(opt); - } - SubProcess compilation = process::create(cmdline.array_view(), { .search_user_path, .no_window, .inherit_environment })!!; - defer compilation.destroy(); - CInt result = compilation.join() ?? 1; - DString out; - io::copy_to(&&compilation.stderr(), &out)!!; - if (result != 0 && result != 1) - { - (void)io::copy_to(&&compilation.stdout(), (OutStream)&out); - io::fprintfn((OutStream)&output.buffer, "FAILED - Error(%s): %s", result, out)!!; - return false; - } - if (!parse_result(out, settings, &cmdline, (OutStream)&output.buffer)) return false; - foreach (file : settings.output_files) - { - Path expected_file = test_dir.tappend(file.ptr.name)!!; - if (!file::exists(expected_file.str_view())) - { - io::fprintfn((OutStream)&output.buffer, "FAILED - Did not compile file %s.", file.ptr.name)!!; + case "warning": + settings.current_file.warnings.push({ line_number, value, false }); + case "error": + settings.current_file.errors.push({ line_number, value, false }); + default: + io::printfn("FAILED - Unknown trailing directive '%s'", clean_line); return false; - } - File file_ll = file::open(expected_file.str_view(), "rb")!!; - defer (void)file_ll.close(); - String? next = file.ptr.expected_lines.pop_first(); - while (try line = io::treadline(&file_ll) && try next_value = next) - { - line = line.trim(); - if (line == "") continue; - if (line.contains(next_value)) - { - next = file.ptr.expected_lines.pop_first(); - } - } - if (try next) - { - io::fprintfn((OutStream)&output.buffer, `FAILED - %s did not contain: %s`, file.ptr.name, next)!!; - io::fprintfn((OutStream)&output.buffer, "\n\n\n---------------------------------------------------> %s\n\n", file.ptr.name)!!; - (void)file_ll.seek(0); - io::fprintn((OutStream)&output.buffer, (String)io::read_fully(tmem, &file_ll))!!; - io::fprintfn((OutStream)&output.buffer, "<---------------------------------------------------- %s\n", file_path)!!; - return false; - } } - context.success_count.add(1); - update_status(index, {}, output); return true; } +fn void parse_header_directive(int* line_no, String line, RunSettings* settings, bool is_single, Path test_dir) +{ + String clean_line = line[4..].trim(); + Splitter s = clean_line.tokenize(":"); + String? key_opt = s.next(); + + if (catch err = key_opt) return; + String key = key_opt; + + String value = (s.current <= clean_line.len) ? clean_line[s.current..].trim() : ""; + + bool is_output = settings.current_file.is_output; + + switch (key) + { + case "error": + if (!is_output) settings.current_file.errors.push({ *line_no, value, false }); + case "safe": + if (!is_output) settings.safe = value == "yes"; + case "debuginfo": + if (!is_output) settings.debuginfo = value == "yes"; + case "opt": + if (!is_output) settings.opts.push(value); + case "target": + if (!is_output) settings.arch = value; + case "deprecation": + if (!is_output) settings.no_deprecation = value == "no"; + case "file": + if (is_single) error_exit("FAILED - 'file' directive only allowed with .c3t"); + settings.current_file.close(); + RunFile* file = create_run_file(test_dir, value, false); + settings.current_file = file; + *line_no = 1; + settings.input_files.push(file); + case "expect": + if (is_single) error_exit("FAILED - 'expect' directive only allowed with .c3t"); + if (settings.current_file) settings.current_file.close(); + RunFile* ofile = create_run_file(test_dir, value, true); + settings.current_file = ofile; + settings.output_files.push(ofile); + default: + io::printfn("FAILED - Unknown header directive '%s'", clean_line); + context.failure_count.add(1); + } +} + +/* ----------------------- Status / Progress --------------------------- */ +struct Winsize { ushort ws_row, ws_col, ws_xpixel, ws_ypixel; } +fn int get_terminal_width() +{ + $if env::LINUX || env::DARWIN || env::BSD_FAMILY: + const ulong TIOCGWINSZ @if(env::LINUX) = 0x5413; + const ulong TIOCGWINSZ @if(env::DARWIN || env::BSD_FAMILY) = 0x40087468; + + Winsize ws; + if (libc::ioctl(1, TIOCGWINSZ, &ws) == 0) return (int)ws.ws_col; + $endif + + return 80; +} + +const String CLEAR_LINE = "\e[2K"; +const String MOVETO_COLUMN_ONE = "\e[1G"; + fn void update_status(usz index, Path filename, TestOutput* output) { if (context.no_terminal) @@ -620,7 +358,8 @@ fn void update_status(usz index, Path filename, TestOutput* output) return; } - Mutex.@in_lock(&context.print_mutex) { + Mutex.@in_lock(&context.print_mutex) + { bool has_entry = filename.str_view().len != 0; const String[8] PARTS = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉" }; int progress_bar_width = 25; @@ -630,63 +369,504 @@ fn void update_status(usz index, Path filename, TestOutput* output) double fraction = total > 0.0 ? finished / (double)total : 0; int full_width = (int)math::floor(fraction * progress_bar_width); int remainder = (int)math::floor(8 * (fraction * progress_bar_width % 1.0)); - io::print("\e[0m\e[2K\e[1GTest progress: ["); + io::printf("%s%s%sTest progress: [", Ansi.RESET, CLEAR_LINE, MOVETO_COLUMN_ONE); usz failed = context.failure_count.load(); - io::print(failed ? "\e[31m" : "\e[32m"); + io::print(failed ? Ansi.RED : Ansi.GREEN); for (int i = 0; i < full_width; i++) io::print("█"); if (full_width != progress_bar_width) io::print(PARTS[remainder]); for (int i = full_width + 1; i < progress_bar_width; i++) io::print(" "); - io::printf("\e[0m] %.1f%% complete (%d failed)", fraction * 100.0, failed); + io::printf("%s] %.1f%% complete (%d failed)", Ansi.RESET, fraction * 100.0, failed); if (has_entry) io::printf(" - Testing: %s", filename.basename()); (void)io::stdout().flush(); }; } +fn void print_failure_box(usz index, Path file_path, String error_output) +{ + Mutex.@in_lock(&context.print_mutex) + { + int width = get_terminal_width(); + + io::printfn("%s%s%s", Ansi.RESET, CLEAR_LINE, MOVETO_COLUMN_ONE); + io::print(Ansi.RED +++ "\u256d"); + for (int i = 2; i < width; i++) io::print("\u2500"); + io::printn("\u256e" +++ Ansi.RESET); + + io::printf("- %d/%d %s: %s", index, context.failure_count.load(), file_path, error_output)!!; + + if (!error_output.ends_with("\n")) io::printn(); + + io::print(Ansi.RED +++ "\u2570"); + for (int i = 2; i < width; i++) io::print("\u2500"); + io::printn("\u256f" +++ Ansi.RESET); + }; +} + +/* ----------------------- File collection ----------------------------- */ fn void? collect_files(Path file_path, List{Path}* files) { PathList entries = path::ls(tmem, file_path)!!; - foreach (f : entries) { Path current_file = file_path.append(tmem, f.str_view())!; switch { - case path::is_dir(current_file): - collect_files(current_file, files)!; + case path::is_dir(current_file): collect_files(current_file, files)!; case path::is_file(current_file): switch (current_file.extension() ?? "") { case "c3": - case "c3t": - files.push(current_file); + case "c3t": files.push(current_file); } default: io::printfn("Skip %s", current_file); - // Ignore } } } -fn void usage(String appname) @noreturn +/* ----------------------- Core test logic ------------------------------ */ +fn bool test_file(Path file_path, TestOutput* output, usz index) { - io::printfn("Usage: %s [options]", appname); - io::printn(); - io::printn("Options:"); - io::printn(" -s, --skipped only run skipped tests"); - io::printn(" --no-terminal disable color and dynamic status line"); - io::printn(" --stdlib override the path to stdlib"); - io::printfn(" --threads [N] run tests in N threads (default: %s)", os::num_cpu()); - os::exit(0); + if (global_should_quit.load()) return false; + context.test_count.add(1); + update_status(index, file_path, output); + + bool single; + int thread_id = context.thread_id_counter.add(1); + + Path test_dir = context.test_root.tappend(string::tformat("_c3test_%d", thread_id))!!; + + (void)path::rmtree(test_dir); + defer @catch(path::rmtree(test_dir)); + + if (@catch(path::mkdir(test_dir))) + { + io::fprintfn((OutStream)&output.buffer, "FAILED - Failed to create temp test directory '%s'.", test_dir)!!; + os::exit(1); + } + + switch (file_path.extension() ?? "") + { + case "c3": single = true; + case "c3t": single = false; + default: + io::fprintfn((OutStream)&output.buffer, + "FAILED - Unexpected file name '%s', expected a file with a '.c3' or '.c3t' suffix.", + file_path.str_view())!!; + return false; + } + + char[]? content_raw = file::load_path_temp(file_path); + if (catch content_raw) + { + io::fprintfn((OutStream)&output.buffer, "FAILED - Failed to open/read '%s'.", file_path)!!; + return false; + } + String content = (String)content_raw; + + RunSettings settings; + settings.opts.init(tmem); + settings.input_files.init(tmem); + settings.output_files.init(tmem); + + RunFile* rf = create_run_file( + test_dir, + file_path.basename()[..^(single ? 4 : 5)].tconcat(".c3"), + false); + + settings.current_file = rf; + settings.input_files.push(rf); + + bool found_skip_directive = false; + + int line_no = 1; + + Splitter lines = content.tokenize_all("\n", skip_last: true); + while (try line = lines.next()) + { + line = line.trim_right("\r"); + + if (line.starts_with("// #") || line.starts_with("/* #")) + { + if (line.contains("#skip")) + { + found_skip_directive = true; + if (!context.only_skipped) + { + context.skip_count.add(1); + settings.current_file.close(); + foreach (r : settings.input_files) r.close(); + return true; + } + continue; + } + parse_header_directive(&line_no, line, &settings, single, test_dir); + continue; + } + else if (line.contains("// #")) + { + if (!parse_trailing_directive(line_no, line, &settings, single)) + { + foreach (r : settings.input_files) r.close(); + return false; + } + } + + settings.current_file.add_line(line); + line_no++; + } + + settings.current_file.close(); + defer foreach (r : settings.input_files) r.close(); + + if (context.only_skipped && !found_skip_directive) + { + context.skip_count.add(1); + return true; + } + + List{String} cmdline; + cmdline.init(tmem, 32); + cmdline.push(context.compiler_path.str_view()); + + cmdline.push_all({"compile-only", "--test","--max-mem", "512"}); + + if (context.stdlib) + { + cmdline.push_all({"--stdlib", context.stdlib}); + } + + cmdline.push_all({"--llvm-out", test_dir.str_view() }); + cmdline.push_all({"--build-dir", test_dir.tappend("build").str_view()!! }); + + foreach (file : settings.input_files) + { + cmdline.push(test_dir.tappend(file.name).str_view()!!); + } + + if (!single) + { + cmdline.push("--emit-llvm"); + } + + cmdline.push(settings.debuginfo ? "-g" : "-g0"); + + if (settings.arch) + { + cmdline.push("--target"); + cmdline.push(settings.arch); + } + + cmdline.push("-O0"); + + if (settings.no_deprecation) + { + cmdline.push("--silence-deprecation"); + } + + cmdline.push(settings.safe ? "--safe=yes" : "--safe=no"); + + foreach (opt : settings.opts) + { + cmdline.push(opt); + } + + + if (global_should_quit.load()) return false; + SubProcess compilation = process::create( + cmdline.array_view(), + { .search_user_path, .no_window, .inherit_environment })!!; + + defer compilation.destroy(); + + DString out; + out.init(tmem, 4096); + char[4096] tmp_buf; + InStream stderr_stream = &&compilation.stderr(); + while (try read = stderr_stream.read(&tmp_buf)) + { + if (read == 0) break; + if (out.len() < 65536) { + out.append(tmp_buf[:read]); + } + } + + CInt result = compilation.join() ?? 1; + + if (result != 0 && result != 1) + { + out.append("\n[STDOUT]:\n"); + InStream stdout_stream = &&compilation.stdout(); + while (try read = stdout_stream.read(&tmp_buf)) + { + if (read == 0) break; + if (out.len() < 70000) out.append(tmp_buf[:read]); + } + + io::fprintfn((OutStream)&output.buffer, "FAILED - Error(%s): %s", result, out)!!; + return false; + } + + if (!parse_result(out, settings, &cmdline, (OutStream)&output.buffer, result)) return false; + + foreach (file : settings.output_files) + { + Path expected_file = test_dir.tappend(file.name)!!; + if (!file::exists(expected_file.str_view())) + { + io::fprintfn((OutStream)&output.buffer, "FAILED - Did not compile file %s.", file.name)!!; + return false; + } + + File file_ll = file::open(expected_file.str_view(), "rb")!!; + defer (void)file_ll.close(); + + String? next = file.expected_lines.pop_first(); + while (try line = io::treadline(&file_ll) && try next_value = next) + { + line = line.trim(); + if (line == "") continue; + if (line.contains(next_value)) + { + next = file.expected_lines.pop_first(); + } + } + + if (try next) + { + io::fprintfn((OutStream)&output.buffer, + "FAILED - %s did not contain: %s", file.name, next)!!; + io::fprintfn((OutStream)&output.buffer, + "\n\n------------------------------------------------>>>> %s\n", file.name)!!; + (void)file_ll.seek(0); + io::fprintn((OutStream)&output.buffer, + (String)io::read_fully(tmem, &file_ll))!!; + io::fprintfn((OutStream)&output.buffer, + "<<<<------------------------------------------------- %s\n", file_path)!!; + return false; + } + } + + context.success_count.add(1); + update_status(index, {}, output); + return true; } -fn void arg_error_exit(String appname, String fmt, args...) @noreturn +fn void test_file_threaded(any[] data) { - io::printfn(fmt, ...args); - usage(appname); + @pool_init(mem, 1024 * 1024) + { + Path file_path = *anycast(data[0], Path)!!; + TestOutput* output = *anycast(data[1], TestOutput*)!!; + usz index = *anycast(data[2], usz)!!; + + if (global_should_quit.load()) return; + + if (!test_file(file_path, output, index)) + { + if (global_should_quit.load()) return; + context.failure_count.add(1); + if (!context.no_terminal) + { + print_failure_box(index, file_path, output.buffer.str_view()); + } + } + + if (context.no_terminal) + { + if (global_should_quit.load() || output.buffer.len() == 0) return; + Mutex.@in_lock(&context.print_mutex) + { + io::printf("- %d/%d %s: ", + context.test_count.load(), + context.failure_count.load(), + file_path)!!; + io::printn(output.buffer.str_view()); + }; + } + }; } -fn void error_exit(String fmt, args...) @noreturn +/* --------------------------------------------------------------------- */ +/* ----------------------------- Main ---------------------------------- */ +/* --------------------------------------------------------------------- */ +fn int main(String[] args) { - io::printfn(fmt, ...args); - libc::exit(1); -} + libc::signal(libc::SIGINT, fn void (CInt _signal) + { + global_should_quit.store(true); + }); + + String appname = args[0]; + if (args.len < 3) usage(appname); + + context.no_terminal = !io::stdout().isatty(); + context.start_cwd = path::tcwd()!!; + context.print_mutex.init()!!; + + $if env::WIN32: + context.test_root = context.start_cwd; + $else + context.test_root = path::temp_directory(mem)!!; + $endif + + Path? path = args[1].to_tpath(); + if (catch path) arg_error_exit(appname, "Invalid compiler path: %s", args[1]); + context.compiler_path = path.absolute(tmem)!!; + if (!path::is_file(context.compiler_path)) + { + error_exit("Error: Invalid path to compiler: %s (%s relative to %s)", context.compiler_path, args[1], context.start_cwd); + } + + context.only_skipped = false; + usz num_threads = os::num_cpu(); + + for (int i = 3; i < args.len; i++) + { + String arg = args[i]; + switch (arg) + { + case "--no-terminal": + context.no_terminal = true; + case "-s": + case "--skipped": + context.only_skipped = true; + case "--stdlib": + if (i == args.len - 1 || args[i + 1].starts_with("-")) + { + arg_error_exit(appname, + "Expected --stdlib to be followed by the path to the alternative standard library."); + } + context.stdlib = args[i + 1]; + i++; + if (!os::native_is_dir(context.stdlib)) + { + error_exit(appname, + "Stdlib directory '%s' cannot be found.", + context.stdlib); + } + context.stdlib = context.start_cwd + .tappend(context.stdlib) + .absolute(mem)!! + .str_view(); + case "--threads": + if (i == args.len - 1 || args[i + 1].starts_with("-")) + { + arg_error_exit(appname, + "Expected --threads to be followed by the number of threads."); + } + num_threads = args[i + 1].to_int()!!; + i++; + default: + arg_error_exit(appname, "Unknown option '%s'.", args[i]); + } + } + + Path? file = args[2].to_tpath().absolute(tmem); + if (catch file) arg_error_exit(appname, "Invalid path: '%s'.", args[2]); + + PathList files_to_test; + files_to_test.init(tmem); + + switch + { + case path::is_file(file): + files_to_test.push(file); + case path::is_dir(file): + collect_files(file, &files_to_test)!!; + default: + error_exit("Error: Path wasn't to directory or file: %s", file); + } + + context.total_test_files = files_to_test.len(); + + TestOutput[] test_outputs = mem::new_array(TestOutput, context.total_test_files); + for (usz i = 0; i < context.total_test_files; i++) + { + test_outputs[i].index = i; + test_outputs[i].buffer.init(mem); + } + + defer + { + foreach (o : test_outputs) + { + o.buffer.free(); + } + mem::free(test_outputs); + } + + context.pool.init(num_threads, context.total_test_files)!!; + defer context.pool.stop_and_destroy(); + + for (usz i = 0; i < context.total_test_files; i++) + { + context.pool.push(&test_file_threaded, + files_to_test[i], + &test_outputs[i], + i)!!; + } + + while (context.success_count.load() + context.failure_count.load() + context.skip_count.load() < context.total_test_files) + { + if (global_should_quit.load()) + { + io::printn("\n[Ctrl+C] Stopping tests and cleaning up..."); + break; + } + thread::sleep_ms(50); + } + + context.pool.stop_and_destroy(); + + io::printn(" Removing temporary directories..."); + PathList entries = path::ls(mem, context.test_root)!!; + foreach (f : entries) + { + if (f.str_view().starts_with("_c3test_")) + { + path::rmtree(context.test_root.tappend(f.str_view())!!)!!; + } + } + + if (global_should_quit.load()) os::exit(1); + + int total_files = context.test_count.load(); + int success = context.success_count.load(); + int skipped_files = context.skip_count.load(); + int failed = context.failure_count.load(); + + int display_total = total_files; + int display_skipped = skipped_files; + int display_run = total_files - skipped_files; + + if (context.only_skipped) + { + display_skipped = 0; + display_total = success + failed; + display_run = display_total; + } + + bool all_ok = (success == display_run) && (failed == 0); + + if (!context.no_terminal) + { + io::print(all_ok ? (String)Ansi.GREEN : (String)Ansi.RED); + } + + io::printfn( + "\nFound %d tests: %.1f%% (%d / %d) passed (%d skipped, %d failed).", + display_total, + (100.0 * success) / math::max(1, display_run), + success, + display_run, + display_skipped, + failed); + + if (!context.no_terminal) + { + io::print(Ansi.RESET); + } + + return all_ok ? 0 : 1; +} \ No newline at end of file diff --git a/test/test_suite/examples/gameoflife.c3 b/test/test_suite/examples/gameoflife.c3 index 623399150..6f337ec79 100644 --- a/test/test_suite/examples/gameoflife.c3 +++ b/test/test_suite/examples/gameoflife.c3 @@ -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; } \ No newline at end of file diff --git a/test/unit/stdlib/math/math.c3 b/test/unit/stdlib/math/math.c3 index faf4362b1..4c94c677a 100644 --- a/test/unit/stdlib/math/math.c3 +++ b/test/unit/stdlib/math/math.c3 @@ -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 :