Skip to content

Commit 38427a6

Browse files
committed
Improve build and Copilot experience
1 parent 17e51f1 commit 38427a6

File tree

3 files changed

+212
-70
lines changed

3 files changed

+212
-70
lines changed

.github/copilot-instructions.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# GitHub Copilot Instructions (WindowsAppSDK-Samples)
2+
3+
Read scripts first; improve their headers instead of duplicating detail here.
4+
5+
## Build
6+
Use only `build.ps1` / `build.cmd`. Read `build.ps1` before invoking. It auto: detects platform, picks solutions (current dir > selected sample > all), initializes VS DevShell, restores via local nuget, emits `.binlog` per solution. Do NOT hand-roll msbuild/nuget restore logic.
7+
8+
## Versions
9+
Run `UpdateVersions.ps1` only after reading it. Get the WinAppSDK version string from: https://www.nuget.org/packages/Microsoft.WindowsAppSdk/ (stable/preview/servicing) and pass as `-WinAppSDKVersion`. If script params unclear, fix that script.
10+
11+
## Coding Guidelines
12+
- Small, focused diffs; no mass reformatting; ensure SOLID principles.
13+
- Preserve APIs, encoding, line endings, relative paths.
14+
- Support arm64 & x64 paths.
15+
- PowerShell: approved verbs; each new script has Synopsis/Description/Parameters/Examples header.
16+
- C#/C++: follow existing style; lean headers; forward declare where practical; keep samples illustrative.
17+
- Minimal necessary comments; avoid noise. Centralize user strings for localization.
18+
- Never log secrets or absolute external paths.
19+
20+
## PR Guidance
21+
- One intent per PR. Update script README/header if behavior changes.
22+
- Provide summary: what / why / validation.
23+
- Run a targeted build (e.g. `pwsh -File build.ps1 -Sample AppLifecycle`).
24+
- For version bumps: inspect at least one changed project file.
25+
- No new warnings/errors or large cosmetic churn.
26+
27+
## Design Docs (Large / Cross-Sample)
28+
Before broad edits create `DESIGN-<topic>.md`:
29+
- Single sample: `Samples/<Sample>/DESIGN-<topic>.md`
30+
- Multi-sample/shared: repo root.
31+
Include: Problem, Goals/Non-Goals, Affected Areas, Approach, Risks, Validation Plan. Reference doc in PR.
32+
33+
## When Unsure
34+
Draft a design doc or WIP PR summarizing assumptions—don't guess.
35+
36+
---
37+
Keep this file lean; source-of-truth for behavior lives in script headers.

build.cmd

Lines changed: 12 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,21 @@
11
@echo off
2+
:: Thin wrapper calling PowerShell implementation
3+
set SCRIPT_DIR=%~dp0
4+
set PS_SCRIPT=%SCRIPT_DIR%build.ps1
5+
26
if "%1"=="/?" goto :usage
37
if "%1"=="-?" goto :usage
4-
if "%VSINSTALLDIR%" == "" goto :usage
5-
6-
setlocal enabledelayedexpansion enableextensions
7-
8-
set BUILDCMDSTARTTIME=%time%
9-
10-
set platform=%1
11-
set configuration=%2
12-
set sample_filter=%3\
13-
14-
if "%platform%"=="" set platform=x64
15-
if "%configuration%"=="" set configuration=Release
16-
17-
if not exist ".\.nuget" mkdir ".\.nuget"
18-
if not exist ".\.nuget\nuget.exe" powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe"
19-
20-
set NUGET_RESTORE_MSBUILD_ARGS=/p:PublishReadyToRun=true
218

