1 cmake_minimum_required(VERSION 3.9)
4 set(BINARY_NAME openttd)
11 if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
12 message(FATAL_ERROR "In-source builds not allowed. Please run \"cmake ..\" from the build directory. You may need to delete \"${CMAKE_SOURCE_DIR}/CMakeCache.txt\" first.")
16 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/os/emscripten/cmake")
19 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
20 set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
22 # Use GNUInstallDirs to allow customisation
23 # but set our own default data and bin dir
24 if(NOT CMAKE_INSTALL_DATADIR)
25 set(CMAKE_INSTALL_DATADIR "share/games")
27 if(NOT CMAKE_INSTALL_BINDIR)
28 set(CMAKE_INSTALL_BINDIR "games")
30 include(GNUInstallDirs)
34 set_directory_options()
37 set_static_if_needed()
39 set(CMAKE_CXX_STANDARD 20)
40 set(CMAKE_CXX_STANDARD_REQUIRED YES)
41 set(CMAKE_CXX_EXTENSIONS NO)
43 set(CMAKE_EXPORT_COMPILE_COMMANDS YES)
45 # An empty target for the tools
46 add_custom_target(tools)
49 add_endian_definition()
55 add_definitions(-DUNIX)
62 list(APPEND GENERATED_SOURCE_FILES "${CMAKE_BINARY_DIR}/generated/rev.cpp")
64 list(APPEND GENERATED_SOURCE_FILES "${CMAKE_BINARY_DIR}/generated/ottdres.rc")
68 if(DOXYGEN_EXECUTABLE)
69 add_custom_target(docs)
70 add_custom_target(docs_source
71 ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/docs
72 COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile
73 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
74 COMMENT "Generating documentation for source"
76 add_dependencies(docs_source
84 include(AddCustomXXXTimestamp)
88 unset(HOST_BINARY_DIR CACHE)
90 add_subdirectory(${CMAKE_SOURCE_DIR}/src)
92 # Cut down find_version which doesn't include cfg defines set in full configure
93 add_custom_target(find_version
95 -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated
96 -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR}
97 -DREV_MAJOR=${PROJECT_VERSION_MAJOR}
98 -DREV_MINOR=${PROJECT_VERSION_MINOR}
99 $<$<PLATFORM_ID:Windows>:-DWIN32=TRUE>
100 -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake"
101 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
102 BYPRODUCTS ${GENERATED_SOURCE_FILES}
109 # Avoid searching for headers in Frameworks, and libraries in LIBDIR.
110 set(CMAKE_FIND_FRAMEWORK LAST)
113 # Prefer -pthread over -lpthread, which is often the better option of the two.
114 set(CMAKE_THREAD_PREFER_PTHREAD YES)
115 # Make sure we have Threads available.
116 find_package(Threads REQUIRED)
119 find_package(LibLZMA)
121 find_package(ZSTD 1.4)
124 if(WIN32 OR EMSCRIPTEN)
125 # Windows uses WinHttp for HTTP requests.
126 # Emscripten uses Javascript for HTTP requests.
128 # All other targets use libcurl.
132 if(NOT OPTION_DEDICATED)
134 find_package(Allegro)
136 find_package(Freetype)
141 find_package(Fluidsynth)
143 find_package(Fontconfig)
145 find_package(Harfbuzz)
146 find_package(ICU OPTIONAL_COMPONENTS i18n uc)
153 find_library(AUDIOTOOLBOX_LIBRARY AudioToolbox)
154 find_library(AUDIOUNIT_LIBRARY AudioUnit)
155 find_library(COCOA_LIBRARY Cocoa)
156 find_library(QUARTZCORE_LIBRARY QuartzCore)
158 find_package(MacUcontext)
161 if(NOT EMSCRIPTEN AND NOT OPTION_DEDICATED)
162 find_package(OpenGL COMPONENTS OpenGL)
166 find_package(Editbin REQUIRED)
168 find_package(Builtins)
173 find_package(Demangle)
174 find_package(Sigaction)
175 find_package(Sigaltstack)
176 find_package(SelfDbg)
177 find_package(Ucontext)
181 if(UNIX AND NOT APPLE AND NOT OPTION_DEDICATED)
182 find_package(Fcitx QUIET)
184 message(STATUS "Found: Fcitx: ${Fcitx_VERSION}")
188 message(STATUS "Could not find Fcitx (Fcitx is OPTIONAL)")
193 find_package(Demangle)
194 if (OPTION_ENABLE_MINGW_BFD)
196 endif (OPTION_ENABLE_MINGW_BFD)
197 find_package(DbgHelp)
201 find_package(Xaudio2)
203 find_package(Grfcodec)
205 include(CheckIPOSupported)
206 check_ipo_supported(RESULT IPO_FOUND)
210 if(UNIX AND NOT APPLE AND NOT OPTION_DEDICATED)
211 if(NOT SDL_FOUND AND NOT SDL2_FOUND AND NOT ALLEGRO_FOUND)
212 message(FATAL_ERROR "SDL, SDL2 or Allegro is required for this platform")
214 if(HARFBUZZ_FOUND AND NOT ICU_i18n_FOUND)
215 message(WARNING "HarfBuzz depends on ICU i18n to function; HarfBuzz will be disabled")
217 if(NOT HARFBUZZ_FOUND)
218 message(WARNING "Without HarfBuzz and ICU i18n the game will not be able to render right-to-left languages correctly")
222 if(NOT AUDIOTOOLBOX_LIBRARY)
223 message(FATAL_ERROR "AudioToolbox is required for this platform")
225 if(NOT AUDIOUNIT_LIBRARY)
226 message(FATAL_ERROR "AudioUnit is required for this platform")
228 if(NOT COCOA_LIBRARY)
229 message(FATAL_ERROR "Cocoa is required for this platform")
231 if(NOT QUARTZCORE_LIBRARY)
232 message(FATAL_ERROR "QuartzCore is required for this platform")
236 if(OPTION_PACKAGE_DEPENDENCIES)
238 message(FATAL_ERROR "Can only package dependencies on Linux")
240 if(OPTION_INSTALL_FHS)
241 message(FATAL_ERROR "Cannot install in FHS folders when we are packaging dependencies")
243 if(${CMAKE_VERSION} VERSION_LESS "3.16.0")
244 message(FATAL_ERROR "OPTION_PACKAGE_DEPENDENCIES can only work with CMake 3.16+; you are using ${CMAKE_VERSION}")
247 # If we are packaging dependencies, we do two things:
248 # 1) set the RPATH to include $ORIGIN/lib; $ORIGIN (that literal string)
249 # is a Linux indicator for "path where application is". In CMake, we
250 # have to do this before add_executable() is executed.
251 # 2) copy the libraries that we compile against to the "lib" folder.
252 # This is done in InstallAndPackage.cmake.
253 set(CMAKE_INSTALL_RPATH "\$ORIGIN/lib")
254 set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
257 if(OPTION_NO_SPLIT_LIB)
258 set(OPENTTD_LIB openttd)
260 set(OPENTTD_LIB openttd_lib)
267 include_directories(${CMAKE_SOURCE_DIR}/src)
268 # Needed by everything that uses Squirrel
269 include_directories(${CMAKE_SOURCE_DIR}/src/3rdparty/squirrel/include)
273 if(OPTION_NO_SPLIT_LIB)
274 add_executable(openttd WIN32 ${GENERATED_SOURCE_FILES})
276 add_library(openttd_lib OBJECT ${GENERATED_SOURCE_FILES})
277 add_executable(openttd WIN32)
278 add_executable(openttd_test)
279 set_target_properties(openttd_test PROPERTIES EXCLUDE_FROM_ALL TRUE)
282 set_target_properties(openttd PROPERTIES OUTPUT_NAME "${BINARY_NAME}")
283 # All other files are added via target_sources()
285 if (MINGW AND OPTION_MINGW_STDTHREADS)
286 target_link_libraries(${OPENTTD_LIB} mingw_stdthreads)
289 set(host_tools_list strgen settingsgen)
292 # Host tools already exist, nothing to do
293 elseif(CMAKE_CROSSCOMPILING)
294 # Pawn off the creation of the host utilities into its own dedicated space
295 file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools)
296 file(TO_NATIVE_PATH ${CMAKE_COMMAND} native_cmake_command)
297 file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} native_cmake_current_source_dir)
299 COMMAND "${native_cmake_command}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" "${native_cmake_current_source_dir}"
300 "-DCMAKE_C_COMPILER=/usr/bin/cc" "-DCMAKE_CXX_COMPILER=/usr/bin/c++"
301 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools
304 add_custom_target(host_tools
305 COMMAND ${CMAKE_COMMAND} --build . --target host_tools --config $<CONFIG>
306 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/host_tools
308 include(${CMAKE_CURRENT_BINARY_DIR}/host_tools/host_tools.cmake)
310 foreach(tgt IN ITEMS ${host_tools_list})
311 add_dependencies(host${tgt} host_tools)
315 # Add an empty target, host tools are built inplace
316 add_custom_target(host_tools
317 DEPENDS ${host_tools_list}
322 # Add DPI manifest to project; other WIN32 targets get this via ottdres.rc
323 target_sources(openttd PRIVATE "${CMAKE_SOURCE_DIR}/os/windows/openttd.manifest")
325 # If target -static is used, switch our project to static (/MT) too.
326 # If the target ends on -static-md, it will remain dynamic (/MD).
327 if(VCPKG_TARGET_TRIPLET MATCHES "-static" AND NOT VCPKG_TARGET_TRIPLET MATCHES "-md")
328 set_property(TARGET openttd_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
329 set_property(TARGET openttd PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
330 set_property(TARGET openttd_test PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
334 add_subdirectory(${CMAKE_SOURCE_DIR}/bin)
335 add_subdirectory(${CMAKE_SOURCE_DIR}/src)
336 add_subdirectory(${CMAKE_SOURCE_DIR}/media)
338 if(NOT CMAKE_CROSSCOMPILING AND NOT HOST_BINARY_DIR)
339 foreach(tgt IN ITEMS ${host_tools_list})
340 add_executable(host${tgt} ALIAS ${tgt})
343 export(TARGETS ${host_tools_list} NAMESPACE host FILE host_tools.cmake)
346 add_dependencies(openttd
349 if(NOT OPTION_NO_SPLIT_LIB)
350 target_link_libraries(openttd openttd_lib)
352 target_link_libraries(openttd_test PRIVATE openttd_lib)
354 target_link_libraries(openttd_test PRIVATE log)
358 catch_discover_tests(openttd_test)
361 target_link_libraries(${OPENTTD_LIB}
368 target_link_libraries(openttd
375 target_link_libraries(openttd "be" "network" "midi")
378 if(IPO_FOUND AND NOT OPTION_NO_LTO)
379 set_target_properties(openttd PROPERTIES INTERPROCEDURAL_OPTIMIZATION_RELEASE True)
380 set_target_properties(openttd PROPERTIES INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL True)
381 set_target_properties(openttd PROPERTIES INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO True)
383 set_target_properties(openttd PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
384 process_compile_flags()
387 link_package(PNG TARGET PNG::PNG ENCOURAGED)
388 link_package(ZLIB TARGET ZLIB::ZLIB ENCOURAGED)
389 link_package(LIBLZMA TARGET LibLZMA::LibLZMA ENCOURAGED)
391 link_package(ZSTD TARGET ZSTD::ZSTD RECOMMENDED)
393 if(NOT WIN32 AND NOT EMSCRIPTEN)
394 link_package(CURL ENCOURAGED)
395 target_link_libraries(openttd_lib ${CMAKE_DL_LIBS})
398 if(NOT OPTION_DEDICATED)
399 link_package(Fluidsynth)
401 link_package(SDL2 TARGET SDL2::SDL2)
402 link_package(Allegro)
403 link_package(FREETYPE TARGET Freetype::Freetype)
404 link_package(Fontconfig TARGET Fontconfig::Fontconfig)
405 link_package(Harfbuzz TARGET harfbuzz::harfbuzz)
406 link_package(ICU_i18n)
412 if(SDL2_FOUND AND OPENGL_FOUND AND UNIX)
413 # SDL2 dynamically loads OpenGL if needed, so do not link to OpenGL when
414 # on Linux. For Windows, we need to link to OpenGL as we also have a win32
416 add_definitions(-DWITH_OPENGL)
417 message(STATUS "OpenGL found -- -DWITH_OPENGL -- (via SDL2)")
419 link_package(OpenGL TARGET OpenGL::GL)
423 include(3rdparty/llvm/CheckAtomic)
426 link_package(Iconv TARGET Iconv::Iconv)
428 target_link_libraries(${OPENTTD_LIB}
429 ${AUDIOTOOLBOX_LIBRARY}
432 ${QUARTZCORE_LIBRARY}
441 add_library(WASM::WASM INTERFACE IMPORTED)
443 # Allow heap-growth, and start with a bigger memory size.
444 target_link_libraries(WASM::WASM INTERFACE "-s ALLOW_MEMORY_GROWTH=1")
445 target_link_libraries(WASM::WASM INTERFACE "-s INITIAL_MEMORY=33554432")
446 target_link_libraries(WASM::WASM INTERFACE "-s DISABLE_EXCEPTION_CATCHING=0")
447 target_link_libraries(WASM::WASM INTERFACE "-s WASM_BIGINT")
448 add_definitions(-s DISABLE_EXCEPTION_CATCHING=0)
450 # Export functions to Javascript.
451 target_link_libraries(WASM::WASM INTERFACE "-s EXPORTED_FUNCTIONS='[\"_main\", \"_em_openttd_add_server\"]' -s EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'")
453 # Preload all the files we generate during build.
454 # As we do not compile with FreeType / FontConfig, we also have no way to
455 # render several languages (like Chinese, ..), so where do you draw the
456 # line what languages to include and which not? In the end, especially as
457 # the more languages you add the slower downloading becomes, we decided to
458 # only ship the English language.
459 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_BINARY_DIR}/baseset@/baseset")
460 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_BINARY_DIR}/lang/english.lng@/lang/english.lng")
461 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/bin/ai@/ai")
462 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/bin/game@/game")
463 # Documentation files for the in-game text file viewer
464 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/README.md@/README.md")
465 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/CREDITS.md@/CREDITS.md")
466 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/CONTRIBUTING.md@/CONTRIBUTING.md")
467 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/COPYING.md@/COPYING.md")
468 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/known-bugs.md@/known-bugs.md")
469 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/changelog.md@/changelog.md")
470 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/admin_network.md@/docs/admin_network.md")
471 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/debugging_desyncs.md@/docs/debugging_desyncs.md")
472 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/desync.md@/docs/desync.md")
473 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/directory_structure.md@/docs/directory_structure.md")
474 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/eints.md@/docs/eints.md")
475 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/linkgraph.md@/docs/linkgraph.md")
476 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/logging_and_performance_metrics.md@/docs/logging_and_performance_metrics.md")
477 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/multiplayer.md@/docs/multiplayer.md")
478 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/savegame_format.md@/docs/savegame_format.md")
479 target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/symbol_server.md@/docs/symbol_server.md")
481 # We use IDBFS for persistent storage.
482 target_link_libraries(WASM::WASM INTERFACE "-lidbfs.js")
484 # Use custom pre-js and shell.html.
485 target_link_libraries(WASM::WASM INTERFACE "--pre-js ${CMAKE_SOURCE_DIR}/os/emscripten/pre.js")
486 target_link_libraries(WASM::WASM INTERFACE "--shell-file ${CMAKE_SOURCE_DIR}/os/emscripten/shell.html")
488 # Build the .html (which builds the .js, .wasm, and .data too).
489 set_target_properties(openttd PROPERTIES SUFFIX ".html")
490 target_link_libraries(openttd WASM::WASM)
493 if(NOT PERSONAL_DIR STREQUAL "(not set)")
496 -DPERSONAL_DIR="${PERSONAL_DIR}"
500 if(NOT SHARED_DIR STREQUAL "(not set)")
503 -DSHARED_DIR="${SHARED_DIR}"
507 if(NOT GLOBAL_DIR STREQUAL "(not set)")
509 -DGLOBAL_DATA_DIR="${GLOBAL_DIR}"
515 add_definitions_based_on_options()
525 target_link_libraries(${OPENTTD_LIB}
536 if(CMAKE_SIZEOF_VOID_P EQUAL 8)
537 add_definitions(-DPOINTER_IS_64BIT)
540 if(OPTION_NO_TAGGED_PTRS)
541 add_definitions(-DNO_TAGGED_PTRS)
546 add_subdirectory(regression)
552 include(InstallAndPackage)
554 get_property(CFG_DEFS DIRECTORY . PROPERTY COMPILE_OPTIONS)
555 list(FILTER CFG_DEFS INCLUDE REGEX "^-D")
557 # list TRANSFORM requires 3.12 or later
558 #list(TRANSFORM CFG_DEFS REPLACE "^-D" "")
559 string(REGEX REPLACE "(^|;)-D" "\\1" CFG_DEFS "${CFG_DEFS}")
561 get_property(CFG_DEFS_2 DIRECTORY . PROPERTY COMPILE_DEFINITIONS)
562 list(APPEND CFG_DEFS ${CFG_DEFS_2})
563 list(FILTER CFG_DEFS EXCLUDE REGEX "_DIR=")
564 list(FILTER CFG_DEFS EXCLUDE REGEX "SURVEY_KEY=")
566 # Generate a target to determine version, which is execute every 'make' run
567 add_custom_target(find_version
569 -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated
570 -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR}
571 -DREV_MAJOR=${PROJECT_VERSION_MAJOR}
572 -DREV_MINOR=${PROJECT_VERSION_MINOR}
573 -DCONFIGURE_DEFINES="${CFG_DEFS}"
574 $<$<PLATFORM_ID:Windows>:-DWIN32=TRUE>
575 -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake"
576 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
577 BYPRODUCTS ${GENERATED_SOURCE_FILES}