PowerShell build framework

Automated, local building of Visual Studio solutions and calling external tools like unit tests, source code commit, obfuscation, digitally signing, file publishing and transfer.

Please bring about 10 minutes to read this page in peace.

This portable, customisable build script does not only build your solution. It rather coordinates diverse tasks that are to be executed for a release build or a commit or at other occasions. The framework consists of multiple modules that each cover a topic, a central function library, and a project-specific control file (example below). To simplify running the particular scenarios there are additional Windows batch files.

A few exemplary use cases:

  • Simple creation of a debug or release build (rebuild all)
  • Start unit tests with MSTest
  • Create a setup package with Inno Setup
  • Digitally signing of the created program files and the setup
  • Check the solution for build errors before committing to Git or Subversion (personal gated check-in)
  • Compose the last changes from Git or Subversion into a change log file
  • Transfer the built files to another directory or with SFTP
  • Start a code obfuscator

These tasks are configured once for the entire project (see example below) and can then be executed by every developer on their local computer because the build scripts are included in the code repository as a part of the project. Unused modules can be left out (e. g. the svn module in a Git repository or dotfuscator in an open source project).

  • No installation (except for the called build tools)
  • No environment configuration
  • No administrator privileges required

Controlling external tools

This script can automatically find the external programs that are required for a particular build step. MSBuild and MSTest are chosen from the .NET directory depending on the platform configuration to build. The installation directory of Inno Setup and the Git/Subversion tools is read from the registry. So it is sufficient to regularly install the required applications – they are automatically available for build tasks thereafter. Only small, portable tools are put into the repository. This especially includes FlashConsoleWindow and NetRevisionTool which are tightly integrated into this script, or PdbConvert from FieldLog.

There is also a progress indicator that is displayed in the task bar. So you can see the approximate progress for a big number of tasks. For that to work, the expected time for each step must be configured. The expected build duration is then summed up for the selected scenario. If an error occurs in one step, a red indicator is shown and the script is cancelled. The console window then remains open so that you can read the error output. If everything went good a green indicator remains visible and the window is closed after a moment, so you don’t even have to look at the window.

Modules

The following modules are currently included:

  • dotfuscator: Starts Dotfuscator CLI (tested with Community Edition in VS 2010 with CLI update)
  • file: Creates archives (with 7-Zip), copies and deletes files, starts external programs
  • git: Commit, export of a clean copy, consolidates commit messages (with TortoiseGit)
  • innosetup: Starts Inno Setup and creates installation packages
  • msbuild: Starts MSBuild and builds Visual Studio solutions
  • mstest: Starts MSTest and runs unit tests
  • nuget: Creates and publishes NuGet packages
  • pdbconvert: Converts .pdb files with FieldLog PdbConvert
  • seeunsharp: Starts SeeUnsharp .NET Obfuscator
  • sign: Digitally signs files
  • ssh: Transfers files via SFTP (with PuTTY pscp)
  • svn: Commit, export of a clean copy, consolidates commit messages (with TortoiseSVN)

Custom modules can easily be created using existing ones as a template. All files in the modules directory are automatically regarded. All commandlets defined in the available modules can be called in the control file. There is no special registration for them.

If you have written an interesting module or extended an existing one, please let me know so I could maybe include it in this distribution. There are still a lot of tools to support: NUnit, Mercurial, TFS/TFVC, SHFB, NDoc, StyleCop, FxCop, InstallShield…

Compatibility: Windows 10 Windows 8 Windows 7 Windows XP 64 bit

When do I need this framework?

If your solution only has one or a few projects and you do not use additional tools then this framework will not be able to help you much. As soon as you create a setup outside Visual Studio, run unit tests, or work together with other developers through a version control system, this framework automates recurring tasks. As a result errors are recognised early, release builds run predictably and nothing is forgotten in the rush.

Where post build events in Visual Studio get confusing and bulky this script brings back flexible order. Consolidating of commit log messages to change logs as well as packing a list of files after a build or transferring to local or remote directories (“Continuous Delivery”) can also be automated.

