Custom Build Scripts

As described in the Build Services Reference, MyGet Build Services supports scripting your own build using a batch file named build.bat, build.cmd or using a PowerShell script. This page lists several example build.bat files which can be used in other projects as well.

Note: All environment variables described in the Build Services Reference can be used in build.bat files.

Simple build.bat

The following build.bat executes several steps:

  • Compile the solution using msbuild
  • Create NuGet packages for specific projects in the solution, also creating symbols packages. Package version will come from %PackageVersion% environment variable, if set.

build.bat:

@echo Off
set config=%1
if "%config%" == "" (
   set config=Release
)

set version=
if not "%PackageVersion%" == "" (
   set version=-Version %PackageVersion%
)

REM Build
"%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" YourSolution.sln /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false

REM Package
mkdir Build
call %nuget% pack "YourSolution\ProjectA.csproj" -symbols -o Build -p Configuration=%config% %version%
call %nuget% pack "YourSolution\ProjectB.csproj" -symbols -o Build -p Configuration=%config% %version%

build.bat creating a single NuGet package containing all projects

The following build.bat executes several steps:

  • Compile the solution using msbuild
  • Create a single NuGet package containing all referenced projects. Package version will come from %PackageVersion% environment variable, if set.

build.bat:

@echo Off
set config=%1
if "%config%" == "" (
   set config=Release
)

set version=
if not "%PackageVersion%" == "" (
   set version=-Version %PackageVersion%
)

REM Build
"%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" YourSolution.sln /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false

REM Package
mkdir Build
call %nuget% pack "YourSolution\ProjectA.csproj" -IncludeReferencedProjects -o Build -p Configuration=%config% %version%

build.bat manually running unit tests using default test runner

The following build.bat executes several steps:

  • Compile the solution using msbuild
  • Run unit tests using default MyGet test runner (Gallio Echo)
  • Create a single NuGet package containing all referenced projects, also creating symbols packages. Package version will come from %PackageVersion% environment variable, if set.

This build.bat also verifies error level after every step and reports build success/failure back to MyGet.

build.bat:

@echo Off
set config=%1
if "%config%" == "" (
   set config=Release
)

set version=
if not "%PackageVersion%" == "" (
   set version=-Version %PackageVersion%
)

REM Build
"%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" YourSolution.sln /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false
if not "%errorlevel%"=="0" goto failure

REM Unit tests
%GallioEcho% YourSolution\ProjectA.Tests\bin\%config%\ProjectA.Tests.dll
if not "%errorlevel%"=="0" goto failure

REM Package
mkdir Build
call %nuget% pack "YourSolution\YourSolution.ProjectA.csproj" -symbols -o Build -p Configuration=%config% %version%
if not "%errorlevel%"=="0" goto failure

:success
exit 0

:failure
exit -1

build.bat manually running unit tests using custom test runner

The following build.bat executes several steps:

  • Compile the solution using msbuild
  • Run unit tests using a custom test runner provided through the project's source control repository
  • Create a single NuGet package containing a specific projects, also creating symbols packages. Package version will come from %PackageVersion% environment variable, if set.

This build.bat also verifies error level after every step and reports build success/failure back to MyGet.

build.bat:

@echo Off
set config=%1
if "%config%" == "" (
   set config=Release
)

set version=
if not "%PackageVersion%" == "" (
   set version=-Version %PackageVersion%
)

set nunit="tools\nunit\nunit-console.exe"

REM Build
"%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" YourSolution.sln /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false
if not "%errorlevel%"=="0" goto failure

REM Unit tests
%nunit% YourSolution\ProjectA.Tests\bin\%config%\ProjectA.Tests.dll
if not "%errorlevel%"=="0" goto failure

REM Package
mkdir Build
call %nuget% pack "YourSolution\YourSolution.ProjectA.csproj" -symbols -o Build -p Configuration=%config% %version%
if not "%errorlevel%"=="0" goto failure

:success
exit 0

:failure
exit -1

build.bat manually running unit tests using custom test runner which is downloaded on the fly

The following build.bat executes several steps:

  • Compile the solution using msbuild
  • Download NUnit 2.6.4 from NuGet.org
  • Run unit tests using the downloaded NUnit 2.6.4 test runner
  • Create a single NuGet package containing a specific projects, also creating symbols packages. Package version will come from %PackageVersion% environment variable, if set.

