Windows PowerShell

From HalfgeekKB
Jump to navigation Jump to search

PowerShell prebuild and postbuild script for Visual Studio projects

Tested with VS2015.

How

  • Add a new file buildevents.ps1 at the top level of the project.
  • Copy the text below into the new file and save.
  • In the project properties, change the Pre-build and Post-build commands to the commands suggested at the comments at the top of the file.
  • Modify the script in the "TODO"-marked areas to add pre-build commands, post-build commands, and commands common to all runs.

buildevents.ps1

# For Prebuild command, use:
#   powershell -ExecutionPolicy ByPass "&'$(ProjectDir)buildevents.ps1'" -Function Prebuild -ConfigurationName '$(ConfigurationName)' -OutDir '$(OutDir)' -DevEnvDir '$(DevEnvDir)' -PlatformName '$(PlatformName)' -ProjectDir '$(ProjectDir)' -ProjectPath '$(ProjectPath)' -ProjectName '$(ProjectName)' -ProjectFileName '$(ProjectFileName)' -ProjectExt '$(ProjectExt)' -SolutionDir '$(SolutionDir)' -SolutionPath '$(SolutionPath)' -SolutionName '$(SolutionName)' -SolutionFileName '$(SolutionFileName)' -SolutionExt '$(SolutionExt)' -TargetDir '$(TargetDir)' -TargetPath '$(TargetPath)' -TargetName '$(TargetName)' -TargetFileName '$(TargetFileName)' -TargetExt '$(TargetExt)'

# For Postbuild command, use:
#   powershell -ExecutionPolicy ByPass "&'$(ProjectDir)buildevents.ps1'" -Function Postbuild -ConfigurationName '$(ConfigurationName)' -OutDir '$(OutDir)' -DevEnvDir '$(DevEnvDir)' -PlatformName '$(PlatformName)' -ProjectDir '$(ProjectDir)' -ProjectPath '$(ProjectPath)' -ProjectName '$(ProjectName)' -ProjectFileName '$(ProjectFileName)' -ProjectExt '$(ProjectExt)' -SolutionDir '$(SolutionDir)' -SolutionPath '$(SolutionPath)' -SolutionName '$(SolutionName)' -SolutionFileName '$(SolutionFileName)' -SolutionExt '$(SolutionExt)' -TargetDir '$(TargetDir)' -TargetPath '$(TargetPath)' -TargetName '$(TargetName)' -TargetFileName '$(TargetFileName)' -TargetExt '$(TargetExt)'

param (
    [string]$ConfigurationName, # The name of the current project configuration, for example, "Debug".
    [string]$OutDir, # Path to the output file directory, relative to the project directory. This resolves to the value for the Output Directory property. It includes the trailing backslash '\'.
    [string]$DevEnvDir, # The installation directory of Visual Studio (defined with drive and path); includes the trailing backslash '\'.
    [string]$PlatformName, # The name of the currently targeted platform. For example, "AnyCPU".
    [string]$ProjectDir, # The directory of the project (defined with drive and path); includes the trailing backslash '\'.
    [string]$ProjectPath, # The absolute path name of the project (defined with drive, path, base name, and file extension).
    [string]$ProjectName, # The base name of the project.
    [string]$ProjectFileName, # The file name of the project (defined with base name and file extension).
    [string]$ProjectExt, # The file extension of the project. It includes the '.' before the file extension.
    [string]$SolutionDir, # The directory of the solution (defined with drive and path); includes the trailing backslash '\'.
    [string]$SolutionPath, # The absolute path name of the solution (defined with drive, path, base name, and file extension).
    [string]$SolutionName, # The base name of the solution.
    [string]$SolutionFileName, # The file name of the solution (defined with base name and file extension).
    [string]$SolutionExt, # The file extension of the solution. It includes the '.' before the file extension.
    [string]$TargetDir, # The directory of the primary output file for the build (defined with drive and path). It includes the trailing backslash '\'.
    [string]$TargetPath, # The absolute path name of the primary output file for the build (defined with drive, path, base name, and file extension).
    [string]$TargetName, # The base name of the primary output file for the build.
    [string]$TargetFileName, # The file name of the primary output file for the build (defined as base name and file extension).
    [string]$TargetExt, # The file extension of the primary output file for the build. It includes the '.' before the file extension.

    [string]$Function # Name of the function to run
)

# TODO Add common code here

function Prebuild ()
{
    # TODO Add code here
}

function Postbuild ()
{
    # TODO Add code here
}

# ------------------------------
# AVOID EDITING BELOW THIS POINT
# ------------------------------

function Run-BuildEventFunction($buildEventFunctionName)
{
    echo "--- Build event '$buildEventFunctionName' started ---"
    Try
    {
        & $buildEventFunctionName
        echo "--- Build event '$buildEventFunctionName' completed normally ---"
    }
    Catch
    {
        echo "--- Build event '$buildEventFunctionName' failed with exception ---"
        throw
    }
}

# Call the event indicated by $Function
Run-BuildEventFunction $Function

Advanced: The script to generate buildevents.ps1

$scriptNameInProjectDir = "buildevents.ps1"