Moreover, this script also works nicely on a build server to execute everything in the correct order without configuring it anew. Changes to the project structure are automatically applied on the build server with checking in the new build script. It is tested before, on the developer’s computer.

Installation

Setting up the build framework in a project is very easy. Just unpack and choose the files and edit the project-specific control file.

  1. The files contained in the download must be unpacked into the solution directory, preferrably with the provided directory structure (pictured below). Deviations in directory names or depth must be adjusted in the script.
  2. Any used portable tools can be moved into the directory _scripts\bin.
  3. The control file _scripts\buildscript\control.ps1 must be edited so that the necessary build steps and configurations are executed. The included file can be used as a template.
  4. For each configuration a batch file should be created in the directory _scripts. The download already contains several common files.

The automated software build is now set up and can be started by running the desired batch file. The following directory tree shows the recommended structure:

  • Solution directory
    • _scripts
      • bin
        • FlashConsoleWindow.exe
        • NetRevisionTool.exe
        • (more portable tools)
      • buildscript
        • modules
          • git.ps1
          • msbuild.ps1
          • (more framework modules)
        • control.ps1
        • (private.ps1)
        • psbuild.ps1
      • build_debug.cmd
      • build_release_setup.cmd
      • commit.cmd
      • publish.cmd
      • (more batch files)
    • Project 1
    • Project 2
    • Solution.sln

Optionally, you can also set up the button in the Visual Studio toolbar as described in the next paragraph. (This applies to the local Visual Studio environment and all projects. Obviously the button will only work there where the specified batch file exists.)

Build & Commit via the toolbar

Now that particular build tasks can be executed by every developer locally through a batch file, another useful script for committing changes into the version control is possible. Today the file commit.cmd exists in all of my projects that employ this framework. Corresponding to this script, I have added it as an external tool in Visual Studio together with an additional toolbar with the button “Build & Commit” that runs it. That way before every commit the build of the entire solution is checked (incl. unloaded projects and setup) and it is verified that the solution and project files are saved to disk. This is also a good place for quick unit tests.

After completing work on a task I can simply press this button to have the build checked and all changes listed. Quickly review the changes, write a commit message, done.

Screenshot of the Visual Studio toolbar “Build & Commit”

The toolbar and the external tool can be set up manually as described below. I have also prepared a VSSettings file that almost entirely automates these steps. On importing it you need to make sure that all sections are imported, also the ones with the warning sign. Afterwards, the new tool must be moved to the beginning of the list, because unfortunately tool buttons for external tools can only be created by list index and not by name. The dialogue is located in the menu “Tools; External tools…”.

psbuild-commit.vssettings2.6 KiBVisual Studio toolbar with the “Build & Commit” command (VS 2010–2015)

External tool, Title: Build && Commit
Command: $(SolutionDir)_scripts\commit.cmd
Options: Use Output window
Position: Move to the top
Toolbar, Category: Tools
Command: External Command 1

The commit is performed for Git and SVN using the Tortoise GUI, which especially avoids the retarded Ankh GUI for SVN and also regards files not included in the solution. To prevent inconsistent revision numbers in SVN working directories, an update is executed after the SVN commit. (This hack is not required for Git.)

Example

The following control file comes from the FieldLog project and shows the build of the solution and the setup, digitally signing, and more program calls. (Here shortened, the complete file is included in the download.) It uses the modules file, git, innosetup, msbuild, nuget, and sign. The private data about the certificate and NuGet upload key are stored in a private file not checked into the repository. Notes on their usage are documented with the respective functions.

The entry point is psbuild.ps1, this file is invoked from the batch files. control.ps1 is included from there. After a few general meta data like the application name and version there come the commands for the respective tasks. They can be restricted to certain configurations with conditions. This arrangement is freely programmable with PowerShell code. Commands for multiple configurations can be shared through suitable conditions. Most path specifications are done relative to the solution directory ($rootDir).

