[vcpkg] Significantly reduce usage of powershell. Reduce console font switching bug

This commit is contained in:
Alexander Karatarakis 2018-05-05 04:23:19 -07:00
parent 52f01eefa6
commit 1b0682a39e
22 changed files with 598 additions and 770 deletions

View File

@ -1,8 +1,3 @@
function vcpkgHasModule([Parameter(Mandatory=$true)][string]$moduleName)
{
return [bool](Get-Module -ListAvailable -Name $moduleName)
}
function vcpkgHasProperty([Parameter(Mandatory=$true)][AllowNull()]$object, [Parameter(Mandatory=$true)]$propertyName)
{
if ($object -eq $null)
@ -10,320 +5,21 @@ function vcpkgHasProperty([Parameter(Mandatory=$true)][AllowNull()]$object, [Par
return $false
}
return [bool]($object.psobject.Properties | where { $_.Name -eq "$propertyName"})
return [bool]($object.psobject.Properties | Where-Object { $_.Name -eq "$propertyName"})
}
function vcpkgCreateDirectoryIfNotExists([Parameter(Mandatory=$true)][string]$dirPath)
function getProgramFiles32bit()
{
if (!(Test-Path $dirPath))
$out = ${env:PROGRAMFILES(X86)}
if ($out -eq $null)
{
New-Item -ItemType Directory -Path $dirPath | Out-Null
$out = ${env:PROGRAMFILES}
}
}
function vcpkgCreateParentDirectoryIfNotExists([Parameter(Mandatory=$true)][string]$path)
{
$parentDir = split-path -parent $path
if ([string]::IsNullOrEmpty($parentDir))
{
return
}
if (!(Test-Path $parentDir))
{
New-Item -ItemType Directory -Path $parentDir | Out-Null
}
}
function vcpkgIsDirectory([Parameter(Mandatory=$true)][string]$path)
{
return (Get-Item $path) -is [System.IO.DirectoryInfo]
}
function vcpkgRemoveItem([Parameter(Mandatory=$true)][string]$path)
{
if ([string]::IsNullOrEmpty($path))
{
return
}
if (Test-Path $path)
{
# Remove-Item -Recurse occasionally fails. This is a workaround
if (vcpkgIsDirectory $path)
{
& cmd.exe /c rd /s /q $path
}
else
{
Remove-Item $path -Force
}
}
}
function vcpkgHasCommand([Parameter(Mandatory=$true)][string]$commandName)
{
return [bool](Get-Command -Name $commandName -ErrorAction SilentlyContinue)
}
function vcpkgHasCommandParameter([Parameter(Mandatory=$true)][string]$commandName, [Parameter(Mandatory=$true)][string]$parameterName)
{
return (Get-Command $commandName).Parameters.Keys -contains $parameterName
}
function vcpkgGetCredentials()
{
if (vcpkgHasCommandParameter -commandName 'Get-Credential' -parameterName 'Message')
{
return Get-Credential -Message "Enter credentials for Proxy Authentication"
}
else
{
Write-Host "Enter credentials for Proxy Authentication"
return Get-Credential
}
}
function vcpkgGetSHA512([Parameter(Mandatory=$true)][string]$filePath)
{
if (vcpkgHasCommand -commandName 'Microsoft.PowerShell.Utility\Get-FileHash')
{
Write-Verbose("Hashing with Microsoft.PowerShell.Utility\Get-FileHash")
$hashresult = Microsoft.PowerShell.Utility\Get-FileHash -Path $filePath -Algorithm SHA512 -ErrorVariable hashError
if ($hashError)
{
Start-Sleep 3
$hashresult = Microsoft.PowerShell.Utility\Get-FileHash -Path $filePath -Algorithm SHA512 -ErrorVariable Stop
}
$hash = $hashresult.Hash
}
elseif(vcpkgHasCommand -commandName 'Pscx\Get-Hash')
{
Write-Verbose("Hashing with Pscx\Get-Hash")
$hash = (Pscx\Get-Hash -Path $filePath -Algorithm SHA512).HashString
}
else
{
Write-Verbose("Hashing with .NET")
$hashAlgorithm = [Security.Cryptography.HashAlgorithm]::Create("SHA512")
$fileAsByteArray = [io.File]::ReadAllBytes($filePath)
$hashByteArray = $hashAlgorithm.ComputeHash($fileAsByteArray)
$hash = -Join ($hashByteArray | ForEach-Object {"{0:x2}" -f $_})
}
return $hash.ToLower()
}
function vcpkgCheckEqualFileHash( [Parameter(Mandatory=$true)][string]$url,
[Parameter(Mandatory=$true)][string]$filePath,
[Parameter(Mandatory=$true)][string]$expectedHash)
{
$actualHash = vcpkgGetSHA512 $filePath
if ($expectedHash -ne $actualHash)
{
Write-Host ("`nFile does not have expected hash:`n" +
" url: [ $url ]`n" +
" File path: [ $filePath ]`n" +
" Expected hash: [ $expectedHash ]`n" +
" Actual hash: [ $actualHash ]`n")
throw
}
}
function vcpkgDownloadFile( [Parameter(Mandatory=$true)][string]$url,
[Parameter(Mandatory=$true)][string]$downloadPath,
[Parameter(Mandatory=$true)][string]$sha512)
{
if ($url -match "github")
{
if ([System.Enum]::IsDefined([Net.SecurityProtocolType], "Tls12"))
{
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}
else
{
Write-Warning "Github has dropped support for TLS versions prior to 1.2, which is not available on your system"
Write-Warning "Please manually download $url to $downloadPath"
Write-Warning "To solve this issue for future downloads, you can also install Windows Management Framework 5.1+"
throw "Download failed"
}
}
vcpkgCreateParentDirectoryIfNotExists $downloadPath
$downloadPartPath = "$downloadPath.part"
vcpkgRemoveItem $downloadPartPath
$wc = New-Object System.Net.WebClient
if (!$wc.Proxy.IsBypassed($url))
{
$wc.Proxy.Credentials = vcpkgGetCredentials
}
$wc.DownloadFile($url, $downloadPartPath)
vcpkgCheckEqualFileHash -url $url -filePath $downloadPartPath -expectedHash $sha512
Move-Item -Path $downloadPartPath -Destination $downloadPath
}
function vcpkgDownloadFileWithAria2( [Parameter(Mandatory=$true)][string]$aria2exe,
[Parameter(Mandatory=$true)][string]$url,
[Parameter(Mandatory=$true)][string]$downloadPath,
[Parameter(Mandatory=$true)][string]$sha512)
{
vcpkgCreateParentDirectoryIfNotExists $downloadPath
$downloadPartPath = "$downloadPath.part"
vcpkgRemoveItem $downloadPartPath
$parentDir = split-path -parent $downloadPath
$filename = split-path -leaf $downloadPath
if ((Test-Path $url) -or ($url.StartsWith("file://"))) # if is local file
{
vcpkgDownloadFile $url $downloadPath $sha512
return
}
$ec = vcpkgInvokeCommand "$aria2exe" "--dir=`"$parentDir`" --out=`"$filename.part`" $url"
if ($ec -ne 0)
{
Write-Host "Could not download $url"
throw
}
vcpkgCheckEqualFileHash -url $url -filePath $downloadPartPath -expectedHash $sha512
Move-Item -Path $downloadPartPath -Destination $downloadPath
}
function vcpkgExtractFileWith7z([Parameter(Mandatory=$true)][string]$sevenZipExe,
[Parameter(Mandatory=$true)][string]$archivePath,
[Parameter(Mandatory=$true)][string]$destinationDir)
{
vcpkgRemoveItem $destinationDir
$destinationPartial = "$destinationDir.partial"
vcpkgRemoveItem $destinationPartial
vcpkgCreateDirectoryIfNotExists $destinationPartial
$ec = vcpkgInvokeCommand "$sevenZipExe" "x `"$archivePath`" -o`"$destinationPartial`" -y"
if ($ec -ne 0)
{
Write-Host "Could not extract $archivePath"
throw
}
Rename-Item -Path "$destinationPartial" -NewName $destinationDir -ErrorVariable renameResult
if ($renameResult)
{
Start-Sleep 3
Rename-Item -Path "$destinationPartial" -NewName $destinationDir -ErrorAction Stop
}
}
function vcpkgExtractZipFile( [Parameter(Mandatory=$true)][string]$archivePath,
[Parameter(Mandatory=$true)][string]$destinationDir)
{
vcpkgRemoveItem $destinationDir
$destinationPartial = "$destinationDir.partial"
vcpkgRemoveItem $destinationPartial
vcpkgCreateDirectoryIfNotExists $destinationPartial
if (vcpkgHasCommand -commandName 'Microsoft.PowerShell.Archive\Expand-Archive')
{
Write-Verbose("Extracting with Microsoft.PowerShell.Archive\Expand-Archive")
Microsoft.PowerShell.Archive\Expand-Archive -path $archivePath -destinationpath $destinationPartial
}
elseif (vcpkgHasCommand -commandName 'Pscx\Expand-Archive')
{
Write-Verbose("Extracting with Pscx\Expand-Archive")
Pscx\Expand-Archive -path $archivePath -OutputPath $destinationPartial
}
else
{
Write-Verbose("Extracting via shell")
$shell = new-object -com shell.application
$zip = $shell.NameSpace($(Get-Item $archivePath).fullname)
foreach($item in $zip.items())
{
# Piping to Out-Null is used to block until finished
$shell.Namespace($destinationPartial).copyhere($item) | Out-Null
}
}
Rename-Item -Path "$destinationPartial" -NewName $destinationDir
}
function vcpkgInvokeCommand()
{
param ( [Parameter(Mandatory=$true)][string]$executable,
[string]$arguments = "")
Write-Verbose "Executing: ${executable} ${arguments}"
$process = Start-Process -FilePath "`"$executable`"" -ArgumentList $arguments -PassThru -NoNewWindow
Wait-Process -InputObject $process
$ec = $process.ExitCode
Write-Verbose "Execution terminated with exit code $ec."
return $ec
}
function vcpkgInvokeCommandClean()
{
param ( [Parameter(Mandatory=$true)][string]$executable,
[string]$arguments = "")
Write-Verbose "Clean-Executing: ${executable} ${arguments}"
$scriptsDir = split-path -parent $script:MyInvocation.MyCommand.Definition
$cleanEnvScript = "$scriptsDir\VcpkgPowershellUtils-ClearEnvironment.ps1"
$tripleQuotes = "`"`"`""
$argumentsWithEscapedQuotes = $arguments -replace "`"", $tripleQuotes
$command = ". $tripleQuotes$cleanEnvScript$tripleQuotes; & $tripleQuotes$executable$tripleQuotes $argumentsWithEscapedQuotes"
$arg = "-NoProfile", "-ExecutionPolicy Bypass", "-command $command"
$process = Start-Process -FilePath powershell.exe -ArgumentList $arg -PassThru -NoNewWindow
Wait-Process -InputObject $process
$ec = $process.ExitCode
Write-Verbose "Execution terminated with exit code $ec."
return $ec
}
function vcpkgFormatElapsedTime([TimeSpan]$ts)
{
if ($ts.TotalHours -ge 1)
{
return [string]::Format( "{0:N2} h", $ts.TotalHours);
}
if ($ts.TotalMinutes -ge 1)
{
return [string]::Format( "{0:N2} min", $ts.TotalMinutes);
}
if ($ts.TotalSeconds -ge 1)
{
return [string]::Format( "{0:N2} s", $ts.TotalSeconds);
}
if ($ts.TotalMilliseconds -ge 1)
{
return [string]::Format( "{0:N2} ms", $ts.TotalMilliseconds);
}
throw $ts
}
function vcpkgFindFileRecursivelyUp()
{
param(
[ValidateNotNullOrEmpty()]
[Parameter(Mandatory=$true)][string]$startingDir,
[ValidateNotNullOrEmpty()]
[Parameter(Mandatory=$true)][string]$filename
)
$currentDir = $startingDir
while (!($currentDir -eq "") -and !(Test-Path "$currentDir\$filename"))
{
Write-Verbose "Examining $currentDir for $filename"
$currentDir = Split-path $currentDir -Parent
}
Write-Verbose "Examining $currentDir for $filename - Found"
return $currentDir
if ($out -eq $null)
{
throw "Could not find [Program Files 32-bit]"
}
return $out
}

View File

@ -18,10 +18,10 @@ $scriptsDir = split-path -parent $script:MyInvocation.MyCommand.Definition
$profileEntry = "Import-Module '$scriptsDir\posh-vcpkg'"
$profilePath = $PROFILE # Implicit powershell variable
if (!(Test-Path $profilePath))
$profileDir = Split-Path $profilePath -Parent
if (!(Test-Path $profileDir))
{
$profileDir = Split-Path $profilePath -Parent
vcpkgCreateDirectoryIfNotExists $profileDir
New-Item -ItemType Directory -Path $profileDir | Out-Null
}
Write-Host "`nAdding the following line to ${profilePath}:"
@ -38,6 +38,7 @@ if ($existingImports.Count -gt 0)
return
}
# Modifying the profile will invalidate any signatures.
# Posh-git does the following check, so we should too.
# https://github.com/dahlbyk/posh-git/blob/master/src/Utils.ps1
# If the profile script exists and is signed, then we should not modify it

