build.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. #
  4. # variables
  5. #
  6. RESET="\033[0m"
  7. RED="\033[0;31m"
  8. YELLOW="\033[0;33m"
  9. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  10. target_os_name=''
  11. ci=false
  12. binary_log=false
  13. exclude_ci_binary_log=false
  14. verbosity='minimal'
  15. run_restore=''
  16. run_build=true
  17. run_pack=false
  18. run_tests=false
  19. build_all=false
  20. build_deps=true
  21. only_build_repo_tasks=false
  22. build_repo_tasks=true
  23. build_managed=''
  24. build_native=''
  25. build_nodejs=''
  26. build_java=''
  27. build_installers=''
  28. build_projects=''
  29. target_arch='x64'
  30. configuration=''
  31. runtime_source_feed=''
  32. runtime_source_feed_key=''
  33. if [ "$(uname)" = "Darwin" ]; then
  34. target_os_name='osx'
  35. else
  36. target_os_name='linux'
  37. fi
  38. msbuild_args=()
  39. #
  40. # Functions
  41. #
  42. __usage() {
  43. echo "Usage: $(basename "${BASH_SOURCE[0]}") [options] [[--] <Arguments>...]
  44. Arguments:
  45. <Arguments>... Arguments passed to the command. Variable number of arguments allowed.
  46. Options:
  47. --configuration|-c The build configuration (Debug, Release). Default=Debug
  48. --arch The CPU architecture to build for (x64, arm, arm64). Default=$target_arch
  49. --os-name The base runtime identifier to build for (linux, osx, linux-musl). Default=$target_os_name
  50. --[no-]restore Run restore.
  51. --[no-]build Compile projects. (Implies --no-restore)
  52. --[no-]pack Produce packages.
  53. --[no-]test Run tests.
  54. --projects A list of projects to build. (Must be an absolute path.)
  55. Globbing patterns are supported, such as \"$(pwd)/**/*.csproj\".
  56. --no-build-deps Do not build project-to-project references and only build the specified project.
  57. --no-build-repo-tasks Suppress building RepoTasks.
  58. --only-build-repo-tasks Only build RepoTasks.
  59. --all Build all project types.
  60. --[no-]build-native Build native projects (C, C++). Ignored in most cases i.e. with `dotnet msbuild`.
  61. --[no-]build-managed Build managed projects (C#, F#, VB).
  62. --[no-]build-nodejs Build NodeJS projects (TypeScript, JS).
  63. --[no-]build-java Build Java projects.
  64. --[no-]build-installers Build installers.
  65. --ci Apply CI specific settings and environment variables.
  66. --binarylog|-bl Use a binary logger
  67. --excludeCIBinarylog Don't output binary log by default in CI builds (short: -nobl).
  68. --verbosity|-v MSBuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]
  69. --runtime-source-feed Additional feed that can be used when downloading .NET runtimes and SDKs
  70. --runtime-source-feed-key Key for feed that can be used when downloading .NET runtimes and SDKs
  71. Description:
  72. This build script installs required tools and runs an MSBuild command on this repository
  73. This script can be used to invoke various targets, such as targets to produce packages
  74. build projects, run tests, and generate code.
  75. "
  76. if [[ "${1:-}" != '--no-exit' ]]; then
  77. exit 2
  78. fi
  79. }
  80. __error() {
  81. echo -e "${RED}error: $*${RESET}" 1>&2
  82. }
  83. __warn() {
  84. echo -e "${YELLOW}warning: $*${RESET}"
  85. }
  86. #
  87. # main
  88. #
  89. while [[ $# -gt 0 ]]; do
  90. opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')"
  91. case "$opt" in
  92. -\?|-h|-help)
  93. __usage --no-exit
  94. exit 0
  95. ;;
  96. -configuration|-c)
  97. shift
  98. configuration="${1:-}"
  99. [ -z "$configuration" ] && __error "Missing value for parameter --configuration" && __usage
  100. ;;
  101. -arch)
  102. shift
  103. target_arch="${1:-}"
  104. [ -z "$target_arch" ] && __error "Missing value for parameter --arch" && __usage
  105. ;;
  106. -os-name|-osname)
  107. shift
  108. target_os_name="${1:-}"
  109. [ -z "$target_os_name" ] && __error "Missing value for parameter --os-name" && __usage
  110. ;;
  111. -restore|-r)
  112. run_restore=true
  113. ;;
  114. -no-restore|-norestore)
  115. run_restore=false
  116. ;;
  117. -build|-b)
  118. run_build=true
  119. ;;
  120. -no-build|-nobuild)
  121. run_build=false
  122. # --no-build implies --no-restore
  123. [ -z "$run_restore" ] && run_restore=false
  124. ;;
  125. -no-build-deps|-nobuilddeps)
  126. build_deps=false
  127. ;;
  128. -pack)
  129. run_pack=true
  130. ;;
  131. -no-pack|-nopack)
  132. run_pack=false
  133. ;;
  134. -test|-t)
  135. run_tests=true
  136. ;;
  137. -no-test|-notest)
  138. run_tests=false
  139. ;;
  140. -projects)
  141. shift
  142. build_projects="${1:-}"
  143. [ -z "$build_projects" ] && __error "Missing value for parameter --projects" && __usage
  144. ;;
  145. -all)
  146. build_all=true
  147. ;;
  148. -build-managed|-buildmanaged)
  149. build_managed=true
  150. ;;
  151. -no-build-managed|-nobuildmanaged)
  152. build_managed=false
  153. ;;
  154. -build-nodejs|-buildnodejs)
  155. build_nodejs=true
  156. ;;
  157. -no-build-nodejs|-nobuildnodejs)
  158. build_nodejs=false
  159. ;;
  160. -build-java|-buildjava)
  161. build_java=true
  162. ;;
  163. -no-build-java|-nobuildjava)
  164. build_java=false
  165. ;;
  166. -build-native|-buildnative)
  167. build_native=true
  168. ;;
  169. -no-build-native|-nobuildnative)
  170. build_native=false
  171. ;;
  172. -build-installers|-buildinstallers)
  173. build_installers=true
  174. ;;
  175. -no-build-installers|-nobuildinstallers)
  176. build_installers=false
  177. ;;
  178. -no-build-repo-tasks|-nobuildrepotasks)
  179. build_repo_tasks=false
  180. ;;
  181. -only-build-repo-tasks|-onlybuildrepotasks)
  182. only_build_repo_tasks=true
  183. ;;
  184. -arch)
  185. shift
  186. target_arch="${1:-}"
  187. [ -z "$target_arch" ] && __error "Missing value for parameter --arch" && __usage
  188. ;;
  189. -ci)
  190. ci=true
  191. ;;
  192. -binarylog|-bl)
  193. binary_log=true
  194. ;;
  195. -excludeCIBinarylog|-nobl)
  196. exclude_ci_binary_log=true
  197. ;;
  198. -dotnet-runtime-source-feed|-dotnetruntimesourcefeed|-runtime-source-feed|-runtimesourcefeed)
  199. shift
  200. [ -z "${1:-}" ] && __error "Missing value for parameter --runtime-source-feed" && __usage
  201. runtime_source_feed="${1:-}"
  202. ;;
  203. -dotnet-runtime-source-feed-key|-dotnetruntimesourcefeedkey|-runtime-source-feed-key|-runtimesourcefeedkey)
  204. shift
  205. [ -z "${1:-}" ] && __error "Missing value for parameter --runtime-source-feed-key" && __usage
  206. runtime_source_feed_key="${1:-}"
  207. ;;
  208. *)
  209. msbuild_args[${#msbuild_args[*]}]="$1"
  210. ;;
  211. esac
  212. shift
  213. done
  214. if [ "$build_all" = true ]; then
  215. msbuild_args[${#msbuild_args[*]}]="-p:BuildAllProjects=true"
  216. fi
  217. if [ ! -z "$build_projects" ]; then
  218. [[ "$build_projects" == /* ]] || build_projects="$DIR/$build_projects"
  219. msbuild_args[${#msbuild_args[*]}]="-p:ProjectToBuild=$build_projects"
  220. elif [ "$build_all" != true ] && [ -z "$build_managed$build_nodejs$build_java$build_native$build_installers" ]; then
  221. # This goal of this is to pick a sensible default for `build.sh` with zero arguments.
  222. # We believe the most common thing our contributors will work on is C#, so if no other build group was picked, build the C# projects.
  223. __warn "No default group of projects was specified, so building the 'managed' and its dependent subset of projects. Run ``build.sh --help`` for more details."
  224. build_managed=true
  225. elif [ "$build_all" != true ] && [ -z "$build_managed" ] && ! [[ "$build_nodejs$build_java$build_native$build_installers" =~ "true" ]]; then
  226. # If only negative options were chosen, assume --build-managed.
  227. build_managed=true
  228. fi
  229. if [ "$build_deps" = false ]; then
  230. msbuild_args[${#msbuild_args[*]}]="-p:BuildProjectReferences=false"
  231. fi
  232. if [ "$build_managed" = true ] || ([ "$build_all" = true ] && [ "$build_managed" != false ]); then
  233. if [ -z "$build_nodejs" ]; then
  234. if [ -x "$(command -v node)" ]; then
  235. __warn "Building of C# project is enabled and has dependencies on NodeJS projects. Building of NodeJS projects is enabled since node is detected on PATH."
  236. build_nodejs=true
  237. else
  238. __warn "Building of NodeJS projects is disabled since node is not detected on Path and no BuildNodeJs or NoBuildNodeJs setting is set explicitly."
  239. build_nodejs=false
  240. fi
  241. fi
  242. if [ "$build_nodejs" = false ]; then
  243. __warn "Some managed projects depend on NodeJS projects. Building NodeJS is disabled so the managed projects will fallback to using the output from previous builds. The output may not be correct or up to date."
  244. fi
  245. fi
  246. # Only set these MSBuild properties if they were explicitly set by build parameters.
  247. [ ! -z "$build_java" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildJava=$build_java"
  248. [ ! -z "$build_native" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildNative=$build_native"
  249. [ ! -z "$build_nodejs" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildNodeJS=$build_nodejs"
  250. [ ! -z "$build_managed" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildManaged=$build_managed"
  251. [ ! -z "$build_installers" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildInstallers=$build_installers"
  252. # Run restore by default unless --no-restore or --no-build was specified.
  253. [ -z "$run_restore" ] && run_restore=true
  254. msbuild_args[${#msbuild_args[*]}]="-p:Restore=$run_restore"
  255. msbuild_args[${#msbuild_args[*]}]="-p:Build=$run_build"
  256. if [ "$run_build" = false ]; then
  257. msbuild_args[${#msbuild_args[*]}]="-p:NoBuild=true"
  258. fi
  259. msbuild_args[${#msbuild_args[*]}]="-p:Pack=$run_pack"
  260. msbuild_args[${#msbuild_args[*]}]="-p:Test=$run_tests"
  261. msbuild_args[${#msbuild_args[*]}]="-p:TargetArchitecture=$target_arch"
  262. msbuild_args[${#msbuild_args[*]}]="-p:TargetOsName=$target_os_name"
  263. if [ -z "$configuration" ]; then
  264. if [ "$ci" = true ]; then
  265. configuration='Release'
  266. else
  267. configuration='Debug'
  268. fi
  269. fi
  270. msbuild_args[${#msbuild_args[*]}]="-p:Configuration=$configuration"
  271. # Set up additional runtime args
  272. toolset_build_args=()
  273. if [ ! -z "$runtime_source_feed$runtime_source_feed_key" ]; then
  274. runtimeFeedArg="/p:DotNetRuntimeSourceFeed=$runtime_source_feed"
  275. runtimeFeedKeyArg="/p:DotNetRuntimeSourceFeedKey=$runtime_source_feed_key"
  276. msbuild_args[${#msbuild_args[*]}]=$runtimeFeedArg
  277. msbuild_args[${#msbuild_args[*]}]=$runtimeFeedKeyArg
  278. toolset_build_args[${#toolset_build_args[*]}]=$runtimeFeedArg
  279. toolset_build_args[${#toolset_build_args[*]}]=$runtimeFeedKeyArg
  280. fi
  281. # Initialize global variables need to be set before the import of Arcade is imported
  282. restore=$run_restore
  283. # Disable node reuse - Workaround perpetual issues in node reuse and custom task assemblies
  284. nodeReuse=false
  285. export MSBUILDDISABLENODEREUSE=1
  286. # Ensure passing neither --bl nor --nobl on CI avoids errors in tools.sh. This is needed because we set both variables
  287. # to false by default i.e. they always exist. (We currently avoid binary logs but that is made visible in the YAML.)
  288. if [[ "$ci" == true && "$exclude_ci_binary_log" == false ]]; then
  289. binary_log=true
  290. fi
  291. # increase file descriptor limit on macOS
  292. if [ "$(uname)" = "Darwin" ]; then
  293. ulimit -n 10000
  294. fi
  295. # Import Arcade
  296. . "$DIR/common/tools.sh"
  297. # Add default .binlog location if not already on the command line. tools.sh does not handle this; it just checks
  298. # $binary_log, $ci and $exclude_ci_binary_log values for an error case.
  299. if [[ "$binary_log" == true ]]; then
  300. found=false
  301. for arg in "${msbuild_args[@]}"; do
  302. opt="$(echo "${arg/#--/-}" | awk '{print tolower($0)}')"
  303. if [[ "$opt" == [-/]bl:* || "$opt" == [-/]binarylogger:* ]]; then
  304. found=true
  305. break
  306. fi
  307. done
  308. if [[ "$found" == false ]]; then
  309. msbuild_args[${#msbuild_args[*]}]="/bl:$log_dir/Build.binlog"
  310. fi
  311. toolset_build_args[${#toolset_build_args[*]}]="/bl:$log_dir/Build.repotasks.binlog"
  312. elif [[ "$ci" == true ]]; then
  313. # Ensure the artifacts/log directory isn't empty to avoid warnings.
  314. touch "$log_dir/empty.log"
  315. fi
  316. # Capture MSBuild crash logs
  317. export MSBUILDDEBUGPATH="$log_dir"
  318. # Set this global property so Arcade will always initialize the toolset. The error message you get when you build on a clean machine
  319. # with -norestore is not obvious about what to do to fix it. As initialization takes very little time, we think always initializing
  320. # the toolset is a better default behavior.
  321. _tmp_restore=$restore
  322. restore=true
  323. InitializeToolset
  324. restore=$_tmp_restore=
  325. if [ "$build_repo_tasks" = true ]; then
  326. MSBuild $_InitializeToolset \
  327. -p:RepoRoot="$repo_root" \
  328. -p:Projects="$DIR/tools/RepoTasks/RepoTasks.csproj" \
  329. -p:Configuration=Release \
  330. -p:Restore=$run_restore \
  331. -p:Build=true \
  332. -clp:NoSummary \
  333. ${toolset_build_args[@]+"${toolset_build_args[@]}"}
  334. fi
  335. if [ "$only_build_repo_tasks" != true ]; then
  336. # This incantation avoids unbound variable issues if msbuild_args is empty
  337. # https://stackoverflow.com/questions/7577052/bash-empty-array-expansion-with-set-u
  338. MSBuild $_InitializeToolset -p:RepoRoot="$repo_root" ${msbuild_args[@]+"${msbuild_args[@]}"}
  339. fi
  340. ExitWithExitCode 0