This build.bat also verifies error level after every step and reports build success/failure back to MyGet.

build.bat:

@echo Off
set config=%1
if "%config%" == "" (
   set config=Release
)

set version=
if not "%PackageVersion%" == "" (
   set version=-Version %PackageVersion%
)

REM Build
"%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" YourSolution.sln /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false
if not "%errorlevel%"=="0" goto failure

REM Unit tests
call %nuget% install NUnit.Runners -Version 2.6.4 -OutputDirectory packages
packages\NUnit.Runners.2.6.4\tools\nunit-console.exe /config:%config% /framework:net-4.5 YourSolution\ProjectA.Tests\bin\%config%\ProjectA.Tests.dll
if not "%errorlevel%"=="0" goto failure

REM Package
mkdir Build
call %nuget% pack "YourSolution\YourSolution.ProjectA.csproj" -symbols -o Build -p Configuration=%config% %version%
if not "%errorlevel%"=="0" goto failure

:success
exit 0

:failure
exit -1

build.bat doing manual package restore before build

The following build.bat executes several steps:

  • Perform manual package restore for several projects' packages.config.
  • Compile the solution using msbuild
  • Create NuGet packages for specific projects in the solution, also creating symbols packages. Package version will come from %PackageVersion% environment variable, if set.

Note that the nuget.exe used is provided by the project itself through its source control, hence the calls to .nuget\nuget.exe.

build.bat:

@echo Off
set config=%1
if "%config%" == "" (
   set config=Release
)

set version=
if not "%PackageVersion%" == "" (
   set version=-Version %PackageVersion%
)

REM Package restore
call %NuGet% restore GoogleAnalyticsTracker\packages.config -OutputDirectory %cd%\packages -NonInteractive
call %NuGet% restore GoogleAnalyticsTracker.WP7\packages.config -OutputDirectory %cd%\packages -NonInteractive

REM Build
"%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" GoogleAnalyticsTracker.sln /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false

REM Package
mkdir Build
mkdir Build\net40
call %NuGet% pack "GoogleAnalyticsTracker\GoogleAnalyticsTracker.csproj" -symbols -o Build\net40 -p Configuration=%config% %version%
copy GoogleAnalyticsTracker\bin\%config%\*.dll Build\net40
copy GoogleAnalyticsTracker\bin\%config%\*.pdb Build\net40

mkdir Build\sl4-wp71
call %NuGet% pack "GoogleAnalyticsTracker.WP7\GoogleAnalyticsTracker.WP7.csproj" -symbols -o Build\sl4-wp71 -p Configuration=%config% %version%
copy GoogleAnalyticsTracker.WP7\bin\%config%\*.dll Build\sl4-wp71
copy GoogleAnalyticsTracker.WP7\bin\%config%\*.pdb Build\sl4-wp71

mkdir Build\wp8
call %NuGet% pack "GoogleAnalyticsTracker.WP8\GoogleAnalyticsTracker.WP8.csproj" -symbols -o Build\wp8 -p Configuration=%config% %version%
copy GoogleAnalyticsTracker.WP8\bin\%config%\*.dll Build\wp8
copy GoogleAnalyticsTracker.WP8\bin\%config%\*.pdb Build\wp8

From the GoogleAnalyticsTracker project on GitHub.

PowerShell Build.ps1 example building for different target framework versions

The following is an advanced PowerShell build script which was contributed by Peter Sunde. It allows building multiple NuGet packages for different target frameworks and CPU versions.

NuSpec file:

<?xml version="1.0"?>
<package >
  <metadata>
    <id>AsposeCloud.SDK</id>
    <version>$version$</version>
    <title>AsposeCloud.SDK for .NET</title>
    <authors>Aspose</authors>
    <owners>Aspose</owners>
  <licenseUrl>https://www.aspose.com/corporate/purchase/end-user-license-agreement.aspx</licenseUrl>
    <projectUrl>https://www.aspose.com/cloud/total-api.aspx</projectUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Aspose for Cloud is a cloud-based document generation, conversion and automation platform for developers. 
    Aspose for Cloud’s REST APIs gives developers on all platforms total control over documents and file formats. 
    It interoperates seamlessly with other cloud services.</description>
    <copyright>Copyright 2013 Aspose</copyright>
    <tags>Aspose.Cloud SDK PDF XFA XPS TIFF PCL SVG HTML XML XSL-FO FDF XFDF PDF/A form Portfolio EPUB to PDF DOC DOCX</tags>
  </metadata>
  <files
     <!-- $bin => bin\$version$\$platform$\$configugration$ -->
     <file src="$bin$\v4.0\AsposeCloud.SDK.dll" target="lib\net40" />
     <file src="$bin$\v4.5\AsposeCloud.SDK.dll" target="lib\net45" />
     <file src="$bin$\v4.5.1\AsposeCloud.SDK.dll" target="lib\net451" />
  </files>
