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)'

Reply via email to