File indexing completed on 2024-05-12 04:28:29

0001 #!/usr/bin/env zsh
0002 #
0003 #  SPDX-License-Identifier: GPL-3.0-or-later
0004 #
0005 
0006 # Krita tool to create dmg from installed source
0007 # Copies all files to a folder to be converted into the final dmg
0008 
0009 # osxdeploy.sh automates the creation of the release DMG.
0010 #       default background and style are used if none provided
0011 
0012 # A short explanation of what it does:
0013 
0014 # - Copies krita.app contents to kritadmg folder
0015 # - Copies i/share to Contents/Resources excluding unnecessary files
0016 # - Copies translations, qml and quicklook PlugIns
0017 # - Copies i/plugins and i/lib/plugins to Contents/PlugIns
0018 
0019 # - Runs macdeployqt: macdeployqt is not built by default in ext_qt
0020 #     build by:
0021 #       cd ${BUILDROOT}/depbuild/ext_qt/ext_qt-prefix/src/ext_qt/qttools/src
0022 #       make sub-macdeployqt-all
0023 #       make sub-macdeployqt-install_subtargets
0024 #       make install
0025 
0026 #     the script changes dir to installation/bin to run macdeployqt as it can be buggy
0027 #     if not run from the same folder as the binary is on.
0028 
0029 # - Fix rpath from krita bin
0030 # - Find missing libraries from plugins and copy to Frameworks or plugins.
0031 #     This uses oTool iterative to find all unique libraries, then it searches each
0032 #     library fond in <kritadmg> folder, and if not found attempts to copy contents
0033 #     to the appropriate folder, either Frameworks (if frameworks is in namefile, or
0034 #         library has plugin is not in path), or plugin if otherwise.
0035 
0036 # - Builds DMG
0037 #     Building DMG creates a new dmg with the contents of <kritadmg>
0038 #     mounts the dmg and sets the style for dmg.
0039 #     unmount
0040 #     Compress resulting dmg into krita_nightly-<gitsha>.dmg
0041 #     deletes temporary files.
0042 
0043 if [[ -n "$ZSH_VERSION" ]]; then
0044     emulate -L ksh;
0045 fi
0046 
0047 if [[ -z $BUILDROOT ]]; then
0048     echo "ERROR: BUILDROOT env not set!"
0049     echo "\t Must point to the root of the buildfiles as stated in 3rdparty Readme"
0050     echo "exiting..."
0051     exit 1
0052 fi
0053 
0054 BUILDROOT="${BUILDROOT%/}"
0055 
0056 # print status messages
0057 print_msg() {
0058     printf "\e[32m${1}\e[0m\n" "${@:2}"
0059     # printf "%s\n" "${1}" >> ${OUTPUT_LOG}
0060 }
0061 
0062 # print error
0063 print_error() {
0064     printf "\e[31m%s %s\e[0m\n" "Error:" "${1}"
0065 }
0066 
0067 get_script_dir() {
0068     if [[ -n "$ZSH_VERSION" ]]; then
0069         script_source="${(%):-%x}"
0070     else
0071         # transitional, macos should use ZSH
0072         script_source="${BASH_SOURCE[0]}"
0073     fi
0074     # go to target until finding root.
0075     while [ -L "${script_source}" ]; do
0076         script_target="$(readlink ${script_source})"
0077         if [[ "${script_source}" = /* ]]; then
0078             script_source="$script_target"
0079         else
0080             script_dir="$(dirname "${script_source}")"
0081             script_source="${script_dir}/${script_target}"
0082         fi
0083     done
0084     echo "$(dirname ${script_source})"
0085 }
0086 
0087 DMG_title="krita" #if changed krita.temp.dmg must be deleted manually
0088 SCRIPT_SOURCE_DIR="$(get_script_dir)"
0089 
0090 # There is some duplication between build and deploy scripts
0091 # a config env file could would be a nice idea.
0092 if [[ -z "${KIS_SRC_DIR}" ]]; then
0093     KIS_SRC_DIR=${BUILDROOT}/krita
0094 fi
0095 if [[ -z "${KIS_BUILD_DIR}" ]]; then
0096     KIS_BUILD_DIR=${BUILDROOT}/kisbuild
0097 fi
0098 KIS_INSTALL_DIR=${BUILDROOT}/i
0099 KRITA_DMG=${BUILDROOT}/kritadmg
0100 KRITA_DMG_TEMPLATE=${BUILDROOT}/kritadmg-template
0101 
0102 export PATH=${KIS_INSTALL_DIR}/bin:$PATH
0103 
0104 # flags for OSX environment
0105 # We only support from 10.13 up
0106 export MACOSX_DEPLOYMENT_TARGET=10.13
0107 export QMAKE_MACOSX_DEPLOYMENT_TARGET=10.13
0108 
0109 KRITA_VERSION="$(${KIS_INSTALL_DIR}/bin/krita_version -v)"
0110 
0111 
0112 print_usage () {
0113     printf "USAGE:
0114   osxdeploy.sh [-s=<identity>] [-notarize-ac=<apple-account>] [-style=<style.txt>] [-bg=<background-image>]
0115 
0116     -s \t\t\t Code sign identity for codesign
0117 
0118     -notarize-ac \t Apple account name for notarization purposes
0119 \t\t\t script will attempt to get password from keychain, if fails provide one with
0120 \t\t\t the -notarize-pass option: To add a password run
0121 
0122 \t\t\t   security add-generic-password -a \"AC_USERNAME\" -w <secret_password> -s \"AC_PASSWORD\"
0123 
0124     -notarize-pass \t If given, the Apple account password. Otherwise an attempt will be macdeployqt_exists
0125 \t\t\t to get the password from keychain using the account given in <notarize-ac> option.
0126 
0127     -asc-provider \t some AppleIds might need this option pass the <shortname>
0128 
0129     -style \t\t Style file defined from 'dmgstyle.sh' output
0130 
0131     -bg \t\t Set a background image for dmg folder.
0132 
0133     -name \t\t Set the DMG name output.
0134 
0135 \t\t\t osxdeploy needs an input image to attach to the dmg background
0136 \t\t\t image recommended size is at least 950x500
0137 "
0138 }
0139 
0140 # Attempt to detach previous mounted DMG
0141 if [[ -d "/Volumes/${DMG_title}" ]]; then
0142     echo "WARNING: Another Krita DMG is mounted!"
0143     echo "Attempting eject…"
0144     hdiutil detach "/Volumes/${DMG_title}"
0145     if [ $? -ne 0  ]; then
0146         exit 1
0147     fi
0148     echo "Success!"
0149 fi
0150 
0151 # -- Parse input args
0152 for arg in "${@}"; do
0153     if [ "${arg}" = -bg=* -a -f "${arg#*=}" ]; then
0154         DMG_validBG=0
0155         bg_filename=${arg#*=}
0156         echo "attempting to check background is valid jpg or png..."
0157         BG_FORMAT=$(sips --getProperty format ${bg_filename} | awk '{printf $2}')
0158 
0159         if [[ "png" = ${BG_FORMAT} || "jpeg" = ${BG_FORMAT} ]];then
0160             echo "valid image file"
0161             DMG_background=$(cd "$(dirname "${bg_filename}")"; pwd -P)/$(basename "${bg_filename}")
0162             DMG_validBG=1
0163             # check imageDPI
0164             BG_DPI=$(sips --getProperty dpiWidth ${DMG_background} | grep dpi | awk '{print $2}')
0165             if [[ $(echo "${BG_DPI} > 150" | bc -l) -eq 1 ]]; then
0166             printf "WARNING: image dpi has an effect on apparent size!
0167     Check dpi is adequate for screen display if image appears very small
0168     Current dpi is: %s\n" ${BG_DPI}
0169             fi
0170         fi
0171     fi
0172     # If string starts with -sign
0173     if [[ ${arg} = -s=* ]]; then
0174         CODE_SIGNATURE="${arg#*=}"
0175     fi
0176 
0177     if [[ ${arg} = -name=* ]]; then
0178         DMG_NAME="${arg#*=}"
0179     fi
0180 
0181     if [[ ${arg} = -notarize-ac=* ]]; then
0182         NOTARIZE_ACC="${arg#*=}"
0183     fi
0184 
0185     if [[ ${arg} = -notarize-pass=* ]]; then
0186         NOTARIZE_PASS="${arg#*=}"
0187     fi
0188 
0189     if [[ ${arg} = -style=* ]]; then
0190         style_filename="${arg#*=}"
0191         if [[ -f "${style_filename}" ]]; then
0192             DMG_STYLE="${style_filename}"
0193         fi
0194     fi
0195 
0196     if [[ ${arg} = -asc-provider=* ]]; then
0197         APPLE_TEAMID="${arg#*=}"
0198     fi
0199 
0200     if [[ ${arg} = "-h" || ${arg} = "--help" ]]; then
0201         print_usage
0202         exit 1
0203     fi
0204 done
0205 
0206 # -- Checks and messages
0207 
0208 ### PYTHONAttempt to find python_version
0209 local_PY_MAYOR_VERSION=$(python -c "import sys; print(sys.version_info[0])")
0210 local_PY_MINOR_VERSION=$(python -c "import sys; print(sys.version_info[1])")
0211 PY_VERSION="${local_PY_MAYOR_VERSION}.${local_PY_MINOR_VERSION}"
0212 
0213 print_msg "Detected Python %s" "${PY_VERSION}"
0214 
0215 ### Code Signature & NOTARIZATION
0216 NOTARIZE="false"
0217 if [[ -z "${CODE_SIGNATURE}" ]]; then
0218     echo "WARNING: No code signature provided, Code will not be signed"
0219 else
0220     print_msg "Code will be signed with %s" "${CODE_SIGNATURE}"
0221     ### NOTARIZATION
0222     # check if we can perform notarization using notarytool
0223     xcrun notarytool history --keychain-profile KritaNotarizeAccount 1> /dev/null
0224     if [[ ${?} -eq 0 ]]; then
0225         NOTARYTOOL="short"
0226         NOTARIZE="true"
0227     fi
0228 
0229     if [[ ${NOTARIZE} = "false" && -n "${NOTARIZE_ACC}" ]]; then
0230         NOTARIZE="true"
0231         ASC_PROVIDER_OP=""
0232 
0233         if [[ -z ${APPLE_TEAMID} ]]; then
0234             echo "No team id provided, extracting from signature"
0235             APPLE_TEAMID=${CODE_SIGNATURE[-11,-2]}
0236         fi
0237 
0238         if [[ -n "${APPLE_TEAMID}" ]]; then
0239             ASC_PROVIDER_OP="--asc-provider ${APPLE_TEAMID}"
0240         fi
0241 
0242         if [[ -z "${NOTARIZE_PASS}" ]]; then
0243             NOTARIZE_PASS="@keychain:AC_PASSWORD"
0244             KEYCHAIN_PASS="true"
0245         fi
0246 
0247         # check if we can perform notarization
0248         
0249         xcrun notarytool history --apple-id "${NOTARIZE_ACC}" --password "${NOTARIZE_PASS}" --team-id "${APPLE_TEAMID}" 1> /dev/null
0250         if [[ ${?} -ne 0 ]]; then
0251             echo "Unable to use notarytool: not setup/missing password, trying altool"
0252             ALTOOL="true"
0253             xcrun altool --notarization-history 0 --username "${NOTARIZE_ACC}" --password "${NOTARIZE_PASS}" ${ASC_PROVIDER_OP} 1> /dev/null
0254 
0255             if [[ ${?} -ne 0 ]]; then
0256                 NOTARIZE="false"
0257                 echo "No password given for notarization or AC_PASSWORD missing in keychain"
0258             fi
0259         else
0260             NOTARYTOOL="long"
0261         fi
0262     fi
0263 fi
0264 
0265 if [[ ${NOTARIZE} = "true" ]]; then
0266     print_msg "Notarization checks complete, This build will be notarized"
0267 else
0268     echo "WARNING: Account information missing, Notarization will not be performed"
0269 fi
0270 
0271 
0272 ### STYLE for DMG
0273 if [[ ! ${DMG_STYLE} ]]; then
0274     DMG_STYLE="${SCRIPT_SOURCE_DIR}/default.style"
0275 fi
0276 
0277 print_msg "Using style from: %s" "${DMG_STYLE}"
0278 
0279 ### Background for DMG
0280 if [[ ${DMG_validBG} -eq 0 ]]; then
0281     echo "No jpg or png valid file detected!!"
0282     echo "Using default style"
0283     DMG_background="${SCRIPT_SOURCE_DIR}/krita_dmgBG.jpg"
0284 fi
0285 
0286 
0287 
0288 # Helper functions
0289 countArgs () {
0290     echo "${#}"
0291 }
0292 
0293 stringContains () {
0294     echo "$(grep "${2}" <<< "${1}")"
0295 }
0296 
0297 waiting_fixed() {
0298     local message="${1}"
0299     local waitTime=${2}
0300 
0301     for i in $(seq ${waitTime}); do
0302         sleep 1
0303         printf -v dots '%*s' ${i}
0304         printf -v spaces '%*s' $((${waitTime} - $i))
0305         printf "\r%s [%s%s]" "${message}" "${dots// /.}" "${spaces}"
0306     done
0307     printf "\n"
0308 }
0309 
0310 add_lib_to_list() {
0311     local llist=${2}
0312     if [[ -z "$(grep ${1##*/} <<< ${llist})" ]]; then
0313         local llist="${llist} ${1##*/} "
0314     fi
0315     echo "${llist}"
0316 }
0317 
0318 # Find all @rpath and Absolute to buildroot path libs
0319 # Add to libs_used
0320 # converts absolute buildroot path to @rpath
0321 find_needed_libs () {
0322     # echo "Analyzing libraries with oTool..." >&2
0323     local libs_used="" # input lib_lists founded
0324 
0325     for libFile in ${@}; do
0326         if [[ -z "$(file ${libFile} | grep 'Mach-O')" ]]; then
0327             # echo "skipping ${libFile}" >&2
0328             continue
0329         fi
0330 
0331         resultArray=($(otool -L ${libFile} | awk '{print $1","substr($2,2)}'))
0332 
0333         printf "Fixing %s\n" "${libFile#${KRITA_DMG}/}" >&2
0334         for entry in ${resultArray[@]:1}; do
0335             # skip fat-bin file markers
0336             if [[ "${entry##*,}" = "architecture" ]]; then
0337                 continue
0338             fi
0339 
0340             lib="${entry%%,*}"
0341 
0342             if [[ "${lib:0:1}" = "@" ]]; then
0343                 local libs_used=$(add_lib_to_list "${lib}" "${libs_used}")
0344             fi
0345 
0346             if [[ "${lib:0:${#BUILDROOT}}" = "${BUILDROOT}" ]]; then
0347                 printf "\t%s\n" "${lib}" >&2
0348                 if [[ "${lib##*/}" = "${libFile##*/}" ]]; then
0349                     install_name_tool -id ${lib##*/} "${libFile}"
0350                 else
0351                     install_name_tool -change ${lib} "@rpath/${lib##*/i/lib/}" "${libFile}"
0352                     local libs_used=$(add_lib_to_list "${lib}" "${libs_used}")
0353                 fi
0354             fi
0355         done
0356     done
0357     echo "${libs_used}" # return updated list
0358 }
0359 
0360 find_missing_libs (){
0361     # echo "Searching for missing libs on deployment folders…" >&2
0362     local libs_missing=""
0363     for lib in ${@}; do
0364         if [[ -z "$(find ${KRITA_DMG}/krita.app/Contents/ -name ${lib})" ]]; then
0365             # echo "Adding ${lib} to missing libraries." >&2
0366             libs_missing="${libs_missing} ${lib}"
0367         fi
0368     done
0369     echo "${libs_missing}"
0370 }
0371 
0372 copy_missing_libs () {
0373     for lib in ${@}; do
0374         result=$(find -L "${BUILDROOT}/i" -name "${lib}")
0375 
0376         if [[ $(countArgs ${result}) -eq 1 ]]; then
0377             if [ "$(stringContains "${result}" "plugin")" ]; then
0378                 cp -pv ${result} ${KRITA_DMG}/krita.app/Contents/PlugIns/
0379                 krita_findmissinglibs "${KRITA_DMG}/krita.app/Contents/PlugIns/${result##*/}"
0380             else
0381                 cp -pv ${result} ${KRITA_DMG}/krita.app/Contents/Frameworks/
0382                 krita_findmissinglibs "${KRITA_DMG}/krita.app/Contents/Frameworks/${result##*/}"
0383             fi
0384         else
0385             echo "${lib} might be a missing framework"
0386             if [ "$(stringContains "${result}" "framework")" ]; then
0387                 echo "copying framework ${BUILDROOT}/i/lib/${lib}.framework to dmg"
0388                 # rsync only included ${lib} Resources Versions
0389                 rsync -priul ${BUILDROOT}/i/lib/${lib}.framework/${lib} ${KRITA_DMG}/krita.app/Contents/Frameworks/${lib}.framework/
0390                 rsync -priul ${BUILDROOT}/i/lib/${lib}.framework/Resources ${KRITA_DMG}/krita.app/Contents/Frameworks/${lib}.framework/
0391                 rsync -priul ${BUILDROOT}/i/lib/${lib}.framework/Versions ${KRITA_DMG}/krita.app/Contents/Frameworks/${lib}.framework/
0392                 krita_findmissinglibs "$(find "${KRITA_DMG}/krita.app/Contents/Frameworks/${lib}.framework/" -type f -perm 755)"
0393             fi
0394         fi
0395     done
0396 }
0397 
0398 krita_findmissinglibs() {
0399     neededLibs=$(find_needed_libs "${@}")
0400     missingLibs=$(find_missing_libs ${neededLibs})
0401 
0402     if [[ $(countArgs ${missingLibs}) -gt 0 ]]; then
0403         printf "Found missing libs: %s\n" "${missingLibs}"
0404         copy_missing_libs ${missingLibs}
0405     fi
0406 }
0407 
0408 strip_python_dmginstall() {
0409     # reduce size of framework python
0410     # Removes tests, installers, pyenv, distutils
0411     echo "Removing unnecessary files from Python.Framework to be packaged..."
0412     PythonFrameworkBase="${KRITA_DMG}/krita.app/Contents/Frameworks/Python.framework"
0413 
0414     cd "${PythonFrameworkBase}"
0415     find . -name "test*" -type d | xargs rm -rf
0416     find "${PythonFrameworkBase}/Versions/${PY_VERSION}/bin" -not -name "python*" \( -type f -or -type l \) | xargs rm -f
0417     cd "${PythonFrameworkBase}/Versions/${PY_VERSION}/lib/python${PY_VERSION}"
0418     rm -rf distutils tkinter ensurepip venv lib2to3 idlelib turtledemo
0419     # remove tkinter module
0420     rm "./lib-dynload/_tkinter.cpython-${local_PY_MAYOR_VERSION}${local_PY_MINOR_VERSION}-darwin.so"
0421 
0422     cd "${PythonFrameworkBase}/Versions/${PY_VERSION}/lib/python${PY_VERSION}/site-packages"
0423     rm -rf pip* PyQt_builder* setuptools* sip* easy-install.pth
0424 
0425     cd "${PythonFrameworkBase}/Versions/${PY_VERSION}/Resources"
0426     rm -rf Python.app
0427 }
0428 
0429 # Remove any missing rpath pointing to BUILDROOT
0430 libs_clean_rpath () {
0431     for libFile in ${@}; do
0432         rpath=($(otool -l "${libFile}" | grep "path ${BUILDROOT}" | awk '{$1=$1;print $2}'))
0433         for lpath in "${rpath[@]}"; do
0434             echo "removed rpath _${lpath}_ from ${libFile}"
0435             install_name_tool -delete_rpath "${lpath}" "${libFile}" 2> /dev/null
0436         done
0437     done
0438 }
0439 
0440 # Multithreaded version
0441 # of libs_clean_rpath, but makes assumptions
0442 delete_install_rpath() {
0443     xargs -P4 -I FILE install_name_tool -delete_rpath "${BUILDROOT}/i/lib" FILE 2> "${BUILDROOT}/deploy_error.log"
0444 }
0445 
0446 fix_python_framework() {
0447     # Fix python.framework rpath and slims down installation
0448     PythonFrameworkBase="${KRITA_DMG}/krita.app/Contents/Frameworks/Python.framework"
0449 
0450     # Fix permissions
0451     find "${PythonFrameworkBase}" -name "*.so" | xargs -P4 -I FILE chmod a+x FILE 2> "${BUILDROOT}/deploy_error.log"
0452     cd "${PythonFrameworkBase}/Versions/${PY_VERSION}/lib/python${PY_VERSION}"
0453     chmod a+x pydoc.py
0454 
0455     # Fix main library
0456     pythonLib="${PythonFrameworkBase}/Python"
0457     install_name_tool -id "${pythonLib##*/}" "${pythonLib}"
0458     install_name_tool -add_rpath @loader_path/../../../ "${pythonLib}" 2> /dev/null
0459     install_name_tool -change @loader_path/../../../../libintl.9.dylib @loader_path/../../../libintl.9.dylib "${pythonLib}"
0460 
0461     # Fix all executables
0462     install_name_tool -add_rpath @executable_path/../../../../../../../ "${PythonFrameworkBase}/Versions/Current/Resources/Python.app/Contents/MacOS/Python"
0463     install_name_tool -change "${KIS_INSTALL_DIR}/lib/Python.framework/Versions/${PY_VERSION}/Python" @executable_path/../../../../../../Python "${PythonFrameworkBase}/Versions/Current/Resources/Python.app/Contents/MacOS/Python"
0464     install_name_tool -add_rpath @executable_path/../../../../ "${PythonFrameworkBase}/Versions/Current/bin/python${PY_VERSION}"
0465     install_name_tool -add_rpath @executable_path/../../../../ "${PythonFrameworkBase}/Versions/Current/bin/python${PY_VERSION}m"
0466 
0467     # Fix rpaths from Python.Framework
0468     find "${PythonFrameworkBase}" -type f -perm 755 | delete_install_rpath
0469     find "${PythonFrameworkBase}/Versions/Current/lib/python${PY_VERSION}/site-packages/PyQt5" -type f -name "*.so" | delete_install_rpath
0470 }
0471 
0472 # Checks for macdeployqt
0473 # If not present attempts to install
0474 # If it fails shows an informative message
0475 # (For now, macdeployqt is fundamental to deploy)
0476 macdeployqt_exists() {
0477     printf "Checking for macdeployqt...  "
0478 
0479     if [[ ! -e "${KIS_INSTALL_DIR}/bin/macdeployqt" ]]; then
0480         printf "Not Found!\n"
0481         printf "Attempting to install macdeployqt\n"
0482 
0483         cd "${BUILDROOT}/depbuild/ext_qt/ext_qt-prefix/src/ext_qt/qttools/src"
0484         make sub-macdeployqt-all
0485         make sub-macdeployqt-install_subtargets
0486         make install
0487 
0488         if [[ ! -e "${KIS_INSTALL_DIR}/bin/macdeployqt" ]]; then
0489         printf "
0490 ERROR: Failed to install macdeployqt!
0491 
0492     Compile and install from qt source directory
0493     Source code to build could be located in qttools/src in qt source dir:
0494 
0495         ${BUILDROOT}/depbuild/ext_qt/ext_qt-prefix/src/ext_qt/qttools/src
0496 
0497     From the source dir, build and install:
0498 
0499         make sub-macdeployqt-all
0500         make sub-macdeployqt-install_subtargets
0501         make install
0502 "
0503         printf "\nexiting...\n"
0504         exit 1
0505         else
0506             echo "Done!"
0507         fi
0508     else
0509         echo "Found!"
0510     fi
0511 }
0512 
0513 krita_deploy () {
0514     # check for macdeployqt
0515     macdeployqt_exists
0516 
0517     cd ${BUILDROOT}
0518     # Update files in krita.app
0519     echo "Deleting previous kritadmg run..."
0520     rm -rf ./krita.dmg ${KRITA_DMG}
0521     # Copy new builtFiles
0522     echo "Preparing ${KRITA_DMG} for deployment..."
0523 
0524     echo "Copying krita.app..."
0525     mkdir "${KRITA_DMG}"
0526 
0527     rsync -prul ${KIS_INSTALL_DIR}/bin/krita.app ${KRITA_DMG}
0528     cp ${KIS_INSTALL_DIR}/bin/kritarunner ${KRITA_DMG}/krita.app/Contents/MacOS
0529     cp ${KIS_INSTALL_DIR}/bin/krita_version ${KRITA_DMG}/krita.app/Contents/MacOS
0530 
0531     mkdir -p ${KRITA_DMG}/krita.app/Contents/PlugIns
0532     mkdir -p ${KRITA_DMG}/krita.app/Contents/Frameworks
0533 
0534     echo "Copying share..."
0535     # Deletes old copies of translation and qml to be recreated
0536     cd ${KIS_INSTALL_DIR}/share/
0537     rsync -prul --delete ./ \
0538             --exclude krita_SRCS.icns \
0539             --exclude aclocal \
0540             --exclude doc \
0541             --exclude ECM \
0542             --exclude eigen3 \
0543             --exclude emacs \
0544             --exclude gettext \
0545             --exclude gettext-0.19.8 \
0546             --exclude info \
0547             --exclude kf5 \
0548             --exclude kservices5 \
0549             --exclude man \
0550             --exclude ocio \
0551             --exclude pkgconfig \
0552             --exclude mime \
0553             --exclude translations \
0554             --exclude qml \
0555             ${KRITA_DMG}/krita.app/Contents/Resources
0556 
0557 
0558     # support localized system menu entries from MacOS, bug #432685.
0559     echo "Creating locale lproj directories for localization"
0560     LOCALE_FILES=$(find "${KRITA_DMG}/krita.app/Contents/Resources/locale" -type d -depth 1)
0561     cd "${KRITA_DMG}/krita.app/Contents/Resources/"
0562     for LOCALE in ${LOCALE_FILES} ; do
0563         BASE=$(basename ${LOCALE})
0564         mkdir "$BASE.lproj"
0565     done
0566 
0567     cd ${BUILDROOT}
0568 
0569     echo "Copying translations..."
0570     rsync -prul ${KIS_INSTALL_DIR}/translations/ \
0571             ${KRITA_DMG}/krita.app/Contents/Resources/translations
0572 
0573     echo "Copying QuickLook plugin..."
0574     mkdir -p ${KRITA_DMG}/krita.app/Contents/Library/QuickLook
0575     rsync -prul ${KIS_INSTALL_DIR}/plugins/kritaquicklook.qlgenerator ${KRITA_DMG}/krita.app/Contents/Library/QuickLook
0576     echo "Copying Spotlight plugin..."
0577     mkdir -p ${KRITA_DMG}/krita.app/Contents/Library/Spotlight
0578     rsync -prul ${KIS_INSTALL_DIR}/plugins/kritaspotlight.mdimporter ${KRITA_DMG}/krita.app/Contents/Library/Spotlight
0579     # TODO fix and reenable - https://bugs.kde.org/show_bug.cgi?id=430553
0580     # echo "Copying QuickLook Thumbnailing extension..."
0581     # rsync -prul ${KIS_INSTALL_DIR}/plugins/kritaquicklookng.appex ${KRITA_DMG}/krita.app/Contents/PlugIns
0582 
0583     cd ${KRITA_DMG}/krita.app/Contents
0584     ln -shF Resources share
0585 
0586     echo "Copying qml..."
0587     rsync -prul ${KIS_INSTALL_DIR}/qml Resources/qml
0588 
0589     echo "Copying plugins..."
0590     local KRITA_DMG_PLUGIN_DIR="${KRITA_DMG}/krita.app/Contents/PlugIns"
0591     # exclude kritaquicklook.qlgenerator/
0592     cd ${KIS_INSTALL_DIR}/plugins/
0593     rsync -prul --delete --delete-excluded ./ \
0594         --exclude kritaquicklook.qlgenerator \
0595         --exclude kritaspotlight.mdimporter \
0596         ${KRITA_DMG_PLUGIN_DIR}
0597 
0598     cd ${BUILDROOT}
0599     rsync -prul ${KIS_INSTALL_DIR}/lib/kritaplugins/* ${KRITA_DMG_PLUGIN_DIR}
0600 
0601     rsync -prul ${KIS_INSTALL_DIR}/lib/mlt ${KRITA_DMG_PLUGIN_DIR}
0602     
0603     echo "Adding ffmpeg to bundle"
0604     cd ${KRITA_DMG}/krita.app/Contents/MacOS
0605     cp ${KIS_INSTALL_DIR}/bin/ffmpeg .
0606     cp ${KIS_INSTALL_DIR}/bin/ffprobe .
0607     for bin in ffmpeg ffprobe; do
0608         cp ${KIS_INSTALL_DIR}/bin/${bin}
0609         install_name_tool -add_rpath @executable_path/../Frameworks/ ${bin}
0610     done
0611 
0612     # rsync -prul {KIS_INSTALL_DIR}/lib/libkrita* Frameworks/
0613 
0614     # To avoid errors macdeployqt must be run from bin location
0615     # ext_qt will not build macdeployqt by default so it must be build manually
0616     #   cd ${BUILDROOT}/depbuild/ext_qt/ext_qt-prefix/src/ext_qt/qttools/src
0617     #   make sub-macdeployqt-all
0618     #   make sub-macdeployqt-install_subtargets
0619     #   make install
0620     echo "Running macdeployqt..."
0621     cd ${KIS_INSTALL_DIR}/bin
0622     ./macdeployqt ${KRITA_DMG}/krita.app \
0623         -verbose=0 \
0624         -executable=${KRITA_DMG}/krita.app/Contents/MacOS/krita \
0625         -libpath=${KIS_INSTALL_DIR}/lib \
0626         -qmldir=${KIS_INSTALL_DIR}/qml \
0627         -appstore-compliant
0628         # -extra-plugins=${KIS_INSTALL_DIR}/lib/kritaplugins \
0629         # -extra-plugins=${KIS_INSTALL_DIR}/lib/plugins \
0630         # -extra-plugins=${KIS_INSTALL_DIR}/plugins
0631 
0632     cd ${BUILDROOT}
0633     echo "macdeployqt done!"
0634 
0635     echo "Copying python..."
0636     # Copy this framework last!
0637     # It is best that macdeployqt does not modify Python.framework
0638     # folders with period in name are treated as Frameworks for codesign
0639     rsync -prul ${KIS_INSTALL_DIR}/lib/Python.framework ${KRITA_DMG}/krita.app/Contents/Frameworks/
0640     rsync -prul ${KIS_INSTALL_DIR}/lib/krita-python-libs ${KRITA_DMG}/krita.app/Contents/Frameworks/
0641     # change perms on Python to allow header change
0642     chmod +w ${KRITA_DMG}/krita.app/Contents/Frameworks/Python.framework/Python
0643 
0644     fix_python_framework
0645     strip_python_dmginstall
0646 
0647     # fix python pyc
0648     # precompile all pyc so the dont alter signature
0649     echo "Precompiling all python files..."
0650     cd ${KRITA_DMG}/krita.app
0651     ${KIS_INSTALL_DIR}/bin/python -m compileall . &> /dev/null
0652 
0653     # remove unnecessary rpaths
0654     install_name_tool -delete_rpath @executable_path/../lib ${KRITA_DMG}/krita.app/Contents/MacOS/krita_version
0655     install_name_tool -delete_rpath @executable_path/../lib ${KRITA_DMG}/krita.app/Contents/MacOS/kritarunner
0656     install_name_tool -delete_rpath @loader_path/../../../../lib ${KRITA_DMG}/krita.app/Contents/MacOS/krita
0657     rm -rf ${KRITA_DMG}/krita.app/Contents/PlugIns/kf5/org.kde.kwindowsystem.platforms
0658 
0659     # Fix permissions
0660     find "${KRITA_DMG}/krita.app/Contents" -type f -name "*.dylib" -or -name "*.so" -or -path "*/Contents/MacOS/*" | xargs -P4 -I FILE chmod a+x FILE
0661     find "${KRITA_DMG}/krita.app/Contents/Resources/applications" -name "*.desktop" | xargs -P4 -I FILE chmod a-x FILE
0662 
0663     # repair krita for plugins
0664     printf "Searching for missing libraries\n"
0665     krita_findmissinglibs $(find ${KRITA_DMG}/krita.app/Contents -type f -perm 755 -or -name "*.dylib" -or -name "*.so")
0666 
0667     # Fix rpath for plugins
0668     # Uncomment if the Finder plugins (kritaquicklook, kritaspotlight) lack the rpath below
0669     # printf "Repairing rpath for Finder plugins\n"
0670     # find "${KRITA_DMG}/krita.app/Contents/Library" -type f -path "*/Contents/MacOS/*" -perm 755 | xargs -I FILE install_name_tool -add_rpath @loader_path/../../../../../Frameworks FILE
0671 
0672     printf "removing absolute or broken linksys, if any\n"
0673     find "${KRITA_DMG}/krita.app/Contents" -type l \( -lname "/*" -or -not -exec test -e {} \; \) -print | xargs rm
0674 
0675     printf "clean any left over rpath\n"
0676     libs_clean_rpath $(find "${KRITA_DMG}/krita.app/Contents" -type f -perm 755 -or -name "*.dylib" -or -name "*.so")
0677 #    libs_clean_rpath $(find "${KRITA_DMG}/krita.app/Contents/" -type f -name "lib*")
0678 
0679     echo "Done!"
0680 }
0681 
0682 
0683 # helper to define function only once
0684 batch_codesign() {
0685     local entitlements="${1}"
0686     if [[ -z "${1}" ]]; then
0687         entitlements="${KIS_SRC_DIR}/packaging/macos/entitlements.plist"
0688     fi
0689     xargs -P4 -I FILE codesign --options runtime --timestamp -f -s "${CODE_SIGNATURE}" --entitlements "${entitlements}" FILE
0690 }
0691 # Code sign must be done as recommended by apple "sign code inside out in individual stages"
0692 signBundle() {
0693     cd ${KRITA_DMG}
0694 
0695     # sign Frameworks and libs
0696     cd ${KRITA_DMG}/krita.app/Contents/Frameworks
0697     # remove debug version as both versions can't be signed.
0698     rm ${KRITA_DMG}/krita.app/Contents/Frameworks/QtScript.framework/Versions/Current/QtScript_debug
0699     # Do not sign binaries inside frameworks except for Python's
0700     find . -type d -path "*.framework" -prune -false -o -perm +111 -not -type d | batch_codesign
0701     find Python.framework -type f -name "*.o" -or -name "*.so" -or -perm +111 -not -type d -not -type l | batch_codesign
0702     find . -type d -name "*.framework" | xargs printf "%s/Versions/Current\n" | batch_codesign
0703 
0704     # Sign all other files in Framework (needed)
0705     # there are many files in python do we need to sign them all?
0706     find krita-python-libs -type f | batch_codesign
0707     # find python -type f | batch_codesign
0708 
0709     # Sign only libraries and plugins
0710     cd ${KRITA_DMG}/krita.app/Contents/PlugIns
0711     find . -type f | batch_codesign
0712 
0713     cd ${KRITA_DMG}/krita.app/Contents/Library/QuickLook
0714     printf "kritaquicklook.qlgenerator" | batch_codesign
0715 
0716     cd ${KRITA_DMG}/krita.app/Contents/Library/Spotlight
0717     printf "kritaspotlight.mdimporter" | batch_codesign
0718 
0719     # It is necessary to sign every binary Resource file
0720     cd ${KRITA_DMG}/krita.app/Contents/Resources
0721     find . -perm +111 -type f | batch_codesign
0722 
0723     printf "${KRITA_DMG}/krita.app/Contents/MacOS/ffmpeg" | batch_codesign
0724     printf "${KRITA_DMG}/krita.app/Contents/MacOS/ffprobe" | batch_codesign
0725     
0726     printf "${KRITA_DMG}/krita.app/Contents/MacOS/kritarunner" | batch_codesign
0727     printf "${KRITA_DMG}/krita.app/Contents/MacOS/krita_version" | batch_codesign
0728 
0729     #Finally sign krita and krita.app
0730     printf "${KRITA_DMG}/krita.app/Contents/MacOS/krita" | batch_codesign
0731     printf "${KRITA_DMG}/krita.app" | batch_codesign
0732 }
0733 
0734 sign_hasError() {
0735     local CODESIGN_STATUS=0
0736     for f in $(find "${KRITA_DMG}" -type f); do
0737         if [[ -z $(file ${f} | grep "Mach-O") ]]; then
0738             continue
0739         fi
0740 
0741         CODESIGN_RESULT=$(codesign -vvv --strict ${f} 2>&1 | grep "not signed")
0742 
0743         if [[ -n "${CODESIGN_RESULT}" ]]; then
0744             CODESIGN_STATUS=1
0745             printf "${f} not signed\n" >&2
0746         fi
0747     done
0748     echo ${CODESIGN_STATUS}
0749 }
0750 
0751 # Notarize build on macOS servers
0752 # based on https://github.com/Beep6581/RawTherapee/blob/6fa533c40b34dec527f1176d47cc6c683422a73f/tools/osx/macosx_bundle.sh#L225-L250
0753 notarize_build() {
0754     local NOT_SRC_DIR=${1}
0755     local NOT_SRC_FILE=${2}
0756 
0757     local notarization_complete="true"
0758 
0759     if [[ ${NOTARIZE} = "true" ]]; then
0760         printf "performing notarization of %s\n" "${2}"
0761         cd "${NOT_SRC_DIR}"
0762 
0763         ditto -c -k --sequesterRsrc --keepParent "${NOT_SRC_FILE}" "${BUILDROOT}/tmp_notarize/${NOT_SRC_FILE}.zip"
0764 
0765         if [[ ${NOTARYTOOL} = "short" ]]; then
0766             xcrun notarytool submit "${BUILDROOT}/tmp_notarize/${NOT_SRC_FILE}.zip" --wait --keychain-profile KritaNotarizeAccount
0767 
0768         elif [[ ${NOTARYTOOL} = "long" ]]; then
0769             xcrun notarytool submit "${BUILDROOT}/tmp_notarize/${NOT_SRC_FILE}.zip" --wait --apple-id "${NOTARIZE_ACC}" --password "${NOTARIZE_PASS}" --team-id "${APPLE_TEAMID}"
0770 
0771         else
0772             # echo "xcrun altool --notarize-app --primary-bundle-id \"org.krita\" --username \"${NOTARIZE_ACC}\" --password \"${NOTARIZE_PASS}\" --file \"${BUILDROOT}/tmp_notarize/${NOT_SRC_FILE}.zip\""
0773             local altoolResponse="$(xcrun altool --notarize-app --primary-bundle-id "org.krita" --username "${NOTARIZE_ACC}" --password "${NOTARIZE_PASS}" ${ASC_PROVIDER_OP} --file "${BUILDROOT}/tmp_notarize/${NOT_SRC_FILE}.zip" 2>&1)"
0774 
0775             if [[ -n "$(grep 'Error' <<< ${altoolResponse})" ]]; then
0776                 printf "ERROR: xcrun altool exited with the following error! \n\n%s\n\n" "${altoolResponse}"
0777                 printf "This could mean there is an error in AppleID authentication!\n"
0778                 printf "aborting notarization\n"
0779                 NOTARIZE="false"
0780                 return
0781             else
0782                 printf "Response:\n\n%s\n\n" "${altoolResponse}"
0783             fi
0784 
0785             local uuid="$(grep 'RequestUUID' <<< ${altoolResponse} | awk '{ print $NF }')"
0786             echo "RequestUUID = ${uuid}" # Display identifier string
0787 
0788             waiting_fixed "Waiting to retrieve notarize status" 120
0789 
0790             while true ; do
0791                 fullstatus=$(xcrun altool --notarization-info "${uuid}" --username "${NOTARIZE_ACC}" --password "${NOTARIZE_PASS}" ${ASC_PROVIDER_OP} 2>&1)  # get the status
0792                 notarize_status=`echo "${fullstatus}" | grep 'Status\:' | awk '{ print $2 }'`
0793                 echo "${fullstatus}"
0794                 if [[ "${notarize_status}" = "success" ]]; then
0795                     print_msg "Notarization success!"
0796                     break
0797                 elif [[ "${notarize_status}" = "in" ]]; then
0798                     waiting_fixed "Notarization still in progress, wait before checking again" 60
0799                 else
0800                     notarization_complete="false"
0801                     echo "Notarization failed! full status below"
0802                     echo "${fullstatus}"
0803                     exit 1
0804                 fi
0805             done
0806         fi
0807 
0808         if [[ "${notarization_complete}" = "true" ]]; then
0809             xcrun stapler staple "${NOT_SRC_FILE}"   #staple the ticket
0810             xcrun stapler validate -v "${NOT_SRC_FILE}"
0811         fi
0812     fi
0813 }
0814 
0815 createDMG () {
0816     printf "Creating of dmg with contents of %s...\n" "${KRITA_DMG}"
0817     cd ${BUILDROOT}
0818     DMG_size=1500
0819 
0820     if [[ -z "${DMG_NAME}" ]]; then
0821         # Add git version number
0822         DMG_NAME="krita-${KRITA_VERSION// /_}.dmg"
0823     else
0824         DMG_NAME="${DMG_NAME}.dmg"
0825     fi
0826 
0827     ## Build dmg from folder
0828 
0829     # create dmg on local system
0830     # usage of -fsargs minimize gaps at front of filesystem (reduce size)
0831     hdiutil create -srcfolder "${KRITA_DMG}" -volname "${DMG_title}" -fs APFS \
0832         -format UDIF -verbose -size ${DMG_size}m krita.temp.dmg
0833 
0834     # Next line is only useful if we have a dmg as a template!
0835     # previous hdiutil must be uncommented
0836     # cp krita-template.dmg krita.dmg
0837     device=$(hdiutil attach -readwrite -noverify -noautoopen "krita.temp.dmg" | egrep '^/dev/' | sed 1q | awk '{print $1}')
0838 
0839     # rsync -priul --delete ${KRITA_DMG}/krita.app "/Volumes/${DMG_title}"
0840 
0841     # Set style for dmg
0842     if [[ ! -d "/Volumes/${DMG_title}/.background" ]]; then
0843         mkdir "/Volumes/${DMG_title}/.background"
0844     fi
0845     cp -v ${DMG_background} "/Volumes/${DMG_title}/.background/"
0846 
0847     mkdir "/Volumes/${DMG_title}/Terms of Use"
0848     cp -v "${KIS_SRC_DIR}/packaging/macos/Terms_of_use.rtf" "/Volumes/${DMG_title}/Terms of Use/"
0849     ln -s "/Applications" "/Volumes/${DMG_title}/Applications"
0850 
0851     ## Apple script to set style
0852     style="$(<"${DMG_STYLE}")"
0853     printf "${style}" "${DMG_title}" "${DMG_background##*/}" | osascript
0854 
0855     #Set Icon for DMG
0856     cp -v "${SCRIPT_SOURCE_DIR}/KritaIcon.icns" "/Volumes/${DMG_title}/.VolumeIcon.icns"
0857     SetFile -a C "/Volumes/${DMG_title}"
0858 
0859     chmod -Rf go-w "/Volumes/${DMG_title}"
0860 
0861     # ensure all writing operations to dmg are over
0862     sync
0863 
0864     hdiutil detach $device
0865     hdiutil convert "krita.temp.dmg" -format UDZO -imagekey -zlib-level=9 -o krita-out.dmg
0866 
0867 
0868     mv krita-out.dmg ${DMG_NAME}
0869     echo "moved krita-out.dmg to ${DMG_NAME}"
0870     rm krita.temp.dmg
0871 
0872     if [[ -n "${CODE_SIGNATURE}" ]]; then
0873         printf "${DMG_NAME}" | batch_codesign
0874     fi
0875 
0876     notarize_build "${BUILDROOT}" "${DMG_NAME}"
0877 
0878     echo "dmg done!"
0879 }
0880 
0881 #######################
0882 # Program starts!!
0883 ########################
0884 # Run deploy command, installation is assumed to exist in BUILDROOT/i
0885 krita_deploy
0886 
0887 # Code sign krita.app if signature given
0888 if [[ -n "${CODE_SIGNATURE}" ]]; then
0889     signBundle
0890 fi
0891 
0892 # Manually check every single Mach-O file for signature status
0893 if [[ "${NOTARIZE}" = "true" ]]; then
0894 print_msg "Checking if all files are signed before sending for notarization..."
0895 if [[ $(sign_hasError) -eq 1 ]]; then
0896     print_error "CodeSign errors cannot send to notarize!"
0897     echo "krita.app not sent to notarization, stopping...."
0898     exit 1
0899 fi
0900 print_msg "Done! all files appear to be correct."
0901 
0902 # notarize apple
0903 notarize_build "${KRITA_DMG}" krita.app
0904 fi
0905 
0906 # Create DMG from files inside ${KRITA_DMG} folder
0907 createDMG
0908 
0909 if [[ "${NOTARIZE}" = "false" ]]; then
0910     macosVersion="$(sw_vers | grep ProductVersion | awk '
0911        BEGIN { FS = "[ .\t]" }
0912              { print $3}
0913     ')"
0914     if (( ${macosVersion} == 15 )); then
0915         print_error "Build not notarized! Needed for macOS versions above 10.14"
0916     fi
0917 fi