Add SDK v1.0.0
This commit is contained in:
commit
40688938ef
76 files changed
+11805
No files matched your search
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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>"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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."
|
||||
Reference in new issue
Block a user