View File

@ -5,35 +5,18 @@ param(
)
Set-StrictMode -Version Latest
$scriptsDir = split-path -parent $script:MyInvocation.MyCommand.Definition
. "$scriptsDir\VcpkgPowershellUtils.ps1"
$vcpkgRootDir = vcpkgFindFileRecursivelyUp $scriptsDir .vcpkg-root
Write-Verbose("vcpkg Path " + $vcpkgRootDir)
$gitHash = "unknownhash"
$oldpath = $env:path
try
$vcpkgRootDir = $scriptsDir
$withVSPath = $withVSPath -replace "\\$" # Remove potential trailing backslash
while (!($vcpkgRootDir -eq "") -and !(Test-Path "$vcpkgRootDir\.vcpkg-root"))
{
[xml]$asXml = Get-Content "$scriptsDir\vcpkgTools.xml"
$toolData = $asXml.SelectSingleNode("//tools/tool[@name=`"git`"]")
$gitFromDownload = "$vcpkgRootDir\downloads\$($toolData.exeRelativePath)"
$gitDir = split-path -parent $gitFromDownload
$env:path += ";$gitDir"
if (Get-Command "git" -ErrorAction SilentlyContinue)
{
$gitHash = git log HEAD -n 1 --format="%cd-%H" --date=short
if ($LASTEXITCODE -ne 0)
{
$gitHash = "unknownhash"
}
}
Write-Verbose "Examining $vcpkgRootDir for .vcpkg-root"
$vcpkgRootDir = Split-path $vcpkgRootDir -Parent
}
finally
{
$env:path = $oldpath
}
Write-Verbose("Git repo version string is " + $gitHash)
Write-Verbose "Examining $vcpkgRootDir for .vcpkg-root - Found"
$gitHash = "nohash"
$vcpkgSourcesPath = "$vcpkgRootDir\toolsrc"
if (!(Test-Path $vcpkgSourcesPath))
@ -42,7 +25,58 @@ if (!(Test-Path $vcpkgSourcesPath))
return
}
$msbuildExeWithPlatformToolset = & $scriptsDir\findAnyMSBuildWithCppPlatformToolset.ps1 $withVSPath
function findAnyMSBuildWithCppPlatformToolset([string]$withVSPath)
{
$VisualStudioInstances = & $scriptsDir\getVisualStudioInstances.ps1
if ($VisualStudioInstances -eq $null)
{
throw "Could not find Visual Studio. VS2015 or VS2017 (with C++) needs to be installed."
}
Write-Verbose "VS Candidates:`n`r$([system.String]::Join([Environment]::NewLine, $VisualStudioInstances))"
foreach ($instanceCandidateWithEOL in $VisualStudioInstances)
{
$instanceCandidate = $instanceCandidateWithEOL -replace "<sol>::" -replace "::<eol>"
Write-Verbose "Inspecting: $instanceCandidate"
$split = $instanceCandidate -split "::"
# $preferenceWeight = $split[0]
# $releaseType = $split[1]
$version = $split[2]
$path = $split[3]
if ($withVSPath -ne "" -and $withVSPath -ne $path)
{
Write-Verbose "Skipping: $instanceCandidate"
continue
}
$majorVersion = $version.Substring(0,2);
if ($majorVersion -eq "15")
{
$VCFolder= "$path\VC\Tools\MSVC\"
if (Test-Path $VCFolder)
{
Write-Verbose "Picking: $instanceCandidate"
return "$path\MSBuild\15.0\Bin\MSBuild.exe", "v141"
}
}
if ($majorVersion -eq "14")
{
$clExe= "$path\VC\bin\cl.exe"
if (Test-Path $clExe)
{
Write-Verbose "Picking: $instanceCandidate"
$programFilesPath = getProgramFiles32bit
return "$programFilesPath\MSBuild\14.0\Bin\MSBuild.exe", "v140"
}
}
}
throw "Could not find MSBuild version with C++ support. VS2015 or VS2017 (with C++) needs to be installed."
}
$msbuildExeWithPlatformToolset = findAnyMSBuildWithCppPlatformToolset $withVSPath
$msbuildExe = $msbuildExeWithPlatformToolset[0]
$platformToolset = $msbuildExeWithPlatformToolset[1]
$windowsSDK = & $scriptsDir\getWindowsSDK.ps1
@ -54,10 +88,33 @@ $arguments = (
"/p:Platform=x86",
"/p:PlatformToolset=$platformToolset",
"/p:TargetPlatformVersion=$windowsSDK",
"/verbosity:minimal",
"/m",
"/nologo",
"`"$vcpkgSourcesPath\dirs.proj`"") -join " "
function vcpkgInvokeCommandClean()
{
param ( [Parameter(Mandatory=$true)][string]$executable,
[string]$arguments = "")
Write-Verbose "Clean-Executing: ${executable} ${arguments}"
$scriptsDir = split-path -parent $script:MyInvocation.MyCommand.Definition
$cleanEnvScript = "$scriptsDir\VcpkgPowershellUtils-ClearEnvironment.ps1"
$tripleQuotes = "`"`"`""
$argumentsWithEscapedQuotes = $arguments -replace "`"", $tripleQuotes
$command = ". $tripleQuotes$cleanEnvScript$tripleQuotes; & $tripleQuotes$executable$tripleQuotes $argumentsWithEscapedQuotes"
$arg = "-NoProfile", "-ExecutionPolicy Bypass", "-command $command"
$process = Start-Process -FilePath powershell.exe -ArgumentList $arg -PassThru -NoNewWindow
Wait-Process -InputObject $process
$ec = $process.ExitCode
Write-Verbose "Execution terminated with exit code $ec."
return $ec
}
# vcpkgInvokeCommandClean cmd "/c echo %PATH%"
Write-Host "`nBuilding vcpkg.exe ...`n"
$ec = vcpkgInvokeCommandClean $msbuildExe $arguments
if ($ec -ne 0)
@ -65,6 +122,7 @@ if ($ec -ne 0)
Write-Error "Building vcpkg.exe failed. Please ensure you have installed Visual Studio with the Desktop C++ workload and the Windows SDK for Desktop C++."
return
}
Write-Host "`nBuilding vcpkg.exe... done.`n"
Write-Verbose("Placing vcpkg.exe in the correct location")

View File

@ -1,109 +0,0 @@
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$tool
)
Set-StrictMode -Version Latest
$scriptsDir = split-path -parent $script:MyInvocation.MyCommand.Definition
. "$scriptsDir\VcpkgPowershellUtils.ps1"
Write-Verbose "Fetching tool: $tool"
$vcpkgRootDir = vcpkgFindFileRecursivelyUp $scriptsDir .vcpkg-root
$downloadsDir = "$vcpkgRootDir\downloads"
vcpkgCreateDirectoryIfNotExists $downloadsDir
$downloadsDir = Resolve-Path $downloadsDir
function fetchToolInternal([Parameter(Mandatory=$true)][string]$tool)
{
$tool = $tool.toLower()
[xml]$asXml = Get-Content "$scriptsDir\vcpkgTools.xml"
$toolData = $asXml.SelectSingleNode("//tools/tool[@name=`"$tool`"]") # Case-sensitive!
if ($toolData -eq $null)
{
throw "Unknown tool $tool"
}
$toolPath="$downloadsDir\tools\$tool-$($toolData.version)-windows"
$exePath = "$toolPath\$($toolData.exeRelativePath)"
if (Test-Path $exePath)
{
return $exePath
}
$isArchive = vcpkgHasProperty -object $toolData -propertyName "archiveName"
if ($isArchive)
{
$downloadPath = "$downloadsDir\$($toolData.archiveName)"
}
else
{
$downloadPath = "$toolPath\$($toolData.exeRelativePath)"
}
[String]$url = $toolData.url
if (!(Test-Path $downloadPath))
{
Write-Host "Downloading $tool..."
# Download aria2 with .NET. aria2 will be used to download everything else.
if ($tool -eq "aria2")
{
vcpkgDownloadFile $url $downloadPath $toolData.sha512
}
else
{
$aria2exe = fetchToolInternal "aria2"
vcpkgDownloadFileWithAria2 $aria2exe $url $downloadPath $toolData.sha512
}
Write-Host "Downloading $tool... done."
}
else
{
vcpkgCheckEqualFileHash -url $url -filePath $downloadPath -expectedHash $toolData.sha512
}
if ($isArchive)
{
Write-Host "Extracting $tool..."
# Extract 7zip920 with shell because we need it to extract 7zip
# Extract aria2 with shell because we need it to download 7zip
if ($tool -eq "7zip920" -or $tool -eq "aria2")
{
vcpkgExtractZipFile -ArchivePath $downloadPath -DestinationDir $toolPath
}
elseif ($tool -eq "7zip")
{
$sevenZip920 = fetchToolInternal "7zip920"
$ec = vcpkgInvokeCommand "$sevenZip920" "x `"$downloadPath`" -o`"$toolPath`" -y"
if ($ec -ne 0)
{
Write-Host "Could not extract $downloadPath"
throw
}
}
else
{
$sevenZipExe = fetchToolInternal "7zip"
vcpkgExtractFileWith7z -sevenZipExe "$sevenZipExe" -ArchivePath $downloadPath -DestinationDir $toolPath
}
Write-Host "Extracting $tool... done."
}
if (-not (Test-Path $exePath))
{
Write-Error "Could not detect or download $tool"
throw
}
return $exePath
}
$path = fetchToolInternal $tool
Write-Verbose "Fetching tool: $tool. Done."
return "<sol>::$path::<eol>"

