diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index af7b3b58ff..f2672f1eaf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,7 @@ spec: - GIMP_CI_SNAP #trigger the snap build (base but slow) - GIMP_CI_WIN_INSTALLER #trigger all native MSYS2 builds then creates Inno Windows installer (base but slow) - GIMP_CI_MS_STORE #trigger arm64 and x64 native MSYS2 builds then creates a .msixbundle (base but slow) + - GIMP_CI_MACOS #trigger arm64 native macOS builds (base but slow) - none default: 'none' test_pipeline: @@ -78,7 +79,6 @@ workflow: retry: max: 1 when: - - 'runner_system_failure' - 'scheduler_failure' needs: [] # Default Docker image (keep variables: DEB_VERSION: consistent with devel-docs/os-support.txt) @@ -647,6 +647,62 @@ gimp-win-eol: artifacts: !reference [gimp-win, artifacts] +## macOS pipelines (Homebrew) ## +.macos-inhouse: + extends: .default + rules: + - if: '$CI_MERGE_REQUEST_LABELS =~ /.*Package:Macos Dmg.*/' + interruptible: true + - if: '$GIMP_CI_MACOS != null || "$[[ inputs.distribution_pipeline ]]" =~ /.*GIMP_CI_MACOS.*/' + tags: + - macos + variables: + #FIXME: Our runner #926 (by MacStadium) do not have cache nor timeout for MacPorts builds + OPT_PREFIX: /opt/homebrew + PKGCONF_RELOCATABLE_OPTION: '-Dpkgconfig.relocatable=true' + DMG_OPTION: '-Ddmg=true' + before_script: + - export GIMP_PREFIX="$PWD/_install-$(uname -m)" + - export PATH="$OPT_PREFIX/bin:$PATH" + timeout: 30m + +.macos_environ: &MAC_ENVIRON +- printf "\e[0Ksection_start:`date +%s`:macos_environ[collapsed=true]\r\e[0KPreparing build environment\n" +## Build-time vars +- export PKG_CONFIG_PATH="${GIMP_PREFIX}/lib/pkgconfig:${OPT_PREFIX}/lib/pkgconfig:${OPT_PREFIX}/opt/libarchive/lib/pkgconfig" +- export XDG_DATA_DIRS="${GIMP_PREFIX}/share:${OPT_PREFIX}/share" +## Runtime vars +- export PATH="${GIMP_PREFIX}/bin:${OPT_PREFIX}/bin:$PATH" +- export GI_TYPELIB_PATH="${GIMP_PREFIX}/lib/girepository-1.0:${OPT_PREFIX}/lib/girepository-1.0" +- printf "\e[0Ksection_end:`date +%s`:macos_environ\r\e[0K\n" + +deps-macos-inhouse: + extends: .macos-inhouse + stage: dependencies + script: + - sh build/macos/1_build-deps-macports.sh + artifacts: + paths: + - _install-* + - babl/_build-*/meson-logs/meson-log.txt + - gegl/_build-*/meson-logs/meson-log.txt + expire_in: 2 hours + +gimp-macos-inhouse: + extends: .macos-inhouse + needs: ["deps-macos-inhouse"] + stage: build + variables: + GIT_SUBMODULE_STRATEGY: recursive + script: + - sh build/macos/2_build-gimp-macports.sh + artifacts: + paths: + - gimp-*.app + - _build*/meson-logs/meson-log.txt + - _build*/done-dylib.list + + ## Analysis ## file-plug-in-tests: diff --git a/build/macos/1_build-deps-macports.sh b/build/macos/1_build-deps-macports.sh new file mode 100644 index 0000000000..e1a8f3b201 --- /dev/null +++ b/build/macos/1_build-deps-macports.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +# Ensure the script work properly +case $(readlink /proc/$$/exe) in + *bash) + set -o posix + ;; +esac +set -e +if [ "$0" != 'build/macos/1_build-deps-macports.sh' ] && [ $(basename "$PWD") != 'macos' ]; then + printf '\033[31m(ERROR)\033[0m: Script called from wrong dir. Please, call this script from the root of gimp git dir/\n' + exit 1 +elif [ $(basename "$PWD") = 'macos' ]; then + cd ../../.. +fi +if [ -z "$GITLAB_CI" ]; then + GIT_DEPTH='1' + + PARENT_DIR='/..' +fi + + +# Install part of the deps +if [ -z "$OPT_PREFIX" ]; then + export OPT_PREFIX=$(which port | sed 's|/bin/port||' || brew --prefix) + if echo "$OPT_PREFIX" | grep -q 'not found'; then + printf '\033[31m(ERROR)\033[0m: MacPorts installation not found. Please, install it on: https://www.macports.org/install.php\n' + exit 1 + fi +fi +if [ "$OPT_PREFIX" != '/opt/local' ] && [ "$OPT_PREFIX" != '/opt/homebrew' ]; then + sed -i .bak "s/^#build_arch.*/build_arch $(uname -m)/" "${OPT_PREFIX}/etc/macports/macports.conf" >/dev/null 2>&1 || true +fi +export MACOSX_DEPLOYMENT_TARGET=$(awk '/LSMinimumSystemVersion/{found=1} found && //{gsub(/.*|<\/string>.*/, ""); print; exit}' build/macos/Info.plist) #End of config + +printf "\e[0Ksection_start:`date +%s`:deps_install[collapsed=true]\r\e[0KInstalling dependencies provided by $( [ -f "$OPT_PREFIX/bin/port" ] && echo MacPorts || echo Homebrew )\n" +if [ -f "$OPT_PREFIX/bin/port" ]; then + eval $( [ "$OPT_PREFIX"=/opt/local ] && echo sudo ) port sync && eval $( [ "$OPT_PREFIX"=/opt/local ] && echo sudo ) port upgrade outdated + eval $( [ "$OPT_PREFIX"=/opt/local ] && echo sudo ) port install -N $(grep -v '^#' build/macos/all-deps-uni.txt | sed 's/|homebrew:[^ ]*//g' | tr -d '\' | xargs) + git apply -v build/macos/patches/0001-meson-Patch-python-version.patch || true +else + brew upgrade --quiet + brew install --quiet $(tr '\\' '\n' < build/macos/all-deps-uni.txt | grep -v '#' | sed -n 's/.*|homebrew://p' | awk '{print $1}' | xargs) + git apply -v build/macos/patches/0001-build-macos-Do-not-require-gexiv2-0.14-on-homebrew.patch || true +fi +git apply -v build/macos/patches/0001-app-libgimpwidgets-meson-plug-ins-Patch-macOS-bundle.patch || true +printf "\e[0Ksection_end:`date +%s`:deps_install\r\e[0K\n" + + +# Prepare env (only GIMP_PREFIX is needed for flatpak) +GIMP_DIR="$PWD" +cd ${GIMP_DIR}${PARENT_DIR} + +if [ -z "$GIMP_PREFIX" ]; then + export GIMP_PREFIX="$PWD/_install" +fi +eval "$(sed -n -e 's/- //' -e '/macos_environ\[/,/macos_environ/p' $GIMP_DIR/.gitlab-ci.yml)" + + +# Build some deps (including babl and GEGL) +self_build() +{ + dep=$(basename "$1" .git) + printf "\e[0Ksection_start:`date +%s`:${dep}_build[collapsed=true]\r\e[0KBuilding $dep\n" + if [ ! -d "$dep" ]; then + git clone --depth $GIT_DEPTH --branch ${2:-master} $1 + fi + cd $dep + git pull + + # Configure and build + if [ ! -f "_build-$(uname -m)/build.ninja" ]; then + meson setup _build-$(uname -m) -Dprefix="$GIMP_PREFIX" $PKGCONF_RELOCATABLE_OPTION \ + -Dbuildtype=debugoptimized \ + -Dc_args="-I${OPT_PREFIX}/include" -Dcpp_args="-I${OPT_PREFIX}/include" -Dc_link_args="-L${OPT_PREFIX}/lib" -Dcpp_link_args="-L${OPT_PREFIX}/lib" + fi + cd _build-$(uname -m) + ninja + ninja install + cd ../.. + printf "\e[0Ksection_end:`date +%s`:${dep}_build\r\e[0K\n" +} + +self_build https://gitlab.gnome.org/GNOME/babl +self_build https://gitlab.gnome.org/GNOME/gegl + +cd $GIMP_DIR diff --git a/build/macos/2_build-gimp-macports.sh b/build/macos/2_build-gimp-macports.sh new file mode 100644 index 0000000000..a8a8a1f07d --- /dev/null +++ b/build/macos/2_build-gimp-macports.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +# Ensure the script work properly +case $(readlink /proc/$$/exe) in + *bash) + set -o posix + ;; +esac +set -e +if [ "$0" != 'build/macos/2_build-gimp-macports.sh' ] && [ $(basename "$PWD") != 'macos' ]; then + printf '\033[31m(ERROR)\033[0m: Script called from wrong dir. Please, call this script from the root of gimp git dir/\n' + exit 1 +elif [ $(basename "$PWD") = 'macos' ]; then + cd ../../.. +fi +if [ -z "$GITLAB_CI" ]; then + git submodule update --init + + NON_RELOCATABLE_OPTION='-Drelocatable-bundle=no' +fi + + +# Install part of the deps (again) +eval "$(sed -n '/Install part/,/End of config/p' build/macos/1_build-deps-macports.sh)" + +if [ "$GITLAB_CI" ]; then + eval "$(sed -n '/deps_install\[/,/deps_install/p' build/macos/1_build-deps-macports.sh)" +fi + + +# Prepare env (only GIMP_PREFIX is needed for flatpak) +if [ -z "$GIMP_PREFIX" ]; then + export GIMP_PREFIX="$PWD/../_install" +fi +eval "$(sed -n -e 's/- //' -e '/macos_environ\[/,/macos_environ/p' .gitlab-ci.yml)" + + +# Build GIMP only +printf "\e[0Ksection_start:`date +%s`:gimp_build[collapsed=true]\r\e[0KBuilding GIMP\n" +if [ ! -f "_build-$(uname -m)/build.ninja" ]; then + meson setup _build-$(uname -m) -Dprefix="$GIMP_PREFIX" $NON_RELOCATABLE_OPTION \ + $PKGCONF_RELOCATABLE_OPTION $DMG_OPTION \ + -Dbuild-id=org.gimp.GIMP_official \ + -Dc_args="-I${OPT_PREFIX}/include" -Dcpp_args="-I${OPT_PREFIX}/include" -Dc_link_args="-L${OPT_PREFIX}/lib" -Dcpp_link_args="-L${OPT_PREFIX}/lib" +fi +cd _build-$(uname -m) +ninja +printf "\e[0Ksection_end:`date +%s`:gimp_build\r\e[0K\n" + + +# Bundle GIMP +printf "\e[0Ksection_start:`date +%s`:gimp_bundle[collapsed=true]\r\e[0K$(if ! grep -q "dmg=true" meson-logs/meson-log.txt; then echo "Installing GIMP as non-relocatable on GIMP_PREFIX"; else echo "Creating .app bundle"; fi)\n" +ninja install > ninja_install.log 2>&1 || { cat ninja_install.log; exit 1; }; +cd .. +printf "\e[0Ksection_end:`date +%s`:gimp_bundle\r\e[0K\n" diff --git a/build/macos/2_bundle-gimp-uni_base.py b/build/macos/2_bundle-gimp-uni_base.py new file mode 100644 index 0000000000..9c64768ec5 --- /dev/null +++ b/build/macos/2_bundle-gimp-uni_base.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python3 +import os +import platform +import re +import shutil +import subprocess +import stat +import sys +from pathlib import Path +from glob import glob + +# This script is used to create a GIMP .app bundle on macOS. A bundle +# is used as source of files for making the .dmg installer +if not os.getenv("MESON_BUILD_ROOT"): + # Let's prevent contributors from creating broken bundles + print("\033[31m(ERROR)\033[0m: Script called standalone. Please build GIMP targeting DMG installer creation.") + sys.exit(1) +# Get variables from MESON_BUILD_ROOT/config.h that can be used on this script +with open("config.h") as file: + for line in file: + match = re.match(r'^#\s*define\s+(\S+)(?:\s+(.*))?$', line) + if match: + key, value = match.groups() + if value is None or not value.strip(): + value = "1" #needed when there is no explicit value + else: + value = value.strip().strip('"').strip("'") + os.environ[key] = value +if not os.getenv("ENABLE_RELOCATABLE_RESOURCES"): + print("\n\033[31m(ERROR)\033[0m: No relocatable GIMP build found. You can build GIMP with '-Drelocatable-bundle=yes' to make a build suitable for .app creation.") + sys.exit(1) + + +# Bundle deps and GIMP files +GIMP_SOURCE = Path(os.getenv("MESON_SOURCE_ROOT")) + +## System prefix: it is OPT_PREFIX (see 1_build-deps-macports) +OPT_PREFIX = Path(os.getenv("OPT_PREFIX")) +## GIMP prefix: as set at meson configure time +GIMP_PREFIX = Path(os.getenv("MESON_INSTALL_DESTDIR_PREFIX")) + +## Bundle dir: we make a "perfect" bundle separated from GIMP_PREFIX +#NOTE: The bundling script need to set $OPT_PREFIX to our dist scripts +#fallback code be able to identify what arch they are distributing +GIMP_DISTRIB = Path(GIMP_SOURCE) / f"gimp-{platform.machine()}.app" / "Contents" + +def bundle(src_root, pattern, option="None", override=None): + ## Search for targets in search path + src_root = Path(src_root) + paths_to_bundle = list(src_root.glob(pattern)) + if not paths_to_bundle: + print(f"\033[31m(ERROR)\033[0m: not found {src_root}/{pattern}") + sys.exit(1) + for src_path in paths_to_bundle: + ## Copy found targets to bundle path + symlink_cleanup = True + if "--dest" in option: + dest_path = GIMP_DISTRIB / Path(override) / src_path.name + elif "--rename" in option: + dest_path = GIMP_DISTRIB / Path(override) + elif "bin/" in pattern: + symlink_cleanup = False + dest_path = GIMP_DISTRIB / "MacOS" / Path(src_path.relative_to(src_root)).name + elif "lib/" in pattern: + dest_path = GIMP_DISTRIB / "Frameworks" / src_path.relative_to(src_root / "lib") + elif "share/" in pattern: + dest_path = GIMP_DISTRIB / "Resources" / src_path.relative_to(src_root / "share") + elif "etc/" in pattern: + dest_path = GIMP_DISTRIB / "SharedSupport" / src_path.relative_to(src_root / "etc") + dest_path.parent.mkdir(parents=True, exist_ok=True) + print(f"Bundling {src_path} to {dest_path.parent}") + if src_path.is_dir(): + try: + shutil.copytree(src_path, dest_path, dirs_exist_ok=True) + except shutil.Error as e: + print(f"\033[33m(WARNING)\033[0m: {dest_path} seems to already exist and have permission problems") + else: + if not str(src_path).endswith(".typelib"): + try: + shutil.copy2(src_path, dest_path, follow_symlinks=symlink_cleanup) + except shutil.Error as e: + print(f"\033[33m(WARNING)\033[0m: {dest_path} seems to already exist and have permission problems") + if "MacOS/" in str(dest_path) and not dest_path.is_symlink(): + os.chmod(dest_path, 0o755) + else: + ## Process .typelib dependencies (as relocatable) + tmp_gir_dir = GIMP_DISTRIB / "tmp" + tmp_gir_dir.mkdir(parents=True, exist_ok=True) + def set_typelib_rpath(typelib, prefix): + typelib_path = Path(f"{prefix}/lib/girepository-1.0/{typelib}.typelib") + target_path = dest_path.parent / typelib_path.name + if typelib_path.exists() and not target_path.exists(): + if typelib_path != src_path: + print(f"Bundling {typelib_path} to {dest_path.parent}") + tmp_gir_path = Path(f"{tmp_gir_dir}/{typelib}.gir") + shutil.copy2(Path(f"{prefix}/share/gir-1.0/{typelib}.gir"), tmp_gir_path) + text = tmp_gir_path.read_text() + text = re.sub(r'shared-library="([^"]+)"', lambda m: 'shared-library="' + ",".join("@rpath/" + os.path.basename(p) for p in m.group(1).split(",")) + '"', text) + tmp_gir_path.write_text(text) + subprocess.run(["g-ir-compiler", f"--includedir={tmp_gir_dir}", str(tmp_gir_path), "-o", target_path], check=True) + def process_typelib(path, typelib_list=None): + set_typelib_rpath(Path(path).stem, GIMP_PREFIX) + if typelib_list is None: + typelib_list = set() + cmd = ['g-ir-inspect', '--print-typelibs', os.path.basename(path).split('-')[0]] + result = subprocess.run(cmd, capture_output=True, text=True) + for line in result.stdout.splitlines(): + typelib = line.replace("typelib: ", "").strip() + if typelib and typelib not in typelib_list: + typelib_list.add(typelib) + for prefix in [GIMP_PREFIX, OPT_PREFIX]: + typelib_path = Path(f"{prefix}/lib/girepository-1.0/{typelib}.typelib") + target_path = dest_path.parent / typelib_path.name + if typelib_path.exists() and not target_path.exists(): + set_typelib_rpath(typelib, prefix) + process_typelib(typelib, typelib_list) + process_typelib(src_path) + shutil.rmtree(tmp_gir_dir) + +def clean(base_path, pattern): + base_path = Path(base_path) + first_found = False + for parent_path in base_path.glob(os.path.dirname(pattern)): + for path in parent_path.rglob(os.path.basename(pattern)): + if path.exists(): + if not first_found: + print(f"Cleaning {base_path}/{pattern}") + first_found = True + if path.is_dir(): + shutil.rmtree(path) + else: + path.unlink() + + +## PREPARE BUNDLE +GIMP_DISTRIB.mkdir(parents=True, exist_ok=True) +### Prevent Git going crazy +(GIMP_DISTRIB / ".." / ".gitignore").write_text("*\n") +### Configure Info.plist +text = (GIMP_SOURCE / "build/macos/Info.plist").read_text() +if not os.getenv("GIMP_RELEASE") or os.getenv("GIMP_IS_RC_GIT"): + BUNDLE_IDENTIFIER = "org.gimp.gimp.internal" + BUNDLE_NAME = "GIMP (Nightly)" + MUTEX_SUFFIX = "" +elif (os.getenv("GIMP_RELEASE") and os.getenv("GIMP_UNSTABLE")) or os.getenv("GIMP_RC_VERSION"): + BUNDLE_IDENTIFIER = "org.gimp.gimp.seed" + BUNDLE_NAME = "GIMP (Beta)" + MUTEX_SUFFIX = f"-{os.getenv('GIMP_MUTEX_VERSION')}" +else: + BUNDLE_IDENTIFIER = "org.gimp.gimp" + BUNDLE_NAME = "GIMP" + MUTEX_SUFFIX = f"-{os.getenv('GIMP_MUTEX_VERSION')}" +new_text = text.replace("%BUNDLE_IDENTIFIER%", BUNDLE_IDENTIFIER) +new_text = new_text.replace("%BUNDLE_NAME%", BUNDLE_NAME) +new_text = new_text.replace("%GIMP_VERSION%", os.getenv("GIMP_VERSION")) +new_text = new_text.replace("%MUTEX_SUFFIX%", MUTEX_SUFFIX) +(GIMP_DISTRIB / "Info.plist").write_text(new_text) +### FIXME: Icon (generate Assets.car for Liquid Glass) +(GIMP_DISTRIB / "Resources").mkdir(parents=True, exist_ok=True) +shutil.copy2(Path(f"{os.getenv('MESON_BUILD_ROOT')}/gimp-data/images/logo/gimp.icns"), GIMP_DISTRIB / "Resources/AppIcon.icns") + + +## BUNDLE BASE (BARE MINIMUM TO RUN GTK APPS). +### FIXME: Needed for 'Send to Email' support (should be on sendmail.c source) +bundle(GIMP_SOURCE, "build/macos/patches/xdg-email", "--dest", "MacOS") +### Needed for file dialogs (only .compiled file is needed on macOS) +bundle(OPT_PREFIX, "share/glib-*/schemas/gschemas.compiled") +### Needed to open remote files +if os.path.exists(OPT_PREFIX / "bin/port"): + bundle(OPT_PREFIX, "lib/libproxy/libpxbackend*.dylib", "--dest", "Frameworks") +bundle(OPT_PREFIX, "lib/gio") +### Needed to not crash UI. See: https://gitlab.gnome.org/GNOME/gimp/-/issues/6165 +bundle(OPT_PREFIX, "share/icons/Adwaita") +### Needed by GTK to use icon themes. See: https://gitlab.gnome.org/GNOME/gimp/-/issues/5080 +bundle(GIMP_PREFIX, "share/icons/hicolor") +### Needed to loading icons in GUI +bundle(OPT_PREFIX, "lib/gdk-pixbuf-*/*/loaders/libpixbufloader*svg*") +bundle(OPT_PREFIX, "lib/gdk-pixbuf-*/*/loaders.cache") +loaders_cache = glob(f"{GIMP_DISTRIB}/Frameworks/gdk-pixbuf-*/*/loaders.cache") +text = Path(loaders_cache[0]).read_text() +new_text = text.replace(f"{OPT_PREFIX}/lib/", "") +Path(loaders_cache[0]).write_text(new_text) +### Needed for printing support +bundle(OPT_PREFIX, "lib/gtk-3.0/*.*.*/printbackends/*.so") +### Needed for macOS emoji keyboard support (with im-quartz.so) +bundle(OPT_PREFIX, "lib/gtk-3.0/*.*.*/immodules/*.so") +if os.path.exists(OPT_PREFIX / "bin/port"): + bundle(OPT_PREFIX, "etc/gtk-3.0/gtk.immodules", "--rename", "Frameworks/gtk-3.0/3.0.0/immodules.cache") +else: #os.path.exists(OPT_PREFIX / "bin/brew"): + bundle(OPT_PREFIX, "lib/gtk-3.0/*.*.*/immodules.cache") +im_cache = glob(f"{GIMP_DISTRIB}/Frameworks/gtk-3.0/*.*.*/immodules.cache") +text = Path(im_cache[0]).read_text() +new_text = re.sub(r'/.*?(?=gtk-3\.0/)', '', text) +Path(im_cache[0]).write_text(new_text) +### Needed for MacOS-style keyboard shortcuts +bundle(OPT_PREFIX, "share/themes/Mac") + + +## CORE FEATURES. +bundle(GIMP_PREFIX, "lib/libbabl*-*.*.*.dylib") +bundle(GIMP_PREFIX, "lib/babl-*") +bundle(GIMP_PREFIX, "lib/libgegl*-*.*.*.dylib") +bundle(GIMP_PREFIX, "lib/gegl-*") +bundle(GIMP_PREFIX, "lib/libgimp*-*.*.*.dylib") +bundle(GIMP_PREFIX, "lib/gimp") +bundle(GIMP_PREFIX, "share/gimp") +lang_array = [Path(f).stem for f in glob(str(Path(GIMP_SOURCE)/"po/*.po"))] +for lang in lang_array: + bundle(GIMP_PREFIX, f"share/locale/{lang}/LC_MESSAGES/*.mo") + # Needed for eventually used widgets, GTK inspector etc + if glob(f"{OPT_PREFIX}/share/locale/{lang}/LC_MESSAGES/gtk*.mo"): + bundle(OPT_PREFIX, f"share/locale/{lang}/LC_MESSAGES/gtk*.mo") + # For language list in text tool options + if glob(f"{OPT_PREFIX}/share/locale/{lang}/LC_MESSAGES/iso_639_3.mo"): + bundle(OPT_PREFIX, f"share/locale/{lang}/LC_MESSAGES/iso_639_3.mo") +bundle(GIMP_PREFIX, "etc/gimp") + + +## OTHER FEATURES AND PLUG-INS. +### Support for non .PAT patterns: https://gitlab.gnome.org/GNOME/gimp/-/issues/12351 +bundle(OPT_PREFIX, "lib/gdk-pixbuf-*/*/loaders/libpixbufloader-bmp*") +bundle(OPT_PREFIX, "lib/gdk-pixbuf-*/*/loaders/libpixbufloader-gif*") +bundle(OPT_PREFIX, "lib/gdk-pixbuf-*/*/loaders/libpixbufloader-tiff*") +### FIXME: mypaint brushes (needs patching https://github.com/Homebrew/homebrew-core/pull/262039) +bundle(OPT_PREFIX, "share/mypaint-data/2.0") +### Needed for fontconfig +bundle(OPT_PREFIX, "etc/fonts") +#### Avoid writing in the system and avoid other programs breaking the cache +fonts_conf = GIMP_DISTRIB / "SharedSupport/fonts/fonts.conf" +text = fonts_conf.read_text() +new_text = text.replace( + f"{os.getenv('OPT_PREFIX')}/var", + f"~/Library/Application Support/GIMP/{os.getenv('GIMP_APP_VERSION')}" +) +fonts_conf.write_text(new_text) +### FIXME: Needed for 'th' word breaking in Text tool etc (https://github.com/macports/macports-ports/pull/30702 e https://github.com/Homebrew/homebrew-core/pull/262071) +#if os.path.exists(OPT_PREFIX / "bin/port"): + #bundle(OPT_PREFIX, "share/libthai") +### Needed for full CJK and Cyrillic support in file-pdf +bundle(OPT_PREFIX, "share/poppler") +#### Needed for signature support in file-pdf lib +if os.path.exists(OPT_PREFIX / "bin/port"): + bundle(OPT_PREFIX, "lib/nss/libssl3.dylib", "--dest", "Frameworks") + bundle(OPT_PREFIX, "lib/nss/libsmime3.dylib", "--dest", "Frameworks") + bundle(OPT_PREFIX, "lib/nss/libnssutil3.dylib", "--dest", "Frameworks") + bundle(OPT_PREFIX, "lib/nss/libnss3.dylib", "--dest", "Frameworks") + bundle(OPT_PREFIX, "lib/nspr/*.dylib", "--dest", "Frameworks") +### FIXME: Needed for file-ps work (but maintaining GS_LIB on app/main.c is tough) +if os.path.exists(OPT_PREFIX / "bin/port"): + bundle(OPT_PREFIX, "share/ghostscript/1*/iccprofiles/*.icc", "--dest", "Resources/ghostscript/iccprofiles") + bundle(OPT_PREFIX, "share/ghostscript/1*/Resource/Init/*", "--dest", "Resources/ghostscript/Resource/Init") + bundle(OPT_PREFIX, "share/ghostscript/1*/Resource/Font/*", "--dest", "Resources/ghostscript/Resource/Font") +else: #os.path.exists(OPT_PREFIX / "bin/brew"): + bundle(OPT_PREFIX, "share/ghostscript/iccprofiles/*.icc") + bundle(OPT_PREFIX, "share/ghostscript/Resource/Init") + bundle(OPT_PREFIX, "share/ghostscript/Resource/Font") +### Needed for file-wmf work +if os.path.exists(OPT_PREFIX / "bin/port"): + bundle(OPT_PREFIX, "share/fonts/libwmf/*", "--dest", "Resources/libwmf/fonts") +else: #os.path.exists(OPT_PREFIX / "bin/brew"): + bundle(OPT_PREFIX, "Cellar/libwmf/*/share/libwmf/fonts/*", "--dest", "Resources/libwmf/fonts") +### Needed for 'Show image graph'. +#### See: https://gitlab.gnome.org/GNOME/gimp/-/issues/6045 +bundle(OPT_PREFIX, "bin/dot", "--dest", "MacOS") +#### See: https://gitlab.gnome.org/GNOME/gimp/-/issues/12119 +bundle(OPT_PREFIX, "lib/graphviz/libgvplugin_dot*.dylib") +bundle(OPT_PREFIX, "lib/graphviz/libgvplugin_pango*.dylib") +bundle(OPT_PREFIX, "lib/graphviz/config*") +### Binaries for GObject Introspection support. See: https://gitlab.gnome.org/GNOME/gimp/-/issues/13170 +bundle(GIMP_PREFIX, "lib/girepository-*/*.typelib") +bundle(OPT_PREFIX, "lib/libgirepository-*.dylib") +#### Python support +bundle(OPT_PREFIX, f"bin/python{os.getenv('PYTHON_VERSION')}", "--rename", "MacOS/python3") +if os.path.exists(OPT_PREFIX / "bin/port"): + bundle(OPT_PREFIX, f"Library/Frameworks/Python.framework/Versions/{os.getenv('PYTHON_VERSION')}", "--dest", "Frameworks/Python.framework/Versions") +else: #os.path.exists(OPT_PREFIX / "bin/brew"): + bundle(OPT_PREFIX, f"Frameworks/Python.framework/Versions/{os.getenv('PYTHON_VERSION')}", "--dest", "Frameworks/Python.framework/Versions") +bundle(OPT_PREFIX, f"lib/python{os.getenv('PYTHON_VERSION')}/site-packages/*", "--dest", f"Frameworks/Python.framework/Versions/{os.getenv('PYTHON_VERSION')}/lib/python{os.getenv('PYTHON_VERSION')}/site-packages") +clean(GIMP_DISTRIB, "Frameworks/Python.framework/*.pyc") +#####Needed for internet connection on python. See: https://gitlab.gnome.org/GNOME/gimp/-/issues/14722 +pythonpath = Path(f"{GIMP_DISTRIB}/Frameworks/Python.framework/Versions/{os.getenv('PYTHON_VERSION')}/lib/python{os.getenv('PYTHON_VERSION')}") +for d in (pythonpath, pythonpath / "site-packages"): + sitecustomize = d / "sitecustomize.py" + code = """\nimport os\nimport certifi\n\n# Only set if not already configured by user\nif not os.environ.get('SSL_CERT_FILE'):\n os.environ['SSL_CERT_FILE'] = certifi.where()\n""" + if sitecustomize.exists() and code.strip() not in sitecustomize.read_text(): + sitecustomize.write_text(sitecustomize.read_text() + code) + elif not sitecustomize.exists(): + sitecustomize.write_text(code) +#####Needed since we use [[NSBundle mainBundle] bundlePath] on libgimpbase/gimpenv.c +real_path = Path(f"{GIMP_DISTRIB}/Resources/icons") +link_path = Path(f"{GIMP_DISTRIB}/Frameworks/Python.framework/Versions/{os.getenv('PYTHON_VERSION')}/Resources/Python.app/Contents/Resources/icons") +link_path.symlink_to(os.path.relpath(real_path, link_path.parent)) +#### lua is buggy, and hard to bundle due to LUA_*PATH etc (see AppImage script) +#if os.path.exists(OPT_PREFIX / "bin/port"): + #bundle(OPT_PREFIX, "bin/luajit", "--dest", "MacOS") + #bundle(OPT_PREFIX, "lib/lua") + #bundle(OPT_PREFIX, "share/lua") + + +## MAIN EXECUTABLES AND DEPENDENCIES +### Minimal (and some additional) executables for the 'MacOS' folder +bundle(GIMP_PREFIX, "bin/gimp*") +if os.path.exists(OPT_PREFIX / "bin/brew"): + bundle(OPT_PREFIX, "Cellar/libarchive/*/lib/libarchive.*.dylib", "--dest", "Frameworks") +### Bundled just to promote GEGL. See: https://gitlab.gnome.org/GNOME/gimp/-/issues/10580 +bundle(GIMP_PREFIX, "bin/gegl") +### Deps (DYLIBs) of the binaries in 'MacOS' and 'Frameworks' dirs +### We save the list of already copied DLLs to keep a state between 2_bundle-gimp-uni_dep runs. +done_dylib = Path(f"{os.getenv('MESON_BUILD_ROOT')}/done-dylib.list") +done_dylib.unlink(missing_ok=True) +for dir in ["MacOS", "Frameworks"]: + search_dir = GIMP_DISTRIB / dir + print(f"Searching for dependencies of {search_dir} in {GIMP_PREFIX} and {OPT_PREFIX}") + for dep in search_dir.rglob("*"): + if "Mach-O" in subprocess.run(["file", str(dep)], capture_output=True, text=True).stdout and ".dSYM" not in str(dep): + subprocess.run([ + sys.executable, f"{GIMP_SOURCE}/tools/lib_bundle.py", + str(dep), f"{GIMP_PREFIX}/", f"{OPT_PREFIX}/", + str(GIMP_DISTRIB), "--output-dll-list", done_dylib + ], check=True) + + +## .DSYM/DWARF DEBUG SYMBOLS (from babl, gegl and GIMP binaries) +for dir in ["MacOS", "Frameworks"]: + search_dir = GIMP_DISTRIB / dir + for binary in search_dir.rglob("*"): + if "Mach-O" in subprocess.run(["file", str(binary)], capture_output=True, text=True).stdout and ".dSYM" not in str(binary) and not binary.is_symlink(): + result = subprocess.run(["dsymutil", "--no-output", binary], capture_output=True, text=True) + if not "no debug symbols" in result.stdout + result.stderr: + print(f"(INFO): generating debug symbols file as {binary}.dSYM") + try: + subprocess.run(["dsymutil", binary, "-o", f"{binary}.dSYM"], check=True, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError as e: + sys.stderr.write(f"Failed to generate debug symbols from {binary}: {e}\n") + + +## FIXME: DEVELOPMENT FILES (bundle babl, gegl and gimp libs as .framework with Headers/ ?). +#clean(GIMP_DISTRIB, "lib/*.a") +#bundle(GIMP_PREFIX, "include/gimp-*") +#bundle(GIMP_PREFIX, "include/babl-*") +#bundle(GIMP_PREFIX, "include/gegl-*") +#bundle(GIMP_PREFIX, "lib/pkgconfig/gimp*") +#bundle(GIMP_PREFIX, "lib/pkgconfig/babl*") +#bundle(GIMP_PREFIX, "lib/pkgconfig/gegl*") diff --git a/build/macos/Info.plist b/build/macos/Info.plist new file mode 100644 index 0000000000..be349640df --- /dev/null +++ b/build/macos/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + gimp%MUTEX_SUFFIX% + CFBundleIconFile + AppIcon.icns + CFBundleIdentifier + %BUNDLE_IDENTIFIER% + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + %BUNDLE_NAME% + CFBundlePackageType + APPL + CFBundleSupportedPlatforms + + MacOSX + + CFBundleShortVersionString + %GIMP_VERSION% + CFBundleSignature + GIMP + CFBundleVersion + %GIMP_VERSION% + NSHumanReadableCopyright + © The GIMP Team + LSApplicationCategoryType + public.app-category.photography + + LSMinimumSystemVersion + 14.0 + NSHighResolutionCapable + + NSRequiresAquaSystemAppearance + + NLSRequiresNativeExecution + + + diff --git a/build/macos/README.md b/build/macos/README.md index 23ecf42509..3b1bd09d3b 100644 --- a/build/macos/README.md +++ b/build/macos/README.md @@ -1,5 +1,7 @@ -## macOS files not here +## Note about macOS build situation -macOS build files are not stored here yet. +These macOS build files on GIMP repo do not fully support older macOS yet. +You can use them only the latest 3 versions of macOS. -They are in: [gimp-macos-build](https://gitlab.gnome.org/Infrastructure/gimp-macos-build) repo. +The build files for older macOS are in: [gimp-macos-build] +(https://gitlab.gnome.org/Infrastructure/gimp-macos-build) repo. diff --git a/build/macos/all-deps-uni.txt b/build/macos/all-deps-uni.txt new file mode 100644 index 0000000000..f8528e9d60 --- /dev/null +++ b/build/macos/all-deps-uni.txt @@ -0,0 +1,50 @@ +# For easier quality of life and to avoid disparity between pacakges, +# KEEP THIS FILE ORDERING SIMILAR to build/windows/all-deps-uni.txt + +# BUILD-TIME ONLY DEPS +gettext \ +gi-docgen \ +gobject-introspection +quartz -x11|homebrew:gobject-introspection \ +libxslt|homebrew:libxslt \ +meson|homebrew:meson \ +pkgconfig|homebrew:pkgconf \ +vala -valadoc \ + +# RUNTIME DEPS +aalib -x11 \ +appstream -x11|homebrew:appstream \ +atk \ +cairo +quartz -x11|homebrew:cairo \ +#cfitsio not built by Apple Clang +gexiv2|homebrew:gexiv2 \ +ghostscript -x11|homebrew:ghostscript \ +glib2 +quartz -x11|homebrew:glib \ +glib-networking|homebrew:glib-networking \ +graphviz -x11|homebrew:graphviz \ +gtk3 +quartz -x11|homebrew:gtk+3 \ adwaita-icon-theme +quartz -x11|homebrew:adwaita-icon-theme \ shared-mime-info|homebrew:shared-mime-info \ +iso-codes|homebrew:iso-codes \ +json-glib|homebrew:json-glib \ +lcms2|homebrew:lcms2 \ +libarchive|homebrew:libarchive \ +libheif \ +libjpeg-turbo|homebrew:libjpeg-turbo \ +libjxl \ +libmng \ +libmypaint \ +libpng|homebrew:libpng \ +librsvg|homebrew:librsvg \ +tiff \ +webp \ +libwmf -x11|homebrew:libwmf \ +#maxflow not available yet +mypaint-brushes|homebrew:mypaint-brushes \ +openexr \ +openjpeg \ +pango +quartz -x11 \ +poppler|homebrew:poppler \ +poppler-data \ +python313|homebrew:python \ py313-certifi|homebrew:certifi +py313-gobject3|homebrew:pygobject3 \ +qoi \ +#suitesparse not built by Apple Clang +#xpm depends on x11 diff --git a/build/macos/patches/0001-app-libgimpwidgets-meson-plug-ins-Patch-macOS-bundle.patch b/build/macos/patches/0001-app-libgimpwidgets-meson-plug-ins-Patch-macOS-bundle.patch new file mode 100644 index 0000000000..37a570963e --- /dev/null +++ b/build/macos/patches/0001-app-libgimpwidgets-meson-plug-ins-Patch-macOS-bundle.patch @@ -0,0 +1,191 @@ +From fc8868bdd2900e6bc1399dc336d73ac1c27e60f5 Mon Sep 17 00:00:00 2001 +From: Bruno Lopes +Date: Tue, 6 Jan 2026 09:59:35 -0300 +Subject: [PATCH] app, libgimpwidgets, meson, plug-ins: Patch macOS bundle + +This is needed because our current code assumes that +the prefix is .app/Contents/Resources, which should not. +--- + app/config/gimpcoreconfig.c | 2 +- + app/main.c | 39 +++++++++++++--------------- + libgimpbase/gimpenv.c | 12 +++++---- + libgimpwidgets/gimpwidgets-private.c | 2 +- + meson.build | 2 +- + plug-ins/common/file-wmf.c | 4 +-- + 6 files changed, 30 insertions(+), 31 deletions(-) + +diff --git a/app/config/gimpcoreconfig.c b/app/config/gimpcoreconfig.c +index a4591055d6..853cf99f10 100644 +--- a/app/config/gimpcoreconfig.c ++++ b/app/config/gimpcoreconfig.c +@@ -311,7 +311,7 @@ gimp_core_config_class_init (GimpCoreConfigClass *klass) + + #ifdef ENABLE_RELOCATABLE_RESOURCES + mypaint_brushes = g_build_filename ("${gimp_installation_dir}", +- "share", "mypaint-data", ++ "Resources", "mypaint-data", + "2.0", "brushes", NULL); + #else + mypaint_brushes = g_strdup (MYPAINT_BRUSHES_DIR); +diff --git a/app/main.c b/app/main.c +index d577dd0b04..b0c326ac5c 100644 +--- a/app/main.c ++++ b/app/main.c +@@ -355,13 +355,13 @@ gimp_macos_setenv (const char * progname) + gboolean need_pythonhome = TRUE; + + bin_dir = g_path_get_dirname (resolved_path); +- tmp = g_strdup_printf ("%s/../Resources/lib", bin_dir); ++ tmp = g_strdup_printf ("%s/../Frameworks", bin_dir); + lib_dir = g_canonicalize_filename (tmp, NULL); + g_free (tmp); +- tmp = g_strdup_printf ("%s/../Resources/share", bin_dir); ++ tmp = g_strdup_printf ("%s/../Resources", bin_dir); + share_dir = g_canonicalize_filename (tmp, NULL); + g_free (tmp); +- tmp = g_strdup_printf ("%s/../Resources/etc", bin_dir); ++ tmp = g_strdup_printf ("%s/../SharedSupport", bin_dir); + etc_dir = g_canonicalize_filename (tmp, NULL); + g_free (tmp); + +@@ -392,23 +392,6 @@ gimp_macos_setenv (const char * progname) + } + } + +- /* Detect we were built in homebrew for MacOS (for PYTHONHOME purposes) */ +- tmp = g_strdup_printf ("%s/../Frameworks/Python.framework", share_dir); +- if (tmp && !stat (tmp, &sb) && S_ISDIR (sb.st_mode)) +- { +- g_print ("GIMP was built with homebrew\n"); +- need_pythonhome = FALSE; +- } +- g_free (tmp); +- /* Detect we were built in MacPorts for MacOS (for PYTHONHOME purposes) */ +- tmp = g_strdup_printf ("%s/../Library/Frameworks/Python.framework", share_dir); +- if (tmp && !stat (tmp, &sb) && S_ISDIR (sb.st_mode)) +- { +- g_print ("GIMP was built with MacPorts\n"); +- need_pythonhome = FALSE; +- } +- g_free (tmp); +- + /* Minimum runtime paths */ + path_len = strlen (g_getenv ("PATH") ? g_getenv ("PATH") : "") + strlen (bin_dir) + 2; + path = g_try_malloc (path_len); +@@ -461,6 +444,20 @@ gimp_macos_setenv (const char * progname) + g_setenv ("GTK_IM_MODULE", tmp, TRUE); + g_free (tmp); + ++ /* Packaging-specific paths */ ++ tmp = g_strdup_printf ("%s/%s/%s", lib_dir, GIMP_PACKAGE, GIMP_PLUGIN_VERSION); ++ g_setenv ("GIMP3_PLUGINDIR", tmp, TRUE); ++ g_free (tmp); ++ tmp = g_strdup_printf ("%s/%s/%s", share_dir, GIMP_PACKAGE, GIMP_DATA_VERSION); ++ g_setenv ("GIMP3_DATADIR", tmp, TRUE); ++ g_free (tmp); ++ tmp = g_strdup_printf ("%s/locale", share_dir); ++ g_setenv ("GIMP3_LOCALEDIR", tmp, TRUE); ++ g_free (tmp); ++ tmp = g_strdup_printf ("%s/%s/%s", etc_dir, GIMP_PACKAGE, GIMP_SYSCONF_VERSION); ++ g_setenv ("GIMP3_SYSCONFDIR", tmp, TRUE); ++ g_free (tmp); ++ + /* Other needed runtime paths (related to features) */ + tmp = g_strdup_printf ("%s/fonts", etc_dir); + g_setenv ("FONTCONFIG_PATH", tmp, TRUE); +@@ -479,7 +476,7 @@ gimp_macos_setenv (const char * progname) + g_free (tmp); + if (need_pythonhome) + { +- tmp = g_strdup_printf ("%s/Library/Frameworks/Python.framework/Versions/%s", share_dir, PYTHON_VERSION); ++ tmp = g_strdup_printf ("%s/Python.framework/Versions/%s", lib_dir, PYTHON_VERSION); + g_setenv ("PYTHONHOME", tmp, TRUE); + g_free (tmp); + } +diff --git a/libgimpbase/gimpenv.c b/libgimpbase/gimpenv.c +index 0af4f2f653..7293fe4bb1 100644 +--- a/libgimpbase/gimpenv.c ++++ b/libgimpbase/gimpenv.c +@@ -415,17 +415,18 @@ gimp_installation_directory (void) + + { + NSAutoreleasePool *pool; +- NSString *resource_path; ++ NSString *app_path; ++ gchar *app_gpath; + gchar *basename; + gchar *basepath; + gchar *dirname; + + pool = [[NSAutoreleasePool alloc] init]; + +- resource_path = [[NSBundle mainBundle] resourcePath]; ++ app_path = [[NSBundle mainBundle] bundlePath]; + +- basename = g_path_get_basename ([resource_path UTF8String]); +- basepath = g_path_get_dirname ([resource_path UTF8String]); ++ basename = g_path_get_basename ([app_path UTF8String]); ++ basepath = g_path_get_dirname ([app_path UTF8String]); + dirname = g_path_get_basename (basepath); + + if (! strcmp (basename, ".libs")) +@@ -488,7 +489,8 @@ gimp_installation_directory (void) + { + /* if none of the above match, we assume that we are really in a bundle */ + +- toplevel = g_strdup ([resource_path UTF8String]); ++ app_gpath = g_strdup ([app_path UTF8String]); ++ toplevel = g_strconcat (app_gpath, "/Contents", NULL); + } + + g_free (basename); +diff --git a/libgimpwidgets/gimpwidgets-private.c b/libgimpwidgets/gimpwidgets-private.c +index 0ebe7fa7e0..c7ab6d6950 100644 +--- a/libgimpwidgets/gimpwidgets-private.c ++++ b/libgimpwidgets/gimpwidgets-private.c +@@ -97,7 +97,7 @@ gimp_widgets_init (GimpHelpFunc standard_help_func, + { + cat_dir = "apps"; + #ifdef ENABLE_RELOCATABLE_RESOURCES +- base_dir = g_build_filename (gimp_installation_directory (), "share", "icons", "hicolor", NULL); ++ base_dir = g_build_filename (gimp_installation_directory (), "Resources", "icons", "hicolor", NULL); + #else + base_dir = g_build_filename (DATAROOTDIR, "icons", "hicolor", NULL); + #endif +diff --git a/meson.build b/meson.build +index 9614be6197..b18955cdf9 100644 +--- a/meson.build ++++ b/meson.build +@@ -536,7 +536,7 @@ mypaint_brushes = dependency('mypaint-brushes-2.0') + + if relocatable_bundle + mypaint_brushes_dir = '${gimp_installation_dir}'\ +- /'share'/'mypaint-data'/'2.0'/'brushes' ++ /'Resources'/'mypaint-data'/'2.0'/'brushes' + else + mypaint_brushes_dir = mypaint_brushes.get_variable(pkgconfig: 'brushesdir') + endif +diff --git a/plug-ins/common/file-wmf.c b/plug-ins/common/file-wmf.c +index 62c33ab221..b7517e0624 100644 +--- a/plug-ins/common/file-wmf.c ++++ b/plug-ins/common/file-wmf.c +@@ -382,7 +382,7 @@ load_wmf_size (GFile *file, + + #ifdef ENABLE_RELOCATABLE_RESOURCES + wmffontdirs[0] = g_build_filename (gimp_installation_directory (), +- "share/libwmf/fonts", NULL); ++ "Resources/libwmf/fonts", NULL); + flags |= WMF_OPT_FONTDIRS; + api_options.fontdirs = wmffontdirs; + #endif +@@ -529,7 +529,7 @@ wmf_load_file (GFile *file, + + #ifdef ENABLE_RELOCATABLE_RESOURCES + wmffontdirs[0] = g_build_filename (gimp_installation_directory (), +- "share/libwmf/fonts/", NULL); ++ "Resources/libwmf/fonts/", NULL); + flags |= WMF_OPT_FONTDIRS; + api_options.fontdirs = wmffontdirs; + #endif +-- +2.50.1 (Apple Git-155) diff --git a/build/macos/patches/0001-build-macos-Do-not-require-gexiv2-0.14-on-homebrew.patch b/build/macos/patches/0001-build-macos-Do-not-require-gexiv2-0.14-on-homebrew.patch new file mode 100644 index 0000000000..9213437ed9 --- /dev/null +++ b/build/macos/patches/0001-build-macos-Do-not-require-gexiv2-0.14-on-homebrew.patch @@ -0,0 +1,49 @@ +From 2dd50107f6a700527741eb2373a8ba26975a961c Mon Sep 17 00:00:00 2001 +From: Bruno Lopes +Date: Fri, 26 Dec 2025 19:54:22 -0300 +Subject: [PATCH] build/macos: Do not require gexiv2-0.14 on homebrew + +--- + libgimp/meson.build | 2 +- + meson.build | 6 ++---- + 2 files changed, 3 insertions(+), 5 deletions(-) + +diff --git a/libgimp/meson.build b/libgimp/meson.build +index c12e60c2cd..738564c2b9 100644 +--- a/libgimp/meson.build ++++ b/libgimp/meson.build +@@ -387,7 +387,7 @@ libgimp_deps_table = [ + { 'gir': 'cairo-1.0', 'vapi': 'cairo-1.0', }, + { 'gir': 'GdkPixbuf-2.0', 'vapi': 'gdk-pixbuf-2.0', }, + { 'gir': 'Gegl-0.4', 'vapi': 'gegl-0.4', }, +- { 'gir': 'GExiv2-0.10', 'vapi': 'gexiv2', }, ++ { 'gir': 'GExiv2-0.16', 'vapi': 'gexiv2-0.16', }, + { 'gir': 'Gio-2.0', 'vapi': 'gio-2.0', }, + { 'gir': gio_specific_gir, 'vapi': gio_specific_vapi, }, + { 'gir': 'GLib-2.0', 'vapi': 'glib-2.0', }, +diff --git a/meson.build b/meson.build +index 97662d62d7..4fcf576cce 100644 +--- a/meson.build ++++ b/meson.build +@@ -454,9 +454,8 @@ gegl_minver = '0.4.66' + gegl = dependency('gegl-0.4', version: '>='+gegl_minver) + exiv2_minver = '0.27.4' + exiv2 = dependency('exiv2', version: '>='+exiv2_minver) +-gexiv2_minver = '0.14.0' +-gexiv2_maxver = '0.15.0' +-gexiv2 = dependency('gexiv2', version: ['>='+gexiv2_minver, '<'+gexiv2_maxver]) ++gexiv2_minver = '0.16.0' ++gexiv2 = dependency('gexiv2-0.16', version: '>='+gexiv2_minver) + + gio = dependency('gio-2.0') + gio_specific_name = platform_windows ? 'gio-windows-2.0' : 'gio-unix-2.0' +@@ -1686,7 +1685,6 @@ install_conf.set('GDK_PIXBUF_REQUIRED_VERSION', gdk_pixbuf_minver) + install_conf.set('GEGL_REQUIRED_VERSION', gegl_minver) + install_conf.set('EXIV2_REQUIRED_VERSION', exiv2_minver) + install_conf.set('GEXIV2_REQUIRED_VERSION', gexiv2_minver) +-install_conf.set('GEXIV2_MAX_VERSION', gexiv2_maxver) + install_conf.set('GLIB_REQUIRED_VERSION', glib_minver) + install_conf.set('GTK_REQUIRED_VERSION', gtk3_minver) + install_conf.set('HARFBUZZ_REQUIRED_VERSION', harfbuzz_minver) +-- +2.50.1 (Apple Git-155) diff --git a/build/macos/patches/0001-meson-Patch-python-version.patch b/build/macos/patches/0001-meson-Patch-python-version.patch new file mode 100644 index 0000000000..91db12fac9 --- /dev/null +++ b/build/macos/patches/0001-meson-Patch-python-version.patch @@ -0,0 +1,24 @@ +From 9ee6b4e10c0b429358640aac4e081e2aca329ddb Mon Sep 17 00:00:00 2001 +From: Bruno Lopes +Date: Sat, 10 Jan 2026 15:49:22 -0300 +Subject: [PATCH] meson: Patch python version + +--- + meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/meson.build b/meson.build +index cd51137d6f..b59fb6cceb 100644 +--- a/meson.build ++++ b/meson.build +@@ -130,7 +130,7 @@ versionconfig.set('GIMP_API_VERSION', gimp_api_version) + pkgconfig = import('pkgconfig') + i18n = import('i18n') + gnome = import('gnome') +-python = import('python').find_installation() ++python = import('python').find_installation('python3.13') + simd = import('unstable-simd') + fs = import('fs') + +-- +2.50.1 (Apple Git-155) diff --git a/build/macos/patches/xdg-email b/build/macos/patches/xdg-email new file mode 100644 index 0000000000..0a419cb7f1 --- /dev/null +++ b/build/macos/patches/xdg-email @@ -0,0 +1,67 @@ +#!/bin/sh + +# this is poor man xdg-email wrapper for osx. +# It is intended to emulate xdg-email functionality using Mail.app and oascript. +# currently only --attach --subject and --body commands are supported +# and only first email address is used + +# There is now ling args support in osx getopt, so we are doing this hack +# and transforming long options to short ones +# +# Written by Alex Samorukov, samm@os2.kiev.ua + +for arg in "$@"; do + shift + case "$arg" in + "--attach") set -- "$@" "-a" ;; + "--subject") set -- "$@" "-s" ;; + "--body") set -- "$@" "-b" ;; + "--help") set -- "$@" "-h" ;; + *) set -- "$@" "$arg" + esac +done + +# Default behavior +rest=false; ws=false + +ATTACH="" +SUBJECT="" +BODY="" +# Parse short options +OPTIND=1 +while getopts "a:s:b:h" opt +do + case "$opt" in + "a") ATTACH="$OPTARG"; ;; + "s") SUBJECT="$OPTARG"; ;; + "b") BODY="$OPTARG"; ;; + "h") echo "sends email using Mail.app from commandline"; exit 1 ;; + esac +done +shift $(expr $OPTIND - 1) # remove options from positional parameters + +TO_ADDR="$1" + +SCRIPT=" +set recipientName to \"\" +set recipientAddress to \"${TO_ADDR}\" +set theSubject to \"${SUBJECT}\" +set theContent to \"${BODY}\" +set theAttachmentFile to \"${ATTACH}\" -- the attachment path + +tell application \"Mail\" + ## set focus + activate + ## Create the message + set theMessage to make new outgoing message with properties {subject:theSubject, content:theContent, visible:true} + + ## attach file + tell theMessage to make new attachment with properties {file name:theAttachmentFile} + + ## Set a recipient + tell theMessage + make new to recipient with properties {name:recipientName, address:recipientAddress} + end tell +end tell +" +echo "$SCRIPT"|/usr/bin/osascript diff --git a/meson.build b/meson.build index cd51137d6f..643ac3ae5a 100644 --- a/meson.build +++ b/meson.build @@ -2143,6 +2143,12 @@ if get_option('windows-installer') or get_option('ms-store') meson.add_install_script(install_win_bundling_script) endif +# On macOS, install deps in an .app bundle before distributing +if get_option('dmg') + install_macos_debug_script = find_program('build/macos/2_bundle-gimp-uni_base.py') + meson.add_install_script(install_macos_debug_script) +endif + ################################################################################ # Subdir installations