Files
switchnote/build.ps1
T
2026-10-29 03:55:40 +00:00

113 lines
4.9 KiB
PowerShell

# Build the Switch NRO from Windows using devkitPro's MSYS2 toolchain.
param(
[string]$DevkitProPath = '',
[switch]$KeepBuild
)
$ErrorActionPreference = 'Stop'
function Quote-Bash([string]$Value) {
return "'" + $Value.Replace("'", "'\''") + "'"
}
$projectRoot = [IO.Path]::GetFullPath($PSScriptRoot)
if (-not $DevkitProPath) {
if ($env:DEVKITPRO -and (Test-Path -LiteralPath $env:DEVKITPRO)) {
$DevkitProPath = $env:DEVKITPRO
} else {
$DevkitProPath = 'C:\devkitPro'
}
}
$devkitRoot = [IO.Path]::GetFullPath($DevkitProPath)
$bash = Join-Path $devkitRoot 'msys2\usr\bin\bash.exe'
$cygpath = Join-Path $devkitRoot 'msys2\usr\bin\cygpath.exe'
$rules = Join-Path $devkitRoot 'libnx\switch_rules'
$compiler = Join-Path $devkitRoot 'devkitA64\bin\aarch64-none-elf-g++.exe'
$pkgConfig = Join-Path $devkitRoot 'portlibs\switch\bin\aarch64-none-elf-pkg-config'
foreach ($required in @($bash, $cygpath, $rules, $compiler, $pkgConfig)) {
if (-not (Test-Path -LiteralPath $required)) {
throw "Missing Switch build dependency: $required`nInstall devkitPro's Switch toolchain and switch-pkg-config packages; see README.md."
}
}
if ($devkitRoot -match '\s') {
throw 'The devkitPro installation path contains spaces; install it at C:\devkitPro or choose a path without spaces.'
}
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
if ($tempRoot -match '\s') {
throw 'The temporary path contains spaces. Set TEMP to a writable directory without spaces before building.'
}
# The upstream devkitPro Makefile pattern requires a path without spaces, so
# compile a fresh copy in the temp folder.
$stage = Join-Path $tempRoot ('switchnote-build-' + [Guid]::NewGuid().ToString('N'))
$resolvedStage = [IO.Path]::GetFullPath($stage)
$tempPrefix = $tempRoot.TrimEnd('\', '/') + [IO.Path]::DirectorySeparatorChar
if (-not $resolvedStage.StartsWith($tempPrefix, [StringComparison]::OrdinalIgnoreCase) -or
-not ([IO.Path]::GetFileName($resolvedStage) -match '^switchnote-build-[0-9a-f]{32}$')) {
throw "Unexpected temporary build path: $resolvedStage"
}
New-Item -ItemType Directory -Path $stage | Out-Null
try {
foreach ($entry in @('Makefile', 'source', 'include', 'third_party', 'romfs')) {
$source = Join-Path $projectRoot $entry
if (-not (Test-Path -LiteralPath $source)) {
throw "Project file or directory is missing: $source"
}
Copy-Item -LiteralPath $source -Destination (Join-Path $stage $entry) -Recurse
}
$rootJpeg = Join-Path $projectRoot 'icon.jpg'
$rootPng = Join-Path $projectRoot 'icon.png'
if (Test-Path -LiteralPath $rootPng) {
try {
Add-Type -AssemblyName System.Drawing
$inputImage = [Drawing.Image]::FromFile($rootPng)
$bitmap = [Drawing.Bitmap]::new(256, 256)
$graphics = [Drawing.Graphics]::FromImage($bitmap)
try {
$graphics.Clear([Drawing.Color]::White)
$graphics.InterpolationMode = [Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graphics.DrawImage($inputImage, 0, 0, 256, 256)
$bitmap.Save((Join-Path $stage 'icon.jpg'), [Drawing.Imaging.ImageFormat]::Jpeg)
} finally {
$graphics.Dispose()
$bitmap.Dispose()
$inputImage.Dispose()
}
} catch {
Write-Warning "Could not convert icon.png to JPEG; the default libnx icon will be used. $($_.Exception.Message)"
}
} elseif (Test-Path -LiteralPath $rootJpeg) {
Copy-Item -LiteralPath $rootJpeg -Destination (Join-Path $stage 'icon.jpg')
}
$devkitUnix = (& $cygpath -u $devkitRoot).Trim()
if ($LASTEXITCODE -ne 0 -or -not $devkitUnix) { throw 'cygpath could not translate the devkitPro path.' }
$stageUnix = (& $cygpath -u $stage).Trim()
if ($LASTEXITCODE -ne 0 -or -not $stageUnix) { throw 'cygpath could not translate the build path.' }
$devkitQuoted = Quote-Bash $devkitUnix
$stageQuoted = Quote-Bash $stageUnix
$command = 'export DEVKITPRO={0}; export DEVKITA64="$DEVKITPRO/devkitA64"; export PATH="$DEVKITA64/bin:$DEVKITPRO/tools/bin:$DEVKITPRO/portlibs/switch/bin:$PATH"; cd {1}; make -j2' -f $devkitQuoted, $stageQuoted
& $bash -lc $command
if ($LASTEXITCODE -ne 0) { throw "Switch build failed with exit code $LASTEXITCODE." }
$builtNro = Join-Path $stage 'switchnote.nro'
if (-not (Test-Path -LiteralPath $builtNro)) { throw "Build reported success but no NRO exists at $builtNro" }
$destination = Join-Path $projectRoot 'switchnote.nro'
Copy-Item -LiteralPath $builtNro -Destination $destination -Force
Write-Host "Built $destination"
} finally {
if ($KeepBuild) {
Write-Host "Temporary build retained at $stage"
} elseif (Test-Path -LiteralPath $stage) {
# This directory was created above and its full path was checked against TEMP.
Remove-Item -LiteralPath $stage -Recurse -Force
}
}