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" 16 17function ci_init_build { 18 # Ensure that the mandatory variables are initialized. 19 CI_MAKE="${CI_MAKE:-make}" 20 case "$CI_CC" in 21 ( *clang* ) 22 CI_MAKEFILES="${CI_MAKEFILES:-"scripts/makefile.clang"}" ;; 23 ( *gcc* ) 24 CI_MAKEFILES="${CI_MAKEFILES:-"scripts/makefile.gcc"}" ;; 25 ( * ) 26 CI_MAKEFILES="${CI_MAKEFILES:-"scripts/makefile.std"}" ;; 27 esac 28} 29 30function ci_trace_build { 31 ci_info "## START OF CONFIGURATION ##" 32 ci_info "build arch: $CI_BUILD_ARCH" 33 ci_info "build system: $CI_BUILD_SYSTEM" 34 [[ "$CI_TARGET_SYSTEM.$CI_TARGET_ARCH" != "$CI_BUILD_SYSTEM.$CI_BUILD_ARCH" ]] && { 35 ci_info "target arch: $CI_TARGET_ARCH" 36 ci_info "target system: $CI_TARGET_SYSTEM" 37 } 38 ci_info "source directory: $CI_SRC_DIR" 39 ci_info "environment option: \$CI_MAKEFILES: '$CI_MAKEFILES'" 40 ci_info "environment option: \$CI_MAKE: '$CI_MAKE'" 41 ci_info "environment option: \$CI_MAKE_FLAGS: '$CI_MAKE_FLAGS'" 42 ci_info "environment option: \$CI_MAKE_VARS: '$CI_MAKE_VARS'" 43 ci_info "environment option: \$CI_CC: '$CI_CC'" 44 ci_info "environment option: \$CI_CC_FLAGS: '$CI_CC_FLAGS'" 45 ci_info "environment option: \$CI_CPP: '$CI_CPP'" 46 ci_info "environment option: \$CI_CPP_FLAGS: '$CI_CPP_FLAGS'" 47 ci_info "environment option: \$CI_AR: '$CI_AR'" 48 ci_info "environment option: \$CI_RANLIB: '$CI_RANLIB'" 49 ci_info "environment option: \$CI_LD: '$CI_LD'" 50 ci_info "environment option: \$CI_LD_FLAGS: '$CI_LD_FLAGS'" 51 ci_info "environment option: \$CI_LIBS: '$CI_LIBS'" 52 ci_info "environment option: \$CI_SANITIZERS: '$CI_SANITIZERS'" 53 ci_info "environment option: \$CI_FORCE: '$CI_FORCE'" 54 ci_info "environment option: \$CI_NO_TEST: '$CI_NO_TEST'" 55 ci_info "environment option: \$CI_NO_CLEAN: '$CI_NO_CLEAN'" 56 ci_info "executable: \$CI_MAKE: $(command -V "$CI_MAKE")" 57 [[ $CI_CC ]] && { 58 ci_info "executable: \$CI_CC: $(command -V "$CI_CC")" 59 } 60 [[ $CI_CPP ]] && { 61 ci_info "executable: \$CI_CPP: $(command -V "$CI_CPP")" 62 } 63 [[ $CI_AR ]] && { 64 ci_info "executable: \$CI_AR: $(command -V "$CI_AR")" 65 } 66 [[ $CI_RANLIB ]] && { 67 ci_info "executable: \$CI_RANLIB: $(command -V "$CI_RANLIB")" 68 } 69 [[ $CI_LD ]] && { 70 ci_info "executable: \$CI_LD: $(command -V "$CI_LD")" 71 } 72 ci_info "## END OF CONFIGURATION ##" 73} 74 75function ci_cleanup_old_build { 76 # Any old makefile-based build will most likely leave a mess 77 # of object files behind if interrupted, e.g., via Ctrl+C. 78 # There may be other files behind, depending on what makefile 79 # had been used. We cannot easily enumerate all of those. 80 # Fortunately, for a clean makefiles-based build, it should be 81 # sufficient to remove the old object files only. 82 ci_info "## START OF PRE-BUILD CLEANUP ##" 83 local find_args=(-maxdepth 1 \( -iname "*.o" -o -iname "*.obj" \)) 84 [[ ! $(find "$CI_SRC_DIR" "${find_args[@]}" | head -n1) ]] || { 85 ci_warn "unexpected build found in '$CI_SRC_DIR'" 86 if ci_expr $((CI_FORCE)) 87 then 88 # Delete the old build. 89 local my_file 90 find "$CI_SRC_DIR" "${find_args[@]}" | 91 while IFS="" read -r my_file 92 do 93 ci_spawn rm -fr "$my_file" 94 done 95 ci_info "## END OF PRE-BUILD CLEANUP ##" 96 else 97 # Alert the user, but do not delete their existing files, 98 # and do not mess up their existing build. 99 ci_info "hint: consider using the option \$CI_FORCE=1" 100 ci_err "unable to continue" 101 fi 102 } 103} 104 105function ci_build { 106 ci_info "## START OF BUILD ##" 107 # Initialize and populate the local arrays. 108 local all_make_flags=() 109 local all_make_vars=() 110 [[ $CI_MAKE_FLAGS ]] && { 111 all_make_flags+=($CI_MAKE_FLAGS) 112 } 113 [[ $CI_CC ]] && { 114 all_make_vars+=("CC=$CI_CC") 115 } 116 [[ $CI_CC_FLAGS || $CI_SANITIZERS ]] && { 117 [[ $CI_SANITIZERS ]] && CI_CC_FLAGS="${CI_CC_FLAGS:-"-O2"} -fsanitize=$CI_SANITIZERS" 118 all_make_vars+=("CFLAGS=$CI_CC_FLAGS") 119 } 120 [[ $CI_CPP ]] && { 121 all_make_vars+=("CPP=$CI_CPP") 122 } 123 [[ $CI_CPP_FLAGS ]] && { 124 all_make_vars+=("CPPFLAGS=$CI_CPP_FLAGS") 125 } 126 [[ $CI_AR ]] && { 127 all_make_vars+=("AR=$CI_AR") 128 } 129 [[ $CI_RANLIB ]] && { 130 all_make_vars+=("RANLIB=$CI_RANLIB") 131 } 132 [[ $CI_LD ]] && { 133 all_make_vars+=("LD=$CI_LD") 134 } 135 [[ $CI_LD_FLAGS || $CI_SANITIZERS ]] && { 136 [[ $CI_SANITIZERS ]] && CI_LD_FLAGS+="${CI_LD_FLAGS:+" "}-fsanitize=$CI_SANITIZERS" 137 all_make_vars+=("LDFLAGS=$CI_LD_FLAGS") 138 } 139 [[ $CI_LIBS ]] && { 140 all_make_vars+=("LIBS=$CI_LIBS") 141 } 142 all_make_vars+=($CI_MAKE_VARS) 143 # And... build! 144 local my_makefile 145 for my_makefile in $CI_MAKEFILES 146 do 147 ci_info "using makefile: $my_makefile" 148 # Spawn "make". 149 ci_spawn "$CI_MAKE" -f "$my_makefile" \ 150 "${all_make_flags[@]}" \ 151 "${all_make_vars[@]}" 152 ci_expr $((CI_NO_TEST)) || { 153 # Spawn "make test" if testing is not disabled. 154 ci_spawn "$CI_MAKE" -f "$my_makefile" \ 155 "${all_make_flags[@]}" \ 156 "${all_make_vars[@]}" \ 157 test 158 } 159 ci_expr $((CI_NO_CLEAN)) || { 160 # Spawn "make clean" if cleaning is not disabled. 161 ci_spawn "$CI_MAKE" -f "$my_makefile" \ 162 "${all_make_flags[@]}" \ 163 "${all_make_vars[@]}" \ 164 clean 165 } 166 done 167 ci_info "## END OF BUILD ##" 168} 169 170function usage { 171 echo "usage: $CI_SCRIPT_NAME [<options>]" 172 echo "options: -?|-h|--help" 173 exit "${@:-0}" 174} 175 176function main { 177 local opt 178 while getopts ":" opt 179 do 180 # This ain't a while-loop. It only pretends to be. 181 [[ $1 == -[?h]* || $1 == --help || $1 == --help=* ]] && usage 0 182 ci_err "unknown option: '$1'" 183 done 184 shift $((OPTIND - 1)) 185 # And... go! 186 ci_init_build 187 ci_trace_build 188 [[ $# -eq 0 ]] || { 189 echo >&2 "error: unexpected argument: '$1'" 190 echo >&2 "note: this program accepts environment options only" 191 usage 2 192 } 193 ci_cleanup_old_build 194 ci_build 195} 196 197main "$@" 198