| # 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 |