diff --git a/.gitlab-ci-files/windows-desktop.yml b/.gitlab-ci-files/windows-desktop.yml index 81c30400e..2518501ef 100644 --- a/.gitlab-ci-files/windows-desktop.yml +++ b/.gitlab-ci-files/windows-desktop.yml @@ -180,14 +180,22 @@ win64-ninja-vs2022-scheduled-windows: needs: [] rules: - !reference [.rules-merge-request-manual, rules] - - if: $NIGHTLY_MASTER - if: $NIGHTLY_RELEASE - if: $PACKAGE_WINDOWS - - if: $DEPLOY_WINDOWS variables: CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER +.vs-win64-package-cr: + stage: package + needs: [] + rules: + - if: $NIGHTLY_MASTER + - if: $DEPLOY_WINDOWS + variables: + CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON -DENABLE_GPL_THIRD_PARTIES=OFF -DENABLE_BUGSPLAT_SYMBOLS_UPLOAD=ON -DBUGSPPLAT_CLIENT_ID=$BUGSPPLAT_CLIENT_ID -DBUGSPPLAT_CLIENT_SECRET=$BUGSPPLAT_CLIENT_SECRET + RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER + win64-ninja-vs2022-package-windows: variables: CMAKE_GENERATOR: "Ninja" @@ -198,6 +206,18 @@ win64-ninja-vs2022-package-windows: - .windows-ninja-variables - .vs-win64-package +#Packaging for deployment (Upload symbols at the same time of the packaging) +win64-ninja-vs2022-package-cr-windows: + variables: + CMAKE_GENERATOR: "Ninja" + CMAKE_ARCHITECTURE: "" + PARALLEL_OPTIONS: "" + extends: + - .windows-vs2022 + - .windows-ninja-variables + - .vs-win64-package-cr + + ################################################# # SIGNING ################################################# @@ -213,8 +233,28 @@ win64-codesigning: MINGW_TYPE: mingw64 rules: - !reference [.rules-merge-request-manual, rules] - - if: $NIGHTLY_MASTER - if: $PACKAGE_WINDOWS + script: + - cd build-desktop/OUTPUT/Packages/ + - Invoke-Expression "& ${WINDOWS_SIGN_TOOL} sign /fd SHA256 /t ${WINDOWS_SIGN_TIMESTAMP_URL} /sha1 ${WINDOWS_SIGN_HASH} *.exe" + - 'if (-not ($LastExitCode -eq 0)) {throw "Error: Signature failed"}' + artifacts: + paths: + - build-desktop\OUTPUT\Packages\* + when: always + expire_in: 1 week + +win64-codesigning-cr: + stage: signing + allow_failure: true + extends: + - .windows-codesigning + needs: + - win64-ninja-vs2022-package-cr-windows + variables: + MINGW_TYPE: mingw64 + rules: + - if: $NIGHTLY_MASTER - if: $DEPLOY_WINDOWS script: - cd build-desktop/OUTPUT/Packages/ @@ -246,7 +286,7 @@ win64-ninja-vs2022-upload: extends: - .win64-upload needs: - - win64-codesigning + - win64-codesigning-cr .win64-plugins-upload: stage: deploy diff --git a/.gitmodules b/.gitmodules index aa3e6c896..a4d6dacd3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,12 @@ [submodule "linphone-sdk"] path = external/linphone-sdk url = https://gitlab.linphone.org/BC/public/linphone-sdk.git +[submodule "external/google/gn"] + path = external/google/gn + url = https://gitlab.linphone.org/BC/public/external/gn.git +[submodule "external/google/crashpad"] + path = external/google/crashpad + url = https://gitlab.linphone.org/BC/public/external/crashpad.git +[submodule "external/google/chromium-depot-tools"] + path = external/google/chromium-depot-tools + url = https://gitlab.linphone.org/BC/public/external/chromium-depot-tools.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 48d937bde..e1be34f81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,7 @@ add_option(OPTION_LIST ENABLE_UNIT_TESTS "Enable unit test of SDK." OFF) add_option(OPTION_LIST ENABLE_UPDATE_CHECK "Enable update check." ON) add_option(OPTION_LIST ENABLE_VIDEO "Enable Video support." YES) add_option(OPTION_LIST ENABLE_WINDOWS_TOOLS_CHECK "Enable tools checks on Windows for auto install." OFF) +add_option(OPTION_LIST ENABLE_CRASH_HANDLER "Enable Crash Handler" YES) add_cache(OPTION_LIST LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file that work along check_version and use this URL" "") @@ -184,6 +185,12 @@ add_cache(OPTION_LIST LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file th add_option(OPTION_LIST ENABLE_OPENH264 "Enable the use of OpenH264 codec" ${ENABLE_VIDEO}) add_option(OPTION_LIST ENABLE_SCREENSHARING "Enable screen sharing." ${ENABLE_VIDEO}) +add_option(OPTION_LIST ENABLE_BUGSPLAT_SYMBOLS_UPLOAD "Generate and upload symbols to Bugsplat." OFF) +add_cache(OPTION_LIST BUGSPPLAT_CLIENT_ID "Client ID for Bugsplat." "") +add_cache(OPTION_LIST BUGSPPLAT_CLIENT_SECRET "Client Secret for Bugsplat." "") + + + # QtKeychain add_option(OPTION_LIST LIBSECRET_SUPPORT "Build with libsecret support" OFF) # Need libsecret-devel if(WIN32) @@ -226,8 +233,8 @@ if(LINPHONEAPP_BUILD_TYPE STREQUAL "Default") endif() + if(NOT APPLE OR MONO_ARCH) - add_custom_target(linphone-deps) if(NOT LINPHONE_QT_ONLY) function(add_external) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here @@ -235,6 +242,9 @@ if(NOT APPLE OR MONO_ARCH) endfunction() add_external() endif() + if(TARGET Crashpad) + set(HAVE_CRASH_HANDLER 1) + endif() function(add_linphone_app) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here if(UNIX) diff --git a/Linphone/CMakeLists.txt b/Linphone/CMakeLists.txt index c7e44e264..632c0e337 100644 --- a/Linphone/CMakeLists.txt +++ b/Linphone/CMakeLists.txt @@ -96,15 +96,12 @@ else() set(MSPLUGINS_DIR "${CMAKE_INSTALL_LIBDIR}/mediastreamer/plugins") endif() -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") - if(${Qt6_VERSION} VERSION_LESS "6.10.0") message( FATAL_ERROR "Linphone requires Qt 6.10.0 or newer. Exiting CMake." ) endif() qt6_standard_project_setup() - ################################################################ # SOURCES ################################################################ @@ -124,6 +121,7 @@ add_subdirectory(core) set(LANGUAGES_DIRECTORY "data/languages") set(I18N_FILENAME i18n.qrc) set(LANGUAGES en fr de) +set(CMAKE_CPP_COMPILER gcc) # Add languages support. add_subdirectory("${LANGUAGES_DIRECTORY}" "data/languages") @@ -154,7 +152,7 @@ qt6_add_big_resources(_LINPHONEAPP_FONTS_FILES data/fonts.qrc) list(APPEND _LINPHONEAPP_FONTS_FILES data/fonts.qrc) set_property(SOURCE data/fonts.qrc PROPERTY SKIP_AUTORCC ON) -qt6_add_executable(Linphone +qt6_add_executable(${TARGET_NAME} ${_LINPHONEAPP_SOURCES} ${_LINPHONEAPP_FONTS_FILES} ${_APPDETAILS_RC_FILE} @@ -170,15 +168,19 @@ qt6_add_qml_module(Linphone RESOURCES data/fonts.qrc ) -if (WIN32) - if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - install(FILES "$" DESTINATION ${CMAKE_INSTALL_BINDIR}) - endif() -endif() - qt6_add_resources(Linphone "resources" PREFIX "/" FILES ${_LINPHONEAPP_RC_FILES}) set_property(TARGET Linphone PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt +################################################################ +# CRASHPAD +################################################################ + +if (HAVE_CRASH_HANDLER) + add_dependencies(${TARGET_NAME} Crashpad) + target_link_libraries(${TARGET_NAME} PRIVATE Crashpad) + get_target_property(CRASHPAD_EXECUTABLE_NAME Crashpad CRASHPAD_EXECUTABLE_NAME) +endif() + ################################################################ # TARGETS LINKS ################################################################ @@ -223,3 +225,14 @@ foreach(T ${QT_PACKAGES}) endforeach() + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") + +if (WIN32) + if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + install(FILES "$" DESTINATION ${CMAKE_INSTALL_BINDIR}) + foreach(T ${APP_TARGETS}) + install(FILES "$" DESTINATION ${CMAKE_INSTALL_BINDIR}) + endforeach () + endif() +endif() \ No newline at end of file diff --git a/Linphone/application_info.cmake b/Linphone/application_info.cmake index b8e4f1556..23345a1a5 100644 --- a/Linphone/application_info.cmake +++ b/Linphone/application_info.cmake @@ -7,5 +7,6 @@ set(APPLICATION_LICENCE "GNU General Public License V3") set(APPLICATION_LICENCE_URL "https://www.gnu.org/licenses/gpl-3.0.html") set(APPLICATION_START_LICENCE "2010") set(APPLICATION_SEMVER ${LINPHONEAPP_VERSION}) +set(BUGSPLAT_DATABASE "Linphone") set(EXECUTABLE_NAME ${LINPHONEAPP_EXECUTABLE_NAME}) diff --git a/Linphone/config.h.cmake b/Linphone/config.h.cmake index f462cbfaf..6a578790e 100644 --- a/Linphone/config.h.cmake +++ b/Linphone/config.h.cmake @@ -39,3 +39,6 @@ #cmakedefine LINPHONEAPP_SHORT_VERSION "${LINPHONEAPP_SHORT_VERSION}" #cmakedefine GIT_BRANCH_NAME "${GIT_BRANCH_NAME}" #cmakedefine LINPHONESDK_VERSION "${LINPHONESDK_VERSION}" +#cmakedefine HAVE_CRASH_HANDLER "${HAVE_CRASH_HANDLER}" +#cmakedefine CRASHPAD_EXECUTABLE_NAME "${CRASHPAD_EXECUTABLE_NAME}" +#cmakedefine BUGSPLAT_DATABASE "${BUGSPLAT_DATABASE}" diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 330a776b5..d044f1246 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -101,6 +101,9 @@ #include "tool/accessibility/AccessibilityHelper.hpp" #include "tool/accessibility/FocusHelper.hpp" #include "tool/accessibility/KeyboardShortcuts.hpp" +#ifdef HAVE_CRASH_HANDLER +#include "tool/crash_reporter/CrashReporter.hpp" +#endif #include "tool/native/DesktopTools.hpp" #include "tool/providers/AvatarProvider.hpp" #include "tool/providers/EmojiProvider.hpp" @@ -112,6 +115,7 @@ #include "tool/thread/Thread.hpp" #include "tool/ui/DashRectangle.hpp" + #if defined(Q_OS_MACOS) #include "core/event-count-notifier/EventCountNotifierMacOs.hpp" #else @@ -291,6 +295,13 @@ App::App(int &argc, char *argv[]) QCoreApplication::setApplicationName(EXECUTABLE_NAME); QApplication::setOrganizationDomain(EXECUTABLE_NAME); QCoreApplication::setApplicationVersion(APPLICATION_SEMVER); + // CarshReporter must be call after app initialization like names. +#ifdef HAVE_CRASH_HANDLER + CrashReporter::start(); +#else + lWarning() << "[Main] The application doesn't support the CrashReporter."; +#endif + // If not OpenGL, createRender is never call. QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); setWindowIcon(QIcon(Constants::WindowIconPath)); @@ -491,7 +502,7 @@ App *App::getInstance() { } Thread *App::getLinphoneThread() { - return App::getInstance()->mLinphoneThread; + return App::getInstance() ? App::getInstance()->mLinphoneThread : nullptr; } Notifier *App::getNotifier() const { diff --git a/Linphone/core/path/Paths.cpp b/Linphone/core/path/Paths.cpp index 94a0ea260..393ecb4e2 100644 --- a/Linphone/core/path/Paths.cpp +++ b/Linphone/core/path/Paths.cpp @@ -139,6 +139,10 @@ static inline QString getAppPackagePluginsDirPath() { return getAppPackageDir().absolutePath() + Constants::PathPlugins; } +static inline QString getAppBinDirPath() { + return getAppPackageDir().absolutePath() + Constants::PathBin; +} + static inline QString getAppAssistantConfigDirPath() { return getAppPackageDataDirPath() + Constants::PathAssistantConfig; } @@ -283,6 +287,20 @@ QString Paths::getLogsDirPath() { Constants::PathLogs); } +QString Paths::getCrashpadDirPath() { +#ifdef HAVE_CRASH_HANDLER + return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + + Constants::PathCrashpad); +#else + return ""; +#endif +} + +QString Paths::getMetricsDirPath() { + return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + + Constants::PathMetrics); +} + QString Paths::getMessageHistoryFilePath() { return getReadableFilePath( getAppMessageHistoryFilePath()); // No need to ensure that the file exists as this DB is deprecated @@ -337,6 +355,14 @@ QString Paths::getZrtpSecretsFilePath() { Constants::PathZrtpSecrets); } +QString Paths::getCrashpadHandlerFilePath() { +#ifdef HAVE_CRASH_HANDLER + return getAppBinDirPath() + Constants::PathCrashpadHandler; +#else + return ""; +#endif +} + // ----------------------------------------------------------------------------- void Paths::migrate() { diff --git a/Linphone/core/path/Paths.hpp b/Linphone/core/path/Paths.hpp index 753fcc3eb..46d39a8a0 100644 --- a/Linphone/core/path/Paths.hpp +++ b/Linphone/core/path/Paths.hpp @@ -43,6 +43,8 @@ QString getFactoryConfigFilePath(); QString getFriendsListFilePath(); QString getLimeDatabasePath(); QString getLogsDirPath(); +QString getCrashpadDirPath(); +QString getMetricsDirPath(); QString getMessageHistoryFilePath(); QString getPackageDataDirPath(); QString getPackageMsPluginsDirPath(); @@ -56,6 +58,7 @@ QString getToolsDirPath(); QString getUserCertificatesDirPath(); QString getZrtpDataFilePath(); QString getZrtpSecretsFilePath(); +QString getCrashpadHandlerFilePath(); void migrate(); } // namespace Paths diff --git a/Linphone/core/setting/SettingsCore.cpp b/Linphone/core/setting/SettingsCore.cpp index 48cc3495b..ef2fa5efc 100644 --- a/Linphone/core/setting/SettingsCore.cpp +++ b/Linphone/core/setting/SettingsCore.cpp @@ -97,6 +97,7 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) { // Logs mLogsEnabled = settingsModel->getLogsEnabled(); mFullLogsEnabled = settingsModel->getFullLogsEnabled(); + mCrashReporterEnabled = settingsModel->getCrashReporterEnabled(); mLogsFolder = settingsModel->getLogsFolder(); mLogsEmail = settingsModel->getLogsEmail(); @@ -189,6 +190,7 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) { // Logs mLogsEnabled = settingsCore.mLogsEnabled; mFullLogsEnabled = settingsCore.mFullLogsEnabled; + mCrashReporterEnabled = settingsCore.mCrashReporterEnabled; mLogsFolder = settingsCore.mLogsFolder; mLogsEmail = settingsCore.mLogsEmail; @@ -416,7 +418,10 @@ void SettingsCore::setSelf(QSharedPointer me) { // Logs mSettingsModelConnection->makeConnectToModel(&SettingsModel::logsEnabledChanged, [this](const bool status) { - mSettingsModelConnection->invokeToCore([this, status]() { setLogsEnabled(status); }); + mSettingsModelConnection->invokeToCore([this, status]() { + setCrashReporterEnabled(status); + setLogsEnabled(status); + }); }); mSettingsModelConnection->makeConnectToModel(&SettingsModel::fullLogsEnabledChanged, [this](const bool status) { @@ -558,6 +563,7 @@ void SettingsCore::reset(const SettingsCore &settingsCore) { // Logs setLogsEnabled(settingsCore.mLogsEnabled); setFullLogsEnabled(settingsCore.mFullLogsEnabled); + setCrashReporterEnabled(settingsCore.mCrashReporterEnabled); setLogsFolder(settingsCore.mLogsFolder); // DND @@ -1007,6 +1013,18 @@ void SettingsCore::setFullLogsEnabled(bool enabled) { } } +bool SettingsCore::getCrashReporterEnabled() const { + return mCrashReporterEnabled; +} + +void SettingsCore::setCrashReporterEnabled(bool enabled) { + if (mCrashReporterEnabled != enabled) { + mCrashReporterEnabled = enabled; + emit crashReporterEnabledChanged(); + setIsSaved(false); + } +} + void SettingsCore::setRingtone(QString path) { if (mRingtonePath != path) { mRingtonePath = path; @@ -1144,6 +1162,7 @@ void SettingsCore::writeIntoModel(std::shared_ptr model) const { // Logs model->setLogsEnabled(mLogsEnabled); model->setFullLogsEnabled(mFullLogsEnabled); + model->setCrashReporterEnabled(mLogsEnabled); // UI model->setDisableChatFeature(mDisableChatFeature); @@ -1220,6 +1239,7 @@ void SettingsCore::writeFromModel(const std::shared_ptr &model) { // Logs mLogsEnabled = model->getLogsEnabled(); mFullLogsEnabled = model->getFullLogsEnabled(); + mCrashReporterEnabled = model->getCrashReporterEnabled(); mLogsFolder = model->getLogsFolder(); mLogsEmail = model->getLogsEmail(); diff --git a/Linphone/core/setting/SettingsCore.hpp b/Linphone/core/setting/SettingsCore.hpp index 8714126fe..50f5dea9b 100644 --- a/Linphone/core/setting/SettingsCore.hpp +++ b/Linphone/core/setting/SettingsCore.hpp @@ -91,6 +91,7 @@ public: Q_PROPERTY(bool logsEnabled READ getLogsEnabled WRITE setLogsEnabled NOTIFY logsEnabledChanged) Q_PROPERTY(bool fullLogsEnabled READ getFullLogsEnabled WRITE setFullLogsEnabled NOTIFY fullLogsEnabledChanged) + Q_PROPERTY(bool crashReporterEnabled READ getCrashReporterEnabled WRITE setCrashReporterEnabled NOTIFY crashReporterEnabledChanged) Q_PROPERTY(QString logsEmail READ getLogsEmail) Q_PROPERTY(QString logsFolder READ getLogsFolder) Q_PROPERTY(QString ringtoneName READ getRingtoneFileName NOTIFY ringtoneChanged) @@ -234,9 +235,10 @@ public: bool getLogsEnabled() const; void setLogsEnabled(bool enabled); - bool getFullLogsEnabled() const; void setFullLogsEnabled(bool enabled); + bool getCrashReporterEnabled() const; + void setCrashReporterEnabled(bool enabled); void setRingtone(QString path); QString getRingtoneFileName() const; @@ -364,6 +366,7 @@ signals: void logsEnabledChanged(); void fullLogsEnabledChanged(); + void crashReporterEnabledChanged(); void logsUploadTerminated(bool status, QString url); void logsFolderChanged(const QString &folder); @@ -439,6 +442,7 @@ private: // Debug logs bool mLogsEnabled; bool mFullLogsEnabled; + bool mCrashReporterEnabled; QString mLogsFolder; QString mLogsEmail; diff --git a/Linphone/main.cpp b/Linphone/main.cpp index 92939ba45..771e1984d 100644 --- a/Linphone/main.cpp +++ b/Linphone/main.cpp @@ -3,6 +3,8 @@ #include "core/App.hpp" #include "core/logger/QtLogger.hpp" +#include "core/path/Paths.hpp" + #include #include #include @@ -22,12 +24,15 @@ FILE *gStream = NULL; #define ACCESSBILITY_WORKAROUND #include #include + void DummyUpdateHandler(QAccessibleEvent *event) { } void DummyRootObjectHandler(QObject *) { } #endif + + void cleanStream() { #ifdef _WIN32 if (gStream) { @@ -39,6 +44,7 @@ void cleanStream() { } int main(int argc, char *argv[]) { + /* #if defined _WIN32 // log in console only if launched from console @@ -48,6 +54,7 @@ int main(int argc, char *argv[]) { } #endif */ + // Useful to share camera on Fullscreen (other context) or multiscreens lDebug() << "[Main] Setting ShareOpenGLContexts"; QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); @@ -63,6 +70,7 @@ int main(int argc, char *argv[]) { setlocale(LC_CTYPE, ".UTF8"); lDebug() << "[Main] Creating application"; auto app = QSharedPointer::create(argc, argv); + #ifdef ACCESSBILITY_WORKAROUND QAccessible::installUpdateHandler(DummyUpdateHandler); QAccessible::installRootObjectHandler(DummyRootObjectHandler); diff --git a/Linphone/model/setting/SettingsModel.cpp b/Linphone/model/setting/SettingsModel.cpp index 168c56356..d20dd8759 100644 --- a/Linphone/model/setting/SettingsModel.cpp +++ b/Linphone/model/setting/SettingsModel.cpp @@ -25,6 +25,9 @@ #include "model/tool/ToolModel.hpp" // #include "model/tool/VfsUtils.hpp" #include "tool/Utils.hpp" +#ifdef HAVE_CRASH_HANDLER +#include "tool/crash_reporter/CrashReporter.hpp" +#endif // ============================================================================= @@ -55,6 +58,7 @@ SettingsModel::SettingsModel() { if (gstate == linphone::GlobalState::On) { // reached when misc|config-uri is set in config and app starts // and after config is fetched. notifyConfigReady(); + } }); QObject::connect(CoreModel::getInstance().get(), &CoreModel::configuringStatus, this, @@ -618,16 +622,32 @@ void SettingsModel::setFullLogsEnabled(bool status) { emit fullLogsEnabledChanged(status); } +bool SettingsModel::getCrashReporterEnabled() const { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return getCrashReporterEnabled(mConfig); +} + +void SettingsModel::setCrashReporterEnabled(bool status) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mConfig->setInt(AppSection, "send_logs_to_crashlytics", status); +#ifdef HAVE_CRASH_HANDLER + CrashReporter::enable(status); +#endif + emit crashReporterEnabledChanged(status); +} + bool SettingsModel::getLogsEnabled(const shared_ptr &config) { - mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? config->getInt(UiSection, "logs_enabled", false) : true; } bool SettingsModel::getFullLogsEnabled(const shared_ptr &config) { - mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? config->getInt(UiSection, "full_logs_enabled", false) : false; } +bool SettingsModel::getCrashReporterEnabled(const shared_ptr &config) { + return config ? config->getInt(AppSection, "send_logs_to_crashlytics", true) : true; +} + QString SettingsModel::getLogsFolder() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return getLogsFolder(mConfig); @@ -686,6 +706,9 @@ QString SettingsModel::getLogsEmail() const { return Utils::coreStringToAppString(mConfig->getString(UiSection, "logs_email", Constants::DefaultLogsEmail)); } + + + // ============================================================================= // Do not disturb // ============================================================================= diff --git a/Linphone/model/setting/SettingsModel.hpp b/Linphone/model/setting/SettingsModel.hpp index 7af530681..3329a70b5 100644 --- a/Linphone/model/setting/SettingsModel.hpp +++ b/Linphone/model/setting/SettingsModel.hpp @@ -143,8 +143,12 @@ public: bool getFullLogsEnabled() const; void setFullLogsEnabled(bool status); + bool getCrashReporterEnabled() const; + void setCrashReporterEnabled(bool status); + static bool getLogsEnabled(const std::shared_ptr &config); static bool getFullLogsEnabled(const std::shared_ptr &config); + static bool getCrashReporterEnabled(const std::shared_ptr &config); QString getLogsFolder() const; void setLogsFolder(const QString &folder); @@ -272,6 +276,7 @@ signals: void logsEnabledChanged(bool status); void fullLogsEnabledChanged(bool status); + void crashReporterEnabledChanged(bool status); void dndChanged(bool value); diff --git a/Linphone/tool/CMakeLists.txt b/Linphone/tool/CMakeLists.txt index aafe1a72b..0235157dc 100644 --- a/Linphone/tool/CMakeLists.txt +++ b/Linphone/tool/CMakeLists.txt @@ -53,6 +53,10 @@ else () ) endif () +if (HAVE_CRASH_HANDLER) + list(APPEND _LINPHONEAPP_SOURCES tool/crash_reporter/CrashReporter.cpp) +endif() + set(_LINPHONEAPP_SOURCES ${_LINPHONEAPP_SOURCES} PARENT_SCOPE) diff --git a/Linphone/tool/Constants.cpp b/Linphone/tool/Constants.cpp index fbcb0e95c..650829783 100644 --- a/Linphone/tool/Constants.cpp +++ b/Linphone/tool/Constants.cpp @@ -60,6 +60,10 @@ constexpr char Constants::PathCodecs[]; constexpr char Constants::PathData[]; constexpr char Constants::PathTools[]; constexpr char Constants::PathLogs[]; +#ifdef HAVE_CRASH_HANDLER +constexpr char Constants::PathCrashpad[]; +constexpr char Constants::PathCrashpadHandler[]; +#endif #ifdef APPLE constexpr char Constants::PathPlugins[]; #else diff --git a/Linphone/tool/Constants.hpp b/Linphone/tool/Constants.hpp index 77ab36ebe..1dbb7eb26 100644 --- a/Linphone/tool/Constants.hpp +++ b/Linphone/tool/Constants.hpp @@ -146,14 +146,21 @@ public: static constexpr char PathTools[] = "/tools/"; static constexpr char PathLogs[] = "/logs/"; static constexpr char PathVCards[] = "/vcards/"; + static constexpr char PathMetrics[] = "/metrics/"; #ifdef APPLE static constexpr char PathPlugins[] = "/Plugins/"; #else static constexpr char PathPlugins[] = "/plugins/"; #endif static constexpr char PathPluginsApp[] = "app/"; + static constexpr char PathBin[] = "/bin/"; static constexpr char PathSounds[] = "/sounds/linphone"; static constexpr char PathUserCertificates[] = "/usr-crt/"; +#ifdef HAVE_CRASH_HANDLER + static constexpr char PathCrashpad[] = "/crashpad/"; + static constexpr char PathCrashpadHandler[] = CRASHPAD_EXECUTABLE_NAME; + static constexpr char BugsplatUrl[] = "https://Linphone.bugsplat.com/post/bp/crash/crashpad.php"; +#endif static constexpr char PathCallHistoryList[] = "/call-history.db"; static constexpr char PathConfig[] = "/linphonerc"; diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index 0e74b556e..9bbdff6d5 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -245,6 +245,12 @@ public: static QUrl getAppIcon(const QString &iconName); static QUrl getRegistrationStateIcon(LinphoneEnums::RegistrationState state); +#ifdef Q_OS_WINDOWS + static inline std::wstring getNativeString(const QString &s){ return s.toStdWString(); } +#else + static std::string getNativeString(const QString &s){ return s.toStdString(); } +#endif + private: DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/tool/crash_reporter/CrashReporter.cpp b/Linphone/tool/crash_reporter/CrashReporter.cpp new file mode 100644 index 000000000..46929efc6 --- /dev/null +++ b/Linphone/tool/crash_reporter/CrashReporter.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010-2026 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "CrashReporter.hpp" +#include "config.h" +#include "tool/Utils.hpp" +#include "tool/Constants.hpp" +#include "core/path/Paths.hpp" +#include "model/setting/SettingsModel.hpp" + + +#include "client/crash_report_database.h" +#include "client/settings.h" +#include + + +CrashReporter* CrashReporter::gHandler = nullptr; + +CrashReporter::CrashReporter(QObject * parent): QObject(parent) { + mHandlerPath = base::FilePath(Utils::getNativeString(Paths::getCrashpadHandlerFilePath())); + mDatabasePath = base::FilePath(Utils::getNativeString(Paths::getCrashpadDirPath())); + mMetricsPath = base::FilePath(Utils::getNativeString(Paths::getMetricsDirPath())); + mBugsplatUrl = Constants::BugsplatUrl; + + mAnnotations["format"] = "minidump"; // Required: Crashpad setting to save crash as a minidump + mAnnotations["database"] = BUGSPLAT_DATABASE; // Required: BugSplat database + mAnnotations["product"] = APPLICATION_NAME; // Required: BugSplat appName + mAnnotations["version"] = APPLICATION_SEMVER; + // annotations["key"] = "Sample key"; // Optional: BugSplat key field + // annotations["user"] = "fred@bugsplat.com"; // Optional: BugSplat user email + // annotations["list_annotations"] = "Sample comment"; // Optional: BugSplat crash description + + // Disable crashpad rate limiting so that all crashes have dmp files + mArguments.push_back("--no-rate-limit"); + auto config = linphone::Factory::get()->createConfig(Utils::appStringToCoreString(Paths::getConfigFilePath("", true))); + + // Attachments to be uploaded alongside the crash - default bundle size limit is 20MB + // Crashpad doesn't manage folders. We have to guess the files to be uploaded. + QString logFiles = Paths::getLogsDirPath()+QDir::separator()+EXECUTABLE_NAME; + mAttachments.push_back(base::FilePath(Utils::getNativeString(logFiles+".log"))); + mAttachments.push_back(base::FilePath(Utils::getNativeString(logFiles + "1.log"))); + mAttachments.push_back(base::FilePath(Utils::getNativeString(logFiles + "2.log"))); +} + +void CrashReporter::start() { + auto config = linphone::Factory::get()->createConfig(Utils::appStringToCoreString(Paths::getConfigFilePath("", true))); + CrashReporter::enable(SettingsModel::getCrashReporterEnabled(config)); +} + +void CrashReporter::run() { + //crashpad::CrashpadClient *client = new crashpad::CrashpadClient(); + bool status = mClient.StartHandler(mHandlerPath, mDatabasePath, mMetricsPath, mBugsplatUrl.toStdString(), mAnnotations.toStdMap(), mArguments, true, true, mAttachments); + + if(!status){ + lWarning() << "[CrashReporter] Failed to start Crashpad handler. Crashes will not be logged."; + } else { + lInfo() << "[CrashReporter] Started Crashpad handler. Database at " << Paths::getCrashpadDirPath(); + } +} + +void CrashReporter::enable(const bool& on) { + if(!gHandler) gHandler = new CrashReporter(); + std::unique_ptr database = crashpad::CrashReportDatabase::Initialize(gHandler->mDatabasePath); + if (database == NULL) return; + crashpad::Settings *settings = database->GetSettings(); + if (settings == NULL) return; + settings->SetUploadsEnabled(on); + if(on) gHandler->run(); + else { + lInfo() << "[CrashReporter] Crashpad has been deactivated by user."; + } +} + diff --git a/Linphone/tool/crash_reporter/CrashReporter.hpp b/Linphone/tool/crash_reporter/CrashReporter.hpp new file mode 100644 index 000000000..8c637a5a2 --- /dev/null +++ b/Linphone/tool/crash_reporter/CrashReporter.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010-2026 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CRASH_REPORTER_H_ +#define CRASH_REPORTER_H_ + +#include +#include +#include +#include + +#include "client/crashpad_client.h" + +// ============================================================================= +class CrashReporter : public QObject{ +public: + CrashReporter(QObject * parent = nullptr); + + static void start(); + static void enable(const bool& on); + void run(); + + crashpad::CrashpadClient mClient; + std::vector mAttachments; + QMap mAnnotations; + std::vector mArguments; + base::FilePath mHandlerPath; + base::FilePath mDatabasePath; + base::FilePath mMetricsPath; + base::FilePath mLogsPath; + QString mBugsplatUrl; + static CrashReporter* gHandler; +}; + +#endif \ No newline at end of file diff --git a/Linphone/tool/thread/Thread.cpp b/Linphone/tool/thread/Thread.cpp index 3319c29bd..96a13bdc7 100644 --- a/Linphone/tool/thread/Thread.cpp +++ b/Linphone/tool/thread/Thread.cpp @@ -48,6 +48,7 @@ bool Thread::isInLinphoneThread() { } bool Thread::mustBeInLinphoneThread(const QString &context) { + if(!App::getInstance()) return true; bool isLinphoneThread = isInLinphoneThread(); if (!isLinphoneThread) { // Bracket to easier debugging. lCritical() << "[Thread] Not processing in Linphone thread from " << context; diff --git a/README.md b/README.md index 29f23f704..296e930b9 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,11 @@ For Desktop : you will need QT6 (_6.10.0 or newer_). `C++17` support is required The following QT optional modules are required: qtmultimedia qtnetworkauth qtshadertools . +If you wan't to build with Crash handler, you need to install the following tools: +- clang +- curl dev (for C development headers) (ex: `sudo apt install libcurl4-openssl-dev`) +- httplib2 v0.22.0 (use `pip install httplib2==0.22.0` or `pip3 install httplib2==0.22.0`) + ### Set your environment 1. Make sure you have all your build dependencies installed @@ -295,6 +300,7 @@ Also, more configurations are available in the docker-files folder of linphone-s | ENABLE_UNIT_TESTS | Enable unit test of SDK. | NO | | ENABLE_OPENLDAP | Build openLDAP external library. | YES | | ENABLE_UPDATE_CHECK | Enable update check. | YES | +| ENABLE_CRASH_HANDLER | Enable crash handler. | YES | | LINPHONE_SDK_MAKE_RELEASE_FILE_URL | Make a RELEASE file that work along check_version and use this URL | "" | diff --git a/cmake/Modules/FindCrashpad.cmake b/cmake/Modules/FindCrashpad.cmake new file mode 100644 index 000000000..96356ac95 --- /dev/null +++ b/cmake/Modules/FindCrashpad.cmake @@ -0,0 +1,154 @@ +############################################################################ +# Copyright (c) 2025 Belledonne Communications SARL. +# +# This file is part of Linphone. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################ +# This module will set the following variables in your project: +# +# Crashpad_FOUND - The crashpad library has been found +# Crashpad::Crashpad - Imported target to build against +# CRASHPAD_BIN_DIR - Directory where the crashpad_handler executable is found +# CRASHPAD_EXECUTABLE_NAME - Crash handler executable +# CRASHPAD_INCLUDE_DIR - Headers folder of Crashpad. + +if(TARGET Crashpad) + get_target_property(CRASHPAD_BIN_DIR Crashpad CRASHPAD_BIN_DIR) + get_target_property(CRASHPAD_BUILD_DIR Crashpad CRASHPAD_BUILD_DIR) + get_target_property(CRASHPAD_EXECUTABLE_NAME Crashpad CRASHPAD_EXECUTABLE_NAME) + get_target_property(CRASHPAD_GEN_DIR Crashpad CRASHPAD_GEN_DIR) + get_target_property(CRASHPAD_INCLUDE_DIR Crashpad CRASHPAD_INCLUDE_DIR) + get_target_property(CRASHPAD_LIB_DIR Crashpad CRASHPAD_LIB_DIR) + get_target_property(CRASHPAD_SOURCE_DIR Crashpad CRASHPAD_SOURCE_DIR) +endif() + +if(NOT CRASHPAD_BUILD_DIR) + set(CRASHPAD_BUILD_DIR ${CMAKE_BINARY_DIR}/external/google/crashpad/out) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CRASHPAD_BUILD_DIR ${CRASHPAD_BUILD_DIR}/debug) + else() + set(CRASHPAD_BUILD_DIR ${CRASHPAD_BUILD_DIR}/release) + endif() +endif() +if(NOT CRASHPAD_SOURCE_DIR) + set(CRASHPAD_SOURCE_DIR ${CMAKE_SOURCE_DIR}/external/google/crashpad) +endif() + +if(NOT CRASHPAD_EXECUTABLE_NAME OR NOT CRASHPAD_BIN_DIR) + find_program(CRASHPAD_EXECUTABLE + NAMES crashpad_handler.exe crashpad_handler + HINTS + "${CRASHPAD_BUILD_DIR}" + "${CMAKE_PREFIX_PATH}" + ) + get_filename_component(CRASHPAD_BIN_DIR ${CRASHPAD_EXECUTABLE} DIRECTORY) + get_filename_component(CRASHPAD_EXECUTABLE_NAME ${CRASHPAD_EXECUTABLE} NAME) +endif() + + + +if(NOT CRASHPAD_INCLUDE_DIR) + find_path(CRASHPAD_INCLUDE_DIR + NAMES client/crashpad_client.h + HINTS + "${CRASHPAD_SOURCE_DIR}" + "${CMAKE_PREFIX_PATH}" + ) +endif() + +if(NOT CRASHPAD_LIB_DIR) + find_path(CRASHPAD_LIB_DIR + NAMES util/libutil.a util/util.lib + HINTS + "${CRASHPAD_BIN_DIR}/obj" + "${CMAKE_PREFIX_PATH}" + ) +endif() + +if(NOT CRASHPAD_GEN_DIR) + find_path(CRASHPAD_GEN_DIR + NAMES build/chromeos_buildflags.h + PATH_SUFFIXES gen + HINTS + "${CRASHPAD_BIN_DIR}" + "${CMAKE_PREFIX_PATH}" + ) +endif() + +if(APPLE) + find_path(CRASHPAD_OBJ_DIR + NAMES mig_output.child_portServer.o + PATH_SUFFIXES gen/util/mach + HINTS + "${CRASHPAD_OBJECT_DIR}" + "${CRASHPAD_LIB_DIR}" + "${CMAKE_PREFIX_PATH}" + ) + set(CRASHPAD_APPLE_VARS CRASHPAD_OBJ_DIR CRASHPAD_GEN_DIR) + find_library(FWbsm bsm) + find_library(FWAppKit AppKit) + find_library(FWIOKit IOKit) + find_library(FWSecurity Security) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Crashpad DEFAULT_MSG + CRASHPAD_BIN_DIR CRASHPAD_INCLUDE_DIR CRASHPAD_LIB_DIR CRASHPAD_EXECUTABLE_NAME ${CRASHPAD_APPLE_VARS} +) + +#[[ + +if(Crashpad_FOUND) + add_library(Crashpad::Crashpad UNKNOWN IMPORTED) + target_include_directories(Crashpad::Crashpad INTERFACE + "${CRASHPAD_INCLUDE_DIR}" + "${CRASHPAD_INCLUDE_DIR}/third_party/mini_chromium/mini_chromium" + "${CRASHPAD_GEN_DIR}") + if(WIN32) + target_link_libraries(Crashpad::Crashpad INTERFACE + "${CRASHPAD_LIB_DIR}/third_party/mini_chromium/mini_chromium/base/base.lib" + "${CRASHPAD_LIB_DIR}/util/util.lib" + "${CRASHPAD_LIB_DIR}/client/client.lib" + "${CRASHPAD_LIB_DIR}/client/common.lib" + advapi32) + set_target_properties(Crashpad::Crashpad PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_LIB_DIR}/client/client.lib") + elseif(APPLE) + target_link_libraries(Crashpad::Crashpad INTERFACE + "${CRASHPAD_LIB_DIR}/third_party/mini_chromium/mini_chromium/base/libbase.a" + "${CRASHPAD_LIB_DIR}/util/libutil.a" + "${CRASHPAD_LIB_DIR}/client/libclient.a" + "${CRASHPAD_OBJ_DIR}/mig_output.child_portServer.o" + "${CRASHPAD_OBJ_DIR}/mig_output.child_portUser.o" + "${CRASHPAD_OBJ_DIR}/mig_output.excServer.o" + "${CRASHPAD_OBJ_DIR}/mig_output.excUser.o" + "${CRASHPAD_OBJ_DIR}/mig_output.mach_excServer.o" + "${CRASHPAD_OBJ_DIR}/mig_output.mach_excUser.o" + "${CRASHPAD_OBJ_DIR}/mig_output.notifyServer.o" + "${CRASHPAD_OBJ_DIR}/mig_output.notifyUser.o" + ${FWbsm} ${FWAppKit} ${FWIOKit} ${FWSecurity}) + set_target_properties(Crashpad::Crashpad PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_LIB_DIR}/client/libclient.a") + elseif(UNIX) + target_link_libraries(Crashpad::Crashpad INTERFACE + "${CRASHPAD_LIB_DIR}/third_party/mini_chromium/mini_chromium/base/libbase.a" + "${CRASHPAD_LIB_DIR}/util/libutil.a" + "${CRASHPAD_LIB_DIR}/client/libclient.a") + set_target_properties(Crashpad::Crashpad PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_LIB_DIR}/client/libclient.a") + endif() +endif() +]]# \ No newline at end of file diff --git a/cmake/install/cleanCPack.cmake.in b/cmake/install/cleanCPack.cmake.in index e9db59aa7..24d35ab01 100644 --- a/cmake/install/cleanCPack.cmake.in +++ b/cmake/install/cleanCPack.cmake.in @@ -25,10 +25,20 @@ # It is neccessary to use it because CPack doesn't take account of some install() (those that do the move) cmake_policy(SET CMP0009 NEW)#Not following symlinks for file() +IF(POLICY CMP0012) + CMAKE_POLICY(SET CMP0012 NEW)#Allow if(ON) +ENDIF() set(DO_SIGNING @LINPHONE_BUILDER_SIGNING_IDENTITY@) set(DEPLOYQT_PROGRAM @DEPLOYQT_PROGRAM@) +function(remove_file expression) + file(GLOB_RECURSE UNWANTED_FILES ${expression}) + foreach(F ${UNWANTED_FILES}) + file(REMOVE ${F}) + endforeach() +endfunction() + if(APPLE) find_program(DSYMUTIL_PROGRAM dsymutil) @@ -61,8 +71,36 @@ if(APPLE) execute_process(COMMAND codesign --force --deep --sign "-" "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@APPLICATION_NAME@.app" )#If not code signed, app can crash because of APPLE on "Code Signature Invalid" (spotted for ARM64) endif() elseif(WIN32) - message(STATUS "Execute : @DEPLOYQT_PROGRAM@ ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@.exe --qmldir=@LINPHONE_QML_DIR@ --verbose=0 --no-compiler-runtime") - execute_process(COMMAND @DEPLOYQT_PROGRAM@ "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@.exe" "--qmldir=@LINPHONE_QML_DIR@" "--verbose=0" "--no-compiler-runtime") + message(STATUS "Execute : @DEPLOYQT_PROGRAM@ ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@.exe --qmldir=@LINPHONE_QML_DIR@ --verbose=0 --no-compiler-runtime --pdb") + execute_process(COMMAND @DEPLOYQT_PROGRAM@ "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@.exe" "--qmldir=@LINPHONE_QML_DIR@" "--verbose=0" "--no-compiler-runtime" "--pdb") # --plugindir "${CMAKE_CURRENT_BINARY_DIR}/winqt/plugins" # --dir "${CMAKE_CURRENT_BINARY_DIR}/winqt/" + if(@ENABLE_BUGSPLAT_SYMBOLS_UPLOAD@) + message(STATUS "Uploading Bugsplat symbols from ${CPACK_TEMPORARY_INSTALL_DIRECTORY}") + execute_process(COMMAND "@CRASHPAD_SYMBOLS_UPLOADER@" -m -b @BUGSPLAT_DATABASE@ -a "@LINPHONEAPP_APPLICATION_NAME@" -v "@LINPHONEAPP_VERSION@" -f "**/*.{pdb,exe,dll}" -d "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_BINDIR@" --clientId "@BUGSPPLAT_CLIENT_ID@" --clientSecret "@BUGSPPLAT_CLIENT_SECRET@" + WORKING_DIRECTORY ${CPACK_TEMPORARY_INSTALL_DIRECTORY} + RESULT_VARIABLE CPACK_COMMAND_RESULT + COMMAND_ECHO NONE + ) + if(CPACK_COMMAND_RESULT) + message(FATAL_ERROR "Failed to upload symbols! ${CPACK_COMMAND_RESULT}") + endif() + execute_process(COMMAND "@CRASHPAD_SYMBOLS_UPLOADER@" -m -b @BUGSPLAT_DATABASE@ -a "@LINPHONEAPP_APPLICATION_NAME@" -v "@LINPHONEAPP_VERSION@" -f "**/*.{pdb,exe,dll}" -d "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_INSTALL_LIBDIR@/mediastreamer/plugins" --clientId "@BUGSPPLAT_CLIENT_ID@" --clientSecret "@BUGSPPLAT_CLIENT_SECRET@" + WORKING_DIRECTORY ${CPACK_TEMPORARY_INSTALL_DIRECTORY} + RESULT_VARIABLE CPACK_COMMAND_RESULT + COMMAND_ECHO NONE + ) + if(CPACK_COMMAND_RESULT) + message(FATAL_ERROR "Failed to upload symbols! ${CPACK_COMMAND_RESULT}") + endif() + endif() + ##################################################### + # Clean all unwanted files to package. + ##################################################### + # TODO: remove all pdb when CrashReporter is available. Symbols for crashing are in server so it is not needed for deployment. + # if debugging is needed, we could rebuild it and use pdb from local build instead of using official binaries. + #remove_file("${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*/*.pdb") + remove_file("${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*/Qt*.pdb") + remove_file("${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*/*.cmake") + remove_file("${CPACK_TEMPORARY_INSTALL_DIRECTORY}/*/*.lib") endif() diff --git a/cmake/install/create_appimage.sh b/cmake/install/create_appimage.sh index fe7837d2c..d08321731 100755 --- a/cmake/install/create_appimage.sh +++ b/cmake/install/create_appimage.sh @@ -126,6 +126,8 @@ else echo "-- Code Signing of AppImage" # APPIMAGETOOL_SIGN_PASSPHRASE has to the parent environment (not here). Do not use export. ./${WORK_DIR}/AppBin/appimagetool-x86_64.AppImage --appimage-extract-and-run ${WORK_DIR}/AppDir --sign --sign-key $4 + + fi echo "Move Appimages into ${BIN_SOURCE_DIR}/Packages/$2.AppImage" diff --git a/cmake/install/install.cmake b/cmake/install/install.cmake index f10b6b9dd..83fe0e18e 100644 --- a/cmake/install/install.cmake +++ b/cmake/install/install.cmake @@ -23,20 +23,18 @@ cmake_minimum_required(VERSION 3.5) set(TARGET_NAME Linphone) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules") -if (GIT_EXECUTABLE AND NOT(LINPHONEAPP_VERSION)) - execute_process( - COMMAND ${GIT_EXECUTABLE} describe --always - OUTPUT_VARIABLE LINPHONEAPP_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - ) +if(NOT LINPHONEAPP_VERSION) + bc_compute_full_version(LINPHONEAPP_VERSION) endif() if (NOT(LINPHONEAPP_VERSION)) set(LINPHONEAPP_VERSION "6.1.0") endif () +include(${CMAKE_SOURCE_DIR}/Linphone/application_info.cmake) + set(LINPHONE_MAJOR_VERSION) set(LINPHONE_MINOR_VERSION) set(LINPHONE_MICRO_VERSION) @@ -161,6 +159,39 @@ function(deployqt_hack target) endif() endfunction() + + +################################################################ +# CRASHPAD +################################################################ +if(HAVE_CRASH_HANDLER) + get_target_property(CRASHPAD_BIN_DIR Crashpad CRASHPAD_BIN_DIR) + get_target_property(CRASHPAD_EXECUTABLE_NAME Crashpad CRASHPAD_EXECUTABLE_NAME) + install(FILES "${CRASHPAD_BIN_DIR}/${CRASHPAD_EXECUTABLE_NAME}" DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + if(ENABLE_BUGSPLAT_SYMBOLS_UPLOAD) + if(NOT LINPHONEAPP_APPLICATION_NAME) + message(FATAL_ERROR "Missing application details for Bugsplat. Please fill LINPHONEAPP_APPLICATION_NAME (${LINPHONEAPP_APPLICATION_NAME} - ${LINPHONEAPP_VERSION})") + elseif(NOT BUGSPLAT_DATABASE) + message(FATAL_ERROR "Missing database application details for Bugsplat. Please fill BUGSPLAT_DATABASE in application_info.cmake") + elseif(NOT BUGSPPLAT_CLIENT_ID OR NOT BUGSPPLAT_CLIENT_SECRET) + message(FATAL_ERROR "Missing identifications for Bugsplat. Please fill BUGSPPLAT_CLIENT_ID and BUGSPPLAT_CLIENT_SECRET to activate symbol uploading") + else() + message(STATUS "Preparing symbol uploader Bugsplat for ${LINPHONEAPP_APPLICATION_NAME} - ${LINPHONEAPP_VERSION}.") + if(WIN32) + set(CRASHPAD_SYMBOLS_UPLOADER "${CMAKE_BINARY_DIR}/symbol-upload-windows.exe") + set(CRASHPAD_SYMBOLS_UPLOADER_URL "https://github.com/BugSplat-Git/symbol-upload/releases/download/v10.2.4/symbol-upload-windows.exe") + else() + set(CRASHPAD_SYMBOLS_UPLOADER "${CMAKE_BINARY_DIR}/symbol-upload-linux") + set(CRASHPAD_SYMBOLS_UPLOADER_URL "https://github.com/BugSplat-Git/symbol-upload/releases/download/v10.2.4/symbol-upload-linux") + endif() + if(NOT EXISTS "${CRASHPAD_SYMBOLS_UPLOADER}") + file(DOWNLOAD ${CRASHPAD_SYMBOLS_UPLOADER_URL} ${CRASHPAD_SYMBOLS_UPLOADER}) + file(CHMOD "${CRASHPAD_SYMBOLS_UPLOADER}" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + endif() + endif() + endif() +endif() + # ============================================================================== # CPack. # ============================================================================== @@ -214,13 +245,17 @@ if(${ENABLE_APP_PACKAGING}) set(DO_GENERATOR YES) set(PACKAGE_EXT "exe") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${PACKAGE_VERSION}-${BIN_ARCH}") + set(CPACK_SOURCE_IGNORE_FILES "*Qt*.pdb;*.lib;*.cmake") #Doesn't seem to work but we let the option just in case. find_program(NSIS_PROGRAM makensis) if(NOT NSIS_PROGRAM) - - message(STATUS "Installing windows tools for nsis") - execute_process( - COMMAND "${MSYS2_PROGRAM}" "-${MINGW_TYPE}" "-here" "-full-path" "-defterm" "-shell" "sh" "-l" "-c" "pacman -Sy ${MINGW_PACKAGE_PREFIX}nsis --noconfirm --needed" - ) + if(ENABLE_WINDOWS_TOOLS_CHECK) + message(STATUS "Installing windows tools for nsis") + execute_process( + COMMAND "${MSYS2_PROGRAM}" "-${MINGW_TYPE}" "-here" "-full-path" "-defterm" "-shell" "sh" "-l" "-c" "pacman -Sy ${MINGW_PACKAGE_PREFIX}nsis --noconfirm --needed" + ) + else() + message(FATAL_ERROR "`makensis` is needed for packaging. Please use pacman -Sy ${MINGW_PACKAGE_PREFIX}nsis to install it.") + endif() endif() set(PACKAGE_EXT "exe") # Use magic `NSIS.template.in` template from the current source directory to force uninstallation @@ -307,4 +342,3 @@ if(${ENABLE_APP_PACKAGING}) endif() include(CPack) endif() - diff --git a/cmake/install/packaging.cmake.in b/cmake/install/packaging.cmake.in index 3e231ff88..52d158203 100644 --- a/cmake/install/packaging.cmake.in +++ b/cmake/install/packaging.cmake.in @@ -57,7 +57,7 @@ if (NOT "${CMAKE_INSTALL_PREFIX}" MATCHES .*/_CPack_Packages/.*) if(DO_GENERATOR) execute_process( COMMAND ${CMAKE_CPACK_COMMAND} -G @CPACK_GENERATOR@ -C @CMAKE_BUILD_TYPE@ RESULT_VARIABLE CPACK_COMMAND_RESULT) if(CPACK_COMMAND_RESULT) - message(FATAL_ERROR "Failed to create @CPACK_GENERATOR@ package!") + message(FATAL_ERROR "Failed to create @CPACK_GENERATOR@ package! ${CPACK_COMMAND_RESULT}") endif() endif() if(DO_APPIMAGE) @@ -69,6 +69,23 @@ if (NOT "${CMAKE_INSTALL_PREFIX}" MATCHES .*/_CPack_Packages/.*) execute_process( COMMAND "@CMAKE_SOURCE_DIR@/cmake/install/create_appimage.sh" @EXECUTABLE_NAME@ "@CPACK_PACKAGE_FILE_NAME@${ARCH}" @QT_PATH@ @LINPHONE_BUILDER_SIGNING_IDENTITY@ RESULT_VARIABLE CPACK_COMMAND_RESULT WORKING_DIRECTORY "@CMAKE_INSTALL_PREFIX@/.." ) if(CPACK_COMMAND_RESULT) message(FATAL_ERROR "Failed to create AppImage package with this command : '@CMAKE_CURRENT_SOURCE_DIR@/../../tools/create_appimage.sh @EXECUTABLE_NAME@ @QT_PATH@ @LINPHONEAPP_VERSION@' at @CMAKE_INSTALL_PREFIX@/..\nMaybe the .appimage already exists and is running. Please remove the file before packaging if it is the case.") + elseif(@ENABLE_BUGSPLAT_SYMBOLS_UPLOAD@) + execute_process(COMMAND "@CRASHPAD_SYMBOLS_UPLOADER@" -m -b @BUGSPLAT_DATABASE@ -a "@LINPHONEAPP_APPLICATION_NAME@" -v "@LINPHONEAPP_VERSION@" -f "@EXECUTABLE_NAME@" --clientId "@BUGSPPLAT_CLIENT_ID@" --clientSecret "@BUGSPPLAT_CLIENT_SECRET@" + WORKING_DIRECTORY @CMAKE_INSTALL_PREFIX@/../WORK/Packages/AppImageDir/AppDir/usr/bin/ + RESULT_VARIABLE CPACK_COMMAND_RESULT + COMMAND_ECHO NONE + ) + if(CPACK_COMMAND_RESULT) + message(FATAL_ERROR "Failed to upload symbols: '@CMAKE_CURRENT_SOURCE_DIR@/../../tools/create_appimage.sh @EXECUTABLE_NAME@ @QT_PATH@ @LINPHONEAPP_VERSION@' at @CMAKE_INSTALL_PREFIX@/..\nMaybe the .appimage already exists and is running. Please remove the file before packaging if it is the case.") + endif() + execute_process(COMMAND "@CRASHPAD_SYMBOLS_UPLOADER@" -m -b @BUGSPLAT_DATABASE@ -a "@LINPHONEAPP_APPLICATION_NAME@" -v "@LINPHONEAPP_VERSION@" -f "**/*.so{,.*}" --clientId "@BUGSPPLAT_CLIENT_ID@" --clientSecret "@BUGSPPLAT_CLIENT_SECRET@" + WORKING_DIRECTORY @CMAKE_INSTALL_PREFIX@/../WORK/Packages/AppImageDir/AppDir/usr/ + RESULT_VARIABLE CPACK_COMMAND_RESULT + COMMAND_ECHO NONE + ) + if(CPACK_COMMAND_RESULT) + message(FATAL_ERROR "Failed to upload symbols: '@CMAKE_CURRENT_SOURCE_DIR@/../../tools/create_appimage.sh @EXECUTABLE_NAME@ @QT_PATH@ @LINPHONEAPP_VERSION@' at @CMAKE_INSTALL_PREFIX@/..\nMaybe the .appimage already exists and is running. Please remove the file before packaging if it is the case.") + endif() endif() endif() if (@PERFORM_SIGNING@) diff --git a/docker-files/bc-dev-ubuntu-22-04-lts b/docker-files/bc-dev-ubuntu-22-04-lts index 88b2d6ec8..5c9018cb5 100644 --- a/docker-files/bc-dev-ubuntu-22-04-lts +++ b/docker-files/bc-dev-ubuntu-22-04-lts @@ -59,6 +59,10 @@ RUN sudo apt-get install -y gnupg2 # Install configuration tools RUN sudo apt-get install -y wget +# Install crashpad build tools +RUN sudo apt-get install -y libcurl4-openssl-dev +RUN sudo pip3 install httplib2==0.22.0 + # Configure user bc RUN useradd -ms /bin/bash bc && \ echo 'bc:cotcot' | chpasswd && \ diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 0ef245c8a..c52c8bcf3 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -3,4 +3,5 @@ add_subdirectory(linphone-sdk/) if(ENABLE_QT_KEYCHAIN) find_package(Qt6 REQUIRED COMPONENTS Test) add_subdirectory(qtkeychain/) -endif() \ No newline at end of file +endif() +add_subdirectory(google) \ No newline at end of file diff --git a/external/google/.gclient b/external/google/.gclient new file mode 100644 index 000000000..67bbc285f --- /dev/null +++ b/external/google/.gclient @@ -0,0 +1 @@ +solutions = [{"name": "crashpad", "url": "https://gitlab.linphone.org/BC/public/external/crashpad", "managed": False}] diff --git a/external/google/CMakeLists.txt b/external/google/CMakeLists.txt new file mode 100644 index 000000000..b40886faf --- /dev/null +++ b/external/google/CMakeLists.txt @@ -0,0 +1,116 @@ +set(DEPOT_TOOLS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/chromium-depot-tools") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CRASHPAD_BIN_DIR "out/debug") +else() + set(CRASHPAD_BIN_DIR "out/release") +endif() +set(CRASHPAD_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/crashpad/${CRASHPAD_BIN_DIR}) + +if(WIN32) + set(GCLIENT_SCRIPT "${DEPOT_TOOLS_DIR}/gclient.bat") + set(GN_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/bc_gn.bat") + set(GN_COMMAND ${GN_SCRIPT} ${CRASHPAD_BUILD_DIR} ${CMAKE_BUILD_TYPE}) +else() + set(GCLIENT_SCRIPT "${DEPOT_TOOLS_DIR}/gclient") + set(GN_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/bc_gn.sh") + set(GN_COMMAND ${GN_SCRIPT} ${CRASHPAD_BUILD_DIR} ${CMAKE_BUILD_TYPE}) +endif() +message(STATUS "gn used ${GN_SCRIPT}") + +if (ENABLE_CRASH_HANDLER AND WIN32) + ########################################## + #Hack utils/Build.gn by adding cflags = ["-Wno-nontrivial-memcall"] + #because newer version of clang cannot build with this warning and it is not fixed by crashpad + message(STATUS "Syncing with gclient") + execute_process( + COMMAND "${GCLIENT_SCRIPT}" sync --nohooks --shallow + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE gclient_sync_result + OUTPUT_VARIABLE gclient_sync_output + ERROR_VARIABLE gclient_sync_error + COMMAND_ECHO STDOUT + ) + if(gclient_sync_result) + message(FATAL_ERROR "Failed syncing with gclient: ${gclient_sync_result} ${gclient_sync_output} ${gclient_sync_error}") + endif() + + message(STATUS "Generating build files for crashpad") + #We use GN_COMMAND because CMAKE mess up with quotes, spaces and arguments on Windows + execute_process( + COMMAND ${GN_COMMAND} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/crashpad" + RESULT_VARIABLE crashpad_gn_result + OUTPUT_VARIABLE crashpad_gn_output + ERROR_VARIABLE crashpad_gn_error + COMMAND_ECHO STDOUT + ) + if(crashpad_gn_result) + message(FATAL_ERROR "Failed generate crashpad build files: ${crashpad_gn_result} ${crashpad_gn_output} ${crashpad_gn_error}") + endif() + message(STATUS "Crashpad build files generated") + set(TARGET_NAME Crashpad) + +#NOTE: Order is important + if(WIN32) + set(CRASHPAD_OUTPUTS + "${CRASHPAD_BUILD_DIR}/obj/client/common.lib" + "${CRASHPAD_BUILD_DIR}/obj/client/client.lib" + "${CRASHPAD_BUILD_DIR}/obj/util/util.lib" + "${CRASHPAD_BUILD_DIR}/obj/third_party/mini_chromium/mini_chromium/base/base.lib" + ) + elseif(APPLE) + set(CRASHPAD_OUTPUTS + "${CRASHPAD_BUILD_DIR}/obj/client/libcommon.a" + "${CRASHPAD_BUILD_DIR}/obj/client/libclient.a" + "${CRASHPAD_BUILD_DIR}/obj/util/libutil.a" + "${CRASHPAD_BUILD_DIR}/obj/util/libmig_output.a" + "${CRASHPAD_BUILD_DIR}/obj/third_party/mini_chromium/mini_chromium/base/libbase.a" + ) + else() + set(CRASHPAD_OUTPUTS + "${CRASHPAD_BUILD_DIR}/obj/client/libcommon.a" + "${CRASHPAD_BUILD_DIR}/obj/client/libclient.a" + "${CRASHPAD_BUILD_DIR}/obj/util/libutil.a" + "${CRASHPAD_BUILD_DIR}/obj/third_party/mini_chromium/mini_chromium/base/libbase.a" + ) + endif() + + add_custom_command( + OUTPUT ${CRASHPAD_OUTPUTS} + COMMAND ninja -C . + WORKING_DIRECTORY "${CRASHPAD_BUILD_DIR}" + COMMENT "Crashpad build files generated" + ) + + add_custom_target(Crashpad_build DEPENDS ${CRASHPAD_OUTPUTS}) + + add_library(${TARGET_NAME} STATIC IMPORTED GLOBAL) + add_dependencies(${TARGET_NAME} Crashpad_build) + + target_include_directories(${TARGET_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/crashpad" + "${CMAKE_CURRENT_SOURCE_DIR}/crashpad/third_party/mini_chromium/mini_chromium" + "${CRASHPAD_BUILD_DIR}/gen") + target_link_libraries(${TARGET_NAME} INTERFACE ${CRASHPAD_OUTPUTS}) + set_target_properties(${TARGET_NAME} PROPERTIES CRASHPAD_EXECUTABLE_NAME "crashpad_handler${CMAKE_EXECUTABLE_SUFFIX}") + if(WIN32) + target_link_libraries(${TARGET_NAME} INTERFACE advapi32) + set_target_properties(${TARGET_NAME} PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_BUILD_DIR}/obj/client/client.lib") + else() + set_target_properties(${TARGET_NAME} PROPERTIES + IMPORTED_LOCATION "${CRASHPAD_BUILD_DIR}/obj/client/libclient.a" + ) + endif() + set_target_properties(${TARGET_NAME} PROPERTIES + CRASHPAD_BIN_DIR "${CRASHPAD_BUILD_DIR}" + CRASHPAD_BUILD_DIR "${CRASHPAD_BUILD_DIR}" + CRASHPAD_GEN_DIR "${CRASHPAD_BUILD_DIR}/gen" + CRASHPAD_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/crashpad" + CRASHPAD_LIB_DIR "${CRASHPAD_BUILD_DIR}/obj" + CRASHPAD_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/crashpad" + ) + + set(HAVE_CRASH_HANDLER 1) + +endif() \ No newline at end of file diff --git a/external/google/bc_gn.bat b/external/google/bc_gn.bat new file mode 100644 index 000000000..16bfbe4fb --- /dev/null +++ b/external/google/bc_gn.bat @@ -0,0 +1,6 @@ + +if /i "%~2"=="Debug" ( + .\..\chromium-depot-tools\gn.bat gen %1 --args="extra_cflags=\"/MDd -Wno-nontrivial-memcall\"" +) else ( + .\..\chromium-depot-tools\gn.bat gen %1 --args="extra_cflags=\"/MD -Wno-nontrivial-memcall\"" +) \ No newline at end of file diff --git a/external/google/bc_gn.sh b/external/google/bc_gn.sh new file mode 100755 index 000000000..d1ec96228 --- /dev/null +++ b/external/google/bc_gn.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./../chromium-depot-tools/gn gen $1 --args="extra_cflags=\"-Wno-nontrivial-memcall\"" diff --git a/external/google/chromium-depot-tools b/external/google/chromium-depot-tools new file mode 160000 index 000000000..db99cc40f --- /dev/null +++ b/external/google/chromium-depot-tools @@ -0,0 +1 @@ +Subproject commit db99cc40f562179c59f12b10e8af2d8fe2770ad2 diff --git a/external/google/crashpad b/external/google/crashpad new file mode 160000 index 000000000..4effb3358 --- /dev/null +++ b/external/google/crashpad @@ -0,0 +1 @@ +Subproject commit 4effb3358160e5c9d699d08c81b9410edd7dedfd diff --git a/external/google/gn b/external/google/gn new file mode 160000 index 000000000..6e0b557db --- /dev/null +++ b/external/google/gn @@ -0,0 +1 @@ +Subproject commit 6e0b557db44b3c164094e57687d20ba036a80667