This is an automated email from the ASF dual-hosted git repository. jimjag pushed a commit to branch msys2-dev-prototype in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit c193fac6023d516a8163f71c7486482d6c01f60e Author: Jim Jagielski <[email protected]> AuthorDate: Tue Aug 11 13:19:56 2026 -0400 Add an MSYS2 environment probe for the alternative Windows build Settles on a real machine what the feasibility document can only assert: whether clang accepts the tree's MSVC extensions, where ATL lives, and which system libraries MSYS2 can supply. Exits non-zero only on findings that invalidate the approach rather than merely resize a phase. --- msys2-probe.sh | 522 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 522 insertions(+) diff --git a/msys2-probe.sh b/msys2-probe.sh new file mode 100755 index 0000000000..405b321776 --- /dev/null +++ b/msys2-probe.sh @@ -0,0 +1,522 @@ +#!/usr/bin/env bash +#************************************************************** +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +#************************************************************** +# +# Environment probe for the proposed MSYS2/clang Windows build of Apache +# OpenOffice. See MSYS2-WINDOWS-BUILD-FEASIBILITY.md. +# +# This script does not build anything. It answers the questions that +# feasibility document lists as asserted-but-unverified, so that the plan's +# effort estimates rest on measurements rather than on assumptions. +# +# Run it from an MSYS2 **CLANG64** shell on a Windows 10+ x86_64 machine: +# +# ./msys2-probe.sh # probe only +# ./msys2-probe.sh --install # pacman -S the toolchain first, then probe +# ./msys2-probe.sh --atl-include=/c/path/to/atlmfc/include +# +# Exit status: 0 if no CRITICAL probe failed, 1 otherwise. + +set -uo pipefail + +DO_INSTALL=0 +ATL_INCLUDE_OVERRIDE="" +KEEP_TMP=0 + +for arg in "$@"; do + case "$arg" in + --install) DO_INSTALL=1 ;; + --atl-include=*) ATL_INCLUDE_OVERRIDE="${arg#*=}" ;; + --keep-tmp) KEEP_TMP=1 ;; + -h|--help) + sed -n '23,37p' "$0" | sed 's/^# \{0,1\}//' + exit 0 ;; + *) + echo "unknown option: $arg (try --help)" >&2 + exit 2 ;; + esac +done + +# ---------------------------------------------------------------- reporting -- + +PASS_N=0; FAIL_N=0; WARN_N=0; INFO_N=0 +CRITICAL_FAILED=0 +declare -a SUMMARY + +_record() { # _record <status> <severity> <label> <detail> + local status="$1" sev="$2" label="$3" detail="$4" + SUMMARY+=("$status|$sev|$label|$detail") + case "$status" in + PASS) PASS_N=$((PASS_N+1)) ;; + FAIL) FAIL_N=$((FAIL_N+1)) + [ "$sev" = "CRITICAL" ] && CRITICAL_FAILED=1 ;; + WARN) WARN_N=$((WARN_N+1)) ;; + INFO) INFO_N=$((INFO_N+1)) ;; + esac +} + +pass() { echo " [ PASS ] $1${2:+ -- $2}"; _record PASS "${3:-}" "$1" "${2:-}"; } +fail() { echo " [ FAIL ] $1${2:+ -- $2}"; _record FAIL "${3:-HIGH}" "$1" "${2:-}"; } +warn() { echo " [ WARN ] $1${2:+ -- $2}"; _record WARN "${3:-}" "$1" "${2:-}"; } +info() { echo " [ info ] $1${2:+ -- $2}"; _record INFO "${3:-}" "$1" "${2:-}"; } +head1() { echo; echo "=============================================================="; echo "$1"; echo "=============================================================="; } +head2() { echo; echo "-- $1"; } + +TMP="$(mktemp -d 2>/dev/null || echo "./msys2-probe-tmp.$$")" +mkdir -p "$TMP" +cleanup() { [ "$KEEP_TMP" -eq 1 ] && echo "temp kept at $TMP" || rm -rf "$TMP"; } +trap cleanup EXIT + +# Compile (and optionally run) a snippet. Echoes compiler output on failure. +# try_compile <name> <ext> <extra-flags> <source...> (source on stdin) +try_compile() { + local name="$1" ext="$2" flags="$3" mode="${4:-compile}" + local src="$TMP/$name.$ext" exe="$TMP/$name.exe" log="$TMP/$name.log" + cat > "$src" + local cc; [ "$ext" = "c" ] && cc="${CC:-clang}" || cc="${CXX:-clang++}" + if [ "$mode" = "run" ]; then + # shellcheck disable=SC2086 + if ! $cc $flags "$src" -o "$exe" > "$log" 2>&1; then return 1; fi + "$exe" >> "$log" 2>&1 || return 2 + else + # shellcheck disable=SC2086 + if ! $cc $flags -c "$src" -o "$TMP/$name.o" > "$log" 2>&1; then return 1; fi + fi + return 0 +} + +show_log() { [ -s "$TMP/$1.log" ] && sed -n '1,6p' "$TMP/$1.log" | sed 's/^/ | /'; } + +# ---------------------------------------------------------------- preflight -- + +head1 "0. Preflight" + +UNAME_S="$(uname -s 2>/dev/null || echo unknown)" +case "$UNAME_S" in + *NT*|MSYS*|MINGW*|CLANG*) : ;; + *) echo "This script must run inside MSYS2 on Windows (uname -s = $UNAME_S)." >&2 + exit 2 ;; +esac + +MSYSTEM_VAL="${MSYSTEM:-<unset>}" +if [ "$MSYSTEM_VAL" = "CLANG64" ]; then + pass "MSYSTEM=CLANG64" +else + fail "MSYSTEM is $MSYSTEM_VAL, expected CLANG64" \ + "start the 'MSYS2 CLANG64' shell, not MINGW64/UCRT64/MSYS" CRITICAL + echo + echo " Why CLANG64 specifically: the tree needs __try/__except, __uuidof and" + echo " __declspec(uuid), none of which GCC-mingw implements. MINGW64 and" + echo " UCRT64 are GCC. See the 'UCRT vs MSVCRT' section of the plan." +fi + +ARCH="$(uname -m 2>/dev/null || echo unknown)" +if [ "$ARCH" = "x86_64" ]; then + pass "architecture x86_64" +else + fail "architecture is $ARCH, expected x86_64" \ + "this build mode is x86_64-only; arm64 Windows is deferred" CRITICAL +fi + +WINVER="$(cmd.exe /c ver 2>/dev/null | tr -d '\r' | grep -o '[0-9][0-9.]*' | head -1)" +info "Windows version" "${WINVER:-unknown}" + +# ----------------------------------------------------------------- packages -- + +# Base MSYS2 tools (MSYS namespace) needed to run configure/dmake/build.pl. +MSYS_PKGS=( + perl make autoconf automake-wrapper libtool patch diffutils findutils + gawk sed grep tar unzip zip dos2unix bison flex gperf git pkgconf +) +# CLANG64 toolchain and libraries. Package existence is verified below rather +# than assumed -- some of these may not exist under the clang64 prefix. +CLANG_PKGS=( + mingw-w64-clang-x86_64-toolchain + mingw-w64-clang-x86_64-cmake + mingw-w64-clang-x86_64-pkgconf +) +# Candidate system libraries, matching AOO's --with-system-* options. +SYSLIB_PKGS=( + boost curl expat icu libxml2 libxslt nss openssl zlib libjpeg-turbo + hunspell graphite2 redland python mdds cairo poppler libtiff + nspr sqlite3 lcms2 harfbuzz freetype fontconfig +) + +head1 "1. MSYS2 packages" + +if ! command -v pacman >/dev/null 2>&1; then + fail "pacman not found" "not a working MSYS2 installation" CRITICAL +else + if [ "$DO_INSTALL" -eq 1 ]; then + head2 "installing base MSYS2 tools" + pacman -S --needed --noconfirm "${MSYS_PKGS[@]}" || \ + warn "some base packages failed to install" + head2 "installing CLANG64 toolchain" + pacman -S --needed --noconfirm "${CLANG_PKGS[@]}" || \ + warn "some toolchain packages failed to install" + else + info "run with --install to install packages" "probing current state only" + fi + + head2 "base tools present?" + for t in perl make autoconf libtool patch gawk sed zip unzip bison flex gperf pkgconf git; do + if command -v "$t" >/dev/null 2>&1; then + pass "$t" "$(command -v "$t")" + else + fail "$t missing" "pacman -S $t" MEDIUM + fi + done + + head2 "toolchain present?" + for t in clang clang++ lld llvm-ar llvm-strip windres widl; do + if command -v "$t" >/dev/null 2>&1; then + pass "$t" "$(command -v "$t")" + else + case "$t" in + clang|clang++) sev=CRITICAL ;; + *) sev=MEDIUM ;; + esac + fail "$t missing" "pacman -S mingw-w64-clang-x86_64-toolchain" "$sev" + fi + done +fi + +# ---------------------------------------------------------------- toolchain -- + +head1 "2. Toolchain facts" + +if command -v clang >/dev/null 2>&1; then + CLANG_VER="$(clang --version 2>/dev/null | head -1)" + info "clang version" "$CLANG_VER" + + TRIPLE="$(clang -dumpmachine 2>/dev/null)" + case "$TRIPLE" in + x86_64-w64-windows-gnu|x86_64-w64-mingw32) + pass "target triple $TRIPLE" "GNU/Itanium C++ ABI as the plan assumes" ;; + *msvc*) + fail "target triple $TRIPLE" \ + "this is the MSVC ABI (clang-cl); the plan targets the GNU ABI" CRITICAL ;; + *) + fail "unexpected target triple: ${TRIPLE:-<none>}" "" CRITICAL ;; + esac + + # -dumpversion under COM=GCC: see section B5a of the plan. + DUMPVER="$(clang -dumpversion 2>/dev/null)" + info "clang -dumpversion" "${DUMPVER:-<none>} -- configure.ac:2301 reads this as a GCC version when COM=GCC (plan B5a)" + + head2 "C++ standard library" + if try_compile stdlib cxx "" compile <<'EOF' +#include <version> +#if defined(_LIBCPP_VERSION) +# error MARKER_LIBCPP +#elif defined(__GLIBCXX__) +# error MARKER_LIBSTDCXX +#else +# error MARKER_UNKNOWN +#endif +EOF + then :; fi + if grep -q MARKER_LIBCPP "$TMP/stdlib.log" 2>/dev/null; then + pass "libc++" "as expected for CLANG64; a divergence from the libstdc++ Linux build" + elif grep -q MARKER_LIBSTDCXX "$TMP/stdlib.log" 2>/dev/null; then + warn "libstdc++, not libc++" "unexpected for CLANG64 -- check which environment is active" + else + warn "could not determine the C++ standard library" + fi + + head2 "C runtime" + if try_compile crt c "" compile <<'EOF' +#include <stdio.h> +#if defined(_UCRT) +# error MARKER_UCRT +#else +# error MARKER_MSVCRT +#endif +EOF + then :; fi + if grep -q MARKER_UCRT "$TMP/crt.log" 2>/dev/null; then + pass "UCRT" "in-box on Windows 10+, no redistributable" + elif grep -q MARKER_MSVCRT "$TMP/crt.log" 2>/dev/null; then + warn "MSVCRT, not UCRT" "the plan recommends UCRT; see 'UCRT vs MSVCRT'" + else + warn "could not determine the C runtime" + fi +fi + +# ------------------------------------------------------- MSVC-ism compiling -- + +head1 "3. MSVC extensions the tree depends on" + +echo " These decide CLANG64 vs the GCC environments. A failure here does not" +echo " mean 'try GCC instead' -- it means source changes at the sites listed." + +head2 "SEH (__try/__except) -- 17 sites in vcl/win, sal/osl/w32, dtrans" +if try_compile seh c "-O1" run <<'EOF' +#include <windows.h> +static int probe(void) { + int r = 0; + __try { r = 1; } + __except (EXCEPTION_EXECUTE_HANDLER) { r = 2; } + return r; +} +int main(void) { return probe() == 1 ? 0 : 1; } +EOF + then pass "__try/__except compiles and runs" + else fail "__try/__except unusable" "would require rewriting ~15 SEH sites" CRITICAL + show_log seh + fi + +head2 "__uuidof -- 13 production files (sal, dtrans, fpicker, extensions/ole)" +if try_compile uuidof cxx "-fms-extensions" compile <<'EOF' +#include <windows.h> +#include <unknwn.h> +const GUID* g = &__uuidof(IUnknown); +EOF + then pass "__uuidof compiles" "with -fms-extensions" + else fail "__uuidof unusable" "would require explicit IID_* GUIDs across 13 files" CRITICAL + show_log uuidof + fi + +head2 "__declspec(uuid) -- avmedia/source/win/interface.hxx" +if try_compile declspec_uuid cxx "-fms-extensions" compile <<'EOF' +#include <windows.h> +#include <unknwn.h> +struct __declspec(uuid("65BD0710-24D2-4ff7-9324-ED2E5D3ABAFA")) IProbe : public IUnknown { + virtual void STDMETHODCALLTYPE probe() = 0; +}; +const GUID* g = &__uuidof(IProbe); +EOF + then pass "__declspec(uuid) compiles" + else fail "__declspec(uuid) unusable" "avmedia/source/win would need rework" CRITICAL + show_log declspec_uuid + fi + +# ------------------------------------------------- cross-DLL exception flow -- + +head1 "4. Cross-DLL typed exceptions (early signal for the UNO bridge)" + +echo " The bridge must let an exception thrown in one DLL be caught by type in" +echo " another. This is a much-reduced stand-in for that requirement -- it does" +echo " not exercise RTTI synthesis or the Win64 marshalling, which remain the" +echo " real Phase 1 work." + +cat > "$TMP/exc.hxx" <<'EOF' +struct ExcBase { virtual ~ExcBase(); int a; }; +struct ExcDerived : public ExcBase { int b; }; +EOF +cat > "$TMP/thrower.cxx" <<'EOF' +#include "exc.hxx" +ExcBase::~ExcBase() {} +extern "C" __declspec(dllexport) void do_throw() { ExcDerived d; d.a = 1; d.b = 2; throw d; } +EOF +cat > "$TMP/catcher.cxx" <<'EOF' +#include "exc.hxx" +#include <cstdio> +extern "C" __declspec(dllimport) void do_throw(); +int main() { + try { do_throw(); } + catch (ExcDerived&) { std::printf("EXACT\n"); return 0; } + catch (ExcBase&) { std::printf("BASE\n"); return 0; } + catch (...) { std::printf("ELLIPSIS\n"); return 1; } + return 1; +} +EOF +if (cd "$TMP" && clang++ -shared -o thrower.dll thrower.cxx -Wl,--out-implib,libthrower.a > exc.log 2>&1 \ + && clang++ -o catcher.exe catcher.cxx -L. -lthrower >> exc.log 2>&1); then + EXC_OUT="$(cd "$TMP" && ./catcher.exe 2>&1)" + case "$EXC_OUT" in + EXACT) pass "exception caught as exact derived type across a DLL boundary" ;; + BASE) warn "caught as base only" "type matching is degraded across images" ;; + *) fail "caught only by catch(...)" \ + "typed exceptions do not cross DLLs -- a serious problem for the bridge" HIGH ;; + esac +else + fail "cross-DLL exception harness did not build" "" MEDIUM + sed -n '1,8p' "$TMP/exc.log" | sed 's/^/ | /' +fi + +# ----------------------------------------------------------------- Win32 API -- + +head1 "5. mingw-w64 Win32 API coverage" + +probe_header() { # probe_header <header> <severity> <what-it-is-for> + local h="$1" sev="$2" what="$3" name + name="hdr_$(echo "$h" | tr './' '__')" + if printf '#include <%s>\nint main(void){return 0;}\n' "$h" | \ + try_compile "$name" c "-fms-extensions" compile; then + pass "$h" "$what" + else + fail "$h not usable" "$what" "$sev" + fi +} + +probe_header windows.h CRITICAL "core Win32" +probe_header unknwn.h CRITICAL "COM" +probe_header shlobj.h MEDIUM "shell extensions" +probe_header uxtheme.h MEDIUM "vcl theming" +probe_header comdef.h MEDIUM "_com_ptr_t -- connectivity/source/drivers/ado" +probe_header d3d9.h LOW "canvas/source/directx" +probe_header strmif.h LOW "avmedia/source/win (DirectShow)" +probe_header adoint.h LOW "ADO driver -- expected to need the Windows SDK, not mingw-w64" + +# ----------------------------------------------------------------------- ATL -- + +head1 "6. ATL (the highest-value probe in this script)" + +echo " If ATL cannot be compiled by clang, the de-ATL port of winaccessibility," +echo " extensions/source/ole and embedserv comes back: roughly 6-10 extra weeks," +echo " moving the project from ~3.5-6 to ~5-9 person-months." +echo +echo " Note: modern ATL ships with Visual Studio / VS Build Tools under" +echo " VC/Tools/MSVC/<ver>/atlmfc, NOT with the standalone Windows SDK. The" +echo " feasibility document assumes the standalone SDK carries it; this probe" +echo " is what settles that." + +ATL_DIR="" +if [ -n "$ATL_INCLUDE_OVERRIDE" ]; then + ATL_DIR="$ATL_INCLUDE_OVERRIDE" +else + for cand in \ + /c/Program\ Files/Microsoft\ Visual\ Studio/*/*/VC/Tools/MSVC/*/atlmfc/include \ + /c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/*/*/VC/Tools/MSVC/*/atlmfc/include \ + /c/Program\ Files\ \(x86\)/Windows\ Kits/10/Include/*/atl \ + /c/Program\ Files/Windows\ Kits/10/Include/*/atl ; do + if [ -f "$cand/atlbase.h" ]; then ATL_DIR="$cand"; break; fi + done +fi + +if [ -z "$ATL_DIR" ]; then + warn "no ATL installation found" \ + "install 'C++ ATL' via VS Build Tools, or pass --atl-include=<dir>" HIGH + info "searched" "Visual Studio VC/Tools/MSVC/*/atlmfc/include and Windows Kits/10/Include/*/atl" +else + info "ATL headers" "$ATL_DIR" + if try_compile atl cxx "-fms-extensions -fms-compatibility -I\"$ATL_DIR\"" compile <<'EOF' +#include <atlbase.h> +#include <atlcom.h> +CComModule _Module; +struct Probe { CComPtr<IUnknown> p; CComBSTR s; }; +EOF + then pass "ATL compiles under clang" "the plan's Phase 3 estimate holds" + else fail "ATL does not compile under clang" \ + "reinstates the de-ATL port; re-estimate Phase 3 at 6-10 weeks" HIGH + show_log atl + fi +fi + +# --------------------------------------------------------------- host tools -- + +head1 "7. Packaging and host tools" + +find_win_tool() { # find_win_tool <exe> <severity> <what-for> + local exe="$1" sev="$2" what="$3" p + if p="$(command -v "$exe" 2>/dev/null)"; then + pass "$exe" "$p" + return + fi + p="$(find "/c/Program Files (x86)/Windows Kits/10/bin" "/c/Program Files/Windows Kits/10/bin" \ + -maxdepth 3 -iname "$exe" 2>/dev/null | head -1)" + if [ -n "$p" ]; then + warn "$exe not on PATH" "found at $p -- add its directory to PATH" + else + fail "$exe not found" "$what" "$sev" + fi +} + +head2 "MSI construction (installer/control.pm:105)" +find_win_tool msidb.exe HIGH "Windows SDK component; required to build the MSI" +find_win_tool msitran.exe HIGH "Windows SDK component; required to build the MSI" +find_win_tool msiinfo.exe HIGH "Windows SDK component; required to build the MSI" +find_win_tool uuidgen.exe HIGH "Windows SDK component" +find_win_tool makecab.exe MEDIUM "ships with Windows itself" +find_win_tool expand.exe MEDIUM "ships with Windows itself" + +head2 "code signing (optional)" +find_win_tool signtool.exe LOW "only needed for signed builds" + +head2 "Java" +if command -v javac >/dev/null 2>&1; then + pass "javac" "$(javac -version 2>&1 | head -1)" + info "JAVA_HOME" "${JAVA_HOME:-<unset>}" +else + warn "javac not found" "install a native Windows JDK; MSYS2 ships none" +fi + +# ------------------------------------------------------- system-lib packages -- + +head1 "8. --with-system-* package availability" + +echo " AOO exposes 37 --with-system-* options and none is gated off on Windows," +echo " so every library MSYS2 can supply is one less bundled build to maintain." +echo " system-python in particular avoids building bundled CPython under mingw." + +if command -v pacman >/dev/null 2>&1; then + AVAILABLE=0; MISSING="" + for p in "${SYSLIB_PKGS[@]}"; do + full="mingw-w64-clang-x86_64-$p" + if pacman -Si "$full" >/dev/null 2>&1; then + if pacman -Qi "$full" >/dev/null 2>&1; then + echo " [ have ] $full" + else + echo " [ avail] $full (not installed)" + fi + AVAILABLE=$((AVAILABLE+1)) + else + echo " [ none ] $full" + MISSING="$MISSING $p" + fi + done + info "system libraries available in CLANG64" "$AVAILABLE of ${#SYSLIB_PKGS[@]}" + [ -n "$MISSING" ] && info "no CLANG64 package (would stay bundled)" "$(echo "$MISSING" | sed 's/^ //')" +fi + +# ------------------------------------------------------------------ summary -- + +head1 "Summary" + +printf ' %-6s %-9s %s\n' STATUS SEVERITY PROBE +printf ' %-6s %-9s %s\n' ------ -------- ----- +for row in "${SUMMARY[@]}"; do + IFS='|' read -r st sev lbl det <<< "$row" + [ "$st" = "INFO" ] && continue + printf ' %-6s %-9s %s\n' "$st" "${sev:--}" "$lbl" + [ -n "$det" ] && printf ' %-6s %-9s %s\n' "" "" "$det" +done + +echo +echo " pass $PASS_N fail $FAIL_N warn $WARN_N" +echo + +if [ "$CRITICAL_FAILED" -eq 1 ]; then + echo " RESULT: a CRITICAL probe failed. The approach as planned does not hold" + echo " on this machine -- resolve those before any Phase 0 work." + exit 1 +fi + +echo " RESULT: no CRITICAL failures." +echo +echo " Next, in order:" +echo " 1. If the ATL probe failed or found nothing, settle that first -- it is" +echo " the difference between a ~3.5-6 and a ~5-9 person-month project." +echo " 2. Prototype the Win64 GNU-ABI UNO bridge (plan section B1). That is the" +echo " real go/no-go gate and needs no working AOO build to start." +echo " 3. Only then begin Phase 0 build-system work." +exit 0
