Windows PowerShell

From HalfgeekKB
Jump to navigation Jump to search

PowerShell prebuild and postbuild scripts for Visual Studio projects

Tested with VS2015.

How

  • Prepare (the macro list and) the ps1 boilerplate and event command line as directed below.
    • Alternatively, try (at your own risk) the examples already prepared from my own configuration.
  • Create prebuild.ps1 and postbuild.ps1 in the top level of the project and paste the ps1 boilerplate into each.
  • Go to the Properties of the project and open the Build Events tab.
    • Click Edit Pre-build… and paste the pre-build event command line into the field. Replace pxxbuild with prebuild. Confirm.
    • Click Edit Post-build… and paste the post-build event command line into the field. Replace pxxbuild with postbuild. Confirm.

Finally, add scripting after the boilerplate in prebuild.ps1 and postbuild.ps1 as necessary. This saves some serious acrobatics later on when some actual processing has to happen in the event.

Prepare the macro list

  • Go to the Properties of the project and open the Build Events tab.
  • Click Edit Pre-build… (which opens the pre-build event command line for editing).
  • In a text editor (or spreadsheet), create a list of all of the names seen in the Macro column of the macro list in the dialog.

There appears to be no way to directly copy this table. For your sanity, I've included the list from my setup below.

Prepare the ps1 boilerplate

The ps1 boilerplate is a param list that makes it possible to pass all macros to the script by name and enable the rest of the file to refer to the macro values as variables.

Convert a copy of the macro name list into a ps1 boilerplate script by making the following changes:

  • Prefix each line with [string]$ and suffix each line except the last with ,.
    • Using a spreadsheet: Create a blank spreadsheet. Paste the macro names in column A. In B1, enter the formula ="[string]$" & A1 & ",", then fill down to the rest of column B. Select the generated column B values, copy, and paste them into a plain text editor. Manually delete the comma at the end of the last line.
    • Using vim: Enter the command :%s/\v(.*)/[string]$\1,/g, then delete the comma at the end of the last line.
  • Enter the line param ( above the list.
  • Enter the line ) below the list.
  • Indent as desired.

Prepare the event command line

The event command line is the command called by Visual Studio for the pre- and post-build commands. This generated command line in particular passes the values of all of the macros as named parameters.

Convert a copy of the macro name list into the event command line by making the following changes:

  • Replace each line NAME with -NAME $('NAME').
    • Using a spreadsheet: Create a blank spreadsheet. Paste the macro names in column A. In B1, enter the formula ="-" & A1 & " $('" & A1 & "')", then fill down to the rest of column B. Select the generated column B values, copy, and paste them into a plain text editor.
    • Using vim: Enter the command :%s/\v(.*)/-\1 $('\1')/g.
  • Enter the line powershell -ExecutionPolicy ByPass "&'$(ProjectDir)pxxbuild.ps1'" above the list.
  • Replace all newlines with a single space.
    • Using a spreadsheet and Notepad: Create a blank spreadsheet. Paste the lines into column A (if the lines were copied from another spreadsheet, use Paste Values Only to avoid copying formulas). Re-copy the lines from A, then paste the transpose (usually under Paste Special) at B1. Select the cells that have been populated (B1, C1, etc.) and copy into Notepad. In the text that has just been pasted, select a tab character and copy it. Using the Replace… function, paste the tab character into Find what: and type a space in Replace with:. Run Replace All.
    • Using Windows-native gvim: Enter the command :%s/\n/ /g
    • Using Visual Studio text editor: Create a new text file in Visual Studio. (This is temporary and should not be associated with the project or solution.) Paste the lines. Open Quick Replace for the current document. Use \r\n for the search term and for the replacement term, and make sure Use Regular Expressions is selected. Replace all instances.

For my configuration

Macro list

ConfigurationName
DevEnvDir
OutDir
PlatformName
ProjectDir
ProjectExt
ProjectFileName
ProjectName
ProjectPath
SolutionDir
SolutionExt
SolutionFileName
SolutionName
SolutionPath
TargetDir
TargetExt
TargetFileName
TargetName

Event command line

Substitute pre or post for pxx.

 powershell -ExecutionPolicy ByPass "&'$(ProjectDir)pxxbuild.ps1'" -ConfigurationName '$(ConfigurationName)'  -DevEnvDir '$(DevEnvDir)'  -OutDir '$(OutDir)'  -PlatformName '$(PlatformName)'  -ProjectDir '$(ProjectDir)'  -ProjectExt '$(ProjectExt)'  -ProjectFileName '$(ProjectFileName)'  -ProjectName '$(ProjectName)'  -ProjectPath '$(ProjectPath)'  -SolutionDir '$(SolutionDir)'  -SolutionExt '$(SolutionExt)'  -SolutionFileName '$(SolutionFileName)'  -SolutionName '$(SolutionName)'  -SolutionPath '$(SolutionPath)'  -TargetDir '$(TargetDir)'  -TargetExt '$(TargetExt)'  -TargetFileName '$(TargetFileName)'  -TargetName '$(TargetName)'

ps1 Boilerplate

param (
	[string]$ConfigurationName,
	[string]$DevEnvDir,
	[string]$OutDir,
	[string]$PlatformName,
	[string]$ProjectDir,
	[string]$ProjectExt,
	[string]$ProjectFileName,
	[string]$ProjectName,
	[string]$ProjectPath,
	[string]$SolutionDir,
	[string]$SolutionExt,
	[string]$SolutionFileName,
	[string]$SolutionName,
	[string]$SolutionPath,
	[string]$TargetDir,
	[string]$TargetExt,
	[string]$TargetFileName,
	[string]$TargetName
)

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)