Merge pull request #2212 from Kazzz-S/0.30.5-mac1
Overhaul the Mac build system for Apple Silicon + Tahoe
|
|
@ -0,0 +1,283 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
# File: KLayoutNightlyBuild.Bash
|
||||||
|
#
|
||||||
|
# What: The Bash contents for "KLayoutNightlyBuild.app" script bundle
|
||||||
|
# that invokes 'nightlyBuild.py' for automation.
|
||||||
|
#
|
||||||
|
# Last modified: 2025-11-05
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
dryrun="no"
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# Tool logging
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
set -Eeuo pipefail
|
||||||
|
export LANG=en_US.UTF-8
|
||||||
|
export LC_ALL=en_US.UTF-8
|
||||||
|
|
||||||
|
LOG="/tmp/KLayoutNightlyBuild.$(date +%Y%m%d-%H%M%S).log"
|
||||||
|
{
|
||||||
|
echo "=== START $(date) ==="
|
||||||
|
echo "whoami: $(whoami)"
|
||||||
|
echo "pwd: $(pwd)"
|
||||||
|
echo "shell: $SHELL"
|
||||||
|
echo "osvers: $(sw_vers -productVersion)"
|
||||||
|
echo "ENV (partial):"
|
||||||
|
env | grep -E '^(PATH|HOME|LANG|LC_|MyEmail)' | sort
|
||||||
|
} >>"$LOG" 2>&1
|
||||||
|
|
||||||
|
BASH_XTRACEFD=9
|
||||||
|
exec 9>>"$LOG"
|
||||||
|
set -x
|
||||||
|
|
||||||
|
exec >>"$LOG" 2>&1
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# Detect and set the OS name.
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
osvers=$(sw_vers -productVersion)
|
||||||
|
osname=$(sw_vers -productName)
|
||||||
|
|
||||||
|
case "$osvers" in
|
||||||
|
12.*) osname="Monterey" ;;
|
||||||
|
13.*) osname="Ventura" ;;
|
||||||
|
14.*) osname="Sonoma" ;;
|
||||||
|
15.*) osname="Sequoia" ;;
|
||||||
|
26.*) osname="Tahoe" ;;
|
||||||
|
*) osname="Unknown" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# Set PATH
|
||||||
|
# to use /opt/local/bin/addr2line -> gaddr2line in MacPorts.
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
export PATH=/opt/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# Decide PYBIN (priority: env → MacPorts → Homebrew → Anaconda → /usr/bin)
|
||||||
|
# KLAYOUT_PYBIN or PYBIN
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
PYBIN_CANDIDATES=()
|
||||||
|
# launchctl setenv KLAYOUT_PYBIN ... will work)
|
||||||
|
[[ -n "${KLAYOUT_PYBIN:-}" ]] && PYBIN_CANDIDATES+=("$KLAYOUT_PYBIN")
|
||||||
|
[[ -n "${PYBIN:-}" ]] && PYBIN_CANDIDATES+=("$PYBIN")
|
||||||
|
|
||||||
|
# candidates
|
||||||
|
PYBIN_CANDIDATES+=(
|
||||||
|
/opt/local/bin/python3 # MacPorts
|
||||||
|
/usr/local/bin/python3 # Homebrew (Intel)
|
||||||
|
/opt/homebrew/bin/python3 # Homebrew (Apple Silicon)
|
||||||
|
/Applications/anaconda3/bin/python3 # Anaconda
|
||||||
|
/usr/bin/python3 # System
|
||||||
|
)
|
||||||
|
|
||||||
|
PYBIN=""
|
||||||
|
for p in "${PYBIN_CANDIDATES[@]}"; do
|
||||||
|
if [[ -n "$p" && -x "$p" ]]; then
|
||||||
|
PYBIN="$p"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$PYBIN" ]]; then
|
||||||
|
echo "[FATAL] python3 not found. Tried: ${PYBIN_CANDIDATES[*]}"
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[INFO] Using PYBIN=$PYBIN"
|
||||||
|
"$PYBIN" -V || true
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# Set build parameters
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
workdir=$HOME/GitWork/klayout
|
||||||
|
passbox=$HOME/Dropbox/PrivatePassBox
|
||||||
|
drylog=nightlyBuild-dryrun.log
|
||||||
|
jobsCSV=nightlyBuild.csv
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# Set mailer (postfix) parameters
|
||||||
|
#
|
||||||
|
# launchctl setenv MyEmail1 "my_real_email_address_1"
|
||||||
|
# launchctl setenv MyEmail2 "my_real_email_address_2"
|
||||||
|
#
|
||||||
|
# launchctl getenv MyEmail1
|
||||||
|
# launchctl getenv MyEmail2
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
if [[ -z "${MyEmail1:-}" || -z "${MyEmail2:-}" ]]; then
|
||||||
|
if [[ -f "$HOME/.klayout_env" ]]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$HOME/.klayout_env"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Re-check
|
||||||
|
MyEmail1_val="${MyEmail1:-}"
|
||||||
|
MyEmail2_val="${MyEmail2:-}"
|
||||||
|
|
||||||
|
if [[ -z "$MyEmail1_val" ]]; then
|
||||||
|
echo "[FATAL] MyEmail1 is not set. Run: launchctl setenv MyEmail1 'you@example.com'"
|
||||||
|
exit 64
|
||||||
|
fi
|
||||||
|
if [[ -z "$MyEmail2_val" ]]; then
|
||||||
|
echo "[WARN] MyEmail2 is not set. Cc will be empty."
|
||||||
|
fi
|
||||||
|
|
||||||
|
title="Status of KLayout <nightlyBuild.py>"
|
||||||
|
addrFrom=$MyEmail1_val
|
||||||
|
addrTo=$MyEmail1_val
|
||||||
|
addrCc=$MyEmail2_val
|
||||||
|
mailer="sendmail"
|
||||||
|
sleeptime=5
|
||||||
|
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
#### Initialize
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
function Initialize()
|
||||||
|
{
|
||||||
|
cd "$workdir"
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] PATH=$PATH PYBIN=$PYBIN" >> "$workdir/nightlyBuild-env.log"
|
||||||
|
|
||||||
|
if [ -f "$jobsCSV" ]; then
|
||||||
|
targetOp="--qttarget=$jobsCSV"
|
||||||
|
else
|
||||||
|
targetOp=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -L ./nightlyBuild.py ]; then
|
||||||
|
echo "OK! nightlyBuild.py symbolic link exists ..."
|
||||||
|
else
|
||||||
|
ln -s ./macbuild/nightlyBuild.py .
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$dryrun" == "yes" ]; then
|
||||||
|
cmd=( "./nightlyBuild.py" $targetOp --build --pymod --dryrun )
|
||||||
|
dryout="> $drylog"
|
||||||
|
else
|
||||||
|
cmd=( "./nightlyBuild.py" $targetOp --build --pymod --test )
|
||||||
|
dryout=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
line1="### Initialized Nightly Build ###"
|
||||||
|
line2=" OS: $osname"
|
||||||
|
msg1=$(printf "%s\n%s\n%s\n" "$line1" "$line2" "${cmd[*]}")
|
||||||
|
SendMail "$msg1"
|
||||||
|
}
|
||||||
|
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
#### Upload log files
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
function UploadLog()
|
||||||
|
{
|
||||||
|
shopt -s nullglob
|
||||||
|
logfiles=(*.log)
|
||||||
|
[ ${#logfiles[@]} -gt 0 ] && cp -p "${logfiles[@]}" "$passbox"
|
||||||
|
shopt -u nullglob
|
||||||
|
}
|
||||||
|
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
#### Build
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
function Build()
|
||||||
|
{
|
||||||
|
# Prepare build log file
|
||||||
|
buildlogfile="$workdir/nightlyBuild-$(date +%Y%m%d-%H%M%S).log"
|
||||||
|
timestamp_start=$(date "+%Y-%m-%d %H:%M:%S")
|
||||||
|
echo "===== Nightly Build Started at $timestamp_start =====" > $buildlogfile
|
||||||
|
|
||||||
|
# Start caffeinate in background to prevent sleep
|
||||||
|
# -d : Prevent display sleep
|
||||||
|
# -i : Prevent idle sleep
|
||||||
|
# -m : Prevent disk sleep
|
||||||
|
# -s : Prevent system sleep
|
||||||
|
# -u : Declare user activity (optional)
|
||||||
|
#caffeinate -dimsu &
|
||||||
|
caffeinate -imsu &
|
||||||
|
caff_pid=$!
|
||||||
|
|
||||||
|
logmsg1="Started caffeinate (PID=$caff_pid) to prevent sleep during build"
|
||||||
|
echo "$logmsg1" | tee -a $buildlogfile
|
||||||
|
|
||||||
|
# Prepare trap cleanup message
|
||||||
|
cleanupmsg="Cleaning up caffeinate (PID=$caff_pid)"
|
||||||
|
# Set trap
|
||||||
|
trap "echo '$cleanupmsg' | tee -a $buildlogfile; kill $caff_pid 2>/dev/null || true; wait $caff_pid 2>/dev/null || true" INT TERM EXIT
|
||||||
|
|
||||||
|
# Run the build and test command
|
||||||
|
build_test_cmd=( "$PYBIN" "${cmd[@]}" )
|
||||||
|
echo "Executing: ${build_test_cmd[*]} $dryout" | tee -a "$buildlogfile"
|
||||||
|
if [ "$dryrun" == "yes" ]; then
|
||||||
|
"${build_test_cmd[@]}" >"$drylog" 2>&1
|
||||||
|
else
|
||||||
|
"${build_test_cmd[@]}"
|
||||||
|
fi
|
||||||
|
build_status=$?
|
||||||
|
|
||||||
|
# Stop caffeinate
|
||||||
|
kill "$caff_pid" 2>/dev/null || true
|
||||||
|
wait "$caff_pid" 2>/dev/null || true
|
||||||
|
|
||||||
|
logmsg2="Stopped caffeinate (PID=$caff_pid)"
|
||||||
|
echo "$logmsg2" | tee -a $buildlogfile
|
||||||
|
|
||||||
|
# Clear trap
|
||||||
|
trap - INT TERM EXIT
|
||||||
|
|
||||||
|
# Build result handling
|
||||||
|
timestamp_end=$(date "+%Y-%m-%d %H:%M:%S")
|
||||||
|
echo "===== Nightly Build Ended at $timestamp_end =====" >> $buildlogfile
|
||||||
|
|
||||||
|
if [ $build_status -ne 0 ]; then
|
||||||
|
line1="!!! Failed Nightly Build !!!"
|
||||||
|
line2=" OS: $osname"
|
||||||
|
msg2=$(printf "%s\n%s\n%s\n" "$line1" "$line2" "${cmd[*]}")
|
||||||
|
echo "$msg2" >> $buildlogfile
|
||||||
|
UploadLog
|
||||||
|
else
|
||||||
|
line1="### Succeeded Nightly Build ###"
|
||||||
|
line2=" OS: $osname"
|
||||||
|
msg2=$(printf "%s\n%s\n%s\n" "$line1" "$line2" "${cmd[*]}")
|
||||||
|
echo "$msg2" >> $buildlogfile
|
||||||
|
if [ "$dryrun" == "yes" ]; then
|
||||||
|
UploadLog
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send mail with entire build log
|
||||||
|
SendMail "$(cat $buildlogfile)"
|
||||||
|
return $build_status
|
||||||
|
}
|
||||||
|
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
#### Send mail
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
function SendMail() {
|
||||||
|
$mailer -i -t <<EOF
|
||||||
|
From: $addrFrom
|
||||||
|
To: $addrTo
|
||||||
|
Cc: $addrCc
|
||||||
|
Subject: $title
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
|
||||||
|
$1
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
#### Sleep
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
function Sleep()
|
||||||
|
{
|
||||||
|
sleep $1
|
||||||
|
# 10 = 10s => 10 seconds
|
||||||
|
# 5m => 5 minutes
|
||||||
|
}
|
||||||
|
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
#### Main
|
||||||
|
####----------------------------------------------------------------------
|
||||||
|
Initialize
|
||||||
|
Build
|
||||||
|
exit $?
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|
@ -1,16 +1,18 @@
|
||||||
Relevant KLayout version: 0.30.2<br>
|
Relevant KLayout version: 0.30.5<br>
|
||||||
Author: Kazzz-S<br>
|
Author: Kazzz-S<br>
|
||||||
Last modified: 2025-05-30<br>
|
Last modified: 2025-11-10<br>
|
||||||
|
|
||||||
# 1. Introduction
|
# 1. Introduction
|
||||||
This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.30.2 or later for different 64-bit macOS, including:
|
This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.30.5 or later for different 64-bit macOS, including:
|
||||||
|
* Tahoe (26.x) : really experimental (on M4 Mac Mini)
|
||||||
* Sequoia (15.x) : the primary development environment
|
* Sequoia (15.x) : the primary development environment
|
||||||
* Sonoma (14.x) : experimental
|
* Sonoma (14.x) : experimental
|
||||||
* Ventura (13.x) : -- ditto --
|
|
||||||
|
|
||||||
Building KLayout for the previous operating systems listed below has been discontinued.<br>
|
Building KLayout for the previous operating systems listed below has been discontinued.<br>
|
||||||
Pre-built DMG packages are also not provided.<br>
|
Pre-built DMG packages are also not provided.<br>
|
||||||
* Monterey (12.7.6; the build is still possible, but Homebrew stopped supporting this OS in September 2024)
|
As of today (November 2025), Homebrew classifies macOS Catalina 10.15 - Ventura 13 as Tier 3.
|
||||||
|
* Ventura (13.7.8; the build would still be possible)
|
||||||
|
* Monterey (12.7.6; the build would still be possible)
|
||||||
* Big Sur (11.7.10)
|
* Big Sur (11.7.10)
|
||||||
* Catalina (10.15.7)
|
* Catalina (10.15.7)
|
||||||
* Mojave (10.14)
|
* Mojave (10.14)
|
||||||
|
|
@ -19,8 +21,9 @@ Pre-built DMG packages are also not provided.<br>
|
||||||
* El Capitan (10.11)
|
* El Capitan (10.11)
|
||||||
|
|
||||||
Throughout this document, the primary target machine is **Intel x86_64** with **macOS Sequoia**.<br>
|
Throughout this document, the primary target machine is **Intel x86_64** with **macOS Sequoia**.<br>
|
||||||
All Apple (M1|M2|M3|M4) chips are still untested, as the author does not own an (M1|M2|M3|M4) Mac.<br>
|
The author recently acquired an M4 Mac Mini and is attempting to build a native ARM64 version in the Tahoe environment **experimentally**.<br>
|
||||||
However, some kind volunteers told me they successfully built on an Apple silicon machine.<br>
|
Therefore, this document does not include detailed build procedures for Apple Silicon environments.<br>
|
||||||
|
However, they are essentially the same as for an Intel Mac.
|
||||||
|
|
||||||
# 2. Qt Frameworks
|
# 2. Qt Frameworks
|
||||||
|
|
||||||
|
|
@ -34,18 +37,17 @@ If you prefer "Qt6" from **Homebrew** (https://brew.sh/), which is usually locat
|
||||||
/usr/local/opt/qt@6/
|
/usr/local/opt/qt@6/
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also choose "Qt5" from Anaconda3 (https://www.anaconda.com/), which is usually located under:
|
You can also choose "Qt6" from Anaconda3 (https://www.anaconda.com/), but its setup is a little complicated in this release.
|
||||||
```
|
First, install Anaconda3 under /Applications. <br>
|
||||||
$HOME/opt/anaconda3/pkgs/qt-{version}
|
If you have installed Anaconda3 under $HOME/opt/anaconda3/ (or other location), make a symbolic link:
|
||||||
```
|
|
||||||
If you have installed Anaconda3 under $HOME/opt/anaconda3/, make a symbolic link:
|
|
||||||
```
|
```
|
||||||
/Applications/anaconda3/ ---> $HOME/opt/anaconda3/
|
/Applications/anaconda3/ ---> $HOME/opt/anaconda3/
|
||||||
```
|
```
|
||||||
|
Then, follow the instructions in Section 6F.
|
||||||
|
|
||||||
The migration work to "Qt6" is ongoing. You can try to use it; however, you might encounter some build or runtime errors.<br>
|
The migration work to "Qt6" is ongoing. You can try to use it; however, you might encounter some build or runtime errors.<br>
|
||||||
If you use **Homebrew** to build KLayout >= 0.29.0, you need "Qt6" to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599).<br>
|
If you use **Homebrew** to build KLayout >= 0.29.0, you need "Qt6" to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599).<br>
|
||||||
I have also tried migrating to "Python 3.12.x" (earlier, Python 3.11.x) in this version.
|
I have also tried migrating to "Python 3.13.x" (earlier, Python 3.12.x) in this version.
|
||||||
|
|
||||||
# 3. Script language support: Ruby and Python
|
# 3. Script language support: Ruby and Python
|
||||||
|
|
||||||
|
|
@ -57,8 +59,8 @@ You need to have the followings:
|
||||||
* The latest Xcode and command-line tool kit compliant with each OS
|
* The latest Xcode and command-line tool kit compliant with each OS
|
||||||
* https://developer.apple.com/xcode/resources/
|
* https://developer.apple.com/xcode/resources/
|
||||||
* https://mac.install.guide/commandlinetools/4
|
* https://mac.install.guide/commandlinetools/4
|
||||||
* Qt5 package from MacPorts or Anaconda3. Qt6, from Homebrew.
|
* Qt5 package from MacPorts. Qt6, from Homebrew or Anaconda3.
|
||||||
* libgit2 form MacPorts, Homebrew,or Anaconda3.
|
* libgit2 form MacPorts, Homebrew, or Anaconda3.
|
||||||
* Optionally, Ruby and Python packages from MacPorts, Homebrew, or Anaconda3
|
* Optionally, Ruby and Python packages from MacPorts, Homebrew, or Anaconda3
|
||||||
#### For matching versions of Ruby and Python, please also refer to `build4mac_env.py`.
|
#### For matching versions of Ruby and Python, please also refer to `build4mac_env.py`.
|
||||||
|
|
||||||
|
|
@ -70,32 +72,36 @@ The operating system type is detected automatically.
|
||||||
```
|
```
|
||||||
-----------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------
|
||||||
<< Usage of 'build4mac.py' >>
|
<< Usage of 'build4mac.py' >>
|
||||||
for building KLayout 0.30.2 or later on different Apple macOS platforms.
|
for building KLayout 0.30.5 or later on different Apple macOS platforms.
|
||||||
|
|
||||||
$ [python] ./build4mac.py
|
$ [python] ./build4mac.py
|
||||||
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value
|
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value
|
||||||
----------------------------------------------------------------------------------------+---------------
|
----------------------------------------------------------------------------------------+---------------
|
||||||
[-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', | qt5macports
|
[-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', | qt5macports
|
||||||
: 'Qt6MacPorts', 'Qt6Brew'] |
|
: 'Qt6MacPorts', 'Qt6Brew', 'Qt6Ana3''] |
|
||||||
: Qt5MacPorts: use Qt5 from MacPorts |
|
: Qt5MacPorts: use Qt5 from MacPorts |
|
||||||
: Qt5Brew: use Qt5 from Homebrew |
|
: Qt5Brew: use Qt5 from Homebrew |
|
||||||
: Qt5Ana3: use Qt5 from Anaconda3 |
|
: Qt5Ana3: use Qt5 from Anaconda3 |
|
||||||
: Qt6MacPorts: use Qt6 from MacPorts (*) |
|
: Qt6MacPorts: use Qt6 from MacPorts (*) |
|
||||||
: Qt6Brew: use Qt6 from Homebrew (*) |
|
: Qt6Brew: use Qt6 from Homebrew (*) |
|
||||||
|
: Qt6Ana3: use Qt6 from Anaconda3 (*) |
|
||||||
: (*) migration to Qt6 is ongoing |
|
: (*) migration to Qt6 is ongoing |
|
||||||
[-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP33', 'HB34', 'Ana3'] | sys
|
[-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP34', 'HB34', 'Ana3'] | sys
|
||||||
: nil: don't bind Ruby |
|
: nil: don't bind Ruby |
|
||||||
: Sys: use [Sequoia|Sonoma|Ventura|Monterey]-bundled Ruby 2.6 |
|
: Sys: use [Tahoe|Sequoia|Sonoma]-bundled Ruby 2.6 |
|
||||||
: MP33: use Ruby 3.3 from MacPorts |
|
: MP34: use Ruby 3.4 from MacPorts |
|
||||||
: HB34: use Ruby 3.4 from Homebrew |
|
: HB34: use Ruby 3.4 from Homebrew |
|
||||||
: Ana3: use Ruby 3.2 from Anaconda3 |
|
: Ana3: use Ruby 3.4 from Anaconda3 |
|
||||||
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP312', 'HB312', 'Ana3', | sys
|
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP313', 'HB313', 'Ana3', | sys
|
||||||
|
: 'MP312', 'MP312', |
|
||||||
: 'MP311', 'HB311', 'HBAuto'] |
|
: 'MP311', 'HB311', 'HBAuto'] |
|
||||||
: nil: don't bind Python |
|
: nil: don't bind Python |
|
||||||
: Sys: use [Sequoia|Sonoma|Ventura|Monterey]-bundled Python 3.9 |
|
: Sys: use [Tahoe|Sequoia|Sonoma]-bundled Python 3.9 |
|
||||||
|
: MP313: use Python 3.13 from MacPorts |
|
||||||
|
: HB313: use Python 3.13 from Homebrew |
|
||||||
|
: Ana3: use Python 3.13 from Anaconda3 |
|
||||||
: MP312: use Python 3.12 from MacPorts |
|
: MP312: use Python 3.12 from MacPorts |
|
||||||
: HB312: use Python 3.12 from Homebrew |
|
: HB312: use Python 3.12 from Homebrew |
|
||||||
: Ana3: use Python 3.12 from Anaconda3 |
|
|
||||||
: MP311: use Python 3.11 from MacPorts |
|
: MP311: use Python 3.11 from MacPorts |
|
||||||
: HB311: use Python 3.11 from Homebrew (+) |
|
: HB311: use Python 3.11 from Homebrew (+) |
|
||||||
: (+) required to provide the legacy pip in HW-*.dmg |
|
: (+) required to provide the legacy pip in HW-*.dmg |
|
||||||
|
|
@ -141,7 +147,7 @@ Confirm that you have:
|
||||||
```
|
```
|
||||||
As of this writing, the provided Python version is `3.9.6`.
|
As of this writing, the provided Python version is `3.9.6`.
|
||||||
|
|
||||||
1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default values for Sequoia, Sonoma, and Ventura.
|
1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default values for Tahoe, Sequoia, and Sonoma.
|
||||||
```
|
```
|
||||||
$ cd /where/'build.sh'/exists
|
$ cd /where/'build.sh'/exists
|
||||||
$ ./build4mac.py -q qt5macports -r sys -p sys
|
$ ./build4mac.py -q qt5macports -r sys -p sys
|
||||||
|
|
@ -160,21 +166,21 @@ $ ./build4mac.py -q qt5macports -r sys -p sys -y
|
||||||
* "RsysPsys" means that Ruby is 2.6 provided by OS; Python is 3.9 provided by OS.
|
* "RsysPsys" means that Ruby is 2.6 provided by OS; Python is 3.9 provided by OS.
|
||||||
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
||||||
|
|
||||||
### 6B. Fully MacPorts-flavored build with MacPorts Ruby 3.3 and MacPorts Python 3.12
|
### 6B. Fully MacPorts-flavored build with MacPorts Ruby 3.4 and MacPorts Python 3.13
|
||||||
0. Install MacPorts, then install Qt5, Ruby 3.3, Python 3.12, and libgit2 by
|
0. Install MacPorts, then install Qt5, Ruby 3.4, Python 3.13, and libgit2 by
|
||||||
```
|
```
|
||||||
$ sudo port install coreutils
|
$ sudo port install coreutils
|
||||||
$ sudo port install findutils
|
$ sudo port install findutils
|
||||||
$ sudo port install qt5
|
$ sudo port install qt5
|
||||||
$ sudo port install ruby33
|
$ sudo port install ruby34
|
||||||
$ sudo port install python312
|
$ sudo port install python313
|
||||||
$ sudo port install py312-pip
|
$ sudo port install py313-pip
|
||||||
$ sudo port install libgit2
|
$ sudo port install libgit2
|
||||||
```
|
```
|
||||||
1. Invoke **`build4mac.py`** with the following options:
|
1. Invoke **`build4mac.py`** with the following options:
|
||||||
```
|
```
|
||||||
$ cd /where/'build.sh'/exists
|
$ cd /where/'build.sh'/exists
|
||||||
$ ./build4mac.py -q qt5macports -r mp33 -p mp312
|
$ ./build4mac.py -q qt5macports -r mp34 -p mp313
|
||||||
```
|
```
|
||||||
2. Confirm successful build (it will take about one hour, depending on your machine spec).
|
2. Confirm successful build (it will take about one hour, depending on your machine spec).
|
||||||
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
|
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
|
||||||
|
|
@ -182,50 +188,50 @@ $ ./build4mac.py -q qt5macports -r mp33 -p mp312
|
||||||
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Standalone Python Package (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
|
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Standalone Python Package (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./build4mac.py -q qt5macports -r mp33 -p mp312 -Y
|
$ ./build4mac.py -q qt5macports -r mp34 -p mp313 -Y
|
||||||
```
|
```
|
||||||
The application bundle **`klayout.app`** is located under:<br>
|
The application bundle **`klayout.app`** is located under:<br>
|
||||||
**`LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312`** directory, where
|
**`LW-qt5MP.pkg.macos-Sequoia-release-Rmp34Pmp313`** directory, where
|
||||||
* "LW-" means this is a lightweight package.
|
* "LW-" means this is a lightweight package.
|
||||||
* "qt5MP" means that Qt5 from MacPorts is used.
|
* "qt5MP" means that Qt5 from MacPorts is used.
|
||||||
* "Rmp33Pmp312" means that Ruby is 3.3 from MacPorts; Python is 3.12 from MacPorts.
|
* "Rmp34Pmp313" means that Ruby is 3.4 from MacPorts; Python is 3.13 from MacPorts.
|
||||||
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
||||||
|
|
||||||
### 6C. Fully Homebrew-flavored build with Homebrew Ruby 3.3 and Homebrew Python 3.12
|
### 6C. Fully Homebrew-flavored build with Homebrew Ruby 3.4 and Homebrew Python 3.13
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> To build KLayout >= 0.29.0, you need "Qt6" >= 6.7.0 to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599).<br>
|
> To build KLayout >= 0.29.0, you need "Qt6" >= 6.7.0 to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599).<br>
|
||||||
|
|
||||||
0. Install Homebrew, then install Qt6, Ruby 3.4, Python 3.12, and libgit2 by
|
0. Install Homebrew, then install Qt6, Ruby 3.4, Python 3.13, and libgit2 by
|
||||||
```
|
```
|
||||||
$ brew install qt@6
|
$ brew install qt@6
|
||||||
$ brew install ruby@3.4
|
$ brew install ruby@3.4
|
||||||
$ brew install python@3.12
|
$ brew install python@3.13
|
||||||
$ brew install libgit2
|
$ brew install libgit2
|
||||||
$ cd /where/'build.sh'/exists
|
$ cd /where/'build.sh'/exists
|
||||||
$ cd macbuild
|
$ cd macbuild
|
||||||
$ ./python3HB.py -v 3.12
|
$ ./python3HB.py -v 3.13
|
||||||
```
|
```
|
||||||
1. Invoke **`build4mac.py`** with the following options:
|
1. Invoke **`build4mac.py`** with the following options:
|
||||||
```
|
```
|
||||||
$ cd /where/'build.sh'/exists
|
$ cd /where/'build.sh'/exists
|
||||||
$ ./build4mac.py -q qt6brew -r hb34 -p hb312
|
$ ./build4mac.py -q qt6brew -r hb34 -p hb313
|
||||||
```
|
```
|
||||||
2. Confirm successful build (it will take about one hour, depending on your machine spec).
|
2. Confirm successful build (it will take about one hour, depending on your machine spec).
|
||||||
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
|
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
|
||||||
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.<br>
|
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.<br>
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./build4mac.py -q qt6brew -r hb34 -p hb312 -Y
|
$ ./build4mac.py -q qt6brew -r hb34 -p hb313 -Y
|
||||||
```
|
```
|
||||||
The application bundle **`klayout.app`** is located under:<br>
|
The application bundle **`klayout.app`** is located under:<br>
|
||||||
**`LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb312`** directory, where
|
**`LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb313`** directory, where
|
||||||
* "LW-" means this is a lightweight package.
|
* "LW-" means this is a lightweight package.
|
||||||
* "qt6Brew" means that Qt6 from Homebrew is used.
|
* "qt6Brew" means that Qt6 from Homebrew is used.
|
||||||
* "Rhb34Phb312" means that Ruby is 3.4 from Homebrew; Python is 3.12 from Homebrew.
|
* "Rhb34Phb313" means that Ruby is 3.4 from Homebrew; Python is 3.13 from Homebrew.
|
||||||
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> We can no longer use the legacy **pip** command with Homebew Python@3.12, and we will get,
|
> We can no longer use the legacy **pip** command with Homebew Python@3.13, and we will get,
|
||||||
> ```
|
> ```
|
||||||
> error: externally-managed-environment
|
> error: externally-managed-environment
|
||||||
> ```
|
> ```
|
||||||
|
|
@ -306,17 +312,39 @@ $ ./build4mac.py -q qt5macports -r sys -p hb311 -y
|
||||||
* "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew.
|
* "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew.
|
||||||
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
||||||
|
|
||||||
### 6F. Fully Anaconda3-flavored build with Anaconda3 Ruby 3.2 and Anaconda3 Python 3.12
|
### 6F. Fully Anaconda3-flavored build with Anaconda3 Ruby 3.4 and Anaconda3 Python 3.13
|
||||||
0. Install Anaconda3 (Anaconda3-2024.10-1-MacOSX-x86_64.pkg), then install Ruby 3.2 and libgit2 by
|
0. Install Anaconda3 (Anaconda3-2025.06-0-MacOSX-x86_64.pkg) under `/Applications` then setup a new virtual environment.
|
||||||
```
|
```
|
||||||
$ conda install ruby=3.2.2
|
1) Create a new env "klayout-qt6" (with stable solver & channels)
|
||||||
$ conda install libgit2=1.6.4
|
switch solver to libmamba for faster/more stable resolves
|
||||||
|
$ conda install -n base -y conda-libmamba-solver
|
||||||
|
$ conda config --set solver libmamba
|
||||||
|
|
||||||
|
Create the environment (on this x86_64 machine it will pull osx-64 builds)
|
||||||
|
$ conda create -n klayout-qt6 python=3.13 -y
|
||||||
|
$ conda activate klayout-qt6
|
||||||
|
|
||||||
|
In this env only, prefer conda-forge strictly (to avoid mixing)
|
||||||
|
$ conda config --env --add channels conda-forge
|
||||||
|
$ conda config --env --add channels defaults
|
||||||
|
$ conda config --env --set channel_priority strict
|
||||||
|
$ conda install -n base -y conda-libmamba-solver
|
||||||
|
$ conda config --set solver libmamba
|
||||||
|
|
||||||
|
2) Install Qt6 (qt6-main and qt6-multimedia) only from conda-forge
|
||||||
|
Qt6 core (builds that typically include Designer/UiTools)
|
||||||
|
$ conda install -y --override-channels -c conda-forge "qt6-main=6.9.3"
|
||||||
|
$ conda install -y --override-channels -c conda-forge "qt6-multimedia=6.9.3"
|
||||||
|
|
||||||
|
3) Additionally, install Ruby and libgit2 only from conda-forge
|
||||||
|
$ conda install -y --override-channels -c conda-forge "ruby=3.4.7"
|
||||||
|
$ conda install -y --override-channels -c conda-forge "libgit2=1.9.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Invoke **`build4mac.py`** with the following options:
|
1. Invoke **`build4mac.py`** with the following options:
|
||||||
```
|
```
|
||||||
$ cd /where/'build.sh'/exists
|
$ cd /where/'build.sh'/exists
|
||||||
$ ./build4mac.py -q qt5ana3 -r ana3 -p ana3
|
$ ./build4mac.py -q qt6ana3 -r ana3 -p ana3
|
||||||
```
|
```
|
||||||
2. Confirm successful build (it will take about one hour, depending on your machine spec).
|
2. Confirm successful build (it will take about one hour, depending on your machine spec).
|
||||||
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
|
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
|
||||||
|
|
@ -324,18 +352,16 @@ $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3
|
||||||
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Standalone Python Package (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
|
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Standalone Python Package (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 -Y
|
$ ./build4mac.py -q qt6ana3 -r ana3 -p ana3 -Y
|
||||||
```
|
```
|
||||||
The application bundle **`klayout.app`** is located under:<br>
|
The application bundle **`klayout.app`** is located under:<br>
|
||||||
**`LW-qt5Ana3.pkg.macos-Sequoia-release-Rana3Pana3`** directory, where
|
**`LW-qt6Ana3.pkg.macos-Sequoia-release-Rana3Pana3`** directory, where
|
||||||
* "LW-" means this is a lightweight package.
|
* "LW-" means this is a lightweight package.
|
||||||
* "qt5Ana3" means that Qt5 from Anaconda3 is used.
|
* "qt6Ana3" means that Qt6 from Anaconda3 is used.
|
||||||
* "Rana3Pana3" means that Ruby (3.2) is from Anaconda3; Python (3.12) is from Anaconda3.
|
* "Rana3Pana3" means that Ruby (3.4) is from Anaconda3; Python (3.13) is from Anaconda3.
|
||||||
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
|
||||||
5. You may have to set the `PYTHONHOME` environment variable like:
|
5. Now, you need to create an Automator wrapper app (Launcher) to start the application.
|
||||||
```
|
See `Anaconda3User-ReadMeFirst.txt` in `Resources/script-bundle-A.zip` for details.
|
||||||
export PYTHONHOME=$HOME/opt/anaconda3
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6G. Other combinations
|
### 6G. Other combinations
|
||||||
Logically, several module combinations other than 6A through 6F are possible, including `nil` choice.<br>
|
Logically, several module combinations other than 6A through 6F are possible, including `nil` choice.<br>
|
||||||
|
|
@ -394,11 +420,11 @@ makeDMG4mac.py -> macbuild/makeDMG4mac.py
|
||||||
2. Invoke **`makeDMG4mac.py`** with -p and -m options, for example,
|
2. Invoke **`makeDMG4mac.py`** with -p and -m options, for example,
|
||||||
```
|
```
|
||||||
$ cd /where/'build.sh'/exists
|
$ cd /where/'build.sh'/exists
|
||||||
$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312 -m
|
$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Sequoia-release-Rmp34Pmp313 -m
|
||||||
```
|
```
|
||||||
This command will generate the two files below:<br>
|
This command will generate the two files below:<br>
|
||||||
* **`LW-klayout-0.30.2-macOS-Sequoia-1-qt5MP-Rmp33Pmp312.dmg`** ---(1) the main DMG file
|
* **`LW-klayout-0.30.5-macOS-Sequoia-1-qt5MP-Rmp34Pmp313.dmg`** ---(1) the main DMG file
|
||||||
* **`LW-klayout-0.30.2-macOS-Sequoia-1-qt5MP-Rmp33Pmp312.dmg.md5`** ---(2) MD5-value text file
|
* **`LW-klayout-0.30.5-macOS-Sequoia-1-qt5MP-Rmp34Pmp313.dmg.md5`** ---(2) MD5-value text file
|
||||||
|
|
||||||
# Known issues
|
# Known issues
|
||||||
Because we assume some specific versions of non-OS-standard Ruby and Python, updating Homebrew, MacPorts, or Anaconda3 may cause build- and link errors.<br>
|
Because we assume some specific versions of non-OS-standard Ruby and Python, updating Homebrew, MacPorts, or Anaconda3 may cause build- and link errors.<br>
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 203 KiB |
|
After Width: | Height: | Size: 209 KiB |
|
After Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
|
@ -1,4 +1,5 @@
|
||||||
[Paths]
|
[Paths]
|
||||||
Plugins = ../PlugIns
|
Prefix = ..
|
||||||
Imports = ../Resources/qml
|
Plugins = PlugIns
|
||||||
Qml2Imports = ../Resources/qml
|
Imports = Resources/qml
|
||||||
|
Qml2Imports = Resources/qml
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# File: "macbuild/build4mac.py"
|
# File: "macbuild/build4mac.py"
|
||||||
#
|
#
|
||||||
# The top Python script for building KLayout (http://www.klayout.de/index.php)
|
# The top Python script for building KLayout (http://www.klayout.de/index.php).
|
||||||
# version 0.30.2 or later on different Apple Mac OSX platforms.
|
# version 0.30.5 or later on different Apple Mac OSX platforms.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
@ -17,6 +17,7 @@ import platform
|
||||||
import optparse
|
import optparse
|
||||||
import subprocess
|
import subprocess
|
||||||
import pprint
|
import pprint
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
## To import global dictionaries of different modules and utility functions
|
## To import global dictionaries of different modules and utility functions
|
||||||
|
|
@ -25,6 +26,7 @@ mydir = os.path.dirname(os.path.abspath(__file__))
|
||||||
sys.path.append( mydir + "/macbuild" )
|
sys.path.append( mydir + "/macbuild" )
|
||||||
from build4mac_env import *
|
from build4mac_env import *
|
||||||
from build4mac_util import *
|
from build4mac_util import *
|
||||||
|
from bundle_qtconf import generate_qtconf, QtConfError
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
## To generate the OS-wise usage strings and the default module set
|
## To generate the OS-wise usage strings and the default module set
|
||||||
|
|
@ -45,32 +47,36 @@ def GenerateUsage(platform):
|
||||||
usage = "\n"
|
usage = "\n"
|
||||||
usage += "-----------------------------------------------------------------------------------------------------------\n"
|
usage += "-----------------------------------------------------------------------------------------------------------\n"
|
||||||
usage += "<< Usage of 'build4mac.py' >>\n"
|
usage += "<< Usage of 'build4mac.py' >>\n"
|
||||||
usage += " for building KLayout 0.30.2 or later on different Apple macOS platforms.\n"
|
usage += " for building KLayout 0.30.5 or later on different Apple macOS platforms.\n"
|
||||||
usage += "\n"
|
usage += "\n"
|
||||||
usage += "$ [python] ./build4mac.py\n"
|
usage += "$ [python] ./build4mac.py\n"
|
||||||
usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value\n"
|
usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value\n"
|
||||||
usage += " ----------------------------------------------------------------------------------------+---------------\n"
|
usage += " ----------------------------------------------------------------------------------------+---------------\n"
|
||||||
usage += " [-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', | %s\n" % myQt56
|
usage += " [-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', | %s\n" % myQt56
|
||||||
usage += " : 'Qt6MacPorts', 'Qt6Brew'] |\n"
|
usage += " : 'Qt6MacPorts', 'Qt6Brew', 'Qt6Ana3''] |\n"
|
||||||
usage += " : Qt5MacPorts: use Qt5 from MacPorts |\n"
|
usage += " : Qt5MacPorts: use Qt5 from MacPorts |\n"
|
||||||
usage += " : Qt5Brew: use Qt5 from Homebrew |\n"
|
usage += " : Qt5Brew: use Qt5 from Homebrew |\n"
|
||||||
usage += " : Qt5Ana3: use Qt5 from Anaconda3 |\n"
|
usage += " : Qt5Ana3: use Qt5 from Anaconda3 |\n"
|
||||||
usage += " : Qt6MacPorts: use Qt6 from MacPorts (*) |\n"
|
usage += " : Qt6MacPorts: use Qt6 from MacPorts (*) |\n"
|
||||||
usage += " : Qt6Brew: use Qt6 from Homebrew (*) |\n"
|
usage += " : Qt6Brew: use Qt6 from Homebrew (*) |\n"
|
||||||
|
usage += " : Qt6Ana3: use Qt6 from Anaconda3 (*) |\n"
|
||||||
usage += " : (*) migration to Qt6 is ongoing |\n"
|
usage += " : (*) migration to Qt6 is ongoing |\n"
|
||||||
usage += " [-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP33', 'HB34', 'Ana3'] | %s\n" % myRuby
|
usage += " [-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP34', 'HB34', 'Ana3'] | %s\n" % myRuby
|
||||||
usage += " : nil: don't bind Ruby |\n"
|
usage += " : nil: don't bind Ruby |\n"
|
||||||
usage += " : Sys: use [Sequoia|Sonoma|Ventura|Monterey]-bundled Ruby 2.6 |\n"
|
usage += " : Sys: use [Tahoe|Sequoia|Sonoma]-bundled Ruby 2.6 |\n"
|
||||||
usage += " : MP33: use Ruby 3.3 from MacPorts |\n"
|
usage += " : MP34: use Ruby 3.4 from MacPorts |\n"
|
||||||
usage += " : HB34: use Ruby 3.4 from Homebrew |\n"
|
usage += " : HB34: use Ruby 3.4 from Homebrew |\n"
|
||||||
usage += " : Ana3: use Ruby 3.2 from Anaconda3 |\n"
|
usage += " : Ana3: use Ruby 3.4 from Anaconda3 |\n"
|
||||||
usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP312', 'HB312', 'Ana3', | %s\n" % myPython
|
usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP313', 'HB313', 'Ana3', | %s\n" % myPython
|
||||||
|
usage += " : 'MP312', 'MP312', |\n"
|
||||||
usage += " : 'MP311', 'HB311', 'HBAuto'] |\n"
|
usage += " : 'MP311', 'HB311', 'HBAuto'] |\n"
|
||||||
usage += " : nil: don't bind Python |\n"
|
usage += " : nil: don't bind Python |\n"
|
||||||
usage += " : Sys: use [Sequoia|Sonoma|Ventura|Monterey]-bundled Python 3.9 |\n"
|
usage += " : Sys: use [Tahoe|Sequoia|Sonoma]-bundled Python 3.9 |\n"
|
||||||
|
usage += " : MP313: use Python 3.13 from MacPorts |\n"
|
||||||
|
usage += " : HB313: use Python 3.13 from Homebrew |\n"
|
||||||
|
usage += " : Ana3: use Python 3.13 from Anaconda3 |\n"
|
||||||
usage += " : MP312: use Python 3.12 from MacPorts |\n"
|
usage += " : MP312: use Python 3.12 from MacPorts |\n"
|
||||||
usage += " : HB312: use Python 3.12 from Homebrew |\n"
|
usage += " : HB312: use Python 3.12 from Homebrew |\n"
|
||||||
usage += " : Ana3: use Python 3.12 from Anaconda3 |\n"
|
|
||||||
usage += " : MP311: use Python 3.11 from MacPorts |\n"
|
usage += " : MP311: use Python 3.11 from MacPorts |\n"
|
||||||
usage += " : HB311: use Python 3.11 from Homebrew (+) |\n"
|
usage += " : HB311: use Python 3.11 from Homebrew (+) |\n"
|
||||||
usage += " : (+) required to provide the legacy pip in HW-*.dmg |\n"
|
usage += " : (+) required to provide the legacy pip in HW-*.dmg |\n"
|
||||||
|
|
@ -273,15 +279,15 @@ def Parse_CLI_Args(config):
|
||||||
p = optparse.OptionParser(usage=Usage)
|
p = optparse.OptionParser(usage=Usage)
|
||||||
p.add_option( '-q', '--qt',
|
p.add_option( '-q', '--qt',
|
||||||
dest='type_qt',
|
dest='type_qt',
|
||||||
help="Qt type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', 'Qt6MacPorts', 'Qt6Brew']" )
|
help="Qt type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', 'Qt6MacPorts', 'Qt6Brew', 'Qt6Ana3'']" )
|
||||||
|
|
||||||
p.add_option( '-r', '--ruby',
|
p.add_option( '-r', '--ruby',
|
||||||
dest='type_ruby',
|
dest='type_ruby',
|
||||||
help="Ruby type=['nil', 'Sys', 'MP33', 'HB34', 'Ana3']" )
|
help="Ruby type=['nil', 'Sys', 'MP34', 'HB34', 'Ana3']" )
|
||||||
|
|
||||||
p.add_option( '-p', '--python',
|
p.add_option( '-p', '--python',
|
||||||
dest='type_python',
|
dest='type_python',
|
||||||
help="Python type=['nil', 'Sys', 'MP312', 'HB312', 'Ana3', 'MP311', 'HB311', 'HBAuto']" )
|
help="Python type=['nil', 'Sys', 'MP313', 'HB313', 'Ana3', 'MP312', 'HB312', 'MP311', 'HB311', 'HBAuto']" )
|
||||||
|
|
||||||
p.add_option( '-P', '--buildPymod',
|
p.add_option( '-P', '--buildPymod',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
|
@ -381,6 +387,7 @@ def Parse_CLI_Args(config):
|
||||||
candidates['QT5ANA3'] = 'Qt5Ana3'
|
candidates['QT5ANA3'] = 'Qt5Ana3'
|
||||||
candidates['QT6MACPORTS'] = 'Qt6MacPorts'
|
candidates['QT6MACPORTS'] = 'Qt6MacPorts'
|
||||||
candidates['QT6BREW'] = 'Qt6Brew'
|
candidates['QT6BREW'] = 'Qt6Brew'
|
||||||
|
candidates['QT6ANA3'] = 'Qt6Ana3'
|
||||||
try:
|
try:
|
||||||
ModuleQt = candidates[ opt.type_qt.upper() ]
|
ModuleQt = candidates[ opt.type_qt.upper() ]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
@ -402,6 +409,8 @@ def Parse_CLI_Args(config):
|
||||||
choiceQt56 = 'qt6MP'
|
choiceQt56 = 'qt6MP'
|
||||||
elif ModuleQt == "Qt6Brew":
|
elif ModuleQt == "Qt6Brew":
|
||||||
choiceQt56 = 'qt6Brew'
|
choiceQt56 = 'qt6Brew'
|
||||||
|
elif ModuleQt == "Qt6Ana3":
|
||||||
|
choiceQt56 = 'qt6Ana3'
|
||||||
|
|
||||||
# Check if non-OS-standard (-bundled) script languages (Ruby and Python) are used
|
# Check if non-OS-standard (-bundled) script languages (Ruby and Python) are used
|
||||||
NonOSStdLang = False
|
NonOSStdLang = False
|
||||||
|
|
@ -410,7 +419,7 @@ def Parse_CLI_Args(config):
|
||||||
candidates = dict()
|
candidates = dict()
|
||||||
candidates['NIL'] = 'nil'
|
candidates['NIL'] = 'nil'
|
||||||
candidates['SYS'] = 'Sys'
|
candidates['SYS'] = 'Sys'
|
||||||
candidates['MP33'] = 'MP33'
|
candidates['MP34'] = 'MP34'
|
||||||
candidates['HB34'] = 'HB34'
|
candidates['HB34'] = 'HB34'
|
||||||
candidates['ANA3'] = 'Ana3'
|
candidates['ANA3'] = 'Ana3'
|
||||||
try:
|
try:
|
||||||
|
|
@ -433,14 +442,17 @@ def Parse_CLI_Args(config):
|
||||||
ModuleRuby = 'RubyVentura'
|
ModuleRuby = 'RubyVentura'
|
||||||
elif Platform == "Monterey":
|
elif Platform == "Monterey":
|
||||||
ModuleRuby = 'RubyMonterey'
|
ModuleRuby = 'RubyMonterey'
|
||||||
elif choiceRuby == "MP33":
|
elif choiceRuby == "MP34":
|
||||||
ModuleRuby = 'Ruby33MacPorts'
|
ModuleRuby = 'Ruby34MacPorts'
|
||||||
NonOSStdLang = True
|
NonOSStdLang = True
|
||||||
elif choiceRuby == "HB34":
|
elif choiceRuby == "HB34":
|
||||||
ModuleRuby = 'Ruby34Brew'
|
ModuleRuby = 'Ruby34Brew'
|
||||||
NonOSStdLang = True
|
NonOSStdLang = True
|
||||||
elif choiceRuby == "Ana3":
|
elif choiceRuby == "Ana3":
|
||||||
ModuleRuby = 'RubyAnaconda3'
|
if choiceQt56 == 'qt5Ana3':
|
||||||
|
ModuleRuby = 'RubyAnaconda3V5'
|
||||||
|
else: # 'qt6Ana3'
|
||||||
|
ModuleRuby = 'RubyAnaconda3V6'
|
||||||
NonOSStdLang = True
|
NonOSStdLang = True
|
||||||
if ModuleRuby == '':
|
if ModuleRuby == '':
|
||||||
print("")
|
print("")
|
||||||
|
|
@ -453,9 +465,11 @@ def Parse_CLI_Args(config):
|
||||||
candidates = dict()
|
candidates = dict()
|
||||||
candidates['NIL'] = 'nil'
|
candidates['NIL'] = 'nil'
|
||||||
candidates['SYS'] = 'Sys'
|
candidates['SYS'] = 'Sys'
|
||||||
|
candidates['MP313'] = 'MP313'
|
||||||
|
candidates['HB313'] = 'HB313'
|
||||||
|
candidates['ANA3'] = 'Ana3'
|
||||||
candidates['MP312'] = 'MP312'
|
candidates['MP312'] = 'MP312'
|
||||||
candidates['HB312'] = 'HB312'
|
candidates['HB312'] = 'HB312'
|
||||||
candidates['ANA3'] = 'Ana3'
|
|
||||||
candidates['MP311'] = 'MP311'
|
candidates['MP311'] = 'MP311'
|
||||||
candidates['HB311'] = 'HB311'
|
candidates['HB311'] = 'HB311'
|
||||||
candidates['HBAUTO'] = 'HBAuto'
|
candidates['HBAUTO'] = 'HBAuto'
|
||||||
|
|
@ -485,6 +499,21 @@ def Parse_CLI_Args(config):
|
||||||
elif Platform == "Monterey":
|
elif Platform == "Monterey":
|
||||||
ModulePython = 'PythonMonterey'
|
ModulePython = 'PythonMonterey'
|
||||||
OSPython3FW = MontereyPy3FW
|
OSPython3FW = MontereyPy3FW
|
||||||
|
elif choicePython == "MP313":
|
||||||
|
ModulePython = 'Python313MacPorts'
|
||||||
|
OSPython3FW = None
|
||||||
|
NonOSStdLang = True
|
||||||
|
elif choicePython == "HB313":
|
||||||
|
ModulePython = 'Python313Brew'
|
||||||
|
OSPython3FW = None
|
||||||
|
NonOSStdLang = True
|
||||||
|
elif choicePython == "Ana3":
|
||||||
|
if choiceQt56 == 'qt5Ana3':
|
||||||
|
ModulePython = 'PythonAnaconda3V5'
|
||||||
|
else: # 'qt6Ana3'
|
||||||
|
ModulePython = 'PythonAnaconda3V6'
|
||||||
|
OSPython3FW = None
|
||||||
|
NonOSStdLang = True
|
||||||
elif choicePython == "MP312":
|
elif choicePython == "MP312":
|
||||||
ModulePython = 'Python312MacPorts'
|
ModulePython = 'Python312MacPorts'
|
||||||
OSPython3FW = None
|
OSPython3FW = None
|
||||||
|
|
@ -493,10 +522,6 @@ def Parse_CLI_Args(config):
|
||||||
ModulePython = 'Python312Brew'
|
ModulePython = 'Python312Brew'
|
||||||
OSPython3FW = None
|
OSPython3FW = None
|
||||||
NonOSStdLang = True
|
NonOSStdLang = True
|
||||||
elif choicePython == "Ana3":
|
|
||||||
ModulePython = 'PythonAnaconda3'
|
|
||||||
OSPython3FW = None
|
|
||||||
NonOSStdLang = True
|
|
||||||
elif choicePython == "HB311":
|
elif choicePython == "HB311":
|
||||||
ModulePython = 'Python311Brew'
|
ModulePython = 'Python311Brew'
|
||||||
OSPython3FW = None
|
OSPython3FW = None
|
||||||
|
|
@ -692,7 +717,7 @@ def Get_Build_Parameters(config):
|
||||||
parameters['qt_lib_root'] = Qt56Dictionary[ModuleQt]['libdir']
|
parameters['qt_lib_root'] = Qt56Dictionary[ModuleQt]['libdir']
|
||||||
|
|
||||||
# (E) rpath
|
# (E) rpath
|
||||||
if OSPython3FW in [ MontereyPy3FW, VenturaPy3FW, SonomaPy3FW, SequoiaPy3FW ]:
|
if OSPython3FW in [ MontereyPy3FW, VenturaPy3FW, SonomaPy3FW, SequoiaPy3FW, TahoePy3FW ]:
|
||||||
parameters['rpath'] = OSPython3FW
|
parameters['rpath'] = OSPython3FW
|
||||||
else:
|
else:
|
||||||
parameters['rpath'] = "@executable_path/../Frameworks"
|
parameters['rpath'] = "@executable_path/../Frameworks"
|
||||||
|
|
@ -740,10 +765,10 @@ def Get_Build_Parameters(config):
|
||||||
# (L) Extra parameters needed for <pymod>
|
# (L) Extra parameters needed for <pymod>
|
||||||
# <pymod> will be built if:
|
# <pymod> will be built if:
|
||||||
# BuildPymodWhl = True
|
# BuildPymodWhl = True
|
||||||
# Platform = [ 'Sequoia', 'Sonoma', 'Ventura', 'Monterey']
|
# Platform = [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]
|
||||||
# ModuleRuby = [ 'Ruby33MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]
|
# ModuleRuby = [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]
|
||||||
# ModulePython = [ 'Python312MacPorts', 'Python311MacPorts',
|
# ModulePython = [ 'Python313MacPorts', 'Python312MacPorts', 'Python311MacPorts',
|
||||||
# 'Python311Brew',
|
# 'Python313Brew', 'Python312Brew', 'Python311Brew',
|
||||||
# 'PythonAnaconda3' ]
|
# 'PythonAnaconda3' ]
|
||||||
parameters['BuildPymodWhl'] = BuildPymodWhl
|
parameters['BuildPymodWhl'] = BuildPymodWhl
|
||||||
parameters['Platform'] = Platform
|
parameters['Platform'] = Platform
|
||||||
|
|
@ -752,12 +777,12 @@ def Get_Build_Parameters(config):
|
||||||
|
|
||||||
PymodDistDir = dict()
|
PymodDistDir = dict()
|
||||||
if Platform in [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]:
|
if Platform in [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]:
|
||||||
if ModuleRuby in [ 'Ruby33MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]:
|
if ModuleRuby in [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3V5', 'RubyAnaconda3V6' ]:
|
||||||
if ModulePython in [ 'Python312MacPorts', 'Python311MacPorts' ]:
|
if ModulePython in [ 'Python313MacPorts', 'Python312MacPorts', 'Python311MacPorts' ]:
|
||||||
PymodDistDir[ModulePython] = 'dist-MP3-%s' % ModuleQt
|
PymodDistDir[ModulePython] = 'dist-MP3-%s' % ModuleQt
|
||||||
elif ModulePython in [ 'Python311Brew' ]:
|
elif ModulePython in [ 'Python313Brew', 'Python312Brew', 'Python311Brew' ]:
|
||||||
PymodDistDir[ModulePython] = 'dist-HB3-%s' % ModuleQt
|
PymodDistDir[ModulePython] = 'dist-HB3-%s' % ModuleQt
|
||||||
elif ModulePython in [ 'PythonAnaconda3' ]:
|
elif ModulePython in [ 'PythonAnaconda3V5', 'PythonAnaconda3V6' ]:
|
||||||
PymodDistDir[ModulePython] = 'dist-ana3-%s' % ModuleQt
|
PymodDistDir[ModulePython] = 'dist-ana3-%s' % ModuleQt
|
||||||
parameters['pymod_dist'] = PymodDistDir
|
parameters['pymod_dist'] = PymodDistDir
|
||||||
return parameters
|
return parameters
|
||||||
|
|
@ -774,11 +799,11 @@ def Build_pymod_wheel(parameters):
|
||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
# [1] <pymod> will be built if:
|
# [1] <pymod> will be built if:
|
||||||
# BuildPymodWhl = True
|
# BuildPymodWhl = True
|
||||||
# Platform = [ 'Sequoia', 'Sonoma', 'Ventura', 'Monterey']
|
# Platform = [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]
|
||||||
# ModuleRuby = [ 'Ruby33MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]
|
# ModuleRuby = [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3V5', 'RubyAnaconda3V6' ]
|
||||||
# ModulePython = [ 'Python312MacPorts', 'Python311MacPorts',
|
# ModulePython = [ 'Python313MacPorts', 'Python312MacPorts', 'Python311MacPorts',
|
||||||
# 'Python311Brew',
|
# 'Python313Brew', 'Python312Brew', 'Python311Brew',
|
||||||
# 'PythonAnaconda3' ]
|
# 'PythonAnaconda3V5', 'PythonAnaconda3V6' ]
|
||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
BuildPymodWhl = parameters['BuildPymodWhl']
|
BuildPymodWhl = parameters['BuildPymodWhl']
|
||||||
Platform = parameters['Platform']
|
Platform = parameters['Platform']
|
||||||
|
|
@ -788,11 +813,11 @@ def Build_pymod_wheel(parameters):
|
||||||
return 0
|
return 0
|
||||||
if not Platform in [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]:
|
if not Platform in [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]:
|
||||||
return 0
|
return 0
|
||||||
elif not ModuleRuby in [ 'Ruby33MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]:
|
elif not ModuleRuby in [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3V5', 'RubyAnaconda3V6' ]:
|
||||||
return 0
|
return 0
|
||||||
elif not ModulePython in [ 'Python312MacPorts', 'Python311MacPorts', \
|
elif not ModulePython in [ 'Python313MacPorts', 'Python312MacPorts', 'Python311MacPorts', \
|
||||||
'Python311Brew', \
|
'Python313Brew', 'Python312Brew', 'Python311Brew', \
|
||||||
'PythonAnaconda3' ]:
|
'PythonAnaconda3V5', 'PythonAnaconda3V6' ]:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
|
@ -814,10 +839,15 @@ def Build_pymod_wheel(parameters):
|
||||||
addLibPath = "%s/lib" % DefaultHomebrewRoot # -- ditto --
|
addLibPath = "%s/lib" % DefaultHomebrewRoot # -- ditto --
|
||||||
whlTarget = "HB3"
|
whlTarget = "HB3"
|
||||||
# Using Anaconda3
|
# Using Anaconda3
|
||||||
elif PymodDistDir[ModulePython].find('dist-ana3') >= 0:
|
elif PymodDistDir[ModulePython].find('dist-ana3-Qt5Ana3') >= 0:
|
||||||
addBinPath = "/Applications/anaconda3/bin"
|
addBinPath = "%s/bin" % Ana3VirEnv5
|
||||||
addIncPath = "/Applications/anaconda3/include"
|
addIncPath = "%s/include" % Ana3VirEnv5
|
||||||
addLibPath = "/Applications/anaconda3/lib"
|
addLibPath = "%s/lib" % Ana3VirEnv5
|
||||||
|
whlTarget = "ana3"
|
||||||
|
elif PymodDistDir[ModulePython].find('dist-ana3-Qt6Ana3') >= 0:
|
||||||
|
addBinPath = "%s/bin" % Ana3VirEnv6
|
||||||
|
addIncPath = "%s/include" % Ana3VirEnv6
|
||||||
|
addLibPath = "%s/lib" % Ana3VirEnv6
|
||||||
whlTarget = "ana3"
|
whlTarget = "ana3"
|
||||||
else:
|
else:
|
||||||
addBinPath = ""
|
addBinPath = ""
|
||||||
|
|
@ -970,7 +1000,7 @@ def Build_pymod_wheel(parameters):
|
||||||
# V
|
# V
|
||||||
# new: klayout-0.30.2-cp312-cp312-macosx_10_9_x86_64.whl
|
# new: klayout-0.30.2-cp312-cp312-macosx_10_9_x86_64.whl
|
||||||
#------------------------------------------------------------------------
|
#------------------------------------------------------------------------
|
||||||
if whlTarget == "ana3":
|
if whlTarget == "ana3" and Platform in ['Sequoia', 'Sonoma', 'Ventura', 'Monterey']:
|
||||||
wheels = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.30.2-cp312-cp312-macosx_10_15_x86_64.whl']
|
wheels = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.30.2-cp312-cp312-macosx_10_15_x86_64.whl']
|
||||||
if not len(wheels) == 1:
|
if not len(wheels) == 1:
|
||||||
print( "", file=sys.stderr )
|
print( "", file=sys.stderr )
|
||||||
|
|
@ -1022,6 +1052,7 @@ def Run_Build_Command(config, parameters):
|
||||||
else:
|
else:
|
||||||
jump2pymod_wheel = True
|
jump2pymod_wheel = True
|
||||||
|
|
||||||
|
Append_qmake_Flags()
|
||||||
#-----------------------------------------------------------------
|
#-----------------------------------------------------------------
|
||||||
# [1] Use the AddressSanitizer (ASan) in the debug build.
|
# [1] Use the AddressSanitizer (ASan) in the debug build.
|
||||||
# This environment variable is tested in ../src/klayout.pri.
|
# This environment variable is tested in ../src/klayout.pri.
|
||||||
|
|
@ -1050,8 +1081,11 @@ def Run_Build_Command(config, parameters):
|
||||||
addLibPath = "%s/lib" % DefaultHomebrewRoot # -- ditto --
|
addLibPath = "%s/lib" % DefaultHomebrewRoot # -- ditto --
|
||||||
# Using Anaconda3
|
# Using Anaconda3
|
||||||
elif ModuleQt.upper() in [ 'QT5ANA3' ]:
|
elif ModuleQt.upper() in [ 'QT5ANA3' ]:
|
||||||
addIncPath = "/Applications/anaconda3/include"
|
addIncPath = "%s/include" % Ana3VirEnv5
|
||||||
addLibPath = "/Applications/anaconda3/lib"
|
addLibPath = "%s/lib" % Ana3VirEnv5
|
||||||
|
elif ModuleQt.upper() in [ 'QT6ANA3' ]:
|
||||||
|
addIncPath = "%s/include" % Ana3VirEnv6
|
||||||
|
addLibPath = "%s/lib" % Ana3VirEnv6
|
||||||
else:
|
else:
|
||||||
addIncPath = ""
|
addIncPath = ""
|
||||||
addLibPath = ""
|
addLibPath = ""
|
||||||
|
|
@ -1226,6 +1260,8 @@ def Deploy_Binaries_For_Bundle(config, parameters):
|
||||||
NonOSStdLang = config['NonOSStdLang']
|
NonOSStdLang = config['NonOSStdLang']
|
||||||
DeploymentF = config['DeploymentF']
|
DeploymentF = config['DeploymentF']
|
||||||
DeploymentP = config['DeploymentP']
|
DeploymentP = config['DeploymentP']
|
||||||
|
PackagePrefix = config['PackagePrefix']
|
||||||
|
ModuleQt = config['ModuleQt']
|
||||||
MacPkgDir = config['MacPkgDir']
|
MacPkgDir = config['MacPkgDir']
|
||||||
Version = config['Version']
|
Version = config['Version']
|
||||||
DeployVerbose = config['DeployVerbose']
|
DeployVerbose = config['DeployVerbose']
|
||||||
|
|
@ -1304,8 +1340,10 @@ def Deploy_Binaries_For_Bundle(config, parameters):
|
||||||
# +-- Contents/+
|
# +-- Contents/+
|
||||||
# +-- Info.plist
|
# +-- Info.plist
|
||||||
# +-- PkgInfo
|
# +-- PkgInfo
|
||||||
|
# +-- PlugIns/
|
||||||
# +-- Resources/+
|
# +-- Resources/+
|
||||||
# | +-- 'klayout.icns'
|
# | +-- 'klayout.icns'
|
||||||
|
# | +-- 'qt.conf'
|
||||||
# +-- Frameworks/+
|
# +-- Frameworks/+
|
||||||
# | +-- '*.framework'
|
# | +-- '*.framework'
|
||||||
# | +-- '*.dylib'
|
# | +-- '*.dylib'
|
||||||
|
|
@ -1793,6 +1831,45 @@ def Deploy_Binaries_For_Bundle(config, parameters):
|
||||||
for item in glob.glob( pymodDistDir + "/*.whl" ):
|
for item in glob.glob( pymodDistDir + "/*.whl" ):
|
||||||
shutil.copy2( item, targetDirP )
|
shutil.copy2( item, targetDirP )
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
# (D) generate a proper "qt.conf" using the "bundle_qtconf.py" module
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
mode = None # ["st", "hw", "lw"]
|
||||||
|
lw_qt_major = None # [5, 6]
|
||||||
|
lw_stack = None # ["macports", "homebrew", "anaconda"]
|
||||||
|
arch_hint = "auto"
|
||||||
|
if PackagePrefix == "ST-":
|
||||||
|
mode = "st"
|
||||||
|
elif PackagePrefix == "HW-":
|
||||||
|
mode = "hw"
|
||||||
|
elif PackagePrefix == "LW-":
|
||||||
|
mode = "lw"
|
||||||
|
else:
|
||||||
|
raise Exception( f"! unsupported PackagePrefix {PackagePrefix}" )
|
||||||
|
|
||||||
|
# ModuleQt = ["Qt5MacPorts", "Qt5Brew", "Qt5Ana3", "Qt6MacPorts", "Qt6Brew", "Qt6Ana3"]
|
||||||
|
lw_qt_major = int(ModuleQt[2])
|
||||||
|
rest = ModuleQt[3:].lower()
|
||||||
|
if rest == "macports":
|
||||||
|
lw_stack = "macports"
|
||||||
|
elif rest == "brew":
|
||||||
|
lw_stack = "homebrew"
|
||||||
|
elif rest == "ana3":
|
||||||
|
lw_stack = "anaconda"
|
||||||
|
else:
|
||||||
|
raise Exception( f"! unknown ModuleQt {ModuleQt}" )
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = generate_qtconf( mode=mode, lw_stack=lw_stack, lw_qt_major=lw_qt_major )
|
||||||
|
# print(text) # -> "[Paths]\nPlugins=/opt/local/libexec/qt5/plugins\n"
|
||||||
|
except QtConfError as e:
|
||||||
|
raise Exception(f"Failed: {e}")
|
||||||
|
|
||||||
|
qtconf = targetDirR + "/qt.conf"
|
||||||
|
with open( qtconf, "w", encoding="utf-8" ) as f:
|
||||||
|
f.write(text)
|
||||||
|
os.chmod( qtconf, 0o0644 )
|
||||||
|
|
||||||
print( " [7] Setting and changing the identification names of KLayout's libraries in each executable ..." )
|
print( " [7] Setting and changing the identification names of KLayout's libraries in each executable ..." )
|
||||||
#------------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------------
|
||||||
# [7] Set and change the library identification name(s) of different executable(s)
|
# [7] Set and change the library identification name(s) of different executable(s)
|
||||||
|
|
@ -1828,10 +1905,6 @@ def Deploy_Binaries_For_Bundle(config, parameters):
|
||||||
options = macdepQtOpt + verbose
|
options = macdepQtOpt + verbose
|
||||||
deploytool = parameters['deploy_tool']
|
deploytool = parameters['deploy_tool']
|
||||||
|
|
||||||
# Without the following, the plugin cocoa would not be found properly.
|
|
||||||
shutil.copy2( sourceDir2 + "/qt.conf", targetDirM )
|
|
||||||
os.chmod( targetDirM + "/qt.conf", 0o0644 )
|
|
||||||
|
|
||||||
os.chdir(ProjectDir)
|
os.chdir(ProjectDir)
|
||||||
os.chdir(MacPkgDir)
|
os.chdir(MacPkgDir)
|
||||||
command = "%s %s %s" % ( deploytool, app_bundle, options )
|
command = "%s %s %s" % ( deploytool, app_bundle, options )
|
||||||
|
|
@ -2190,7 +2263,21 @@ def Deploy_Binaries_For_Bundle(config, parameters):
|
||||||
print( " [8] Skipped deploying Qt's Frameworks and optional Python/Ruby Frameworks..." )
|
print( " [8] Skipped deploying Qt's Frameworks and optional Python/Ruby Frameworks..." )
|
||||||
print( "##### Finished deploying the libraries and executables for <klayout.app> #####" )
|
print( "##### Finished deploying the libraries and executables for <klayout.app> #####" )
|
||||||
print("")
|
print("")
|
||||||
os.chdir(ProjectDir)
|
|
||||||
|
#-------------------------------------------------------------
|
||||||
|
# [11] Sign the application bundle
|
||||||
|
#-------------------------------------------------------------
|
||||||
|
if Platform in ['Tahoe']:
|
||||||
|
print( " [11] Signing the macOS application bundle (ad-hoc) after all post-build edits (install_name_tool/strip)..." )
|
||||||
|
appbundle = "%s/klayout.app" % AbsMacPkgDir
|
||||||
|
res = Sign_App_Bundle(appbundle)
|
||||||
|
print(res["ok"], res["verify_codesign_ok"], res["verify_spctl_ok"])
|
||||||
|
if not res["ok"]:
|
||||||
|
print("ERROR:", res.get("error",""))
|
||||||
|
for tag, ok, out in res["log"][-6:]:
|
||||||
|
print(f"[{tag}] ok={ok}\n{out}")
|
||||||
|
os.chdir(ProjectDir)
|
||||||
|
print("")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@
|
||||||
#
|
#
|
||||||
# Here are dictionaries of ...
|
# Here are dictionaries of ...
|
||||||
# different modules for building KLayout (http://www.klayout.de/index.php)
|
# different modules for building KLayout (http://www.klayout.de/index.php)
|
||||||
# version 0.30.2 or later on different Apple Mac OSX platforms.
|
# version 0.30.5 or later on different Apple Mac OSX platforms.
|
||||||
#
|
#
|
||||||
# This file is imported by 'build4mac.py' script.
|
# This file is imported by the 'build4mac.py' script.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -27,14 +27,20 @@ XcodeToolChain = { 'nameID': '/usr/bin/install_name_tool -id ',
|
||||||
|
|
||||||
(System, Node, Release, MacVersion, Machine, Processor) = platform.uname()
|
(System, Node, Release, MacVersion, Machine, Processor) = platform.uname()
|
||||||
if Machine == "arm64": # Apple Silicon!
|
if Machine == "arm64": # Apple Silicon!
|
||||||
DefaultHomebrewRoot = '/opt/homebrew'
|
DefaultHomebrewRoot = '/opt/homebrew'
|
||||||
|
DefaultAnaconda3Root = '/opt/anaconda3'
|
||||||
|
Ana3VirEnv5 = '%s/envs/klayout-qt5' % DefaultAnaconda3Root
|
||||||
|
Ana3VirEnv6 = '%s/envs/klayout-qt6' % DefaultAnaconda3Root
|
||||||
HomebrewSearchPathFilter1 = '\t+%s/opt' % DefaultHomebrewRoot
|
HomebrewSearchPathFilter1 = '\t+%s/opt' % DefaultHomebrewRoot
|
||||||
HomebrewSearchPathFilter2 = '\t+@loader_path/../../../../../../../../../../opt'
|
HomebrewSearchPathFilter2 = '\t+@loader_path/../../../../../../../../../../opt'
|
||||||
HomebrewSearchPathFilter3 = '@loader_path/../../../../../../../../../../opt' # no leading white space
|
HomebrewSearchPathFilter3 = '@loader_path/../../../../../../../../../../opt' # no leading white space
|
||||||
# 1: absolute path as seen in ~python@3.9.17
|
# 1: absolute path as seen in ~python@3.9.17
|
||||||
# 2: relative path as seen in python@3.9.18
|
# 2: relative path as seen in python@3.9.18
|
||||||
else:
|
else: # x86_64|Intel
|
||||||
DefaultHomebrewRoot = '/usr/local'
|
DefaultHomebrewRoot = '/usr/local'
|
||||||
|
DefaultAnaconda3Root = '/Applications/anaconda3'
|
||||||
|
Ana3VirEnv5 = '%s/envs/klayout-qt5' % DefaultAnaconda3Root
|
||||||
|
Ana3VirEnv6 = '%s/envs/klayout-qt6' % DefaultAnaconda3Root
|
||||||
HomebrewSearchPathFilter1 = '\t+%s/opt' % DefaultHomebrewRoot
|
HomebrewSearchPathFilter1 = '\t+%s/opt' % DefaultHomebrewRoot
|
||||||
HomebrewSearchPathFilter2 = '\t+@loader_path/../../../../../../../../../../opt'
|
HomebrewSearchPathFilter2 = '\t+@loader_path/../../../../../../../../../../opt'
|
||||||
HomebrewSearchPathFilter3 = '@loader_path/../../../../../../../../../../opt' # no leading white space
|
HomebrewSearchPathFilter3 = '@loader_path/../../../../../../../../../../opt' # no leading white space
|
||||||
|
|
@ -63,7 +69,7 @@ del System, Node, Release, MacVersion, Machine, Processor
|
||||||
# [1] Qt5 or Qt6
|
# [1] Qt5 or Qt6
|
||||||
#-----------------------------------------------------
|
#-----------------------------------------------------
|
||||||
Qts = [ 'Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3' ]
|
Qts = [ 'Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3' ]
|
||||||
Qts += [ 'Qt6MacPorts', 'Qt6Brew' ]
|
Qts += [ 'Qt6MacPorts', 'Qt6Brew', 'Qt6Ana3' ]
|
||||||
|
|
||||||
#-----------------------------------------------------
|
#-----------------------------------------------------
|
||||||
# Whereabouts of different components of Qt5
|
# Whereabouts of different components of Qt5
|
||||||
|
|
@ -84,13 +90,67 @@ Qt5Brew = { 'qmake' : '%s/opt/qt@5/bin/qmake' % DefaultHomebrewRoot,
|
||||||
'libdir': '%s/opt/qt@5/lib' % DefaultHomebrewRoot
|
'libdir': '%s/opt/qt@5/lib' % DefaultHomebrewRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
# Qt5 bundled with anaconda3 installed under /Applications/anaconda3/
|
#---------------------------------------------------------------------------------------------------
|
||||||
# The standard installation deploys the tool under $HOME/opt/anaconda3/.
|
# [Apple Silicon]
|
||||||
# If so, you need to make a symbolic link: /Applications/anaconda3 ---> $HOME/opt/anaconda3/
|
# Qt5 is to be installed under /opt/anaconda3/envs/klayout-qt5
|
||||||
|
# after installing "Anaconda3-2025.06-0-MacOSX-arm64.pkg" under /opt/anaconda3/.
|
||||||
|
#
|
||||||
|
# 1) Create a new env "klayout-qt5" (with stable solver & channels)
|
||||||
|
# switch solver to libmamba for faster/more stable resolves
|
||||||
|
# $ conda install -n base -y conda-libmamba-solver
|
||||||
|
# $ conda config --set solver libmamba
|
||||||
|
#
|
||||||
|
# Create the environment (on this ARM machine it will pull osx-arm64 builds)
|
||||||
|
# $ conda create -n klayout-qt5 python=3.13 -y
|
||||||
|
# $ conda activate klayout-qt5
|
||||||
|
#
|
||||||
|
# In this env only, prefer conda-forge strictly (to avoid mixing)
|
||||||
|
# $ conda config --env --add channels conda-forge
|
||||||
|
# $ conda config --env --add channels defaults
|
||||||
|
# $ conda config --env --set channel_priority strict
|
||||||
|
# $ conda install -n base -y conda-libmamba-solver
|
||||||
|
# $ conda config --set solver libmamba
|
||||||
|
#
|
||||||
|
# 2) Install Qt5 (qt-main) only from conda-forge
|
||||||
|
# Qt5 core (builds that typically include Designer/UiTools)
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "qt-main=5.15.15"
|
||||||
|
#
|
||||||
|
# 3) Additionally, install Ruby and libgit2 only from conda-forge
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "ruby=3.4.7"
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "libgit2=1.9.1"
|
||||||
|
#---------------------------------------------------------------------------------------------------
|
||||||
|
# [x86_64|Intel]
|
||||||
|
# Qt5 is to be installed under /Applications/anaconda3/envs/klayout-qt5
|
||||||
|
# after installing "Anaconda3-2025.06-0-MacOSX-x86_64.pkg" under /Applications/anaconda3/.
|
||||||
|
#
|
||||||
|
# 1) Create a new env "klayout-qt5" (with stable solver & channels)
|
||||||
|
# switch solver to libmamba for faster/more stable resolves
|
||||||
|
# $ conda install -n base -y conda-libmamba-solver
|
||||||
|
# $ conda config --set solver libmamba
|
||||||
|
#
|
||||||
|
# Create the environment (on this x86_64 machine it will pull osx-64 builds)
|
||||||
|
# $ conda create -n klayout-qt5 python=3.13 -y
|
||||||
|
# $ conda activate klayout-qt5
|
||||||
|
#
|
||||||
|
# In this env only, prefer conda-forge strictly (to avoid mixing)
|
||||||
|
# $ conda config --env --add channels conda-forge
|
||||||
|
# $ conda config --env --add channels defaults
|
||||||
|
# $ conda config --env --set channel_priority strict
|
||||||
|
# $ conda install -n base -y conda-libmamba-solver
|
||||||
|
# $ conda config --set solver libmamba
|
||||||
|
#
|
||||||
|
# 2) Install Qt5 (qt-main) only from conda-forge
|
||||||
|
# Qt5 core (builds that typically include Designer/UiTools)
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "qt-main=5.15.15"
|
||||||
|
#
|
||||||
|
# 3) Additionally, install Ruby and libgit2 only from conda-forge
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "ruby=3.4.7"
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "libgit2=1.9.1"
|
||||||
|
#---------------------------------------------------------------------------------------------------
|
||||||
# [Key Type Name] = 'Qt5Ana3'
|
# [Key Type Name] = 'Qt5Ana3'
|
||||||
Qt5Ana3 = { 'qmake' : '/Applications/anaconda3/bin/qmake',
|
Qt5Ana3 = { 'qmake' : '%s/bin/qmake' % Ana3VirEnv5,
|
||||||
'deploy': '/Applications/anaconda3/bin/macdeployqt',
|
'deploy': '%s/bin/macdeployqt' % Ana3VirEnv5,
|
||||||
'libdir': '/Applications/anaconda3/lib'
|
'libdir': '%s/lib' % Ana3VirEnv5
|
||||||
}
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
@ -112,12 +172,78 @@ Qt6Brew = { 'qmake' : '%s/opt/qt@6/bin/qmake' % DefaultHomebrewRoot,
|
||||||
'libdir': '%s/opt/qt@6/lib' % DefaultHomebrewRoot
|
'libdir': '%s/opt/qt@6/lib' % DefaultHomebrewRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------------------
|
||||||
|
# [Apple Silicon]
|
||||||
|
# Qt6 is to be installed under /opt/anaconda3/envs/klayout-qt6
|
||||||
|
# after installing "Anaconda3-2025.06-0-MacOSX-arm64.pkg" under /opt/anaconda3/.
|
||||||
|
#
|
||||||
|
# 1) Create a new env "klayout-qt6" (with stable solver & channels)
|
||||||
|
# switch solver to libmamba for faster/more stable resolves
|
||||||
|
# $ conda install -n base -y conda-libmamba-solver
|
||||||
|
# $ conda config --set solver libmamba
|
||||||
|
#
|
||||||
|
# Create the environment (on this ARM machine it will pull osx-arm64 builds)
|
||||||
|
# $ conda create -n klayout-qt6 python=3.13 -y
|
||||||
|
# $ conda activate klayout-qt6
|
||||||
|
#
|
||||||
|
# In this env only, prefer conda-forge strictly (to avoid mixing)
|
||||||
|
# $ conda config --env --add channels conda-forge
|
||||||
|
# $ conda config --env --add channels defaults
|
||||||
|
# $ conda config --env --set channel_priority strict
|
||||||
|
# $ conda install -n base -y conda-libmamba-solver
|
||||||
|
# $ conda config --set solver libmamba
|
||||||
|
#
|
||||||
|
# 2) Install Qt6 (qt6-main and qt6-multimedia) only from conda-forge
|
||||||
|
# Qt6 core (builds that typically include Designer/UiTools)
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "qt6-main=6.9.3"
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "qt6-multimedia=6.9.3"
|
||||||
|
#
|
||||||
|
# 3) Additionally, install Ruby and libgit2 only from conda-forge
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "ruby=3.4.7"
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "libgit2=1.9.1"
|
||||||
|
#---------------------------------------------------------------------------------------------------
|
||||||
|
# [x86_64|Intel]
|
||||||
|
# Qt6 is to be installed under /Applications/anaconda3/envs/klayout-qt6
|
||||||
|
# after installing "Anaconda3-2025.06-0-MacOSX-x86_64.pkg" under /Applications/anaconda3/.
|
||||||
|
#
|
||||||
|
# 1) Create a new env "klayout-qt6" (with stable solver & channels)
|
||||||
|
# switch solver to libmamba for faster/more stable resolves
|
||||||
|
# $ conda install -n base -y conda-libmamba-solver
|
||||||
|
# $ conda config --set solver libmamba
|
||||||
|
#
|
||||||
|
# Create the environment (on this x86_64 machine it will pull osx-64 builds)
|
||||||
|
# $ conda create -n klayout-qt6 python=3.13 -y
|
||||||
|
# $ conda activate klayout-qt6
|
||||||
|
#
|
||||||
|
# In this env only, prefer conda-forge strictly (to avoid mixing)
|
||||||
|
# $ conda config --env --add channels conda-forge
|
||||||
|
# $ conda config --env --add channels defaults
|
||||||
|
# $ conda config --env --set channel_priority strict
|
||||||
|
# $ conda install -n base -y conda-libmamba-solver
|
||||||
|
# $ conda config --set solver libmamba
|
||||||
|
#
|
||||||
|
# 2) Install Qt6 (qt6-main and qt6-multimedia) only from conda-forge
|
||||||
|
# Qt6 core (builds that typically include Designer/UiTools)
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "qt6-main=6.9.3"
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "qt6-multimedia=6.9.3"
|
||||||
|
#
|
||||||
|
# 3) Additionally, install Ruby and libgit2 only from conda-forge
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "ruby=3.4.7"
|
||||||
|
# $ conda install -y --override-channels -c conda-forge "libgit2=1.9.1"
|
||||||
|
#---------------------------------------------------------------------------------------------------
|
||||||
|
# [Key Type Name] = 'Qt6Ana3'
|
||||||
|
Qt6Ana3 = { 'qmake' : '%s/bin/qmake6' % Ana3VirEnv6,
|
||||||
|
'deploy': '%s/bin/macdeployqt6' % Ana3VirEnv6,
|
||||||
|
'libdir': '%s/lib' % Ana3VirEnv6
|
||||||
|
}
|
||||||
|
|
||||||
# Consolidated dictionary kit for Qt[5|6]
|
# Consolidated dictionary kit for Qt[5|6]
|
||||||
Qt56Dictionary = { 'Qt5MacPorts': Qt5MacPorts,
|
Qt56Dictionary = { 'Qt5MacPorts': Qt5MacPorts,
|
||||||
'Qt5Brew' : Qt5Brew,
|
'Qt5Brew' : Qt5Brew,
|
||||||
'Qt5Ana3' : Qt5Ana3,
|
'Qt5Ana3' : Qt5Ana3,
|
||||||
'Qt6MacPorts': Qt6MacPorts,
|
'Qt6MacPorts': Qt6MacPorts,
|
||||||
'Qt6Brew' : Qt6Brew
|
'Qt6Brew' : Qt6Brew,
|
||||||
|
'Qt6Ana3' : Qt6Ana3
|
||||||
}
|
}
|
||||||
|
|
||||||
#-----------------------------------------------------
|
#-----------------------------------------------------
|
||||||
|
|
@ -129,8 +255,8 @@ Qt56Dictionary = { 'Qt5MacPorts': Qt5MacPorts,
|
||||||
# for the previous states.
|
# for the previous states.
|
||||||
#-----------------------------------------------------
|
#-----------------------------------------------------
|
||||||
RubyNil = [ 'nil' ]
|
RubyNil = [ 'nil' ]
|
||||||
RubySys = [ 'RubyMonterey', 'RubyVentura', 'RubySonoma', 'RubySequoia' ]
|
RubySys = [ 'RubyMonterey', 'RubyVentura', 'RubySonoma', 'RubySequoia', 'RubyTahoe' ]
|
||||||
RubyExt = [ 'Ruby33MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]
|
RubyExt = [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]
|
||||||
Rubies = RubyNil + RubySys + RubyExt
|
Rubies = RubyNil + RubySys + RubyExt
|
||||||
|
|
||||||
#-----------------------------------------------------
|
#-----------------------------------------------------
|
||||||
|
|
@ -188,12 +314,22 @@ RubySequoia = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions
|
||||||
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % SequoiaXcSDK
|
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % SequoiaXcSDK
|
||||||
}
|
}
|
||||||
|
|
||||||
# Ruby 3.3 from MacPorts (https://www.macports.org/)
|
# Bundled with Tahoe (26.x)
|
||||||
# install with 'sudo port install ruby33'
|
# [Key Type Name] = 'Sys'
|
||||||
# [Key Type Name] = 'MP33'
|
TahoeXcSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
|
||||||
Ruby33MacPorts = { 'exe': '/opt/local/bin/ruby3.3',
|
TahoeCLTSDK = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
|
||||||
'inc': '/opt/local/include/ruby-3.3.9',
|
RubyTahoe = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby',
|
||||||
'lib': '/opt/local/lib/libruby.3.3.dylib'
|
'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % TahoeXcSDK,
|
||||||
|
'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % TahoeXcSDK,
|
||||||
|
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % TahoeXcSDK
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ruby 3.4 from MacPorts (https://www.macports.org/)
|
||||||
|
# install with 'sudo port install ruby34'
|
||||||
|
# [Key Type Name] = 'MP34'
|
||||||
|
Ruby34MacPorts = { 'exe': '/opt/local/bin/ruby3.4',
|
||||||
|
'inc': '/opt/local/include/ruby-3.4.7',
|
||||||
|
'lib': '/opt/local/lib/libruby.3.4.dylib'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Ruby 3.4 from Homebrew
|
# Ruby 3.4 from Homebrew
|
||||||
|
|
@ -205,24 +341,30 @@ Ruby34Brew = { 'exe': '%s/bin/ruby' % HBRuby34Path,
|
||||||
'lib': '%s/lib/libruby.3.4.dylib' % HBRuby34Path
|
'lib': '%s/lib/libruby.3.4.dylib' % HBRuby34Path
|
||||||
}
|
}
|
||||||
|
|
||||||
# Ruby 3.2 bundled with anaconda3 installed under /Applications/anaconda3/
|
# Ruby 3.4 installed under [/opt|/Applications]/anaconda3/envs/klayout-qt[5|6]
|
||||||
# The standard installation deploys the tool under $HOME/opt/anaconda3/.
|
# See the [Qt5] [Qt6] section above.
|
||||||
# If so, you need to make a symbolic link: /Applications/anaconda3 ---> $HOME/opt/anaconda3/
|
|
||||||
# [Key Type Name] = 'Ana3'
|
# [Key Type Name] = 'Ana3'
|
||||||
RubyAnaconda3 = { 'exe': '/Applications/anaconda3/bin/ruby',
|
RubyAnaconda3V5 = { 'exe': '%s/bin/ruby' % Ana3VirEnv5,
|
||||||
'inc': '/Applications/anaconda3/include/ruby-3.2.0',
|
'inc': '%s/include/ruby-3.4.0' % Ana3VirEnv5,
|
||||||
'lib': '/Applications/anaconda3/lib/libruby.3.2.dylib'
|
'lib': '%s/lib/libruby.3.4.dylib' % Ana3VirEnv5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RubyAnaconda3V6 = { 'exe': '%s/bin/ruby' % Ana3VirEnv6,
|
||||||
|
'inc': '%s/include/ruby-3.4.0' % Ana3VirEnv6,
|
||||||
|
'lib': '%s/lib/libruby.3.4.dylib' % Ana3VirEnv6
|
||||||
|
}
|
||||||
|
|
||||||
# Consolidated dictionary kit for Ruby
|
# Consolidated dictionary kit for Ruby
|
||||||
RubyDictionary = { 'nil' : None,
|
RubyDictionary = { 'nil' : None,
|
||||||
'RubyMonterey' : RubyMonterey,
|
'RubyMonterey' : RubyMonterey,
|
||||||
'RubyVentura' : RubyVentura,
|
'RubyVentura' : RubyVentura,
|
||||||
'RubySonoma' : RubySonoma,
|
'RubySonoma' : RubySonoma,
|
||||||
'RubySequoia' : RubySequoia,
|
'RubySequoia' : RubySequoia,
|
||||||
'Ruby33MacPorts': Ruby33MacPorts,
|
'RubyTahoe' : RubyTahoe,
|
||||||
'Ruby34Brew' : Ruby34Brew,
|
'Ruby34MacPorts' : Ruby34MacPorts,
|
||||||
'RubyAnaconda3' : RubyAnaconda3
|
'Ruby34Brew' : Ruby34Brew,
|
||||||
|
'RubyAnaconda3V5' : RubyAnaconda3V5,
|
||||||
|
'RubyAnaconda3V6' : RubyAnaconda3V6
|
||||||
}
|
}
|
||||||
|
|
||||||
#-----------------------------------------------------
|
#-----------------------------------------------------
|
||||||
|
|
@ -234,9 +376,9 @@ RubyDictionary = { 'nil' : None,
|
||||||
# for the previous states.
|
# for the previous states.
|
||||||
#-----------------------------------------------------
|
#-----------------------------------------------------
|
||||||
PythonNil = [ 'nil' ]
|
PythonNil = [ 'nil' ]
|
||||||
PythonSys = [ 'PythonMonterey', 'PythonVentura', 'PythonSonoma', 'PythonSequoia' ]
|
PythonSys = [ 'PythonMonterey', 'PythonVentura', 'PythonSonoma', 'PythonSequoia', 'PythonTahoe' ]
|
||||||
PythonExt = [ 'Python311MacPorts', 'Python312MacPorts' ]
|
PythonExt = [ 'Python311MacPorts', 'Python312MacPorts', 'Python313MacPorts' ]
|
||||||
PythonExt += [ 'Python311Brew', 'Python312Brew', 'PythonAutoBrew' ]
|
PythonExt += [ 'Python311Brew', 'Python312Brew', 'Python313Brew', 'PythonAutoBrew' ]
|
||||||
PythonExt += [ 'PythonAnaconda3' ]
|
PythonExt += [ 'PythonAnaconda3' ]
|
||||||
Pythons = PythonNil + PythonSys + PythonExt
|
Pythons = PythonNil + PythonSys + PythonExt
|
||||||
|
|
||||||
|
|
@ -279,6 +421,15 @@ PythonSequoia = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' %
|
||||||
'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % SequoiaPy3FW
|
'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % SequoiaPy3FW
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Bundled with Tahoe (26.x)
|
||||||
|
# [Key Type Name] = 'Sys'
|
||||||
|
TahoePy3FWXc = "/Applications/Xcode.app/Contents/Developer/Library/Frameworks"
|
||||||
|
TahoePy3FW = "/Library/Developer/CommandLineTools/Library/Frameworks"
|
||||||
|
PythonTahoe = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' % TahoePy3FW,
|
||||||
|
'inc': '%s/Python3.framework/Versions/3.9/include/python3.9' % TahoePy3FW,
|
||||||
|
'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % TahoePy3FW
|
||||||
|
}
|
||||||
|
|
||||||
# Python 3.11 from MacPorts (https://www.macports.org/)
|
# Python 3.11 from MacPorts (https://www.macports.org/)
|
||||||
# install with 'sudo port install python311'
|
# install with 'sudo port install python311'
|
||||||
# [Key Type Name] = 'MP311'
|
# [Key Type Name] = 'MP311'
|
||||||
|
|
@ -295,6 +446,14 @@ Python312MacPorts = { 'exe': '/opt/local/Library/Frameworks/Python.framework/Ver
|
||||||
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.12/lib/libpython3.12.dylib'
|
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.12/lib/libpython3.12.dylib'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Python 3.13 from MacPorts (https://www.macports.org/)
|
||||||
|
# install with 'sudo port install python313'
|
||||||
|
# [Key Type Name] = 'MP313'
|
||||||
|
Python313MacPorts = { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.13/bin/python3.13',
|
||||||
|
'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.13/include/python3.13',
|
||||||
|
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.13/lib/libpython3.13.dylib'
|
||||||
|
}
|
||||||
|
|
||||||
# Python 3.11 from Homebrew
|
# Python 3.11 from Homebrew
|
||||||
# install with 'brew install python@3.11'
|
# install with 'brew install python@3.11'
|
||||||
# [Key Type Name] = 'HB311'
|
# [Key Type Name] = 'HB311'
|
||||||
|
|
@ -313,14 +472,27 @@ Python312Brew = { 'exe': '%s/Versions/3.12/bin/python3.12' % HBPython312Fram
|
||||||
'lib': '%s/Versions/3.12/lib/libpython3.12.dylib' % HBPython312FrameworkPath
|
'lib': '%s/Versions/3.12/lib/libpython3.12.dylib' % HBPython312FrameworkPath
|
||||||
}
|
}
|
||||||
|
|
||||||
# Python 3.12 bundled with anaconda3 installed under /Applications/anaconda3/
|
# Python 3.13 from Homebrew
|
||||||
# The standard installation deploys the tool under $HOME/opt/anaconda3/.
|
# install with 'brew install python@3.13'
|
||||||
# If so, you need to make a symbolic link: /Applications/anaconda3 ---> $HOME/opt/anaconda3/
|
# [Key Type Name] = 'HB313'
|
||||||
|
HBPython313FrameworkPath = '%s/opt/python@3.13/Frameworks/Python.framework' % DefaultHomebrewRoot
|
||||||
|
Python313Brew = { 'exe': '%s/Versions/3.13/bin/python3.13' % HBPython313FrameworkPath,
|
||||||
|
'inc': '%s/Versions/3.13/include/python3.13' % HBPython313FrameworkPath,
|
||||||
|
'lib': '%s/Versions/3.13/lib/libpython3.13.dylib' % HBPython313FrameworkPath
|
||||||
|
}
|
||||||
|
|
||||||
|
# Python 3.13 installed under [/opt|/Applications]/anaconda3/klayout-qt[5|6]
|
||||||
|
# See the [Qt5] [Qt6] section above.
|
||||||
# [Key Type Name] = 'Ana3'
|
# [Key Type Name] = 'Ana3'
|
||||||
PythonAnaconda3 = { 'exe': '/Applications/anaconda3/bin/python3.12',
|
PythonAnaconda3V5 = { 'exe': '%s/bin/python3.13' % Ana3VirEnv5,
|
||||||
'inc': '/Applications/anaconda3/include/python3.12',
|
'inc': '%s/include/python3.13' % Ana3VirEnv5,
|
||||||
'lib': '/Applications/anaconda3/lib/libpython3.12.dylib'
|
'lib': '%s/lib/libpython3.13.dylib' % Ana3VirEnv5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PythonAnaconda3V6 = { 'exe': '%s/bin/python3.13' % Ana3VirEnv6,
|
||||||
|
'inc': '%s/include/python3.13' % Ana3VirEnv6,
|
||||||
|
'lib': '%s/lib/libpython3.13.dylib' % Ana3VirEnv6
|
||||||
|
}
|
||||||
|
|
||||||
# Latest Python from Homebrew
|
# Latest Python from Homebrew
|
||||||
# install with 'brew install python'
|
# install with 'brew install python'
|
||||||
|
|
@ -373,16 +545,20 @@ else:
|
||||||
_have_Homebrew_Python = True
|
_have_Homebrew_Python = True
|
||||||
|
|
||||||
# Consolidated dictionary kit for Python
|
# Consolidated dictionary kit for Python
|
||||||
PythonDictionary = { 'nil' : None,
|
PythonDictionary = { 'nil' : None,
|
||||||
'PythonMonterey' : PythonMonterey,
|
'PythonMonterey' : PythonMonterey,
|
||||||
'PythonVentura' : PythonVentura,
|
'PythonVentura' : PythonVentura,
|
||||||
'PythonSonoma' : PythonSonoma,
|
'PythonSonoma' : PythonSonoma,
|
||||||
'PythonSequoia' : PythonSequoia,
|
'PythonSequoia' : PythonSequoia,
|
||||||
'Python312MacPorts': Python312MacPorts,
|
'PythonTahoe' : PythonTahoe,
|
||||||
'Python312Brew' : Python312Brew,
|
'Python313MacPorts' : Python313MacPorts,
|
||||||
'PythonAnaconda3' : PythonAnaconda3,
|
'Python313Brew' : Python313Brew,
|
||||||
'Python311MacPorts': Python311MacPorts,
|
'PythonAnaconda3V5' : PythonAnaconda3V5,
|
||||||
'Python311Brew' : Python311Brew
|
'PythonAnaconda3V6' : PythonAnaconda3V6,
|
||||||
|
'Python312MacPorts' : Python312MacPorts,
|
||||||
|
'Python312Brew' : Python312Brew,
|
||||||
|
'Python311MacPorts' : Python311MacPorts,
|
||||||
|
'Python311Brew' : Python311Brew
|
||||||
}
|
}
|
||||||
if _have_Homebrew_Python:
|
if _have_Homebrew_Python:
|
||||||
PythonDictionary['PythonAutoBrew'] = PythonAutoBrew
|
PythonDictionary['PythonAutoBrew'] = PythonAutoBrew
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#
|
#
|
||||||
# Here are utility functions and classes ...
|
# Here are utility functions and classes ...
|
||||||
# for building KLayout (http://www.klayout.de/index.php)
|
# for building KLayout (http://www.klayout.de/index.php)
|
||||||
# version 0.30.2 or later on different Apple Mac OSX platforms.
|
# version 0.30.5 or later on different Apple Mac OSX platforms.
|
||||||
#
|
#
|
||||||
# This file is imported by 'build4mac.py' script.
|
# This file is imported by 'build4mac.py' script.
|
||||||
#========================================================================================
|
#========================================================================================
|
||||||
|
|
@ -131,8 +131,10 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
|
||||||
# +-- Contents/+
|
# +-- Contents/+
|
||||||
# +-- Info.plist
|
# +-- Info.plist
|
||||||
# +-- PkgInfo
|
# +-- PkgInfo
|
||||||
|
# +-- PlugIns/
|
||||||
# +-- Resources/+
|
# +-- Resources/+
|
||||||
# | +-- 'klayout.icns'
|
# | +-- 'klayout.icns'
|
||||||
|
# | +-- 'qt.conf'
|
||||||
# +-- Frameworks/+
|
# +-- Frameworks/+
|
||||||
# | +-- '*.framework'
|
# | +-- '*.framework'
|
||||||
# | +-- '*.dylib'
|
# | +-- '*.dylib'
|
||||||
|
|
@ -146,11 +148,15 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
|
||||||
# | +-- pymod/
|
# | +-- pymod/
|
||||||
# |
|
# |
|
||||||
# +-- Buddy/+
|
# +-- Buddy/+
|
||||||
# +-- 'strm2cif'
|
# | +-- 'strm2cif'
|
||||||
# +-- 'strm2dxf'
|
# | +-- 'strm2dxf'
|
||||||
# :
|
# | :
|
||||||
# +-- 'strmxor'
|
# | +-- 'strmxor'
|
||||||
|
# |
|
||||||
|
# +-- pymod-dist/+ (created only if *.whl is available)
|
||||||
|
# +-- klayout-0.27.8-cp38-cp38-macosx_10_9_x86_64.whl (example)(1)
|
||||||
#
|
#
|
||||||
|
# (1) *.whl is install with 'pip3'
|
||||||
# @return 0 on success; non-zero on failure
|
# @return 0 on success; non-zero on failure
|
||||||
#----------------------------------------------------------------------------------------
|
#----------------------------------------------------------------------------------------
|
||||||
def SetChangeLibIdentificationName( executable, relativedir ):
|
def SetChangeLibIdentificationName( executable, relativedir ):
|
||||||
|
|
@ -870,6 +876,255 @@ def DumpDependencyDicPair( title, depDic, pathDic ):
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------------------------
|
||||||
|
## To append qmake LFLAGS with -Wl,-adhoc_codesign only if the linker supports it.
|
||||||
|
# [ChatGPT]
|
||||||
|
#
|
||||||
|
# Call this once BEFORE running qmake.
|
||||||
|
#
|
||||||
|
# @return void
|
||||||
|
#----------------------------------------------------------------------------------------
|
||||||
|
def Append_qmake_Flags():
|
||||||
|
import os, subprocess, tempfile, textwrap, shutil
|
||||||
|
|
||||||
|
def _run(cmd):
|
||||||
|
"""Return True if command exits successfully, False otherwise."""
|
||||||
|
try:
|
||||||
|
subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Allow disabling the probe via environment variable, if needed.
|
||||||
|
if os.environ.get("DISABLE_ADHOC_CODESIGN_PROBE") == "1":
|
||||||
|
return
|
||||||
|
|
||||||
|
# Prefer clang; fall back to cc if clang is not available.
|
||||||
|
compiler = shutil.which("clang") or shutil.which("cc")
|
||||||
|
if not compiler:
|
||||||
|
# No compiler to probe with; skip injecting the flag.
|
||||||
|
return
|
||||||
|
|
||||||
|
# Probe: attempt a tiny link with -Wl,-adhoc_codesign (toolchain dependent).
|
||||||
|
probe_src = textwrap.dedent("int main(){return 0;}\n")
|
||||||
|
with tempfile.TemporaryDirectory() as td:
|
||||||
|
src = os.path.join(td, "t.c")
|
||||||
|
out = os.path.join(td, "t.out")
|
||||||
|
with open(src, "w") as f:
|
||||||
|
f.write(probe_src)
|
||||||
|
supported = _run([compiler, src, "-Wl,-adhoc_codesign", "-o", out])
|
||||||
|
|
||||||
|
if not supported:
|
||||||
|
# Older ld64 (e.g., some Monterey toolchains) may not support this flag.
|
||||||
|
# Skip injecting; post-build signing will still make the app runnable.
|
||||||
|
return
|
||||||
|
|
||||||
|
def _append(name, extra):
|
||||||
|
"""Append 'extra' to env var 'name' with a space if it already exists."""
|
||||||
|
prev = os.environ.get(name, "")
|
||||||
|
os.environ[name] = (prev + " " + extra).strip() if prev else extra
|
||||||
|
|
||||||
|
extra = "-Wl,-adhoc_codesign"
|
||||||
|
# Cover all target types to ensure both executables and shared libs get the flag.
|
||||||
|
for var in ("QMAKE_LFLAGS", "QMAKE_LFLAGS_APP", "QMAKE_LFLAGS_SHLIB", "QMAKE_LFLAGS_PLUGIN"):
|
||||||
|
_append(var, extra)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------------
|
||||||
|
## Sign a macOS application bundle (ad-hoc) after all post-build edits (install_name_tool/strip).
|
||||||
|
# [ChatGPT]
|
||||||
|
#
|
||||||
|
# What it does:
|
||||||
|
# - Removes quarantine recursively
|
||||||
|
# - Drops exec bits on *.so (prevents dyld from treating them as executables)
|
||||||
|
# - Signs all Mach-O candidates (.dylib, .so, executables), inner code first
|
||||||
|
# - Deep-signs the .app
|
||||||
|
# - Verifies with codesign & spctl
|
||||||
|
#
|
||||||
|
# Always returns a dict with these keys:
|
||||||
|
# ok (bool), main_executable (str), so_execbits_dropped (list[str]),
|
||||||
|
# sign_errors (list[str]), verify_codesign_ok (bool), verify_spctl_ok (bool),
|
||||||
|
# verify_codesign_out (str), verify_spctl_out (str), log (list[tuple]), error (str)
|
||||||
|
#
|
||||||
|
# Usage example:
|
||||||
|
# res = Sign_App_Bundle("/Applications/klayout.app")
|
||||||
|
# print(res["ok"], res["verify_codesign_ok"], res["verify_spctl_ok"])
|
||||||
|
#------------------------------------------------------------------------------------------------
|
||||||
|
def Sign_App_Bundle(app_path: str, gatekeeper_required: bool = False) -> dict:
|
||||||
|
"""
|
||||||
|
Ad-hoc sign a macOS .app bundle for local execution.
|
||||||
|
If gatekeeper_required=False (default), overall 'ok' reflects codesign verification only.
|
||||||
|
If gatekeeper_required=True, overall 'ok' requires both codesign AND spctl to pass.
|
||||||
|
|
||||||
|
@param[in] app_path: Path to the .app bundle
|
||||||
|
@param[in] gatekeeper_required: Whether to require Gatekeeper assessment (spctl) to pass
|
||||||
|
|
||||||
|
@return: Dict with keys:
|
||||||
|
ok, main_executable, so_execbits_dropped, sign_errors,
|
||||||
|
verify_codesign_ok, verify_spctl_ok,
|
||||||
|
verify_codesign_out, verify_spctl_out, log, error
|
||||||
|
"""
|
||||||
|
import os, subprocess, plistlib, shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def _blank(error_msg=""):
|
||||||
|
return {
|
||||||
|
"ok": False,
|
||||||
|
"main_executable": "",
|
||||||
|
"so_execbits_dropped": [],
|
||||||
|
"sign_errors": [],
|
||||||
|
"verify_codesign_ok": False,
|
||||||
|
"verify_spctl_ok": False,
|
||||||
|
"verify_codesign_out": "",
|
||||||
|
"verify_spctl_out": "",
|
||||||
|
"log": [],
|
||||||
|
"error": error_msg,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _run(cmd):
|
||||||
|
try:
|
||||||
|
out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
|
||||||
|
return True, out
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return False, e.output
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
for tool in ("codesign", "spctl", "xattr"):
|
||||||
|
if not shutil.which(tool):
|
||||||
|
return _blank(f"Required tool not found: {tool}")
|
||||||
|
|
||||||
|
app = Path(app_path).resolve()
|
||||||
|
if not app.exists():
|
||||||
|
return _blank(f"App not found: {app}")
|
||||||
|
|
||||||
|
info_plist = app / "Contents" / "Info.plist"
|
||||||
|
if not info_plist.exists():
|
||||||
|
return _blank(f"Info.plist not found: {info_plist}")
|
||||||
|
|
||||||
|
# CFBundleExecutable
|
||||||
|
try:
|
||||||
|
with info_plist.open("rb") as f:
|
||||||
|
info = plistlib.load(f)
|
||||||
|
except Exception as e:
|
||||||
|
return _blank(f"Failed to read Info.plist: {e}")
|
||||||
|
|
||||||
|
exe_name = info.get("CFBundleExecutable")
|
||||||
|
if not exe_name:
|
||||||
|
return _blank("CFBundleExecutable not set in Info.plist")
|
||||||
|
|
||||||
|
main_bin = app / "Contents" / "MacOS" / exe_name
|
||||||
|
steps_log = []
|
||||||
|
result = _blank()
|
||||||
|
result["main_executable"] = str(main_bin)
|
||||||
|
|
||||||
|
# 0) Clear quarantine
|
||||||
|
ok, out = _run(["xattr", "-dr", "com.apple.quarantine", str(app)])
|
||||||
|
steps_log.append(("xattr_clear_quarantine", ok, out))
|
||||||
|
|
||||||
|
# 1) Ensure qt.conf is under Resources (harmless if already correct)
|
||||||
|
macos_qtconf = app / "Contents" / "MacOS" / "qt.conf"
|
||||||
|
if macos_qtconf.exists():
|
||||||
|
try:
|
||||||
|
resdir = app / "Contents" / "Resources"
|
||||||
|
resdir.mkdir(parents=True, exist_ok=True)
|
||||||
|
target = resdir / "qt.conf"
|
||||||
|
if target.exists():
|
||||||
|
target.unlink()
|
||||||
|
shutil.move(str(macos_qtconf), str(target))
|
||||||
|
steps_log.append(("relocate_qt_conf", True, f"Moved to {target}"))
|
||||||
|
except Exception as e:
|
||||||
|
steps_log.append(("relocate_qt_conf", False, f"{e}"))
|
||||||
|
|
||||||
|
# 2) Drop exec bits on *.so
|
||||||
|
so_execbits_dropped = []
|
||||||
|
for p in app.rglob("*.so"):
|
||||||
|
try:
|
||||||
|
if p.is_symlink():
|
||||||
|
continue
|
||||||
|
mode = p.stat().st_mode
|
||||||
|
if mode & 0o111:
|
||||||
|
os.chmod(p, mode & ~0o111)
|
||||||
|
so_execbits_dropped.append(str(p))
|
||||||
|
except Exception as e:
|
||||||
|
steps_log.append(("chmod_so", False, f"{p}: {e}"))
|
||||||
|
result["so_execbits_dropped"] = so_execbits_dropped
|
||||||
|
|
||||||
|
# 3) Collect inner targets (exclude main)
|
||||||
|
inner = []
|
||||||
|
for sub in ("Contents/MacOS", "Contents/Buddy"):
|
||||||
|
root = app / sub
|
||||||
|
if root.exists():
|
||||||
|
for p in root.rglob("*"):
|
||||||
|
try:
|
||||||
|
if p.is_symlink() or not p.is_file():
|
||||||
|
continue
|
||||||
|
if p == main_bin:
|
||||||
|
continue
|
||||||
|
if os.access(p, os.X_OK):
|
||||||
|
inner.append(p)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for ext in ("*.dylib", "*.so"):
|
||||||
|
for p in app.rglob(ext):
|
||||||
|
if p.is_symlink() or not p.is_file():
|
||||||
|
continue
|
||||||
|
inner.append(p)
|
||||||
|
|
||||||
|
# De-duplicate while preserving order
|
||||||
|
seen = set()
|
||||||
|
inner_unique = []
|
||||||
|
for p in inner:
|
||||||
|
sp = str(p)
|
||||||
|
if sp not in seen:
|
||||||
|
seen.add(sp)
|
||||||
|
inner_unique.append(p)
|
||||||
|
|
||||||
|
# 4) Sign inner
|
||||||
|
sign_errors = []
|
||||||
|
for p in inner_unique:
|
||||||
|
ok, out = _run(["codesign", "-f", "-s", "-", "--timestamp=none", str(p)])
|
||||||
|
steps_log.append(("codesign_inner", ok, f"{p}\n{out}"))
|
||||||
|
if not ok:
|
||||||
|
sign_errors.append(str(p))
|
||||||
|
result["sign_errors"] = sign_errors
|
||||||
|
|
||||||
|
# 5) Sign main
|
||||||
|
if not main_bin.exists():
|
||||||
|
result["log"] = steps_log
|
||||||
|
result["error"] = f"Main executable not found: {main_bin}"
|
||||||
|
return result
|
||||||
|
ok, out = _run(["codesign", "-f", "-s", "-", "--timestamp=none", str(main_bin)])
|
||||||
|
steps_log.append(("codesign_main", ok, f"{main_bin}\n{out}"))
|
||||||
|
if not ok:
|
||||||
|
result["log"] = steps_log
|
||||||
|
result["error"] = "Failed to sign main executable"
|
||||||
|
return result
|
||||||
|
|
||||||
|
# 6) Deep-sign app
|
||||||
|
ok, out = _run(["codesign", "-f", "-s", "-", "--timestamp=none", "--deep", str(app)])
|
||||||
|
steps_log.append(("codesign_app_deep", ok, out))
|
||||||
|
if not ok:
|
||||||
|
result["log"] = steps_log
|
||||||
|
result["error"] = "Deep codesign failed"
|
||||||
|
return result
|
||||||
|
|
||||||
|
# 7) Verify
|
||||||
|
ok1, out1 = _run(["codesign", "--verify", "--deep", "--strict", "--verbose=4", str(app)])
|
||||||
|
ok2, out2 = _run(["spctl", "--assess", "--type", "execute", "--verbose=4", str(app)])
|
||||||
|
steps_log.append(("verify_codesign", ok1, out1))
|
||||||
|
steps_log.append(("assess_spctl", ok2, out2))
|
||||||
|
|
||||||
|
result["verify_codesign_ok"] = bool(ok1)
|
||||||
|
result["verify_spctl_ok"] = bool(ok2)
|
||||||
|
result["verify_codesign_out"] = out1
|
||||||
|
result["verify_spctl_out"] = out2
|
||||||
|
result["log"] = steps_log
|
||||||
|
|
||||||
|
# Overall result: choose policy based on gatekeeper_required
|
||||||
|
result["ok"] = bool(ok1 and ok2) if gatekeeper_required else bool(ok1)
|
||||||
|
return result
|
||||||
|
|
||||||
#----------------
|
#----------------
|
||||||
# End of File
|
# End of File
|
||||||
#----------------
|
#----------------
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,473 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
File: "macbuild/bundle_qtconf.py"
|
||||||
|
|
||||||
|
Author: ChatGPT + Kazzz-S
|
||||||
|
|
||||||
|
Utilities to generate and embed a proper qt.conf into a macOS .app bundle
|
||||||
|
for KLayout (or any Qt-based app), supporting two strategies:
|
||||||
|
|
||||||
|
- ST/HW (Qt embedded in the bundle): relative qt.conf (Prefix=.., Plugins=PlugIns)
|
||||||
|
- LW (use system-wide Qt): absolute qt.conf (Plugins=<absolute path>)
|
||||||
|
|
||||||
|
Policy:
|
||||||
|
- The bundle creator ("macbuild/build4mac.py") decides the target stack at build time.
|
||||||
|
- The distributed app contains exactly ONE qt.conf (no scripts post-distribution).
|
||||||
|
|
||||||
|
Command-line test usage:
|
||||||
|
python bundle_qtconf.py --mode lw --stack macports --qt 5
|
||||||
|
python bundle_qtconf.py --app ./dist/klayout.app --mode st --plugins /opt/local/libexec/qt5/plugins
|
||||||
|
|
||||||
|
Typical usage:
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from bundle_qtconf import generate_qtconf, QtConfError
|
||||||
|
|
||||||
|
# 1) LW + MacPorts Qt5 (print-only)
|
||||||
|
try:
|
||||||
|
text = generate_qtconf(
|
||||||
|
mode="lw",
|
||||||
|
lw_stack="macports",
|
||||||
|
lw_qt_major=5,
|
||||||
|
)
|
||||||
|
print(text)
|
||||||
|
except QtConfError as e:
|
||||||
|
print(f"Failed: {e}")
|
||||||
|
|
||||||
|
# 2) LW + Homebrew Qt6 (write into app)
|
||||||
|
try:
|
||||||
|
text = generate_qtconf(
|
||||||
|
app_path="dist/klayout.app",
|
||||||
|
mode="lw",
|
||||||
|
lw_stack="homebrew",
|
||||||
|
lw_qt_major=6,
|
||||||
|
arch_hint="arm64", # "x86_64" for Intel; "auto" works too
|
||||||
|
)
|
||||||
|
print(text)
|
||||||
|
except QtConfError as e:
|
||||||
|
print(f"Failed: {e}")
|
||||||
|
|
||||||
|
# 3) LW + Anaconda (Automator-safe: pass explicit prefix if needed)
|
||||||
|
try:
|
||||||
|
text = generate_qtconf(
|
||||||
|
app_path=Path("dist/klayout.app"),
|
||||||
|
mode="lw",
|
||||||
|
lw_stack="anaconda",
|
||||||
|
conda_prefix="/opt/anaconda3",
|
||||||
|
)
|
||||||
|
print(text)
|
||||||
|
except QtConfError as e:
|
||||||
|
print(f"Failed: {e}")
|
||||||
|
|
||||||
|
# 4) ST/HW (Qt embedded in the bundle)
|
||||||
|
try:
|
||||||
|
text = generate_qtconf(
|
||||||
|
app_path="dist/klayout.app",
|
||||||
|
mode="st", # or "hw"
|
||||||
|
embedded_plugins_src="/opt/local/libexec/qt5/plugins",
|
||||||
|
validate=True,
|
||||||
|
)
|
||||||
|
print(text)
|
||||||
|
except QtConfError as e:
|
||||||
|
print(f"Failed: {e}")
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Iterable, Optional, Tuple, List, Union
|
||||||
|
|
||||||
|
|
||||||
|
class QtConfError(RuntimeError):
|
||||||
|
"""Raised when qt.conf generation or validation fails."""
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Utility helpers
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def _app_paths(app_path: Path) -> Tuple[Path, Path, Path]:
|
||||||
|
"""Return (Resources, PlugIns, MacOS) directories for the .app bundle."""
|
||||||
|
app_path = app_path.resolve()
|
||||||
|
contents = app_path / "Contents"
|
||||||
|
resources = contents / "Resources"
|
||||||
|
plugins = contents / "PlugIns"
|
||||||
|
macos = contents / "MacOS"
|
||||||
|
return resources, plugins, macos
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_dir(p: Path) -> None:
|
||||||
|
p.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def choose_homebrew_root(arch_hint: str = "auto") -> Path:
|
||||||
|
"""Choose Homebrew prefix based on architecture hint."""
|
||||||
|
if arch_hint == "arm64" or (arch_hint == "auto" and Path("/opt/homebrew").is_dir()):
|
||||||
|
return Path("/opt/homebrew")
|
||||||
|
return Path("/usr/local")
|
||||||
|
|
||||||
|
|
||||||
|
def _is_executable(p: Path) -> bool:
|
||||||
|
try:
|
||||||
|
return p.is_file() and os.access(str(p), os.X_OK)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _expand_candidates_with_glob(candidates: List[Path]) -> List[Path]:
|
||||||
|
expanded: List[Path] = []
|
||||||
|
for c in candidates:
|
||||||
|
s = str(c)
|
||||||
|
if "*" in s or "?" in s or "[" in s:
|
||||||
|
try:
|
||||||
|
expanded.extend(Path(x) for x in sorted(map(str, c.parent.glob(c.name))))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
expanded.append(c)
|
||||||
|
return expanded
|
||||||
|
|
||||||
|
|
||||||
|
def _first_existing_platforms_dir(candidates: List[Path]) -> Optional[Path]:
|
||||||
|
for c in _expand_candidates_with_glob(candidates):
|
||||||
|
if (c / "platforms").is_dir():
|
||||||
|
return c
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _home_dir() -> Path:
|
||||||
|
"""Return user's home directory, safe for Automator/launchd environments."""
|
||||||
|
try:
|
||||||
|
h = os.environ.get("HOME")
|
||||||
|
if h:
|
||||||
|
return Path(h)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return Path.home()
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# LW plugin dir resolvers (MacPorts / Homebrew / Anaconda)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def find_plugins_dir_lw(
|
||||||
|
lw_stack: str,
|
||||||
|
lw_qt_major: Optional[int] = None,
|
||||||
|
arch_hint: str = "auto",
|
||||||
|
conda_prefix: Optional[Path] = None,
|
||||||
|
) -> Path:
|
||||||
|
"""Resolve the absolute Qt plugins directory for LW mode."""
|
||||||
|
stack = lw_stack.lower().strip()
|
||||||
|
|
||||||
|
# --- MacPorts ---
|
||||||
|
if stack == "macports":
|
||||||
|
if lw_qt_major not in (5, 6):
|
||||||
|
raise QtConfError("MacPorts requires lw_qt_major to be 5 or 6.")
|
||||||
|
return Path(f"/opt/local/libexec/qt{lw_qt_major}/plugins")
|
||||||
|
|
||||||
|
# --- Homebrew ---
|
||||||
|
if stack == "homebrew":
|
||||||
|
if lw_qt_major not in (5, 6):
|
||||||
|
raise QtConfError("Homebrew requires lw_qt_major to be 5 or 6.")
|
||||||
|
hb = choose_homebrew_root(arch_hint)
|
||||||
|
|
||||||
|
def _looks_like_qt6(p):
|
||||||
|
s = str(p)
|
||||||
|
return "/qt6/" in s or s.endswith("/share/qt/plugins") or "/qtbase/" in s
|
||||||
|
|
||||||
|
def _looks_like_qt5(p):
|
||||||
|
s = str(p)
|
||||||
|
return "/qt@5/" in s or "/qt5/" in s
|
||||||
|
|
||||||
|
candidates: List[Path] = []
|
||||||
|
|
||||||
|
if lw_qt_major == 6:
|
||||||
|
# Prefer qtpaths from qt or qtbase (Qt6 split)
|
||||||
|
for formula in ("qt", "qtbase"):
|
||||||
|
qtpaths_bin = hb / "opt" / formula / "bin" / "qtpaths"
|
||||||
|
if _is_executable(qtpaths_bin):
|
||||||
|
try:
|
||||||
|
out = subprocess.check_output([str(qtpaths_bin), "--plugin-dir"], text=True).strip()
|
||||||
|
p = Path(out)
|
||||||
|
if (p / "platforms").is_dir():
|
||||||
|
return p
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
candidates += [
|
||||||
|
hb / "opt" / "qt" / "share" / "qt" / "plugins",
|
||||||
|
hb / "opt" / "qtbase" / "share" / "qt" / "plugins",
|
||||||
|
hb / "Cellar" / "qt" / "*" / "share" / "qt" / "plugins",
|
||||||
|
hb / "Cellar" / "qtbase" / "*" / "share" / "qt" / "plugins",
|
||||||
|
hb / "opt" / "qt" / "lib" / "qt6" / "plugins",
|
||||||
|
hb / "opt" / "qt" / "plugins",
|
||||||
|
]
|
||||||
|
found = _first_existing_platforms_dir(candidates)
|
||||||
|
if found and _looks_like_qt6(found):
|
||||||
|
return found
|
||||||
|
else:
|
||||||
|
qtpaths_bin = hb / "opt" / "qt@5" / "bin" / "qtpaths"
|
||||||
|
if _is_executable(qtpaths_bin):
|
||||||
|
try:
|
||||||
|
out = subprocess.check_output([str(qtpaths_bin), "--plugin-dir"], text=True).strip()
|
||||||
|
p = Path(out)
|
||||||
|
if (p / "platforms").is_dir() and _looks_like_qt5(p):
|
||||||
|
return p
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
candidates += [
|
||||||
|
hb / "opt" / "qt@5" / "plugins",
|
||||||
|
hb / "opt" / "qt@5" / "lib" / "qt5" / "plugins",
|
||||||
|
hb / "Cellar" / "qt@5" / "*" / "plugins",
|
||||||
|
hb / "Cellar" / "qt@5" / "*" / "lib" / "qt5" / "plugins",
|
||||||
|
]
|
||||||
|
for c in _expand_candidates_with_glob(candidates):
|
||||||
|
if (c / "platforms").is_dir() and _looks_like_qt5(c):
|
||||||
|
return c
|
||||||
|
|
||||||
|
raise QtConfError(
|
||||||
|
f"Homebrew Qt{lw_qt_major} plugins not found under {hb}. Checked: "
|
||||||
|
+ ", ".join(str(p) for p in _expand_candidates_with_glob(candidates))
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Anaconda / Miniconda / Mambaforge / Miniforge ---
|
||||||
|
if stack == "anaconda":
|
||||||
|
def _env_plugins_candidates(env_root, qt_major):
|
||||||
|
if qt_major == 6:
|
||||||
|
return [Path(env_root) / "lib" / "qt6" / "plugins"]
|
||||||
|
else:
|
||||||
|
return [Path(env_root) / "plugins"]
|
||||||
|
|
||||||
|
def _base_preferred_envs(base_root, qt_major):
|
||||||
|
names = ["klayout-qt6"] if qt_major == 6 else ["klayout-qt5"]
|
||||||
|
env_roots = [Path(base_root) / "envs" / n for n in names]
|
||||||
|
cands = []
|
||||||
|
for er in env_roots:
|
||||||
|
cands.extend(_env_plugins_candidates(er, qt_major))
|
||||||
|
return cands
|
||||||
|
|
||||||
|
def _scan_all_envs(base_root, qt_major):
|
||||||
|
cands = []
|
||||||
|
envs_dir = Path(base_root) / "envs"
|
||||||
|
if envs_dir.is_dir():
|
||||||
|
for er in sorted(envs_dir.iterdir()):
|
||||||
|
if not er.is_dir():
|
||||||
|
continue
|
||||||
|
n = er.name.lower()
|
||||||
|
if qt_major == 6 and "qt6" in n:
|
||||||
|
cands.extend(_env_plugins_candidates(er, 6))
|
||||||
|
elif qt_major == 5 and "qt5" in n:
|
||||||
|
cands.extend(_env_plugins_candidates(er, 5))
|
||||||
|
for er in sorted(envs_dir.iterdir()):
|
||||||
|
if er.is_dir():
|
||||||
|
cands.extend(_env_plugins_candidates(er, qt_major))
|
||||||
|
return cands
|
||||||
|
|
||||||
|
def _base_generic_candidates(base_root):
|
||||||
|
return [
|
||||||
|
Path(base_root) / "plugins",
|
||||||
|
Path(base_root) / "lib" / "qt" / "plugins",
|
||||||
|
Path(base_root) / "lib" / "qt5" / "plugins",
|
||||||
|
Path(base_root) / "lib" / "qt6" / "plugins",
|
||||||
|
]
|
||||||
|
|
||||||
|
qt_major = lw_qt_major or 6
|
||||||
|
|
||||||
|
roots: List[Path] = []
|
||||||
|
if conda_prefix:
|
||||||
|
roots.append(Path(conda_prefix))
|
||||||
|
env_prefix = os.environ.get("CONDA_PREFIX", "")
|
||||||
|
if env_prefix:
|
||||||
|
roots.append(Path(env_prefix))
|
||||||
|
home = _home_dir()
|
||||||
|
roots += [
|
||||||
|
Path("/opt/anaconda3"),
|
||||||
|
Path("/usr/local/anaconda3"),
|
||||||
|
home / "opt" / "anaconda3",
|
||||||
|
home / "anaconda3",
|
||||||
|
Path("/opt/miniconda3"),
|
||||||
|
Path("/usr/local/miniconda3"),
|
||||||
|
home / "miniconda3",
|
||||||
|
Path("/opt/mambaforge"),
|
||||||
|
home / "mambaforge",
|
||||||
|
Path("/opt/miniforge3"),
|
||||||
|
home / "miniforge3",
|
||||||
|
Path("/Applications/anaconda3"),
|
||||||
|
Path("/Applications/miniconda3"),
|
||||||
|
Path("/Applications/mambaforge"),
|
||||||
|
Path("/Applications/miniforge3"),
|
||||||
|
]
|
||||||
|
|
||||||
|
plugin_candidates: List[Path] = []
|
||||||
|
|
||||||
|
if conda_prefix:
|
||||||
|
cp = Path(conda_prefix)
|
||||||
|
if (cp / "conda-meta").is_dir() and not (cp / "envs").is_dir():
|
||||||
|
plugin_candidates.extend(_env_plugins_candidates(cp, qt_major))
|
||||||
|
|
||||||
|
for base in roots:
|
||||||
|
b = Path(base)
|
||||||
|
try:
|
||||||
|
b = b.resolve()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
plugin_candidates.extend(_base_preferred_envs(b, qt_major))
|
||||||
|
plugin_candidates.extend(_scan_all_envs(b, qt_major))
|
||||||
|
plugin_candidates.extend(_base_generic_candidates(b))
|
||||||
|
|
||||||
|
# Highest priority: Intel GUI installer layout
|
||||||
|
apps_direct = Path("/Applications/anaconda3/plugins")
|
||||||
|
if apps_direct.exists():
|
||||||
|
plugin_candidates.insert(0, apps_direct)
|
||||||
|
|
||||||
|
found = _first_existing_platforms_dir(plugin_candidates)
|
||||||
|
if found:
|
||||||
|
return found
|
||||||
|
|
||||||
|
raise QtConfError(
|
||||||
|
"Anaconda plugins not found. Checked: "
|
||||||
|
+ ", ".join(str(p) for p in _expand_candidates_with_glob(plugin_candidates))
|
||||||
|
)
|
||||||
|
|
||||||
|
raise QtConfError(f"Unknown lw_stack: {lw_stack}")
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Core functions
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def _validate_libqcocoa(plugins_dir: Path) -> None:
|
||||||
|
"""Ensure libqcocoa.dylib exists under <plugins_dir>/platforms."""
|
||||||
|
lib = plugins_dir / "platforms" / "libqcocoa.dylib"
|
||||||
|
if not lib.is_file():
|
||||||
|
raise QtConfError(f"libqcocoa.dylib not found: {lib}")
|
||||||
|
|
||||||
|
|
||||||
|
def copy_embedded_plugins(
|
||||||
|
embedded_plugins_src: Path,
|
||||||
|
bundle_plugins_dir: Path,
|
||||||
|
subdirs: Iterable[str] = ("platforms",),
|
||||||
|
overwrite: bool = True,
|
||||||
|
) -> None:
|
||||||
|
"""Copy selected plugin subdirectories into the bundle."""
|
||||||
|
embedded_plugins_src = embedded_plugins_src.resolve()
|
||||||
|
bundle_plugins_dir = bundle_plugins_dir.resolve()
|
||||||
|
_ensure_dir(bundle_plugins_dir)
|
||||||
|
for d in subdirs:
|
||||||
|
src = embedded_plugins_src / d
|
||||||
|
dst = bundle_plugins_dir / d
|
||||||
|
if not src.is_dir():
|
||||||
|
raise QtConfError(f"Missing plugin subdir at source: {src}")
|
||||||
|
if dst.exists() and overwrite:
|
||||||
|
shutil.rmtree(dst)
|
||||||
|
shutil.copytree(src, dst)
|
||||||
|
|
||||||
|
|
||||||
|
def make_qtconf_text_relative() -> str:
|
||||||
|
"""Return relative qt.conf text for ST/HW bundles."""
|
||||||
|
return (
|
||||||
|
"[Paths]\n"
|
||||||
|
"Prefix=..\n"
|
||||||
|
"Plugins=PlugIns\n"
|
||||||
|
"# Uncomment if QML is embedded:\n"
|
||||||
|
"# Imports=Resources/qml\n"
|
||||||
|
"# Qml2Imports=Resources/qml\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_qtconf_text_absolute(plugins_dir: Path) -> str:
|
||||||
|
"""Return absolute qt.conf text for LW bundles."""
|
||||||
|
return f"[Paths]\nPlugins={plugins_dir}\n"
|
||||||
|
|
||||||
|
|
||||||
|
def generate_qtconf(
|
||||||
|
app_path: Optional[Union[str, Path]] = None,
|
||||||
|
*,
|
||||||
|
mode: str,
|
||||||
|
embedded_plugins_src: Optional[Union[str, Path]] = None,
|
||||||
|
lw_stack: Optional[str] = None,
|
||||||
|
lw_qt_major: Optional[int] = None,
|
||||||
|
arch_hint: str = "auto",
|
||||||
|
conda_prefix: Optional[Union[str, Path]] = None,
|
||||||
|
validate: bool = True,
|
||||||
|
) -> str:
|
||||||
|
"""Generate qt.conf content (and optionally write it to the bundle)."""
|
||||||
|
app_path_p: Optional[Path] = Path(app_path).resolve() if app_path else None
|
||||||
|
qtconf_text: str
|
||||||
|
|
||||||
|
if mode in ("st", "hw"):
|
||||||
|
qtconf_text = make_qtconf_text_relative()
|
||||||
|
if app_path_p:
|
||||||
|
resources, plugins, _macos = _app_paths(app_path_p)
|
||||||
|
_ensure_dir(resources)
|
||||||
|
if embedded_plugins_src:
|
||||||
|
copy_embedded_plugins(Path(embedded_plugins_src), plugins)
|
||||||
|
if validate:
|
||||||
|
_validate_libqcocoa(plugins)
|
||||||
|
(resources / "qt.conf").write_text(qtconf_text, encoding="utf-8")
|
||||||
|
|
||||||
|
elif mode == "lw":
|
||||||
|
if lw_stack is None:
|
||||||
|
raise QtConfError("lw_stack is required for LW mode (macports|homebrew|anaconda).")
|
||||||
|
plugins_dir = find_plugins_dir_lw(
|
||||||
|
lw_stack=lw_stack,
|
||||||
|
lw_qt_major=lw_qt_major,
|
||||||
|
arch_hint=arch_hint,
|
||||||
|
conda_prefix=Path(conda_prefix) if conda_prefix else None,
|
||||||
|
)
|
||||||
|
if validate:
|
||||||
|
_validate_libqcocoa(plugins_dir)
|
||||||
|
qtconf_text = make_qtconf_text_absolute(plugins_dir)
|
||||||
|
if app_path_p:
|
||||||
|
resources, _, _ = _app_paths(app_path_p)
|
||||||
|
_ensure_dir(resources)
|
||||||
|
(resources / "qt.conf").write_text(qtconf_text, encoding="utf-8")
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise QtConfError(f"Unknown mode: {mode}")
|
||||||
|
|
||||||
|
return qtconf_text
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# CLI for testing
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def main() -> None:
|
||||||
|
"""Standalone CLI for testing or dry-run output."""
|
||||||
|
parser = argparse.ArgumentParser(description="Generate qt.conf or print its content.")
|
||||||
|
parser.add_argument("--app", help="Path to the .app bundle (optional; if omitted, only print the content)")
|
||||||
|
parser.add_argument("--mode", choices=["st", "hw", "lw"], required=True, help="Bundle mode")
|
||||||
|
parser.add_argument("--stack", choices=["macports", "homebrew", "anaconda"], help="LW: Qt stack type")
|
||||||
|
parser.add_argument("--qt", type=int, choices=[5, 6], help="LW: Qt major version (5 or 6)")
|
||||||
|
parser.add_argument("--arch", default="auto", choices=["auto", "arm64", "x86_64"], help="LW: arch hint for Homebrew")
|
||||||
|
parser.add_argument("--plugins", help="ST/HW: source path of Qt plugins")
|
||||||
|
parser.add_argument("--no-validate", action="store_true", help="Skip validation of libqcocoa.dylib existence")
|
||||||
|
parser.add_argument("--conda-prefix", help="LW(Anaconda) only: explicit CONDA_PREFIX to use")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
qtconf_text = generate_qtconf(
|
||||||
|
app_path=args.app,
|
||||||
|
mode=args.mode,
|
||||||
|
embedded_plugins_src=args.plugins,
|
||||||
|
lw_stack=args.stack,
|
||||||
|
lw_qt_major=args.qt,
|
||||||
|
arch_hint=args.arch,
|
||||||
|
conda_prefix=args.conda_prefix,
|
||||||
|
validate=not args.no_validate,
|
||||||
|
)
|
||||||
|
if args.app:
|
||||||
|
print(f"[OK] qt.conf written to bundle: {args.app}")
|
||||||
|
print("----- qt.conf content -----")
|
||||||
|
print(qtconf_text.strip())
|
||||||
|
print("---------------------------")
|
||||||
|
except QtConfError as e:
|
||||||
|
print(f"[ERROR] {e}")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# File: macbuild/cleanQAT.sh
|
||||||
|
#
|
||||||
|
# Task: cleanup qt*.macQAT directories
|
||||||
|
|
||||||
|
for d in *macQAT*; do
|
||||||
|
if [ -d "$d" ]; then
|
||||||
|
echo "Processing: $d"
|
||||||
|
(
|
||||||
|
cd "$d" || exit 1
|
||||||
|
\rm -rf QATest*
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# File: src/mac_no_agl.pri
|
||||||
|
#
|
||||||
|
# Aims: To avoid linking "AGL" when building with macOS SDK >= 26 (Xcode 26 or later)
|
||||||
|
#
|
||||||
|
# Refs: https://github.com/KLayout/klayout/issues/2159
|
||||||
|
#
|
||||||
|
# Usage: Include this file in all leaf "*.pro" files, for example,
|
||||||
|
# ---> src/tl/tl/tl.pro
|
||||||
|
# include($$PWD/../../lib.pri)
|
||||||
|
# include($$PWD/../../mac_no_agl.pri) <===
|
||||||
|
# --- src/tl/unit_tests/unit_tests.pro
|
||||||
|
# include($$PWD/../../lib_ut.pri)
|
||||||
|
# include($$PWD/../../mac_no_agl.pri) <===
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
macx {
|
||||||
|
# Prevent qmake from injecting dependencies from .prl (most reliable protection)
|
||||||
|
CONFIG -= link_prl
|
||||||
|
|
||||||
|
# QMAKE_MAC_SDK examples: "macosx26.0", "macosx26", "macosx27.1"
|
||||||
|
SDK_TAG = $$QMAKE_MAC_SDK
|
||||||
|
SDK_VER_STR = $$replace(SDK_TAG, "macosx", "")
|
||||||
|
SDK_VER_MAJOR = $$section(SDK_VER_STR, ., 0, 0)
|
||||||
|
|
||||||
|
# Fallback: when parsing fails, also match explicit "macosx26"
|
||||||
|
contains(SDK_TAG, "macosx26") {
|
||||||
|
SDK_VER_MAJOR = 26
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- fetch actual SDK info when QMAKE_MAC_SDK only gives "macosx" ---
|
||||||
|
SDK_PATH = $$system("/usr/bin/xcrun --sdk macosx --show-sdk-path")
|
||||||
|
SDK_VER_STR = $$system("/usr/bin/xcrun --sdk macosx --show-sdk-version")
|
||||||
|
|
||||||
|
# Backup extraction: derive version from SDK path (e.g., MacOSX26.0.sdk → 26.0)
|
||||||
|
isEmpty(SDK_VER_STR) {
|
||||||
|
SDK_BASE = $$basename($$SDK_PATH) # MacOSX26.0.sdk
|
||||||
|
SDK_VER_STR = $$replace(SDK_BASE, "MacOSX", "")
|
||||||
|
SDK_VER_STR = $$replace(SDK_VER_STR, ".sdk", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract only the major version number (e.g., 26.0 → 26)
|
||||||
|
SDK_VER_MAJOR = $$section(SDK_VER_STR, ., 0, 0)
|
||||||
|
|
||||||
|
# Debug output
|
||||||
|
message("DEBUG: SDK_PATH = $$SDK_PATH")
|
||||||
|
message("DEBUG: SDK_VER_STR = $$SDK_VER_STR")
|
||||||
|
message("DEBUG: SDK_VER_MAJOR = $$SDK_VER_MAJOR")
|
||||||
|
|
||||||
|
# Apply AGL removal if SDK version >= 26
|
||||||
|
greaterThan(SDK_VER_MAJOR, 25) {
|
||||||
|
message("Detected macOS SDK >= 26 ($$SDK_VER_STR). Adjusting flags...")
|
||||||
|
|
||||||
|
# Aggressively remove AGL in case it’s inserted by Qt or manually
|
||||||
|
LIBS -= -framework
|
||||||
|
LIBS -= AGL
|
||||||
|
QMAKE_LIBS_OPENGL -= -framework
|
||||||
|
QMAKE_LIBS_OPENGL -= AGL
|
||||||
|
QMAKE_LIBS_OPENGL = -framework OpenGL
|
||||||
|
|
||||||
|
# Set consistent minimum deployment target for modern macOS/Apple Silicon
|
||||||
|
QMAKE_CXXFLAGS -= -mmacosx-version-min=10.13
|
||||||
|
QMAKE_LFLAGS -= -mmacosx-version-min=10.13
|
||||||
|
|
||||||
|
QMAKE_MACOSX_DEPLOYMENT_TARGET = 12.0
|
||||||
|
QMAKE_CXXFLAGS += -mmacosx-version-min=12.0
|
||||||
|
QMAKE_LFLAGS += -mmacosx-version-min=12.0
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- stop execution after printing ---
|
||||||
|
#error("DEBUG STOP: printed all mac_no_agl.pri variables, stopping qmake.")
|
||||||
|
}
|
||||||
|
|
@ -78,13 +78,13 @@ def SetGlobals():
|
||||||
Usage = "\n"
|
Usage = "\n"
|
||||||
Usage += "---------------------------------------------------------------------------------------------------------\n"
|
Usage += "---------------------------------------------------------------------------------------------------------\n"
|
||||||
Usage += "<< Usage of 'makeDMG4mac.py' >>\n"
|
Usage += "<< Usage of 'makeDMG4mac.py' >>\n"
|
||||||
Usage += " for making a DMG file of KLayout 0.30.2 or later on different Apple macOS platforms.\n"
|
Usage += " for making a DMG file of KLayout 0.30.5 or later on different Apple macOS platforms.\n"
|
||||||
Usage += "\n"
|
Usage += "\n"
|
||||||
Usage += "$ [python] ./makeDMG4mac.py\n"
|
Usage += "$ [python] ./makeDMG4mac.py\n"
|
||||||
Usage += " option & argument : descriptions | default value\n"
|
Usage += " option & argument : descriptions | default value\n"
|
||||||
Usage += " ----------------------------------------------------------------------------------+-----------------\n"
|
Usage += " ----------------------------------------------------------------------------------+-----------------\n"
|
||||||
Usage += " <-p|--pkg <dir>> : package directory created by `build4mac.py` with [-y|-Y] | ``\n"
|
Usage += " <-p|--pkg <dir>> : package directory created by `build4mac.py` with [-y|-Y] | ``\n"
|
||||||
Usage += " : like 'LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312' | \n"
|
Usage += " : like 'LW-qt5MP.pkg.macos-Sequoia-release-Rmp34Pmp313' | \n"
|
||||||
Usage += " <-c|--clean> : clean the work directory | disabled\n"
|
Usage += " <-c|--clean> : clean the work directory | disabled\n"
|
||||||
Usage += " <-m|--make> : make a compressed DMG file | disabled\n"
|
Usage += " <-m|--make> : make a compressed DMG file | disabled\n"
|
||||||
Usage += " : <-c|--clean> and <-m|--make> are mutually exclusive | \n"
|
Usage += " : <-c|--clean> and <-m|--make> are mutually exclusive | \n"
|
||||||
|
|
@ -106,7 +106,11 @@ def SetGlobals():
|
||||||
|
|
||||||
release = int( Release.split(".")[0] ) # take the first of ['21', '0', '0']
|
release = int( Release.split(".")[0] ) # take the first of ['21', '0', '0']
|
||||||
LatestOS = ""
|
LatestOS = ""
|
||||||
if release == 24:
|
if release == 25:
|
||||||
|
GenOSName = "macOS"
|
||||||
|
Platform = "Tahoe"
|
||||||
|
LatestOS = Platform
|
||||||
|
elif release == 24:
|
||||||
GenOSName = "macOS"
|
GenOSName = "macOS"
|
||||||
Platform = "Sequoia"
|
Platform = "Sequoia"
|
||||||
LatestOS = Platform
|
LatestOS = Platform
|
||||||
|
|
@ -131,7 +135,7 @@ def SetGlobals():
|
||||||
|
|
||||||
if not Machine == "x86_64":
|
if not Machine == "x86_64":
|
||||||
# with an Apple Silicon Chip?
|
# with an Apple Silicon Chip?
|
||||||
if Machine == "arm64" and Platform in ["Sequoia", "Sonoma", "Ventura", "Monterey"]:
|
if Machine == "arm64" and Platform in ["Tahoe", "Sequoia", "Sonoma", "Ventura", "Monterey"]:
|
||||||
print("")
|
print("")
|
||||||
print( "### Your Mac equips an Apple Silicon Chip ###" )
|
print( "### Your Mac equips an Apple Silicon Chip ###" )
|
||||||
print("")
|
print("")
|
||||||
|
|
@ -293,9 +297,15 @@ def CheckPkgDirectory():
|
||||||
PackagePrefix = pkgdirComponents[0]
|
PackagePrefix = pkgdirComponents[0]
|
||||||
QtIdentification = pkgdirComponents[2]
|
QtIdentification = pkgdirComponents[2]
|
||||||
if QtIdentification.find('qt5') == 0:
|
if QtIdentification.find('qt5') == 0:
|
||||||
BackgroundPNG = "KLayoutDMG-BackQt5.png"
|
if Machine == "x86_64":
|
||||||
|
BackgroundPNG = "KLayoutDMG-BackQt5-X86.png"
|
||||||
|
else: # arm64
|
||||||
|
BackgroundPNG = "KLayoutDMG-BackQt5-Mx.png"
|
||||||
elif QtIdentification.find('qt6') == 0:
|
elif QtIdentification.find('qt6') == 0:
|
||||||
BackgroundPNG = "KLayoutDMG-BackQt6.png"
|
if Machine == "x86_64":
|
||||||
|
BackgroundPNG = "KLayoutDMG-BackQt6-X86.png"
|
||||||
|
else: # arm64
|
||||||
|
BackgroundPNG = "KLayoutDMG-BackQt6-Mx.png"
|
||||||
else:
|
else:
|
||||||
BackgroundPNG = None
|
BackgroundPNG = None
|
||||||
raise Exception( "! neither qt5 nor qt6" )
|
raise Exception( "! neither qt5 nor qt6" )
|
||||||
|
|
@ -319,16 +329,16 @@ def CheckPkgDirectory():
|
||||||
LatestOSMacPorts = Platform == LatestOS
|
LatestOSMacPorts = Platform == LatestOS
|
||||||
LatestOSMacPorts &= PackagePrefix == "LW"
|
LatestOSMacPorts &= PackagePrefix == "LW"
|
||||||
LatestOSMacPorts &= QtIdentification in [ "qt5MP", "qt6MP" ]
|
LatestOSMacPorts &= QtIdentification in [ "qt5MP", "qt6MP" ]
|
||||||
LatestOSMacPorts &= RubyPythonID in [ "Rmp33Pmp312", "Rmp33Pmp311" ]
|
LatestOSMacPorts &= RubyPythonID in [ "Rmp34Pmp313", "Rmp34Pmp312", "Rmp34Pmp311" ]
|
||||||
|
|
||||||
LatestOSHomebrew = Platform == LatestOS
|
LatestOSHomebrew = Platform == LatestOS
|
||||||
LatestOSHomebrew &= PackagePrefix == "LW"
|
LatestOSHomebrew &= PackagePrefix == "LW"
|
||||||
LatestOSHomebrew &= QtIdentification in [ "qt5Brew", "qt6Brew", "qt5MP", "qt6MP" ] # "qt[5|6]MP" are the alternatives
|
LatestOSHomebrew &= QtIdentification in [ "qt5Brew", "qt6Brew", "qt5MP", "qt6MP" ] # "qt[5|6]MP" are the alternatives
|
||||||
LatestOSHomebrew &= RubyPythonID in [ "Rhb34Phb312", "Rhb34Phb311", "Rhb34Phbauto" ]
|
LatestOSHomebrew &= RubyPythonID in [ "Rhb34Phb313", "Rhb34Phb312", "Rhb34Phb311", "Rhb34Phbauto" ]
|
||||||
|
|
||||||
LatestOSAnaconda3 = Platform == LatestOS
|
LatestOSAnaconda3 = Platform == LatestOS
|
||||||
LatestOSAnaconda3 &= PackagePrefix == "LW"
|
LatestOSAnaconda3 &= PackagePrefix == "LW"
|
||||||
LatestOSAnaconda3 &= QtIdentification in [ "qt5Ana3" ]
|
LatestOSAnaconda3 &= QtIdentification in [ "qt5Ana3", "qt6Ana3" ]
|
||||||
LatestOSAnaconda3 &= RubyPythonID in [ "Rana3Pana3" ]
|
LatestOSAnaconda3 &= RubyPythonID in [ "Rana3Pana3" ]
|
||||||
|
|
||||||
LatestOSHomebrewH = Platform == LatestOS
|
LatestOSHomebrewH = Platform == LatestOS
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,16 @@ import pandas as pd
|
||||||
#
|
#
|
||||||
# @return matching platform name on success; "" on failure
|
# @return matching platform name on success; "" on failure
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
def Test_My_Platform( platforms=[ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia'] ):
|
def Test_My_Platform( platforms=[ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia', 'Tahoe' ] ):
|
||||||
(System, Node, Release, MacVersion, Machine, Processor) = platform.uname()
|
(System, Node, Release, MacVersion, Machine, Processor) = platform.uname()
|
||||||
|
|
||||||
if not System == "Darwin":
|
if not System == "Darwin":
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
release = int( Release.split(".")[0] ) # take the first of ['21', '0', '0']
|
release = int( Release.split(".")[0] ) # take the first of ['21', '0', '0']
|
||||||
if release == 24:
|
if release == 25:
|
||||||
|
Platform = "Tahoe"
|
||||||
|
elif release == 24:
|
||||||
Platform = "Sequoia"
|
Platform = "Sequoia"
|
||||||
elif release == 23:
|
elif release == 23:
|
||||||
Platform = "Sonoma"
|
Platform = "Sonoma"
|
||||||
|
|
@ -101,15 +103,15 @@ def Get_Build_Options( targetDic, platform ):
|
||||||
buildOp[(qtVer, "std", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'sys', '--debug' ]
|
buildOp[(qtVer, "std", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'sys', '--debug' ]
|
||||||
logfile[(qtVer, "std", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "RsysPsys")
|
logfile[(qtVer, "std", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "RsysPsys")
|
||||||
elif target == "ports":
|
elif target == "ports":
|
||||||
buildOp[(qtVer, "ports", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP33', '-p', 'MP312' ]
|
buildOp[(qtVer, "ports", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP34', '-p', 'MP313' ]
|
||||||
logfile[(qtVer, "ports", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rmp33Pmp312")
|
logfile[(qtVer, "ports", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rmp34Pmp313")
|
||||||
buildOp[(qtVer, "ports", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP33', '-p', 'MP312', '--debug' ]
|
buildOp[(qtVer, "ports", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP34', '-p', 'MP313', '--debug' ]
|
||||||
logfile[(qtVer, "ports", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rmp33Pmp312")
|
logfile[(qtVer, "ports", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rmp34Pmp313")
|
||||||
elif target == "brew":
|
elif target == "brew":
|
||||||
buildOp[(qtVer, "brew", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HB312' ]
|
buildOp[(qtVer, "brew", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HB313' ]
|
||||||
logfile[(qtVer, "brew", "r")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb34Phb312")
|
logfile[(qtVer, "brew", "r")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb34Phb313")
|
||||||
buildOp[(qtVer, "brew", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HB312', '--debug' ]
|
buildOp[(qtVer, "brew", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HB313', '--debug' ]
|
||||||
logfile[(qtVer, "brew", "d")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rhb34Phb312")
|
logfile[(qtVer, "brew", "d")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rhb34Phb313")
|
||||||
elif target == "brewHW":
|
elif target == "brewHW":
|
||||||
buildOp[(qtVer, "brewHW", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311' ]
|
buildOp[(qtVer, "brewHW", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311' ]
|
||||||
logfile[(qtVer, "brewHW", "r")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311")
|
logfile[(qtVer, "brewHW", "r")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311")
|
||||||
|
|
@ -131,10 +133,10 @@ def Get_Build_Options( targetDic, platform ):
|
||||||
buildOp[(qtVer, "brewAHW", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto', '--debug' ]
|
buildOp[(qtVer, "brewAHW", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto', '--debug' ]
|
||||||
logfile[(qtVer, "brewAHW", "d")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "RsysPhbauto")
|
logfile[(qtVer, "brewAHW", "d")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "RsysPhbauto")
|
||||||
elif target == "pbrew":
|
elif target == "pbrew":
|
||||||
buildOp[(qtVer, "pbrew", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB34', '-p', 'HB312' ]
|
buildOp[(qtVer, "pbrew", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB34', '-p', 'HB313' ]
|
||||||
logfile[(qtVer, "pbrew", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb34Phb312")
|
logfile[(qtVer, "pbrew", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb34Phb313")
|
||||||
buildOp[(qtVer, "pbrew", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB34', '-p', 'HB312', '--debug' ]
|
buildOp[(qtVer, "pbrew", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB34', '-p', 'HB313', '--debug' ]
|
||||||
logfile[(qtVer, "pbrew", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rhb34Phb312")
|
logfile[(qtVer, "pbrew", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rhb34Phb313")
|
||||||
elif target == "pbrewHW":
|
elif target == "pbrewHW":
|
||||||
buildOp[(qtVer, "pbrewHW", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'HB311' ]
|
buildOp[(qtVer, "pbrewHW", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'HB311' ]
|
||||||
logfile[(qtVer, "pbrewHW", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311")
|
logfile[(qtVer, "pbrewHW", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311")
|
||||||
|
|
@ -177,11 +179,11 @@ def Get_QAT_Directory( targetDic, platform ):
|
||||||
dirQAT[(qtVer, "std", "r")] = '%sMP.build.macos-%s-release-RsysPsys.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "std", "r")] = '%sMP.build.macos-%s-release-RsysPsys.macQAT' % (qtType.lower(), platform)
|
||||||
dirQAT[(qtVer, "std", "d")] = '%sMP.build.macos-%s-debug-RsysPsys.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "std", "d")] = '%sMP.build.macos-%s-debug-RsysPsys.macQAT' % (qtType.lower(), platform)
|
||||||
elif target == "ports":
|
elif target == "ports":
|
||||||
dirQAT[(qtVer, "ports", "r")] = '%sMP.build.macos-%s-release-Rmp33Pmp312.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "ports", "r")] = '%sMP.build.macos-%s-release-Rmp34Pmp313.macQAT' % (qtType.lower(), platform)
|
||||||
dirQAT[(qtVer, "ports", "d")] = '%sMP.build.macos-%s-debug-Rmp33Pmp312.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "ports", "d")] = '%sMP.build.macos-%s-debug-Rmp34Pmp313.macQAT' % (qtType.lower(), platform)
|
||||||
elif target == "brew":
|
elif target == "brew":
|
||||||
dirQAT[(qtVer, "brew", "r")] = '%sBrew.build.macos-%s-release-Rhb34Phb312.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "brew", "r")] = '%sBrew.build.macos-%s-release-Rhb34Phb313.macQAT' % (qtType.lower(), platform)
|
||||||
dirQAT[(qtVer, "brew", "d")] = '%sBrew.build.macos-%s-debug-Rhb34Phb312.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "brew", "d")] = '%sBrew.build.macos-%s-debug-Rhb34Phb313.macQAT' % (qtType.lower(), platform)
|
||||||
elif target == "brewHW":
|
elif target == "brewHW":
|
||||||
dirQAT[(qtVer, "brewHW", "r")] = '%sBrew.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "brewHW", "r")] = '%sBrew.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform)
|
||||||
dirQAT[(qtVer, "brewHW", "d")] = '%sBrew.build.macos-%s-debug-RsysPhb311.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "brewHW", "d")] = '%sBrew.build.macos-%s-debug-RsysPhb311.macQAT' % (qtType.lower(), platform)
|
||||||
|
|
@ -195,8 +197,8 @@ def Get_QAT_Directory( targetDic, platform ):
|
||||||
dirQAT[(qtVer, "brewAHW", "r")] = '%sBrew.build.macos-%s-release-RsysPhbauto.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "brewAHW", "r")] = '%sBrew.build.macos-%s-release-RsysPhbauto.macQAT' % (qtType.lower(), platform)
|
||||||
dirQAT[(qtVer, "brewAHW", "d")] = '%sBrew.build.macos-%s-debug-RsysPhbauto.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "brewAHW", "d")] = '%sBrew.build.macos-%s-debug-RsysPhbauto.macQAT' % (qtType.lower(), platform)
|
||||||
elif target == "pbrew":
|
elif target == "pbrew":
|
||||||
dirQAT[(qtVer, "pbrew", "r")] = '%sMP.build.macos-%s-release-Rhb34Phb312.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "pbrew", "r")] = '%sMP.build.macos-%s-release-Rhb34Phb313.macQAT' % (qtType.lower(), platform)
|
||||||
dirQAT[(qtVer, "pbrew", "d")] = '%sMP.build.macos-%s-debug-Rhb34Phb312.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "pbrew", "d")] = '%sMP.build.macos-%s-debug-Rhb34Phb313.macQAT' % (qtType.lower(), platform)
|
||||||
elif target == "pbrewHW":
|
elif target == "pbrewHW":
|
||||||
dirQAT[(qtVer, "pbrewHW", "r")] = '%sMP.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "pbrewHW", "r")] = '%sMP.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform)
|
||||||
dirQAT[(qtVer, "pbrewHW", "d")] = '%sMP.build.macos-%s-debug-RsysPhb311.macQAT' % (qtType.lower(), platform)
|
dirQAT[(qtVer, "pbrewHW", "d")] = '%sMP.build.macos-%s-debug-RsysPhb311.macQAT' % (qtType.lower(), platform)
|
||||||
|
|
@ -235,14 +237,14 @@ def Get_Package_Options( targetDic, platform, srlDMG, makeflag ):
|
||||||
packOp[(qtVer, "std", "d")] = [ '-p', 'ST-%sMP.pkg.macos-%s-debug-RsysPsys' % (qtType.lower(), platform),
|
packOp[(qtVer, "std", "d")] = [ '-p', 'ST-%sMP.pkg.macos-%s-debug-RsysPsys' % (qtType.lower(), platform),
|
||||||
'-s', '%d' % srlDMG, '%s' % flag ]
|
'-s', '%d' % srlDMG, '%s' % flag ]
|
||||||
elif target == "ports":
|
elif target == "ports":
|
||||||
packOp[(qtVer, "ports", "r")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rmp33Pmp312' % (qtType.lower(), platform),
|
packOp[(qtVer, "ports", "r")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rmp34Pmp313' % (qtType.lower(), platform),
|
||||||
'-s', '%d' % srlDMG, '%s' % flag ]
|
'-s', '%d' % srlDMG, '%s' % flag ]
|
||||||
packOp[(qtVer, "ports", "d")] = [ '-p', 'LW-%sMP.pkg.macos-%s-debug-Rmp33Pmp312' % (qtType.lower(), platform),
|
packOp[(qtVer, "ports", "d")] = [ '-p', 'LW-%sMP.pkg.macos-%s-debug-Rmp34Pmp313' % (qtType.lower(), platform),
|
||||||
'-s', '%d' % srlDMG, '%s' % flag ]
|
'-s', '%d' % srlDMG, '%s' % flag ]
|
||||||
elif target == "brew":
|
elif target == "brew":
|
||||||
packOp[(qtVer, "brew", "r")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb34Phb312' % (qtType.lower(), platform),
|
packOp[(qtVer, "brew", "r")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb34Phb313' % (qtType.lower(), platform),
|
||||||
'-s', '%d' % srlDMG, '%s' % flag ]
|
'-s', '%d' % srlDMG, '%s' % flag ]
|
||||||
packOp[(qtVer, "brew", "d")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-debug-Rhb34Phb312' % (qtType.lower(), platform),
|
packOp[(qtVer, "brew", "d")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-debug-Rhb34Phb313' % (qtType.lower(), platform),
|
||||||
'-s', '%d' % srlDMG, '%s' % flag ]
|
'-s', '%d' % srlDMG, '%s' % flag ]
|
||||||
elif target == "brewHW":
|
elif target == "brewHW":
|
||||||
packOp[(qtVer, "brewHW", "r")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform),
|
packOp[(qtVer, "brewHW", "r")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform),
|
||||||
|
|
@ -265,9 +267,9 @@ def Get_Package_Options( targetDic, platform, srlDMG, makeflag ):
|
||||||
packOp[(qtVer, "brewAHW", "d")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-debug-RsysPhbauto' % (qtType.lower(), platform),
|
packOp[(qtVer, "brewAHW", "d")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-debug-RsysPhbauto' % (qtType.lower(), platform),
|
||||||
'-s', '%d' % srlDMG, '%s' % flag ]
|
'-s', '%d' % srlDMG, '%s' % flag ]
|
||||||
elif target == "pbrew":
|
elif target == "pbrew":
|
||||||
packOp[(qtVer, "pbrew", "r")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rhb34Phb312' % (qtType.lower(), platform),
|
packOp[(qtVer, "pbrew", "r")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rhb34Phb313' % (qtType.lower(), platform),
|
||||||
'-s', '%d' % srlDMG, '%s' % flag ]
|
'-s', '%d' % srlDMG, '%s' % flag ]
|
||||||
packOp[(qtVer, "pbrew", "d")] = [ '-p', 'LW-%sMP.pkg.macos-%s-debug-Rhb34Phb312' % (qtType.lower(), platform),
|
packOp[(qtVer, "pbrew", "d")] = [ '-p', 'LW-%sMP.pkg.macos-%s-debug-Rhb34Phb313' % (qtType.lower(), platform),
|
||||||
'-s', '%d' % srlDMG, '%s' % flag ]
|
'-s', '%d' % srlDMG, '%s' % flag ]
|
||||||
elif target == "pbrewHW":
|
elif target == "pbrewHW":
|
||||||
packOp[(qtVer, "pbrewHW", "r")] = [ '-p', 'HW-%sMP.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform),
|
packOp[(qtVer, "pbrewHW", "r")] = [ '-p', 'HW-%sMP.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform),
|
||||||
|
|
@ -285,6 +287,7 @@ def Parse_CommandLine_Arguments():
|
||||||
global Target # target list
|
global Target # target list
|
||||||
global QtTarget # list of (Qt, target, bdType)-tuple
|
global QtTarget # list of (Qt, target, bdType)-tuple
|
||||||
global Build # operation flag
|
global Build # operation flag
|
||||||
|
global Deploy # operation flag
|
||||||
global WithPymod # operation flag
|
global WithPymod # operation flag
|
||||||
global QATest # operation flag
|
global QATest # operation flag
|
||||||
global QACheck # operation flag
|
global QACheck # operation flag
|
||||||
|
|
@ -296,7 +299,7 @@ def Parse_CommandLine_Arguments():
|
||||||
global DryRun # True for dry-run
|
global DryRun # True for dry-run
|
||||||
|
|
||||||
platform = Test_My_Platform()
|
platform = Test_My_Platform()
|
||||||
if platform in [ "Sequoia", "Sonoma", "Ventura", "Monterey" ]:
|
if platform in [ "Tahoe", "Sequoia", "Sonoma", "Ventura", "Monterey" ]:
|
||||||
targetopt = "0,1,2,13,4"
|
targetopt = "0,1,2,13,4"
|
||||||
else:
|
else:
|
||||||
targetopt = ""
|
targetopt = ""
|
||||||
|
|
@ -320,10 +323,11 @@ def Parse_CommandLine_Arguments():
|
||||||
Usage += " + You can use this option multiple times. |\n"
|
Usage += " + You can use this option multiple times. |\n"
|
||||||
Usage += " + Or you can pass those list by the 'nightlyBuild.csv' file. |\n"
|
Usage += " + Or you can pass those list by the 'nightlyBuild.csv' file. |\n"
|
||||||
Usage += " A sample file 'macbuild/nightlyBuild.sample.csv' is available. |\n"
|
Usage += " A sample file 'macbuild/nightlyBuild.sample.csv' is available. |\n"
|
||||||
Usage += " [--build] : build and deploy | disabled\n"
|
Usage += " [--build] : build and deploy | disabled\n"
|
||||||
Usage += " [--pymod] : build and deploy Pymod, too (release build only) | disabled\n"
|
Usage += " [--deploy] : deploy only | disabled\n"
|
||||||
Usage += " [--test] : run the QA Test | disabled\n"
|
Usage += " [--pymod] : build and deploy Pymod, too (release build only) | disabled\n"
|
||||||
Usage += " [--check] : check the QA Test results | disabled\n"
|
Usage += " [--test] : run the QA Test | disabled\n"
|
||||||
|
Usage += " [--check] : check the QA Test results | disabled\n"
|
||||||
Usage += " [--makedmg|--cleandmg <srlno>] : make or clean DMGs | disabled\n"
|
Usage += " [--makedmg|--cleandmg <srlno>] : make or clean DMGs | disabled\n"
|
||||||
Usage += " [--upload <dropbox>] : upload DMGs to $HOME/Dropbox/klayout/<dropbox> | disabled\n"
|
Usage += " [--upload <dropbox>] : upload DMGs to $HOME/Dropbox/klayout/<dropbox> | disabled\n"
|
||||||
Usage += " [--dryrun] : dry-run for --build option | disabled\n"
|
Usage += " [--dryrun] : dry-run for --build option | disabled\n"
|
||||||
|
|
@ -363,6 +367,12 @@ def Parse_CommandLine_Arguments():
|
||||||
default=False,
|
default=False,
|
||||||
help='build and deploy' )
|
help='build and deploy' )
|
||||||
|
|
||||||
|
p.add_option( '--deploy',
|
||||||
|
action='store_true',
|
||||||
|
dest='deploy',
|
||||||
|
default=False,
|
||||||
|
help='deploy only' )
|
||||||
|
|
||||||
p.add_option( '--pymod',
|
p.add_option( '--pymod',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
dest='with_pymod',
|
dest='with_pymod',
|
||||||
|
|
@ -409,6 +419,7 @@ def Parse_CommandLine_Arguments():
|
||||||
targets = "%s" % targetopt,
|
targets = "%s" % targetopt,
|
||||||
qt_target = list(),
|
qt_target = list(),
|
||||||
build = False,
|
build = False,
|
||||||
|
deploy = False,
|
||||||
with_pymod = False,
|
with_pymod = False,
|
||||||
qa_test = False,
|
qa_test = False,
|
||||||
qa_check = False,
|
qa_check = False,
|
||||||
|
|
@ -421,24 +432,28 @@ def Parse_CommandLine_Arguments():
|
||||||
opt, args = p.parse_args()
|
opt, args = p.parse_args()
|
||||||
if opt.checkusage:
|
if opt.checkusage:
|
||||||
print(Usage)
|
print(Usage)
|
||||||
quit()
|
sys.exit(0)
|
||||||
|
|
||||||
myPlatform = Test_My_Platform( [ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia' ] )
|
myPlatform = Test_My_Platform( [ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia', 'Tahoe' ] )
|
||||||
if myPlatform == "":
|
if myPlatform == "":
|
||||||
print( "! Current platform is not [ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia' ]" )
|
print( "! Current platform is not [ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia', 'Tahoe' ]" )
|
||||||
print(Usage)
|
print(Usage)
|
||||||
quit()
|
sys.exit(0)
|
||||||
|
|
||||||
QtType = int(opt.qt_type)
|
QtType = int(opt.qt_type)
|
||||||
if not QtType in [5, 6]:
|
if not QtType in [5, 6]:
|
||||||
print( "! Invalid Qt type <%d>" % QtType )
|
print( "! Invalid Qt type <%d>" % QtType )
|
||||||
print(Usage)
|
print(Usage)
|
||||||
quit()
|
sys.exit(0)
|
||||||
|
|
||||||
targetIdx = list()
|
targetIdx = list()
|
||||||
for target in [ int(item) for item in opt.targets.split(",") ]:
|
raw = (opt.targets or "").strip()
|
||||||
if not target in targetIdx:
|
targets = [int(item) for item in raw.split(",") if item.strip() != ""]
|
||||||
targetIdx.append(target) # first appeared and non-duplicated index
|
print(targets)
|
||||||
|
if len(targets) != 0:
|
||||||
|
for target in targets:
|
||||||
|
if not target in targetIdx:
|
||||||
|
targetIdx.append(target) # first appeared and non-duplicated index
|
||||||
|
|
||||||
targetDic = Get_Build_Target_Dict()
|
targetDic = Get_Build_Target_Dict()
|
||||||
Target = list()
|
Target = list()
|
||||||
|
|
@ -462,14 +477,14 @@ def Parse_CommandLine_Arguments():
|
||||||
if len(df) == 0:
|
if len(df) == 0:
|
||||||
print( "! --qttarget==nightlyBuild.csv is used but DataFrame is empty" )
|
print( "! --qttarget==nightlyBuild.csv is used but DataFrame is empty" )
|
||||||
print(Usage)
|
print(Usage)
|
||||||
quit()
|
sys.exit(0)
|
||||||
for i in range(0, len(df)):
|
for i in range(0, len(df)):
|
||||||
qt = df.iloc[i,0]
|
qt = df.iloc[i,0]
|
||||||
idx = df.iloc[i,1]
|
idx = df.iloc[i,1]
|
||||||
bdType = df.iloc[i,2].lower()[0]
|
bdType = df.iloc[i,2].lower()[0]
|
||||||
if (qt == 5 and idx in [0,1,2,3,4,5,6,12,13] and bdType in ['r']) or \
|
if (qt == 5 and idx in [0,1,2,3,4,5,6,12,13] and bdType in ['r']) or \
|
||||||
(qt == 5 and idx in [0,1,2,3, 5,6,12,13] and bdType in ['d']) or \
|
(qt == 5 and idx in [0,1,2,3, 5,6,12,13] and bdType in ['d']) or \
|
||||||
(qt == 6 and idx in [0,1,2,3, 5,6,12,13] and bdType in ['r', 'd']):
|
(qt == 6 and idx in [0,1,2,3,4,5,6,12,13] and bdType in ['r', 'd']):
|
||||||
QtTarget.append( (qt, targetDic[idx], bdType) )
|
QtTarget.append( (qt, targetDic[idx], bdType) )
|
||||||
elif len(opt.qt_target) > 0:
|
elif len(opt.qt_target) > 0:
|
||||||
QtTarget = list()
|
QtTarget = list()
|
||||||
|
|
@ -480,7 +495,7 @@ def Parse_CommandLine_Arguments():
|
||||||
bdType = (item.split(",")[2]).lower()[0]
|
bdType = (item.split(",")[2]).lower()[0]
|
||||||
if (qt == 5 and idx in [0,1,2,3,4,5,6,12,13] and bdType in ['r']) or \
|
if (qt == 5 and idx in [0,1,2,3,4,5,6,12,13] and bdType in ['r']) or \
|
||||||
(qt == 5 and idx in [0,1,2,3, 5,6,12,13] and bdType in ['d']) or \
|
(qt == 5 and idx in [0,1,2,3, 5,6,12,13] and bdType in ['d']) or \
|
||||||
(qt == 6 and idx in [0,1,2,3, 5,6,12,13] and bdType in ['r', 'd']):
|
(qt == 6 and idx in [0,1,2,3,4,5,6,12,13] and bdType in ['r', 'd']):
|
||||||
QtTarget.append( (qt, targetDic[idx], bdType) )
|
QtTarget.append( (qt, targetDic[idx], bdType) )
|
||||||
else:
|
else:
|
||||||
withqttarget = False
|
withqttarget = False
|
||||||
|
|
@ -492,9 +507,10 @@ def Parse_CommandLine_Arguments():
|
||||||
else:
|
else:
|
||||||
print( "! --qttarget is used but there is no valid (Qt, target, bdTye)-tuple" )
|
print( "! --qttarget is used but there is no valid (Qt, target, bdTye)-tuple" )
|
||||||
print(Usage)
|
print(Usage)
|
||||||
quit()
|
sys.exit(0)
|
||||||
|
|
||||||
Build = opt.build
|
Build = opt.build
|
||||||
|
Deploy = opt.deploy
|
||||||
WithPymod = opt.with_pymod
|
WithPymod = opt.with_pymod
|
||||||
QATest = opt.qa_test
|
QATest = opt.qa_test
|
||||||
QACheck = opt.qa_check
|
QACheck = opt.qa_check
|
||||||
|
|
@ -514,27 +530,27 @@ def Parse_CommandLine_Arguments():
|
||||||
if MakeDMG and CleanDMG:
|
if MakeDMG and CleanDMG:
|
||||||
print( "! --makedmg and --cleandmg cannot be used simultaneously" )
|
print( "! --makedmg and --cleandmg cannot be used simultaneously" )
|
||||||
print(Usage)
|
print(Usage)
|
||||||
quit()
|
sys.exit(0)
|
||||||
|
|
||||||
if not opt.upload == "":
|
if not opt.upload == "":
|
||||||
Upload = True
|
Upload = True
|
||||||
Dropbox = opt.upload
|
Dropbox = opt.upload
|
||||||
|
|
||||||
if not (Build or QATest or QACheck or MakeDMG or CleanDMG or Upload):
|
if not (Build or Deploy or QATest or QACheck or MakeDMG or CleanDMG or Upload):
|
||||||
print( "! No action selected" )
|
print( "! No action selected" )
|
||||||
print(Usage)
|
print(Usage)
|
||||||
quit()
|
sys.exit(0)
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
## To build and deploy
|
## To build and deploy
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
def Build_Deploy():
|
def Build_Deploy( deployonly=False ):
|
||||||
pyBuilder = "./build4mac.py"
|
pyBuilder = "./build4mac.py"
|
||||||
myPlatform = Test_My_Platform()
|
myPlatform = Test_My_Platform()
|
||||||
buildOp, logfile = Get_Build_Options( Get_Build_Target_Dict(), myPlatform )
|
buildOp, logfile = Get_Build_Options( Get_Build_Target_Dict(), myPlatform )
|
||||||
|
|
||||||
for qttype, key, bdType in QtTarget:
|
for qttype, key, bdType in QtTarget:
|
||||||
if key == "ana3" and (qttype == 6 or bdType == 'd'): # anaconda3 does not provide Qt6 | debug_lib
|
if key == "ana3" and bdType == 'd': # anaconda3 does not provide debug_lib
|
||||||
continue
|
continue
|
||||||
|
|
||||||
deplog = logfile[(qttype, key, bdType)].replace( ".log", ".dep.log" )
|
deplog = logfile[(qttype, key, bdType)].replace( ".log", ".dep.log" )
|
||||||
|
|
@ -565,19 +581,20 @@ def Build_Deploy():
|
||||||
print( "" )
|
print( "" )
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if subprocess.call( command1, shell=False ) != 0:
|
if not deployonly:
|
||||||
print( "", file=sys.stderr )
|
if subprocess.call( command1, shell=False ) != 0:
|
||||||
print( "-----------------------------------------------------------------", file=sys.stderr )
|
print( "", file=sys.stderr )
|
||||||
print( "!!! <%s>: failed to build KLayout" % pyBuilder, file=sys.stderr )
|
print( "-----------------------------------------------------------------", file=sys.stderr )
|
||||||
print( "-----------------------------------------------------------------", file=sys.stderr )
|
print( "!!! <%s>: failed to build KLayout" % pyBuilder, file=sys.stderr )
|
||||||
print( "", file=sys.stderr )
|
print( "-----------------------------------------------------------------", file=sys.stderr )
|
||||||
sys.exit(1)
|
print( "", file=sys.stderr )
|
||||||
else:
|
sys.exit(1)
|
||||||
print( "", file=sys.stderr )
|
else:
|
||||||
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
|
print( "", file=sys.stderr )
|
||||||
print( "### <%s>: successfully built KLayout" % pyBuilder, file=sys.stderr )
|
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
|
||||||
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
|
print( "### <%s>: successfully built KLayout" % pyBuilder, file=sys.stderr )
|
||||||
print( "", file=sys.stderr )
|
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
|
||||||
|
print( "", file=sys.stderr )
|
||||||
|
|
||||||
if subprocess.call( command2, shell=True ) != 0:
|
if subprocess.call( command2, shell=True ) != 0:
|
||||||
print( "", file=sys.stderr )
|
print( "", file=sys.stderr )
|
||||||
|
|
@ -604,7 +621,7 @@ def Run_QATest( excludeList ):
|
||||||
dirQAT = Get_QAT_Directory( Get_Build_Target_Dict(), myPlatform )
|
dirQAT = Get_QAT_Directory( Get_Build_Target_Dict(), myPlatform )
|
||||||
|
|
||||||
for qttype, key, bdType in QtTarget:
|
for qttype, key, bdType in QtTarget:
|
||||||
if key == "ana3" and (qttype == 6 or bdType == 'd'): # anaconda3 does not provide Qt6 | debug_lib
|
if key == "ana3" and bdType == 'd': # anaconda3 does not provide debug_lib
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if key == "ana3":
|
if key == "ana3":
|
||||||
|
|
@ -645,11 +662,19 @@ def Check_QATest_Results( lines ):
|
||||||
dirQAT = Get_QAT_Directory( Get_Build_Target_Dict(), myPlatform )
|
dirQAT = Get_QAT_Directory( Get_Build_Target_Dict(), myPlatform )
|
||||||
|
|
||||||
for qttype, key, bdType in QtTarget:
|
for qttype, key, bdType in QtTarget:
|
||||||
if key == "ana3" and (qttype == 6 or bdType == 'd'): # anaconda3 does not provide Qt6 | debug_lib
|
if key == "ana3" and bdType == 'd': # anaconda3 does not provide debug_lib
|
||||||
continue
|
continue
|
||||||
|
|
||||||
os.chdir( dirQAT[(qttype, key, bdType)] )
|
os.chdir( dirQAT[(qttype, key, bdType)] )
|
||||||
logfile = glob.glob( "*.log" )
|
logfile = glob.glob( "*.log" )
|
||||||
|
|
||||||
|
if not logfile:
|
||||||
|
print( f"", file=sys.stderr )
|
||||||
|
print( f"[skip] No *.log files found in '{dirQAT[(qttype, key, bdType)]}'", file=sys.stderr )
|
||||||
|
print( f"", file=sys.stderr )
|
||||||
|
os.chdir("../")
|
||||||
|
continue
|
||||||
|
|
||||||
command1 = [ tailCommand ] + [ '-n', '%d' % lines ] + logfile
|
command1 = [ tailCommand ] + [ '-n', '%d' % lines ] + logfile
|
||||||
print( dirQAT[(qttype, key, bdType)], command1 )
|
print( dirQAT[(qttype, key, bdType)], command1 )
|
||||||
#continue
|
#continue
|
||||||
|
|
@ -686,7 +711,7 @@ def DMG_Make( srlDMG ):
|
||||||
os.mkdir( stashDMG )
|
os.mkdir( stashDMG )
|
||||||
|
|
||||||
for qttype, key, bdType in QtTarget:
|
for qttype, key, bdType in QtTarget:
|
||||||
if key == "ana3" and (qttype == 6 or bdType == 'd'): # anaconda3 does not provide Qt6 | debug_lib
|
if key == "ana3" and bdType == 'd': # anaconda3 does not provide debug_lib
|
||||||
continue
|
continue
|
||||||
|
|
||||||
command1 = [ pyDMGmaker ] + packOp[(qttype, key, bdType)]
|
command1 = [ pyDMGmaker ] + packOp[(qttype, key, bdType)]
|
||||||
|
|
@ -726,7 +751,7 @@ def DMG_Clean( srlDMG ):
|
||||||
shutil.rmtree( stashDMG )
|
shutil.rmtree( stashDMG )
|
||||||
|
|
||||||
for qttype, key, bdType in QtTarget:
|
for qttype, key, bdType in QtTarget:
|
||||||
if key == "ana3" and (qttype == 6 or bdType == 'd'): # anaconda3 does not provide Qt6 | debug_lib
|
if key == "ana3" and bdType == 'd': # anaconda3 does not provide debug_lib
|
||||||
continue
|
continue
|
||||||
|
|
||||||
command1 = [ pyDMGmaker ] + packOp[(qttype, key, bdType)]
|
command1 = [ pyDMGmaker ] + packOp[(qttype, key, bdType)]
|
||||||
|
|
@ -770,7 +795,10 @@ def Main():
|
||||||
Parse_CommandLine_Arguments()
|
Parse_CommandLine_Arguments()
|
||||||
|
|
||||||
if Build:
|
if Build:
|
||||||
Build_Deploy()
|
Build_Deploy(deployonly=False)
|
||||||
|
if Deploy:
|
||||||
|
Build_Deploy(deployonly=True)
|
||||||
|
sys.exit(0)
|
||||||
if QATest:
|
if QATest:
|
||||||
Run_QATest( [] ) # ex. ['pymod', 'pya']
|
Run_QATest( [] ) # ex. ['pymod', 'pya']
|
||||||
if QACheck:
|
if QACheck:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,295 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
File: "macbuild/pureARM64.py"
|
||||||
|
|
||||||
|
Author: ChatGPT + Kazzz-S
|
||||||
|
|
||||||
|
------------
|
||||||
|
|
||||||
|
Goal:
|
||||||
|
- Verify that Mach-O binaries inside a .app bundle (and optionally their external
|
||||||
|
dependencies) are *pure arm64* (no arm64e / no x86_64).
|
||||||
|
- Summarize results as OK / WARN / FAIL with an appropriate exit code.
|
||||||
|
|
||||||
|
Why:
|
||||||
|
- On macOS/Apple Silicon, mixing arm64e or x86_64 into a process that expects arm64
|
||||||
|
can trigger dyld / code-signing validation issues (e.g., SIGKILL: Invalid Page).
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- macOS with /usr/bin/file, /usr/bin/otool, /usr/bin/lipo available.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
$ python3 pureARM64.py "/Applications/klayout.app"
|
||||||
|
# Do not follow external dependencies:
|
||||||
|
$ python3 pureARM64.py "/Applications/klayout.app" --no-deps
|
||||||
|
# Limit which external paths are checked (default covers Homebrew/MacPorts/Conda):
|
||||||
|
$ python3 pureARM64.py "/Applications/klayout.app" \
|
||||||
|
--deps-filter '^/(opt/homebrew|opt/local|usr/local|Users/.*/(mambaforge|miniforge|anaconda3))'
|
||||||
|
# Also show whether each Mach-O has LC_CODE_SIGNATURE:
|
||||||
|
$ python3 pureARM64.py "/Applications/klayout.app" --show-code-sign
|
||||||
|
|
||||||
|
Exit codes:
|
||||||
|
0 = all OK (pure arm64)
|
||||||
|
1 = warnings present (fat mix includes arm64; investigate)
|
||||||
|
2 = failures present (no arm64 slice, or only arm64e/x86_64)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Default external dependency filter (you can customize this per project)
|
||||||
|
DEFAULT_DEPS_FILTER = r'^/(opt/homebrew|opt/local|usr/local|Users/.*/(mambaforge|miniforge|anaconda3))'
|
||||||
|
|
||||||
|
# File name patterns that are likely to be Mach-O payloads
|
||||||
|
MACHO_EXTS = ('.dylib', '.so', '.bundle')
|
||||||
|
|
||||||
|
# ANSI color if stdout is a TTY
|
||||||
|
COLOR = sys.stdout.isatty()
|
||||||
|
|
||||||
|
|
||||||
|
def color(text: str, code: str) -> str:
|
||||||
|
if not COLOR:
|
||||||
|
return text
|
||||||
|
return f'\033[{code}m{text}\033[0m'
|
||||||
|
|
||||||
|
|
||||||
|
OKC = lambda s: color(s, '32') # green
|
||||||
|
WRNC = lambda s: color(s, '33') # yellow
|
||||||
|
ERRC = lambda s: color(s, '31') # red
|
||||||
|
DIM = lambda s: color(s, '2') # dim
|
||||||
|
|
||||||
|
|
||||||
|
def run_cmd(cmd: list[str]) -> tuple[int, str]:
|
||||||
|
"""Run a command and capture stdout+stderr as text."""
|
||||||
|
try:
|
||||||
|
out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
|
||||||
|
return 0, out
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
return e.returncode, e.output
|
||||||
|
except FileNotFoundError:
|
||||||
|
return 127, f"command not found: {' '.join(map(shlex.quote, cmd))}"
|
||||||
|
|
||||||
|
|
||||||
|
def is_executable(path: str) -> bool:
|
||||||
|
"""Return True if file is executable by mode bit."""
|
||||||
|
try:
|
||||||
|
st = os.stat(path)
|
||||||
|
return bool(st.st_mode & 0o111)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def looks_like_macho(path: str) -> bool:
|
||||||
|
"""Heuristic: use 'file' to check if the payload is Mach-O."""
|
||||||
|
rc, out = run_cmd(["/usr/bin/file", path])
|
||||||
|
return rc == 0 and "Mach-O" in out
|
||||||
|
|
||||||
|
|
||||||
|
def list_bundle_machos(app_path: str) -> list[str]:
|
||||||
|
"""
|
||||||
|
Enumerate Mach-O candidates inside a .app:
|
||||||
|
- Contents/MacOS
|
||||||
|
- Contents/Frameworks
|
||||||
|
- Contents/PlugIns
|
||||||
|
- Contents/Buddy
|
||||||
|
Follow symlinks to real paths; deduplicate by real path.
|
||||||
|
"""
|
||||||
|
roots = [
|
||||||
|
os.path.join(app_path, "Contents", "MacOS"),
|
||||||
|
os.path.join(app_path, "Contents", "Frameworks"),
|
||||||
|
os.path.join(app_path, "Contents", "PlugIns"),
|
||||||
|
os.path.join(app_path, "Contents", "Buddy"),
|
||||||
|
]
|
||||||
|
seen: set[str] = set()
|
||||||
|
results: list[str] = []
|
||||||
|
for root in roots:
|
||||||
|
if not os.path.isdir(root):
|
||||||
|
continue
|
||||||
|
for dirpath, _dirnames, filenames in os.walk(root):
|
||||||
|
try:
|
||||||
|
real_dirpath = os.path.realpath(dirpath)
|
||||||
|
except OSError:
|
||||||
|
real_dirpath = dirpath
|
||||||
|
for name in filenames:
|
||||||
|
p = os.path.join(real_dirpath, name)
|
||||||
|
rp = os.path.realpath(p)
|
||||||
|
if rp in seen:
|
||||||
|
continue
|
||||||
|
is_macho = looks_like_macho(p)
|
||||||
|
# Include only: explicit Mach-O, typical Mach-O extensions (.dylib/.so/.bundle),
|
||||||
|
# or executables that are confirmed Mach-O.
|
||||||
|
if name.endswith(MACHO_EXTS) or is_macho or (is_executable(p) and is_macho):
|
||||||
|
seen.add(rp)
|
||||||
|
results.append(rp)
|
||||||
|
results.sort()
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def parse_archs_via_lipo(path: str) -> list[str]:
|
||||||
|
"""
|
||||||
|
Parse architecture slices using lipo/file.
|
||||||
|
Prefer lipo -info. Fallback to 'file' if necessary.
|
||||||
|
Returns a sorted unique list like ['arm64'] or ['arm64', 'arm64e'] etc.
|
||||||
|
"""
|
||||||
|
rc, out = run_cmd(["/usr/bin/lipo", "-info", path])
|
||||||
|
if rc == 0 and "Architectures in the fat file" in out:
|
||||||
|
# e.g. "Architectures in the fat file: QtGui are: arm64 arm64e"
|
||||||
|
m = re.search(r'are:\s+(.+)$', out.strip())
|
||||||
|
if m:
|
||||||
|
return sorted(set(m.group(1).split()))
|
||||||
|
if rc == 0 and "Non-fat file" in out:
|
||||||
|
# e.g. "Non-fat file: foo is architecture: arm64"
|
||||||
|
m = re.search(r'architecture:\s+(\S+)', out)
|
||||||
|
if m:
|
||||||
|
return [m.group(1)]
|
||||||
|
|
||||||
|
# Fallback to 'file'
|
||||||
|
rc2, out2 = run_cmd(["/usr/bin/file", path])
|
||||||
|
if rc2 == 0:
|
||||||
|
# e.g. "Mach-O 64-bit dynamically linked shared library arm64"
|
||||||
|
archs = re.findall(r'\b(arm64e?|x86_64)\b', out2)
|
||||||
|
if archs:
|
||||||
|
return sorted(set(archs))
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
_OT_LIB_RE = re.compile(r'^\s+(/.+?)\s+\(')
|
||||||
|
|
||||||
|
|
||||||
|
def deps_from_otool(path: str) -> list[str]:
|
||||||
|
"""
|
||||||
|
Extract absolute dependency paths from 'otool -L'.
|
||||||
|
Only returns absolute paths found in the listing.
|
||||||
|
"""
|
||||||
|
rc, out = run_cmd(["/usr/bin/otool", "-L", path])
|
||||||
|
deps: list[str] = []
|
||||||
|
if rc != 0:
|
||||||
|
return deps
|
||||||
|
for line in out.splitlines():
|
||||||
|
m = _OT_LIB_RE.match(line)
|
||||||
|
if m:
|
||||||
|
deps.append(m.group(1))
|
||||||
|
return deps
|
||||||
|
|
||||||
|
|
||||||
|
def classify_archs(archs: list[str]) -> tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Classify architecture set:
|
||||||
|
- OK : exactly {'arm64'}
|
||||||
|
- WARN : contains 'arm64' plus others (e.g., {'arm64', 'arm64e'})
|
||||||
|
- FAIL : does not contain 'arm64' (e.g., {'arm64e'} or {'x86_64'})
|
||||||
|
Returns (state, reason).
|
||||||
|
"""
|
||||||
|
s = set(archs)
|
||||||
|
if not s:
|
||||||
|
return "FAIL", "no-arch-detected"
|
||||||
|
if s == {"arm64"}:
|
||||||
|
return "OK", "pure-arm64"
|
||||||
|
if "arm64" in s and (("arm64e" in s) or ("x86_64" in s)):
|
||||||
|
return "WARN", "fat-mix:" + ",".join(sorted(s))
|
||||||
|
return "FAIL", "unsupported-arch:" + ",".join(sorted(s))
|
||||||
|
|
||||||
|
|
||||||
|
def has_code_signature(path: str) -> bool:
|
||||||
|
"""
|
||||||
|
Check whether Mach-O has LC_CODE_SIGNATURE.
|
||||||
|
Note: this indicates a code signature load command exists; it does NOT validate it.
|
||||||
|
"""
|
||||||
|
rc, out = run_cmd(["/usr/bin/otool", "-l", path])
|
||||||
|
return (rc == 0 and "LC_CODE_SIGNATURE" in out)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Check Mach-O binaries for pure arm64 (no arm64e/x86_64)."
|
||||||
|
)
|
||||||
|
parser.add_argument("target", help=".app path or a single Mach-O path")
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-deps", action="store_true",
|
||||||
|
help="Do not follow external dependencies (ignore 'otool -L')."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--deps-filter", default=DEFAULT_DEPS_FILTER,
|
||||||
|
help=f"Regex for which absolute dependency paths to include (default: {DEFAULT_DEPS_FILTER})"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--show-code-sign", action="store_true",
|
||||||
|
help="Also indicate whether LC_CODE_SIGNATURE exists (informational)."
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
target = os.path.abspath(args.target)
|
||||||
|
is_app = target.endswith(".app") and os.path.isdir(target)
|
||||||
|
|
||||||
|
# Collect Mach-O files in the bundle or single target
|
||||||
|
if is_app:
|
||||||
|
macho_files = list_bundle_machos(target)
|
||||||
|
if not macho_files:
|
||||||
|
print(ERRC("No Mach-O files found under .app bundle"), file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
else:
|
||||||
|
if not os.path.exists(target):
|
||||||
|
print(ERRC(f"Not found: {target}"), file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
macho_files = [target]
|
||||||
|
|
||||||
|
# Optionally add external dependencies filtered by regex
|
||||||
|
deps_pat = None if args.no_deps else re.compile(args.deps_filter)
|
||||||
|
all_targets: set[str] = set(macho_files)
|
||||||
|
if deps_pat:
|
||||||
|
for mf in list(macho_files):
|
||||||
|
for dep in deps_from_otool(mf):
|
||||||
|
if deps_pat.search(dep):
|
||||||
|
all_targets.add(dep)
|
||||||
|
|
||||||
|
# Header
|
||||||
|
print(DIM("# pureARM64: verify pure arm64 (detect arm64e/x86_64)"))
|
||||||
|
print(DIM(f"# target: {target}"))
|
||||||
|
print(DIM(f"# deps: {'OFF' if deps_pat is None else 'ON'}"))
|
||||||
|
if deps_pat is not None:
|
||||||
|
print(DIM(f"# deps-filter: {deps_pat.pattern}"))
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Evaluate each file
|
||||||
|
warn = 0
|
||||||
|
fail = 0
|
||||||
|
for p in sorted(all_targets):
|
||||||
|
archs = parse_archs_via_lipo(p)
|
||||||
|
state, reason = classify_archs(archs)
|
||||||
|
head = {"OK": OKC("[OK] "), "WARN": WRNC("[WARN] "), "FAIL": ERRC("[FAIL] ")}[state]
|
||||||
|
tail = f" ({reason})"
|
||||||
|
if args.show_code_sign:
|
||||||
|
tail += " " + (DIM("[csig]") if has_code_signature(p) else DIM("[no-sig]"))
|
||||||
|
arch_str = ",".join(archs) if archs else "?"
|
||||||
|
print(f"{head}{p}\n arch={arch_str}{tail}")
|
||||||
|
|
||||||
|
if state == "WARN":
|
||||||
|
warn += 1
|
||||||
|
elif state == "FAIL":
|
||||||
|
fail += 1
|
||||||
|
|
||||||
|
# Summary and exit code
|
||||||
|
total = len(all_targets)
|
||||||
|
ok = total - warn - fail
|
||||||
|
print()
|
||||||
|
print(f"Summary: {OKC('OK='+str(ok))}, {WRNC('WARN='+str(warn))}, {ERRC('FAIL='+str(fail))}")
|
||||||
|
|
||||||
|
if fail > 0:
|
||||||
|
return 2
|
||||||
|
if warn > 0:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
sys.exit(main())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(ERRC("\nInterrupted"), file=sys.stderr)
|
||||||
|
sys.exit(130)
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
# capnp needs C++ 14 in version 1.0.1
|
# capnp needs C++ 14 in version 1.0.1
|
||||||
# Qt6 comes with C++ 17 requirement.
|
# Qt6 comes with C++ 17 requirement.
|
||||||
equals(HAVE_QT, "0") || lessThan(QT_MAJOR_VERSION, 6) {
|
equals(HAVE_QT, "0") || lessThan(QT_MAJOR_VERSION, 6) {
|
||||||
|
# (1) and (2) are required by (macOS Sonoma) x (Qt5 MacPorts)
|
||||||
|
CONFIG -= c++11 # (1)
|
||||||
|
CONFIG += c++14 # (2)
|
||||||
QMAKE_CXXFLAGS += -std=c++14
|
QMAKE_CXXFLAGS += -std=c++14
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||