.build.zsh 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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 "%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. _trap_error() {
  21. print -u2 -PR '%F{1} ✖︎ script execution error%f'
  22. print -PR -e "
  23. Callstack:
  24. ${(j:\n :)funcfiletrace}
  25. "
  26. exit 2
  27. }
  28. build() {
  29. if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
  30. local host_os=${${(s:-:)ZSH_ARGZERO:t:r}[2]}
  31. local target="${host_os}-${CPUTYPE}"
  32. local project_root=${SCRIPT_HOME:A:h:h}
  33. local buildspec_file="${project_root}/buildspec.json"
  34. trap '_trap_error' ZERR
  35. fpath=("${SCRIPT_HOME}/utils.zsh" ${fpath})
  36. autoload -Uz log_info log_error log_output set_loglevel check_${host_os} setup_${host_os} setup_obs setup_ccache
  37. if [[ ! -r ${buildspec_file} ]] {
  38. log_error \
  39. 'No buildspec.json found. Please create a build specification for your project.' \
  40. 'A buildspec.json.template file is provided in the repository to get you started.'
  41. return 2
  42. }
  43. typeset -g -a skips=()
  44. local -i _verbosity=1
  45. local -r _version='1.0.0'
  46. local -r -a _valid_targets=(
  47. macos-x86_64
  48. macos-arm64
  49. macos-universal
  50. linux-x86_64
  51. )
  52. local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)
  53. if [[ ${host_os} == 'macos' ]] {
  54. local -r -a _valid_generators=(Xcode Ninja 'Unix Makefiles')
  55. } else {
  56. local -r -a _valid_generators=(Ninja 'Unix Makefiles')
  57. }
  58. local generator='Ninja'
  59. local -r _usage="
  60. Usage: %B${functrace[1]%:*}%b <option> [<options>]
  61. %BOptions%b:
  62. %F{yellow} Build configuration options%f
  63. -----------------------------------------------------------------------------
  64. %B-t | --target%b Specify target - default: %B%F{green}${host_os}-${CPUTYPE}%f%b
  65. %B-c | --config%b Build configuration - default: %B%F{green}RelWithDebInfo%f%b
  66. %B-s | --codesign%b Enable codesigning (macOS only)
  67. %B--generator%b Specify build system to generate - default: %B%F{green}Ninja%f%b
  68. Available generators:
  69. - Ninja
  70. - Unix Makefiles
  71. - Xcode (macOS only)
  72. %F{yellow} Output options%f
  73. -----------------------------------------------------------------------------
  74. %B-q | --quiet%b Quiet (error output only)
  75. %B-v | --verbose%b Verbose (more detailed output)
  76. %B--skip-[all|build|deps|unpack]%b Skip all|building OBS|checking for dependencies|unpacking dependencies
  77. %B--debug%b Debug (very detailed and added output)
  78. %F{yellow} General options%f
  79. -----------------------------------------------------------------------------
  80. %B-h | --help%b Print this usage help
  81. %B-V | --version%b Print script version information"
  82. local -a args
  83. while (( # )) {
  84. case ${1} {
  85. -t|--target|-c|--config|--generator)
  86. if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {
  87. log_error "Missing value for option %B${1}%b"
  88. log_output ${_usage}
  89. exit 2
  90. }
  91. ;;
  92. }
  93. case ${1} {
  94. --)
  95. shift
  96. args+=($@)
  97. break
  98. ;;
  99. -t|--target)
  100. if (( ! ${_valid_targets[(Ie)${2}]} )) {
  101. log_error "Invalid value %B${2}%b for option %B${1}%b"
  102. log_output ${_usage}
  103. exit 2
  104. }
  105. target=${2}
  106. shift 2
  107. ;;
  108. -c|--config)
  109. if (( ! ${_valid_configs[(Ie)${2}]} )) {
  110. log_error "Invalid value %B${2}%b for option %B${1}%b"
  111. log_output ${_usage}
  112. exit 2
  113. }
  114. BUILD_CONFIG=${2}
  115. shift 2
  116. ;;
  117. -s|--codesign) CODESIGN=1; shift ;;
  118. -q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
  119. -v|--verbose) (( _verbosity += 1 )); shift ;;
  120. -h|--help) log_output ${_usage}; exit 0 ;;
  121. -V|--version) print -Pr "${_version}"; exit 0 ;;
  122. --debug) _verbosity=3; shift ;;
  123. --generator)
  124. if (( ! ${_valid_generators[(Ie)${2}]} )) {
  125. log_error "Invalid value %B${2}%b for option %B${1}%b"
  126. log_output ${_usage}
  127. exit 2
  128. }
  129. generator=${2}
  130. shift 2
  131. ;;
  132. --skip-*)
  133. local _skip="${${(s:-:)1}[-1]}"
  134. local _check=(all deps unpack build)
  135. (( ${_check[(Ie)${_skip}]} )) || log_warning "Invalid skip mode %B${_skip}%b supplied"
  136. typeset -g -a skips=(${skips} ${_skip})
  137. shift
  138. ;;
  139. *) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
  140. }
  141. }
  142. set -- ${(@)args}
  143. set_loglevel ${_verbosity}
  144. check_${host_os}
  145. setup_ccache
  146. typeset -g QT_VERSION
  147. typeset -g DEPLOYMENT_TARGET
  148. typeset -g OBS_DEPS_VERSION
  149. setup_${host_os}
  150. local product_name
  151. local product_version
  152. read -r product_name product_version <<< \
  153. "$(jq -r '. | {name, version} | join(" ")' ${buildspec_file})"
  154. case ${host_os} {
  155. macos)
  156. sed -i '' \
  157. "s/project(\(.*\) VERSION \(.*\))/project(${product_name} VERSION ${product_version})/" \
  158. "${project_root}/CMakeLists.txt"
  159. ;;
  160. linux)
  161. sed -i'' \
  162. "s/project(\(.*\) VERSION \(.*\))/project(${product_name} VERSION ${product_version})/"\
  163. "${project_root}/CMakeLists.txt"
  164. ;;
  165. }
  166. setup_obs
  167. pushd ${project_root}
  168. if (( ! (${skips[(Ie)all]} + ${skips[(Ie)build]}) )) {
  169. log_info "Configuring ${product_name}..."
  170. local _plugin_deps="${project_root:h}/obs-build-dependencies/plugin-deps-${OBS_DEPS_VERSION}-qt${QT_VERSION}-${target##*-}"
  171. local -a cmake_args=(
  172. -DCMAKE_BUILD_TYPE=${BUILD_CONFIG:-RelWithDebInfo}
  173. -DQT_VERSION=${QT_VERSION}
  174. -DCMAKE_PREFIX_PATH="${_plugin_deps}"
  175. )
  176. if (( _loglevel == 0 )) cmake_args+=(-Wno_deprecated -Wno-dev --log-level=ERROR)
  177. if (( _logLevel > 2 )) cmake_args+=(--debug-output)
  178. local num_procs
  179. case ${target} {
  180. macos-*)
  181. autoload -Uz read_codesign
  182. if (( ${+CODESIGN} )) {
  183. read_codesign
  184. }
  185. cmake_args+=(
  186. -DCMAKE_FRAMEWORK_PATH="${_plugin_deps}/Frameworks"
  187. -DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
  188. -DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
  189. -DOBS_CODESIGN_LINKER=ON
  190. -DOBS_BUNDLE_CODESIGN_IDENTITY="${CODESIGN_IDENT:--}"
  191. )
  192. num_procs=$(( $(sysctl -n hw.ncpu) + 1 ))
  193. ;;
  194. linux-*)
  195. if (( ${+CI} )) {
  196. cmake_args+=(-DCMAKE_INSTALL_PREFIX=/usr)
  197. }
  198. num_procs=$(( $(nproc) + 1 ))
  199. ;;
  200. }
  201. log_debug "Attempting to configure ${product_name} with CMake arguments: ${cmake_args}"
  202. cmake -S . -B build_${target##*-} -G ${generator} ${cmake_args}
  203. log_info "Building ${product_name}..."
  204. local -a cmake_args=()
  205. if (( _loglevel > 1 )) cmake_args+=(--verbose)
  206. if [[ ${generator} == 'Unix Makefiles' ]] cmake_args+=(--parallel ${num_procs})
  207. cmake --build build_${target##*-} --config ${BUILD_CONFIG:-RelWithDebInfo} ${cmake_args}
  208. }
  209. log_info "Installing ${product_name}..."
  210. local -a cmake_args=()
  211. if (( _loglevel > 1 )) cmake_args+=(--verbose)
  212. cmake --install build_${target##*-} --config ${BUILD_CONFIG:-RelWithDebInfo} --prefix "${project_root}/release" ${cmake_args}
  213. popd
  214. }
  215. build ${@}