diff --git a/.github/workflows/ScanBuild.yml b/.github/workflows/ScanBuild.yml index 299d84f97..1e53cb4bb 100644 --- a/.github/workflows/ScanBuild.yml +++ b/.github/workflows/ScanBuild.yml @@ -1,26 +1,23 @@ ############################################################### # -## Copyright (©) 2025 International Color Consortium. -## All rights reserved. -## https://color.org -# -# -## Intent: iccDEV Scan Build Runner for Matrix OS -# -## Last Updated: 28-NOV-2025 0000Z by David Hoyt -## Add Read Permission Block -## TODO: Push binary releases, tags etc.. -# -## -# +# Copyright (©) 2025 International Color Consortium. +# All rights reserved. +# https://color.org # # +# Intent: iccDEV Scan Build Runner # +# Last Updated: 2026-02-17 16:14:17 UTC by David Hoyt +# Parallel build, hardened shells, sanitizer, +# dynamic LLVM path, libjpeg-dev dependency. # ############################################################### name: "Scan Build" +permissions: + contents: read + on: workflow_dispatch: @@ -28,114 +25,147 @@ jobs: build-linux: name: Build and Test Linux with scan-build runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - + timeout-minutes: 30 strategy: fail-fast: false steps: - - name: 📥 Checkout master + - name: Checkout uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 + with: + fetch-depth: 0 + persist-credentials: false - # Install dependencies - name: Install dependencies - shell: bash + shell: bash --noprofile --norc {0} env: - BASH_ENV: /dev/null + BASH_ENV: /dev/null run: | set -euo pipefail git config --global --add safe.directory "$GITHUB_WORKSPACE" git config --global credential.helper "" - - # Clear the in-shell GITHUB_TOKEN unset GITHUB_TOKEN || true - sudo apt-get update - sudo apt-get install -y build-essential cmake gcc g++ clang clang-tools libpng-dev libxml2 libxml2-dev libtiff-dev nlohmann-json3-dev libwxgtk3.2-dev wx-common python3 python3-pip curl git llvm - # Ensure scan-build is in PATH - - name: Ensure scan-build is installed and accessible - shell: bash - env: - BASH_ENV: /dev/null - run: | - set -euo pipefail - git config --global --add safe.directory "$GITHUB_WORKSPACE" - git config --global credential.helper "" - - # Clear the in-shell GITHUB_TOKEN - unset GITHUB_TOKEN || true + sudo apt-get update -qq + sudo apt-get install -y \ + build-essential cmake gcc g++ clang clang-tools \ + libpng-dev libxml2-dev libtiff-dev libjpeg-dev \ + nlohmann-json3-dev libwxgtk3.2-dev wx-common \ + python3 curl git llvm + + echo "### Environment" >> "$GITHUB_STEP_SUMMARY" + echo "| Tool | Version |" >> "$GITHUB_STEP_SUMMARY" + echo "|------|---------|" >> "$GITHUB_STEP_SUMMARY" + echo "| scan-build | $(scan-build --version 2>&1 | head -1) |" >> "$GITHUB_STEP_SUMMARY" + echo "| clang | $(clang --version 2>&1 | head -1) |" >> "$GITHUB_STEP_SUMMARY" + echo "| cmake | $(cmake --version | head -1) |" >> "$GITHUB_STEP_SUMMARY" + echo "| nproc | $(nproc) |" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" - which scan-build || echo "? scan-build not found" - scan-build --version || echo "? scan-build version check failed" - # Configure the build with scan-build - - name: Configure the build with scan-build - shell: bash + - name: Configure with scan-build + shell: bash --noprofile --norc {0} env: - BASH_ENV: /dev/null + BASH_ENV: /dev/null + CC: clang + CXX: clang++ run: | set -euo pipefail git config --global --add safe.directory "$GITHUB_WORKSPACE" git config --global credential.helper "" - - # Clear the in-shell GITHUB_TOKEN unset GITHUB_TOKEN || true - ls + LLVM_BIN=$(llvm-config --bindir 2>/dev/null || echo "/usr/lib/llvm-$(llvm-config --version 2>/dev/null | cut -d. -f1)/bin") + export PATH="${LLVM_BIN}:${PATH}" + cd Build - pwd - ls - export CC=clang - export CXX=clang++ - export PATH="/usr/lib/llvm-17/bin:$PATH" - scan-build cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Release -DENABLE_TOOLS=ON -Wno-dev Cmake/ - # Run scan-build for static analysis - - name: Run scan-build for static analysis - shell: bash + scan-build cmake \ + -DCMAKE_INSTALL_PREFIX=$HOME/.local \ + -DCMAKE_BUILD_TYPE=Release \ + -DENABLE_TOOLS=ON \ + -Wno-dev \ + Cmake/ + + - name: Run scan-build with all processors + shell: bash --noprofile --norc {0} env: - BASH_ENV: /dev/null + BASH_ENV: /dev/null run: | set -euo pipefail git config --global --add safe.directory "$GITHUB_WORKSPACE" git config --global credential.helper "" - - # Clear the in-shell GITHUB_TOKEN unset GITHUB_TOKEN || true - pwd - ls + LLVM_BIN=$(llvm-config --bindir 2>/dev/null || echo "/usr/lib/llvm-$(llvm-config --version 2>/dev/null | cut -d. -f1)/bin") + export PATH="${LLVM_BIN}:${PATH}" + + NPROC=$(nproc) + echo "Running scan-build with $NPROC parallel jobs" cd Build - pwd - ls - export PATH="/usr/lib/llvm-17/bin:$PATH" - scan-build --status-bugs --keep-going -o scan-build-reports make -j$(nproc) || true - continue-on-error: true # Allow the step to complete even if issues are found + scan-build --status-bugs --keep-going -o scan-build-reports \ + make -j"$NPROC" 2>&1 | tee scan-build-output.log || true + + # Count findings + BUGS=$({ grep -c 'warning:' scan-build-output.log 2>/dev/null || true; }) + echo "scan-build found $BUGS warnings" + echo "SCAN_BUGS=$BUGS" >> "$GITHUB_ENV" + continue-on-error: true - # Upload scan-build reports - name: Upload scan-build reports uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 with: name: scan-build-reports path: Build/scan-build-reports + if-no-files-found: warn - # Upload built binaries as artifacts - name: Upload build artifacts uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 with: name: master-build-linux - path: Build + path: | + Build/**/*.so + Build/**/*.a + Build/**/Icc* + LICENSE.md + if-no-files-found: warn - # Upload build logs - name: Upload build logs uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 with: name: build-logs - path: Build/CMakeCache.txt + path: | + Build/CMakeCache.txt + Build/scan-build-output.log + - name: Summary Report if: always() + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null run: | - echo "### Build Summary" >> $GITHUB_STEP_SUMMARY - echo "- Build Directory: Build/" >> $GITHUB_STEP_SUMMARY - echo "- Artifacts Uploaded: iccdev-linux-clang" >> $GITHUB_STEP_SUMMARY - echo "- Status: Success" >> $GITHUB_STEP_SUMMARY \ No newline at end of file + set -euo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + SANITIZER=".github/scripts/sanitize-sed.sh" + if [[ -f "$SANITIZER" ]]; then + # shellcheck disable=SC1090 + source "$SANITIZER" + else + sanitize_line() { printf '%s' "$1"; } + fi + + BUGS="${SCAN_BUGS:-0}" + REPORT_COUNT=$(find Build/scan-build-reports -name '*.html' 2>/dev/null | wc -l | tr -d ' ') + + { + echo "### 🧠 Scan-Build Summary" + echo "" + echo "| Metric | Value |" + echo "|--------|-------|" + echo "| Parallel jobs | $(nproc) |" + echo "| Warnings logged | $(sanitize_line "$BUGS") |" + echo "| HTML reports | $(sanitize_line "$REPORT_COUNT") |" + echo "| Status | ${{ job.status }} |" + echo "" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/ci-clang-tidy-coreguidelines.yml b/.github/workflows/ci-clang-tidy-coreguidelines.yml index 56ccab8a5..1507c468a 100644 --- a/.github/workflows/ci-clang-tidy-coreguidelines.yml +++ b/.github/workflows/ci-clang-tidy-coreguidelines.yml @@ -1,21 +1,16 @@ ############################################################### # -## Copyright (©) 2025 International Color Consortium. -## All rights reserved. +## Copyright (©) 2025 International Color Consortium. +## All rights reserved. ## https://color.org # # -## Intent:iccdev-tidy-coreguidelines -# -## Last Updated: 02-DEC-2025 1700Z by David Hoyt -# Add Permission Block -# -# -# -# -# -# +## Intent: iccdev-tidy-coreguidelines # +## Last Updated: 2026-02-17 16:14:17 UTC by David Hoyt +# Housekeeping +# +# # ############################################################### @@ -24,14 +19,14 @@ name: iccdev-tidy-coreguidelines permissions: contents: read pull-requests: read - + on: workflow_dispatch: jobs: - build: + clang-tidy: runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 30 strategy: matrix: build_type: [Release] @@ -39,91 +34,237 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 + - name: Install dependencies - shell: bash + shell: bash --noprofile --norc {0} env: - BASH_ENV: /dev/null + BASH_ENV: /dev/null run: | set -euo pipefail - git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --add safe.directory "$PWD" git config --global credential.helper "" - - # Clear the in-shell GITHUB_TOKEN unset GITHUB_TOKEN || true sudo apt-get update - sudo apt-get install -y build-essential cmake git pkg-config sudo apt-get install -y \ - build-essential cmake gcc g++ clang clang-tools \ - libpng-dev libxml2 libxml2-dev libtiff-dev \ + build-essential cmake gcc g++ clang clang-tools clang-tidy \ + libpng-dev libxml2 libxml2-dev libtiff-dev libjpeg-dev \ nlohmann-json3-dev libwxgtk3.2-dev wx-common \ - python3 python3-pip curl git llvm zsh - git clone https://github.com/InternationalColorConsortium/iccDEV.git - cd iccDEV - BUILD_DIR="Build" - CHECKS="modernize-*,readability-*,cppcoreguidelines-*" - OUTDIR="tidy-out" - mkdir -p "${OUTDIR}" + python3 curl git llvm jq + echo "### Environment" >> "$GITHUB_STEP_SUMMARY" + echo "| Tool | Version |" >> "$GITHUB_STEP_SUMMARY" + echo "|------|---------|" >> "$GITHUB_STEP_SUMMARY" + echo "| clang-tidy | $(clang-tidy --version 2>&1 | head -1) |" >> "$GITHUB_STEP_SUMMARY" + echo "| cmake | $(cmake --version | head -1) |" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + + - name: CMake Configure (compile_commands.json) + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + cd Build + cmake Cmake \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -Wno-dev + echo "✅ compile_commands.json generated with $(jq length compile_commands.json) translation units" + + - name: Build + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true cd Build - cmake Cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON make -j$(nproc) - cd .. - - # Extract translation units + + - name: Run clang-tidy (cppcoreguidelines) + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + BUILD_DIR="Build" + CHECKS="-*,cppcoreguidelines-*" + OUTDIR="tidy-out/cppcoreguidelines" + mkdir -p "$OUTDIR" + jq -r '.[].file' "${BUILD_DIR}/compile_commands.json" \ | xargs -I{} -P"$(nproc)" sh -c ' f="{}" base=$(basename "$f") log="'"${OUTDIR}"'/${base}.log" echo "[+] $(date +"%H:%M:%S") START $f" - run-clang-tidy -p "'"${BUILD_DIR}"'" -checks="'"${CHECKS}"'" "$f" > "$log" 2>&1 + clang-tidy -p "'"${BUILD_DIR}"'" --checks="'"${CHECKS}"'" "$f" > "$log" 2>&1 || true echo "[+] $(date +"%H:%M:%S") DONE $f" ' - - - name: Upload build artifacts + + - name: Run clang-tidy (modernize) + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + BUILD_DIR="Build" + CHECKS="-*,modernize-*" + OUTDIR="tidy-out/modernize" + mkdir -p "$OUTDIR" + + jq -r '.[].file' "${BUILD_DIR}/compile_commands.json" \ + | xargs -I{} -P"$(nproc)" sh -c ' + f="{}" + base=$(basename "$f") + log="'"${OUTDIR}"'/${base}.log" + echo "[+] $(date +"%H:%M:%S") START $f" + clang-tidy -p "'"${BUILD_DIR}"'" --checks="'"${CHECKS}"'" "$f" > "$log" 2>&1 || true + echo "[+] $(date +"%H:%M:%S") DONE $f" + ' + + - name: Run clang-tidy (readability) + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + BUILD_DIR="Build" + CHECKS="-*,readability-*" + OUTDIR="tidy-out/readability" + mkdir -p "$OUTDIR" + + jq -r '.[].file' "${BUILD_DIR}/compile_commands.json" \ + | xargs -I{} -P"$(nproc)" sh -c ' + f="{}" + base=$(basename "$f") + log="'"${OUTDIR}"'/${base}.log" + echo "[+] $(date +"%H:%M:%S") START $f" + clang-tidy -p "'"${BUILD_DIR}"'" --checks="'"${CHECKS}"'" "$f" > "$log" 2>&1 || true + echo "[+] $(date +"%H:%M:%S") DONE $f" + ' + + - name: Upload clang-tidy artifacts uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 with: name: iccdev-clang-tidy-${{ matrix.build_type }}-linux path: | - iccDEV/tidy-out/* + tidy-out/**/*.log if-no-files-found: warn - - name: Generate clang-tidy report summary - shell: bash + - name: Generate report summary + if: always() + shell: bash --noprofile --norc {0} env: BASH_ENV: /dev/null run: | set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true - echo "## 🧹 Clang-Tidy Summary — iccdev" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" + # Source trusted sanitizer from checked-out workspace + SANITIZER=".github/scripts/sanitize-sed.sh" + if [[ -f "$SANITIZER" ]]; then + # shellcheck disable=SC1090 + source "$SANITIZER" + else + escape_html() { + local s="$1" + s="${s//&/&}" + s="${s///>}" + s="${s//\"/"}" + s="${s//\'/'}" + printf '%s' "$s" + } + sanitize_line() { escape_html "$1"; } + fi - OUTDIR="iccDEV/tidy-out" + echo "## 🧹 Clang-Tidy Analysis Report" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" - if ! [ -d "$OUTDIR" ]; then + BASEDIR="tidy-out" + if ! [ -d "$BASEDIR" ]; then echo "**No tidy output directory found.**" >> "$GITHUB_STEP_SUMMARY" exit 0 fi - total_logs=$(find "$OUTDIR" -type f -name "*.log" | wc -l | tr -d ' ') - echo "**Total translation units analyzed:** $total_logs" >> "$GITHUB_STEP_SUMMARY" + # --- Per-category summary --- + echo "### Findings by Check Category" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Category | Warnings | Errors | Files Analyzed |" >> "$GITHUB_STEP_SUMMARY" + echo "|----------|----------|--------|----------------|" >> "$GITHUB_STEP_SUMMARY" + + GRAND_WARN=0 + GRAND_ERR=0 + for category in cppcoreguidelines modernize readability; do + CATDIR="$BASEDIR/$category" + if [ -d "$CATDIR" ]; then + files=$(find "$CATDIR" -type f -name "*.log" | wc -l | tr -d ' ') + warns=$(grep -rch "warning:" "$CATDIR" 2>/dev/null | paste -sd+ | bc 2>/dev/null || echo 0) + errs=$({ grep -rh "error:" "$CATDIR" 2>/dev/null || true; } | { grep -vc "warnings and" 2>/dev/null || true; }) + echo "| $category | $warns | $errs | $files |" >> "$GITHUB_STEP_SUMMARY" + GRAND_WARN=$((GRAND_WARN + warns)) + GRAND_ERR=$((GRAND_ERR + errs)) + fi + done + echo "| **Total** | **$GRAND_WARN** | **$GRAND_ERR** | |" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + + # --- Per-component breakdown --- + echo "### Findings by Source Component" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Component | Warnings |" >> "$GITHUB_STEP_SUMMARY" + echo "|-----------|----------|" >> "$GITHUB_STEP_SUMMARY" - # Count warnings/errors without parsing user-controlled filenames into the shell - total_issues=$(grep -R "warning:" -H "$OUTDIR" | wc -l | tr -d ' ') - echo "**Total warnings detected:** $total_issues" >> "$GITHUB_STEP_SUMMARY" + for component in IccProfLib IccXML Tools; do + count=$({ grep -rh "warning:.*\[" "$BASEDIR" 2>/dev/null || true; } | { grep -c "/${component}/" || true; }) + echo "| $component | $count |" >> "$GITHUB_STEP_SUMMARY" + done echo "" >> "$GITHUB_STEP_SUMMARY" - echo "### Sample Files With Most Warnings" >> "$GITHUB_STEP_SUMMARY" + # --- Top 15 most frequent check names --- + echo "### Top 15 Most Frequent Checks" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Count | Check |" >> "$GITHUB_STEP_SUMMARY" + echo "|-------|-------|" >> "$GITHUB_STEP_SUMMARY" - # Top 10 files — safely aggregated - grep -R "warning:" -H "$OUTDIR" \ - | cut -d: -f1 \ - | sort \ - | uniq -c \ - | sort -rn \ - | head -n 10 \ - | sed 's/^/ - /' >> "$GITHUB_STEP_SUMMARY" + { grep -roh '\[[-a-z]*\]' "$BASEDIR" 2>/dev/null || true; } \ + | sort | uniq -c | sort -rn | head -15 > /tmp/tidy_checks.txt + while read -r cnt chk; do + echo "| $cnt | $chk |" >> "$GITHUB_STEP_SUMMARY" + done < /tmp/tidy_checks.txt + echo "" >> "$GITHUB_STEP_SUMMARY" + # --- Top 10 files with most warnings --- + echo "### Top 10 Files by Warning Count" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "✔ Report summary generated." >> "$GITHUB_STEP_SUMMARY" + echo "| Warnings | File |" >> "$GITHUB_STEP_SUMMARY" + echo "|----------|------|" >> "$GITHUB_STEP_SUMMARY" + + { grep -rh "warning:.*\[" "$BASEDIR" 2>/dev/null || true; } \ + | { grep -oP '[^ ]+\.(cpp|h)' || true; } \ + | sort | uniq -c | sort -rn | head -10 > /tmp/tidy_files.txt + while read -r cnt fname; do + echo "| $cnt | $fname |" >> "$GITHUB_STEP_SUMMARY" + done < /tmp/tidy_files.txt + echo "" >> "$GITHUB_STEP_SUMMARY" + + echo "✔ Report generated." >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/ci-code-coverage.yml b/.github/workflows/ci-code-coverage.yml new file mode 100644 index 000000000..649086b61 --- /dev/null +++ b/.github/workflows/ci-code-coverage.yml @@ -0,0 +1,298 @@ +############################################################### +# +# Copyright (c) 2026 International Color Consortium. +# All rights reserved. +# https://color.org +# +# Intent: Build iccDEV with Clang source-based coverage, +# run full test suite + profile exerciser, generate +# llvm-cov HTML and lcov summary reports +# +# Based on: research/iccanalyzer-lite-coverage-report.yml +# Adapted: For cfl/ integrated build with ENABLE_COVERAGE +# +############################################################### + +name: Code Coverage Report + +permissions: + contents: read + +on: + workflow_dispatch: + pull_request: + branches: [ master, cfl ] + +concurrency: + group: coverage-${{ github.ref }} + cancel-in-progress: true + +jobs: + coverage: + name: Coverage Report + runs-on: ubuntu-24.04 + timeout-minutes: 30 + defaults: + run: + shell: bash --noprofile --norc {0} + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Install dependencies + env: + DEBIAN_FRONTEND: noninteractive + run: | + set -euo pipefail + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends \ + build-essential cmake \ + clang-18 llvm-18 \ + libxml2-dev libtiff-dev libjpeg-dev libpng-dev \ + zlib1g-dev nlohmann-json3-dev \ + lcov + + - name: Build with coverage instrumentation + env: + CC: clang-18 + CXX: clang++-18 + run: | + set -euo pipefail + mkdir -p build-coverage && cd build-coverage + cmake ../Build/Cmake \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_C_COMPILER=clang-18 \ + -DCMAKE_CXX_COMPILER=clang++-18 \ + -DENABLE_COVERAGE=ON \ + -DENABLE_TOOLS=ON \ + -DENABLE_STATIC_LIBS=ON \ + -DENABLE_TESTS=OFF \ + -Wno-dev + make -j$(nproc) + + # Verify coverage instrumentation + TOOL=$(find . -name "iccDumpProfile" -type f -executable | head -1) + if [ -z "$TOOL" ]; then + echo "::error::No iccDumpProfile binary found" + exit 1 + fi + echo "tool_path=$(pwd)/$TOOL" >> $GITHUB_ENV + echo "build_dir=$(pwd)" >> $GITHUB_ENV + + # Verify instrumentation flags + echo "Checking coverage flags..." + grep -r "fprofile-instr-generate" CMakeFiles/*/flags.make | head -3 || true + + # Count libraries and tools + LIB_COUNT=$(find . -name "*.a" | wc -l) + TOOL_COUNT=$(find . -path "*/Tools/*" -type f -executable | wc -l) + echo "Built: $LIB_COUNT libraries, $TOOL_COUNT tools" + + - name: Create test profiles + run: | + set -euo pipefail + export LLVM_PROFILE_FILE="${{ github.workspace }}/profraw/create-%p-%m.profraw" + mkdir -p "${{ github.workspace }}/profraw" + + # CreateAllProfiles.sh expects ../Build/Tools — symlink build-coverage + ln -sfn build-coverage Build + + # Add build tools to PATH + BUILD_DIR="${{ env.build_dir }}" + for d in $(find "$BUILD_DIR" -path "*/Tools/*" -type f -executable -exec dirname {} \; | sort -u); do + PATH="$d:$PATH" + done + export PATH + + cd Testing + bash CreateAllProfiles.sh 2>&1 | tail -20 + PROFILE_COUNT=$(find . -name "*.icc" | wc -l) + echo "Created $PROFILE_COUNT ICC profiles" + echo "profile_count=$PROFILE_COUNT" >> $GITHUB_ENV + + - name: Run test suite + run: | + set -euo pipefail + export LLVM_PROFILE_FILE="${{ github.workspace }}/profraw/test-%p-%m.profraw" + + BUILD_DIR="${{ env.build_dir }}" + for d in $(find "$BUILD_DIR" -path "*/Tools/*" -type f -executable -exec dirname {} \; | sort -u); do + PATH="$d:$PATH" + done + export PATH + + cd Testing + bash RunTests.sh 2>&1 | tail -40 + echo "Test suite complete" + + - name: Exercise all profiles with IccDumpProfile + run: | + set -euo pipefail + export LLVM_PROFILE_FILE="${{ github.workspace }}/profraw/dump-%p-%m.profraw" + + TOOL="${{ env.tool_path }}" + TOTAL=0 + PASS=0 + FAIL=0 + + while IFS= read -r icc; do + TOTAL=$((TOTAL + 1)) + if timeout 10s "$TOOL" "$icc" > /dev/null 2>&1; then + PASS=$((PASS + 1)) + else + FAIL=$((FAIL + 1)) + fi + done < <(find Testing -name "*.icc" -type f) + + echo "IccDumpProfile: $TOTAL profiles, $PASS pass, $FAIL fail" + echo "dump_total=$TOTAL" >> $GITHUB_ENV + echo "dump_pass=$PASS" >> $GITHUB_ENV + echo "dump_fail=$FAIL" >> $GITHUB_ENV + + - name: Exercise XML round-trip tools + run: | + set -euo pipefail + export LLVM_PROFILE_FILE="${{ github.workspace }}/profraw/xml-%p-%m.profraw" + + BUILD_DIR="${{ env.build_dir }}" + TO_XML=$(find "$BUILD_DIR" -name "iccToXml" -type f -executable -print -quit) + FROM_XML=$(find "$BUILD_DIR" -name "iccFromXml" -type f -executable -print -quit) + ROUNDTRIP=$(find "$BUILD_DIR" -name "iccRoundTrip" -type f -executable -print -quit) + + CONVERTED=0 + # Convert a sample of profiles to XML and back + mapfile -t ICC_FILES < <(find Testing -name "*.icc" -type f | shuf -n 50) + for icc in "${ICC_FILES[@]}"; do + XML_OUT="/tmp/$(basename "$icc" .icc).xml" + ICC_OUT="/tmp/$(basename "$icc")" + if [ -n "$TO_XML" ] && timeout 10s "$TO_XML" "$icc" "$XML_OUT" > /dev/null 2>&1; then + CONVERTED=$((CONVERTED + 1)) + if [ -n "$FROM_XML" ]; then + timeout 10s "$FROM_XML" "$XML_OUT" "$ICC_OUT" > /dev/null 2>&1 || true + fi + fi + rm -f "$XML_OUT" "$ICC_OUT" + done + + # Run roundtrip on sample profiles + if [ -n "$ROUNDTRIP" ]; then + mapfile -t RT_FILES < <(find Testing -name "*.icc" -type f | shuf -n 30) + for icc in "${RT_FILES[@]}"; do + timeout 10s "$ROUNDTRIP" "$icc" > /dev/null 2>&1 || true + done + fi + + echo "XML round-trip: $CONVERTED conversions complete" + + - name: Merge profdata and generate report + run: | + set -euo pipefail + mkdir -p coverage-report + + # Merge all .profraw files + PROFRAW_COUNT=$(find "${{ github.workspace }}/profraw" -name "*.profraw" 2>/dev/null | wc -l) + echo "Found $PROFRAW_COUNT .profraw files" + + if [ "$PROFRAW_COUNT" -eq 0 ]; then + echo "::error::No profraw files generated — coverage instrumentation may have failed" + exit 1 + fi + + llvm-profdata-18 merge \ + -sparse \ + "${{ github.workspace }}"/profraw/*.profraw \ + -o coverage-report/merged.profdata + + echo "Merged profdata: $(stat -c%s coverage-report/merged.profdata) bytes" + + # Find all instrumented binaries + BUILD_DIR="${{ env.build_dir }}" + BINARIES=$(find "$BUILD_DIR" -type f -executable \( -path "*/Tools/*" -o -name "*.a" \) | head -20) + MAIN_BIN="${{ env.tool_path }}" + + # Build object list for llvm-cov (all tool binaries) + OBJECT_ARGS="" + for bin in $(find "$BUILD_DIR" -path "*/Tools/*" -type f -executable); do + if [ "$bin" != "$MAIN_BIN" ]; then + OBJECT_ARGS="$OBJECT_ARGS -object=$bin" + fi + done + + # Generate text report + llvm-cov-18 report \ + "$MAIN_BIN" $OBJECT_ARGS \ + -instr-profile=coverage-report/merged.profdata \ + -ignore-filename-regex='(third_party|/usr/|test)' \ + 2>&1 | tee coverage-report/coverage-summary.txt + + # Generate HTML report + llvm-cov-18 show \ + "$MAIN_BIN" $OBJECT_ARGS \ + -instr-profile=coverage-report/merged.profdata \ + -format=html \ + -output-dir=coverage-report/html \ + -show-line-counts-or-regions \ + -show-expansions \ + -ignore-filename-regex='(third_party|/usr/|test)' \ + 2>&1 | tail -5 + + # Generate lcov-compatible export + llvm-cov-18 export \ + "$MAIN_BIN" $OBJECT_ARGS \ + -instr-profile=coverage-report/merged.profdata \ + -format=lcov \ + -ignore-filename-regex='(third_party|/usr/|test)' \ + > coverage-report/lcov.info 2>/dev/null || true + + # Summary stats + if [ -d coverage-report/html ]; then + HTML_COUNT=$(find coverage-report/html -name "*.html" | wc -l) + echo "HTML report: $HTML_COUNT files" + fi + LCOV_RECORDS=$(grep -c "^SF:" coverage-report/lcov.info 2>/dev/null || echo 0) + echo "LCOV: $LCOV_RECORDS source file records" + + - name: Report to summary + if: always() + run: | + set -euo pipefail + { + echo "## Code Coverage Report" + echo "" + echo "### Test Execution" + echo "| Metric | Value |" + echo "|--------|-------|" + echo "| ICC Profiles Created | ${{ env.profile_count }} |" + echo "| IccDumpProfile Total | ${{ env.dump_total }} |" + echo "| IccDumpProfile Pass | ${{ env.dump_pass }} |" + echo "| IccDumpProfile Fail | ${{ env.dump_fail }} |" + echo "" + echo "### Coverage Summary" + echo '```' + } >> $GITHUB_STEP_SUMMARY + + if [ -f coverage-report/coverage-summary.txt ]; then + cat coverage-report/coverage-summary.txt >> $GITHUB_STEP_SUMMARY + else + echo "No coverage data available" >> $GITHUB_STEP_SUMMARY + fi + + { + echo '```' + echo "" + echo "### Artifacts" + echo "- \`coverage-report\`: llvm-cov HTML report, lcov.info, text summary" + } >> $GITHUB_STEP_SUMMARY + + - name: Upload coverage report + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: coverage-report + path: coverage-report/ + retention-days: 30 + if-no-files-found: warn diff --git a/.github/workflows/ci-comprehensive-build-test.yml b/.github/workflows/ci-comprehensive-build-test.yml new file mode 100644 index 000000000..f80a58011 --- /dev/null +++ b/.github/workflows/ci-comprehensive-build-test.yml @@ -0,0 +1,1058 @@ +############################################################### +# Copyright (c) 2026 International Color Consortium +# +# Intent: ci-build-matrix +# Tests all build configurations, sanitizers, and options +# This workflow will replace ci-pr-actions in time +# +# Last Updated: 2026-02-17 16:15:56 UTC by David Hoyt +# +# All PR _must_ PASS this workflow +# +# +# +# +############################################################### + +name: "ci-build-matrix" + +permissions: + contents: read + pull-requests: read + +on: + workflow_dispatch: + inputs: + ref: + description: 'Branch to build (default: master)' + required: false + default: 'master' + type: choice + options: + - 'master' + - 'cfl' + pr_number: + description: 'Pull request number to build (overrides branch)' + required: false + type: number + +concurrency: + group: comprehensive-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + GH_REPO: ${{ github.repository }} + GH_NO_UPDATE_NOTIFIER: 1 + GH_PROMPT_DISABLED: 1 + RUN_URL: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} + CHECKOUT_REF: ${{ github.event.inputs.pr_number && format('refs/pull/{0}/head', github.event.inputs.pr_number) || github.event.inputs.ref || 'master' }} + +jobs: + ############################################################################# + # Job 1: Standard Project Builds (Tools Only - No Fuzzing) + ############################################################################# + standard-builds: + name: "Standard • ${{ matrix.os }} • ${{ matrix.compiler }} • ${{ matrix.build_type }}" + runs-on: ${{ matrix.os }} + timeout-minutes: 25 + defaults: + run: + shell: bash --noprofile --norc {0} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + compiler: [gcc, clang] + build_type: [Release, Debug, RelWithDebInfo] + exclude: + - os: macos-latest + compiler: gcc + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.CHECKOUT_REF }} + fetch-depth: 0 + persist-credentials: false + + - name: Cache Dependencies (Linux) + if: runner.os == 'Linux' + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: | + /var/cache/apt/archives + ~/.cache/ccache + key: ${{ runner.os }}-deps-${{ matrix.compiler }}-${{ hashFiles('Build/Cmake/CMakeLists.txt') }} + restore-keys: | + ${{ runner.os }}-deps-${{ matrix.compiler }}- + ${{ runner.os }}-deps- + + - name: Cache Dependencies (macOS) + if: runner.os == 'macOS' + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: | + ~/Library/Caches/Homebrew + ~/.cache/ccache + key: ${{ runner.os }}-deps-${{ matrix.compiler }}-${{ hashFiles('Build/Cmake/CMakeLists.txt') }} + restore-keys: | + ${{ runner.os }}-deps-${{ matrix.compiler }}- + ${{ runner.os }}-deps- + + - name: Configure Git Environment + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + - name: Install Dependencies (Linux) + if: runner.os == 'Linux' + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + sudo apt-get update -qq + sudo apt-get install -y \ + build-essential cmake gcc g++ clang ccache \ + libpng-dev libxml2-dev libtiff-dev \ + nlohmann-json3-dev + ccache --zero-stats || true + + - name: Install Dependencies (macOS) + if: runner.os == 'macOS' + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + brew install cmake llvm libpng libtiff libxml2 nlohmann-json ccache + ccache --zero-stats || true + + - name: Disable wxWidgets for CFL + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + sed -i.bak 's/^ find_package(wxWidgets COMPONENTS core base REQUIRED)/ # find_package(wxWidgets COMPONENTS core base REQUIRED) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i.bak 's/^ ADD_SUBDIRECTORY(Tools\/wxProfileDump)/ # ADD_SUBDIRECTORY(Tools\/wxProfileDump) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i.bak 's/^ message(FATAL_ERROR "wxWidgets not found/ # message(FATAL_ERROR "wxWidgets not found/g' Build/Cmake/CMakeLists.txt + + - name: Set Compiler + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + if [ "${{ matrix.compiler }}" = "gcc" ]; then + echo "CC=gcc" >> $GITHUB_ENV + echo "CXX=g++" >> $GITHUB_ENV + else + echo "CC=clang" >> $GITHUB_ENV + echo "CXX=clang++" >> $GITHUB_ENV + fi + + - name: Configure CMake + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + mkdir -p Build && cd Build + cmake ../Build/Cmake \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DENABLE_SHARED_LIBS=ON \ + -DENABLE_STATIC_LIBS=ON \ + -DENABLE_TOOLS=ON + + - name: Build + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + # Enable ccache if available + if command -v ccache >/dev/null 2>&1; then + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + export CC="ccache ${{ matrix.compiler == 'gcc' && 'gcc' || 'clang' }}" + export CXX="ccache ${{ matrix.compiler == 'gcc' && 'g++' || 'clang++' }}" + fi + + cd Build + echo "::group::Build Output" + BUILD_START=$(date +%s) + make -j$(nproc || sysctl -n hw.logicalcpu) 2>&1 | tee build.log + BUILD_END=$(date +%s) + echo "::endgroup::" + BUILD_TIME=$((BUILD_END - BUILD_START)) + echo "::notice title=Build Time::Build completed in ${BUILD_TIME}s" + if grep -iE '(error|failed|undefined reference|cannot find)' build.log; then + echo "::error title=Build Failed::Build errors detected in log" + exit 1 + fi + # Show ccache stats if available + if command -v ccache >/dev/null 2>&1; then + echo "::group::ccache Statistics" + ccache --show-stats || true + echo "::endgroup::" + fi + + - name: Upload Build Artifacts (on failure) + if: failure() + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: build-logs-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.build_type }} + path: | + Build/build.log + Build/CMakeCache.txt + Build/CMakeFiles/CMakeError.log + Build/CMakeFiles/CMakeOutput.log + retention-days: 7 + + - name: Upload Build Artifacts (on success) + if: success() + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: build-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.build_type }} + path: | + Build/Tools/IccApplyNamedCmm/**/IccApplyNamedCmm + Build/Tools/IccDumpProfile/**/IccDumpProfile + Build/Tools/IccFromXml/**/IccFromXml + Build/Tools/IccToXml/**/IccToXml + Build/IccProfLib/libIccProfLib2* + Build/IccXML/libIccXML2* + if-no-files-found: warn + retention-days: 14 + compression-level: 9 + + - name: Test Profiles + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + cd Testing + for d in ../Build/Tools/*; do + [ -d "$d" ] && export PATH="$(realpath "$d"):$PATH" + done + sh CreateAllProfiles.sh + PROFILE_COUNT=$(find . -name "*.icc" | wc -l) + echo "Created $PROFILE_COUNT profiles" + if [ "$PROFILE_COUNT" -eq 0 ]; then + echo "ERROR: No profiles created" + exit 1 + fi + + ############################################################################# + # Job 2: Fuzzer Builds (Debug + Full Instrumentation) + ############################################################################# + fuzzer-builds: + name: "Fuzzer • ${{ matrix.os }} • Debug+Instrumentation" + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + defaults: + run: + shell: bash --noprofile --norc {0} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.CHECKOUT_REF }} + fetch-depth: 0 + persist-credentials: false + + - name: Cache Dependencies + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: | + /var/cache/apt/archives + ~/.cache/ccache + key: fuzzer-deps-${{ hashFiles('Build/Cmake/CMakeLists.txt', 'Testing/Fuzzing/CMakeLists.txt') }} + restore-keys: | + fuzzer-deps- + + - name: Configure Git Environment + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + - name: Install Dependencies + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + sudo apt-get update -qq + sudo apt-get install -y \ + build-essential cmake clang llvm ccache \ + libpng-dev libxml2-dev libtiff-dev nlohmann-json3-dev + ccache --zero-stats || true + + - name: Disable wxWidgets for CFL + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + sed -i 's/^ find_package(wxWidgets COMPONENTS core base REQUIRED)/# find_package(wxWidgets COMPONENTS core base REQUIRED) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ ADD_SUBDIRECTORY(Tools\/wxProfileDump)/# ADD_SUBDIRECTORY(Tools\/wxProfileDump) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ message(FATAL_ERROR "wxWidgets not found/ # message(FATAL_ERROR "wxWidgets not found/g' Build/Cmake/CMakeLists.txt + + - name: Configure Fuzzer Build + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + mkdir -p Build-fuzzing && cd Build-fuzzing + CC=clang CXX=clang++ cmake ../Build/Cmake \ + -DCMAKE_BUILD_TYPE=Debug \ + -DENABLE_FUZZING=ON \ + -DENABLE_STATIC_LIBS=ON \ + -DENABLE_SHARED_LIBS=ON \ + -DENABLE_TOOLS=OFF + + - name: Build Fuzzers + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + # Enable ccache + if command -v ccache >/dev/null 2>&1; then + export PATH="/usr/lib/ccache:$PATH" + export CC="ccache clang" + export CXX="ccache clang++" + fi + cd Build-fuzzing + echo "::group::Fuzzer Build Output" + BUILD_START=$(date +%s) + make -j$(nproc) 2>&1 | tee build-fuzzing.log + BUILD_END=$(date +%s) + echo "::endgroup::" + BUILD_TIME=$((BUILD_END - BUILD_START)) + echo "::notice title=Fuzzer Build Time::Fuzzer build completed in ${BUILD_TIME}s" + if grep -iE '(error|failed|undefined reference|cannot find)' build-fuzzing.log; then + echo "::error title=Fuzzer Build Failed::Fuzzer build errors detected" + exit 1 + fi + # Show ccache stats + if command -v ccache >/dev/null 2>&1; then + echo "::group::ccache Statistics" + ccache --show-stats || true + echo "::endgroup::" + fi + + - name: Verify Fuzzer Build + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + FUZZER_COUNT=$(ls Build-fuzzing/Testing/Fuzzing/icc_*_fuzzer 2>/dev/null | wc -l) + echo "::notice title=Fuzzer Count::Built $FUZZER_COUNT fuzzers" + # Note: Fuzzer count varies by platform and configuration + # Just verify at least 1 fuzzer was built + if [ "$FUZZER_COUNT" -lt 1 ]; then + echo "::error title=Fuzzer Verification Failed::Expected at least 1 fuzzer, got $FUZZER_COUNT" + exit 1 + fi + + - name: Upload Fuzzer Build Artifacts (on failure) + if: failure() + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: fuzzer-build-logs + path: | + Build-fuzzing/build-fuzzing.log + Build-fuzzing/CMakeCache.txt + Build-fuzzing/CMakeFiles/CMakeError.log + Build-fuzzing/CMakeFiles/CMakeOutput.log + Build-fuzzing/Testing/Fuzzing/*.log + retention-days: 7 + + - name: Upload Fuzzer Executables (on success) + if: success() + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: fuzzer-executables-debug-instrumentation + path: Build-fuzzing/Testing/Fuzzing/*_fuzzer + retention-days: 14 + compression-level: 9 + + - name: Test Fuzzers + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + cd Build-fuzzing/Testing/Fuzzing + # Verify all 14 fuzzers are built and executable + EXPECTED_FUZZERS="icc_apply_fuzzer icc_applynamedcmm_fuzzer icc_applyprofiles_fuzzer icc_calculator_fuzzer icc_dump_fuzzer icc_fromxml_fuzzer icc_io_fuzzer icc_link_fuzzer icc_multitag_fuzzer icc_profile_fuzzer icc_roundtrip_fuzzer icc_spectral_fuzzer icc_toxml_fuzzer icc_v5dspobs_fuzzer" + PASS=0 + FAIL=0 + for f in $EXPECTED_FUZZERS; do + if [ -x "$f" ]; then + PASS=$((PASS + 1)) + echo "✓ $f" + else + FAIL=$((FAIL + 1)) + echo "✗ $f (not found or not executable)" + fi + done + echo "::notice title=Fuzzer Build Verification::$PASS of 14 fuzzers built successfully" + if [ "$FAIL" -gt 0 ]; then + echo "::error title=Fuzzer Build Failed::$FAIL fuzzers missing or not executable" + exit 1 + fi + + ############################################################################# + # Job 3: Sanitizer Builds (Individual Sanitizers) + ############################################################################# + sanitizer-builds: + name: "Sanitizer • ${{ matrix.sanitizer }} • ${{ matrix.build_type }}" + runs-on: ubuntu-latest + timeout-minutes: 25 + defaults: + run: + shell: bash --noprofile --norc {0} + + strategy: + fail-fast: false + matrix: + build_type: [Debug, RelWithDebInfo] + sanitizer: + - asan + - ubsan + - asan-ubsan + exclude: + - sanitizer: asan-ubsan + build_type: RelWithDebInfo + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.CHECKOUT_REF }} + fetch-depth: 0 + persist-credentials: false + + - name: Cache Dependencies + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: | + /var/cache/apt/archives + ~/.cache/ccache + key: sanitizer-deps-${{ matrix.sanitizer }}-${{ hashFiles('Build/Cmake/CMakeLists.txt') }} + restore-keys: | + sanitizer-deps-${{ matrix.sanitizer }}- + sanitizer-deps- + + - name: Configure Git Environment + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + - name: Install Dependencies + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + sudo apt-get update -qq + sudo apt-get install -y \ + build-essential cmake clang llvm \ + libpng-dev libxml2-dev libtiff-dev nlohmann-json3-dev + + - name: Disable wxWidgets for CFL + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + sed -i 's/^ find_package(wxWidgets COMPONENTS core base REQUIRED)/# find_package(wxWidgets COMPONENTS core base REQUIRED) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ ADD_SUBDIRECTORY(Tools\/wxProfileDump)/# ADD_SUBDIRECTORY(Tools\/wxProfileDump) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ message(FATAL_ERROR "wxWidgets not found/ # message(FATAL_ERROR "wxWidgets not found/g' Build/Cmake/CMakeLists.txt + + - name: Configure with Sanitizer + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + mkdir -p Build && cd Build + case "${{ matrix.sanitizer }}" in + asan) + OPTS="-DENABLE_ASAN=ON" + ;; + ubsan) + OPTS="-DENABLE_UBSAN=ON" + ;; + asan-ubsan) + OPTS="-DENABLE_ASAN=ON -DENABLE_UBSAN=ON" + ;; + esac + CC=clang CXX=clang++ cmake ../Build/Cmake \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + $OPTS + + - name: Build with Sanitizer + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + cd Build + make -j$(nproc) 2>&1 | tee build.log + if grep -iE 'error:' build.log; then + echo "ERROR: Build failed" + exit 1 + fi + + - name: Test with Sanitizer + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + cd Testing + for d in ../Build/Tools/*; do + [ -d "$d" ] && export PATH="$(realpath "$d"):$PATH" + done + timeout 300 sh CreateAllProfiles.sh || echo "Profile creation timed out (acceptable for sanitizer builds)" + + - name: Upload Sanitizer Build Artifacts + if: always() + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: sanitizer-${{ matrix.sanitizer }}-${{ matrix.build_type }} + path: | + Build/Tools/IccApplyNamedCmm/IccApplyNamedCmm/IccApplyNamedCmm + Build/Tools/IccDumpProfile/IccDumpProfile/IccDumpProfile + Build/IccProfLib/libIccProfLib2* + Build/build.log + if-no-files-found: warn + retention-days: 7 + compression-level: 9 + + ############################################################################# + # Job 4: Build Option Matrix (Test Each Option Independently) + ############################################################################# + option-matrix: + name: "Option • ${{ matrix.option_name }}" + runs-on: ubuntu-latest + timeout-minutes: 20 + defaults: + run: + shell: bash --noprofile --norc {0} + + strategy: + fail-fast: false + matrix: + include: + - option_name: "VERBOSE_CONFIG=ON" + cmake_opts: "-DVERBOSE_CONFIG=ON" + - option_name: "ENABLE_COVERAGE=ON" + cmake_opts: "-DENABLE_COVERAGE=ON" + - option_name: "ICC_ENABLE_ASSERTS=ON" + cmake_opts: "-DICC_ENABLE_ASSERTS=ON" + - option_name: "ICC_TRACE_NAN_ENABLED=ON" + cmake_opts: "-DICC_TRACE_NAN_ENABLED=ON" + - option_name: "SHARED_ONLY" + cmake_opts: "-DENABLE_SHARED_LIBS=ON -DENABLE_STATIC_LIBS=OFF" + - option_name: "STATIC_ONLY" + cmake_opts: "-DENABLE_SHARED_LIBS=OFF -DENABLE_STATIC_LIBS=ON" + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.CHECKOUT_REF }} + fetch-depth: 0 + persist-credentials: false + + - name: Configure Git Environment + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + - name: Install Dependencies + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + sudo apt-get update -qq + sudo apt-get install -y \ + build-essential cmake clang \ + libpng-dev libxml2-dev libtiff-dev nlohmann-json3-dev + + - name: Disable wxWidgets for CFL + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + sed -i 's/^ find_package(wxWidgets COMPONENTS core base REQUIRED)/# find_package(wxWidgets COMPONENTS core base REQUIRED) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ ADD_SUBDIRECTORY(Tools\/wxProfileDump)/# ADD_SUBDIRECTORY(Tools\/wxProfileDump) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ message(FATAL_ERROR "wxWidgets not found/ # message(FATAL_ERROR "wxWidgets not found/g' Build/Cmake/CMakeLists.txt + + - name: Configure with Option + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + mkdir -p Build && cd Build + cmake Cmake/ \ + -DCMAKE_BUILD_TYPE=Release \ + ${{ matrix.cmake_opts }} + + - name: Build + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + cd Build + make -j$(nproc) 2>&1 | tee build.log + if grep -iE 'error:' build.log; then + echo "ERROR: Build failed" + exit 1 + fi + + ############################################################################# + # Job 5: Windows Build (MSVC) + ############################################################################# + windows-build: + name: "Windows • MSVC • ${{ matrix.build_type }}" + runs-on: windows-latest + timeout-minutes: 30 + env: + POWERSHELL_TELEMETRY_OPTOUT: "1" + defaults: + run: + shell: pwsh + + strategy: + fail-fast: false + matrix: + build_type: [Release, Debug] + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.CHECKOUT_REF }} + fetch-depth: 0 + persist-credentials: false + + - name: Configure Git Environment + shell: pwsh + run: | + Set-StrictMode -Version Latest + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + if (Test-Path env:GITHUB_TOKEN) { Remove-Item env:GITHUB_TOKEN -ErrorAction SilentlyContinue } + git config --global --add safe.directory "$env:GITHUB_WORKSPACE" + git config --global credential.helper "" + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 + + - name: Install Dependencies + shell: pwsh + run: | + Set-StrictMode -Version Latest + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + if (Test-Path Env:GITHUB_TOKEN) { Remove-Item Env:GITHUB_TOKEN -ErrorAction SilentlyContinue } + Write-Host "Fetching dependencies..." + Start-BitsTransfer -Source "https://github.com/InternationalColorConsortium/iccDEV/releases/download/v2.3.1/vcpkg-exported-deps.zip" -Destination "deps.zip" + tar -xf deps.zip + + - name: Configure + shell: pwsh + run: | + Set-StrictMode -Version Latest + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + if (Test-Path Env:GITHUB_TOKEN) { Remove-Item Env:GITHUB_TOKEN -ErrorAction SilentlyContinue } + cd Build/Cmake + cmake -B build -S . ` + -DCMAKE_TOOLCHAIN_FILE="..\..\scripts\buildsystems\vcpkg.cmake" ` + -DVCPKG_MANIFEST_MODE=OFF ` + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} ` + -Wno-dev + + - name: Build + shell: pwsh + run: | + Set-StrictMode -Version Latest + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + if (Test-Path Env:GITHUB_TOKEN) { Remove-Item Env:GITHUB_TOKEN -ErrorAction SilentlyContinue } + cd Build/Cmake + cmake --build build --config ${{ matrix.build_type }} -- /m /maxcpucount + cmake --build build --config ${{ matrix.build_type }} -- /m /maxcpucount + + - name: Test + shell: pwsh + run: | + Set-StrictMode -Version Latest + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + if (Test-Path Env:GITHUB_TOKEN) { Remove-Item Env:GITHUB_TOKEN -ErrorAction SilentlyContinue } + cd Build/Cmake + $exeDirs = Get-ChildItem -Recurse -File -Include *.exe -Path .\build\ | + Where-Object { $_.FullName -match 'icc' -and $_.FullName -notmatch '\\CMakeFiles\\' -and $_.Name -notmatch '^CMake(C|CXX)CompilerId\.exe$' } | + ForEach-Object { Split-Path $_.FullName -Parent } | + Sort-Object -Unique + $env:PATH = ($exeDirs -join ';') + ';' + $env:PATH + cd ..\..\Testing + .\CreateAllProfiles.bat + + ############################################################################# + # Job 6: Version Header Generation Test + ############################################################################# + version-header-test: + name: "Version Headers • Build Directory Generation" + runs-on: ubuntu-latest + timeout-minutes: 15 + defaults: + run: + shell: bash --noprofile --norc {0} + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.CHECKOUT_REF }} + fetch-depth: 0 + persist-credentials: false + + - name: Configure Git Environment + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + - name: Install Dependencies + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + sudo apt-get update -qq + sudo apt-get install -y build-essential cmake libpng-dev libxml2-dev libtiff-dev nlohmann-json3-dev + + - name: Disable wxWidgets for CFL + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + sed -i 's/^ find_package(wxWidgets COMPONENTS core base REQUIRED)/# find_package(wxWidgets COMPONENTS core base REQUIRED) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ ADD_SUBDIRECTORY(Tools\/wxProfileDump)/# ADD_SUBDIRECTORY(Tools\/wxProfileDump) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ message(FATAL_ERROR "wxWidgets not found/ # message(FATAL_ERROR "wxWidgets not found/g' Build/Cmake/CMakeLists.txt + + - name: Build + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + mkdir -p Build && cd Build + cmake ../Build/Cmake -DCMAKE_BUILD_TYPE=Release + make -j$(nproc) + + - name: Verify Version Headers in Build Directory + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + # Version headers should be in build directory after cmake configure + # Note: These are generated by CMake, not committed to repository + if [ ! -f "Build/IccProfLib/IccProfLibVer.h" ] && [ ! -f "IccProfLib/IccProfLibVer.h" ]; then + echo "WARNING: IccProfLibVer.h not found (may not be generated yet)" + fi + if [ ! -f "Build/IccXML/IccLibXMLVer.h" ] && [ ! -f "IccXML/IccLibXML/IccLibXMLVer.h" ]; then + echo "WARNING: IccLibXMLVer.h not found (may not be generated yet)" + fi + echo "✅ Version headers correctly generated in build directory" + + ############################################################################# + # Job 7: Clean/Rebuild Cycle Test + ############################################################################# + clean-rebuild-test: + name: "Clean/Rebuild Cycle Test" + runs-on: ubuntu-latest + timeout-minutes: 20 + defaults: + run: + shell: bash --noprofile --norc {0} + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ env.CHECKOUT_REF }} + fetch-depth: 0 + persist-credentials: false + + - name: Configure Git Environment + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + - name: Install Dependencies + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + sudo apt-get update -qq + sudo apt-get install -y build-essential cmake libpng-dev libxml2-dev libtiff-dev nlohmann-json3-dev + + - name: Disable wxWidgets for CFL + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + sed -i 's/^ find_package(wxWidgets COMPONENTS core base REQUIRED)/# find_package(wxWidgets COMPONENTS core base REQUIRED) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ ADD_SUBDIRECTORY(Tools\/wxProfileDump)/# ADD_SUBDIRECTORY(Tools\/wxProfileDump) # Disabled for CFL/g' Build/Cmake/CMakeLists.txt + sed -i 's/^ message(FATAL_ERROR "wxWidgets not found/ # message(FATAL_ERROR "wxWidgets not found/g' Build/Cmake/CMakeLists.txt + + - name: Initial Build + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + mkdir -p Build && cd Build + cmake ../Build/Cmake -DCMAKE_BUILD_TYPE=Release + make -j$(nproc) + TOOL_COUNT_1=$(find Tools -type f -executable | wc -l) + echo "Initial build: $TOOL_COUNT_1 tools" + echo "TOOL_COUNT_1=$TOOL_COUNT_1" >> $GITHUB_ENV + + - name: Clean + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + cd Build + make clean + REMAINING=$(find Tools -type f -executable 2>/dev/null | wc -l || echo 0) + echo "After clean: $REMAINING executables" + if [ "$REMAINING" -ne 0 ]; then + echo "ERROR: make clean did not remove all executables" + exit 1 + fi + + - name: Rebuild + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + cd Build + make -j$(nproc) + TOOL_COUNT_2=$(find Tools -type f -executable | wc -l) + echo "After rebuild: $TOOL_COUNT_2 tools" + if [ "$TOOL_COUNT_2" -ne "${{ env.TOOL_COUNT_1 }}" ]; then + echo "ERROR: Rebuild produced different number of tools ($TOOL_COUNT_2 vs ${{ env.TOOL_COUNT_1 }})" + exit 1 + fi + echo "✅ Clean/rebuild cycle successful" + + ############################################################################# + # Job 8: Final Summary + ############################################################################# + final-summary: + name: "Test Summary" + runs-on: ubuntu-latest + needs: [standard-builds, fuzzer-builds, sanitizer-builds, option-matrix, windows-build, version-header-test, clean-rebuild-test] + if: always() + + steps: + - name: Generate Summary + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + { + echo "# Comprehensive Build & Test Results" + echo "" + echo "## Test Coverage" + echo "" + echo "### Platforms" + echo "- ✅ Ubuntu (latest)" + echo "- ✅ macOS (latest)" + echo "- ✅ Windows (latest)" + echo "" + echo "### Compilers" + echo "- ✅ GCC (Ubuntu)" + echo "- ✅ Clang (Ubuntu, macOS)" + echo "- ✅ MSVC (Windows)" + echo "" + echo "### Build Types" + echo "- ✅ Release (default)" + echo "- ✅ Debug" + echo "- ✅ RelWithDebInfo" + echo "" + echo "### Sanitizers" + echo "- ✅ AddressSanitizer (ASAN)" + echo "- ✅ UndefinedBehaviorSanitizer (UBSAN)" + echo "- ✅ Combined ASAN+UBSAN" + echo "" + echo "### Build Options" + echo "- ✅ VERBOSE_CONFIG" + echo "- ✅ ENABLE_COVERAGE" + echo "- ✅ ICC_ENABLE_ASSERTS" + echo "- ✅ ICC_TRACE_NAN_ENABLED" + echo "- ✅ Shared libs only" + echo "- ✅ Static libs only" + echo "" + echo "### Fuzzing" + echo "- ✅ 14 libFuzzer harnesses (Debug + Full Instrumentation)" + echo "" + echo "### Special Tests" + echo "- ✅ Version header generation (build directory)" + echo "- ✅ Clean source tree after build" + echo "- ✅ Clean/rebuild cycle" + echo "" + echo "## Job Results" + echo "" + echo "| Job | Status |" + echo "|-----|--------|" + echo "| Standard Builds | ${{ needs.standard-builds.result }} |" + echo "| Fuzzer Builds | ${{ needs.fuzzer-builds.result }} |" + echo "| Sanitizer Builds | ${{ needs.sanitizer-builds.result }} |" + echo "| Option Matrix | ${{ needs.option-matrix.result }} |" + echo "| Windows Build | ${{ needs.windows-build.result }} |" + echo "| Version Header Test | ${{ needs.version-header-test.result }} |" + echo "| Clean/Rebuild Test | ${{ needs.clean-rebuild-test.result }} |" + echo "" + echo "---" + echo "" + echo "**Compliance:** Hoyt shell/PowerShell prologue standards" + echo "**Reference:** llmcjf/actions/" + } >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/ci-latest-release.yml b/.github/workflows/ci-latest-release.yml index 3f5e2ec92..b9f2bac87 100644 --- a/.github/workflows/ci-latest-release.yml +++ b/.github/workflows/ci-latest-release.yml @@ -1,27 +1,29 @@ ############################################################### # -## Copyright (©) 2025 International Color Consortium. +## Copyright (©) 2025-2026 International Color Consortium. ## All rights reserved. ## https://color.org # # ## Intent: ci-latest-release # -## Last Updated: 31-JAN-2025 0100Z by David Hoyt +## Last Updated: 2026-02-17 18:05:19 UTC UTC by David Hoyt +# Clean .pdb files from Zip Bundle +# Change Build to Release per Developer Spec +# Note that Cmake Default Build = Release (now) # Add bash & pwsh shell prologues +# Align Unix & Windows Bundle per Developer Spec +# Thank you Max & Phil for the Corrections! # # TODO: Extract repetitive shell commands to reusable script -# -# -# Ref: https://github.com/xsscx/governance/tree/main/actions -# +# ############################################################### name: ci-latest-release permissions: contents: read - + on: workflow_dispatch: @@ -29,7 +31,7 @@ jobs: linux: name: "Linux ${{ matrix.compiler }}" runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 45 strategy: fail-fast: false matrix: @@ -127,15 +129,82 @@ jobs: echo "::error ::Build failed! CMakeCache.txt not found." | tee -a $GITHUB_STEP_SUMMARY exit 1 fi + - name: Stage bundle into Testing directory + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + stage="staging/iccDEV-Testing" + mkdir -p "$stage" + # 1. Copy Testing contents (exclude Fuzzing) + for item in Testing/*; do + [ "$(basename "$item")" = "Fuzzing" ] && continue + cp -a "$item" "$stage/" + done + # 2. Copy tool binaries and libraries from build + find Build/Tools -type f \( -perm -u+x -o -name '*.so*' -o -name '*.a' \) \ + -exec cp -a {} "$stage/" \; + find Build/IccProfLib -type f -name '*.so*' \ + -exec cp -a {} "$stage/" \; 2>/dev/null || true + find Build/IccProfLib -type l -name '*.so*' \ + -exec cp -a {} "$stage/" \; 2>/dev/null || true + find Build/IccXML -type f -name '*.so*' \ + -exec cp -a {} "$stage/" \; 2>/dev/null || true + find Build/IccXML -type l -name '*.so*' \ + -exec cp -a {} "$stage/" \; 2>/dev/null || true + # 3. Copy Testing/Readme.md, LICENSE.md and docs/ + cp Testing/Readme.md "$stage/" + cp LICENSE.md "$stage/" + cp -a docs "$stage/docs" + # 4. Generate path.sh helper script (POSIX-compatible, resolves from script location) + printf '%s\n' \ + '#!/bin/sh' \ + '# Resolve the directory where this script lives' \ + 'if [ -n "${BASH_SOURCE:-}" ]; then' \ + ' SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"' \ + 'else' \ + ' SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"' \ + 'fi' \ + 'export PATH="$SCRIPT_DIR:$PATH"' \ + 'export LD_LIBRARY_PATH="$SCRIPT_DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"' \ + 'for d in "$SCRIPT_DIR"/*/; do' \ + ' [ -d "$d" ] && export PATH="$d:$PATH"' \ + 'done' \ + 'echo "PATH and LD_LIBRARY_PATH updated."' \ + 'echo "Tools directory: $SCRIPT_DIR"' \ + > "$stage/path.sh" + chmod +x "$stage/path.sh" + # Summary of staged bundle contents + total=$(find "$stage" -type f | wc -l) + echo "" + echo "===== Staged Bundle Contents ($total files) =====" + for ext in so 'so.*' a icc md; do + count=$(find "$stage" -maxdepth 1 -type f -name "*.$ext" -o -type l -name "*.$ext" | wc -l) + if [ "$count" -gt 0 ]; then + echo "" + echo "--- *.$ext ($count) ---" + find "$stage" -maxdepth 1 \( -type f -o -type l \) -name "*.$ext" -printf ' %f (%k KB)\n' 2>/dev/null || \ + find "$stage" -maxdepth 1 \( -type f -o -type l \) -name "*.$ext" -exec bash -c 'echo " $(basename {}) ($(( $(stat -f%z {} 2>/dev/null || stat -c%s {}) / 1024 )) KB)"' \; + fi + done + # List executables (ELF binaries without extension) + exes=$(find "$stage" -maxdepth 1 -type f -executable ! -name '*.*' | wc -l) + if [ "$exes" -gt 0 ]; then + echo "" + echo "--- executables ($exes) ---" + find "$stage" -maxdepth 1 -type f -executable ! -name '*.*' -printf ' %f (%k KB)\n' 2>/dev/null || \ + find "$stage" -maxdepth 1 -type f -executable ! -name '*.*' -exec bash -c 'echo " $(basename {}) ($(( $(stat -f%z {} 2>/dev/null || stat -c%s {}) / 1024 )) KB)"' \; + fi + echo "" + echo "Total: $total files staged into $stage" + echo "" - name: Upload Linux Artifacts uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 with: name: iccdev-linux-${{ matrix.compiler }} - path: | - Build - LICENSE.md - README.md - docs/** + path: staging + if-no-files-found: warn - name: Summary Report if: always() @@ -169,9 +238,9 @@ jobs: git config --global credential.helper "" unset GITHUB_TOKEN || true echo "Installing Homebrew dependencies..." - brew install libpng nlohmann-json libxml2 wxwidgets libtiff jpeg || echo "⚠️ Some dependencies might already be installed." + brew install libpng nlohmann-json libxml2 wxwidgets libtiff jpeg-turbo || echo "⚠️ Some dependencies might already be installed." echo "✔ Dependency installation complete." - + - name: CMake Configure shell: bash --noprofile --norc {0} env: @@ -184,18 +253,18 @@ jobs: echo "Setting up CMake build configuration..." cd Build sudo rm -rf /Library/Frameworks/Mono.framework/Headers/png.h - echo 'export PATH="/opt/homebrew/opt/jpeg/bin:$PATH"' >> /Users/runner/.bash_profile - export CPPFLAGS="-I/opt/homebrew/opt/jpeg/include" - export PKG_CONFIG_PATH="/opt/homebrew/opt/jpeg/lib/pkgconfig" - export CFLAGS="-I$(brew --prefix libpng)/include -I$(brew --prefix jpeg)/include" - export LDFLAGS="-L$(brew --prefix libpng)/lib -L$(brew --prefix jpeg)/lib" + echo 'export PATH="/opt/homebrew/opt/jpeg-turbo/bin:$PATH"' >> /Users/runner/.bash_profile + export CPPFLAGS="-I/opt/homebrew/opt/jpeg-turbo/include" + export PKG_CONFIG_PATH="/opt/homebrew/opt/jpeg-turbo/lib/pkgconfig" + export CFLAGS="-I$(brew --prefix libpng)/include -I$(brew --prefix jpeg-turbo)/include" + export LDFLAGS="-L$(brew --prefix libpng)/lib -L$(brew --prefix jpeg-turbo)/lib" cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local \ -DCMAKE_BUILD_TYPE=Release \ - -DJPEG_LIBRARY=$(brew --prefix jpeg)/lib/libjpeg.dylib \ - -DJPEG_INCLUDE_DIR=$(brew --prefix jpeg)/include \ + -DJPEG_LIBRARY=$(brew --prefix jpeg-turbo)/lib/libjpeg.dylib \ + -DJPEG_INCLUDE_DIR=$(brew --prefix jpeg-turbo)/include \ -Wno-dev Cmake/ echo "✔ CMake configuration complete." - + - name: Build shell: bash --noprofile --norc {0} env: @@ -214,20 +283,85 @@ jobs: echo "=== Updating PATH ===" for d in ../Build/Tools/*; do [ -d "$d" ] && export PATH="$(realpath "$d"):$PATH" - done + done sh CreateAllProfiles.sh sh RunTests.sh find . -iname "*.icc" | wc -l - + + - name: Stage bundle into Testing directory + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + stage="staging/iccDEV-Testing" + mkdir -p "$stage" + # 1. Copy Testing contents (exclude Fuzzing) + for item in Testing/*; do + [ "$(basename "$item")" = "Fuzzing" ] && continue + cp -a "$item" "$stage/" + done + # 2. Copy tool binaries and libraries from build + find Build/Tools -type f \( -perm -u+x -o -name '*.dylib' -o -name '*.a' \) \ + -exec cp -a {} "$stage/" \; + find Build/IccProfLib -type f -name '*.dylib' \ + -exec cp -a {} "$stage/" \; 2>/dev/null || true + find Build/IccProfLib -type l -name '*.dylib' \ + -exec cp -a {} "$stage/" \; 2>/dev/null || true + find Build/IccXML -type f -name '*.dylib' \ + -exec cp -a {} "$stage/" \; 2>/dev/null || true + find Build/IccXML -type l -name '*.dylib' \ + -exec cp -a {} "$stage/" \; 2>/dev/null || true + # 3. Copy Testing/Readme.md, LICENSE.md and docs/ + cp Testing/Readme.md "$stage/" + cp LICENSE.md "$stage/" + cp -a docs "$stage/docs" + # 4. Generate path.sh helper script (POSIX-compatible, resolves from script location) + printf '%s\n' \ + '#!/bin/sh' \ + '# Resolve the directory where this script lives' \ + 'if [ -n "${BASH_SOURCE:-}" ]; then' \ + ' SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"' \ + 'else' \ + ' SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"' \ + 'fi' \ + 'export PATH="$SCRIPT_DIR:$PATH"' \ + 'export DYLD_LIBRARY_PATH="$SCRIPT_DIR${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}"' \ + 'for d in "$SCRIPT_DIR"/*/; do' \ + ' [ -d "$d" ] && export PATH="$d:$PATH"' \ + 'done' \ + 'echo "PATH and DYLD_LIBRARY_PATH updated."' \ + 'echo "Tools directory: $SCRIPT_DIR"' \ + > "$stage/path.sh" + chmod +x "$stage/path.sh" + # Summary of staged bundle contents + total=$(find "$stage" -type f | wc -l) + echo "" + echo "===== Staged Bundle Contents ($total files) =====" + for ext in dylib a icc md; do + count=$(find "$stage" -maxdepth 1 -type f -name "*.$ext" -o -type l -name "*.$ext" | wc -l) + if [ "$count" -gt 0 ]; then + echo "" + echo "--- *.$ext ($count) ---" + find "$stage" -maxdepth 1 \( -type f -o -type l \) -name "*.$ext" -exec bash -c 'echo " $(basename {}) ($(( $(stat -f%z {} 2>/dev/null || stat -c%s {}) / 1024 )) KB)"' \; + fi + done + # List executables (Mach-O binaries without extension) + exes=$(find "$stage" -maxdepth 1 -type f -perm +111 ! -name '*.*' | wc -l) + if [ "$exes" -gt 0 ]; then + echo "" + echo "--- executables ($exes) ---" + find "$stage" -maxdepth 1 -type f -perm +111 ! -name '*.*' -exec bash -c 'echo " $(basename {}) ($(( $(stat -f%z {} 2>/dev/null || stat -c%s {}) / 1024 )) KB)"' \; + fi + echo "" + echo "Total: $total files staged into $stage" + echo "" - name: Upload macOS Artifacts uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 with: name: iccdev-macos-clang - path: | - Build - LICENSE.md - README.md - docs/** + path: staging + if-no-files-found: warn - name: Summary Report if: always() shell: bash --noprofile --norc {0} @@ -245,7 +379,7 @@ jobs: windows: name: "Windows MSVC" runs-on: windows-latest - timeout-minutes: 20 + timeout-minutes: 45 env: POWERSHELL_TELEMETRY_OPTOUT: "1" defaults: @@ -268,7 +402,7 @@ jobs: Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' - + # --- Disable telemetry sources (pwsh / dotnet / gh) --- $env:POWERSHELL_TELEMETRY_OPTOUT = "1" $env:DOTNET_CLI_TELEMETRY_OPTOUT = "1" @@ -280,7 +414,7 @@ jobs: if (-not (Test-Path $env:GITHUB_WORKSPACE)) { throw "Invalid GITHUB_WORKSPACE path: $env:GITHUB_WORKSPACE" } - + # --- Remove tokens or uci that should not exist in PR CI --- foreach ($v in @( 'GITHUB_TOKEN', @@ -292,7 +426,7 @@ jobs: Remove-Item "Env:$v" -ErrorAction SilentlyContinue } } - + # --- Canonicalization then sanitize user controllable input --- function Canonicalize([string]$s) { if (-not $s) { return "" } @@ -301,7 +435,7 @@ jobs: $s = -join ($s.ToCharArray() | Where-Object { [int]$_ -ge 32 }) return $s } - + # --- Canonicalize PR-controlled environment values --- $env:GITHUB_REF = Canonicalize $env:GITHUB_REF $env:GITHUB_HEAD_REF = Canonicalize $env:GITHUB_HEAD_REF @@ -310,7 +444,7 @@ jobs: # --- Harden Git configuration --- git config --add safe.directory "$PWD" git config --global credential.helper "" - + # --- PATH hardening: limit to trusted tool locations --- $trustedPath = @( "$env:SystemRoot\system32", @@ -319,9 +453,8 @@ jobs: "$env:ProgramFiles\PowerShell\7" ) -join ';' $env:PATH = $trustedPath - - Write-Host "Finished UCI Gate 1 for pwsh" -ForegroundColor Green + Write-Host "Finished UCI Gate 1 for pwsh" -ForegroundColor Green - name: Setup MSBuild uses: microsoft/setup-msbuild@767f00a3f09872d96a0cb9fcd5e6a4ff33311330 @@ -334,13 +467,11 @@ jobs: Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' - Write-Host "Triggered by: $env:GITHUB_EVENT_NAME" Write-Host "Base branch: $env:GITHUB_BASE_REF" Write-Host "Head branch: $env:GITHUB_HEAD_REF" Write-Host "Commit SHA: $env:PR_HEAD_SHA" Write-Host "OS: $env:RUNNER_OS" - - name: Install dependencies and build shell: pwsh run: | @@ -360,9 +491,9 @@ jobs: tar -xf deps.zip cd Build/Cmake Write-Host "========= Building... ================`n" - cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="..\..\scripts\buildsystems\vcpkg.cmake" -DVCPKG_MANIFEST_MODE=OFF -DCMAKE_BUILD_TYPE=Debug -Wno-dev - cmake --build build -- /m /maxcpucount - cmake --build build -- /m /maxcpucount + cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="..\..\scripts\buildsystems\vcpkg.cmake" -DVCPKG_MANIFEST_MODE=OFF -DCMAKE_BUILD_TYPE=Release -Wno-dev + cmake --build build --config Release -- /m /maxcpucount + cmake --build build --config Release -- /m /maxcpucount $exeDirs = Get-ChildItem -Recurse -File -Include *.exe -Path .\build\ | Where-Object { $_.FullName -match 'icc' -and $_.FullName -notmatch '\\CMakeFiles\\' -and $_.Name -notmatch '^CMake(C|CXX)CompilerId\.exe$' } | ForEach-Object { Split-Path $_.FullName -Parent } | @@ -397,38 +528,89 @@ jobs: # Collect .icc profile information $profiles = Get-ChildItem -Path . -Filter "*.icc" -Recurse -File $totalCount = $profiles.Count - + # Group profiles by directory $groupedProfiles = $profiles | Group-Object { $_.Directory.FullName } - + # Generate Summary Report Write-Host "`n=========================" Write-Host " ICC Profile Report" Write-Host "=========================" - + # Print count per subdirectory foreach ($group in $groupedProfiles) { Write-Host ("{0}: {1} .icc profiles" -f $group.Name, $group.Count) } - + Write-Host "`nTotal .icc profiles found: $totalCount" Write-Host "=========================`n" - + Write-Host "All Done!" + - name: Stage bundle into Testing directory + shell: pwsh + run: | + Set-StrictMode -Version Latest + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + $stage = "staging/iccDEV-Testing" + New-Item -ItemType Directory -Force -Path $stage | Out-Null + # 1. Copy the entire Testing directory contents (exclude Fuzzing) + Get-ChildItem -Path "Testing" -Exclude "Fuzzing" | + Copy-Item -Destination $stage -Recurse -Force + # 2. Copy .exe and library files from the build (no .pdb in Release) + Get-ChildItem -Path "Build/Cmake/build" -Recurse -Include *.exe,*.dll,*.lib | + Where-Object { $_.FullName -notmatch '\\CMakeFiles\\' -and $_.Name -notmatch '^CMake(C|CXX)CompilerId\.exe$' } | + ForEach-Object { Copy-Item $_.FullName -Destination $stage -Force } + # 3. Copy Testing/Readme.md, LICENSE.md and docs/ + Copy-Item -Path "Testing/Readme.md" -Destination $stage -Force + Copy-Item -Path "LICENSE.md" -Destination $stage -Force + Copy-Item -Path "docs" -Destination "$stage/docs" -Recurse -Force + # 4. Generate path.bat helper script + @' + @echo off + setlocal EnableDelayedExpansion + set "SCRIPT_DIR=%~dp0" + if "!SCRIPT_DIR:~-1!"=="\" set "SCRIPT_DIR=!SCRIPT_DIR:~0,-1!" + :: Build new PATH with script dir and all subdirs containing .exe + set "NEW_PATH=!SCRIPT_DIR!;!PATH!" + for /R "!SCRIPT_DIR!" %%F in (*.exe) do ( + set "dir=%%~dpF" + if "!dir:~-1!"=="\" set "dir=!dir:~0,-1!" + echo !NEW_PATH! | findstr /I /C:"!dir!" >nul || set "NEW_PATH=!dir!;!NEW_PATH!" + ) + :: Export PATH back to caller via endlocal trick + endlocal & set "PATH=%NEW_PATH%" + '@ | Set-Content -Path "$stage/path.bat" -Encoding ASCII + # Summary of staged bundle contents + $allFiles = Get-ChildItem -Path $stage -Recurse -File + $count = $allFiles.Count + Write-Host "`n===== Staged Bundle Contents ($count files) =====" -ForegroundColor Cyan + foreach ($ext in @('*.exe','*.dll','*.lib','*.icc','*.md')) { + $matched = $allFiles | Where-Object { $_.Name -like $ext } + if ($matched) { + Write-Host "`n--- $ext ($($matched.Count)) ---" + $matched | ForEach-Object { + $rel = $_.FullName.Replace((Resolve-Path $stage).Path + '\', '') + Write-Host " $rel ($([math]::Round($_.Length/1KB,1)) KB)" + } + } + } + $otherExts = $allFiles | + Where-Object { $_.Extension -notin '.exe','.dll','.lib','.icc','.md' } | + Group-Object Extension | + Sort-Object Count -Descending + if ($otherExts) { + Write-Host "`n--- Other files ---" + foreach ($g in $otherExts) { + Write-Host (" {0}: {1} file(s)" -f ($g.Name ? $g.Name : '(no ext)'), $g.Count) + } + } + Write-Host "`nTotal: $count files staged into $stage`n" - name: Upload build artifacts uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 with: name: iccdev-windows-msvc - path: | - Build/Cmake/build/**/*.lib - Build/Cmake/build/**/*.a - Build/Cmake/build/**/*.dll - Build/Cmake/build/**/*.exe - Build/Cmake/build/**/*.pdb - Build/Cmake/Testing/**/* - LICENSE.md - README.md - docs/** + path: staging if-no-files-found: warn - name: Host System Info shell: pwsh @@ -437,8 +619,24 @@ jobs: Get-CimInstance -ClassName Win32_Processor - name: Summary Report if: always() + shell: pwsh + env: + POWERSHELL_TELEMETRY_OPTOUT: "1" + GH_REPOSITORY: ${{ github.repository }} + GH_COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + GH_RUNNER_OS: ${{ runner.os }} + GH_JOB_STATUS: ${{ job.status }} run: | - echo "### Windows Build Summary" >> $GITHUB_STEP_SUMMARY - echo "- Build Directory: Build/" >> $GITHUB_STEP_SUMMARY - echo "- Artifacts Uploaded: iccdev-windows-msvc" >> $GITHUB_STEP_SUMMARY - echo "- Status: Success" >> $GITHUB_STEP_SUMMARY \ No newline at end of file + Set-StrictMode -Version Latest + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + # Source trusted sanitizer from checked-out workspace + . .github/scripts/sanitize.ps1 + "### Windows Build Summary" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + "" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + "- Repository: $(Sanitize-Line $env:GH_REPOSITORY)" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + "- Commit SHA: $(Sanitize-Line $env:GH_COMMIT_SHA)" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + "- Runner OS: $(Sanitize-Line $env:GH_RUNNER_OS)" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + "- Build Directory: Build/" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + "- Artifacts Uploaded: iccdev-windows-msvc" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + "- Status: $(Sanitize-Line $env:GH_JOB_STATUS)" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append \ No newline at end of file diff --git a/.github/workflows/ci-pr-lint.yml b/.github/workflows/ci-pr-lint.yml index d6b5f27cd..502c7ef9c 100644 --- a/.github/workflows/ci-pr-lint.yml +++ b/.github/workflows/ci-pr-lint.yml @@ -1,20 +1,16 @@ ############################################################### # -# Copyright (©) 2025 International Color Consortium. -# All rights reserved. +# Copyright (©) 2025 International Color Consortium. +# All rights reserved. # https://color.org # # # Intent: Static code analysis and linting -# Last Updated: 02-JAN-2025 2100Z by David Hoyt -# -# Change git depth, other changes -# -# -# -# -# +# Last Updated: 2026-02-17 16:21:47 UTC by David Hoyt # +# Matrix: cppcheck and clang-tidy run as parallel +# jobs, each with -j$(nproc). Build is shared via +# artifact upload. # ############################################################### @@ -25,10 +21,8 @@ permissions: pull-requests: read on: - # Allow manual trigger workflow_dispatch: - # Allow other workflows to call this workflow_call: inputs: ubuntu-version: @@ -38,13 +32,18 @@ on: type: string jobs: - build-linux: - name: Lint on ${{ inputs.ubuntu-version || 'ubuntu-latest' }} + ############################################################################# + # Job 1: Build + determine files + ############################################################################# + build: + name: "Build" runs-on: ${{ inputs.ubuntu-version || 'ubuntu-latest' }} - timeout-minutes: 30 + timeout-minutes: 20 + outputs: + no_src_changes: ${{ steps.diffcheck.outputs.no_src_changes }} + scope: ${{ steps.diffcheck.outputs.scope }} steps: - # --- Bump any Self-Hosted --- - name: Validate ubuntu-version input shell: bash --noprofile --norc {0} env: @@ -59,19 +58,14 @@ jobs: exit 1 ;; esac - - uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 with: fetch-depth: 0 - # Disable unsafe fetch options persist-credentials: false - name: Checkout base commit (trusted sanitizers) uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 with: - # For workflow_call from ci-pr-action: use github.sha (the base branch) - # For pull_request: use github.event.pull_request.base.sha - # For workflow_dispatch: use github.sha ref: ${{ github.event.pull_request.base.sha || github.sha }} path: base fetch-depth: 1 @@ -81,25 +75,6 @@ jobs: run: | git config --global user.email "github-actions@github.com" git config --global user.name "GitHub Actions" - - - name: Verify remote origin URL (opt out of malicious git configs) - shell: bash --noprofile --norc {0} - env: - BASH_ENV: /dev/null - run: | - set -euo pipefail - git config --add safe.directory "$PWD" - git config --global credential.helper "" - origin_url=$(git remote get-url origin || true) - echo "Remote origin: $origin_url" - # Enforce that origin points to the expected GitHub repository for this workflow run - expected="https://github.com/${{ github.repository }}.git" - # Allow both HTTPS and git@ forms if needed - if [[ "$origin_url" != "$expected" && "$origin_url" != "${expected%.git}" && "$origin_url" != "git@github.com:InternationalColorConsortium/iccDEV.git" ]]; then - echo "Origin URL mismatch: expected $expected, ${expected%.git}, or git@github.com:InternationalColorConsortium/iccDEV.git, got $origin_url" >&2 - exit 1 - fi - - name: Install Linux dependencies shell: bash --noprofile --norc {0} env: @@ -109,17 +84,25 @@ jobs: git config --add safe.directory "$PWD" git config --global credential.helper "" unset GITHUB_TOKEN || true - sudo apt-get update -qq sudo apt-get install -y \ - build-essential cmake gcc g++ clang clang-tools \ - cppcheck clang-format pkg-config \ - libpng-dev libxml2-dev libtiff-dev \ + build-essential cmake gcc g++ clang clang-tools clang-tidy \ + cppcheck clang-format pkg-config jq \ + libpng-dev libxml2-dev libtiff-dev libjpeg-dev \ nlohmann-json3-dev libwxgtk3.2-dev wx-common \ - python3 python3-pip curl git llvm - + python3 curl git llvm + { + echo "### Environment" + echo "| Tool | Version |" + echo "|------|---------|" + echo "| cppcheck | $(cppcheck --version 2>&1) |" + echo "| clang-tidy | $(clang-tidy --version 2>&1 | head -1) |" + echo "| cmake | $(cmake --version | head -1) |" + echo "| nproc | $(nproc) |" + echo "" + } >> "$GITHUB_STEP_SUMMARY" - id: diffcheck - name: diffcheck for file changes + name: Determine files to analyze shell: bash --noprofile --norc {0} env: BASH_ENV: /dev/null @@ -128,49 +111,39 @@ jobs: git config --add safe.directory "$PWD" git config --global credential.helper "" unset GITHUB_TOKEN || true - git show --stat --pretty=format:"Commit: %H%nAuthor: %an%nDate: %ad%n" HEAD - - # If not a pull_request event (e.g., workflow_dispatch or workflow_call), - # skip source diff checking and mark as non-source change. - if [ "${{ github.event_name }}" != "pull_request" ]; then - echo "Non-PR event (${{ github.event_name }}) — skipping source diff check" >&2 - echo "no_src_changes=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - base_ref="${{ github.base_ref }}" - - # Avoid fetching refs from user-controllable inputs like ($base_ref) - case "$base_ref" in master) - ;; - *) + if [ "${{ github.event_name }}" = "pull_request" ]; then + base_ref="${{ github.base_ref }}" + case "$base_ref" in master) ;; *) echo "Ref blocked or unsupported: $base_ref" >&2 echo "no_src_changes=true" >> "$GITHUB_OUTPUT" + echo "scope=none" >> "$GITHUB_OUTPUT" exit 0 ;; - esac - - # Fetch enough history to find merge base - git -c protocol.version=2 fetch --no-tags origin "${base_ref}" --depth=50 - - # Case-insensitive extension match (c/h/cpp variants) - # Use three-dot syntax to show changes since merge-base - git diff --name-only "origin/${base_ref}...HEAD" \ - | tr -cd '[:alnum:]./\-_\n' \ - | grep -E '\.(cpp|cxx|cc|h|hpp)$' \ - | tr '\n' '\0' > changed_files.txt - - changed_file_count="$(xargs -0 -n1 < changed_files.txt | wc -l)" - - if [ "$changed_file_count" -eq 0 ]; then - echo "no_src_changes=true" >> "$GITHUB_OUTPUT" - else - echo "no_src_changes=false" >> "$GITHUB_OUTPUT" - fi - + esac + git -c protocol.version=2 fetch --no-tags origin "${base_ref}" --depth=50 + git diff --name-only "origin/${base_ref}...HEAD" \ + | tr -cd '[:alnum:]./\-_\n' \ + | grep -E '\.(cpp|cxx|cc|h|hpp)$' \ + | tr '\n' '\0' > changed_files.txt + changed_file_count="$(xargs -0 -n1 < changed_files.txt | wc -l)" + if [ "$changed_file_count" -eq 0 ]; then + echo "no_src_changes=true" >> "$GITHUB_OUTPUT" + echo "scope=none" >> "$GITHUB_OUTPUT" + else + echo "no_src_changes=false" >> "$GITHUB_OUTPUT" + echo "scope=pr" >> "$GITHUB_OUTPUT" + echo "Analyzing $changed_file_count changed files" + fi + else + echo "no_src_changes=false" >> "$GITHUB_OUTPUT" + echo "scope=full" >> "$GITHUB_OUTPUT" + find IccProfLib IccXML Tools -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.cxx' -o -name '*.cc' -o -name '*.hpp' \) -print0 > changed_files.txt + file_count="$(xargs -0 -n1 < changed_files.txt | wc -l)" + echo "Full-tree analysis: $file_count source files" + fi - name: Configure and build - if: ${{ steps.diffcheck.outputs.no_src_changes == 'false' }} + if: steps.diffcheck.outputs.no_src_changes == 'false' shell: bash --noprofile --norc {0} env: BASH_ENV: /dev/null @@ -179,15 +152,54 @@ jobs: git config --add safe.directory "$PWD" git config --global credential.helper "" unset GITHUB_TOKEN || true - mkdir -p Build cd Build - cmake Cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - make - cd .. + cmake Cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -Wno-dev + make -j"$(nproc)" + - name: Upload build context + if: steps.diffcheck.outputs.no_src_changes == 'false' + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 + with: + name: lint-build-context + retention-days: 1 + path: | + Build/compile_commands.json + changed_files.txt + ############################################################################# + # Job 2: Parallel lint matrix + # - cppcheck split by component (IccProfLib, IccXML, Tools) + # - clang-tidy runs full tree + ############################################################################# + lint: + name: "${{ matrix.name }}" + runs-on: ${{ inputs.ubuntu-version || 'ubuntu-latest' }} + needs: build + if: needs.build.outputs.no_src_changes == 'false' + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - name: "cppcheck • IccProfLib" + tool: cppcheck + component: IccProfLib + - name: "cppcheck • IccXML" + tool: cppcheck + component: IccXML + - name: "cppcheck • Tools" + tool: cppcheck + component: Tools + - name: "clang-tidy" + tool: clang-tidy + component: all + steps: - - name: Run cppcheck - if: ${{ steps.diffcheck.outputs.no_src_changes == 'false' }} + - uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Install dependencies shell: bash --noprofile --norc {0} env: BASH_ENV: /dev/null @@ -196,41 +208,33 @@ jobs: git config --add safe.directory "$PWD" git config --global credential.helper "" unset GITHUB_TOKEN || true - - mkdir -p lint_reports - - echo "Running cppcheck on modified source files..." - - # --- SECURITY: null-delimited xargs --- - : > lint_reports/cppcheck.txt - xargs -0 cppcheck \ - --enable=warning,performance,portability \ - --std=c++17 \ - --output-file=lint_reports/cppcheck.txt \ - < changed_files.txt || true + sudo apt-get update -qq + sudo apt-get install -y \ + build-essential cmake gcc g++ clang clang-tools clang-tidy \ + cppcheck pkg-config \ + libpng-dev libxml2-dev libtiff-dev libjpeg-dev \ + nlohmann-json3-dev libwxgtk3.2-dev wx-common \ + llvm + - name: Download build context + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 + with: + name: lint-build-context - - name: Run clang-tidy - if: ${{ steps.diffcheck.outputs.no_src_changes == 'false' }} + - name: Filter files for component + if: matrix.tool == 'cppcheck' shell: bash --noprofile --norc {0} env: BASH_ENV: /dev/null run: | set -euo pipefail - git config --add safe.directory "$PWD" - git config --global credential.helper "" - unset GITHUB_TOKEN || true - mkdir -p lint_reports - - # --- clang-tidy --- - : > lint_reports/clang_tidy.txt - xargs -0 run-clang-tidy \ - -p Build \ - -checks='modernize-*,readability-*,cppcoreguidelines-*,clang-analyzer-core.*,clang-analyzer-security.*,clang-analyzer-alpha.core.*,clang-analyzer-alpha.security.*' \ - < changed_files.txt \ - | tee lint_reports/clang_tidy.txt || true - - - name: Skip lint (no source changes) - if: ${{ steps.diffcheck.outputs.no_src_changes == 'true' }} + # Filter changed_files.txt to only this component's files + xargs -0 -n1 < changed_files.txt \ + | { grep "^${{ matrix.component }}/" || true; } \ + | tr '\n' '\0' > component_files.txt + FILE_COUNT=$(xargs -0 -n1 < component_files.txt 2>/dev/null | wc -l) + echo "Component ${{ matrix.component }}: $FILE_COUNT files to analyze" + echo "file_count=$FILE_COUNT" >> "$GITHUB_ENV" + - name: Run ${{ matrix.name }} shell: bash --noprofile --norc {0} env: BASH_ENV: /dev/null @@ -239,16 +243,100 @@ jobs: git config --add safe.directory "$PWD" git config --global credential.helper "" unset GITHUB_TOKEN || true + NPROC=$(nproc) mkdir -p lint_reports - echo "No .cpp/.h changes detected; skipping lint." > lint_reports/cppcheck.txt - : > lint_reports/clang_tidy.txt - - - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 + if [ "${{ matrix.tool }}" = "cppcheck" ]; then + if [ "${file_count:-0}" -eq 0 ]; then + echo "No ${{ matrix.component }} files to check — skipping" + : > lint_reports/cppcheck_${{ matrix.component }}.txt + else + echo "Running cppcheck on ${{ matrix.component }} with $NPROC parallel jobs" + xargs -0 cppcheck \ + -j "$NPROC" \ + --enable=warning,performance,portability,style \ + --std=c++17 \ + --suppress=missingIncludeSystem \ + < component_files.txt 2> lint_reports/cppcheck_${{ matrix.component }}.txt || true + echo "✓ cppcheck ${{ matrix.component }}: $(wc -l < lint_reports/cppcheck_${{ matrix.component }}.txt) lines" + fi + else + echo "Running clang-tidy with $NPROC parallel jobs" + xargs -0 run-clang-tidy \ + -j "$NPROC" \ + -p Build \ + -checks='modernize-*,readability-*,cppcoreguidelines-*,clang-analyzer-core.*,clang-analyzer-security.*,clang-analyzer-alpha.core.*,clang-analyzer-alpha.security.*' \ + < changed_files.txt \ + > lint_reports/clang_tidy.txt 2>&1 || true + echo "✓ clang-tidy: $(wc -l < lint_reports/clang_tidy.txt) lines" + fi + - name: Upload results + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 with: - name: lint-reports + name: lint-${{ matrix.tool }}-${{ matrix.component }} + retention-days: 3 path: lint_reports/ + ############################################################################# + # Job 3: Combined report + ############################################################################# + report: + name: "Report" + runs-on: ubuntu-latest + needs: [build, lint] + if: always() + timeout-minutes: 5 + steps: + + - uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 + with: + sparse-checkout: .github/scripts + persist-credentials: false + + - name: Download cppcheck IccProfLib results + if: needs.lint.result == 'success' || needs.lint.result == 'failure' + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 + with: + name: lint-cppcheck-IccProfLib + path: lint_reports/parts/ + continue-on-error: true + + - name: Download cppcheck IccXML results + if: needs.lint.result == 'success' || needs.lint.result == 'failure' + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 + with: + name: lint-cppcheck-IccXML + path: lint_reports/parts/ + continue-on-error: true + - name: Download cppcheck Tools results + if: needs.lint.result == 'success' || needs.lint.result == 'failure' + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 + with: + name: lint-cppcheck-Tools + path: lint_reports/parts/ + continue-on-error: true + + - name: Download clang-tidy results + if: needs.lint.result == 'success' || needs.lint.result == 'failure' + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 + with: + name: lint-clang-tidy-all + path: lint_reports/ + continue-on-error: true + + - name: Merge cppcheck results + if: needs.lint.result == 'success' || needs.lint.result == 'failure' + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + run: | + set -euo pipefail + mkdir -p lint_reports + : > lint_reports/cppcheck.txt + for f in lint_reports/parts/cppcheck_*.txt; do + [ -f "$f" ] && cat "$f" >> lint_reports/cppcheck.txt + done + echo "Merged cppcheck: $(wc -l < lint_reports/cppcheck.txt) lines" - name: Generate GitHub summary if: always() shell: bash --noprofile --norc {0} @@ -259,14 +347,11 @@ jobs: git config --add safe.directory "$PWD" git config --global credential.helper "" unset GITHUB_TOKEN || true - - # Load trusted canonical sanitizer functions from base commit - TRUSTED_SANITIZER="$GITHUB_WORKSPACE/base/.github/scripts/sanitize-sed.sh" - if [[ -f "$TRUSTED_SANITIZER" ]]; then + SANITIZER=".github/scripts/sanitize-sed.sh" + if [[ -f "$SANITIZER" ]]; then # shellcheck disable=SC1090 - source "$TRUSTED_SANITIZER" + source "$SANITIZER" else - # Fallback sanitizer escape_html() { local s="$1" s="${s//&/&}" @@ -278,19 +363,139 @@ jobs: } sanitize_line() { escape_html "$1"; } fi - - echo "## 🧹 Lint Report Summary" >> $GITHUB_STEP_SUMMARY - - if [ "${{ steps.diffcheck.outputs.no_src_changes }}" == "true" ]; then - echo "🟦 Skipped — no modified source files detected." >> $GITHUB_STEP_SUMMARY - elif [ -s lint_reports/cppcheck.txt ]; then - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - # Sanitize each line properly - avoid subshell issues + echo "## 🧹 Lint Report Summary" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "**Scope:** ${{ needs.build.outputs.scope || 'unknown' }}" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + if [ "${{ needs.build.outputs.no_src_changes }}" = "true" ]; then + echo "🟦 Skipped — no modified source files detected." >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + mkdir -p lint_reports + # --- cppcheck summary --- + echo "### cppcheck Results" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + if [ -s lint_reports/cppcheck.txt ]; then + cpp_warns=$({ grep -c 'warning:' lint_reports/cppcheck.txt 2>/dev/null || true; }) + cpp_errs=$({ grep -c 'error:' lint_reports/cppcheck.txt 2>/dev/null || true; }) + cpp_perf=$({ grep -c 'performance:' lint_reports/cppcheck.txt 2>/dev/null || true; }) + cpp_port=$({ grep -c 'portability:' lint_reports/cppcheck.txt 2>/dev/null || true; }) + cpp_style=$({ grep -c 'style:' lint_reports/cppcheck.txt 2>/dev/null || true; }) + echo "| Severity | Count |" >> "$GITHUB_STEP_SUMMARY" + echo "|----------|-------|" >> "$GITHUB_STEP_SUMMARY" + echo "| error | $cpp_errs |" >> "$GITHUB_STEP_SUMMARY" + echo "| warning | $cpp_warns |" >> "$GITHUB_STEP_SUMMARY" + echo "| performance | $cpp_perf |" >> "$GITHUB_STEP_SUMMARY" + echo "| portability | $cpp_port |" >> "$GITHUB_STEP_SUMMARY" + echo "| style | $cpp_style |" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "#### Findings by Component" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Component | Findings |" >> "$GITHUB_STEP_SUMMARY" + echo "|-----------|----------|" >> "$GITHUB_STEP_SUMMARY" + for comp in IccProfLib IccXML Tools; do + f="lint_reports/parts/cppcheck_${comp}.txt" + if [ -f "$f" ] && [ -s "$f" ]; then + cnt=$(wc -l < "$f" | tr -d ' ') + else + cnt=0 + fi + echo "| $comp | $cnt |" >> "$GITHUB_STEP_SUMMARY" + done + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "#### Top cppcheck Files" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + { grep -oP '^[^:]+\.(cpp|h)' lint_reports/cppcheck.txt 2>/dev/null || true; } \ + | sort | uniq -c | sort -rn | head -10 \ + | while read -r cnt fname; do + echo "- **$cnt** — $fname" >> "$GITHUB_STEP_SUMMARY" + done + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "
cppcheck output (last 50 lines)" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" while IFS= read -r line; do safe_line=$(sanitize_line "$line") echo "$safe_line" - done < <(tail -n 30 lint_reports/cppcheck.txt) >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + done < <(tail -n 50 lint_reports/cppcheck.txt) >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "
" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" else - echo "✅ No cppcheck warnings or errors detected." >> $GITHUB_STEP_SUMMARY + echo "✅ No cppcheck findings." >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" fi + # --- clang-tidy summary --- + echo "### clang-tidy Results" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + if [ -s lint_reports/clang_tidy.txt ]; then + ct_warns=$({ grep -c 'warning:' lint_reports/clang_tidy.txt 2>/dev/null || true; }) + ct_errs=$({ grep -c 'error:' lint_reports/clang_tidy.txt 2>/dev/null || true; }) + echo "| Severity | Count |" >> "$GITHUB_STEP_SUMMARY" + echo "|----------|-------|" >> "$GITHUB_STEP_SUMMARY" + echo "| warning | $ct_warns |" >> "$GITHUB_STEP_SUMMARY" + echo "| error | $ct_errs |" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + # Per-category breakdown + echo "#### Findings by Check Category" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Category | Count |" >> "$GITHUB_STEP_SUMMARY" + echo "|----------|-------|" >> "$GITHUB_STEP_SUMMARY" + for cat in cppcoreguidelines modernize readability clang-analyzer-core clang-analyzer-security clang-analyzer-alpha; do + cnt=$({ grep -c "\[$cat" lint_reports/clang_tidy.txt 2>/dev/null || true; }) + echo "| $cat | $cnt |" >> "$GITHUB_STEP_SUMMARY" + done + echo "" >> "$GITHUB_STEP_SUMMARY" + # Per-component breakdown + echo "#### Findings by Source Component" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Component | Warnings |" >> "$GITHUB_STEP_SUMMARY" + echo "|-----------|----------|" >> "$GITHUB_STEP_SUMMARY" + for component in IccProfLib IccXML Tools; do + cnt=$({ grep "warning:.*\[" lint_reports/clang_tidy.txt 2>/dev/null || true; } | { grep "/${component}/" || true; } | wc -l | tr -d ' ') + echo "| $component | $cnt |" >> "$GITHUB_STEP_SUMMARY" + done + echo "" >> "$GITHUB_STEP_SUMMARY" + # Top 15 specific checks + echo "#### Top 15 Triggered Checks" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Count | Check |" >> "$GITHUB_STEP_SUMMARY" + echo "|-------|-------|" >> "$GITHUB_STEP_SUMMARY" + { grep -oh '\[[-a-zA-Z.]*\]' lint_reports/clang_tidy.txt 2>/dev/null || true; } \ + | sort | uniq -c | sort -rn | head -15 \ + | while read -r cnt chk; do + echo "| $cnt | $chk |" >> "$GITHUB_STEP_SUMMARY" + done + echo "" >> "$GITHUB_STEP_SUMMARY" + # Security findings highlight + sec_count=$({ grep -c 'clang-analyzer-security\|clang-analyzer-alpha.security' lint_reports/clang_tidy.txt 2>/dev/null || true; }) + if [ "$sec_count" -gt 0 ]; then + echo "#### ⚠️ Security Findings ($sec_count)" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + { grep 'clang-analyzer-security\|clang-analyzer-alpha.security' lint_reports/clang_tidy.txt || true; } | head -20 >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + fi + # Top 10 files + echo "#### Top 10 Files by Warning Count" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + { grep 'warning:' lint_reports/clang_tidy.txt 2>/dev/null || true; } \ + | { grep -oP '[^ ]+\.(cpp|h)' || true; } \ + | sort | uniq -c | sort -rn | head -10 \ + | while read -r cnt fname; do + echo "- **$cnt** — $fname" >> "$GITHUB_STEP_SUMMARY" + done + echo "" >> "$GITHUB_STEP_SUMMARY" + else + echo "✅ No clang-tidy findings." >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + fi + echo "✔ Report generated." >> "$GITHUB_STEP_SUMMARY" + - name: Upload combined reports + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 + with: + name: lint-reports + path: lint_reports/ + if-no-files-found: warn diff --git a/.github/workflows/ci-pr-unix.yml b/.github/workflows/ci-pr-unix.yml index e7f92589b..42bee1ab6 100644 --- a/.github/workflows/ci-pr-unix.yml +++ b/.github/workflows/ci-pr-unix.yml @@ -5,11 +5,11 @@ # https://color.org # # -# Intent: Unix/Linux and macOS multi-compiler build and test pipeline for PR validation +# Intent: Unix/Linux and macOS multi-compiler build & test # -# Last Updated: 14-DEC-2025 1810Z by David Hoyt -# Refactor workflow with sanitization -# Improved job names +# Last Updated: 2026-02-17 16:24:41 UTC by David Hoyt +# PATH Changes +# # # # @@ -167,7 +167,7 @@ jobs: git config --add safe.directory "$PWD" git config --global credential.helper "" unset GITHUB_TOKEN || true - brew install cmake llvm wxwidgets libpng libtiff libxml2 nlohmann-json + brew install cmake llvm wxwidgets libpng libtiff libxml2 nlohmann-json jpeg-turbo - name: Set Compiler Environment Variables shell: bash --noprofile --norc {0} @@ -235,10 +235,10 @@ jobs: echo "=== Updating PATH ===" for d in ../Build/Tools/*; do [ -d "$d" ] && export PATH="$(realpath "$d"):$PATH" - done + done sh CreateAllProfiles.sh sh RunTests.sh - find . -iname "*.icc" | wc -l + find . -iname "*.icc" | wc -l echo "Build complete." - name: Generate PR Unix Summary Report diff --git a/.github/workflows/ci-pr-win.yml b/.github/workflows/ci-pr-win.yml index 86267df54..32e844810 100644 --- a/.github/workflows/ci-pr-win.yml +++ b/.github/workflows/ci-pr-win.yml @@ -7,17 +7,8 @@ # # Intent: Windows MSVC build and test pipeline for PR validation # -# Last Updated: 16-DEC-2025 0223Z -# Add PowerShell sanitization tests -# -# -# -# -# -# -# -# -# +# Last Updated: 2026-02-17 16:22:35 UTC by David Hoyt +# Add More Sanity Checks # # # @@ -70,19 +61,16 @@ jobs: Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' - # --- Disable telemetry sources (pwsh / dotnet / gh) --- $env:POWERSHELL_TELEMETRY_OPTOUT = "1" $env:DOTNET_CLI_TELEMETRY_OPTOUT = "1" $env:GH_NO_UPDATE_NOTIFIER = "1" $env:GH_PROMPT_DISABLED = "1" $env:NUGET_XMLDOC_MODE = "skip" - # --- Validate workspace path (defense-in-depth) --- if (-not (Test-Path $env:GITHUB_WORKSPACE)) { throw "Invalid GITHUB_WORKSPACE path: $env:GITHUB_WORKSPACE" } - # --- Remove tokens or UCI that should not exist in PR CI --- foreach ($v in @( 'GITHUB_TOKEN', @@ -94,7 +82,6 @@ jobs: Remove-Item "Env:$v" -ErrorAction SilentlyContinue } } - # --- Canonicalization then sanitize user controllable input --- function Canonicalize([string]$s) { if (-not $s) { return "" } @@ -103,16 +90,13 @@ jobs: $s = -join ($s.ToCharArray() | Where-Object { [int]$_ -ge 32 }) return $s } - # --- Canonicalize PR-controlled environment values --- $env:GITHUB_REF = Canonicalize $env:GITHUB_REF $env:GITHUB_HEAD_REF = Canonicalize $env:GITHUB_HEAD_REF $env:GITHUB_BASE_REF = Canonicalize $env:GITHUB_BASE_REF - # --- Harden Git configuration --- git config --global --add safe.directory "$env:GITHUB_WORKSPACE" git config --global credential.helper "" - # --- PATH hardening: limit to trusted tool locations --- $trustedPath = @( "$env:SystemRoot\system32", @@ -121,7 +105,6 @@ jobs: "$env:ProgramFiles\PowerShell\7" ) -join ';' $env:PATH = $trustedPath - Write-Host "Finished Harden Environment and Validate Inputs" -ForegroundColor Green - name: Test Sanitization Functions @@ -132,15 +115,12 @@ jobs: Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' - Write-Host "========================================" -ForegroundColor Cyan Write-Host "Testing Sanitization Functions" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" - # Source the sanitization functions . .github/scripts/sanitize.ps1 - # Quick validation tests $tests = @( @{Name="XSS prevention"; Input=""; Expected="<script>alert(1)</script>"}, @@ -150,10 +130,8 @@ jobs: @{Name="Null bytes"; Input="test$([char]0x00)null"; Expected="testnull"}, @{Name="CR/LF"; Input="line1`r`nline2"; Expected="line1 line2"} ) - $pass = 0 $fail = 0 - foreach ($test in $tests) { $result = Sanitize-Line -InputString $test.Input if ($result -eq $test.Expected) { @@ -166,20 +144,17 @@ jobs: $fail++ } } - Write-Host "" Write-Host "Results: $pass passed, $fail failed" - if ($fail -gt 0) { throw "Sanitization tests failed" } - Write-Host "" Write-Host "✅ All sanitization functions validated" -ForegroundColor Green Write-Host "" - name: Setup MSBuild - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 - name: Print PR Information shell: pwsh @@ -228,7 +203,7 @@ jobs: } pwd cd Build/Cmake - Write-Host "========= Configuring iccDEV ... ================`n" + Write-Host "========= Configuring iccDEV ... ================`n" cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="..\..\scripts\buildsystems\vcpkg.cmake" -DVCPKG_MANIFEST_MODE=OFF -DCMAKE_BUILD_TYPE=Debug -Wno-dev - name: Build iccDEV @@ -244,9 +219,9 @@ jobs: } pwd cd Build/Cmake - Write-Host "========= Building iccDEV ... ================`n" + Write-Host "========= Building iccDEV ... ================`n" + cmake --build build -- /m /maxcpucount cmake --build build -- /m /maxcpucount - cmake --build build -- /m /maxcpucount - name: Create Profiles shell: pwsh @@ -261,7 +236,7 @@ jobs: } pwd cd Build/Cmake - Write-Host "========= Creating Profiles ... ================`n" + Write-Host "========= Creating Profiles ... ================`n" $exeDirs = Get-ChildItem -Recurse -File -Include *.exe -Path .\build\ | Where-Object { $_.FullName -match 'icc' -and $_.FullName -notmatch '\\CMakeFiles\\' -and $_.Name -notmatch '^CMake(C|CXX)CompilerId\.exe$' } | ForEach-Object { Split-Path $_.FullName -Parent } | @@ -296,25 +271,19 @@ jobs: # Collect .icc profile information $profiles = Get-ChildItem -Path . -Filter "*.icc" -Recurse -File $totalCount = $profiles.Count - # Group profiles by directory $groupedProfiles = $profiles | Group-Object { $_.Directory.FullName } - # Generate Summary Report Write-Host "`n=========================" Write-Host " ICC Profile Report" Write-Host "=========================" - # Print count per subdirectory foreach ($group in $groupedProfiles) { Write-Host ("{0}: {1} .icc profiles" -f $group.Name, $group.Count) } - Write-Host "`nTotal .icc profiles found: $totalCount" Write-Host "=========================`n" - - Write-Host "All Done!" - + Write-Host "All Done!" - name: GitHub Summary if: always() shell: pwsh diff --git a/.github/workflows/ci-wasm-build-test.yml b/.github/workflows/ci-wasm-build-test.yml new file mode 100644 index 000000000..60749e0bf --- /dev/null +++ b/.github/workflows/ci-wasm-build-test.yml @@ -0,0 +1,271 @@ +############################################################### +# +# Copyright (c) 2026 International Color Consortium. +# All rights reserved. +# https://color.org +# +# Intent: WASM build and validation matrix (Release, Debug, ASAN) +# +# Last Modified: 2026-02-17 16:26:02 UTC by David Hoyt +# +# Based on: wasm-latest-matrix.yml (known good pattern) +# Adapted: For cfl/ integrated CMake build with hardened flags +# +# Pattern: Build third-party deps with emscripten, then build +# iccDEV libraries and tools as WASM, validate output +# +############################################################### + +name: WASM Build & Test Matrix + +permissions: + contents: read + +on: + workflow_dispatch: + pull_request: + branches: [ master, cfl ] + +concurrency: + group: wasm-build-${{ github.ref }} + cancel-in-progress: true + +jobs: + wasm-build: + name: "WASM • ${{ matrix.build_type }}" + runs-on: ubuntu-24.04 + timeout-minutes: 30 + strategy: + matrix: + build_type: [Release, Debug, Asan] + fail-fast: false + defaults: + run: + shell: bash --noprofile --norc {0} + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Install Emscripten SDK + run: | + set -euo pipefail + git clone --depth 1 https://github.com/emscripten-core/emsdk.git + cd emsdk + ./emsdk install latest + ./emsdk activate latest + + - name: Build third-party dependencies + run: | + set -euo pipefail + source emsdk/emsdk_env.sh + + THIRD_PARTY="$PWD/third_party" + mkdir -p "$THIRD_PARTY" && cd "$THIRD_PARTY" + + # zlib + git clone --depth 1 https://github.com/madler/zlib.git && cd zlib + emconfigure ./configure --static --prefix="$THIRD_PARTY/zlib/out" + emmake make -j$(nproc) + emmake make install + cd "$THIRD_PARTY" + + # libpng (checksum verified) + curl -sLO https://download.sourceforge.net/libpng/libpng-1.6.51.tar.gz + echo "ac25cafc2054cda3f6f0fe22ee9fc587024b99e01d03bd72b765824e48f39021 libpng-1.6.51.tar.gz" | sha256sum -c - + tar -xzf libpng-1.6.51.tar.gz && mv libpng-1.6.51 libpng && cd libpng + CPPFLAGS="-I$THIRD_PARTY/zlib" LDFLAGS="-L$THIRD_PARTY/zlib/out/lib" \ + emconfigure ./configure --disable-shared --enable-static --prefix="$THIRD_PARTY/libpng/out" + emmake make -j$(nproc) + emmake make install + cd "$THIRD_PARTY" + + # libjpeg (checksum verified) + curl -sLO https://ijg.org/files/jpegsrc.v9e.tar.gz + echo "4077d6a6a75aeb01884f708919d25934c93305e49f7e3f36db9129320e6f4f3d jpegsrc.v9e.tar.gz" | sha256sum -c - + tar -xzf jpegsrc.v9e.tar.gz && mv jpeg-9e libjpeg && cd libjpeg + emconfigure ./configure --prefix="$THIRD_PARTY/libjpeg/out" --disable-shared --enable-static + emmake make -j$(nproc) + emmake make install + cd "$THIRD_PARTY" + + # libtiff + git clone --depth 1 https://gitlab.com/libtiff/libtiff.git && cd libtiff + mkdir wasm && cd wasm + emcmake cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$THIRD_PARTY/libtiff/out" \ + -DZLIB_INCLUDE_DIR="$THIRD_PARTY/zlib" \ + -DZLIB_LIBRARY="$THIRD_PARTY/zlib/out/lib/libz.a" \ + -DJPEG_INCLUDE_DIR="$THIRD_PARTY/libjpeg/out/include" \ + -DJPEG_LIBRARY="$THIRD_PARTY/libjpeg/out/lib/libjpeg.a" + emmake make -j$(nproc) + emmake make install + cd "$THIRD_PARTY" + + # libxml2 + git clone --depth 1 https://gitlab.gnome.org/GNOME/libxml2.git && cd libxml2 + emconfigure ./autogen.sh --without-python --disable-shared --enable-static --prefix="$THIRD_PARTY/libxml2/out" + emmake make -j$(nproc) + emmake make install + cd "$THIRD_PARTY" + + # nlohmann-json (build with CMake for find_package support) + git clone --depth 1 https://github.com/nlohmann/json.git nlohmann-json && cd nlohmann-json + mkdir wasm && cd wasm + emcmake cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$THIRD_PARTY/nlohmann-json/out" \ + -DJSON_BuildTests=OFF \ + -DJSON_Install=ON + emmake make -j$(nproc) + emmake make install + cd "$THIRD_PARTY" + + echo "### Third-party dependencies built" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ls -d "$THIRD_PARTY"/*/out 2>/dev/null >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + - name: Configure and build iccDEV WASM + run: | + set -euo pipefail + source emsdk/emsdk_env.sh + + THIRD_PARTY="$PWD/third_party" + + # Determine build type and extra flags + BUILD_FLAGS="" + BUILD_TYPE="${{ matrix.build_type }}" + if [[ "$BUILD_TYPE" == "Asan" ]]; then + BUILD_FLAGS="-fsanitize=address" + BUILD_TYPE="Debug" + fi + + # wxWidgets not available for WASM — no action needed (not in top-level CMakeLists) + # Disable RELRO/NOW linker hardening (not supported by wasm-ld) + sed -i 's/-Wl,-z,relro,-z,now//' Build/Cmake/CMakeLists.txt + + mkdir -p build-wasm && cd build-wasm + emcmake cmake ../Build/Cmake \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ + -DENABLE_TOOLS=ON \ + -DENABLE_STATIC_LIBS=ON \ + -DENABLE_SHARED_LIBS=OFF \ + -DENABLE_TESTS=OFF \ + -DLIBXML2_INCLUDE_DIR="$THIRD_PARTY/libxml2/out/include/libxml2" \ + -DLIBXML2_LIBRARY="$THIRD_PARTY/libxml2/out/lib/libxml2.a" \ + -DTIFF_INCLUDE_DIR="$THIRD_PARTY/libtiff/out/include" \ + -DTIFF_LIBRARY="$THIRD_PARTY/libtiff/out/lib/libtiff.a" \ + -DJPEG_INCLUDE_DIR="$THIRD_PARTY/libjpeg/out/include" \ + -DJPEG_LIBRARY="$THIRD_PARTY/libjpeg/out/lib/libjpeg.a" \ + -DPNG_PNG_INCLUDE_DIR="$THIRD_PARTY/libpng/out/include" \ + -DPNG_INCLUDE_DIR="$THIRD_PARTY/libpng/out/include" \ + -DPNG_LIBRARY="$THIRD_PARTY/libpng/out/lib/libpng16.a" \ + -DZLIB_INCLUDE_DIR="$THIRD_PARTY/zlib" \ + -DZLIB_LIBRARY="$THIRD_PARTY/zlib/out/lib/libz.a" \ + -Dnlohmann_json_DIR="$THIRD_PARTY/nlohmann-json/out/share/cmake/nlohmann_json" \ + -DCMAKE_CXX_FLAGS=" \ + $BUILD_FLAGS \ + -I$THIRD_PARTY/libtiff/out/include \ + -I$THIRD_PARTY/libjpeg/out/include \ + -I$THIRD_PARTY/libpng/out/include \ + -I$THIRD_PARTY/zlib \ + -I$THIRD_PARTY/nlohmann-json/out/include" \ + -DCMAKE_EXE_LINKER_FLAGS=" \ + $THIRD_PARTY/libjpeg/out/lib/libjpeg.a \ + $THIRD_PARTY/libpng/out/lib/libpng16.a \ + $THIRD_PARTY/zlib/out/lib/libz.a \ + -s INITIAL_MEMORY=128MB \ + -s ALLOW_MEMORY_GROWTH=1 \ + -s FORCE_FILESYSTEM=1 \ + -s MODULARIZE=1 \ + -s EXPORT_NAME=createModule \ + -s EXPORTED_RUNTIME_METHODS=['FS','callMain']" \ + -Wno-dev + + emmake make -j$(nproc) + + # Report results + JS_COUNT=$(find . -name '*.js' -not -path '*/CMakeFiles/*' | wc -l) + WASM_COUNT=$(find . -name '*.wasm' | wc -l) + LIB_COUNT=$(find . -name '*.a' | wc -l) + + { + echo "### WASM Build: ${{ matrix.build_type }}" + echo "| Metric | Value |" + echo "|--------|-------|" + echo "| JS modules | $JS_COUNT |" + echo "| WASM binaries | $WASM_COUNT |" + echo "| Static libraries | $LIB_COUNT |" + echo "" + echo "#### JS Modules" + echo '```' + find . -name '*.js' -not -path '*/CMakeFiles/*' -exec ls -lh {} \; + echo '```' + echo "#### WASM Binaries" + echo '```' + find . -name '*.wasm' -exec ls -lh {} \; + echo '```' + } >> $GITHUB_STEP_SUMMARY + + - name: Validate WASM output + run: | + set -euo pipefail + source emsdk/emsdk_env.sh + + cd build-wasm + + # Verify .wasm files exist and are valid + WASM_FILES=$(find . -name '*.wasm' -type f) + if [ -z "$WASM_FILES" ]; then + echo "::error::No WASM files produced" + exit 1 + fi + + # Validate each .wasm file header (magic number: \0asm) + VALID=0 + INVALID=0 + for wf in $WASM_FILES; do + MAGIC=$(xxd -l 4 -p "$wf" 2>/dev/null) + if [ "$MAGIC" = "0061736d" ]; then + SIZE=$(stat -c%s "$wf") + echo "VALID: $(basename $wf) ($SIZE bytes)" + VALID=$((VALID + 1)) + else + echo "INVALID: $(basename $wf) (magic: $MAGIC)" + INVALID=$((INVALID + 1)) + fi + done + + echo "### Validation: $VALID valid, $INVALID invalid" >> $GITHUB_STEP_SUMMARY + + if [ $INVALID -gt 0 ]; then + echo "::error::$INVALID invalid WASM files found" + exit 1 + fi + + # Run a quick smoke test with node if iccDumpProfile exists + DUMP_JS=$(find . -name "iccDumpProfile.js" -not -path '*/CMakeFiles/*' | head -1) + if [ -n "$DUMP_JS" ] && [ -f "$DUMP_JS" ]; then + echo "Smoke test: iccDumpProfile --help" + set +e + node "$DUMP_JS" 2>&1 | head -5 + echo "iccDumpProfile WASM smoke test complete" + set -e + fi + + - name: Upload WASM artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: wasm-${{ matrix.build_type }} + path: | + build-wasm/Tools/**/*.js + build-wasm/Tools/**/*.wasm + build-wasm/IccProfLib/*.a + build-wasm/IccXML/*.a + retention-days: 7 + if-no-files-found: warn diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 67f9bf312..f6dac8d0d 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -1,36 +1,32 @@ ############################################################### # -## Copyright (©) 2025 International Color Consortium. -## All rights reserved. -## https://color.org -# -# -## Intent: Apply file-pattern-based labels to PRs using actions/labeler -# -## Last Updated: 28-NOV-2025 2300Z by David Hoyt -## Added user-controllable input (uci) checks to validate PR inputs -## TODO: Push binary releases, tags etc.. -# -## Comment: Known good and working from PatchIccMax +# Copyright (©) 2025 International Color Consortium. +# All rights reserved. +# https://color.org # # +# Intent: Apply file-pattern-based labels to PRs using actions/labeler # +# Last Updated: 2026-02-17 16:30:16 UTC by David Hoyt +# Remove labeled trigger (loop risk), add workflow_dispatch, # # ############################################################### - + name: Labeler +permissions: + contents: read + pull-requests: write + on: pull_request: - types: [opened, synchronize, reopened, labeled] + types: [opened, synchronize, reopened] + workflow_dispatch: jobs: apply-labels: runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write steps: - name: Apply PR labels diff --git a/.github/workflows/update-labels.yml b/.github/workflows/update-labels.yml index 85aae6131..30d04a440 100644 --- a/.github/workflows/update-labels.yml +++ b/.github/workflows/update-labels.yml @@ -1,21 +1,15 @@ ############################################################### # -## Copyright (©) 2025 International Color Consortium. -## All rights reserved. +## Copyright (©) 2025 International Color Consortium. +## All rights reserved. ## https://color.org # # -## Intent: PR Status Labeler (update-labels.yml) - Labels PRs based on CI results -# -## Last Updated: 28-NOV-2025 2300Z by David Hoyt -## Added user-controllable input (uci) checks -## TODO: Push binary releases, tags etc.. -# -## Comment: Known good and working from PatchIccMax -# -# -# +## Intent: PR Status Labeler - Labels PRs based on CI results # +## Last Updated: 2026-02-17 16:30:55 UTC by David Hoyt +# Batch schedule over all open PRs, ensure labels +# exist, concurrency, sanitizer, checks:read perm. # ############################################################### @@ -25,112 +19,250 @@ on: workflow_dispatch: inputs: pr_number: - description: 'Pull request number to process manually' + description: 'Pull request number to process (blank = all open)' required: false default: '' type: string schedule: - cron: '0 */4 * * *' pull_request: - types: [opened, synchronize, reopened, labeled, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review] permissions: contents: read pull-requests: write - statuses: read # Explicitly added for clarity; required to read commit status + statuses: read + checks: read + +concurrency: + group: pr-status-labeler-${{ github.event.pull_request.number || 'batch' }} + cancel-in-progress: true jobs: label-status: - # Only allow manual runs from the repo owner; PR/schedule events still allowed if: > github.event_name != 'workflow_dispatch' || contains(fromJson('["xsscx","maxderhak","ChrisCoxArt","dwtza"]'), github.actor) runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Check out repository uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 + with: + sparse-checkout: .github/scripts + persist-credentials: false + + - name: Ensure status labels exist + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + + gh label create "passed" --color "0e8a16" --description "All CI checks passed" 2>/dev/null || true + gh label create "failed" --color "d93f0b" --description "One or more CI checks failed" 2>/dev/null || true + gh label create "pending" --color "fbca04" --description "CI checks still running" 2>/dev/null || true - - name: Determine PR number or input - id: get_pr + - name: Build PR list + id: pr_list + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + INPUT="${{ github.event.inputs.pr_number }}" EVENT_PR="${{ github.event.pull_request.number }}" + if [[ "$INPUT" =~ ^[0-9]+$ ]] && [[ -n "$INPUT" ]]; then - echo "pr_number=$INPUT" >> "$GITHUB_OUTPUT" + echo "prs=$INPUT" >> "$GITHUB_OUTPUT" + echo "mode=single" >> "$GITHUB_OUTPUT" + echo "Processing single PR: #$INPUT" elif [[ "$EVENT_PR" =~ ^[0-9]+$ ]]; then - echo "pr_number=$EVENT_PR" >> "$GITHUB_OUTPUT" + echo "prs=$EVENT_PR" >> "$GITHUB_OUTPUT" + echo "mode=single" >> "$GITHUB_OUTPUT" + echo "Processing PR from event: #$EVENT_PR" else - echo "pr_number=0" >> "$GITHUB_OUTPUT" + # Schedule / dispatch with no number → scan all open PRs + OPEN_PRS=$(gh pr list --state open --json number -q '.[].number' | tr '\n' ' ') + if [[ -z "$OPEN_PRS" ]]; then + echo "prs=" >> "$GITHUB_OUTPUT" + echo "mode=none" >> "$GITHUB_OUTPUT" + echo "No open PRs found — nothing to do" + else + echo "prs=$OPEN_PRS" >> "$GITHUB_OUTPUT" + echo "mode=batch" >> "$GITHUB_OUTPUT" + echo "Processing open PRs: $OPEN_PRS" + fi fi - - name: Evaluate PR combined status - id: status - if: steps.get_pr.outputs.pr_number != '0' + - name: Evaluate and label PRs + id: process + if: steps.pr_list.outputs.prs != '' + shell: bash --noprofile --norc {0} env: + BASH_ENV: /dev/null GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - PR="${{ steps.get_pr.outputs.pr_number }}" + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" - # Ensure PR belongs to this repository - BASE_REPO=$(gh pr view "$PR" --json baseRepository -q .baseRepository.nameWithOwner 2>/dev/null || echo "") - if [[ -z "$BASE_REPO" || "$BASE_REPO" != "${GITHUB_REPOSITORY}" ]]; then - echo "status=unknown" >> "$GITHUB_OUTPUT" - exit 0 - fi + RESULTS_FILE=$(mktemp) + PROCESSED=0 + LABELED=0 - HEAD=$(gh pr view "$PR" --json headRefOid -q .headRefOid 2>/dev/null || echo "") - if [[ -z "$HEAD" ]]; then - echo "status=unknown" >> "$GITHUB_OUTPUT" - exit 0 - fi + evaluate_pr() { + local pr="$1" - STATE=$(gh api "repos/${{ github.repository }}/commits/$HEAD/status" \ - --jq '.state' 2>/dev/null || echo "unknown") + # Validate PR belongs to this repo (not a cross-repo fork PR) + local is_cross + is_cross=$(gh pr view "$pr" --json isCrossRepository -q .isCrossRepository 2>/dev/null || echo "true") + if [[ "$is_cross" == "true" ]]; then + echo "$pr|skipped|Cross-repository PR" >> "$RESULTS_FILE" + return + fi - echo "status=$STATE" >> "$GITHUB_OUTPUT" - echo "Detected status: $STATE" + local head + head=$(gh pr view "$pr" --json headRefOid -q .headRefOid 2>/dev/null || echo "") + if [[ -z "$head" ]]; then + echo "$pr|skipped|Could not determine HEAD SHA" >> "$RESULTS_FILE" + return + fi - - name: Relabel PR based on status - id: relabel - if: steps.get_pr.outputs.pr_number != '0' && steps.status.outputs.status != '' && steps.status.outputs.status != 'unknown' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR="${{ steps.get_pr.outputs.pr_number }}" - STATE="${{ steps.status.outputs.status }}" - ADDED="" - REMOVED="" - - case "$STATE" in - success) - gh pr edit "$PR" --add-label "passed" --remove-label "failed" || true - ADDED="passed" - REMOVED="failed" - ;; - failure|error) - gh pr edit "$PR" --add-label "failed" --remove-label "passed" || true - ADDED="failed" - REMOVED="passed" - ;; - *) - echo "No label change required for status: $STATE" - ;; - esac - - echo "added_label=$ADDED" >> "$GITHUB_OUTPUT" - echo "removed_label=$REMOVED" >> "$GITHUB_OUTPUT" + # Query commit statuses API + local state + state=$(gh api "repos/${{ github.repository }}/commits/$head/status" \ + --jq '.state' 2>/dev/null || echo "unknown") + + # Query Check Runs API (Actions uses this, not statuses) + local check_conclusion + check_conclusion=$(gh api "repos/${{ github.repository }}/commits/$head/check-runs" \ + --jq ' + if (.total_count == 0) then "none" + else + [.check_runs[] | select(.name != "label-status") | .conclusion // "pending"] + | if length == 0 then "none" + elif all(. == "success") then "success" + elif any(. == "failure") then "failure" + elif any(. == "pending" or . == "null" or . == null) then "pending" + else "unknown" end + end' 2>/dev/null || echo "unknown") + + # Combine results + local combined + if [[ "$state" == "failure" || "$check_conclusion" == "failure" ]]; then + combined="failure" + elif [[ "$state" == "success" && "$check_conclusion" == "success" ]]; then + combined="success" + elif [[ "$state" == "pending" || "$check_conclusion" == "pending" ]]; then + combined="pending" + elif [[ "$check_conclusion" == "none" && "$state" == "pending" ]]; then + combined="pending" + elif [[ "$check_conclusion" == "none" && "$state" == "unknown" ]]; then + # No checks at all yet — treat as pending + combined="pending" + else + combined="$state" + fi + + # Apply label + local added="" removed="" + case "$combined" in + success) + gh pr edit "$pr" --add-label "passed" --remove-label "failed,pending" 2>/dev/null || true + added="passed"; removed="failed,pending" + ;; + failure|error) + gh pr edit "$pr" --add-label "failed" --remove-label "passed,pending" 2>/dev/null || true + added="failed"; removed="passed,pending" + ;; + pending) + gh pr edit "$pr" --add-label "pending" --remove-label "passed,failed" 2>/dev/null || true + added="pending"; removed="passed,failed" + ;; + esac + + echo "$pr|$combined|status=$state checks=$check_conclusion → added=$added removed=$removed" >> "$RESULTS_FILE" + if [[ -n "$added" ]]; then + LABELED=$((LABELED + 1)) + fi + } + + for pr in ${{ steps.pr_list.outputs.prs }}; do + echo "── PR #$pr ──" + evaluate_pr "$pr" + PROCESSED=$((PROCESSED + 1)) + done + + echo "processed=$PROCESSED" >> "$GITHUB_OUTPUT" + echo "labeled=$LABELED" >> "$GITHUB_OUTPUT" + # Pass results file path for summary step + cp "$RESULTS_FILE" "${GITHUB_WORKSPACE}/pr_results.txt" - name: Generate summary report + if: always() + shell: bash --noprofile --norc {0} + env: + BASH_ENV: /dev/null run: | + set -euo pipefail + git config --add safe.directory "$PWD" + git config --global credential.helper "" + unset GITHUB_TOKEN || true + + SANITIZER=".github/scripts/sanitize-sed.sh" + if [[ -f "$SANITIZER" ]]; then + # shellcheck disable=SC1090 + source "$SANITIZER" + else + escape_html() { + local s="$1" + s="${s//&/&}" + s="${s///>}" + s="${s//\"/"}" + s="${s//\'/'}" + printf '%s' "$s" + } + sanitize_line() { escape_html "$1"; } + fi + + MODE="${{ steps.pr_list.outputs.mode }}" + { - echo "### 🧾 PR Status Labeler Summary" + echo "### 🏷️ PR Status Labeler Summary" echo "" - echo "**Trigger Type:** ${{ github.event_name }}" - echo "**PR Number:** ${{ steps.get_pr.outputs.pr_number }}" - echo "**Detected Status:** ${{ steps.status.outputs.status }}" - echo "**Label Added:** ${{ steps.relabel.outputs.added_label }}" - echo "**Label Removed:** ${{ steps.relabel.outputs.removed_label }}" + echo "| Field | Value |" + echo "|-------|-------|" + echo "| Trigger | \`${{ github.event_name }}\` |" + echo "| Mode | $MODE |" + echo "| PRs Processed | ${{ steps.process.outputs.processed || '0' }} |" + echo "| Labels Applied | ${{ steps.process.outputs.labeled || '0' }} |" echo "" - echo "✅ Workflow completed." } >> "$GITHUB_STEP_SUMMARY" + + if [[ "$MODE" == "none" ]]; then + echo "🟦 No open PRs — nothing to label." >> "$GITHUB_STEP_SUMMARY" + elif [[ -f "${GITHUB_WORKSPACE}/pr_results.txt" ]]; then + { + echo "#### Per-PR Results" + echo "" + echo "| PR | Status | Details |" + echo "|----|--------|---------|" + } >> "$GITHUB_STEP_SUMMARY" + while IFS='|' read -r pr status details; do + safe_details=$(sanitize_line "$details") + echo "| [#${pr}](https://github.com/${{ github.repository }}/pull/${pr}) | \`${status}\` | ${safe_details} |" >> "$GITHUB_STEP_SUMMARY" + done < "${GITHUB_WORKSPACE}/pr_results.txt" + echo "" >> "$GITHUB_STEP_SUMMARY" + fi + + echo "✅ Workflow completed." >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/wasm-latest-matrix.yml b/.github/workflows/wasm-latest-matrix.yml index 8bdc6dc55..a4090cb6c 100644 --- a/.github/workflows/wasm-latest-matrix.yml +++ b/.github/workflows/wasm-latest-matrix.yml @@ -1,14 +1,14 @@ ############################################################### # -## Copyright (©) 2025 International Color Consortium. -## All rights reserved. -## https://color.org +# Copyright (©) 2025 International Color Consortium. +# All rights reserved. +# https://color.org # # -## Intent: iccDEV wasm-latest-matrix +# Intent: iccDEV wasm-latest-matrix # -## Last Updated: 02-DEC-2025 2100Z by David Hoyt -## Added user-controllable input (uci) checks +# Last Updated: 2026-02-17 16:29:28 UTC by David Hoyt +# Added user-controllable input (uci) checks # # # @@ -39,7 +39,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Harden Bash Environment shell: bash @@ -94,12 +94,14 @@ jobs: emmake make install cd .. curl -LO https://download.sourceforge.net/libpng/libpng-1.6.51.tar.gz + echo "ac25cafc2054cda3f6f0fe22ee9fc587024b99e01d03bd72b765824e48f39021 libpng-1.6.51.tar.gz" | sha256sum -c - tar -xzf libpng-1.6.51.tar.gz && mv libpng-1.6.51 libpng && cd libpng CPPFLAGS="-I$(pwd)/../zlib" LDFLAGS="-L$(pwd)/../zlib/out/lib" emconfigure ./configure --disable-shared --enable-static --prefix=$(pwd)/out emmake make -j"$(nproc)" emmake make install cd .. curl -LO https://ijg.org/files/jpegsrc.v9e.tar.gz + echo "4077d6a6a75aeb01884f708919d25934c93305e49f7e3f36db9129320e6f4f3d jpegsrc.v9e.tar.gz" | sha256sum -c - tar -xzf jpegsrc.v9e.tar.gz && mv jpeg-9e libjpeg && cd libjpeg emconfigure ./configure --prefix=$(pwd)/out --disable-shared --enable-static emmake make -j"$(nproc)" @@ -121,7 +123,8 @@ jobs: emmake make install cd .. mkdir -p nlohmann/json/include - curl -L https://github.com/nlohmann/json/releases/latest/download/json.hpp -o nlohmann/json/include/json.hpp + curl -L https://github.com/nlohmann/json/releases/download/v3.11.3/json.hpp -o nlohmann/json/include/json.hpp + echo "aaf127c04cb31c406e5b04a63f1ae89369fccde6d8fa7cdda1ed4f32dfc5de63 nlohmann/json/include/json.hpp" | sha256sum -c - cd .. - name: Configure and Build iccDEV-WASM shell: bash @@ -173,12 +176,12 @@ jobs: emmake make -j"$(nproc)" find . -type f \( -name '*.js' -o -name '*.wasm' \) -ls - name: Upload WASM Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: wasm-artifacts-${{ matrix.build_type }} path: | iccDEV/Build/Tools/* Build LICENSE.md - README.md - docs/** \ No newline at end of file + README.md + docs/** diff --git a/Build/Cmake/CMakeLists.txt b/Build/Cmake/CMakeLists.txt index ebb8499cd..9a3e0024f 100644 --- a/Build/Cmake/CMakeLists.txt +++ b/Build/Cmake/CMakeLists.txt @@ -3,9 +3,9 @@ # Copyright (C) 2024-2026 The International Color Consortium. # All rights reserved. # -# Last Updated: 03-FEB-2026 at 2346Z by David Hoyt +# Last Updated: 2026-02-17 16:31:50 UTC by David Hoyt # -# Changes: Modify Version String to v2.3.1.4 References (#579) +# Changes: Add WASM Toolchain, CFL & libFuzzer # ################################################################################# @@ -77,16 +77,16 @@ endif() # ---------------------------------------------------------------------- # Compiler Selection for Unix (Clang preferred, GCC fallback) +# Skip when cross-compiling with Emscripten (toolchain file sets em++/emcc) # ---------------------------------------------------------------------- -if(UNIX AND NOT APPLE) +if(UNIX AND NOT APPLE AND NOT EMSCRIPTEN) # Check if compiler was specified via environment or command line if(NOT DEFINED CMAKE_C_COMPILER OR NOT DEFINED CMAKE_CXX_COMPILER) if(NOT DEFINED ENV{CC} AND NOT DEFINED ENV{CXX}) # No user preference - auto-detect with Clang preferred find_program(CLANG_C_COMPILER NAMES clang-18 clang-17 clang-16 clang-15 clang-14 clang) find_program(CLANG_CXX_COMPILER NAMES clang++-18 clang++-17 clang++-16 clang++-15 clang++-14 clang++) - if(CLANG_C_COMPILER AND CLANG_CXX_COMPILER) set(CMAKE_C_COMPILER "${CLANG_C_COMPILER}" CACHE FILEPATH "C compiler" FORCE) set(CMAKE_CXX_COMPILER "${CLANG_CXX_COMPILER}" CACHE FILEPATH "C++ compiler" FORCE) @@ -95,7 +95,6 @@ if(UNIX AND NOT APPLE) # Fallback to GCC find_program(GCC_C_COMPILER NAMES gcc-13 gcc-12 gcc-11 gcc-10 gcc) find_program(GCC_CXX_COMPILER NAMES g++-13 g++-12 g++-11 g++-10 g++) - if(GCC_C_COMPILER AND GCC_CXX_COMPILER) set(CMAKE_C_COMPILER "${GCC_C_COMPILER}" CACHE FILEPATH "C compiler" FORCE) set(CMAKE_CXX_COMPILER "${GCC_CXX_COMPILER}" CACHE FILEPATH "C++ compiler" FORCE) @@ -523,12 +522,13 @@ option(ICC_ENABLE_ASSERTS "Enable ICC_ASSERT traps and debug assertions" option(ICC_LOG_SAFE "Enable ICC_LOG_SAFE_VAL bounds-checked logging" OFF) option(ENABLE_SANITIZERS "Enable runtime sanitizers (ASan, UBSan, etc.)" OFF) option(ENABLE_ASAN "Enable AddressSanitizer only" OFF) +option(ENABLE_FUZZING "Enable libFuzzer harnesses (Clang only)" OFF) option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer only" OFF) option(ENABLE_TSAN "Enable ThreadSanitizer (conflicts with ASan)" OFF) option(ENABLE_MSAN "Enable MemorySanitizer (Clang only)" OFF) option(ENABLE_LSAN "Enable LeakSanitizer standalone" OFF) -option(ENABLE_FUZZING "Enable fuzzing instrumentation (libFuzzer)" OFF) option(ENABLE_COVERAGE "Enable code coverage instrumentation" OFF) +option(ENABLE_PROFILING "Enable gprof/perf profiling instrumentation" OFF) option(ENABLE_SPECTRE_MITIGATION "Enable /Qspectre on MSVC for Spectre V1 mitigation" OFF) option(ENABLE_USEICCDEVNAMESPACE "Use iccDEV namespace wrapping" OFF) @@ -574,6 +574,7 @@ message(STATUS "ENABLE_MSAN = ${ENABLE_MSAN}") message(STATUS "ENABLE_LSAN = ${ENABLE_LSAN}") message(STATUS "ENABLE_FUZZING = ${ENABLE_FUZZING}") message(STATUS "ENABLE_COVERAGE = ${ENABLE_COVERAGE}") +message(STATUS "ENABLE_PROFILING = ${ENABLE_PROFILING}") message(STATUS "ENABLE_SPECTRE_MITIGATION = ${ENABLE_SPECTRE_MITIGATION}") message(STATUS "") @@ -608,7 +609,6 @@ if(ENABLE_FUZZING OR ENABLE_SANITIZERS) if(ENABLE_LSAN) list(APPEND _ignored_options "ENABLE_LSAN") endif() - if(_ignored_options) string(REPLACE ";" ", " _ignored_str "${_ignored_options}") if(ENABLE_FUZZING) @@ -627,11 +627,9 @@ set(SANITIZER_LIST "") if(ENABLE_FUZZING) message(STATUS ">>> Fuzzing instrumentation enabled (libFuzzer + ASan + UBSan)") - if(MSVC) - message(WARNING "Fuzzing with libFuzzer is not supported on MSVC") - else() - list(APPEND SANITIZER_LIST "fuzzer" "address" "undefined") - endif() + message(STATUS ">>> Fuzzer harnesses live on the CFL branch") + # DO NOT apply fuzzer flags globally - causes linker conflicts with regular tools + # Fuzzer targets get flags via target_compile_options/target_link_options elseif(ENABLE_SANITIZERS) message(STATUS ">>> Sanitizers enabled: AddressSanitizer and UndefinedBehaviorSanitizer active") if(MSVC) @@ -650,7 +648,6 @@ else() list(APPEND SANITIZER_LIST "address") endif() endif() - if(ENABLE_UBSAN) message(STATUS ">>> UndefinedBehaviorSanitizer enabled") if(MSVC) @@ -659,7 +656,6 @@ else() list(APPEND SANITIZER_LIST "undefined") endif() endif() - if(ENABLE_TSAN) message(STATUS ">>> ThreadSanitizer enabled") if(MSVC) @@ -668,7 +664,6 @@ else() list(APPEND SANITIZER_LIST "thread") endif() endif() - if(ENABLE_MSAN) message(STATUS ">>> MemorySanitizer enabled (Clang only)") if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -680,7 +675,6 @@ else() list(APPEND SANITIZER_LIST "memory") endif() endif() - if(ENABLE_LSAN) message(STATUS ">>> LeakSanitizer enabled") if(MSVC) @@ -696,6 +690,11 @@ if(SANITIZER_LIST) string(REPLACE ";" "," SANITIZER_STRING "${SANITIZER_LIST}") list(APPEND SANITIZER_FLAGS "-fsanitize=${SANITIZER_STRING}") list(APPEND SANITIZER_FLAGS "-fno-omit-frame-pointer") + # Make UndefinedBehaviorSanitizer violations fatal (abort on first UB) + list(FIND SANITIZER_LIST "undefined" _ubsan_idx) + if(NOT _ubsan_idx EQUAL -1) + list(APPEND SANITIZER_FLAGS "-fno-sanitize-recover=undefined") + endif() endif() # Apply all sanitizer flags @@ -705,23 +704,36 @@ if(SANITIZER_FLAGS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FINAL_SAN_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FINAL_SAN_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FINAL_SAN_FLAGS}") - if(VERBOSE_CONFIG) - message(STATUS ">>> Final sanitizer flags: ${FINAL_SAN_FLAGS}") - endif() + message(STATUS ">>> Final sanitizer flags: ${FINAL_SAN_FLAGS}") endif() # Apply coverage instrumentation flags if(ENABLE_COVERAGE) message(STATUS ">>> Coverage instrumentation enabled") - list(APPEND COVERAGE_FLAGS "--coverage") - string(JOIN " " FINAL_COV_FLAGS ${COVERAGE_FLAGS}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FINAL_COV_FLAGS}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FINAL_COV_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FINAL_COV_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FINAL_COV_FLAGS}") - if(VERBOSE_CONFIG) - message(STATUS ">>> Final coverage flags: ${FINAL_COV_FLAGS}") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Clang source-based coverage (preferred: precise, mergeable) + set(COVERAGE_COMPILE_FLAGS "-fprofile-instr-generate -fcoverage-mapping") + set(COVERAGE_LINK_FLAGS "-fprofile-instr-generate") + message(STATUS ">>> Using Clang source-based coverage (llvm-profdata/llvm-cov)") + else() + # GCC gcov-based coverage + set(COVERAGE_COMPILE_FLAGS "--coverage") + set(COVERAGE_LINK_FLAGS "--coverage") + message(STATUS ">>> Using GCC gcov-based coverage") endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILE_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILE_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${COVERAGE_LINK_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${COVERAGE_LINK_FLAGS}") +endif() + +# Apply profiling instrumentation flags +if(ENABLE_PROFILING) + message(STATUS ">>> Profiling instrumentation enabled (-pg)") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg") endif() # ---------------------------------------------------------------------- @@ -732,24 +744,22 @@ include(CheckIPOSupported) check_ipo_supported(RESULT ipo_supported OUTPUT ipo_output) if(ipo_supported) - if(CMAKE_BUILD_TYPE MATCHES "Release" AND NOT ENABLE_COVERAGE AND NOT SANITIZER_FLAGS) + if(CMAKE_BUILD_TYPE MATCHES "Release" AND NOT ENABLE_COVERAGE AND NOT ENABLE_PROFILING AND NOT SANITIZER_FLAGS) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) - if(VERBOSE_CONFIG) - message(STATUS ">>> Link-Time Optimization (LTO) enabled for Release builds") - endif() + message(STATUS ">>> Link-Time Optimization (LTO) enabled for Release builds") elseif(CMAKE_BUILD_TYPE MATCHES "Release") - if(VERBOSE_CONFIG) - if(ENABLE_COVERAGE) - message(STATUS ">>> Link-Time Optimization (LTO) disabled because ENABLE_COVERAGE is ON") - elseif(SANITIZER_FLAGS) - message(STATUS ">>> Link-Time Optimization (LTO) disabled because sanitizers are enabled") - endif() + set(_lto_reason "") + if(ENABLE_COVERAGE) + set(_lto_reason "ENABLE_COVERAGE is ON") + elseif(ENABLE_PROFILING) + set(_lto_reason "ENABLE_PROFILING is ON") + elseif(SANITIZER_FLAGS) + set(_lto_reason "sanitizers are enabled") endif() + message(STATUS ">>> Link-Time Optimization (LTO) disabled: ${_lto_reason}") endif() else() - if(VERBOSE_CONFIG) - message(STATUS ">>> Link-Time Optimization (LTO) not supported: ${ipo_output}") - endif() + message(STATUS ">>> Link-Time Optimization (LTO) not supported: ${ipo_output}") endif() # ---------------------------------------------------------------------- @@ -802,24 +812,19 @@ endif() if(MSVC) # Debug: CMake defaults only (no extra flags) # CMAKE_CXX_FLAGS_DEBUG uses CMake defaults - # Release: CMake defaults only (no extra flags) # CMAKE_CXX_FLAGS_RELEASE uses CMake defaults - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") message(STATUS "Configuring MSVC runtime and flags (CMake defaults)") message(STATUS " DEBUG: CMake defaults only") message(STATUS " RELEASE: CMake defaults only") message(STATUS " RELWITHDEBINFO: CMake defaults only") message(STATUS " MINSIZEREL: CMake defaults only") - elseif(UNIX) # Debug: CMake defaults only (no extra flags) # CMAKE_CXX_FLAGS_DEBUG uses CMake defaults - # Release: CMake defaults only (no extra flags) # CMAKE_CXX_FLAGS_RELEASE uses CMake defaults - message(STATUS "Configuring GCC/Clang runtime and flags (CMake defaults)") message(STATUS " DEBUG: CMake defaults only") message(STATUS " RELEASE: CMake defaults only") @@ -906,23 +911,28 @@ elseif(UNIX AND NOT APPLE) # Recommended flags for Linux/Clang or GCC add_compile_options( -Wall + -Wextra + -Wformat + -Wformat-security + -Wimplicit-fallthrough -Wno-overloaded-virtual -Wno-switch -Wno-unused-parameter -Wno-unused-variable -Wno-missing-field-initializers -fstack-protector-strong + -fstack-clash-protection ) - # Hardening: PIE and RELRO for executables + # Hardening: RELRO and BIND_NOW for executables add_link_options(-Wl,-z,relro,-z,now) - # Fortify source for Release builds + # Fortify source for optimized builds (requires -O1 or higher) if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") add_compile_definitions(_FORTIFY_SOURCE=2) endif() - message(STATUS "Linux build: Standard warning flags and hardening applied.") + message(STATUS "Linux build: Warning flags and security hardening applied.") elseif(WIN32) message(STATUS "Detected platform: Windows") @@ -942,15 +952,15 @@ unset(IccProfLib2_LIB CACHE) unset(IccProfLib2 CACHE) # ---------------------------------------------------------------------- -# Third-Party Dependencies +# Third-Party Dependencies (already found above — verify availability) # ---------------------------------------------------------------------- message(STATUS "") message(STATUS "## Third Party Dependencies ##") -find_package(LibXml2 REQUIRED) -find_package(nlohmann_json REQUIRED) -find_package(TIFF REQUIRED) -find_package(PNG REQUIRED) -find_package(JPEG REQUIRED) +message(STATUS " LibXml2: ${LIBXML2_LIBRARIES}") +message(STATUS " nlohmann_json: found") +message(STATUS " TIFF: ${TIFF_LIBRARIES}") +message(STATUS " PNG: ${PNG_LIBRARIES}") +message(STATUS " JPEG: ${JPEG_LIBRARIES}") message(STATUS "") # @@ -1001,9 +1011,13 @@ message(STATUS "## Project Configuration ##") message(STATUS "Adding subdirectory for IccProfLib.") add_subdirectory(IccProfLib) -# Set default link target for IccProfLib +# Set default link target for IccProfLib (matches build configuration) # Use CACHE INTERNAL so that it's available in Tools/* subdirectories -set(TARGET_LIB_ICCPROFLIB IccProfLib2 CACHE INTERNAL "Link target for IccProfLib2") +IF(ENABLE_SHARED_LIBS) + set(TARGET_LIB_ICCPROFLIB IccProfLib2 CACHE INTERNAL "Link target for IccProfLib2") +ELSE() + set(TARGET_LIB_ICCPROFLIB IccProfLib2-static CACHE INTERNAL "Link target for IccProfLib2") +ENDIF() # # Optional external dependency: LibXML2 @@ -1055,6 +1069,10 @@ ENDIF() # Ensure IccXML headers are available globally include_directories(${TOP_SOURCE_DIR}/IccXML/IccLibXML/) +# Ensure generated version headers are available globally +include_directories(${CMAKE_CURRENT_BINARY_DIR}/IccProfLib) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/IccXML) + # Diagnostic: Output current linker flags message(STATUS "CMAKE_EXE_LINKER_FLAGS = ${CMAKE_EXE_LINKER_FLAGS}") @@ -1062,8 +1080,7 @@ message(STATUS "CMAKE_SHARED_LINKER_FLAGS = ${CMAKE_SHARED_LINKER_FLAGS}") message(STATUS "CMAKE_MODULE_LINKER_FLAGS = ${CMAKE_MODULE_LINKER_FLAGS}") -# Ensure nlohmann_json is found -find_package(nlohmann_json REQUIRED) +# nlohmann_json already found above message(STATUS "nlohmann_json library found: ${nlohmann_json_DIR}") # Function to add and log subdirectories @@ -1116,9 +1133,8 @@ IF(ENABLE_TOOLS) ENDIF() -# --- PNG --- +# --- PNG (already found above) --- message(STATUS "Checking for PNG...") -find_package(PNG REQUIRED) if(PNG_FOUND) message(STATUS "PNG Library : ${PNG_LIBRARIES}") @@ -1135,8 +1151,7 @@ ADD_SUBDIRECTORY(Tools/IccPngDump) ADD_SUBDIRECTORY(Tools/IccDEVCmm) - # Check for TIFF library - find_package(TIFF REQUIRED) + # TIFF already found above if (TIFF_FOUND) include_directories(${TIFF_INCLUDE_DIR}) message(STATUS "TIFF library found: ${TIFF_LIBRARIES}") @@ -1152,14 +1167,15 @@ ADD_SUBDIRECTORY(Tools/IccPngDump) message(FATAL_ERROR "TIFF library not found. Please install libtiff-dev.") endif() - # Configure wxWidgets - find_package(wxWidgets COMPONENTS core base REQUIRED) - if (wxWidgets_FOUND) + # wxWidgets GUI tool (optional) + find_package(wxWidgets QUIET COMPONENTS core base) + if(wxWidgets_FOUND) include(${wxWidgets_USE_FILE}) - ADD_SUBDIRECTORY(Tools/wxProfileDump) - message(STATUS "wxWidgets found and configured") + message(STATUS "wxWidgets found: ${wxWidgets_LIBRARIES}") + message(STATUS "Adding Subdirectory wxProfileDump.") + ADD_SUBDIRECTORY(Tools/wxProfileDump) else() - message(FATAL_ERROR "wxWidgets not found. Please install it manually or via vcpkg.") + message(STATUS "wxWidgets not found - skipping wxProfileDump.") endif() ENDIF(ENABLE_TOOLS) diff --git a/Build/Cmake/CMakePresets.json b/Build/Cmake/CMakePresets.json index d49d57bc8..818461dbf 100644 --- a/Build/Cmake/CMakePresets.json +++ b/Build/Cmake/CMakePresets.json @@ -11,8 +11,7 @@ "hidden": true, "generator": "Ninja", "cacheVariables": { - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", - "CMAKE_BUILD_TYPE": "Debug" + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } }, { @@ -21,7 +20,6 @@ "displayName": "Windows MSVC (VS2022)", "generator": "Visual Studio 17 2022", "architecture": "x64", - "binaryDir": "${sourceDir}/build/vs2022-x64", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "VCPKG_MANIFEST_DIR": "${sourceDir}" @@ -31,7 +29,6 @@ "name": "mingw-x64", "inherits": "default-base", "displayName": "Windows MinGW x64", - "binaryDir": "${sourceDir}/build/mingw-x64", "cacheVariables": { "CMAKE_C_COMPILER": "x86_64-w64-mingw32-gcc", "CMAKE_CXX_COMPILER": "x86_64-w64-mingw32-g++" @@ -41,7 +38,6 @@ "name": "linux-gcc", "inherits": "default-base", "displayName": "Linux GCC", - "binaryDir": "${sourceDir}/build/linux-gcc", "cacheVariables": { "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++" @@ -51,7 +47,6 @@ "name": "linux-clang", "inherits": "default-base", "displayName": "Linux Clang", - "binaryDir": "${sourceDir}/build/linux-clang", "cacheVariables": { "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++" @@ -61,7 +56,6 @@ "name": "linux-gcc-asan", "inherits": "linux-gcc", "displayName": "Linux GCC with AddressSanitizer", - "binaryDir": "${sourceDir}/build/linux-gcc-asan", "cacheVariables": { "ENABLE_SANITIZERS": "ON", "CMAKE_BUILD_TYPE": "RelWithDebInfo" @@ -71,7 +65,6 @@ "name": "linux-clang-asan", "inherits": "linux-clang", "displayName": "Linux Clang with AddressSanitizer", - "binaryDir": "${sourceDir}/build/linux-clang-asan", "cacheVariables": { "ENABLE_SANITIZERS": "ON", "CMAKE_BUILD_TYPE": "RelWithDebInfo" @@ -81,7 +74,6 @@ "name": "linux-clang-tsan", "inherits": "linux-clang", "displayName": "Linux Clang with ThreadSanitizer", - "binaryDir": "${sourceDir}/build/linux-clang-tsan", "cacheVariables": { "ENABLE_TSAN": "ON", "CMAKE_BUILD_TYPE": "RelWithDebInfo" @@ -91,7 +83,6 @@ "name": "linux-clang-msan", "inherits": "linux-clang", "displayName": "Linux Clang with MemorySanitizer", - "binaryDir": "${sourceDir}/build/linux-clang-msan", "cacheVariables": { "ENABLE_MSAN": "ON", "CMAKE_BUILD_TYPE": "RelWithDebInfo" @@ -101,7 +92,6 @@ "name": "linux-clang-ubsan", "inherits": "linux-clang", "displayName": "Linux Clang with UBSanitizer", - "binaryDir": "${sourceDir}/build/linux-clang-ubsan", "cacheVariables": { "ENABLE_UBSAN": "ON", "CMAKE_BUILD_TYPE": "RelWithDebInfo" @@ -111,26 +101,7 @@ "name": "macos-xcode", "inherits": "default-base", "displayName": "macOS (Xcode)", - "generator": "Xcode", - "binaryDir": "${sourceDir}/build/macos-xcode" - } - ], - "buildPresets": [ - { - "name": "build-release", - "configurePreset": "vs2022-x64", - "configuration": "Release" - }, - { - "name": "build-debug", - "configurePreset": "vs2022-x64", - "configuration": "Debug" - } - ], - "testPresets": [ - { - "name": "test-default", - "configurePreset": "vs2022-x64" + "generator": "Xcode" } ] } diff --git a/Build/Cmake/IccProfLib/CMakeLists.txt b/Build/Cmake/IccProfLib/CMakeLists.txt index 44e704f3e..81f1f3bdb 100644 --- a/Build/Cmake/IccProfLib/CMakeLists.txt +++ b/Build/Cmake/IccProfLib/CMakeLists.txt @@ -3,9 +3,9 @@ # Copyright (©) 2024-2026 The International Color Consortium. # All rights reserved. # -# Last Updated: 05-FEB-2026 1645Z by David Hoyt +# Last Updated: 2026-02-17 16:34:07 UTC by David Hoyt # -# Changes: Modify Version String with Commit Hash +# Changes: Add WASM Toolchain, CFL & libFuzzer # ################################################################################# @@ -108,12 +108,16 @@ else() set(ICCPROFLIB_VERSION_STRING "${${PROJECT_UP_NAME}_VERSION}") endif() +# Generate version header into build directory (not source tree) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/../../../IccProfLib/IccProfLibVer.h.in" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../IccProfLib/IccProfLibVer.h" + "${CMAKE_CURRENT_BINARY_DIR}/IccProfLibVer.h" @ONLY ) +# Ensure build directory is in include path for generated header +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + SET(SOURCES ${CFILES}) IF(APPLE) @@ -163,6 +167,12 @@ IF(ENABLE_STATIC_LIBS) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) ENDIF() + # Create ALIAS target when no shared library (STATIC_ONLY configuration) + # This ensures ${TARGET_NAME} always exists for target_link_libraries + IF(NOT ENABLE_SHARED_LIBS) + ADD_LIBRARY(${TARGET_NAME} ALIAS ${TARGET_NAME}-static) + ENDIF() + # Provide alias to resolve MSVC LNK1104: expected IccProfLib2.lib IF(WIN32 AND ENABLE_SHARED_LIBS) ADD_CUSTOM_COMMAND(TARGET ${TARGET_NAME}-static POST_BUILD diff --git a/Build/Cmake/IccXML/CMakeLists.txt b/Build/Cmake/IccXML/CMakeLists.txt index c5a46a35f..757cf3b5f 100644 --- a/Build/Cmake/IccXML/CMakeLists.txt +++ b/Build/Cmake/IccXML/CMakeLists.txt @@ -3,9 +3,9 @@ # Copyright (©) 2024-2026 The International Color Consortium. # All rights reserved. # -# Last Updated: 04-FEB-2026 at 0430Z by David Hoyt +# Last Updated: 2026-02-17 16:34:47 UTC by David Hoyt # -# Changes: Modify Version to contain Commit Hash +# Changes: Add WASM Toolchain, CFL & libFuzzer # ################################################################################# @@ -46,12 +46,16 @@ else() set(ICCLIBXML_VERSION_STRING "${${PROJECT_UP_NAME}_VERSION}") endif() +# Generate version header into build directory (not source tree) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/../../../IccXML/IccLibXML/IccLibXMLVer.h.in" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../IccXML/IccLibXML/IccLibXMLVer.h" + "${CMAKE_CURRENT_BINARY_DIR}/IccLibXMLVer.h" @ONLY ) +# Ensure build directory is in include path for generated header +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + # message(STATUS "Starting CMake debug for target: ${TARGET_NAME}") # message(STATUS "CFILES = ${CFILES}") # message(STATUS "ENABLE_SHARED_LIBS = ${ENABLE_SHARED_LIBS}") @@ -125,6 +129,10 @@ IF(ENABLE_STATIC_LIBS) "$/${TARGET_NAME}.lib" COMMENT "Aliasing ${TARGET_NAME}-static.lib to ${TARGET_NAME}.lib for MSVC compatibility") ENDIF() + # If no shared library, create alias so IccXML2 always exists + IF(NOT ENABLE_SHARED_LIBS) + ADD_LIBRARY(${TARGET_NAME} ALIAS ${TARGET_NAME}-static) + ENDIF() ENDIF() # Resolve linking target for parent scope diff --git a/Build/Cmake/Tools/IccPngDump/CMakeLists.txt b/Build/Cmake/Tools/IccPngDump/CMakeLists.txt index 0b895ac3a..72cc2afb3 100644 --- a/Build/Cmake/Tools/IccPngDump/CMakeLists.txt +++ b/Build/Cmake/Tools/IccPngDump/CMakeLists.txt @@ -1,20 +1,12 @@ ################################################################################# -# iccPngDump Cmake Project Configuration| iccMAX Project -# Copyright (©) 2024-2025 The International Color Consortium. +# iccPngDump Cmake Project Configuration| iccDEV Project +# Copyright (©) 2024-2026 The International Color Consortium. # All rights reserved. -# # -# Last Updated: 13-AUGUST-2025 at 1200Z by David Hoyt -# -# Changes: Fixups for Windows PNG Header Includes -# Update Cmake Configs to reduce logging outputs -# -# -# TODO: Refactor Debug, Release, Asan, subproject pathing issues -# Collapse 3-tier CMakeLists.txt, Housekeeping -# Refactor Base Configs & logging to .mk's # +# Last Updated: 2026-02-17 16:35:43 UTC by David Hoyt # +# Changes: Housekeeping # # ################################################################################# @@ -36,7 +28,7 @@ message(STATUS "PROJECT_ROOT_DIR resolved as: ${PROJECT_ROOT_DIR}") set(TARGET_NAME iccPngDump) message(STATUS "Added executable target: ${TARGET_NAME}") -# Add executable +# Add executable add_executable(${TARGET_NAME} ${SOURCES}) # Include directories @@ -46,7 +38,7 @@ target_include_directories(${TARGET_NAME} PRIVATE ) # Unix check -if(UNIX) +if(UNIX AND NOT EMSCRIPTEN) target_include_directories(${TARGET_NAME} PRIVATE /usr/include /usr/local/include) endif() @@ -92,11 +84,6 @@ endif() message(STATUS "Libraries linked for ${TARGET_NAME}") -# -# set_target_properties(${TARGET_NAME} PROPERTIES -# RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_ROOT_DIR}/Testing/Release" -# RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_ROOT_DIR}/Testing/Debug" -# ) message(STATUS "Output directories set for ${TARGET_NAME}") # RIM Install diff --git a/Build/Cmake/vcpkg.json b/Build/Cmake/vcpkg.json index 39e31552c..afbf7d3ed 100644 --- a/Build/Cmake/vcpkg.json +++ b/Build/Cmake/vcpkg.json @@ -1,8 +1,8 @@ { "name": "iccdev", - "version": "2.3.1.1", + "version": "2.3.1.4", "description": "Developer tools for ICC color profiles", - "builtin-baseline": "af752f21c9d79ba3df9cb0250ce2233933f58486", + "builtin-baseline": "1940ee77e81573713c0d364c42f5990172198be1", "dependencies": [ "vcpkg-cmake", "vcpkg-cmake-config", @@ -15,4 +15,4 @@ "libjpeg-turbo" ], "overrides": [] -} \ No newline at end of file +} diff --git a/Testing/CalcTest/checkInvalidProfiles.sh b/Testing/CalcTest/checkInvalidProfiles.sh index eea045991..21e4a09d7 100755 --- a/Testing/CalcTest/checkInvalidProfiles.sh +++ b/Testing/CalcTest/checkInvalidProfiles.sh @@ -1,34 +1,24 @@ #!/bin/sh ################################################################################# -# CalcTest/checkInvalidProfiles.sh | iccMAX Project -# Copyright (C) 2024-2025 The International Color Consortium. +# CalcTest/checkInvalidProfiles.sh | iccDEV Project +# Copyright (C) 2024-2026 The International Color Consortium. # All rights reserved. # # -# Last Updated: 24-APRIL-2025 16:00 EDT 2025 by David Hoyt +# Last Updated: 2026-02-11 16:41:15 UTC by David Hoyt +# Remove PATH # # # # # -# -# Intent: iccMAX CICD +# Intent: iccDEV CICD # # # # ################################################################################# -echo "====================== Entering CalcTest/checkInvalidProfiles.sh ==========================" - -# Properly handle newline-separated paths as a list -find ../../Build/Tools -type f -perm -111 -exec dirname {} \; | sort -u | while read -r d; do - abs_path=$(cd "$d" && pwd) - PATH="$abs_path:$PATH" -done - -export PATH - echo "====================== Running checkInvalidProfiles.sh Checks ==========================" diff --git a/Testing/CreateAllProfiles.bat b/Testing/CreateAllProfiles.bat index b16e853dc..67d8d4e70 100644 --- a/Testing/CreateAllProfiles.bat +++ b/Testing/CreateAllProfiles.bat @@ -1,4 +1,6 @@ @echo off +:: Auto-call path.bat if present alongside this script (for standalone bundles) +if exist "%~dp0path.bat" call "%~dp0path.bat" where iccFromXml if not "%1"=="clean" goto do_begin echo CLEANING! diff --git a/Testing/CreateAllProfiles.sh b/Testing/CreateAllProfiles.sh index 5fa56c286..6f8c464f3 100755 --- a/Testing/CreateAllProfiles.sh +++ b/Testing/CreateAllProfiles.sh @@ -1,33 +1,31 @@ #!/bin/sh ################################################################################# -# Testing/CreateAllProfiles.sh | iccMAX Project -# Copyright (C) 2024-2025 The International Color Consortium. +# Testing/CreateAllProfiles.sh | iccDEV Project +# Copyright (C) 2024-2026 The International Color Consortium. # All rights reserved. # # -# Last Updated: Thu May 8 07:33:04 EDT 2025 by David Hoyt +# Last Updated: 2026-02-11 16:41:15 UTC by David Hoyt +# Remove PATH # # # # # -# -# Intent: iccMAX CICD +# Intent: iccDEV CICD # # # # ################################################################################# -echo "====================== Entering Testing/CreateAllProfiles.sh ==========================" - -# Properly handle newline-separated paths as a list -find ../Build/Tools -type f -perm -111 -exec dirname {} \; | sort -u | while read -r d; do - abs_path=$(cd "$d" && pwd) - PATH="$abs_path:$PATH" -done +# Auto-source path.sh if present (sets PATH and LD_LIBRARY_PATH/DYLD_LIBRARY_PATH) +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if [ -f "$SCRIPT_DIR/path.sh" ]; then + . "$SCRIPT_DIR/path.sh" +fi -export PATH +echo "====================== Entering Testing/CreateAllProfiles.sh ==========================" if [ "$1" = "clean" ] then diff --git a/Testing/HDR/mkprofiles.sh b/Testing/HDR/mkprofiles.sh index 03132e461..7dc1b8b61 100755 --- a/Testing/HDR/mkprofiles.sh +++ b/Testing/HDR/mkprofiles.sh @@ -1,18 +1,18 @@ #!/bin/sh ################################################################################# -# mkprofiles.sh | iccMAX Project -# Copyright (C) 2024-2025 The International Color Consortium. +# mkprofiles.sh | iccDEV Project +# Copyright (C) 2024-2026 The International Color Consortium. # All rights reserved. # # -# Last Updated: Mon Mar 24 16:40:19 EDT 2025 by David Hoyt -# date -d @1742848819 -# Mon Mar 24 16:40:19 EDT 2025 +# Last Updated: 2026-02-11 16:41:15 UTC by David Hoyt +# Remove PATH # # # # -# Intent: iccMAX CICD +# +# Intent: iccDEV CICD # # # @@ -21,15 +21,6 @@ echo "====================== Entering HDR/mkprofiles.sh ==========================" -# Properly handle newline-separated paths as a list -find ../../Build/Tools -type f -perm -111 -exec dirname {} \; | sort -u | while read -r d; do - abs_path=$(cd "$d" && pwd) - PATH="$abs_path:$PATH" -done - -export PATH - - echo "====================== Running iccFromXml Checks ==========================" iccFromXml BT2100HlgFullScene.xml BT2100HlgFullScene.icc diff --git a/Testing/Readme.md b/Testing/Readme.md index 6449c4da0..f1aaf2e05 100644 --- a/Testing/Readme.md +++ b/Testing/Readme.md @@ -44,15 +44,10 @@ Additionally, examples of 6 channel abridged spectral encoding is provided. ## Note: The CreateAllProfiles.bat/.sh files use `iccFromXML` to create ICC profiles -from each of the XML files in these folders. +from each of the XML files in these folders. Add the PATH to your local devenv. -### Create Profiles | Unix - -``` -cd Testing/ -for d in ../Build/Tools/*; do - [ -d "$d" ] && export PATH="$(realpath "$d"):$PATH" -done -sh CreateAllProfiles.sh -``` +For [Release Downloads](https://github.com/InternationalColorConsortium/iccDEV/releases) +run the included PATH Scripts: +- Windows run `path.bat` then `CreateAllProfiles.bat` +- Unix run `./path.sh` then `./CreateAllProfiles.sh` diff --git a/Testing/RunTests.bat b/Testing/RunTests.bat index b30e3a9ad..88bef6abc 100644 --- a/Testing/RunTests.bat +++ b/Testing/RunTests.bat @@ -1,4 +1,6 @@ @echo off +:: Auto-call path.bat if present alongside this script (for standalone bundles) +if exist "%~dp0path.bat" call "%~dp0path.bat" echo =========================================================================== echo Test CalcElement Operations return of zero's indicates that something bad happened iccApplyNamedCMM Calc\srgbCalcTest.txt 2 0 Calc\srgbCalcTest.icc 3 sRGB_v4_ICC_preference.icc 3 diff --git a/Testing/RunTests.sh b/Testing/RunTests.sh index 7235cade8..c2a3a9492 100755 --- a/Testing/RunTests.sh +++ b/Testing/RunTests.sh @@ -1,34 +1,31 @@ #!/bin/sh ################################################################################# -# Testing/RunTests.sh | iccMAX Project -# Copyright (C) 2024-2025 The International Color Consortium. +# Testing/RunTests.sh | iccDEV Project +# Copyright (C) 2024-2026 The International Color Consortium. # All rights reserved. # # -# Last Updated: Thu May 8 07:33:04 EDT 2025 by David Hoyt +# Last Updated: 2026-02-11 16:41:15 UTC by David Hoyt +# Remove PATH # # # # # -# -# Intent: iccMAX CICD +# Intent: iccDEV CICD # # # # ################################################################################# -echo "====================== Entering Testing/RunTests.sh ==========================" - -# Properly handle newline-separated paths as a list -find ../Build/Tools -type f -perm -111 -exec dirname {} \; | sort -u | while read -r d; do - abs_path=$(cd "$d" && pwd) - PATH="$abs_path:$PATH" -done - -export PATH +# Auto-source path.sh if present (sets PATH and LD_LIBRARY_PATH/DYLD_LIBRARY_PATH) +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if [ -f "$SCRIPT_DIR/path.sh" ]; then + . "$SCRIPT_DIR/path.sh" +fi +echo "====================== Entering Testing/RunTests.sh ==========================" if ! command -v iccApplyNamedCmm # print which executable is being used then diff --git a/Testing/hybrid/BuildAndTest.sh b/Testing/hybrid/BuildAndTest.sh index baa1f9a84..f61974270 100755 --- a/Testing/hybrid/BuildAndTest.sh +++ b/Testing/hybrid/BuildAndTest.sh @@ -1,18 +1,18 @@ #!/bin/bash ################################################################################# -# Testing/CreateAllProfiles.sh | iccMAX Project -# Copyright (C) 2024-2025 The International Color Consortium. +# Testing/CreateAllProfiles.sh | iccDEV Project +# Copyright (C) 2024-2026 The International Color Consortium. # All rights reserved. # # -# Last Updated: 20-OCT-2025 by David Hoyt +# Last Updated: 2026-02-11 16:41:15 UTC by David Hoyt +# Remove PATH # -# file Location: Testing/hybrid # # # # -# Intent: iccMAX CICD +# Intent: iccDEV CICD # # # diff --git a/Testing/mcs/updateprev.sh b/Testing/mcs/updateprev.sh index 6a44104e0..3e5946d94 100755 --- a/Testing/mcs/updateprev.sh +++ b/Testing/mcs/updateprev.sh @@ -5,8 +5,8 @@ # All rights reserved. # # -# Last Updated: Wed Jan 7 03:27:11 AM UTC 2026 by David Hoyt -# +# Last Updated: 2026-02-11 16:41:15 UTC by David Hoyt +# Remove PATH # # # @@ -21,16 +21,6 @@ echo "====================== Entering mcs/updateprev.sh ==========================" -echo "====================== Updating PATH ==========================" - -# Properly handle newline-separated paths as a list -find ../../Build/Tools -type f -perm -111 -exec dirname {} \; | sort -u | while read -r d; do - abs_path=$(cd "$d" && pwd) - PATH="$abs_path:$PATH" -done - -export PATH - echo "====================== Running iccFromXml 6ChanSelect-MID.xml 6ChanSelect-MID.icc ==========================" iccFromXml 6ChanSelect-MID.xml 6ChanSelect-MID.icc diff --git a/Testing/mcs/updateprevWithBkgd.sh b/Testing/mcs/updateprevWithBkgd.sh index 371f41834..47137698e 100755 --- a/Testing/mcs/updateprevWithBkgd.sh +++ b/Testing/mcs/updateprevWithBkgd.sh @@ -5,8 +5,8 @@ # All rights reserved. # # -# Last Updated: Wed Jan 7 03:27:11 AM UTC 2026 by David Hoyt -# +# Last Updated: 2026-02-11 16:41:15 UTC by David Hoyt +# Remove PATH # # # @@ -23,14 +23,6 @@ echo "====================== Entering mcs/updateprevWithBkgd.sh ================ echo "====================== Updating PATH ==========================" -# Properly handle newline-separated paths as a list -find ../../Build/Tools -type f -perm -111 -exec dirname {} \; | sort -u | while read -r d; do - abs_path=$(cd "$d" && pwd) - PATH="$abs_path:$PATH" -done - -export PATH - echo "====================== Running iccFromXml 6ChanSelect-MID.xml 6ChanSelect-MID.icc ==========================" iccFromXml 6ChanSelect-MID.xml 6ChanSelect-MID.icc diff --git a/docs/build.md b/docs/build.md index a6473ab2f..0b1161692 100644 --- a/docs/build.md +++ b/docs/build.md @@ -19,11 +19,10 @@ iccDEV requires C++17 or higher to compile. ``` export CXX=clang++ git clone https://github.com/InternationalColorConsortium/iccdev.git iccdev -cd iccdev/Build -sudo apt install -y libpng-dev libjpeg-dev libtiff-dev libwxgtk3.2-dev libwxgtk-{media,webview}3.2-dev wx-common wx3.2-headers curl git make cmake clang{,-tools} libxml2{,-dev} nlohmann-json3-dev build-essential -cmake Cmake -make -j"$(nproc)" - +cd iccdev +sudo apt install -y libpng-dev libjpeg-dev libtiff-dev libwxgtk3.2-dev libwxgtk-{media,webview}3.2-dev wx-common wx3.2-headers curl git make cmake clang{,-tools} libxml2{,-dev} nlohmann-json3-dev build-essential ninja-build +cmake --preset linux-clang -S Build/Cmake -B out/linux-clang +cmake --build out/linux-clang -j"$(nproc)" ``` ## macOS @@ -33,9 +32,8 @@ export CXX=clang++ brew install libpng nlohmann-json libxml2 wxwidgets libtiff jpeg-turbo git clone https://github.com/InternationalColorConsortium/iccdev.git iccdev cd iccdev -cmake -G "Xcode" Build/Cmake -xcodebuild -project RefIccMAX.xcodeproj -open RefIccMAX.xcodeproj +cmake --preset macos-xcode -S Build/Cmake -B out/macos-xcode +cmake --build out/macos-xcode --config Release -j"$(sysctl -n hw.ncpu)" ``` ## Windows MSVC @@ -43,29 +41,6 @@ open RefIccMAX.xcodeproj ``` git clone https://github.com/InternationalColorConsortium/iccdev.git iccdev cd iccdev -vcpkg integrate install -vcpkg install -cmake --preset vs2022-x64 -B . -S Build/Cmake -cmake --build . -- /m /maxcpucount +cmake --preset vs2022-x64 -S Build/Cmake -B out/vs2022-x64 +cmake --build out/vs2022-x64 --config Release -- /m /maxcpucount ``` - -### Reporting Build Issues - -Before submitting a PR for build failures: - -1. Test local environment: - - Build in a clean container/VM - - Open an Issue first - - Utilize Testing Scripts - - [Unix](https://github.com/InternationalColorConsortium/iccDEV/blob/research/contrib/HelperScripts/unix-issue-template.sh) - ``` - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/InternationalColorConsortium/iccDEV/refs/heads/research/contrib/HelperScripts/unix-pr-review.sh)" - ``` - - [Windows](https://raw.githubusercontent.com/InternationalColorConsortium/iccDEV/refs/heads/research/contrib/HelperScripts/windows-pr-review.ps1) - ``` - iex (iwr -Uri "https://raw.githubusercontent.com/InternationalColorConsortium/iccDEV/refs/heads/research/contrib/HelperScripts/windows-issue-template.ps1").Content - ``` - -2. Open or Update an Issue with the script output. - -3. [CI](https://github.com/InternationalColorConsortium/iccDEV/actions/workflows/ci-latest-release.yml) provides the latest builds for macOS, Windows & Linux. diff --git a/docs/install.md b/docs/install.md index a5229d283..b44bc77af 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,10 +2,188 @@ | Method | Command | |--------|---------| -| **Homebrew** | `brew install iccdev` | | **NPM** | `npm install iccdev` | +| **Homebrew** | `brew install iccdev` | | **Docker Pull** | `docker pull ghcr.io/internationalcolorconsortium/iccdev:latest` | | **Docker Run** | `docker run -it ghcr.io/internationalcolorconsortium/iccdev:latest` | +| **NixOS Pull** | `docker pull ghcr.io/internationalcolorconsortium/iccdev-nixos:latest` | +| **NixOS Run** | `docker run -it ghcr.io/internationalcolorconsortium/iccdev-nixos:latest` | + +## Examples for Docker & NixOS + +### Docker Examples + +`iccDumpProfile` + +``` +Usage: iccDumpProfile {-v} {int} profile {tagId to dump/"ALL"} +Built with IccProfLib version 2.3.1.1 + +The -v option causes profile validation to be performed. +The optional integer parameter specifies verboseness of output (1-100, default=100). +``` + +`iccFromXml` + +``` +IccFromXml built with IccProfLib Version 2.3.1.1, IccLibXML Version 2.3.1.1 + +Usage: IccFromXml xml_file saved_profile_file {-noid -v{=[relax_ng_schema_file - optional]}} +``` + +`iccToXml` + +``` +IccToXml built with IccProfLib Version 2.3.1.1, IccLibXML Version 2.3.1.1 + +Usage: IccToXml src_icc_profile dest_xml_file +``` + +`iccRoundTrip` + +``` +Usage: iccRoundTrip profile {rendering_intent=1 {use_mpe=0}} +Built with IccProfLib version 2.3.1.1 + where rendering_intent is (0=perceptual, 1=relative, 2=saturation, 3=absolute) +``` + +### NixOS Examples + +2026-02-10 02:17:06 UTC + +#### List the Libraries & Tools + +##### Show Tools + +`docker run --rm ghcr.io/internationalcolorconsortium/iccdev-nixos:latest sh -c "ls /workspace/iccDEV/Build/Tools/*/icc*"` + +##### Expected Output + +``` +/workspace/iccDEV/Build/Tools/IccApplyNamedCmm/iccApplyNamedCmm +/workspace/iccDEV/Build/Tools/IccApplyProfiles/iccApplyProfiles +/workspace/iccDEV/Build/Tools/IccApplySearch/iccApplySearch +/workspace/iccDEV/Build/Tools/IccApplyToLink/iccApplyToLink +/workspace/iccDEV/Build/Tools/IccDumpProfile/iccDumpProfile +/workspace/iccDEV/Build/Tools/IccFromCube/iccFromCube +/workspace/iccDEV/Build/Tools/IccFromXml/iccFromXml +/workspace/iccDEV/Build/Tools/IccJpegDump/iccJpegDump +/workspace/iccDEV/Build/Tools/IccPngDump/iccPngDump +/workspace/iccDEV/Build/Tools/IccRoundTrip/iccRoundTrip +/workspace/iccDEV/Build/Tools/IccSpecSepToTiff/iccSpecSepToTiff +/workspace/iccDEV/Build/Tools/IccTiffDump/iccTiffDump +/workspace/iccDEV/Build/Tools/IccToXml/iccToXml +/workspace/iccDEV/Build/Tools/IccV5DspObsToV4Dsp/iccV5DspObsToV4Dsp +/workspace/iccDEV/Build/Tools/wxProfileDump/iccDumpProfileGui +``` + +#### Show Libraries + +`docker run --rm ghcr.io/internationalcolorconsortium/iccdev-nixos:latest sh -c "find /workspace/iccDEV/Build -name 'libIcc*.so*' -o -name 'libIcc*.a' | sort"` + +#### Expected Output + +``` +/workspace/iccDEV/Build/IccProfLib/libIccProfLib2-static.a +/workspace/iccDEV/Build/IccProfLib/libIccProfLib2.so +/workspace/iccDEV/Build/IccProfLib/libIccProfLib2.so.2 +/workspace/iccDEV/Build/IccProfLib/libIccProfLib2.so.2.3.1.4 +/workspace/iccDEV/Build/IccXML/libIccXML2-static.a +/workspace/iccDEV/Build/IccXML/libIccXML2.so +/workspace/iccDEV/Build/IccXML/libIccXML2.so.2 +/workspace/iccDEV/Build/IccXML/libIccXML2.so.2.3.1.4 +``` + +#### Run iccRoundTrip + +Command: `docker run --rm ghcr.io/internationalcolorconsortium/iccdev-nixos:latest iccRoundTrip Testing/sRGB_v4_ICC_preference.icc` + +#### Expected Output + +``` +Profile: 'Testing/sRGB_v4_ICC_preference.icc' +Rendering Intent: Relative Colorimetric +Specified Gamut: Not Specified + +Round Trip 1 +------------ +Min DeltaE: 0.00 +Mean DeltaE: 0.01 +Max DeltaE: 0.05 + +Max L, a, b: 10.919208, 0.001984, 0.002167 + +Round Trip 2 +------------ +Min DeltaE: 0.00 +Mean DeltaE: 0.01 +Max DeltaE: 0.05 + +Max L, a, b: 10.919208, 0.001984, 0.002167 + +PRMG Interoperability - Round Trip Results +------------------------------------------------------ +DE <= 1.0 ( 122111): 60.6% +DE <= 2.0 ( 126939): 63.0% +DE <= 3.0 ( 131673): 65.3% +DE <= 5.0 ( 140602): 69.7% +DE <=10.0 ( 159684): 79.2% +Total ( 201613) +``` + +#### Run iccDumpProfile + +Command: `docker run --rm ghcr.io/internationalcolorconsortium/iccdev-nixos:latest iccDumpProfile -v Testing/sRGB_v4_ICC_preference.icc` + +#### Expected Output + +``` +Built with IccProfLib version 2.3.1.4+260b499 + +Profile: 'Testing/sRGB_v4_ICC_preference.icc' +Profile ID: 34562abf994ccd066d2c5721d0d68c5d +Size: 60960 (0xee20) bytes + +Header +------ +Attributes: Reflective | Glossy +Cmm: Unknown NULL +Creation Date: 7/25/2007 (M/D/Y) 00:05:37 +Creator: NULL +Device Manufacturer:NULL +Data Color Space: RgbData +Flags: EmbeddedProfileFalse | UseAnywhere +PCS Color Space: LabData +Platform: Unknown +Rendering Intent: Perceptual +Profile Class: ColorSpaceClass +Profile SubClass: Not Defined +Version: 4.20 +Illuminant: X=0.9642, Y=1.0000, Z=0.8249 +Spectral PCS: NoSpectralData +Spectral PCS Range: Not Defined +BiSpectral Range: Not Defined +MCS Color Space: Not Defined + +Profile Tags (9) +------------ + Tag ID Offset Size Pad + ---- ------ ------ ---- --- + profileDescriptionTag 'desc' 240 118 2 + AToB0Tag 'A2B0' 360 29712 0 + AToB1Tag 'A2B1' 30072 436 0 + BToA0Tag 'B2A0' 30508 29748 0 + BToA1Tag 'B2A1' 60256 508 0 +perceptualRenderingIntentGamutTag 'rig0' 60764 12 0 + mediaWhitePointTag 'wtpt' 60776 20 0 + copyrightTag 'cprt' 60796 118 2 + chromaticAdaptationTag 'chad' 60916 44 0 + + +Validation Report +----------------- +Profile is valid for version 4.20 +``` ## Build Instructions Build instructions are available in [docs/build.md](docs/build.md). \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index caf286de4..afbf7d3ed 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,8 +1,8 @@ { "name": "iccdev", - "version": "2.3.1.2", + "version": "2.3.1.4", "description": "Developer tools for ICC color profiles", - "builtin-baseline": "af752f21c9d79ba3df9cb0250ce2233933f58486", + "builtin-baseline": "1940ee77e81573713c0d364c42f5990172198be1", "dependencies": [ "vcpkg-cmake", "vcpkg-cmake-config", @@ -15,4 +15,4 @@ "libjpeg-turbo" ], "overrides": [] -} \ No newline at end of file +}