diff --git a/CMakeModules/DuckStationUtils.cmake b/CMakeModules/DuckStationUtils.cmake index 6e47245eb..e2f103da4 100644 --- a/CMakeModules/DuckStationUtils.cmake +++ b/CMakeModules/DuckStationUtils.cmake @@ -370,10 +370,11 @@ function(add_resources TARGET DEST_SUBDIR SOURCE_DIR) endforeach() endfunction() -function(bundle_libraries TARGET) +function(add_runtime_libraries TARGET) unset(LIBRARY_SOURCES) + unset(LIBRARY_RPATHS) foreach(NAME IN LISTS ARGN) - get_target_property(dyn_lib_path ${NAME} IMPORTED_LOCATION_RELEASE) + get_target_property(dyn_lib_path ${NAME} IMPORTED_LOCATION_RELEASE) get_target_property(dyn_lib_soname ${NAME} IMPORTED_SONAME_RELEASE) if(dyn_lib_soname) string(REPLACE "@rpath/" "" dyn_lib_soname_filename "${dyn_lib_soname}") @@ -388,19 +389,35 @@ function(bundle_libraries TARGET) message(FATAL_ERROR "Could not find ${NAME}.") endif() - message(STATUS "Bundling imported library ${dyn_lib_soname}") + get_filename_component(dyn_lib_dir "${dyn_lib_path}" DIRECTORY) - if(APPLE) - target_sources(${target} PRIVATE "${dyn_lib_path}") - set_source_files_properties("${dyn_lib_path}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks) + if(APPLE AND NOT CMAKE_GENERATOR STREQUAL "Xcode" AND NOT SKIP_POSTPROCESS_BUNDLE) + # For normal macOS bundle generators, put the dylib into Contents/Frameworks. + message(STATUS "Bundling imported library ${dyn_lib_soname}") + target_sources(${TARGET} PRIVATE "${dyn_lib_path}") + set_source_files_properties( + "${dyn_lib_path}" + PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks + ) else() - list(APPEND LIBRARY_SOURCES "${dyn_lib_path}") + # Linux, and macOS when using the Xcode generator/not using bundles. + list(APPEND LIBRARY_RPATHS "${dyn_lib_dir}") endif() endforeach() - if(NOT APPLE) - add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${LIBRARY_SOURCES} "$" + # Add the required library directories to the target's existing + # build RPATH, without adding duplicate entries. + if(LIBRARY_RPATHS) + get_target_property(existing_rpath ${TARGET} BUILD_RPATH) + if(NOT existing_rpath) + set(existing_rpath "") + endif() + + list(APPEND existing_rpath ${LIBRARY_RPATHS}) + list(REMOVE_DUPLICATES existing_rpath) + set_target_properties( + ${TARGET} + PROPERTIES BUILD_RPATH "${existing_rpath}" ) endif() endfunction() diff --git a/scripts/appimage/make-appimage.sh b/scripts/appimage/make-appimage.sh index 0cce89d30..610416a95 100755 --- a/scripts/appimage/make-appimage.sh +++ b/scripts/appimage/make-appimage.sh @@ -33,15 +33,28 @@ APPDIRNAME=DuckStation.AppDir STRIP=strip declare -a MANUAL_LIBS=( + "libharfbuzz.so" + "libfreetype.so.6" + "libjpeg.so.62" + "libpng16.so.16" + "libsharpyuv.so.0" + "libwebpdecoder.so.3" + "libwebpdemux.so.2" + "libwebpmux.so.3" + "libwebp.so.7" "libz.so.1" + + "libdiscord-rpc.so" + "libshaderc_shared.so" + "libspirv-cross-c-shared.so.0" + "libsqlite3.so.3" + "libavcodec.so.63" "libavformat.so.63" "libavutil.so.61" "libswscale.so.10" "libswresample.so.7" "libopenh264.so.8" - "libharfbuzz.so" - "libfreetype.so.6" ) set -e @@ -117,7 +130,6 @@ $LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/duckstat echo "Copying resources into AppDir..." cp -a "$BUILDDIR/bin/resources" "$OUTDIR/usr/bin" -cp "$BUILDDIR/bin/"*.so* "$OUTDIR/usr/bin" # Restore unstripped deps (for cache). rm -fr "$DEPSDIR" diff --git a/scripts/appimage/make-cross-appimage.sh b/scripts/appimage/make-cross-appimage.sh index d3f6aabd8..415539f43 100755 --- a/scripts/appimage/make-cross-appimage.sh +++ b/scripts/appimage/make-cross-appimage.sh @@ -85,6 +85,7 @@ declare -a DEPLIBS=( "libpng16.so.16" "libSDL3.so.0" "libsharpyuv.so.0" + "libwebpdecoder.so.3" "libwebpdemux.so.2" "libwebpmux.so.3" "libwebp.so.7" @@ -97,6 +98,7 @@ declare -a DEPLIBS=( "libshaderc_shared.so" "libsoundtouch.so.2" "libspirv-cross-c-shared.so.0" + "libsqlite3.so.3" #"libavcodec.so.61" #"libavformat.so.61" diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp index 33cd880dc..5b7dab6a0 100644 --- a/src/common/dynamic_library.cpp +++ b/src/common/dynamic_library.cpp @@ -88,26 +88,6 @@ std::string DynamicLibrary::GetVersionedFilename(const char* libname, int major, #endif } -std::string DynamicLibrary::GetBundledLibraryPath(const char* libname, int major /*= -1*/, int minor /*= -1*/, - int patch /*= -1*/) -{ - Error error; - std::string program_path = FileSystem::GetProgramPath(&error); - if (program_path.empty()) [[unlikely]] - { - ERROR_LOG("Failed to get program path: {}", error.GetDescription()); - return {}; - } - -#ifdef __APPLE__ - program_path = Path::Combine(Path::GetDirectory(program_path), ".." FS_OSPATH_SEPARATOR_STR "Frameworks"); - Path::Canonicalize(&program_path); - return Path::Combine(program_path, GetVersionedFilename(libname, major, minor, patch)); -#else - return Path::Combine(Path::GetDirectory(program_path), GetVersionedFilename(libname, major, minor, patch)); -#endif -} - bool DynamicLibrary::Open(const char* filename, Error* error) { #ifdef _WIN32 diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h index d500992f4..4566a4b5f 100644 --- a/src/common/dynamic_library.h +++ b/src/common/dynamic_library.h @@ -39,9 +39,6 @@ public: /// Mac: libLIBNAME.MAJOR.MINOR.PATCH.dylib static std::string GetVersionedFilename(const char* libname, int major = -1, int minor = -1, int patch = -1); - /// Returns the path to the specified library name, depending on the platform. - static std::string GetBundledLibraryPath(const char* libname, int major = -1, int minor = -1, int patch = -1); - /// Returns true if a module is loaded, otherwise false. bool IsOpen() const { return m_handle != nullptr; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6b7bebb57..52f5c5d6d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -226,5 +226,5 @@ function(add_core_resources target) add_resources(${target} "resources" ${CMAKE_SOURCE_DIR}/data/resources) # Copy dynamically-loaded libraries into the bundle. - bundle_libraries(${target} DiscordRPC::discord-rpc) + add_runtime_libraries(${target} DiscordRPC::discord-rpc) endfunction() diff --git a/src/core/discord_presence.cpp b/src/core/discord_presence.cpp index b2b4e5059..a268111a8 100644 --- a/src/core/discord_presence.cpp +++ b/src/core/discord_presence.cpp @@ -58,7 +58,7 @@ bool DiscordPresence::OpenDiscordRPC(Error* error) if (s_locals.rpc_library.IsOpen()) return true; - if (!s_locals.rpc_library.Open(DynamicLibrary::GetBundledLibraryPath("discord-rpc").c_str(), error)) + if (!s_locals.rpc_library.Open(DynamicLibrary::GetVersionedFilename("discord-rpc").c_str(), error)) { Error::AddPrefix(error, "Failed to load discord-rpc: "); return false; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index e91c2ba87..595205f65 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -280,10 +280,10 @@ function(add_util_resources target) endif() # Copy dynamically-loaded libraries into the bundle. - bundle_libraries(${target} spirv-cross-c-shared Shaderc::shaderc_shared SQLite3::sqlite3-shared - libjpeg-turbo::jpeg PNG::png_shared WebP::webp WebP::sharpyuv WebP::webpdecoder - WebP::webpdemux WebP::libwebpmux) + add_runtime_libraries(${target} spirv-cross-c-shared Shaderc::shaderc_shared SQLite3::sqlite3-shared + libjpeg-turbo::jpeg PNG::png_shared WebP::webp WebP::sharpyuv WebP::webpdecoder + WebP::webpdemux WebP::libwebpmux) if(APPLE) - bundle_libraries(${target} harfbuzz::harfbuzz) + add_runtime_libraries(${target} harfbuzz::harfbuzz) endif() endfunction() diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index 41597861a..718f9d490 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -1403,7 +1403,7 @@ bool DynShaderc::Open(Error* error) std::call_once(s_locals.shaderc_init_flag, [&error]() { Error lerror; DynamicLibrary lib; - if (!lib.Open(DynamicLibrary::GetBundledLibraryPath("shaderc_shared").c_str(), error)) + if (!lib.Open(DynamicLibrary::GetVersionedFilename("shaderc_shared").c_str(), error)) { ERROR_LOG("Failed to load shaderc: {}", (error ? error : &lerror)->GetDescription()); Error::AddPrefix(error, "Failed to load shaderc: "); @@ -1444,9 +1444,9 @@ bool DynSpirvCross::Open(Error* error) DynamicLibrary lib; #if defined(_WIN32) || defined(__ANDROID__) // SPVC's build on Windows doesn't spit out a versioned DLL. - const std::string libpath = DynamicLibrary::GetBundledLibraryPath("spirv-cross-c-shared"); + const std::string libpath = DynamicLibrary::GetVersionedFilename("spirv-cross-c-shared"); #else - const std::string libpath = DynamicLibrary::GetBundledLibraryPath("spirv-cross-c-shared", SPVC_C_API_VERSION_MAJOR); + const std::string libpath = DynamicLibrary::GetVersionedFilename("spirv-cross-c-shared", SPVC_C_API_VERSION_MAJOR); #endif if (!lib.Open(libpath.c_str(), error ? error : &lerror)) { diff --git a/src/util/sqlite_helpers.cpp b/src/util/sqlite_helpers.cpp index 77afc1286..28114b82e 100644 --- a/src/util/sqlite_helpers.cpp +++ b/src/util/sqlite_helpers.cpp @@ -43,7 +43,7 @@ bool DynSqlite::Open(Error* error) std::call_once(s_locals.sqlite_init_flag, [&error]() { Error lerror; DynamicLibrary lib; - if (!lib.Open(DynamicLibrary::GetBundledLibraryPath("sqlite3", lib_major_version).c_str(), &lerror)) + if (!lib.Open(DynamicLibrary::GetVersionedFilename("sqlite3", lib_major_version).c_str(), &lerror)) { ERROR_LOG("Failed to load sqlite: {}", lerror.GetDescription()); Error::SetStringFmt(error, "Failed to load sqlite: {}", lerror.GetDescription());