diff --git a/.gitignore b/.gitignore index a6dea49..e49795d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ _ReSharper*/ *.DotSettings.user _UpgradeReport.* packages/ +[Rr]elease/ *.nupkg diff --git a/packaging/nuget/WebDav.Client.nuspec b/build/WebDav.Client.nuspec similarity index 57% rename from packaging/nuget/WebDav.Client.nuspec rename to build/WebDav.Client.nuspec index 3a8d13f..4386372 100644 --- a/packaging/nuget/WebDav.Client.nuspec +++ b/build/WebDav.Client.nuspec @@ -1,16 +1,16 @@ - + - $id$ - $version$ - $title$ - $author$ - $author$ + WebDav.Client + + WebDav.Client + skazantsev + skazantsev https://raw.githubusercontent.com/skazantsev/WebDavClient/master/LICENSE.txt https://github.com/skazantsev/WebDavClient false - $description$ - Copyright 2016 skazantsev + An easy-to-use async WebDAV client for .NET + Copyright © 2016 skazantsev WebDAV WebDAVClient WebDAV.Client diff --git a/build/build.ps1 b/build/build.ps1 new file mode 100644 index 0000000..d3c604e --- /dev/null +++ b/build/build.ps1 @@ -0,0 +1,67 @@ +properties { + $version = "1.0.4.0" + $signAssemblies = $false + $signKeyPath = "C:\snkey\skazantsev.snk" + + $baseDir = resolve-path .. + $srcDir = "$baseDir\src" + $slnFile = "$srcDir\WebDav.sln" + $releaseDir = "$baseDir\release" + $nugetPath = "$srcDir\.nuget\nuget.exe" + $xunitPath = "$srcDir\packages\xunit.runner.console.2.1.0\tools\xunit.console.exe" +} + +task default -depends Build, RunTests, CreateNuGetPackage + +task Build -depends RestorePackages { + Update-AssemblyInfoVersion $srcDir $version + + $constants = "TRACE" + if ($signAssemblies -eq $true) { + $constants += ";SIGNED" + } + exec { msbuild /nologo $slnFile "/t:Clean;Rebuild" /p:Configuration=Release /p:TargetFrameworkVersion=v4.5 /p:DefineConstants=`"$constants`" "/p:SignAssembly=$signAssemblies" "/p:AssemblyOriginatorKeyFile=$signKeyPath" } +} + +task RestorePackages { + exec { . $nugetPath restore $slnFile } +} + +task RunTests -depends Build { + exec {. $xunitPath "$srcDir\WebDav.Client.Tests\bin\Release\WebDav.Client.Tests.dll" } +} + +task CreateNuGetPackage -depends Build, RunTests { + mkdir "$releaseDir\lib\net45" -force | Out-Null + rm "$releaseDir\lib\net45\*" + cp "$srcDir\WebDav.Client\bin\Release\WebDav.Client.dll" "$releaseDir\lib\net45" + cp "$baseDir\build\WebDav.Client.nuspec" "$releaseDir" + + $nuspec = [xml](cat "$releaseDir\WebDav.Client.nuspec" -encoding UTF8) + $nuspec.package.metadata.version = $version + + write-host "Creating a nuget package with nuspec file:" + write-host $nuspec.OuterXml + + $nuspec.Save("$releaseDir\WebDav.Client.nuspec") + + exec { . $nugetPath pack "$releaseDir\WebDav.Client.nuspec" -BasePath "$releaseDir" -OutputDirectory "$releaseDir" } + + if ($signAssemblies -eq $true) { + write-host "Created signed package!" -foreground green + } else { + write-host "[WARN] Created package is not signed!" -foreground yellow + } +} + +function Update-AssemblyInfoVersion([string] $srcDir, [string] $version) { + get-childitem -path $srcDir -recurse -filter AssemblyInfo.cs | foreach { + $filename = $_.FullName + write-host "Setting $version version for $filename" + + (cat $filename -encoding UTF8) ` + -replace "AssemblyVersion\(""[0-9]+(\.([0-9]+|\*)){1,3}""\)", "AssemblyVersion(""$version"")" ` + -replace "AssemblyFileVersion\(""[0-9]+(\.([0-9]+|\*)){1,3}""\)", "AssemblyFileVersion(""$version"")" ` + | out-file $filename -encoding UTF8 + } +} \ No newline at end of file diff --git a/build/runbuild.ps1 b/build/runbuild.ps1 new file mode 100644 index 0000000..ebdefcc --- /dev/null +++ b/build/runbuild.ps1 @@ -0,0 +1,13 @@ +param( + [hashtable] $properties = @{} +) + +import-module ($PSScriptRoot + '\..\tools\psake-4.6.0\psake.psm1') + +try { + invoke-psake ($PSScriptRoot + '\build.ps1') -properties $properties +} +finally +{ + remove-module psake +} \ No newline at end of file diff --git a/src/WebDav.Client.Tests/Properties/AssemblyInfo.cs b/src/WebDav.Client.Tests/Properties/AssemblyInfo.cs index d55a57c..82294e7 100644 --- a/src/WebDav.Client.Tests/Properties/AssemblyInfo.cs +++ b/src/WebDav.Client.Tests/Properties/AssemblyInfo.cs @@ -7,5 +7,5 @@ [assembly: AssemblyCopyright("Copyright © 2015 skazantsev")] [assembly: ComVisible(false)] [assembly: Guid("429c1b04-3658-42f1-b673-2cb90b1096bc")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.4.0")] +[assembly: AssemblyFileVersion("1.0.4.0")] diff --git a/src/WebDav.Client.Tests/packages.config b/src/WebDav.Client.Tests/packages.config index fd0794d..b6b8a28 100644 --- a/src/WebDav.Client.Tests/packages.config +++ b/src/WebDav.Client.Tests/packages.config @@ -10,5 +10,6 @@ + \ No newline at end of file diff --git a/src/WebDav.Client/Properties/AssemblyInfo.cs b/src/WebDav.Client/Properties/AssemblyInfo.cs index 110f0b4..9cc8c72 100644 --- a/src/WebDav.Client/Properties/AssemblyInfo.cs +++ b/src/WebDav.Client/Properties/AssemblyInfo.cs @@ -9,8 +9,13 @@ [assembly: AssemblyCopyright("Copyright © 2016 skazantsev")] [assembly: ComVisible(false)] [assembly: Guid("0cc4b1b7-57f0-456a-8512-b8d69e34a550")] -[assembly: AssemblyVersion("1.0.3.0")] -[assembly: AssemblyFileVersion("1.0.3.0")] +[assembly: AssemblyVersion("1.0.4.0")] +[assembly: AssemblyFileVersion("1.0.4.0")] +#if SIGNED +[assembly: InternalsVisibleTo("WebDav.Client.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f130ca0ab17e20a726c71d1629f62002952816a0e1c5dc255118b348a8137f0e9c4b96f448d680a6fc72b30b1c28b7d8b19dca5c812f76230c51c8247e3ee687725635ec749a1f32bcf540913d9bd04d40cd834e9c1103cc4ec47669a0f6348a35fd11d1f935a39d8bfe9721e66f9996ccfa7e88cf38e1806e9cbc1b7256c9b6")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +#else [assembly: InternalsVisibleTo("WebDav.Client.Tests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] +#endif diff --git a/src/WebDav.ClientConsole/Properties/AssemblyInfo.cs b/src/WebDav.ClientConsole/Properties/AssemblyInfo.cs index bb7af28..d90f4b3 100644 --- a/src/WebDav.ClientConsole/Properties/AssemblyInfo.cs +++ b/src/WebDav.ClientConsole/Properties/AssemblyInfo.cs @@ -6,5 +6,5 @@ [assembly: AssemblyCopyright("Copyright © 2015 skazantsev")] [assembly: ComVisible(false)] [assembly: Guid("ac761356-7945-4fc2-bcc6-3a112b791ca2")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.0.4.0")] +[assembly: AssemblyFileVersion("1.0.4.0")] diff --git a/tools/psake-4.6.0/psake.psd1 b/tools/psake-4.6.0/psake.psd1 new file mode 100644 index 0000000..3d01672 --- /dev/null +++ b/tools/psake-4.6.0/psake.psd1 @@ -0,0 +1,31 @@ +@{ + ModuleToProcess = 'psake.psm1' + ModuleVersion = '4.6.0' + GUID = 'cfb53216-072f-4a46-8975-ff7e6bda05a5' + Author = 'James Kovacs' + Copyright = 'Copyright (c) 2012-16 James Kovacs, Damian Hickey and Contributors' + PowerShellVersion = '2.0' + Description = 'psake is a build automation tool written in PowerShell.' + FunctionsToExport = @('Invoke-psake', + 'Invoke-Task', + 'Get-PSakeScriptTasks', + 'Task', + 'Properties', + 'Include', + 'FormatTaskName', + 'TaskSetup', + 'TaskTearDown', + 'Framework', + 'Assert', + 'Exec') + VariablesToExport = 'psake' + + PrivateData = @{ + PSData = @{ + LicenseUri = 'https://github.com/psake/psake/blob/master/license.txt' + ProjectUri = 'https://github.com/psake/psake' + Tags = @('Build', 'Task') + IconUri = 'https://raw.githubusercontent.com/psake/graphics/master/png/psake-single-icon-teal-bg-256x256.png' + } + } +} diff --git a/tools/psake-4.6.0/psake.psm1 b/tools/psake-4.6.0/psake.psm1 new file mode 100644 index 0000000..e5f8f3c --- /dev/null +++ b/tools/psake-4.6.0/psake.psm1 @@ -0,0 +1,925 @@ +# psake +# Copyright (c) 2012 James Kovacs +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +#Requires -Version 2.0 + +if ($PSVersionTable.PSVersion.Major -ge 3) +{ + $script:IgnoreError = 'Ignore' +} +else +{ + $script:IgnoreError = 'SilentlyContinue' +} + +#-- Public Module Functions --# + +# .ExternalHelp psake.psm1-help.xml +function Invoke-Task +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)] [string]$taskName + ) + + Assert $taskName ($msgs.error_invalid_task_name) + + $taskKey = $taskName.ToLower() + + if ($currentContext.aliases.Contains($taskKey)) { + $taskName = $currentContext.aliases.$taskKey.Name + $taskKey = $taskName.ToLower() + } + + $currentContext = $psake.context.Peek() + + Assert ($currentContext.tasks.Contains($taskKey)) ($msgs.error_task_name_does_not_exist -f $taskName) + + if ($currentContext.executedTasks.Contains($taskKey)) { return } + + Assert (!$currentContext.callStack.Contains($taskKey)) ($msgs.error_circular_reference -f $taskName) + + $currentContext.callStack.Push($taskKey) + + $task = $currentContext.tasks.$taskKey + + $precondition_is_valid = & $task.Precondition + + if (!$precondition_is_valid) { + WriteColoredOutput ($msgs.precondition_was_false -f $taskName) -foregroundcolor Cyan + } else { + if ($taskKey -ne 'default') { + + if ($task.PreAction -or $task.PostAction) { + Assert ($task.Action -ne $null) ($msgs.error_missing_action_parameter -f $taskName) + } + + if ($task.Action) { + try { + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } + + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + $currentContext.currentTaskName = $taskName + + & $currentContext.taskSetupScriptBlock + + if ($task.PreAction) { + & $task.PreAction + } + + if ($currentContext.config.taskNameFormat -is [ScriptBlock]) { + & $currentContext.config.taskNameFormat $taskName + } else { + WriteColoredOutput ($currentContext.config.taskNameFormat -f $taskName) -foregroundcolor Cyan + } + + foreach ($variable in $task.requiredVariables) { + Assert ((test-path "variable:$variable") -and ((get-variable $variable).Value -ne $null)) ($msgs.required_variable_not_set -f $variable, $taskName) + } + + & $task.Action + + if ($task.PostAction) { + & $task.PostAction + } + + & $currentContext.taskTearDownScriptBlock + $task.Duration = $stopwatch.Elapsed + } catch { + if ($task.ContinueOnError) { + "-"*70 + WriteColoredOutput ($msgs.continue_on_error -f $taskName,$_) -foregroundcolor Yellow + "-"*70 + $task.Duration = $stopwatch.Elapsed + } else { + throw $_ + } + } + } else { + # no action was specified but we still execute all the dependencies + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } + } + } else { + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } + } + + Assert (& $task.Postcondition) ($msgs.postcondition_failed -f $taskName) + } + + $poppedTaskKey = $currentContext.callStack.Pop() + Assert ($poppedTaskKey -eq $taskKey) ($msgs.error_corrupt_callstack -f $taskKey,$poppedTaskKey) + + $currentContext.executedTasks.Push($taskKey) +} + +# .ExternalHelp psake.psm1-help.xml +function Exec +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd, + [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd), + [Parameter(Position=2,Mandatory=0)][int]$maxRetries = 0, + [Parameter(Position=3,Mandatory=0)][string]$retryTriggerErrorPattern = $null + ) + + $tryCount = 1 + + do { + try { + $global:lastexitcode = 0 + & $cmd + if ($lastexitcode -ne 0) { + throw ("Exec: " + $errorMessage) + } + break + } + catch [Exception] + { + if ($tryCount -gt $maxRetries) { + throw $_ + } + + if ($retryTriggerErrorPattern -ne $null) { + $isMatch = [regex]::IsMatch($_.Exception.Message, $retryTriggerErrorPattern) + + if ($isMatch -eq $false) { + throw $_ + } + } + + Write-Host "Try $tryCount failed, retrying again in 1 second..." + + $tryCount++ + + [System.Threading.Thread]::Sleep([System.TimeSpan]::FromSeconds(1)) + } + } + while ($true) +} + +# .ExternalHelp psake.psm1-help.xml +function Assert +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)]$conditionToCheck, + [Parameter(Position=1,Mandatory=1)]$failureMessage + ) + if (!$conditionToCheck) { + throw ("Assert: " + $failureMessage) + } +} + +# .ExternalHelp psake.psm1-help.xml +function Task +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$name = $null, + [Parameter(Position=1,Mandatory=0)][scriptblock]$action = $null, + [Parameter(Position=2,Mandatory=0)][scriptblock]$preaction = $null, + [Parameter(Position=3,Mandatory=0)][scriptblock]$postaction = $null, + [Parameter(Position=4,Mandatory=0)][scriptblock]$precondition = {$true}, + [Parameter(Position=5,Mandatory=0)][scriptblock]$postcondition = {$true}, + [Parameter(Position=6,Mandatory=0)][switch]$continueOnError = $false, + [Parameter(Position=7,Mandatory=0)][string[]]$depends = @(), + [Parameter(Position=8,Mandatory=0)][string[]]$requiredVariables = @(), + [Parameter(Position=9,Mandatory=0)][string]$description = $null, + [Parameter(Position=10,Mandatory=0)][string]$alias = $null + ) + if ($name -eq 'default') { + Assert (!$action) ($msgs.error_default_task_cannot_have_action) + } + + $newTask = @{ + Name = $name + DependsOn = $depends + PreAction = $preaction + Action = $action + PostAction = $postaction + Precondition = $precondition + Postcondition = $postcondition + ContinueOnError = $continueOnError + Description = $description + Duration = [System.TimeSpan]::Zero + RequiredVariables = $requiredVariables + Alias = $alias + } + + $taskKey = $name.ToLower() + + $currentContext = $psake.context.Peek() + + Assert (!$currentContext.tasks.ContainsKey($taskKey)) ($msgs.error_duplicate_task_name -f $name) + + $currentContext.tasks.$taskKey = $newTask + + if($alias) + { + $aliasKey = $alias.ToLower() + + Assert (!$currentContext.aliases.ContainsKey($aliasKey)) ($msgs.error_duplicate_alias_name -f $alias) + + $currentContext.aliases.$aliasKey = $newTask + } +} + +# .ExternalHelp psake.psm1-help.xml +function Properties { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$properties + ) + $psake.context.Peek().properties += $properties +} + +# .ExternalHelp psake.psm1-help.xml +function Include { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$fileNamePathToInclude + ) + Assert (test-path $fileNamePathToInclude -pathType Leaf) ($msgs.error_invalid_include_path -f $fileNamePathToInclude) + $psake.context.Peek().includes.Enqueue((Resolve-Path $fileNamePathToInclude)); +} + +# .ExternalHelp psake.psm1-help.xml +function FormatTaskName { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)]$format + ) + $psake.context.Peek().config.taskNameFormat = $format +} + +# .ExternalHelp psake.psm1-help.xml +function TaskSetup { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$setup + ) + $psake.context.Peek().taskSetupScriptBlock = $setup +} + +# .ExternalHelp psake.psm1-help.xml +function TaskTearDown { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$teardown + ) + $psake.context.Peek().taskTearDownScriptBlock = $teardown +} + +# .ExternalHelp psake.psm1-help.xml +function Framework { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$framework + ) + $psake.context.Peek().config.framework = $framework + ConfigureBuildEnvironment +} + +# .ExternalHelp psake.psm1-help.xml +function Get-PSakeScriptTasks { + [CmdletBinding()] + param( + [Parameter(Position = 0, Mandatory = 0)][string] $buildFile + ) + + if (!$buildFile) { + $buildFile = $psake.config_default.buildFileName + } + + try + { + ExecuteInBuildFileScope $buildFile $MyInvocation.MyCommand.Module { + param($currentContext, $module) + return GetTasksFromContext $currentContext + } + + } finally { + + CleanupEnvironment + } +} + +# .ExternalHelp psake.psm1-help.xml +function Invoke-psake { + [CmdletBinding()] + param( + [Parameter(Position = 0, Mandatory = 0)][string] $buildFile, + [Parameter(Position = 1, Mandatory = 0)][string[]] $taskList = @(), + [Parameter(Position = 2, Mandatory = 0)][string] $framework, + [Parameter(Position = 3, Mandatory = 0)][switch] $docs = $false, + [Parameter(Position = 4, Mandatory = 0)][hashtable] $parameters = @{}, + [Parameter(Position = 5, Mandatory = 0)][hashtable] $properties = @{}, + [Parameter(Position = 6, Mandatory = 0)][alias("init")][scriptblock] $initialization = {}, + [Parameter(Position = 7, Mandatory = 0)][switch] $nologo = $false, + [Parameter(Position = 8, Mandatory = 0)][switch] $detailedDocs = $false, + [Parameter(Position = 9, Mandatory = 0)][switch] $notr = $false # disable time report + ) + try { + if (-not $nologo) { + "psake version {0}`nCopyright (c) 2010-2014 James Kovacs & Contributors`n" -f $psake.version + } + + if (!$buildFile) { + $buildFile = $psake.config_default.buildFileName + } + elseif (!(test-path $buildFile -pathType Leaf) -and (test-path $psake.config_default.buildFileName -pathType Leaf)) { + # If the $config.buildFileName file exists and the given "buildfile" isn 't found assume that the given + # $buildFile is actually the target Tasks to execute in the $config.buildFileName script. + $taskList = $buildFile.Split(', ') + $buildFile = $psake.config_default.buildFileName + } + + ExecuteInBuildFileScope $buildFile $MyInvocation.MyCommand.Module { + param($currentContext, $module) + + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + if ($docs -or $detailedDocs) { + WriteDocumentation($detailedDocs) + return + } + + foreach ($key in $parameters.keys) { + if (test-path "variable:\$key") { + set-item -path "variable:\$key" -value $parameters.$key -WhatIf:$false -Confirm:$false | out-null + } else { + new-item -path "variable:\$key" -value $parameters.$key -WhatIf:$false -Confirm:$false | out-null + } + } + + # The initial dot (.) indicates that variables initialized/modified in the propertyBlock are available in the parent scope. + foreach ($propertyBlock in $currentContext.properties) { + . $propertyBlock + } + + foreach ($key in $properties.keys) { + if (test-path "variable:\$key") { + set-item -path "variable:\$key" -value $properties.$key -WhatIf:$false -Confirm:$false | out-null + } + } + + # Simple dot sourcing will not work. We have to force the script block into our + # module's scope in order to initialize variables properly. + . $module $initialization + + # Execute the list of tasks or the default task + if ($taskList) { + foreach ($task in $taskList) { + invoke-task $task + } + } elseif ($currentContext.tasks.default) { + invoke-task default + } else { + throw $msgs.error_no_default_task + } + + WriteColoredOutput ("`n" + $msgs.build_success + "`n") -foregroundcolor Green + + $stopwatch.Stop() + if (-not $notr) { + WriteTaskTimeSummary $stopwatch.Elapsed + } + } + + $psake.build_success = $true + + } catch { + $currentConfig = GetCurrentConfigurationOrDefault + if ($currentConfig.verboseError) { + $error_message = "{0}: An Error Occurred. See Error Details Below: `n" -f (Get-Date) + $error_message += ("-" * 70) + "`n" + $error_message += "Error: {0}`n" -f (ResolveError $_ -Short) + $error_message += ("-" * 70) + "`n" + $error_message += ResolveError $_ + $error_message += ("-" * 70) + "`n" + $error_message += "Script Variables" + "`n" + $error_message += ("-" * 70) + "`n" + $error_message += get-variable -scope script | format-table | out-string + } else { + # ($_ | Out-String) gets error messages with source information included. + $error_message = "Error: {0}: `n{1}" -f (Get-Date), (ResolveError $_ -Short) + } + + $psake.build_success = $false + + # if we are running in a nested scope (i.e. running a psake script from a psake script) then we need to re-throw the exception + # so that the parent script will fail otherwise the parent script will report a successful build + $inNestedScope = ($psake.context.count -gt 1) + if ( $inNestedScope ) { + throw $_ + } else { + if (!$psake.run_by_psake_build_tester) { + WriteColoredOutput $error_message -foregroundcolor Red + } + } + } finally { + CleanupEnvironment + } +} + +#-- Private Module Functions --# +function WriteColoredOutput { + param( + [string] $message, + [System.ConsoleColor] $foregroundcolor + ) + + $currentConfig = GetCurrentConfigurationOrDefault + if ($currentConfig.coloredOutput -eq $true) { + if (($Host.UI -ne $null) -and ($Host.UI.RawUI -ne $null) -and ($Host.UI.RawUI.ForegroundColor -ne $null)) { + $previousColor = $Host.UI.RawUI.ForegroundColor + $Host.UI.RawUI.ForegroundColor = $foregroundcolor + } + } + + $message + + if ($previousColor -ne $null) { + $Host.UI.RawUI.ForegroundColor = $previousColor + } +} + +function LoadModules { + $currentConfig = $psake.context.peek().config + if ($currentConfig.modules) { + + $scope = $currentConfig.moduleScope + + $global = [string]::Equals($scope, "global", [StringComparison]::CurrentCultureIgnoreCase) + + $currentConfig.modules | foreach { + resolve-path $_ | foreach { + "Loading module: $_" + $module = import-module $_ -passthru -DisableNameChecking -global:$global + if (!$module) { + throw ($msgs.error_loading_module -f $_.Name) + } + } + } + "" + } +} + +function LoadConfiguration { + param( + [string] $configdir = $PSScriptRoot + ) + + $psakeConfigFilePath = (join-path $configdir "psake-config.ps1") + + if (test-path $psakeConfigFilePath -pathType Leaf) { + try { + $config = GetCurrentConfigurationOrDefault + . $psakeConfigFilePath + } catch { + throw "Error Loading Configuration from psake-config.ps1: " + $_ + } + } +} + +function GetCurrentConfigurationOrDefault() { + if ($psake.context.count -gt 0) { + return $psake.context.peek().config + } else { + return $psake.config_default + } +} + +function CreateConfigurationForNewContext { + param( + [string] $buildFile, + [string] $framework + ) + + $previousConfig = GetCurrentConfigurationOrDefault + + $config = new-object psobject -property @{ + buildFileName = $previousConfig.buildFileName; + framework = $previousConfig.framework; + taskNameFormat = $previousConfig.taskNameFormat; + verboseError = $previousConfig.verboseError; + coloredOutput = $previousConfig.coloredOutput; + modules = $previousConfig.modules; + moduleScope = $previousConfig.moduleScope; + } + + if ($framework) { + $config.framework = $framework; + } + + if ($buildFile) { + $config.buildFileName = $buildFile; + } + + return $config +} + +function ConfigureBuildEnvironment { + $framework = $psake.context.peek().config.framework + if ($framework -cmatch '^((?:\d+\.\d+)(?:\.\d+){0,1})(x86|x64){0,1}$') { + $versionPart = $matches[1] + $bitnessPart = $matches[2] + } else { + throw ($msgs.error_invalid_framework -f $framework) + } + $versions = $null + $buildToolsVersions = $null + switch ($versionPart) { + '1.0' { + $versions = @('v1.0.3705') + } + '1.1' { + $versions = @('v1.1.4322') + } + '2.0' { + $versions = @('v2.0.50727') + } + '3.0' { + $versions = @('v2.0.50727') + } + '3.5' { + $versions = @('v3.5', 'v2.0.50727') + } + '4.0' { + $versions = @('v4.0.30319') + } + {($_ -eq '4.5.1') -or ($_ -eq '4.5.2')} { + $versions = @('v4.0.30319') + $buildToolsVersions = @('14.0', '12.0') + } + {($_ -eq '4.6') -or ($_ -eq '4.6.1')} { + $versions = @('v4.0.30319') + $buildToolsVersions = @('14.0') + } + + default { + throw ($msgs.error_unknown_framework -f $versionPart, $framework) + } + } + + $bitness = 'Framework' + if ($versionPart -ne '1.0' -and $versionPart -ne '1.1') { + switch ($bitnessPart) { + 'x86' { + $bitness = 'Framework' + $buildToolsKey = 'MSBuildToolsPath32' + } + 'x64' { + $bitness = 'Framework64' + $buildToolsKey = 'MSBuildToolsPath' + } + { [string]::IsNullOrEmpty($_) } { + $ptrSize = [System.IntPtr]::Size + switch ($ptrSize) { + 4 { + $bitness = 'Framework' + $buildToolsKey = 'MSBuildToolsPath32' + } + 8 { + $bitness = 'Framework64' + $buildToolsKey = 'MSBuildToolsPath' + } + default { + throw ($msgs.error_unknown_pointersize -f $ptrSize) + } + } + } + default { + throw ($msgs.error_unknown_bitnesspart -f $bitnessPart, $framework) + } + } + } + $frameworkDirs = @() + if ($buildToolsVersions -ne $null) { + foreach($ver in $buildToolsVersions) { + if (Test-Path "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\$ver") { + $frameworkDirs += (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\$ver" -Name $buildToolsKey).$buildToolsKey + } + } + } + $frameworkDirs = $frameworkDirs + @($versions | foreach { "$env:windir\Microsoft.NET\$bitness\$_\" }) + + for ($i = 0; $i -lt $frameworkDirs.Count; $i++) { + $dir = $frameworkDirs[$i] + if ($dir -Match "\$\(Registry:HKEY_LOCAL_MACHINE(.*?)@(.*)\)") { + $key = "HKLM:" + $matches[1] + $name = $matches[2] + $dir = (Get-ItemProperty -Path $key -Name $name).$name + $frameworkDirs[$i] = $dir + } + } + + $frameworkDirs | foreach { Assert (test-path $_ -pathType Container) ($msgs.error_no_framework_install_dir_found -f $_)} + + $env:path = ($frameworkDirs -join ";") + ";$env:path" + # if any error occurs in a PS function then "stop" processing immediately + # this does not effect any external programs that return a non-zero exit code + $global:ErrorActionPreference = "Stop" +} + +function ExecuteInBuildFileScope { + param([string]$buildFile, $module, [scriptblock]$sb) + + # Execute the build file to set up the tasks and defaults + Assert (test-path $buildFile -pathType Leaf) ($msgs.error_build_file_not_found -f $buildFile) + + $psake.build_script_file = get-item $buildFile + $psake.build_script_dir = $psake.build_script_file.DirectoryName + $psake.build_success = $false + + $psake.context.push(@{ + "taskSetupScriptBlock" = {}; + "taskTearDownScriptBlock" = {}; + "executedTasks" = new-object System.Collections.Stack; + "callStack" = new-object System.Collections.Stack; + "originalEnvPath" = $env:path; + "originalDirectory" = get-location; + "originalErrorActionPreference" = $global:ErrorActionPreference; + "tasks" = @{}; + "aliases" = @{}; + "properties" = @(); + "includes" = new-object System.Collections.Queue; + "config" = CreateConfigurationForNewContext $buildFile $framework + }) + + LoadConfiguration $psake.build_script_dir + + set-location $psake.build_script_dir + + LoadModules + + $frameworkOldValue = $framework + . $psake.build_script_file.FullName + + $currentContext = $psake.context.Peek() + + if ($framework -ne $frameworkOldValue) { + writecoloredoutput $msgs.warning_deprecated_framework_variable -foregroundcolor Yellow + $currentContext.config.framework = $framework + } + + ConfigureBuildEnvironment + + while ($currentContext.includes.Count -gt 0) { + $includeFilename = $currentContext.includes.Dequeue() + . $includeFilename + } + + & $sb $currentContext $module +} + +function CleanupEnvironment { + if ($psake.context.Count -gt 0) { + $currentContext = $psake.context.Peek() + $env:path = $currentContext.originalEnvPath + Set-Location $currentContext.originalDirectory + $global:ErrorActionPreference = $currentContext.originalErrorActionPreference + [void] $psake.context.Pop() + } +} + +function SelectObjectWithDefault +{ + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true)] + [PSObject] + $InputObject, + [string] + $Name, + $Value + ) + + process { + if ($_ -eq $null) { $Value } + elseif ($_ | Get-Member -Name $Name) { + $_.$Name + } + elseif (($_ -is [Hashtable]) -and ($_.Keys -contains $Name)) { + $_.$Name + } + else { $Value } + } +} + +# borrowed from Jeffrey Snover http://blogs.msdn.com/powershell/archive/2006/12/07/resolve-error.aspx +# modified to better handle SQL errors +function ResolveError +{ + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true)] + $ErrorRecord=$Error[0], + [Switch] + $Short + ) + + process { + if ($_ -eq $null) { $_ = $ErrorRecord } + $ex = $_.Exception + + if (-not $Short) { + $error_message = "`nErrorRecord:{0}ErrorRecord.InvocationInfo:{1}Exception:`n{2}" + $formatted_errorRecord = $_ | format-list * -force | out-string + $formatted_invocationInfo = $_.InvocationInfo | format-list * -force | out-string + $formatted_exception = '' + + $i = 0 + while ($ex -ne $null) { + $i++ + $formatted_exception += ("$i" * 70) + "`n" + + ($ex | format-list * -force | out-string) + "`n" + $ex = $ex | SelectObjectWithDefault -Name 'InnerException' -Value $null + } + + return $error_message -f $formatted_errorRecord, $formatted_invocationInfo, $formatted_exception + } + + $lastException = @() + while ($ex -ne $null) { + $lastMessage = $ex | SelectObjectWithDefault -Name 'Message' -Value '' + $lastException += ($lastMessage -replace "`n", '') + if ($ex -is [Data.SqlClient.SqlException]) { + $lastException += "(Line [$($ex.LineNumber)] " + + "Procedure [$($ex.Procedure)] Class [$($ex.Class)] " + + " Number [$($ex.Number)] State [$($ex.State)] )" + } + $ex = $ex | SelectObjectWithDefault -Name 'InnerException' -Value $null + } + $shortException = $lastException -join ' --> ' + + $header = $null + $current = $_ + $header = (($_.InvocationInfo | + SelectObjectWithDefault -Name 'PositionMessage' -Value '') -replace "`n", ' '), + ($_ | SelectObjectWithDefault -Name 'Message' -Value ''), + ($_ | SelectObjectWithDefault -Name 'Exception' -Value '') | + ? { -not [String]::IsNullOrEmpty($_) } | + Select -First 1 + + $delimiter = '' + if ((-not [String]::IsNullOrEmpty($header)) -and + (-not [String]::IsNullOrEmpty($shortException))) + { $delimiter = ' [<<==>>] ' } + + return "$($header)$($delimiter)Exception: $($shortException)" + } +} + +function GetTasksFromContext($currentContext) { + + $docs = $currentContext.tasks.Keys | foreach-object { + + $task = $currentContext.tasks.$_ + new-object PSObject -property @{ + Name = $task.Name; + Alias = $task.Alias; + Description = $task.Description; + DependsOn = $task.DependsOn; + } + } + + return $docs +} + +function WriteDocumentation($showDetailed) { + + $currentContext = $psake.context.Peek() + + if ($currentContext.tasks.default) { + $defaultTaskDependencies = $currentContext.tasks.default.DependsOn + } else { + $defaultTaskDependencies = @() + } + + $docs = GetTasksFromContext $currentContext | + Where {$_.Name -ne 'default'} | + ForEach { + $isDefault = $null + if ($defaultTaskDependencies -contains $_.Name) { + $isDefault = $true + } + return Add-Member -InputObject $_ 'Default' $isDefault -PassThru + } + + if ($showDetailed) { + $docs | sort 'Name' | format-list -property Name,Alias,Description,@{Label="Depends On";Expression={$_.DependsOn -join ', '}},Default + } else { + $docs | sort 'Name' | format-table -autoSize -wrap -property Name,Alias,@{Label="Depends On";Expression={$_.DependsOn -join ', '}},Default,Description + } +} + +function WriteTaskTimeSummary($invokePsakeDuration) { + if ($psake.context.count -gt 0) { + "-" * 70 + "Build Time Report" + "-" * 70 + $list = @() + $currentContext = $psake.context.Peek() + while ($currentContext.executedTasks.Count -gt 0) { + $taskKey = $currentContext.executedTasks.Pop() + $task = $currentContext.tasks.$taskKey + if ($taskKey -eq "default") { + continue + } + $list += new-object PSObject -property @{ + Name = $task.Name; + Duration = $task.Duration + } + } + [Array]::Reverse($list) + $list += new-object PSObject -property @{ + Name = "Total:"; + Duration = $invokePsakeDuration + } + # using "out-string | where-object" to filter out the blank line that format-table prepends + $list | format-table -autoSize -property Name,Duration | out-string -stream | where-object { $_ } + } +} + +DATA msgs { +convertfrom-stringdata @' + error_invalid_task_name = Task name should not be null or empty string. + error_task_name_does_not_exist = Task {0} does not exist. + error_circular_reference = Circular reference found for task {0}. + error_missing_action_parameter = Action parameter must be specified when using PreAction or PostAction parameters for task {0}. + error_corrupt_callstack = Call stack was corrupt. Expected {0}, but got {1}. + error_invalid_framework = Invalid .NET Framework version, {0} specified. + error_unknown_framework = Unknown .NET Framework version, {0} specified in {1}. + error_unknown_pointersize = Unknown pointer size ({0}) returned from System.IntPtr. + error_unknown_bitnesspart = Unknown .NET Framework bitness, {0}, specified in {1}. + error_no_framework_install_dir_found = No .NET Framework installation directory found at {0}. + error_bad_command = Error executing command {0}. + error_default_task_cannot_have_action = 'default' task cannot specify an action. + error_duplicate_task_name = Task {0} has already been defined. + error_duplicate_alias_name = Alias {0} has already been defined. + error_invalid_include_path = Unable to include {0}. File not found. + error_build_file_not_found = Could not find the build file {0}. + error_no_default_task = 'default' task required. + error_loading_module = Error loading module {0}. + warning_deprecated_framework_variable = Warning: Using global variable $framework to set .NET framework version used is deprecated. Instead use Framework function or configuration file psake-config.ps1. + required_variable_not_set = Variable {0} must be set to run task {1}. + postcondition_failed = Postcondition failed for task {0}. + precondition_was_false = Precondition was false, not executing task {0}. + continue_on_error = Error in task {0}. {1} + build_success = Build Succeeded! +'@ +} + +Import-LocalizedData -BindingVariable msgs -FileName messages.psd1 -ErrorAction $script:IgnoreError + +$scriptDir = Split-Path $MyInvocation.MyCommand.Path +$manifestPath = Join-Path $scriptDir psake.psd1 +$manifest = Test-ModuleManifest -Path $manifestPath -WarningAction SilentlyContinue + +$script:psake = @{} + +$psake.version = $manifest.Version.ToString() +$psake.context = new-object system.collections.stack # holds onto the current state of all variables +$psake.run_by_psake_build_tester = $false # indicates that build is being run by psake-BuildTester +$psake.config_default = new-object psobject -property @{ + buildFileName = "default.ps1"; + framework = "4.0"; + taskNameFormat = "Executing {0}"; + verboseError = $false; + coloredOutput = $true; + modules = $null; + moduleScope = ""; +} # contains default configuration, can be overriden in psake-config.ps1 in directory with psake.psm1 or in directory with current build script + +$psake.build_success = $false # indicates that the current build was successful +$psake.build_script_file = $null # contains a System.IO.FileInfo for the current build script +$psake.build_script_dir = "" # contains a string with fully-qualified path to current build script + +LoadConfiguration + +export-modulemember -function Invoke-psake, Invoke-Task, Get-PSakeScriptTasks, Task, Properties, Include, FormatTaskName, TaskSetup, TaskTearDown, Framework, Assert, Exec -variable psake