22-
for /f "delims=" %%D in ('dir /s/b samples\%sample_filter%*.sln') do (
23-
call .nuget\nuget.exe restore "%%D" -configfile Samples\nuget.config -PackagesDirectory %~dp0packages
24-
call msbuild /warnaserror /p:platform=%platform% /p:configuration=%configuration% /p:NugetPackageDirectory=%~dp0packages /bl:"%%~nD.binlog" "%%D"
25-
26-
if ERRORLEVEL 1 goto :eof
27-
)
28-
29-
:showDurationAndExit
30-
set BUILDCMDENDTIME=%time%
31-
:: Note: The '1's in this line are to convert a value like "08" to "108", since numbers which
32-
:: begin with '0' are interpreted as octal, which makes "08" and "09" invalid. Adding the
33-
:: '1's effectively adds 100 to both sides of the subtraction, avoiding this issue.
34-
:: Hours has a leading space instead of 0, so the '1's trick isn't used on that one.
35-
set /a BUILDDURATION_HRS= %BUILDCMDENDTIME:~0,2%- %BUILDCMDSTARTTIME:~0,2%
36-
set /a BUILDDURATION_MIN=1%BUILDCMDENDTIME:~3,2%-1%BUILDCMDSTARTTIME:~3,2%
37-
set /a BUILDDURATION_SEC=1%BUILDCMDENDTIME:~6,2%-1%BUILDCMDSTARTTIME:~6,2%
38-
set /a BUILDDURATION_HSC=1%BUILDCMDENDTIME:~9,2%-1%BUILDCMDSTARTTIME:~9,2%
39-
if %BUILDDURATION_HSC% lss 0 (
40-
set /a BUILDDURATION_HSC=!BUILDDURATION_HSC!+100
41-
set /a BUILDDURATION_SEC=!BUILDDURATION_SEC!-1
42-
)
43-
if %BUILDDURATION_SEC% lss 0 (
44-
set /a BUILDDURATION_SEC=!BUILDDURATION_SEC!+60
45-
set /a BUILDDURATION_MIN=!BUILDDURATION_MIN!-1
46-
)
47-
if %BUILDDURATION_MIN% lss 0 (
48-
set /a BUILDDURATION_MIN=!BUILDDURATION_MIN!+60
49-
set /a BUILDDURATION_HRS=!BUILDDURATION_HRS!-1
50-
)
51-
if %BUILDDURATION_HRS% lss 0 (
52-
set /a BUILDDURATION_HRS=!BUILDDURATION_HRS!+24
53-
)
54-
:: Add a '0' at the start to ensure at least two digits. The output will then just
55-
:: show the last two digits for each.
56-
set BUILDDURATION_HRS=0%BUILDDURATION_HRS%
57-
set BUILDDURATION_MIN=0%BUILDDURATION_MIN%
58-
set BUILDDURATION_SEC=0%BUILDDURATION_SEC%
59-
set BUILDDURATION_HSC=0%BUILDDURATION_HSC%
60-
echo ---
61-
echo Start time: %BUILDCMDSTARTTIME%. End time: %BUILDCMDENDTIME%
62-
echo Elapsed: %BUILDDURATION_HRS:~-2%:%BUILDDURATION_MIN:~-2%:%BUILDDURATION_SEC:~-2%.%BUILDDURATION_HSC:~-2%
63-
endlocal
64-
65-
goto :eof
9+
:: Forward all arguments directly; build.ps1 handles defaults/validation
10+
powershell -NoProfile -ExecutionPolicy Bypass -File "%PS_SCRIPT%" %*
11+
exit /b %ERRORLEVEL%
6612

6713
:usage
6814
echo Usage:
69-
echo This script should be run under a Visual Studio Developer Command Prompt.
70-
echo.
7115
echo build.cmd [Platform] [Configuration] [Sample]
7216
echo.
73-
echo [Platform] Either x86, x64, or arm64. Default is x64.
74-
echo [Configuration] Either Debug or Release. Default is Release.
75-
echo [Sample] The sample folder under Samples to build. If none specified, all samples are built.
76-
echo.
77-
echo If no parameters are specified, all samples are built for x64 Release.
78-
79-
exit /b /1
17+
echo (Wrapper over build.ps1)
18+
echo Platform: x86|x64|arm64 (default x64)
19+
echo Configuration: Debug|Release (default Release)
20+
echo Sample: Optional sample folder name under Samples
21+
exit /b 1