The commands only collect all parameters into a list at first. Only the call of End-BuildScript processes the items on that list. Thereby the total time and the current progress are available. But this also means that you cannot use arbitrary commands in the build workflow (they are executed immediately), it must be functions provided by one of the modules.

Complete descriptions of all commands are available in the comments above each function in the module files. The requirements for each command are also documented there.

# PowerShell build framework
# Project-specific control file

Begin-BuildScript "FieldLog"

# Find revision format from the source code, require Git checkout
Set-VcsVersion "" "/require git"

# FieldLog.*NET* projects are overlapping, don't build them in parallel
Disable-ParallelBuild

# Debug builds
if (IsSelected "build-debug")
{
    Build-Solution "FieldLog.sln" "Debug" "Any CPU" 8
}

# Release builds
if ((IsSelected "build-release") -or (IsSelected "commit") -or (IsSelected "publish"))
{
    Build-Solution "FieldLog.sln" "Release" "Any CPU" 8
   
    if ((IsSelected "sign-app") -or (IsSelected "publish"))
    {
        Sign-File "...\bin\Release\FieldLogViewer.exe" "$signKeyFile" "$signPassword" 1
        Sign-File "PdbConvert\bin\Release\PdbConvert.exe" "$signKeyFile" "$signPassword" 1
    }

    Create-NuGet "FieldLog\Unclassified.FieldLog.nuspec" "FieldLog\bin" 2
}

# Release setups
if ((IsSelected "setup-release") -or (IsSelected "commit") -or (IsSelected "publish"))
{
    Create-Setup "Setup\FieldLog.iss" Release 1

    if ((IsSelected "sign-setup") -or (IsSelected "publish"))
    {
        Sign-File "Setup\bin\FieldLogSetup-$revId.exe" "$signKeyFile" "$signPassword" 1
    }
}

# Install release setup
if (IsSelected "install")
{
    Exec-File "Setup\bin\FieldLogSetup-$revId.exe" "/norestart /verysilent" 1
}

# Commit to repository
if (IsSelected "commit")
{
    # Clean up test build files
    Delete-File "Setup\bin\FieldLogSetup-$revId.exe" 0
    Delete-File ".local\FieldLog-$revId.pdbx" 0

    Git-Commit 5
}

# Prepare publishing a release
if (IsSelected "publish")
{
    Git-Log ".local\FieldLogChanges.txt" 1
}

# Upload to NuGet
if (IsSelected "transfer-nuget")
{
    Push-NuGet "FieldLog\bin\Unclassified.FieldLog" $nuGetApiKey 45
}

End-BuildScript

Download

master.zipLatest source code with all modules directly from GitHub

There’s a public Git repository of psbuild on GitHub. Changes to the code are documented there.

Why PowerShell?

This script is written in PowerShell because it is available on all Windows systems and supports complex tasks (work with registry data, parse XML program output etc.) considerably easier than batch files. At the same time I wanted to keep the script open and customisable so that you can quickly make changes to it when the need arises. That is why it is no closed binary. Additionally, the control file is always specific to a project and as it is also written in PowerShell I do not need a separate configuration parser for all the necessary conditions.

In the beginning of 2013 I had set up a simple build script for a project with a batch file from old stock that could already find and call MSBuild, Inno Setup and Dotfuscator CLI. It was supposed to run the necessary build and packaging steps easily and with no further configuration for all developers. But especially the registry access was so clumsy that a few days after it was converted into a PowerShell script that was more maintainable. Throughout 2014 lots of additions were made to it that were later split up into modules. Early 2015 this script was cleaned up of unnecessary code, fully documented, and published as an independent project. That also make it easier to keep it updated in all projects.

Licence and terms of use

Copying and distribution of this file, with or without modification, are permitted provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. (GNU All-Permissive licence)

Statistic data

  • Created on 2013-02-01, updated on 2016-01-28.
  • Ca. 1 300 lines of code, estimated development costs: 1 300 - 5 200 €