2016-04-07 10:32:52 -07:00
# 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) "
normal = " $( tput sgr0) "
black = " $( tput setaf 0) "
red = " $( tput setaf 1) "
green = " $( tput setaf 2) "
yellow = " $( tput setaf 3) "
blue = " $( tput setaf 4) "
magenta = " $( tput setaf 5) "
cyan = " $( tput setaf 6) "
white = " $( tput setaf 7) "
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
2016-05-26 12:48:16 -07:00
if [ " $( cat /etc/*-release | grep -cim1 16.04) " -eq 1 ] ; then
echo "ubuntu.16.04"
return 0
fi
2016-04-07 10:32:52 -07:00
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
2016-05-26 23:33:37 -07:00
elif [ " $( cat /etc/*-release | grep -cim1 fedora) " -eq 1 ] ; then
2016-05-26 23:16:18 -07:00
if [ " $( cat /etc/*-release | grep -cim1 23) " -eq 1 ] ; then
echo "fedora.23"
return 0
fi
2016-05-31 13:54:35 -07:00
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
2016-04-07 10:32:52 -07:00
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
2016-05-02 00:52:21 -07:00
local specific_version = ${ 3 //[ $'\t\r\n' ] }
2016-04-07 10:32:52 -07:00
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)
2016-05-27 12:19:53 -07:00
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
2016-06-21 18:36:43 -05:00
version_file_url = " $azure_feed /Sdk/ $azure_channel /latest.version "
2016-05-27 12:19:53 -07:00
fi
2016-04-07 10:32:52 -07:00
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
2016-06-21 18:36:43 -05:00
echo $channel
return 0
2016-04-07 10:32:52 -07:00
}
# 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
2016-05-02 00:52:21 -07:00
local specific_version = ${ 4 //[ $'\t\r\n' ] }
2016-04-07 10:32:52 -07:00
local osname = $( get_current_os_name)
2016-05-27 12:19:53 -07:00
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
2016-06-21 18:36:43 -05:00
download_link = " $azure_feed /Sdk/ $specific_version /dotnet-dev- $osname - $normalized_architecture . $specific_version .tar.gz "
2016-05-27 12:19:53 -07:00
fi
2016-04-07 10:32:52 -07:00
echo " $download_link "
return 0
}
get_user_share_path( ) {
eval $invocation
if [ ! -z " ${ DOTNET_INSTALL_DIR :- } " ] ; then
echo $DOTNET_INSTALL_DIR
else
2016-05-04 11:55:06 -07:00
echo " $HOME /.dotnet "
2016-04-07 10:32:52 -07:00
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 "
2016-06-21 18:36:43 -05:00
channel = "rel-1.0.0"
2016-04-07 10:32:52 -07:00
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
2016-05-27 12:19:53 -07:00
shared_runtime = false
2016-04-07 10:32:52 -07:00
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 "
; ;
2016-05-27 12:19:53 -07:00
--shared-runtime| -[ Ss] hared[ Rr] untime)
shared_runtime = true
; ;
2016-04-07 10:32:52 -07:00
--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"
2016-05-27 12:19:53 -07:00
echo " --shared-runtime Installs just the shared runtime bits, not the entire SDK."
echo " -SharedRuntime"
2016-04-07 10:32:52 -07:00
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 "
2016-05-04 11:55:06 -07:00
exit 0
2016-04-07 10:32:52 -07:00
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. "
2016-05-20 11:05:33 -07:00
export PATH = $bin_path :$PATH
2016-04-07 10:32:52 -07:00
else
say " Binaries of dotnet can be found in $bin_path "
fi
2016-06-22 15:17:54 -07:00
say "Installation finished successfully."