openclonk/tools/ci/appv-AfterBuild.ps1

200 lines
8.3 KiB
PowerShell

pushd $env:BUILD_TARGET_FOLDER
trap {popd}
$ErrorActionPreference = 'Stop'
cmd /D /Q /C "`"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\vsdevcmd.bat`" -arch=${env:PLATFORM} > NUL && SET" | %{
$k, $v = $_.Split('=', 2)
[System.Environment]::SetEnvironmentVariable($k, $v)
}
Add-Type -Path "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\Microsoft.Build.dll"
$projects = New-Object Microsoft.Build.Evaluation.ProjectCollection
$projects.SetGlobalProperty('Configuration', $env:CONFIGURATION)
function Resolve-CanonicalPath {
param([string]$Path)
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public static class ResolveCanonicalPath {
[DllImport("kernel32.dll", EntryPoint="CreateFileW", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
int dwDesiredAccess,
int dwShareMode,
IntPtr securityAttributes,
int dwCreationDisposition,
int dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", EntryPoint="GetFinalPathNameByHandleW", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern int GetFinalPathNameByHandle(
IntPtr hFile,
[In, Out] System.Text.StringBuilder lpszFilePath,
int cchFilePath,
int dwFlags);
}
'@
# Ask the file system to provide us with the canonical path, which includes
# proper capitalization
$fh = [ResolveCanonicalPath]::CreateFile($Path, 0, 2, [System.IntPtr]::Zero, 3, 0x2000000, [System.IntPtr]::Zero)
try {
$real_path = New-Object System.Text.StringBuilder 512
$rp_len = [ResolveCanonicalPath]::GetFinalPathNameByHandle($fh.DangerousGetHandle(), $real_path, $real_path.Capacity, 0)
if ($rp_len -lt 0) {
throw [System.ComponentModel.Win32Exception]::new([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())
}
$real_path = $real_path.ToString()
if ($real_path.StartsWith('\\?\')) {
return $real_path.Substring(4)
}
return $real_path
} finally {
$fh.Close()
}
}
function Add-SourceServerData {
param([string]$pdb)
$srctool = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\srcsrv\srctool.exe"
$pdbstr = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\srcsrv\pdbstr.exe"
$temp_name = New-TemporaryFile
try {
$temp = New-Object System.IO.StreamWriter $temp_name.OpenWrite()
$temp.WriteLine(@"
SRCSRV: ini ------------------------------------------------
VERSION=2
VERCTRL=http
SRCSRV: variables ------------------------------------------
DEPOT=https://raw.githubusercontent.com/$env:APPVEYOR_REPO_NAME/%COMMIT%/
COMMIT=$env:APPVEYOR_REPO_COMMIT
SRCSRVTRG=%DEPOT%%var2%
SRCSRV: source files ---------------------------------------
"@)
& $srctool -r $pdb | %{
Resolve-CanonicalPath $_
} | ?{
# Filter everything outside of the source path
$_.StartsWith($env:APPVEYOR_BUILD_FOLDER, [System.StringComparison]::OrdinalIgnoreCase)
} | ?{
# Filter everything that doesn't exist
Test-Path $_
} | %{
# Strip source folder prefix for building an URL
$path = $_.Substring($env:APPVEYOR_BUILD_FOLDER.Length)
$path = $path.TrimStart(@([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar))
$path = $path.Replace('\','/')
$temp.WriteLine("$_*$path")
}
$temp.WriteLine('SRCSRV: end ------------------------------------------------')
$temp.Close()
# Write source info to PDB
& $pdbstr -w -p:$pdb -i:$($temp_name.FullName) -s:srcsrv
} finally {
$temp_name.Delete()
}
}
if (-not $env:APPVEYOR) {
function appveyor {
param ([string]$action, [string[]]$rest)
}
}
# Add source server information to all debug symbols and push the
# binaries/symbols to AppVeyor
Get-Item *.vcxproj | %{
$p = $projects.LoadProject($_.FullName)
if ($p.GetPropertyValue('ConfigurationType') -eq 'Application') {
# For all executable files
$binary = $p.GetPropertyValue('TargetPath')
if (Test-Path $binary) {
# Upload the executable itself as an artifact
appveyor PushArtifact $binary
$pdb = $p.ItemDefinitions['Link'].GetMetadataValue('ProgramDataBaseFile')
if (Test-Path $pdb) {
# If we generated a .pdb file, add source server information
Add-SourceServerData $pdb
appveyor PushArtifact $pdb
}
}
}
}
if ($env:DEPLOYMENT_URL -and $env:DEPLOYMENT_SECRET) {
# Build a zip file with the main executable and its dependencies
$deploy_dir = "${env:BUILD_TARGET_FOLDER}\deployment"
[void](mkdir $deploy_dir)
$openclonk = $projects.LoadedProjects | ?{ $_.GetPropertyValue('ProjectName') -eq 'openclonk' }
$c4group = $projects.LoadedProjects | ?{ $_.GetPropertyValue('ProjectName') -eq 'c4group' }
$groups = $projects.LoadedProjects | ?{ $_.GetPropertyValue('ProjectName') -eq 'groups' }
if ($openclonk -and $c4group -and $groups) {
$groupFiles = ($groups.GetItems('CustomBuild') |
?{ $_.GetMetadataValue('AdditionalInputs') -split ';' -contains $c4group.GetPropertyValue('TargetPath') }).
GetMetadataValue('Outputs')
}
# Copy executable, Qt
C:\Qt\5.11.1\msvc2017_64\bin\windeployqt.exe --no-translations --no-compiler-runtime --dir $deploy_dir $openclonk.GetPropertyValue('TargetPath')
Copy-Item $openclonk.GetPropertyValue('TargetPath') "${deploy_dir}\"
# Copy other DLLs openclonk depends on
function Get-Imports {
param([string]$file)
(dumpbin /imports $file | ?{ $_ -match ' {4}\w+\.dll'}).Trim()
}
$unresolved = [System.Collections.Queue]::new()
Get-Imports $openclonk.GetPropertyValue('TargetPath') | %{ $unresolved.Enqueue($_) }
while ($unresolved.Count -gt 0) {
$library = $unresolved.Dequeue()
$file = Join-Path $deploy_dir $library
$dep_file = "${env:BUILD_DEPS_FOLDER}\${env:PLATFORM}\bin\${library}"
if (-not (Test-Path $file) -and (Test-Path $dep_file)) {
Write-Host "Bundling ${library}"
Copy-Item $dep_file $file
Get-Imports $file | %{ $unresolved.Enqueue($_) }
}
}
# Copy generated group files
$groupFiles | %{ Copy-Item $_ $deploy_dir }
# Create archive
$archive_name = "OpenClonk-win-${env:PLATFORM}.zip"
7z a -mx=9 -y -r -- $archive_name "${deploy_dir}\*"
$deployment_user, $deployment_password = $env:DEPLOYMENT_SECRET.Split(':', 2)
$timestamp = Get-Date -Date (Get-Date $env:APPVEYOR_REPO_COMMIT_TIMESTAMP).ToUniversalTime() -UFormat '%Y-%m-%dT%H:%M:%SZ'
$deployment_url = [System.UriBuilder]::new("${env:DEPLOYMENT_URL}${timestamp}-${env:APPVEYOR_REPO_BRANCH}-$($env:APPVEYOR_REPO_COMMIT.Substring(0, 9))/${archive_name}")
$deployment_url.UserName = $deployment_user
$deployment_url.Password = $deployment_password
$deployment_auth = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("${deployment_user}:${deployment_password}"))
[System.Net.ServicePointManager]::SecurityProtocol = 'Tls12'
[System.Net.HttpWebRequest]$req = [System.Net.WebRequest]::CreateHttp($deployment_url.Uri)
$deployment_url.Password = '********'
$req.Timeout = 30 * 60 * 1000
$req.Method = 'POST'
$req.ContentType = 'application/octet-stream'
$req.AllowWriteStreamBuffering = $false
$req.SendChunked = $true
$req.Headers.Add('Authorization', "Basic ${deployment_auth}")
$fileStream = [System.IO.File]::OpenRead((Resolve-Path $archive_name))
Write-Host "Uploading to $($deployment_url.Uri)"
$reqStream = $req.GetRequestStream()
$fileStream.CopyTo($reqStream)
$fileStream.Close()
$reqStream.Close()
try {
[System.Net.HttpWebResponse]$resp = $req.GetResponse()
Write-Host "Upload successful: $([uint32]$resp.StatusCode) $($resp.StatusDescription)"
} catch [System.Net.WebException] {
Write-Host "Upload failed: $($_.Exception.Status) $($_.Exception.Response.StatusDescription)"
}
}