function(detect_operating_system) message(STATUS "CMake Version: ${CMAKE_VERSION}") message(STATUS "CMake System Name: ${CMAKE_SYSTEM_NAME}") # LINUX wasn't added until CMake 3.25. if (CMAKE_VERSION VERSION_LESS 3.25.0 AND CMAKE_SYSTEM_NAME MATCHES "Linux") # Have to make it visible in this scope as well for below. set(LINUX TRUE PARENT_SCOPE) set(LINUX TRUE) endif() if(WIN32) message(STATUS "Building for Windows.") elseif(APPLE AND NOT IOS) message(STATUS "Building for MacOS.") elseif(LINUX) message(STATUS "Building for Linux.") elseif(BSD) message(STATUS "Building for *BSD.") else() message(FATAL_ERROR "Unsupported platform.") endif() endfunction() function(detect_compiler) if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(USE_CLANG_CL TRUE PARENT_SCOPE) set(IS_SUPPORTED_COMPILER TRUE PARENT_SCOPE) message(STATUS "Building with Clang-CL.") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") set(USE_CLANG TRUE PARENT_SCOPE) set(IS_SUPPORTED_COMPILER TRUE PARENT_SCOPE) message(STATUS "Building with Clang/LLVM.") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(USE_GCC TRUE PARENT_SCOPE) set(IS_SUPPORTED_COMPILER FALSE PARENT_SCOPE) message(STATUS "Building with GNU GCC.") elseif(MSVC) set(IS_SUPPORTED_COMPILER TRUE PARENT_SCOPE) message(STATUS "Building with MSVC.") else() message(FATAL_ERROR "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}") endif() endfunction() function(get_git_version_info) set(PCSX2_GIT_REV "") set(PCSX2_GIT_TAG "") set(PCSX2_GIT_HASH "") if (GIT_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.git) EXECUTE_PROCESS(WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags OUTPUT_VARIABLE PCSX2_GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) EXECUTE_PROCESS(WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} tag --points-at HEAD --sort=version:refname OUTPUT_VARIABLE PCSX2_GIT_TAG_LIST RESULT_VARIABLE TAG_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) # CAUTION: There is a race here, this solves the problem of a commit being tagged multiple times (take the last tag) # however, if simultaneous builds are pushing tags to the same commit you might get inconsistent results (it's a race) # # The easy solution is, don't do that, but just something to be aware of. if(PCSX2_GIT_TAG_LIST AND TAG_RESULT EQUAL 0) string(REPLACE "\n" ";" PCSX2_GIT_TAG_LIST "${PCSX2_GIT_TAG_LIST}") if (PCSX2_GIT_TAG_LIST) list(GET PCSX2_GIT_TAG_LIST -1 PCSX2_GIT_TAG) message("Using tag: ${PCSX2_GIT_TAG}") endif() endif() EXECUTE_PROCESS(WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse HEAD OUTPUT_VARIABLE PCSX2_GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) EXECUTE_PROCESS(WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} log -1 --format=%cd --date=local OUTPUT_VARIABLE PCSX2_GIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) endif() if (NOT PCSX2_GIT_REV) EXECUTE_PROCESS(WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD OUTPUT_VARIABLE PCSX2_GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) if (NOT PCSX2_GIT_REV) set(PCSX2_GIT_REV "Unknown") endif() endif() set(PCSX2_GIT_REV "${PCSX2_GIT_REV}" PARENT_SCOPE) set(PCSX2_GIT_TAG "${PCSX2_GIT_TAG}" PARENT_SCOPE) set(PCSX2_GIT_HASH "${PCSX2_GIT_HASH}" PARENT_SCOPE) set(PCSX2_GIT_DATE "${PCSX2_GIT_DATE}" PARENT_SCOPE) endfunction() function(write_svnrev_h) if ("${PCSX2_GIT_TAG}" MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)$") file(WRITE ${CMAKE_BINARY_DIR}/common/include/svnrev.h "#define GIT_TAG \"${PCSX2_GIT_TAG}\"\n" "#define GIT_TAGGED_COMMIT 1\n" "#define GIT_TAG_HI ${CMAKE_MATCH_1}\n" "#define GIT_TAG_MID ${CMAKE_MATCH_2}\n" "#define GIT_TAG_LO ${CMAKE_MATCH_3}\n" "#define GIT_REV \"${PCSX2_GIT_TAG}\"\n" "#define GIT_HASH \"${PCSX2_GIT_HASH}\"\n" "#define GIT_DATE \"${PCSX2_GIT_DATE}\"\n" ) elseif ("${PCSX2_GIT_REV}" MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)") file(WRITE ${CMAKE_BINARY_DIR}/common/include/svnrev.h "#define GIT_TAG \"${PCSX2_GIT_TAG}\"\n" "#define GIT_TAGGED_COMMIT 0\n" "#define GIT_TAG_HI ${CMAKE_MATCH_1}\n" "#define GIT_TAG_MID ${CMAKE_MATCH_2}\n" "#define GIT_TAG_LO ${CMAKE_MATCH_3}\n" "#define GIT_REV \"${PCSX2_GIT_REV}\"\n" "#define GIT_HASH \"${PCSX2_GIT_HASH}\"\n" "#define GIT_DATE \"${PCSX2_GIT_DATE}\"\n" ) else() file(WRITE ${CMAKE_BINARY_DIR}/common/include/svnrev.h "#define GIT_TAG \"${PCSX2_GIT_TAG}\"\n" "#define GIT_TAGGED_COMMIT 0\n" "#define GIT_TAG_HI 0\n" "#define GIT_TAG_MID 0\n" "#define GIT_TAG_LO 0\n" "#define GIT_REV \"${PCSX2_GIT_REV}\"\n" "#define GIT_HASH \"${PCSX2_GIT_HASH}\"\n" "#define GIT_DATE \"${PCSX2_GIT_DATE}\"\n" ) endif() endfunction() function(check_no_parenthesis_in_path) if ("${CMAKE_BINARY_DIR}" MATCHES "[()]" OR "${CMAKE_SOURCE_DIR}" MATCHES "[()]") message(FATAL_ERROR "Your path contains some parenthesis. Unfortunately Cmake doesn't support them correctly.\nPlease rename your directory to avoid '(' and ')' characters\n") endif() endfunction() # like add_library(new ALIAS old) but avoids add_library cannot create ALIAS target "new" because target "old" is imported but not globally visible. on older cmake function(alias_library new old) string(REPLACE "::" "" library_no_namespace ${old}) if (NOT TARGET _alias_${library_no_namespace}) add_library(_alias_${library_no_namespace} INTERFACE) target_link_libraries(_alias_${library_no_namespace} INTERFACE ${old}) endif() add_library(${new} ALIAS _alias_${library_no_namespace}) endfunction() function(source_groups_from_vcxproj_filters file) file(READ "${file}" filecontent) get_filename_component(parent "${file}" DIRECTORY) if (parent STREQUAL "") set(parent ".") endif() set(regex "<[^ ]+ Include=\"([^\"]+)\">[ \t\r\n]+([^<]+)<\\/Filter>[ \t\r\n]+<\\/[^ >]+>") string(REGEX MATCHALL "${regex}" filterstrings "${filecontent}") foreach(filterstring IN LISTS filterstrings) string(REGEX REPLACE "${regex}" "\\1" path "${filterstring}") string(REGEX REPLACE "${regex}" "\\2" group "${filterstring}") source_group("${group}" FILES "${parent}/${path}") endforeach() endfunction() # Extracts a translation with the given type ("source" or "translation") from the given category of the given ts file # (If there's multiple strings under the same category, which one it extracts is implementation defined. Just don't do it.) function(extract_translation_from_ts file type category output) file(READ "${file}" filecontent) # Don't you love it when the best parser your language has to offer is regex? set(regex_search "(<[^\\/>]+>[^<>]+<\\/[^>\\/]+>|<\\/?context>)") set(regex_extract "<[^\\/>]+>([^<>]+)<\\/([^>\\/]+)>") string(REGEX MATCHALL "${regex_search}" pieces "${filecontent}") foreach(piece IN LISTS pieces) if (piece STREQUAL "") set(found "") set(name_match FALSE) elseif (piece STREQUAL "") if (name_match) set(${output} "${found}" PARENT_SCOPE) break() endif() else() string(REGEX REPLACE "${regex_extract}" "\\1" content "${piece}") string(REGEX REPLACE "${regex_extract}" "\\2" tag "${piece}") if (tag STREQUAL "name" AND content STREQUAL "${category}") set(name_match TRUE) endif() if (tag STREQUAL "${type}") set(found "${content}") endif() endif() endforeach() endfunction() function(fixup_file_properties target) get_target_property(SOURCES ${target} SOURCES) if(APPLE) foreach(source IN LISTS SOURCES) # Set the right file types for .inl files in Xcode if("${source}" MATCHES "\\.(inl|h)$") set_source_files_properties("${source}" PROPERTIES XCODE_EXPLICIT_FILE_TYPE sourcecode.cpp.h) endif() if("${source}" MATCHES "\\.(qm)$") set_source_files_properties("${source}" PROPERTIES XCODE_EXPLICIT_FILE_TYPE compiled) endif() # CMake makefile and ninja generators will attempt to share one PCH for both cpp and mm files # That's not actually OK if("${source}" MATCHES "\\.mm$") set_source_files_properties("${source}" PROPERTIES SKIP_PRECOMPILE_HEADERS ON) endif() endforeach() endif() endfunction() function(disable_compiler_warnings_for_target target) if(MSVC) target_compile_options(${target} PRIVATE "/W0") else() target_compile_options(${target} PRIVATE "-w") endif() endfunction() function(detect_page_size) message(STATUS "Determining host page size") set(detect_page_size_file ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c) file(WRITE ${detect_page_size_file} " #include #include #include int main() { int res = sysconf(_SC_PAGESIZE); printf(\"%d\", res); return (res > 0) ? EXIT_SUCCESS : EXIT_FAILURE; }") try_run( detect_page_size_run_result detect_page_size_compile_result ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY} ${detect_page_size_file} RUN_OUTPUT_VARIABLE detect_page_size_output) if(NOT detect_page_size_compile_result OR NOT detect_page_size_run_result EQUAL 0 OR CMAKE_CROSSCOMPILING) message(FATAL_ERROR "Could not determine host page size.") else() message(STATUS "Host page size: ${detect_page_size_output}") set(HOST_PAGE_SIZE ${detect_page_size_output} CACHE STRING "Reported host page size") endif() endfunction() function(detect_cache_line_size) message(STATUS "Determining host cache line size") set(detect_cache_line_size_file ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c) file(WRITE ${detect_cache_line_size_file} " #include #include #include int main() { int l1i = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); int l1d = sysconf(_SC_LEVEL1_ICACHE_LINESIZE); int res = (l1i > l1d) ? l1i : l1d; for (int index = 0; index < 16; index++) { char buf[128]; snprintf(buf, sizeof(buf), \"/sys/devices/system/cpu/cpu0/cache/index%d/coherency_line_size\", index); FILE* fp = fopen(buf, \"rb\"); if (!fp) break; fread(buf, sizeof(buf), 1, fp); fclose(fp); int val = atoi(buf); res = (val > res) ? val : res; } printf(\"%d\", res); return (res > 0) ? EXIT_SUCCESS : EXIT_FAILURE; }") try_run( detect_cache_line_size_run_result detect_cache_line_size_compile_result ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY} ${detect_cache_line_size_file} RUN_OUTPUT_VARIABLE detect_cache_line_size_output) if(NOT detect_cache_line_size_compile_result OR NOT detect_cache_line_size_run_result EQUAL 0 OR CMAKE_CROSSCOMPILING) message(FATAL_ERROR "Could not determine host cache line size.") else() message(STATUS "Host cache line size: ${detect_cache_line_size_output}") set(HOST_CACHE_LINE_SIZE ${detect_cache_line_size_output} CACHE STRING "Reported host cache line size") endif() endfunction() function(get_recursive_include_directories output target inc_prop link_prop) get_target_property(dirs ${target} ${inc_prop}) if(NOT dirs) set(dirs) endif() get_target_property(deps ${target} ${link_prop}) if(deps) foreach(dep IN LISTS deps) if(TARGET ${dep}) get_recursive_include_directories(depdirs ${dep} INTERFACE_INCLUDE_DIRECTORIES INTERFACE_LINK_LIBRARIES) foreach(depdir IN LISTS depdirs) # Only match absolute paths # We'll hope any non-absolute paths will not get set as system directories if(depdir MATCHES "^/") list(APPEND dirs ${depdir}) endif() endforeach() endif() endforeach() list(REMOVE_DUPLICATES dirs) endif() set(${output} "${dirs}" PARENT_SCOPE) endfunction() function(force_include_last_impl target include inc_prop link_prop) get_recursive_include_directories(dirs ${target} ${inc_prop} ${link_prop}) set(remove) foreach(dir IN LISTS dirs) if("${dir}" MATCHES "${include}") list(APPEND remove ${dir}) endif() endforeach() if(NOT "${remove}" STREQUAL "") get_target_property(sysdirs ${target} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) if(NOT sysdirs) set(sysdirs) endif() # Move matching items to the end list(REMOVE_ITEM dirs ${remove}) list(APPEND dirs ${remove}) # Set them as system include directories list(APPEND sysdirs ${remove}) list(REMOVE_DUPLICATES sysdirs) set_target_properties(${target} PROPERTIES ${inc_prop} "${dirs}" INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${sysdirs}" ) endif() endfunction() function(force_include_last target include) force_include_last_impl(${target} "${include}" INTERFACE_INCLUDE_DIRECTORIES INTERFACE_LINK_LIBRARIES) force_include_last_impl(${target} "${include}" INCLUDE_DIRECTORIES LINK_LIBRARIES) endfunction()