View File

@ -1,58 +0,0 @@
[CmdletBinding()]
param(
[Parameter(Mandatory=$False)]
[string]$withVSPath = ""
)
Set-StrictMode -Version Latest
$scriptsDir = split-path -parent $script:MyInvocation.MyCommand.Definition
$withVSPath = $withVSPath -replace "\\$" # Remove potential trailing backslash
$VisualStudioInstallationInstances = & $scriptsDir\findVisualStudioInstallationInstances.ps1
if ($VisualStudioInstallationInstances -eq $null)
{
throw "Could not find Visual Studio. VS2015 or VS2017 (with C++) needs to be installed."
}
Write-Verbose "VS Candidates:`n`r$([system.String]::Join([Environment]::NewLine, $VisualStudioInstallationInstances))"
foreach ($instanceCandidateWithEOL in $VisualStudioInstallationInstances)
{
$instanceCandidate = $instanceCandidateWithEOL -replace "<sol>::" -replace "::<eol>"
Write-Verbose "Inspecting: $instanceCandidate"
$split = $instanceCandidate -split "::"
# $preferenceWeight = $split[0]
# $releaseType = $split[1]
$version = $split[2]
$path = $split[3]
if ($withVSPath -ne "" -and $withVSPath -ne $path)
{
Write-Verbose "Skipping: $instanceCandidate"
continue
}
$majorVersion = $version.Substring(0,2);
if ($majorVersion -eq "15")
{
$VCFolder= "$path\VC\Tools\MSVC\"
if (Test-Path $VCFolder)
{
Write-Verbose "Picking: $instanceCandidate"
return "$path\MSBuild\15.0\Bin\MSBuild.exe", "v141"
}
}
if ($majorVersion -eq "14")
{
$clExe= "$path\VC\bin\cl.exe"
if (Test-Path $clExe)
{
Write-Verbose "Picking: $instanceCandidate"
$programFilesPath = & $scriptsDir\getProgramFiles32bit.ps1
return "$programFilesPath\MSBuild\14.0\Bin\MSBuild.exe", "v140"
}
}
}
throw "Could not find MSBuild version with C++ support. VS2015 or VS2017 (with C++) needs to be installed."

View File

