2026-10-14 17:54:45 -06:00
#!/usr/bin/env python3
"""Compile, test, stage and package ZoiteChat on the host Mac."""
from __future__ import annotations
import argparse
import os
from pathlib import Path
import platform
import shutil
import subprocess
import sys
from bundle import ROOT , package , run
def main () -> None :
parser = argparse . ArgumentParser ( description = __doc__ )
parser . add_argument ( "--build-dir" , type = Path , default = Path ( "build-macos" ))
parser . add_argument ( "--output" , type = Path , default = Path ( "dist/ZoiteChat.app" ))
parser . add_argument ( "--build-number" , default = "1" )
parser . add_argument ( "--store-experimental" , action = "store_true" , help = "Build a separate sandbox test variant without loadable plugins; not submission approval" )
args = parser . parse_args ()
if platform . system () != "Darwin" :
parser . error ( "Build on macOS; cross-compiling from Linux is not supported" )
if args . output . exists ():
parser . error ( "Output already exists; use a new --output or explicitly remove the old app" )
build = args . build_dir . resolve ()
if build == ROOT or ROOT . is_relative_to ( build ):
parser . error ( "Build directory must not contain the source checkout" )
run ( "xcrun" , "--find" , "clang" )
env = os . environ . copy ()
prefixes = [ Path ( run ( "brew" , "--prefix" , name )) for name in ( "gettext" , "openssl@3" , "libarchive" , "lua" , "gtk+3" , "gtk-mac-integration" )]
brew = Path ( run ( "brew" , "--prefix" ))
# Keg-only gettext, OpenSSL and libarchive are not necessarily in PATH/pkg-config.
env [ "PATH" ] = os . pathsep . join ([ str ( p / "bin" ) for p in prefixes ] + [ str ( brew / "bin" ), env . get ( "PATH" , "" )])
env [ "PKG_CONFIG_PATH" ] = os . pathsep . join ([ str ( p / "lib/pkgconfig" ) for p in prefixes ] + [ str ( brew / "lib/pkgconfig" ), str ( brew / "share/pkgconfig" ), env . get ( "PKG_CONFIG_PATH" , "" )])
# Native dependency bottles determine the true deployment target; the bundle
# auditor derives the final minimum rather than promising an arbitrary old OS.
env . setdefault ( "MACOSX_DEPLOYMENT_TARGET" , "11.0" )
backend = run ( "pkg-config" , "--variable=targets" , "gtk+-3.0" , env = env ) . split ()
if "quartz" not in backend :
parser . error ( f "GTK must provide the native Quartz backend, got { backend } " )
# Do not silently select an XQuartz GTK stack or mix Intel/ARM Homebrew prefixes.
machine = platform . machine ()
if machine not in ( "arm64" , "x86_64" ):
parser . error ( f "Unsupported architecture: { machine } " )
options = [
"--prefix=/" , "--libdir=lib" , "--buildtype=release" , "-Dgtk-frontend=true" ,
"-Dtext-frontend=false" , "-Dmac-integration=enabled" , "-Ddbus=disabled" ,
"-Dlibcanberra=disabled" , "-Dappindicator=disabled" , "-Dinstall-appdata=false" ,
2026-10-20 13:39:18 -06:00
"-Dinstall-docs=false" ,
2026-10-14 17:54:45 -06:00
"-Dwith-sysinfo=false" , "-Dwith-python=false" , "-Dwith-perl=false" ,
"-Dwith-lua=lua" , "-Dwith-fishlim=true" , "-Dwith-checksum=true" ,
"-Dplugin=" + ( "false" if args . store_experimental else "true" ),
]
if ( build / "meson-private/coredata.dat" ) . exists ():
options . insert ( 0 , "--reconfigure" )
subprocess . run ([ "meson" , "setup" , str ( build ), str ( ROOT ), * options ], env = env , check = True )
subprocess . run ([ "meson" , "compile" , "-C" , str ( build )], env = env , check = True )
subprocess . run ([ "meson" , "test" , "-C" , str ( build ), "--print-errorlogs" ], env = env , check = True )
stage = build / "macos-stage"
if stage . exists ():
shutil . rmtree ( stage )
subprocess . run ([ "meson" , "install" , "-C" , str ( build ), "--destdir" , str ( stage )], env = env , check = True )
package ( stage , args . output . resolve (), args . build_number )
if __name__ == "__main__" :
try :
main ()
except ( RuntimeError , subprocess . CalledProcessError , OSError ) as error :
sys . exit ( f "macOS build failed: { error } " )