Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/install-deps-macos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/bin/bash
set -euo pipefail

brew install llvm@14 gcc@13 cppcheck openldap bear

echo "$(brew --prefix llvm@14)/bin" >> "$GITHUB_PATH"
echo "$(brew --prefix gcc@13)/bin" >> "$GITHUB_PATH"

# Create g++ symlink matching Linux CI naming
GCC_BIN="$(brew --prefix gcc@13)/bin"
ln -sf "$GCC_BIN/g++-13" "$GCC_BIN/g++"
ln -sf "$GCC_BIN/gcc-13" "$GCC_BIN/gcc"

# Create intercept-build wrapper using bear.
# The LLVM intercept-build is broken on macOS ARM64 (libear.dylib arch
# mismatch with SIP). Bear provides equivalent functionality.
WRAPPER_DIR="$(pwd)/build/intercept-build-wrapper"
mkdir -p "$WRAPPER_DIR"
cat > "$WRAPPER_DIR/intercept-build" << 'EOF'
#!/bin/bash
CDB=""
CMD=()
while [[ $# -gt 0 ]]; do
case "$1" in
--cdb) CDB="$2"; shift 2 ;;
--help) echo "intercept-build wrapper using bear"; exit 0 ;;
*) CMD+=("$1"); shift ;;
esac
done
[[ -z "$CDB" ]] && CDB="compile_commands.json"
exec bear --output "$CDB" -- "${CMD[@]}"
EOF
chmod +x "$WRAPPER_DIR/intercept-build"
echo "$WRAPPER_DIR" >> "$GITHUB_PATH"

# Facebook Infer has no Homebrew formula on current macOS runners,
# but provides prebuilt binaries on GitHub releases.
# Source: https://github.com/facebook/infer/releases
INFER_VERSION=1.3.0
ARCH=$(uname -m)
curl -sSL "https://github.com/facebook/infer/releases/download/v${INFER_VERSION}/infer-osx-${ARCH}-v${INFER_VERSION}.tar.xz" \
| sudo tar -C /opt -xJ
sudo ln -sf "/opt/infer-osx-${ARCH}-v${INFER_VERSION}/bin/infer" /usr/local/bin/infer
infer --version

# Homebrew's llvm@14 does not know where the macOS SDK lives, so libc++
# headers that use '#include_next <ctype.h>' (and other platform C headers)
# fail with "file not found". Apple's own clang resolves this via xcrun, but
# the standalone llvm@14 needs SDKROOT to be set explicitly. Recent runner
# images removed the implicit header path clang@14 used to fall back on,
# which is why analyzer and web tests started failing with:
# fatal error: 'ctype.h' file not found
# Pin SDKROOT to the active SDK so subsequent build and test steps can compile.
if [ -n "$GITHUB_ENV" ]; then
echo "SDKROOT=$(xcrun --show-sdk-path)" >> "$GITHUB_ENV"
# Restrict native builds to the host arch. clang@14 does not support
# universal2 builds against the current macOS SDK.
echo "ARCHFLAGS=-arch $(uname -m)" >> "$GITHUB_ENV"
# gcc@13 defaults to an older deployment target than the installed SDK, so
# the Xcode toolchain's (clang-based) assembler prints
# "clang: warning: overriding deployment version ... [-Woverriding-deployment-version]"
# to stderr. The GCC analyzer uses '-fdiagnostics-format=sarif-stderr', so
# this warning is appended to the SARIF stream and makes it invalid JSON
# (breaking analyze_and_parse's gcc tests). Pin the deployment target to the
# SDK version so no override (and no warning) is emitted.
echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun --show-sdk-version)" >> "$GITHUB_ENV"
# Build pip C extensions (e.g. python-ldap) with Apple's clang, not the
# Homebrew llvm@14 clang that this script puts on PATH for the analyzer.
# clang@14 cannot link against the current macOS SDK's TBD libraries
# (e.g. 'ld: library ldap_r not found'), whereas Apple clang handles the
# SDK natively. CodeChecker selects its analyzer binary independently of
# $CC, so this does not affect which compiler is analyzed/used as analyzer.
echo "CC=/usr/bin/clang" >> "$GITHUB_ENV"
echo "CXX=/usr/bin/clang++" >> "$GITHUB_ENV"
fi
10 changes: 10 additions & 0 deletions .github/workflows/install-deps-windows.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail

# Force LLVM 14 to match Linux/macOS CI.
choco install llvm --version=14.0.6 --allow-downgrade -y
choco install cppcheck -y

echo "C:\Program Files\LLVM\bin" >> "$GITHUB_PATH"
echo "C:\Program Files\Cppcheck" >> "$GITHUB_PATH"

179 changes: 176 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,31 @@ jobs:

tools:
name: Tools (report-converter, etc.)
runs-on: ubuntu-24.04
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.os == 'macos-latest' }}

strategy:
matrix:
os: [ubuntu-24.04, macos-latest]

steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Setup Bazel
if: runner.os == 'Linux'
uses: abhinavsingh/setup-bazel@v3
with:
version: 4.0.0
- name: Install common dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update -q
sudo apt-get install gcc-multilib

- name: Run build-logger tests
if: runner.os == 'Linux'
working-directory: analyzer/tools/build-logger
run: |
pip install -r requirements_py/dev/requirements.txt
Expand Down Expand Up @@ -95,24 +103,35 @@ jobs:
make test

- name: Run bazel-compile-commands tests
if: runner.os == 'Linux'
working-directory: tools/bazel
run: |
pip install -r requirements_py/dev/requirements.txt
make test

analyzer:
name: Analyzer
runs-on: ubuntu-24.04
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.os == 'macos-latest' }}

strategy:
matrix:
os: [ubuntu-24.04, macos-latest]

steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install dependencies
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: sh .github/workflows/install-deps.sh

- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: sh .github/workflows/install-deps-macos.sh

- name: Build the package
run: |
make pip_dev_deps
Expand Down Expand Up @@ -181,6 +200,160 @@ jobs:
working-directory: web
run: make test_unit_cov

web-macos:
name: Web (macOS)
runs-on: macos-latest
continue-on-error: true

steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install dependencies
run: sh .github/workflows/install-deps-macos.sh

- name: Run tests
run: |
make pip_dev_deps
pip3 install -r web/requirements_py/auth/requirements.txt
BUILD_UI_DIST=NO make package

# Run full functional test suite.
cd web
make test_matrix_sqlite
env:
CC_TEST_API_WORKERS: "1"
CC_TEST_TASK_WORKERS: "1"

- name: Run unit tests coverage
working-directory: web
run: make test_unit_cov

analyzer-windows:
name: Analyzer (Windows)
runs-on: windows-latest
continue-on-error: true
timeout-minutes: 30

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Cache LLVM and Cppcheck
id: cache-llvm
uses: actions/cache@v4
with:
path: |
C:\Program Files\LLVM
C:\Program Files\Cppcheck
key: win-llvm14-cppcheck-v1

- name: Install dependencies
if: steps.cache-llvm.outputs.cache-hit != 'true'
shell: bash
run: sh .github/workflows/install-deps-windows.sh

- name: Add tools to PATH
if: steps.cache-llvm.outputs.cache-hit == 'true'
shell: bash
run: |
echo "C:\\Program Files\\LLVM\\bin" >> "$GITHUB_PATH"
echo "C:\\Program Files\\Cppcheck" >> "$GITHUB_PATH"

- name: Build the package
shell: bash
run: |
make pip_dev_deps
BUILD_UI_DIST=NO make package

- name: Run analyzer unit tests
shell: bash
working-directory: analyzer
run: |
make test_unit \
EXTRA_PYTEST_ARGS="--timeout=120 --timeout-method=thread"

web-windows:
name: Web (Windows)
runs-on: windows-latest
continue-on-error: true
timeout-minutes: 30

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Cache LLVM and Cppcheck
id: cache-llvm
uses: actions/cache@v4
with:
path: |
C:\Program Files\LLVM
C:\Program Files\Cppcheck
key: win-llvm14-cppcheck-v1

- name: Install dependencies
if: steps.cache-llvm.outputs.cache-hit != 'true'
shell: bash
run: sh .github/workflows/install-deps-windows.sh