</package>

build.ps1:

# original: https://gist.github.com/peters/6812859/raw/a965bdb5ae39dbff351420da64df4e9925e07e36/Build.ps1

param(
    [string]$packageVersion = $null,
    [string]$config = "Release",
    [string[]]$targetFrameworks = @("v4.0", "v4.5", "v4.5.1"),
    [string[]]$platforms = @("AnyCpu"),
    [ValidateSet("rebuild", "build")]
    [string]$target = "rebuild",
    [ValidateSet("quiet", "minimal", "normal", "detailed", "diagnostic")]
    [string]$verbosity = "minimal",
    [bool]$alwaysClean = $true
)

# Diagnostic 
function Write-Diagnostic {
    param([string]$message)

    Write-Host
    Write-Host $message -ForegroundColor Green
    Write-Host
}

function Die([string]$message, [object[]]$output) {
    if ($output) {
        Write-Output $output
        $message += ". See output above."
    }
    Write-Error $message
    exit 1
}

function Create-Folder-Safe {
    param(
        [string]$folder = $(throw "-folder is required.")
    )

    if(-not (Test-Path $folder)) {
        [System.IO.Directory]::CreateDirectory($folder)
    }

}

# Build
function Build-Clean {
    param(
        [string]$rootFolder = $(throw "-rootFolder is required."),
        [string]$folders = "bin,obj"
    )

    Write-Diagnostic "Build: Clean"

    Get-ChildItem $rootFolder -Include $folders -Recurse | ForEach-Object {
       Remove-Item $_.fullname -Force -Recurse 
    }
}

function Build-Bootstrap {
    param(
        [string]$solutionFile = $(throw "-solutionFile is required."),
        [string]$nugetExe = $(throw "-nugetExe is required.")
    )

    Write-Diagnostic "Build: Bootstrap"

    $solutionFolder = [System.IO.Path]::GetDirectoryName($solutionFile)

    . $nugetExe config -Set Verbosity=quiet
    . $nugetExe restore $solutionFile -NonInteractive

    Get-ChildItem $solutionFolder -filter packages.config -recurse | 
        Where-Object { -not ($_.PSIsContainer) } | 
        ForEach-Object {

        . $nugetExe restore $_.FullName -NonInteractive -SolutionDirectory $solutionFolder

    }
}

