2cba2c7688
When probing for terminal capabilities, tput will return exit-code 1 when the attribute is not valid for the current terminal. If we don't handle that exit-code, the shell-script too will exit with failure. This patch simply ignores errors for attributes we cannot use, resulting in the same result as if we never probed for it in the first place.
663 lines
18 KiB
Bash
Executable file
663 lines
18 KiB
Bash
Executable file
# Copyright (c) .NET Foundation and contributors. All rights reserved.
|
|
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
#
|
|
|
|
# Note: This script should be compatible with the dash shell used in Ubuntu. So avoid bashisms! See https://wiki.ubuntu.com/DashAsBinSh for more info
|
|
|
|
# Stop script on NZEC
|
|
set -e
|
|
# Stop script if unbound variable found (use ${var:-} if intentional)
|
|
set -u
|
|
# By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success
|
|
# This is causing it to fail
|
|
set -o pipefail
|
|
|
|
# Use in the the functions: eval $invocation
|
|
invocation='say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"'
|
|
|
|
# standard output may be used as a return value in the functions
|
|
# we need a way to write text on the screen in the functions so that
|
|
# it won't interfere with the return value.
|
|
# Exposing stream 3 as a pipe to standard output of the script itself
|
|
exec 3>&1
|
|
|
|
# Setup some colors to use. These need to work in fairly limited shells, like the Ubuntu Docker container where there are only 8 colors.
|
|
# See if stdout is a terminal
|
|
if [ -t 1 ]; then
|
|
# see if it supports colors
|
|
ncolors=$(tput colors)
|
|
if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
|
|
bold="$(tput bold || echo)"
|
|
normal="$(tput sgr0 || echo)"
|
|
black="$(tput setaf 0 || echo)"
|
|
red="$(tput setaf 1 || echo)"
|
|
green="$(tput setaf 2 || echo)"
|
|
yellow="$(tput setaf 3 || echo)"
|
|
blue="$(tput setaf 4 || echo)"
|
|
magenta="$(tput setaf 5 || echo)"
|
|
cyan="$(tput setaf 6 || echo)"
|
|
white="$(tput setaf 7 || echo)"
|
|
fi
|
|
fi
|
|
|
|
say_err() {
|
|
printf "%b\n" "${red:-}dotnet_install: Error: $1${normal:-}" >&2
|
|
}
|
|
|
|
say() {
|
|
# using stream 3 (defined in the beginning) to not interfere with stdout of functions
|
|
# which may be used as return value
|
|
printf "%b\n" "${cyan:-}dotnet-install:${normal:-} $1" >&3
|
|
}
|
|
|
|
say_verbose() {
|
|
if [ "$verbose" = true ]; then
|
|
say "$1"
|
|
fi
|
|
}
|
|
|
|
get_current_os_name() {
|
|
eval $invocation
|
|
|
|
local uname=$(uname)
|
|
if [ "$uname" = "Darwin" ]; then
|
|
echo "osx"
|
|
return 0
|
|
else
|
|
# Detect Distro
|
|
if [ "$(cat /etc/*-release | grep -cim1 ubuntu)" -eq 1 ]; then
|
|
if [ "$(cat /etc/*-release | grep -cim1 16.04)" -eq 1 ]; then
|
|
echo "ubuntu.16.04"
|
|
return 0
|
|
fi
|
|
|
|
echo "ubuntu"
|
|
return 0
|
|
elif [ "$(cat /etc/*-release | grep -cim1 centos)" -eq 1 ]; then
|
|
echo "centos"
|
|
return 0
|
|
elif [ "$(cat /etc/*-release | grep -cim1 rhel)" -eq 1 ]; then
|
|
echo "rhel"
|
|
return 0
|
|
elif [ "$(cat /etc/*-release | grep -cim1 debian)" -eq 1 ]; then
|
|
echo "debian"
|
|
return 0
|
|
elif [ "$(cat /etc/*-release | grep -cim1 fedora)" -eq 1 ]; then
|
|
if [ "$(cat /etc/*-release | grep -cim1 23)" -eq 1 ]; then
|
|
echo "fedora.23"
|
|
return 0
|
|
fi
|
|
elif [ "$(cat /etc/*-release | grep -cim1 opensuse)" -eq 1 ]; then
|
|
if [ "$(cat /etc/*-release | grep -cim1 13.2)" -eq 1 ]; then
|
|
echo "opensuse.13.2"
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
say_err "OS name could not be detected"
|
|
return 1
|
|
}
|
|
|
|
machine_has() {
|
|
eval $invocation
|
|
|
|
which "$1" > /dev/null 2>&1
|
|
return $?
|
|
}
|
|
|
|
check_min_reqs() {
|
|
if ! machine_has "curl"; then
|
|
say_err "curl is required to download dotnet. Install curl to proceed."
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
check_pre_reqs() {
|
|
eval $invocation
|
|
|
|
local failing=false;
|
|
|
|
if [ "${DOTNET_INSTALL_SKIP_PREREQS:-}" = "1" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if [ "$(uname)" = "Linux" ]; then
|
|
if ! [ -x "$(command -v ldconfig)" ]; then
|
|
echo "ldconfig is not in PATH, trying /sbin/ldconfig."
|
|
LDCONFIG_COMMAND="/sbin/ldconfig"
|
|
else
|
|
LDCONFIG_COMMAND="ldconfig"
|
|
fi
|
|
|
|
[ -z "$($LDCONFIG_COMMAND -p | grep libunwind)" ] && say_err "Unable to locate libunwind. Install libunwind to continue" && failing=true
|
|
[ -z "$($LDCONFIG_COMMAND -p | grep libssl)" ] && say_err "Unable to locate libssl. Install libssl to continue" && failing=true
|
|
[ -z "$($LDCONFIG_COMMAND -p | grep libcurl)" ] && say_err "Unable to locate libcurl. Install libcurl to continue" && failing=true
|
|
[ -z "$($LDCONFIG_COMMAND -p | grep libicu)" ] && say_err "Unable to locate libicu. Install libicu to continue" && failing=true
|
|
fi
|
|
|
|
if [ "$failing" = true ]; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# input - $1
|
|
to_lowercase() {
|
|
#eval $invocation
|
|
|
|
echo "$1" | tr '[:upper:]' '[:lower:]'
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# input - $1
|
|
remove_trailing_slash() {
|
|
#eval $invocation
|
|
|
|
local input=${1:-}
|
|
echo "${input%/}"
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# input - $1
|
|
remove_beginning_slash() {
|
|
#eval $invocation
|
|
|
|
local input=${1:-}
|
|
echo "${input#/}"
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# root_path - $1
|
|
# child_path - $2 - this parameter can be empty
|
|
combine_paths() {
|
|
eval $invocation
|
|
|
|
# TODO: Consider making it work with any number of paths. For now:
|
|
if [ ! -z "${3:-}" ]; then
|
|
say_err "combine_paths: Function takes two parameters."
|
|
return 1
|
|
fi
|
|
|
|
local root_path=$(remove_trailing_slash $1)
|
|
local child_path=$(remove_beginning_slash ${2:-})
|
|
say_verbose "combine_paths: root_path=$root_path"
|
|
say_verbose "combine_paths: child_path=$child_path"
|
|
echo "$root_path/$child_path"
|
|
return 0
|
|
}
|
|
|
|
get_machine_architecture() {
|
|
eval $invocation
|
|
|
|
# Currently the only one supported
|
|
echo "x64"
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# architecture - $1
|
|
get_normalized_architecture_from_architecture() {
|
|
eval $invocation
|
|
|
|
local architecture=$(to_lowercase $1)
|
|
case $architecture in
|
|
\<auto\>)
|
|
echo "$(get_normalized_architecture_from_architecture $(get_machine_architecture))"
|
|
return 0
|
|
;;
|
|
amd64|x64)
|
|
echo "x64"
|
|
return 0
|
|
;;
|
|
x86)
|
|
say_err "Architecture ``x86`` currently not supported"
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
say_err "Architecture ``$architecture`` not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues"
|
|
return 1
|
|
}
|
|
|
|
# version_info is a conceptual two line string representing commit hash and 4-part version
|
|
# format:
|
|
# Line 1: # commit_hash
|
|
# Line 2: # 4-part version
|
|
|
|
# args:
|
|
# version_text - stdin
|
|
get_version_from_version_info() {
|
|
eval $invocation
|
|
|
|
cat | tail -n 1
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# version_text - stdin
|
|
get_commit_hash_from_version_info() {
|
|
eval $invocation
|
|
|
|
cat | head -n 1
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# install_root - $1
|
|
# relative_path_to_package - $2
|
|
# specific_version - $3
|
|
is_dotnet_package_installed() {
|
|
eval $invocation
|
|
|
|
local install_root=$1
|
|
local relative_path_to_package=$2
|
|
local specific_version=${3//[$'\t\r\n']}
|
|
|
|
local dotnet_package_path=$(combine_paths $(combine_paths $install_root $relative_path_to_package) $specific_version)
|
|
say_verbose "is_dotnet_package_installed: dotnet_package_path=$dotnet_package_path"
|
|
|
|
if [ -d "$dotnet_package_path" ]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# args:
|
|
# azure_feed - $1
|
|
# azure_channel - $2
|
|
# normalized_architecture - $3
|
|
get_latest_version_info() {
|
|
eval $invocation
|
|
|
|
local azure_feed=$1
|
|
local azure_channel=$2
|
|
local normalized_architecture=$3
|
|
|
|
local osname=$(get_current_os_name)
|
|
|
|
local version_file_url=null
|
|
if [ "$shared_runtime" = true ]; then
|
|
version_file_url="$azure_feed/$azure_channel/dnvm/latest.sharedfx.$osname.$normalized_architecture.version"
|
|
else
|
|
version_file_url="$azure_feed/Sdk/$azure_channel/latest.version"
|
|
fi
|
|
say_verbose "get_latest_version_info: latest url: $version_file_url"
|
|
|
|
download $version_file_url
|
|
return $?
|
|
}
|
|
|
|
# args:
|
|
# channel - $1
|
|
get_azure_channel_from_channel() {
|
|
eval $invocation
|
|
|
|
local channel=$(to_lowercase $1)
|
|
case $channel in
|
|
future|dev)
|
|
echo "dev"
|
|
return 0
|
|
;;
|
|
production)
|
|
say_err "Production channel does not exist yet"
|
|
return 1
|
|
esac
|
|
|
|
echo $channel
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# azure_feed - $1
|
|
# azure_channel - $2
|
|
# normalized_architecture - $3
|
|
# version - $4
|
|
get_specific_version_from_version() {
|
|
eval $invocation
|
|
|
|
local azure_feed=$1
|
|
local azure_channel=$2
|
|
local normalized_architecture=$3
|
|
local version=$(to_lowercase $4)
|
|
|
|
case $version in
|
|
latest)
|
|
local version_info="$(get_latest_version_info $azure_feed $azure_channel $normalized_architecture)"
|
|
say_verbose "get_specific_version_from_version: version_info=$version_info"
|
|
echo "$version_info" | get_version_from_version_info
|
|
return 0
|
|
;;
|
|
lkg)
|
|
say_err "``--version LKG`` not supported yet."
|
|
return 1
|
|
;;
|
|
*)
|
|
echo $version
|
|
return 0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# args:
|
|
# azure_feed - $1
|
|
# azure_channel - $2
|
|
# normalized_architecture - $3
|
|
# specific_version - $4
|
|
construct_download_link() {
|
|
eval $invocation
|
|
|
|
local azure_feed=$1
|
|
local azure_channel=$2
|
|
local normalized_architecture=$3
|
|
local specific_version=${4//[$'\t\r\n']}
|
|
|
|
local osname=$(get_current_os_name)
|
|
|
|
local download_link=null
|
|
if [ "$shared_runtime" = true ]; then
|
|
download_link="$azure_feed/$azure_channel/Binaries/$specific_version/dotnet-$osname-$normalized_architecture.$specific_version.tar.gz"
|
|
else
|
|
download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$osname-$normalized_architecture.$specific_version.tar.gz"
|
|
fi
|
|
|
|
echo "$download_link"
|
|
return 0
|
|
}
|
|
|
|
get_user_share_path() {
|
|
eval $invocation
|
|
|
|
if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then
|
|
echo $DOTNET_INSTALL_DIR
|
|
else
|
|
echo "$HOME/.dotnet"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# install_dir - $1
|
|
resolve_installation_path() {
|
|
eval $invocation
|
|
|
|
local install_dir=$1
|
|
if [ "$install_dir" = "<auto>" ]; then
|
|
local user_share_path=$(get_user_share_path)
|
|
say_verbose "resolve_installation_path: share_path=$user_share_path"
|
|
echo "$user_share_path"
|
|
return 0
|
|
fi
|
|
|
|
echo "$install_dir"
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# install_root - $1
|
|
get_installed_version_info() {
|
|
eval $invocation
|
|
|
|
local install_root=$1
|
|
local version_file=$(combine_paths "$install_root" "$local_version_file_relative_path")
|
|
say_verbose "Local version file: $version_file"
|
|
if [ ! -z "$version_file" ] | [ -r "$version_file" ]; then
|
|
local version_info="$(cat $version_file)"
|
|
echo "$version_info"
|
|
return 0
|
|
fi
|
|
|
|
say_verbose "Local version file not found."
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# relative_or_absolute_path - $1
|
|
get_absolute_path() {
|
|
eval $invocation
|
|
|
|
local relative_or_absolute_path=$1
|
|
echo $(cd $(dirname "$1") && pwd -P)/$(basename "$1")
|
|
return 0
|
|
}
|
|
|
|
# args:
|
|
# input_files - stdin
|
|
# root_path - $1
|
|
# out_path - $2
|
|
# override - $3
|
|
copy_files_or_dirs_from_list() {
|
|
eval $invocation
|
|
|
|
local root_path=$(remove_trailing_slash $1)
|
|
local out_path=$(remove_trailing_slash $2)
|
|
local override=$3
|
|
local override_switch=$(if [ "$override" = false ]; then printf -- "-n"; fi)
|
|
|
|
cat | uniq | while read -r file_path; do
|
|
local path=$(remove_beginning_slash ${file_path#$root_path})
|
|
local target=$out_path/$path
|
|
if [ "$override" = true ] || (! ([ -d "$target" ] || [ -e "$target" ])); then
|
|
mkdir -p $out_path/$(dirname $path)
|
|
cp -R $override_switch $root_path/$path $target
|
|
fi
|
|
done
|
|
}
|
|
|
|
# args:
|
|
# zip_path - $1
|
|
# out_path - $2
|
|
extract_dotnet_package() {
|
|
eval $invocation
|
|
|
|
local zip_path=$1
|
|
local out_path=$2
|
|
|
|
local temp_out_path=$(mktemp -d $temporary_file_template)
|
|
|
|
local failed=false
|
|
tar -xzf "$zip_path" -C "$temp_out_path" > /dev/null || failed=true
|
|
|
|
local folders_with_version_regex='^.*/[0-9]+\.[0-9]+[^/]+/'
|
|
find $temp_out_path -type f | grep -Eo $folders_with_version_regex | copy_files_or_dirs_from_list $temp_out_path $out_path false
|
|
find $temp_out_path -type f | grep -Ev $folders_with_version_regex | copy_files_or_dirs_from_list $temp_out_path $out_path true
|
|
|
|
rm -rf $temp_out_path
|
|
|
|
if [ "$failed" = true ]; then
|
|
say_err "Extraction failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# args:
|
|
# remote_path - $1
|
|
# [out_path] - $2 - stdout if not provided
|
|
download() {
|
|
eval $invocation
|
|
|
|
local remote_path=$1
|
|
local out_path=${2:-}
|
|
|
|
local failed=false
|
|
if [ -z "$out_path" ]; then
|
|
curl --fail -s $remote_path || failed=true
|
|
else
|
|
curl --fail -s -o $out_path $remote_path || failed=true
|
|
fi
|
|
|
|
if [ "$failed" = true ]; then
|
|
say_err "Download failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
calculate_vars() {
|
|
eval $invocation
|
|
|
|
azure_channel=$(get_azure_channel_from_channel "$channel")
|
|
say_verbose "azure_channel=$azure_channel"
|
|
|
|
normalized_architecture=$(get_normalized_architecture_from_architecture "$architecture")
|
|
say_verbose "normalized_architecture=$normalized_architecture"
|
|
|
|
specific_version=$(get_specific_version_from_version $azure_feed $azure_channel $normalized_architecture $version)
|
|
say_verbose "specific_version=$specific_version"
|
|
if [ -z "$specific_version" ]; then
|
|
say_err "Could not get version information."
|
|
return 1
|
|
fi
|
|
|
|
download_link=$(construct_download_link $azure_feed $azure_channel $normalized_architecture $specific_version)
|
|
say_verbose "download_link=$download_link"
|
|
|
|
install_root=$(resolve_installation_path $install_dir)
|
|
say_verbose "install_root=$install_root"
|
|
}
|
|
|
|
install_dotnet() {
|
|
eval $invocation
|
|
|
|
if is_dotnet_package_installed $install_root "sdk" $specific_version; then
|
|
say ".NET SDK version $specific_version is already installed."
|
|
return 0
|
|
fi
|
|
|
|
mkdir -p $install_root
|
|
zip_path=$(mktemp $temporary_file_template)
|
|
say_verbose "Zip path: $zip_path"
|
|
|
|
say "Downloading $download_link"
|
|
download "$download_link" $zip_path
|
|
say_verbose "Downloaded file exists and readable? $(if [ -r $zip_path ]; then echo "yes"; else echo "no"; fi)"
|
|
|
|
say "Extracting zip"
|
|
extract_dotnet_package $zip_path $install_root
|
|
|
|
return 0
|
|
}
|
|
|
|
local_version_file_relative_path="/.version"
|
|
bin_folder_relative_path=""
|
|
temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX"
|
|
|
|
channel="rel-1.0.0"
|
|
version="Latest"
|
|
install_dir="<auto>"
|
|
architecture="<auto>"
|
|
debug_symbols=false
|
|
dry_run=false
|
|
no_path=false
|
|
azure_feed="https://dotnetcli.blob.core.windows.net/dotnet"
|
|
verbose=false
|
|
shared_runtime=false
|
|
|
|
while [ $# -ne 0 ]
|
|
do
|
|
name=$1
|
|
case $name in
|
|
-c|--channel|-[Cc]hannel)
|
|
shift
|
|
channel=$1
|
|
;;
|
|
-v|--version|-[Vv]ersion)
|
|
shift
|
|
version="$1"
|
|
;;
|
|
-i|--install-dir|-[Ii]nstall[Dd]ir)
|
|
shift
|
|
install_dir="$1"
|
|
;;
|
|
--arch|--architecture|-[Aa]rch|-[Aa]rchitecture)
|
|
shift
|
|
architecture="$1"
|
|
;;
|
|
--shared-runtime|-[Ss]hared[Rr]untime)
|
|
shared_runtime=true
|
|
;;
|
|
--debug-symbols|-[Dd]ebug[Ss]ymbols)
|
|
debug_symbols=true
|
|
;;
|
|
--dry-run|-[Dd]ry[Rr]un)
|
|
dry_run=true
|
|
;;
|
|
--no-path|-[Nn]o[Pp]ath)
|
|
no_path=true
|
|
;;
|
|
--verbose|-[Vv]erbose)
|
|
verbose=true
|
|
;;
|
|
--azure-feed|-[Aa]zure[Ff]eed)
|
|
shift
|
|
azure_feed="$1"
|
|
;;
|
|
-?|--?|-h|--help|-[Hh]elp)
|
|
script_name="$(basename $0)"
|
|
echo ".NET Tools Installer"
|
|
echo "Usage: $script_name [-c|--channel <CHANNEL>] [-v|--version <VERSION>] [-p|--prefix <DESTINATION>]"
|
|
echo " $script_name -h|-?|--help"
|
|
echo ""
|
|
echo "$script_name is a simple command line interface for obtaining dotnet cli."
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -c,--channel <CHANNEL> Download from the CHANNEL specified (default: $channel)."
|
|
echo " -Channel"
|
|
echo " -v,--version <VERSION> Use specific version, ``latest`` or ``lkg``. Defaults to ``latest``."
|
|
echo " -Version"
|
|
echo " -i,--install-dir <DIR> Install under specified location (see Install Location below)"
|
|
echo " -InstallDir"
|
|
echo " --architecture <ARCHITECTURE> Architecture of .NET Tools. Currently only x64 is supported."
|
|
echo " --arch,-Architecture,-Arch"
|
|
echo " --shared-runtime Installs just the shared runtime bits, not the entire SDK."
|
|
echo " -SharedRuntime"
|
|
echo " --debug-symbols,-DebugSymbols Specifies if symbols should be included in the installation."
|
|
echo " --dry-run,-DryRun Do not perform installation. Display download link."
|
|
echo " --no-path, -NoPath Do not set PATH for the current process."
|
|
echo " --verbose,-Verbose Display diagnostics information."
|
|
echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed"
|
|
echo " -?,--?,-h,--help,-Help Shows this help message"
|
|
echo ""
|
|
echo "Install Location:"
|
|
echo " Location is chosen in following order:"
|
|
echo " - --install-dir option"
|
|
echo " - Environmental variable DOTNET_INSTALL_DIR"
|
|
echo " - /usr/local/share/dotnet"
|
|
exit 0
|
|
;;
|
|
*)
|
|
say_err "Unknown argument \`$name\`"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
shift
|
|
done
|
|
|
|
check_min_reqs
|
|
calculate_vars
|
|
if [ "$dry_run" = true ]; then
|
|
say "Payload URL: $download_link"
|
|
say "Repeatable invocation: ./$(basename $0) --version $specific_version --channel $channel --install-dir $install_dir"
|
|
exit 0
|
|
fi
|
|
|
|
check_pre_reqs
|
|
install_dotnet
|
|
|
|
bin_path=$(get_absolute_path $(combine_paths $install_root $bin_folder_relative_path))
|
|
if [ "$no_path" = false ]; then
|
|
say "Adding to current process PATH: ``$bin_path``. Note: This change will be visible only when sourcing script."
|
|
export PATH=$bin_path:$PATH
|
|
else
|
|
say "Binaries of dotnet can be found in $bin_path"
|
|
fi
|
|
|
|
say "Installation finished successfully."
|