diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..171fd824f --- /dev/null +++ b/build.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +echo "" +echo "Installing dotnet cli..." +echo "" + +export DOTNET_INSTALL_DIR="./.dotnet/" + +tools/install.sh + +origPath=$PATH +export PATH="./dotnet/bin/:$PATH" + +if [ $? -ne 0 ]; then + echo >&2 ".NET Execution Environment installation has failed." + exit 1 +fi + +export DOTNET_HOME="$DOTNET_INSTALL_DIR/cli" +export PATH="$DOTNET_HOME/bin:$PATH" + +export autoGeneratedVersion=false + +# Generate version number if not set +if [[ -z "$BuildSemanticVersion" ]]; then + autoVersion="$((($(date +%s) - 1451606400)/60))-$(date +%S)" + export BuildSemanticVersion="rc2-$autoVersion" + autoGeneratedVersion=true + + echo "Set version to $BuildSemanticVersion" +fi + +sed -i '' "s/99.99.99-rc2/1.0.0-$BuildSemanticVersion/g" */*/project.json + +# Restore packages and build product +dotnet restore -v Minimal # Restore all packages +dotnet pack "Dapper" --configuration Release --output "artifacts/packages" +dotnet pack "Dapper.Contrib" --configuration Release --output "artifacts/packages" + +# Build all +# Note the exclude: https://github.com/dotnet/cli/issues/1342 +for d in Dapper*/; do + if [ "$d" != "*.EntityFramework.StrongName" ]; then + echo "Building $d" + pushd "$d" + dotnet build + popd + fi +done + +# Run tests +for d in *.Tests*/; do + echo "Testing $d" + pushd "$d" + dotnet test + popd +done + +sed -i '' "s/1.0.0-$BuildSemanticVersion/99.99.99-rc2/g" */*/project.json + +if [ $autoGeneratedVersion ]; then + unset BuildSemanticVersion +fi + +export PATH=$origPath \ No newline at end of file diff --git a/tools/install.sh b/tools/install.sh new file mode 100644 index 000000000..64601dbb2 --- /dev/null +++ b/tools/install.sh @@ -0,0 +1,636 @@ +# 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 + 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 + 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 + [ -z "$($LDCONFIG_COMMAND -p | grep gettext)" ] && say_err "Unable to locate gettext. Install gettext 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 + \) + 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 + + 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="$azure_feed/$azure_channel/dnvm/latest.$osname.$normalized_architecture.version" + 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 + ;; + preview|beta) + echo "beta" + return 0 + ;; + production) + say_err "Production channel does not exist yet" + return 1 + esac + + say_err "``$1`` is an invalid channel name. Use one of the following: ``future``, ``preview``, ``production``" + return 1 +} + +# 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 + + local osname=$(get_current_os_name) + + local download_link="$azure_feed/$azure_channel/Binaries/$specific_version/dotnet-dev-$osname-$normalized_architecture.$specific_version.tar.gz" + echo "$download_link" + return 0 +} + +get_user_share_path() { + eval $invocation + + if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then + echo $DOTNET_INSTALL_DIR + else + echo "/usr/local/share/dotnet" + fi + return 0 +} + +# args: +# install_dir - $1 +resolve_installation_path() { + eval $invocation + + local install_dir=$1 + if [ "$install_dir" = "" ]; 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="preview" +version="Latest" +install_dir="" +architecture="" +debug_symbols=false +dry_run=false +no_path=false +azure_feed="https://dotnetcli.blob.core.windows.net/dotnet" +verbose=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" + ;; + --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 ] [-v|--version ] [-p|--prefix ]" + 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 Download from the CHANNEL specified (default: $channel)." + echo " -Channel" + echo " -v,--version Use specific version, ``latest`` or ``lkg``. Defaults to ``latest``." + echo " -Version" + echo " -i,--install-dir Install under specified location (see Install Location below)" + echo " -InstallDir" + echo " --architecture Architecture of .NET Tools. Currently only x64 is supported." + echo " --arch,-Architecture,-Arch" + 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" + return 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=$PATH:$bin_path +else + say "Binaries of dotnet can be found in $bin_path" +fi + +say "Installation finished successfuly." \ No newline at end of file