diff --git a/build/Microsoft.Windows.Toolkit.Notifications.JavaScript.nuspec b/build/Microsoft.Windows.Toolkit.Notifications.JavaScript.nuspec new file mode 100644 index 000000000..1ed8f8cf3 --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.Notifications.JavaScript.nuspec @@ -0,0 +1,36 @@ + + + + Microsoft.Windows.Toolkit.Notifications.JavaScript + 0.0.0 + Microsoft + microsoft, andrewbares + https://github.com/anbare/NotificationsExtensions/wiki + false + Generate tile, toast, and badge notifications for Windows 10 via code, with the help of IntelliSense, instead of directly using XML. + The official NotificationsExtensions library from Microsoft. Supports JavaScript UWP projects (see NotificationsExtensions.Win10 for the C#/C++ version). + +Generate tile, toast, and badge notifications for Windows 10 via code, with the help of IntelliSense, instead of directly using XML. + +Supports adaptive tiles and adaptive/interactive toasts for Windows 10! + © Microsoft Corporation. All rights reserved. + notifications, win10, windows 10, tile, tiles, toast, toasts, badge, xml, uwp, javascript + https://raw.githubusercontent.com/anbare/NotificationsExtensions/master/LICENSE + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/Microsoft.Windows.Toolkit.Notifications.JavaScript.targets b/build/Microsoft.Windows.Toolkit.Notifications.JavaScript.targets new file mode 100644 index 000000000..d6795fcaa --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.Notifications.JavaScript.targets @@ -0,0 +1,11 @@ + + + + + + $(MSBuildThisFileDirectory)..\..\lib\Windows\Microsoft.Windows.Toolkit.Notifications.winmd + true + + + + diff --git a/build/Microsoft.Windows.Toolkit.Notifications.native.targets b/build/Microsoft.Windows.Toolkit.Notifications.native.targets new file mode 100644 index 000000000..909226547 --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.Notifications.native.targets @@ -0,0 +1,11 @@ + + + + + + $(MSBuildThisFileDirectory)..\..\lib\native\Microsoft.Windows.Toolkit.Notifications.winmd + true + + + + diff --git a/build/Microsoft.Windows.Toolkit.Notifications.nuspec b/build/Microsoft.Windows.Toolkit.Notifications.nuspec new file mode 100644 index 000000000..6fa0ecbc5 --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.Notifications.nuspec @@ -0,0 +1,51 @@ + + + + Microsoft.Windows.Toolkit.Notifications + 0.0.0 + Microsoft + microsoft, andrewbares + https://github.com/anbare/NotificationsExtensions/wiki + false + Generate tile, toast, and badge notifications for Windows 10 via code, with the help of IntelliSense, instead of directly using XML. + The official NotificationsExtensions library from Microsoft. C# and C++ UWP project types (see NotificationsExtensions.Win10.JavaScript for the JS version). Also works with C# portable class libraries and non-UWP C# projects like server projects. + +Generate tile, toast, and badge notifications for Windows 10 via code, with the help of IntelliSense, instead of directly using XML. + +Supports adaptive tiles and adaptive/interactive toasts for Windows 10! + © Microsoft Corporation. All rights reserved. + notifications, win10, windows 10, tile, tiles, toast, toasts, badge, xml, uwp, c#, csharp, c++ + https://raw.githubusercontent.com/anbare/NotificationsExtensions/master/LICENSE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/Microsoft.Windows.Toolkit.Services.nuspec b/build/Microsoft.Windows.Toolkit.Services.nuspec new file mode 100644 index 000000000..c23197f8d --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.Services.nuspec @@ -0,0 +1,26 @@ + + + + Microsoft.Windows.Toolkit.Services + 0.5.0 + UWP Toolkit Services + Microsoft Corp + Microsoft Corp + https://github.com/deltakosh/UWPToolkit + https://github.com/deltakosh/UWPToolkit + false + This library enable access to diferent data source like Bing, Facebook, and Twitter. + Initial internal release of core data services. + Copyright 2016 + UWP Toolkit Windows AppStudio Net Portable Twitter OAuth RSS Facebook Bing + + + + + + + + + + + diff --git a/build/Microsoft.Windows.Toolkit.UI.Animations.nuspec b/build/Microsoft.Windows.Toolkit.UI.Animations.nuspec new file mode 100644 index 000000000..a9537bf15 --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.UI.Animations.nuspec @@ -0,0 +1,24 @@ + + + + Microsoft.Windows.Toolkit.UI.Animations + 0.0.0 + UWP Toolkit Services + Microsoft Corp + Microsoft Corp + https://github.com/deltakosh/UWPToolkit + https://github.com/deltakosh/UWPToolkit + false + This library enable access to diferent data source like Bing, Facebook, and Twitter. + Initial internal release of core data services. + Copyright 2016 + UWP Toolkit Windows AppStudio Net Portable Twitter OAuth RSS Facebook Bing + + + + + + + + + diff --git a/build/Microsoft.Windows.Toolkit.UI.Controls.nuspec b/build/Microsoft.Windows.Toolkit.UI.Controls.nuspec new file mode 100644 index 000000000..fc2c6017d --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.UI.Controls.nuspec @@ -0,0 +1,25 @@ + + + + Microsoft.Windows.Toolkit.UI.Controls + 0.0.0 + UWP Toolkit Services + Microsoft Corp + Microsoft Corp + https://github.com/deltakosh/UWPToolkit + https://github.com/deltakosh/UWPToolkit + false + This library enable access to diferent data source like Bing, Facebook, and Twitter. + Initial internal release of core data services. + Copyright 2016 + UWP Toolkit Windows AppStudio Net Portable Twitter OAuth RSS Facebook Bing + + + + + + + + + + diff --git a/build/Microsoft.Windows.Toolkit.UI.nuspec b/build/Microsoft.Windows.Toolkit.UI.nuspec new file mode 100644 index 000000000..7f2f6e3e4 --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.UI.nuspec @@ -0,0 +1,23 @@ + + + + Microsoft.Windows.Toolkit.UI + 0.0.0 + UWP Toolkit Services + Microsoft Corp + Microsoft Corp + https://github.com/deltakosh/UWPToolkit + https://github.com/deltakosh/UWPToolkit + false + This library enable access to diferent data source like Bing, Facebook, and Twitter. + Initial internal release of core data services. + Copyright 2016 + UWP Toolkit Windows AppStudio Net Portable Twitter OAuth RSS Facebook Bing + + + + + + + + diff --git a/build/Microsoft.Windows.Toolkit.nuspec b/build/Microsoft.Windows.Toolkit.nuspec new file mode 100644 index 000000000..84529a832 --- /dev/null +++ b/build/Microsoft.Windows.Toolkit.nuspec @@ -0,0 +1,20 @@ + + + + Microsoft.Windows.Toolkit + 0.0.0 + UWP Toolkit Services + Microsoft Corp + Microsoft Corp + https://github.com/deltakosh/UWPToolkit + https://github.com/deltakosh/UWPToolkit + false + This library enable access to diferent data source like Bing, Facebook, and Twitter. + Initial internal release of core data services. + Copyright 2016 + UWP Toolkit Windows AppStudio Net Portable Twitter OAuth RSS Facebook Bing + + + + + diff --git a/build/build.ps1 b/build/build.ps1 new file mode 100644 index 000000000..74bd3ff18 --- /dev/null +++ b/build/build.ps1 @@ -0,0 +1,42 @@ +param( + [string]$Task = "default", + [hashtable] $Parameters = @{}, + [hashtable] $Properties = @{}, + [switch]$Help +) + +$path = Split-Path -Parent $MyInvocation.MyCommand.Definition + +Import-Module "$path\tools\psake\psake.psm1" + +try +{ + if ($Help){ + try { + Get-Help $MyInvocation.MyCommand.Definition -full + + Write-Host "Available build tasks:" + + Invoke-psake -nologo -docs + } + catch { + } + + return + } + + Invoke-psake "$path/default.ps1" -task $Task -properties $Properties -parameters $Parameters -nologo + + if ($psake.build_success -eq $false) + { + exit 1 + } + else + { + exit 0 + } +} +finally +{ + Remove-Module psake +} \ No newline at end of file diff --git a/build/default.ps1 b/build/default.ps1 new file mode 100644 index 000000000..a78d4f7e1 --- /dev/null +++ b/build/default.ps1 @@ -0,0 +1,68 @@ +$psake.use_exit_on_error = $true + +properties { + $baseDir = Resolve-Path .. + $buildDir = "$baseDir\build" + $sourceDir = "$baseDir" + $toolsDir = "$baseDir\build\tools" + $binDir = "$baseDir\bin" + + $isAppVeyor = Test-Path -Path env:\APPVEYOR + + $version = "2.2.1" + + $tempDir = "$binDir\temp" + $binariesDir = "$binDir\binaries" + $nupkgDir = "$binDir\nupkg" + + $nuget = "$toolsDir\nuget\nuget.exe" +} + +Framework "4.6x86" + +task default -depends ? + +task Clean -description "Clean the output folder" { + if (Test-Path -path $binDir) { + WriteColoredOutput -ForegroundColor Green "Deleting Working Directory...`n" + + Remove-Item $binDir -Recurse -Force + } + + New-Item -Path $binDir -ItemType Directory | Out-Null +} + +task Setup -description "Setup environment" { + WriteColoredOutput -ForegroundColor Green "Setup environment...`n" + + if ($isAppVeyor) { + $script:version = $version -replace '([0-9]+(\.[0-9]+){2}).*', ('$1-dev' + $env:APPVEYOR_BUILD_NUMBER) + + Update-AppveyorBuild -Version $script:version + } + else { + $script:version = $version + } + + Exec { .$nuget restore $packagesConfig "$sourceDir\Windows App Toolkit.sln" } "Error pre-installing NuGet packages" +} + +task Build -depends Clean, Setup -description "Build all projects and get the assemblies" { + New-Item -Path $binariesDir -ItemType Directory | Out-Null + + Exec { msbuild "/t:Clean;Build" /p:Configuration=Release "/p:OutDir=$binariesDir" /p:GenerateProjectSpecificOutputFolder=true /p:TreatWarningsAsErrors=false /m "$sourceDir\Windows App Toolkit.sln" } "Error building $solutionFile" +} + +task PackNuGet -depends Build -description "Create the NuGet packages" { + New-Item -Path $nupkgDir -ItemType Directory | Out-Null + + Get-ChildItem $buildDir\*.nuspec | % { + $fullFilename = $_.FullName + + Exec { .$nuget pack "$fullFilename" -Version "$script:version" -Output "$nupkgDir" } "Error packaging $projectName" + } +} + +task ? -description "Show the help screen" { + WriteDocumentation +} \ No newline at end of file diff --git a/build/tools/nuget/NuGet.exe b/build/tools/nuget/NuGet.exe new file mode 100644 index 000000000..6bb79fe53 Binary files /dev/null and b/build/tools/nuget/NuGet.exe differ diff --git a/build/tools/psake/psake.ps1 b/build/tools/psake/psake.ps1 new file mode 100644 index 000000000..ac9ec3a45 --- /dev/null +++ b/build/tools/psake/psake.ps1 @@ -0,0 +1,53 @@ +# Helper script for those who want to run psake without importing the module. +# Example run from PowerShell: +# .\psake.ps1 "default.ps1" "BuildHelloWord" "4.0" + +# Must match parameter definitions for psake.psm1/invoke-psake +# otherwise named parameter binding fails +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)] + [System.Collections.Hashtable]$parameters = @{}, + [Parameter(Position=5, Mandatory=0)] + [System.Collections.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]$help = $false, + [Parameter(Position=9, Mandatory=0)] + [string]$scriptPath, + [Parameter(Position=10,Mandatory=0)] + [switch]$detailedDocs = $false +) + +# setting $scriptPath here, not as default argument, to support calling as "powershell -File psake.ps1" +if (!$scriptPath) { + $scriptPath = $(Split-Path -parent $MyInvocation.MyCommand.path) +} + +# '[p]sake' is the same as 'psake' but $Error is not polluted +remove-module [p]sake +import-module (join-path $scriptPath psake.psm1) +if ($help) { + Get-Help Invoke-psake -full + return +} + +if ($buildFile -and (-not(test-path $buildFile))) { + $absoluteBuildFile = (join-path $scriptPath $buildFile) + if (test-path $absoluteBuildFile) { + $buildFile = $absoluteBuildFile + } +} + +Invoke-psake $buildFile $taskList $framework $docs $parameters $properties $initialization $nologo $detailedDocs \ No newline at end of file diff --git a/build/tools/psake/psake.psm1 b/build/tools/psake/psake.psm1 new file mode 100644 index 000000000..30fdb6496 --- /dev/null +++ b/build/tools/psake/psake.psm1 @@ -0,0 +1,865 @@ +# 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 + +#-- 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, + [Parameter(Position=11,Mandatory=0)][string]$maxRetries = 0, + [Parameter(Position=12,Mandatory=0)][string]$retryTriggerErrorPattern = $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 + MaxRetries = $maxRetries + RetryTriggerErrorPattern = $retryTriggerErrorPattern + } + + $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 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 + ) + 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 + } + + # 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 + + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + 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 + } + + if ($docs -or $detailedDocs) { + WriteDocumentation($detailedDocs) + CleanupEnvironment + 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. + . $MyInvocation.MyCommand.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 + + 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') + } + '4.6' { + $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 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 WriteDocumentation($showDetailed) { + $currentContext = $psake.context.Peek() + + if ($currentContext.tasks.default) { + $defaultTaskDependencies = $currentContext.tasks.default.DependsOn + } else { + $defaultTaskDependencies = @() + } + + $docs = $currentContext.tasks.Keys | foreach-object { + if ($_ -eq "default") { + return + } + + $task = $currentContext.tasks.$_ + new-object PSObject -property @{ + Name = $task.Name; + Alias = $task.Alias; + Description = $task.Description; + "Depends On" = $task.DependsOn -join ", " + Default = if ($defaultTaskDependencies -contains $task.Name) { $true } + } + } + if ($showDetailed) { + $docs | sort 'Name' | format-list -property Name,Alias,Description,"Depends On",Default + } else { + $docs | sort 'Name' | format-table -autoSize -wrap -property Name,Alias,"Depends On",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 -erroraction silentlycontinue + +$script:psake = @{} +$psake.version = "4.4.1" # contains the current version of psake +$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, Task, Properties, Include, FormatTaskName, TaskSetup, TaskTearDown, Framework, Assert, Exec -variable psake \ No newline at end of file