diff --git a/.appveyor.yml b/.appveyor.yml index 849a98a6..d8efea05 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,5 @@ version: '2.4.0-git-{build}' -image: 'Visual Studio 2017' +image: 'Visual Studio 2019' clone_depth: 1 # Build configuration @@ -9,7 +9,7 @@ configuration: # Environment environment: NINJA_URL: https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-win.zip - VSVARSALLPATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat' + VSVARSALLPATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat' ARCH: x64 PYTHON: 'C:\Python36-x64' PACKAGE_NAME: cutter-git-x64.Windows @@ -25,7 +25,6 @@ install: - cmd: python -m pip install meson ninja - ps: choco install winflexbison3 # Artifacts - - cmd: set "ARTIFACT_NAME=%PACKAGE_NAME%.zip" - cmd: set "ARTIFACT_PATH=build\%ARTIFACT_NAME%" before_build: @@ -40,6 +39,7 @@ build_script: - cmd: "cmake -DCMAKE_BUILD_TYPE=Release -DCUTTER_USE_BUNDLED_RIZIN=ON + -DCUTTER_QT6=ON -DCUTTER_ENABLE_PYTHON=ON -DCUTTER_ENABLE_PYTHON_BINDINGS=ON -DCUTTER_ENABLE_PACKAGING=ON diff --git a/.github/actions/build-linux-old/action.yml b/.github/actions/build-linux-old/action.yml new file mode 100644 index 00000000..032aba6b --- /dev/null +++ b/.github/actions/build-linux-old/action.yml @@ -0,0 +1,26 @@ +name: 'Build linux' +description: 'Build cutter in a docker image' +inputs: + system-deps: + description: 'Use system libraries instead of cutter-deps' + required: true + image: + description: 'Docker image' + required: true + qt-major: + description: 'Qt major version' + required: true + package: + description: 'Package appimage' + required: true +runs: + using: 'docker' + image: 'ubuntu:18.04' + entrypoint: './.github/actions/build-linux-old/entrypoint.sh' + args: + - ${{ inputs.system-deps }} + env: + package: ${{ inputs.package }} + qt_major: ${{ inputs.qt-major }} + image: ${{ inputs.image }} + system_deps: ${{ inputs.system-deps }} \ No newline at end of file diff --git a/.github/actions/build-linux-old/entrypoint.sh b/.github/actions/build-linux-old/entrypoint.sh new file mode 100755 index 00000000..59f9fb3d --- /dev/null +++ b/.github/actions/build-linux-old/entrypoint.sh @@ -0,0 +1,206 @@ +#!/bin/bash + +set -euo pipefail + +pwd +ls + +#export TZ=UTC +#ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +apt-get -y update + +# latest git and cmake +export GIT_VERSION="git-2.36.1" +export CMAKE_VERSION="3.25.3" + +apt-get -y install wget libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev build-essential + +wget "https://www.kernel.org/pub/software/scm/git/$GIT_VERSION.tar.gz" +tar -zxf "$GIT_VERSION.tar.gz" +# build. +#make -C "$GIT_VERSION" prefix=/usr install -j > "$GIT_VERSION/build.log" +# ensure git is installed. +#git version +wget "https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-linux-x86_64.sh" +bash ./cmake-$CMAKE_VERSION-linux-x86_64.sh --skip-license --prefix=/usr +# ensure cmake is installed. +cmake --version +# cleanup dev environment. +rm -rf "$GIT_VERSION.tar.gz" "$GIT_VERSION" cmake-$CMAKE_VERSION-linux-x86_64.sh +unset CMAKE_VERSION +unset GIT_VERSION + +apt-get -y install libgraphviz-dev \ + mesa-common-dev \ + libxkbcommon-x11-dev \ + ninja-build \ + python3-pip \ + curl \ + libpcre2-dev \ + libfuse2 \ + pkg-config \ + git + + +if [ "$image" = "ubuntu:18.04" ]; then + # install additional packages needed for appimage + apt-get -y install gcc-7 \ + libglu1-mesa-dev \ + freeglut3-dev \ + mesa-common-dev \ + libclang-8-dev \ + llvm-8 + ln -s /usr/bin/llvm-config-8 /usr/bin/llvm-config +fi +if [ "$image" = "ubuntu:18.04" ] || [ "$image" = "ubuntu:20.04" ]; then + # install additional packages needed for appimage + apt-get -y install libxcb1-dev \ + libxkbcommon-dev \ + libxcb-*-dev \ + libegl1 +fi +if [ "$image" = "ubuntu:20.04" ] && [ "$system_deps" = "false" ]; then + # install additional packages needed for appimage + apt-get -y install libclang-12-dev \ + llvm-12 \ + libsm6 \ + libwayland-dev \ + libgl1-mesa-dev +fi +if [ "$image" = "ubuntu:18.04" ] && [ "$system_deps" = "true" ]; then + apt-get -y install qt5-default \ + libqt5svg5-dev \ + qttools5-dev \ + qttools5-dev-tools +fi +if [ "$image" = "ubuntu:22.04" ]; then + apt-get -y install libclang-12-dev \ + llvm-12 \ + qt6-base-dev \ + qt6-tools-dev \ + qt6-tools-dev-tools \ + libqt6svg6-dev \ + libqt6core5compat6-dev \ + libqt6svgwidgets6 \ + qt6-l10n-tools \ + gcc-12 \ + g++-12 +fi + +# https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true +python3 -m pip install meson==0.61.5 + + +if [ "$system_deps" = "false" ] +then + CUTTER_QT="$qt_major" scripts/fetch_deps.sh + set +u # TODO: remove temp code after updating cutter_deps + . cutter-deps/env.sh + set -u + #export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH" +fi +#if [ "${{ matrix.cc-override }}" != "default" ] +#then +# export CC="${{matrix.cc-override}}" +# export CXX="${{matrix.cxx-override}}" +#fi + +# otherwise git complains about dubious ownership, due to code being checked out outside the container with a different user +git config --global --add safe.directory /github/workspace/rizin + +if [ $qt_major = 6] +then + CMAKE_QT_ARG='ON' +else + CMAKE_QT_ARG='OFF' +fi + +mkdir build +cd build +if [ "$system_deps" = "false" ] +then + locale + locale -a + export LANG="C.UTF-8" + export LC_ALL="C.UTF-8" + cmake \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCUTTER_ENABLE_PYTHON=ON \ + -DPython3_ROOT_DIR="$CUTTER_DEPS_PYTHON_PREFIX" \ + -DCUTTER_ENABLE_PYTHON_BINDINGS=ON \ + -DCUTTER_ENABLE_GRAPHVIZ=ON \ + -DCUTTER_USE_BUNDLED_RIZIN=ON \ + -DCUTTER_APPIMAGE_BUILD=ON \ + -DCUTTER_ENABLE_PACKAGING=ON \ + -DCUTTER_ENABLE_KSYNTAXHIGHLIGHTING=OFF \ + -DCUTTER_ENABLE_SIGDB=ON \ + -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \ + -DCUTTER_PACKAGE_RZ_GHIDRA=ON \ + -DCUTTER_PACKAGE_JSDEC=ON \ + -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \ + -DCUTTER_PACKAGE_RZ_LIBYARA=ON \ + -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \ + -DCMAKE_INSTALL_PREFIX=appdir/usr \ + -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ + -DCUTTER_QT6=$CMAKE_QT_ARG \ + .. +else + cmake \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCUTTER_USE_BUNDLED_RIZIN=ON \ + -DCUTTER_QT6=$CMAKE_QT_ARG \ + .. +fi +ninja +if [ "$package" = "true" ] +then + export CUTTER_VERSION=$(python ../scripts/get_version.py) + export VERSION=$CUTTER_VERSION + ninja install + if [ $qt_major == "6" ] + then + pyside_ver=6 + else + pyside_ver=2 + fi + "../scripts/appimage_embed_python.sh" appdir $pyside_ver + APP_PREFIX=`pwd`/appdir/usr + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:$APP_PREFIX/lib/rizin/plugins" + export PATH=$PATH:${APP_PREFIX}/bin + wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" + chmod a+x linuxdeployqt*.AppImage + rm -fv "../cutter-deps/qt/plugins/imageformats/libqjp2.so" + if [ "$qt_major" == "5" ]; then + export APPIMAGE_FILE="Cutter-${PACKAGE_ID}-Linux-Qt5-x86_64.AppImage" + ./linuxdeployqt*.AppImage --appimage-extract-and-run \ + ./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.12/**/* \ + -verbose=2 + else + export APPIMAGE_FILE="Cutter-${PACKAGE_ID}-Linux-x86_64.AppImage" + ./linuxdeployqt*.AppImage --appimage-extract-and-run \ + ./appdir/usr/share/applications/*.desktop \ + -executable=./appdir/usr/bin/python3 \ + -appimage \ + -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so \ + -exclude-libs="libwayland-client.so,libwayland-egl.so,libwayland-cursor.so" \ + -ignore-glob=usr/lib/python3.12/**/* \ + -extra-plugins="platforms/libqwayland-egl.so,platforms/libqwayland-generic.so,wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-integration,wayland-graphics-integration-server" \ + -verbose=2 + fi + find ./appdir -executable -type f -exec ldd {} \; | cut -d " " -f 2-3 | sort | uniq + # find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq + + mv Cutter-*-x86_64.AppImage "$APPIMAGE_FILE" + echo PACKAGE_NAME=$APPIMAGE_FILE >> $GITHUB_ENV + echo PACKAGE_NAME=$APPIMAGE_FILE >> $GITHUB_OUTPUT + echo PACKAGE_PATH=build/$APPIMAGE_FILE >> $GITHUB_ENV + echo PACKAGE_PATH=build/$APPIMAGE_FILE >> $GITHUB_OUTPUT + echo UPLOAD_ASSET_TYPE=application/x-executable >> $GITHUB_ENV +fi \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc72cc1f..764a898d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,19 +30,13 @@ jobs: name: [ linux-x86_64, linux-x86_64-system-deps, - linux-x86_64-qt6-system-deps, tarball ] include: - - name: linux-x86_64-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 18.04 using sytem libraries - image: ubuntu:18.04 - python-version: 3.6.x - system-deps: true - package: false - tarball: false - cc-override: '/usr/bin/gcc-7' - cxx-override: '/usr/bin/g++-7' - - name: linux-x86_64-qt6-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 22.04 using sytem libraries + - qt-major: 6 + cc-override: default + cxx-override: default + - name: linux-x86_64-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 22.04 using sytem libraries image: ubuntu:22.04 python-version: 3.10.x system-deps: true @@ -50,14 +44,12 @@ jobs: tarball: false cc-override: '/usr/bin/gcc-12' cxx-override: '/usr/bin/g++-12' - - name: linux-x86_64 - image: ubuntu:18.04 + - name: linux-x86_64 # main Appimage build + image: ubuntu:20.04 python-version: 3.6.x system-deps: false package: true tarball: false - cc-override: default - cxx-override: default - name: tarball python-version: 3.6.x image: ubuntu:20.04 @@ -129,18 +121,25 @@ jobs: apt-get -y install gcc-7 \ libglu1-mesa-dev \ freeglut3-dev \ - mesa-common-dev - + mesa-common-dev \ + libclang-8-dev \ + llvm-8 + ln -s /usr/bin/llvm-config-8 /usr/bin/llvm-config fi if [ "${{ matrix.image }}" = "ubuntu:18.04" ] || [ "${{ matrix.image }}" = "ubuntu:20.04" ]; then # install additional packages needed for appimage apt-get -y install libxcb1-dev \ libxkbcommon-dev \ libxcb-*-dev \ - libegl1 \ - libclang-8-dev \ - llvm-8 - ln -s /usr/bin/llvm-config-8 /usr/bin/llvm-config + libegl1 + fi + if [ "${{ matrix.image }}" = "ubuntu:20.04" ] && [ "${{ matrix.system-deps }}" = "false" ]; then + # install additional packages needed for appimage + apt-get -y install libclang-12-dev \ + llvm-12 \ + libsm6 \ + libwayland-dev \ + libgl1-mesa-dev fi if [ "${{ matrix.image }}" = "ubuntu:18.04" ] && [ "${{ matrix.system-deps }}" = "true" ]; then apt-get -y install qt5-default \ @@ -184,9 +183,8 @@ jobs: run: | if [ "${{ matrix.system-deps }}" = "false" ] then - scripts/fetch_deps.sh + CUTTER_QT="${{ matrix.qt-major }}" scripts/fetch_deps.sh . cutter-deps/env.sh - export PKG_CONFIG_PATH="$CUTTER_DEPS_PYTHON_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH" fi set -e #TODO: move to top once cutter-deps doesn't fail @@ -200,13 +198,15 @@ jobs: cd build if [ "${{ matrix.system-deps }}" = "false" ] then + locale + locale -a + export LANG="C.UTF-8" + export LC_ALL="C.UTF-8" cmake \ -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCUTTER_ENABLE_PYTHON=ON \ - -DPYTHON_LIBRARY="$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.so" \ - -DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \ - -DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \ + -DPython3_ROOT_DIR="$CUTTER_DEPS_PYTHON_PREFIX" \ -DCUTTER_ENABLE_PYTHON_BINDINGS=ON \ -DCUTTER_ENABLE_GRAPHVIZ=ON \ -DCUTTER_USE_BUNDLED_RIZIN=ON \ @@ -222,6 +222,7 @@ jobs: -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \ -DCMAKE_INSTALL_PREFIX=appdir/usr \ -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ + -DCUTTER_QT6=${{ matrix.qt-major == '6' && 'ON' || 'OFF' }} \ .. elif [ "${{ matrix.image }}" = "ubuntu:22.04" ] then @@ -244,21 +245,35 @@ jobs: export CUTTER_VERSION=$(python ../scripts/get_version.py) export VERSION=$CUTTER_VERSION ninja install - "../scripts/appimage_embed_python.sh" appdir + "../scripts/appimage_embed_python.sh" appdir ${{ matrix.qt-major == '6' && '6' || '2' }} APP_PREFIX=`pwd`/appdir/usr export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$APP_PREFIX/lib/rizin/plugins" export PATH=$PATH:${APP_PREFIX}/bin wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" chmod a+x linuxdeployqt*.AppImage rm -fv "../cutter-deps/qt/plugins/imageformats/libqjp2.so" - ./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.9/**/* \ - -verbose=2 - find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq - export APPIMAGE_FILE="Cutter-${PACKAGE_ID}-Linux-x86_64.AppImage" + if [ "${{ matrix.qt-major }}" == "5" ]; then + export APPIMAGE_FILE="Cutter-${PACKAGE_ID}-Linux-Qt5-x86_64.AppImage" + ./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.12/**/* \ + -verbose=2 + else + export APPIMAGE_FILE="Cutter-${PACKAGE_ID}-Linux-x86_64.AppImage" + ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop \ + -executable=./appdir/usr/bin/python3 \ + -appimage \ + -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so \ + -exclude-libs="libwayland-client.so,libwayland-egl.so,libwayland-cursor.so" \ + -ignore-glob=usr/lib/python3.12/**/* \ + -extra-plugins="platforms/libqwayland-egl.so,platforms/libqwayland-generic.so,wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-integration,wayland-graphics-integration-server" \ + -verbose=2 + fi + find ./appdir -executable -type f -exec ldd {} \; | cut -d " " -f 2-3 | sort | uniq + # find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq + mv Cutter-*-x86_64.AppImage "$APPIMAGE_FILE" echo PACKAGE_NAME=$APPIMAGE_FILE >> $GITHUB_ENV echo PACKAGE_PATH=build/$APPIMAGE_FILE >> $GITHUB_ENV @@ -293,6 +308,85 @@ jobs: asset_path: ${{ env.PACKAGE_PATH }} asset_name: ${{ env.PACKAGE_NAME }} asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }} + build-linux-old: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + strategy: + matrix: + name: [ + linux-x86_64-qt5, + linux-x86_64-qt5-system-deps, + ] + include: + - qt-major: 6 + cc-override: default + cxx-override: default + - name: linux-x86_64-qt5-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 18.04 using sytem libraries + image: ubuntu:18.04 + python-version: 3.6.x + system-deps: true + package: false + cc-override: '/usr/bin/gcc-7' + cxx-override: '/usr/bin/g++-7' + qt-major: 5 + - name: linux-x86_64-qt5 # qt5 Appimage build for increased compatibility with older distros + image: ubuntu:18.04 + python-version: 3.6.x + system-deps: false + package: true + qt-major: 5 + # Prevent one job from pausing the rest + fail-fast: false + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + persist-credentials: false + - name: Prepare package id + shell: bash + run: | + if [ "${{ startsWith(github.event.ref, 'refs/tags')}}" = "true" ] + then + PACKAGE_ID="${{ github.event.ref }}" + else + PACKAGE_ID="git-`date "+%Y-%m-%d"`-${{ format('{0}', github.sha) }}" + fi + PACKAGE_ID=${PACKAGE_ID##refs/tags/} + echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV + ls -alh + who + + - name: build linux + id: build + uses: ./.github/actions/build-linux-old + with: + system-deps: ${{ matrix.system-deps }} + image: ${{ matrix.image }} + qt-major: ${{ matrix.qt-major }} + package: ${{ matrix.package }} + + - uses: actions/upload-artifact@v3 + if: env.PACKAGE_NAME != null + with: + name: ${{ steps.build.outputs.PACKAGE_NAME }} + path: ${{ steps.build.outputs.PACKAGE_PATH }} + - name: Get release + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + id: get_release + uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload release assets + if: steps.get_release.outputs.upload_url != null && env.PACKAGE_NAME != null + uses: actions/upload-release-asset@v1.0.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_path: ${{ env.PACKAGE_PATH }} + asset_name: ${{ env.PACKAGE_NAME }} + asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }} + build: name: ${{ matrix.name }} @@ -301,21 +395,24 @@ jobs: matrix: name: [ macos-x86_64, + macos-arm64, windows-x86_64, ] include: + - python-version: 3.12.x + - system-deps: false - name: windows-x86_64 os: windows-2019 package: true - system-deps: false - python-version: 3.7.x + python-version: 3.12.x - name: macos-x86_64 - os: macos-14-large - python-version: 3.7.x - system-deps: false + os: macos-12 + arch: x86_64 + package: true + - name: macos-arm64 + os: macos-14 + arch: arm64 package: true - cc-override: default - cxx-override: default # Prevent one job from pausing the rest fail-fast: false steps: @@ -334,8 +431,9 @@ jobs: brew bundle - name: py dependencies run: | - python3 -m pip install -U pip==21.3.1 - pip install meson==0.61.5 # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true + #python3 -m pip install -U pip==21.3.1 + pip install meson # ==0.61.5 # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true + pip install setuptools - name: Prepare package id shell: bash run: | @@ -351,19 +449,18 @@ jobs: shell: bash if: contains(matrix.os, 'macos') run: | - export MACOSX_DEPLOYMENT_TARGET=10.14 + export MACOSX_DEPLOYMENT_TARGET=10.15 scripts/fetch_deps.sh source cutter-deps/env.sh set -euo pipefail export PATH=/usr/local/opt/llvm/bin:$PATH mkdir build cd build - PACKAGE_NAME=Cutter-${PACKAGE_ID}-macOS-x86_64 + PACKAGE_NAME=Cutter-${PACKAGE_ID}-macOS-${{ matrix.arch }} cmake \ -DCMAKE_BUILD_TYPE=Release \ - -DPYTHON_LIBRARY="$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.dylib" \ - -DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \ - -DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \ + -DPython3_ROOT_DIR="${CUTTER_DEPS_PYTHON_PREFIX}" \ + -DPython_ROOT_DIR="${CUTTER_DEPS_PYTHON_PREFIX}" \ -DCUTTER_ENABLE_PYTHON=ON \ -DCUTTER_ENABLE_PYTHON_BINDINGS=ON \ -DCUTTER_USE_BUNDLED_RIZIN=ON \ @@ -376,6 +473,7 @@ jobs: -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \ -DCUTTER_PACKAGE_RZ_LIBYARA=ON \ -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \ + -DCUTTER_QT6=ON \ -DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \ -DCPACK_BUNDLE_APPLE_CERT_APP="-" \ .. && \ @@ -420,6 +518,7 @@ jobs: -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^ -DCMAKE_PREFIX_PATH="%CUTTER_DEPS%\pyside" ^ -DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME% ^ + -DCUTTER_QT6=ON ^ -G Ninja ^ .. cmake --build . --config Release diff --git a/.gitignore b/.gitignore index f572d83c..5745e9d2 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ moc_*.cpp qrc_*.cpp moc_*.h ui_*.h -build*/ +/build*/ cmake-build-*/ /src-build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 4eefd16e..ac5fa726 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,10 +93,15 @@ endif() find_package(${QT_PREFIX} REQUIRED COMPONENTS ${QT_COMPONENTS}) if(CUTTER_ENABLE_PYTHON) - find_package(PythonInterp REQUIRED) - find_package(PythonLibs ${CUTTER_PYTHON_MIN} REQUIRED) + if(CMAKE_VERSION VERSION_LESS "3.12.0") + find_package(PythonInterp REQUIRED) + find_package(PythonLibs ${CUTTER_PYTHON_MIN} REQUIRED) + include_directories(${PYTHON_INCLUDE_DIRS}) + else() + find_package (Python3 ${CUTTER_PYTHON_MIN} REQUIRED COMPONENTS Interpreter Development) + set (PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) + endif() - include_directories(${PYTHON_INCLUDE_DIRS}) add_definitions(-DCUTTER_ENABLE_PYTHON) if(CUTTER_ENABLE_PYTHON_BINDINGS) diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt index 489bf8f1..7046c4d0 100644 --- a/dist/CMakeLists.txt +++ b/dist/CMakeLists.txt @@ -14,9 +14,14 @@ if(WIN32) if (CUTTER_ENABLE_DEPENDENCY_DOWNLOADS) set(CPACK_INSTALL_SCRIPTS ${CMAKE_CURRENT_SOURCE_DIR}/WindowsBundlePython.cmake) endif() - find_package(PythonInterp REQUIRED) - install(DIRECTORY ${CUTTER_DEPS}/pyside/lib/site-packages DESTINATION "python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}") - install(FILES ${CUTTER_DEPS}/pyside/bin/shiboken2.abi3.dll ${CUTTER_DEPS}/pyside/bin/pyside2.abi3.dll DESTINATION .) + if(CMAKE_VERSION VERSION_LESS "3.12.0") + find_package(PythonInterp REQUIRED) + install(DIRECTORY ${CUTTER_DEPS}/pyside/lib/site-packages DESTINATION "python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}") + else() + find_package (Python3 ${CUTTER_PYTHON_MIN} REQUIRED COMPONENTS) + install(DIRECTORY ${CUTTER_DEPS}/pyside/lib/site-packages DESTINATION "python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}") + endif() + install(FILES ${CUTTER_DEPS}/pyside/bin/shiboken6.abi3.dll ${CUTTER_DEPS}/pyside/bin/pyside6.abi3.dll DESTINATION .) endif() install(SCRIPT WindowsBundleQt.cmake) endif() @@ -104,7 +109,7 @@ if(APPLE) if(CUTTER_PACKAGE_DEPENDENCIES AND CUTTER_ENABLE_PYTHON) set(EMBED_PYTHON_SH "${CMAKE_CURRENT_SOURCE_DIR}/appbundle_embed_python.sh") - set(PYTHON_FRAMEWORK_DIR "${CUTTER_DEPS}/python/Python.framework") + set(PYTHON_FRAMEWORK_DIR "${CUTTER_DEPS}/python/Library/Frameworks/Python.framework") set(PYSIDE_PREFIX "${CUTTER_DEPS}/pyside") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/MacOSBundlePython.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/MacOSBundlePython.cmake" @ONLY) @@ -158,6 +163,9 @@ if(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32)) else() set (YARA_PLUGIN_OPTIONS "") endif() + if (CUTTER_QT6) + set (YARA_PLUGIN_OPTIONS "${YARA_PLUGIN_OPTIONS} -DCUTTER_QT6=ON") + endif() install(CODE " execute_process(COMMAND \"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-libyara.sh\" diff --git a/dist/appbundle_embed_python.sh b/dist/appbundle_embed_python.sh index d2b30d72..ce8a0ee0 100755 --- a/dist/appbundle_embed_python.sh +++ b/dist/appbundle_embed_python.sh @@ -6,7 +6,7 @@ if ! [[ $# -eq 3 ]]; then exit 1 fi -python_version=python3.9 +python_version=python3.12 py_framework=$1 appbundle=$2 @@ -30,21 +30,21 @@ fi echo "Making executable $executable point to embedded Framework" install_name_tool -change `otool -L "$executable" | sed -n "s/^[[:blank:]]*\([^[:blank:]]*Python\) (.*$/\1/p"` @executable_path/../Frameworks/Python.framework/Versions/Current/Python "$executable" -echo "Checking if PySide2 is available" +echo "Checking if PySide is available" -pyside_prefix=$(pkg-config --variable=prefix pyside2) +pyside_prefix=$(pkg-config --variable=prefix pyside6) if [ $? -ne 0 ]; then - echo "PySide2 is not available, ignoring." + echo "PySide is not available, ignoring." exit 0 fi echo "PySide is at $pyside_prefix" -if [ ! -d "Versions/Current/lib/$python_version/site-packages/PySide2" ] +if [ ! -d "Versions/Current/lib/$python_version/site-packages/PySide6" ] then cp -va "$pyside_prefix/lib/$python_version/" "Versions/Current/lib/$python_version" cd .. # $appbundle/Contents/Frameworks cp -va "$pyside_prefix/lib/"*.dylib . else - echo "site-packages/Pyside2 exists, skipping copying" + echo "site-packages/Pyside6 exists, skipping copying" fi diff --git a/scripts/appimage_embed_python.sh b/scripts/appimage_embed_python.sh index a7a98f03..069f777c 100755 --- a/scripts/appimage_embed_python.sh +++ b/scripts/appimage_embed_python.sh @@ -1,13 +1,19 @@ #!/bin/bash -if ! [[ $# -eq 1 ]]; then - echo "Usage: $0 [appdir]" +set -euo pipefail + +if ! [[ $# -eq 2 ]]; then + echo "Usage: $0 [appdir] [pyside_major]" exit 1 fi -python_version=python3.9 - python_prefix=$(pkg-config --variable=prefix python3) + +python_version=`$python_prefix/bin/python3 --version` +python_version=${python_version##* } +python_version=python${python_version%.*} + +pyside_major=$2 appdir=$1 echo "Embedding Python from prefix $python_prefix in appdir $appdir" @@ -20,11 +26,11 @@ echo "Cleaning up embedded Python" find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf rm -r lib/$python_version/test lib/$python_version/idlelib lib/$python_version/curses lib/$python_version/lib2to3 -echo "Checking if PySide2 is available" +echo "Checking if PySide is available" -pyside_prefix=$(pkg-config --variable=prefix pyside2) +pyside_prefix=$(pkg-config --variable=prefix pyside$pyside_major) if [ $? -ne 0 ]; then - echo "PySide2 is not available, ignoring." + echo "PySide is not available, ignoring." exit 0 fi diff --git a/scripts/fetch_deps.sh b/scripts/fetch_deps.sh index fedace6d..b74fbb87 100755 --- a/scripts/fetch_deps.sh +++ b/scripts/fetch_deps.sh @@ -5,19 +5,27 @@ set -e cd $(dirname "${BASH_SOURCE[0]}")/.. mkdir -p cutter-deps && cd cutter-deps -DEPS_FILE_linux_x86_64=cutter-deps-linux-x86_64.tar.gz -DEPS_SHA256_linux_x86_64=0721c85548bbcf31f6911cdb2227e5efb4a20c34262672d4cd2193db166b2f8c +DEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/v16 + +if [ "$CUTTER_QT" == "5" ]; then + DEPS_FILE_linux_x86_64=cutter-deps-linux-x86_64.tar.gz + DEPS_SHA256_linux_x86_64=0721c85548bbcf31f6911cdb2227e5efb4a20c34262672d4cd2193db166b2f8c + DEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/v15 +else + DEPS_FILE_linux_x86_64=cutter-deps-linux-x86_64.tar.gz + DEPS_SHA256_linux_x86_64=f63c5af2d9872bc6538a94c839d6ef6645c7630c42cff30f1d9da8eefd9eb040 +fi +echo $DEPS_SHA256_linux_x86_64 DEPS_FILE_macos_x86_64=cutter-deps-macos-x86_64.tar.gz -DEPS_SHA256_macos_x86_64=0a23fdec3012a8af76675d6f3ff39cf9df9b08c13d1156fb7ffcc0e495c9407f +DEPS_SHA256_macos_x86_64=bcdc214e34dc3fd720327ad42e03fe3ec996ca28a9987e99898f149a65299a8c DEPS_FILE_macos_arm64=cutter-deps-macos-arm64.tar.gz -DEPS_SHA256_macos_arm64=f9b9a5569bd23c9b5e45836b82aba7576a5c53df4871380a55c370b9d7f88615 +DEPS_SHA256_macos_arm64=aa3f5ae91b93c5176d6bd4313af0888a2b6dcdaa2ef1750dd7e2f98156882e0f DEPS_FILE_win_x86_64=cutter-deps-win-x86_64.tar.gz -DEPS_SHA256_win_x86_64=9ab4e89732a3df0859a26fd5de6d9f3cb80106cbe2539340af831ed298625076 +DEPS_SHA256_win_x86_64=710e40cf8329205d09535cc56a9fb155a56ff1a1ca112145864382fb3d4e8160 -DEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/v15 ARCH=x86_64 if [ "$OS" == "Windows_NT" ]; then diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81c40220..d5e0d529 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -438,7 +438,7 @@ if(CUTTER_ENABLE_PYTHON_BINDINGS) COMMAND "${SHIBOKEN_COMMAND}" --project-file="${BINDINGS_BUILD_DIR}/bindings.txt" ${SHIBOKEN_OPTIONS} ${SHIBOKEN_EXTRA_OPTIONS} DEPENDS "${BINDINGS_BUILD_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.txt" IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.h" - COMMENT "Generating Python bindings with shiboken2") + COMMENT "Generating Python bindings with ${SHIBOKEN_COMMAND}") else() set(BINDINGS_SOURCE "") endif() @@ -498,19 +498,24 @@ endif() target_link_libraries(Cutter PUBLIC ${RIZIN_TARGET}) if(CUTTER_ENABLE_PYTHON) - if (WIN32) + if(CMAKE_VERSION VERSION_LESS "3.12.0") # On windows some of the Python STABLE API functions are in seperate library # which isn't added by CMake. - foreach(_PYTHON_LIBRARY ${PYTHON_LIBRARIES}) - # Skip extra "optimized" and "debug" values that are only meant for passing to target_link_libraries() - if((NOT _PYTHON_LIBRARY STREQUAL "optimized") AND (NOT _PYTHON_LIBRARY STREQUAL "debug")) - get_filename_component(_PYTHON_LIB_DIR ${_PYTHON_LIBRARY} DIRECTORY) - message(STATUS "Add extra library dir for Python: ${_PYTHON_LIB_DIR}") - target_link_directories(Cutter PRIVATE ${_PYTHON_LIB_DIR}) - endif() - endforeach() + if (WIN32) + foreach(_PYTHON_LIBRARY ${PYTHON_LIBRARIES}) + # Skip extra "optimized" and "debug" values that are only meant for passing to target_link_libraries() + if((NOT _PYTHON_LIBRARY STREQUAL "optimized") AND (NOT _PYTHON_LIBRARY STREQUAL "debug")) + get_filename_component(_PYTHON_LIB_DIR ${_PYTHON_LIBRARY} DIRECTORY) + message(STATUS "Add extra library dir for Python: ${_PYTHON_LIB_DIR}") + target_link_directories(Cutter PRIVATE ${_PYTHON_LIB_DIR}) + endif() + endforeach() + endif() + target_link_libraries(Cutter PRIVATE ${PYTHON_LIBRARIES}) + else() + target_link_libraries(Cutter PRIVATE Python3::Python) endif() - target_link_libraries(Cutter PRIVATE ${PYTHON_LIBRARIES}) + if(CUTTER_ENABLE_PYTHON_BINDINGS) if (CUTTER_QT6) target_link_libraries(Cutter PRIVATE Shiboken6::libshiboken PySide6::pyside6) diff --git a/src/bindings/bindings.xml.in b/src/bindings/bindings.xml.in index c20aea67..7e512b87 100644 --- a/src/bindings/bindings.xml.in +++ b/src/bindings/bindings.xml.in @@ -31,9 +31,14 @@ PyErr_Print(); return QString(); } - PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(Sbk${PYSIDE_NAME}_QtCoreTypeConverters[SBK_QSTRING_IDX], pyResult); + auto converter = Shiboken::Conversions::getConverter("str"); + if (!converter) { + Shiboken::warning(PyExc_RuntimeWarning, 0, "Can't find converter for str."); + return ::QString(); + } + PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(converter, pyResult); if (!pythonToCpp) { - Shiboken::warning(PyExc_RuntimeWarning, 2, "Invalid return value for plugin metadata VAR_NAME, expected %s, got %s.", "QString", Py_TYPE(pyResult)->tp_name); + Shiboken::warning(PyExc_RuntimeWarning, 1, "Invalid return value for plugin metadata VAR_NAME, expected str, got %s.", Py_TYPE(pyResult)->tp_name); return ::QString(); } QString cppResult; diff --git a/src/plugins/PluginManager.cpp b/src/plugins/PluginManager.cpp index 6f6a5aa5..d1c5465d 100644 --- a/src/plugins/PluginManager.cpp +++ b/src/plugins/PluginManager.cpp @@ -215,7 +215,11 @@ CutterPlugin *PluginManager::loadPythonPlugin(const char *moduleName) } PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppPointerConvertible( +# if QT_VERSION < QT_VERSION_CHECK(6, 2, 0) reinterpret_cast(SbkCutterBindingsTypes[SBK_CUTTERPLUGIN_IDX]), +# else + reinterpret_cast(SbkCutterBindingsTypeStructs)[SBK_CUTTERPLUGIN_IDX], +# endif pluginObject); if (!pythonToCpp) { qWarning() << "Plugin's create_cutter_plugin() function did not return an instance of " diff --git a/src/plugins/sample-python-qt5/sample_python.py b/src/plugins/sample-python-qt5/sample_python.py new file mode 100644 index 00000000..3be1ad76 --- /dev/null +++ b/src/plugins/sample-python-qt5/sample_python.py @@ -0,0 +1,107 @@ + +import cutter + +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton + + +class FortuneWidget(cutter.CutterDockWidget): + def __init__(self, parent): + super(FortuneWidget, self).__init__(parent) + self.setObjectName("FancyDockWidgetFromCoolPlugin") + self.setWindowTitle("Sample Python Plugin") + + content = QWidget() + self.setWidget(content) + + # Create layout and label + layout = QVBoxLayout(content) + content.setLayout(layout) + self.text = QLabel(content) + self.text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) + self.text.setFont(cutter.Configuration.instance().getFont()) + layout.addWidget(self.text) + + button = QPushButton(content) + button.setText("Want a fortune?") + button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) + button.setMaximumHeight(50) + button.setMaximumWidth(200) + layout.addWidget(button) + layout.setAlignment(button, Qt.AlignHCenter) + + button.clicked.connect(self.generate_fortune) + cutter.core().seekChanged.connect(self.generate_fortune) + + self.show() + + def generate_fortune(self): + fortune = cutter.cmd("fortune").replace("\n", "") + res = cutter.core().cmdRaw(f"?E {fortune}") + self.text.setText(res) + + +class CutterSamplePlugin(cutter.CutterPlugin): + name = "Sample Plugin" + description = "A sample plugin written in python." + version = "1.2" + author = "Cutter developers" + + # Override CutterPlugin methods + + def __init__(self): + super(CutterSamplePlugin, self).__init__() + self.disassembly_actions = [] + self.addressable_item_actions = [] + self.disas_action = None + self.addr_submenu = None + self.main = None + + def setupPlugin(self): + pass + + def setupInterface(self, main): + # Dock widget + widget = FortuneWidget(main) + main.addPluginDockWidget(widget) + + # Dissassembly context menu + menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly) + self.disas_action = menu.addAction("CutterSamplePlugin dissassembly action") + self.disas_action.triggered.connect(self.handle_disassembler_action) + self.main = main + + # Context menu for tables with addressable items like Flags,Functions,Strings,Search results,... + addressable_item_menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable) + self.addr_submenu = addressable_item_menu.addMenu("CutterSamplePlugin") # create submenu + adrr_action = self.addr_submenu.addAction("Action 1") + self.addr_submenu.addSeparator() # can use separator and other qt functionality + adrr_action2 = self.addr_submenu.addAction("Action 2") + adrr_action.triggered.connect(self.handle_addressable_item_action) + adrr_action2.triggered.connect(self.handle_addressable_item_action) + + def terminate(self): # optional + print("CutterSamplePlugin shutting down") + if self.main: + menu = self.main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly) + menu.removeAction(self.disas_action) + addressable_item_menu = self.main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable) + submenu_action = self.addr_submenu.menuAction() + addressable_item_menu.removeAction(submenu_action) + print("CutterSamplePlugin finished clean up") + + # Plugin methods + + def handle_addressable_item_action(self): + # for actions in plugin menu Cutter sets data to current item address + submenu_action = self.addr_submenu.menuAction() + cutter.message("Context menu action callback 0x{:x}".format(submenu_action.data())) + + def handle_disassembler_action(self): + # for actions in plugin menu Cutter sets data to address for current dissasembly line + cutter.message("Dissasembly menu action callback 0x{:x}".format(self.disas_action.data())) + + +# This function will be called by Cutter and should return an instance of the plugin. +def create_cutter_plugin(): + return CutterSamplePlugin() diff --git a/src/plugins/sample-python/sample_python.py b/src/plugins/sample-python/sample_python.py index 3be1ad76..4db2a34f 100644 --- a/src/plugins/sample-python/sample_python.py +++ b/src/plugins/sample-python/sample_python.py @@ -1,8 +1,8 @@ import cutter -from PySide2.QtCore import Qt -from PySide2.QtWidgets import QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton class FortuneWidget(cutter.CutterDockWidget): @@ -19,7 +19,6 @@ class FortuneWidget(cutter.CutterDockWidget): content.setLayout(layout) self.text = QLabel(content) self.text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) - self.text.setFont(cutter.Configuration.instance().getFont()) layout.addWidget(self.text) button = QPushButton(content)