scripts/build.ps1 (332 lines of code) (raw):
<#
.SYNOPSIS
Build the Device Update product.
.DESCRIPTION
Does all of the work to build the device update product, and provides additional support,
such as static checks and building documentation.
.EXAMPLE
PS> .\scripts\build.ps1 -Clean -Type Debug -BuildUnitTests
#>
Param(
# Deletes targets before building.
[switch]$Clean,
# Build type
[ValidateSet('Release', 'RelWithDebInfo', 'Debug')]
[string]$Type = 'Debug',
# Cross-compilation target
[ValidateSet('ARM64')]
[string]$GeneratorPlatform,
# Should documentation be built?
[switch]$BuildDocumentation,
# Should unit tests be built?
[switch]$BuildUnitTests,
# Output directory. Default is {git_root}/out/{Type}
[string]$BuildOutputPath,
# Logging library to use
[string]$LogLib = 'zlog',
# Log folder to use for DU logs
# TODO(JeffMill): Change this when folder structure determined.
[string]$LogDir = '/var/log/adu',
# Non-Interactive?
[switch]$NoPrompt
)
function Show-Warning {
Param([Parameter(mandatory = $true, position = 0)][string]$Message)
Write-Host -ForegroundColor Yellow -NoNewline 'Warning:'
Write-Host " $Message"
}
function Show-Error {
Param([Parameter(mandatory = $true, position = 0)][string]$Message)
Write-Host -ForegroundColor Red -NoNewline 'Error:'
Write-Host " $Message"
}
function Show-Header {
Param([Parameter(mandatory = $true, position = 0)][string]$Message)
$sep = ":{0}:" -f (' ' * ($Message.Length + 2))
Write-Host -ForegroundColor DarkYellow -BackgroundColor DarkBlue $sep
Write-Host -ForegroundColor White -BackgroundColor DarkBlue (" {0} " -f $Message)
Write-Host -ForegroundColor DarkYellow -BackgroundColor DarkBlue $sep
''
}
function Show-Bullet {
Param([Parameter(mandatory = $true, position = 0)][string]$Message)
Write-Host -ForegroundColor Blue -NoNewline '*'
Write-Host " $Message"
}
function Show-CMakeErrors {
Param([string[]]$BuildOutput)
$regex = 'CMake Error at (?<File>.+):(?<Line>\d+)\s+\((?<Description>.+)\)'
$result = $BuildOutput | ForEach-Object {
if ($_ -match $regex) {
$matches
}
}
if ($result.Count -ne 0) {
Write-Host -ForegroundColor Red 'CMake errors:'
$result | ForEach-Object {
Show-Bullet ("{0} ({1}): {2}" -f $_.File, $_.Line, $_.Description)
}
''
}
}
function Show-CompilerErrors {
Param([string]$RootDir, [string[]]$BuildOutput)
# Parse compiler errors
# e.g. C:\du\src\logging\zlog\src\zlog.c(13,10): fatal error C1083: Cannot open include file: 'aducpal/unistd.h': No such file or directory [C:\du\out\src\logging\zlog\zlog.vcxproj]
$regex = '(?<File>.+)\((?<Line>\d+),(?<Column>\d+)\):.+error (?<Code>C\d+):\s+(?<Description>.+)\s+\[(?<Project>.+)\]'
$result = $BuildOutput | ForEach-Object {
if ($_ -match $regex) {
$matches
}
}
if ($result.Count -ne 0) {
Write-Host -ForegroundColor Red 'Compiler errors:'
$result | ForEach-Object {
Show-Bullet ("{0} ({1},{2}): {3} [{4}]" -f $_.File.SubString($RootDir.Length + 1), $_.Line, $_.Column, $_.Description, (Split-Path $_.Project -Leaf))
}
''
}
}
function Show-LinkerErrors {
Param([string]$RootDir, [string[]]$BuildOutput)
# Parse linker errors
$regex = '.+ error (?<Code>LNK\d+):\s+(?<Description>.+)\s+\[(?<Project>.+)\]'
$result = $BuildOutput | ForEach-Object {
if ($_ -match $regex) {
$matches
}
}
if ($result.Count -ne 0) {
Write-Host -ForegroundColor Red 'Linker errors:'
$result | ForEach-Object {
$project = $_.Project.SubString($RootDir.Length + 1)
Show-Bullet ("{0}: {1}" -f $project, $_.Description)
}
''
}
}
function Show-DoxygenErrors {
Param([string]$RootDir, [string[]] $BuildOutput)
# Parse Doxygen errors
$regex = '\s*(?<File>.+?):(?<Line>\d+?): warning: (?<Message>.+)'
$result = $BuildOutput | ForEach-Object {
if ($_ -match $regex) {
$matches
}
}
if ($result.Count -ne 0) {
Write-Host -ForegroundColor Red 'Doxygen errors:'
$result | ForEach-Object {
$file = $_.File.SubString($RootDir.Length + 1)
Show-Bullet ("{0} ({1}): {2}" -f $file, $_.Line, $_.Message)
}
''
}
}
function Install-DeliveryOptimization {
Param(
[Parameter(Mandatory = $true)][string]$Type,
[Parameter(Mandatory = $true)][string]$Path,
[Parameter(Mandatory = $true)][string]$Branch,
[string]$GeneratorPlatform,
[string]$Commit)
Show-Header 'Building Delivery Optimization'
"Branch: $Branch"
"Folder: $Path"
''
if (-not (Test-Path -LiteralPath $Path -PathType Container)) {
mkdir $Path | Out-Null
}
Push-Location $Path
# do_url=git@github.com:Microsoft/do-client.git
$do_url = 'https://github.com/Microsoft/do-client.git'
# Avoid "fatal: destination path '.' already exists and is not an empty directory."
if (-not (Test-Path '.git' -PathType Container)) {
git.exe clone --recursive --single-branch --branch $Branch --depth 1 $do_url .
}
if ($Commit) {
git.exe checkout $Commit
}
$build_dir = 'cmake'
# Note: bootstrap-windows.ps1 installs CMake and Python, but we already installed those,
# so not calling that script.
# Note: install-vcpkg-deps.ps1 uses vcpkg to install
# gtest:x64-windows,boost-filesystem:x64-windows,boost-program-options:x64-windows
# but we can't use "vcpkg install" as we're in vcpkg manifest mode.
if (-not (Test-Path -LiteralPath $build_dir -PathType Container)) {
mkdir $build_dir | Out-Null
}
# Bug 43044349: DO-client cmakefile not properly building correct type using cmake
# -DCMAKE_BUILD_TYPE should ultimately be removed.
$DO_CMAKE_OPTIONS = @(
'-DDO_BUILD_TESTS:BOOL=OFF',
'-DDO_INCLUDE_SDK=ON',
"-DCMAKE_BUILD_TYPE=$Type"
)
if ($GeneratorPlatform) {
$DO_CMAKE_OPTIONS += "-A$GeneratorPlatform"
}
'CMAKE_OPTIONS:'
$DO_CMAKE_OPTIONS | ForEach-Object { Show-Bullet $_ }
''
& $cmake_bin -S . -B $build_dir @DO_CMAKE_OPTIONS
& $cmake_bin --build $build_dir --config $Type --parallel
Pop-Location
''
}
#
# _ __ __ _(_)_ _
# | ' \/ _` | | ' \
# |_|_|_\__,_|_|_||_|
#
$root_dir = git.exe rev-parse --show-toplevel
if (!$root_dir) {
Show-Error 'Unable to determine repo root.'
exit 1
}
if (!$BuildOutputPath) {
if (!$GeneratorPlatform) {
$BuildOutputPath = "$root_dir/out/$Type"
}
else {
$BuildOutputPath = "$root_dir/out/$GeneratorPlatform-$Type"
}
}
if ($BuildDocumentation) {
if (-not (Get-Command 'doxygen.exe' -CommandType Application -ErrorAction SilentlyContinue)) {
Show-Error 'Can''t build documentation - doxygen is not installed or not in PATH.'
exit 1
}
if (-not (Get-Command 'dot.exe' -CommandType Application -ErrorAction SilentlyContinue)) {
Show-Error 'Can''t build documentation - dot (graphviz) is not installed or not in PATH.'
exit 1
}
}
# Set default log dir if not specified.
$runtime_dir = "$BuildOutputPath/bin"
$library_dir = "$BuildOutputPath/lib"
$cmake_bin = 'cmake.exe'
$cmake_cmd = Get-Command -CommandType Application cmake.exe
$cmake_bin = $cmake_cmd.Path
Write-Verbose "Using CMake path: $cmake_bin"
$PlatformLayer = 'windows'
# Output banner
''
Show-Header 'Building ADU Agent'
Show-Bullet "Clean build: $Clean"
Show-Bullet "Documentation: $BuildDocumentation"
Show-Bullet "Platform layer: $PlatformLayer"
Show-Bullet "Build type: $Type"
Show-Bullet "Log directory: $LogDir"
Show-Bullet "Logging library: $LogLib"
Show-Bullet "Output directory: $BuildOutputPath"
Show-Bullet "Build unit tests: $BuildUnitTests"
Show-Bullet "CMake: $cmake_bin"
Show-Bullet ("CMake version: {0}" -f (& $cmake_bin --version | Select-String '^cmake version (.*)$').Matches.Groups[1].Value)
''
# Store options for CMAKE in an array
$DU_CMAKE_OPTIONS = @(
"-DADUC_BUILD_DOCUMENTATION:BOOL=$BuildDocumentation",
"-DADUC_BUILD_UNIT_TESTS:BOOL=$BuildUnitTests",
"-DADUC_LOG_FOLDER:STRING=$LogDir",
"-DADUC_LOGGING_LIBRARY:STRING=$LogLib",
"-DADUC_PLATFORM_LAYER:STRING=$PlatformLayer",
"-DCMAKE_BUILD_TYPE:STRING=$Type",
"-DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON",
# Note: Multi-configuration generators (Visual Studio, Xcode, Ninja Multi-Config) append a
# per-configuration subdirectory to the specified directory unless a generator expression is used.
"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:STRING=$library_dir",
"-DCMAKE_RUNTIME_OUTPUT_DIRECTORY:STRING=$runtime_dir"
)
if (-not $Clean) {
# -ErrorAction SilentlyContinue doesn't work on Select-String
try {
if (-not (Select-String 'CMAKE_PROJECT_NAME:' "$BuildOutputPath/CMakeCache.txt")) {
$Clean = $true
}
}
catch {
$Clean = $true
}
if ($Clean) {
Show-Warning 'CMake cache seems out of date - forcing clean build.'
''
$Clean = $true
}
}
if ($Clean) {
if (-not $NoPrompt) {
$decision = $Host.UI.PromptForChoice(
'Clean Build',
'Are you sure?',
@(
(New-Object Management.Automation.Host.ChoiceDescription '&Yes', 'Perform clean build'),
(New-Object Management.Automation.Host.ChoiceDescription '&No', 'Stop build')
),
1)
if ($decision -ne 0) {
exit 1
}
''
}
Show-Header 'Cleaning repo'
if (Test-Path $BuildOutputPath) {
Show-Bullet $BuildOutputPath
Remove-Item -Force -Recurse -LiteralPath $BuildOutputPath
}
''
}
mkdir -Path $BuildOutputPath -Force | Out-Null
if ($Clean) {
Show-Header 'Generating Makefiles'
# show every find_package call (vcpkg specific):
# $DU_CMAKE_OPTIONS += '-DVCPKG_TRACE_FIND_PACKAGE:BOOL=ON'
# Verbose output (very verbose, but useful!):
# $DU_CMAKE_OPTIONS += '--trace-expand'
# See cmake dependencies (very verbose):
# $DU_CMAKE_OPTIONS += '--debug-output'
# See compiler output at build time:
# $DU_CMAKE_OPTIONS += '-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON'
# See library search output:
# $DU_CMAKE_OPTIONS += '-DCMAKE_EXE_LINKER_FLAGS=/VERBOSE:LIB'
if ($GeneratorPlatform) {
$DU_CMAKE_OPTIONS += "-A$GeneratorPlatform"
}
'CMAKE_OPTIONS:'
$DU_CMAKE_OPTIONS | ForEach-Object { Show-Bullet $_ }
''
& $cmake_bin -S "$root_dir" -B $BuildOutputPath @DU_CMAKE_OPTIONS
$ret_val = $LASTEXITCODE
if ($ret_val -ne 0) {
Write-Error "ERROR: CMake failed (Error $ret_val)"
''
Show-CMakeErrors -BuildOutput $cmake_output
exit $ret_val
}
''
}
#
# Delivery Optimization
#
# Reusing ".vcpkg-installed" folder ... why not?
$DoPath = "{0}/.vcpkg-installed/do-client" -f (git.exe rev-parse --show-toplevel)
if ($GeneratorPlatform) {
$DoPath += "-$GeneratorPlatform"
}
Install-DeliveryOptimization -Type $Type -Path $DoPath -Branch 'v1.0.1' -GeneratorPlatform $GeneratorPlatform
Show-Header 'Building Product'
& $cmake_bin --build $BuildOutputPath --config $Type --parallel 2>&1 | Tee-Object -Variable build_output
$ret_val = $LASTEXITCODE
''
if ($BuildDocumentation) {
Show-DoxygenErrors -RootDir $root_dir -BuildOutput $build_output
}
if ($ret_val -ne 0) {
Write-Host -ForegroundColor Red "ERROR: Build failed (Error $ret_val)"
''
Show-CMakeErrors -BuildOutput $build_output
Show-CompilerErrors -RootDir $root_dir -BuildOutput $build_output
Show-LinkerErrors -RootDir $root_dir -BuildOutput $build_output
exit $ret_val
}
exit $ret_val