build.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. MAGENTA="\033[0;95m"
  10. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  11. verbose=false
  12. update=false
  13. reinstall=false
  14. lockfile_path="$DIR/korebuild-lock.txt"
  15. config_file="$DIR/korebuild.json"
  16. channel='master'
  17. tools_source='https://aspnetcore.blob.core.windows.net/buildtools'
  18. target_os_name=''
  19. ci=false
  20. run_restore=''
  21. run_build=true
  22. run_pack=false
  23. run_tests=false
  24. build_all=false
  25. build_deps=true
  26. build_managed=''
  27. build_native=''
  28. build_nodejs=''
  29. build_java=''
  30. build_projects=''
  31. target_arch='x64'
  32. if [ "$(uname)" = "Darwin" ]; then
  33. target_os_name='osx'
  34. else
  35. target_os_name='linux'
  36. fi
  37. msbuild_args=()
  38. #
  39. # Functions
  40. #
  41. __usage() {
  42. echo "Usage: $(basename "${BASH_SOURCE[0]}") [options] [[--] <Arguments>...]
  43. Arguments:
  44. <Arguments>... Arguments passed to the command. Variable number of arguments allowed.
  45. Options:
  46. --arch The CPU architecture to build for (x64, arm, arm64). Default=$target_arch
  47. --os-name The base runtime identifier to build for (linux, osx, linux-musl). Default=$target_os_name
  48. --[no-]restore Run restore.
  49. --[no-]build Compile projects. (Implies --no-restore)
  50. --[no-]pack Produce packages.
  51. --[no-]test Run tests.
  52. --projects A list of projects to build. (Must be an absolute path.)
  53. Globbing patterns are supported, such as \"$(pwd)/**/*.csproj\".
  54. --no-build-deps Do not build project-to-project references and only build the specified project.
  55. --all Build all project types.
  56. --[no-]build-native Build native projects (C, C++).
  57. --[no-]build-managed Build managed projects (C#, F#, VB).
  58. --[no-]build-nodejs Build NodeJS projects (TypeScript, JS).
  59. --[no-]build-java Build Java projects.
  60. --ci Apply CI specific settings and environment variables.
  61. --verbose Show verbose output.
  62. Description:
  63. This build script installs required tools and runs an MSBuild command on this repository
  64. This script can be used to invoke various targets, such as targets to produce packages
  65. build projects, run tests, and generate code.
  66. "
  67. if [[ "${1:-}" != '--no-exit' ]]; then
  68. exit 2
  69. fi
  70. }
  71. get_korebuild() {
  72. local version
  73. if [ ! -f "$lockfile_path" ]; then
  74. __get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lockfile_path"
  75. fi
  76. version="$(grep 'version:*' -m 1 "$lockfile_path")"
  77. if [[ "$version" == '' ]]; then
  78. __error "Failed to parse version from $lockfile_path. Expected a line that begins with 'version:'"
  79. return 1
  80. fi
  81. version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
  82. local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version"
  83. {
  84. if [ ! -d "$korebuild_path" ]; then
  85. mkdir -p "$korebuild_path"
  86. local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip"
  87. tmpfile="$(mktemp)"
  88. echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}"
  89. if __get_remote_file "$remote_path" "$tmpfile"; then
  90. unzip -q -d "$korebuild_path" "$tmpfile"
  91. fi
  92. rm "$tmpfile" || true
  93. fi
  94. source "$korebuild_path/KoreBuild.sh"
  95. } || {
  96. if [ -d "$korebuild_path" ]; then
  97. echo "Cleaning up after failed installation"
  98. rm -rf "$korebuild_path" || true
  99. fi
  100. return 1
  101. }
  102. }
  103. __error() {
  104. echo -e "${RED}error: $*${RESET}" 1>&2
  105. }
  106. __warn() {
  107. echo -e "${YELLOW}warning: $*${RESET}"
  108. }
  109. __machine_has() {
  110. hash "$1" > /dev/null 2>&1
  111. return $?
  112. }
  113. __get_remote_file() {
  114. local remote_path=$1
  115. local local_path=$2
  116. if [[ "$remote_path" != 'http'* ]]; then
  117. cp "$remote_path" "$local_path"
  118. return 0
  119. fi
  120. local failed=false
  121. if __machine_has wget; then
  122. wget --tries 10 --quiet -O "$local_path" "$remote_path" || failed=true
  123. else
  124. failed=true
  125. fi
  126. if [ "$failed" = true ] && __machine_has curl; then
  127. failed=false
  128. curl --retry 10 -sSL -f --create-dirs -o "$local_path" "$remote_path" || failed=true
  129. fi
  130. if [ "$failed" = true ]; then
  131. __error "Download failed: $remote_path" 1>&2
  132. return 1
  133. fi
  134. }
  135. #
  136. # main
  137. #
  138. while [[ $# -gt 0 ]]; do
  139. case $1 in
  140. -\?|-h|--help)
  141. __usage --no-exit
  142. exit 0
  143. ;;
  144. --arch)
  145. shift
  146. target_arch="${1:-}"
  147. [ -z "$target_arch" ] && __error "Missing value for parameter --arch" && __usage
  148. ;;
  149. --os-name)
  150. shift
  151. target_os_name="${1:-}"
  152. [ -z "$target_os_name" ] && __error "Missing value for parameter --os-name" && __usage
  153. ;;
  154. --restore|-[Rr]estore)
  155. run_restore=true
  156. ;;
  157. --no-restore)
  158. run_restore=false
  159. ;;
  160. --build|-[Bb]build)
  161. run_build=true
  162. ;;
  163. --no-build)
  164. run_build=false
  165. # --no-build implies --no-restore
  166. [ -z "$run_restore" ] && run_restore=false
  167. ;;
  168. --no-build-deps)
  169. build_deps=false
  170. ;;
  171. --pack|-[Pp]ack)
  172. run_pack=true
  173. ;;
  174. --no-pack)
  175. run_pack=false
  176. ;;
  177. --test|-[Tt]est)
  178. run_tests=true
  179. ;;
  180. --no-test)
  181. run_tests=false
  182. ;;
  183. --projects|-[Pp]rojects)
  184. shift
  185. build_projects="${1:-}"
  186. [ -z "$build_projects" ] && __error "Missing value for parameter --projects" && __usage
  187. ;;
  188. --all|-[Aa]ll)
  189. build_all=true
  190. ;;
  191. --build-managed|-BuildManaged)
  192. build_managed=true
  193. ;;
  194. --no-build-managed|-NoBuildManaged)
  195. build_managed=false
  196. ;;
  197. --build-nodejs|-BuildNodeJs)
  198. build_nodejs=true
  199. ;;
  200. --no-build-nodejs|-NoBuildNodeJs)
  201. build_nodejs=false
  202. ;;
  203. --build-java|-BuildJava)
  204. build_java=true
  205. ;;
  206. --no-build-java|-NoBuildJava)
  207. build_java=false
  208. ;;
  209. --build-native|-BuildNative)
  210. build_native=true
  211. ;;
  212. --no-build-native|-NoBuildNative)
  213. build_native=false
  214. ;;
  215. --ci|-[Cc][Ii])
  216. ci=true
  217. ;;
  218. --verbose|-[Vv]erbose)
  219. verbose=true
  220. ;;
  221. *)
  222. msbuild_args[${#msbuild_args[*]}]="$1"
  223. ;;
  224. esac
  225. shift
  226. done
  227. if ! __machine_has unzip; then
  228. __error 'Missing required command: unzip'
  229. exit 1
  230. fi
  231. if ! __machine_has curl && ! __machine_has wget; then
  232. __error 'Missing required command. Either wget or curl is required.'
  233. exit 1
  234. fi
  235. if [ -f "$config_file" ]; then
  236. if __machine_has jq ; then
  237. if jq '.' "$config_file" >/dev/null ; then
  238. config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")"
  239. config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")"
  240. else
  241. __error "$config_file is invalid JSON. Its settings will be ignored."
  242. exit 1
  243. fi
  244. elif __machine_has python ; then
  245. if python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then
  246. config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")"
  247. config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")"
  248. else
  249. __error "$config_file is invalid JSON. Its settings will be ignored."
  250. exit 1
  251. fi
  252. else
  253. __error 'Missing required command: jq or python. Could not parse the JSON file. Its settings will be ignored.'
  254. exit 1
  255. fi
  256. [ ! -z "${config_channel:-}" ] && channel="$config_channel"
  257. [ ! -z "${config_tools_source:-}" ] && tools_source="$config_tools_source"
  258. fi
  259. export DOTNET_HOME="$DIR/.dotnet"
  260. get_korebuild
  261. if [ "$build_all" = true ]; then
  262. msbuild_args[${#msbuild_args[*]}]="-p:BuildAllProjects=true"
  263. elif [ ! -z "$build_projects" ]; then
  264. msbuild_args[${#msbuild_args[*]}]="-p:Projects=$build_projects"
  265. elif [ -z "$build_managed" ] && [ -z "$build_nodejs" ] && [ -z "$build_java" ] && [ -z "$build_native" ]; then
  266. # This goal of this is to pick a sensible default for `build.sh` with zero arguments.
  267. # 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.
  268. __warn "No default group of projects was specified, so building the 'managed' subset of projects. Run ``build.sh -help`` for more details."
  269. build_managed=true
  270. fi
  271. if [ "$build_deps" = false ]; then
  272. msbuild_args[${#msbuild_args[*]}]="-p:BuildProjectReferences=false"
  273. fi
  274. # Only set these MSBuild properties if they were explicitly set by build parameters.
  275. [ ! -z "$build_java" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildJava=$build_java"
  276. [ ! -z "$build_native" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildNative=$build_native"
  277. [ ! -z "$build_nodejs" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildNodeJS=$build_nodejs"
  278. [ ! -z "$build_managed" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildManaged=$build_managed"
  279. # Run restore by default unless --no-restore or --no-build was specified.
  280. [ -z "$run_restore" ] && run_restore=true
  281. [ "$run_restore" = true ] && msbuild_args[${#msbuild_args[*]}]="-restore"
  282. msbuild_args[${#msbuild_args[*]}]="-p:_RunBuild=$run_build"
  283. msbuild_args[${#msbuild_args[*]}]="-p:_RunPack=$run_pack"
  284. msbuild_args[${#msbuild_args[*]}]="-p:_RunTests=$run_tests"
  285. msbuild_args[${#msbuild_args[*]}]="-p:TargetArchitecture=$target_arch"
  286. msbuild_args[${#msbuild_args[*]}]="-p:TargetOsName=$target_os_name"
  287. # Disable downloading ref assemblies as a tarball. Use netfx refs from the Microsoft.NETFramework.ReferenceAssemblies NuGet package instead.
  288. [ -z "${KOREBUILD_SKIP_INSTALL_NETFX:-}" ] && KOREBUILD_SKIP_INSTALL_NETFX=1
  289. export KOREBUILD_KEEPGLOBALJSON=1
  290. set_korebuildsettings "$tools_source" "$DOTNET_HOME" "$DIR" "$config_file" "$ci"
  291. # This incantation avoids unbound variable issues if msbuild_args is empty
  292. # https://stackoverflow.com/questions/7577052/bash-empty-array-expansion-with-set-u
  293. invoke_korebuild_command 'default-build' ${msbuild_args[@]+"${msbuild_args[@]}"}