This is an automated email from the ASF dual-hosted git repository. leginee pushed a commit to branch mac-vcl-button-contrast in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit ce13a97af859b8c62e2a6694f386118769016f57 Author: peter kovacs <[email protected]> AuthorDate: Sat Sep 5 06:07:14 2026 +0200 Apple Silicon: make the macOS application bundle code-signable codesign refuses to seal a bundle that holds anything but Mach-O binaries in Contents/MacOS, and scp2 installed the whole payload there -- 993 data files (rdb, jar, res, rc, png, the Python tree) plus loose NOTICE/README in Contents. That is why no AOO build has ever been signed: official 4.1.15 reports "code object is not signed at all" as well. Only the linker's per-binary ad-hoc signatures were present. Contents/MacOS now holds just the soffice launcher, and the installation lives in Contents/program -- the directory name every other UNX platform already uses. Nothing moves relative to anything else. * scp2: gid_Brand_Dir_Program -> Contents/program, soffice into a new gid_Dir_Bundle_Contents_MacOS, NOTICE next to LICENSE in the program dir. Drop gid_Brand_Unixlink_Program (program -> MacOS, now a real directory) and gid_Brand_Unixlink_Urelibs, whose target has not existed since the basis/ure split was dropped and which by itself fails codesign --deep --strict. Add program/soffice -> ../MacOS/soffice so cppuhelper's findsofficepath() and the SDK keep working. * sal: $ORIGIN is derived from the ini file's path, so having getIniFileName_Impl look for sofficerc in Contents/program anchors the whole bootstrap chain (URE_LIB_DIR, OOO_BASE_DIR, services.rdb, unorc) there. Falls back to the old location when that file is absent. * install names: new BRANDBIN location (@executable_path/../program) for the launcher; OXT/BOXT libraries move to the same, correct both from the launcher and from the helper binaries in program; ICU switches from @executable_path to @loader_path, since its libraries are loaded by their neighbours. * installer: skip the "copy README/LICENSE to the installation root" step on macOS, where that root is the bundle's Contents; the copies users see are the ones in the .dmg root. Replace hdiutil makehybrid, which stamps an empty com.apple.FinderInfo onto every file in the image and thereby breaks --strict verification (and notarization), with hdiutil create -srcfolder. Its -hfs-openfolder auto-open is unsupported on Apple Silicon anyway. Sign each .app before imaging when MACOSX_CODESIGNING_IDENTITY is set. Adds mac-silicon-sign.sh (signs inside-out: every Mach-O, nested bundles, then the app) and the hardened-runtime entitlements the JVM, the external JDK, extensions and Python modules need. Verified ad-hoc: the app inside the built .dmg is "valid on disk, satisfies its Designated Requirement", boots to the first-start wizard, import uno works (pyuno 3.11.15) and unopkg lists the bundled extensions. Gatekeeper acceptance still needs a Developer ID certificate and notarization. All changes are macOS-scoped (#ifdef MACOSX, .IF "$(OS)" == "MACOSX", ismacosx, mac-only files, Darwin-only patch hunks); the one shared line, gid_Brand_Dir_Program's DosName, was already "program" off macOS. Co-Authored-By: Claude Opus 5 <[email protected]> --- .agent/mac-silicon-port.md | 95 ++++++++++++-- main/cppuhelper/source/findsofficepath.c | 7 +- main/desktop/util/makefile.mk | 6 + main/icu/icu-darwin.patch | 2 +- main/icu/icu4c-4_2_1-src.patch | 2 +- main/mac-silicon-entitlements.plist | 21 ++++ main/mac-silicon-sign.sh | 140 +++++++++++++++++++++ main/sal/rtl/source/bootstrap.cxx | 22 ++++ main/scp2/source/ooo/common_brand.scp | 44 ++++--- main/scp2/source/ooo/directory_ooo.scp | 10 ++ main/scp2/source/ooo/ooo_brand.scp | 7 +- main/solenv/bin/macosx-change-install-names.pl | 12 +- main/solenv/bin/modules/installer/scriptitems.pm | 18 ++- main/solenv/bin/modules/installer/simplepackage.pm | 22 +++- main/solenv/inc/unxmacc.mk | 1 + main/solenv/inc/unxmacx.mk | 1 + 16 files changed, 362 insertions(+), 48 deletions(-) diff --git a/.agent/mac-silicon-port.md b/.agent/mac-silicon-port.md index 41359f84bf..7b0aef437c 100644 --- a/.agent/mac-silicon-port.md +++ b/.agent/mac-silicon-port.md @@ -297,20 +297,91 @@ the macOS `scp2` branches to that layout: first file and logs a scary line. dmake returns 0, "Successful packaging process!". - "Some modules contain old output trees" — leftover from earlier partial runs; cosmetic. +## Milestone 4 — a signable bundle layout ✅ (2026-09-05) + +The `.dmg` now ships an application that passes `codesign --verify --deep --strict`. +The fix is in the build and the installer, not in a post-processing pass. + + ./mac-silicon-sign.sh /Applications/OpenOffice.app # ad-hoc + ./mac-silicon-sign.sh -i "Developer ID Application: NAME (TEAMID)" OpenOffice.app + ./mac-silicon-sign.sh --verify OpenOffice.app + MACOSX_CODESIGNING_IDENTITY=- ./mac-silicon-build.sh # signed .dmg + +### What was wrong + +The linker ad-hoc-signs every Mach-O on arm64 (`flags=0x20002 adhoc,linker-signed`), which +is why all 367 dylibs load. But no *bundle* was ever sealed: `Sealed Resources=none`, no +`_CodeSignature/`, identifier `soffice.21391-1788411067` (linker-invented). Upstream never +fixed it — **official AOO 4.1.15 is "code object is not signed at all"** with this same +layout. + +`codesign` refuses to seal a bundle that has anything but Mach-O in `Contents/MacOS` +(verified: one stray data file is enough), and `gid_Brand_Dir_Program` put AOO's entire +payload there — 993 non-Mach-O files plus loose `NOTICE`/`README` in `Contents`. + +### The layout change + +`Contents/MacOS` now holds only the `soffice` launcher; the installation sits in +`Contents/program`, the same directory name every other UNX platform uses. Nothing moves +relative to anything else. + +| Change | Why | +| --- | --- | +| `scp2/source/ooo/common_brand.scp`: `gid_Brand_Dir_Program` → `Contents/program`; `gid_Brand_File_Bin_Soffice` → new `gid_Dir_Bundle_Contents_MacOS`; dropped `gid_Brand_Unixlink_Program` (`program -> MacOS`) and `gid_Brand_Unixlink_Urelibs` (dangling in *every* AOO build incl. 4.1.15 — a pre-3.4 basis/ure leftover that `--deep --strict` rejects); added `gid_Brand_Unixlink_Soffice` (`program/soffice -> ../MacOS/soffice`) | the payload leaves `MacOS`; the launcher stays as `CFBundleExecu [...] +| `sal/rtl/source/bootstrap.cxx` (`#ifdef MACOSX`) | `getIniFileName_Impl` maps `…/Contents/MacOS/<exe>rc` to `…/Contents/program/`. `$ORIGIN` comes from the *ini file's* path (`bootstrap.cxx:496`), so this one hop anchors the whole chain — `URE_LIB_DIR`, `OOO_BASE_DIR`, services.rdb, unorc. Falls back to the old path if the file is not there (plain bundles, the mdimporter) | +| `desktop/util/makefile.mk` + `macosx-change-install-names.pl` + `unxmac{c,x}.mk` | new mac-only `BRANDBIN` rpath location = `@executable_path/../program`, used for `soffice` only. `OXT`/`BOXT` libraries switched from `@executable_path` to `@executable_path/../program` — correct from both the launcher and the helper binaries in `program` | +| `icu/icu4c-4_2_1-src.patch` (+ `icu-darwin.patch`) | ICU baked `-install_name @executable_path/…`; now `@loader_path/`, since ICU libs are loaded by their neighbours, not by the launcher | +| `installer/scriptitems.pm` (`ismacosx` guard) | the generic "copy README/LICENSE to the installation root" step targets OFFICEDIRECTORY = `Contents` on macOS. Suppressed there; the copies users actually see are the ones in the `.dmg` root | +| `scp2/source/ooo/ooo_brand.scp` | `NOTICE` went to `Contents` on macOS only; now next to `LICENSE` in `program` on every platform | +| `installer/simplepackage.pm` | `hdiutil makehybrid -hfs` stamps an empty `com.apple.FinderInfo` on **every** file in the image, which makes `--strict` reject the app inside the `.dmg` (and would fail notarization). Replaced with `hdiutil create -srcfolder`. The `-hfs-openfolder` auto-open it also did is dead anyway: `bless --openfolder` is unsupported on Apple Silicon. Also signs each `.app` before imaging when `MACOSX_CODESIGNING_IDENTITY` is set | + +Everything is macOS-scoped: `#ifdef MACOSX` / `.IF "$(OS)" == "MACOSX"` / `ismacosx`, +mac-only files (`unxmac*.mk`, `macosx-change-install-names.pl`), or Darwin-only patch hunks +(`config/mh-darwin`). The one cross-platform line, `gid_Brand_Dir_Program`'s `DosName`, was +already `"program"` everywhere but macOS. + +### Verified + +- App inside the built `.dmg`: `codesign --verify --deep --strict` → **valid on disk, + satisfies its Designated Requirement**. Same after copying it out of the image. +- Boots to the first-start wizard (sampled `FirstStart::execute`), signed and unsigned. +- `Contents/program/python -c "import uno"` → `pyuno ok 3.11.15`; `unopkg list` lists the + bundled dictionary extensions (exercises the OXT install-name path). +- `Contents/MacOS` contains exactly one file; no dangling symlinks anywhere in the bundle. + +### Bisected along the way + +`Contents/presets` must stay a real directory in `Contents` — relocating it kills startup +in `Desktop::HandleBootstrapErrors → FatalError`. Directories in `Contents` seal fine as +resources, so only the loose files ever needed to move. + +### Still open for distribution + +- No signing identity on this machine (`security find-identity -p codesigning` → 0). A + **Developer ID Application** certificate (Apple Developer Program, 99 USD/yr) is needed + to get past Gatekeeper elsewhere; ad-hoc signatures are always `spctl: rejected`. +- With a real identity the script adds hardened runtime + `--timestamp` and applies + `mac-silicon-entitlements.plist` (JIT and unsigned-exec-memory for the JVM, + disable-library-validation for the external JDK / extensions / Python modules, + dyld-env-vars, apple-events). Untested — no certificate to test with. +- Then `xcrun notarytool submit --wait` the `.dmg`, `xcrun stapler staple`, and sign the + `.dmg` itself (`mac-silicon-sign.sh -i <ID> foo.dmg`). +- Installing a *shared* extension writes into `share/uno_packages` inside the bundle and + breaks the seal. That is inherent to signing an app that modifies itself, not to this + layout change. + ## Status / Next -**A native arm64 Apache OpenOffice 5.1.0 `.dmg` for Apple Silicon now builds from trunk.** -Committed on `mac-silicon-minimal`. - -**Next (validation / polish, not yet done):** -1. Smoke-test the app: mount en-US dmg, copy `OpenOffice.app` to /Applications, launch - (`open`), open Writer, run a Basic macro, run a `pyuno` script (`python-core` layout is - new on macOS — `PYTHONHOME`/`PYTHONPATH` + `libpython3.11.dylib` install-name - relocatability are the things to check). -2. `libpython3.11.dylib` / `pyuno.so` install-name check (`--enable-shared` non-framework - CPython can bake an absolute `install_name`). -3. From-scratch `./mac-silicon-build.sh` on a clean tree to confirm no ordering luck. -4. Code-signing / notarization for distribution. +**A native arm64 Apache OpenOffice 5.1.0 `.dmg` for Apple Silicon builds from trunk, and +the application it installs is code-signable and verifies strictly.** + +**Next (not done):** +1. Full 8-language + SDK repackage (only `openoffice_en-US` has been rebuilt since the + layout change; `dmake openoffice_en-US` in `instsetoo_native/util` is the fast loop). +2. Developer ID + notarization (needs the certificate above). +3. Fuller smoke test: Writer/Calc round-trip, a Basic macro, a pyuno script against a + running soffice. +4. From-scratch `./mac-silicon-build.sh` on a clean tree to confirm no ordering luck. 5. NSS 3.39 age — compiled fine; watch at runtime (signatures / cert UI). **Milestone 1 (configure + bootstrap) DONE** — committed on `mac-silicon-minimal` diff --git a/main/cppuhelper/source/findsofficepath.c b/main/cppuhelper/source/findsofficepath.c index eccb7bdeba..bf0d3aa3f6 100644 --- a/main/cppuhelper/source/findsofficepath.c +++ b/main/cppuhelper/source/findsofficepath.c @@ -135,8 +135,11 @@ static char* platformSpecific() /* On MacOS we have no soffice link under /usr/bin but the default office location is known and we check this only */ - const char* MACDEFAULTOFFICEPATH = "/Applications/OpenOffice.app/Contents/MacOS"; - const char* MACDEFAULTSOFFICE = "/Applications/OpenOffice.app/Contents/MacOS/soffice"; + /* The installation lives in Contents/program (Contents/MacOS holds only the + launcher, so that the bundle can be code-signed); soffice is reachable + there through a symlink. */ + const char* MACDEFAULTOFFICEPATH = "/Applications/OpenOffice.app/Contents/program"; + const char* MACDEFAULTSOFFICE = "/Applications/OpenOffice.app/Contents/program/soffice"; if ( !access( MACDEFAULTSOFFICE, F_OK ) ) { diff --git a/main/desktop/util/makefile.mk b/main/desktop/util/makefile.mk index 3163142cc9..f19ecad786 100644 --- a/main/desktop/util/makefile.mk +++ b/main/desktop/util/makefile.mk @@ -101,6 +101,12 @@ APP1STACK=10000000 APP5TARGET=soffice APP5NOSAL=TRUE APP5RPATH=BRAND +.IF "$(OS)" == "MACOSX" +# soffice is the bundle launcher and lives alone in Contents/MacOS (only +# Mach-O binaries may, or the bundle cannot be code-signed); the libraries it +# links against are installed in Contents/program. +APP5RPATH=BRANDBIN +.ENDIF # MACOSX APP5OBJS=$(OBJ)$/copyright_ascii_ooo.obj $(OBJ)$/main.obj APP5STDLIBS = $(SALLIB) $(SOFFICELIB) .IF "$(OS)" == "LINUX" diff --git a/main/icu/icu-darwin.patch b/main/icu/icu-darwin.patch index 8ee7976878..4f9df08f3d 100644 --- a/main/icu/icu-darwin.patch +++ b/main/icu/icu-darwin.patch @@ -8,7 +8,7 @@ diff -ru misc/icu/source/data/pkgdataMakefile.in misc/build/icu/source/data/pkgd +# LD_SONAME must end with a path, but on Darwin this does not happen, because +# file source/config/mh-darwin makes it end with FINAL_SO_TARGET, instead of +# MIDDLE_SO_TARGET like other Unixes. -+LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @executable_path/ ++LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @loader_path/ all : clean @echo GENCCODE_ASSEMBLY_TYPE=$(GENCCODE_ASSEMBLY) >> $(OUTPUTFILE) diff --git a/main/icu/icu4c-4_2_1-src.patch b/main/icu/icu4c-4_2_1-src.patch index 572572a3ea..2649f18999 100644 --- a/main/icu/icu4c-4_2_1-src.patch +++ b/main/icu/icu4c-4_2_1-src.patch @@ -56,7 +56,7 @@ diff -ru misc/icu/source/config/mh-darwin misc/build/icu/source/config/mh-darwin ## Compiler switches to embed a library name and version information -LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name $(notdir $(MIDDLE_SO_TARGET)) -+LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @executable_path/$(notdir $(FINAL_SO_TARGET)) ++LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @loader_path/$(notdir $(FINAL_SO_TARGET)) ## Compiler switch to embed a runtime search path LD_RPATH= diff --git a/main/mac-silicon-entitlements.plist b/main/mac-silicon-entitlements.plist new file mode 100644 index 0000000000..ca5913f7e6 --- /dev/null +++ b/main/mac-silicon-entitlements.plist @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <!-- The bundled JVM/Java bridge and Basic runtime generate and run code at runtime. --> + <key>com.apple.security.cs.allow-jit</key> + <true/> + <key>com.apple.security.cs.allow-unsigned-executable-memory</key> + <true/> + <!-- soffice dlopens an external JDK's libjvm.dylib, user-installed UNO extensions + and Python modules; none of those carry our Team ID. --> + <key>com.apple.security.cs.disable-library-validation</key> + <true/> + <!-- sofficerc / the launcher set DYLD_* to find the bundled URE libraries. --> + <key>com.apple.security.cs.allow-dyld-environment-variables</key> + <true/> + <!-- Mail merge, "Send Document as E-Mail" and the installer script drive other apps. --> + <key>com.apple.security.automation.apple-events</key> + <true/> +</dict> +</plist> diff --git a/main/mac-silicon-sign.sh b/main/mac-silicon-sign.sh new file mode 100755 index 0000000000..5a085604c2 --- /dev/null +++ b/main/mac-silicon-sign.sh @@ -0,0 +1,140 @@ +#!/bin/bash +# +# Code-sign a built Apache OpenOffice .app (or .dmg) on macOS. +# +# ./mac-silicon-sign.sh [options] <OpenOffice.app|installer.dmg> ... +# +# Options: +# -i, --identity ID codesign identity; "-" = ad-hoc (default, or +# $MACOSX_CODESIGNING_IDENTITY when set) +# -e, --entitlements entitlements plist (default: mac-silicon-entitlements.plist) +# --hardened force hardened runtime even for an ad-hoc signature +# (implied by a real identity) +# --verify only report the current signing state, change nothing +# +# The linker already ad-hoc-signs each Mach-O it produces, which is why the +# binaries load at all on arm64. What it does not do is seal the *bundles*: +# without a _CodeSignature/CodeResources the .app has no identity, Gatekeeper +# rejects it once it carries a quarantine flag, and it can never be notarized. +# +# This signs inside-out -- every Mach-O, then the nested bundles, then the app. +# It relies on the installation being laid out so that Contents/MacOS holds +# only the soffice launcher (see scp2/source/ooo/common_brand.scp); codesign +# refuses to seal a bundle with anything else in there. + +set -euo pipefail + +SRCDIR=$(cd "$(dirname "$0")" && pwd) +IDENTITY="${MACOSX_CODESIGNING_IDENTITY:--}" +ENTITLEMENTS="$SRCDIR/mac-silicon-entitlements.plist" +HARDENED=no +VERIFY_ONLY=no +TARGETS=() + +while [ $# -gt 0 ]; do + case "$1" in + -i|--identity) IDENTITY="$2"; shift 2 ;; + -e|--entitlements) ENTITLEMENTS="$2"; shift 2 ;; + --hardened) HARDENED=yes; shift ;; + --verify) VERIFY_ONLY=yes; shift ;; + -h|--help) sed -n '2,25p' "$0"; exit 0 ;; + -*) echo "unknown option: $1" >&2; exit 2 ;; + *) TARGETS+=("$1"); shift ;; + esac +done + +[ ${#TARGETS[@]} -gt 0 ] || { echo "usage: $(basename "$0") [options] <app-or-dmg> ..." >&2; exit 2; } +[ "$IDENTITY" = "-" ] || HARDENED=yes + +is_macho() { + [ -f "$1" ] && [ ! -L "$1" ] && file -b "$1" 2>/dev/null | grep -q '^Mach-O' +} + +sign_one() { + local path="$1"; shift + local args=(--force --sign "$IDENTITY" --timestamp=none) + if [ "$IDENTITY" != "-" ]; then + args=(--force --sign "$IDENTITY" --timestamp) + fi + if [ "$HARDENED" = yes ]; then + args+=(--options runtime --entitlements "$ENTITLEMENTS") + fi + codesign "${args[@]}" "$@" "$path" +} + +report() { + local app="$1" + echo "--- $app" + codesign -dv --verbose=2 "$app" 2>&1 | grep -E 'Identifier|Format|CodeDirectory|Authority|TeamIdentifier|Sealed' || true + if codesign --verify --deep --strict "$app" 2>/dev/null; then + echo "verify: OK" + else + echo "verify: FAILED" + codesign --verify --deep --strict --verbose=2 "$app" 2>&1 | tail -5 + fi + spctl --assess --type exec --verbose=4 "$app" 2>&1 | tail -2 || true +} + +sign_app() { + local app="$1" + echo "==> signing $app (identity: $IDENTITY, hardened: $HARDENED)" + + # The installer stages every file read-only, and codesign has to rewrite them. + chmod -R u+w "$app" + # Quarantine and other xattrs make codesign fail or produce an unstable seal. + xattr -cr "$app" 2>/dev/null || true + + # A bundle's main executable is signed as part of its bundle, not on its own: + # codesign silently redirects such a path to the enclosing bundle. + local bundles=() main_execs=() b exe + while IFS= read -r b; do bundles+=("$b"); done < <(find "$app" \ + \( -name '*.app' -o -name '*.framework' -o -name '*.bundle' \ + -o -name '*.mdimporter' -o -name '*.plugin' -o -name '*.qlgenerator' \) \ + | awk '{ print gsub(/\//,"/") "\t" $0 }' | sort -rn | cut -f2-) + for b in "${bundles[@]}"; do + exe=$(/usr/libexec/PlistBuddy -c 'Print CFBundleExecutable' "$b/Contents/Info.plist" 2>/dev/null) || continue + main_execs+=("$b/Contents/MacOS/$exe") + done + + # 1. every Mach-O object, deepest path first + local count=0 + while IFS= read -r f; do + is_macho "$f" || continue + case " ${main_execs[*]} " in *" $f "*) continue ;; esac + sign_one "$f" + count=$((count + 1)) + done < <(find "$app" -type f | awk '{ print gsub(/\//,"/") "\t" $0 }' | sort -rn | cut -f2-) + echo " signed $count Mach-O objects" + + # 2. nested bundles, deepest first, so each seal covers already-signed contents + for b in "${bundles[@]}"; do + [ "$b" = "$app" ] && continue + sign_one "$b" + echo " sealed nested bundle: ${b#"$app"/}" + done + + # 3. the app bundle itself + sign_one "$app" + echo " sealed $app" + report "$app" +} + +for target in "${TARGETS[@]}"; do + [ -e "$target" ] || { echo "no such path: $target" >&2; exit 1; } + case "$target" in + *.dmg) + if [ "$VERIFY_ONLY" = yes ]; then report "$target"; continue; fi + # A .dmg is signed as a whole; the .app inside must already be signed. + if [ "$IDENTITY" = "-" ]; then + echo "refusing to ad-hoc sign a .dmg (pointless); pass -i <Developer ID>" >&2 + exit 1 + fi + codesign --force --sign "$IDENTITY" --timestamp "$target" + report "$target" + ;; + *) + if [ "$VERIFY_ONLY" = yes ]; then report "$target"; continue; fi + sign_app "$target" + ;; + esac +done diff --git a/main/sal/rtl/source/bootstrap.cxx b/main/sal/rtl/source/bootstrap.cxx index 35a3a9f8c1..2ca20daa1d 100644 --- a/main/sal/rtl/source/bootstrap.cxx +++ b/main/sal/rtl/source/bootstrap.cxx @@ -271,6 +271,28 @@ static OUString & getIniFileName_Impl() // append config file suffix fileName += OUString(RTL_CONSTASCII_USTRINGPARAM(SAL_CONFIGFILE(""))); + +#ifdef MACOSX + // In an application bundle only Mach-O binaries may live in + // Contents/MacOS -- code signing rejects the bundle otherwise -- + // so the installation itself (rc files, rdbs, the libraries) sits + // in Contents/program, exactly as on the other UNX platforms. + // Look the ini file up there; $ORIGIN is derived from it below, + // which anchors the whole bootstrap chain in the program dir. + OUString macOSDir (RTL_CONSTASCII_USTRINGPARAM("/Contents/MacOS/")); + sal_Int32 nMacOSDir = fileName.lastIndexOf(macOSDir); + if (nMacOSDir >= 0) + { + OUString programName = + fileName.replaceAt(nMacOSDir, macOSDir.getLength(), + OUString(RTL_CONSTASCII_USTRINGPARAM("/Contents/program/"))); + // Fall back to the old location for anything that is not laid + // out this way (a plain bundle, an mdimporter, ...). + ::osl::DirectoryItem item; + if (::osl::DirectoryItem::get(programName, item) == ::osl::DirectoryItem::E_None) + fileName = programName; + } +#endif } static OUString theFileName; diff --git a/main/scp2/source/ooo/common_brand.scp b/main/scp2/source/ooo/common_brand.scp index 42769143ed..ff45b6060a 100644 --- a/main/scp2/source/ooo/common_brand.scp +++ b/main/scp2/source/ooo/common_brand.scp @@ -84,7 +84,7 @@ Module gid_Module_Root_Brand gid_Brand_File_Share_Xdg_StartCenter, gid_Brand_File_Share_Xdg_Writer, gid_Brand_File_Txt_Package); - Unixlinks = (gid_Brand_Unixlink_Program, + Unixlinks = (gid_Brand_Unixlink_Soffice, gid_Brand_Unixlink_Unopkg); // Unixlinks = (gid_Brand_Unixlink_BasisLink, // gid_Brand_Unixlink_Program, @@ -122,11 +122,10 @@ End Directory gid_Brand_Dir_Program #if defined MACOSX ParentID = gid_Dir_Bundle_Contents; - DosName = "MacOS"; #else ParentID = gid_Dir_Brand_Root; - DosName = "program"; #endif + DosName = "program"; End Directory gid_Brand_Dir_Resource @@ -269,29 +268,21 @@ End // UnixLinks -#if defined MACOSX -Unixlink gid_Brand_Unixlink_Urelibs - Dir = gid_Brand_Dir_Program; - Name = "urelibs"; - Target = "../basis-link/ure-link/lib"; -End -#endif - -#if defined MACOSX -Unixlink gid_Brand_Unixlink_Program - BIN_FILE_BODY; - Dir = gid_Dir_Bundle_Contents; - Name = "program"; - Target = "MacOS"; - Styles = (); -End -#endif +// gid_Brand_Unixlink_Urelibs (urelibs -> ../basis-link/ure-link/lib) and +// gid_Brand_Unixlink_Program (Contents/program -> MacOS) are gone: the first +// has pointed at nothing since the basis/ure split was dropped, and +// Contents/program is now the real installation directory. // Files File gid_Brand_File_Bin_Soffice BIN_FILE_BODY; +#if defined MACOSX + // the bundle's CFBundleExecutable, and the only thing in Contents/MacOS + Dir = gid_Dir_Bundle_Contents_MacOS; +#else Dir = gid_Brand_Dir_Program; +#endif Name = EXENAME(soffice); #ifdef WNT Styles = (PACKED, PATCH_SO_NAME); @@ -411,6 +402,19 @@ Unixlink gid_Brand_Unixlink_Unopkg End #endif +// The launcher itself has to sit in Contents/MacOS, but everything that looks +// for the office binary next to the installation -- cppuhelper's +// findsofficepath(), scripts, the SDK -- expects it in the program directory. +#if defined MACOSX +Unixlink gid_Brand_Unixlink_Soffice + BIN_FILE_BODY; + Dir = gid_Brand_Dir_Program; + Name = "soffice"; + Target = "../MacOS/soffice"; + Styles = (); +End +#endif + #ifndef OS2 File gid_Brand_File_Bin_Unopkg BIN_FILE_BODY; diff --git a/main/scp2/source/ooo/directory_ooo.scp b/main/scp2/source/ooo/directory_ooo.scp index b69c6a2079..52fa8869bd 100644 --- a/main/scp2/source/ooo/directory_ooo.scp +++ b/main/scp2/source/ooo/directory_ooo.scp @@ -37,6 +37,16 @@ Directory gid_Dir_Bundle_Contents_Resources_Lang End #endif +// Only Mach-O binaries may live here: code signing refuses to seal a bundle +// with anything else in Contents/MacOS. The installation itself goes to +// Contents/program, so this holds just the soffice launcher. +#ifdef MACOSX +Directory gid_Dir_Bundle_Contents_MacOS + ParentID = gid_Dir_Bundle_Contents; + HostName = "MacOS"; +End +#endif + #ifdef MACOSX Directory gid_Dir_Bundle_Contents_Library ParentID = gid_Dir_Bundle_Contents; diff --git a/main/scp2/source/ooo/ooo_brand.scp b/main/scp2/source/ooo/ooo_brand.scp index fc617ad70a..d521fbd6d8 100644 --- a/main/scp2/source/ooo/ooo_brand.scp +++ b/main/scp2/source/ooo/ooo_brand.scp @@ -37,11 +37,10 @@ End File gid_File_Txt_Notice TXT_FILE_BODY; -#ifndef MACOSX + // next to LICENSE in the program directory on every platform; on Mac OS X + // it used to go into the bundle's Contents, which cannot be signed with a + // stray file in it Dir = gid_Brand_Dir_Program; -#else - Dir = gid_Dir_Bundle_Contents; -#endif Name = "NOTICE"; Styles = (PACKED); End diff --git a/main/solenv/bin/macosx-change-install-names.pl b/main/solenv/bin/macosx-change-install-names.pl index ba4a60a91c..0b10e1ec16 100644 --- a/main/solenv/bin/macosx-change-install-names.pl +++ b/main/solenv/bin/macosx-change-install-names.pl @@ -33,6 +33,10 @@ sub action($$$) 'app/SDK/URELIB' => '@executable_path', 'app/BRAND/URELIB' => '@executable_path', 'app/BRAND/OOO' => '@executable_path', + # BRANDBIN: the executable is the bundle launcher in Contents/MacOS, + # while the libraries are installed in Contents/program. + 'app/BRANDBIN/URELIB' => '@executable_path/../program', + 'app/BRANDBIN/OOO' => '@executable_path/../program', 'app/NONE/URELIB' => '@__VIA_LIBRARY_PATH__', 'app/NONE/OOO' => '@__VIA_LIBRARY_PATH__', 'app/NONE/NONE' => '@__VIA_LIBRARY_PATH__', @@ -40,8 +44,12 @@ sub action($$$) 'shl/OOO/URELIB' => '@loader_path', 'shl/OOO/OOO' => '@loader_path', 'shl/LOADER/LOADER' => '@loader_path', - 'shl/OXT/URELIB' => '@executable_path', - 'shl/BOXT/URELIB' => '@executable_path', + # Extension libraries can live inside the installation or in an + # extension directory, so they cannot use @loader_path; the office + # libraries are always in Contents/program, one level up from the + # launcher in Contents/MacOS and from the helper binaries themselves. + 'shl/OXT/URELIB' => '@executable_path/../program', + 'shl/BOXT/URELIB' => '@executable_path/../program', 'shl/BOXT/OOO' => '@loader_path', 'shl/NONE/URELIB' => '@__VIA_LIBRARY_PATH__', 'shl/NONE/OOO' => '@__VIA_LIBRARY_PATH__', diff --git a/main/solenv/bin/modules/installer/scriptitems.pm b/main/solenv/bin/modules/installer/scriptitems.pm index fe002f4a30..9d931573b9 100644 --- a/main/solenv/bin/modules/installer/scriptitems.pm +++ b/main/solenv/bin/modules/installer/scriptitems.pm @@ -1713,12 +1713,20 @@ sub add_License_Files_into_Installdir $newfile->{'modules'} = $installer::globals::rootbrandpackage; } - push(@newfilesarray, $newfile); + # On Mac OS X the installation root is the bundle's Contents + # directory, where only the entries defined by Apple may live -- + # a stray LICENSE or README there makes the bundle unsignable. + # The copy in the installation set (the .dmg root, below) is the + # one users actually see anyway. + if ( ! $installer::globals::ismacosx ) + { + push(@newfilesarray, $newfile); - $installer::logger::Lang->printf( - "New files: Adding file %s for the installation root to the file list. Language: %s\n", - $newfilename, - $defaultlanguage); + $installer::logger::Lang->printf( + "New files: Adding file %s for the installation root to the file list. Language: %s\n", + $newfilename, + $defaultlanguage); + } if ( defined $newfile->{'InstallName'} ) { diff --git a/main/solenv/bin/modules/installer/simplepackage.pm b/main/solenv/bin/modules/installer/simplepackage.pm index 63b78932c3..d77cefab59 100644 --- a/main/solenv/bin/modules/installer/simplepackage.pm +++ b/main/solenv/bin/modules/installer/simplepackage.pm @@ -541,7 +541,27 @@ sub create_package chdir $localfrom; } - $systemcall = "cd $localtempdir && hdiutil makehybrid -hfs -hfs-openfolder $folder $folder -hfs-volume-name \"$volume_name\" -ov -o $installdir/tmp && hdiutil convert -ov -format UDZO $installdir/tmp.dmg -o $archive && "; + # Code-sign the .app before it is sealed into the .dmg. Opt-in: without + # MACOSX_CODESIGNING_IDENTITY the installation set is left as it was. + if ( $ENV{'MACOSX_CODESIGNING_IDENTITY'} ) + { + my $signscript = $ENV{'SRC_ROOT'} . "/mac-silicon-sign.sh"; + foreach my $appdir ( glob("$localtempdir/$folder/*.app") ) + { + my $signcall = "$signscript -i \"$ENV{'MACOSX_CODESIGNING_IDENTITY'}\" \"$appdir\""; + my $signreturn = system($signcall); + if ( $signreturn ) { installer::exiter::exit_program("ERROR: Could not code-sign $appdir!", "create_package"); } + $installer::logger::Lang->print("Success: Code-signed $appdir\n"); + } + } + + # "hdiutil makehybrid -hfs" stamps an (empty) com.apple.FinderInfo onto + # every file in the image, which makes "codesign --verify --strict" + # reject the signed application inside the .dmg and would fail + # notarization. "hdiutil create -srcfolder" copies the files as they + # are. (The -hfs-openfolder auto-open it also did is not supported on + # Apple Silicon any more: bless refuses it.) + $systemcall = "cd $localtempdir && hdiutil create -srcfolder $folder -volname \"$volume_name\" -format UDZO -ov $archive && "; if (( $ref ne "" ) && ( $$ref ne "" )) { $systemcall .= "hdiutil unflatten $archive && Rez -a $$ref -o $archive && hdiutil flatten $archive &&"; } diff --git a/main/solenv/inc/unxmacc.mk b/main/solenv/inc/unxmacc.mk index 5f2d00fbda..9cb1c134e4 100644 --- a/main/solenv/inc/unxmacc.mk +++ b/main/solenv/inc/unxmacc.mk @@ -185,6 +185,7 @@ LINKFLAGSRUNPATH_UREBIN= LINKFLAGSRUNPATH_OOO=-install_name '@_______OOO/$(@:f)' LINKFLAGSRUNPATH_SDK= LINKFLAGSRUNPATH_BRAND= +LINKFLAGSRUNPATH_BRANDBIN= LINKFLAGSRUNPATH_OXT= LINKFLAGSRUNPATH_BOXT= LINKFLAGSRUNPATH_NONE=-install_name '@_______NONE/$(@:f)' diff --git a/main/solenv/inc/unxmacx.mk b/main/solenv/inc/unxmacx.mk index 0980e03e27..13dfaa9b13 100644 --- a/main/solenv/inc/unxmacx.mk +++ b/main/solenv/inc/unxmacx.mk @@ -194,6 +194,7 @@ LINKFLAGSRUNPATH_UREBIN= LINKFLAGSRUNPATH_OOO=-install_name '@_______OOO/$(@:f)' LINKFLAGSRUNPATH_SDK= LINKFLAGSRUNPATH_BRAND= +LINKFLAGSRUNPATH_BRANDBIN= LINKFLAGSRUNPATH_OXT= LINKFLAGSRUNPATH_BOXT= LINKFLAGSRUNPATH_NONE=-install_name '@_______NONE/$(@:f)'