function Build-Nupkg {
    param(
        [string]$rootFolder = $(throw "-rootFolder is required."),
        [string]$project = $(throw "-project is required."),
        [string]$nugetExe = $(throw "-nugetExe is required."),
        [string]$outputFolder = $(throw "-outputFolder is required."),
        [string]$config = $(throw "-config is required."),
        [string]$version = $(throw "-version is required."),
        [string]$platform = $(throw "-platform is required.")
    )

    $outputFolder = Join-Path $outputFolder "$config"
    $nuspecFilename = [System.IO.Path]::GetFullPath($project) -ireplace ".csproj$", ".nuspec"

    if(-not (Test-Path $nuspecFilename)) {
        Die("Could not find nuspec: $nuspecFilename")
    }

    Write-Diagnostic "Creating nuget package for platform $platform"

    # https://docs.nuget.org/docs/reference/command-line-reference#Pack_Command
    . $nugetExe pack $nuspecFilename -OutputDirectory $outputFolder -Symbols -NonInteractive `
        -Properties "Configuration=$config;Bin=$outputFolder;Platform=$platform" -Version $version

    if($LASTEXITCODE -ne 0) {
        Die("Build failed: $projectName")
    }

    # Support for multiple build runners
    if(Test-Path env:BuildRunner) {
        $buildRunner = Get-Content env:BuildRunner

        switch -Wildcard ($buildRunner.ToString().ToLower()) {
            "myget" {

                $mygetBuildFolder = Join-Path $rootFolder "Build"

                Create-Folder-Safe -folder $mygetBuildFolder

                Get-ChildItem $outputFolder -filter *.nupkg | 
                Where-Object { -not ($_.PSIsContainer) } | 
                ForEach-Object {
                    $fullpath = $_.FullName
                    $filename = $_.Name

                    cp $fullpath $mygetBuildFolder\$filename
                }

            }
        }
    }
}

function Build-Project {
    param(
        [string]$project = $(throw "-project is required."),
        [string]$outputFolder = $(throw "-outputFolder is required."),
        [string]$nugetExe = $(throw "-nugetExe is required."),
        [string]$config = $(throw "-config is required."),
        [string]$target = $(throw "-target is required."),
        [string[]]$targetFrameworks = $(throw "-targetFrameworks is required."),
        [string[]]$platform = $(throw "-platform is required.")
    )

    $projectPath = [System.IO.Path]::GetFullPath($project)
    $projectName = [System.IO.Path]::GetFileName($projectPath) -ireplace ".csproj$", ""

    Create-Folder-Safe -folder $outputFolder

    if(-not (Test-Path $projectPath)) {
        Die("Could not find csproj: $projectPath")
    }

    $targetFrameworks | foreach-object {
        $targetFramework = $_
        $platformOutputFolder = Join-Path $outputFolder "$config\$targetFramework"

        Create-Folder-Safe -folder $platformOutputFolder

        Write-Diagnostic "Build: $projectName ($platform / $config - $targetFramework)"

        & "$(Get-Content env:windir)\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" `
            $projectPath `
            /t:$target `
            /p:Configuration=$config `
            /p:OutputPath=$platformOutputFolder `
            /p:TargetFrameworkVersion=$targetFramework `
            /p:Platform=$platform `
            /m `
            /v:M `
            /fl `
            /flp:LogFile=$platformOutputFolder\msbuild.log `
            /nr:false

        if($LASTEXITCODE -ne 0) {
            Die("Build failed: $projectName ($Config - $targetFramework)")
        }
    }
}