build.ps1

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<#!
2+
.SYNOPSIS
3+
Builds WindowsAppSDK Samples solutions (or local solution(s)).
4+
.DESCRIPTION
5+
PowerShell port of the original build.cmd logic.
6+
* Ensures Visual Studio Developer environment (DevShell) is initialized.
7+
* Downloads a local nuget.exe if not present and restores packages.
8+
* Builds one or more .sln files with MSBuild producing a .binlog per solution.
9+
10+
Solution discovery order:
11+
1. If -Sample is specified: build all .sln files under Samples/<Sample>.
12+
2. Else if the current working directory (where you invoked the script) contains one or more .sln files: build those only (no recursion).
13+
3. Else build all .sln files under Samples (recursive).
14+
15+
.PARAMETER Platform
16+
Target platform (x86, x64, arm64, auto). Default: auto (arm64 on ARM64 OS, else x64).
17+
.PARAMETER Configuration
18+
Build configuration (Debug or Release). Default: Release.
19+
.PARAMETER Sample
20+
Optional sample folder name (child of Samples) to restrict the build scope.
21+
.PARAMETER Help
22+
Show usage information.
23+
24+
.EXAMPLE
25+
./build.ps1
26+
(Auto-detect platform, build local solutions or all samples.)
27+
28+
.EXAMPLE
29+
./build.ps1 -Platform arm64 -Configuration Debug -Sample AppLifecycle
30+
(Build only the AppLifecycle sample solutions for arm64 Debug.)
31+
#>
32+
#[CmdletBinding()] parameters
33+
[CmdletBinding()] param(
34+
[Parameter(Position=0)] [ValidateSet('x86','x64','arm64','auto')] [string]$Platform = 'auto',
35+
[Parameter(Position=1)] [ValidateSet('Debug','Release')] [string]$Configuration = 'Release',
36+
[Parameter(Position=2)] [string]$Sample = '',
37+
[switch]$Help
38+
)
39+
40+
# --- Functions (grouped at top for clarity) ---
41+
42+
# Show-Usage: Display help/usage information.
43+
function Show-Usage {
44+
Write-Host 'Usage:'
45+
Write-Host ' build.ps1 [-Platform x86|x64|arm64|auto] [-Configuration Debug|Release] [-Sample <SampleFolder>]'
46+
Write-Host ''
47+
Write-Host 'Platform:'
48+
Write-Host ' auto (default) Detects host OS arch: arm64 on ARM64, else x64.'
49+
Write-Host ''
50+
Write-Host 'Solution selection:'
51+
Write-Host ' -Sample <Name> Build solutions under Samples/<Name>'
52+
Write-Host ' (no Sample) If current directory has .sln file(s), build only those; otherwise build all Samples solutions.'
53+
Write-Host ''
54+
Write-Host 'Examples:'
55+
Write-Host ' ./build.ps1'
56+
Write-Host ' ./build.ps1 -Platform arm64 -Configuration Debug'
57+
Write-Host ' ./build.ps1 -Sample AppLifecycle'
58+
}
59+
60+
# Initialize-VSEnvironment: Ensure VS dev environment & MSBuild are available.
61+
function Initialize-VSEnvironment {
62+
if (Get-Command msbuild -ErrorAction SilentlyContinue) { Write-Host 'MSBuild already on PATH; assuming VS env initialized.'; return }
63+
if ($env:VSINSTALLDIR) { Write-Host "VS environment already initialized: $env:VSINSTALLDIR"; return }
64+
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
65+
if (-not (Test-Path $vswhere)) { Write-Error 'vswhere.exe not found. Install Visual Studio 2022 with MSBuild.'; exit 1 }
66+
$installationPath = & $vswhere -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath | Select-Object -First 1
67+
if (-not $installationPath) { Write-Error 'Unable to locate a suitable Visual Studio installation.'; exit 1 }
68+
$devShellModule = Join-Path $installationPath 'Common7\Tools\Microsoft.VisualStudio.DevShell.dll'
69+
if (Test-Path $devShellModule) {
70+
Write-Host "Initializing VS DevShell from: $devShellModule" -ForegroundColor Yellow
71+
try { Import-Module $devShellModule -ErrorAction Stop; Enter-VsDevShell -VsInstallPath $installationPath -SkipAutomaticLocation | Out-Null }
72+
catch { Write-Warning "DevShell initialization failed: $($_.Exception.Message)" }
73+
}
74+
}
75+
76+
# Resolve-Platform: Turn 'auto' into a concrete platform value.
77+
function Resolve-Platform { param([string]$PlatformValue)
78+
if ($PlatformValue -ne 'auto') { return $PlatformValue }
79+
$detected = 'x64'
80+
try {
81+
$osArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant()
82+
switch ($osArch) { 'arm64' { $detected = 'arm64' } 'x86' { $detected = 'x86' } default { $detected = 'x64' } }
83+
if ($detected -eq 'x86' -and $env:PROCESSOR_ARCHITEW6432) { $detected = 'x64' }
84+
} catch {}
85+
Write-Host "Auto-detected platform: $detected" -ForegroundColor Yellow
86+
return $detected
87+
}
88+
89+
# Initialize-NuGetEnvironment: Create folders/download nuget.exe if needed.
90+
function Initialize-NuGetEnvironment {
91+
if (-not (Test-Path $nugetDir)) { New-Item -ItemType Directory -Path $nugetDir | Out-Null }
92+
if (-not (Test-Path $nugetExe)) { Write-Host 'Downloading nuget.exe...'; Invoke-WebRequest -UseBasicParsing https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile $nugetExe }
93+
if (-not (Test-Path $packagesDir)) { New-Item -ItemType Directory -Path $packagesDir | Out-Null }
94+
}
95+
96+
# Get-Solutions: Discover which solutions to build.
97+
function Get-Solutions { param([string]$SampleFilter)
98+
$targetRoot = $null; $solutions = @()
99+
if (-not [string]::IsNullOrWhiteSpace($SampleFilter)) {
100+
$targetRoot = Join-Path $samplesRoot $SampleFilter
101+
if (-not (Test-Path $targetRoot)) { Write-Error "Sample path not found: $targetRoot"; exit 1 }
102+
$solutions = Get-ChildItem -Path $targetRoot -Filter *.sln -Recurse | Sort-Object FullName
103+
} else {
104+
$currentDir = Get-Location
105+
$localSolutions = Get-ChildItem -Path $currentDir -Filter *.sln -File -ErrorAction SilentlyContinue | Sort-Object FullName
106+
if ($localSolutions) { Write-Host "Detected $($localSolutions.Count) solution(s) in current directory: $currentDir" -ForegroundColor Yellow; $solutions = $localSolutions }
107+
else { $targetRoot = $samplesRoot; $solutions = Get-ChildItem -Path $targetRoot -Filter *.sln -Recurse | Sort-Object FullName }
108+
}
109+
return $solutions
110+
}
111+
112+
# Restore-Solution: Perform NuGet restore for a solution (.sln) using repo nuget.exe.
113+
function Restore-Solution { param([string]$SolutionPath)
114+
Write-Host "Restoring: $SolutionPath" -ForegroundColor Cyan
115+
& $nugetExe restore $SolutionPath -ConfigFile (Join-Path $samplesRoot 'nuget.config') -PackagesDirectory $packagesDir
116+
if ($LASTEXITCODE -ne 0) { Write-Error 'NuGet restore failed.'; exit $LASTEXITCODE }
117+
}
118+
119+
# Build-Solution: Invoke MSBuild on a solution with platform/config and emit binlog.
120+
function Build-Solution { param([string]$SolutionPath,[string]$Platform,[string]$Configuration)
121+
$binlog = Join-Path (Split-Path $SolutionPath -Parent) ("{0}.binlog" -f ([IO.Path]::GetFileNameWithoutExtension($SolutionPath)))
122+
Write-Host "Building: $SolutionPath" -ForegroundColor Cyan
123+
& msbuild /warnaserror /p:Platform=$Platform /p:Configuration=$Configuration /p:NugetPackageDirectory=$packagesDir /bl:"$binlog" "$SolutionPath"
124+
if ($LASTEXITCODE -ne 0) { Write-Error 'MSBuild failed.'; exit $LASTEXITCODE }
125+
}
126+
127+
# --- Main Execution Flow ---
128+
if ($Help -or $PSBoundParameters.ContainsKey('?')) { Show-Usage; return }
129+
Initialize-VSEnvironment
130+
131+
if (-not $env:VSINSTALLDIR) { Write-Warning 'VSINSTALLDIR environment variable not found. Run this from a VS Developer PowerShell prompt.'; Show-Usage; exit 1 }
132+
133+
$Platform = Resolve-Platform -PlatformValue $Platform
134+
$repoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
135+
$nugetDir = Join-Path $repoRoot '.nuget'
136+
$nugetExe = Join-Path $nugetDir 'nuget.exe'
137+
$packagesDir = Join-Path $repoRoot 'packages'
138+
$samplesRoot = Join-Path $repoRoot 'Samples'
139+
140+
Initialize-NuGetEnvironment
141+
$solutions = Get-Solutions -SampleFilter $Sample
142+
143+
if (-not $solutions -or $solutions.Count -eq 0) {
144+
Write-Warning 'No solution files found to build.'
145+
exit 0
146+
}
147+
148+
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
149+
$startTime = Get-Date
150+
151+
Write-Host "Building $($solutions.Count) solution(s) for Platform=$Platform Configuration=$Configuration" -ForegroundColor Green
152+
foreach ($sln in $solutions) {
153+
Write-Host '---' -ForegroundColor Cyan
154+
Restore-Solution -SolutionPath $sln.FullName
155+
Build-Solution -SolutionPath $sln.FullName -Platform $Platform -Configuration $Configuration
156+
}
157+
158+
$stopwatch.Stop()
159+
$endTime = Get-Date
160+
161+
Write-Host '---'
162+
Write-Host ("Start time: {0}. End time: {1}" -f $startTime.ToLongTimeString(), $endTime.ToLongTimeString())
163+
Write-Host (" Elapsed: {0}" -f [System.String]::Format('{0:hh\\:mm\\:ss\.ff}', $stopwatch.Elapsed))

0 commit comments

Comments
 (0)