shithub: candycrisis

ref: def29be003fc02173a96f61f6942e769914b8144
dir: /CMakeLists.txt/

View raw version
if(APPLE OR WIN32)
	cmake_minimum_required(VERSION 3.21)
else()
	# Oldest supported Ubuntu is 20.04, which ships with cmake 3.16.
	# When we can stop supporting Ubuntu 20.04, bump to CMake 3.21.
	cmake_minimum_required(VERSION 3.16)
endif()

if(APPLE OR WIN32)
	set(GAME_TARGET "CandyCrisis")
else()
	set(GAME_TARGET "candycrisis")
endif()
set(GAME_FRIENDLY_NAME "Candy Crisis")
set(GAME_BUNDLE_ID "io.jor.candycrisis")
set(GAME_DATA_DIR "CandyCrisisResources")
set(GAME_MAC_COPYRIGHT "https://github.com/jorio/CandyCrisis")

# Apply macOS deployment target and architectures to all subprojects
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "Minimum macOS deployment version")
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "Target macOS architectures")

if (${CMAKE_VERSION} VERSION_LESS "3.21")
	set(CMAKE_C_STANDARD	11)
else()
	set(CMAKE_C_STANDARD	17)
endif()

project(${GAME_TARGET} LANGUAGES C
	VERSION 3.0.1)

# Create an option to switch between a system sdl library and a vendored sdl library
option(BUILD_SDL_FROM_SOURCE "Build SDL from source" OFF)
option(STATIC_SDL "Static link SDL" OFF)
option(SANITIZE "Build with asan/ubsan" OFF)
set(CODE_SIGN_IDENTITY "" CACHE STRING "macOS code signing identity. If omitted, the app won't be signed.")

if(NOT WIN32 AND NOT APPLE)
	if(SANITIZE)
		message("Sanitizers enabled")
	else()
		message("Sanitizers disabled (pass -DSANITIZE=1 to enable)")
	endif()
endif()

# Set Visual Studio startup project
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${GAME_TARGET})

set(GAME_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)

#------------------------------------------------------------------------------
# FIND SDL
#------------------------------------------------------------------------------

# If SDL2_DIR wasn't specified, discover if the user put some prebuilt SDL2 package in the project's root directory
if(NOT DEFINED SDL2_DIR)
	if(APPLE)
		set(_sdl2_maybe "${CMAKE_SOURCE_DIR}/SDL2.framework/Resources/CMake")
	elseif(WIN32)
		set(_sdl2_maybe "${CMAKE_SOURCE_DIR}/SDL2/cmake")
	endif()

	if(DEFINED _sdl2_maybe AND EXISTS "${_sdl2_maybe}")
		set(SDL2_DIR "${_sdl2_maybe}")
	else()
		message("Couldn't find prebuilt SDL2 package in: ${_sdl2_maybe}")
	endif()
	unset(_sdl2_maybe)
endif()

if(NOT BUILD_SDL_FROM_SOURCE)
	# 1. Look for an SDL2 package, 2. look for the SDL2 component and 3. fail if none can be found
	find_package(SDL2 CONFIG REQUIRED COMPONENTS SDL2)

	# 1. Look for an SDL2 package, 2. Look for the SDL2maincomponent and 3. DO NOT fail when SDL2main is not available 
	find_package(SDL2 REQUIRED CONFIG COMPONENTS SDL2main)

	message("Found pre-built SDL: " ${SDL2_PREFIX})
else()
	if(NOT DEFINED SDL2_DIR)
		set(SDL2_DIR "${CMAKE_SOURCE_DIR}/SDL")
	endif()
	
	message("Building SDL from source: " ${SDL2_DIR})
	add_subdirectory("${SDL2_DIR}" EXCLUDE_FROM_ALL)
endif()

#------------------------------------------------------------------------------
# GENERATED FILES
#------------------------------------------------------------------------------

# Write header file containing version info
configure_file(${GAME_SOURCE_DIR}/version.h.in ${GAME_SOURCE_DIR}/version.h)

# Readme file containing version info
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packaging/ReadMe.txt.in ${CMAKE_CURRENT_BINARY_DIR}/ReadMe.txt)

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packaging/version.txt.in ${CMAKE_CURRENT_BINARY_DIR}/version.txt)

