Files
Attacks-for-Bombs-1/scripts/build.bat
T

359 lines
15 KiB
Batchfile
Executable File

@echo off
REM Configure (if needed) and build the project, preferably with Ninja.
REM
REM Usage (from the project root):
REM scripts\build.bat [--preset ^<name^>] [BUILD_TYPE] [CMAKE_ARGS...]
REM scripts\build.bat --list-presets
REM scripts\build.bat --help
REM
REM --preset <name> Use a CMakePresets.json preset. A preset selects that
REM target's toolchain file (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 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: Configuration.cmake's DEFAULT_BUILD_TYPE)
REM CMAKE_ARGS Anything else is forwarded to `cmake` verbatim, e.g.:
REM scripts\build.bat --preset windows-x64-mingw64 Debug -DBUILD_TESTS=ON
REM 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=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, Configuration.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 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. 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: scripts\build.bat [--preset ^<name^>] [BUILD_TYPE] [CMAKE_ARGS...]
echo scripts\build.bat --list-presets
echo 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: 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 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 ^(scripts\build.bat^) and Configuration.cmake's "Target selection"
echo section for why picking a DIST_TARGET is one or the other, not both.
echo.
echo Examples:
echo scripts\build.bat --preset linux-x64-clang
echo scripts\build.bat --preset windows-x64-mingw64 Debug -DBUILD_TESTS=ON
echo 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 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. cmake\BuildDir.cmake owns the
REM layout (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 scripts\build.bat --preset %PRESET%
echo scripts\build.bat -DCMAKE_TOOLCHAIN_FILE=cmake\toolchains\^<target^>.cmake
echo See the comment at the top of this script, or Configuration.cmake's
echo "Target selection" section, for why these two aren't combined.
exit /b 1
)
)
REM Prefer a pinned ninja (fetched by scripts\setup.bat -- see
REM 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 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 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 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 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
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 cmake\BuildDir.cmake >nul
echo ==^> Building
cmake --build "%BUILD_DIR%"
if errorlevel 1 exit /b 1
echo ==^> Done. Run a module with: scripts\run.bat ^<target-name^>
exit /b 0
REM ---------------------------------------------------------------------
REM :read_config <ConfigurationVarName> <LocalVarName>
REM
REM Reads one value out of Configuration.cmake -- this project's single
REM source of truth -- via 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 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 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 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 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: cmake\BuildDir.cmake did not answer. 1>&2
)
:resolve_failed
endlocal
exit /b 1
:resolve_ok
endlocal & set "%~2=%_pmn_val:/=\%"
goto :eof