function Build-Solution {
    param(
        [string]$rootFolder = $(throw "-rootFolder is required."),
        [string]$solutionFile = $(throw "-solutionFile is required."),
        [string]$outputFolder = $(throw "-outputFolder is required."),
        [string]$version = $(throw "-version is required"),
        [string]$config = $(throw "-config is required."),
        [string]$target = $(throw "-target is required."),
        [bool]$alwaysClean = $(throw "-alwaysclean is required"),
        [string[]]$targetFrameworks = $(throw "-targetFrameworks is required."),
        [string[]]$projects = $(throw "-projects is required."),
        [string[]]$platforms = $(throw "-platforms is required.")
    )

    if(-not (Test-Path $solutionFile)) {
        Die("Could not find solution: $solutionFile")
    }

    $solutionFolder = [System.IO.Path]::GetDirectoryName($solutionFile)
    $nugetExe = if(Test-Path Env:NuGet) { Get-Content env:NuGet } else { Join-Path $solutionFolder ".nuget\nuget.exe" }

    if($alwaysClean) {
        Build-Clean -root $solutionFolder
    }

    Build-Bootstrap -solutionFile $solutionFile -nugetExe $nugetExe

    $projects | ForEach-Object {

        $project = $_

        $platforms | ForEach-Object {
            $platform = $_

            $buildOutputFolder = Join-Path $outputFolder "$version\$platform"

            Build-Project -rootFolder $solutionFolder -project $project -outputFolder $buildOutputFolder `
                -nugetExe $nugetExe -target $target -config $config `
                -targetFrameworks $targetFrameworks -version $version -platform $platform

            Build-Nupkg -rootFolder $rootFolder -project $project -nugetExe $nugetExe -outputFolder $buildOutputFolder `
                -config $config -version $version -platform $platform
        }
    }
}

function TestRunner-Nunit {
    param(
        [string]$outputFolder = $(throw "-outputFolder is required."),
        [string]$config = $(throw "-config is required."),
        [string]$target = $(throw "-target is required."),
        [string[]]$projects = $(throw "-projects is required."),
        [string[]]$platforms = $(throw "-platforms is required.")
    )

    Die("TODO")
}

# Bootstrap
$rootFolder = Split-Path -parent $script:MyInvocation.MyCommand.Definition
$outputFolder = Join-Path $rootFolder "bin"
$testsFolder = Join-Path $outputFolder "tests"

$config = $config.substring(0, 1).toupper() + $config.substring(1)
$version = $config.trim()

# Myget
$currentVersion = if(Test-Path env:PackageVersion) { Get-Content env:PackageVersion } else { $packageVersion }

if($currentVersion -eq "") {
    Die("Package version cannot be empty")
}

# Build AsposeCloud
Build-Solution -solutionFile $rootFolder\AsposeCloud.SDK-for-.NET-master\AsposeCloud.SDK.sln `
    -projects @( `
        "$rootFolder\AsposeCloud.SDK-for-.NET-master\AsposeCloud.SDK\AsposeCloud.csproj"
    ) `
    -rootFolder $rootFolder `
    -outputFolder $outputFolder `
    -platforms $platforms `
    -version $currentVersion `
    -config $config `
    -target $target `
    -targetFrameworks $targetFrameworks `
    -alwaysClean $alwaysClean

PowerShell Build.ps1 example creating Chocolatey packages

The following is a Powershell script that, instead of creating NuGet packages, creates Chocolatey packages.

build.ps1:

Write-Host "Building Chocolatey packages..."

$nuspecs = Get-ChildItem -Path $PSScriptRoot -Filter *.nuspec -Recurse

foreach ($nuspec in $nuspecs) {
    choco pack $nuspec.FullName
}

$artifactsFolder = "./artifacts"

Remove-Item -Path $artifactsFolder -Force -Recurse -ErrorAction SilentlyContinue
New-Item $artifactsFolder -Force -Type Directory | Out-Null
Move-Item *.nupkg $artifactsFolder

Write-Host "Finished building Chocolatey packages."

.NET Core running xunit tests

MyGet can build .NET Core projects out of the box. However, creating a build.bat often makes a lot of sense if you want to add a custom test runner or want to have more control over the build process. The following files make up a build for a .NET Core library that:

  • Restores packages using dotnet restore
  • Builds packages using dotnet build (or MSBuild 15.0)
  • Runs tests using dotnet test
  • Packages the library using dotnet pack

Make sure the library project itself either has a MyLibrary.nuspec which holds package metadata, or that the .csproj contains required metadata.

The following build uses the dotnet command to do all the work, except compilation. We'll be using msbuild for that as it automatically resolves project dependencies and builds projects in the right order.

We're also using MyGet's %BuildCounter% environment variable to generate a version number suffix (which will be appended to the version number). This is optional and can be removed if you prefer using the version numbers .NET Core generates itself.

build.bat:

@echo Off
set config=%1
if "%config%" == "" (
    set config=Release
)

set version=
if not "%BuildCounter%" == "" (
   set packversionsuffix=--version-suffix ci-%BuildCounter%
)

REM Detect MSBuild 15.0 path
if exist "%programfiles(x86)%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" (
    set msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"
)
if exist "%programfiles(x86)%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe" (
    set msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe"
)
if exist "%programfiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe" (
    set msbuild="%programfiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe"
)

REM (optional) build.bat is in the root of our repo, cd to the correct folder where sources/projects are
cd src

REM Restore
call dotnet restore
if not "%errorlevel%"=="0" goto failure

REM Build
REM - Option 1: Run dotnet build for every source folder in the project
REM   e.g. call dotnet build <path> --configuration %config%
REM - Option 2: Let msbuild handle things and build the solution
call "%msbuild%" MyLibrary.sln /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false
REM call dotnet build --configuration %config%
if not "%errorlevel%"=="0" goto failure

REM Unit tests
call dotnet test MyLibrary.Tests\MyLibrary.Tests.csproj --configuration %config% --no-build
if not "%errorlevel%"=="0" goto failure

REM Package
mkdir %cd%\..\artifacts
call dotnet pack MyLibrary --configuration %config% %packversionsuffix% --output %cd%\..\artifacts
if not "%errorlevel%"=="0" goto failure

:success
exit 0

:failure
exit -1
Found an issue with the documentation on this page? We accept contributions!
Read our contribution guidance or edit this page's source on GitHub.