ci_verify_makefiles.sh 6.8 KB

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