@ -1,61 +0,0 @@
[CmdletBinding()]
param(
)
Set-StrictMode -Version Latest
$scriptsDir = split-path -parent $script:MyInvocation.MyCommand.Definition
. "$scriptsDir\VcpkgPowershellUtils.ps1"
$vswhereExe = (& $scriptsDir\fetchTool.ps1 "vswhere") -replace "<sol>::" -replace "::<eol>"
$output = & $vswhereExe -prerelease -legacy -products * -format xml
[xml]$asXml = $output
$results = New-Object System.Collections.ArrayList
foreach ($instance in $asXml.instances.instance)
{
$installationPath = $instance.InstallationPath -replace "\\$" # Remove potential trailing backslash
$installationVersion = $instance.InstallationVersion
$isPrerelease = -7
if (vcpkgHasProperty -object $instance -propertyName "isPrerelease")
{
$isPrerelease = $instance.isPrerelease
}
if ($isPrerelease -eq 0)
{
$releaseType = "PreferenceWeight3::StableRelease"
}
elseif ($isPrerelease -eq 1)
{
$releaseType = "PreferenceWeight2::PreRelease"
}
else
{
$releaseType = "PreferenceWeight1::Legacy"
}
# Placed like that for easy sorting according to preference
$results.Add("<sol>::${releaseType}::${installationVersion}::${installationPath}::<eol>") > $null
}
# If nothing is found, attempt to find VS2015 Build Tools (not detected by vswhere.exe)
if ($results.Count -eq 0)
{
$programFiles = & $scriptsDir\getProgramFiles32bit.ps1
$installationPath = "$programFiles\Microsoft Visual Studio 14.0"
$clExe = "$installationPath\VC\bin\cl.exe"
$vcvarsallbat = "$installationPath\VC\vcvarsall.bat"
if ((Test-Path $clExe) -And (Test-Path $vcvarsallbat))
{
return "<sol>::PreferenceWeight1::Legacy::14.0::$installationPath::<eol>"
}
}
$results.Sort()
$results.Reverse()
return $results

View File

@ -1,17 +0,0 @@
[CmdletBinding()]
param(
)
$out = ${env:PROGRAMFILES(X86)}
if ($out -eq $null)
{
$out = ${env:PROGRAMFILES}
}
if ($out -eq $null)
{
throw "Could not find [Program Files 32-bit]"
}
return $out

View File

@ -1,17 +0,0 @@
[CmdletBinding()]
param(
)
$out = ${env:ProgramW6432}
if ($out -eq $null)
{
$out = ${env:PROGRAMFILES}
}
if ($out -eq $null)
{
throw "Could not find [Program Files Platform Bitness]"
}
return $out

View File

@ -0,0 +1,74 @@
[CmdletBinding()]
param(
)
Set-StrictMode -Version Latest
$scriptsDir = split-path -parent $script:MyInvocation.MyCommand.Definition
. "$scriptsDir\VcpkgPowershellUtils.ps1"
$programFiles = getProgramFiles32bit
$results = New-Object System.Collections.ArrayList
$vswhereExe = "$programFiles\Microsoft Visual Studio\Installer\vswhere.exe"
if (Test-Path $vswhereExe)
{
$output = & $vswhereExe -prerelease -legacy -products * -format xml
[xml]$asXml = $output
foreach ($instance in $asXml.instances.instance)
{
$installationPath = $instance.InstallationPath -replace "\\$" # Remove potential trailing backslash
$installationVersion = $instance.InstallationVersion
$isPrerelease = -7
if (vcpkgHasProperty -object $instance -propertyName "isPrerelease")
{
$isPrerelease = $instance.isPrerelease
}
if ($isPrerelease -eq 0)
{
$releaseType = "PreferenceWeight3::StableRelease"
}
elseif ($isPrerelease -eq 1)
{
$releaseType = "PreferenceWeight2::PreRelease"
}
else
{
$releaseType = "PreferenceWeight1::Legacy"
}
# Placed like that for easy sorting according to preference
$results.Add("<sol>::${releaseType}::${installationVersion}::${installationPath}::<eol>") > $null
}
}
else
{
Write-Verbose "Could not locate vswhere at $vswhereExe"
}
$installationPath = Split-Path -Parent $(Split-Path -Parent "$env:vs140comntools")
$clExe = "$installationPath\VC\bin\cl.exe"
$vcvarsallbat = "$installationPath\VC\vcvarsall.bat"
if ((Test-Path $clExe) -And (Test-Path $vcvarsallbat))
{
$results.Add("<sol>::PreferenceWeight1::Legacy::14.0::$installationPath::<eol>") > $null
}
$installationPath = "$programFiles\Microsoft Visual Studio 14.0"
$clExe = "$installationPath\VC\bin\cl.exe"
$vcvarsallbat = "$installationPath\VC\vcvarsall.bat"
if ((Test-Path $clExe) -And (Test-Path $vcvarsallbat))
{
$results.Add("<sol>::PreferenceWeight1::Legacy::14.0::$installationPath::<eol>") > $null
}
$results.Sort()
$results.Reverse()
return $results

View File

@ -61,17 +61,10 @@
</tool>
<tool name="7zip" os="windows">
<version>18.01.0</version>
<exeRelativePath>7za.exe</exeRelativePath>
<url>https://www.7-zip.org/a/7z1801-extra.7z</url>
<sha512>9133fc551d76515e37fdd4dd8c1e28d464aea493548246b44565a42bba46715764f41f9cfa14d470d298c3a6e9829d200f8be5168cb67cf8f23d8042fca833bc</sha512>
<archiveName>7z1801-extra.7z</archiveName>
</tool>
<tool name="7zip920" os="windows">
<version>9.20.0</version>
<exeRelativePath>7za.exe</exeRelativePath>
<url>https://www.7-zip.org/a/7za920.zip</url>
<sha512>84e830c91a0e8ae499cc4814080da6569d8a6acbddc585c8b62abc86c809793aeb669b0a741063a379fd281ade85f120bc27efeb67d63bf961be893eec8bc3b3</sha512>
<archiveName>7za920.zip</archiveName>
<exeRelativePath>7-Zip.CommandLine.18.1.0\tools\7za.exe</exeRelativePath>
<url>https://www.nuget.org/api/v2/package/7-Zip.CommandLine/18.1.0</url>
<sha512>8c75314102e68d2b2347d592f8e3eb05812e1ebb525decbac472231633753f1d4ca31c8e6881a36144a8da26b2571305b3ae3f4e2b85fc4a290aeda63d1a13b8</sha512>
<archiveName>7-zip.commandline.18.1.0.nupkg</archiveName>
</tool>
<tool name="aria2" os="windows">
<version>18.01.0</version>

View File

