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//\"/"}"
+ 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//\"/"}"
+ 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
+}