Revert "[Tizen] Fix crash on accessing 0x0 while unwinding (#287)"
[platform/upstream/coreclr.git] / .dotnet / dotnet-install.sh
1 #!/usr/bin/env bash
2 # Copyright (c) .NET Foundation and contributors. All rights reserved.
3 # Licensed under the MIT license. See LICENSE file in the project root for full license information.
4 #
5
6 # Stop script on NZEC
7 set -e
8 # Stop script if unbound variable found (use ${var:-} if intentional)
9 set -u
10 # By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success
11 # This is causing it to fail
12 set -o pipefail
13
14 # Use in the the functions: eval $invocation
15 invocation='say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"'
16
17 # standard output may be used as a return value in the functions
18 # we need a way to write text on the screen in the functions so that
19 # it won't interfere with the return value.
20 # Exposing stream 3 as a pipe to standard output of the script itself
21 exec 3>&1
22
23 # Setup some colors to use. These need to work in fairly limited shells, like the Ubuntu Docker container where there are only 8 colors.
24 # See if stdout is a terminal
25 if [ -t 1 ] && command -v tput > /dev/null; then
26     # see if it supports colors
27     ncolors=$(tput colors)
28     if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
29         bold="$(tput bold       || echo)"
30         normal="$(tput sgr0     || echo)"
31         black="$(tput setaf 0   || echo)"
32         red="$(tput setaf 1     || echo)"
33         green="$(tput setaf 2   || echo)"
34         yellow="$(tput setaf 3  || echo)"
35         blue="$(tput setaf 4    || echo)"
36         magenta="$(tput setaf 5 || echo)"
37         cyan="$(tput setaf 6    || echo)"
38         white="$(tput setaf 7   || echo)"
39     fi
40 fi
41
42 say_warning() {
43     printf "%b\n" "${yellow:-}dotnet_install: Warning: $1${normal:-}"
44 }
45
46 say_err() {
47     printf "%b\n" "${red:-}dotnet_install: Error: $1${normal:-}" >&2
48 }
49
50 say() {
51     # using stream 3 (defined in the beginning) to not interfere with stdout of functions
52     # which may be used as return value
53     printf "%b\n" "${cyan:-}dotnet-install:${normal:-} $1" >&3
54 }
55
56 say_verbose() {
57     if [ "$verbose" = true ]; then
58         say "$1"
59     fi
60 }
61
62 # This platform list is finite - if the SDK/Runtime has supported Linux distribution-specific assets,
63 #   then and only then should the Linux distribution appear in this list.
64 # Adding a Linux distribution to this list does not imply distribution-specific support.
65 get_legacy_os_name_from_platform() {
66     eval $invocation
67
68     platform="$1"
69     case "$platform" in
70         "centos.7")
71             echo "centos"
72             return 0
73             ;;
74         "debian.8")
75             echo "debian"
76             return 0
77             ;;
78         "debian.9")
79             echo "debian.9"
80             return 0
81             ;;
82         "fedora.23")
83             echo "fedora.23"
84             return 0
85             ;;
86         "fedora.24")
87             echo "fedora.24"
88             return 0
89             ;;
90         "fedora.27")
91             echo "fedora.27"
92             return 0
93             ;;
94         "fedora.28")
95             echo "fedora.28"
96             return 0
97             ;;
98         "opensuse.13.2")
99             echo "opensuse.13.2"
100             return 0
101             ;;
102         "opensuse.42.1")
103             echo "opensuse.42.1"
104             return 0
105             ;;
106         "opensuse.42.3")
107             echo "opensuse.42.3"
108             return 0
109             ;;
110         "rhel.7"*)
111             echo "rhel"
112             return 0
113             ;;
114         "ubuntu.14.04")
115             echo "ubuntu"
116             return 0
117             ;;
118         "ubuntu.16.04")
119             echo "ubuntu.16.04"
120             return 0
121             ;;
122         "ubuntu.16.10")
123             echo "ubuntu.16.10"
124             return 0
125             ;;
126         "ubuntu.18.04")
127             echo "ubuntu.18.04"
128             return 0
129             ;;
130         "alpine.3.4.3")
131             echo "alpine"
132             return 0
133             ;;
134     esac
135     return 1
136 }
137
138 get_linux_platform_name() {
139     eval $invocation
140
141     if [ -n "$runtime_id" ]; then
142         echo "${runtime_id%-*}"
143         return 0
144     else
145         if [ -e /etc/os-release ]; then
146             . /etc/os-release
147             echo "$ID${VERSION_ID:+.${VERSION_ID}}"
148             return 0
149         elif [ -e /etc/redhat-release ]; then
150             local redhatRelease=$(</etc/redhat-release)
151             if [[ $redhatRelease == "CentOS release 6."* || $redhatRelease == "Red Hat Enterprise Linux "*" release 6."* ]]; then
152                 echo "rhel.6"
153                 return 0
154             fi
155         fi
156     fi
157
158     say_verbose "Linux specific platform name and version could not be detected: UName = $uname"
159     return 1
160 }
161
162 is_musl_based_distro() {
163     (ldd --version 2>&1 || true) | grep -q musl
164 }
165
166 get_current_os_name() {
167     eval $invocation
168
169     local uname=$(uname)
170     if [ "$uname" = "Darwin" ]; then
171         echo "osx"
172         return 0
173     elif [ "$uname" = "FreeBSD" ]; then
174         echo "freebsd"
175         return 0        
176     elif [ "$uname" = "Linux" ]; then
177         local linux_platform_name
178         linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; }
179
180         if [ "$linux_platform_name" = "rhel.6" ]; then
181             echo $linux_platform_name
182             return 0
183         elif is_musl_based_distro; then
184             echo "linux-musl"
185             return 0
186         else
187             echo "linux"
188             return 0
189         fi
190     fi
191
192     say_err "OS name could not be detected: UName = $uname"
193     return 1
194 }
195
196 get_legacy_os_name() {
197     eval $invocation
198
199     local uname=$(uname)
200     if [ "$uname" = "Darwin" ]; then
201         echo "osx"
202         return 0
203     elif [ -n "$runtime_id" ]; then
204         echo $(get_legacy_os_name_from_platform "${runtime_id%-*}" || echo "${runtime_id%-*}")
205         return 0
206     else
207         if [ -e /etc/os-release ]; then
208             . /etc/os-release
209             os=$(get_legacy_os_name_from_platform "$ID${VERSION_ID:+.${VERSION_ID}}" || echo "")
210             if [ -n "$os" ]; then
211                 echo "$os"
212                 return 0
213             fi
214         fi
215     fi
216
217     say_verbose "Distribution specific OS name and version could not be detected: UName = $uname"
218     return 1
219 }
220
221 machine_has() {
222     eval $invocation
223
224     hash "$1" > /dev/null 2>&1
225     return $?
226 }
227
228
229 check_min_reqs() {
230     local hasMinimum=false
231     if machine_has "curl"; then
232         hasMinimum=true
233     elif machine_has "wget"; then
234         hasMinimum=true
235     fi
236
237     if [ "$hasMinimum" = "false" ]; then
238         say_err "curl (recommended) or wget are required to download dotnet. Install missing prerequisite to proceed."
239         return 1
240     fi
241     return 0
242 }
243
244 check_pre_reqs() {
245     eval $invocation
246
247     if [ "${DOTNET_INSTALL_SKIP_PREREQS:-}" = "1" ]; then
248         return 0
249     fi
250
251     if [ "$(uname)" = "Linux" ]; then
252         if is_musl_based_distro; then
253             if ! command -v scanelf > /dev/null; then
254                 say_warning "scanelf not found, please install pax-utils package."
255                 return 0
256             fi
257             LDCONFIG_COMMAND="scanelf --ldpath -BF '%f'"
258             [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libintl)" ] && say_warning "Unable to locate libintl. Probable prerequisite missing; install libintl (or gettext)."
259         else
260             if [ ! -x "$(command -v ldconfig)" ]; then
261                 say_verbose "ldconfig is not in PATH, trying /sbin/ldconfig."
262                 LDCONFIG_COMMAND="/sbin/ldconfig"
263             else
264                 LDCONFIG_COMMAND="ldconfig"
265             fi
266             local librarypath=${LD_LIBRARY_PATH:-}
267             LDCONFIG_COMMAND="$LDCONFIG_COMMAND -NXv ${librarypath//:/ }"
268         fi
269
270         [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep zlib)" ] && say_warning "Unable to locate zlib. Probable prerequisite missing; install zlib."
271         [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep ssl)" ] && say_warning "Unable to locate libssl. Probable prerequisite missing; install libssl."
272         [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libicu)" ] && say_warning "Unable to locate libicu. Probable prerequisite missing; install libicu."
273         [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep lttng)" ] && say_warning "Unable to locate liblttng. Probable prerequisite missing; install libcurl."
274         [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libcurl)" ] && say_warning "Unable to locate libcurl. Probable prerequisite missing; install libcurl."
275     fi
276
277     return 0
278 }
279
280 # args:
281 # input - $1
282 to_lowercase() {
283     #eval $invocation
284
285     echo "$1" | tr '[:upper:]' '[:lower:]'
286     return 0
287 }
288
289 # args:
290 # input - $1
291 remove_trailing_slash() {
292     #eval $invocation
293
294     local input="${1:-}"
295     echo "${input%/}"
296     return 0
297 }
298
299 # args:
300 # input - $1
301 remove_beginning_slash() {
302     #eval $invocation
303
304     local input="${1:-}"
305     echo "${input#/}"
306     return 0
307 }
308
309 # args:
310 # root_path - $1
311 # child_path - $2 - this parameter can be empty
312 combine_paths() {
313     eval $invocation
314
315     # TODO: Consider making it work with any number of paths. For now:
316     if [ ! -z "${3:-}" ]; then
317         say_err "combine_paths: Function takes two parameters."
318         return 1
319     fi
320
321     local root_path="$(remove_trailing_slash "$1")"
322     local child_path="$(remove_beginning_slash "${2:-}")"
323     say_verbose "combine_paths: root_path=$root_path"
324     say_verbose "combine_paths: child_path=$child_path"
325     echo "$root_path/$child_path"
326     return 0
327 }
328
329 get_machine_architecture() {
330     eval $invocation
331
332     if command -v uname > /dev/null; then
333         CPUName=$(uname -m)
334         case $CPUName in
335         armv7l)
336             echo "arm"
337             return 0
338             ;;
339         aarch64)
340             echo "arm64"
341             return 0
342             ;;
343         esac
344     fi
345
346     # Always default to 'x64'
347     echo "x64"
348     return 0
349 }
350
351 # args:
352 # architecture - $1
353 get_normalized_architecture_from_architecture() {
354     eval $invocation
355
356     local architecture="$(to_lowercase "$1")"
357     case "$architecture" in
358         \<auto\>)
359             echo "$(get_normalized_architecture_from_architecture "$(get_machine_architecture)")"
360             return 0
361             ;;
362         amd64|x64)
363             echo "x64"
364             return 0
365             ;;
366         arm)
367             echo "arm"
368             return 0
369             ;;
370         arm64)
371             echo "arm64"
372             return 0
373             ;;
374     esac
375
376     say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues"
377     return 1
378 }
379
380 # The version text returned from the feeds is a 1-line or 2-line string:
381 # For the SDK and the dotnet runtime (2 lines):
382 # Line 1: # commit_hash
383 # Line 2: # 4-part version
384 # For the aspnetcore runtime (1 line):
385 # Line 1: # 4-part version
386
387 # args:
388 # version_text - stdin
389 get_version_from_version_info() {
390     eval $invocation
391
392     cat | tail -n 1 | sed 's/\r$//'
393     return 0
394 }
395
396 # args:
397 # install_root - $1
398 # relative_path_to_package - $2
399 # specific_version - $3
400 is_dotnet_package_installed() {
401     eval $invocation
402
403     local install_root="$1"
404     local relative_path_to_package="$2"
405     local specific_version="${3//[$'\t\r\n']}"
406
407     local dotnet_package_path="$(combine_paths "$(combine_paths "$install_root" "$relative_path_to_package")" "$specific_version")"
408     say_verbose "is_dotnet_package_installed: dotnet_package_path=$dotnet_package_path"
409
410     if [ -d "$dotnet_package_path" ]; then
411         return 0
412     else
413         return 1
414     fi
415 }
416
417 # args:
418 # azure_feed - $1
419 # channel - $2
420 # normalized_architecture - $3
421 # coherent - $4
422 get_latest_version_info() {
423     eval $invocation
424
425     local azure_feed="$1"
426     local channel="$2"
427     local normalized_architecture="$3"
428     local coherent="$4"
429
430     local version_file_url=null
431     if [[ "$runtime" == "dotnet" ]]; then
432         version_file_url="$uncached_feed/Runtime/$channel/latest.version"
433     elif [[ "$runtime" == "aspnetcore" ]]; then
434         version_file_url="$uncached_feed/aspnetcore/Runtime/$channel/latest.version"
435     elif [ -z "$runtime" ]; then
436         if [ "$coherent" = true ]; then
437             version_file_url="$uncached_feed/Sdk/$channel/latest.coherent.version"
438         else
439             version_file_url="$uncached_feed/Sdk/$channel/latest.version"
440         fi
441     else
442         say_err "Invalid value for \$runtime"
443         return 1
444     fi
445     say_verbose "get_latest_version_info: latest url: $version_file_url"
446
447     download "$version_file_url"
448     return $?
449 }
450
451 # args:
452 # json_file - $1
453 parse_jsonfile_for_version() {
454     eval $invocation
455
456     local json_file="$1"
457     if [ ! -f "$json_file" ]; then
458         say_err "Unable to find \`$json_file\`"
459         return 1
460     fi
461
462     sdk_section=$(cat $json_file | awk '/"sdk"/,/}/')
463     if [ -z "$sdk_section" ]; then
464         say_err "Unable to parse the SDK node in \`$json_file\`"
465         return 1
466     fi
467
468     sdk_list=$(echo $sdk_section | awk -F"[{}]" '{print $2}')
469     sdk_list=${sdk_list//[\" ]/}
470     sdk_list=${sdk_list//,/$'\n'}
471     sdk_list="$(echo -e "${sdk_list}" | tr -d '[[:space:]]')"
472
473     local version_info=""
474     while read -r line; do
475       IFS=:
476       while read -r key value; do
477         if [[ "$key" == "version" ]]; then
478           version_info=$value
479         fi
480       done <<< "$line"
481     done <<< "$sdk_list"
482     if [ -z "$version_info" ]; then
483         say_err "Unable to find the SDK:version node in \`$json_file\`"
484         return 1
485     fi
486
487     unset IFS;
488     echo "$version_info"
489     return 0
490 }
491
492 # args:
493 # azure_feed - $1
494 # channel - $2
495 # normalized_architecture - $3
496 # version - $4
497 # json_file - $5
498 get_specific_version_from_version() {
499     eval $invocation
500
501     local azure_feed="$1"
502     local channel="$2"
503     local normalized_architecture="$3"
504     local version="$(to_lowercase "$4")"
505     local json_file="$5"
506
507     if [ -z "$json_file" ]; then
508         case "$version" in
509             latest)
510                 local version_info
511                 version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
512                 say_verbose "get_specific_version_from_version: version_info=$version_info"
513                 echo "$version_info" | get_version_from_version_info
514                 return 0
515                 ;;
516             coherent)
517                 local version_info
518                 version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" true)" || return 1
519                 say_verbose "get_specific_version_from_version: version_info=$version_info"
520                 echo "$version_info" | get_version_from_version_info
521                 return 0
522                 ;;
523             *)
524                 echo "$version"
525                 return 0
526                 ;;
527         esac
528     else
529         local version_info
530         version_info="$(parse_jsonfile_for_version "$json_file")" || return 1
531         echo "$version_info"
532         return 0
533     fi
534 }
535
536 # args:
537 # azure_feed - $1
538 # channel - $2
539 # normalized_architecture - $3
540 # specific_version - $4
541 construct_download_link() {
542     eval $invocation
543
544     local azure_feed="$1"
545     local channel="$2"
546     local normalized_architecture="$3"
547     local specific_version="${4//[$'\t\r\n']}"
548
549     local osname
550     osname="$(get_current_os_name)" || return 1
551
552     local download_link=null
553     if [[ "$runtime" == "dotnet" ]]; then
554         download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_version-$osname-$normalized_architecture.tar.gz"
555     elif [[ "$runtime" == "aspnetcore" ]]; then
556         download_link="$azure_feed/aspnetcore/Runtime/$specific_version/aspnetcore-runtime-$specific_version-$osname-$normalized_architecture.tar.gz"
557     elif [ -z "$runtime" ]; then
558         download_link="$azure_feed/Sdk/$specific_version/dotnet-sdk-$specific_version-$osname-$normalized_architecture.tar.gz"
559     else
560         return 1
561     fi
562
563     echo "$download_link"
564     return 0
565 }
566
567 # args:
568 # azure_feed - $1
569 # channel - $2
570 # normalized_architecture - $3
571 # specific_version - $4
572 construct_legacy_download_link() {
573     eval $invocation
574
575     local azure_feed="$1"
576     local channel="$2"
577     local normalized_architecture="$3"
578     local specific_version="${4//[$'\t\r\n']}"
579
580     local distro_specific_osname
581     distro_specific_osname="$(get_legacy_os_name)" || return 1
582
583     local legacy_download_link=null
584     if [[ "$runtime" == "dotnet" ]]; then
585         legacy_download_link="$azure_feed/Runtime/$specific_version/dotnet-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz"
586     elif [ -z "$runtime" ]; then
587         legacy_download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz"
588     else
589         return 1
590     fi
591
592     echo "$legacy_download_link"
593     return 0
594 }
595
596 get_user_install_path() {
597     eval $invocation
598
599     if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then
600         echo "$DOTNET_INSTALL_DIR"
601     else
602         echo "$HOME/.dotnet"
603     fi
604     return 0
605 }
606
607 # args:
608 # install_dir - $1
609 resolve_installation_path() {
610     eval $invocation
611
612     local install_dir=$1
613     if [ "$install_dir" = "<auto>" ]; then
614         local user_install_path="$(get_user_install_path)"
615         say_verbose "resolve_installation_path: user_install_path=$user_install_path"
616         echo "$user_install_path"
617         return 0
618     fi
619
620     echo "$install_dir"
621     return 0
622 }
623
624 # args:
625 # relative_or_absolute_path - $1
626 get_absolute_path() {
627     eval $invocation
628
629     local relative_or_absolute_path=$1
630     echo "$(cd "$(dirname "$1")" && pwd -P)/$(basename "$1")"
631     return 0
632 }
633
634 # args:
635 # input_files - stdin
636 # root_path - $1
637 # out_path - $2
638 # override - $3
639 copy_files_or_dirs_from_list() {
640     eval $invocation
641
642     local root_path="$(remove_trailing_slash "$1")"
643     local out_path="$(remove_trailing_slash "$2")"
644     local override="$3"
645     local osname="$(get_current_os_name)"
646     local override_switch=$(
647         if [ "$override" = false ]; then
648             if [ "$osname" = "linux-musl" ]; then
649                 printf -- "-u";
650             else
651                 printf -- "-n";
652             fi
653         fi)
654
655     cat | uniq | while read -r file_path; do
656         local path="$(remove_beginning_slash "${file_path#$root_path}")"
657         local target="$out_path/$path"
658         if [ "$override" = true ] || (! ([ -d "$target" ] || [ -e "$target" ])); then
659             mkdir -p "$out_path/$(dirname "$path")"
660             if [ -d "$target" ]; then
661                 rm -rf "$target"
662             fi
663             cp -R $override_switch "$root_path/$path" "$target"
664         fi
665     done
666 }
667
668 # args:
669 # zip_path - $1
670 # out_path - $2
671 extract_dotnet_package() {
672     eval $invocation
673
674     local zip_path="$1"
675     local out_path="$2"
676
677     local temp_out_path="$(mktemp -d "$temporary_file_template")"
678
679     local failed=false
680     tar -xzf "$zip_path" -C "$temp_out_path" > /dev/null || failed=true
681
682     local folders_with_version_regex='^.*/[0-9]+\.[0-9]+[^/]+/'
683     find "$temp_out_path" -type f | grep -Eo "$folders_with_version_regex" | sort | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" false
684     find "$temp_out_path" -type f | grep -Ev "$folders_with_version_regex" | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" "$override_non_versioned_files"
685
686     rm -rf "$temp_out_path"
687
688     if [ "$failed" = true ]; then
689         say_err "Extraction failed"
690         return 1
691     fi
692 }
693
694 # args:
695 # remote_path - $1
696 # [out_path] - $2 - stdout if not provided
697 download() {
698     eval $invocation
699
700     local remote_path="$1"
701     local out_path="${2:-}"
702
703     if [[ "$remote_path" != "http"* ]]; then
704         cp "$remote_path" "$out_path"
705         return $?
706     fi
707
708     local failed=false
709     if machine_has "curl"; then
710         downloadcurl "$remote_path" "$out_path" || failed=true
711     elif machine_has "wget"; then
712         downloadwget "$remote_path" "$out_path" || failed=true
713     else
714         failed=true
715     fi
716     if [ "$failed" = true ]; then
717         say_verbose "Download failed: $remote_path"
718         return 1
719     fi
720     return 0
721 }
722
723 downloadcurl() {
724     eval $invocation
725     local remote_path="$1"
726     local out_path="${2:-}"
727
728     # Append feed_credential as late as possible before calling curl to avoid logging feed_credential
729     remote_path="${remote_path}${feed_credential}"
730
731     local failed=false
732     if [ -z "$out_path" ]; then
733         curl --retry 10 -sSL -f --create-dirs "$remote_path" || failed=true
734     else
735         curl --retry 10 -sSL -f --create-dirs -o "$out_path" "$remote_path" || failed=true
736     fi
737     if [ "$failed" = true ]; then
738         say_verbose "Curl download failed"
739         return 1
740     fi
741     return 0
742 }
743
744 downloadwget() {
745     eval $invocation
746     local remote_path="$1"
747     local out_path="${2:-}"
748
749     # Append feed_credential as late as possible before calling wget to avoid logging feed_credential
750     remote_path="${remote_path}${feed_credential}"
751
752     local failed=false
753     if [ -z "$out_path" ]; then
754         wget -q --tries 10 -O - "$remote_path" || failed=true
755     else
756         wget --tries 10 -O "$out_path" "$remote_path" || failed=true
757     fi
758     if [ "$failed" = true ]; then
759         say_verbose "Wget download failed"
760         return 1
761     fi
762     return 0
763 }
764
765 calculate_vars() {
766     eval $invocation
767     valid_legacy_download_link=true
768
769     normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")"
770     say_verbose "normalized_architecture=$normalized_architecture"
771
772     specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version" "$json_file")"
773     say_verbose "specific_version=$specific_version"
774     if [ -z "$specific_version" ]; then
775         say_err "Could not resolve version information."
776         return 1
777     fi
778
779     download_link="$(construct_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version")"
780     say_verbose "Constructed primary named payload URL: $download_link"
781
782     legacy_download_link="$(construct_legacy_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version")" || valid_legacy_download_link=false
783
784     if [ "$valid_legacy_download_link" = true ]; then
785         say_verbose "Constructed legacy named payload URL: $legacy_download_link"
786     else
787         say_verbose "Cound not construct a legacy_download_link; omitting..."
788     fi
789
790     install_root="$(resolve_installation_path "$install_dir")"
791     say_verbose "InstallRoot: $install_root"
792 }
793
794 install_dotnet() {
795     eval $invocation
796     local download_failed=false
797     local asset_name=''
798     local asset_relative_path=''
799
800     if [[ "$runtime" == "dotnet" ]]; then
801         asset_relative_path="shared/Microsoft.NETCore.App"
802         asset_name=".NET Core Runtime"
803     elif [[ "$runtime" == "aspnetcore" ]]; then
804         asset_relative_path="shared/Microsoft.AspNetCore.App"
805         asset_name="ASP.NET Core Runtime"
806     elif [ -z "$runtime" ]; then
807         asset_relative_path="sdk"
808         asset_name=".NET Core SDK"
809     else
810         say_err "Invalid value for \$runtime"
811         return 1
812     fi
813
814     #  Check if the SDK version is already installed.
815     if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then
816         say "$asset_name version $specific_version is already installed."
817         return 0
818     fi
819
820     mkdir -p "$install_root"
821     zip_path="$(mktemp "$temporary_file_template")"
822     say_verbose "Zip path: $zip_path"
823
824     say "Downloading link: $download_link"
825
826     # Failures are normal in the non-legacy case for ultimately legacy downloads.
827     # Do not output to stderr, since output to stderr is considered an error.
828     download "$download_link" "$zip_path" 2>&1 || download_failed=true
829
830     #  if the download fails, download the legacy_download_link
831     if [ "$download_failed" = true ]; then
832         say "Cannot download: $download_link"
833
834         if [ "$valid_legacy_download_link" = true ]; then
835             download_failed=false
836             download_link="$legacy_download_link"
837             zip_path="$(mktemp "$temporary_file_template")"
838             say_verbose "Legacy zip path: $zip_path"
839             say "Downloading legacy link: $download_link"
840             download "$download_link" "$zip_path" 2>&1 || download_failed=true
841
842             if [ "$download_failed" = true ]; then
843                 say "Cannot download: $download_link"
844             fi
845         fi
846     fi
847
848     if [ "$download_failed" = true ]; then
849         say_err "Could not find/download: \`$asset_name\` with version = $specific_version"
850         say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support"
851         return 1
852     fi
853
854     say "Extracting zip from $download_link"
855     extract_dotnet_package "$zip_path" "$install_root"
856
857     #  Check if the SDK version is installed; if not, fail the installation.
858     # if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed.
859     if [[ $specific_version == *"rtm"* || $specific_version == *"servicing"* ]]; then
860         IFS='-'
861         read -ra verArr <<< "$specific_version"
862         release_version="${verArr[0]}"
863         unset IFS;
864         say_verbose "Checking installation: version = $release_version"
865         if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$release_version"; then
866             return 0
867         fi
868     fi
869
870     #  Check if the standard SDK version is installed.
871     say_verbose "Checking installation: version = $specific_version"
872     if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then
873         return 0
874     fi
875
876     say_err "\`$asset_name\` with version = $specific_version failed to install with an unknown error."
877     return 1
878 }
879
880 args=("$@")
881
882 local_version_file_relative_path="/.version"
883 bin_folder_relative_path=""
884 temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX"
885
886 channel="LTS"
887 version="Latest"
888 json_file=""
889 install_dir="<auto>"
890 architecture="<auto>"
891 dry_run=false
892 no_path=false
893 no_cdn=false
894 azure_feed="https://dotnetcli.azureedge.net/dotnet"
895 uncached_feed="https://dotnetcli.blob.core.windows.net/dotnet"
896 feed_credential=""
897 verbose=false
898 runtime=""
899 runtime_id=""
900 override_non_versioned_files=true
901 non_dynamic_parameters=""
902
903 while [ $# -ne 0 ]
904 do
905     name="$1"
906     case "$name" in
907         -c|--channel|-[Cc]hannel)
908             shift
909             channel="$1"
910             ;;
911         -v|--version|-[Vv]ersion)
912             shift
913             version="$1"
914             ;;
915         -i|--install-dir|-[Ii]nstall[Dd]ir)
916             shift
917             install_dir="$1"
918             ;;
919         --arch|--architecture|-[Aa]rch|-[Aa]rchitecture)
920             shift
921             architecture="$1"
922             ;;
923         --shared-runtime|-[Ss]hared[Rr]untime)
924             say_warning "The --shared-runtime flag is obsolete and may be removed in a future version of this script. The recommended usage is to specify '--runtime dotnet'."
925             if [ -z "$runtime" ]; then
926                 runtime="dotnet"
927             fi
928             ;;
929         --runtime|-[Rr]untime)
930             shift
931             runtime="$1"
932             if [[ "$runtime" != "dotnet" ]] && [[ "$runtime" != "aspnetcore" ]]; then
933                 say_err "Unsupported value for --runtime: '$1'. Valid values are 'dotnet' and 'aspnetcore'."
934                 if [[ "$runtime" == "windowsdesktop" ]]; then
935                     say_err "WindowsDesktop archives are manufactured for Windows platforms only."
936                 fi
937                 exit 1
938             fi
939             ;;
940         --dry-run|-[Dd]ry[Rr]un)
941             dry_run=true
942             ;;
943         --no-path|-[Nn]o[Pp]ath)
944             no_path=true
945             non_dynamic_parameters+=" $name"
946             ;;
947         --verbose|-[Vv]erbose)
948             verbose=true
949             non_dynamic_parameters+=" $name"
950             ;;
951         --no-cdn|-[Nn]o[Cc]dn)
952             no_cdn=true
953             non_dynamic_parameters+=" $name"
954             ;;
955         --azure-feed|-[Aa]zure[Ff]eed)
956             shift
957             azure_feed="$1"
958             non_dynamic_parameters+=" $name "\""$1"\"""
959             ;;
960         --uncached-feed|-[Uu]ncached[Ff]eed)
961             shift
962             uncached_feed="$1"
963             non_dynamic_parameters+=" $name "\""$1"\"""
964             ;;
965         --feed-credential|-[Ff]eed[Cc]redential)
966             shift
967             feed_credential="$1"
968             non_dynamic_parameters+=" $name "\""$1"\"""
969             ;;
970         --runtime-id|-[Rr]untime[Ii]d)
971             shift
972             runtime_id="$1"
973             non_dynamic_parameters+=" $name "\""$1"\"""
974             ;;
975         --jsonfile|-[Jj][Ss]on[Ff]ile)
976             shift
977             json_file="$1"
978             ;;
979         --skip-non-versioned-files|-[Ss]kip[Nn]on[Vv]ersioned[Ff]iles)
980             override_non_versioned_files=false
981             non_dynamic_parameters+=" $name"
982             ;;
983         -?|--?|-h|--help|-[Hh]elp)
984             script_name="$(basename "$0")"
985             echo ".NET Tools Installer"
986             echo "Usage: $script_name [-c|--channel <CHANNEL>] [-v|--version <VERSION>] [-p|--prefix <DESTINATION>]"
987             echo "       $script_name -h|-?|--help"
988             echo ""
989             echo "$script_name is a simple command line interface for obtaining dotnet cli."
990             echo ""
991             echo "Options:"
992             echo "  -c,--channel <CHANNEL>         Download from the channel specified, Defaults to \`$channel\`."
993             echo "      -Channel"
994             echo "          Possible values:"
995             echo "          - Current - most current release"
996             echo "          - LTS - most current supported release"
997             echo "          - 2-part version in a format A.B - represents a specific release"
998             echo "              examples: 2.0; 1.0"
999             echo "          - Branch name"
1000             echo "              examples: release/2.0.0; Master"
1001             echo "          Note: The version parameter overrides the channel parameter."
1002             echo "  -v,--version <VERSION>         Use specific VERSION, Defaults to \`$version\`."
1003             echo "      -Version"
1004             echo "          Possible values:"
1005             echo "          - latest - most latest build on specific channel"
1006             echo "          - coherent - most latest coherent build on specific channel"
1007             echo "              coherent applies only to SDK downloads"
1008             echo "          - 3-part version in a format A.B.C - represents specific version of build"
1009             echo "              examples: 2.0.0-preview2-006120; 1.1.0"
1010             echo "  -i,--install-dir <DIR>             Install under specified location (see Install Location below)"
1011             echo "      -InstallDir"
1012             echo "  --architecture <ARCHITECTURE>      Architecture of dotnet binaries to be installed, Defaults to \`$architecture\`."
1013             echo "      --arch,-Architecture,-Arch"
1014             echo "          Possible values: x64, arm, and arm64"
1015             echo "  --runtime <RUNTIME>                Installs a shared runtime only, without the SDK."
1016             echo "      -Runtime"
1017             echo "          Possible values:"
1018             echo "          - dotnet     - the Microsoft.NETCore.App shared runtime"
1019             echo "          - aspnetcore - the Microsoft.AspNetCore.App shared runtime"
1020             echo "  --dry-run,-DryRun                  Do not perform installation. Display download link."
1021             echo "  --no-path, -NoPath                 Do not set PATH for the current process."
1022             echo "  --verbose,-Verbose                 Display diagnostics information."
1023             echo "  --azure-feed,-AzureFeed            Azure feed location. Defaults to $azure_feed, This parameter typically is not changed by the user."
1024             echo "  --uncached-feed,-UncachedFeed      Uncached feed location. This parameter typically is not changed by the user."
1025             echo "  --feed-credential,-FeedCredential  Azure feed shared access token. This parameter typically is not specified."
1026             echo "  --skip-non-versioned-files         Skips non-versioned files if they already exist, such as the dotnet executable."
1027             echo "      -SkipNonVersionedFiles"
1028             echo "  --no-cdn,-NoCdn                    Disable downloading from the Azure CDN, and use the uncached feed directly."
1029             echo "  --jsonfile <JSONFILE>              Determines the SDK version from a user specified global.json file."
1030             echo "                                     Note: global.json must have a value for 'SDK:Version'"
1031             echo "  --runtime-id                       Installs the .NET Tools for the given platform (use linux-x64 for portable linux)."
1032             echo "      -RuntimeId"
1033             echo "  -?,--?,-h,--help,-Help             Shows this help message"
1034             echo ""
1035             echo "Obsolete parameters:"
1036             echo "  --shared-runtime                   The recommended alternative is '--runtime dotnet'."
1037             echo "                                     This parameter is obsolete and may be removed in a future version of this script."
1038             echo "                                     Installs just the shared runtime bits, not the entire SDK."
1039             echo ""
1040             echo "Install Location:"
1041             echo "  Location is chosen in following order:"
1042             echo "    - --install-dir option"
1043             echo "    - Environmental variable DOTNET_INSTALL_DIR"
1044             echo "    - $HOME/.dotnet"
1045             exit 0
1046             ;;
1047         *)
1048             say_err "Unknown argument \`$name\`"
1049             exit 1
1050             ;;
1051     esac
1052
1053     shift
1054 done
1055
1056 if [ "$no_cdn" = true ]; then
1057     azure_feed="$uncached_feed"
1058 fi
1059
1060 check_min_reqs
1061 calculate_vars
1062 script_name=$(basename "$0")
1063
1064 if [ "$dry_run" = true ]; then
1065     say "Payload URLs:"
1066     say "Primary named payload URL: $download_link"
1067     if [ "$valid_legacy_download_link" = true ]; then
1068         say "Legacy named payload URL: $legacy_download_link"
1069     fi
1070     repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"""
1071     if [[ "$runtime" == "dotnet" ]]; then
1072         repeatable_command+=" --runtime "\""dotnet"\"""
1073     elif [[ "$runtime" == "aspnetcore" ]]; then
1074         repeatable_command+=" --runtime "\""aspnetcore"\"""
1075     fi
1076     repeatable_command+="$non_dynamic_parameters"
1077     say "Repeatable invocation: $repeatable_command"
1078     exit 0
1079 fi
1080
1081 check_pre_reqs
1082 install_dotnet
1083
1084 bin_path="$(get_absolute_path "$(combine_paths "$install_root" "$bin_folder_relative_path")")"
1085 if [ "$no_path" = false ]; then
1086     say "Adding to current process PATH: \`$bin_path\`. Note: This change will be visible only when sourcing script."
1087     export PATH="$bin_path":"$PATH"
1088 else
1089     say "Binaries of dotnet can be found in $bin_path"
1090 fi
1091
1092 say "Installation finished successfully."