2015-11-16 19:21:57 +00: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 22:03:17 +00: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 19:41:25 +00:00
|
|
|
Default: preview
|
2016-03-02 22:03:17 +00:00
|
|
|
Channel is the way of reasoning about stability and quality of dotnet. This parameter takes one of the values:
|
2016-03-03 19:41:25 +00:00
|
|
|
- future - Possibly unstable, frequently changing, may contain new finished and unfinished features
|
2016-03-02 22:03:17 +00:00
|
|
|
- preview - Pre-release stable with known issues and feature gaps
|
|
|
|
- production - Most stable releases
|
|
|
|
.PARAMETER Version
|
2016-03-03 19:41:25 +00:00
|
|
|
Default: latest
|
2016-03-02 22:03:17 +00: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 19:41:25 +00:00
|
|
|
Note: LKG work is in progress. Once the work is finished, this will become new default
|
2016-03-02 22:03:17 +00:00
|
|
|
.PARAMETER InstallDir
|
2016-03-03 19:41:25 +00:00
|
|
|
Default: %LocalAppData%\Microsoft\.dotnet
|
2016-03-02 22:03:17 +00:00
|
|
|
Path to where to install dotnet. Note that binaries will be placed directly in a given directory.
|
|
|
|
.PARAMETER Architecture
|
2016-03-14 18:46:14 +00:00
|
|
|
Default: <auto> - this value represents currently running OS architecture
|
2016-03-03 19:41:25 +00:00
|
|
|
Architecture of dotnet binaries to be installed.
|
2016-03-14 18:46:14 +00:00
|
|
|
Possible values are: <auto>, x64 and x86
|
2016-03-02 22:03:17 +00: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 19:41:25 +00:00
|
|
|
Default: https://dotnetcli.blob.core.windows.net/dotnet
|
2016-03-02 22:03:17 +00: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-02 00:37:25 +00:00
|
|
|
[cmdletbinding()]
|
2016-02-11 18:47:49 +00:00
|
|
|
param(
|
2016-03-03 19:41:25 +00:00
|
|
|
[string]$Channel="preview",
|
2016-03-02 00:37:25 +00:00
|
|
|
[string]$Version="Latest",
|
2016-03-14 18:46:14 +00:00
|
|
|
[string]$InstallDir="<auto>",
|
|
|
|
[string]$Architecture="<auto>",
|
2016-03-02 22:03:17 +00:00
|
|
|
[switch]$DebugSymbols, # TODO: Switch does not work yet. Symbols zip is not being uploaded yet.
|
2016-03-02 00:37:25 +00:00
|
|
|
[switch]$DryRun,
|
2016-03-02 22:03:17 +00:00
|
|
|
[switch]$NoPath,
|
|
|
|
[string]$AzureFeed="https://dotnetcli.blob.core.windows.net/dotnet"
|
2016-02-11 18:47:49 +00:00
|
|
|
)
|
2016-01-21 02:49:47 +00:00
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
Set-StrictMode -Version Latest
|
2015-11-11 01:30:01 +00:00
|
|
|
$ErrorActionPreference="Stop"
|
|
|
|
$ProgressPreference="SilentlyContinue"
|
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
$LocalVersionFileRelativePath="\.version"
|
2016-03-16 22:28:55 +00:00
|
|
|
$BinFolderRelativePath=""
|
2016-03-02 00:37:25 +00:00
|
|
|
|
2016-03-16 21:36:38 +00:00
|
|
|
# example path with regex: shared/1.0.0-beta-12345/somepath
|
|
|
|
$VersionRegEx="/\d+\.\d+[^/]+/"
|
2016-03-16 22:28:55 +00:00
|
|
|
$OverrideNonVersionedFiles=$true
|
2016-03-16 21:36:38 +00:00
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
function Say($str) {
|
|
|
|
Write-Host "dotnet_install: $str"
|
2016-02-11 18:47:49 +00:00
|
|
|
}
|
2016-03-16 17:55:24 +00:00
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
function Say-Verbose($str) {
|
|
|
|
Write-Verbose "dotnet_install: $str"
|
|
|
|
}
|
2015-11-11 01:30:01 +00:00
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
function Say-Invocation($Invocation) {
|
|
|
|
$command = $Invocation.MyCommand;
|
|
|
|
$args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ")
|
|
|
|
Say-Verbose "$command $args"
|
2015-11-11 01:30:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-02 00:37:25 +00: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 18:46:14 +00:00
|
|
|
{ $_ -eq "<auto>" } { return Get-CLIArchitecture-From-Architecture $(Get-Machine-Architecture) }
|
2016-03-02 22:03:17 +00:00
|
|
|
{ ($_ -eq "amd64") -or ($_ -eq "x64") } { return "x64" }
|
2016-03-02 00:37:25 +00: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 22:03:17 +00:00
|
|
|
# For compatibility with build scripts accept also directly Azure channels names
|
2016-03-02 00:37:25 +00:00
|
|
|
switch ($Channel.ToLower()) {
|
2016-03-03 19:41:25 +00:00
|
|
|
{ ($_ -eq "future") -or ($_ -eq "dev") } { return "dev" }
|
2016-03-02 22:03:17 +00:00
|
|
|
{ ($_ -eq "preview") -or ($_ -eq "beta") } { return "beta" }
|
2016-03-02 00:37:25 +00:00
|
|
|
{ $_ -eq "production" } { throw "Production channel does not exist yet" }
|
2016-03-03 19:41:25 +00:00
|
|
|
default { throw "``$Channel`` is an invalid channel name. Use one of the following: ``future``, ``preview``, ``production``" }
|
2016-03-02 00:37:25 +00: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-11 01:30:01 +00:00
|
|
|
}
|
2016-03-02 00:37:25 +00:00
|
|
|
{ $_ -eq "lkg" } { throw "``-Version LKG`` not supported yet." }
|
|
|
|
default { return $Version }
|
2015-11-11 01:30:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-16 21:36:38 +00:00
|
|
|
function Get-Download-Links([string]$AzureFeed, [string]$AzureChannel, [string]$SpecificVersion, [string]$CLIArchitecture) {
|
2016-03-02 00:37:25 +00:00
|
|
|
Say-Invocation $MyInvocation
|
2016-03-16 21:36:38 +00:00
|
|
|
|
|
|
|
$ret = @()
|
2016-03-21 18:11:37 +00:00
|
|
|
$files = @("dotnet-dev")
|
2016-03-16 21:36:38 +00: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-02 00:37:25 +00:00
|
|
|
|
2016-03-16 21:36:38 +00:00
|
|
|
return $ret
|
2015-11-11 01:30:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
function Get-User-Share-Path() {
|
|
|
|
Say-Invocation $MyInvocation
|
2015-11-11 01:30:01 +00:00
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
$InstallRoot = $env:DOTNET_INSTALL_DIR
|
|
|
|
if (!$InstallRoot) {
|
|
|
|
$InstallRoot = "$env:LocalAppData\Microsoft\.dotnet"
|
|
|
|
}
|
|
|
|
return $InstallRoot
|
|
|
|
}
|
2015-11-11 01:30:01 +00:00
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
function Resolve-Installation-Path([string]$InstallDir) {
|
|
|
|
Say-Invocation $MyInvocation
|
2015-11-11 01:30:01 +00:00
|
|
|
|
2016-03-14 18:46:14 +00:00
|
|
|
if ($InstallDir -eq "<auto>") {
|
2016-03-02 00:37:25 +00:00
|
|
|
return Get-User-Share-Path
|
|
|
|
}
|
|
|
|
return $InstallDir
|
2015-11-11 01:30:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
# TODO: check for global.json
|
|
|
|
function Get-Installed-Version-Info([string]$InstallRoot) {
|
|
|
|
Say-Invocation $MyInvocation
|
|
|
|
|
2016-03-02 22:03:17 +00:00
|
|
|
$VersionFile = Join-Path -Path $InstallRoot -ChildPath $LocalVersionFileRelativePath
|
2016-03-02 00:37:25 +00: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-11 01:30:01 +00:00
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
return $null
|
2015-11-11 01:30:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
function Get-Absolute-Path([string]$RelativeOrAbsolutePath) {
|
|
|
|
# Too much spam
|
|
|
|
# Say-Invocation $MyInvocation
|
|
|
|
|
|
|
|
return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($RelativeOrAbsolutePath)
|
|
|
|
}
|
|
|
|
|
2016-03-16 21:36:38 +00: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 23:15:55 +00: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 21:36:38 +00:00
|
|
|
function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) {
|
2016-03-02 00:37:25 +00: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 21:36:38 +00:00
|
|
|
$DirectoriesToUnpack = Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package -Zip $Zip -OutPath $OutPath
|
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
foreach ($entry in $Zip.Entries) {
|
2016-03-16 21:36:38 +00: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 22:28:55 +00:00
|
|
|
$OverrideFiles=$OverrideNonVersionedFiles -Or (-Not (Test-Path $DestinationPath))
|
|
|
|
if ((-Not $DestinationPath.EndsWith("\")) -And $OverrideFiles) {
|
2016-03-16 21:36:38 +00:00
|
|
|
New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null
|
2016-03-16 22:28:55 +00:00
|
|
|
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $DestinationPath, $OverrideNonVersionedFiles)
|
2016-03-16 21:36:38 +00:00
|
|
|
}
|
2016-03-14 19:23:27 +00:00
|
|
|
}
|
2016-03-02 00:37:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
if ($Zip -ne $null) {
|
|
|
|
$Zip.Dispose()
|
|
|
|
}
|
|
|
|
}
|
2015-11-11 01:30:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-02 00:37:25 +00: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 21:36:38 +00:00
|
|
|
$DownloadLinks = Get-Download-Links -AzureFeed $AzureFeed -AzureChannel $AzureChannel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
|
2016-03-02 00:37:25 +00:00
|
|
|
|
|
|
|
if ($DryRun) {
|
2016-03-16 21:36:38 +00:00
|
|
|
Say "Payload URLs:"
|
|
|
|
foreach ($DownloadLink in $DownloadLinks) {
|
|
|
|
Say "- $DownloadLink"
|
|
|
|
}
|
2016-03-16 22:46:15 +00:00
|
|
|
Say "Repeatable invocation: .\$($MyInvocation.MyCommand) -Version $SpecificVersion -Channel $Channel -Architecture $CLIArchitecture -InstallDir $InstallDir"
|
2016-03-02 00:37:25 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
$InstallRoot = Resolve-Installation-Path $InstallDir
|
|
|
|
Say-Verbose "InstallRoot: $InstallRoot"
|
|
|
|
|
|
|
|
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
|
|
|
|
|
2016-03-16 21:36:38 +00:00
|
|
|
foreach ($DownloadLink in $DownloadLinks) {
|
|
|
|
$ZipPath = [System.IO.Path]::GetTempFileName()
|
|
|
|
Say "Downloading $DownloadLink"
|
|
|
|
$resp = Invoke-WebRequest -UseBasicParsing $DownloadLink -OutFile $ZipPath
|
2016-03-02 00:37:25 +00:00
|
|
|
|
2016-03-16 21:36:38 +00:00
|
|
|
Say "Extracting zip from $DownloadLink"
|
|
|
|
Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot
|
2016-03-02 00:37:25 +00:00
|
|
|
|
2016-03-16 21:36:38 +00:00
|
|
|
Remove-Item $ZipPath
|
|
|
|
}
|
2016-03-02 00:37:25 +00:00
|
|
|
|
2016-03-02 22:03:17 +00:00
|
|
|
$BinPath = Get-Absolute-Path $(Join-Path -Path $InstallRoot -ChildPath $BinFolderRelativePath)
|
2016-03-02 00:37:25 +00:00
|
|
|
if (-Not $NoPath) {
|
2016-03-02 22:03:17 +00: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-02 00:37:25 +00:00
|
|
|
$env:path += ";$BinPath"
|
|
|
|
}
|
2016-03-02 22:03:17 +00:00
|
|
|
else {
|
|
|
|
Say "Binaries of dotnet can be found in $BinPath"
|
|
|
|
}
|
2015-11-16 21:49:29 +00:00
|
|
|
|
2016-03-02 00:37:25 +00:00
|
|
|
Say "Installation finished"
|