diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..47f1966 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,55 @@ +root = true + +# Language indent/EOL/charset overlap with .vscode/settings.json. +# Rulers, renderWhitespace, detectIndentation, mergeEditor, and +# language-server settings cannot be expressed here — they stay in +# settings.json. + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +# [bat] +[*.{bat,cmd}] +end_of_line = crlf +indent_size = 4 +indent_style = space + +# [c] +[*.{c,h}] +indent_size = 4 +indent_style = space + +# [cmake] +[{CMakeLists.txt,*.cmake}] +indent_size = 4 +indent_style = tab + +# [cpp] +[*.{cpp,cxx,hpp,hxx}] +indent_size = 4 +indent_style = space + +# [json] +[*.json] +indent_size = 2 +indent_style = space + +# [markdown] +[*.md] +indent_size = 2 +indent_style = space + +# [shellscript] +[*.{bash,sh,zsh}] +indent_size = 2 +indent_style = space + +# [yaml] +[*.{yaml,yml}] +indent_size = 2 +indent_style = space diff --git a/.gitattributes b/.gitattributes index 5495a20..1ad0590 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,17 +1,126 @@ +# .gitattributes — C / C++ (GitHub-hosted) +# +# Sources: GitHub Docs (line endings), git-scm gitattributes (diff=cpp), +# gitattributes/gitattributes C++.gitattributes + Common.gitattributes, +# github-linguist overrides. +# +# Linguist language-bar policy (Synesis): classify public C API headers as C +# and *.cmake as CMake scripts; keep helper scripts, IDE project files, and +# CMakeLists.txt config out of the advertised language mix. Prefer +# -linguist-detectable over linguist-detectable=false. + +# Default: detect text, normalize to LF in the repository +* text=auto + +# --- Source --- +*.c text diff=cpp +*.c++ text diff=cpp +*.cc text diff=cpp +*.cpp text diff=cpp +*.cxx text diff=cpp +*.h text diff=cpp +*.h++ text diff=cpp +*.hh text diff=cpp +*.hpp text diff=cpp +*.hxx text diff=cpp +*.inc text +*.inl text +*.ipp text +*.tcc text +*.tpp text + +# --- Build / config --- +*.am text +*.cmake text +*.m4 text +*.mak text +*.mk text +*akefile text +CMakeLists.txt text +configure text eol=lf +configure.ac text + +# --- Scripts --- +*.bash text eol=lf +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf +*.sh text eol=lf +*.zsh text eol=lf + +# --- Docs / meta --- +*.adoc text +*.markdown text diff=markdown +*.md text diff=markdown +*.txt text +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +COPYING text +LICENSE text +NEWS text +README text +TODO text +.gitattributes text +.gitignore text + +# --- Serialisation --- +*.json text +*.toml text +*.xml text +*.yaml text +*.yml text + +# --- Compiled / binary artefacts --- +*.a binary +*.dll binary +*.dylib binary +*.exe binary +*.gch binary +*.la binary +*.lai binary +*.lib binary +*.lo binary +*.o binary +*.obj binary +*.out binary +*.pch binary +*.slo binary +*.so binary + +# --- Archives / images (common) --- +*.gif binary +*.gz binary +*.ico binary +*.jpeg binary +*.jpg binary +*.png binary +*.tar binary +*.zip binary + +# --- GitHub Linguist --- +**/build/** linguist-generated +**/cmake-build-*/** linguist-generated + +# Classification (C-primary / C API with C++ tests; *.cmake as CMake scripts) *.c linguist-language=C -*.cmake linguist-detectable=false -*.cmd linguist-detectable=false +*.cmake linguist-language=CMake *.cpp linguist-language=C++ *.cxx linguist-language=C++ -*.dsp linguist-detectable=false -*.dsw linguist-detectable=false *.h linguist-language=C *.hpp linguist-language=C++ -*.html linguist-detectable=false *.hxx linguist-language=C++ -*.sh linguist-detectable=false -*.sln linguist-detectable=false -*.vcxproj linguist-detectable=false -*.vimrc linguist-detectable=false -CMakeLists.txt linguist-detectable=false -makefile linguist-detectable=false + +# Keep the bar on library languages (helpers / legacy IDE / docs / +# CMakeLists.txt config; leave *.cmake detectable as CMake) +*.cmd -linguist-detectable +*.dsp -linguist-detectable +*.dsw -linguist-detectable +*.html -linguist-detectable +*.sh -linguist-detectable +*.sln -linguist-detectable +*.vcxproj -linguist-detectable +*.vimrc -linguist-detectable +CMakeLists.txt -linguist-detectable +makefile -linguist-detectable diff --git a/.github/actions/install-sis-deps/action.yml b/.github/actions/install-sis-deps/action.yml new file mode 100644 index 0000000..73209b7 --- /dev/null +++ b/.github/actions/install-sis-deps/action.yml @@ -0,0 +1,109 @@ +name: Install Synesis dependencies +description: >- + Clones, builds, and installs the named Synesis Information Systems + dependencies into a single CMake prefix, and exports that prefix both as + the SIS_DEPS environment variable and as the `prefix` output. + +inputs: + build-type: + description: CMake build type, for single-configuration generators + required: false + default: Release + c-compiler: + description: C compiler name, or the selector `cl` + required: true + cpp-compiler: + description: C++ compiler name; unused for the `cl` selector + required: true + dependencies: + description: >- + Whitespace-separated synesissoftware repository names, in dependency + order. Each is configured against those already installed, so this + list is deliberately NOT in lexicographic order. + required: true + install-prefix: + description: Directory into which every dependency is installed + required: false + default: '' + toolchain: + description: >- + Toolchain selector, for a toolchain that is not implied by the compiler + names and needs its own generator; presently only `mingw`. Empty + otherwise, in which case the compiler names alone select the toolchain. + required: false + default: '' + +outputs: + prefix: + description: Directory into which every dependency was installed + value: ${{ steps.install.outputs.prefix }} + +runs: + using: composite + + steps: + - id: install + shell: bash + run: | + set -euo pipefail + + PREFIX="${{ inputs.install-prefix }}" + [ -n "$PREFIX" ] || PREFIX="${RUNNER_TEMP}/sis-deps" + + mkdir -p "$PREFIX" + + if [ "${{ inputs.toolchain }}" = "mingw" ]; then + export PATH="/mingw64/bin:$PATH" + fi + + for dep in ${{ inputs.dependencies }}; do + + echo "::group::Installing $dep" + + SRC="${RUNNER_TEMP}/${dep}-src" + BUILD="${RUNNER_TEMP}/${dep}-build" + + git clone --depth 1 "https://github.com/synesissoftware/${dep}" "$SRC" + + # NOTE: the generator and the build-type/compiler options are the + # *only* things that may differ between these paths; every other + # -D option is passed in all cases, so that no branch can silently + # ignore one. + if [ "${{ inputs.c-compiler }}" = "cl" ]; then + cmake -S "$SRC" -B "$BUILD" \ + -DBUILD_EXAMPLES=OFF \ + -DBUILD_TESTING=OFF \ + -DCMAKE_DISABLE_FIND_PACKAGE_MFC=TRUE \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DCMAKE_PREFIX_PATH="$PREFIX" + cmake --build "$BUILD" --config "${{ inputs.build-type }}" --parallel + cmake --install "$BUILD" --config "${{ inputs.build-type }}" + elif [ "${{ inputs.toolchain }}" = "mingw" ]; then + cmake -S "$SRC" -B "$BUILD" -G "MinGW Makefiles" \ + -DBUILD_EXAMPLES=OFF \ + -DBUILD_TESTING=OFF \ + -DCMAKE_BUILD_TYPE="${{ inputs.build-type }}" \ + -DCMAKE_C_COMPILER="${{ inputs.c-compiler }}" \ + -DCMAKE_CXX_COMPILER="${{ inputs.cpp-compiler }}" \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DCMAKE_PREFIX_PATH="$PREFIX" + cmake --build "$BUILD" --parallel 2 + cmake --install "$BUILD" + else + cmake -S "$SRC" -B "$BUILD" \ + -DBUILD_EXAMPLES=OFF \ + -DBUILD_TESTING=OFF \ + -DCMAKE_BUILD_TYPE="${{ inputs.build-type }}" \ + -DCMAKE_C_COMPILER="${{ inputs.c-compiler }}" \ + -DCMAKE_CXX_COMPILER="${{ inputs.cpp-compiler }}" \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DCMAKE_PREFIX_PATH="$PREFIX" + cmake --build "$BUILD" --parallel + cmake --install "$BUILD" + fi + + echo "::endgroup::" + done + + echo "prefix=$PREFIX" >> "$GITHUB_OUTPUT" + echo "SIS_DEPS=$PREFIX" >> "$GITHUB_ENV" diff --git a/.github/workflows/ci-cell.yml b/.github/workflows/ci-cell.yml new file mode 100644 index 0000000..84d6852 --- /dev/null +++ b/.github/workflows/ci-cell.yml @@ -0,0 +1,377 @@ +# +# Created: 13th October 2026 +# Updated: 13th October 2026 +# + +name: CI cell + +on: + workflow_call: + inputs: + cell-id: + required: true + type: string + os: + required: true + type: string + c-compiler: + required: true + type: string + cpp-compiler: + required: true + type: string + build-type: + required: false + type: string + default: Release + toolchain: + required: false + type: string + default: '' + +permissions: + contents: read + +jobs: + build: + name: Build (${{ inputs.cell-id }}) + runs-on: ${{ inputs.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Install Clang (Ubuntu) + if: runner.os == 'Linux' && inputs.c-compiler == 'clang' + run: sudo apt-get update && sudo apt-get install -y clang + + - name: Setup MSYS2 MinGW + id: msys2 + if: runner.os == 'Windows' && inputs.toolchain == 'mingw' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-cmake + mingw-w64-x86_64-make + + # Dependency order is deliberate (not lexicographic): each package + # configures against those already installed in the shared prefix. + # Omit shwild — Diagnosticism finds it QUIET and CI historically + # built xTests with NO_SHWILD (match actual CI needs). + - name: Install SIS dependencies + uses: ./.github/actions/install-sis-deps + with: + build-type: ${{ inputs.build-type }} + c-compiler: ${{ inputs.c-compiler }} + cpp-compiler: ${{ inputs.cpp-compiler }} + dependencies: STLSoft xTests + toolchain: ${{ inputs.toolchain }} + + - name: Configure + shell: bash + run: | + set -euo pipefail + if [ "${{ inputs.c-compiler }}" = "cl" ]; then + cmake -B build -S . \ + -DCMAKE_DISABLE_FIND_PACKAGE_MFC=TRUE \ + -DCMAKE_PREFIX_PATH="${{ env.SIS_DEPS }}" \ + -DBUILD_EXAMPLES=ON \ + -DBUILD_TESTING=ON + elif [ "${{ inputs.toolchain }}" = "mingw" ]; then + export PATH="/mingw64/bin:$PATH" + cmake -B build -S . -G "MinGW Makefiles" \ + -DCMAKE_BUILD_TYPE="${{ inputs.build-type }}" \ + -DCMAKE_C_COMPILER="${{ inputs.c-compiler }}" \ + -DCMAKE_CXX_COMPILER="${{ inputs.cpp-compiler }}" \ + -DCMAKE_PREFIX_PATH="${{ env.SIS_DEPS }}" \ + -DBUILD_EXAMPLES=ON \ + -DBUILD_TESTING=ON + else + cmake -B build -S . \ + -DCMAKE_BUILD_TYPE="${{ inputs.build-type }}" \ + -DCMAKE_C_COMPILER="${{ inputs.c-compiler }}" \ + -DCMAKE_CXX_COMPILER="${{ inputs.cpp-compiler }}" \ + -DCMAKE_PREFIX_PATH="${{ env.SIS_DEPS }}" \ + -DBUILD_EXAMPLES=ON \ + -DBUILD_TESTING=ON + fi + + - name: Build + shell: bash + run: | + set -euo pipefail + if [ "${{ inputs.c-compiler }}" = "cl" ]; then + cmake --build build --config "${{ inputs.build-type }}" --parallel + elif [ "${{ inputs.toolchain }}" = "mingw" ]; then + export PATH="/mingw64/bin:$PATH" + cmake --build build --parallel 2 + else + cmake --build build --parallel + fi + + - name: Upload build tree + uses: actions/upload-artifact@v4 + with: + name: sis-build-${{ inputs.cell-id }} + path: build + retention-days: 1 + + # Windows cells dogfood the native .cmd runners (never the .sh); every + # other cell uses the Bash runners. download-artifact strips execute bits. + run-examples: + name: Examples (${{ inputs.cell-id }}) + needs: build + runs-on: ${{ inputs.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup MSYS2 MinGW + id: msys2 + if: runner.os == 'Windows' && inputs.toolchain == 'mingw' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: mingw-w64-x86_64-toolchain + + - name: Download build tree + uses: actions/download-artifact@v4 + with: + name: sis-build-${{ inputs.cell-id }} + path: build + + # download-artifact does not restore Unix execute bits; + # run_all_*.sh discovery requires -perm -100 (or *.exe on Windows). + # Skip CMakeFiles and compiler deps — name globs also match + # e.g. test.unit.*.c.o.d / example*.cpp.o.d; chmod +x on those + # makes tip runners treat them as programs (exit 127). + - name: Restore execute bits on test and example binaries + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + find build -type f \ + ! -path '*/CMakeFiles/*' \ + ! -name '*.d' \ + ! -name '*.o' \ + ! -name '*.obj' \ + \( \ + -name 'test_unit*' -o -name 'test.unit.*' -o \ + -name 'test_component*' -o -name 'test.component.*' -o \ + -name 'test_performance*' -o -name 'test.performance.*' -o \ + -name 'test_scratch*' -o -name 'test.scratch.*' -o \ + \( -name 'example*' ! -name '*.md' \) \ + \) -exec chmod +x {} + + + - name: Run examples (Bash) + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_examples.sh -M + + - name: Run examples (native cmd) + if: runner.os == 'Windows' + shell: cmd + env: + MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }} + SIS_CMAKE_BUILD_DIR: ${{ github.workspace }}\build + run: | + if "${{ inputs.toolchain }}"=="mingw" set "PATH=%MSYS2_LOCATION%\mingw64\bin;%PATH%" + call run_all_examples.cmd + if errorlevel 1 exit /b 1 + + test-unit: + name: Unit tests (${{ inputs.cell-id }}) + needs: build + runs-on: ${{ inputs.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup MSYS2 MinGW + id: msys2 + if: runner.os == 'Windows' && inputs.toolchain == 'mingw' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: mingw-w64-x86_64-toolchain + + - name: Download build tree + uses: actions/download-artifact@v4 + with: + name: sis-build-${{ inputs.cell-id }} + path: build + + # download-artifact does not restore Unix execute bits; + # run_all_*.sh discovery requires -perm -100 (or *.exe on Windows). + # Skip CMakeFiles and compiler deps — name globs also match + # e.g. test.unit.*.c.o.d / example*.cpp.o.d; chmod +x on those + # makes tip runners treat them as programs (exit 127). + - name: Restore execute bits on test and example binaries + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + find build -type f \ + ! -path '*/CMakeFiles/*' \ + ! -name '*.d' \ + ! -name '*.o' \ + ! -name '*.obj' \ + \( \ + -name 'test_unit*' -o -name 'test.unit.*' -o \ + -name 'test_component*' -o -name 'test.component.*' -o \ + -name 'test_performance*' -o -name 'test.performance.*' -o \ + -name 'test_scratch*' -o -name 'test.scratch.*' -o \ + \( -name 'example*' ! -name '*.md' \) \ + \) -exec chmod +x {} + + + - name: Run unit tests (Bash) + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_unit_tests.sh -M --unit-only + + - name: Run unit tests (native cmd) + if: runner.os == 'Windows' + shell: cmd + env: + MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }} + SIS_CMAKE_BUILD_DIR: ${{ github.workspace }}\build + run: | + if "${{ inputs.toolchain }}"=="mingw" set "PATH=%MSYS2_LOCATION%\mingw64\bin;%PATH%" + call run_all_unit_tests.cmd --unit-only + if errorlevel 1 exit /b 1 + + test-component: + name: Component tests (${{ inputs.cell-id }}) + needs: build + runs-on: ${{ inputs.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup MSYS2 MinGW + id: msys2 + if: runner.os == 'Windows' && inputs.toolchain == 'mingw' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: mingw-w64-x86_64-toolchain + + - name: Download build tree + uses: actions/download-artifact@v4 + with: + name: sis-build-${{ inputs.cell-id }} + path: build + + # download-artifact does not restore Unix execute bits; + # run_all_*.sh discovery requires -perm -100 (or *.exe on Windows). + # Skip CMakeFiles and compiler deps — name globs also match + # e.g. test.unit.*.c.o.d / example*.cpp.o.d; chmod +x on those + # makes tip runners treat them as programs (exit 127). + - name: Restore execute bits on test and example binaries + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + find build -type f \ + ! -path '*/CMakeFiles/*' \ + ! -name '*.d' \ + ! -name '*.o' \ + ! -name '*.obj' \ + \( \ + -name 'test_unit*' -o -name 'test.unit.*' -o \ + -name 'test_component*' -o -name 'test.component.*' -o \ + -name 'test_performance*' -o -name 'test.performance.*' -o \ + -name 'test_scratch*' -o -name 'test.scratch.*' -o \ + \( -name 'example*' ! -name '*.md' \) \ + \) -exec chmod +x {} + + + - name: Run component tests (Bash) + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_component_tests.sh -M + + - name: Run component tests (native cmd) + if: runner.os == 'Windows' + shell: cmd + env: + MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }} + SIS_CMAKE_BUILD_DIR: ${{ github.workspace }}\build + run: | + if "${{ inputs.toolchain }}"=="mingw" set "PATH=%MSYS2_LOCATION%\mingw64\bin;%PATH%" + call run_all_component_tests.cmd + if errorlevel 1 exit /b 1 + + test-scratch: + name: Scratch tests (${{ inputs.cell-id }}) + needs: build + runs-on: ${{ inputs.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup MSYS2 MinGW + id: msys2 + if: runner.os == 'Windows' && inputs.toolchain == 'mingw' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: mingw-w64-x86_64-toolchain + + - name: Download build tree + uses: actions/download-artifact@v4 + with: + name: sis-build-${{ inputs.cell-id }} + path: build + + # download-artifact does not restore Unix execute bits; + # run_all_*.sh discovery requires -perm -100 (or *.exe on Windows). + # Skip CMakeFiles and compiler deps — name globs also match + # e.g. test.unit.*.c.o.d / example*.cpp.o.d; chmod +x on those + # makes tip runners treat them as programs (exit 127). + - name: Restore execute bits on test and example binaries + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + find build -type f \ + ! -path '*/CMakeFiles/*' \ + ! -name '*.d' \ + ! -name '*.o' \ + ! -name '*.obj' \ + \( \ + -name 'test_unit*' -o -name 'test.unit.*' -o \ + -name 'test_component*' -o -name 'test.component.*' -o \ + -name 'test_performance*' -o -name 'test.performance.*' -o \ + -name 'test_scratch*' -o -name 'test.scratch.*' -o \ + \( -name 'example*' ! -name '*.md' \) \ + \) -exec chmod +x {} + + + - name: Run scratch tests (Bash) + if: runner.os != 'Windows' + shell: bash + run: | + set -euo pipefail + SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_scratch_tests.sh -M + + - name: Run scratch tests (native cmd) + if: runner.os == 'Windows' + shell: cmd + env: + MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }} + SIS_CMAKE_BUILD_DIR: ${{ github.workspace }}\build + run: | + if "${{ inputs.toolchain }}"=="mingw" set "PATH=%MSYS2_LOCATION%\mingw64\bin;%PATH%" + call run_all_scratch_tests.cmd + if errorlevel 1 exit /b 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed2be81..2436e87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,3 +1,8 @@ +# +# Created: 4th August 2026 +# Updated: 13th October 2026 +# + name: CI on: @@ -6,14 +11,68 @@ on: - master - dev - boilerplate + - idiomatic + - rc1 + - rc2 + - rc3 pull_request: +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + env: BUILD_TYPE: Release jobs: - build: - name: Build (library + examples) + cell: + name: CI (${{ matrix.id }}) + strategy: + fail-fast: false + matrix: + include: + - id: linux-clang + os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + - id: linux-gcc + os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - id: macos-clang + os: macos-latest + c_compiler: clang + cpp_compiler: clang++ + - id: macos-gcc + os: macos-latest + c_compiler: gcc + cpp_compiler: g++ + - id: windows-cl + os: windows-latest + c_compiler: cl + cpp_compiler: cl + # `toolchain` carries what is not a compiler: MinGW needs its own + # generator and PATH, but the compilers it drives are gcc and g++ + - id: windows-mingw + os: windows-latest + c_compiler: gcc + cpp_compiler: g++ + toolchain: mingw + uses: ./.github/workflows/ci-cell.yml + with: + cell-id: ${{ matrix.id }} + os: ${{ matrix.os }} + c-compiler: ${{ matrix.c_compiler }} + cpp-compiler: ${{ matrix.cpp_compiler }} + build-type: Release + toolchain: ${{ matrix.toolchain }} + secrets: inherit + + install-smoke: + name: Install smoke (${{ matrix.os }}, ${{ matrix.toolchain || matrix.c_compiler }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -29,8 +88,12 @@ jobs: c_compiler: clang cpp_compiler: clang++ - os: windows-latest - c_compiler: msvc - cpp_compiler: msvc + c_compiler: cl + cpp_compiler: cl + - os: windows-latest + c_compiler: gcc + cpp_compiler: g++ + toolchain: mingw steps: - uses: actions/checkout@v4 @@ -39,139 +102,114 @@ jobs: if: runner.os == 'Linux' && matrix.c_compiler == 'clang' run: sudo apt-get update && sudo apt-get install -y clang + - name: Setup MSYS2 MinGW + if: runner.os == 'Windows' && matrix.toolchain == 'mingw' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-cmake + mingw-w64-x86_64-make + + # Diagnosticism is standalone on its public interface; install-smoke + # needs no SIS dependencies in the consumer prefix. - name: Configure shell: bash run: | - set -e - if [ "${{ matrix.c_compiler }}" = "msvc" ]; then + set -euo pipefail + if [ "${{ matrix.c_compiler }}" = "cl" ]; then cmake -B build -S . \ - -DBUILD_EXAMPLES=ON \ + -DCMAKE_DISABLE_FIND_PACKAGE_MFC=TRUE \ + -DBUILD_EXAMPLES=OFF \ + -DBUILD_TESTING=OFF + elif [ "${{ matrix.toolchain }}" = "mingw" ]; then + export PATH="/mingw64/bin:$PATH" + cmake -B build -S . -G "MinGW Makefiles" \ + -DCMAKE_BUILD_TYPE="${{ env.BUILD_TYPE }}" \ + -DCMAKE_C_COMPILER="${{ matrix.c_compiler }}" \ + -DCMAKE_CXX_COMPILER="${{ matrix.cpp_compiler }}" \ + -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTING=OFF else cmake -B build -S . \ - -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \ - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ - -DBUILD_EXAMPLES=ON \ + -DCMAKE_BUILD_TYPE="${{ env.BUILD_TYPE }}" \ + -DCMAKE_C_COMPILER="${{ matrix.c_compiler }}" \ + -DCMAKE_CXX_COMPILER="${{ matrix.cpp_compiler }}" \ + -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTING=OFF fi - name: Build shell: bash run: | - set -e - if [ "${{ matrix.c_compiler }}" = "msvc" ]; then - cmake --build build --config ${{ env.BUILD_TYPE }} --parallel + set -euo pipefail + if [ "${{ matrix.c_compiler }}" = "cl" ]; then + cmake --build build --config "${{ env.BUILD_TYPE }}" --parallel + elif [ "${{ matrix.toolchain }}" = "mingw" ]; then + export PATH="/mingw64/bin:$PATH" + cmake --build build --parallel 2 else cmake --build build --parallel fi - - name: Smoke-run examples + - name: Install shell: bash run: | - set -e - if [ "${{ matrix.c_compiler }}" = "msvc" ]; then - EXE_SUFFIX=.exe - EXE_DIR=${{ env.BUILD_TYPE }} + set -euo pipefail + PREFIX="${RUNNER_TEMP}/install-prefix" + echo "INSTALL_PREFIX=$PREFIX" >> "$GITHUB_ENV" + if [ "${{ matrix.c_compiler }}" = "cl" ]; then + cmake --install build --config "${{ env.BUILD_TYPE }}" --prefix "$PREFIX" else - EXE_SUFFIX= - EXE_DIR=. + cmake --install build --prefix "$PREFIX" fi - "build/examples/doomgram/${EXE_DIR}/doomgram${EXE_SUFFIX}" - "build/examples/tracing/${EXE_DIR}/tracing${EXE_SUFFIX}" - "build/examples/version_string/${EXE_DIR}/version_string${EXE_SUFFIX}" - "build/examples/ev/${EXE_DIR}/ev${EXE_SUFFIX}" - test: - name: Unit tests (${{ matrix.os }}, ${{ matrix.c_compiler }}) - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-latest - c_compiler: gcc - cpp_compiler: g++ - - os: macos-latest - c_compiler: clang - cpp_compiler: clang++ - - os: windows-latest - c_compiler: msvc - cpp_compiler: msvc - - steps: - - uses: actions/checkout@v4 - - - name: Install test dependencies + - name: Verify install tree shell: bash run: | - set -euxo pipefail - - DEPS="${RUNNER_TEMP}/sis-deps" - STLSOFT="${RUNNER_TEMP}/stlsoft/STLSoft_1_11" - XTSRC="${RUNNER_TEMP}/xtests-src" - XTBUILDDIR="${RUNNER_TEMP}/xtests-build" - - mkdir -p "$(dirname "$STLSOFT")" - - git clone --depth 1 https://github.com/synesissoftware/STLSoft "$STLSOFT" - git clone --depth 1 https://github.com/synesissoftware/xTests "$XTSRC" - - if [ "${{ matrix.c_compiler }}" = "msvc" ]; then - cmake -S "$XTSRC" -B "$XTBUILDDIR" \ - -DCMAKE_INSTALL_PREFIX="$DEPS" \ - -DBUILD_EXAMPLES=OFF \ - -DBUILD_TESTING=OFF \ - -DNO_SHWILD=TRUE \ - -DSTLSOFT="$STLSOFT" - cmake --build "$XTBUILDDIR" --config ${{ env.BUILD_TYPE }} --parallel - cmake --install "$XTBUILDDIR" --config ${{ env.BUILD_TYPE }} - else - cmake -S "$XTSRC" -B "$XTBUILDDIR" \ - -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ - -DCMAKE_INSTALL_PREFIX="$DEPS" \ - -DBUILD_EXAMPLES=OFF \ - -DBUILD_TESTING=OFF \ - -DNO_SHWILD=TRUE \ - -DSTLSOFT="$STLSOFT" - cmake --build "$XTBUILDDIR" --parallel - cmake --install "$XTBUILDDIR" - fi + set -euo pipefail + test -f "${INSTALL_PREFIX}/include/diagnosticism/diagnosticism.h" + test -f "${INSTALL_PREFIX}/lib/cmake/diagnosticism/diagnosticism-config.cmake" - echo "SIS_CMAKE_DEPS_PREFIX=$DEPS" >> "$GITHUB_ENV" - echo "STLSOFT=$STLSOFT" >> "$GITHUB_ENV" - - - name: Configure + - name: Configure and run install smoke consumer shell: bash run: | - set -e - if [ "${{ matrix.c_compiler }}" = "msvc" ]; then - cmake -B build -S . \ - -DCMAKE_PREFIX_PATH="${SIS_CMAKE_DEPS_PREFIX}" \ - -DSTLSOFT="${STLSOFT}" \ - -DBUILD_EXAMPLES=OFF \ - -DBUILD_TESTING=ON + set -euo pipefail + CONSUMER="${RUNNER_TEMP}/consumer" + mkdir -p "$CONSUMER" + cat > "$CONSUMER/CMakeLists.txt" <<'EOF' + cmake_minimum_required(VERSION 3.13) + project(diagnosticism_install_smoke C) + find_package(Diagnosticism REQUIRED) + add_executable(consumer consumer.c) + target_link_libraries(consumer PRIVATE Diagnosticism::core) + EOF + cat > "$CONSUMER/consumer.c" <<'EOF' + #include + int main(void) { return 0; } + EOF + if [ "${{ matrix.c_compiler }}" = "cl" ]; then + cmake -S "$CONSUMER" -B consumer-build \ + -DCMAKE_PREFIX_PATH="${INSTALL_PREFIX}" + cmake --build consumer-build --config "${{ env.BUILD_TYPE }}" --parallel + consumer-build/${{ env.BUILD_TYPE }}/consumer.exe + elif [ "${{ matrix.toolchain }}" = "mingw" ]; then + export PATH="/mingw64/bin:$PATH" + cmake -S "$CONSUMER" -B consumer-build -G "MinGW Makefiles" \ + -DCMAKE_BUILD_TYPE="${{ env.BUILD_TYPE }}" \ + -DCMAKE_C_COMPILER="${{ matrix.c_compiler }}" \ + -DCMAKE_CXX_COMPILER="${{ matrix.cpp_compiler }}" \ + -DCMAKE_PREFIX_PATH="${INSTALL_PREFIX}" + cmake --build consumer-build --parallel 2 + consumer-build/consumer.exe else - cmake -B build -S . \ - -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \ - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ - -DCMAKE_PREFIX_PATH="${SIS_CMAKE_DEPS_PREFIX}" \ - -DSTLSOFT="${STLSOFT}" \ - -DBUILD_EXAMPLES=OFF \ - -DBUILD_TESTING=ON + cmake -S "$CONSUMER" -B consumer-build \ + -DCMAKE_BUILD_TYPE="${{ env.BUILD_TYPE }}" \ + -DCMAKE_C_COMPILER="${{ matrix.c_compiler }}" \ + -DCMAKE_CXX_COMPILER="${{ matrix.cpp_compiler }}" \ + -DCMAKE_PREFIX_PATH="${INSTALL_PREFIX}" + cmake --build consumer-build --parallel + consumer-build/consumer fi - - - name: Build - shell: bash - run: | - set -e - if [ "${{ matrix.c_compiler }}" = "msvc" ]; then - cmake --build build --config ${{ env.BUILD_TYPE }} --parallel - else - cmake --build build --parallel - fi - - - name: Run unit tests - shell: bash - run: SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_unit_tests.sh -M diff --git a/.gitignore b/.gitignore index 1be1e18..226c160 100644 --- a/.gitignore +++ b/.gitignore @@ -1,161 +1,127 @@ -# Synesis Information Systems .gitignore for C/C++ libraries +# Synesis Information Systems .gitignore for C / C++ libraries # directories (by name) -/.git-old/ -/.vs/ +bin/ +ideas/ +ipch/ +lib/ +scripts/internal/ +Win32/ +x64/ +.idea/ .svn/ +.vs/ -.classpath -/.idea/ -/__pycache__ -/node_modules +__pycache__/ -/_build/ +/_analyses/ /_build-ci/ +/_build/ /_internal/ -/_analyses/ - -/bin/ -/ipch/ -/lib/ -bin/ -ipch/ -lib/ -/doc/ /docs/ /dox/ -/dox /ideas/ -ideas/ +/include/xcontract/ +/include/xcover/ /included/ +/node_modules/ /releases/ /scratch/ -/temp/ - -/--include -/--src -/-include -/-src - -/examples/bin -/examples/c/bin -/examples/cpp/bin - -/examples/lib -/examples/c/lib -/examples/cpp/lib - -projects/util/vc10-generated/ - -perf_test/ - -scripts/internal/ - -/include/b64/ -/include/shwild/ -/include/xcontract/ -/include/xcover/ -/include/xtests/ -/src/b64/ -/src/shwild/ /src/xcontract/ /src/xcover/ -/src/xtests/ +/target/ +/temp/ + +/examples/bin/ +/examples/c/bin/ +/examples/c/lib/ +/examples/cpp/bin/ +/examples/cpp/lib/ +/examples/lib/ -Debug Multithreaded DLL/ Debug Multithreaded Dll/ -Debug Multithreaded pseudoUNIX/ +Debug Multithreaded DLL/ Debug Multithreaded pseudoUnix/ +Debug Multithreaded pseudoUNIX/ Debug Multithreaded/ Debug/ +DebugDll/ DebugDLL/ DebugDLLUNIX/ -DebugDll/ -DebugMTDLL/ +DebugMt/ DebugMTDll/ +DebugMTDLL/ DebugMTUnix/ -DebugMt/ DebugNox/ -DebugUNIX/ DebugUnix/ -Release Multithreaded DLL/ +DebugUNIX/ Release Multithreaded Dll/ -Release Multithreaded pseudoUNIX/ +Release Multithreaded DLL/ Release Multithreaded pseudoUnix/ +Release Multithreaded pseudoUNIX/ Release Multithreaded/ Release/ +ReleaseDll/ ReleaseDLL/ ReleaseDLLUNIX/ -ReleaseDll/ -ReleaseMTDLL/ +ReleaseMt/ ReleaseMTDll/ +ReleaseMTDLL/ ReleaseMTUnix/ -ReleaseMt/ ReleaseNox/ ReleaseUnix/ UDebug/ UDebugMt/ -URelease/ -UReleaseMt/ -Unicode Debug Multithreaded DLL/ Unicode Debug Multithreaded Dll/ +Unicode Debug Multithreaded DLL/ Unicode Debug Multithreaded/ Unicode Debug/ -Unicode Release Multithreaded DLL/ Unicode Release Multithreaded Dll/ +Unicode Release Multithreaded DLL/ Unicode Release Multithreaded/ Unicode Release/ - -Win32/ -x64/ +URelease/ +UReleaseMt/ # directories (by pattern) -/build/ar* -/build/bc5* -/build/como* -/build/cw* -/build/gcc3* -/build/gcc4* -/build/icl* -/build/vc5* -/build/*.zip - /include-*/ # files (by name) .DS_Store + +.classpath +.env.development.local +.env.local +.env.production.local +.env.test.local .ruby-version -.ruby-version-exclusions .svnignore .svnignore-at-root .update_deps -.env.local -.env.development.local -.env.test.local -.env.production.local +build/CMakeCache.txt +build/makefile.tmpl +build/makefile.tools.xml +build/shwild.tmplgen +include/pantheios/internal/dos2unix.has.been.performed +include/stlsoft/dos2unix.has.been.performed +include/stlsoft/internal/dos2unix.has.been.performed list_changes.cmd logging-bailout.txt prepare.cmd sh.exe.stackdump -tidydir.cmd -update_deps.cmd - -build/makefile.tmpl -build/makefile.tools.xml -build/shwild.tmplgen -build/CMakeCache.txt - test/testpatterns.orj test/tests-plan.txt +tidydir.cmd +update_deps.cmd # files (by pattern) @@ -163,8 +129,6 @@ test/tests-plan.txt *~ *.*~ *.???_obj -*.VC.db -*.VC.opendb *.a *.app *.aps @@ -172,6 +136,7 @@ test/tests-plan.txt *.bak *.chm *.class +*.csproj*user *.d *.diff *.dll @@ -208,32 +173,42 @@ test/tests-plan.txt *.slo *.so *.suo -*.swp *.sw[ponm] +*.swp *.test *.tlog *.tmp *.unsuccessfulbuild -*.vcproj*.user -*.vcxproj*.user -*.csproj*user -*.vcproj*user -*.vcxproj*user +*.VC.db +*.VC.opendb *.xcuserstate *.zip -*.vc71.sln -*.vc8.sln -*.vc9.sln +/build/*.tmpl +/build/*.tmplgen +/build/*.tools.xml +/build/*.zip +/build/ar* +/build/bc5* +/build/como* +/build/cw* +/build/gcc3* +/build/gcc4* +/build/icl* +/build/vc5* RCa0* RCb0* -build/*.tmplgen -build/*.tmpl -build/*.tools.xml - npm-debug.log* yarn-debug.log* yarn-error.log* +*.vc71.sln +*.vc8.sln +*.vc9.sln +*.vcproj*.user +*.vcproj*user +*.vcxproj*.user +*.vcxproj*user + diff --git a/.sis/ci_examples_allowed_to_fail.txt b/.sis/ci_examples_allowed_to_fail.txt new file mode 100644 index 0000000..914fcf4 --- /dev/null +++ b/.sis/ci_examples_allowed_to_fail.txt @@ -0,0 +1,5 @@ +# Example programs that are allowed to fail in CI / run_all_examples. +# example.abort calls diagnosticism_abortf() and therefore exits non-zero +# by design; tip runners still execute it and treat the failure as +# anticipated when listed here. +example.abort diff --git a/.vimrc b/.vimrc index 1bf33a0..cebe6af 100644 --- a/.vimrc +++ b/.vimrc @@ -42,29 +42,29 @@ autocmd BufWritePre * %s/\s\+$//e augroup sis_c_cxx autocmd! + " [bat] + autocmd FileType bat,dosbatch setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=60,76 " [c] / [cpp] autocmd FileType c,cpp setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=60,64,68,72,76 - " [rust] - autocmd FileType rs setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=76 - " [cmake] autocmd FileType cmake setlocal noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 - " [shellscript] - autocmd FileType sh,bash,zsh setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2 colorcolumn=60,76 - - " [bat] - autocmd FileType bat,dosbatch setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=60,76 - - " [json] / [markdown] / [yaml] / [ruby] - autocmd FileType json,markdown,yaml,ruby setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2 + " [json] / [markdown] / [ruby] / [yaml] + autocmd FileType json,markdown,ruby,yaml setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2 " [python] autocmd FileType python setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=60,76 + " [rust] + autocmd FileType rs setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=76 + + " [shellscript] + autocmd FileType bash,sh,zsh setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2 colorcolumn=60,76 + " [toml] autocmd FileType toml setlocal noexpandtab tabstop=2 shiftwidth=2 softtabstop=2 + augroup END diff --git a/.vscode/settings.json b/.vscode/settings.json index 7e91279..6aa78f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,8 +5,10 @@ "editor.tabSize": 4, }, "[c]": { + "editor.defaultFormatter": "ms-vscode.cpptools", + "editor.formatOnSave": true, "editor.insertSpaces": true, - "editor.rulers": [ 60, 64, 68, 72, 76 ], + "editor.rulers": [ 60, 64, 68, 72, 76, 120 ], "editor.tabSize": 4, }, "[cmake]": { @@ -14,8 +16,10 @@ "editor.tabSize": 4, }, "[cpp]": { + "editor.defaultFormatter": "ms-vscode.cpptools", + "editor.formatOnSave": true, "editor.insertSpaces": true, - "editor.rulers": [ 60, 64, 68, 72, 76 ], + "editor.rulers": [ 60, 64, 68, 72, 76, 120 ], "editor.tabSize": 4, }, "[json]": { @@ -34,6 +38,7 @@ }, "[ruby]": { "editor.insertSpaces": true, + "editor.rulers": [ 60, 76 ], "editor.tabSize": 2, }, "[shellscript]": { @@ -49,13 +54,28 @@ "editor.insertSpaces": true, "editor.tabSize": 2, }, + "C_Cpp.autocompleteAddParentheses": true, + "C_Cpp.clang_format_fallbackStyle": "none", + "C_Cpp.clang_format_style": "file", + "C_Cpp.default.cStandard": "c17", + "C_Cpp.default.cppStandard": "c++17", + "C_Cpp.enhancedColorization": "enabled", + "C_Cpp.errorSquiggles": "enabled", + "C_Cpp.formatting": "clangFormat", + "C_Cpp.intelliSenseEngine": "default", "cmake.configureOnOpen": false, + "debug.allowBreakpointsEverywhere": true, "editor.detectIndentation": false, "editor.insertSpaces": false, "editor.renderWhitespace": "all", "editor.rulers": [ 76 ], "editor.tabSize": 2, "files.associations": { + "*.gemspec": "ruby", + "*.mdoc": "troff", + "*.roff": "troff", + "Gemfile": "ruby", + "Rakefile": "ruby", "__bit_reference": "cpp", "__bits": "cpp", "__config": "cpp", @@ -76,13 +96,17 @@ "__tuple": "cpp", "__verbose_abort": "cpp", "algorithm": "cpp", + "api.h": "c", "array": "cpp", "atomic": "cpp", + "auto_buffer.h": "c", "bit": "cpp", "bitset": "cpp", "cctype": "cpp", "charconv": "cpp", "chrono": "cpp", + "clasp.h": "c", + "cli_helpers.h": "c", "clocale": "cpp", "cmath": "cpp", "compare": "cpp", @@ -109,6 +133,7 @@ "functional": "cpp", "implicit_link.h": "c", "initializer_list": "cpp", + "invoke.h": "c", "io.h": "c", "iomanip": "cpp", "ios": "cpp", @@ -119,36 +144,48 @@ "limits": "cpp", "list": "cpp", "locale": "cpp", + "main.h": "c", "map": "cpp", - "memory_resource": "cpp", "memory": "cpp", + "memory_resource": "cpp", "mutex": "cpp", + "net_helpers.h": "c", "new": "cpp", "numbers": "cpp", "numeric": "cpp", "optional": "cpp", "ostream": "cpp", + "path_functions.h": "c", + "print": "cpp", "queue": "cpp", "random": "cpp", "ranges": "cpp", "ratio": "cpp", + "rc.h": "c", + "recls.h": "c", + "result_codes_.h": "c", "semaphore": "cpp", "set": "cpp", "setenv.h": "c", "shwild.h": "c", + "sleep_functions.h": "c", "span": "cpp", "sstream": "cpp", "stack": "cpp", + "stdatomic.h": "c", + "stdbool.h": "c", "stdexcept": "cpp", "stdio.h": "c", + "stock.h": "c", "stop_token": "cpp", "streambuf": "cpp", - "string_view": "cpp", "string": "cpp", + "string_view": "cpp", "system_error": "cpp", "terse-api.h": "c", "text_encoding": "cpp", "thread": "cpp", + "trace.h": "c", "tuple": "cpp", "type_traits": "cpp", "typeinfo": "cpp", @@ -159,6 +196,7 @@ "utility": "cpp", "variant": "cpp", "vector": "cpp", + "williamscrc.h": "c", "xfacet": "cpp", "xhash": "cpp", "xiosbase": "cpp", @@ -180,4 +218,3 @@ "files.trimTrailingWhitespace": true, "git.mergeEditor": false, } - diff --git a/CHANGES.md b/CHANGES.md index 54fae98..a290ed3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,22 @@ # Diagnosticism - Changes +## 0.3.2 - 13th October 2026 + +* Modular GitHub Actions CI (**ci.yml** + **ci-cell.yml** + **install-sis-deps**; execute-bit restore; Windows **.cmd** dogfood; install-smoke); +* Tip **run_all_examples** / **run_all_scratch_tests** helpers with allowed-to-fail support; **example.*** target names; **.sis/ci_examples_allowed_to_fail.txt** for **example.abort**; +* **test.scratch.versions** dogfoods `diagnosticism_calc_version_string()` and prints conditional **STLSoft** / **shwild** efferents; +* **NEWS.md** three-column **Details** chronology; +* Migrated version low-byte macro to `DIAGNOSTICISM_VER_RELSTATUS` (legacy `DIAGNOSTICISM_VER_ALPHABETA` retained as a documentation-hidden alias); +* Merged **dev** packaging fix so **Diagnosticism::core** no longer exports a false **STLSoft** interface dependency (see **0.3.1-alpha1**); + + +## 0.3.1 - 10th October 2026 + +* Applied **misc-dev-scripts** **0.6.0** editor/Git/`.sis` drop-in templates on **boilerplate**; +* Restored historical **.gitignore** patterns as a sorted union with **misc-dev-scripts** gold section layout; + + ## 0.3.1-alpha1 - 10th August 2026 * **CMake**: removed erroneous `PUBLIC` link of **STLSoft::STLSoft** from **Diagnosticism::core** (exported interface no longer advertises a transitive **STLSoft** dependency); diff --git a/EXAMPLES.md b/EXAMPLES.md index 1e3bdcb..85e1d33 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,13 +1,14 @@ # Diagnosticism - Examples + |Name|Source & Description|Summary| |---|---|---| -|**abort**|[examples/abort/main.c](./examples/abort/main.c)|Example illustrating use of `diagnosticism_abort()` / `diagnosticism_abortf()`| -|**doomgram**|[examples/doomgram/main.c](./examples/doomgram/main.c)
[examples/doomgram.md](./examples/doomgram.md)|Example illustrating use of `diagnosticism_doomgram_t` and its 12-character timing strip| -|**ev**|[examples/ev/main.c](./examples/ev/main.c)
[examples/ev.md](./examples/ev.md)|Example illustrating use of `diagnosticism_getenv()` and `diagnosticism_getenv_atoi()`| -|**tracing**|[examples/tracing/main.c](./examples/tracing/main.c)
[examples/tracing.md](./examples/tracing.md)|Example illustrating use of the `diagnosticism_trace()` macro| -|**version_string**|[examples/version_string/main.c](./examples/version_string/main.c)
[examples/version_string.md](./examples/version_string.md)|Example illustrating use of `diagnosticism_calc_version_string()` (8-bit elements) and pre-release suffix encoding| -|**version_string_16**|[examples/version_string_16/main.c](./examples/version_string_16/main.c)
[examples/version_string_16.md](./examples/version_string_16.md)|Example illustrating use of `diagnosticism_calc_version_string_16()` (16-bit elements) and pre-release suffix encoding| +|**example.abort**|[examples/abort/main.c](./examples/abort/main.c)|Example illustrating use of `diagnosticism_abort()` / `diagnosticism_abortf()`| +|**example.doomgram**|[examples/doomgram/main.c](./examples/doomgram/main.c)
[examples/doomgram.md](./examples/doomgram.md)|Example illustrating use of `diagnosticism_doomgram_t` and its 12-character timing strip| +|**example.ev**|[examples/ev/main.c](./examples/ev/main.c)
[examples/ev.md](./examples/ev.md)|Example illustrating use of `diagnosticism_getenv()` and `diagnosticism_getenv_atoi()`| +|**example.tracing**|[examples/tracing/main.c](./examples/tracing/main.c)
[examples/tracing.md](./examples/tracing.md)|Example illustrating use of the `diagnosticism_trace()` macro| +|**example.version_string**|[examples/version_string/main.c](./examples/version_string/main.c)
[examples/version_string.md](./examples/version_string.md)|Example illustrating use of `diagnosticism_calc_version_string()` (8-bit elements) and pre-release suffix encoding| +|**example.version_string_16**|[examples/version_string_16/main.c](./examples/version_string_16/main.c)
[examples/version_string_16.md](./examples/version_string_16.md)|Example illustrating use of `diagnosticism_calc_version_string_16()` (16-bit elements) and pre-release suffix encoding| diff --git a/NEWS.md b/NEWS.md index 6cb6ebb..40edddd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,22 +1,25 @@ # Diagnosticism - News -| Date | News Item | -| -------------------- | -------------------------------------------------------------------------------------------------------------------- | -| 9th August 2026 | [Diagnosticism 0.3.0 3) released](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.0) | -| 5th August 2026 | [Diagnosticism 0.3.0 (alpha 3) released](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.0-alpha3) | -| 5th August 2026 | [Diagnosticism 0.3.0 (alpha 2) released](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.0-alpha2) | -| 5th August 2026 | [Diagnosticism 0.3.0 (alpha 1) released](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.0-alpha1) | -| 4th August 2026 | [Diagnosticism 0.2.2 (alpha 1) released](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.2.2-alpha1) | -| 1st August 2026 | [Diagnosticism 0.2.1 released](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.2.1) | -| 25th August 2025 | [Diagnosticism 0.2.0 released](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.2.0) | -| 23rd August 2025 | [Diagnosticism 0.2.0 (beta 4) released](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.2.0-beta4) | -| 12th August 2025 | Diagnosticism 0.2.0 (beta 3) released | -| 11th August 2025 | Diagnosticism 0.2.0 (beta 2) released | -| 7th July 2025 | Diagnosticism 0.2.0 (beta 1) released | -| 23rd February 2025 | Diagnosticism 0.1.1 released | -| 20th February 2025 | Diagnosticism 0.1.0 released | -| 23rd November 2024 | Diagnosticism 0.0.1 released | +| Date | News Item | Details | +| ------------------- | -------------------------------------------------------------------------------------------------------------------- | ------- | +| 13th October 2026 | [**Diagnosticism** 0.3.2](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.2) | Modular CI; tip helpers; versions reporter; `DIAGNOSTICISM_VER_RELSTATUS`; standalone **core** (no **PUBLIC** **STLSoft**) | +| 10th October 2026 | [**Diagnosticism** 0.3.1](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.1) | **misc-dev-scripts** **0.6.0** drop-ins; **.gitignore** sorted union | +| 10th August 2026 | [**Diagnosticism** 0.3.1-alpha1](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.1-alpha1) | **Diagnosticism::core** standalone; no **PUBLIC** **STLSoft**; test-only deps | +| 9th August 2026 | [**Diagnosticism** 0.3.0](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.0) | Stable **0.3.0** | +| 5th August 2026 | [**Diagnosticism** 0.3.0-alpha3](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.0-alpha3) | Install **internal/** headers; **abort.h** install | +| 5th August 2026 | [**Diagnosticism** 0.3.0-alpha2](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.0-alpha2) | Abort API; **examples/abort/**; `DIAGNOSTICISM_CALL_NORETURN` | +| 5th August 2026 | [**Diagnosticism** 0.3.0-alpha1](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.3.0-alpha1) | 8-bit `diagnosticism_calc_version_string()`; `*_16` API; **.sis** | +| 4th August 2026 | [**Diagnosticism** 0.2.2-alpha1](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.2.2-alpha1) | Optional **cstring**; **CMake** OS flags; helper status | +| 1st August 2026 | [**Diagnosticism** 0.2.1](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.2.1) | `diagnosticism_doomgram_clear()`; examples; GitHub Actions CI | +| 25th August 2025 | [**Diagnosticism** 0.2.0](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.2.0) | MinGW detection; dependency fix | +| 23rd August 2025 | [**Diagnosticism** 0.2.0-beta4](https://github.com/synesissoftware/Diagnosticism/releases/tag/0.2.0-beta4) | `diagnosticism::calc_version_string()` | +| 12th August 2025 | Diagnosticism 0.2.0 (beta 3) released | Documentation markup | +| 11th August 2025 | Diagnosticism 0.2.0 (beta 2) released | **CMake** distribution fixes | +| 7th July 2025 | Diagnosticism 0.2.0 (beta 1) released | `diagnosticism_calc_version_string()`; getenv helpers | +| 23rd February 2025 | Diagnosticism 0.1.1 released | Tidying | +| 20th February 2025 | Diagnosticism 0.1.0 released | **doomgram** API | +| 23rd November 2024 | Diagnosticism 0.0.1 released | Initial version | diff --git a/README.md b/README.md index fcf6471..c907486 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ The C library is organised by header under `include/diagnosticism/`. Include ``: * `DIAGNOSTICISM_VER_MAJOR`, `DIAGNOSTICISM_VER_MINOR`, `DIAGNOSTICISM_VER_PATCH` - the major, minor, and patch components of the library version; -* `DIAGNOSTICISM_VER_ALPHABETA` - the alpha/beta component of the library version (0xFF when not applicable); +* `DIAGNOSTICISM_VER_RELSTATUS` - release-status discriminator in the low byte of `DIAGNOSTICISM_VER` (`0xFF` = full release); * `DIAGNOSTICISM_VER` - a composite 32-bit encoding of the library version; diff --git a/build_cmake.sh b/build_cmake.sh index 38e198d..a675a9d 100755 --- a/build_cmake.sh +++ b/build_cmake.sh @@ -1,23 +1,119 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 - +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} IgnoreRemainingFlagsAndOptions=0 +SisUseColours=0 Targets=() # ########################################################## -# functions +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done -function join_by { local IFS="$1"; shift; echo "$*"; } +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} # ########################################################## @@ -27,21 +123,16 @@ while [[ $# -gt 0 ]]; do if [ $IgnoreRemainingFlagsAndOptions -ne 0 ]; then - Targets+=($1) - + Targets+=("$1") shift - continue - else - - if [ ! ${1:0:1} = '-' ]; then - - Targets+=($1) + fi - shift + if [ "${1:0:1}" != '-' ]; then - continue - fi + Targets+=("$1") + shift + continue fi case $1 in @@ -49,18 +140,33 @@ while [[ $# -gt 0 ]]; do IgnoreRemainingFlagsAndOptions=1 ;; + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF Executes CMake-generated artefacts to (re)build project -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + (no arguments) + builds all default targets + + ... + builds one or more specific targets + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + standard flags: @@ -73,7 +179,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -86,41 +192,26 @@ done # ########################################################## # main() -if [ ! -d "$CMakeDir" ]; then +if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts" + >&2 echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found or not configured; use script 'prepare_cmake.sh' first" exit 1 -else - - cd $CMakeDir - - if [ ! -f "$CMakeDir/Makefile" ]; then - - >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' does not contain expected file 'Makefile', so a clean cannot be performed. It is recommended that you remove all CMake artefacts using script 'remove_cmake_artefacts.sh' followed by regeneration via 'prepare_cmake.sh'" - - cd ->/dev/null - - exit 1 - else - - if [ -z "$Targets" ]; then - - echo "Executing build of ${ProjectName} (via command \`$MakeCmd\`)" - else - - echo "Executing build of ${ProjectName} (via command \`$MakeCmd\`) with specific target(s) $(join_by , "${Targets[@]}")" - fi +fi - $MakeCmd ${Targets[*]} - status=$? +if [ ${#Targets[@]} -eq 0 ]; then - cd ->/dev/null + echo "Executing build of ${ProjectNameClr} (via cmake --build)" + sis_cmake_build + status=$? +else - exit $status - fi + echo "Executing build of ${ProjectNameClr} (via cmake --build) with specific target(s): ${Targets[*]}" + sis_cmake_build "${Targets[@]}" + status=$? fi +exit $status -# ############################## end of file ############################# # +# ############################## end of file ############################# # diff --git a/clean_cmake.sh b/clean_cmake.sh index 5d14956..733afbd 100755 --- a/clean_cmake.sh +++ b/clean_cmake.sh @@ -1,13 +1,117 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} # ########################################################## @@ -16,18 +120,27 @@ ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF -Executes CMake-generated artefacts to clean project +Cleans CMake-generated build artefacts via cmake --build --target clean -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + standard flags: @@ -40,7 +153,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -53,35 +166,16 @@ done # ########################################################## # main() -if [ ! -d "$CMakeDir" ]; then +if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts" + >&2 echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found or not configured; nothing to clean" exit 1 -else - - cd $CMakeDir - - if [ ! -f "$CMakeDir/Makefile" ]; then - - >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' does not contain expected file 'Makefile', so a clean cannot be performed. It is recommended that you remove all CMake artefacts using script 'remove_cmake_artefacts.sh' followed by regeneration via 'prepare_cmake.sh'" - - cd ->/dev/null - - exit 1 - else - - echo "Cleaning build of ${ProjectName} (via command \`$MakeCmd clean\`)" - - $MakeCmd clean - status=$? - - cd ->/dev/null - - exit $status - fi fi +echo "Executing clean of ${ProjectNameClr} (via cmake --build --target clean)" +sis_cmake_build clean +exit $? -# ############################## end of file ############################# # +# ############################## end of file ############################# # diff --git a/ctest_cmake.sh b/ctest_cmake.sh new file mode 100755 index 0000000..4e09ee2 --- /dev/null +++ b/ctest_cmake.sh @@ -0,0 +1,239 @@ +#! /bin/bash + +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} +ProjectNameFile="$Dir/.sis/project_name.txt" +ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +CTestVerbose= +RunMake=1 +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + + +# ########################################################## +# command-line handling + +while [[ $# -gt 0 ]]; do + + case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; + --no-make|-M) + + RunMake=0 + ;; + --verbose|-V) + + CTestVerbose=--verbose + ;; + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + cat << EOF +Runs CMake's CTest test program(s) + +${ScriptPath} [ ... flags/options ... ] + +Flags/options: + + behaviour: + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + -M + --no-make + does not execute a build before running tests + + -V + --verbose + verbose test output + + + standard flags: + + --help + displays this help and terminates + +EOF + + exit 0 + ;; + *) + + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" + + exit 1 + ;; + esac + + shift +done + + +# ########################################################## +# main() + +status=0 + +if [ $RunMake -ne 0 ]; then + + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running CTest" + + mkdir -p "$CMakeDir" || exit 1 + + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" + + exit 1 + fi + + sis_cmake_build + status=$? +else + + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" + + exit 1 + fi +fi + +if [ $status -eq 0 ]; then + + echo + echo "Running ${ProjectNameClr} CMake tests" + + # Multi-config generators (e.g. Visual Studio) need -C ; + # mirror sis_cmake_build's CMAKE_CONFIGURATION_TYPES detection. + ctest_args=(--test-dir "$CMakeDir" --output-on-failure) + if [ -n "$CTestVerbose" ]; then + + ctest_args+=("$CTestVerbose") + fi + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + ctest_args+=(-C "${SIS_CMAKE_CONFIG:-Release}") + fi + + ctest "${ctest_args[@]}" + status=$? +fi + +exit $status + + +# ############################## end of file ############################# # diff --git a/examples/abort/CMakeLists.txt b/examples/abort/CMakeLists.txt index 6b8ad76..f0f2f71 100644 --- a/examples/abort/CMakeLists.txt +++ b/examples/abort/CMakeLists.txt @@ -1,3 +1,3 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_example_program(abort main.c) +define_example_program(example.abort main.c) diff --git a/examples/doomgram/CMakeLists.txt b/examples/doomgram/CMakeLists.txt index a5a3902..837968a 100644 --- a/examples/doomgram/CMakeLists.txt +++ b/examples/doomgram/CMakeLists.txt @@ -1,3 +1,3 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_example_program(doomgram main.c) +define_example_program(example.doomgram main.c) diff --git a/examples/ev/CMakeLists.txt b/examples/ev/CMakeLists.txt index d3f99cc..81dca40 100644 --- a/examples/ev/CMakeLists.txt +++ b/examples/ev/CMakeLists.txt @@ -1,3 +1,3 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_example_program(ev main.c) +define_example_program(example.ev main.c) diff --git a/examples/tracing/CMakeLists.txt b/examples/tracing/CMakeLists.txt index a23bb18..3307185 100644 --- a/examples/tracing/CMakeLists.txt +++ b/examples/tracing/CMakeLists.txt @@ -1,3 +1,3 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_example_program(tracing main.c) +define_example_program(example.tracing main.c) diff --git a/examples/version_string/CMakeLists.txt b/examples/version_string/CMakeLists.txt index d62813b..3484cda 100644 --- a/examples/version_string/CMakeLists.txt +++ b/examples/version_string/CMakeLists.txt @@ -1,3 +1,3 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_example_program(version_string main.c) +define_example_program(example.version_string main.c) diff --git a/examples/version_string_16/CMakeLists.txt b/examples/version_string_16/CMakeLists.txt index 4896845..ad3c6ca 100644 --- a/examples/version_string_16/CMakeLists.txt +++ b/examples/version_string_16/CMakeLists.txt @@ -1,2 +1,3 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten -define_example_program(version_string_16 main.c) +define_example_program(example.version_string_16 main.c) + diff --git a/include/diagnosticism/diagnosticism.h b/include/diagnosticism/diagnosticism.h index cbe5d79..c3762c8 100644 --- a/include/diagnosticism/diagnosticism.h +++ b/include/diagnosticism/diagnosticism.h @@ -4,7 +4,7 @@ * Purpose: Main header file for Diagnosticism (C-API). * * Created: 23rd November 2024 - * Updated: 10th August 2026 + * Updated: 13th October 2026 * * Home: https://github.com/synesissoftware/Diagnosticism/ * @@ -53,8 +53,8 @@ #ifndef DIAGNOSTICISM_DOCUMENTATION_SKIP_SECTION # define DIAGNOSTICISM_VER_DIAGNOSTICISM_H_DIAGNOSTICISM_MAJOR 0 # define DIAGNOSTICISM_VER_DIAGNOSTICISM_H_DIAGNOSTICISM_MINOR 0 -# define DIAGNOSTICISM_VER_DIAGNOSTICISM_H_DIAGNOSTICISM_PATCH 15 -# define DIAGNOSTICISM_VER_DIAGNOSTICISM_H_DIAGNOSTICISM_EDIT 18 +# define DIAGNOSTICISM_VER_DIAGNOSTICISM_H_DIAGNOSTICISM_PATCH 17 +# define DIAGNOSTICISM_VER_DIAGNOSTICISM_H_DIAGNOSTICISM_EDIT 19 #endif /* !DIAGNOSTICISM_DOCUMENTATION_SKIP_SECTION */ @@ -75,6 +75,11 @@ * * The patch number of the DIAGNOSTICISM library * + * \def DIAGNOSTICISM_VER_RELSTATUS + * + * Release-status discriminator in the low byte of DIAGNOSTICISM_VER + * (`0xFF` = full release; otherwise pre-alpha / alpha / beta / rc ranges) + * * \def DIAGNOSTICISM_VER * * The composite version of the DIAGNOSTICISM library @@ -82,18 +87,21 @@ #define DIAGNOSTICISM_VER_MAJOR 0 #define DIAGNOSTICISM_VER_MINOR 3 -#define DIAGNOSTICISM_VER_PATCH 1 -#define DIAGNOSTICISM_VER_ALPHABETA 0xA1 +#define DIAGNOSTICISM_VER_PATCH 2 +#define DIAGNOSTICISM_VER_RELSTATUS 0xFF #define DIAGNOSTICISM_VER \ (0\ | ( DIAGNOSTICISM_VER_MAJOR << 24 ) \ | ( DIAGNOSTICISM_VER_MINOR << 16 ) \ | ( DIAGNOSTICISM_VER_PATCH << 8 ) \ - | ( DIAGNOSTICISM_VER_ALPHABETA << 0 ) \ + | ( DIAGNOSTICISM_VER_RELSTATUS << 0 ) \ ) -#define DIAGNOSTICISM_VER_REVISION DIAGNOSTICISM_VER_PATCH +#ifndef DIAGNOSTICISM_DOCUMENTATION_SKIP_SECTION +# define DIAGNOSTICISM_VER_ALPHABETA DIAGNOSTICISM_VER_RELSTATUS +# define DIAGNOSTICISM_VER_REVISION DIAGNOSTICISM_VER_PATCH +#endif /* !DIAGNOSTICISM_DOCUMENTATION_SKIP_SECTION */ /* ///////////////////////////////////////////////////////////////////////// diff --git a/prepare_cmake.sh b/prepare_cmake.sh index 8002720..1fe9c96 100755 --- a/prepare_cmake.sh +++ b/prepare_cmake.sh @@ -1,31 +1,135 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") -CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -if [[ -n "$MSYSTEM" ]]; then +# ########################################################## +# functions - 1 - DefaultMakeCmd=mingw32-make.exe - MinGW=1 -else +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in - DefaultMakeCmd=make -fi -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 - +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +BuildSharedLibs=0 Configuration=Release ExamplesDisabled=0 +MinGW=$(sis_cmake_is_truey "${SIS_CMAKE_MINGW:-}" && echo 1 || echo 0) MSVC_MT=0 -MinGW="${MinGW:=0}" RunMake=0 -STLSoftDirGiven= +SisUseColours=0 TestingDisabled=0 VerboseMakefile=0 +# ########################################################## +# PROJECT-SPECIFIC FLAGS (customise per library; keep marker) +# +# Add project-only options here (e.g. --no-cpp, --stlsoft-root-dir, +# --no-shwild). The common body below must stay aligned with +# misc-dev-scripts/shell-scripts/cmake-helpers/prepare_cmake.sh. + +STLSoftDirGiven= + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + # ########################################################## # command-line handling @@ -33,6 +137,14 @@ VerboseMakefile=0 while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; + --build-shared-libs) + + BuildSharedLibs=1 + ;; --cmake-verbose-makefile|-v) VerboseMakefile=1 @@ -72,25 +184,31 @@ while [[ $# -gt 0 ]]; do cat << EOF Creates/reinitialises the CMake build script(s) -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + --build-shared-libs + builds ${ProjectName} as a shared library (BUILD_SHARED_LIBS=ON) + -v --cmake-verbose-makefile - configures CMake to run verbosely (by setting CMAKE_VERBOSE_MAKEFILE - to be ON) + configures CMake to run verbosely (CMAKE_VERBOSE_MAKEFILE=ON) -d --debug-configuration - use Debug configuration (by setting CMAKE_BUILD_TYPE=Debug). Default - is to use Release + use Debug configuration (CMAKE_BUILD_TYPE=Debug). Default is Release -E --disable-examples - disables building of examples (by setting BUILD_EXAMPLES=OFF) + disables building of examples (BUILD_EXAMPLES=OFF) -T --disable-testing @@ -101,16 +219,15 @@ Flags/options: library itself — only for automated tests --mingw - uses explicitly the "MinGW Makefiles" generator, and defaults the - make-command to "mingw32-make.exe" + uses explicitly the "MinGW Makefiles" generator --msvc-mt - when using Visual C++ (MSVC), the static runtime library will be - selected; the default is the dynamic runtime library + when using Visual C++ (MSVC), select the static runtime library -m --run-make - executes make after a successful running of CMake + executes a build via cmake --build after a successful configure + -s --stlsoft-root-dir @@ -120,7 +237,6 @@ Flags/options: is disabled. Does not affect Diagnosticism::core's exported interface - standard flags: --help @@ -132,7 +248,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -145,63 +261,66 @@ done # ########################################################## # main() -mkdir -p $CMakeDir || exit 1 - -cd $CMakeDir +mkdir -p "$CMakeDir" || exit 1 -echo "Executing CMake for ${ProjectName} (in ${CMakeDir})" +echo +echo "Executing CMake for ${ProjectNameClr} (in ${CMakeDirClr})" +if [ $BuildSharedLibs -eq 0 ]; then CMakeBuildSharedLibsFlag="OFF" ; else CMakeBuildSharedLibsFlag="ON" ; fi if [ $ExamplesDisabled -eq 0 ]; then CMakeBuildExamplesFlag="ON" ; else CMakeBuildExamplesFlag="OFF" ; fi if [ $MSVC_MT -eq 0 ]; then CMakeMsvcMtFlag="OFF" ; else CMakeMsvcMtFlag="ON" ; fi -if [ -z $STLSoftDirGiven ]; then CMakeSTLSoftVariable="" ; else CMakeSTLSoftVariable="-DSTLSOFT=$STLSoftDirGiven/" ; fi +if [ -z "$STLSoftDirGiven" ]; then CMakeSTLSoftVariable="" ; else CMakeSTLSoftVariable="-DSTLSOFT=$STLSoftDirGiven/" ; fi if [ $TestingDisabled -eq 0 ]; then CMakeBuildTestingFlag="ON" ; else CMakeBuildTestingFlag="OFF" ; fi if [ $VerboseMakefile -eq 0 ]; then CMakeVerboseMakefileFlag="OFF" ; else CMakeVerboseMakefileFlag="ON" ; fi +# NOTE: the generator is the *only* thing that may differ between the MinGW +# and the default paths; every -D option is passed in both cases, so that no +# flag can be silently ignored according to the generator selected. + +CMakeGeneratorArgs=() + +if [ -n "${SIS_CMAKE_GENERATOR:-}" ] && [ $MinGW -eq 0 ]; then + + CMakeGeneratorArgs=(-G "$SIS_CMAKE_GENERATOR") +fi + if [ $MinGW -ne 0 ]; then - cmake \ - $CMakeSTLSoftVariable \ - -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \ - -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \ - -DCMAKE_BUILD_TYPE=$Configuration \ - -G "MinGW Makefiles" \ - -S $Dir \ - -B $CMakeDir \ - || (cd ->/dev/null ; exit 1) -else - - cmake \ - $CMakeSTLSoftVariable \ - -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \ - -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \ - -DCMAKE_BUILD_TYPE=$Configuration \ - -DCMAKE_VERBOSE_MAKEFILE:BOOL=$CMakeVerboseMakefileFlag \ - -DMSVC_USE_MT:BOOL=$CMakeMsvcMtFlag \ - -S $Dir \ - -B $CMakeDir \ - || (cd ->/dev/null ; exit 1) + CMakeGeneratorArgs=(-G "MinGW Makefiles") fi +cmake \ + -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \ + -DBUILD_SHARED_LIBS:BOOL=$CMakeBuildSharedLibsFlag \ + -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \ + -DCMAKE_BUILD_TYPE=$Configuration \ + -DCMAKE_VERBOSE_MAKEFILE:BOOL=$CMakeVerboseMakefileFlag \ + -DMSVC_USE_MT:BOOL=$CMakeMsvcMtFlag \ + $CMakeSTLSoftVariable \ + "${CMakeGeneratorArgs[@]}" \ + -B "$CMakeDir" \ + -S "$Dir" \ + || exit 1 + status=0 if [ $RunMake -ne 0 ]; then - echo "Executing build (via command \`$MakeCmd\`)" + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build)" - $MakeCmd + sis_cmake_build status=$? fi -cd ->/dev/null - if [ $VerboseMakefile -ne 0 ]; then - echo -e "contents of $CMakeDir:" - ls -al $CMakeDir + echo + echo -e "contents of ${CMakeDirClr}:" + ls -al "$CMakeDir" fi exit $status # ############################## end of file ############################# # - diff --git a/remove_cmake_artefacts.sh b/remove_cmake_artefacts.sh index a5e461c..377d3ab 100755 --- a/remove_cmake_artefacts.sh +++ b/remove_cmake_artefacts.sh @@ -1,13 +1,16 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 - +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} Directories=( CMakeFiles Testing @@ -25,14 +28,73 @@ Files=( cmake_install.cmake install_manifest.txt ) +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" # ########################################################## # operating environment detection -OsName="$(uname -s)" +OsName="$(uname -s 2>/dev/null || echo Unknown)" case "${OsName}" in - CYGWIN*|MINGW*|MSYS_NT*) + CYGWIN*|MINGW*|MSYS_NT*|Windows_NT) Directories+=( ARM64 @@ -44,9 +106,6 @@ case "${OsName}" in "*.sln" "*.vcxproj" ) - ;; - *) - ;; esac @@ -57,18 +116,27 @@ esac while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF Removes all known CMake artefacts -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + standard flags: @@ -81,7 +149,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -96,59 +164,50 @@ done if [ ! -d "$CMakeDir" ]; then - echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts" + echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts" exit 0 -else - - echo "Removing all ${ProjectName} cmake artefacts in '$CMakeDir'" +fi - num_dirs_removed=0 - num_files_removed=0 +echo "Removing all ${ProjectNameClr} cmake artefacts in '${CMakeDirClr}'" - for d in ${Directories[@]} - do +num_dirs_removed=0 +num_files_removed=0 - fq_dir_path="$CMakeDir/$d" +for d in "${Directories[@]}"; do - [ -d "$fq_dir_path" ] || continue + fq_dir_path="$CMakeDir/$d" - echo "removing directory '$d'" + [ -d "$fq_dir_path" ] || continue - rm -dfr "$fq_dir_path" + echo "removing directory '$d'" - num_dirs_removed=$((num_dirs_removed+1)) - done + rm -dfr "$fq_dir_path" - cd "$CMakeDir" + num_dirs_removed=$((num_dirs_removed+1)) +done - for f in ${Files[@]} - do +for f in "${Files[@]}"; do - for fq_file_path in $f - do + for fq_file_path in "$CMakeDir"/$f; do - [ -f "$fq_file_path" ] || continue + [ -f "$fq_file_path" ] || continue - echo "removing file '$fq_file_path'" + echo "removing file '$fq_file_path'" - rm -f "$fq_file_path" + rm -f "$fq_file_path" - num_files_removed=$((num_files_removed+1)) - done + num_files_removed=$((num_files_removed+1)) done +done - cd ->/dev/null - - if [ 0 -eq $num_dirs_removed ] && [ 0 -eq $num_files_removed ]; then +if [ 0 -eq $num_dirs_removed ] && [ 0 -eq $num_files_removed ]; then - echo "nothing to do" - else + echo "nothing to do" +else - echo "removed $num_dirs_removed directories and $num_files_removed files" - fi + echo "removed $num_dirs_removed directories and $num_files_removed files" fi # ############################## end of file ############################# # - diff --git a/run_all_automated_tests.cmd b/run_all_automated_tests.cmd new file mode 100644 index 0000000..b6dd937 --- /dev/null +++ b/run_all_automated_tests.cmd @@ -0,0 +1,106 @@ +@echo off + +SETLOCAL + +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] + +SET UnitOnly=0 +SET ComponentOnly=0 + +FOR %%a IN (%*) DO ( + + IF /I {--help}=={%%a} ( + + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ + +Runs all ^(matching^) automated test programs ^(unit and component^) ^ + +^ + +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ + +^ + +Flags/options: ^ + + behaviour: ^ + +^ + + --unit-only ^ + + runs only unit-test programs ^ + +^ + + --component-only ^ + + runs only component-test programs ^ + +^ + + standard flags: ^ + +^ + + --help ^ + + displays this help and terminates ^ + + + EXIT /B 0 + ) ELSE IF /I {--unit-only}=={%%a} ( + + SET UnitOnly=1 + ) ELSE IF /I {--component-only}=={%%a} ( + + SET ComponentOnly=1 + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) +) + +IF %UnitOnly% NEQ 0 IF %ComponentOnly% NEQ 0 ( + + ECHO %SCRIPT_PATH_DOC%: --unit-only and --component-only are mutually exclusive 1>&2 + + EXIT /B 1 +) + +IF %UnitOnly% NEQ 0 ( + CALL "%SCRIPT_DIRECTORY%run_all_unit_tests.cmd" + EXIT /B %ERRORLEVEL% +) + +IF %ComponentOnly% NEQ 0 ( + CALL "%SCRIPT_DIRECTORY%run_all_component_tests.cmd" + EXIT /B %ERRORLEVEL% +) + +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" + +IF NOT DEFINED ProjectName ( + + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 +) + +ECHO Running all %ProjectName% automated test programs ^(unit and component^) + +CALL "%SCRIPT_DIRECTORY%run_all_unit_tests.cmd" +IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% + +CALL "%SCRIPT_DIRECTORY%run_all_component_tests.cmd" +EXIT /B %ERRORLEVEL% + +ENDLOCAL diff --git a/run_all_automated_tests.sh b/run_all_automated_tests.sh new file mode 100755 index 0000000..2c13b16 --- /dev/null +++ b/run_all_automated_tests.sh @@ -0,0 +1,180 @@ +#! /bin/bash + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} +ProjectNameFile="$Dir/.sis/project_name.txt" +ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +ComponentOnly=0 +ForwardedArgs=() +SisUseColours=0 +UnitOnly=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# command-line handling + +while [[ $# -gt 0 ]]; do + + case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - handled above; forward so category runners see it + ForwardedArgs+=("$1") + ;; + --unit-only) + + UnitOnly=1 + ;; + --component-only) + + ComponentOnly=1 + ;; + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + cat << EOF +Runs all (matching) automated test programs (unit and component) + +${ScriptPath} [ ... flags/options ... ] + +Flags/options: + + behaviour: + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + --component-only + runs only component-test programs + + --unit-only + runs only unit-test programs + + (all other flags are forwarded to the category runner script) + + + standard flags: + + --help + displays this help and terminates + +EOF + + exit 0 + ;; + *) + + ForwardedArgs+=("$1") + ;; + esac + + shift +done + +if [ $UnitOnly -ne 0 ] && [ $ComponentOnly -ne 0 ]; then + + >&2 echo "${ScriptPathClr}: ${SisClr_Red}${SisClr_Bold}--unit-only${SisClr_None} and ${SisClr_Red}${SisClr_Bold}--component-only${SisClr_None} are mutually exclusive" + + exit 1 +fi + + +# ########################################################## +# main() + +status=0 + +if [ $UnitOnly -ne 0 ]; then + + "$Dir/run_all_unit_tests.sh" "${ForwardedArgs[@]}" + exit $? +fi + +if [ $ComponentOnly -ne 0 ]; then + + "$Dir/run_all_component_tests.sh" "${ForwardedArgs[@]}" + exit $? +fi + +echo +echo "Running all ${ProjectNameClr} automated test programs (unit and component)" + +"$Dir/run_all_unit_tests.sh" "${ForwardedArgs[@]}" +status=$? + +if [ $status -eq 0 ]; then + + "$Dir/run_all_component_tests.sh" "${ForwardedArgs[@]}" + status=$? +fi + +exit $status + + +# ############################## end of file ############################# # diff --git a/run_all_component_tests.cmd b/run_all_component_tests.cmd new file mode 100644 index 0000000..970c9ba --- /dev/null +++ b/run_all_component_tests.cmd @@ -0,0 +1,94 @@ +@echo off + +SETLOCAL + +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] + +IF DEFINED SIS_CMAKE_BUILD_DIR ( + + SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% +) ELSE ( + + SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build +) + +FOR %%a IN (%*) DO ( + + IF /I {--help}=={%%a} ( + + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ + +Runs all ^(matching^) component-test programs ^ + +^ + +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ + +^ + +Flags/options: ^ + + behaviour: ^ + +^ + + --component-only ^ + + accepted for compatibility; this script always runs component tests only ^ + +^ + + standard flags: ^ + +^ + + --help ^ + + displays this help and terminates ^ + + + EXIT /B 0 + ) ELSE IF /I {--component-only}=={%%a} ( + + REM Benign: this script is already component-only + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) +) + +if NOT EXIST "%CMAKE_DIR%" ( + + ECHO "CMake build directory '%CMAKE_DIR%' does not exist" + + EXIT /B 1 +) + +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" + +IF NOT DEFINED ProjectName ( + + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 +) + +ECHO Running all %ProjectName% component-test programs + +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*component.*\.exe$`) DO ( + + ECHO . + ECHO executing %%f + %%f + IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% +) + +ENDLOCAL diff --git a/run_all_component_tests.sh b/run_all_component_tests.sh new file mode 100755 index 0000000..c0bd27e --- /dev/null +++ b/run_all_component_tests.sh @@ -0,0 +1,300 @@ +#! /bin/bash + +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} +ProjectNameFile="$Dir/.sis/project_name.txt" +ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +ListOnly=0 +RunMake=1 +SisUseColours=0 +Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + + +# ########################################################## +# command-line handling + +while [[ $# -gt 0 ]]; do + + case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; + --component-only) + + # Benign: this script is already component-only (aggregate / CI may + # pass it) + ;; + --list-only|-l) + + ListOnly=1 + ;; + --no-make|-M) + + RunMake=0 + ;; + --verbosity) + + shift + Verbosity=$1 + ;; + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + cat << EOF +Runs all (matching) component-test programs + +${ScriptPath} [ ... flags/options ... ] + +Flags/options: + + behaviour: + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + --component-only + accepted for compatibility; this script always runs component tests + only + + -l + --list-only + lists the target programs but does not execute them + + -M + --no-make + does not execute a build before running programs + + --verbosity + specifies an explicit verbosity, forwarded to each program + + + standard flags: + + --help + displays this help and terminates + +EOF + + exit 0 + ;; + *) + + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" + + exit 1 + ;; + esac + + shift +done + + +# ########################################################## +# main() + +status=0 + +if [ $RunMake -ne 0 ]; then + + if [ $ListOnly -eq 0 ]; then + + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all component-test programs" + + mkdir -p "$CMakeDir" || exit 1 + + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" + + exit 1 + fi + + sis_cmake_build + status=$? + fi +else + + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" + + exit 1 + fi +fi + +if [ $status -eq 0 ]; then + + if [ $ListOnly -ne 0 ]; then + + echo + echo "Listing all ${ProjectNameClr} component-test programs" + else + + echo + echo "Running all ${ProjectNameClr} component-test programs" + fi + + NumPrograms=0 + + while IFS= read -r -d '' f; do + + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac + + NumPrograms=$((NumPrograms + 1)) + + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" + + if [ $ListOnly -ne 0 ]; then + + echo "would execute ${fClr}:" + + continue + fi + + if [ $Verbosity -ge 3 ]; then + + echo + fi + if [ $Verbosity -ge 2 ]; then + + echo "executing ${fClr}:" + fi + + if "$f" --verbosity="$Verbosity"; then + + : + else + + status=$? + + break 1 + fi + done < <(find "$CMakeDir" -type f \( -name 'test_component*' -o -name 'test.component.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + echo "${ScriptPathClr}: found no component-test programs under '${CMakeDirClr}' (none found)" + + exit 0 + fi +fi + +exit $status + + +# ############################## end of file ############################# # diff --git a/run_all_examples.cmd b/run_all_examples.cmd new file mode 100644 index 0000000..b10e3d2 --- /dev/null +++ b/run_all_examples.cmd @@ -0,0 +1,131 @@ +@echo off + +SETLOCAL + +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] +SET "ALLOWED_TO_FAIL_FILE=%SCRIPT_DIRECTORY%.sis\ci_examples_allowed_to_fail.txt" +SET "PROGRAM_STATUS=0" + +IF DEFINED SIS_CMAKE_BUILD_DIR ( + + SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% +) ELSE ( + + SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build +) + +FOR %%a IN (%*) DO ( + + IF /I {--help}=={%%a} ( + + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ + +Runs all ^(matching^) example programs ^ + +^ + +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ + +^ + +Flags/options: ^ + + behaviour: ^ + +^ + + standard flags: ^ + +^ + + --help ^ + + displays this help and terminates ^ + +^ + + files: ^ + +^ + + .sis\ci_examples_allowed_to_fail.txt ^ + + optional list of example programs ^(one name per line; ^ + + lines beginning with '#' are ignored^) that are allowed to fail; ^ + + such a program is still executed, but a non-zero exit is ^ + + reported as anticipated and does not stop the run ^ + + + EXIT /B 0 + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) +) + +if NOT EXIST "%CMAKE_DIR%" ( + + ECHO "CMake build directory '%CMAKE_DIR%' does not exist" + + EXIT /B 1 +) + +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" + +IF NOT DEFINED ProjectName ( + + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 +) + +REM Examples that require human input may honour SIS_EXAMPLE_SMOKE for a +REM no-arg built-in tmpfile demo (see example.c.cstring_vector). +SET SIS_EXAMPLE_SMOKE=1 + +ECHO Running all %ProjectName% example programs + +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I example.*\.exe$`) DO CALL :run_program "%%f" + +ENDLOCAL & EXIT /B %PROGRAM_STATUS% + + +REM ########################################################## +REM subroutines + +:run_program +IF %PROGRAM_STATUS% NEQ 0 EXIT /B 0 +ECHO . +ECHO executing %~1 +"%~1" +SET "FAILURE_STATUS=%ERRORLEVEL%" +IF %FAILURE_STATUS% EQU 0 EXIT /B 0 +CALL :is_allowed_to_fail "%~1" +IF NOT DEFINED ALLOWED_TO_FAIL GOTO run_program_failed +ECHO anticipated failure: %~1 exited with status %FAILURE_STATUS%; it is listed in .sis\ci_examples_allowed_to_fail.txt +EXIT /B 0 +:run_program_failed +SET "PROGRAM_STATUS=%FAILURE_STATUS%" +EXIT /B 0 + +:is_allowed_to_fail +SET "ALLOWED_TO_FAIL=" +IF NOT EXIST "%ALLOWED_TO_FAIL_FILE%" EXIT /B 0 +FOR /F "usebackq eol=# tokens=1" %%n IN ("%ALLOWED_TO_FAIL_FILE%") DO CALL :match_allowed "%%n" "%~n1" "%~nx1" +EXIT /B 0 + +:match_allowed +IF /I "%~1"=="%~2" SET "ALLOWED_TO_FAIL=1" +IF /I "%~1"=="%~3" SET "ALLOWED_TO_FAIL=1" +EXIT /B 0 diff --git a/run_all_examples.sh b/run_all_examples.sh index d817561..722326a 100755 --- a/run_all_examples.sh +++ b/run_all_examples.sh @@ -1,36 +1,164 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 +AllowedToFailFile="$Dir/.sis/ci_examples_allowed_to_fail.txt" - +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} ListOnly=0 RunMake=1 +SisUseColours=0 # ########################################################## # colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done -if command -v tput > /dev/null; then +if command -v tput >/dev/null 2>&1; then - RbEnvClr_Blue=${FG_BLUE:-$(tput setaf 4)} - RbEnvClr_Red=${FG_BLUE:-$(tput setaf 1)} - RbEnvClr_Bold=${FD_BOLD:-$(tput bold)} - RbEnvClr_None=${FD_NONE:-$(tput sgr0)} -else + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then - RbEnvClr_Blue= - RbEnvClr_Red= - RbEnvClr_Bold= - RbEnvClr_None= + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} fi +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + +# Reduces a program path to its (lowercase) name, without any directory or +# .exe suffix. +sis_program_stem() { + + local p="${1//\\//}" + + p="${p##*/}" + + case "$p" in + *.exe|*.EXE) p="${p%.*}" ;; + esac + + printf '%s' "$p" | tr '[:upper:]' '[:lower:]' +} + +# Succeeds if the program is named (by name or stem, case-insensitively) in +# the optional .sis/ci_examples_allowed_to_fail.txt file; blank lines and +# lines beginning with '#' are ignored. +sis_is_allowed_to_fail() { + + local name line + + [ -f "$AllowedToFailFile" ] || return 1 + + name=$(sis_program_stem "$1") + + while IFS= read -r line || [ -n "$line" ]; do + + line="${line//$'\r'/}" + + case "$line" in + ''|\#*) continue ;; + esac + + if [ "$name" = "$(sis_program_stem "$line")" ]; then + + return 0 + fi + done < "$AllowedToFailFile" + + return 1 +} + # ########################################################## # command-line handling @@ -38,6 +166,10 @@ fi while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --list-only|-l) ListOnly=1 @@ -52,19 +184,33 @@ while [[ $# -gt 0 ]]; do cat << EOF Runs all (matching) example programs -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + -l --list-only lists the target programs but does not execute them -M --no-make - does not execute CMake and make before running tests + does not execute a build before running programs + + + files: + + .sis/ci_examples_allowed_to_fail.txt + optional list of example programs (one name per line; blank lines + and lines beginning with '#' are ignored) that are allowed to fail; + such a program is still executed, but a non-zero exit is reported as + anticipated and neither stops the run nor affects the exit status standard flags: @@ -78,7 +224,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -97,22 +243,28 @@ if [ $RunMake -ne 0 ]; then if [ $ListOnly -eq 0 ]; then - echo "Executing build of ${ProjectName} (via command \`$MakeCmd\`) and then running all example programs" + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all example programs" - mkdir -p $CMakeDir || exit 1 + mkdir -p "$CMakeDir" || exit 1 - cd $CMakeDir + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - $MakeCmd - status=$? + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" - cd ->/dev/null + exit 1 + fi + + sis_cmake_build + status=$? fi else - if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" - >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step" + exit 1 fi fi @@ -120,31 +272,77 @@ if [ $status -eq 0 ]; then if [ $ListOnly -ne 0 ]; then - echo "Listing all ${ProjectName} example programs" + echo + echo "Listing all ${ProjectNameClr} example programs" else - echo "Running all ${ProjectName} example programs" + echo + echo "Running all ${ProjectNameClr} example programs" fi - for f in $(find $CMakeDir/examples -type f -exec test -x {} \; -print) - do + # Examples that require human input may honour SIS_EXAMPLE_SMOKE for a + # no-arg built-in tmpfile demo (see example.c.cstring_vector). + export SIS_EXAMPLE_SMOKE=1 + + NumPrograms=0 + + while IFS= read -r -d '' f; do + + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac + + NumPrograms=$((NumPrograms + 1)) + + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" if [ $ListOnly -ne 0 ]; then - echo "would execute $RbEnvClr_Blue$RbEnvClr_Bold$f$RbEnvClr_None:" + if sis_is_allowed_to_fail "$f"; then + + echo "would execute ${fClr} (allowed to fail):" + else + + echo "would execute ${fClr}:" + fi continue fi echo - echo "executing $RbEnvClr_Blue$RbEnvClr_Bold$f$RbEnvClr_None:" + echo "executing ${fClr}:" + + if "$f"; then + + : + else - $f - done + fStatus=$? + + if sis_is_allowed_to_fail "$f"; then + + echo "${SisClr_Yellow}${SisClr_Bold}anticipated failure${SisClr_None}: ${fClr} exited with status ${fStatus}; it is listed in .sis/ci_examples_allowed_to_fail.txt" + + continue + fi + + status=$fStatus + + break 1 + fi + done < <(find "$CMakeDir" -type f \( -name 'example*' ! -name '*.md' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + echo "${ScriptPathClr}: found no example programs under '${CMakeDirClr}' (none found)" + + exit 0 + fi fi exit $status # ############################## end of file ############################# # - diff --git a/run_all_performance_tests.cmd b/run_all_performance_tests.cmd new file mode 100644 index 0000000..ae97766 --- /dev/null +++ b/run_all_performance_tests.cmd @@ -0,0 +1,85 @@ +@echo off + +SETLOCAL + +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] + +IF DEFINED SIS_CMAKE_BUILD_DIR ( + + SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% +) ELSE ( + + SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build +) + +FOR %%a IN (%*) DO ( + + IF /I {--help}=={%%a} ( + + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ + +Runs all ^(matching^) performance-test programs ^ + +^ + +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ + +^ + +Flags/options: ^ + + behaviour: ^ + +^ + + standard flags: ^ + +^ + + --help ^ + + displays this help and terminates ^ + + + EXIT /B 0 + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) +) + +if NOT EXIST "%CMAKE_DIR%" ( + + ECHO "CMake build directory '%CMAKE_DIR%' does not exist" + + EXIT /B 1 +) + +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" + +IF NOT DEFINED ProjectName ( + + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 +) + +ECHO Running all %ProjectName% performance-test programs + +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*performance.*\.exe$`) DO ( + + ECHO . + ECHO executing %%f + %%f + IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% +) + +ENDLOCAL diff --git a/run_all_performance_tests.sh b/run_all_performance_tests.sh new file mode 100755 index 0000000..3feaaff --- /dev/null +++ b/run_all_performance_tests.sh @@ -0,0 +1,276 @@ +#! /bin/bash + +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} +ProjectNameFile="$Dir/.sis/project_name.txt" +ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +ListOnly=0 +RunMake=1 +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + + +# ########################################################## +# command-line handling + +while [[ $# -gt 0 ]]; do + + case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; + --list-only|-l) + + ListOnly=1 + ;; + --no-make|-M) + + RunMake=0 + ;; + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + cat << EOF +Runs all (matching) performance-test programs + +${ScriptPath} [ ... flags/options ... ] + +Flags/options: + + behaviour: + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + -l + --list-only + lists the target programs but does not execute them + + -M + --no-make + does not execute a build before running programs + + + standard flags: + + --help + displays this help and terminates + +EOF + + exit 0 + ;; + *) + + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" + + exit 1 + ;; + esac + + shift +done + + +# ########################################################## +# main() + +status=0 + +if [ $RunMake -ne 0 ]; then + + if [ $ListOnly -eq 0 ]; then + + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all performance-test programs" + + mkdir -p "$CMakeDir" || exit 1 + + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" + + exit 1 + fi + + sis_cmake_build + status=$? + fi +else + + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" + + exit 1 + fi +fi + +if [ $status -eq 0 ]; then + + if [ $ListOnly -ne 0 ]; then + + echo + echo "Listing all ${ProjectNameClr} performance-test programs" + else + + echo + echo "Running all ${ProjectNameClr} performance-test programs" + fi + + NumPrograms=0 + + while IFS= read -r -d '' f; do + + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac + + NumPrograms=$((NumPrograms + 1)) + + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" + + if [ $ListOnly -ne 0 ]; then + + echo "would execute ${fClr}:" + + continue + fi + + echo + echo "executing ${fClr}:" + + if "$f"; then + + : + else + + status=$? + + break 1 + fi + done < <(find "$CMakeDir" -type f \( -name 'test_performance*' -o -name 'test.performance.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + echo "${ScriptPathClr}: found no performance-test programs under '${CMakeDirClr}' (none found)" + + exit 0 + fi +fi + +exit $status + + +# ############################## end of file ############################# # diff --git a/run_all_scratch_tests.cmd b/run_all_scratch_tests.cmd new file mode 100644 index 0000000..0402fb1 --- /dev/null +++ b/run_all_scratch_tests.cmd @@ -0,0 +1,127 @@ +@echo off + +SETLOCAL + +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] +SET "ALLOWED_TO_FAIL_FILE=%SCRIPT_DIRECTORY%.sis\ci_scratch_tests_allowed_to_fail.txt" +SET "PROGRAM_STATUS=0" + +IF DEFINED SIS_CMAKE_BUILD_DIR ( + + SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% +) ELSE ( + + SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build +) + +FOR %%a IN (%*) DO ( + + IF /I {--help}=={%%a} ( + + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ + +Runs all ^(matching^) scratch-test programs ^ + +^ + +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ + +^ + +Flags/options: ^ + + behaviour: ^ + +^ + + standard flags: ^ + +^ + + --help ^ + + displays this help and terminates ^ + +^ + + files: ^ + +^ + + .sis\ci_scratch_tests_allowed_to_fail.txt ^ + + optional list of scratch-test programs ^(one name per line; ^ + + lines beginning with '#' are ignored^) that are allowed to fail; ^ + + such a program is still executed, but a non-zero exit is ^ + + reported as anticipated and does not stop the run ^ + + + EXIT /B 0 + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) +) + +if NOT EXIST "%CMAKE_DIR%" ( + + ECHO "CMake build directory '%CMAKE_DIR%' does not exist" + + EXIT /B 1 +) + +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" + +IF NOT DEFINED ProjectName ( + + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 +) + +ECHO Running all %ProjectName% scratch-test programs + +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*scratch.*\.exe$`) DO CALL :run_program "%%f" + +ENDLOCAL & EXIT /B %PROGRAM_STATUS% + + +REM ########################################################## +REM subroutines + +:run_program +IF %PROGRAM_STATUS% NEQ 0 EXIT /B 0 +ECHO . +ECHO executing %~1 +"%~1" +SET "FAILURE_STATUS=%ERRORLEVEL%" +IF %FAILURE_STATUS% EQU 0 EXIT /B 0 +CALL :is_allowed_to_fail "%~1" +IF NOT DEFINED ALLOWED_TO_FAIL GOTO run_program_failed +ECHO anticipated failure: %~1 exited with status %FAILURE_STATUS%; it is listed in .sis\ci_scratch_tests_allowed_to_fail.txt +EXIT /B 0 +:run_program_failed +SET "PROGRAM_STATUS=%FAILURE_STATUS%" +EXIT /B 0 + +:is_allowed_to_fail +SET "ALLOWED_TO_FAIL=" +IF NOT EXIST "%ALLOWED_TO_FAIL_FILE%" EXIT /B 0 +FOR /F "usebackq eol=# tokens=1" %%n IN ("%ALLOWED_TO_FAIL_FILE%") DO CALL :match_allowed "%%n" "%~n1" "%~nx1" +EXIT /B 0 + +:match_allowed +IF /I "%~1"=="%~2" SET "ALLOWED_TO_FAIL=1" +IF /I "%~1"=="%~3" SET "ALLOWED_TO_FAIL=1" +EXIT /B 0 diff --git a/run_all_scratch_tests.sh b/run_all_scratch_tests.sh index beaf1ea..6e06f13 100755 --- a/run_all_scratch_tests.sh +++ b/run_all_scratch_tests.sh @@ -1,18 +1,163 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 +AllowedToFailFile="$Dir/.sis/ci_scratch_tests_allowed_to_fail.txt" - +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} ListOnly=0 RunMake=1 -Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + +# Reduces a program path to its (lowercase) name, without any directory or +# .exe suffix. +sis_program_stem() { + + local p="${1//\\//}" + + p="${p##*/}" + + case "$p" in + *.exe|*.EXE) p="${p%.*}" ;; + esac + + printf '%s' "$p" | tr '[:upper:]' '[:lower:]' +} + +# Succeeds if the program is named (by name or stem, case-insensitively) in +# the optional .sis/ci_scratch_tests_allowed_to_fail.txt file; blank lines +# and lines beginning with '#' are ignored. +sis_is_allowed_to_fail() { + + local name line + + [ -f "$AllowedToFailFile" ] || return 1 + + name=$(sis_program_stem "$1") + + while IFS= read -r line || [ -n "$line" ]; do + + line="${line//$'\r'/}" + + case "$line" in + ''|\#*) continue ;; + esac + + if [ "$name" = "$(sis_program_stem "$line")" ]; then + + return 0 + fi + done < "$AllowedToFailFile" + + return 1 +} # ########################################################## @@ -21,6 +166,10 @@ Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --list-only|-l) ListOnly=1 @@ -29,33 +178,40 @@ while [[ $# -gt 0 ]]; do RunMake=0 ;; - --verbosity) - - shift - Verbosity=$1 - ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF -Runs all (matching) performance-test and scratch-test programs +Runs all (matching) scratch-test programs -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + -l --list-only lists the target programs but does not execute them -M --no-make - does not execute CMake and make before running tests + does not execute a build before running programs + - --verbosity - specifies an explicit verbosity for the unit-test(s) + files: + + .sis/ci_scratch_tests_allowed_to_fail.txt + optional list of scratch-test programs (one name per line; blank + lines and lines beginning with '#' are ignored) that are allowed to + fail; such a program is still executed, but a non-zero exit is + reported as anticipated and neither stops the run nor affects the + exit status standard flags: @@ -69,7 +225,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -88,22 +244,28 @@ if [ $RunMake -ne 0 ]; then if [ $ListOnly -eq 0 ]; then - echo "Executing build of ${ProjectName} (via command \`$MakeCmd\`) and then running all scratch (and performance) test programs" + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all scratch-test programs" - mkdir -p $CMakeDir || exit 1 + mkdir -p "$CMakeDir" || exit 1 - cd $CMakeDir + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - $MakeCmd - status=$? + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" - cd ->/dev/null + exit 1 + fi + + sis_cmake_build + status=$? fi else - if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" - >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step" + exit 1 fi fi @@ -111,43 +273,73 @@ if [ $status -eq 0 ]; then if [ $ListOnly -ne 0 ]; then - echo "Listing all ${ProjectName} scratch (and performance) test programs" + echo + echo "Listing all ${ProjectNameClr} scratch-test programs" else - echo "Running all ${ProjectName} scratch (and performance) test programs" + echo + echo "Running all ${ProjectNameClr} scratch-test programs" fi - for f in $(find $CMakeDir -type f '(' -name 'test_scratch*' -o -name 'test.scratch.*' -o -name 'test_performance*' -o -name 'test.performance.*' ')' -exec test -x {} \; -print) - do + NumPrograms=0 + + while IFS= read -r -d '' f; do + + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac + + NumPrograms=$((NumPrograms + 1)) + + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" if [ $ListOnly -ne 0 ]; then - echo "would execute $f:" + if sis_is_allowed_to_fail "$f"; then - continue - fi + echo "would execute ${fClr} (allowed to fail):" + else - if [ $Verbosity -ge 3 ]; then + echo "would execute ${fClr}:" + fi - echo + continue fi - if [ $Verbosity -ge 2 ]; then - echo "executing $f:" - fi + echo + echo "executing ${fClr}:" - if $f; then + if "$f"; then : else - status=$? + fStatus=$? + + if sis_is_allowed_to_fail "$f"; then + + echo "${SisClr_Yellow}${SisClr_Bold}anticipated failure${SisClr_None}: ${fClr} exited with status ${fStatus}; it is listed in .sis/ci_scratch_tests_allowed_to_fail.txt" + + continue + fi + + status=$fStatus + + break 1 fi - done + done < <(find "$CMakeDir" -type f \( -name 'test_scratch*' -o -name 'test.scratch.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + echo "${ScriptPathClr}: found no scratch-test programs under '${CMakeDirClr}' (none found)" + + exit 0 + fi fi exit $status # ############################## end of file ############################# # - diff --git a/run_all_unit_tests.cmd b/run_all_unit_tests.cmd index 691010b..18b040a 100644 --- a/run_all_unit_tests.cmd +++ b/run_all_unit_tests.cmd @@ -4,6 +4,7 @@ SETLOCAL SET SCRIPT_DIRECTORY=%~dp0 SET SCRIPT_PATH_DOC=%~n0[%~x0] + IF DEFINED SIS_CMAKE_BUILD_DIR ( SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% @@ -13,14 +14,16 @@ IF DEFINED SIS_CMAKE_BUILD_DIR ( ) FOR %%a IN (%*) DO ( + IF /I {--help}=={%%a} ( + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ) ECHO ^ -Runs all ^(matching^) component-test and unit-test programs ^ +Runs all ^(matching^) unit-test programs ^ ^ @@ -32,6 +35,12 @@ Flags/options: ^ behaviour: ^ +^ + + --unit-only ^ + + accepted for compatibility; this script always runs unit tests only ^ + ^ standard flags: ^ @@ -44,7 +53,11 @@ Flags/options: ^ EXIT /B 0 + ) ELSE IF /I {--unit-only}=={%%a} ( + + REM Benign: this script is already unit-only ) ELSE ( + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 EXIT /B 1 @@ -63,18 +76,19 @@ FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO IF NOT DEFINED ProjectName ( - ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 - EXIT /B 1 + EXIT /B 1 ) -ECHO Running all %ProjectName% unit test programs +ECHO Running all %ProjectName% unit-test programs + +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*unit.*\.exe$`) DO ( -FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S %CMAKE_DIR% ^| FINDSTR /I test[._]unit.*\.exe$`) DO ( ECHO . ECHO executing %%f %%f + IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% ) ENDLOCAL - diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh index b986829..301ccae 100755 --- a/run_all_unit_tests.sh +++ b/run_all_unit_tests.sh @@ -1,28 +1,132 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 - +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} ListOnly=0 RunMake=1 -UnitOnly=0 -ComponentOnly=0 +SisUseColours=0 Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + + # ########################################################## # command-line handling while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --list-only|-l) ListOnly=1 @@ -33,11 +137,7 @@ while [[ $# -gt 0 ]]; do ;; --unit-only) - UnitOnly=1 - ;; - --component-only) - - ComponentOnly=1 + # Benign: this script is already unit-only (aggregate / CI may pass it) ;; --verbosity) @@ -48,30 +148,32 @@ while [[ $# -gt 0 ]]; do [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF -Runs all (matching) unit-test and/or component-test programs +Runs all (matching) unit-test programs -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + -l --list-only lists the target programs but does not execute them -M --no-make - does not execute CMake and make before running tests + does not execute a build before running programs --unit-only - runs only unit-test programs (test.unit.* / test_unit*) - - --component-only - runs only component-test programs (test.component.* / test_component*) + accepted for compatibility; this script always runs unit tests only --verbosity - specifies an explicit verbosity for the unit-test(s) + specifies an explicit verbosity, forwarded to each program standard flags: @@ -85,7 +187,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -94,50 +196,36 @@ EOF shift done -if [ $UnitOnly -ne 0 ] && [ $ComponentOnly -ne 0 ]; then - - >&2 echo "$ScriptPath: --unit-only and --component-only are mutually exclusive" - - exit 1 -fi - # ########################################################## # main() status=0 -if [ $UnitOnly -ne 0 ]; then - - TestKindDescription='unit test' -elif [ $ComponentOnly -ne 0 ]; then - - TestKindDescription='component test' -else - - TestKindDescription='component and unit test' -fi - if [ $RunMake -ne 0 ]; then if [ $ListOnly -eq 0 ]; then - echo "Executing build of ${ProjectName} (via command \`$MakeCmd\`) and then running all ${TestKindDescription} programs" + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all unit-test programs" - mkdir -p $CMakeDir || exit 1 + mkdir -p "$CMakeDir" || exit 1 - cd $CMakeDir + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - $MakeCmd - status=$? + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" + + exit 1 + fi - cd ->/dev/null + sis_cmake_build + status=$? fi else - if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step" + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" exit 1 fi @@ -147,29 +235,31 @@ if [ $status -eq 0 ]; then if [ $ListOnly -ne 0 ]; then - echo "Listing all ${ProjectName} ${TestKindDescription} programs" + echo + echo "Listing all ${ProjectNameClr} unit-test programs" else - echo "Running all ${ProjectName} ${TestKindDescription} programs" + echo + echo "Running all ${ProjectNameClr} unit-test programs" fi - if [ $UnitOnly -ne 0 ]; then + NumPrograms=0 - find_name_expr=( \( -name 'test_unit*' -o -name 'test.unit.*' \) ) - elif [ $ComponentOnly -ne 0 ]; then + while IFS= read -r -d '' f; do - find_name_expr=( \( -name 'test_component*' -o -name 'test.component.*' \) ) - else + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac - find_name_expr=( \( -name 'test_unit*' -o -name 'test.unit.*' -o -name 'test_component*' -o -name 'test.component.*' \) ) - fi + NumPrograms=$((NumPrograms + 1)) - for f in $(find "$CMakeDir" -type f "${find_name_expr[@]}" -exec test -x {} \; -print | sort) - do + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" if [ $ListOnly -ne 0 ]; then - echo "would execute $f:" + echo "would execute ${fClr}:" continue fi @@ -180,10 +270,10 @@ if [ $status -eq 0 ]; then fi if [ $Verbosity -ge 2 ]; then - echo "executing $f:" + echo "executing ${fClr}:" fi - if $f --verbosity=$Verbosity; then + if "$f" --verbosity="$Verbosity"; then : else @@ -192,11 +282,17 @@ if [ $status -eq 0 ]; then break 1 fi - done + done < <(find "$CMakeDir" -type f \( -name 'test_unit*' -o -name 'test.unit.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + echo "${ScriptPathClr}: found no unit-test programs under '${CMakeDirClr}' (none found)" + + exit 0 + fi fi exit $status # ############################## end of file ############################# # - diff --git a/test/performance/CMakeLists.txt b/test/performance/CMakeLists.txt index 5d84725..8b13789 100644 --- a/test/performance/CMakeLists.txt +++ b/test/performance/CMakeLists.txt @@ -1,2 +1 @@ -# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten diff --git a/test/scratch/CMakeLists.txt b/test/scratch/CMakeLists.txt index 5d84725..c6a5e32 100644 --- a/test/scratch/CMakeLists.txt +++ b/test/scratch/CMakeLists.txt @@ -1,2 +1,2 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten - +add_subdirectory(versions) diff --git a/test/scratch/versions/CMakeLists.txt b/test/scratch/versions/CMakeLists.txt new file mode 100644 index 0000000..b94b3ed --- /dev/null +++ b/test/scratch/versions/CMakeLists.txt @@ -0,0 +1,21 @@ +# Manual: conditional HAS_STLSoft / shwild link for the versions reporter +# (generator cannot preserve these). Do not restore SIS:AUTO_GENERATED. +define_example_program(test.scratch.versions main.cpp) + +if(STLSoft_FOUND OR DEFINED STLSOFT_INCLUDE_DIR) + + target_compile_definitions(test.scratch.versions + PRIVATE + HAS_STLSoft + ) + + target_link_STLSoft(test.scratch.versions) +endif() + +if(shwild_FOUND) + + target_link_libraries(test.scratch.versions + PRIVATE + shwild::core + ) +endif() diff --git a/test/scratch/versions/main.cpp b/test/scratch/versions/main.cpp new file mode 100644 index 0000000..d4ba209 --- /dev/null +++ b/test/scratch/versions/main.cpp @@ -0,0 +1,137 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/scratch/versions/main.cpp + * + * Purpose: Scratch version reporter for Diagnosticism (self-formatted) + * and optional test-time efferent siblings. + * + * Created: 5th August 2026 + * Updated: 13th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +#include +#include + +#ifdef HAS_STLSoft +# include +#endif /* HAS_STLSoft */ +#ifdef HAS_shwild +# include +#endif /* HAS_shwild */ + +#include +#include + +#include + + +#define PROGRAM_NAME "test.scratch.versions" + + +template< + typename T_stream +, typename T_integer +> +int +version( + T_stream& stm +, char const* prefix +, char const* libname +, char const* macroname +, T_integer libver +) +{ + char buffer[101]; + size_t n; + int const r = diagnosticism_calc_version_string( + buffer + , sizeof(buffer) / sizeof(buffer[0]) + , (int)((libver >> 24) & 0xff) + , (int)((libver >> 16) & 0xff) + , (int)((libver >> 8) & 0xff) + , (int)((libver >> 0) & 0xff) + , &n + ); + + if (0 != r) + { + std::cerr + << PROGRAM_NAME + << ": failed to format version string for " + << libname + << std::endl + ; + + return EXIT_FAILURE; + } + + buffer[n] = '\0'; + + stm + << prefix + << libname + << ": " + << buffer + << " (" + << macroname + << " = 0x" + << std::hex << std::setfill('0') << std::setw(8) + << static_cast(libver) + << std::dec + << ")" + << std::endl + ; + + return EXIT_SUCCESS; +} + + +int main(int /* argc */, char* /* argv */[]) +{ + { + unsigned const libver = DIAGNOSTICISM_VER; + + if (EXIT_SUCCESS != version(std::cout, "", "Diagnosticism", "DIAGNOSTICISM_VER", libver)) + { + return EXIT_FAILURE; + } + } + +#if defined(HAS_STLSoft) ||\ + defined(HAS_shwild) + + std::cout << "\n" << "efferent dependencies:" << std::endl; + +# ifdef HAS_STLSoft + + { + unsigned const libver = _STLSOFT_VER; + + if (EXIT_SUCCESS != version(std::cout, "\t", "STLSoft", "_STLSOFT_VER", libver)) + { + return EXIT_FAILURE; + } + } +# endif /* HAS_STLSoft */ + +# ifdef HAS_shwild + + { + unsigned const libver = SHWILD_VER; + + if (EXIT_SUCCESS != version(std::cout, "\t", "shwild", "SHWILD_VER", libver)) + { + return EXIT_FAILURE; + } + } +# endif /* HAS_shwild */ + +#endif /* HAS_STLSoft || HAS_shwild */ + + return EXIT_SUCCESS; +} + + +/* ///////////////////////////// end of file //////////////////////////// */ +