# This table is from https://msdn.microsoft.com/en-us/library/42x5kfw4%28v=vs.140%29.aspx
# Copy table and paste into Excel, then copy from Excel into text editor.
# (Copying straight from browser into the text editor doesn't give the tab-separated values needed here.)

$macroList = @'

$(ConfigurationName)	The name of the current project configuration, for example, "Debug".
$(OutDir)	Path to the output file directory, relative to the project directory. This resolves to the value for the Output Directory property. It includes the trailing backslash '\'.
$(DevEnvDir)	The installation directory of Visual Studio (defined with drive and path); includes the trailing backslash '\'.
$(PlatformName)	The name of the currently targeted platform. For example, "AnyCPU".
$(ProjectDir)	The directory of the project (defined with drive and path); includes the trailing backslash '\'.
$(ProjectPath)	The absolute path name of the project (defined with drive, path, base name, and file extension).
$(ProjectName)	The base name of the project.
$(ProjectFileName)	The file name of the project (defined with base name and file extension).
$(ProjectExt)	The file extension of the project. It includes the '.' before the file extension.
$(SolutionDir)	The directory of the solution (defined with drive and path); includes the trailing backslash '\'.
$(SolutionPath)	The absolute path name of the solution (defined with drive, path, base name, and file extension).
$(SolutionName)	The base name of the solution.
$(SolutionFileName)	The file name of the solution (defined with base name and file extension).
$(SolutionExt)	The file extension of the solution. It includes the '.' before the file extension.
$(TargetDir)	The directory of the primary output file for the build (defined with drive and path). It includes the trailing backslash '\'.
$(TargetPath)	The absolute path name of the primary output file for the build (defined with drive, path, base name, and file extension).
$(TargetName)	The base name of the primary output file for the build.
$(TargetFileName)	The file name of the primary output file for the build (defined as base name and file extension).
$(TargetExt)	The file extension of the primary output file for the build. It includes the '.' before the file extension.
'@




function Get-BuildEventCommand ($buildEventFunctionName, $macroData)
{
    $commandStart = "powershell -ExecutionPolicy ByPass `"`&'`$(ProjectDir)$scriptNameInProjectDir'`""
    $macroNames = $macroData | foreach-object { $_.Name }
    $macrosAsArgs = $macroNames | foreach-object { "-$_ '`$($_)'" }
    #$commandMacroArgs = [string]::Join(" ", $macrosAsArgs)
    echo "$commandStart -Function $buildEventFunctionName $macrosAsArgs"
}

function Get-BuildEventCommandComment ($buildEventFunctionName, $macroData)
{
    echo "# For $buildEventFunctionName command, use:"
    echo "#   $(Get-BuildEventCommand $buildEventFunctionName $macroData)"
    echo ""
}

function Get-ParamList ($macroData)
{
    echo "param ("
    foreach($macro in $macroData) {
        echo "    [string]`$$($macro.Name), # $($macro.Description)"
    }
    echo ""
    echo "    [string]`$Function # Name of the function to run"
    echo ")"
    echo ""
}

function Get-FunctionTemplate ($buildEventFunctionName)
{
    echo "function $buildEventFunctionName ()"
    echo "{"
    echo "    # TODO Add code here"
    echo "}"
    echo ""
}

function Parse-MacroLine ($line)
{
    $line = "$line"
    if ($line -match "^\$\((.+?)\)\t(.*?)$") {
        $result = @{
            Name = "$($matches[1])"
            Description = "$($matches[2])"
        }
        $result
    }
    else {
        $null
    }
}



$macroLines = $([Regex]::Split($macroList,"\r\n|\r|\n")) | where { $_ -ne $null -and $_.Trim() -ne "" }

$macroData = $macroLines | foreach-object { Parse-MacroLine $_ } | where { $_ -ne $null }

$buildEventFunctionNames = "Prebuild", "Postbuild"

foreach($buildEventFunctionName in $buildEventFunctionNames) {
    Get-BuildEventCommandComment $buildEventFunctionName $macroData
}

Get-ParamList $macroData

echo "# TODO Add common code here"
echo ""

foreach($buildEventFunctionName in $buildEventFunctionNames) {
    Get-FunctionTemplate $buildEventFunctionName
}

echo @'
# ------------------------------
# AVOID EDITING BELOW THIS POINT
# ------------------------------

function Run-BuildEventFunction($buildEventFunctionName)
{
    echo "--- Build event '$buildEventFunctionName' started ---"
    Try
    {
        & $buildEventFunctionName
        echo "--- Build event '$buildEventFunctionName' completed normally ---"
    }
    Catch
    {
        echo "--- Build event '$buildEventFunctionName' failed with exception ---"
        throw
    }
}

# Call the event indicated by $Function
Run-BuildEventFunction $Function
'@

PSModulePath

PowerShell's search path for the Import-Module command is in the PSModulePath env var:

echo $env:PSModulePath

By default on one system it seems to be

  • a user modules dir at My Documents\WindowsPowerShell\Modules
  • a global modules dir at system32\WindowsPowerShell\v1.0\Modules\

PowerShell Community Extensions (pscx)

Get it from http://pscx.codeplex.com/. The directory structure in the ZIP file needs to be such that the path to Pscx.psm1 is

Modules/Pscx/Pscx.psm1

where Modules is one of the directories in $env:PSModulePath.

(end)