2015-11-16 11:21:57 -08: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.
#
2016-03-02 14:03:17 -08:00
<#
. SYNOPSIS
Installs dotnet cli
. DESCRIPTION
Installs dotnet cli . If dotnet installation already exists in the given directory
it will update it only if the requested version differs from the one already installed .
. PARAMETER Channel
2016-03-03 11:41:25 -08:00
Default : preview
2016-03-02 14:03:17 -08:00
Channel is the way of reasoning about stability and quality of dotnet . This parameter takes one of the values :
2016-03-03 11:41:25 -08:00
- future - Possibly unstable , frequently changing , may contain new finished and unfinished features
2016-03-02 14:03:17 -08:00
- preview - Pre-release stable with known issues and feature gaps
- production - Most stable releases
. PARAMETER Version
2016-03-03 11:41:25 -08:00
Default : latest
2016-03-02 14:03:17 -08:00
Represents a build version on specific channel . Possible values :
- 4 -part version in a format A . B . C . D - represents specific version of build
- latest - most latest build on specific channel
- lkg - last known good version on specific channel
2016-03-03 11:41:25 -08:00
Note : LKG work is in progress . Once the work is finished , this will become new default
2016-03-02 14:03:17 -08:00
. PARAMETER InstallDir
2016-03-25 12:19:46 -07:00
Default : % LocalAppData % \ Microsoft \ dotnet
2016-03-02 14:03:17 -08:00
Path to where to install dotnet . Note that binaries will be placed directly in a given directory .
. PARAMETER Architecture
2016-03-14 11:46:14 -07:00
Default : < auto > - this value represents currently running OS architecture
2016-03-03 11:41:25 -08:00
Architecture of dotnet binaries to be installed .
2016-03-14 11:46:14 -07:00
Possible values are : < auto > , x64 and x86
2016-03-02 14:03:17 -08:00
. PARAMETER DebugSymbols
If set the installer will include symbols in the installation .
. PARAMETER DryRun
If set it will not perform installation but instead display what command line to use to consistently install
currently requested version of dotnet cli . In example if you specify version 'latest' it will display a link
with specific version so that this command can be used deterministicly in a build script .
It also displays binaries location if you prefer to install or download it yourself .
. PARAMETER NoPath
By default this script will set environment variable PATH for the current process to the binaries folder inside installation folder .
If set it will display binaries location but not set any environment variable .
. PARAMETER Verbose
Displays diagnostics information .
. PARAMETER AzureFeed
2016-03-03 11:41:25 -08:00
Default : https : / / dotnetcli . blob . core . windows . net / dotnet
2016-03-02 14:03:17 -08:00
This parameter should not be usually changed by user . It allows to change URL for the Azure feed used by this installer .
#>
2016-03-01 16:37:25 -08:00
[ cmdletbinding ( ) ]
2016-02-11 10:47:49 -08:00
param (
2016-03-03 11:41:25 -08:00
[ string ] $Channel = " preview " ,
2016-03-01 16:37:25 -08:00
[ string ] $Version = " Latest " ,
2016-03-14 11:46:14 -07:00
[ string ] $InstallDir = " <auto> " ,
[ string ] $Architecture = " <auto> " ,
2016-03-02 14:03:17 -08:00
[ switch ] $DebugSymbols , # TODO: Switch does not work yet. Symbols zip is not being uploaded yet.
2016-03-01 16:37:25 -08:00
[ switch ] $DryRun ,
2016-03-02 14:03:17 -08:00
[ switch ] $NoPath ,
[ string ] $AzureFeed = " https://dotnetcli.blob.core.windows.net/dotnet "
2016-02-11 10:47:49 -08:00
)
2016-01-20 18:49:47 -08:00
2016-03-01 16:37:25 -08:00
Set-StrictMode -Version Latest
2015-11-10 17:30:01 -08:00
$ErrorActionPreference = " Stop "
$ProgressPreference = " SilentlyContinue "
2016-03-16 15:28:55 -07:00
$BinFolderRelativePath = " "
2016-03-01 16:37:25 -08:00
2016-03-16 14:36:38 -07:00
# example path with regex: shared/1.0.0-beta-12345/somepath
$VersionRegEx = " /\d+\.\d+[^/]+/ "
2016-03-16 15:28:55 -07:00
$OverrideNonVersionedFiles = $true
2016-03-16 14:36:38 -07:00
2016-03-01 16:37:25 -08:00
function Say($str ) {
Write-Host " dotnet_install: $str "
2016-02-11 10:47:49 -08:00
}
2016-03-16 10:55:24 -07:00
2016-03-01 16:37:25 -08:00
function Say-Verbose($str ) {
Write-Verbose " dotnet_install: $str "
}
2015-11-10 17:30:01 -08:00
2016-03-01 16:37:25 -08:00
function Say-Invocation($Invocation ) {
$command = $Invocation . MyCommand ;
$args = ( ( $Invocation . BoundParameters . Keys | foreach { " - $_ `" $( $Invocation . BoundParameters [ $_ ] ) `" " } ) -join " " )
Say-Verbose " $command $args "
2015-11-10 17:30:01 -08:00
}
2016-03-01 16:37:25 -08:00
function Get-Machine-Architecture ( ) {
Say-Invocation $MyInvocation
# possible values: AMD64, IA64, x86
return $ENV:PROCESSOR_ARCHITECTURE
}
# TODO: Architecture and CLIArchitecture should be unified
function Get-CLIArchitecture-From-Architecture([string]$Architecture ) {
Say-Invocation $MyInvocation
switch ( $Architecture . ToLower ( ) ) {
2016-03-14 11:46:14 -07:00
{ $_ -eq " <auto> " } { return Get-CLIArchitecture -From -Architecture $ ( Get-Machine -Architecture ) }
2016-03-02 14:03:17 -08:00
{ ( $_ -eq " amd64 " ) -or ( $_ -eq " x64 " ) } { return " x64 " }
2016-03-01 16:37:25 -08:00
{ $_ -eq " x86 " } { return " x86 " }
default { throw " Architecture not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues " }
}
}
function Get-Version-Info-From-Version-Text([string]$VersionText ) {
Say-Invocation $MyInvocation
$Data = @ ( $VersionText . Split ( [ char[] ] @ ( ) , [ StringSplitOptions ] :: RemoveEmptyEntries ) ) ;
$VersionInfo = @ { }
$VersionInfo . CommitHash = $Data [ 0 ] . Trim ( )
$VersionInfo . Version = $Data [ 1 ] . Trim ( )
return $VersionInfo
}
function Get-Latest-Version-Info([string]$AzureFeed , [ string ] $AzureChannel , [ string ] $CLIArchitecture ) {
Say-Invocation $MyInvocation
$VersionFileUrl = " $AzureFeed / $AzureChannel /dnvm/latest.win. $CLIArchitecture .version "
$Response = Invoke-WebRequest -UseBasicParsing $VersionFileUrl
$VersionText = [ Text.Encoding ] :: UTF8 . GetString ( $Response . Content )
$VersionInfo = Get-Version -Info -From -Version -Text $VersionText
return $VersionInfo
}
# TODO: AzureChannel and Channel should be unified
function Get-Azure-Channel-From-Channel([string]$Channel ) {
Say-Invocation $MyInvocation
2016-03-02 14:03:17 -08:00
# For compatibility with build scripts accept also directly Azure channels names
2016-03-01 16:37:25 -08:00
switch ( $Channel . ToLower ( ) ) {
2016-03-03 11:41:25 -08:00
{ ( $_ -eq " future " ) -or ( $_ -eq " dev " ) } { return " dev " }
2016-03-02 14:03:17 -08:00
{ ( $_ -eq " preview " ) -or ( $_ -eq " beta " ) } { return " beta " }
2016-03-01 16:37:25 -08:00
{ $_ -eq " production " } { throw " Production channel does not exist yet " }
2016-03-03 11:41:25 -08:00
default { throw " `` $Channel `` is an invalid channel name. Use one of the following: `` future `` , `` preview `` , `` production `` " }
2016-03-01 16:37:25 -08:00
}
}
function Get-Specific-Version-From-Version([string]$AzureFeed , [ string ] $AzureChannel , [ string ] $CLIArchitecture , [ string ] $Version ) {
Say-Invocation $MyInvocation
switch ( $Version . ToLower ( ) ) {
{ $_ -eq " latest " } {
$LatestVersionInfo = Get-Latest -Version -Info -AzureFeed $AzureFeed -AzureChannel $AzureChannel -CLIArchitecture $CLIArchitecture
return $LatestVersionInfo . Version
2015-11-10 17:30:01 -08:00
}
2016-03-01 16:37:25 -08:00
{ $_ -eq " lkg " } { throw " `` -Version LKG `` not supported yet. " }
default { return $Version }
2015-11-10 17:30:01 -08:00
}
}
2016-03-16 14:36:38 -07:00
function Get-Download-Links([string]$AzureFeed , [ string ] $AzureChannel , [ string ] $SpecificVersion , [ string ] $CLIArchitecture ) {
2016-03-01 16:37:25 -08:00
Say-Invocation $MyInvocation
2016-03-16 14:36:38 -07:00
$ret = @ ( )
2016-03-21 11:11:37 -07:00
$files = @ ( " dotnet-dev " )
2016-03-16 14:36:38 -07:00
foreach ( $file in $files ) {
$PayloadURL = " $AzureFeed / $AzureChannel /Binaries/ $SpecificVersion / $file -win- $CLIArchitecture . $SpecificVersion .zip "
Say-Verbose " Constructed payload URL: $PayloadURL "
$ret + = $PayloadURL
}
2016-03-01 16:37:25 -08:00
2016-03-16 14:36:38 -07:00
return $ret
2015-11-10 17:30:01 -08:00
}
2016-03-01 16:37:25 -08:00
function Get-User-Share-Path ( ) {
Say-Invocation $MyInvocation
2015-11-10 17:30:01 -08:00
2016-03-01 16:37:25 -08:00
$InstallRoot = $env:DOTNET_INSTALL_DIR
if ( ! $InstallRoot ) {
2016-03-25 12:19:46 -07:00
$InstallRoot = " $env:LocalAppData \Microsoft\dotnet "
2016-03-01 16:37:25 -08:00
}
return $InstallRoot
}
2015-11-10 17:30:01 -08:00
2016-03-01 16:37:25 -08:00
function Resolve-Installation-Path([string]$InstallDir ) {
Say-Invocation $MyInvocation
2015-11-10 17:30:01 -08:00
2016-03-14 11:46:14 -07:00
if ( $InstallDir -eq " <auto> " ) {
2016-03-01 16:37:25 -08:00
return Get-User -Share -Path
}
return $InstallDir
2015-11-10 17:30:01 -08:00
}
2016-03-23 12:14:19 -07:00
function Get-Version-Info-From-Version-File([string]$InstallRoot , [ string ] $RelativePathToVersionFile ) {
2016-03-01 16:37:25 -08:00
Say-Invocation $MyInvocation
2016-03-23 12:14:19 -07:00
$VersionFile = Join-Path -Path $InstallRoot -ChildPath $RelativePathToVersionFile
2016-03-01 16:37:25 -08:00
Say-Verbose " Local version file: $VersionFile "
if ( Test-Path $VersionFile ) {
$VersionText = cat $VersionFile
Say-Verbose " Local version file text: $VersionText "
return Get-Version -Info -From -Version -Text $VersionText
}
Say-Verbose " Local version file not found. "
2015-11-10 17:30:01 -08:00
2016-03-01 16:37:25 -08:00
return $null
2015-11-10 17:30:01 -08:00
}
2016-03-23 12:14:19 -07:00
function Is-Dotnet-Package-Installed([string]$InstallRoot , [ string ] $RelativePathToPackage , [ string ] $SpecificVersion ) {
Say-Invocation $MyInvocation
$DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion
Say-Verbose " Is-Dotnet-Package-Installed: Path to a package: $DotnetPackagePath "
return Test-Path $DotnetPackagePath -PathType Container
}
2016-03-01 16:37:25 -08:00
function Get-Absolute-Path([string]$RelativeOrAbsolutePath ) {
# Too much spam
# Say-Invocation $MyInvocation
return $ExecutionContext . SessionState . Path . GetUnresolvedProviderPathFromPSPath ( $RelativeOrAbsolutePath )
}
2016-03-16 14:36:38 -07:00
function Get-Path-Prefix-With-Version($path ) {
$match = [ regex ] :: match ( $path , $VersionRegEx )
if ( $match . Success ) {
return $entry . FullName . Substring ( 0 , $match . Index + $match . Length )
}
return $null
}
function Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package([System.IO.Compression.ZipArchive]$Zip , [ string ] $OutPath ) {
Say-Invocation $MyInvocation
$ret = @ ( )
foreach ( $entry in $Zip . Entries ) {
$dir = Get-Path -Prefix -With -Version $entry . FullName
if ( $dir -ne $null ) {
$path = Get-Absolute -Path $ ( Join-Path -Path $OutPath -ChildPath $dir )
if ( -Not ( Test-Path $path -PathType Container ) ) {
$ret + = $dir
}
}
}
$ret = $ret | Sort-Object | Get-Unique
$values = ( $ret | foreach { " $_ " } ) -join " ; "
Say-Verbose " Directories to unpack: $values "
return $ret
}
2016-03-17 16:15:55 -07:00
# Example zip content and extraction algorithm:
# Rule: files if extracted are always being extracted to the same relative path locally
# .\
# a.exe # file does not exist locally, extract
# b.dll # file exists locally, override only if $OverrideFiles set
# aaa\ # same rules as for files
# ...
# abc\1.0.0\ # directory contains version and exists locally
# ... # do not extract content under versioned part
# abc\asd\ # same rules as for files
# ...
# def\ghi\1.0.1\ # directory contains version and does not exist locally
# ... # extract content
2016-03-16 14:36:38 -07:00
function Extract-Dotnet-Package([string]$ZipPath , [ string ] $OutPath ) {
2016-03-01 16:37:25 -08:00
Say-Invocation $MyInvocation
Add-Type -Assembly System . IO . Compression . FileSystem | Out-Null
Set-Variable -Name Zip
try {
$Zip = [ System.IO.Compression.ZipFile ] :: OpenRead ( $ZipPath )
2016-03-16 14:36:38 -07:00
$DirectoriesToUnpack = Get-List -Of -Directories -And -Versions -To -Unpack -From -Dotnet -Package -Zip $Zip -OutPath $OutPath
2016-03-01 16:37:25 -08:00
foreach ( $entry in $Zip . Entries ) {
2016-03-16 14:36:38 -07:00
$PathWithVersion = Get-Path -Prefix -With -Version $entry . FullName
if ( ( $PathWithVersion -eq $null ) -Or ( $DirectoriesToUnpack -contains $PathWithVersion ) ) {
$DestinationPath = Get-Absolute -Path $ ( Join-Path -Path $OutPath -ChildPath $entry . FullName )
$DestinationDir = Split-Path -Parent $DestinationPath
2016-03-16 15:28:55 -07:00
$OverrideFiles = $OverrideNonVersionedFiles -Or ( -Not ( Test-Path $DestinationPath ) )
if ( ( -Not $DestinationPath . EndsWith ( " \ " ) ) -And $OverrideFiles ) {
2016-03-16 14:36:38 -07:00
New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null
2016-03-16 15:28:55 -07:00
[ System.IO.Compression.ZipFileExtensions ] :: ExtractToFile ( $entry , $DestinationPath , $OverrideNonVersionedFiles )
2016-03-16 14:36:38 -07:00
}
2016-03-14 12:23:27 -07:00
}
2016-03-01 16:37:25 -08:00
}
}
finally {
if ( $Zip -ne $null ) {
$Zip . Dispose ( )
}
}
2015-11-10 17:30:01 -08:00
}
2016-03-01 16:37:25 -08:00
$AzureChannel = Get-Azure -Channel -From -Channel -Channel $Channel
$CLIArchitecture = Get-CLIArchitecture -From -Architecture $Architecture
$SpecificVersion = Get-Specific -Version -From -Version -AzureFeed $AzureFeed -AzureChannel $AzureChannel -CLIArchitecture $CLIArchitecture -Version $Version
2016-03-16 14:36:38 -07:00
$DownloadLinks = Get-Download -Links -AzureFeed $AzureFeed -AzureChannel $AzureChannel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
2016-03-01 16:37:25 -08:00
if ( $DryRun ) {
2016-03-16 14:36:38 -07:00
Say " Payload URLs: "
foreach ( $DownloadLink in $DownloadLinks ) {
Say " - $DownloadLink "
}
2016-03-16 15:46:15 -07:00
Say " Repeatable invocation: .\ $( $MyInvocation . MyCommand ) -Version $SpecificVersion -Channel $Channel -Architecture $CLIArchitecture -InstallDir $InstallDir "
2016-03-01 16:37:25 -08:00
return
}
$InstallRoot = Resolve-Installation -Path $InstallDir
Say-Verbose " InstallRoot: $InstallRoot "
2016-03-23 12:14:19 -07:00
$IsSdkInstalled = Is-Dotnet -Package -Installed -InstallRoot $InstallRoot -RelativePathToPackage " sdk " -SpecificVersion $SpecificVersion
Say-Verbose " .NET SDK installed? $IsSdkInstalled "
if ( $IsSdkInstalled ) {
Say " .NET SDK version $SpecificVersion is already installed. "
return
}
2016-03-01 16:37:25 -08:00
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
2016-03-16 14:36:38 -07:00
foreach ( $DownloadLink in $DownloadLinks ) {
$ZipPath = [ System.IO.Path ] :: GetTempFileName ( )
Say " Downloading $DownloadLink "
$resp = Invoke-WebRequest -UseBasicParsing $DownloadLink -OutFile $ZipPath
2016-03-01 16:37:25 -08:00
2016-03-16 14:36:38 -07:00
Say " Extracting zip from $DownloadLink "
Extract-Dotnet -Package -ZipPath $ZipPath -OutPath $InstallRoot
2016-03-01 16:37:25 -08:00
2016-03-16 14:36:38 -07:00
Remove-Item $ZipPath
}
2016-03-01 16:37:25 -08:00
2016-03-02 14:03:17 -08:00
$BinPath = Get-Absolute -Path $ ( Join-Path -Path $InstallRoot -ChildPath $BinFolderRelativePath )
2016-03-01 16:37:25 -08:00
if ( -Not $NoPath ) {
2016-03-02 14:03:17 -08:00
Say " Adding to current process PATH: `" $BinPath `" . Note: This change will not be visible if PowerShell was run as a child process. "
2016-03-01 16:37:25 -08:00
$env:path + = " ; $BinPath "
}
2016-03-02 14:03:17 -08:00
else {
Say " Binaries of dotnet can be found in $BinPath "
}
2015-11-16 13:49:29 -08:00
2016-03-01 16:37:25 -08:00
Say " Installation finished "