Add SDK v1.0.0

This commit is contained in:
alejo committed 2026-10-24 08:05:45 -03:00
commit 40688938ef
76 files changed
+11805

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
# Shared helpers for breeze/scripts/*.sh. Sourced, never run directly (hence the
# leading underscore and no executable bit); every script sources it after
# it has cd'd to the project root, because both helpers below run
# `cmake -P breeze/cmake/<something>.cmake` relative to that.
#
# There is deliberately no Windows twin of this file: breeze/scripts/*.bat each keep
# their own copy of these two small wrappers as labels (:read_config and
# :resolve_build_dir), because sharing labels between .bat files is more
# fragile than repeating them. What the wrappers *ask* is answered by the
# same cmake scripts either way, so no logic is duplicated -- only the glue.
# Reads one value out of project/Configuration.cmake -- this project's single
# source of truth -- via breeze/cmake/PrintConfig.cmake, instead of hardcoding a
# second copy of a default that can silently drift out of sync. See that
# file for exactly what this strips off of its output.
_pmn_read_config() {
cmake "-DPRINT_VARS=$1" -P breeze/cmake/PrintConfig.cmake | sed -E 's/^-- [^=]*=//'
}
# _pmn_resolve_build_dir [<name>]
#
# Prints the build directory to use. With a <name> (a preset, or "default")
# that is simply BUILD_OUTPUT_DIR/<name>; without one it is discovered: the
# directory breeze/scripts/build.sh built last, else the only one that exists. If
# neither works, the reason goes to stderr and the return status is 1.
#
# The logic itself is breeze/cmake/BuildDir.cmake's -- see it for the details and
# the one-line answer protocol parsed here.
_pmn_resolve_build_dir() {
local answer
# run.sh never needed cmake before it had to ask which build directory to
# use, so say what's wrong instead of letting the shell's own "command not
# found" and an empty answer speak for it.
if ! command -v cmake >/dev/null 2>&1; then
echo "error: cmake not found on PATH -- it is needed to work out which build directory to use." >&2
echo " Put it on PATH, or set BUILD_DIR to the directory explicitly, e.g. BUILD_DIR=out/build/<preset>." >&2
return 1
fi
answer="$(cmake "-DPMN_NAME=${1:-}" -P breeze/cmake/BuildDir.cmake | sed -E 's/^-- //')"
case "$answer" in
BUILD_DIR=*)
printf '%s\n' "${answer#BUILD_DIR=}"
;;
ERROR=*)
echo "error: ${answer#ERROR=}" >&2
return 1
;;
*)
echo "error: breeze/cmake/BuildDir.cmake did not answer as expected: ${answer}" >&2
return 1
;;
esac
}
# _pmn_record_build_dir <name>
#
# Remembers <name> as the build directory built last, so the next
# `breeze/scripts/run.sh` / `install.sh` / `docs.sh` without a --preset uses it.
_pmn_record_build_dir() {
cmake "-DPMN_NAME=$1" -DPMN_RECORD=ON -P breeze/cmake/BuildDir.cmake >/dev/null
}
# _pmn_explain_configure_failure <configure-log> [<build-directory>]
#
# Called by breeze/scripts/build.sh after a failed configure. When it was a cross
# build with no target sysroot (the toolchain says so in the first lines of
# its output -- breeze/cmake/toolchains/_common.cmake, pmn_linux_toolchain) AND
# something could not be found, says what that means: the dependency asked
# for a system library (X11, OpenGL, ALSA, ...) that only a target sysroot
# can provide. Silent in every other case -- not every "Could NOT find" is
# this one, and the configure's own error is already on screen.
_pmn_explain_configure_failure() {
[ -r "$1" ] || return 0
grep -q 'Cross build without a target sysroot' "$1" || return 0
grep -qE 'Could NOT find|Could not find|No package .* found' "$1" || return 0
cat >&2 <<'EOF'
==> Configure failed in a cross build that has no target sysroot, and something
it needs was not found. That is most likely this: a cross build looks only
in the target's own root (the cross compiler's /usr/<triplet>), never at the
host's libraries -- those are for the wrong architecture -- and that root has
the C/C++ runtime and nothing else. A library such as X11, OpenGL or ALSA is
there only if you provide it:
* put a root filesystem for the target, with those libraries installed, in
.cache/sysroots/linux-<arch> (arm64, arm32 or x64), or
* pass -DCMAKE_SYSROOT=<dir> to breeze/scripts/build.sh.
Then delete the build directory before building again: it keeps what it detected
about the compiler without the sysroot, and a configure over that stops.
EOF
if [ -n "${2:-}" ]; then
echo " rm -rf $2" >&2
fi
echo ' breeze/README.md, "Cross builds and system libraries", has the steps.' >&2
}
# On macOS, Homebrew's llvm formula -- the one that ships clang-tidy -- is
# keg-only: installed, but deliberately not linked into PATH, so a script
# looking for `clang-tidy` can't find it. Add its bin directory to the END of
# PATH (never the front: Apple's clang must stay the one builds use). A
# no-op everywhere else, and when llvm isn't installed.
_pmn_extend_tool_path() {
[ "$(uname -s)" = Darwin ] || return 0
local d
for d in /opt/homebrew/opt/llvm/bin /usr/local/opt/llvm/bin; do
if [ -d "$d" ]; then
case ":$PATH:" in
*":$d:"*) ;;
*) PATH="$PATH:$d" ;;
esac
return 0
fi
done
return 0
}
+697
View File
@@ -0,0 +1,697 @@
<#
.SYNOPSIS
Interactive installer for the tools this project builds with. Windows;
called by breeze\scripts\setup.bat (Linux and macOS have breeze/scripts/_tools.sh).
.DESCRIPTION
Checks each tool, and for every one that is missing asks -- ONE TOOL AT
A TIME -- whether to install it, showing the exact command first. Nothing
is installed without an explicit yes (or -Yes / -All).
Required Git, CMake 3.25+, and a C++ compiler: at least one of
Visual Studio Build Tools (MSVC) or MinGW-w64 (via MSYS2)
Optional LLVM (clang-cl, Clang, clang-format, clang-tidy), the MSVC
ARM64 build tools (to build the windows-arm64-msvc and
-clang-cl presets), and the other MSYS2 toolchains (32-bit
MinGW, CLANG64)
Installs go through winget (Windows Package Manager, part of the App
Installer that ships with Windows 10 1809+ and Windows 11). Two things
winget does not do for you, and this script does:
* put CMake and LLVM on PATH -- their installers leave it to you, and
every guide that runs `winget install Kitware.CMake` ends with "now
add C:\Program Files\CMake\bin to PATH by hand". A tool found in its
usual folder but not on PATH is added to this session's PATH (which
is handed back to setup.bat through -PathOut so the rest of setup
can use it) and, if you agree, to your user PATH permanently.
* update a fresh MSYS2, whose package databases are stale (twice, as
MSYS2 itself recommends -- updating its runtime asks for a restart)
before installing MinGW-w64 into it.
Written for Windows PowerShell 5.1 as much as for PowerShell 7: no ??,
no ternary, no && / ||, nested Join-Path (5.1 has no -AdditionalChildPath),
and plain ASCII only (5.1 reads a BOM-less file as the ANSI code page).
.PARAMETER Yes
Install the REQUIRED tools without asking. For scripts and CI. Without a
terminal and without this, nothing is asked and nothing is installed.
.PARAMETER All
Also install the optional tools without asking (implies -Yes).
.PARAMETER Check
Only report what is and is not installed; change nothing. Exits 1 if a
required tool is missing.
.PARAMETER PathOut
File to write the directories that were added to this session's PATH to,
one per line, so the calling batch file can add them to its own.
.NOTES
Exit status: 0 if every required tool is present at the end, 1 if not.
#>
[CmdletBinding()]
param(
[switch]$Yes,
[switch]$All,
[switch]$Check,
[string]$PathOut = ''
)
if ($All) { $Yes = $true }
# Oldest CMake this project supports (cmake_minimum_required in
# CMakeLists.txt, breeze/cmake/Breeze.cmake, and the presets files' cmakeMinimumRequired).
$script:MinCmake = New-Object System.Version 3, 25
# The Visual Studio Build Tools package. 'Microsoft.VisualStudio.BuildTools'
# (no year) is the newest release; the 2022 one is the long-established,
# documented path, so it is the default -- change the id here to prefer
# the newer one.
$script:MsvcWingetId = 'Microsoft.VisualStudio.2022.BuildTools'
$script:MsvcOverride = '--passive --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended'
# Answers that carry over from one prompt to the next.
$script:AnsAll = $false # "a": yes to everything still to come
$script:AnsSkipOptional = $false # "s": no to every OPTIONAL tool still to come
$script:AnsQuit = $false # "q": stop asking altogether
# Directories added to this session's PATH, for setup.bat to pick up.
$script:PathAdds = New-Object System.Collections.ArrayList
# ---------------------------------------------------------------------------
# Small helpers
# ---------------------------------------------------------------------------
function Say([string]$Text = '') { Write-Host $Text }
function Note([string]$Text) { Write-Host $Text -ForegroundColor DarkGray }
function Good([string]$Text) { Write-Host $Text -ForegroundColor Green }
function Warn([string]$Text) { Write-Host $Text -ForegroundColor Yellow }
function Bad([string]$Text) { Write-Host $Text -ForegroundColor Red }
# Nested Join-Path: Windows PowerShell 5.1's Join-Path takes only two parts.
function Join-Parts {
$p = [string]$args[0]
for ($i = 1; $i -lt $args.Count; $i++) { $p = Join-Path $p ([string]$args[$i]) }
return $p
}
function Get-ProgramFiles { return $env:ProgramFiles }
function Get-ProgramFilesX86 { return ${env:ProgramFiles(x86)} }
# MSYS2's install folder: C:\msys64 unless MSYS2_ROOT says otherwise.
function Get-Msys2Root {
if ($env:MSYS2_ROOT) { return $env:MSYS2_ROOT }
$drive = $env:SystemDrive
if (-not $drive) { $drive = 'C:' }
return (Join-Path ($drive + '\') 'msys64')
}
# The first executable called $Name on PATH, or one in $FallbackDirs (the
# folder the installer puts it in even when it doesn't touch PATH), or $null.
function Find-Exe([string]$Name, [string[]]$FallbackDirs) {
$cmd = Get-Command $Name -ErrorAction SilentlyContinue | Select-Object -First 1
if ($cmd) { return $cmd.Source }
foreach ($dir in $FallbackDirs) {
if (-not $dir) { continue }
foreach ($ext in @('.exe', '')) {
$candidate = Join-Path $dir ($Name + $ext)
if (Test-Path -LiteralPath $candidate -PathType Leaf) { return $candidate }
}
}
return $null
}
function New-Status([string]$State, [string]$Detail = '') {
return [pscustomobject]@{ State = $State; Detail = $Detail }
}
# ---------------------------------------------------------------------------
# PATH (session and user)
#
# Wrapped in functions so a test can replace the two that touch the real
# registry.
# ---------------------------------------------------------------------------
function Get-UserPathValue { return [Environment]::GetEnvironmentVariable('Path', 'User') }
function Set-UserPathValue([string]$Value) { [Environment]::SetEnvironmentVariable('Path', $Value, 'User') }
function Get-MachinePathValue { return [Environment]::GetEnvironmentVariable('Path', 'Machine') }
function Split-PathList([string]$List) {
if (-not $List) { return @() }
$sep = [System.IO.Path]::PathSeparator
$items = @()
foreach ($part in $List.Split($sep)) {
$trimmed = $part.Trim().TrimEnd('\', '/')
if ($trimmed) { $items += $trimmed }
}
return $items
}
function Test-InList([string]$List, [string]$Dir) {
$want = $Dir.TrimEnd('\', '/')
foreach ($item in (Split-PathList $List)) {
if ($item -ieq $want) { return $true }
}
return $false
}
function Test-OnSessionPath([string]$Dir) { return (Test-InList $env:Path $Dir) }
function Test-OnPersistentPath([string]$Dir) {
return ((Test-InList (Get-UserPathValue) $Dir) -or (Test-InList (Get-MachinePathValue) $Dir))
}
function Add-ToSessionPath([string]$Dir) {
if (Test-OnSessionPath $Dir) { return }
$sep = [System.IO.Path]::PathSeparator
$current = $env:Path
if (-not $current) { $current = '' }
$env:Path = $current.TrimEnd($sep) + $sep + $Dir
[void]$script:PathAdds.Add($Dir)
}
function Add-ToUserPath([string]$Dir) {
$current = Get-UserPathValue
$sep = [System.IO.Path]::PathSeparator
if ($current) { $new = $current.TrimEnd($sep) + $sep + $Dir } else { $new = $Dir }
Set-UserPathValue $new
}
# ---------------------------------------------------------------------------
# Detection
# ---------------------------------------------------------------------------
function Get-WingetStatus {
if (Get-Command winget -ErrorAction SilentlyContinue) { return (New-Status 'ok' 'winget') }
return (New-Status 'missing')
}
function Get-GitStatus {
$exe = Find-Exe 'git' @((Join-Parts (Get-ProgramFiles) 'Git' 'cmd'))
if (-not $exe) { return (New-Status 'missing') }
$ver = ''
try { $ver = (& $exe --version 2>$null | Select-Object -First 1) -replace '^git version ', '' } catch { }
return (New-Status 'ok' $ver)
}
function Get-CmakeStatus {
$exe = Find-Exe 'cmake' @((Join-Parts (Get-ProgramFiles) 'CMake' 'bin'))
if (-not $exe) { return (New-Status 'missing') }
$ver = ''
try {
$first = & $exe --version 2>$null | Select-Object -First 1
if ($first -match '(\d+\.\d+(\.\d+)?)') { $ver = $Matches[1] }
} catch { }
if (-not $ver) { return (New-Status 'ok' '') }
$parsed = $null
if ([System.Version]::TryParse($ver, [ref]$parsed) -and $parsed -lt $script:MinCmake) {
return (New-Status 'old' $ver)
}
return (New-Status 'ok' $ver)
}
# MSVC: the Visual Studio Installer ships vswhere.exe, which knows whether
# the C++ x86/x64 tools are installed in ANY Visual Studio or Build Tools.
function Get-MsvcStatus {
$pf86 = Get-ProgramFilesX86
if (-not $pf86) { return (New-Status 'missing') }
$vswhere = Join-Parts $pf86 'Microsoft Visual Studio' 'Installer' 'vswhere.exe'
if (-not (Test-Path -LiteralPath $vswhere -PathType Leaf)) { return (New-Status 'missing') }
$where = $null
try {
$where = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 2>$null | Select-Object -First 1
} catch { }
if ($where) { return (New-Status 'ok' ([string]$where)) }
return (New-Status 'missing')
}
# The ARM64 half of the Visual Studio tools: what an x64 machine cross-compiles
# the windows-arm64-* presets with (the x64-hosted ARM64 compiler and the ARM64
# runtime libraries), and an ARM64 machine builds them natively with. vswhere
# again, asking for both components: the x86/x64 one is what the ARM64 tools run
# on from an x64 machine.
function Get-MsvcArm64Status {
$pf86 = Get-ProgramFilesX86
if (-not $pf86) { return (New-Status 'missing') }
$vswhere = Join-Parts $pf86 'Microsoft Visual Studio' 'Installer' 'vswhere.exe'
if (-not (Test-Path -LiteralPath $vswhere -PathType Leaf)) { return (New-Status 'missing') }
$where = $null
try {
$where = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 Microsoft.VisualStudio.Component.VC.Tools.ARM64 -property installationPath 2>$null | Select-Object -First 1
} catch { }
if ($where) { return (New-Status 'ok' ([string]$where)) }
return (New-Status 'missing')
}
function Get-LlvmStatus {
$exe = Find-Exe 'clang-cl' @((Join-Parts (Get-ProgramFiles) 'LLVM' 'bin'))
if (-not $exe) { return (New-Status 'missing') }
return (New-Status 'ok' (Split-Path -Parent $exe))
}
function Get-Msys2Status {
$bash = Join-Parts (Get-Msys2Root) 'usr' 'bin' 'bash.exe'
if (Test-Path -LiteralPath $bash -PathType Leaf) { return (New-Status 'ok' (Get-Msys2Root)) }
return (New-Status 'missing')
}
function Get-MingwStatus([string]$Subdir) {
$gpp = Join-Parts (Get-Msys2Root) $Subdir 'bin' 'g++.exe'
if (Test-Path -LiteralPath $gpp -PathType Leaf) { return (New-Status 'ok' (Split-Path -Parent $gpp)) }
return (New-Status 'missing')
}
function Get-Clang64Status {
$clang = Join-Parts (Get-Msys2Root) 'clang64' 'bin' 'clang++.exe'
if (Test-Path -LiteralPath $clang -PathType Leaf) { return (New-Status 'ok' (Split-Path -Parent $clang)) }
return (New-Status 'missing')
}
# ---------------------------------------------------------------------------
# Asking
# ---------------------------------------------------------------------------
function Test-CanAsk { return (-not [Console]::IsInputRedirected) }
# One line from the user, or $null at end of input. A function of its own so
# a test can stand in for the console.
function Read-Answer { return [Console]::ReadLine() }
# Would an OPTIONAL tool be offered (asked about, or installed by -All) now?
function Test-OptionalWanted {
if ($script:AnsQuit -or $script:AnsSkipOptional) { return $false }
if ($All -or $script:AnsAll) { return $true }
return (Test-CanAsk)
}
# Returns $true for "install it". Sets $script:Ans* for answers that outlast
# one prompt.
function Confirm-Install([string]$Kind, [string]$Question) {
if ($script:AnsQuit) { return $false }
if ($script:AnsAll) { return $true }
if ($Kind -eq 'optional' -and $script:AnsSkipOptional) { return $false }
# Flags answer for you...
if ($Kind -eq 'required' -and $Yes) { return $true }
if ($All) { return $true }
# ...and with no terminal there is nobody to ask.
if (-not (Test-CanAsk)) {
Note ' (not asking: no terminal. Re-run interactively, or pass --yes / --all.)'
return $false
}
if ($Kind -eq 'required') { $prompt = " $Question [Y/n/a/q] " } else { $prompt = " $Question [y/N/a/s/q] " }
while ($true) {
Write-Host $prompt -NoNewline
$answer = Read-Answer
if ($null -eq $answer) {
# End of input (Ctrl-Z): stop asking rather than loop on it.
Write-Host ''
$script:AnsQuit = $true
return $false
}
switch ($answer.Trim().ToLowerInvariant()) {
{ $_ -in @('y', 'yes') } { return $true }
{ $_ -in @('n', 'no') } { return $false }
'' { return ($Kind -eq 'required') }
{ $_ -in @('a', 'all') } { $script:AnsAll = $true; return $true }
{ $_ -in @('s', 'skip') } { $script:AnsSkipOptional = $true; return $false }
{ $_ -in @('q', 'quit') } { $script:AnsQuit = $true; return $false }
default {
if ($Kind -eq 'required') {
Note ' y = install n = not this one a = install everything left q = stop asking'
} else {
Note ' y = install n = not this one a = install everything left s = skip the rest of the optional tools q = stop asking'
}
}
}
}
}
# ---------------------------------------------------------------------------
# Installing
# ---------------------------------------------------------------------------
function Invoke-Winget([string]$Id, [string]$Override = '') {
$winget = @('install', '--id', $Id, '-e', '--source', 'winget',
'--accept-package-agreements', '--accept-source-agreements')
$shown = 'winget ' + ($winget -join ' ')
if ($Override) {
$winget += @('--override', $Override)
$shown += ' --override "' + $Override + '"'
}
Write-Host (' $ ' + $shown) -ForegroundColor DarkGray
& winget @winget
return ($LASTEXITCODE -eq 0)
}
# Adds a component to the Visual Studio (or Build Tools) install Get-MsvcStatus
# found, with the Visual Studio Installer's own `modify` command: the documented
# way to add a component to an existing install. It needs administrator rights,
# so the installer is started elevated (the usual UAC prompt) and waited for.
# Exit code 3010 means "done, restart needed", which is still a success.
function Install-MsvcComponent([string]$ComponentId) {
$pf86 = Get-ProgramFilesX86
if (-not $pf86) { return $false }
$setup = Join-Parts $pf86 'Microsoft Visual Studio' 'Installer' 'setup.exe'
$vswhere = Join-Parts $pf86 'Microsoft Visual Studio' 'Installer' 'vswhere.exe'
if (-not ((Test-Path -LiteralPath $setup -PathType Leaf) -and (Test-Path -LiteralPath $vswhere -PathType Leaf))) {
Bad ' The Visual Studio Installer was not found.'
return $false
}
$installPath = $null
try {
$installPath = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 2>$null | Select-Object -First 1
} catch { }
if (-not $installPath) {
Bad ' No Visual Studio install with the C++ tools was found to add it to.'
return $false
}
$installerArgs = @('modify', '--installPath', ('"' + $installPath + '"'), '--add', $ComponentId, '--passive', '--norestart')
Write-Host (' $ "' + $setup + '" ' + ($installerArgs -join ' ')) -ForegroundColor DarkGray
try {
$proc = Start-Process -FilePath $setup -ArgumentList $installerArgs -Verb RunAs -Wait -PassThru
} catch {
Bad (' Could not start the Visual Studio Installer: ' + $_.Exception.Message)
return $false
}
return (($proc.ExitCode -eq 0) -or ($proc.ExitCode -eq 3010))
}
# Whether Update-Msys2Base has already run (successfully or not) in this
# process, so installing several MSYS2-hosted toolchains in one setup.bat
# run (e.g. -All pulling in mingw64, mingw32 AND clang64) only pays for the
# update dance -- the slow, disruptive part -- once, not once per package.
$script:Msys2BaseUpdated = $false
# Brings the MSYS2 installation's OWN packages (msys2-runtime, bash, pacman,
# filesystem, ...) up to date, which a fresh install -- what a winget install
# just produced -- badly needs: its package databases are whatever shipped
# in that installer image.
#
# This is exactly the three-step sequence msys2/setup-msys2 -- the official
# GitHub Action, and about as battle-tested a reference as this gets --
# uses, confirmed by reading its source (main.js): an update whose result is
# NOT trusted, an explicit taskkill, then a second, final update whose
# result IS. It exists because updating msys2-runtime/bash/pacman while
# they are the very things running the update can end with MSYS2 force-
# closing every one of its own processes for safety (mid-transaction, to
# avoid the heap corruption a live-running old copy of msys2-runtime would
# otherwise risk) rather than the update finishing normally -- documented
# behavior (msys2.org/docs/updating), not a bug in this project, and not
# something --noconfirm reliably suppresses in every MSYS2-pacman version
# (see msys2/MSYS2-packages#1141, "Updating MSYS2 unattended is broken").
# Running the update a second time is what actually finishes the job (per
# that same documentation: "you need to start a new terminal and run the
# update again"); the taskkill in between is what makes that safe to do
# from the SAME script, back-to-back, without a real terminal to close --
# and it must run from OUTSIDE the MSYS2 process tree to have any effect,
# i.e. from PowerShell, exactly as done here (see the discussion on
# msys2/MSYS2-packages#4340, where MSYS2's own maintainers say so).
# `-FI "MODULES eq msys-2.0.dll"` targets any process with that DLL loaded,
# not any process literally named bash.exe/pacman.exe -- so it cannot catch
# an unrelated bash.exe (WSL's, Git for Windows', a plain MSYS bash someone
# else is using) that never loaded MSYS2's runtime in the first place.
# `--overwrite '*'` on every pacman call, also lifted from that same
# reference implementation, avoids pacman refusing an update over an
# unimportant file conflict -- a real, if uncommon, way this could
# otherwise still stall unattended.
function Update-Msys2Base {
if ($script:Msys2BaseUpdated) { return $true }
$bash = Join-Parts (Get-Msys2Root) 'usr' 'bin' 'bash.exe'
if (-not (Test-Path -LiteralPath $bash -PathType Leaf)) {
Bad ' MSYS2 is not installed at the expected place -- install it first.'
return $false
}
Write-Host ' $ bash -lc "pacman --noconfirm --overwrite ''*'' -Syuu" (updates MSYS2 itself; may need to close and reopen)' -ForegroundColor DarkGray
& $bash -lc "pacman --noconfirm --overwrite '*' -Syuu"
Write-Host ' $ taskkill /F /FI "MODULES eq msys-2.0.dll" (clears any MSYS2 process the update above force-closed)' -ForegroundColor DarkGray
& taskkill /F /FI 'MODULES eq msys-2.0.dll' 2>$null | Out-Null
Write-Host ' $ bash -lc "pacman --noconfirm --overwrite ''*'' -Syuu" (final update)' -ForegroundColor DarkGray
& $bash -lc "pacman --noconfirm --overwrite '*' -Syuu"
$script:Msys2BaseUpdated = $true
return ($LASTEXITCODE -eq 0)
}
function Invoke-Msys2Packages([string[]]$Packages) {
$bash = Join-Parts (Get-Msys2Root) 'usr' 'bin' 'bash.exe'
if (-not (Test-Path -LiteralPath $bash -PathType Leaf)) {
Bad ' MSYS2 is not installed at the expected place -- install it first.'
return $false
}
if (-not (Update-Msys2Base)) {
Bad ' Updating MSYS2 itself failed -- see the output above. Try re-running breeze\scripts\setup.bat once MSYS2 has settled (it may need a moment after force-closing its own processes), or update it by hand in an MSYS2 terminal: pacman -Syuu (repeat until it reports nothing to do).'
return $false
}
$list = $Packages -join ' '
Write-Host (' $ ' + $bash + ' -lc "pacman -S --needed --noconfirm --overwrite ''*'' ' + $list + '"') -ForegroundColor DarkGray
& $bash -lc ("pacman -S --needed --noconfirm --overwrite '*' " + $list)
return ($LASTEXITCODE -eq 0)
}
# ---------------------------------------------------------------------------
# The catalog
#
# Ordered as they are asked. Each row: a stable Id, whether it is Required or
# optional (msvc is decided at run time -- see below), a label, why it
# matters, how to tell if it is there, how to install it, and the folder
# that has to be on PATH for it to be usable (or $null).
# ---------------------------------------------------------------------------
function Get-Catalog {
$pf = Get-ProgramFiles
$msys = Get-Msys2Root
return @(
@{ Id = 'git'; Kind = 'required'; Label = 'Git';
Why = 'FetchContent clones dependencies (fmt) with it';
Status = { Get-GitStatus }; Install = { Invoke-Winget 'Git.Git' };
PathDir = (Join-Parts $pf 'Git' 'cmd'); PathDefault = $true },
@{ Id = 'cmake'; Kind = 'required'; Label = 'CMake 3.25 or newer';
Why = 'configures and drives every build';
Status = { Get-CmakeStatus }; Install = { Invoke-Winget 'Kitware.CMake' };
PathDir = (Join-Parts $pf 'CMake' 'bin'); PathDefault = $true },
# A compiler: at least one of MSVC or MinGW-w64 is required. Which
# kind msvc is depends on whether one is already there (see Main).
@{ Id = 'msvc'; Kind = 'compiler'; Label = 'Visual Studio Build Tools (MSVC)';
Why = 'the windows-*-msvc presets, and the headers/libraries the windows-*-clang-cl ones need';
Status = { Get-MsvcStatus };
Install = { Invoke-Winget $script:MsvcWingetId $script:MsvcOverride };
PathDir = $null },
# Needs msvc: it adds a component to that install. Without it an x64
# machine cannot build the windows-arm64-msvc and -clang-cl presets, which
# cross-compile through it.
@{ Id = 'msvc-arm64'; Kind = 'optional'; Label = 'MSVC ARM64 build tools';
Why = 'the windows-arm64-msvc and windows-arm64-clang-cl presets (a cross build from an x64 machine)';
Needs = 'msvc';
Status = { Get-MsvcArm64Status };
Install = { Install-MsvcComponent 'Microsoft.VisualStudio.Component.VC.Tools.ARM64' };
PathDir = $null },
@{ Id = 'llvm'; Kind = 'optional'; Label = 'LLVM (clang-cl, Clang, clang-format, clang-tidy)';
Why = 'the windows-*-clang-cl presets, breeze\scripts\refactor.bat and the pre-commit hook';
Status = { Get-LlvmStatus }; Install = { Invoke-Winget 'LLVM.LLVM' };
PathDir = (Join-Parts $pf 'LLVM' 'bin'); PathDefault = $true },
@{ Id = 'msys2'; Kind = 'optional'; Label = 'MSYS2';
Why = 'the home of MinGW-w64 on Windows (needed for the windows-*-mingw* presets)';
Status = { Get-Msys2Status }; Install = { Invoke-Winget 'MSYS2.MSYS2' };
PathDir = $null },
@{ Id = 'mingw64'; Kind = 'optional'; Label = 'MinGW-w64 GCC, 64-bit';
Why = 'the windows-x64-mingw64 preset'; Needs = 'msys2';
Status = { Get-MingwStatus 'mingw64' };
Install = { Invoke-Msys2Packages @('mingw-w64-x86_64-gcc') };
PathDir = (Join-Parts $msys 'mingw64' 'bin'); PathDefault = $false },
@{ Id = 'mingw32'; Kind = 'optional'; Label = 'MinGW-w64 GCC, 32-bit';
Why = 'the windows-x86-mingw32 preset'; Needs = 'msys2';
Status = { Get-MingwStatus 'mingw32' };
Install = { Invoke-Msys2Packages @('mingw-w64-i686-gcc') };
PathDir = (Join-Parts $msys 'mingw32' 'bin'); PathDefault = $false },
@{ Id = 'clang64'; Kind = 'optional'; Label = 'MSYS2 CLANG64 (GNU-style Clang)';
Why = 'the windows-x64-clang preset'; Needs = 'msys2';
Status = { Get-Clang64Status };
Install = { Invoke-Msys2Packages @('mingw-w64-clang-x86_64-clang') };
PathDir = (Join-Parts $msys 'clang64' 'bin'); PathDefault = $false }
)
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
function Show-ManualHelp {
Say ' Install these yourself instead:'
Say ' Git https://git-scm.com/download/win'
Say ' CMake https://cmake.org/download/ (tick "Add CMake to the system PATH")'
Say ' MSVC https://visualstudio.microsoft.com/visual-cpp-build-tools/ (workload: Desktop development with C++)'
Say ' LLVM https://github.com/llvm/llvm-project/releases'
Say ' MSYS2 https://www.msys2.org/'
}
function Invoke-Main {
$catalog = Get-Catalog
$wingetStatus = Get-WingetStatus
$haveWinget = ($wingetStatus.State -eq 'ok')
Say ('==> Checking the tools this project builds with (Windows, winget: ' + $(if ($haveWinget) { 'found' } else { 'NOT found' }) + ')')
# ---- report ---------------------------------------------------------
$status = @{}
foreach ($row in $catalog) { $status[$row.Id] = (& $row.Status) }
$haveCompiler = ($status['msvc'].State -eq 'ok') -or ($status['mingw64'].State -eq 'ok') -or ($status['mingw32'].State -eq 'ok')
foreach ($row in $catalog) {
$st = $status[$row.Id]
$kind = $row.Kind
if ($kind -eq 'compiler') { if ($haveCompiler) { $kind = 'optional' } else { $kind = 'required' } }
$row.EffKind = $kind
switch ($st.State) {
'ok' {
if ($st.Detail) { Write-Host (' [ ok ] ' + $row.Label.PadRight(50) + ' ') -NoNewline -ForegroundColor Green; Note $st.Detail }
else { Good (' [ ok ] ' + $row.Label) }
}
'old' { Warn (' [old ] ' + $row.Label.PadRight(50) + ' ' + $st.Detail + ' (needs ' + $script:MinCmake.ToString() + '+)') }
default {
if ($kind -eq 'required') { Bad (' [ !! ] ' + $row.Label.PadRight(50) + ' missing (required)') }
else { Note (' [ -- ] ' + $row.Label.PadRight(50) + ' missing (optional)') }
}
}
}
if (-not $haveCompiler) {
Warn ' A C++ compiler is required: Visual Studio Build Tools, or MinGW-w64 through MSYS2 (either one).'
}
# ---- -Check: report only --------------------------------------------
if ($Check) {
$missing = ($status['git'].State -ne 'ok') -or ($status['cmake'].State -ne 'ok') -or (-not $haveCompiler)
if ($missing) { return 1 }
return 0
}
$anythingToInstall = $false
foreach ($row in $catalog) { if ($status[$row.Id].State -ne 'ok') { $anythingToInstall = $true } }
if ($anythingToInstall -and -not $haveWinget) {
Say ''
Warn ' winget (Windows Package Manager) was not found, so this script cannot install anything.'
Say ' It comes with the App Installer: https://aka.ms/getwinget (or the Microsoft Store).'
Show-ManualHelp
}
# ---- offer installs, one at a time ----------------------------------
$skipped = @()
$failed = @()
foreach ($row in $catalog) {
if ($script:AnsQuit) { break }
$st = & $row.Status # re-check: an earlier install may have provided it
if ($st.State -eq 'ok') { continue }
if (-not $haveWinget) { continue }
# MinGW rows live inside MSYS2.
if ($row.Needs) {
$needRow = $catalog | Where-Object { $_.Id -eq $row.Needs } | Select-Object -First 1
$needed = & $needRow.Status
if ($needed.State -ne 'ok') { continue }
}
# After the msvc row, "a compiler exists" may have changed the kind
# of the rows still to come.
$kind = $row.EffKind
if ($kind -eq 'required' -and $row.Id -eq 'msvc') {
$now = ((Get-MingwStatus 'mingw64').State -eq 'ok') -or ((Get-MingwStatus 'mingw32').State -eq 'ok')
if ($now) { $kind = 'optional' }
}
if ($kind -eq 'optional' -and -not (Test-OptionalWanted)) {
$skipped += $row.Label
continue
}
Say ''
Write-Host (' ' + $row.Label) -NoNewline -ForegroundColor White
Say (' -- ' + $row.Why)
if ($st.State -eq 'old') { Note (' (installed: ' + $st.Detail + ', but too old)') }
if (Confirm-Install $kind ('Install ' + $row.Label + '?')) {
$ok = & $row.Install
$after = & $row.Status
if ($after.State -eq 'ok') {
Good ' installed'
} elseif ($after.State -eq 'old') {
Warn (' CMake ' + $after.Detail + ' is older than the ' + $script:MinCmake.ToString() + ' this project needs.')
$failed += $row.Label
} elseif (-not $ok) {
Bad ' install failed -- see the output above.'
$failed += $row.Label
} else {
Warn (' the install finished, but ' + $row.Label + ' still is not found -- open a new terminal, or check the output above.')
$failed += $row.Label
}
} elseif ($kind -eq 'optional') {
$skipped += $row.Label
}
}
if ($skipped.Count -gt 0) {
Say ''
Note (' Optional, not installed: ' + ($skipped -join ', '))
Note ' (breeze\scripts\setup.bat, run interactively, offers each one; --all installs them all.)'
}
# ---- PATH ------------------------------------------------------------
# A tool that is installed but whose folder is not on PATH is unusable
# from a plain terminal -- and, for CMake, from the rest of setup.bat.
foreach ($row in $catalog) {
if (-not $row.PathDir) { continue }
$st = & $row.Status
if ($st.State -ne 'ok') { continue }
if (-not (Test-Path -LiteralPath $row.PathDir -PathType Container)) { continue }
if (Test-OnSessionPath $row.PathDir) { continue }
# This session, always: what follows in setup needs it.
Add-ToSessionPath $row.PathDir
if (Test-OnPersistentPath $row.PathDir) { continue }
Say ''
Say (' ' + $row.Label + ' is installed, but its folder is not on your PATH:')
Say (' ' + $row.PathDir)
if ($row.PathDefault) { $kind = 'required' } else { $kind = 'optional' }
if (-not $row.PathDefault) {
Note ' (breeze\scripts\build.bat does not need this: it puts the right MSYS2 toolchain folder first on PATH'
Note ' for the preset it builds, for that run only. Adding the folder here puts its g++/gcc on PATH for every program.)'
}
if (Confirm-Install $kind 'Add it to your user PATH (new terminals will see it)?') {
Add-ToUserPath $row.PathDir
Good ' added -- open a new terminal to use it there.'
}
}
# ---- hand this session's PATH additions back to setup.bat ------------
if ($PathOut -and $script:PathAdds.Count -gt 0) {
Set-Content -LiteralPath $PathOut -Value ($script:PathAdds.ToArray()) -Encoding ASCII
}
# ---- verdict ---------------------------------------------------------
$exit = 0
$needGit = (Get-GitStatus).State -ne 'ok'
$needCmake = (Get-CmakeStatus).State -ne 'ok'
$needCompiler = -not (((Get-MsvcStatus).State -eq 'ok') -or ((Get-MingwStatus 'mingw64').State -eq 'ok') -or ((Get-MingwStatus 'mingw32').State -eq 'ok'))
if ($needGit) { Bad ' still missing (required): Git'; $exit = 1 }
if ($needCmake) { Bad ' still missing (required): CMake 3.25 or newer'; $exit = 1 }
if ($needCompiler) { Bad ' still missing (required): a C++ compiler (Visual Studio Build Tools, or MinGW-w64 via MSYS2)'; $exit = 1 }
if ($failed.Count -gt 0 -and $exit -eq 0) { Warn (' These did not install: ' + ($failed -join ', ')) }
return $exit
}
# Run only when executed, not when dot-sourced (a test dot-sources this file
# to replace the functions that touch the registry).
if ($MyInvocation.InvocationName -ne '.') {
$code = Invoke-Main
exit $code
}
File diff suppressed because it is too large. Load diff
+318
View File
@@ -0,0 +1,318 @@
@echo off
REM Enters the compiler environment a Windows preset needs, in THIS cmd.exe
REM session: what opening "x64 Native Tools Command Prompt for VS" or an MSYS2
REM MINGW64 shell by hand does, without opening a window or leaving the
REM console you are in.
REM
REM Not run directly -- breeze\scripts\build.bat calls it once the preset is known:
REM call "breeze\scripts\_winenv.bat" ^<preset^>
REM It uses no SETLOCAL on purpose: the variables vcvarsall.bat sets (PATH,
REM INCLUDE, LIB, ...) have to land in the CALLER's session. build.bat's own
REM SETLOCAL discards them again when it ends, so nothing leaks into the
REM prompt you started it from.
REM
REM preset environment entered
REM windows-x64-msvc, windows-arm64-msvc the Visual Studio developer
REM prompt for that target
REM (vcvarsall.bat from the newest
REM Visual Studio or Build Tools
REM that has the C++ tools)
REM windows-x64-clang-cl, the same, plus clang-cl on PATH
REM windows-arm64-clang-cl if it is not already (LLVM's own
REM folder, else the copy Visual
REM Studio ships)
REM windows-x64-mingw64, MSYS2's mingw64\bin or
REM windows-x86-mingw32 mingw32\bin first on PATH, unless
REM that compiler is already there
REM windows-x64-clang, MSYS2's clang64\bin or
REM windows-arm64-clang clangarm64\bin first on PATH --
REM always: a clang++ from anywhere
REM else targets a different ABI
REM
REM After entering a Visual Studio environment it checks that the target's runtime
REM libraries exist (lib\TARGET\msvcrt.lib), and for arm64 picks the newest Visual
REM Studio that has the ARM64 component: from an x64 machine that is the only way
REM to build arm64, as a cross build, and without it the failure would only appear
REM at CMake's compiler test. MSYS2 has no CLANGARM64 environment on an x64
REM machine; windows-arm64-clang says so and points at the cross-building presets.
REM
REM Left alone: a shell that is already a developer prompt for the right
REM architecture; any preset not listed (cmake reports an unknown one itself);
REM and everything, when PMN_NO_ENV is set to anything -- for a toolchain this script does
REM not know about, or a prompt you have set up yourself.
REM MSYS2 is looked for in MSYS2_ROOT, else the msys64 folder on the system
REM drive -- the same place breeze\scripts\setup.bat installs it.
REM
REM Exit status: 0 = ready, or nothing to do. 1 = the environment the preset
REM needs is not available, after saying what to install.
REM
REM Flat on purpose: single-line IF and GOTO, no parenthesized blocks. The paths
REM expanded here can contain "(x86)", and an unquoted close-paren ends a block
REM early.
if "%~1"=="" exit /b 0
if defined PMN_NO_ENV exit /b 0
set "_pmn_e_tgt="
set "_pmn_e_llvm="
set "_pmn_e_sub="
set "_pmn_e_exe="
set "_pmn_e_probe="
set "_pmn_e_pkg="
set "_pmn_e_vs="
set "_pmn_e_arg="
set "_pmn_e_need="
if /i "%~1"=="windows-x64-msvc" goto pick_msvc_x64
if /i "%~1"=="windows-arm64-msvc" goto pick_msvc_arm64
if /i "%~1"=="windows-x64-clang-cl" goto pick_clangcl_x64
if /i "%~1"=="windows-arm64-clang-cl" goto pick_clangcl_arm64
if /i "%~1"=="windows-x64-mingw64" goto pick_mingw64
if /i "%~1"=="windows-x86-mingw32" goto pick_mingw32
if /i "%~1"=="windows-x64-clang" goto pick_clang64
if /i "%~1"=="windows-arm64-clang" goto pick_clangarm64
goto env_ok
:pick_msvc_x64
set "_pmn_e_tgt=x64"
goto run_vs
:pick_msvc_arm64
set "_pmn_e_tgt=arm64"
goto run_vs
:pick_clangcl_x64
set "_pmn_e_tgt=x64"
set "_pmn_e_llvm=1"
goto run_vs
:pick_clangcl_arm64
set "_pmn_e_tgt=arm64"
set "_pmn_e_llvm=1"
goto run_vs
:pick_mingw64
set "_pmn_e_sub=mingw64"
set "_pmn_e_exe=g++.exe"
set "_pmn_e_probe=x86_64-w64-mingw32-g++"
set "_pmn_e_pkg=mingw-w64-x86_64-gcc"
goto run_msys
:pick_mingw32
set "_pmn_e_sub=mingw32"
set "_pmn_e_exe=g++.exe"
set "_pmn_e_probe=i686-w64-mingw32-g++"
set "_pmn_e_pkg=mingw-w64-i686-gcc"
goto run_msys
:pick_clang64
set "_pmn_e_sub=clang64"
set "_pmn_e_exe=clang++.exe"
set "_pmn_e_pkg=mingw-w64-clang-x86_64-clang"
goto run_msys
:pick_clangarm64
set "_pmn_e_sub=clangarm64"
set "_pmn_e_exe=clang++.exe"
set "_pmn_e_pkg=mingw-w64-clang-aarch64-clang"
goto run_msys
REM ---------------------------------------------------------------------
REM Visual Studio: msvc and clang-cl presets
REM ---------------------------------------------------------------------
:run_vs
if not defined VSCMD_VER goto vs_find
if /i "%VSCMD_ARG_TGT_ARCH%"=="%_pmn_e_tgt%" goto vs_have
goto vs_find
:vs_have
where cl >nul 2>nul
if errorlevel 1 goto vs_find
if defined VSINSTALLDIR set "_pmn_e_vs=%VSINSTALLDIR%"
goto vs_verify
:vs_find
set "_pmn_e_vs="
set "_pmn_e_vswhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
if not exist "%_pmn_e_vswhere%" goto vs_missing
REM Which component provides the target's compilers and libraries: the x86/x64
REM tools for x64, and for arm64 the ARM64 tools too -- an x64 machine reaches
REM arm64 through them (a cross build), and an ARM64 machine builds natively.
REM The newest install that has what the target needs wins, so an older Visual
REM Studio that has it beats a newer one that does not.
set "_pmn_e_need=Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
if /i "%_pmn_e_tgt%"=="arm64" set "_pmn_e_need=Microsoft.VisualStudio.Component.VC.Tools.x86.x64 Microsoft.VisualStudio.Component.VC.Tools.ARM64"
for /f "usebackq delims=" %%I in (`"%_pmn_e_vswhere%" -latest -products * -requires %_pmn_e_need% -property installationPath`) do set "_pmn_e_vs=%%I"
if defined _pmn_e_vs goto vs_have_install
REM None has it. Is there a Visual Studio with C++ at all? Then it is only the
REM target's component that is missing, which has its own message.
for /f "usebackq delims=" %%I in (`"%_pmn_e_vswhere%" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "_pmn_e_vs=%%I"
if not defined _pmn_e_vs goto vs_missing
goto vs_target_missing
:vs_have_install
set "_pmn_e_vcvars=%_pmn_e_vs%\VC\Auxiliary\Build\vcvarsall.bat"
if not exist "%_pmn_e_vcvars%" goto vs_missing
REM vcvarsall's argument is HOST_TARGET, or just TARGET when the two match.
set "_pmn_e_host=%PROCESSOR_ARCHITECTURE%"
if defined PROCESSOR_ARCHITEW6432 set "_pmn_e_host=%PROCESSOR_ARCHITEW6432%"
if /i "%_pmn_e_host%"=="AMD64" goto vs_arg_amd64
if /i "%_pmn_e_host%"=="ARM64" goto vs_arg_arm64
set "_pmn_e_arg=x86_%_pmn_e_tgt%"
goto vs_enter
:vs_arg_amd64
if /i "%_pmn_e_tgt%"=="x64" set "_pmn_e_arg=x64"
if /i "%_pmn_e_tgt%"=="arm64" set "_pmn_e_arg=x64_arm64"
goto vs_enter
:vs_arg_arm64
if /i "%_pmn_e_tgt%"=="x64" set "_pmn_e_arg=arm64_x64"
if /i "%_pmn_e_tgt%"=="arm64" set "_pmn_e_arg=arm64"
goto vs_enter
REM Success is judged by what vcvarsall left behind -- the target it reports
REM and a cl on PATH, which is what the build needs -- not by its exit status.
:vs_enter
set "VSCMD_SKIP_SENDTELEMETRY=1"
echo ==^> Environment: Visual Studio developer prompt for %_pmn_e_tgt% ^(vcvarsall %_pmn_e_arg%^)
call "%_pmn_e_vcvars%" %_pmn_e_arg% >"%TEMP%\pmn-vcvarsall.log" 2>&1
if /i "%VSCMD_ARG_TGT_ARCH%"=="%_pmn_e_tgt%" goto vs_check_cl
goto vs_failed
:vs_check_cl
where cl >nul 2>nul
if errorlevel 1 goto vs_failed
if not defined VCToolsInstallDir goto vs_failed
goto vs_verify
REM The target's runtime libraries must exist: with the target's component
REM missing, vcvarsall can still report the target and leave a cl on PATH (the
REM host's x64 one), and the failure only shows up at CMake's compiler test as
REM "cannot open msvcrt.lib". This catches it first, with the fix.
:vs_verify
if not defined VCToolsInstallDir goto vs_after
if exist "%VCToolsInstallDir%lib\%_pmn_e_tgt%\msvcrt.lib" goto vs_after
goto vs_target_missing
:vs_after
del "%TEMP%\pmn-vcvarsall.log" >nul 2>nul
if not "%_pmn_e_llvm%"=="1" goto env_ok
where clang-cl >nul 2>nul
if not errorlevel 1 goto env_ok
set "_pmn_e_pf=%ProgramW6432%"
if not defined _pmn_e_pf set "_pmn_e_pf=%ProgramFiles%"
if exist "%_pmn_e_pf%\LLVM\bin\clang-cl.exe" goto llvm_standalone
if not defined _pmn_e_vs goto llvm_missing
if exist "%_pmn_e_vs%\VC\Tools\Llvm\x64\bin\clang-cl.exe" goto llvm_vs_x64
if exist "%_pmn_e_vs%\VC\Tools\Llvm\ARM64\bin\clang-cl.exe" goto llvm_vs_arm64
if exist "%_pmn_e_vs%\VC\Tools\Llvm\bin\clang-cl.exe" goto llvm_vs_x86
goto llvm_missing
:llvm_standalone
set "PATH=%_pmn_e_pf%\LLVM\bin;%PATH%"
echo ==^> Environment: clang-cl from %_pmn_e_pf%\LLVM\bin
goto env_ok
:llvm_vs_x64
set "PATH=%_pmn_e_vs%\VC\Tools\Llvm\x64\bin;%PATH%"
echo ==^> Environment: clang-cl from Visual Studio ^(VC\Tools\Llvm\x64\bin^)
goto env_ok
:llvm_vs_arm64
set "PATH=%_pmn_e_vs%\VC\Tools\Llvm\ARM64\bin;%PATH%"
echo ==^> Environment: clang-cl from Visual Studio ^(VC\Tools\Llvm\ARM64\bin^)
goto env_ok
:llvm_vs_x86
set "PATH=%_pmn_e_vs%\VC\Tools\Llvm\bin;%PATH%"
echo ==^> Environment: clang-cl from Visual Studio ^(VC\Tools\Llvm\bin^)
goto env_ok
:llvm_missing
echo error: preset %~1 needs clang-cl, and none was found on PATH, in %_pmn_e_pf%\LLVM\bin or in Visual Studio. 1>&2
echo Run breeze\scripts\setup.bat to install LLVM, or: winget install LLVM.LLVM 1>&2
goto env_fail
:vs_missing
echo error: preset %~1 needs Visual Studio or Build Tools with the C++ tools ^(workload "Desktop development with C++"^), and none was found. 1>&2
echo Run breeze\scripts\setup.bat to install it. If you use a Visual Studio this script cannot find, open its 1>&2
echo developer prompt for %_pmn_e_tgt% yourself and set PMN_NO_ENV=1. 1>&2
if /i "%_pmn_e_tgt%"=="arm64" echo For ARM64 targets add the "MSVC ARM64 build tools" component in the Visual Studio Installer. 1>&2
goto env_fail
:vs_target_missing
echo error: preset %~1 needs the MSVC tools and libraries for %_pmn_e_tgt%, and the Visual Studio install here does not have them. 1>&2
if defined VCToolsInstallDir echo %VCToolsInstallDir%lib\%_pmn_e_tgt%\msvcrt.lib is missing. 1>&2
if exist "%TEMP%\pmn-vcvarsall.log" type "%TEMP%\pmn-vcvarsall.log" 1>&2
if /i "%_pmn_e_tgt%"=="arm64" goto vs_target_missing_arm64
echo Add the component "MSVC ... C++ x64/x86 build tools (Latest)" in the Visual Studio Installer ^(Modify, Individual components^). 1>&2
goto env_fail
:vs_target_missing_arm64
echo Building for ARM64 -- from an x64 machine that is a cross build -- needs the component 1>&2
echo "MSVC ... C++ ARM64/ARM64EC build tools (Latest)". Run breeze\scripts\setup.bat, which offers to add it, or add it in the 1>&2
echo Visual Studio Installer: Modify, Individual components, search for ARM64. 1>&2
goto env_fail
:vs_failed
echo error: could not enter the Visual Studio environment for %_pmn_e_tgt% ^(vcvarsall %_pmn_e_arg%^). Its output: 1>&2
if exist "%TEMP%\pmn-vcvarsall.log" type "%TEMP%\pmn-vcvarsall.log" 1>&2
echo If this shell is already a developer prompt for another architecture, open a fresh one. 1>&2
if /i "%_pmn_e_tgt%"=="arm64" echo ARM64 targets need the "MSVC ARM64 build tools" component of Visual Studio. 1>&2
goto env_fail
REM ---------------------------------------------------------------------
REM MSYS2: mingw and clang presets
REM ---------------------------------------------------------------------
:run_msys
set "_pmn_e_root=%MSYS2_ROOT%"
if not defined _pmn_e_root set "_pmn_e_root=%SystemDrive%\msys64"
if not defined _pmn_e_probe goto msys_enter
where %_pmn_e_probe% >nul 2>nul
if not errorlevel 1 goto env_ok
:msys_enter
if not exist "%_pmn_e_root%\%_pmn_e_sub%\bin\%_pmn_e_exe%" goto msys_missing
set "PATH=%_pmn_e_root%\%_pmn_e_sub%\bin;%PATH%"
echo ==^> Environment: MSYS2 %_pmn_e_sub% ^(%_pmn_e_root%\%_pmn_e_sub%\bin first on PATH^)
goto env_ok
:msys_missing
if /i "%_pmn_e_sub%"=="clangarm64" goto msys_missing_arm
goto msys_missing_pkg
REM MSYS2 has a CLANGARM64 environment only on ARM64 Windows; on an x64 machine
REM there is nothing to install, so say what to use instead.
:msys_missing_arm
set "_pmn_e_host=%PROCESSOR_ARCHITECTURE%"
if defined PROCESSOR_ARCHITEW6432 set "_pmn_e_host=%PROCESSOR_ARCHITEW6432%"
if /i "%_pmn_e_host%"=="ARM64" goto msys_missing_pkg
echo error: preset %~1 needs MSYS2's CLANGARM64 environment, which MSYS2 provides only on ARM64 Windows ^(this machine is %_pmn_e_host%^). 1>&2
echo To build for Windows ARM64 from here use windows-arm64-clang-cl or windows-arm64-msvc: they cross-compile 1>&2
echo with Visual Studio's ARM64 build tools ^(breeze\scripts\setup.bat offers to add them^). 1>&2
goto env_fail
:msys_missing_pkg
echo error: preset %~1 needs MSYS2's %_pmn_e_sub% toolchain, and %_pmn_e_root%\%_pmn_e_sub%\bin\%_pmn_e_exe% was not found. 1>&2
echo Run breeze\scripts\setup.bat to install it, or in an MSYS2 shell: pacman -S %_pmn_e_pkg% 1>&2
echo MSYS2 itself: winget install MSYS2.MSYS2 ^(set MSYS2_ROOT if it lives somewhere other than the msys64 folder on the system drive^) 1>&2
goto env_fail
REM ---------------------------------------------------------------------
REM Exits. Every path out goes through one of these two, which clear the
REM scratch variables this script made (all start with _pmn_e_).
REM ---------------------------------------------------------------------
:env_ok
call :clear
exit /b 0
:env_fail
call :clear
exit /b 1
:clear
set "_pmn_e_tgt="
set "_pmn_e_llvm="
set "_pmn_e_sub="
set "_pmn_e_exe="
set "_pmn_e_probe="
set "_pmn_e_pkg="
set "_pmn_e_vs="
set "_pmn_e_arg="
set "_pmn_e_vswhere="
set "_pmn_e_vcvars="
set "_pmn_e_need="
set "_pmn_e_host="
set "_pmn_e_pf="
set "_pmn_e_root="
goto :eof
+383
View File
@@ -0,0 +1,383 @@
@echo off
REM Configure (if needed) and build the project, preferably with Ninja.
REM
REM Usage (from the project root):
REM breeze\scripts\build.bat [--preset ^<name^>] [BUILD_TYPE] [CMAKE_ARGS...]
REM breeze\scripts\build.bat --list-presets
REM breeze\scripts\build.bat --help
REM
REM --preset <name> Use a CMakePresets.json preset. A preset selects that
REM target's toolchain file (breeze\cmake\toolchains\<name>.cmake),
REM which picks the compiler and declares the DIST_TARGET
REM together, instead of you having to pass a matching
REM pair by hand:
REM breeze\scripts\build.bat --preset windows-x64-mingw64
REM Each preset builds in its own directory,
REM out\build\<name>, so switching between presets never
REM invalidates -- or deletes -- another one's build.
REM Run --list-presets to see every preset for your OS.
REM Must come first if given at all; --preset=<name>
REM also works. Given with no value (or a value that
REM looks like another flag), this fails fast with that
REM same preset list shown right there. A value that IS
REM given but isn't a real preset name is instead
REM caught by cmake itself failing to configure, at
REM which point this script shows the preset list too
REM -- see the "configure" step below.
REM BUILD_TYPE Debug^|Release^|RelWithDebInfo^|MinSizeRel
REM (default: project/Configuration.cmake's DEFAULT_BUILD_TYPE)
REM CMAKE_ARGS Anything else is forwarded to `cmake` verbatim, e.g.:
REM breeze\scripts\build.bat --preset windows-x64-mingw64 Debug -DBUILD_TESTS=ON
REM breeze\scripts\build.bat -DBUILD_TESTS=ON
REM (BUILD_TYPE omitted -- recognized by not
REM starting with "-")
REM
REM Picking a DIST_TARGET: three ways; the first two set the compiler and the
REM label together, and this script rejects mixing --preset with a
REM -DDIST_TARGET rather than leaving it ambiguous which one wins.
REM 1. --preset <name> The recommended way; see --list-presets.
REM 2. -DCMAKE_TOOLCHAIN_FILE=breeze\cmake\toolchains\<name>.cmake
REM (no --preset) What a preset does, by hand, as one of
REM CMAKE_ARGS above.
REM 3. -DDIST_TARGET=<value> Sets the label alone, as one of CMAKE_ARGS
REM (no --preset) above. Without also passing a matching
REM compiler (or, better, the toolchain file
REM above), DIST_TARGET is auto-detected from
REM whatever compiler CMake finds on its own; if
REM the two disagree, TargetSelection.cmake
REM FATAL_ERRORs rather than silently building
REM the wrong toolchain and mislabeling the
REM result -- see that file and the README.
REM Passing --preset together with an explicit -DDIST_TARGET=... in
REM CMAKE_ARGS is rejected by this script before cmake even runs: the
REM preset's toolchain already declares DIST_TARGET, so a second value
REM alongside it is always either redundant or contradictory -- never
REM useful.
REM
REM cmd.exe splits %1, %2, ... (and SHIFT) on space, comma, semicolon, AND
REM "=" -- type "-DFOO=BAR" as an argument and it arrives as TWO separate
REM parameters, %1=-DFOO %2=BAR, silently, with no way to tell after the
REM fact. This script never reads a "="-laden argument (or --preset's own
REM value) through %1 or SHIFT; everything is peeled off of the raw,
REM unsplit %* instead using FOR /F (which only ever splits on space/tab),
REM so "=" survives intact -- see the parsing block below. (bash's
REM "$@"/shift has no such bug -- see build.sh for that half of this same
REM feature.)
REM
REM Compiler environment: for a Windows preset (windows-*-msvc, -clang-cl,
REM -mingw* and -clang) this script first enters the environment that preset's
REM compiler needs, for this run only: the Visual Studio developer prompt for the
REM preset's target (vcvarsall.bat), or MSYS2's toolchain folder first on PATH. So
REM it works from any prompt -- no need to open the x64 Native Tools Command
REM Prompt for VS or an MSYS2 shell first. A prompt that is already right is left alone;
REM set PMN_NO_ENV=1 to skip all of this and use your shell as it is. See
REM _winenv.bat for exactly what it does.
REM
REM Build directory: out\build\<preset> for --preset <preset>; without a
REM preset, out\build\default (the compiler is auto-detected there, so keep
REM to one compiler per directory or use presets). Set BUILD_DIR to use an
REM exact directory of your own instead. breeze\scripts\run.bat, install.bat and
REM docs.bat use the directory built last, or --preset <name> to pick another.
setlocal enabledelayedexpansion
REM -h/--help is checked here, off the raw %1, before anything else --
REM including whether cmake is even on PATH below -- since help shouldn't
REM depend on that.
if /i "%~1"=="-h" goto show_help
if /i "%~1"=="--help" goto show_help
goto after_help
:show_help
echo Usage: breeze\scripts\build.bat [--preset ^<name^>] [BUILD_TYPE] [CMAKE_ARGS...]
echo breeze\scripts\build.bat --list-presets
echo breeze\scripts\build.bat --help
echo.
echo --preset ^<name^> Use a CMakePresets.json preset: it selects the target's
echo toolchain file ^(compiler and DIST_TARGET together^) and
echo builds in its own directory, out\build\^<name^>. Must come
echo first if given at all; --preset=^<name^> also works. Run
echo --list-presets to see every preset for your OS.
echo BUILD_TYPE Debug^|Release^|RelWithDebInfo^|MinSizeRel
echo ^(default: project/Configuration.cmake's DEFAULT_BUILD_TYPE^)
echo CMAKE_ARGS Anything else is forwarded to `cmake` verbatim, e.g.
echo -DDIST_TARGET=^<value^> or -DBUILD_TESTS=ON.
echo.
echo Windows presets: the compiler's environment ^(the Visual Studio developer prompt
echo for the target, or MSYS2's toolchain folder^) is entered for you,
echo for this run only -- no special prompt needed first. Set
echo PMN_NO_ENV=1 to use your shell as it is.
echo.
echo Don't combine --preset with an explicit -DDIST_TARGET=... in CMAKE_ARGS --
echo the preset's toolchain already declares one; see the comment at the top of
echo this script ^(breeze\scripts\build.bat^) and TargetSelection.cmake's "Target selection"
echo section for why picking a DIST_TARGET is one or the other, not both.
echo.
echo Examples:
echo breeze\scripts\build.bat --preset linux-x64-clang
echo breeze\scripts\build.bat --preset windows-x64-mingw64 Debug -DBUILD_TESTS=ON
echo breeze\scripts\build.bat -DBUILD_TESTS=ON
exit /b 0
:after_help
cd /d "%~dp0..\.."
REM An explicit BUILD_DIR from the environment beats the one-directory-per-
REM preset layout resolved below, and is then also not recorded as "the
REM build made last" (run/install/docs would otherwise be sent to a
REM directory the layout doesn't know about).
set "_pmn_record_build=1"
if not "%BUILD_DIR%"=="" set "_pmn_record_build=0"
where cmake >nul 2>nul
if errorlevel 1 (
echo error: cmake not found on PATH.
exit /b 1
)
REM Which cmake the user's PATH resolves to, so it can be put back in front after
REM a Visual Studio environment is entered below: that puts Visual Studio's own
REM cmake -- often an older one -- ahead of it.
set "_pmn_cmake_dir="
for /f "delims=" %%C in ('where cmake') do if not defined _pmn_cmake_dir set "_pmn_cmake_dir=%%~dpC"
REM See the header comment above for why this doesn't use %1/SHIFT.
set "ALL_ARGS=%*"
set "PRESET="
set "BUILD_TYPE="
set "EXTRA_ARGS="
if not defined ALL_ARGS goto args_done
for /f "tokens=1,* delims= " %%A in ("%ALL_ARGS%") do (
set "_FIRST=%%A"
set "_REST=%%B"
)
if /i "%_FIRST%"=="--list-presets" (
cmake --list-presets
exit /b !ERRORLEVEL!
)
if /i "%_FIRST%"=="--preset" goto preset_space_form
if /i "%_FIRST:~0,9%"=="--preset=" goto preset_equals_form
goto no_preset
:preset_space_form
if not defined _REST (
echo error: --preset requires a value, e.g. --preset windows-x64-mingw64
echo.
cmake --list-presets
exit /b 1
)
for /f "tokens=1,* delims= " %%A in ("%_REST%") do (
set "PRESET=%%A"
set "_REST=%%B"
)
if "%PRESET:~0,1%"=="-" (
echo error: --preset requires a value, e.g. --preset windows-x64-mingw64
echo.
cmake --list-presets
exit /b 1
)
goto have_preset
:preset_equals_form
set "PRESET=%_FIRST:~9%"
if not defined PRESET (
echo error: --preset= requires a value, e.g. --preset=windows-x64-mingw64
echo.
cmake --list-presets
exit /b 1
)
goto have_preset
:have_preset
set "ALL_ARGS=%_REST%"
if not defined ALL_ARGS goto args_done
for /f "tokens=1,* delims= " %%A in ("%ALL_ARGS%") do (
set "_FIRST=%%A"
set "_REST=%%B"
)
:no_preset
if "%_FIRST:~0,1%"=="-" (
set "EXTRA_ARGS=%ALL_ARGS%"
) else (
set "BUILD_TYPE=%_FIRST%"
set "EXTRA_ARGS=%_REST%"
)
:args_done
if "%BUILD_TYPE%"=="" call :read_config DEFAULT_BUILD_TYPE BUILD_TYPE
if "%TOOLCACHE_DIR%"=="" call :read_config TOOLCACHE_DIR TOOLCACHE_DIR
REM One build directory per preset (out\build\<preset>; "default" when no
REM preset was given), so switching presets never has to reuse -- or wipe -- a
REM directory configured for another compiler. breeze\cmake\BuildDir.cmake owns the
REM layout (project/Configuration.cmake's BUILD_OUTPUT_DIR); goto rather than an
REM if-block for the reason given further down about nesting.
set "_pmn_name=default"
if defined PRESET set "_pmn_name=%PRESET%"
if not "%BUILD_DIR%"=="" goto build_dir_known
call :resolve_build_dir "%_pmn_name%" BUILD_DIR
if errorlevel 1 exit /b 1
:build_dir_known
REM Combining --preset with an explicit -DDIST_TARGET=... in CMAKE_ARGS is
REM ambiguous (the preset already sets one) -- caught here, before cmake
REM ever runs, rather than leaving it to whichever of the two CMake
REM happens to prefer. findstr's /C: does a literal (non-regex) substring
REM search, which sidesteps cmd.exe's own "=" splitting quirk (see the
REM header comment) entirely -- there's no argument-parsing involved here,
REM just a plain string search over EXTRA_ARGS as already assembled.
if defined PRESET (
echo %EXTRA_ARGS%| findstr /C:"-DDIST_TARGET=" >nul
if not errorlevel 1 (
echo error: both --preset %PRESET% and an explicit -DDIST_TARGET=... were given.
echo A preset's toolchain already declares DIST_TARGET ^(and picks the compiler^) -- pick one:
echo breeze\scripts\build.bat --preset %PRESET%
echo breeze\scripts\build.bat -DCMAKE_TOOLCHAIN_FILE=breeze\cmake\toolchains\^<target^>.cmake
echo See the comment at the top of this script, or TargetSelection.cmake's
echo "Target selection" section, for why these two aren't combined.
exit /b 1
)
)
REM Prefer a pinned ninja (fetched by breeze\scripts\setup.bat -- see
REM breeze\cmake\FetchNinja.cmake) over a system one, same "always the pinned
REM copy" preference as Doxygen. Needed whether or not --preset is used:
REM every preset sets its generator to Ninja but doesn't hardcode which
REM ninja binary to use (that would just be TOOLCACHE_DIR's default
REM duplicated a third time -- see project/Configuration.cmake) --
REM CMAKE_MAKE_PROGRAM supplies it either way; only -G Ninja itself is
REM skipped below when a preset already sets the generator.
REM
REM Deliberately goto-based, and deliberately `pushd` + a bare wildcard
REM rather than `for /d %%D in ("path\*")` -- both tested directly; a
REM path-prefixed wildcard didn't match here, this form does, and it also
REM sidesteps nesting a FOR loop inside an if/else block, which
REM separately corrupts cmd.exe's paren-matching for the enclosing block
REM (see breeze\scripts\refactor.bat for that one).
set "NINJA_BIN="
if not exist "%TOOLCACHE_DIR%" goto check_system_ninja
pushd "%TOOLCACHE_DIR%"
for /d %%D in (ninja-*) do if exist "%%D\ninja.exe" if not defined NINJA_BIN set "NINJA_BIN=%%~fD\ninja.exe"
popd
if defined NINJA_BIN goto have_pinned_ninja
:check_system_ninja
where ninja >nul 2>nul
if errorlevel 1 goto no_ninja
echo ==^> Using system ninja
if not defined PRESET set "GENERATOR_ARGS=-G Ninja"
goto configure
:have_pinned_ninja
echo ==^> Using pinned ninja: %NINJA_BIN%
if defined PRESET (
set "GENERATOR_ARGS=-DCMAKE_MAKE_PROGRAM=%NINJA_BIN%"
) else (
set "GENERATOR_ARGS=-G Ninja -DCMAKE_MAKE_PROGRAM=%NINJA_BIN%"
)
goto configure
:no_ninja
if defined PRESET (
echo warning: ninja not found ^(no pinned copy, none on PATH^) -- the '%PRESET%'
echo preset requires it; configure will likely fail. Run
echo breeze\scripts\setup.bat to fetch a pinned ninja, or install ninja
echo yourself.
) else (
echo warning: ninja not found ^(no pinned copy, none on PATH^) -- falling back to
echo CMake's default generator for this platform. Run breeze\scripts\setup.bat
echo to fetch a pinned ninja, or install ninja yourself, for a faster
echo and more consistent build. Continuing without it.
)
set "GENERATOR_ARGS="
:configure
if not defined PRESET goto configure_env_done
call "%~dp0_winenv.bat" "%PRESET%"
if errorlevel 1 exit /b 1
if defined _pmn_cmake_dir set "PATH=%_pmn_cmake_dir%;%PATH%"
:configure_env_done
echo ==^> Configuring (%BUILD_TYPE%) into %BUILD_DIR%\
if defined PRESET echo Preset: %PRESET%
if defined EXTRA_ARGS echo Extra CMake arguments: %EXTRA_ARGS%
if defined PRESET (
cmake --preset %PRESET% -S . -B "%BUILD_DIR%" %GENERATOR_ARGS% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% %EXTRA_ARGS%
) else (
cmake %GENERATOR_ARGS% -S . -B "%BUILD_DIR%" -DCMAKE_BUILD_TYPE=%BUILD_TYPE% %EXTRA_ARGS%
)
if errorlevel 1 (
if defined PRESET (
echo.
echo error: configuring with preset '%PRESET%' failed -- see the cmake output above.
echo If '%PRESET%' isn't the preset name you meant, here is what --list-presets
echo shows for this OS:
echo.
cmake --list-presets
)
exit /b 1
)
REM Configure succeeded: this is now the build the other scripts default to.
if "%_pmn_record_build%"=="1" cmake "-DPMN_NAME=%_pmn_name%" -DPMN_RECORD=ON -P breeze\cmake\BuildDir.cmake >nul
echo ==^> Building
cmake --build "%BUILD_DIR%"
if errorlevel 1 exit /b 1
echo ==^> Done. Run a module with: breeze\scripts\run.bat ^<target-name^>
exit /b 0
REM ---------------------------------------------------------------------
REM :read_config <ConfigurationVarName> <LocalVarName>
REM
REM Reads one value out of project/Configuration.cmake -- this project's single
REM source of truth -- via breeze\cmake\PrintConfig.cmake, instead of hardcoding
REM a second copy of a default that can silently drift out of sync, and
REM stores it into %2 in the CALLER's scope. See breeze\cmake\PrintConfig.cmake
REM for exactly what's being stripped off of its output below.
REM ---------------------------------------------------------------------
:read_config
setlocal
set "_pmn_line="
for /f "usebackq delims=" %%V in (`cmake "-DPRINT_VARS=%~1" -P breeze\cmake\PrintConfig.cmake`) do set "_pmn_line=%%V"
for /f "tokens=1,* delims==" %%A in ("%_pmn_line%") do set "_pmn_value=%%B"
endlocal & set "%~2=%_pmn_value%"
goto :eof
REM ---------------------------------------------------------------------
REM :resolve_build_dir <name-or-empty> <LocalVarName> [quiet]
REM
REM Asks breeze\cmake\BuildDir.cmake which build directory to use -- %1 is a preset
REM name, or "default"; empty means "discover one" (the build made last, or
REM the only one) -- and stores it into %2 in the CALLER's scope, with
REM backslashes. Same shape as :read_config above, and the same
REM `cmake -P` one-line-answer protocol: "-- BUILD_DIR=<path>" or
REM "-- ERROR=<text>" (see that file). On failure it prints the reason to
REM stderr, unless a third argument "quiet" is given, and returns errorlevel 1.
REM ---------------------------------------------------------------------
:resolve_build_dir
setlocal
set "_pmn_key="
set "_pmn_val="
for /f "usebackq tokens=1,* delims==" %%A in (`cmake "-DPMN_NAME=%~1" -P breeze\cmake\BuildDir.cmake`) do (
set "_pmn_key=%%A"
set "_pmn_val=%%B"
)
if "%_pmn_key%"=="-- BUILD_DIR" if defined _pmn_val goto resolve_ok
if /i "%~3"=="quiet" goto resolve_failed
if defined _pmn_val (
echo error: %_pmn_val% 1>&2
) else (
echo error: breeze\cmake\BuildDir.cmake did not answer. 1>&2
)
:resolve_failed
endlocal
exit /b 1
:resolve_ok
endlocal & set "%~2=%_pmn_val:/=\%"
goto :eof
+289
View File
@@ -0,0 +1,289 @@
#!/usr/bin/env bash
# Configure (if needed) and build the project, preferably with Ninja.
#
# Usage (from the project root):
# breeze/scripts/build.sh [--preset <name>] [BUILD_TYPE] [CMAKE_ARGS...]
# breeze/scripts/build.sh --list-presets
# breeze/scripts/build.sh --help
#
# --preset <name> Use a CMakePresets.json preset. A preset selects that
# target's toolchain file (breeze/cmake/toolchains/<name>.cmake),
# which picks the compiler and declares the DIST_TARGET
# together, instead of you having to pass a matching
# pair by hand:
# breeze/scripts/build.sh --preset windows-x64-mingw64
# Each preset builds in its own directory,
# out/build/<name>, so switching between presets never
# invalidates -- or deletes -- another one's build.
# Run --list-presets to see every preset for your OS.
# Must come first if given at all; --preset=<name> also
# works. Given with no value, or a value that isn't one
# of --list-presets' own, this fails fast with that
# same preset list shown right there, rather than
# letting a typo fall through to a less obvious `cmake`
# error further down.
# BUILD_TYPE Debug|Release|RelWithDebInfo|MinSizeRel
# (default: project/Configuration.cmake's DEFAULT_BUILD_TYPE)
# CMAKE_ARGS Anything else is forwarded to `cmake` verbatim, e.g.:
# breeze/scripts/build.sh --preset windows-x64-mingw64 Debug -DBUILD_TESTS=ON
# breeze/scripts/build.sh -DBUILD_TESTS=ON
# (BUILD_TYPE omitted -- recognized by not
# starting with "-")
#
# Picking a DIST_TARGET: three ways; the first two set the compiler and the
# label together, and this script rejects mixing --preset with a
# -DDIST_TARGET rather than leaving it ambiguous which one wins.
# 1. --preset <name> The recommended way; see --list-presets.
# 2. -DCMAKE_TOOLCHAIN_FILE=breeze/cmake/toolchains/<name>.cmake
# (no --preset) What a preset does, by hand, as one of
# CMAKE_ARGS above.
# 3. -DDIST_TARGET=<value> Sets the label alone, as one of CMAKE_ARGS
# (no --preset) above. Without also passing a matching
# compiler (or, better, the toolchain file
# above), DIST_TARGET is auto-detected from
# whatever compiler CMake finds on its own; if
# the two disagree, TargetSelection.cmake
# FATAL_ERRORs rather than silently building
# the wrong toolchain and mislabeling the
# result -- see that file and the README.
# Passing --preset together with an explicit -DDIST_TARGET=... in
# CMAKE_ARGS is rejected by this script before cmake even runs: the
# preset's toolchain already declares DIST_TARGET, so a second value
# alongside it is always either redundant (same value) or contradictory
# (different value) -- never useful.
#
# Build directory: out/build/<preset> for --preset <preset>; without a
# preset, out/build/default (the compiler is auto-detected there, so keep
# to one compiler per directory or use presets). Set BUILD_DIR to use an
# exact directory of your own instead. breeze/scripts/run.sh, install.sh and
# docs.sh use the directory built last, or --preset <name> to pick another.
set -euo pipefail
shopt -s nullglob
usage() {
cat <<'EOF'
Usage: breeze/scripts/build.sh [--preset <name>] [BUILD_TYPE] [CMAKE_ARGS...]
breeze/scripts/build.sh --list-presets
breeze/scripts/build.sh --help
--preset <name> Use a CMakePresets.json preset: it selects the target's
toolchain file (compiler and DIST_TARGET together) and
builds in its own directory, out/build/<name>. Must come
first if given at all; --preset=<name> also works. Run
--list-presets to see every preset for your OS.
BUILD_TYPE Debug|Release|RelWithDebInfo|MinSizeRel
(default: project/Configuration.cmake's DEFAULT_BUILD_TYPE)
CMAKE_ARGS Anything else is forwarded to `cmake` verbatim, e.g.
-DDIST_TARGET=<value> or -DBUILD_TESTS=ON.
Don't combine --preset with an explicit -DDIST_TARGET=... in CMAKE_ARGS --
the preset's toolchain already declares one; see the comment at the top of
this script (breeze/scripts/build.sh) and TargetSelection.cmake's "Target selection"
section for why picking a DIST_TARGET is one or the other, not both.
Examples:
breeze/scripts/build.sh --preset linux-x64-clang
breeze/scripts/build.sh --preset windows-x64-mingw64 Debug -DBUILD_TESTS=ON
breeze/scripts/build.sh -DBUILD_TESTS=ON
EOF
}
case "${1:-}" in
-h|--help)
usage
exit 0
;;
esac
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../.."
command -v cmake >/dev/null 2>&1 || { echo "error: cmake not found on PATH." >&2; exit 1; }
if [ "${1:-}" = "--list-presets" ]; then
exec cmake --list-presets
fi
# _pmn_read_config, _pmn_resolve_build_dir, _pmn_record_build_dir.
source "$SCRIPT_DIR/_common.sh"
# Prints CMake's own preset listing (already filtered to this OS by each
# preset's "condition" in CMakePresets.json) to stderr -- used to show the
# actual options right alongside a preset-related error below, instead of
# just telling the user to go run --list-presets separately.
_pmn_show_presets() {
echo "" >&2
cmake --list-presets >&2
}
# Membership test against that same listing -- $1 matches a real preset
# name for this OS only if it appears there as a quoted entry (anchored on
# both quote characters so e.g. "windows-x64-mingw32" can't false-match a
# check for "windows-x64-mingw3").
_pmn_known_preset() {
cmake --list-presets 2>/dev/null | grep -qF "\"$1\""
}
_pmn_has_explicit_dist_target() {
local arg
for arg in "$@"; do
case "$arg" in
-DDIST_TARGET=*) return 0 ;;
esac
done
return 1
}
# --preset must come first if given at all -- see TargetSelection.cmake's
# and CMakePresets.json's own comments for why this is the recommended
# way to pick a non-default DIST_TARGET: its toolchain file sets the
# compiler and the label together, instead of you having to pass both by
# hand.
PRESET=""
case "${1:-}" in
--preset)
if [ "$#" -lt 2 ] || [[ "$2" == -* ]]; then
echo "error: --preset requires a value, e.g. --preset windows-x64-mingw64" >&2
_pmn_show_presets
exit 1
fi
PRESET="$2"
shift 2
;;
--preset=*)
PRESET="${1#--preset=}"
shift
if [ -z "$PRESET" ]; then
echo "error: --preset= requires a value, e.g. --preset=windows-x64-mingw64" >&2
_pmn_show_presets
exit 1
fi
;;
esac
if [ -n "$PRESET" ] && ! _pmn_known_preset "$PRESET"; then
echo "error: '${PRESET}' is not a preset --list-presets knows about for this OS." >&2
_pmn_show_presets
exit 1
fi
# One build directory per preset (out/build/<preset>; "default" when no
# preset was given), so switching presets never has to reuse -- or wipe -- a
# directory configured for another compiler. breeze/cmake/BuildDir.cmake owns the
# layout (project/Configuration.cmake's BUILD_OUTPUT_DIR); an explicit BUILD_DIR
# from the environment beats it, and is then also not recorded as "the one
# built last" (run/install/docs would otherwise be sent to a directory the
# layout doesn't know about).
_pmn_record_build=1
if [ -n "${BUILD_DIR:-}" ]; then
_pmn_record_build=0
else
BUILD_DIR="$(_pmn_resolve_build_dir "${PRESET:-default}")"
fi
# If the first remaining argument doesn't start with "-", it's BUILD_TYPE
# and everything after it is CMAKE_ARGS; otherwise there's no BUILD_TYPE
# override and every argument is CMAKE_ARGS. Plain bash "$@"/shift, no
# special-casing needed: unlike Windows cmd.exe (see build.bat), bash
# never splits a "-DFOO=BAR"-style argument on its own "=".
if [ "$#" -gt 0 ] && [[ "$1" != -* ]]; then
BUILD_TYPE="$1"
shift
else
BUILD_TYPE="$(_pmn_read_config DEFAULT_BUILD_TYPE)"
fi
CMAKE_ARGS=("$@")
# ${arr[@]+"${arr[@]}"} rather than a plain "${arr[@]}" wherever an array can be
# empty: under `set -u`, bash before 4.4 -- which includes the 3.2 that macOS
# still ships as /bin/bash -- treats expanding an EMPTY array as an unset
# variable and aborts with "unbound variable".
if [ -n "$PRESET" ] && _pmn_has_explicit_dist_target ${CMAKE_ARGS[@]+"${CMAKE_ARGS[@]}"}; then
echo "error: both --preset ${PRESET} and an explicit -DDIST_TARGET=... were given." >&2
echo " A preset's toolchain already declares DIST_TARGET (and picks the compiler) -- pick one:" >&2
echo " breeze/scripts/build.sh --preset ${PRESET}" >&2
echo " breeze/scripts/build.sh -DCMAKE_TOOLCHAIN_FILE=breeze/cmake/toolchains/<target>.cmake" >&2
echo " See the comment at the top of this script, or TargetSelection.cmake's" >&2
echo " \"Target selection\" section, for why these two aren't combined." >&2
exit 1
fi
# Prefer a pinned ninja (fetched by breeze/scripts/setup.sh -- see
# breeze/cmake/FetchNinja.cmake) over a system one, same "always the pinned copy"
# preference as Doxygen. Needed whether or not --preset is used: every
# preset sets its generator to Ninja but doesn't hardcode which ninja
# binary to use (that would just be TOOLCACHE_DIR's default duplicated a
# third time -- see project/Configuration.cmake) -- CMAKE_MAKE_PROGRAM supplies it
# either way; only -G Ninja itself is skipped below when a preset already
# sets the generator.
TOOLCACHE_DIR="${TOOLCACHE_DIR:-$(_pmn_read_config TOOLCACHE_DIR)}"
NINJA_BIN=""
for _candidate in "${TOOLCACHE_DIR}"/ninja-*/ninja; do
if [ -x "$_candidate" ]; then
# Must be absolute: CMake invokes CMAKE_MAKE_PROGRAM from several
# different working directories during its own internal
# try-compile steps, where a relative path doesn't resolve.
NINJA_BIN="$(cd "$(dirname "$_candidate")" && pwd)/$(basename "$_candidate")"
break
fi
done
GENERATOR_ARGS=()
if [ -n "$NINJA_BIN" ]; then
echo "==> Using pinned ninja: ${NINJA_BIN}"
if [ -z "$PRESET" ]; then
GENERATOR_ARGS=(-G Ninja "-DCMAKE_MAKE_PROGRAM=${NINJA_BIN}")
else
GENERATOR_ARGS=("-DCMAKE_MAKE_PROGRAM=${NINJA_BIN}")
fi
elif command -v ninja >/dev/null 2>&1; then
echo "==> Using system ninja: $(command -v ninja)"
if [ -z "$PRESET" ]; then
GENERATOR_ARGS=(-G Ninja)
fi
else
if [ -n "$PRESET" ]; then
echo "warning: ninja not found (no pinned copy, none on PATH) -- the '${PRESET}'" >&2
echo " preset requires it; configure will likely fail. Run" >&2
echo " breeze/scripts/setup.sh to fetch a pinned ninja, or install ninja" >&2
echo " yourself." >&2
else
echo "warning: ninja not found (no pinned copy, none on PATH) -- falling back to" >&2
echo " CMake's default generator for this platform. Run breeze/scripts/setup.sh" >&2
echo " to fetch a pinned ninja, or install ninja yourself, for a faster" >&2
echo " and more consistent build. Continuing without it." >&2
fi
fi
CONFIGURE_ARGS=(-S . -B "${BUILD_DIR}")
if [ -n "$PRESET" ]; then
CONFIGURE_ARGS=(--preset "${PRESET}" "${CONFIGURE_ARGS[@]}")
fi
echo "==> Configuring (${BUILD_TYPE}) into ${BUILD_DIR}/"
if [ -n "$PRESET" ]; then
echo " Preset: ${PRESET}"
fi
if [ "${#CMAKE_ARGS[@]}" -gt 0 ]; then
echo " Extra CMake arguments: ${CMAKE_ARGS[*]}"
fi
# The configure's output is also kept in a temporary file, only so that a
# failed cross configure can be explained afterwards (_pmn_explain_configure_
# failure, breeze/scripts/_common.sh). Its exit status is still what decides.
# If no temporary file can be made the build goes on without the explanation.
_pmn_cfg_log="$(mktemp "${TMPDIR:-/tmp}/pmn-configure.XXXXXX" 2>/dev/null)" || _pmn_cfg_log=/dev/null
trap 'if [ -f "$_pmn_cfg_log" ]; then rm -f "$_pmn_cfg_log"; fi' EXIT
if ! cmake "${CONFIGURE_ARGS[@]}" ${GENERATOR_ARGS[@]+"${GENERATOR_ARGS[@]}"} -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" ${CMAKE_ARGS[@]+"${CMAKE_ARGS[@]}"} 2>&1 | tee "$_pmn_cfg_log"; then
_pmn_explain_configure_failure "$_pmn_cfg_log" "${BUILD_DIR:-}"
exit 1
fi
# Configure succeeded: this is now the build the other scripts default to.
if [ "$_pmn_record_build" = 1 ]; then
_pmn_record_build_dir "${PRESET:-default}"
fi
echo "==> Building"
cmake --build "${BUILD_DIR}"
echo "==> Done. Run a module with: breeze/scripts/run.sh <target-name>"
+136
View File
@@ -0,0 +1,136 @@
@echo off
REM Build Doxygen documentation for every module (see breeze\cmake\Documentation.cmake)
REM and print the path to the landing page. A no-op with a clear message if
REM Doxygen isn't installed or ENABLE_DOXYGEN=OFF.
REM
REM Output goes to out\docs\ (DOCS_OUTPUT_DIR in project/Configuration.cmake) --
REM note this is a sibling of out\build, not nested inside it, and shared by
REM every preset: documentation doesn't depend on which compiler built the
REM code. The docs targets just need SOME configured build directory to run
REM from, so this uses the one built last unless you pick with --preset. If
REM you've overridden DOCS_OUTPUT_DIR at configure time, also set DOCS_DIR here
REM to match so the printed path is correct (docs still land in the right
REM place either way -- this only affects the message below).
REM
REM Usage (from the project root):
REM breeze\scripts\docs.bat REM every module
REM breeze\scripts\docs.bat core_lib REM just one module's docs (docs_<target>)
REM breeze\scripts\docs.bat --preset windows-x64-clang-cl REM ... from that preset's build directory
REM breeze\scripts\docs.bat --help
REM
REM --preset must come first if given at all; --preset=<name> also works. Set
REM BUILD_DIR to use an exact build directory instead.
setlocal
if /i "%~1"=="-h" goto show_help
if /i "%~1"=="--help" goto show_help
goto after_help
:show_help
echo Usage: breeze\scripts\docs.bat [--preset ^<name^>] [module-target-name]
echo.
echo Builds Doxygen documentation for every module ^(see breeze\cmake\Documentation.cmake^)
echo and prints the path to the landing page. A no-op with a clear message if
echo Doxygen isn't installed or ENABLE_DOXYGEN=OFF.
echo.
echo --preset ^<name^> Run from that preset's build directory
echo ^(out\build\^<name^>^). Default: the build
echo breeze\scripts\build.bat made last. Must come first if
echo given at all; --preset=^<name^> also works.
echo ^(no argument^) Build docs for every module.
echo module-target-name Build docs for just that one module ^(its own
echo docs_^<name^> target^), e.g.:
echo breeze\scripts\docs.bat core_lib
echo.
echo Output goes to out\docs\ ^(DOCS_OUTPUT_DIR in project/Configuration.cmake^), shared
echo by every preset -- see the comment at the top of this script if you've
echo overridden that.
exit /b 0
:after_help
cd /d "%~dp0..\.."
where cmake >nul 2>nul
if errorlevel 1 (
echo error: cmake not found on PATH.
exit /b 1
)
REM --preset must come first if given at all, same as breeze\scripts\build.bat.
REM cmd.exe splits "--preset=<name>" into "--preset" and "<name>" on its own
REM (see build.bat's note on "="), so both spellings arrive here identically
REM and this one branch handles both.
set "PRESET="
if /i not "%~1"=="--preset" goto preset_done
if "%~2"=="" (
echo error: --preset requires a value, e.g. --preset windows-x64-clang-cl 1>&2
exit /b 1
)
set "PRESET=%~2"
shift
shift
:preset_done
REM Suffix for the error hints below that say to run build.bat first.
set "_pmn_hint="
if defined PRESET set "_pmn_hint= --preset %PRESET%"
REM BUILD_DIR from the environment beats everything; otherwise --preset, else
REM whichever build was made last (see breeze\cmake\BuildDir.cmake). Explains itself
REM and stops if there's nothing to pick.
if not "%BUILD_DIR%"=="" goto build_dir_known
call :resolve_build_dir "%PRESET%" BUILD_DIR
if errorlevel 1 exit /b 1
:build_dir_known
if "%DOCS_DIR%"=="" set "DOCS_DIR=out\docs"
if not exist "%BUILD_DIR%" (
echo error: "%BUILD_DIR%" not found. Run breeze\scripts\build.bat%_pmn_hint% first.
exit /b 1
)
echo ==^> Using build directory %BUILD_DIR%
if "%~1"=="" (
cmake --build "%BUILD_DIR%" --target docs
if exist "%DOCS_DIR%\index.html" (
echo ==^> Open: %DOCS_DIR%\index.html
)
) else (
cmake --build "%BUILD_DIR%" --target docs_%1
echo ==^> Open: %DOCS_DIR%\%1\html\index.html
)
exit /b %ERRORLEVEL%
REM ---------------------------------------------------------------------
REM :resolve_build_dir <name-or-empty> <LocalVarName> [quiet]
REM
REM Asks breeze\cmake\BuildDir.cmake which build directory to use -- %1 is a preset
REM name, or "default"; empty means "discover one" (the build made last, or
REM the only one) -- and stores it into %2 in the CALLER's scope, with
REM backslashes. Same shape as build.bat's, and the same `cmake -P`
REM one-line-answer protocol: "-- BUILD_DIR=<path>" or "-- ERROR=<text>"
REM (see that file). On failure it prints the reason to stderr, unless a
REM third argument "quiet" is given, and returns errorlevel 1.
REM ---------------------------------------------------------------------
:resolve_build_dir
setlocal
set "_pmn_key="
set "_pmn_val="
for /f "usebackq tokens=1,* delims==" %%A in (`cmake "-DPMN_NAME=%~1" -P breeze\cmake\BuildDir.cmake`) do (
set "_pmn_key=%%A"
set "_pmn_val=%%B"
)
if "%_pmn_key%"=="-- BUILD_DIR" if defined _pmn_val goto resolve_ok
if /i "%~3"=="quiet" goto resolve_failed
if defined _pmn_val (
echo error: %_pmn_val% 1>&2
) else (
echo error: breeze\cmake\BuildDir.cmake did not answer. 1>&2
)
:resolve_failed
endlocal
exit /b 1
:resolve_ok
endlocal & set "%~2=%_pmn_val:/=\%"
goto :eof
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
# Build Doxygen documentation for every module (see breeze/cmake/Documentation.cmake)
# and print the path to the landing page. A no-op with a clear message if
# Doxygen isn't installed or ENABLE_DOXYGEN=OFF.
#
# Output goes to out/docs/ (DOCS_OUTPUT_DIR in project/Configuration.cmake) --
# note this is a sibling of out/build, not nested inside it, and shared by
# every preset: documentation doesn't depend on which compiler built the
# code. The docs targets just need SOME configured build directory to run
# from, so this uses the one built last unless you pick with --preset. If
# you've overridden DOCS_OUTPUT_DIR at configure time, also set DOCS_DIR here
# to match so the printed path is correct (docs still land in the right
# place either way -- this only affects the message below).
#
# Usage (from the project root):
# breeze/scripts/docs.sh # every module
# breeze/scripts/docs.sh core_lib # just one module's docs (docs_<target>)
# breeze/scripts/docs.sh --preset linux-x64-gcc # ... from that preset's build directory
# breeze/scripts/docs.sh --help
#
# --preset must come first if given at all; --preset=<name> also works. Set
# BUILD_DIR to use an exact build directory instead.
set -euo pipefail
case "${1:-}" in
-h|--help)
cat <<'HELP'
Usage: breeze/scripts/docs.sh [--preset <name>] [module-target-name]
Builds Doxygen documentation for every module (see breeze/cmake/Documentation.cmake)
and prints the path to the landing page. A no-op with a clear message if
Doxygen isn't installed or ENABLE_DOXYGEN=OFF.
--preset <name> Run from that preset's build directory
(out/build/<name>). Default: the build
breeze/scripts/build.sh made last. Must come first if
given at all; --preset=<name> also works.
(no argument) Build docs for every module.
module-target-name Build docs for just that one module (its own
docs_<name> target), e.g.:
breeze/scripts/docs.sh core_lib
Output goes to out/docs/ (DOCS_OUTPUT_DIR in project/Configuration.cmake), shared
by every preset -- see the comment at the top of this script if you've
overridden that.
HELP
exit 0
;;
esac
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../.."
# _pmn_read_config, _pmn_resolve_build_dir, _pmn_record_build_dir.
source "$SCRIPT_DIR/_common.sh"
# --preset must come first if given at all, same as breeze/scripts/build.sh.
PRESET=""
case "${1:-}" in
--preset)
if [ "$#" -lt 2 ] || [[ "$2" == -* ]]; then
echo "error: --preset requires a value, e.g. --preset linux-x64-gcc" >&2
exit 1
fi
PRESET="$2"
shift 2
;;
--preset=*)
PRESET="${1#--preset=}"
shift
if [ -z "$PRESET" ]; then
echo "error: --preset= requires a value, e.g. --preset=linux-x64-gcc" >&2
exit 1
fi
;;
esac
if [ -z "${BUILD_DIR:-}" ]; then
# Explains itself and stops if there's nothing to pick.
BUILD_DIR="$(_pmn_resolve_build_dir "$PRESET")"
fi
DOCS_DIR="${DOCS_DIR:-out/docs}"
if [ ! -d "$BUILD_DIR" ]; then
echo "error: '${BUILD_DIR}' not found. Run breeze/scripts/build.sh${PRESET:+ --preset $PRESET} first." >&2
exit 1
fi
echo "==> Using build directory ${BUILD_DIR}"
if [ $# -ge 1 ]; then
TARGET="docs_$1"
cmake --build "${BUILD_DIR}" --target "${TARGET}"
echo "==> Open: ${DOCS_DIR}/$1/html/index.html"
else
cmake --build "${BUILD_DIR}" --target docs
if [ -f "${DOCS_DIR}/index.html" ]; then
echo "==> Open: ${DOCS_DIR}/index.html"
fi
fi
+151
View File
@@ -0,0 +1,151 @@
@echo off
REM Install this project's own build artifacts to
REM out\install\<platform>-<arch>-<compiler>\ (INSTALL_OUTPUT_DIR in
REM project/Configuration.cmake; see breeze\cmake\Breeze.cmake's install() DESTINATION paths) --
REM the same name as the out\build\<preset> directory they came from, since
REM a preset is named after its target.
REM
REM Always passes --component to `cmake --install`. Without it, any
REM vendored/fetched dependency's own install() rules (see
REM breeze\cmake\Dependencies.cmake) would also run, typically dumping files under
REM CMAKE_INSTALL_PREFIX -- COMPONENT is what keeps `cmake --install`
REM scoped to just this project's own targets.
REM
REM Usage (from the project root):
REM breeze\scripts\install.bat [--preset <name>] [BUILD_TYPE]
REM breeze\scripts\install.bat --help
REM
REM --preset <name> Install the build made with that preset
REM (out\build\<name>). Without it: the build
REM breeze\scripts\build.bat made last, or the only one that
REM exists. Must come first if given at all;
REM --preset=<name> also works.
REM BUILD_TYPE Debug|Release|RelWithDebInfo|MinSizeRel
REM (default: project/Configuration.cmake's DEFAULT_BUILD_TYPE) --
REM only relevant for a Ninja Multi-Config build.
REM
REM Set BUILD_DIR to install from an exact build directory instead.
setlocal enabledelayedexpansion
if /i "%~1"=="-h" goto show_help
if /i "%~1"=="--help" goto show_help
goto after_help
:show_help
echo Usage: breeze\scripts\install.bat [--preset ^<name^>] [BUILD_TYPE]
echo.
echo Installs this project's own build artifacts to
echo out\install\^<platform^>-^<arch^>-^<compiler^>\ ^(INSTALL_OUTPUT_DIR in
echo project/Configuration.cmake^), scoped to just this project via `cmake --install
echo --component` -- see the comment at the top of this script.
echo.
echo --preset ^<name^> Install the build made with that preset
echo ^(out\build\^<name^>^). Default: the build
echo breeze\scripts\build.bat made last. Must come first if given
echo at all; --preset=^<name^> also works.
echo BUILD_TYPE Debug^|Release^|RelWithDebInfo^|MinSizeRel
echo ^(default: project/Configuration.cmake's DEFAULT_BUILD_TYPE^) --
echo only relevant for a Ninja Multi-Config build.
exit /b 0
:after_help
cd /d "%~dp0..\.."
where cmake >nul 2>nul
if errorlevel 1 (
echo error: cmake not found on PATH.
exit /b 1
)
REM --preset must come first if given at all, same as breeze\scripts\build.bat.
REM cmd.exe splits "--preset=<name>" into "--preset" and "<name>" on its own
REM (see build.bat's note on "="), so both spellings arrive here identically
REM and this one branch handles both.
set "PRESET="
if /i not "%~1"=="--preset" goto preset_done
if "%~2"=="" (
echo error: --preset requires a value, e.g. --preset windows-x64-clang-cl 1>&2
exit /b 1
)
set "PRESET=%~2"
shift
shift
:preset_done
REM Suffix for the error hints below that say to run build.bat first.
set "_pmn_hint="
if defined PRESET set "_pmn_hint= --preset %PRESET%"
REM BUILD_DIR from the environment beats everything; otherwise --preset, else
REM whichever build was made last (see breeze\cmake\BuildDir.cmake). Explains itself
REM and stops if there's nothing to pick.
if not "%BUILD_DIR%"=="" goto build_dir_known
call :resolve_build_dir "%PRESET%" BUILD_DIR
if errorlevel 1 exit /b 1
:build_dir_known
REM A plain %~1 is safe here (unlike build.bat's arguments): a build type
REM like "Debug" never contains "=", so cmd.exe's positional-parameter
REM splitting on it never comes into play.
set "BUILD_TYPE=%~1"
if "%BUILD_TYPE%"=="" call :read_config DEFAULT_BUILD_TYPE BUILD_TYPE
call :read_config PROJECT_CODE_NAME COMPONENT
if not exist "%BUILD_DIR%" (
echo error: "%BUILD_DIR%" not found. Run breeze\scripts\build.bat%_pmn_hint% first.
exit /b 1
)
echo ==^> Installing (component: %COMPONENT%) from %BUILD_DIR%
cmake --install "%BUILD_DIR%" --config %BUILD_TYPE% --component %COMPONENT%
exit /b %ERRORLEVEL%
REM ---------------------------------------------------------------------
REM :read_config <ConfigurationVarName> <LocalVarName>
REM
REM Reads one value out of project/Configuration.cmake -- this project's single
REM source of truth -- via breeze\cmake\PrintConfig.cmake, instead of hardcoding
REM a second copy of a default that can silently drift out of sync, and
REM stores it into %2 in the CALLER's scope. See breeze\cmake\PrintConfig.cmake
REM for exactly what's being stripped off of its output below.
REM ---------------------------------------------------------------------
:read_config
setlocal
set "_pmn_line="
for /f "usebackq delims=" %%V in (`cmake "-DPRINT_VARS=%~1" -P breeze\cmake\PrintConfig.cmake`) do set "_pmn_line=%%V"
for /f "tokens=1,* delims==" %%A in ("%_pmn_line%") do set "_pmn_value=%%B"
endlocal & set "%~2=%_pmn_value%"
goto :eof
REM ---------------------------------------------------------------------
REM :resolve_build_dir <name-or-empty> <LocalVarName> [quiet]
REM
REM Asks breeze\cmake\BuildDir.cmake which build directory to use -- %1 is a preset
REM name, or "default"; empty means "discover one" (the build made last, or
REM the only one) -- and stores it into %2 in the CALLER's scope, with
REM backslashes. Same shape as build.bat's, and the same `cmake -P`
REM one-line-answer protocol: "-- BUILD_DIR=<path>" or "-- ERROR=<text>"
REM (see that file). On failure it prints the reason to stderr, unless a
REM third argument "quiet" is given, and returns errorlevel 1.
REM ---------------------------------------------------------------------
:resolve_build_dir
setlocal
set "_pmn_key="
set "_pmn_val="
for /f "usebackq tokens=1,* delims==" %%A in (`cmake "-DPMN_NAME=%~1" -P breeze\cmake\BuildDir.cmake`) do (
set "_pmn_key=%%A"
set "_pmn_val=%%B"
)
if "%_pmn_key%"=="-- BUILD_DIR" if defined _pmn_val goto resolve_ok
if /i "%~3"=="quiet" goto resolve_failed
if defined _pmn_val (
echo error: %_pmn_val% 1>&2
) else (
echo error: breeze\cmake\BuildDir.cmake did not answer. 1>&2
)
:resolve_failed
endlocal
exit /b 1
:resolve_ok
endlocal & set "%~2=%_pmn_val:/=\%"
goto :eof
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
# Install this project's own build artifacts to
# out/install/<platform>-<arch>-<compiler>/ (INSTALL_OUTPUT_DIR in
# project/Configuration.cmake; see breeze/cmake/Breeze.cmake's install() DESTINATION paths) --
# the same name as the out/build/<preset> directory they came from, since
# a preset is named after its target.
#
# Always passes --component to `cmake --install`. Without it, any
# vendored/fetched dependency's own install() rules (see
# breeze/cmake/Dependencies.cmake) would also run, typically dumping files under
# CMAKE_INSTALL_PREFIX (e.g. /usr/local) -- COMPONENT is what keeps
# `cmake --install` scoped to just this project's own targets.
#
# Usage (from the project root):
# breeze/scripts/install.sh [--preset <name>] [BUILD_TYPE]
# breeze/scripts/install.sh --help
#
# --preset <name> Install the build made with that preset
# (out/build/<name>). Without it: the build
# breeze/scripts/build.sh made last, or the only one that
# exists. Must come first if given at all;
# --preset=<name> also works.
# BUILD_TYPE Debug|Release|RelWithDebInfo|MinSizeRel
# (default: project/Configuration.cmake's DEFAULT_BUILD_TYPE) --
# only relevant for a Ninja Multi-Config build.
#
# Set BUILD_DIR to install from an exact build directory instead.
set -euo pipefail
case "${1:-}" in
-h|--help)
cat <<'HELP'
Usage: breeze/scripts/install.sh [--preset <name>] [BUILD_TYPE]
Installs this project's own build artifacts to
out/install/<platform>-<arch>-<compiler>/ (INSTALL_OUTPUT_DIR in
project/Configuration.cmake), scoped to just this project via `cmake --install
--component` -- see the comment at the top of this script.
--preset <name> Install the build made with that preset (out/build/<name>).
Default: the build breeze/scripts/build.sh made last. Must come
first if given at all; --preset=<name> also works.
BUILD_TYPE Debug|Release|RelWithDebInfo|MinSizeRel
(default: project/Configuration.cmake's DEFAULT_BUILD_TYPE) --
only relevant for a Ninja Multi-Config build.
HELP
exit 0
;;
esac
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../.."
command -v cmake >/dev/null 2>&1 || { echo "error: cmake not found on PATH." >&2; exit 1; }
# _pmn_read_config, _pmn_resolve_build_dir, _pmn_record_build_dir. Reading
# values out of project/Configuration.cmake this way -- its single source of truth
# -- is the bug fix that replaced COMPONENT being a literal "demo" here,
# with a comment warning it had to be kept in sync with PROJECT_CODE_NAME by
# hand.
source "$SCRIPT_DIR/_common.sh"
# --preset must come first if given at all, same as breeze/scripts/build.sh.
PRESET=""
case "${1:-}" in
--preset)
if [ "$#" -lt 2 ] || [[ "$2" == -* ]]; then
echo "error: --preset requires a value, e.g. --preset linux-x64-gcc" >&2
exit 1
fi
PRESET="$2"
shift 2
;;
--preset=*)
PRESET="${1#--preset=}"
shift
if [ -z "$PRESET" ]; then
echo "error: --preset= requires a value, e.g. --preset=linux-x64-gcc" >&2
exit 1
fi
;;
esac
if [ -z "${BUILD_DIR:-}" ]; then
# Explains itself and stops if there's nothing to pick.
BUILD_DIR="$(_pmn_resolve_build_dir "$PRESET")"
fi
BUILD_TYPE="${1:-$(_pmn_read_config DEFAULT_BUILD_TYPE)}"
COMPONENT="$(_pmn_read_config PROJECT_CODE_NAME)"
if [ ! -d "$BUILD_DIR" ]; then
echo "error: '${BUILD_DIR}' not found. Run breeze/scripts/build.sh${PRESET:+ --preset $PRESET} first." >&2
exit 1
fi
echo "==> Installing (component: ${COMPONENT}) from ${BUILD_DIR}"
cmake --install "${BUILD_DIR}" --config "${BUILD_TYPE}" --component "${COMPONENT}"
+104
View File
@@ -0,0 +1,104 @@
@echo off
REM Applies clang-format and clang-tidy's --fix across this project's own
REM C/C++ code. The pre-commit hook only CHECKS; this is the "just fix it"
REM counterpart -- review the result with `git diff` afterward, same as any
REM auto-formatter.
REM
REM Defaults to source\ and c-api\, this template's own-code roots -- NEVER
REM walks out\, .cache\, vendor\, or .git\ by default, so it can't
REM accidentally spend minutes reformatting/analyzing a fetched dependency's
REM entire source tree the way scanning the whole repo from "." would.
REM
REM Usage, from the project root: breeze\scripts\refactor.bat [optional path]
REM breeze\scripts\refactor.bat every file under source\ and c-api\
REM breeze\scripts\refactor.bat source\Core just one file or directory -- taken
REM as given, with no exclusion filter,
REM so pick a path that doesn't overlap
REM with out\/.cache\/vendor\/.git\
REM breeze\scripts\refactor.bat --help
setlocal enabledelayedexpansion
if /i "%~1"=="-h" goto show_help
if /i "%~1"=="--help" goto show_help
goto after_help
:show_help
echo Usage: breeze\scripts\refactor.bat [scope]
echo.
echo Applies clang-format and clang-tidy's --fix across this project's own
echo C/C++ code -- review the result with `git diff` afterward. See the
echo comment at the top of this script for the default scope and why it
echo never walks out\, .cache\, vendor\, or .git\.
echo.
echo ^(no argument^) Every file under source\ and c-api\ ^(whichever exist^).
echo scope Just one file or directory, e.g.:
echo breeze\scripts\refactor.bat source\Core
exit /b 0
:after_help
cd /d "%~dp0..\.."
where clang-format >nul 2>nul
set "HAVE_FORMAT=0"
if not errorlevel 1 set "HAVE_FORMAT=1"
where clang-tidy >nul 2>nul
set "HAVE_TIDY=0"
if not errorlevel 1 set "HAVE_TIDY=1"
if "%HAVE_FORMAT%"=="1" goto have_a_tool
if "%HAVE_TIDY%"=="1" goto have_a_tool
echo error: neither clang-format nor clang-tidy found on PATH. Run breeze\scripts\setup.bat for install guidance.
exit /b 1
:have_a_tool
set "FILELIST=%TEMP%\refactor_files_%RANDOM%.txt"
if exist "%FILELIST%" del "%FILELIST%"
REM Collect the file list. Deliberately NOT using an if/else block here --
REM a for /f or for /r loop nested inside a parenthesized if(...) else(...)
REM block confuses cmd.exe's paren-matching for the enclosing block (this
REM was tested and confirmed, not a guess); goto-based branching sidesteps
REM the problem entirely by never nesting a FOR loop inside another block.
if not "%~1"=="" goto collect_custom
set "SCOPE_DESC=source and c-api"
if exist "source" for /r "source" %%F in (*.c *.h *.cpp *.hpp *.cc *.cxx *.tpp) do echo %%F>>"%FILELIST%"
if exist "c-api" for /r "c-api" %%F in (*.c *.h *.cpp *.hpp *.cc *.cxx *.tpp) do echo %%F>>"%FILELIST%"
goto collected
:collect_custom
set "SCOPE_DESC=%~1"
if not exist "%~1" goto collected
for /r "%~1" %%F in (*.c *.h *.cpp *.hpp *.cc *.cxx *.tpp) do echo %%F>>"%FILELIST%"
:collected
if exist "%FILELIST%" goto have_files
echo No C/C++ files found under: %SCOPE_DESC%
exit /b 0
:have_files
echo ==^> Files in scope: %SCOPE_DESC%
if "%HAVE_FORMAT%"=="1" goto do_format
echo ==^> Skipping clang-format ^(not found on PATH -- see breeze\scripts\setup.bat^)
goto after_format
:do_format
echo ==^> Running clang-format -i
for /f "usebackq delims=" %%F in ("%FILELIST%") do clang-format -i "%%F"
:after_format
if "%HAVE_TIDY%"=="1" goto do_tidy
echo ==^> Skipping clang-tidy ^(not found on PATH -- see breeze\scripts\setup.bat^)
goto after_tidy
:do_tidy
if exist compile_commands.json goto run_tidy
echo ==^> Skipping clang-tidy ^(no compile_commands.json -- run breeze\scripts\build.bat first^)
goto after_tidy
:run_tidy
echo ==^> Running clang-tidy --fix ^(auto-fixable issues only; build once first if this is stale^)
for /f "usebackq delims=" %%F in ("%FILELIST%") do clang-tidy --quiet --fix "%%F"
:after_tidy
del "%FILELIST%" 2>nul
echo ==^> Done. Review the changes: git diff
exit /b 0
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env bash
# Applies clang-format and clang-tidy's --fix across this project's own
# C/C++ code. The pre-commit hook only CHECKS; this is the "just fix it"
# counterpart -- review the result with `git diff` afterward, same as any
# auto-formatter.
#
# Defaults to source/ and c-api/ (this template's own-code roots) --
# NEVER walks out/, .cache/, vendor/, or .git/ by default, so it can't
# accidentally spend minutes reformatting/analyzing a fetched dependency's
# entire source tree (e.g. out/build/_deps/*-src/) the way scanning the
# whole repo from "." would. Passing an explicit scope still excludes
# those four directories defensively, in case the scope you give overlaps
# with them.
#
# Usage (from the project root):
# breeze/scripts/refactor.sh # every file under source/ and c-api/
# breeze/scripts/refactor.sh source/Core # just one file or directory
# breeze/scripts/refactor.sh --help
set -euo pipefail
case "${1:-}" in
-h|--help)
cat <<'EOF'
Usage: breeze/scripts/refactor.sh [scope]
Applies clang-format and clang-tidy's --fix across this project's own
C/C++ code -- review the result with `git diff` afterward. See the
comment at the top of this script for the default scope and why it never
walks out/, .cache/, vendor/, or .git/.
(no argument) Every file under source/ and c-api/ (whichever exist).
scope Just one file or directory, e.g.:
breeze/scripts/refactor.sh source/Core
EOF
exit 0
;;
esac
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../.."
# _pmn_extend_tool_path: finds Homebrew's keg-only llvm (clang-tidy) on macOS.
source "$SCRIPT_DIR/_common.sh"
_pmn_extend_tool_path
if [ $# -ge 1 ]; then
scopes=("$1")
else
scopes=()
[ -d source ] && scopes+=(source)
[ -d c-api ] && scopes+=(c-api)
fi
if [ "${#scopes[@]}" -eq 0 ]; then
echo "No default scope found (expected a source/ and/or c-api/ directory here)." >&2
exit 1
fi
if ! command -v clang-format >/dev/null 2>&1 && ! command -v clang-tidy >/dev/null 2>&1; then
echo "error: neither clang-format nor clang-tidy found on PATH. Run breeze/scripts/setup.sh for install guidance." >&2
exit 1
fi
# A read loop rather than mapfile/readarray: those are bash 4+, and macOS
# ships bash 3.2.
files=()
while IFS= read -r _file; do
files+=("$_file")
done < <(
find "${scopes[@]}" \
\( -path './vendor' -o -path './out' -o -path './.cache' -o -path './.git' \) -prune -o \
-type f \( -name '*.c' -o -name '*.h' -o -name '*.cpp' -o -name '*.hpp' -o -name '*.cc' -o -name '*.cxx' -o -name '*.tpp' \) -print \
| sed 's|^\./||' | sort
)
if [ "${#files[@]}" -eq 0 ]; then
echo "No C/C++ files found under: ${scopes[*]}"
exit 0
fi
echo "==> ${#files[@]} file(s) in scope: ${scopes[*]}"
if command -v clang-format >/dev/null 2>&1; then
echo "==> Running clang-format -i"
clang-format -i "${files[@]}"
else
echo "==> Skipping clang-format (not found on PATH -- see breeze/scripts/setup.sh)"
fi
if command -v clang-tidy >/dev/null 2>&1; then
if [ ! -f compile_commands.json ]; then
echo "==> Skipping clang-tidy (no compile_commands.json -- run breeze/scripts/build.sh first)"
else
echo "==> Running clang-tidy --fix (auto-fixable issues only; build once first if this is stale)"
for f in "${files[@]}"; do
clang-tidy --quiet --fix "$f" || true
done
fi
else
echo "==> Skipping clang-tidy (not found on PATH -- see breeze/scripts/setup.sh)"
fi
echo "==> Done. Review the changes: git diff"
+185
View File
@@ -0,0 +1,185 @@
@echo off
REM Run an executable module by its CMake target name (as registered in
REM that module's own CMakeLists.txt), resolved via the manifest that
REM _write_module_manifest() generates at configure time -- so this script
REM never needs updating when modules are added, removed, or renamed.
REM
REM Usage (from the project root):
REM breeze\scripts\run.bat [--preset <name>] <target-name> [-- program-args...]
REM
REM --preset <name> Run the build made with that preset (out\build\<name>).
REM Without it: the build breeze\scripts\build.bat made last, or
REM the only one that exists. Must come first if given at
REM all; --preset=<name> also works.
REM
REM Examples:
REM breeze\scripts\run.bat hello_exe
REM breeze\scripts\run.bat --preset windows-x64-clang-cl hello_exe
REM breeze\scripts\run.bat farewell_exe -- Ale
REM
REM Set BUILD_DIR to run out of an exact build directory instead.
setlocal enabledelayedexpansion
cd /d "%~dp0..\.."
REM --preset must come first if given at all, same as breeze\scripts\build.bat.
REM cmd.exe splits "--preset=<name>" into "--preset" and "<name>" on its own
REM (see build.bat's note on "="), so both spellings arrive here identically
REM and this one branch handles both.
set "PRESET="
if /i not "%~1"=="--preset" goto preset_done
if "%~2"=="" (
echo error: --preset requires a value, e.g. --preset windows-x64-clang-cl 1>&2
exit /b 1
)
set "PRESET=%~2"
shift
shift
:preset_done
REM Suffix for the error hints below that say to run build.bat first.
set "_pmn_hint="
if defined PRESET set "_pmn_hint= --preset %PRESET%"
if /i "%~1"=="-h" goto show_help
if /i "%~1"=="--help" goto show_help
if "%~1"=="" goto usage
set "TARGET_NAME=%~1"
shift
REM Collect any remaining args to pass through to the program (skipping a
REM leading "--" separator if present).
set "PASSTHROUGH="
set "skip_next_dashes=1"
:collect
if "%~1"=="" goto collected
if "!skip_next_dashes!"=="1" if "%~1"=="--" (
set "skip_next_dashes=0"
shift
goto collect
)
set "skip_next_dashes=0"
set "PASSTHROUGH=!PASSTHROUGH! "%~1""
shift
goto collect
:collected
REM BUILD_DIR from the environment beats everything; otherwise --preset, else
REM whichever build was made last (see breeze\cmake\BuildDir.cmake). Explains itself
REM and stops if there's nothing to pick.
if not "%BUILD_DIR%"=="" goto build_dir_known
where cmake >nul 2>nul
if errorlevel 1 (
echo error: cmake not found on PATH -- it is needed to work out which build directory to use. 1>&2
echo Put it on PATH, or set BUILD_DIR to the directory explicitly, e.g. out\build\^<preset^>. 1>&2
exit /b 1
)
call :resolve_build_dir "%PRESET%" BUILD_DIR
if errorlevel 1 exit /b 1
:build_dir_known
set "MANIFEST="
for %%F in ("%BUILD_DIR%\module_manifest-*.txt") do (
if not defined MANIFEST set "MANIFEST=%%F"
)
if not defined MANIFEST (
echo error: no build manifest found in "%BUILD_DIR%". Run breeze\scripts\build.bat%_pmn_hint% first. 1>&2
exit /b 1
)
set "BIN_PATH="
for /f "usebackq tokens=1,* delims==" %%K in ("%MANIFEST%") do (
if "%%K"=="%TARGET_NAME%" set "BIN_PATH=%%L"
)
if not defined BIN_PATH (
echo error: no executable target named "%TARGET_NAME%" in "%BUILD_DIR%". 1>&2
echo Available targets: 1>&2
for /f "usebackq tokens=1 delims==" %%T in ("%MANIFEST%") do echo %%T 1>&2
exit /b 1
)
if not exist "%BIN_PATH%" (
echo error: "%BIN_PATH%" was not found. Rebuild with breeze\scripts\build.bat%_pmn_hint%. 1>&2
exit /b 1
)
REM Which build this came from -- to stderr, so a program's own stdout stays
REM clean to pipe.
echo ==^> Running %TARGET_NAME% ^(%BUILD_DIR%^) 1>&2
"%BIN_PATH%" !PASSTHROUGH!
exit /b %ERRORLEVEL%
:usage
REM Best-effort here: usage should still print with nothing built yet.
if "%BUILD_DIR%"=="" call :resolve_build_dir "%PRESET%" BUILD_DIR quiet
echo Usage: %~nx0 [--preset ^<name^>] ^<cmake-target-name^> [-- program-args...] 1>&2
echo. 1>&2
echo Available executable targets: 1>&2
set "found=0"
for %%F in ("%BUILD_DIR%\module_manifest-*.txt") do (
set "found=1"
for /f "usebackq tokens=1 delims==" %%T in ("%%F") do echo %%T 1>&2
)
if "!found!"=="0" echo (none found -- did you run breeze\scripts\build.bat?) 1>&2
exit /b 1
:show_help
REM Best-effort here: help should still print with nothing built yet.
if "%BUILD_DIR%"=="" call :resolve_build_dir "%PRESET%" BUILD_DIR quiet
echo Usage: %~nx0 [--preset ^<name^>] ^<cmake-target-name^> [-- program-args...]
echo.
echo Runs an executable module by its CMake target name, resolved via the
echo manifest breeze\scripts\build.bat generates at configure time -- see the
echo comment at the top of this script.
echo.
echo --preset ^<name^> Run the build made with that preset. Default: the
echo build breeze\scripts\build.bat made last.
echo.
echo Available executable targets:
set "found=0"
for %%F in ("%BUILD_DIR%\module_manifest-*.txt") do (
set "found=1"
for /f "usebackq tokens=1 delims==" %%T in ("%%F") do echo %%T
)
if "!found!"=="0" echo (none found -- did you run breeze\scripts\build.bat?)
echo.
echo Examples:
echo breeze\scripts\run.bat hello_exe
echo breeze\scripts\run.bat --preset windows-x64-clang-cl hello_exe
echo breeze\scripts\run.bat farewell_exe -- Ale
exit /b 0
REM ---------------------------------------------------------------------
REM :resolve_build_dir <name-or-empty> <LocalVarName> [quiet]
REM
REM Asks breeze\cmake\BuildDir.cmake which build directory to use -- %1 is a preset
REM name, or "default"; empty means "discover one" (the build made last, or
REM the only one) -- and stores it into %2 in the CALLER's scope, with
REM backslashes. Same shape as build.bat's, and the same `cmake -P`
REM one-line-answer protocol: "-- BUILD_DIR=<path>" or "-- ERROR=<text>"
REM (see that file). On failure it prints the reason to stderr, unless a
REM third argument "quiet" is given, and returns errorlevel 1.
REM ---------------------------------------------------------------------
:resolve_build_dir
setlocal
set "_pmn_key="
set "_pmn_val="
for /f "usebackq tokens=1,* delims==" %%A in (`cmake "-DPMN_NAME=%~1" -P breeze\cmake\BuildDir.cmake`) do (
set "_pmn_key=%%A"
set "_pmn_val=%%B"
)
if "%_pmn_key%"=="-- BUILD_DIR" if defined _pmn_val goto resolve_ok
if /i "%~3"=="quiet" goto resolve_failed
if defined _pmn_val (
echo error: %_pmn_val% 1>&2
) else (
echo error: breeze\cmake\BuildDir.cmake did not answer. 1>&2
)
:resolve_failed
endlocal
exit /b 1
:resolve_ok
endlocal & set "%~2=%_pmn_val:/=\%"
goto :eof
+367
View File
@@ -0,0 +1,367 @@
#!/usr/bin/env bash
# Run an executable module by its CMake target name (as registered in
# that module's own CMakeLists.txt), resolved via the manifest that
# _write_module_manifest() generates at configure time -- so this script
# never needs updating when modules are added, removed, or renamed.
#
# Usage (from the project root):
# breeze/scripts/run.sh [--preset <name>] <target-name> [-- program-args...]
#
# --preset <name> Run the build made with that preset (out/build/<name>).
# Without it: the build breeze/scripts/build.sh made last, or
# the only one that exists. Must come first if given at
# all; --preset=<name> also works.
#
# Examples:
# breeze/scripts/run.sh hello_exe
# breeze/scripts/run.sh --preset linux-x64-clang hello_exe
# breeze/scripts/run.sh farewell_exe -- Ale
#
# Set BUILD_DIR to run out of an exact build directory instead.
#
# An executable built for another architecture or OS -- linux-arm64-* on an
# x64 machine, windows-*-mingw* from Linux -- is run under the emulator its
# toolchain registered (qemu-user, wine) if one was installed when the build
# was configured; otherwise it can only run on the target itself.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../.."
# _pmn_read_config, _pmn_resolve_build_dir, _pmn_record_build_dir.
source "$SCRIPT_DIR/_common.sh"
# --preset must come first if given at all, same as breeze/scripts/build.sh.
PRESET=""
case "${1:-}" in
--preset)
if [ "$#" -lt 2 ] || [[ "$2" == -* ]]; then
echo "error: --preset requires a value, e.g. --preset linux-x64-gcc" >&2
exit 1
fi
PRESET="$2"
shift 2
;;
--preset=*)
PRESET="${1#--preset=}"
shift
if [ -z "$PRESET" ]; then
echo "error: --preset= requires a value, e.g. --preset=linux-x64-gcc" >&2
exit 1
fi
;;
esac
# BUILD_DIR from the environment beats everything; otherwise --preset, else
# whichever build was made last (see breeze/cmake/BuildDir.cmake). Help and the
# usage message look it up best-effort -- they should still print on a
# fresh checkout with nothing built yet -- while actually running
# something needs a real answer, and stops with the reason if there isn't
# one.
_pmn_try_build_dir() {
if [ -z "${BUILD_DIR:-}" ]; then
BUILD_DIR="$(_pmn_resolve_build_dir "$PRESET" 2>/dev/null || true)"
fi
}
list_targets() {
local manifest
[ -n "${BUILD_DIR:-}" ] || return 0
for manifest in "${BUILD_DIR}"/module_manifest-*.txt; do
[ -e "$manifest" ] || continue
cut -d= -f1 "$manifest"
done | sort -u
}
usage() {
_pmn_try_build_dir
echo "Usage: $0 [--preset <name>] <cmake-target-name> [-- program-args...]" >&2
echo >&2
echo "Available executable targets:" >&2
local targets
targets="$(list_targets)"
if [ -n "$targets" ]; then
echo "$targets" | sed 's/^/ /' >&2
else
echo " (none found -- did you run breeze/scripts/build.sh?)" >&2
fi
exit 1
}
show_help() {
_pmn_try_build_dir
echo "Usage: $0 [--preset <name>] <cmake-target-name> [-- program-args...]"
echo
echo "Runs an executable module by its CMake target name, resolved via the"
echo "manifest breeze/scripts/build.sh generates at configure time -- see the"
echo "comment at the top of this script."
echo
echo " --preset <name> Run the build made with that preset. Default: the"
echo " build breeze/scripts/build.sh made last."
echo
echo "Available executable targets:"
local targets
targets="$(list_targets)"
if [ -n "$targets" ]; then
echo "$targets" | sed 's/^/ /'
else
echo " (none found -- did you run breeze/scripts/build.sh?)"
fi
echo
echo "Examples:"
echo " breeze/scripts/run.sh hello_exe"
echo " breeze/scripts/run.sh --preset linux-x64-clang hello_exe"
echo " breeze/scripts/run.sh farewell_exe -- Ale"
exit 0
}
case "${1:-}" in
-h|--help) show_help ;;
esac
[ $# -ge 1 ] || usage
TARGET_NAME="$1"
shift
if [ "${1:-}" = "--" ]; then
shift
fi
if [ -z "${BUILD_DIR:-}" ]; then
# Explains itself and stops if there's nothing to pick.
BUILD_DIR="$(_pmn_resolve_build_dir "$PRESET")"
fi
MANIFEST=""
for m in "${BUILD_DIR}"/module_manifest-*.txt; do
[ -e "$m" ] || continue
MANIFEST="$m"
break
done
if [ -z "$MANIFEST" ]; then
echo "error: no build manifest found in '${BUILD_DIR}'. Run breeze/scripts/build.sh${PRESET:+ --preset $PRESET} first." >&2
exit 1
fi
BIN_PATH="$(grep "^${TARGET_NAME}=" "$MANIFEST" | head -n1 | cut -d= -f2- || true)"
if [ -z "$BIN_PATH" ]; then
echo "error: no executable target named '${TARGET_NAME}' in '${BUILD_DIR}'." >&2
echo "Available targets:" >&2
list_targets | sed 's/^/ /' >&2
exit 1
fi
if [ ! -x "$BIN_PATH" ]; then
echo "error: '${BIN_PATH}' was not found or is not executable. Rebuild with breeze/scripts/build.sh${PRESET:+ --preset $PRESET}." >&2
exit 1
fi
# _pmn_pe_kind <file>: what kind of Windows program this is, read from the
# Machine field of its PE header -- the two bytes after the "PE\0\0"
# signature, which e_lfanew (the 4 bytes at offset 0x3C) points at. Prints
# x86 (32-bit), x64 (64-bit x86), or other (ARM64, a file that isn't a PE at
# all, one that can't be read). `od` rather than `file`, so nothing has to be
# installed, and byte by byte so the host's endianness doesn't matter.
_pmn_pe_kind() {
local o0 o1 o2 o3 s0 s1 s2 s3 m0 m1 off
read -r o0 o1 o2 o3 <<< "$(od -An -tu1 -j60 -N4 "$1" 2>/dev/null || true)"
if [ -z "${o3:-}" ]; then
echo other
return 0
fi
off=$(( o0 + o1 * 256 + o2 * 65536 + o3 * 16777216 ))
read -r s0 s1 s2 s3 m0 m1 <<< "$(od -An -tu1 -j"$off" -N6 "$1" 2>/dev/null || true)"
# 80 69 0 0 is "PE\0\0".
if [ "${s0:-}" = 80 ] && [ "${s1:-}" = 69 ] && [ "${s2:-}" = 0 ] && [ "${s3:-}" = 0 ] && [ -n "${m1:-}" ]; then
case $(( m0 + m1 * 256 )) in
332) echo x86; return 0 ;; # 0x014c IMAGE_FILE_MACHINE_I386
34404) echo x64; return 0 ;; # 0x8664 IMAGE_FILE_MACHINE_AMD64
esac
fi
echo other
}
# _pmn_wine_lacks_wine64 <wine>: 0 if <wine> is Debian/Ubuntu's helper script
# and this machine has that packaging's 32-bit loader but no 64-bit one -- the
# state described below -- so that a 64-bit program cannot run here whatever
# else is tried. Recognised by layout rather than by which version of wine it
# is: a script under /usr/bin (alternatives -> wine-stable or
# wine-development) beside a loader directory that holds wine but no wine64.
_pmn_wine_lacks_wine64() {
local emu real dir
emu="$(command -v "$1" 2>/dev/null)" || return 1
real="$(readlink -f "$emu" 2>/dev/null)" || real="$emu"
case "$real" in
/usr/bin/wine-development) dir=/usr/lib/wine-development ;;
/usr/bin/*) dir=/usr/lib/wine ;;
*) return 1 ;;
esac
[ "$(head -c2 "$real" 2>/dev/null)" = '#!' ] && [ -x "$dir/wine" ] && [ ! -x "$dir/wine64" ]
}
# _pmn_wine64_loader <wine>: prints a loader that can run 64-bit Windows
# programs and create a 64-bit prefix for them, given the plain `wine` the
# toolchain registered -- or fails if it knows of nothing better than <wine>.
#
# Plain `wine` isn't always that. On Debian and Ubuntu it is a helper script
# (alternatives -> wine-stable / wine-development), not a loader, and the one
# in wine 9.0's packaging runs the 32-bit loader whenever wine32 is installed
# -- which breeze/scripts/setup.sh makes sure of, for the 32-bit preset. A 32-bit
# loader with no wine64 to hand over to creates a 32-bit prefix even under
# WINEARCH=win64 and then refuses it: "WINEARCH set to win64 but '<prefix>'
# is a 32-bit installation". That is what a machine looks like after
# `apt-get install wine wine32:i386` (wine only depends on "wine64 | wine32",
# so wine64 gets left out): reproduced, with that exact output, from that
# command. The 64-bit loader is a separate file there, /usr/lib/wine/wine64,
# and nothing puts it on PATH.
_pmn_wine64_loader() {
local emu real w64
emu="$(command -v "$1" 2>/dev/null)" || return 1
real="$(readlink -f "$emu" 2>/dev/null)" || real="$emu"
# A wine64 right next to it: Arch, Fedora, WineHQ's /opt/wine-*/bin, ...
for w64 in "${real%/*}/wine64" "${emu%/*}/wine64"; do
if [ -x "$w64" ]; then
printf '%s\n' "$w64"
return 0
fi
done
case "${real##*/}" in
wine-stable) w64=/usr/lib/wine/wine64 ;;
wine-development) w64=/usr/lib/wine-development/wine64 ;;
*) return 1 ;;
esac
if [ -x "$w64" ]; then
printf '%s\n' "$w64"
return 0
fi
return 1
}
# A cross build's toolchain file (breeze/cmake/toolchains/) registers an emulator --
# qemu-user for another CPU, wine for Windows -- as
# CMAKE_CROSSCOMPILING_EMULATOR when one is installed, which is also what
# lets ctest run the same binaries. Use it the same way. It's a
# ";"-separated list (the emulator, then its arguments).
RUN_PREFIX=()
if [ -f "${BUILD_DIR}/CMakeCache.txt" ]; then
_pmn_emulator="$(sed -n 's/^CMAKE_CROSSCOMPILING_EMULATOR:[A-Z]*=//p' "${BUILD_DIR}/CMakeCache.txt")"
if [ -n "$_pmn_emulator" ]; then
IFS=';' read -r -a RUN_PREFIX <<< "$_pmn_emulator"
fi
fi
# Wine refuses to start at all without a valid XDG_RUNTIME_DIR -- "error:
# XDG_RUNTIME_DIR is invalid or not set in the environment" -- even for a
# console-only .exe that needs no desktop integration whatsoever; a
# session manager normally sets this, which containers (confirmed on a
# distrobox one) typically have no reason to run. Wine only checks that
# the directory exists with the right ownership/permissions, not that it
# came from a real session, so a private one under TMPDIR is enough to
# get past this. Only for wine, and only if nothing is already set -- a
# real desktop session already has a real one, left alone.
case "${RUN_PREFIX[0]:-}" in
*/wine|wine|*/wine64|wine64)
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
export XDG_RUNTIME_DIR="${TMPDIR:-/tmp}/xdg-runtime-$(id -u)"
mkdir -p "$XDG_RUNTIME_DIR" 2>/dev/null
chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null
fi
# 32-bit or 64-bit, read from the program itself. Everything below
# keeps the two apart, because a wine prefix is one or the other from
# the moment it is created and is never shared safely between them.
_pmn_kind="$(_pmn_pe_kind "$BIN_PATH")"
# A 64-bit program needs a loader that can build and run a 64-bit
# prefix, which plain `wine` isn't everywhere (see
# _pmn_wine64_loader).
if [ "$_pmn_kind" = x64 ] && [ "${RUN_PREFIX[0]##*/}" = wine ]; then
if _pmn_w64="$(_pmn_wine64_loader "${RUN_PREFIX[0]}")"; then
RUN_PREFIX[0]="$_pmn_w64"
elif _pmn_wine_lacks_wine64 "${RUN_PREFIX[0]}"; then
# Nothing below can make this work, and trying only ends in
# wine's own "WINEARCH set to win64 but ... is a 32-bit
# installation" -- which says nothing about the cause.
echo "error: ${TARGET_NAME} is a 64-bit Windows program, but this machine has only the" >&2
echo " 32-bit wine, which can neither run it nor create the 64-bit prefix it" >&2
echo " needs. Debian and Ubuntu package the 64-bit part separately:" >&2
echo " sudo apt install wine64 (breeze/scripts/setup.sh installs it too)" >&2
exit 1
fi
fi
# Running Debian's own loader directly -- chosen just above, or
# already what the build registered -- means going around the helper
# script that normally runs it, so do the two things that script does
# for wine64: point WINELOADER at it, and quiet wine's "fixme:"
# chatter with WINEDEBUG=fixme-all. Each only if nothing set it.
case "${RUN_PREFIX[0]}" in
/usr/lib/wine/wine64|/usr/lib/wine-development/wine64)
if [ -z "${WINELOADER:-}" ]; then export WINELOADER="${RUN_PREFIX[0]}"; fi
if [ -z "${WINEDEBUG:-}" ]; then export WINEDEBUG=fixme-all; fi
;;
esac
# Default wine prefix is ~/.wine, shared with every other use of
# wine on the machine -- scoping it under .cache/ instead makes a
# fresh `rm -rf .cache/wine` the fix for anything that looks like a
# wine problem rather than a build one, and keeps this project's
# own wine state from mixing with anything else's. One prefix per
# kind of program (.cache/wine/x86, .cache/wine/x64): a prefix's
# architecture is fixed by whichever program first creates it, so
# sharing one made the outcome depend on which preset happened to
# run first, and on a wine whose 32-bit loader can only make 32-bit
# prefixes, no amount of recreating it helped. Only if WINEPREFIX
# isn't already set -- someone who's deliberately pointed it
# elsewhere gets left alone, and that includes never deleting it.
if [ -z "${WINEPREFIX:-}" ]; then
# Older versions used .cache/wine itself as the one prefix. A
# prefix has a system.reg at its top, so that is easy to tell
# apart from this layout; its content is only a disposable cache.
if [ -f "$PWD/.cache/wine/system.reg" ]; then
rm -rf "$PWD/.cache/wine"
fi
export WINEPREFIX="$PWD/.cache/wine/$_pmn_kind"
# A 64-bit prefix recorded as 32-bit (a prefix's own system.reg
# says which, in a line reading exactly "#arch=win32" or
# "#arch=win64") can't be what asked for 64-bit, and wine refuses
# to run anything against it once WINEARCH=win64 disagrees --
# so it's rebuilt, since it's this project's own cache.
if [ "$_pmn_kind" = x64 ] && [ -f "$WINEPREFIX/system.reg" ] \
&& grep -q '^#arch=win32' "$WINEPREFIX/system.reg" 2>/dev/null; then
rm -rf "$WINEPREFIX"
fi
fi
# Wine creates the last component of the prefix path and no more, so
# with no .cache/ yet (a fresh checkout) it fails: "wine: chdir to
# <prefix> : No such file or directory".
mkdir -p "$WINEPREFIX"
# A 64-bit prefix is asked for outright, so that one that can't be
# made fails at once and by name rather than as a 32-bit prefix that
# then can't run the program. Left alone for 32-bit programs: any
# prefix runs those, and forcing win32 isn't possible on every wine.
if [ "$_pmn_kind" = x64 ] && [ -z "${WINEARCH:-}" ]; then
export WINEARCH=win64
fi
;;
esac
# Which build this came from -- to stderr, so a program's own stdout stays
# clean to pipe.
echo "==> Running ${TARGET_NAME} (${BUILD_DIR})${RUN_PREFIX[0]:+ under ${RUN_PREFIX[0]##*/}}" >&2
# Not `exec`, so a failure to start at all can be explained below.
_pmn_status=0
${RUN_PREFIX[@]+"${RUN_PREFIX[@]}"} "$BIN_PATH" "$@" || _pmn_status=$?
# 126 is the shell's "found it, couldn't execute it" -- which is what running
# a binary built for another architecture or OS looks like.
if [ "$_pmn_status" -eq 126 ]; then
echo "error: '${BIN_PATH}' couldn't be started on this machine. If it is a cross build" >&2
echo " (built for another CPU or OS), run it on the target, or install an" >&2
echo " emulator -- qemu-user for another CPU, wine for Windows -- and reconfigure" >&2
echo " (breeze/scripts/build.sh --preset <name>) so the toolchain picks it up." >&2
fi
exit "$_pmn_status"
+226
View File
@@ -0,0 +1,226 @@
@echo off
REM One-time (or re-run any time) environment setup for this template.
REM Windows; on Linux and macOS it is breeze/scripts/setup.sh.
REM
REM 1. Checks for the tools this project builds with -- Git, CMake 3.25+ and a
REM C++ compiler (Visual Studio Build Tools, or MinGW-w64 through MSYS2:
REM required), plus LLVM, the MSVC ARM64 build tools and the other MSYS2
REM toolchains (optional) -- and
REM offers to install whatever is missing, ONE TOOL AT A TIME, showing the
REM exact command before running it. Nothing is installed without
REM your yes. The work is done by breeze\scripts\_tools.ps1 (PowerShell: prompts,
REM PATH handling and winget are far more robust there than in batch).
REM 2. Fetches the pinned ninja (breeze\cmake\FetchNinja.cmake) -- used by
REM breeze\scripts\build.bat if found (see there); a system ninja still
REM works fine too, this just avoids needing one installed at all.
REM 3. Fetches the pinned Doxygen (breeze\cmake\FetchDoxygen.cmake) -- NOT
REM required to build the project itself (see breeze\cmake\Documentation.cmake),
REM only to run breeze\scripts\docs.bat afterward.
REM 4. Activates the pre-commit hook (breeze\githooks\pre-commit) for this
REM clone, if it's a git repository.
REM
REM A failure in step 2 or 3 (no pinned binary for this machine, no network)
REM is reported and setup carries on -- it used to stop there, before the hook
REM was activated.
REM
REM Usage (from the project root):
REM breeze\scripts\setup.bat [--yes] [--all] [--everything] [--check] [--skip-tools]
REM breeze\scripts\setup.bat --help
REM
REM No delayed expansion in this file, on purpose: PATH is edited below, and
REM with delayed expansion on, a "!" in any directory name would be eaten.
setlocal
REM %~dp0 is captured first: a plain SHIFT (used to walk the arguments below)
REM shifts %0 as well.
set "_PMN_HERE=%~dp0"
set "_PMN_TOOLS_ARGS="
set "_PMN_SKIP_TOOLS=0"
set "_PMN_CHECK=0"
set "_PMN_TOOLS_RC=0"
:parse_args
if "%~1"=="" goto args_done
if /i "%~1"=="-h" goto show_help
if /i "%~1"=="--help" goto show_help
if /i "%~1"=="-y" goto opt_yes
if /i "%~1"=="--yes" goto opt_yes
if /i "%~1"=="--all" goto opt_all
REM --everything: on Linux it also does slow extras (AUR builds); nothing on
REM Windows needs more than --all, so it means the same here.
if /i "%~1"=="--everything" goto opt_all
if /i "%~1"=="--check" goto opt_check
if /i "%~1"=="--skip-tools" goto opt_skip_tools
echo error: unknown option "%~1" 1>&2
echo Run breeze\scripts\setup.bat --help for usage. 1>&2
exit /b 1
:opt_yes
set "_PMN_TOOLS_ARGS=%_PMN_TOOLS_ARGS% -Yes"
shift
goto parse_args
:opt_all
set "_PMN_TOOLS_ARGS=%_PMN_TOOLS_ARGS% -All"
shift
goto parse_args
:opt_check
set "_PMN_TOOLS_ARGS=%_PMN_TOOLS_ARGS% -Check"
set "_PMN_CHECK=1"
shift
goto parse_args
:opt_skip_tools
set "_PMN_SKIP_TOOLS=1"
shift
goto parse_args
:show_help
echo Usage: breeze\scripts\setup.bat [--yes] [--all] [--everything] [--check] [--skip-tools]
echo.
echo One-time ^(or re-run any time^) environment setup for this template:
echo 1. Checks the tools the project builds with and offers to install what's
echo missing, one tool at a time ^(Git, CMake and a C++ compiler are
echo required; LLVM, the MSVC ARM64 build tools and the other MSYS2
echo toolchains are optional^). Each
echo install shows its exact command and waits for your yes.
echo 2. Fetches the pinned ninja ^(breeze\cmake\FetchNinja.cmake^).
echo 3. Fetches the pinned Doxygen ^(breeze\cmake\FetchDoxygen.cmake^).
echo 4. Activates the pre-commit hook ^(breeze\githooks\pre-commit^) for this clone,
echo if it's a git repository.
echo.
echo Options:
echo -y, --yes Install the REQUIRED tools without asking. For scripts and
echo CI; without a terminal and without this, nothing is asked
echo and nothing is installed.
echo --all Also install the optional tools without asking ^(implies
echo --yes^).
echo --everything Same as --all here. ^(On Linux, breeze/scripts/setup.sh --everything
echo also does slow extras such as AUR builds; Windows has none.^)
echo --check Only report what is and isn't installed; change nothing,
echo fetch nothing. Exits 1 if a required tool is missing.
echo --skip-tools Skip step 1 and go straight to the pinned downloads.
echo.
echo At each question: y install it n not this one a install everything
echo left s skip the rest of the optional tools q stop asking.
echo.
echo See the comment at the top of this script for more on each step.
exit /b 0
:args_done
cd /d "%_PMN_HERE%..\.."
REM ---- 1. tools --------------------------------------------------------------
if "%_PMN_SKIP_TOOLS%"=="1" goto tools_done
where powershell >nul 2>nul
if errorlevel 1 goto no_powershell
set "_PMN_PATHFILE=%TEMP%\pmn-path-%RANDOM%%RANDOM%.txt"
powershell -NoProfile -ExecutionPolicy Bypass -File "%_PMN_HERE%_tools.ps1" %_PMN_TOOLS_ARGS% -PathOut "%_PMN_PATHFILE%"
set "_PMN_TOOLS_RC=%ERRORLEVEL%"
REM The installer adds tool folders (CMake, Git, ...) to ITS copy of PATH so
REM it can use them; a child process can't change ours, so it hands the list
REM back in a file and they're appended here, for the steps below.
if exist "%_PMN_PATHFILE%" call :take_path_additions
echo.
goto tools_done
:no_powershell
echo error: powershell.exe not found on PATH -- it runs the tool installer. 1>&2
echo Install the tools yourself and re-run with --skip-tools. 1>&2
exit /b 1
:tools_done
if not "%_PMN_CHECK%"=="1" goto after_check
if "%_PMN_TOOLS_RC%"=="0" goto check_ok
echo ==^> Required tools are missing. Run breeze\scripts\setup.bat to install them.
exit /b %_PMN_TOOLS_RC%
:check_ok
echo ==^> All required tools are present.
exit /b 0
:after_check
REM Everything below runs CMake scripts.
where cmake >nul 2>nul
if errorlevel 1 goto no_cmake
goto have_cmake
:no_cmake
echo error: cmake not found on PATH -- nothing below can run without it. 1>&2
if "%_PMN_SKIP_TOOLS%"=="1" goto no_cmake_skip
echo Install CMake ^(3.25 or newer^), open a new terminal, and re-run this script. 1>&2
exit /b 1
:no_cmake_skip
echo Re-run without --skip-tools to be offered an install. 1>&2
exit /b 1
:have_cmake
REM ---- 2. pinned ninja -------------------------------------------------------
echo ==^> Fetching pinned ninja (breeze\cmake\FetchNinja.cmake)
cmake -P breeze\cmake\FetchNinja.cmake
if errorlevel 1 goto ninja_failed
goto ninja_done
:ninja_failed
echo.
echo No pinned ninja here ^(no upstream binary for this machine, or the
echo download failed -- see above^).
where ninja >nul 2>nul
if errorlevel 1 goto ninja_missing
echo The ninja on PATH will be used instead.
goto ninja_done
:ninja_missing
echo breeze\scripts\build.bat needs a ninja: e.g. winget install Ninja-build.Ninja
:ninja_done
REM ---- 3. pinned Doxygen -----------------------------------------------------
echo.
echo ==^> Fetching pinned Doxygen (breeze\cmake\FetchDoxygen.cmake)
cmake -P breeze\cmake\FetchDoxygen.cmake
if errorlevel 1 goto doxygen_failed
goto doxygen_done
:doxygen_failed
echo.
echo Documentation ^(breeze\scripts\docs.bat^) won't be available: there's no pinned
echo Doxygen for this machine, or the download failed -- see above.
echo The project itself builds fine without it.
:doxygen_done
REM ---- 4. pre-commit hook ----------------------------------------------------
echo.
git rev-parse --is-inside-work-tree >nul 2>nul
if errorlevel 1 goto hook_skip
echo ==^> Activating pre-commit hook
git config core.hooksPath breeze/githooks
echo core.hooksPath = breeze/githooks
goto hook_done
:hook_skip
echo ==^> Skipping pre-commit hook activation ^(not a git repository yet^)
echo Once you've run "git init", re-run this script or:
echo git config core.hooksPath breeze/githooks
:hook_done
echo.
if "%_PMN_TOOLS_RC%"=="0" goto setup_ok
echo ==^> Setup finished, but a required tool is still missing -- see above.
exit /b 1
:setup_ok
echo ==^> Setup complete. breeze\scripts\build.bat and breeze\scripts\docs.bat are ready to use.
exit /b 0
REM ---------------------------------------------------------------------
REM :take_path_additions
REM
REM Appends every directory listed in %_PMN_PATHFILE% to PATH, then deletes
REM the file. One "for /f ... do call" line, deliberately not a parenthesized
REM block: a directory like "C:\Program Files (x86)\..." would close it early.
REM ---------------------------------------------------------------------
:take_path_additions
for /f "usebackq delims=" %%D in ("%_PMN_PATHFILE%") do call :add_path "%%D"
del "%_PMN_PATHFILE%" >nul 2>nul
goto :eof
:add_path
set "PATH=%PATH%;%~1"
goto :eof
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env bash
# One-time (or re-run any time) environment setup for this template. Linux
# and macOS; on Windows it is breeze\scripts\setup.bat.
#
# 1. Checks for the tools this project builds with -- git, CMake 3.25+ and a
# C++ compiler (both of which are required), and Clang, clang-format,
# clang-tidy, cross compilers, MinGW and emulators (all optional) -- and
# offers to install whatever is missing, ONE TOOL AT A TIME, showing
# the exact command before running it. Nothing is installed without
# your yes. See breeze/scripts/_tools.sh for what it knows and how.
# 2. Fetches the pinned ninja (breeze/cmake/FetchNinja.cmake) -- used by
# breeze/scripts/build.sh / build.bat if found (see there); a system ninja
# still works fine too, this just avoids needing one installed at all.
# Where upstream ships no ninja binary for your machine, it offers to
# install one through your package manager instead.
# 3. Fetches the pinned Doxygen (breeze/cmake/FetchDoxygen.cmake) -- NOT
# required to build the project itself (see breeze/cmake/Documentation.cmake),
# only to run breeze/scripts/docs.sh / docs.bat afterward.
# 4. Activates the pre-commit hook (breeze/githooks/pre-commit) for this
# clone, if it's a git repository.
#
# A failure in step 2 or 3 (no pinned binary for this machine, no network)
# is reported and setup carries on -- it used to abort here, before the hook
# was activated.
#
# Usage (from the project root):
# breeze/scripts/setup.sh [--yes] [--all] [--everything] [--check] [--skip-tools]
# breeze/scripts/setup.sh --help
set -euo pipefail
_usage() {
cat <<'EOF'
Usage: breeze/scripts/setup.sh [--yes] [--all] [--everything] [--check] [--skip-tools]
One-time (or re-run any time) environment setup for this template:
1. Checks the tools the project builds with and offers to install what's
missing, one tool at a time (git, CMake and a C++ compiler are
required; Clang, clang-format/clang-tidy, cross compilers, MinGW and
emulators are optional). Each install shows its exact command and
waits for your yes.
2. Fetches the pinned ninja (breeze/cmake/FetchNinja.cmake).
3. Fetches the pinned Doxygen (breeze/cmake/FetchDoxygen.cmake).
4. Activates the pre-commit hook (breeze/githooks/pre-commit) for this clone,
if it's a git repository.
Options:
-y, --yes Install the REQUIRED tools without asking. For scripts and
CI; without a terminal and without this, nothing is asked
and nothing is installed.
--all Also install the optional tools without asking (implies
--yes). This includes saying yes to the extra-repository
questions that come with some of them: enabling pacman's
multilib, dnf's COPR for the ARM cross compilers, apt's
i386 architecture for wine32.
--everything Everything --all does, plus the third-party extras that
--all leaves for you to opt into (implies --all). On
Arch: the prebuilt 32-bit ARM cross toolchain from the
AUR -- four packages built with makepkg (git and
base-devel are installed first if missing): a download
and a few minutes, no compiling. Run interactively,
setup asks about it as its own question instead.
Elsewhere there is nothing extra, so it means the same
as --all.
--check Only report what is and isn't installed; change nothing,
fetch nothing. Exits 1 if a required tool is missing.
--skip-tools Skip step 1 and go straight to the pinned downloads.
At each question: y install it n not this one a install everything
left s skip the rest of the optional tools q stop asking.
See the comment at the top of this script for more on each step.
EOF
}
PMN_TOOLS_YES=0
PMN_TOOLS_ALL=0
PMN_TOOLS_HEAVY=0
PMN_TOOLS_CHECK=0
SKIP_TOOLS=0
while [ $# -gt 0 ]; do
case "$1" in
-h|--help) _usage; exit 0 ;;
-y|--yes) PMN_TOOLS_YES=1 ;;
--all) PMN_TOOLS_ALL=1; PMN_TOOLS_YES=1 ;;
--everything) PMN_TOOLS_HEAVY=1; PMN_TOOLS_ALL=1; PMN_TOOLS_YES=1 ;;
--check) PMN_TOOLS_CHECK=1 ;;
--skip-tools) SKIP_TOOLS=1 ;;
*)
echo "error: unknown option '$1'" >&2
echo "Run breeze/scripts/setup.sh --help for usage." >&2
exit 1
;;
esac
shift
done
export PMN_TOOLS_YES PMN_TOOLS_ALL PMN_TOOLS_HEAVY PMN_TOOLS_CHECK
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../.."
# _pmn_tools_run, _pmn_tools_offer and their helpers.
source "$SCRIPT_DIR/_tools.sh"
# ---- 1. tools --------------------------------------------------------------
_tools_rc=0
if [ "$SKIP_TOOLS" = 0 ]; then
_pmn_tools_run || _tools_rc=$?
echo
fi
if [ "$PMN_TOOLS_CHECK" = 1 ]; then
if [ "$_tools_rc" -ne 0 ]; then
echo "==> Required tools are missing. Run breeze/scripts/setup.sh to install them."
else
echo "==> All required tools are present."
fi
exit "$_tools_rc"
fi
# Everything below runs CMake scripts.
if ! command -v cmake >/dev/null 2>&1; then
echo "error: cmake not found on PATH -- nothing below can run without it." >&2
if [ "$SKIP_TOOLS" = 1 ]; then
echo " Re-run without --skip-tools to be offered an install." >&2
else
echo " Install CMake (3.25 or newer), open a new terminal, and re-run this script." >&2
fi
exit 1
fi
# ---- 2. pinned ninja -------------------------------------------------------
echo "==> Fetching pinned ninja (breeze/cmake/FetchNinja.cmake)"
if ! cmake -P breeze/cmake/FetchNinja.cmake; then
echo
echo " No pinned ninja here (no upstream binary for this machine, or the"
echo " download failed -- see above)."
if command -v ninja >/dev/null 2>&1; then
echo " The ninja on PATH will be used instead: $(command -v ninja)"
elif [ "$SKIP_TOOLS" = 0 ]; then
# A build needs SOME ninja; this is the machine-specific moment to say so.
_pmn_tools_offer ninja || echo " breeze/scripts/build.sh needs a ninja: install one and put it on PATH."
else
echo " breeze/scripts/build.sh needs a ninja: install one and put it on PATH."
fi
fi
# ---- 3. pinned Doxygen -----------------------------------------------------
echo
echo "==> Fetching pinned Doxygen (breeze/cmake/FetchDoxygen.cmake)"
if ! cmake -P breeze/cmake/FetchDoxygen.cmake; then
echo
echo " Documentation (breeze/scripts/docs.sh) won't be available: there's no pinned"
echo " Doxygen for this machine, or the download failed -- see above."
echo " The project itself builds fine without it."
fi
# ---- 4. pre-commit hook ----------------------------------------------------
echo
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "==> Activating pre-commit hook"
chmod +x breeze/githooks/pre-commit
git config core.hooksPath breeze/githooks
echo " core.hooksPath = breeze/githooks"
else
echo "==> Skipping pre-commit hook activation (not a git repository yet)"
echo " Once you've run 'git init', re-run this script or:"
echo " chmod +x breeze/githooks/pre-commit && git config core.hooksPath breeze/githooks"
fi
echo
if [ "$_tools_rc" -ne 0 ]; then
echo "==> Setup finished, but a required tool is still missing -- see above."
exit 1
fi
echo "==> Setup complete. breeze/scripts/build.sh and breeze/scripts/docs.sh are ready to use."