mirror of
https://github.com/gbdev/rgbds.git
synced 2026-03-25 14:23:04 +00:00
`FetchContent` respects existing installs, and downloads and compiles the libs if they aren't found. This is admittedly a little finicky, since this ignores the usual `Find*` modules provided by CMake, requiring a bit of glue on our side. But, one upside is that this moves that logic from our CI into the build system, which can thus benefit other downstream users. This also opens the door to some improvements in upcoming commits. Doing this with Bison turned out to be much more painful, however, due to `FindBISON` providing the specific `bison_target` command; thus, it remains installed externally, so that it can be picked up by `FindBISON`. This also bumps our CMake version requirement slightly, though it's possible that older versions keep working, or could be supported with small patches; however, our CI doesn't provide anything below 3.31, so we can't check.
191 lines
8.0 KiB
CMake
191 lines
8.0 KiB
CMake
# SPDX-License-Identifier: MIT
|
|
|
|
# - 3.24 is required for `FetchContent`'s `find_package` integration.
|
|
# Older versions *may* work, but we can't test them; compat patches are welcome.
|
|
# - 3.17 is required for `CHECK_*` messages to display properly, but is not essential.
|
|
# - 3.9 is required for LTO checks.
|
|
cmake_minimum_required(VERSION 3.24...4.2 FATAL_ERROR)
|
|
|
|
# Read the project version from the header (the canonical source of truth).
|
|
file(STRINGS "include/version.hpp" version_defines REGEX "^[ \t]*#define[ \t]+PACKAGE_VERSION_")
|
|
foreach(line IN LISTS version_defines)
|
|
# We want the `CMAKE_MATCH_n` variables, so we just need to run *some* regex op.
|
|
string(REGEX MATCH "PACKAGE_(VERSION_[^ \t]+)[ \t]+([0-9]+)" dummy "${line}")
|
|
set("${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
|
|
endforeach()
|
|
|
|
project(rgbds
|
|
VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}"
|
|
LANGUAGES CXX
|
|
DESCRIPTION "Game Boy assembly toolchain"
|
|
HOMEPAGE_URL "https://rgbds.gbdev.io")
|
|
if(DEFINED VERSION_RC)
|
|
string(APPEND CMAKE_PROJECT_VERSION "-rc${VERSION_RC}")
|
|
endif()
|
|
|
|
# Reject in-source builds, as they may conflict with the Makefile.
|
|
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
|
|
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
|
|
if(srcdir STREQUAL bindir)
|
|
message(FATAL_ERROR "RGBDS should not be built in the source directory.
|
|
Instead, create a separate build directory and specify to CMake the path to the source directory.")
|
|
endif()
|
|
|
|
include(CTest) # Note: CTest only functions properly if included from the top-level CMakeLists.
|
|
include(GNUInstallDirs)
|
|
|
|
## Compiler switches.
|
|
|
|
include(CMakeDependentOption)
|
|
option(SANITIZERS "Build with sanitizers enabled" OFF)
|
|
cmake_dependent_option(MORE_WARNINGS "Turn on more warnings" OFF !MSVC OFF)
|
|
|
|
if(MSVC)
|
|
add_compile_options(
|
|
/wd5030 # Warning C5030 is about unknown attributes (`[[gnu::ATTR]]`), none of ours being load-bearing.
|
|
/wd4996 # Warning C4996 is about using POSIX names, which we want to do for portability.
|
|
/Zc:preprocessor # Opt into the C++20-conformant preprocessor.
|
|
)
|
|
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
|
|
|
if(SANITIZERS)
|
|
message(STATUS "ASan enabled")
|
|
add_compile_options(/fsanitize=address) # Note that this shouldn't be passed to the linker.
|
|
endif()
|
|
else()
|
|
add_compile_options(-Wall -pedantic -fno-exceptions -fno-rtti -Wno-unknown-warning-option
|
|
# C++20 allows macros to take zero variadic arguments.
|
|
# Some versions of Clang don't recognize this, and treat them as a GNU extension.
|
|
-Wno-gnu-zero-variadic-macro-arguments)
|
|
if(SANITIZERS)
|
|
message(STATUS "ASan and UBSan enabled")
|
|
set(SAN_FLAGS -fsanitize=address -fsanitize=undefined
|
|
-fsanitize=float-divide-by-zero)
|
|
add_compile_options(${SAN_FLAGS})
|
|
add_link_options(${SAN_FLAGS})
|
|
add_definitions(-D_GLIBCXX_ASSERTIONS -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG)
|
|
# A non-zero optimization level is desired even in debug mode (especially for Clang),
|
|
# and the two codegen flags improve the sanitizers' backtraces, but we want the user to
|
|
# be able to override these easily so we put them first.
|
|
string(PREPEND CMAKE_CXX_FLAGS_DEBUG "-Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ")
|
|
endif()
|
|
|
|
if(MORE_WARNINGS)
|
|
add_compile_options(-Werror -Wextra
|
|
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
|
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2
|
|
-Wstringop-overflow=4 -Wtrampolines -Wundef -Wuninitialized -Wunused -Wshadow
|
|
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
|
-Wno-format-nonliteral -Wno-strict-overflow
|
|
-Wno-unused-but-set-variable # bison's `yynerrs_` is incremented but unused
|
|
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare
|
|
-Wvla # MSVC does not support VLAs
|
|
-Wno-unknown-warning-option) # Clang shouldn't diagnose unknown warnings
|
|
endif()
|
|
endif()
|
|
|
|
include_directories("include")
|
|
|
|
set(CMAKE_CXX_STANDARD 20)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|
|
|
message(CHECK_START "Checking if LTO is supported")
|
|
include(CheckIPOSupported)
|
|
check_ipo_supported(RESULT enable_lto)
|
|
if(enable_lto)
|
|
message(CHECK_PASS "yes")
|
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
|
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)
|
|
else()
|
|
message(CHECK_FAIL "no")
|
|
endif()
|
|
|
|
# Use versioning consistent with Makefile:
|
|
# the git revision is used but uses the fallback in an archive.
|
|
|
|
message(CHECK_START "Determining RGBDS version from Git history")
|
|
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
|
set(GIT_REV "") # This fallback is important!
|
|
find_package(Git)
|
|
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
|
if(NOT Git_FOUND)
|
|
message(CHECK_FAIL "Git not found")
|
|
else()
|
|
execute_process(COMMAND "${GIT_EXECUTABLE}" --git-dir=.git -c safe.directory='*'
|
|
describe --tags --dirty --always --match "v[0-9]*"
|
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
|
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
ERROR_VARIABLE git_err ERROR_STRIP_TRAILING_WHITESPACE
|
|
RESULT_VARIABLE result)
|
|
if(NOT result EQUAL 0)
|
|
# Note that this happens e.g. when building from a tarball, so it shouldn't fail the build!
|
|
message(CHECK_FAIL "error ${result} from Git:")
|
|
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
|
message("${git_err}")
|
|
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
|
else()
|
|
message(CHECK_PASS "${GIT_REV}")
|
|
if(NOT "${GIT_REV}" MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+(-rc[0-9]+)?")
|
|
# Can't find an ancestor tag! (That passes `--match`, anyway.)
|
|
message(WARNING "No `v*` Git tag reachable; falling back")
|
|
elseif(NOT CMAKE_MATCH_0 STREQUAL "v${CMAKE_PROJECT_VERSION}")
|
|
message(SEND_ERROR "\
|
|
Version mismatch! Git says ${CMAKE_MATCH_0},
|
|
version.hpp says v${CMAKE_PROJECT_VERSION}!")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
## Dependencies.
|
|
|
|
include(FetchContent)
|
|
FetchContent_Declare(PNG
|
|
URL https://download.sourceforge.net/libpng/libpng-1.6.55.tar.xz
|
|
URL_HASH SHA256=d925722864837ad5ae2a82070d4b2e0603dc72af44bd457c3962298258b8e82d
|
|
FIND_PACKAGE_ARGS 1.5.4)
|
|
FetchContent_Declare(ZLIB
|
|
URL https://www.zlib.net/zlib-1.3.2.tar.xz
|
|
URL_HASH SHA256=d7a0654783a4da529d1bb793b7ad9c3318020af77667bcae35f95d0e42a792f3
|
|
# libpng documents requiring "zlib 1.0.4 or later (1.2.13 or later recommended for performance and security reasons)".
|
|
# We thus enforce 1.0.4, but note that the libpng source code mentions that "it may work with versions as old as zlib 0.95".
|
|
FIND_PACKAGE_ARGS 1.0.4)
|
|
set(ZLIB_BUILD_SHARED ON CACHE INTERNAL "")
|
|
set(ZLIB_BUILD_STATIC OFF CACHE INTERNAL "")
|
|
FetchContent_MakeAvailable(ZLIB)
|
|
if(NOT DEFINED ZLIB_INCLUDE_DIRS)
|
|
set(ZLIB_INCLUDE_DIRS "${zlib_BINARY_DIR};${zlib_SOURCE_DIR}") # libpng's `genout` script relies on this variable to be set.
|
|
endif()
|
|
set(PNG_TESTS OFF CACHE INTERNAL "") # We do not care for these two (and they can even cause compile errors!)
|
|
set(PNG_TOOLS OFF CACHE INTERNAL "")
|
|
set(PNG_SHARED ON CACHE INTERNAL "") # Upstream seems to favour the dynamic lib over the static one?
|
|
set(PNG_STATIC OFF CACHE INTERNAL "")
|
|
FetchContent_MakeAvailable(PNG)
|
|
if(NOT TARGET PNG::PNG)
|
|
if(PNG_SHARED)
|
|
add_library(PNG::PNG ALIAS png_shared)
|
|
else()
|
|
add_library(PNG::PNG ALIAS png_static)
|
|
endif()
|
|
endif()
|
|
|
|
## The actual stuff.
|
|
|
|
add_subdirectory(src)
|
|
add_subdirectory(test)
|
|
|
|
set(man1 "man/rgbasm.1"
|
|
"man/rgbfix.1"
|
|
"man/rgbgfx.1"
|
|
"man/rgblink.1")
|
|
set(man5 "man/rgbasm.5"
|
|
"man/rgbasm-old.5"
|
|
"man/rgblink.5"
|
|
"man/rgbds.5")
|
|
set(man7 "man/gbz80.7"
|
|
"man/rgbds.7")
|
|
|
|
foreach(SECTION "man1" "man5" "man7")
|
|
install(FILES ${${SECTION}} DESTINATION "${CMAKE_INSTALL_MANDIR}/${SECTION}")
|
|
endforeach()
|