diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 8a958d24..4000ef02 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -165,9 +165,9 @@ jobs: run: | scripts/fetch_deps.sh source cutter-deps/env.sh + set -euo pipefail export PATH=/usr/local/opt/llvm/bin:$PATH source scripts/prepare_breakpad_macos.sh - export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:$CUSTOM_PYTHON_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH" mkdir build cd build cmake \ @@ -180,9 +180,15 @@ jobs: -DCUTTER_ENABLE_CRASH_REPORTS=ON \ -DCUTTER_USE_BUNDLED_RIZIN=ON \ -DCUTTER_ENABLE_PACKAGING=ON \ - -DBREAKPAD_FRAMEWORK_DIR="$BREAKPAD_FRAMEWORK_DIR" \ + -DCUTTER_PACKAGE_DEPENDENCIES=ON \ + -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \ + -DCUTTER_PACKAGE_RZ_GHIDRA=ON \ + -DCMAKE_FRAMEWORK_PATH="$BREAKPAD_FRAMEWORK_DIR" \ .. && \ make -j4; + make package + export CUTTER_VERSION=$(python3 ../scripts/get_version.py) + echo PACKAGE_NAME=Cutter-${CUTTER_VERSION}-Darwin.dmg >> $GITHUB_ENV - name: windows dependencies if: contains(matrix.os, 'windows') shell: bash diff --git a/cmake/CutterInstallDirs.cmake b/cmake/CutterInstallDirs.cmake index 52328b8c..645d66d8 100644 --- a/cmake/CutterInstallDirs.cmake +++ b/cmake/CutterInstallDirs.cmake @@ -6,8 +6,16 @@ if(WIN32) 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") + if (CUTTER_ENABLE_PACKAGING) + 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(CMAKE_INSTALL_BINDIR "../MacOS" CACHE PATH "Executable install directory") # BUNDLE step sets prefix to Resources + set(CUTTER_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}" CACHE PATH "Resource installation directory") + else() + include(GNUInstallDirs) + set(CUTTER_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}/${CUTTER_DIR_NAME}" CACHE PATH "Resource installation directory") + endif() else() include(GNUInstallDirs) set(CUTTER_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}/${CUTTER_DIR_NAME}" CACHE PATH "Resource installation directory") diff --git a/cmake/FindBreakpad.cmake b/cmake/FindBreakpad.cmake index c6bc5424..3cc01ee8 100644 --- a/cmake/FindBreakpad.cmake +++ b/cmake/FindBreakpad.cmake @@ -33,6 +33,11 @@ if(WIN32) set (Breakpad_LINK_LIBRARIES ${Breakpad_LIBRARIES}) set(Breakpad_LIBRARY_DIRS "") +elseif(APPLE) + find_library(Breakpad_LINK_LIBRARIES Breakpad REQUIRED) + set(Breakpad_LIBRARIES ${Breakpad_LINK_LIBRARIES}) + # Assumes Breakpad is packed as Framework + set(Breakpad_INCLUDE_DIRS "${Breakpad_LINK_LIBRARIES}/Headers") else() set(Breakpad_CMAKE_PREFIX_PATH_TEMP ${CMAKE_PREFIX_PATH}) list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Breakpad/prefix") diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt index 3a6fce50..38e8eb98 100644 --- a/dist/CMakeLists.txt +++ b/dist/CMakeLists.txt @@ -45,16 +45,29 @@ if(APPLE) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in" "${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist") set(CPACK_BUNDLE_PLIST "${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist") set(CPACK_BUNDLE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/macos/cutter.icns") + set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_CURRENT_SOURCE_DIR}/macos/cutter_mac_app.png") + set(CPACK_DMG_DS_STORE "${CMAKE_CURRENT_SOURCE_DIR}/macos/DS_Store_ForDMGBackground") + find_program(MACDEPLOYQT_PATH macdeployqt HINTS "${Qt5_DIR}/../../../bin") if(NOT MACDEPLOYQT_PATH) message(FATAL_ERROR "Failed to find macdeployqt") endif() - set(CUTTER_SH_PATH "${CMAKE_CURRENT_SOURCE_DIR}/macos/Cutter.sh") + unset(ADJUST_RIZIN_LIBS) foreach(_lib ${RZ_LIBS}) list(APPEND ADJUST_RIZIN_LIBS "${RIZIN_INSTALL_DIR}/lib/lib${_lib}.dylib") endforeach() + + 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(PYSIDE_PREFIX "${CUTTER_DEPS}/pyside") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/MacOSBundlePython.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/MacOSBundlePython.cmake" @ONLY) + install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/MacOSBundlePython.cmake") + endif() + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/MacOSSetupBundle.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/MacOSSetupBundle.cmake" @ONLY) install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/MacOSSetupBundle.cmake") @@ -63,14 +76,7 @@ if(APPLE) list(APPEND RZ_GHIDRA_PREFIX_PATH "${QT_PREFIX}") set(RIZIN_INSTALL_PLUGDIR "share/rizin/plugins") # escaped backslash on purpose, should be evaluated later - if(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(PYSIDE_PREFIX "${CUTTER_DEPS}/pyside") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/MacOSBundlePython.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/MacOSBundlePython.cmake" @ONLY) - install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/MacOSBundlePython.cmake") - endif() + endif() ################################################ diff --git a/dist/MacOSBundlePython.cmake.in b/dist/MacOSBundlePython.cmake.in index 09ddb5fe..19fcf035 100644 --- a/dist/MacOSBundlePython.cmake.in +++ b/dist/MacOSBundlePython.cmake.in @@ -12,7 +12,7 @@ execute_process(COMMAND "${EMBED_PYTHON_SH}" "${PYTHON_FRAMEWORK_DIR}" "${BUNDLE_PATH}" - "${BUNDLE_PATH}/MacOS/Cutter.bin" + "${BUNDLE_PATH}/Contents/MacOS/Cutter" RESULT_VARIABLE SCRIPT_RESULT) if(SCRIPT_RESULT) message(FATAL_ERROR "Failed to bundle python") diff --git a/dist/MacOSSetupBundle.cmake.in b/dist/MacOSSetupBundle.cmake.in index ed2d6c71..807d2f00 100644 --- a/dist/MacOSSetupBundle.cmake.in +++ b/dist/MacOSSetupBundle.cmake.in @@ -1,8 +1,12 @@ +include(BundleUtilities) set(MACDEPLOYQT_PATH "@MACDEPLOYQT_PATH@") set(INFO_PLIST_PATH "@CPACK_BUNDLE_PLIST@") -set(CUTTER_SH_PATH "@CUTTER_SH_PATH@") set(ADJUST_RIZIN_LIBS "@ADJUST_RIZIN_LIBS@") +set(CUTTER_ENABLE_CRASH_REPORTS "@CUTTER_ENABLE_CRASH_REPORTS@") +set(Breakpad_LINK_LIBRARIES "@Breakpad_LINK_LIBRARIES@") +set(CUTTER_PACKAGE_DEPENDENCIES "@CUTTER_PACKAGE_DEPENDENCIES@") +set(CUTTER_ENABLE_PYTHON "@CUTTER_ENABLE_PYTHON@") macro(run_or_die) execute_process(${ARGV} RESULT_VARIABLE PROC_RESULT) @@ -13,25 +17,33 @@ endmacro() get_filename_component(BUNDLE_PATH "${CMAKE_INSTALL_PREFIX}/../.." ABSOLUTE) set(EXECUTABLE_DIR "${BUNDLE_PATH}/Contents/MacOS") -file(MAKE_DIRECTORY "${EXECUTABLE_DIR}") -# Move the executable from Resources/bin/Cutter to MacOS/Cutter -# macdeployqt can't handle anything else -file(RENAME "${CMAKE_INSTALL_PREFIX}/bin/Cutter" "${EXECUTABLE_DIR}/Cutter") -# Make a symlink from the original location so CutterConfig.cmake won't be broken -run_or_die(COMMAND ${CMAKE_COMMAND} -E create_symlink "../../MacOS/Cutter.bin" "${CMAKE_INSTALL_PREFIX}/bin/Cutter") +set(FRAMEWORK_DIR "${BUNDLE_PATH}/Contents/Frameworks") # Copying the Info.plist will be done later again by CPack but we need it a bit earlier # so macdeployqt has enough info. file(COPY "${INFO_PLIST_PATH}" DESTINATION "${BUNDLE_PATH}/Contents") +# replace absolute path from build directory in rizin pkgconfig files with relative ones +file(GLOB RZ_PCFILES "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig/rz_*.pc") +list(APPEND RZ_PCFILES "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig/librz.pc") +foreach (_pcfile ${RZ_PCFILES}) + file(READ "${_pcfile}" _text) + string(REGEX REPLACE "^prefix=[^\n]*\n" "prefix=\${pcfiledir}/../..\n" _text "${_text}") + file(WRITE "${_pcfile}" "${_text}") +endforeach() # macdeployqt would put the rz libraries into Contents/Frameworks by default, but we want to have them # only in the prefix, so we fix the paths manually. foreach(_lib ${ADJUST_RIZIN_LIBS}) get_filename_component(_lib "${_lib}" REALPATH) get_filename_component(_name "${_lib}" NAME) + # Cutter reference to lib run_or_die(COMMAND install_name_tool -change "${_lib}" "@rpath/${_name}" "${EXECUTABLE_DIR}/Cutter") + # lib LC_ID_DYLIB + run_or_die(COMMAND install_name_tool + -id "@rpath/${_name}" + "${CMAKE_INSTALL_PREFIX}/lib/${_name}") # Fix every lib for every lib too foreach(_lib2 ${ADJUST_RIZIN_LIBS}) get_filename_component(_lib2 "${_lib2}" REALPATH) @@ -50,10 +62,17 @@ run_or_die(COMMAND install_name_tool -add_rpath "@executable_path/../Resources/lib" "${EXECUTABLE_DIR}/Cutter") -set(MACDEPLOYQT_COMMAND "${MACDEPLOYQT_PATH}" "${BUNDLE_PATH}" "-libpath=${CMAKE_INSTALL_PREFIX}/lib") +set(MACDEPLOYQT_COMMAND "${MACDEPLOYQT_PATH}" "${BUNDLE_PATH}" "-verbose=2" "-libpath=${CMAKE_INSTALL_PREFIX}/lib") message("Running macdeployqt \"${MACDEPLOYQT_COMMAND}\"") -run_or_die(COMMAND ${MACDEPLOYQT_COMMAND}) -run_or_die(COMMAND ${MACDEPLOYQT_COMMAND}) +run_or_die(COMMAND ${MACDEPLOYQT_COMMAND}) # First run +if (CUTTER_PACKAGE_DEPENDENCIES AND CUTTER_ENABLE_PYTHON) + file(REAL_PATH "${FRAMEWORK_DIR}/Python.framework/Python" _python_lib_path) + message("Python lib ${_python_lib_path}") + list(APPEND MACDEPLOYQT_COMMAND "-executable=${_python_lib_path}") +endif() +# Qt plugins are not getting deployed on first macdeployqt run. Runing twice helps. +# If python is added as additional executable in first run it also breaks plugin copying. +run_or_die(COMMAND ${MACDEPLOYQT_COMMAND}) # Second run # Clean up the mess that macdeployqt made (duplicate rz libs, we only want the ones in the prefix) foreach(_lib ${ADJUST_RIZIN_LIBS}) @@ -62,8 +81,8 @@ foreach(_lib ${ADJUST_RIZIN_LIBS}) file(REMOVE "${BUNDLE_PATH}/Contents/Frameworks/${_name}") endforeach() -# Replace main executable by Cutter.sh -file(RENAME "${EXECUTABLE_DIR}/Cutter" "${EXECUTABLE_DIR}/Cutter.bin") -file(COPY "${CUTTER_SH_PATH}" DESTINATION "${EXECUTABLE_DIR}") -file(RENAME "${EXECUTABLE_DIR}/Cutter.sh" "${EXECUTABLE_DIR}/Cutter") - +if (CUTTER_ENABLE_CRASH_REPORTS) + message("Copying Breakpad ${Breakpad_LINK_LIBRARIES}") + set(_breakpad_lib "Versions/A/Breakpad") + copy_resolved_framework_into_bundle("${Breakpad_LINK_LIBRARIES}/${_breakpad_lib}" "${FRAMEWORK_DIR}/Breakpad.framework/${_breakpad_lib}") +endif() diff --git a/dist/appbundle_embed_python.sh b/dist/appbundle_embed_python.sh index c41159f2..e8cf2a8f 100755 --- a/dist/appbundle_embed_python.sh +++ b/dist/appbundle_embed_python.sh @@ -1,5 +1,6 @@ #!/bin/bash +set -euo pipefail if ! [[ $# -eq 3 ]]; then echo "Usage: $0 [Python.framework] [AppBundle.app] [AppBundle.app/Contents/MacOS/Executable]" exit 1 @@ -11,19 +12,24 @@ py_framework=$1 appbundle=$2 executable=$3 -echo "Embedding $py_framework into $appbundle" +echo "Embedding $py_framework into $appbundle | $executable" -mkdir -p "$appbundle/Contents/Frameworks" || exit 1 -cp -a "$py_framework" "$appbundle/Contents/Frameworks/" || exit 1 +mkdir -p "$appbundle/Contents/Frameworks" +if [ ! -d "$appbundle/Contents/Frameworks/Python.framework" ] +then + cp -a -n "$py_framework" "$appbundle/Contents/Frameworks/" + echo "Cleaning up embedded Python Framework" + cd "$appbundle/Contents/Frameworks/Python.framework" + find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf + rm -r Versions/Current/Resources/* "Versions/Current/lib/$python_version/test" "Versions/Current/lib/$python_version/idlelib" "Versions/Current/lib/$python_version/curses" "Versions/Current/lib/$python_version/lib2to3" || echo "Couldn't remove something" +else + echo "Python.framework already exists, skipping copying" + cd "$appbundle/Contents/Frameworks/Python.framework" +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 "Cleaning up embedded Python Framework" -cd "$appbundle/Contents/Frameworks/Python.framework" || exit 1 -find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf || exit 1 -rm -r Versions/Current/Resources/* Versions/Current/lib/$python_version/test Versions/Current/lib/$python_version/idlelib Versions/Current/lib/$python_version/curses Versions/Current/lib/$python_version/lib2to3 || exit 1 - echo "Checking if PySide2 is available" pyside_prefix=$(pkg-config --variable=prefix pyside2) @@ -34,7 +40,12 @@ fi echo "PySide is at $pyside_prefix" -cp -va "$pyside_prefix/lib/$python_version/" "Versions/Current/lib/$python_version" || exit 1 -cd .. # $appbundle/Contents/Frameworks -cp -va "$pyside_prefix/lib/"*.dylib . || exit 1 +if [ ! -d "Versions/Current/lib/$python_version/site-packages/PySide2" ] +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" +fi diff --git a/dist/macos/Cutter.sh b/dist/macos/Cutter.sh deleted file mode 100755 index 8a536594..00000000 --- a/dist/macos/Cutter.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -echo "Cutter Launch Script for macOS 🥞" - -HERE=$(dirname "$0") -PREFIX=$HERE/../Resources -export DYLD_LIBRARY_PATH="$PREFIX/lib" -export DYLD_FRAMEWORK_PATH="$PREFIX/lib" -"$HERE/Cutter.bin" "$@" diff --git a/dist/macos/DS_Store_ForDMGBackground b/dist/macos/DS_Store_ForDMGBackground new file mode 100644 index 00000000..d38aded7 Binary files /dev/null and b/dist/macos/DS_Store_ForDMGBackground differ diff --git a/scripts/prepare_breakpad_macos.sh b/scripts/prepare_breakpad_macos.sh index a8ec36fb..0a790f4e 100755 --- a/scripts/prepare_breakpad_macos.sh +++ b/scripts/prepare_breakpad_macos.sh @@ -1,22 +1,24 @@ #!/bin/sh +set -euo pipefail + SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")") DIR="$SCRIPTPATH/.." -cd "$DIR" || exit 1 +cd "$DIR" BREAKPAD_FRAMEWORK_DIR="$DIR/breakpad/framework" BREAKPAD_DUMP_SYMS_DIR="$DIR/breakpad/bin" -git clone https://github.com/google/breakpad.git || exit 1 +git clone https://github.com/google/breakpad.git mkdir $BREAKPAD_FRAMEWORK_DIR mkdir $BREAKPAD_DUMP_SYMS_DIR -cd breakpad || exit 1 -git checkout 4d550cceca107f36c4bc1ea1126b7d32cc50f424 || exit 1 -git apply "$SCRIPTPATH/breakpad_macos.patch" || exit 1 -cd src/client/mac/ && xcodebuild -sdk macosx || exit 1 -cp -R build/Release/Breakpad.framework "$BREAKPAD_FRAMEWORK_DIR" || exit 1 +cd breakpad +git checkout 4d550cceca107f36c4bc1ea1126b7d32cc50f424 +git apply "$SCRIPTPATH/breakpad_macos.patch" +cd src/client/mac/ && xcodebuild -sdk macosx +cp -R build/Release/Breakpad.framework "$BREAKPAD_FRAMEWORK_DIR" -cd $DIR/breakpad || exit 1 -cp -R src/. framework/Breakpad.framework/Headers || exit 1 +cd $DIR/breakpad +cp -R src/. framework/Breakpad.framework/Headers export BREAKPAD_FRAMEWORK_DIR=$BREAKPAD_FRAMEWORK_DIR cd $DIR diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3abb3b84..e9fd48d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,15 +74,11 @@ if(CUTTER_ENABLE_CRASH_REPORTS) target_link_libraries(Cutter PRIVATE Threads::Threads) add_definitions(-DCUTTER_ENABLE_CRASH_REPORTS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g ") - if(DEFINED BREAKPAD_FRAMEWORK_DIR) - include_directories("${BREAKPAD_FRAMEWORK_DIR}/Breakpad.framework/Headers") - set_target_properties(Cutter PROPERTIES LINK_FLAGS "-Wl,-F${BREAKPAD_FRAMEWORK_DIR}") - target_link_libraries(Cutter PRIVATE "-framework Breakpad") - else() - find_package(Breakpad REQUIRED) - target_link_libraries(Cutter PRIVATE Breakpad::client) + if (NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g ") endif() + find_package(Breakpad REQUIRED) + target_link_libraries(Cutter PRIVATE Breakpad::client) endif() target_link_libraries(Cutter PUBLIC ${QT_PREFIX}::Core ${QT_PREFIX}::Widgets ${QT_PREFIX}::Gui PRIVATE ${QT_PREFIX}::Svg ${QT_PREFIX}::Network)