diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index d93b32267319..5449706e3005 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -26,7 +26,6 @@ jobs: - name: Run checks run: | - zsh Scripts/developer_scripts/test_merge_of_branch HEAD + bash Scripts/developer_scripts/test_merge_of_branch HEAD >> $GITHUB_STEP_SUMMARY #test dependencies bash Scripts/developer_scripts/cgal_check_dependencies.sh --check_headers /usr/bin/doxygen - diff --git a/Scripts/developer_scripts/test_merge_of_branch b/Scripts/developer_scripts/test_merge_of_branch index 44647a59ae45..6e0c7fcb5de4 100755 --- a/Scripts/developer_scripts/test_merge_of_branch +++ b/Scripts/developer_scripts/test_merge_of_branch @@ -1,277 +1,399 @@ -#!/bin/zsh +#!/usr/bin/env bash -zmodload zsh/stat -setopt extendedglob +shopt -s nullglob extglob -dir=`dirname "$0"` +dir=$(dirname "$0") PATH=$dir:$PATH -if [ -z "`git --version`" ]; then +exit_code=0 + +heading() +{ + printf '\n## %s\n' "$*" +} + +list_cgal_packages() +{ + find ./*/package_info -name 'license.txt' | awk -F "/" '{print $2}' +} + +list_cgal_headers() +{ + for pkg in $(list_cgal_packages); do + if [ -e "${pkg}/include" ]; then + find "${pkg}/include" -type f \( -name '*.h' -o -name '*.hpp' \) + fi + done +} + +report_error_with_list_of_files() +{ + local exit_code=0 + local files + local msg + msg=$1 + files=$2 + if [ -n "${files}" ]; then + # shellcheck disable=SC2016 + printf '\n%s\n\n```\n%s\n```\n' "$msg" "${files}" + exit_code=1 + fi + return $exit_code +} + +setup_git_command() +{ + if ! git --version > /dev/null 2>&1; then echo 'This script needs git!' >&2 exit 1 -fi + fi +} -git=git -if hub --version 2> /dev/null > /dev/null; then - case "$(hub --version)" in - *hub\ version*) git=hub;; - esac -fi +require_branch_argument() +{ + if [ -z "$branch" ]; then + echo 'Usage: test_merge_of_branch ' >&2 + exit 1 + fi +} -if ! ${git} diff --exit-code > /dev/null || ! ${git} diff --staged --exit-code > /dev/null ; then +ensure_clean_worktree() +{ + if ! git diff --exit-code > /dev/null || ! git diff --staged --exit-code > /dev/null; then echo 'Your working directory contains local modifications!' >&2 exit 1 -fi + fi -cd "`${git} rev-parse --show-toplevel`" + cd "$(git rev-parse --show-toplevel)" || exit 1 -if [ -n "`${git} ls-files --other --exclude-standard --exclude=build\*`" ]; then + if [ -n "$(git ls-files --other --exclude-standard --exclude=build\*)" ]; then echo 'You have untracked files!' >&2 exit 1 -fi - -branch=$1 -# if ! ${git} rev-parse "$branch" > /dev/null 2>&1; then -# echo "\"$branch\" is not a valid branch!" >&2 -# exit 1 -#fi - -trap 'echo "(aborting the merge now)" && ${git} merge --abort' EXIT - -echo ".. Test merging of the branch \"$branch\"..." -${git} merge --no-ff --no-commit "$branch" && ${git} status && ${git} diff --staged --summary + fi +} -if true; then # REMOVE -echo '.. Testing permissions...' -detect_wrong_permissions || exit 1 +test_merge() +{ + trap 'echo "(aborting the merge now)" && git merge --abort' EXIT -echo '.. Testing licenses...' -detect_packages_licenses || exit 1 -if ! ${git} diff --exit-code > /dev/null; then - echo '=> There are modifications in licenses (check if all modified/added headers files contain a valid SPDX tag compatible with the one in package_info/PKG/license and that no license text is present):' - ${git} status --porcelain + heading "Test merging of the branch \"$branch\"" + if ! git merge --no-ff --no-commit "$branch"; then exit 1 -fi + fi + git status + git diff --staged --summary +} + +test_permissions() +{ + heading 'Testing permissions' + detect_wrong_permissions +} + +test_licenses() +{ + heading 'Testing licenses' + detect_packages_licenses || return 1 + if ! git diff --exit-code ./*/package_info > /dev/null; then + echo '=> There are modifications in licenses (check if all modified/added headers files contain a valid SPDX tag compatible with the one in package_info/PKG/license and that no license text is present):' + git status --porcelain + return 1 + fi +} -fi # REMOVE +test_cmake_configuration() +{ + heading 'Testing CMake configuration' + if [ -d build ] && \ + [ -f build/CMakeCache.txt ] && \ + [ "$(grep -c -iE 'WITH_(demos|examples|tests):BOOL=(ON|TRUE|1)' build/CMakeCache.txt)" -eq "3" ]; then + local cmake_output + local cmake_exit_code -if [ -d build ] && \ - [ -f build/CMakeCache.txt ] && \ - [ "$(grep -c -iE 'WITH_(demos|examples|tests):BOOL=(ON|TRUE|1)' build/CMakeCache.txt)" -eq "3" ]; then - echo '.. Testing CMake configuration...' cmake_output=$(cmake build 2>&1) cmake_exit_code=$? if [ "$cmake_exit_code" -ne 0 ]; then - echo 'CMake error:' - printf '%s\n' "${cmake_output}" - exit 1 - else - echo OK + report_error_with_list_of_files 'CMake configuration failed:' "${cmake_output}" + return 1 fi -else - echo '.. Skip the CMake test (please configure a build/ CMake binary directory,' - echo 'with -DWITH_tests:BOOL=TRUE -DWITH_examples:BOOL=TRUE -DWITH_demos:BOOL=TRUE)' -fi - -echo '.. List of new files (please check that it is the expected list:' -new_files=( ${(f)"$(${git} status --porcelain | grep -E '^A')"} ) -if [ ${#new_files[@]} -gt 0 ]; then - string=${(F)new_files} - echo $string -else + else + echo + echo 'Skip the CMake test (please configure a build/ CMake binary directory,' + # shellcheck disable=SC2016 + echo 'with `-DWITH_tests:BOOL=TRUE -DWITH_examples:BOOL=TRUE -DWITH_demos:BOOL=TRUE`)' + fi +} + +list_new_files() +{ + local new_files + + heading 'List of new files (please check that it is the expected list):' + mapfile -t new_files < <(git status --porcelain | awk '/^A/ { print $0 }') + if [ ${#new_files[@]} -gt 0 ]; then + printf '%s\n' "${new_files[@]}" + else + echo echo 'No new files.' -fi - -#check cmake scripts of tests, examples are present -echo '.. Checking if all CMakeLists.txt are present...' -for i in `ls -d ^build*/examples/*/ ^build*/test/*/ ^build*/demo/^(icons|resources)/`; do - if ! [ -f $i/CMakeLists.txt ]; then - echo "Error: $i/CMakeLists.txt does not exist!" - exit 1 fi -done +} + +check_cmakelists_presence() +{ + local i + local exit_code=0 + + heading 'Checking if all CMakeLists.txt are present' + for i in !(build*)/@(examples|test|demo)/*/; do + case $(basename "$i") in + icons|resources) + continue + ;; + esac -#check all tests/examples are tested -echo '.. Checking if all .cpp files in examples/test are compiled...' -CML_ERRORS="" -for CML in `grep -L "GLOB cppfiles" */test/*/CMakeLists.txt */examples/*/CMakeLists.txt`; do - DIR=`dirname $CML` + if ! [ -f "$i/CMakeLists.txt" ]; then + echo "Error: $i/CMakeLists.txt does not exist!" + exit_code=1 + fi + done + return $exit_code +} + +check_cpp_files_are_tested() +{ + local CML + local CML_ERRORS="" + local DIR + local i + local f + + heading 'Checking if all .cpp files in examples/test are compiled' + while IFS= read -r CML; do + DIR=$(dirname "$CML") + + if [ "./Arrangement_on_surface_2/test/Arrangement_on_surface_2" = "$DIR" ]; then + continue + fi + if [ "./Installation/test/Installation" = "$DIR" ]; then + continue + fi - if [ "Arrangement_on_surface_2/test/Arrangement_on_surface_2" = $DIR ]; then - continue + for i in "$DIR"/*.cpp; do + f=$(basename "$i" .cpp) + if ! grep -q "$f" "$CML"; then + CML_ERRORS+=$'\n' + CML_ERRORS+="$i is not tested!" + fi + done + done < <(grep -L "GLOB cppfiles" ./*/test/*/CMakeLists.txt ./*/examples/*/CMakeLists.txt) + + report_error_with_list_of_files 'Some .cpp files are not tested:' "${CML_ERRORS}" +} + +pkg_of_cmakelist() { + awk -F "/" '{print $3}' <<< "$1" +} + +check_cmake_project_names() +{ + declare -a project_names + local pkg + + heading 'Checking if all CMakeLists.txt project names are correct' + mapfile -t project_names < <( { + for i in !(build*)/test/*/CMakeLists.txt; do pkg=$(pkg_of_cmakelist "$i"); grep -E "${pkg}_Tests[[:space:]]*\)" -L "$i"; done + for i in !(build*)/examples/*/CMakeLists.txt; do pkg=$(pkg_of_cmakelist "$i"); grep -E "${pkg}_Examples[[:space:]]*\)" -L "$i"; done + for i in !(build*)/demo/*/CMakeLists.txt; do pkg=$(pkg_of_cmakelist "$i"); grep -E "${pkg}_Demo[[:space:]]*\)" -L "$i"; done + } ) + + report_error_with_list_of_files 'CMakeLists with incorrect project name:' "${project_names[@]}" +} + +check_cmake_minimum_required() +{ + local res + + # shellcheck disable=SC2016 + heading 'Checking if all CMakeLists.txt starts with `cmake_minimum_required`' + cmr=$(for i in !(build*)/@(test|examples|demo|benchmark*)/*/CMakeLists.txt; do res=$(grep -Ev "^[[:space:]]*(#|$)" "$i" | head -n 1 | grep -v cmake_minimum_required); if [ -n "${res}" ]; then echo "$i"; fi; done) + report_error_with_list_of_files 'CMakeLists with issues:' "${cmr}" +} + +check_header_spdx_identifiers() +{ + local files_without_spdx + + heading 'Checking SPDX license identifier presence in header files' + files_without_spdx=$(list_cgal_headers | xargs -r grep -L "SPDX-License-Identifier") + report_error_with_list_of_files 'The following files do not have a SPDX license identifier:' "${files_without_spdx}" +} + +check_header_id_tags() +{ + local files_without_id_tag + + # shellcheck disable=SC2016 + heading 'Checking `$Id$` tag presence in header files' + # shellcheck disable=SC2016 + files_without_id_tag=$(list_cgal_headers | xargs -r grep -L -F '$Id$') + # shellcheck disable=SC2016 + report_error_with_list_of_files 'The following files do not have a `$Id$` tag:' "${files_without_id_tag}" +} + +check_header_url_tags() +{ + local files_without_url_tag + + # shellcheck disable=SC2016 + heading 'Checking `$URL$` tag presence in header files' + # shellcheck disable=SC2016 + files_without_url_tag=$(list_cgal_headers | xargs -r grep -L -F '$URL$') + # shellcheck disable=SC2016 + report_error_with_list_of_files 'The following files do not have a `$URL$` tag:' "${files_without_url_tag}" +} + +check_gpl_header_license_includes() +{ + local files_without_license_include + + heading 'Checking include directives in GPL header files' + files_without_license_include=$(list_cgal_headers | xargs -r grep -E -l '^#\s*define\s+CGAL_.*_H\s*$' | xargs -r grep -E -l 'SPDX-License-Identifier.*[ (]GPL' | xargs -r grep -L '#include \nstruct A{};\nint main(){\nA a;\n' > main.cpp + for i in $nps; do + echo " CGAL::parameters::${i}(a).${i}(a);" >> main.cpp + done + echo '}' >> main.cpp + if ! g++ -DCGAL_NO_STATIC_ASSERTION_TESTS -I"$git_dir"/STL_Extension/include -I"$git_dir"/Stream_support/include -I"$git_dir"/Installation/include main.cpp; then + echo 'ERROR: At least one documented named parameter does not exist' + cd - > /dev/null || exit 1 + rm -r "$tmp_dir" + exit_code=1 fi - if [ "Installation/test/Installation" = $DIR ]; then - continue + cd - > /dev/null || exit 1 + rm -r "$tmp_dir" + return $exit_code +} + +create_dummy_merge_commit() +{ + local conflicts + + current_rev=$(git rev-parse HEAD) + trap 'echo "(aborting the merge now)" && git reset --quiet --hard "$current_rev"' EXIT + + heading 'Dummy merge commit' + mapfile -t conflicts < <(git status --porcelain | awk '/^[^ ][^ ]/ {print $2}') + if [ ${#conflicts[@]} -gt 0 ]; then + git add -- "${conflicts[@]}" || exit 1 fi + git commit --allow-empty -m 'dummy merge commit' || exit 1 +} + +report_bundle_size() +{ + local size + + heading 'Size of the gzipped bundle' + trap 'echo "(aborting the merge now)" && rm -f bundle.gz && git reset --quiet --hard "$current_rev"' EXIT + git bundle create bundle "${current_rev}..HEAD" > /dev/null 2>&1 && gzip bundle || exit 1 + size=$(stat -c '%s' bundle.gz) + echo "=> $size" +} + +display_result() +{ + if [ $? -eq 0 ]; then + printf '\n%s\n' "✅" + else + printf '\n%s\n' "❌" + exit_code=1 + fi +} + +main() +{ + branch=$1 + if [ -n "$branch" ]; then + setup_git_command + require_branch_argument + ensure_clean_worktree + test_merge + fi + test_permissions; display_result + test_licenses; display_result + test_cmake_configuration; display_result + list_new_files; display_result + check_cmakelists_presence; display_result + check_cpp_files_are_tested; display_result + check_cmake_project_names; display_result + check_cmake_minimum_required; display_result + check_header_spdx_identifiers; display_result + check_header_id_tags; display_result + check_header_url_tags; display_result + check_gpl_header_license_includes; display_result + check_non_utf8_files; display_result + check_tab_characters; display_result + check_trailing_whitespaces; display_result + check_documentation_headers; display_result + check_documented_named_parameters; display_result + if [ -n "$branch" ]; then + create_dummy_merge_commit + report_bundle_size + fi +} - for i in `ls ${DIR}/*.cpp`; do - f=`basename $i .cpp` - if ! grep -q $f ${CML}; then - CML_ERRORS=`echo "${CML_ERRORS}\n$i is not tested!"` - fi - done -done - - -if [ -n "${CML_ERRORS}" ]; then - echo -n "Some tests/examples are not tested:" - echo ${CML_ERRORS} - exit 1 -fi - -# check project in cmake scripts is correct -echo '.. Checking if all CMakeLists.txt project names are correct...' -project_name_tests=$(for i in ^build*/test/*/CMakeLists.txt; do pkg=$(echo $i | awk -F "/" '{print $3}'); grep -E "${pkg}_Tests\s*\)" -L $i; done) -project_name_examples=$(for i in ^build*/examples/*/CMakeLists.txt; do pkg=$(echo $i | awk -F "/" '{print $3}'); grep -E "${pkg}_Examples\s*\)" -L $i; done) -project_name_demo=$(for i in ^build*/demo/*/CMakeLists.txt; do pkg=$(echo $i | awk -F "/" '{print $3}'); grep -E "${pkg}_Demo\s*\)" -L $i; done) - -if [ -n "${project_name_tests}" ]; then - echo "CMakeLists with incorrect project name" - echo ${project_name_tests} - exit 1 -fi - -if [ -n "${project_name_examples}" ]; then - echo "CMakeLists with incorrect project name" - echo ${project_name_examples} - exit 1 -fi - -if [ -n "${project_name_demo}" ]; then - echo "CMakeLists with incorrect project name" - echo ${project_name_demo} - exit 1 -fi - -# check minimal version is the first instruction in cmake scripts -echo '.. Checking if all CMakeLists.txt starts with cmake_minimum_required...' -cmr_tests=$(for i in ^build*/test/*/CMakeLists.txt; do pkg=$(echo $i | awk -F "/" '{print $3}'); res=$(grep -Ev "^\s*#" $i | grep -v "^\s*$" | head -n 1 | grep -v cmake_minimum_required); if [ -n "${res}" ]; then echo $pkg; fi; done) -cmr_examples=$(for i in ^build*/examples/*/CMakeLists.txt; do pkg=$(echo $i | awk -F "/" '{print $3}'); res=$(grep -Ev "^s*#" $i | grep -v "^\s*$" | head -n 1 | grep -v cmake_minimum_required); if [ -n "${res}" ]; then echo $pkg; fi; done) -cmr_demo=$(for i in ^build*/demo/*/CMakeLists.txt; do pkg=$(echo $i | awk -F "/" '{print $3}'); res=$(grep -Ev "^s*#" $i | grep -v "^\s*$" | head -n 1 | grep -v cmake_minimum_required); if [ -n "${res}" ]; then echo $pkg; fi; done) - -if [ -n "${cmr_tests}" ]; then - echo "CMakeLists in test with issues:" - echo ${cmr_tests} - exit 1 -fi - -if [ -n "${cmr_examples}" ]; then - echo "CMakeLists in examples with issues:" - echo ${cmr_examples} - exit 1 -fi - -if [ -n "${cmr_demo}" ]; then - echo "CMakeLists in demo with issues:" - echo ${cmr_demo} - exit 1 -fi - -#check header files without SPDX license identifier -echo '.. Checking SPDX license identifier presence in header files...' -file_without_SPDX_identifiers=$(for pkg in `find */package_info -name 'license.txt' | awk -F "/" '{print $1}'`; do if [ -e ${pkg}/include ]; then find ${pkg}/include -type f \( -name '*.h' -o -name '*.hpp' \) | xargs -r grep -L "SPDX-License-Identifier"; fi; done) -if [ -n "${file_without_SPDX_identifiers}" ]; then - echo "The following files do not have a SPDX license identifier:" - echo ${file_without_SPDX_identifiers} - exit 1 -fi - -#check header files without $Id$ tags -echo '.. Checking $Id$ tag presence in header files...' -file_without_Id_tag=$(for pkg in `find */package_info -name 'license.txt' | awk -F "/" '{print $1}'`; do if [ -e ${pkg}/include ]; then find ${pkg}/include -type f \( -name '*.h' -o -name '*.hpp' \) | xargs -r grep -L '$Id\$'; fi; done) -if [ -n "${file_without_Id_tag}" ]; then - echo 'The following files do not have a $Id$ tag:' - echo ${file_without_Id_tag} - exit 1 -fi - -#check header files without $URL$ tags -echo '.. Checking $URL$ tag presence in header files...' -file_without_URL_tag=$(for pkg in `find */package_info -name 'license.txt' | awk -F "/" '{print $1}'`; do if [ -e ${pkg}/include ]; then find ${pkg}/include -type f \( -name '*.h' -o -name '*.hpp' \) | xargs -r grep -L '$URL\$'; fi; done) -if [ -n "${file_without_URL_tag}" ]; then - echo 'The following files do not have a $URL$:' - echo ${file_without_URL_tag} - exit 1 -fi - -#check GPL header files without license include directive -echo '.. Checking include directives in GPL header files...' -file_without_license_include=$(for pkg in `find */package_info -name 'license.txt' | awk -F "/" '{print $1}'`; do if [ -e ${pkg}/include ]; then find ${pkg}/include -type f -name '*.h' | xargs -r grep -E -l "^#\s*define\s+CGAL_.*_H\s*$" | xargs -r grep -E -l "SPDX-License-Identifier.*[ (]GPL" | xargs -r grep -L "#include \nstruct A{};\nint main(){\nA a;\n" > main.cpp -for i in `echo $nps`; do - echo " CGAL::parameters::${i}(a).${i}(a);" >> main.cpp -done -echo "}" >> main.cpp -if ! g++ -DCGAL_NO_STATIC_ASSERTION_TESTS -I$git_dir/STL_Extension/include -I$git_dir/Stream_support/include -I$git_dir/Installation/include main.cpp; then - echo "ERROR: At least one documented named parameter does not exist" - cd - - rm -r $tmp_dir - exit 1; -fi -cd - -rm -r $tmp_dir - -current_rev=$(${git} rev-parse HEAD) -trap 'echo "(aborting the merge now)" && ${git} reset --quiet --hard ${current_rev}' EXIT - -echo '.. Dummy merge commit...' -conflicts=( ${(f)"$(${git} status --porcelain | awk '/^[^ ][^ ]/ {print $2}')"} ) -if [ ${#conflicts[@]} -gt 0 ]; then ${git} add $conflicts || exit 1; fi -${git} commit --allow-empty -m 'dummy merge commit' || exit 1 - -echo '.. Size of the gzipped bundle' -trap 'echo "(aborting the merge now)" && rm bundle.gz && ${git} reset --quiet --hard ${current_rev}' EXIT -${git} bundle create bundle ${current_rev}..HEAD > /dev/null 2>&1 && gzip bundle || exit 1 -size=$(zstat +size bundle.gz) -echo "=> $size" -exit 0 +main "$@" +exit "$exit_code" diff --git a/Shape_regularization/benchmark/Shape_regularization/CMakeLists.txt b/Shape_regularization/benchmark/Shape_regularization/CMakeLists.txt index d9dd81993d47..6e57c51c255c 100644 --- a/Shape_regularization/benchmark/Shape_regularization/CMakeLists.txt +++ b/Shape_regularization/benchmark/Shape_regularization/CMakeLists.txt @@ -1,9 +1,8 @@ # Created by the script cgal_create_CMakeLists. # This is the CMake script for compiling a set of CGAL applications. -project(Shape_regularization_Benchmarks) - cmake_minimum_required(VERSION 3.12...3.31) +project(Shape_regularization_Benchmarks) find_package(CGAL REQUIRED COMPONENTS Core) diff --git a/Surface_mesh_topology/benchmark/Surface_mesh_topology/CMakeLists.txt b/Surface_mesh_topology/benchmark/Surface_mesh_topology/CMakeLists.txt index 287b89df09e5..0f2ef45ba96f 100644 --- a/Surface_mesh_topology/benchmark/Surface_mesh_topology/CMakeLists.txt +++ b/Surface_mesh_topology/benchmark/Surface_mesh_topology/CMakeLists.txt @@ -1,7 +1,7 @@ -project(Surface_mesh_topology_Benchmarks) - cmake_minimum_required(VERSION 3.12...3.31) +project(Surface_mesh_topology_Benchmarks) + find_package(CGAL REQUIRED) # add_compile_definitions(CGAL_TRACE_PATH_TESTS)