- name: Add tools to PATH
if: steps.cache-llvm.outputs.cache-hit == 'true'
shell: bash
run: |
echo "C:\\Program Files\\LLVM\\bin" >> "$GITHUB_PATH"
echo "C:\\Program Files\\Cppcheck" >> "$GITHUB_PATH"

- name: Build the package
shell: bash
run: |
make pip_dev_deps
pip3 install -r web/requirements_py/auth/requirements.txt
BUILD_UI_DIST=NO make package
python3 -m compileall -q build/CodeChecker/lib/python3 web/tests || true

- name: Run web unit tests
shell: bash
working-directory: web
run: |
# The server and client unit suites share the package name 'unit',
# so they must be collected in separate pytest sessions.
REPO_ROOT="$(cd .. && pwd)" \
pytest -v -c pytest.ini --timeout=300 --timeout-method=thread \
server server/tests/unit
REPO_ROOT="$(cd .. && pwd)" \
pytest -v -c pytest.ini --timeout=300 --timeout-method=thread \
client client/tests/unit

- name: Run web functional tests
shell: bash
working-directory: web
env:
CC_TEST_API_WORKERS: "1"
CC_TEST_TASK_WORKERS: "1"
run: |
# Build logging (CodeChecker log / check -b) is unsupported on
# Windows, so only functional suites that store results via
# check_and_store() (which uses CodeChecker analyze) can run. The
# two excluded tests need re-analysis/detection-status changes that
# depend on build logging.
REPO_ROOT="$(cd .. && pwd)" \
BUILD_DIR="$(cd .. && pwd)/build" \
TEST_CLANG_VERSION=stable \
TEST_PROJ="$(pwd)/tests/projects" \
pytest -v --tb=short --timeout=300 --timeout-method=thread \
tests/functional/db_cleanup \
tests/functional/run_tag \
tests/functional/review_status \
tests/functional/suppress \
-k "not test_review_and_detection_status_changes and not test_suppress_import"

gui:
name: GUI
runs-on: ubuntu-24.04
Expand Down
18 changes: 15 additions & 3 deletions analyzer/codechecker_analyzer/analysis_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@


import glob
import logging
import os
import shlex
import shutil
Expand Down Expand Up @@ -118,10 +119,14 @@ def worker_result_handler(results, metadata_tool, output_path):
PROGRESS_ACTIONS = None


def init_worker(checked_num, action_num):
def init_worker(checked_num, action_num, log_level=None):
global PROGRESS_CHECKED_NUM, PROGRESS_ACTIONS
PROGRESS_CHECKED_NUM = checked_num
PROGRESS_ACTIONS = action_num
# With spawn, workers need explicit logger setup (no fork inheritance).
if log_level:
from codechecker_common.logger import setup_logger
setup_logger(log_level)


def save_output(base_file_name, out, err):
Expand Down Expand Up @@ -704,9 +709,16 @@ def signal_handler(signum, _):
# Start checking parallel.
checked_var = multiprocess.Value('i', 1)
actions_num = multiprocess.Value('i', len(actions))
pool = multiprocess.Pool(jobs,
# Spawned workers (macOS/Windows) do not inherit the parent's logging
# configuration and must set it up explicitly. Forked workers (Linux)
# already inherit it; re-running the logging setup in every worker is
# unnecessary and can cause intermittent analysis failures.
log_level = None
if multiprocess.get_start_method() != 'fork':
log_level = logging.getLevelName(LOG.getEffectiveLevel())
pool = multiprocess.Pool(jobs, # pylint: disable=not-callable
initializer=init_worker,
initargs=(checked_var, actions_num))
initargs=(checked_var, actions_num, log_level))
signal.signal(signal.SIGINT, signal_handler)

# If the analysis has failed, we help debugging.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ def get_analyzer_checkers(cls):
cls.__analyzer_checkers = checker_description

return checker_description
except (subprocess.CalledProcessError, OSError):
except (subprocess.CalledProcessError, OSError) as e:
LOG.debug("Failed to get clang-tidy checkers: %s", e)
return []

@classmethod
Expand Down
Loading
Loading