From cd42adc832284acfe485656fbe7bc70f960bbf34 Mon Sep 17 00:00:00 2001 From: karliss Date: Wed, 17 Jun 2020 12:35:26 +0300 Subject: [PATCH] Use cmake for linux DEPLOY build. (#2223) * Add cmake script for building and installing translations. * Improve APPIMAGE related path handling. --- .gitignore | 1 + .travis.yml | 60 ++++++++++++++-------- docs/source/building.rst | 4 +- src/CMakeLists.txt | 7 +++ src/Cutter.pro | 6 ++- src/CutterApplication.cpp | 3 +- src/cmake/CutterInstallDirs.cmake | 7 ++- src/cmake/Translations.cmake | 28 ++++++++++ src/common/Configuration.cpp | 19 +------ src/common/Configuration.h | 6 --- src/common/ResourcePaths.cpp | 85 +++++++++++++++++++++++++++++++ src/common/ResourcePaths.h | 29 +++++++++++ 12 files changed, 205 insertions(+), 50 deletions(-) create mode 100644 src/cmake/Translations.cmake create mode 100644 src/common/ResourcePaths.cpp create mode 100644 src/common/ResourcePaths.h diff --git a/.gitignore b/.gitignore index 363b1cf9..b5fb7728 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ moc_*.h ui_*.h build*/ cmake-build-*/ +/src-build/ # QtCreator *.autosave diff --git a/.travis.yml b/.travis.yml index e6604f9f..a35717bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,10 @@ branches: matrix: include: - - name: Linux QMake + Deploy + - name: Linux QMake os: linux dist: xenial - env: BUILD_SYSTEM=qmake DEPLOY=1 + env: BUILD_SYSTEM=qmake addons: apt: packages: @@ -23,24 +23,34 @@ matrix: - libgl1-mesa-dev - libxkbcommon-x11-dev - ninja-build/xenial-backports - before_install: - - pyenv global 3.7 - - pip install meson - name: Linux CMake os: linux - env: BUILD_SYSTEM=cmake + env: BUILD_SYSTEM=cmake_nodep addons: apt: packages: - mesa-common-dev - ninja-build - libgraphviz-dev - before_install: - - pyenv global 3.7 - - pip install meson + - qt5-default + - libqt5svg5-dev + - qttools5-dev - - name: Linux Ubuntu 16.04 + - name: Linux CMake + Deploy + os: linux + env: BUILD_SYSTEM=cmake DEPLOY=1 + dist: xenial + addons: + apt: + packages: + - mesa-common-dev + - libgl1-mesa-glx + - libgl1-mesa-dev + - libxkbcommon-x11-dev + - ninja-build/xenial-backports + + - name: Linux Ubuntu 16.04 # test that Cutter can be built on an old distro using system libraries os: linux env: BUILD_SYSTEM=cmake_nodep PATH="/usr/bin:$PATH" dist: xenial @@ -51,6 +61,8 @@ matrix: - libgraphviz-dev - qt5-default - libqt5svg5-dev + - qttools5-dev + - qttools5-dev-tools - cmake before_install: - pyenv install 3.5.2 @@ -62,11 +74,13 @@ matrix: os: osx osx_image: xcode10.3 env: BUILD_SYSTEM=qmake DEPLOY=1 + before_install: ~ - name: macOS CMake os: osx osx_image: xcode10.3 env: BUILD_SYSTEM=cmake + before_install: ~ - name: Documentation + Deploy os: linux @@ -112,6 +126,10 @@ addons: brewfile: scripts/Brewfile update: true +before_install: + - pyenv global 3.7 + - pip install meson + install: - if [[ "$BUILD_SYSTEM" != "cmake_nodep" ]]; then scripts/fetch_deps.sh; @@ -128,7 +146,7 @@ install: before_script: - cd radare2 - - if [[ "$BUILD_SYSTEM" == "qmake" ]]; then + - if [[ "$BUILD_SYSTEM" == "qmake" ]] || ([[ "$BUILD_SYSTEM" == "cmake" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]) ; then if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then INSTALL_TARGET=install sys/install.sh /usr; else @@ -148,19 +166,21 @@ script: CUTTER_ENABLE_PYTHON_BINDINGS=true CUTTER_ENABLE_CRASH_REPORTS=true PREFIX=/usr - APPIMAGE=1 ../src && make -j4; elif [[ "$BUILD_SYSTEM" == "cmake" ]]; then cmake -DCMAKE_BUILD_TYPE=Release + -DCMAKE_PREFIX_PATH="$Qt5_ROOT" -DPYTHON_LIBRARY="$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.6m.so.1.0" -DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.6m" -DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" -DCUTTER_ENABLE_PYTHON=ON -DCUTTER_ENABLE_PYTHON_BINDINGS=ON -DCUTTER_ENABLE_CRASH_REPORTS=ON - -DCUTTER_USE_BUNDLED_RADARE2=ON + -DCUTTER_USE_BUNDLED_RADARE2=OFF + -DCUTTER_APPIMAGE_BUILD=ON + -DCMAKE_INSTALL_PREFIX=appdir/usr ../src && make -j4; elif [[ "$BUILD_SYSTEM" == "cmake_nodep" ]]; then @@ -201,8 +221,8 @@ script: after_success: - export CUTTER_VERSION=$(python ../scripts/get_version.py) - - lrelease ../src/Cutter.pro - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + lrelease ../src/Cutter.pro && mkdir -p Cutter.app/Contents/Resources/translations && cp ../src/translations/*.qm Cutter.app/Contents/Resources/translations/ && macdeployqt Cutter.app -executable=Cutter.app/Contents/MacOS/Cutter -libpath="../Frameworks" && @@ -231,20 +251,18 @@ after_success: hdiutil convert "${DMG_TMP_FILE}" -format UDZO -imagekey zlib-level=9 -o "${DMG_FILE}" && export FILE_TO_UPLOAD="$DMG_FILE" ; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - export VERSION=$CUTTER_VERSION - make INSTALL_ROOT=appdir install && + - if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ $DEPLOY == 1 ]]; then + export VERSION=$CUTTER_VERSION && + make install && cp -r /usr/share/radare2 appdir/usr/share/ && - mkdir -p appdir/usr/bin/translations && - cp ../src/translations/*.qm appdir/usr/bin/translations/ && "$TRAVIS_BUILD_DIR/scripts/appimage_embed_python.sh" appdir && "$TRAVIS_BUILD_DIR/scripts/r2ghidra.sh" -DCMAKE_INSTALL_PREFIX="`pwd`/appdir/usr" -DBUILD_CUTTER_PLUGIN=ON -DCUTTER_SOURCE_DIR="$TRAVIS_BUILD_DIR" && "$TRAVIS_BUILD_DIR/scripts/r2dec.sh" --prefix=`pwd`/appdir/usr && wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage" && chmod a+x linuxdeployqt*.AppImage && rm -fv "$TRAVIS_BUILD_DIR/cutter-deps/qt/plugins/imageformats/libqjp2.so" && - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -unsupported-allow-new-glibc -bundle-non-qt-libs -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so -ignore-glob=usr/lib/python3.6/**/* -verbose=2 && - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -unsupported-allow-new-glibc -appimage -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so -ignore-glob=usr/lib/python3.6/**/* -verbose=2 && + ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -executable=./appdir/usr/bin/python3 -bundle-non-qt-libs -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so -ignore-glob=usr/lib/python3.6/**/* -verbose=2 && + ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -executable=./appdir/usr/bin/python3 -appimage -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so -ignore-glob=usr/lib/python3.6/**/* -verbose=2 && find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq && export APPIMAGE_FILE="Cutter-v$CUTTER_VERSION-x64.Linux.AppImage" && mv Cutter-*-x86_64.AppImage "$APPIMAGE_FILE" && diff --git a/docs/source/building.rst b/docs/source/building.rst index d7b36f0c..d5169bd7 100644 --- a/docs/source/building.rst +++ b/docs/source/building.rst @@ -30,13 +30,13 @@ On Debian-based Linux distributions, all of these packages can be installed with :: - sudo apt install git build-essential cmake meson libzip-dev zlib1g-dev qt5-default libqt5svg5-dev + sudo apt install git build-essential cmake meson libzip-dev zlib1g-dev qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools On Arch-based Linux distributions, build-essential should be replaced by base-devel: :: - sudo pacman -Syu git base-devel cmake meson qt5-base qt5-svg + sudo pacman -Syu git base-devel cmake meson qt5-base qt5-svg qt5-tools Building steps ~~~~~~~~~~~~~~ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 63906ea0..7c923bf2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,7 @@ option(CUTTER_USE_ADDITIONAL_RADARE2_PATHS "Search radare2 in additional paths w option(CUTTER_ENABLE_PYTHON "Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}." OFF) option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken2. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF) option(CUTTER_ENABLE_CRASH_REPORTS "Enable crash report system. Unused if CUTTER_ENABLE_CRASH_REPORTS=OFF" OFF) +option(CUTTER_APPIMAGE_BUILD "Enable Appimage specific changes. Doesn't cause building of Appimage itself." OFF) tri_option(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING "Use KSyntaxHighlighting" AUTO) tri_option(CUTTER_ENABLE_GRAPHVIZ "Enable use of graphviz for graph layout" AUTO) option (option SHIBOKEN_EXTRA_OPTIONS "Extra options for shiboken generator") @@ -242,6 +243,12 @@ if(TARGET KF5::SyntaxHighlighting) target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_KSYNTAXHIGHLIGHTING) endif() +if (CUTTER_APPIMAGE_BUILD) + target_compile_definitions(Cutter PRIVATE APPIMAGE) +endif() + +include(Translations) + # Install files install(TARGETS Cutter EXPORT CutterTargets diff --git a/src/Cutter.pro b/src/Cutter.pro index 3d95f8fa..86b65626 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -427,7 +427,8 @@ SOURCES += \ common/SettingsUpgrade.cpp \ dialogs/LayoutManager.cpp \ common/CutterLayout.cpp \ - widgets/GraphHorizontalAdapter.cpp + widgets/GraphHorizontalAdapter.cpp \ + common/ResourcePaths.cpp GRAPHVIZ_SOURCES = \ widgets/GraphvizLayout.cpp @@ -581,7 +582,8 @@ HEADERS += \ common/CutterLayout.h \ common/BinaryTrees.h \ common/LinkedListPool.h \ - widgets/GraphHorizontalAdapter.h + widgets/GraphHorizontalAdapter.h \ + common/ResourcePaths.h GRAPHVIZ_HEADERS = widgets/GraphvizLayout.h diff --git a/src/CutterApplication.cpp b/src/CutterApplication.cpp index 7e646e08..ec1c5f95 100644 --- a/src/CutterApplication.cpp +++ b/src/CutterApplication.cpp @@ -4,6 +4,7 @@ #include "plugins/PluginManager.h" #include "CutterConfig.h" #include "common/Decompiler.h" +#include "common/ResourcePaths.h" #include #include @@ -255,7 +256,7 @@ bool CutterApplication::loadTranslations() QTranslator *trQtBase = new QTranslator; QTranslator *trQt = new QTranslator; - const QStringList &cutterTrPaths = Config()->getTranslationsDirectories(); + const QStringList &cutterTrPaths = Cutter::getTranslationsDirectories(); for (const auto &trPath : cutterTrPaths) { if (trCutter && trCutter->load(it, QLatin1String("cutter"), QLatin1String("_"), trPath)) { diff --git a/src/cmake/CutterInstallDirs.cmake b/src/cmake/CutterInstallDirs.cmake index cf03f771..276fe36c 100644 --- a/src/cmake/CutterInstallDirs.cmake +++ b/src/cmake/CutterInstallDirs.cmake @@ -1,10 +1,15 @@ +set(CUTTER_DIR_NAME "RadareOrg/Cutter") if(WIN32) set(CMAKE_INSTALL_BINDIR "." CACHE PATH "Executable install directory") set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "Include install directory") set(CMAKE_INSTALL_LIBDIR "lib" CACHE PATH "Library install directory") + set(CMAKE_INSTALL_DATAROOTDIR "./" CACHE PATH "Resource installation directory") + set(CUTTER_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}" CACHE PATH "Resource installation directory") elseif(APPLE) include(GNUInstallDirs) #TODO: use appropriate paths for macOS + set(CUTTER_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}/${CUTTER_DIR_NAME}" CACHE PATH "Resource installation directory") else() include(GNUInstallDirs) + set(CUTTER_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}/${CUTTER_DIR_NAME}" CACHE PATH "Resource installation directory") endif() -set(ConfigPackageLocation "${CMAKE_INSTALL_LIBDIR}/Cutter" CACHE PATH "Cmake file install location") \ No newline at end of file +set(ConfigPackageLocation "${CMAKE_INSTALL_LIBDIR}/Cutter" CACHE PATH "Cmake file install location") diff --git a/src/cmake/Translations.cmake b/src/cmake/Translations.cmake new file mode 100644 index 00000000..8f9083fa --- /dev/null +++ b/src/cmake/Translations.cmake @@ -0,0 +1,28 @@ +set(TS_FILES + translations/cutter_ar.ts + translations/cutter_ca.ts + translations/cutter_cn.ts + translations/cutter_de.ts + translations/cutter_es.ts + translations/cutter_fa.ts + translations/cutter_fr.ts + translations/cutter_it.ts + translations/cutter_nl.ts + translations/cutter_pt.ts + translations/cutter_ro.ts + translations/cutter_ru.ts + translations/cutter_tr.ts +) + +set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/translations) +find_package(Qt5LinguistTools REQUIRED) +qt5_add_translation(qmFiles ${TS_FILES}) +add_custom_target(translations ALL DEPENDS ${qmFiles} SOURCES ${TS_FILES}) + +install(FILES + ${qmFiles} + # For Linux it might be more correct to use ${MAKE_INSTALL_LOCALEDIR}, but that + # uses share/locale_name/software_name layout instead of share/software_name/locale_files. + DESTINATION ${CUTTER_INSTALL_DATADIR}/translations +) + diff --git a/src/common/Configuration.cpp b/src/common/Configuration.cpp index 5933d539..6402007e 100644 --- a/src/common/Configuration.cpp +++ b/src/common/Configuration.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING #include @@ -15,6 +14,7 @@ #include "common/ColorThemeWorker.h" #include "common/SyntaxHighlighter.h" +#include "common/ResourcePaths.h" /* Map with names of themes associated with its color palette * (Dark or Light), so for dark interface themes will be shown only Dark color themes @@ -652,7 +652,7 @@ void Configuration::setConfig(const QString &key, const QVariant &value) */ QStringList Configuration::getAvailableTranslations() { - const auto &trDirs = getTranslationsDirectories(); + const auto &trDirs = Cutter::getTranslationsDirectories(); QSet fileNamesSet; for (const auto &trDir : trDirs) { @@ -704,21 +704,6 @@ bool Configuration::isFirstExecution() } } -QStringList Configuration::getTranslationsDirectories() const -{ - static const QString cutterTranslationPath = QCoreApplication::applicationDirPath() + - QDir::separator() - + QLatin1String("translations"); - - return { - cutterTranslationPath, - QLibraryInfo::location(QLibraryInfo::TranslationsPath), -#ifdef Q_OS_MAC - QStringLiteral("%1/../Resources/translations").arg(QCoreApplication::applicationDirPath()), -#endif // Q_OS_MAC - }; -} - QString Configuration::getSelectedDecompiler() { return s.value("selectedDecompiler").toString(); diff --git a/src/common/Configuration.h b/src/common/Configuration.h index b2aa27c1..cd981940 100644 --- a/src/common/Configuration.h +++ b/src/common/Configuration.h @@ -154,12 +154,6 @@ public: void setConfig(const QString &key, const QVariant &value); bool isFirstExecution(); - /** - * @brief Get list of available translation directories (depends on configuration and OS) - * @return list of directories - */ - QStringList getTranslationsDirectories() const; - /** * @return id of the last selected decompiler (see CutterCore::getDecompilerById) */ diff --git a/src/common/ResourcePaths.cpp b/src/common/ResourcePaths.cpp new file mode 100644 index 00000000..774538f3 --- /dev/null +++ b/src/common/ResourcePaths.cpp @@ -0,0 +1,85 @@ +#include "common/ResourcePaths.h" + +#include +#include +#include +#include +#include + + +#ifdef APPIMAGE +static QDir appimageRoot() +{ + auto dir = QDir(QCoreApplication::applicationDirPath()); // appimage/usr/bin + dir.cdUp(); // /usr + dir.cdUp(); // / + return dir; +} +#endif + +static QString substitutePath(QString path) +{ +#ifdef APPIMAGE + if (path.startsWith("/usr")) { + return appimageRoot().absoluteFilePath("." + path); + } +#endif + return path; +} + +/** + * @brief Substitute or filter paths returned by standardLocations based on Cutter package kind. + * @param paths list of paths to process + * @return + */ +static QStringList substitutePaths(const QStringList &paths) +{ + QStringList result; + result.reserve(paths.size()); + for (auto &path : paths) { + // consider ignoring some of system folders for portable packages here or standardLocations if it depends on path type + result.push_back(substitutePath(path)); + } + return result; +} + +QStringList Cutter::locateAll(QStandardPaths::StandardLocation type, const QString &fileName, + QStandardPaths::LocateOptions options) +{ + // This function is reimplemented here instead of forwarded to Qt becauase existence check needs to be done + // after substitutions + QStringList result; + for (auto path : standardLocations(type)) { + QString filePath = path + QLatin1Char('/') + fileName; + bool exists = false; + qDebug() << "checking " << filePath; + if (options & QStandardPaths::LocateDirectory) { + exists = QDir(filePath).exists(); + } else { + exists = QFileInfo(filePath).isFile(); + } + if (exists) { + result.append(filePath); + } + } + return result; +} + +QStringList Cutter::standardLocations(QStandardPaths::StandardLocation type) +{ + return substitutePaths(QStandardPaths::standardLocations(type)); +} + +QString Cutter::writableLocation(QStandardPaths::StandardLocation type) +{ + return substitutePath(QStandardPaths::writableLocation(type)); +} + +QStringList Cutter::getTranslationsDirectories() +{ + auto result = locateAll(QStandardPaths::DataLocation, "translations", + QStandardPaths::LocateDirectory); + result << QLibraryInfo::location(QLibraryInfo::TranslationsPath); + return result; +} + diff --git a/src/common/ResourcePaths.h b/src/common/ResourcePaths.h new file mode 100644 index 00000000..41c3cdca --- /dev/null +++ b/src/common/ResourcePaths.h @@ -0,0 +1,29 @@ +#ifndef RESOURCE_PATHS_H +#define RESOURCE_PATHS_H + +#include + +/** + * \file ResourcePaths.h + * Set of functions for obtaining various paths. Some of the functions are wrappers around + * QStandardPaths functions having the same name but with modifications specific to way + * Cutter is packaged. + */ + +namespace Cutter { +QStringList locateAll( + QStandardPaths::StandardLocation type, + const QString &fileName, + QStandardPaths::LocateOptions options = QStandardPaths::LocateFile); +QStringList standardLocations(QStandardPaths::StandardLocation type); +QString writableLocation(QStandardPaths::StandardLocation type); + + +/** + * @brief Get list of available translation directories (depends on configuration and OS) + * @return list of directories + */ +QStringList getTranslationsDirectories(); +} + +#endif