pipeline { agent none options { timestamps() disableConcurrentBuilds() } environment { PROJECT_DIR = "qt6-after/qt6-migrated-benchmark" BUILD_DIR = "build" COVERAGE_BUILD_DIR = "build-coverage" CHANGE_TARGET = "${env.CHANGE_TARGET ?: 'origin/main'}" QT_QPA_PLATFORM = "offscreen" LANG = "C.UTF-8" LC_ALL = "C.UTF-8" DOCKER_IMAGE = "lifemaps/ci-cpp:latest" } stages { stage('Docker Image') { agent any steps { script { echo "Checking if Docker image exists locally..." def imageExists = sh(script: "docker inspect ${DOCKER_IMAGE} >/dev/null 2>&1", returnStatus: true) == 0 if (!imageExists) { echo "Docker image not found. Building from ci/Dockerfile.ci..." sh 'docker build -t ${DOCKER_IMAGE} -f ci/Dockerfile.ci .' } else { echo "Docker image ${DOCKER_IMAGE} found locally." } } } } stage('Build and Test') { agent { docker { image 'lifemaps/ci-cpp:latest' reuseNode true } } stages { stage('Cleanup') { steps { script { echo "Cleaning workspace to avoid corruption issues..." sh ''' if [ -d "${WORKSPACE}/${BUILD_DIR}" ]; then echo "Removing build directory with permission fix..." chmod -R u+w "${WORKSPACE}/${BUILD_DIR}" 2>/dev/null || true rm -rf "${WORKSPACE}/${BUILD_DIR}" fi if [ -d "${WORKSPACE}/${COVERAGE_BUILD_DIR}" ]; then echo "Removing coverage build directory..." chmod -R u+w "${WORKSPACE}/${COVERAGE_BUILD_DIR}" 2>/dev/null || true rm -rf "${WORKSPACE}/${COVERAGE_BUILD_DIR}" fi if [ -d "${WORKSPACE}/coverage" ]; then echo "Removing coverage output directory..." chmod -R u+w "${WORKSPACE}/coverage" 2>/dev/null || true rm -rf "${WORKSPACE}/coverage" fi ''' } } } stage('Checkout') { steps { checkout scm sh 'git fetch --all --prune' sh 'git submodule update --init --recursive || true' } } stage('Tool Versions') { steps { sh ''' python3 --version cmake --version ninja --version g++ --version ''' } } stage('Qt Diagnostics') { steps { sh ''' echo "QT_QPA_PLATFORM=$QT_QPA_PLATFORM" echo "LANG=$LANG" echo "LC_ALL=$LC_ALL" dpkg -l | grep -E "qt6-base-dev|qtbase5-dev|qt6-tools" || true pkg-config --list-all | grep -i qt || true ''' } } stage('Policy Checks') { steps { sh 'python3 ci/check_pr_policies.py' } } stage('Configure') { steps { sh 'cmake -S ${PROJECT_DIR} -B ${BUILD_DIR} -G Ninja -DCMAKE_BUILD_TYPE=Debug' } } stage('Build') { steps { sh 'cmake --build ${BUILD_DIR} --parallel' } } stage('Test') { steps { sh 'QT_QPA_PLATFORM=offscreen LANG=C.UTF-8 LC_ALL=C.UTF-8 ctest --test-dir ${BUILD_DIR} --output-on-failure' } } stage('Coverage') { when { expression { def branch = env.CHANGE_BRANCH ?: env.BRANCH_NAME return branch ==~ /main|master|coverage.*|feature.*/ } } steps { echo "Running coverage collection..." sh ''' ${PROJECT_DIR}/scripts/collect-coverage.sh ${WORKSPACE}/${COVERAGE_BUILD_DIR} ''' } post { always { recordCoverage tools: [[parser: 'LCOV', pattern: "${PROJECT_DIR}/coverage/lcov.info"]], sourceCodeRetention: 'LAST_BUILD' } } } } } } post { always { script { if (getContext(hudson.FilePath)) { sh ''' if [ -d "${WORKSPACE}/${BUILD_DIR}" ]; then echo "Final cleanup: Removing build directory..." chmod -R u+w "${WORKSPACE}/${BUILD_DIR}" 2>/dev/null || true rm -rf "${WORKSPACE}/${BUILD_DIR}" fi if [ -d "${WORKSPACE}/${COVERAGE_BUILD_DIR}" ]; then echo "Final cleanup: Removing coverage build directory..." chmod -R u+w "${WORKSPACE}/${COVERAGE_BUILD_DIR}" 2>/dev/null || true rm -rf "${WORKSPACE}/${COVERAGE_BUILD_DIR}" fi if [ -d "${WORKSPACE}/coverage" ]; then echo "Final cleanup: Removing coverage output..." chmod -R u+w "${WORKSPACE}/coverage" 2>/dev/null || true rm -rf "${WORKSPACE}/coverage" fi ''' } } } } }