@ -47,6 +47,7 @@ namespace vcpkg::System
ExitCodeAndOutput cmd_execute_and_capture_output(const CStringView cmd_line);
#if defined(_WIN32)
void powershell_execute(const std::string& title,
const fs::path& script_path,
const std::vector<PowershellParameter>& parameters = {});
@ -54,6 +55,7 @@ namespace vcpkg::System
std::string powershell_execute_and_capture_output(const std::string& title,
const fs::path& script_path,
const std::vector<PowershellParameter>& parameters = {});
#endif
enum class Color
{

View File

@ -132,14 +132,14 @@ namespace vcpkg::Commands
namespace Hash
{
std::string get_string_hash(const std::string& s, const std::string& hash_type);
std::string get_file_hash(const VcpkgPaths& paths, const fs::path& path, const std::string& hash_type);
std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type);
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
namespace Fetch
{
std::vector<Toolset> find_toolset_instances(const VcpkgPaths& paths);
std::vector<Toolset> find_toolset_instances_prefered_first(const VcpkgPaths& paths);
fs::path get_tool_path(const VcpkgPaths& paths, const std::string& tool);
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}

View File

@ -66,6 +66,7 @@ namespace vcpkg
fs::path triplets;
fs::path scripts;
fs::path tools;
fs::path buildsystems;
fs::path buildsystems_msbuild_targets;

View File

@ -16,6 +16,8 @@ namespace vcpkg::Checks
const auto elapsed_us = GlobalState::timer.lock()->microseconds();
Debug::println("Exiting after %d us", static_cast<int>(elapsed_us));
auto metrics = Metrics::g_metrics.lock();
metrics->track_metric("elapsed_us", elapsed_us);
GlobalState::debugging = false;

View File

@ -155,6 +155,7 @@ namespace vcpkg::System
int cmd_execute_clean(const CStringView cmd_line, const std::unordered_map<std::string, std::string>& extra_env)
{
auto timer = Chrono::ElapsedTimer::create_started();
#if defined(_WIN32)
static const std::string SYSTEM_ROOT = get_environment_variable("SystemRoot").value_or_exit(VCPKG_LINE_INFO);
static const std::string SYSTEM_32 = SYSTEM_ROOT + R"(\system32)";
@ -271,7 +272,7 @@ namespace vcpkg::System
DWORD exit_code = 0;
GetExitCodeProcess(process_info.hProcess, &exit_code);
Debug::println("CreateProcessW() returned %lu", exit_code);
Debug::println("CreateProcessW() returned %lu after %d us", exit_code, static_cast<int>(timer.microseconds()));
return static_cast<int>(exit_code);
#else
Debug::println("system(%s)", cmd_line.c_str());
@ -369,13 +370,20 @@ namespace vcpkg::System
#endif
}
#if defined(_WIN32)
void powershell_execute(const std::string& title,
const fs::path& script_path,
const std::vector<PowershellParameter>& parameters)
{
SetConsoleCP(437);
SetConsoleOutputCP(437);
const std::string cmd = make_powershell_cmd(script_path, parameters);
const int rc = System::cmd_execute(cmd);
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
if (rc)
{
System::println(Color::error,
@ -394,14 +402,22 @@ namespace vcpkg::System
Checks::exit_with_code(VCPKG_LINE_INFO, rc);
}
}
#endif
#if defined(_WIN32)
std::string powershell_execute_and_capture_output(const std::string& title,
const fs::path& script_path,
const std::vector<PowershellParameter>& parameters)
{
SetConsoleCP(437);
SetConsoleOutputCP(437);
const std::string cmd = make_powershell_cmd(script_path, parameters);
auto rc = System::cmd_execute_and_capture_output(cmd);
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
if (rc.exit_code)
{
System::println(Color::error,
@ -433,6 +449,7 @@ namespace vcpkg::System
return rc.output;
}
#endif
void println() { putchar('\n'); }

View File

@ -461,9 +461,9 @@ namespace vcpkg::Build
abi_tag_entries.insert(abi_tag_entries.end(), dependency_abis.begin(), dependency_abis.end());
abi_tag_entries.emplace_back(
AbiEntry{"portfile", Commands::Hash::get_file_hash(paths, config.port_dir / "portfile.cmake", "SHA1")});
AbiEntry{"portfile", Commands::Hash::get_file_hash(fs, config.port_dir / "portfile.cmake", "SHA1")});
abi_tag_entries.emplace_back(
AbiEntry{"control", Commands::Hash::get_file_hash(paths, config.port_dir / "CONTROL", "SHA1")});
AbiEntry{"control", Commands::Hash::get_file_hash(fs, config.port_dir / "CONTROL", "SHA1")});
abi_tag_entries.emplace_back(AbiEntry{"triplet", pre_build_info.triplet_abi_tag});
@ -498,7 +498,7 @@ namespace vcpkg::Build
const auto abi_file_path = paths.buildtrees / name / (triplet.canonical_name() + ".vcpkg_abi_info.txt");
fs.write_contents(abi_file_path, full_abi_info);
return AbiTagAndFile{Commands::Hash::get_file_hash(paths, abi_file_path, "SHA1"), abi_file_path};
return AbiTagAndFile{Commands::Hash::get_file_hash(fs, abi_file_path, "SHA1"), abi_file_path};
}
System::println(
@ -783,14 +783,12 @@ namespace vcpkg::Build
{
return it_hash->second;
}
auto hash = Commands::Hash::get_file_hash(paths, triplet_file_path, "SHA1");
auto hash = Commands::Hash::get_file_hash(paths.get_filesystem(), triplet_file_path, "SHA1");
s_hash_cache.emplace(triplet_file_path, hash);
return hash;
}
else
{
return std::string();
}
}();
const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path,

View File

@ -43,7 +43,7 @@ namespace vcpkg::Commands
{"portsdiff", &PortsDiff::perform_and_exit},
{"autocomplete", &Autocomplete::perform_and_exit},
{"hash", &Hash::perform_and_exit},
// {"fetch", &Fetch::perform_and_exit},
{"fetch", &Fetch::perform_and_exit},
};
return t;
}

View File

