.build.zsh 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. #!/usr/bin/env zsh
  2. builtin emulate -L zsh
  3. setopt EXTENDED_GLOB
  4. setopt PUSHD_SILENT
  5. setopt ERR_EXIT
  6. setopt ERR_RETURN
  7. setopt NO_UNSET
  8. setopt PIPE_FAIL
  9. setopt NO_AUTO_PUSHD
  10. setopt NO_PUSHD_IGNORE_DUPS
  11. setopt FUNCTION_ARGZERO
  12. ## Enable for script debugging
  13. # setopt WARN_CREATE_GLOBAL
  14. # setopt WARN_NESTED_VAR
  15. # setopt XTRACE
  16. autoload -Uz is-at-least && if ! is-at-least 5.2; then
  17. print -u2 -PR "${CI:+::error::}%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade Zsh to fix this issue."
  18. exit 1
  19. fi
  20. TRAPEXIT() {
  21. local return_value=$?
  22. if (( ${+CI} )) unset NSUnbufferedIO
  23. return ${return_value}
  24. }
  25. TRAPZERR() {
  26. if (( ${_loglevel:-3} > 2 )) {
  27. print -u2 -PR "${CI:+::error::}%F{1} ✖︎ script execution error%f"
  28. print -PR -e "
  29. Callstack:
  30. ${(j:\n :)funcfiletrace}
  31. "
  32. }
  33. exit 2
  34. }
  35. build() {
  36. if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
  37. local host_os=${${(s:-:)ZSH_ARGZERO:t:r}[2]}
  38. local project_root=${SCRIPT_HOME:A:h:h}
  39. local buildspec_file=${project_root}/buildspec.json
  40. fpath=("${SCRIPT_HOME}/utils.zsh" ${fpath})
  41. autoload -Uz log_group log_info log_error log_output set_loglevel check_${host_os} setup_ccache
  42. if [[ ! -r ${buildspec_file} ]] {
  43. log_error \
  44. 'No buildspec.json found. Please create a build specification for your project.'
  45. return 2
  46. }
  47. typeset -g -a skips=()
  48. local -i verbosity=1
  49. local -r _version='2.0.0'
  50. local -r -a _valid_targets=(
  51. macos-universal
  52. linux-x86_64
  53. linux-aarch64
  54. )
  55. local target
  56. local config='RelWithDebInfo'
  57. local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)
  58. local -i codesign=0
  59. if [[ ${host_os} == linux ]] {
  60. local -r -a _valid_generators=(Ninja 'Unix Makefiles')
  61. local generator='Ninja'
  62. local -r _usage_host="
  63. %F{yellow} Additional options for Linux builds%f
  64. -----------------------------------------------------------------------------
  65. %B--generator%b Specify build system to generate
  66. Available generators:
  67. - Ninja
  68. - Unix Makefiles"
  69. } elif [[ ${host_os} == macos ]] {
  70. local -r _usage_host="
  71. %F{yellow} Additional options for macOS builds%f
  72. -----------------------------------------------------------------------------
  73. %B-s | --codesign%b Enable codesigning (macOS only)"
  74. }
  75. local -i print_config=0
  76. local -r _usage="
  77. Usage: %B${functrace[1]%:*}%b <option> [<options>]
  78. %BOptions%b:
  79. %F{yellow} Build configuration options%f
  80. -----------------------------------------------------------------------------
  81. %B-t | --target%b Specify target
  82. %B-c | --config%b Build configuration
  83. %B--skip-[all|build|deps]%b Skip all|building|checking for dependencies
  84. %F{yellow} Output options%f
  85. -----------------------------------------------------------------------------
  86. %B-q | --quiet%b Quiet (error output only)
  87. %B-v | --verbose%b Verbose (more detailed output)
  88. %B--debug%b Debug (very detailed and added output)
  89. %F{yellow} General options%f
  90. -----------------------------------------------------------------------------
  91. %B-h | --help%b Print this usage help
  92. %B-V | --version%b Print script version information
  93. ${_usage_host:-}"
  94. local -a args
  95. while (( # )) {
  96. case ${1} {
  97. -t|--target|-c|--config|--generator)
  98. if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {
  99. log_error "Missing value for option %B${1}%b"
  100. log_output ${_usage}
  101. exit 2
  102. }
  103. ;;
  104. }
  105. case ${1} {
  106. --)
  107. shift
  108. args+=($@)
  109. break
  110. ;;
  111. -t|--target)
  112. if (( ! ${_valid_targets[(Ie)${2}]} )) {
  113. log_error "Invalid value %B${2}%b for option %B${1}%b"
  114. log_output ${_usage}
  115. exit 2
  116. }
  117. target=${2}
  118. shift 2
  119. ;;
  120. -c|--config)
  121. if (( ! ${_valid_configs[(Ie)${2}]} )) {
  122. log_error "Invalid value %B${2}%b for option %B${1}%b"
  123. log_output ${_usage}
  124. exit 2
  125. }
  126. config=${2}
  127. shift 2
  128. ;;
  129. -s|--codesign) codesign=1; shift ;;
  130. -q|--quiet) (( verbosity -= 1 )) || true; shift ;;
  131. -v|--verbose) (( verbosity += 1 )); shift ;;
  132. -h|--help) log_output ${_usage}; exit 0 ;;
  133. -V|--version) print -Pr "${_version}"; exit 0 ;;
  134. --debug) verbosity=3; shift ;;
  135. --generator)
  136. if [[ ${host_os} == linux ]] {
  137. if (( ! ${_valid_generators[(Ie)${2}]} )) {
  138. log_error "Invalid value %B${2}%b for option %B${1}%b"
  139. log_output ${_usage}
  140. exit 2
  141. }
  142. generator=${2}
  143. }
  144. shift 2
  145. ;;
  146. --print-config) print_config=1; skips+=(deps); shift ;;
  147. --skip-*)
  148. local -r _skip="${${(s:-:)1}[-1]}"
  149. local -r -a _check=(all build deps)
  150. (( ${_check[(Ie)${_skip}]} )) || log_warning "Invalid skip mode %B${_skip}%b supplied"
  151. typeset -g -a skips=(${skips} ${_skip})
  152. shift
  153. ;;
  154. *) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
  155. }
  156. }
  157. : "${target:="${host_os}-${CPUTYPE}"}"
  158. set -- ${(@)args}
  159. set_loglevel ${verbosity}
  160. if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) {
  161. check_${host_os}
  162. setup_ccache
  163. }
  164. if [[ ${host_os} == linux ]] {
  165. autoload -Uz setup_linux && setup_linux
  166. }
  167. local product_name
  168. local product_version
  169. read -r product_name product_version <<< \
  170. "$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})"
  171. pushd ${project_root}
  172. if (( ! (${skips[(Ie)all]} + ${skips[(Ie)build]}) )) {
  173. log_group "Configuring ${product_name}..."
  174. local -a cmake_args=()
  175. local -a cmake_build_args=(--build)
  176. local -a cmake_install_args=(--install)
  177. case ${_loglevel} {
  178. 0) cmake_args+=(-Wno_deprecated -Wno-dev --log-level=ERROR) ;;
  179. 1) ;;
  180. 2) cmake_build_args+=(--verbose) ;;
  181. *) cmake_args+=(--debug-output) ;;
  182. }
  183. local -r _preset="${target%%-*}${CI:+-ci}"
  184. case ${target} {
  185. macos-*)
  186. if (( ${+CI} )) typeset -gx NSUnbufferedIO=YES
  187. cmake_args+=(
  188. --preset ${_preset}
  189. )
  190. if (( codesign )) {
  191. autoload -Uz read_codesign_team && read_codesign_team
  192. if [[ -z ${CODESIGN_TEAM} ]] {
  193. autoload -Uz read_codesign && read_codesign
  194. }
  195. }
  196. cmake_args+=(
  197. -DCODESIGN_TEAM=${CODESIGN_TEAM:-}
  198. -DCODESIGN_IDENTITY=${CODESIGN_IDENT:--}
  199. )
  200. cmake_build_args+=(--preset ${_preset} --parallel --config ${config} -- ONLY_ACTIVE_ARCH=NO -arch arm64 -arch x86_64)
  201. cmake_install_args+=(build_macos --config ${config} --prefix "${project_root}/release/${config}")
  202. local -a xcbeautify_opts=()
  203. if (( _loglevel == 0 )) xcbeautify_opts+=(--quiet)
  204. ;;
  205. linux-*)
  206. cmake_args+=(
  207. --preset ${_preset}-${target##*-}
  208. -G "${generator}"
  209. -DQT_VERSION=${QT_VERSION:-6}
  210. -DCMAKE_BUILD_TYPE=${config}
  211. -DCMAKE_INSTALL_PREFIX=/usr
  212. )
  213. local cmake_version
  214. read -r _ _ cmake_version <<< "$(cmake --version)"
  215. if [[ ${CPUTYPE} != ${target##*-} ]] {
  216. if is-at-least 3.21.0 ${cmake_version}; then
  217. cmake_args+=(--toolchain "${project_root}/cmake/linux/toolchains/${target##*-}-linux-gcc.cmake")
  218. else
  219. cmake_args+=(-D"CMAKE_TOOLCHAIN_FILE=${project_root}/cmake/linux/toolchains/${target##*-}-linux-gcc.cmake")
  220. fi
  221. }
  222. cmake_build_args+=(--preset ${_preset}-${target##*-} --config ${config})
  223. if [[ ${generator} == 'Unix Makefiles' ]] {
  224. cmake_build_args+=(--parallel $(( $(nproc) + 1 )))
  225. } else {
  226. cmake_build_args+=(--parallel)
  227. }
  228. cmake_install_args+=(build_${target##*-} --prefix ${project_root}/release/${config})
  229. ;;
  230. }
  231. log_debug "Attempting to configure with CMake arguments: ${cmake_args}"
  232. cmake ${cmake_args}
  233. log_group "Building ${product_name}..."
  234. if [[ ${host_os} == macos ]] {
  235. if (( _loglevel > 1 )) {
  236. cmake ${cmake_build_args}
  237. } else {
  238. cmake ${cmake_build_args} 2>&1 | xcbeautify ${xcbeautify_opts}
  239. }
  240. } else {
  241. cmake ${cmake_build_args}
  242. }
  243. }
  244. log_group "Installing ${product_name}..."
  245. if (( _loglevel > 1 )) cmake_install_args+=(--verbose)
  246. cmake ${cmake_install_args}
  247. popd
  248. log_group
  249. }
  250. build ${@}