1#!/usr/bin/env bash 2set -o errexit -o pipefail -o posix 3 4# Copyright (c) 2019-2024 Cosmin Truta. 5# 6# Use, modification and distribution are subject to the MIT License. 7# Please see the accompanying file LICENSE_MIT.txt 8# 9# SPDX-License-Identifier: MIT 10 11# shellcheck source=ci/lib/ci.lib.sh 12source "$(dirname "$0")/lib/ci.lib.sh" 13cd "$CI_TOPLEVEL_DIR" 14 15CI_SRC_DIR="$CI_TOPLEVEL_DIR" 16CI_OUT_DIR="$CI_TOPLEVEL_DIR/out" 17CI_BUILD_DIR="$CI_OUT_DIR/ci_verify_cmake.$CI_TARGET_SYSTEM.$CI_TARGET_ARCH.build" 18CI_INSTALL_DIR="$CI_OUT_DIR/ci_verify_cmake.$CI_TARGET_SYSTEM.$CI_TARGET_ARCH.install" 19 20# Keep the following relative paths in sync with the absolute paths. 21# We use them for the benefit of native Windows tools that might be 22# otherwise confused by the path encoding used by Bash-on-Windows. 23CI_BUILD_TO_SRC_RELDIR="../.." 24CI_BUILD_TO_INSTALL_RELDIR="../ci_verify_cmake.$CI_TARGET_SYSTEM.$CI_TARGET_ARCH.install" 25 26function ci_init_build { 27 # Ensure that the mandatory variables are initialized. 28 CI_CMAKE="${CI_CMAKE:-cmake}" 29 CI_CTEST="${CI_CTEST:-ctest}" 30 CI_CMAKE_BUILD_TYPE="${CI_CMAKE_BUILD_TYPE:-Release}" 31 if [[ $CI_CMAKE_GENERATOR == "Visual Studio"* ]] 32 then 33 # Clean up incidental mixtures of Windows and Bash-on-Windows 34 # environment variables, to avoid confusing MSBuild. 35 [[ $TEMP && ( $Temp || $temp ) ]] && unset TEMP 36 [[ $TMP && ( $Tmp || $tmp ) ]] && unset TMP 37 # Ensure that CI_CMAKE_GENERATOR_PLATFORM is initialized for this generator. 38 [[ $CI_CMAKE_GENERATOR_PLATFORM ]] || { 39 ci_err_internal "missing \$CI_CMAKE_GENERATOR_PLATFORM" 40 } 41 elif [[ -x $(command -v ninja) ]] 42 then 43 CI_CMAKE_GENERATOR="${CI_CMAKE_GENERATOR:-Ninja}" 44 fi 45} 46 47function ci_trace_build { 48 ci_info "## START OF CONFIGURATION ##" 49 ci_info "build arch: $CI_BUILD_ARCH" 50 ci_info "build system: $CI_BUILD_SYSTEM" 51 [[ "$CI_TARGET_SYSTEM.$CI_TARGET_ARCH" != "$CI_BUILD_SYSTEM.$CI_BUILD_ARCH" ]] && { 52 ci_info "target arch: $CI_TARGET_ARCH" 53 ci_info "target system: $CI_TARGET_SYSTEM" 54 } 55 ci_info "source directory: $CI_SRC_DIR" 56 ci_info "build directory: $CI_BUILD_DIR" 57 ci_info "install directory: $CI_INSTALL_DIR" 58 ci_info "environment option: \$CI_CMAKE: '$CI_CMAKE'" 59 ci_info "environment option: \$CI_CMAKE_GENERATOR: '$CI_CMAKE_GENERATOR'" 60 ci_info "environment option: \$CI_CMAKE_GENERATOR_PLATFORM: '$CI_CMAKE_GENERATOR_PLATFORM'" 61 ci_info "environment option: \$CI_CMAKE_BUILD_TYPE: '$CI_CMAKE_BUILD_TYPE'" 62 ci_info "environment option: \$CI_CMAKE_BUILD_FLAGS: '$CI_CMAKE_BUILD_FLAGS'" 63 ci_info "environment option: \$CI_CMAKE_TOOLCHAIN_FILE: '$CI_CMAKE_TOOLCHAIN_FILE'" 64 ci_info "environment option: \$CI_CMAKE_VARS: '$CI_CMAKE_VARS'" 65 ci_info "environment option: \$CI_CTEST: '$CI_CTEST'" 66 ci_info "environment option: \$CI_CTEST_FLAGS: '$CI_CTEST_FLAGS'" 67 ci_info "environment option: \$CI_CC: '$CI_CC'" 68 ci_info "environment option: \$CI_CC_FLAGS: '$CI_CC_FLAGS'" 69 ci_info "environment option: \$CI_AR: '$CI_AR'" 70 ci_info "environment option: \$CI_RANLIB: '$CI_RANLIB'" 71 ci_info "environment option: \$CI_SANITIZERS: '$CI_SANITIZERS'" 72 ci_info "environment option: \$CI_FORCE: '$CI_FORCE'" 73 ci_info "environment option: \$CI_NO_TEST: '$CI_NO_TEST'" 74 ci_info "environment option: \$CI_NO_INSTALL: '$CI_NO_INSTALL'" 75 ci_info "environment option: \$CI_NO_CLEAN: '$CI_NO_CLEAN'" 76 ci_info "executable: \$CI_CMAKE: $(command -V "$CI_CMAKE")" 77 ci_info "executable: \$CI_CTEST: $(command -V "$CI_CTEST")" 78 [[ $CI_CC ]] && { 79 ci_info "executable: \$CI_CC: $(command -V "$CI_CC")" 80 } 81 [[ $CI_AR ]] && { 82 ci_info "executable: \$CI_AR: $(command -V "$CI_AR")" 83 } 84 [[ $CI_RANLIB ]] && { 85 ci_info "executable: \$CI_RANLIB: $(command -V "$CI_RANLIB")" 86 } 87 ci_info "## END OF CONFIGURATION ##" 88} 89 90function ci_cleanup_old_build { 91 ci_info "## START OF PRE-BUILD CLEANUP ##" 92 [[ ! -e $CI_BUILD_DIR && ! -e $CI_INSTALL_DIR ]] || { 93 ci_spawn rm -fr "$CI_BUILD_DIR" 94 ci_spawn rm -fr "$CI_INSTALL_DIR" 95 } 96 ci_info "## END OF PRE-BUILD CLEANUP ##" 97} 98 99function ci_build { 100 ci_info "## START OF BUILD ##" 101 # Adjust the CI environment variables, as needed. 102 CI_CMAKE="$(command -v "$CI_CMAKE")" || ci_err "bad or missing \$CI_CMAKE" 103 ci_spawn "$CI_CMAKE" --version 104 CI_CTEST="$(command -v "$CI_CTEST")" || ci_err "bad or missing \$CI_CTEST" 105 ci_spawn "$CI_CTEST" --version 106 [[ $CI_CMAKE_GENERATOR == *"Ninja"* ]] && { 107 CI_NINJA="$(command -v ninja)" || ci_err "bad or missing ninja, no pun intended" 108 ci_spawn "$CI_NINJA" --version 109 } 110 [[ $CI_AR ]] && { 111 # Use the full path of CI_AR to work around a mysterious CMake error. 112 CI_AR="$(command -v "$CI_AR")" || ci_err "bad or missing \$CI_AR" 113 } 114 [[ $CI_RANLIB ]] && { 115 # Use the full path of CI_RANLIB to work around a mysterious CMake error. 116 CI_RANLIB="$(command -v "$CI_RANLIB")" || ci_err "bad or missing \$CI_RANLIB" 117 } 118 # Export the CMake environment variables. 119 [[ $CI_CMAKE_GENERATOR ]] && { 120 ci_spawn export CMAKE_GENERATOR="$CI_CMAKE_GENERATOR" 121 } 122 [[ $CI_CMAKE_GENERATOR_PLATFORM ]] && { 123 ci_spawn export CMAKE_GENERATOR_PLATFORM="$CI_CMAKE_GENERATOR_PLATFORM" 124 } 125 # Initialize and populate the local arrays. 126 local all_cmake_vars=() 127 local all_cmake_build_flags=() 128 local all_ctest_flags=() 129 [[ $CI_CMAKE_TOOLCHAIN_FILE ]] && { 130 all_cmake_vars+=("-DCMAKE_TOOLCHAIN_FILE=$CI_CMAKE_TOOLCHAIN_FILE") 131 } 132 [[ $CI_CC ]] && { 133 all_cmake_vars+=("-DCMAKE_C_COMPILER=$CI_CC") 134 } 135 [[ $CI_CC_FLAGS || $CI_SANITIZERS ]] && { 136 [[ $CI_SANITIZERS ]] && CI_CC_FLAGS+="${CI_CC_FLAGS:+" "}-fsanitize=$CI_SANITIZERS" 137 all_cmake_vars+=("-DCMAKE_C_FLAGS=$CI_CC_FLAGS") 138 } 139 [[ $CI_AR ]] && { 140 all_cmake_vars+=("-DCMAKE_AR=$CI_AR") 141 } 142 [[ $CI_RANLIB ]] && { 143 all_cmake_vars+=("-DCMAKE_RANLIB=$CI_RANLIB") 144 } 145 all_cmake_vars+=("-DCMAKE_BUILD_TYPE=$CI_CMAKE_BUILD_TYPE") 146 all_cmake_vars+=("-DCMAKE_VERBOSE_MAKEFILE=ON") 147 all_cmake_vars+=($CI_CMAKE_VARS) 148 all_cmake_build_flags+=($CI_CMAKE_BUILD_FLAGS) 149 all_ctest_flags+=($CI_CTEST_FLAGS) 150 # And... build! 151 # Use $CI_BUILD_TO_SRC_RELDIR and $CI_BUILD_TO_INSTALL_RELDIR 152 # instead of $CI_SRC_DIR and $CI_INSTALL_DIR from this point onwards. 153 ci_spawn mkdir -p "$CI_BUILD_DIR" 154 ci_spawn cd "$CI_BUILD_DIR" 155 [[ $CI_BUILD_TO_SRC_RELDIR -ef $CI_SRC_DIR ]] || { 156 ci_err_internal "bad or missing \$CI_BUILD_TO_SRC_RELDIR" 157 } 158 ci_spawn mkdir -p "$CI_INSTALL_DIR" 159 [[ $CI_BUILD_TO_INSTALL_RELDIR -ef $CI_INSTALL_DIR ]] || { 160 ci_err_internal "bad or missing \$CI_BUILD_TO_INSTALL_RELDIR" 161 } 162 # Spawn "cmake ...". 163 ci_spawn "$CI_CMAKE" -DCMAKE_INSTALL_PREFIX="$CI_BUILD_TO_INSTALL_RELDIR" \ 164 "${all_cmake_vars[@]}" \ 165 "$CI_BUILD_TO_SRC_RELDIR" 166 # Spawn "cmake --build ...". 167 ci_spawn "$CI_CMAKE" --build . \ 168 --config "$CI_CMAKE_BUILD_TYPE" \ 169 "${all_cmake_build_flags[@]}" 170 ci_expr $((CI_NO_TEST)) || { 171 # Spawn "ctest" if testing is not disabled. 172 ci_spawn "$CI_CTEST" --build-config "$CI_CMAKE_BUILD_TYPE" \ 173 "${all_ctest_flags[@]}" 174 } 175 ci_expr $((CI_NO_INSTALL)) || { 176 # Spawn "cmake --build ... --target install" if installation is not disabled. 177 ci_spawn "$CI_CMAKE" --build . \ 178 --config "$CI_CMAKE_BUILD_TYPE" \ 179 --target install \ 180 "${all_cmake_build_flags[@]}" 181 } 182 ci_expr $((CI_NO_CLEAN)) || { 183 # Spawn "make --build ... --target clean" if cleaning is not disabled. 184 ci_spawn "$CI_CMAKE" --build . \ 185 --config "$CI_CMAKE_BUILD_TYPE" \ 186 --target clean \ 187 "${all_cmake_build_flags[@]}" 188 } 189 ci_info "## END OF BUILD ##" 190} 191 192function usage { 193 echo "usage: $CI_SCRIPT_NAME [<options>]" 194 echo "options: -?|-h|--help" 195 exit "${@:-0}" 196} 197 198function main { 199 local opt 200 while getopts ":" opt 201 do 202 # This ain't a while-loop. It only pretends to be. 203 [[ $1 == -[?h]* || $1 == --help || $1 == --help=* ]] && usage 0 204 ci_err "unknown option: '$1'" 205 done 206 shift $((OPTIND - 1)) 207 # And... go! 208 ci_init_build 209 ci_trace_build 210 [[ $# -eq 0 ]] || { 211 echo >&2 "error: unexpected argument: '$1'" 212 echo >&2 "note: this program accepts environment options only" 213 usage 2 214 } 215 ci_cleanup_old_build 216 ci_build 217} 218 219main "$@" 220