#------------------------------------------------------------------------------
# EXECUTABLE TARGET
#------------------------------------------------------------------------------

file(GLOB_RECURSE GAME_SOURCES CONFIGURE_DEPENDS ${GAME_SOURCE_DIR}/*.c ${GAME_SOURCE_DIR}/*.h)

if(WIN32)
	list(APPEND GAME_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/app.exe.rc)
elseif(APPLE)
	list(APPEND GAME_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/app.icns)
	set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/packaging/app.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
endif()

# Executable
add_executable(${GAME_TARGET} ${GAME_SOURCES})

target_include_directories(${GAME_TARGET} PRIVATE ${GAME_SOURCE_DIR})

# Create macOS app bundle with friendly name
if(APPLE)
	set_target_properties(${GAME_TARGET} PROPERTIES OUTPUT_NAME "${GAME_FRIENDLY_NAME}")
endif()

set_target_properties(${GAME_TARGET} PROPERTIES
	#--------------------------------------------------------------------------
	# MSVC/WIN32
	#--------------------------------------------------------------------------

	WIN32_EXECUTABLE					TRUE					# GUI application instead of console application
	VS_DEBUGGER_WORKING_DIRECTORY		"${CMAKE_SOURCE_DIR}"
	VS_DPI_AWARE						"PerMonitor"

	#--------------------------------------------------------------------------
	# APPLE
	#--------------------------------------------------------------------------

	# Build it as an .app bundle
	MACOSX_BUNDLE						TRUE

	# Set up Info.plist values
	MACOSX_BUNDLE_ICON_FILE				"app.icns"	# CFBundleIconFile
	MACOSX_BUNDLE_EXECUTABLE_NAME		"${GAME_TARGET}"		# CFBundleExecutable - executable name inside the bundle
	MACOSX_BUNDLE_SHORT_VERSION_STRING	"${PROJECT_VERSION}"	# CFBundleShortVersionString
	MACOSX_BUNDLE_COPYRIGHT				"${GAME_MAC_COPYRIGHT}"	# NSHumanReadableCopyright (supersedes CFBundleGetInfoString (MACOSX_BUNDLE_INFO_STRING))
	MACOSX_BUNDLE_BUNDLE_NAME			"${GAME_FRIENDLY_NAME}"	# CFBundleName - user-visible short name for the bundle
	MACOSX_BUNDLE_GUI_IDENTIFIER		"${GAME_BUNDLE_ID}"		# CFBundleIdentifier - unique bundle ID in reverse-DNS format

	# Set framework search path to (App bundle)/Contents/Frameworks so the game can use its embedded SDL2.framework
	XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks"

	# If CODE_SIGN_IDENTITY is NOT empty: tell Xcode to codesign the app properly
	# Otherwise, if it's empty: explicitly turn off code signing, otherwise downloaded app will be quarantined forever
	XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${CODE_SIGN_IDENTITY}"
	XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] ""		# don't bother signing debug build

	# Bundle ID required for code signing - must match CFBundleIdentifier otherwise xcode will complain
	XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER ${GAME_BUNDLE_ID}

	# Don't bother with universal builds when we're working on the debug version
	XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] "YES"

	XCODE_EMBED_FRAMEWORKS							"${SDL2_FRAMEWORK_PATH}"
	XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY		"YES"		# frameworks must be signed by the same developer as the binary
	XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY	"YES"		# not strictly necessary, but that's cleaner
	XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Debug]	"NO"		# avoid "skipping copy phase strip" warning while working on Debug config

	# The following is to pass notarization requirements
	XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME			"YES"
	XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS "NO"
	XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS			"--options=runtime --timestamp"
)

#------------------------------------------------------------------------------
# COMPILER/LINKER OPTIONS
#------------------------------------------------------------------------------

add_compile_definitions(
	"$<$<CONFIG:DEBUG>:_DEBUG>"
)

if(NOT MSVC)
	target_compile_options(${GAME_TARGET} PRIVATE
		-Wall
		-Wextra
		#-Wshadow  # Note: Candy Crisis goes crazy with shadowing
		-Werror=return-type
		-Wstrict-aliasing=2
		-Wno-multichar
	)

	# Sanitizers in debug mode (Linux only)
	# When using a debugger, you should export LSAN_OPTIONS=detect_leaks=0
	if(SANITIZE)
		list(INSERT GAME_LIBRARIES 0 asan ubsan)
		target_compile_options(${GAME_TARGET} PRIVATE
			-fsanitize=alignment
			-fsanitize=address
			-fsanitize=leak
			-fsanitize=undefined
			-fno-omit-frame-pointer
		)
	endif()
else()
	# On Win32, we need NOGDI and NOUSER to be able to define some Mac functions
	# whose names are otherwise taken by Windows APIs.
	target_compile_definitions(${GAME_TARGET} PRIVATE
		WIN32_LEAN_AND_MEAN
		_CRT_SECURE_NO_WARNINGS		# quit whining about snprintf_s
	)

	target_compile_options(${GAME_TARGET} PRIVATE
		/W4
		/wd5105 # see https://developercommunity.visualstudio.com/t/1249671
		/we4013	# treat warning as error: undefined function (assuming extern returning int)
		/MP		# multiprocessor build
		/Zi		# output info to PDB
	)

	# Let executable be debugged with PDB, even in Release builds
	target_link_options(${GAME_TARGET} PRIVATE /DEBUG)

	# Enable console for debug builds
	set_target_properties(${GAME_TARGET} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
	set_target_properties(${GAME_TARGET} PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
endif()

#------------------------------------------------------------------------------
# LINK LIBRARIES
#------------------------------------------------------------------------------

# Explicitly link math lib on Linux
if(NOT APPLE AND NOT WIN32)
	target_link_libraries(${GAME_TARGET} PRIVATE m)
endif()

# SDL2::SDL2main may or may not be available. It is e.g. required by Windows GUI applications  
if(TARGET SDL2::SDL2main)
	# It has an implicit dependency on SDL2 functions, so it MUST be added before SDL2::SDL2 (or SDL2::SDL2-static)
	target_link_libraries(${GAME_TARGET} PRIVATE SDL2::SDL2main)
endif()

if(NOT STATIC_SDL)
	target_link_libraries(${GAME_TARGET} PRIVATE SDL2::SDL2)
else()
	target_link_libraries(${GAME_TARGET} PRIVATE SDL2::SDL2-static)
endif()

#------------------------------------------------------------------------------
# COPY ASSETS
#------------------------------------------------------------------------------

if(APPLE)
	set(GAME_DATA_TARGET_LOCATION "$<TARGET_FILE_DIR:${GAME_TARGET}>/../Resources")
else()
	set(GAME_DATA_TARGET_LOCATION "$<TARGET_FILE_DIR:${GAME_TARGET}>/${GAME_DATA_DIR}")
endif()

add_custom_command(TARGET ${GAME_TARGET} POST_BUILD
	COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/${GAME_DATA_DIR}" "${GAME_DATA_TARGET_LOCATION}")

# Copy SDL2 DLL for convenience (WARNING: TARGET_RUNTIME_DLLS requires CMake 3.21, so this copy command was separated from the command above.)
if(WIN32)
	add_custom_command(TARGET ${GAME_TARGET} POST_BUILD
		COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:${GAME_TARGET}> $<TARGET_FILE_DIR:${GAME_TARGET}>)
endif()

#------------------------------------------------------------------------------
# INSTALL
#------------------------------------------------------------------------------

# Install Windows-specific libraries (cmake --install): copy Visual Studio redistributable DLLs to install location
if(WIN32)
	include(InstallRequiredSystemLibraries)
elseif(APPLE)
	# no-op
else()
	install(TARGETS ${GAME_TARGET} DESTINATION bin)
	install(FILES ${CMAKE_SOURCE_DIR}/packaging/${GAME_TARGET}.desktop DESTINATION share/applications/)
	install(FILES ${CMAKE_SOURCE_DIR}/packaging/${GAME_TARGET}.png DESTINATION share/icons/hicolor/128x128/apps/)
	install(DIRECTORY ${CMAKE_SOURCE_DIR}/${GAME_DATA_DIR}/ DESTINATION share/${GAME_TARGET}/)
endif()