diff --git a/CMakeLists.txt b/CMakeLists.txt index 357ee6954..6fe98c90f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,20 +65,27 @@ endif() # We don't need exceptions, disable them to save a bit of code size. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti") -# Rewrite paths in macros to be relative to the source directory. -# Helpful for reproducible builds. +# Rewrite paths in macros and debug info to fixed names, so that the output does not depend +# on where the source and build directories are located. Helpful for reproducible builds. +# The build directory is mapped last, so it takes precedence when it is inside the source directory. if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT CMAKE_GENERATOR MATCHES "Xcode" AND (COMPILER_CLANG OR COMPILER_CLANG_CL OR COMPILER_GCC)) - file(RELATIVE_PATH source_dir_remap "${CMAKE_BINARY_DIR}" "${CMAKE_SOURCE_DIR}") - string(REGEX REPLACE "\/+$" "" source_dir_remap "${source_dir_remap}") - set(source_dir_remap_str "\"${CMAKE_SOURCE_DIR}\"=\"${source_dir_remap}\"") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffile-prefix-map=${source_dir_remap_str}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffile-prefix-map=${source_dir_remap_str}") + set(dir_remap_str "-ffile-prefix-map=\"${CMAKE_SOURCE_DIR}\"=. -ffile-prefix-map=\"${CMAKE_BINARY_DIR}\"=build") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${dir_remap_str}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${dir_remap_str}") endif() # Write binaries to a seperate directory. set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") +# Locate the prebuilt dependencies relative to the binary, instead of using the absolute +# paths that CMake adds to the RUNPATH by default, which leak the source directory. +if(LINUX) + get_origin_relative_path(deps_rpath "${DEPS_PATH}/lib") + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + set(CMAKE_INSTALL_RPATH "${deps_rpath}") +endif() + # Enable large file support on Linux 32-bit platforms. if(CMAKE_SIZEOF_VOID_P EQUAL 4) add_definitions("-D_FILE_OFFSET_BITS=64") diff --git a/CMakeModules/DuckStationUtils.cmake b/CMakeModules/DuckStationUtils.cmake index 2bd7d72ed..29e45ff5f 100644 --- a/CMakeModules/DuckStationUtils.cmake +++ b/CMakeModules/DuckStationUtils.cmake @@ -412,14 +412,23 @@ function(add_runtime_libraries TARGET) ) else() # Linux, and macOS when using the Xcode generator/not using bundles. + if(LINUX) + get_origin_relative_path(dyn_lib_dir "${dyn_lib_dir}") + endif() list(APPEND LIBRARY_RPATHS "${dyn_lib_dir}") endif() endforeach() # Add the required library directories to the target's existing # build RPATH, without adding duplicate entries. + # Linux is built with the install RPATH, see the top-level CMakeLists.txt. + if(LINUX) + set(rpath_property INSTALL_RPATH) + else() + set(rpath_property BUILD_RPATH) + endif() if(LIBRARY_RPATHS) - get_target_property(existing_rpath ${TARGET} BUILD_RPATH) + get_target_property(existing_rpath ${TARGET} ${rpath_property}) if(NOT existing_rpath) set(existing_rpath "") endif() @@ -428,7 +437,58 @@ function(add_runtime_libraries TARGET) list(REMOVE_DUPLICATES existing_rpath) set_target_properties( ${TARGET} - PROPERTIES BUILD_RPATH "${existing_rpath}" + PROPERTIES ${rpath_property} "${existing_rpath}" ) endif() endfunction() + +# Returns an RPATH entry for DIR that is relative to the directory binaries are written to. +function(get_origin_relative_path OUTVAR DIR) + # $ORIGIN is the real location of the binary, so symlinks have to be resolved first. + file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + file(REAL_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" binary_dir) + file(REAL_PATH "${DIR}" real_dir) + file(RELATIVE_PATH relative_dir "${binary_dir}" "${real_dir}") + set(${OUTVAR} "$ORIGIN/${relative_dir}" PARENT_SCOPE) +endfunction() + +# Compiles a .qrc file to a binary .rcc file, without embedding file modification times. +function(add_binary_resources TARGET QRC_FILE OUTPUT_FILE) + get_filename_component(qrc_path "${QRC_FILE}" ABSOLUTE) + get_filename_component(qrc_dir "${qrc_path}" DIRECTORY) + + # Pick up the files listed in the .qrc as dependencies, re-running CMake if the list changes. + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${qrc_path}") + file(READ "${qrc_path}" qrc_contents) + string(REGEX MATCHALL "]*>[^<]+" qrc_entries "${qrc_contents}") + set(qrc_depends) + foreach(entry IN LISTS qrc_entries) + string(REGEX REPLACE "]*>([^<]+)" "\\1" entry "${entry}") + list(APPEND qrc_depends "${qrc_dir}/${entry}") + endforeach() + + # rcc stamps every entry with the mtime of its source file, i.e. the checkout time. + # Pin it to the commit date instead, unless the caller has already provided a date. + set(source_date "$ENV{SOURCE_DATE_EPOCH}") + if(NOT source_date) + find_package(Git) + if(EXISTS "${PROJECT_SOURCE_DIR}/.git" AND GIT_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} log -1 --format=%ct + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + OUTPUT_VARIABLE source_date + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() + endif() + set(rcc_env) + if(source_date) + set(rcc_env "${CMAKE_COMMAND}" -E env "SOURCE_DATE_EPOCH=${source_date}") + endif() + + add_custom_command(OUTPUT "${OUTPUT_FILE}" + COMMAND ${rcc_env} "$" -no-compress --binary --name ${TARGET} --output "${OUTPUT_FILE}" "${qrc_path}" + DEPENDS Qt6::rcc "${qrc_path}" ${qrc_depends} + VERBATIM) + add_custom_target(${TARGET} ALL DEPENDS "${OUTPUT_FILE}") +endfunction() diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index 118c35ae9..2a7f72619 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -283,14 +283,14 @@ if(NOT BUILD_MACOS_BUNDLE) set(RCC_FILENAME "duckstation-qt.rcc") set(RCC_BUILD_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RCC_FILENAME}") set(RCC_OUTPUT_DIR "$/resources") - qt_add_binary_resources(duckstation-qt-rcc resources/duckstation-qt.qrc DESTINATION ${RCC_BUILD_PATH} OPTIONS -no-compress) + add_binary_resources(duckstation-qt-rcc resources/duckstation-qt.qrc "${RCC_BUILD_PATH}") add_dependencies(duckstation-qt duckstation-qt-rcc) add_custom_command(TARGET duckstation-qt POST_BUILD COMMAND "${CMAKE_COMMAND}" -E make_directory "${RCC_OUTPUT_DIR}") add_custom_command(TARGET duckstation-qt POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${RCC_BUILD_PATH}" "${RCC_OUTPUT_DIR}/${RCC_FILENAME}") else() set(RCC_FILE "${CMAKE_CURRENT_BINARY_DIR}/duckstation-qt.rcc") - qt_add_binary_resources(duckstation-qt-rcc resources/duckstation-qt.qrc DESTINATION ${RCC_FILE} OPTIONS -no-compress) + add_binary_resources(duckstation-qt-rcc resources/duckstation-qt.qrc "${RCC_FILE}") add_dependencies(duckstation-qt duckstation-qt-rcc) target_sources(duckstation-qt PRIVATE ${RCC_FILE}) set_source_files_properties(${RCC_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)