.build.zsh 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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. linux-aarch64
  52. )
  53. local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)
  54. if [[ ${host_os} == 'macos' ]] {
  55. local -r -a _valid_generators=(Xcode Ninja 'Unix Makefiles')
  56. local generator="${${CI:+Ninja}:-Xcode}"
  57. } else {
  58. local -r -a _valid_generators=(Ninja 'Unix Makefiles')
  59. local generator='Ninja'
  60. }
  61. local -r _usage="
  62. Usage: %B${functrace[1]%:*}%b <option> [<options>]
  63. %BOptions%b:
  64. %F{yellow} Build configuration options%f
  65. -----------------------------------------------------------------------------
  66. %B-t | --target%b Specify target - default: %B%F{green}${host_os}-${CPUTYPE}%f%b
  67. %B-c | --config%b Build configuration - default: %B%F{green}RelWithDebInfo%f%b
  68. %B-s | --codesign%b Enable codesigning (macOS only)
  69. %B--generator%b Specify build system to generate - default: %B%F{green}Ninja%f%b
  70. Available generators:
  71. - Ninja
  72. - Unix Makefiles
  73. - Xcode (macOS only)
  74. %F{yellow} Output options%f
  75. -----------------------------------------------------------------------------
  76. %B-q | --quiet%b Quiet (error output only)
  77. %B-v | --verbose%b Verbose (more detailed output)
  78. %B--skip-[all|build|deps|unpack]%b Skip all|building OBS|checking for dependencies|unpacking dependencies
  79. %B--debug%b Debug (very detailed and added output)
  80. %F{yellow} General options%f
  81. -----------------------------------------------------------------------------
  82. %B-h | --help%b Print this usage help
  83. %B-V | --version%b Print script version information"
  84. local -a args
  85. while (( # )) {
  86. case ${1} {
  87. -t|--target|-c|--config|--generator)
  88. if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {
  89. log_error "Missing value for option %B${1}%b"
  90. log_output ${_usage}
  91. exit 2
  92. }
  93. ;;
  94. }
  95. case ${1} {
  96. --)
  97. shift
  98. args+=($@)
  99. break
  100. ;;
  101. -t|--target)
  102. if (( ! ${_valid_targets[(Ie)${2}]} )) {
  103. log_error "Invalid value %B${2}%b for option %B${1}%b"
  104. log_output ${_usage}
  105. exit 2
  106. }
  107. target=${2}
  108. shift 2
  109. ;;
  110. -c|--config)
  111. if (( ! ${_valid_configs[(Ie)${2}]} )) {
  112. log_error "Invalid value %B${2}%b for option %B${1}%b"
  113. log_output ${_usage}
  114. exit 2
  115. }
  116. BUILD_CONFIG=${2}
  117. shift 2
  118. ;;
  119. -s|--codesign) CODESIGN=1; shift ;;
  120. -q|--quiet) (( _verbosity -= 1 )) || true; shift ;;
  121. -v|--verbose) (( _verbosity += 1 )); shift ;;
  122. -h|--help) log_output ${_usage}; exit 0 ;;
  123. -V|--version) print -Pr "${_version}"; exit 0 ;;
  124. --debug) _verbosity=3; shift ;;
  125. --generator)
  126. if (( ! ${_valid_generators[(Ie)${2}]} )) {
  127. log_error "Invalid value %B${2}%b for option %B${1}%b"
  128. log_output ${_usage}
  129. exit 2
  130. }
  131. generator=${2}
  132. shift 2
  133. ;;
  134. --skip-*)
  135. local _skip="${${(s:-:)1}[-1]}"
  136. local _check=(all deps unpack build)
  137. (( ${_check[(Ie)${_skip}]} )) || log_warning "Invalid skip mode %B${_skip}%b supplied"
  138. typeset -g -a skips=(${skips} ${_skip})
  139. shift
  140. ;;
  141. *) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
  142. }
  143. }
  144. set -- ${(@)args}
  145. set_loglevel ${_verbosity}
  146. check_${host_os}
  147. setup_ccache
  148. typeset -g QT_VERSION
  149. typeset -g DEPLOYMENT_TARGET
  150. typeset -g OBS_DEPS_VERSION
  151. setup_${host_os}
  152. local product_name
  153. local product_version
  154. local product_author
  155. local product_email
  156. IFS=/ read -r product_name product_version product_author product_email <<< \
  157. "$(jq -r '. | {name, version, author, email} | join("/")' ${buildspec_file})"
  158. product_author=\"${product_author}\"
  159. product_email=\"${product_email}\"
  160. case ${host_os} {
  161. macos)
  162. sed -i '' \
  163. "s/project(\(.*\) VERSION \(.*\))/project(${product_name} VERSION ${product_version})/" \
  164. "${project_root}/CMakeLists.txt"
  165. sed -i '' \
  166. "s/set(PLUGIN_AUTHOR \(.*\))/set(PLUGIN_AUTHOR ${product_author})/"\
  167. "${project_root}/CMakeLists.txt"
  168. sed -i '' \
  169. "s/set(LINUX_MAINTAINER_EMAIL \(.*\))/set(LINUX_MAINTAINER_EMAIL ${product_email})/"\
  170. "${project_root}/CMakeLists.txt"
  171. ;;
  172. linux)
  173. sed -i'' \
  174. "s/project(\(.*\) VERSION \(.*\))/project(${product_name} VERSION ${product_version})/"\
  175. "${project_root}/CMakeLists.txt"
  176. sed -i'' \
  177. "s/set(PLUGIN_AUTHOR \(.*\))/set(PLUGIN_AUTHOR ${product_author})/"\
  178. "${project_root}/CMakeLists.txt"
  179. sed -i'' \
  180. "s/set(LINUX_MAINTAINER_EMAIL \(.*\))/set(LINUX_MAINTAINER_EMAIL ${product_email})/"\
  181. "${project_root}/CMakeLists.txt"
  182. ;;
  183. }
  184. setup_obs
  185. pushd ${project_root}
  186. if (( ! (${skips[(Ie)all]} + ${skips[(Ie)build]}) )) {
  187. log_info "Configuring ${product_name}..."
  188. local _plugin_deps="${project_root:h}/obs-build-dependencies/plugin-deps-${OBS_DEPS_VERSION}-qt${QT_VERSION}-${target##*-}"
  189. local -a cmake_args=(
  190. -DCMAKE_BUILD_TYPE=${BUILD_CONFIG:-RelWithDebInfo}
  191. -DQT_VERSION=${QT_VERSION}
  192. -DCMAKE_PREFIX_PATH="${_plugin_deps}"
  193. )
  194. if (( _loglevel == 0 )) cmake_args+=(-Wno_deprecated -Wno-dev --log-level=ERROR)
  195. if (( _loglevel > 2 )) cmake_args+=(--debug-output)
  196. local num_procs
  197. case ${target} {
  198. macos-*)
  199. autoload -Uz read_codesign
  200. if (( ${+CODESIGN} )) {
  201. read_codesign
  202. }
  203. cmake_args+=(
  204. -DCMAKE_FRAMEWORK_PATH="${_plugin_deps}/Frameworks"
  205. -DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
  206. -DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
  207. -DOBS_CODESIGN_LINKER=ON
  208. -DOBS_BUNDLE_CODESIGN_IDENTITY="${CODESIGN_IDENT:--}"
  209. )
  210. num_procs=$(( $(sysctl -n hw.ncpu) + 1 ))
  211. ;;
  212. linux-*)
  213. if (( ${+CI} )) {
  214. cmake_args+=(-DCMAKE_INSTALL_PREFIX=/usr -DLINUX_PORTABLE=OFF)
  215. }
  216. num_procs=$(( $(nproc) + 1 ))
  217. ;;
  218. }
  219. log_debug "Attempting to configure ${product_name} with CMake arguments: ${cmake_args}"
  220. cmake -S . -B build_${target##*-} -G ${generator} ${cmake_args}
  221. log_info "Building ${product_name}..."
  222. local -a cmake_args=()
  223. if (( _loglevel > 1 )) cmake_args+=(--verbose)
  224. if [[ ${generator} == 'Unix Makefiles' ]] cmake_args+=(--parallel ${num_procs})
  225. cmake --build build_${target##*-} --config ${BUILD_CONFIG:-RelWithDebInfo} ${cmake_args}
  226. }
  227. log_info "Installing ${product_name}..."
  228. local -a cmake_args=()
  229. if (( _loglevel > 1 )) cmake_args+=(--verbose)
  230. cmake --install build_${target##*-} --config ${BUILD_CONFIG:-RelWithDebInfo} --prefix "${project_root}/release" ${cmake_args}
  231. popd
  232. }
  233. build ${@}