From 4b71d33a0de51611a399e61dbc590ca0980b7d96 Mon Sep 17 00:00:00 2001
From: Florent Pruvost <florent.pruvost@inria.fr>
Date: Fri, 22 Sep 2023 16:51:27 +0200
Subject: [PATCH] Add macosx and windows jobs for build and test. Reorganize ci
 jobs for readibility.

---
 .gitlab-ci.yml                     | 222 +++++++++++++++++++----------
 analysis.sh => .gitlab/analysis.sh |  49 +++----
 .gitlab/build.sh                   |  26 ++++
 .gitlab/test.sh                    |  27 ++++
 .gitlab/validate.sh                |  58 ++++++++
 README.org                         |   2 -
 6 files changed, 276 insertions(+), 108 deletions(-)
 rename analysis.sh => .gitlab/analysis.sh (58%)
 create mode 100755 .gitlab/build.sh
 create mode 100755 .gitlab/test.sh
 create mode 100755 .gitlab/validate.sh

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a75e89e..1239392 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,108 +3,174 @@ image: registry.gitlab.inria.fr/solverstack/docker/distrib
 stages:
   - build
   - test
-  - sonar
-#  - deploy
+  - analyze
+  - validate
 
 before_script:
   - git config --global --add safe.directory $CI_PROJECT_DIR
   - git submodule update --init --recursive
-  - mkdir -p build
+
+.only-master-mr:
+  interruptible: true
+  rules:
+    - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^ci-.*$/)
+    - if: ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME !~ /^notest-.*$/)
+
+.only-mr:
+  interruptible: true
+  rules:
+    - if: ($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME !~ /^notest-.*$/)
 
 hqr_build_linux:
   stage: build
+  tags: ["docker", "large"]
+  extends: .only-master-mr
+  variables:
+    SYSTEM: linux
+  script:
+    - bash .gitlab/build.sh | tee hqr-build-linux.log
   artifacts:
-    name: hqr_build_linux
-    expire_in: 42 minutes
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+    expire_in: 180 minutes
     untracked: true
-  script:
-    - cd build
-    - cmake .. -DCMAKE_INSTALL_PREFIX=${PWD}/../install -DBUILD_SHARED_LIBS=ON -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_C_FLAGS="-O0 -g -fPIC --coverage -Wall -fdiagnostics-show-option -fno-inline" -DCMAKE_EXE_LINKER_FLAGS="--coverage" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON | tee ../hqr-build.log
-    - ctest --no-compress-output -V -j 5
-            -D ExperimentalBuild
-            -D ExperimentalSubmit | tee -a hqr_build.log
-    - make install | tee -a ../hqr-build.log
-  only:
-    - branches
-    - master@solverstack/hqr
 
-hqr_test_linux:
-  stage: test
-  dependencies:
-    - hqr_build_linux
+hqr_build_macosx:
+  stage: build
+  tags: ['macosx']
+  extends: .only-master-mr
+  variables:
+    SYSTEM: macosx
+  script:
+    - bash .gitlab/build.sh | tee hqr-build-linux.log
   artifacts:
-    name: hqr_test_linux
-    expire_in: 42 minutes
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+    expire_in: 180 minutes
+    paths:
+      - hqr-build-linux.log
+  cache:
+    key: "${SYSTEM}-${VERSION}-$CI_COMMIT_REF_SLUG"
     untracked: true
+    policy: push
+
+hqr_build_windows:
+  stage: build
+  tags: ['windows']
+  extends: .only-master-mr
+  variables:
+    SYSTEM: windows
+    CHERE_INVOKING: "yes"
+    MSYSTEM: UCRT64
   script:
-    - source install/bin/hqr_env.sh
-    - (cd build &&
-       eval "ctest -D ExperimentalTest
-            -D ExperimentalCoverage
-            -D ExperimentalSubmit | tee ../hqr-tests.log")
-    - lcov --directory . --capture --output-file hqr.lcov
-    - lcov --summary hqr.lcov | tee -a hqr-gcov.log
+    - bash -lc .gitlab/build.sh | tee hqr-build-linux.log
+  artifacts:
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+    expire_in: 180 minutes
+    paths:
+      - hqr-build-linux.log
+  cache:
+    key: "${SYSTEM}-${VERSION}-$CI_COMMIT_REF_SLUG"
+    untracked: true
+    policy: push
 
-hqr_sonar:
-  stage: sonar
-  dependencies:
-  - hqr_build_linux
-  - hqr_test_linux
+hqr_test_linux:
+  stage: test
+  tags: ["docker", "large"]
+  extends: .only-master-mr
+  dependencies: [hqr_build_linux]
+  needs: [hqr_build_linux]
+  variables:
+    SYSTEM: linux
+  script:
+    - bash .gitlab/test.sh | tee hqr-test-linux.log
+  coverage: /^\s*lines......:\s*\d+.\d+\%/
   artifacts:
-    name: hqr_sonar
-    expire_in: 1 week
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+    expire_in: 180 minutes
     paths:
+      - hqr-test-linux.log
+      - junit.xml
+      - hqr.lcov
       - hqr-coverage.xml
-      - hqr-cppcheck.xml
-      - hqr-rats.xml
-      - sonar.log
-  script:
-    - ./analysis.sh
-  only:
-    - master@solverstack/hqr
+    reports:
+      junit: junit.xml
 
-hqr_build_macosx:
+hqr_test_macosx:
+  stage: test
   tags: ['macosx']
-  stage: build
+  extends: .only-master-mr
+  dependencies: [hqr_build_macosx]
+  needs: [hqr_build_macosx]
+  variables:
+    SYSTEM: macosx
+  script:
+    - bash .gitlab/test.sh | tee hqr-test-macosx.log
   artifacts:
-    name: hqr_build_macosx
-    expire_in: 42 minutes
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+    expire_in: 180 minutes
+    paths:
+      - hqr-test-macosx.log
+    reports:
+      junit: junit.xml
+  cache:
+    key: "${SYSTEM}-${VERSION}-$CI_COMMIT_REF_SLUG"
     untracked: true
-  script:
-    - cd build
-    - cmake .. -DCMAKE_INSTALL_PREFIX=${PWD}/../install -DBUILD_SHARED_LIBS=ON -DCMAKE_VERBOSE_MAKEFILE=ON | tee ../hqr-build.log
-    - ctest --no-compress-output -V -j 5
-            -D ExperimentalBuild
-            -D ExperimentalSubmit | tee -a hqr_build.log
-    - make install | tee -a ../hqr-build.log
-  only:
-    - branches
-    - master@solverstack/hqr
+    policy: pull
 
-hqr_test_macosx:
-  tags: ['macosx']
+hqr_test_windows:
   stage: test
-  dependencies:
-    - hqr_build_macosx
+  tags: ['windows']
+  extends: .only-master-mr
+  dependencies: [hqr_build_windows]
+  needs: [hqr_build_windows]
+  variables:
+    SYSTEM: windows
+    CHERE_INVOKING: "yes"
+    MSYSTEM: UCRT64
+  script:
+    - bash .gitlab/test.sh | tee hqr-test-windows.log
   artifacts:
-    name: hqr_test_macosx
-    expire_in: 42 minutes
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+    expire_in: 180 minutes
+    paths:
+      - hqr-test-windows.log
+    reports:
+      junit: junit.xml
+  cache:
+    key: "${SYSTEM}-${VERSION}-$CI_COMMIT_REF_SLUG"
     untracked: true
+    policy: pull
+
+sonarqube:
+  stage: analyze
+  tags: ["docker", "large"]
+  extends: .only-master-mr
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+      when: manual
+      allow_failure: true
+  dependencies: [hqr_build_linux,hqr_test_linux]
+  needs: [hqr_build_linux,hqr_test_linux]
   script:
-    - source install/bin/hqr_env.sh
-    - (cd build &&
-       eval "ctest -D ExperimentalTest
-            -D ExperimentalCoverage
-            -D ExperimentalSubmit | tee ../hqr-tests.log")
+    - ./.gitlab/analysis.sh
+  artifacts:
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+    expire_in: 180 minutes
+    paths:
+      - filelist.txt
+      - hqr-cppcheck.xml
+      - hqr-rats.xml
+      - sonar-project.properties
+      - sonar.log
+    when: always
 
-# pages:
-#   stage: deploy
-#   script:
-#     - cmake . -DBUILD_DOCUMENTATION=ON
-#     - make
-#     - mv docs/out/html ../public/
-#   artifacts:
-#     paths:
-#       - public
-#   only:
-#     - master@solverstack/hqr
+validate:
+  stage: validate
+  tags: ["docker", "large"]
+  extends: .only-mr
+  needs: [sonarqube]
+  parallel:
+    matrix:
+      - METRIC: [BUG, COVERAGE]
+  script:
+    - ./.gitlab/validate.sh $METRIC
+  allow_failure: true
diff --git a/analysis.sh b/.gitlab/analysis.sh
similarity index 58%
rename from analysis.sh
rename to .gitlab/analysis.sh
index a98ea1d..dc20068 100755
--- a/analysis.sh
+++ b/.gitlab/analysis.sh
@@ -13,7 +13,7 @@
 
 # Performs an analysis of HQR source code:
 # - we consider to be in HQR's source code root
-# - we consider having the coverage file hqr.lcov in the root directory
+# - we consider having the junit.xml file and the coverage file hqr-coverage.xml in the root directory
 # - we consider having cppcheck, rats, sonar-scanner programs available in the environment
 
 if [ $# -gt 0 ]
@@ -26,12 +26,6 @@ BUILDDIR=${BUILDDIR:-build}
 rm -f filelist.txt
 git ls-files | grep "\.[ch]$" > filelist.txt
 
-# Generate coverage analysis report
-python3 /usr/local/lib/python3.8/dist-packages/lcov_cobertura.py hqr.lcov --output hqr-coverage.xml
-
-# to get it displayed and captured by gitlab to expose the badge on the main page
-cat ./hqr-gcov.log
-
 # Undefine this because not relevant in our configuration
 export UNDEFINITIONS="-UWIN32 -UWIN64 -U_MSC_EXTENSIONS -U_MSC_VER -U__SUNPRO_C -U__SUNPRO_CC -U__sun -Usun -U__cplusplus"
 
@@ -41,39 +35,38 @@ cppcheck -v -f --project=build/compile_commands.json --language=c --platform=uni
 # run rats analysis
 rats -w 3 --xml  `cat filelist.txt` > hqr-rats.xml
 
-# Set the default for the project key
-SONARQUBE_PROJECTKEY=${SONARQUBE_PROJECTKEY:-hiepacs:hqr:gitlab:master}
-
 # create the sonarqube config file
 cat > sonar-project.properties << EOF
 sonar.host.url=https://sonarqube.inria.fr/sonarqube
-sonar.login=$SONARQUBE_LOGIN
 
 sonar.links.homepage=$CI_PROJECT_URL
 sonar.links.scm=$CI_REPOSITORY_URL
 sonar.links.ci=$CI_PROJECT_URL/pipelines
 sonar.links.issue=$CI_PROJECT_URL/issues
 
-sonar.projectKey=$SONARQUBE_PROJECTKEY
+sonar.projectKey=${CI_PROJECT_NAMESPACE}:${CI_PROJECT_NAME}
 sonar.projectDescription=Library for hierarchical QR/LQ reduction trees
-sonar.projectVersion=master
+sonar.projectVersion=0.1
+
+sonar.scm.disabled=false
+sonar.scm.provider=git
+sonar.scm.exclusions.disabled=true
 
+sonar.sourceEncoding=UTF-8
 sonar.sources=include, src, testings
 sonar.inclusions=`cat filelist.txt | xargs echo | sed 's/ /, /g'`
-sonar.sourceEncoding=UTF-8
-sonar.c.errorRecoveryEnabled=true
-sonar.c.compiler.charset=UTF-8
-sonar.c.compiler.parser=GCC
-sonar.c.compiler.regex=^(.*):(\\d+):\\d+: warning: (.*)\\[(.*)\\]$
-sonar.c.compiler.reportPath=hqr-build.log
-sonar.c.coverage.reportPath=hqr-coverage.xml
-sonar.c.cppcheck.reportPath=hqr-cppcheck.xml
-sonar.c.rats.reportPath=hqr-rats.xml
-sonar.c.jsonCompilationDatabase=${BUILDDIR}/compile_commands.json
-sonar.lang.patterns.c++: **/*.cxx,**/*.cpp,**/*.cc,**/*.hxx,**/*.hpp,**/*.hh
-sonar.lang.patterns.c: **/*.c,**/*.h
-sonar.lang.patterns.python: **/*.py
+
+sonar.cxx.jsonCompilationDatabase=${BUILDDIR}/compile_commands.json
+sonar.cxx.file.suffixes=.h,.c
+sonar.cxx.errorRecoveryEnabled=true
+sonar.cxx.gcc.encoding=UTF-8
+sonar.cxx.gcc.regex=(?<file>.*):(?<line>[0-9]+):[0-9]+:\\\x20warning:\\\x20(?<message>.*)\\\x20\\\[(?<id>.*)\\\]
+sonar.cxx.gcc.reportPaths=hqr-build*.log
+sonar.cxx.xunit.reportPaths=junit.xml
+sonar.cxx.cobertura.reportPaths=hqr-coverage.xml
+sonar.cxx.cppcheck.reportPaths=hqr-cppcheck.xml
+sonar.cxx.rats.reportPaths=hqr-rats.xml
 EOF
 
-# run sonar analysis + publish on sonarqube-dev
-sonar-scanner -X > sonar.log
+# run sonar analysis + publish on sonarqube
+sonar-scanner -X -Dsonar.login=$SONARQUBE_LOGIN > sonar.log
diff --git a/.gitlab/build.sh b/.gitlab/build.sh
new file mode 100755
index 0000000..86efc5c
--- /dev/null
+++ b/.gitlab/build.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+fatal() {
+    echo "$0: error occurred, exit"
+    exit 1
+}
+
+set -x
+
+if [[ "$SYSTEM" == "linux" ]]; then
+
+  # on linux we perform a coverage analysis
+  cmake -B build -S . -DCMAKE_INSTALL_PREFIX=$PWD/install \
+        -DBUILD_SHARED_LIBS=ON -DCMAKE_VERBOSE_MAKEFILE=ON \
+        -DCMAKE_C_FLAGS="-O0 -g -fPIC --coverage -Wall -fdiagnostics-show-option -fno-inline" \
+        -DCMAKE_EXE_LINKER_FLAGS="--coverage" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON || fatal
+
+else
+
+  # no coverage analysis on other platforms
+  cmake -B build -S . -DCMAKE_INSTALL_PREFIX=$PWD/install \
+        -DBUILD_SHARED_LIBS=ON -DCMAKE_VERBOSE_MAKEFILE=ON || fatal
+
+fi
+cmake --build build -j 4 || fatal
+cmake --install build || fatal
diff --git a/.gitlab/test.sh b/.gitlab/test.sh
new file mode 100755
index 0000000..2edbdc7
--- /dev/null
+++ b/.gitlab/test.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+fatal() {
+    echo "$0: error occurred, exit"
+    exit 1
+}
+
+set -x
+
+if [[ "$SYSTEM" == "linux" ]]; then
+  source install/bin/hqr_env.sh || fatal
+fi
+cd build
+if [[ "$SYSTEM" == "windows" ]]; then
+  # this is required with BUILD_SHARED_LIBS=ON
+  export PATH="/c/Windows/WinSxS/x86_microsoft-windows-m..namespace-downlevel_31bf3856ad364e35_10.0.19041.1_none_21374cb0681a6320":$PATH
+  export PATH=$PWD/src:$PATH
+fi
+ctest --output-on-failure --no-compress-output -T Test --output-junit ../junit.xml || fatal
+if [[ "$SYSTEM" == "linux" ]]; then
+  # clang is used on macosx and it is not compatible with --coverage option
+  # so that we can only make the coverage report on the linux runner with gcc
+  cd ..
+  lcov --capture --directory build -q --output-file hqr.lcov
+  lcov --summary hqr.lcov
+  lcov_cobertura hqr.lcov --output hqr-coverage.xml
+fi
diff --git a/.gitlab/validate.sh b/.gitlab/validate.sh
new file mode 100755
index 0000000..daf827c
--- /dev/null
+++ b/.gitlab/validate.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+###
+#
+#  @file validate.sh
+#  @copyright 2023-2023 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
+#                       Univ. Bordeaux. All rights reserved.
+#
+#  @version 1.0.0
+#  @author Mathieu Faverge
+#  @author Florent Pruvost
+#  @date 2023-09-22
+#
+###
+
+# Check some metrics on sonarqube (https://sonarqube.inria.fr/sonarqube/)
+# and depending on the value return 0 (success) or 1 (failure).
+
+if [ $# -gt 0 ]; then
+  METRIC=$1
+fi
+METRIC=${METRIC:-BUG}
+
+if [[ -z $CI_MERGE_REQUEST_IID || -z $CI_PROJECT_NAMESPACE || -z $CI_PROJECT_NAME ]]; then
+  echo "One of the variables CI_MERGE_REQUEST_IID, CI_PROJECT_NAMESPACE,
+  CI_PROJECT_NAME is empty. This script must be used during a gitlab merge
+  request only -> Failure."
+  exit 1
+fi
+
+if [[ -z $SONARQUBE_LOGIN ]]; then
+  echo "SONARQUBE_LOGIN is empty, please give a valid sonarqube user's token,
+  with permissions set on the project -> Failure."
+  exit 1
+fi
+
+if [[ $METRIC == "BUG" ]]; then
+  BUG=`curl -u $TOKEN: -X GET "https://sonarqube.inria.fr/sonarqube/api/measures/component?component=${CI_PROJECT_NAMESPACE}%3A${CI_PROJECT_NAME}&pullRequest=${CI_MERGE_REQUEST_IID}&metricKeys=new_bugs" |jq '.component.measures[0].period.value' | sed -e "s#\"##g"`
+  if [[ $BUG > 0 ]]; then
+    echo "%{BUG} new bugs detected by Sonarqube -> Failure."
+    exit 1
+  else
+    echo "No new bugs detected by Sonarqube -> Success."
+    exit 0
+  fi
+elif [[ $METRIC == "COVERAGE" ]]; then
+  COV=`curl -u $TOKEN: -X GET "https://sonarqube.inria.fr/sonarqube/api/measures/component?component=${CI_PROJECT_NAMESPACE}%3A${CI_PROJECT_NAME}&pullRequest=${CI_MERGE_REQUEST_IID}&metricKeys=new_coverage" |jq '.component.measures[0].period.value' | sed -e "s#\"##g"`
+  if [[ $COV == "null" || -z $COV  ]]; then
+    echo "Coverage is empty, certainly that there are no lines of new code (considered during the analysis) to compare -> Success."
+  else
+    if [[ $COV < 80 ]]; then
+      echo "Coverage on new lines is ${COV}%, which is < 80% -> Failure."
+      exit 1
+    else
+      echo "Coverage on new lines is ${COV}%, which is >= 80% -> Success."
+      exit 0
+    fi
+  fi
+fi
diff --git a/README.org b/README.org
index ac371c5..4780442 100644
--- a/README.org
+++ b/README.org
@@ -3,8 +3,6 @@
 #+OPTIONS: H:3 num:t \n:nil @:t ::t |:t _:nil ^:nil -:t f:t *:t <:t
 #+OPTIONS: TeX:t LaTeX:t skip:nil d:nil pri:nil tags:not-in-toc html-style:nil
 
-[[https://gitlab.inria.fr/solverstack/hqr/pipelines][https://gitlab.inria.fr/solverstack/hqr/badges/master/pipeline.svg]] [[https://gitlab.inria.fr/solverstack/hqr/commits/master][https://gitlab.inria.fr/solverstack/hqr/badges/master/coverage.svg]]
-
 HQR is a C library providing tools to generate hierarchical trees
 adapted to 2D block-cyclic data distribution and algorithms based on
 tiled QR/algorithms. This library is used in [[https://gitlab.inria.fr/solverstack/chameleon][Chameleon]] and [[https://bitbucket.org/mfaverge/parsec][DPLASMA]].
-- 
GitLab