@ -1,6 +1,7 @@
#include "pch.h"
#include <vcpkg/base/checks.h>
#include <vcpkg/base/sortedvector.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/util.h>
@ -19,6 +20,7 @@ namespace vcpkg::Commands::Fetch
fs::path exe_path;
std::string url;
fs::path download_path;
bool is_archive;
fs::path tool_dir_path;
std::string sha512;
};
@ -41,20 +43,85 @@ namespace vcpkg::Commands::Fetch
return result;
}
static Optional<std::string> extract_string_between_delimiters(const std::string& input,
const std::string& left_delim,
const std::string& right_delim,
const size_t& starting_offset = 0)
struct VcpkgStringRange
{
const size_t from = input.find(left_delim, starting_offset);
if (from == std::string::npos) return nullopt;
VcpkgStringRange() = default;
const size_t substring_start = from + left_delim.length();
// Implicit by design
VcpkgStringRange(const std::string& s) : begin(s.cbegin()), end(s.cend()) {}
const size_t to = input.find(right_delim, substring_start);
if (from == std::string::npos) return nullopt;
VcpkgStringRange(const std::string::const_iterator begin, const std::string::const_iterator end)
: begin(begin), end(end)
{
}
return input.substr(substring_start, to - substring_start);
std::string::const_iterator begin;
std::string::const_iterator end;
std::string to_string() const { return std::string(this->begin, this->end); }
};
static std::vector<VcpkgStringRange> find_all_enclosed(const VcpkgStringRange& input,
const std::string& left_delim,
const std::string& right_delim)
{
std::string::const_iterator it_left = input.begin;
std::string::const_iterator it_right = input.begin;
std::vector<VcpkgStringRange> output;
while (true)
{
it_left = std::search(it_right, input.end, left_delim.cbegin(), left_delim.cend());
if (it_left == input.end) break;
it_left += left_delim.length();
it_right = std::search(it_left, input.end, right_delim.cbegin(), right_delim.cend());
if (it_right == input.end) break;
output.emplace_back(it_left, it_right);
++it_right;
}
return output;
}
static VcpkgStringRange find_exactly_one_enclosed(const VcpkgStringRange& input,
const std::string& left_tag,
const std::string& right_tag)
{
std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag);
Checks::check_exit(VCPKG_LINE_INFO,
result.size() == 1,
"Found %d sets of %s.*%s but expected exactly 1, in block:\n%s",
result.size(),
left_tag,
right_tag,
input);
return std::move(result.front());
}
static Optional<VcpkgStringRange> find_at_most_one_enclosed(const VcpkgStringRange& input,
const std::string& left_tag,
const std::string& right_tag)
{
std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag);
Checks::check_exit(VCPKG_LINE_INFO,
result.size() <= 1,
"Found %d sets of %s.*%s but expected at most 1, in block:\n%s",
result.size(),
left_tag,
right_tag,
input);
if (result.empty())
{
return nullopt;
}
return result.front();
}
static ToolData parse_tool_data_from_xml(const VcpkgPaths& paths, const std::string& tool)
@ -72,21 +139,6 @@ namespace vcpkg::Commands::Fetch
#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
static const std::string XML_VERSION = "2";
static const fs::path XML_PATH = paths.scripts / "vcpkgTools.xml";
const auto get_string_inside_tags =
[](const std::string& input, const std::string& left_delim, const std::string& right_delim) -> std::string {
Optional<std::string> result = extract_string_between_delimiters(input, left_delim, right_delim);
Checks::check_exit(VCPKG_LINE_INFO,
result.has_value(),
"Could not find tag <%s>.*<%s> in %s",
left_delim,
right_delim,
XML_PATH.generic_string());
auto r = *result.get();
return Strings::trim(std::move(r));
};
static const std::regex XML_VERSION_REGEX{R"###(<tools[\s]+version="([^"]+)">)###"};
static const std::string XML = paths.get_filesystem().read_contents(XML_PATH).value_or_exit(VCPKG_LINE_INFO);
std::smatch match_xml_version;
@ -112,14 +164,14 @@ namespace vcpkg::Commands::Fetch
tool,
XML_PATH.generic_string());
const std::string tool_data = get_string_inside_tags(XML, match_tool_entry[0], R"(</tool>)");
const std::string version_as_string = get_string_inside_tags(tool_data, "<version>", R"(</version>)");
const std::string tool_data = find_exactly_one_enclosed(XML, match_tool_entry[0], "</tool>").to_string();
const std::string version_as_string =
find_exactly_one_enclosed(tool_data, "<version>", "</version>").to_string();
const std::string exe_relative_path =
get_string_inside_tags(tool_data, "<exeRelativePath>", R"(</exeRelativePath>)");
const std::string url = get_string_inside_tags(tool_data, "<url>", R"(</url>)");
const std::string sha512 = get_string_inside_tags(tool_data, "<sha512>", R"(</sha512>)");
auto archive_name = extract_string_between_delimiters(tool_data, "<archiveName>", R"(</archiveName>)");
find_exactly_one_enclosed(tool_data, "<exeRelativePath>", "</exeRelativePath>").to_string();
const std::string url = find_exactly_one_enclosed(tool_data, "<url>", "</url>").to_string();
const std::string sha512 = find_exactly_one_enclosed(tool_data, "<sha512>", "</sha512>").to_string();
auto archive_name = find_at_most_one_enclosed(tool_data, "<archiveName>", "</archiveName>");
const Optional<std::array<int, 3>> version = parse_version_string(version_as_string);
Checks::check_exit(VCPKG_LINE_INFO,
@ -129,13 +181,14 @@ namespace vcpkg::Commands::Fetch
version_as_string);
const std::string tool_dir_name = Strings::format("%s-%s-%s", tool, version_as_string, OS_STRING);
const fs::path tool_dir_path = paths.downloads / "tools" / tool_dir_name;
const fs::path tool_dir_path = paths.tools / tool_dir_name;
const fs::path exe_path = tool_dir_path / exe_relative_path;
return ToolData{*version.get(),
exe_path,
url,
paths.downloads / archive_name.value_or(exe_relative_path),
paths.downloads / archive_name.value_or(exe_relative_path).to_string(),
archive_name.has_value(),
tool_dir_path,
sha512};
#endif
@ -163,51 +216,87 @@ namespace vcpkg::Commands::Fetch
actual_version[2] >= expected_version[2]));
}
static Optional<fs::path> find_if_has_equal_or_greater_version(const std::vector<fs::path>& candidate_paths,
static Optional<fs::path> find_if_has_equal_or_greater_version(Files::Filesystem& fs,
const std::vector<fs::path>& candidate_paths,
const std::string& version_check_arguments,
const std::array<int, 3>& expected_version)
{
auto it = Util::find_if(candidate_paths, [&](const fs::path& p) {
const auto it = Util::find_if(candidate_paths, [&](const fs::path& p) {
if (!fs.exists(p)) return false;
const std::string cmd = Strings::format(R"("%s" %s)", p.u8string(), version_check_arguments);
return exists_and_has_equal_or_greater_version(cmd, expected_version);
});
if (it != candidate_paths.cend())
{
return std::move(*it);
return *it;
}
return nullopt;
}
static std::vector<std::string> keep_data_lines(const std::string& data_blob)
{
static const std::regex DATA_LINE_REGEX(R"(<sol>::(.+?)(?=::<eol>))");
std::vector<std::string> data_lines;
const std::sregex_iterator it(data_blob.cbegin(), data_blob.cend(), DATA_LINE_REGEX);
const std::sregex_iterator end;
for (std::sregex_iterator i = it; i != end; ++i)
{
const std::smatch match = *i;
data_lines.push_back(match[1].str());
}
return data_lines;
}
#if !defined(_WIN32)
static void extract_archive(const VcpkgPaths& paths, const fs::path& archive, const fs::path& to_path)
{
Files::Filesystem& fs = paths.get_filesystem();
const fs::path to_path_partial = to_path.u8string() + ".partial";
std::error_code ec;
fs.remove_all(to_path, ec);
fs.remove_all(to_path_partial, ec);
fs.create_directories(to_path_partial, ec);
const auto ext = archive.extension();
#if defined(_WIN32)
if (ext == ".nupkg")
{
static bool recursion_limiter_sevenzip_old = false;
Checks::check_exit(VCPKG_LINE_INFO, !recursion_limiter_sevenzip_old);
recursion_limiter_sevenzip_old = true;
const auto nuget_exe = get_tool_path(paths, Tools::NUGET);
const std::string stem = archive.stem().u8string();
// assuming format of [name].[version in the form d.d.d]
// This assumption may not always hold
std::smatch match;
const bool has_match = std::regex_match(stem, match, std::regex{R"###(^(.+)\.(\d+\.\d+\.\d+)$)###"});
Checks::check_exit(VCPKG_LINE_INFO,
has_match,
"Could not deduce nuget id and version from filename: %s",
archive.u8string());
const std::string nugetid = match[1];
const std::string version = match[2];
const auto code_and_output = System::cmd_execute_and_capture_output(Strings::format(
R"("%s" install %s -Version %s -OutputDirectory "%s" -Source "%s" -nocache -DirectDownload -NonInteractive -ForceEnglishOutput -PackageSaveMode nuspec)",
nuget_exe.u8string(),
nugetid,
version,
to_path_partial.u8string(),
paths.downloads.u8string()));
Checks::check_exit(VCPKG_LINE_INFO,
code_and_output.exit_code == 0,
"Failed to extract '%s' with message:\n%s",
archive.u8string(),
code_and_output.output);
recursion_limiter_sevenzip_old = false;
}
else
{
static bool recursion_limiter_sevenzip = false;
Checks::check_exit(VCPKG_LINE_INFO, !recursion_limiter_sevenzip);
recursion_limiter_sevenzip = true;
const auto seven_zip = get_tool_path(paths, Tools::SEVEN_ZIP);
const auto code_and_output = System::cmd_execute_and_capture_output(Strings::format(
R"("%s" x "%s" -o"%s" -y)", seven_zip.u8string(), archive.u8string(), to_path_partial.u8string()));
Checks::check_exit(VCPKG_LINE_INFO,
code_and_output.exit_code == 0,
"7zip failed while extracting '%s' with message:\n%s",
archive.u8string(),
code_and_output.output);
recursion_limiter_sevenzip = false;
}
#else
if (ext == ".gz" && ext.extension() != ".tar")
{
const auto code = System::cmd_execute(
@ -224,16 +313,23 @@ namespace vcpkg::Commands::Fetch
{
Checks::exit_with_message(VCPKG_LINE_INFO, "Unexpected archive extension: %s", ext.u8string());
}
#endif
fs.rename(to_path_partial, to_path);
fs.rename(to_path_partial, to_path, ec);
Checks::check_exit(VCPKG_LINE_INFO,
!ec,
"Failed to do post-extract rename-in-place.\nfs.rename(%s, %s, %s)",
to_path_partial.u8string(),
to_path.u8string(),
ec.message());
}
static void verify_hash(const VcpkgPaths& paths,
static void verify_hash(const Files::Filesystem& fs,
const std::string& url,
const fs::path& path,
const std::string& sha512)
{
const std::string actual_hash = Hash::get_file_hash(paths, path, "SHA512");
const std::string actual_hash = Hash::get_file_hash(fs, path, "SHA512");
Checks::check_exit(VCPKG_LINE_INFO,
sha512 == actual_hash,
"File does not have the expected hash:\n"
@ -247,24 +343,119 @@ namespace vcpkg::Commands::Fetch
actual_hash);
}
static void download_file(const VcpkgPaths& paths,
#if defined(_WIN32)
static void winhttp_download_file(Files::Filesystem& fs,
CStringView target_file_path,
CStringView hostname,
CStringView url_path)
{
// Make sure the directories are present, otherwise fopen_s fails
const auto dir = fs::path(target_file_path.c_str()).parent_path();
std::error_code ec;
fs.create_directories(dir, ec);
Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not create directories %s", dir.u8string());
FILE* f = nullptr;
const errno_t err = fopen_s(&f, target_file_path.c_str(), "wb");
Checks::check_exit(VCPKG_LINE_INFO,
!err,
"Could not download https://%s%s. Failed to open file %s. Error code was %s",
hostname,
url_path,
target_file_path,
std::to_string(err));
auto hSession = WinHttpOpen(
L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
Checks::check_exit(VCPKG_LINE_INFO, hSession, "WinHttpOpen() failed: %d", GetLastError());
// Use Windows 10 defaults on Windows 7
DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2);
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols));
// Specify an HTTP server.
auto hConnect = WinHttpConnect(hSession, Strings::to_utf16(hostname).c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
Checks::check_exit(VCPKG_LINE_INFO, hConnect, "WinHttpConnect() failed: %d", GetLastError());
// Create an HTTP request handle.
auto hRequest = WinHttpOpenRequest(hConnect,
L"GET",
Strings::to_utf16(url_path).c_str(),
nullptr,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE);
Checks::check_exit(VCPKG_LINE_INFO, hRequest, "WinHttpOpenRequest() failed: %d", GetLastError());
// Send a request.
auto bResults =
WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpSendRequest() failed: %d", GetLastError());
// End the request.
bResults = WinHttpReceiveResponse(hRequest, NULL);
Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReceiveResponse() failed: %d", GetLastError());
std::vector<char> buf;
size_t total_downloaded_size = 0;
DWORD dwSize = 0;
do
{
DWORD downloaded_size = 0;
bResults = WinHttpQueryDataAvailable(hRequest, &dwSize);
Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpQueryDataAvailable() failed: %d", GetLastError());
if (buf.size() < dwSize) buf.resize(dwSize * 2);
bResults = WinHttpReadData(hRequest, (LPVOID)buf.data(), dwSize, &downloaded_size);
Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReadData() failed: %d", GetLastError());
fwrite(buf.data(), 1, downloaded_size, f);
total_downloaded_size += downloaded_size;
} while (dwSize > 0);
WinHttpCloseHandle(hSession);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hRequest);
fflush(f);
fclose(f);
}
#endif
static void download_file(Files::Filesystem& fs,
const std::string& url,
const fs::path& download_path,
const std::string& sha512)
{
Files::Filesystem& fs = paths.get_filesystem();
const std::string download_path_part = download_path.u8string() + ".part";
std::error_code ec;
fs.remove(download_path, ec);
fs.remove(download_path_part, ec);
#if defined(_WIN32)
auto url_no_proto = url.substr(8); // drop https://
auto path_begin = Util::find(url_no_proto, '/');
std::string hostname(url_no_proto.begin(), path_begin);
std::string path(path_begin, url_no_proto.end());
winhttp_download_file(fs, download_path_part.c_str(), hostname, path);
#else
const auto code = System::cmd_execute(
Strings::format(R"(curl -L '%s' --create-dirs --output '%s')", url, download_path_part));
Checks::check_exit(VCPKG_LINE_INFO, code == 0, "Could not download %s", url);
#endif
verify_hash(paths, url, download_path_part, sha512);
fs.rename(download_path_part, download_path);
verify_hash(fs, url, download_path_part, sha512);
fs.rename(download_path_part, download_path, ec);
Checks::check_exit(VCPKG_LINE_INFO,
!ec,
"Failed to do post-download rename-in-place.\nfs.rename(%s, %s, %s)",
download_path_part,
download_path.u8string(),
ec.message());
}
#endif
static fs::path fetch_tool(const VcpkgPaths& paths, const std::string& tool_name, const ToolData& tool_data)
{
const std::array<int, 3>& version = tool_data.version;
@ -280,50 +471,37 @@ namespace vcpkg::Commands::Fetch
version_as_string,
tool_name,
version_as_string);
#if defined(_WIN32)
const fs::path script = paths.scripts / "fetchtool.ps1";
const std::string title = Strings::format(
"Fetching %s version %s (No sufficient installed version was found)", tool_name, version_as_string);
const System::PowershellParameter tool_param("tool", tool_name);
const std::string output = System::powershell_execute_and_capture_output(title, script, {tool_param});
const std::vector<std::string> tool_path = keep_data_lines(output);
Checks::check_exit(VCPKG_LINE_INFO, tool_path.size() == 1, "Expected tool path, but got %s", output);
const fs::path actual_downloaded_path = Strings::trim(std::string{tool_path.at(0)});
const fs::path& expected_downloaded_path = tool_data.exe_path;
std::error_code ec;
const auto eq = fs::stdfs::equivalent(expected_downloaded_path, actual_downloaded_path, ec);
Checks::check_exit(VCPKG_LINE_INFO,
eq && !ec,
"Expected tool downloaded path to be %s, but was %s",
expected_downloaded_path.u8string(),
actual_downloaded_path.u8string());
return actual_downloaded_path;
#else
const auto& fs = paths.get_filesystem();
auto& fs = paths.get_filesystem();
if (!fs.exists(tool_data.download_path))
{
System::println("Downloading %s...", tool_name);
download_file(paths, tool_data.url, tool_data.download_path, tool_data.sha512);
download_file(fs, tool_data.url, tool_data.download_path, tool_data.sha512);
System::println("Downloading %s... done.", tool_name);
}
else
{
verify_hash(paths, tool_data.url, tool_data.download_path, tool_data.sha512);
verify_hash(fs, tool_data.url, tool_data.download_path, tool_data.sha512);
}
if (tool_data.is_archive)
{
System::println("Extracting %s...", tool_name);
extract_archive(paths, tool_data.download_path, tool_data.tool_dir_path);
System::println("Extracting %s... done.", tool_name);
}
else
{
std::error_code ec;
fs.create_directories(tool_data.exe_path.parent_path(), ec);
fs.rename(tool_data.download_path, tool_data.exe_path, ec);
}
Checks::check_exit(VCPKG_LINE_INFO,
fs.exists(tool_data.exe_path),
"Expected %s to exist after extracting",
tool_data.exe_path);
"Expected %s to exist after fetching",
tool_data.exe_path.u8string());
return tool_data.exe_path;
#endif
}
static fs::path get_cmake_path(const VcpkgPaths& paths)
@ -345,8 +523,8 @@ namespace vcpkg::Commands::Fetch
const auto& program_files_32_bit = System::get_program_files_32_bit();
if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
const Optional<fs::path> path = find_if_has_equal_or_greater_version(
paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
if (const auto p = path.get())
{
return *p;
@ -378,7 +556,8 @@ namespace vcpkg::Commands::Fetch
const std::vector<fs::path> from_path = Files::find_from_PATH("ninja");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
auto path = find_if_has_equal_or_greater_version(candidate_paths, "--version", TOOL_DATA.version);
auto path = find_if_has_equal_or_greater_version(
paths.get_filesystem(), candidate_paths, "--version", TOOL_DATA.version);
if (const auto p = path.get())
{
return *p;
@ -396,7 +575,8 @@ namespace vcpkg::Commands::Fetch
const std::vector<fs::path> from_path = Files::find_from_PATH("nuget");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
auto path = find_if_has_equal_or_greater_version(candidate_paths, "", TOOL_DATA.version);
auto path =
find_if_has_equal_or_greater_version(paths.get_filesystem(), candidate_paths, "", TOOL_DATA.version);
if (const auto p = path.get())
{
return *p;
@ -422,8 +602,8 @@ namespace vcpkg::Commands::Fetch
const auto& program_files_32_bit = System::get_program_files_32_bit();
if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
const Optional<fs::path> path = find_if_has_equal_or_greater_version(
paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
if (const auto p = path.get())
{
return *p;
@ -448,8 +628,8 @@ namespace vcpkg::Commands::Fetch
// candidate_paths.push_back(fs::path(System::get_environment_variable("HOMEDRIVE").value_or("C:")) / "Qt" /
// "QtIFW-3.1.0" / "bin" / "installerbase.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
const Optional<fs::path> path = find_if_has_equal_or_greater_version(
paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
if (const auto p = path.get())
{
return *p;
@ -460,48 +640,114 @@ namespace vcpkg::Commands::Fetch
struct VisualStudioInstance
{
enum class ReleaseType
{
STABLE,
PRERELEASE,
LEGACY
};
static bool prefered_first_comparator(const VisualStudioInstance& left, const VisualStudioInstance& right)
{
const auto get_preference_weight = [](const ReleaseType& type) -> int {
switch (type)
{
case ReleaseType::STABLE: return 3;
case ReleaseType::PRERELEASE: return 2;
case ReleaseType::LEGACY: return 1;
default: Checks::unreachable(VCPKG_LINE_INFO);
}
};
if (left.release_type != right.release_type)
{
return get_preference_weight(left.release_type) > get_preference_weight(right.release_type);
}
return left.version > right.version;
}
VisualStudioInstance(fs::path&& root_path, std::string&& version, const ReleaseType& release_type)
: root_path(std::move(root_path)), version(std::move(version)), release_type(release_type)
{
}
fs::path root_path;
std::string version;
std::string release_type;
std::string preference_weight; // Mostly unused, just for verification that order is as intended
ReleaseType release_type;
std::string major_version() const { return version.substr(0, 2); }
};
static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths)
{
const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1";
const std::string output =
System::powershell_execute_and_capture_output("Detecting Visual Studio instances", script);
const auto& fs = paths.get_filesystem();
const auto& program_files_32_bit = System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO);
const fs::path vswhere_exe = program_files_32_bit / "Microsoft Visual Studio" / "Installer" / "vswhere.exe";
Checks::check_exit(
VCPKG_LINE_INFO, fs.exists(vswhere_exe), "Could not locate vswhere at %s", vswhere_exe.u8string());
const auto code_and_output = System::cmd_execute_and_capture_output(
Strings::format(R"("%s" -prerelease -legacy -products * -format xml)", vswhere_exe.u8string()));
const std::vector<std::string> instances_as_strings = keep_data_lines(output);
Checks::check_exit(VCPKG_LINE_INFO,
!instances_as_strings.empty(),
"Could not detect any Visual Studio instances.\n"
"Powershell script:\n"
" %s\n"
"returned:\n"
"%s",
script.generic_string(),
output);
code_and_output.exit_code == 0,
"Running vswhere.exe failed with message:\n%s",
code_and_output.output);
const auto& xml_as_string = code_and_output.output;
const auto instance_entries = find_all_enclosed(xml_as_string, "<instance>", "</instance>");
std::vector<VisualStudioInstance> instances;
for (const std::string& instance_as_string : instances_as_strings)
for (const VcpkgStringRange& instance : instance_entries)
{
const std::vector<std::string> split = Strings::split(instance_as_string, "::");
Checks::check_exit(VCPKG_LINE_INFO,
split.size() == 4,
"Invalid Visual Studio instance format.\n"
"Expected: PreferenceWeight::ReleaseType::Version::PathToVisualStudio\n"
"Actual : %s\n",
instance_as_string);
instances.push_back({split.at(3), split.at(2), split.at(1), split.at(0)});
auto maybe_is_prerelease = find_at_most_one_enclosed(instance, "<isPrerelease>", "</isPrerelease>");
VisualStudioInstance::ReleaseType release_type = VisualStudioInstance::ReleaseType::LEGACY;
if (auto p = maybe_is_prerelease.get())
{
auto s = p->to_string();
if (s == "0")
release_type = VisualStudioInstance::ReleaseType::STABLE;
else if (s == "1")
release_type = VisualStudioInstance::ReleaseType::PRERELEASE;
else
Checks::unreachable(VCPKG_LINE_INFO);
}
instances.emplace_back(
find_exactly_one_enclosed(instance, "<installationPath>", "</installationPath>").to_string(),
find_exactly_one_enclosed(instance, "<installationVersion>", "</installationVersion>").to_string(),
release_type);
}
const auto append_if_has_cl = [&](fs::path&& path_root) {
const auto cl_exe = path_root / "VC" / "bin" / "cl.exe";
const auto vcvarsall_bat = path_root / "VC" / "vcvarsall.bat";
if (fs.exists(cl_exe) && fs.exists(vcvarsall_bat))
instances.emplace_back(std::move(path_root), "14.0", VisualStudioInstance::ReleaseType::LEGACY);
};
auto maybe_vs140comntools = System::get_environment_variable("vs140comntools");
if (const auto path_as_string = maybe_vs140comntools.get())
{
// We want lexically_normal(), but it is not available
// Correct root path might be 2 or 3 levels up, depending on if the path has trailing backslash. Try both.
auto common7_tools = fs::path{*path_as_string};
append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path());
append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path().parent_path());
}
append_if_has_cl(program_files_32_bit / "Microsoft Visual Studio 14.0");
return instances;
}
std::vector<Toolset> find_toolset_instances(const VcpkgPaths& paths)
#if defined(_WIN32)
std::vector<Toolset> find_toolset_instances_prefered_first(const VcpkgPaths& paths)
{
using CPU = System::CPUArchitecture;
@ -513,12 +759,14 @@ namespace vcpkg::Commands::Fetch
std::vector<Toolset> found_toolsets;
std::vector<Toolset> excluded_toolsets;
const std::vector<VisualStudioInstance> vs_instances = get_visual_studio_instances(paths);
const bool v140_is_available = Util::find_if(vs_instances, [&](const VisualStudioInstance& vs_instance) {
return vs_instance.major_version() == "14";
}) != vs_instances.cend();
const SortedVector<VisualStudioInstance> sorted{get_visual_studio_instances(paths),
VisualStudioInstance::prefered_first_comparator};
for (const VisualStudioInstance& vs_instance : vs_instances)
const bool v140_is_available = Util::find_if(sorted, [&](const VisualStudioInstance& vs_instance) {
return vs_instance.major_version() == "14";
}) != sorted.end();
for (const VisualStudioInstance& vs_instance : sorted)
{
const std::string major_version = vs_instance.major_version();
if (major_version == "15")
@ -672,6 +920,7 @@ namespace vcpkg::Commands::Fetch
return found_toolsets;
}
#endif
fs::path get_tool_path(const VcpkgPaths& paths, const std::string& tool)
{

View File

@ -154,10 +154,9 @@ namespace vcpkg::Commands::Hash
};
}
std::string get_file_hash(const VcpkgPaths& paths, const fs::path& path, const std::string& hash_type)
std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type)
{
Checks::check_exit(
VCPKG_LINE_INFO, paths.get_filesystem().exists(path), "File %s does not exist", path.u8string());
Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
return BCryptHasher{hash_type}.hash_file(path);
}
@ -239,7 +238,7 @@ namespace vcpkg::Commands::Hash
const fs::path file_to_hash = args.command_arguments[0];
const std::string algorithm = args.command_arguments.size() == 2 ? args.command_arguments[1] : "SHA512";
const std::string hash = get_file_hash(paths, file_to_hash, algorithm);
const std::string hash = get_file_hash(paths.get_filesystem(), file_to_hash, algorithm);
System::println(hash);
Checks::exit_success(VCPKG_LINE_INFO);
}

View File

@ -227,8 +227,6 @@ namespace vcpkg::Export
{fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"},
{fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"},
{fs::path{"scripts"} / "getWindowsSDK.ps1"},
{fs::path{"scripts"} / "getProgramFilesPlatformBitness.ps1"},
{fs::path{"scripts"} / "getProgramFiles32bit.ps1"},
{fs::path{"scripts"} / "VcpkgPowershellUtils.ps1"},
};

View File

@ -39,6 +39,7 @@ namespace vcpkg
paths.triplets = paths.root / "triplets";
paths.scripts = paths.root / "scripts";
paths.tools = paths.downloads / "tools";
paths.buildsystems = paths.scripts / "buildsystems";
paths.buildsystems_msbuild_targets = paths.buildsystems / "msbuild" / "vcpkg.targets";
@ -113,9 +114,11 @@ namespace vcpkg
return external_toolset;
}
// Invariant: toolsets are non-empty and sorted with newest at back()
#if !defined(_WIN32)
Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot build windows triplets from non-windows.");
#else
const std::vector<Toolset>& vs_toolsets =
this->toolsets.get_lazy([this]() { return Commands::Fetch::find_toolset_instances(*this); });
this->toolsets.get_lazy([this]() { return Commands::Fetch::find_toolset_instances_prefered_first(*this); });
std::vector<const Toolset*> candidates = Util::element_pointers(vs_toolsets);
const auto tsv = prebuildinfo.platform_toolset.get();
@ -159,6 +162,8 @@ namespace vcpkg
Checks::check_exit(VCPKG_LINE_INFO, !candidates.empty(), "No suitable Visual Studio instances were found");
return *candidates.front();
#endif
}
Files::Filesystem& VcpkgPaths::get_filesystem() const { return Files::get_real_filesystem(); }

View File

@ -15,11 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{F589
ProjectSection(SolutionItems) = preProject
..\scripts\bootstrap.ps1 = ..\scripts\bootstrap.ps1
..\scripts\fetchDependency.ps1 = ..\scripts\fetchDependency.ps1
..\scripts\findAnyMSBuildWithCppPlatformToolset.ps1 = ..\scripts\findAnyMSBuildWithCppPlatformToolset.ps1
..\scripts\findVisualStudioInstallationInstances.ps1 = ..\scripts\findVisualStudioInstallationInstances.ps1
..\scripts\getVisualStudioInstances.ps1 = ..\scripts\getVisualStudioInstances.ps1
..\scripts\get_triplet_environment.cmake = ..\scripts\get_triplet_environment.cmake
..\scripts\getProgramFiles32bit.ps1 = ..\scripts\getProgramFiles32bit.ps1
..\scripts\getProgramFilesPlatformBitness.ps1 = ..\scripts\getProgramFilesPlatformBitness.ps1
..\scripts\getWindowsSDK.ps1 = ..\scripts\getWindowsSDK.ps1
..\scripts\internalCI.ps1 = ..\scripts\internalCI.ps1
..\scripts\ports.cmake = ..\scripts\ports.cmake