File indexing completed on 2024-05-12 16:02:32

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