From 8b26347f0d5a8b26a0e984c759bc12bd27c6adca Mon Sep 17 00:00:00 2001 From: Kazunari Sekigawa Date: Mon, 1 Apr 2024 17:59:06 +0900 Subject: [PATCH 1/2] Updated the build system for MacOS to build KLayout 0.29.0 or later --- macbuild/ReadMe.md | 140 +++--- macbuild/Resources/script-bundle-A.zip | Bin 6550916 -> 6543255 bytes macbuild/Resources/script-bundle-B.zip | Bin 6547629 -> 6539894 bytes macbuild/Resources/script-bundle-H.zip | Bin 6544541 -> 6536945 bytes macbuild/Resources/script-bundle-P.zip | Bin 6599677 -> 6591939 bytes macbuild/Resources/script-bundle-S.zip | Bin 6574223 -> 6566322 bytes macbuild/build4mac.py | 609 ++++++++++++++++++------- macbuild/build4mac_env.py | 64 ++- macbuild/build4mac_util.py | 92 +++- macbuild/macQAT.py | 84 ++-- macbuild/makeDMG4mac.py | 98 ++-- macbuild/nightlyBuild.py | 72 +-- macbuild/nightlyBuild.sample.csv | 9 +- macbuild/python3HB.py | 8 +- 14 files changed, 823 insertions(+), 353 deletions(-) diff --git a/macbuild/ReadMe.md b/macbuild/ReadMe.md index cfd8fe3c4f..4a8da914d7 100644 --- a/macbuild/ReadMe.md +++ b/macbuild/ReadMe.md @@ -1,9 +1,9 @@ -Relevant KLayout version: 0.28.17
+Relevant KLayout version: 0.29.0
Author: Kazzz-S
-Last modified: 2024-02-16
+Last modified: 2024-03-27
# 1. Introduction -This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.28.17 or later for different 64-bit macOS, including: +This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.29.0 or later for different 64-bit macOS, including: * Monterey (12.x) : the primary development environment * Ventura (13.x) : experimental * Sonoma (14.x) : -- ditto -- @@ -22,14 +22,14 @@ All Apple (M1|M2|M3) chips are still untested, as the author does not own an (M1 # 2. Qt Frameworks -The default Qt framework is "Qt5" from Homebrew (https://brew.sh/), which is usually located under: +The default Qt framework is "Qt5" from **MacPorts** (https://www.macports.org/), which is usually located under: ``` -/usr/local/opt/qt@5/ +/opt/local/libexec/qt5/ ``` -If you prefer **MacPorts** (https://www.macports.org/), "Qt5" is usually located under: +If you prefer "Qt6" from **Homebrew** (https://brew.sh/), which is usually located under: ``` -/opt/local/libexec/qt5/ +/usr/local/opt/qt@6/ ``` You can also choose "Qt5" from Anaconda3 (https://www.anaconda.com/), which is usually located under: @@ -41,11 +41,12 @@ If you have installed Anaconda3 under $HOME/opt/anaconda3/, make a symbolic link /Applications/anaconda3/ ---> $HOME/opt/anaconda3/ ``` -The migration work to "Qt6" is ongoing. You can try to use it; however, you will encounter some build and runtime errors. +The migration work to "Qt6" is ongoing. You can try to use it; however, you will encounter some build and runtime errors.
+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). # 3. Script language support: Ruby and Python -The build script **`build4mac.py`** provides several possible combinations of Qt5, Ruby, and Python modules to suit the user's needs and preferences.
+The build script **`build4mac.py`** provides several possible combinations of Qt, Ruby, and Python modules to suit the user's needs and preferences.
Some typical use cases are described in Section 6. # 4. Prerequisites @@ -53,8 +54,8 @@ You need to have the followings: * The latest Xcode and command-line tool kit compliant with each OS * https://developer.apple.com/xcode/resources/ * https://mac.install.guide/commandlinetools/4 -* Qt5 package from Homebrew, MacPorts, or Anaconda3 -* Optionally, Ruby and Python packages from Homebrew, MacPorts, or Anaconda3 +* Qt5 package from MacPorts or Anaconda3. Qt6, from Homebrew. +* Optionally, Ruby and Python packages from MacPorts, Homebrew, or Anaconda3 #### For matching versions of Ruby and Python, please also refer to `build4mac_env.py`. # 5. Command-line options of **`build4mac.py`** @@ -65,7 +66,7 @@ The operating system type is detected automatically. ``` --------------------------------------------------------------------------------------------------------- << Usage of 'build4mac.py' >> - for building KLayout 0.28.17 or later on different Apple macOS platforms. + for building KLayout 0.29.0 or later on different Apple macOS platforms. $ [python] ./build4mac.py option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value @@ -121,7 +122,7 @@ $ [python] ./build4mac.py In this section, the actual file and directory names are those obtained on macOS Monterey.
On different OS, those names differ accordingly. -### 6A. Standard build using the OS-bundled Ruby and Python with MacPorts Qt +### 6A. Standard build using the OS-bundled Ruby and Python with MacPorts Qt5 0. Install MacPorts, then install Qt5 and libgit2 by ``` $ sudo port install coreutils @@ -155,41 +156,45 @@ $ ./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. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. -### 6B. Fully Homebrew-flavored build with Homebrew Ruby 3.3 and Homebrew Python 3.11 -0. Install Homebrew, then install Qt5, Ruby 3.3, Python 3.11, and libgit2 by +### 6B. Fully MacPorts-flavored build with MacPorts Ruby 3.3 and MacPorts Python 3.11 +0. Install MacPorts, then install Qt5, Ruby 3.3, Python 3.11, and libgit2 by ``` -$ brew install qt@5 -$ brew install ruby@3.3 -$ brew install python@3.11 -$ brew install libgit2 -$ cd /where/'build.sh'/exists -$ cd macbuild -$ ./python3HB.py -v 3.11 +$ sudo port install coreutils +$ sudo port install findutils +$ sudo port install qt5 +$ sudo port install ruby33 +$ sudo port install python311 +$ sudo port install py311-pip +$ sudo port install libgit2 ``` 1. Invoke **`build4mac.py`** with the following options: ``` $ cd /where/'build.sh'/exists -$ ./build4mac.py -q qt5brew -r hb33 -p hb311 +$ ./build4mac.py -q qt5macports -r mp33 -p mp311 ``` 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.
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.
- If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.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 qt5brew -r hb33 -p hb311 -Y +$ ./build4mac.py -q qt5macports -r mp33 -p mp311 -Y ``` The application bundle **`klayout.app`** is located under:
- **`LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311`** directory, where + **`LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311`** directory, where * "LW-" means this is a lightweight package. -* "qt5Brew" means that Qt5 from Homebrew is used. -* "Rhb33Phb311" means that Ruby is 3.3 from Homebrew; Python is 3.11 from Homebrew. +* "qt5MP" means that Qt5 from MacPorts is used. +* "Rmp33Pmp311" means that Ruby is 3.3 from MacPorts; Python is 3.11 from MacPorts. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. -### 6C. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.11 -0. Install Homebrew, then install Qt5, Python 3.11, and libgit2 by +### 6C. Fully Homebrew-flavored build with Homebrew Ruby 3.3 and Homebrew Python 3.11 +> [!IMPORTANT] +> To build KLayout >= 0.29.0, you need "Qt6" to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599). + +0. Install Homebrew, then install Qt6, Ruby 3.3, Python 3.11, and libgit2 by ``` -$ brew install qt@5 +$ brew install qt@6 +$ brew install ruby@3.3 $ brew install python@3.11 $ brew install libgit2 $ cd /where/'build.sh'/exists @@ -199,55 +204,62 @@ $ ./python3HB.py -v 3.11 1. Invoke **`build4mac.py`** with the following options: ``` $ cd /where/'build.sh'/exists -$ ./build4mac.py -q qt5brew -r sys -p hb311 +$ ./build4mac.py -q qt6brew -r hb33 -p hb311 ``` 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 (including Qt and Python frameworks) under the **`klayout.app`** bundle.
- The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step. +3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.
+ The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.
+ 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 qt5brew -r sys -p hb311 -y +$ ./build4mac.py -q qt6brew -r hb33 -p hb311 -Y ``` The application bundle **`klayout.app`** is located under:
- **`HW-qt5Brew.pkg.macos-Monterey-release-RsysPhb311`** directory, where -* "HW-" means this is a heavyweight package because both Qt5 and Python Frameworks are deployed. -* "qt5Brew" means that Qt5 from Homebrew is used. -* "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew. + **`LW-qt6Brew.pkg.macos-Monterey-release-Rhb33Phb311`** directory, where +* "LW-" means this is a lightweight package. +* "qt6Brew" means that Qt6 from Homebrew is used. +* "Rhb33Phb311" means that Ruby is 3.3 from Homebrew; Python is 3.11 from Homebrew. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. -### Important -So far, the deployment of Homebrew Ruby is not supported.
-Therefore, if you intend to use the "-y" option for deployment, you need to use the "-r sys" option for building. -### 6D. Fully MacPorts-flavored build with MacPorts Ruby 3.3 and MacPorts Python 3.11 -0. Install MacPorts, then install Qt5, Ruby 3.3, Python 3.11, and libgit2 by +### 6D. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.11 +> [!IMPORTANT] +> To build KLayout >= 0.29.0, you need "Qt6" to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599). + +> [!CAUTION] +> Homebrew Qt6.6.2 seems buggy. More precisely, it does not perfectly deploy the required Qt frameworks. +> If you plan to distribute an HW*.dmg package, consider using Qt6.4.x from MacPorts. +> That is, substitute `qt6brew` with `qt6macports` in the following command lines. + +0. Install Homebrew, then install Qt6, Python 3.11, and libgit2 by ``` -$ sudo port install coreutils -$ sudo port install findutils -$ sudo port install qt5 -$ sudo port install ruby33 -$ sudo port install python311 -$ sudo port install py311-pip -$ sudo port install libgit2 +$ brew install qt@6 +$ brew install python@3.11 +$ brew install libgit2 +$ cd /where/'build.sh'/exists +$ cd macbuild +$ ./python3HB.py -v 3.11 ``` 1. Invoke **`build4mac.py`** with the following options: ``` $ cd /where/'build.sh'/exists -$ ./build4mac.py -q qt5macports -r mp33 -p mp311 +$ ./build4mac.py -q qt6brew -r sys -p hb311 ``` 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.
- The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.
- If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**. +3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-y" to deploy executables and libraries (including Qt and Python frameworks) under the **`klayout.app`** bundle.
+ The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step. ``` -$ ./build4mac.py -q qt5macports -r mp33 -p mp311 -Y +$ ./build4mac.py -q qt6brew -r sys -p hb311 -y ``` The application bundle **`klayout.app`** is located under:
- **`LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311`** directory, where -* "LW-" means this is a lightweight package. -* "qt5MP" means that Qt5 from MacPorts is used. -* "Rmp33Pmp311" means that Ruby is 3.3 from MacPorts; Python is 3.11 from MacPorts. + **`HW-qt6Brew.pkg.macos-Monterey-release-RsysPhb311`** directory, where +* "HW-" means this is a heavyweight package because both Qt6 and Python Frameworks are deployed. +* "qt6Brew" means that Qt6 from Homebrew is used. +* "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. +> [!IMPORTANT] +> So far, the deployment of Homebrew Ruby is not supported.
+> Therefore, if you intend to use the "-y" option for deployment, you need to use the "-r sys" option for building. ### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 3.2 and Anaconda3 Python 3.11 0. Install Anaconda3 (Anaconda3-2023.09-0-MacOSX-x86_64.pkg), then install Ruby 3.2 and libgit2 by @@ -264,7 +276,7 @@ $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 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.
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.
- If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.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 @@ -292,7 +304,7 @@ $ export KLAYOUT_GIT_HTTP_PROXY="http://111.222.333.444:5678" Ask your system administrator for the actual IP address and port number of your proxy server. It is highly recommended that this setting is included in a launching service script bundle.
-A sample content (`*.app.Bash`) of the script bundle can be found in `Resources/script-bundle-[A|B|H|P].zip`. +A sample content (`*.app.Bash`) of the script bundle can be found in `Resources/script-bundle-[A|B|H|P|S].zip`. ---- @@ -310,8 +322,8 @@ $ cd /where/'build.sh'/exists $ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311 -m ``` This command will generate the two files below:
-* **`LW-klayout-0.28.17-macOS-Monterey-1-qt5MP-Rmp33Pmp311.dmg`** ---(1) the main DMG file -* **`LW-klayout-0.28.17-macOS-Monterey-1-qt5MP-Rmp33Pmp311.dmg.md5`** ---(2) MD5-value text file +* **`LW-klayout-0.29.0-macOS-Monterey-1-qt5MP-Rmp33Pmp311.dmg`** ---(1) the main DMG file +* **`LW-klayout-0.29.0-macOS-Monterey-1-qt5MP-Rmp33Pmp311.dmg.md5`** ---(2) MD5-value text file # 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.
diff --git a/macbuild/Resources/script-bundle-A.zip b/macbuild/Resources/script-bundle-A.zip index 3f7e538fca73bac909255dc66cafc6432845f3db..89409aa7f253fb8ceb2af1b720bd0e7c5fa1f8e8 100644 GIT binary patch delta 10803 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 diff --git a/macbuild/Resources/script-bundle-B.zip b/macbuild/Resources/script-bundle-B.zip index 9855a5f57ccc99cf237b4ca11c5b1aa0cb617413..95870471e10ce6be4cf754f88ad82d8ef70aacbc 100644 GIT binary patch delta 13055 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 F{{!}me3bwI delta 20599 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 ARR910 diff --git a/macbuild/Resources/script-bundle-H.zip b/macbuild/Resources/script-bundle-H.zip index f3d73ffb4140b8793bf82dc33e482e31f0961d65..4db6f545d2cae9a66d750f1db6036f3185315b35 100644 GIT binary patch delta 11141 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 P!G9J2;houog8%zJPIqiD delta 18747 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 Z5?~x0C;(d+!(!k+0~ZX-n}@^y{U3dU_t^jd delta 18944 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 diff --git a/macbuild/Resources/script-bundle-S.zip b/macbuild/Resources/script-bundle-S.zip index 164a970ba2aa0f1b383a5a8be4df47adca493639..ad3ac4b17798d23896569fa5f9600afa7e3364f7 100644 GIT binary patch delta 11245 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 J>B`}s{tqsMTkrq? delta 19175 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 diff --git a/macbuild/build4mac.py b/macbuild/build4mac.py index de2204b040..f0ce5ea26e 100755 --- a/macbuild/build4mac.py +++ b/macbuild/build4mac.py @@ -5,7 +5,7 @@ # File: "macbuild/build4mac.py" # # The top Python script for building KLayout (http://www.klayout.de/index.php) -# version 0.28.17 or later on different Apple Mac OSX platforms. +# version 0.29.0 or later on different Apple Mac OSX platforms. #=============================================================================== import sys import os @@ -45,7 +45,7 @@ def GenerateUsage(platform): usage = "\n" usage += "---------------------------------------------------------------------------------------------------------\n" usage += "<< Usage of 'build4mac.py' >>\n" - usage += " for building KLayout 0.28.17 or later on different Apple macOS platforms.\n" + usage += " for building KLayout 0.29.0 or later on different Apple macOS platforms.\n" usage += "\n" usage += "$ [python] ./build4mac.py\n" usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value\n" @@ -100,7 +100,7 @@ def GenerateUsage(platform): #------------------------------------------------------------------------------- ## To get the default configurations # -# @return a dictionary containing the default configuration for the macOS build +# @return a dictionary containing the default configurations for the macOS build #------------------------------------------------------------------------------- def Get_Default_Config(): ProjectDir = os.getcwd() @@ -166,7 +166,7 @@ def Get_Default_Config(): ModuleRuby = "nil" ModulePython = "nil" - BuildPymod = False + BuildPymodWhl = False NonOSStdLang = False NoQtBindings = False NoQtUiTools = False @@ -181,6 +181,8 @@ def Get_Default_Config(): Version = GetKLayoutVersionFrom( "./version.sh" ) HBPythonIs39 = False # because ModulePython == "Python311Brew" by default OSPython3FW = None # system Python3 frameworks in [ None, MontereyPy3FW, VenturaPy3FW, SonomaPy3FW ] + EmbedQt = False + EmbedPython3 = False config = dict() config['ProjectDir'] = ProjectDir # project directory where "build.sh" exists @@ -190,7 +192,7 @@ def Get_Default_Config(): config['ModuleQt'] = ModuleQt # Qt module to be used config['ModuleRuby'] = ModuleRuby # Ruby module to be used config['ModulePython'] = ModulePython # Python module to be used - config['BuildPymod'] = BuildPymod # True to build and deploy "Pymod" + config['BuildPymodWhl'] = BuildPymodWhl # True to build and deploy "Pymod (*.whl)" config['NonOSStdLang'] = NonOSStdLang # True if non-OS-standard language is chosen config['NoQtBindings'] = NoQtBindings # True if not creating Qt bindings for Ruby scripts config['NoQtUiTools'] = NoQtUiTools # True if not to include QtUiTools in Qt binding @@ -207,6 +209,8 @@ def Get_Default_Config(): config['ToolDebug'] = ToolDebug # debug level list for this tool config['HBPythonIs39'] = HBPythonIs39 # True if the Homebrew Python version <= 3.9 config['OSPython3FW'] = OSPython3FW # system Python3 frameworks in [ None, MontereyPy3FW, VenturaPy3FW, SonomaPy3FW ] + config['EmbedQt'] = EmbedQt # True if Qt is embedded + config['EmbedPython3'] = EmbedPython3 # True if Python3 is embedded # auxiliary variables on platform config['System'] = System # 6-tuple from platform.uname() config['Node'] = Node # - do - @@ -234,7 +238,7 @@ def Parse_CLI_Args(config): ModuleQt = config['ModuleQt'] ModuleRuby = config['ModuleRuby'] ModulePython = config['ModulePython'] - BuildPymod = config['BuildPymod'] + BuildPymodWhl = config['BuildPymodWhl'] NonOSStdLang = config['NonOSStdLang'] NoQtBindings = config['NoQtBindings'] NoQtUiTools = config['NoQtUiTools'] @@ -250,6 +254,8 @@ def Parse_CLI_Args(config): ToolDebug = config['ToolDebug'] HBPythonIs39 = config['HBPythonIs39'] OSPython3FW = config['OSPython3FW'] + EmbedQt = config['EmbedQt'] + EmbedPython3 = config['EmbedPython3'] #----------------------------------------------------- # [2] Parse the CLI arguments @@ -269,9 +275,9 @@ def Parse_CLI_Args(config): p.add_option( '-P', '--buildPymod', action='store_true', - dest='build_pymod', + dest='build_pymod_whl', default=False, - help="build and deploy (disabled)" ) + help="build and deploy Pymod (*.whl) (disabled)" ) p.add_option( '-n', '--noqtbinding', action='store_true', @@ -335,21 +341,21 @@ def Parse_CLI_Args(config): help='check usage' ) if Platform.upper() in [ "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ] - p.set_defaults( type_qt = "qt5macports", - type_ruby = "sys", - type_python = "sys", - build_pymod = False, - no_qt_binding = False, - no_qt_uitools = False, - no_libgit2 = False, - make_option = "--jobs=4", - debug_build = False, - check_command = False, - deploy_full = False, - deploy_partial = False, - deploy_verbose = "1", - tool_debug = [], - checkusage = False ) + p.set_defaults( type_qt = "qt5macports", + type_ruby = "sys", + type_python = "sys", + build_pymod_whl = False, + no_qt_binding = False, + no_qt_uitools = False, + no_libgit2 = False, + make_option = "--jobs=4", + debug_build = False, + check_command = False, + deploy_full = False, + deploy_partial = False, + deploy_verbose = "1", + tool_debug = [], + checkusage = False ) else: raise Exception( "! Too obsolete platform <%s>" % Platform ) @@ -504,16 +510,16 @@ def Parse_CLI_Args(config): ModuleSet = ( choiceQt56, choiceRuby, choicePython ) # (E) Set other parameters - BuildPymod = opt.build_pymod - NoQtBindings = opt.no_qt_binding - NoQtUiTools = opt.no_qt_uitools - NoLibGit2 = opt.no_libgit2 - MakeOptions = opt.make_option - DebugMode = opt.debug_build - CheckComOnly = opt.check_command - DeploymentF = opt.deploy_full - DeploymentP = opt.deploy_partial - ToolDebug = sorted( set([ int(val) for val in opt.tool_debug ]) ) + BuildPymodWhl = opt.build_pymod_whl + NoQtBindings = opt.no_qt_binding + NoQtUiTools = opt.no_qt_uitools + NoLibGit2 = opt.no_libgit2 + MakeOptions = opt.make_option + DebugMode = opt.debug_build + CheckComOnly = opt.check_command + DeploymentF = opt.deploy_full + DeploymentP = opt.deploy_partial + ToolDebug = sorted( set([ int(val) for val in opt.tool_debug ]) ) if DeploymentF and DeploymentP: print("") @@ -528,31 +534,42 @@ def Parse_CLI_Args(config): print(Usage) sys.exit(1) + # (F) Build mode if not DeploymentF and not DeploymentP: target = "%s %s %s" % (Platform, Release, Machine) modules = "Qt=%s, Ruby=%s, Python=%s" % (ModuleQt, ModuleRuby, ModulePython) - if BuildPymod: - pymodbuild = "enabled" + if BuildPymodWhl: + pymodWhlbuild = "enabled" else: - pymodbuild = "disabled" + pymodWhlbuild = "disabled" message = "### You are going to build KLayout\n for <%s>\n with <%s>\n with Pymod <%s>...\n" print("") - print( message % (target, modules, pymodbuild) ) + print( message % (target, modules, pymodWhlbuild) ) + # (G) Deploy mode else: - okHWdmg = True - message = "### You are going to make " + EmbedQt = False + EmbedPython3 = False + okHWdmg = True + message = "### You are going to make " if DeploymentP: PackagePrefix = "LW-" - if not BuildPymod: - message += "a lightweight (LW-) package excluding Qt5, Ruby, and Python..." + if not BuildPymodWhl: + message += "a lightweight (LW-) package excluding Qt[5|6], Ruby, and Python..." else: - message += "a lightweight (LW-) package with Pymod excluding Qt5, Ruby, and Python..." + message += "a lightweight (LW-) package with Pymod (*.whl) excluding Qt[5|6], Ruby, and Python..." elif DeploymentF: - if (ModuleRuby in RubySys) and (ModulePython in PythonSys): + if ModuleQt == "Qt5Ana3": + EmbedQt = False + message += "Qt5 from Anaconda3 embedded, which is not allowed!" + okHWdmg = False + elif (ModuleRuby in RubySys) and (ModulePython in PythonSys): PackagePrefix = "ST-" + EmbedQt = True message += "a standard (ST-) package including Qt[5|6] and using OS-bundled Ruby and Python..." elif ModulePython in ['Python311Brew', 'Python39Brew', 'PythonAutoBrew']: PackagePrefix = "HW-" + EmbedQt = True + EmbedPython3 = True message += "a heavyweight (HW-) package including Qt[5|6] and Python3.[11|9] from Homebrew..." okHWdmg = (ModulePython == 'Python311Brew') or \ (ModulePython == 'Python39Brew') or \ @@ -564,7 +581,9 @@ def Parse_CLI_Args(config): print( message ) print( "" ) if not okHWdmg: - print( "!!! HW-dmg package assumes either python@3.11 or python@3.9" ) + print( "!!! HW-dmg package assumes the two conditions:" ) + print( " (1) either Qt5 or Qt6 from MacPorts or Homebrew (Anaconda3 is not a candidate)" ) + print( " (2) either python@3.11 or python@3.9 from Homebrew" ) sys.exit(1) if CheckComOnly: sys.exit(0) @@ -577,7 +596,7 @@ def Parse_CLI_Args(config): config['ModuleQt'] = ModuleQt config['ModuleRuby'] = ModuleRuby config['ModulePython'] = ModulePython - config['BuildPymod'] = BuildPymod + config['BuildPymodWhl'] = BuildPymodWhl config['NonOSStdLang'] = NonOSStdLang config['NoQtBindings'] = NoQtBindings config['NoQtUiTools'] = NoQtUiTools @@ -593,11 +612,13 @@ def Parse_CLI_Args(config): config['ToolDebug'] = ToolDebug config['HBPythonIs39'] = HBPythonIs39 config['OSPython3FW'] = OSPython3FW + config['EmbedQt'] = EmbedQt + config['EmbedPython3'] = EmbedPython3 if CheckComOnly: pp = pprint.PrettyPrinter( indent=4, width=140 ) parameters = Get_Build_Parameters(config) - Build_pymod(parameters) + Build_pymod_wheel(parameters) pp.pprint(parameters) sys.exit(0) else: @@ -620,7 +641,7 @@ def Get_Build_Parameters(config): ModuleQt = config['ModuleQt'] ModuleRuby = config['ModuleRuby'] ModulePython = config['ModulePython'] - BuildPymod = config['BuildPymod'] + BuildPymodWhl = config['BuildPymodWhl'] ModuleSet = config['ModuleSet'] NoQtBindings = config['NoQtBindings'] NoQtUiTools = config['NoQtUiTools'] @@ -657,39 +678,28 @@ def Get_Build_Parameters(config): MacBuildDir = "%s.build.macos-%s-%s-%s" % ( qt, Platform, mode, ruby_python) MacBuildLog = "%s.build.macos-%s-%s-%s.log" % ( qt, Platform, mode, ruby_python) MacBuildDirQAT = MacBuildDir + ".macQAT" + parameters['bin'] = MacBinDir + parameters['build'] = MacBuildDir parameters['logfile'] = MacBuildLog - # (D) Qt5|6 - if ModuleQt == 'Qt5MacPorts': - parameters['qmake'] = Qt5MacPorts['qmake'] - parameters['deploy_tool'] = Qt5MacPorts['deploy'] - elif ModuleQt == 'Qt5Brew': - parameters['qmake'] = Qt5Brew['qmake'] - parameters['deploy_tool'] = Qt5Brew['deploy'] - elif ModuleQt == 'Qt5Ana3': - parameters['qmake'] = Qt5Ana3['qmake'] - parameters['deploy_tool'] = Qt5Ana3['deploy'] - elif ModuleQt == 'Qt6MacPorts': - parameters['qmake'] = Qt6MacPorts['qmake'] - parameters['deploy_tool'] = Qt6MacPorts['deploy'] - elif ModuleQt == 'Qt6Brew': - parameters['qmake'] = Qt6Brew['qmake'] - parameters['deploy_tool'] = Qt6Brew['deploy'] - - parameters['bin'] = MacBinDir - parameters['build'] = MacBuildDir + # (D) about Qt[5|6] + parameters['qmake'] = Qt56Dictionary[ModuleQt]['qmake'] + parameters['deploy_tool'] = Qt56Dictionary[ModuleQt]['deploy'] + parameters['qt_lib_root'] = Qt56Dictionary[ModuleQt]['libdir'] + + # (E) rpath if OSPython3FW in [ MontereyPy3FW, VenturaPy3FW, SonomaPy3FW ]: parameters['rpath'] = OSPython3FW else: parameters['rpath'] = "@executable_path/../Frameworks" - # (E) want Qt bindings with Ruby scripts? + # (F) want Qt bindings with Ruby scripts? parameters['no_qt_bindings'] = NoQtBindings - # (F) want QtUiTools? + # (G) want QtUiTools? parameters['no_qt_uitools'] = NoQtUiTools - # (G) options to `make` tool + # (H) options to `make` tool if not MakeOptions == "": parameters['make_options'] = MakeOptions try: @@ -700,7 +710,7 @@ def Get_Build_Parameters(config): else: parameters['num_parallel'] = pnum - # (H) about Ruby + # (I) about Ruby if ModuleRuby != "nil": parameters['ruby'] = RubyDictionary[ModuleRuby]['exe'] parameters['rbinc'] = RubyDictionary[ModuleRuby]['inc'] @@ -708,7 +718,7 @@ def Get_Build_Parameters(config): if 'inc2' in RubyDictionary[ModuleRuby]: parameters['rbinc2'] = RubyDictionary[ModuleRuby]['inc2'] - # (I) about Python + # (J) about Python if ModulePython != "nil": parameters['python'] = PythonDictionary[ModulePython]['exe'] parameters['pyinc'] = PythonDictionary[ModulePython]['inc'] @@ -720,21 +730,21 @@ def Get_Build_Parameters(config): config['MacBuildDirQAT'] = MacBuildDirQAT # relative path to build directory for QATest config['MacBuildLog'] = MacBuildLog # relative path to build log file - # (J) Extra parameters needed for deployment + # (K) Extra parameters needed for deployment parameters['project_dir'] = ProjectDir - # (K) Extra parameters needed for + # (L) Extra parameters needed for # will be built if: - # BuildPymod = True - # Platform = [ 'Sonoma', 'Ventura', 'Monterey'] - # ModuleRuby = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ] - # ModulePython = [ 'Python311MacPorts', 'Python39MacPorts', - # 'Python311Brew', Python39Brew', 'PythonAutoBrew', - # 'PythonAnaconda3' ] - parameters['BuildPymod'] = BuildPymod - parameters['Platform'] = Platform - parameters['ModuleRuby'] = ModuleRuby - parameters['ModulePython'] = ModulePython + # BuildPymodWhl = True + # Platform = [ 'Sonoma', 'Ventura', 'Monterey'] + # ModuleRuby = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ] + # ModulePython = [ 'Python311MacPorts', 'Python39MacPorts', + # 'Python311Brew', Python39Brew', 'PythonAutoBrew', + # 'PythonAnaconda3' ] + parameters['BuildPymodWhl'] = BuildPymodWhl + parameters['Platform'] = Platform + parameters['ModuleRuby'] = ModuleRuby + parameters['ModulePython'] = ModulePython PymodDistDir = dict() if Platform in [ 'Sonoma', 'Ventura', 'Monterey' ]: @@ -750,27 +760,27 @@ def Get_Build_Parameters(config): #------------------------------------------------------------------------------ ## To run the "setup.py" script with appropriate options for building -# the klayout Python Module "pymod". +# the klayout Python Module "pymod (*.whl)". # # @param[in] parameters dictionary containing the build parameters # # @return 0 on success; non-zero (1), otherwise #------------------------------------------------------------------------------ -def Build_pymod(parameters): +def Build_pymod_wheel(parameters): #----------------------------------------------------------------------------------------------------------- # [1] will be built if: - # BuildPymod = True - # Platform = [ 'Sonoma', 'Ventura', 'Monterey'] - # ModuleRuby = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ] - # ModulePython = [ 'Python311MacPorts', 'Python39MacPorts', - # 'Python311Brew', Python39Brew', 'PythonAutoBrew', - # 'PythonAnaconda3' ] + # BuildPymodWhl = True + # Platform = [ 'Sonoma', 'Ventura', 'Monterey'] + # ModuleRuby = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ] + # ModulePython = [ 'Python311MacPorts', 'Python39MacPorts', + # 'Python311Brew', Python39Brew', 'PythonAutoBrew', + # 'PythonAnaconda3' ] #----------------------------------------------------------------------------------------------------------- - BuildPymod = parameters['BuildPymod'] - Platform = parameters['Platform'] - ModuleRuby = parameters['ModuleRuby'] - ModulePython = parameters['ModulePython'] - if not BuildPymod: + BuildPymodWhl = parameters['BuildPymodWhl'] + Platform = parameters['Platform'] + ModuleRuby = parameters['ModuleRuby'] + ModulePython = parameters['ModulePython'] + if not BuildPymodWhl: return 0 if not Platform in [ 'Sonoma', 'Ventura', 'Monterey' ]: return 0 @@ -961,9 +971,9 @@ def Run_Build_Command(config, parameters): NoLibGit2 = config['NoLibGit2'] ToolDebug = config['ToolDebug'] if 100 not in ToolDebug: # default - jump2pymod = False + jump2pymod_wheel = False else: - jump2pymod = True + jump2pymod_wheel = True #----------------------------------------------------- # [1] Set two environment variables to use libgit2 @@ -996,7 +1006,7 @@ def Run_Build_Command(config, parameters): else: os.environ['MAC_LIBGIT2_LIB'] = "_invalid_MAC_LIBGIT2_LIB_" # link should fail - if not jump2pymod: + if not jump2pymod_wheel: #----------------------------------------------------- # [2] Set parameters passed to the main Bash script #----------------------------------------------------- @@ -1013,8 +1023,7 @@ def Run_Build_Command(config, parameters): # (C) Target directories and files MacBuildDirQAT = parameters['build'] + ".macQAT" - # (D) Qt5 | Qt6 (Homebrew) - #cmd_args += " \\\n -qt5" # make 'build.sh' detect the Qt type automatically + # (D) Qt5 (MacPorts, Homebrew, Anaconda3) | Qt6 (MacPorts, Homebrew) cmd_args += " \\\n -qmake %s" % parameters['qmake'] cmd_args += " \\\n -bin %s" % parameters['bin'] cmd_args += " \\\n -build %s" % parameters['build'] @@ -1130,9 +1139,9 @@ def Run_Build_Command(config, parameters): #------------------------------------------------------------------------ # [6] Build for some predetermined environments on demand #------------------------------------------------------------------------ - BuildPymod = parameters['BuildPymod'] - if BuildPymod: - ret = Build_pymod(parameters) + BuildPymodWhl = parameters['BuildPymodWhl'] + if BuildPymodWhl: + ret = Build_pymod_wheel(parameters) return ret else: return 0 @@ -1162,20 +1171,23 @@ def Deploy_Binaries_For_Bundle(config, parameters): ModulePython = config['ModulePython'] ToolDebug = config['ToolDebug'] HBPythonIs39 = config['HBPythonIs39'] + EmbedQt = config['EmbedQt'] + EmbedPython3 = config['EmbedPython3'] - BuildPymod = parameters['BuildPymod'] + BuildPymodWhl = parameters['BuildPymodWhl'] ProjectDir = parameters['project_dir'] MacBinDir = parameters['bin'] MacBuildDir = parameters['build'] MacBuildLog = parameters['logfile'] Platform = parameters['Platform'] + QtLibRoot = parameters['qt_lib_root'] AbsMacPkgDir = "%s/%s" % (ProjectDir, MacPkgDir) AbsMacBinDir = "%s/%s" % (ProjectDir, MacBinDir) AbsMacBuildDir = "%s/%s" % (ProjectDir, MacBuildDir) AbsMacBuildLog = "%s/%s" % (ProjectDir, MacBuildLog) - if BuildPymod: + if BuildPymodWhl: try: PymodDistDir = parameters['pymod_dist'] pymodDistDir = PymodDistDir[ModulePython] # [ 'dist-MP3-${ModuleQt}', 'dist-HB3-${ModuleQt}', 'dist-ana3-${ModuleQt}' ] @@ -1224,8 +1236,7 @@ def Deploy_Binaries_For_Bundle(config, parameters): print( " [3] Creating the standard directory structure for 'klayout.app' bundle ..." ) #-------------------------------------------------------------------------------------------------------------- - # [3] Create the directory skeleton for "klayout.app" bundle - # and command line buddy tools such as "strm2cif". + # [3] Create the directory skeleton for "klayout.app" bundle and command line buddy tools such as "strm2cif". # They are stored in the directory structure below. # # klayout.app/+ @@ -1237,12 +1248,15 @@ def Deploy_Binaries_For_Bundle(config, parameters): # +-- Frameworks/+ # | +-- '*.framework' # | +-- '*.dylib' - # | +-- 'db_plugins' --sym.link--> ../MacOS/db_plugins/ + # | +-- 'db_plugins' --sym.link--> ../MacOS/db_plugins/ + # | +-- 'lay_plugins' --sym.link--> ../MacOS/lay_plugins/ + # | +-- 'pymod' --sym.link--> ../MacOS/pymod/ # +-- MacOS/+ # | +-- 'klayout' # | +-- db_plugins/ # | +-- lay_plugins/ # | +-- pymod/ + # | # +-- Buddy/+ # | +-- 'strm2cif' # | +-- 'strm2dxf' @@ -1264,15 +1278,14 @@ def Deploy_Binaries_For_Bundle(config, parameters): os.makedirs(targetDirF) os.makedirs(targetDirM) os.makedirs(targetDirB) - if BuildPymod and not pymodDistDir == "": + if BuildPymodWhl and not pymodDistDir == "": os.makedirs(targetDirP) - print( " [4] Copying KLayout's dynamic link libraries to 'Frameworks' ..." ) - #--------------------------------------------------------------------------------------- - # [4] Copy KLayout's dynamic link libraries to "Frameworks/" and create - # the library dependency dictionary. - # <<< Do this job in "Frameworks/" >>> + print( " [4-1] Copying KLayout's dynamic link libraries to 'Frameworks' ..." ) + #-------------------------------------------------------------------------------------------------------------- + # [4-1] Copy KLayout's dynamic link libraries to "Frameworks/" and create the library dependency dictionary. + # <<< Do this job in "Frameworks/" >>> # # Note: # KLayout's dynamic link library is built as below: @@ -1299,11 +1312,11 @@ def Deploy_Binaries_For_Bundle(config, parameters): # libklayout_gsi.0.dylib (compatibility version 0.26.0, current version 0.26.1) # libklayout_db.0.dylib (compatibility version 0.26.0, current version 0.26.1) # : - #--------------------------------------------------------------------------------------- - os.chdir( targetDirF ) - dynamicLinkLibs = glob.glob( os.path.join( AbsMacBinDir, "*.dylib" ) ) - depDicOrdinary = {} # inter-library dependency dictionary - pathDic = {} # paths to insert for each library + #-------------------------------------------------------------------------------------------------------------- + os.chdir(targetDirF) + dynamicLinkLibs = glob.glob( os.path.join( AbsMacBinDir, "*.dylib" ) ) + dependencyDic_1 = dict() # inter-library dependency dictionary + pathDic_1 = dict() # paths to insert for each library for item in dynamicLinkLibs: if os.path.isfile(item) and not os.path.islink(item): #------------------------------------------------------------------- @@ -1323,88 +1336,364 @@ def Deploy_Binaries_For_Bundle(config, parameters): otoolCm = "otool -L %s | grep dylib" % nameStyle3 otoolOut = os.popen( otoolCm ).read() dependDic = DecomposeLibraryDependency(otoolOut) - depDicOrdinary.update(dependDic) + dependencyDic_1.update(dependDic) #------------------------------------------------------------------- # (C) This library goes into Frameworks, hence record it's path there #------------------------------------------------------------------- - pathDic[nameStyle3] = "@executable_path/../Frameworks/" + nameStyle3 + pathDic_1[nameStyle3] = "@executable_path/../Frameworks/" + nameStyle3 + if 410 in ToolDebug: + DumpDependencyDicPair( "In [4-1 410]:", dependencyDic_1, pathDic_1 ) + + print( " [4-2] Copying the contents of the plugins to the place next to the application..." ) os.chdir(ProjectDir) - #------------------------------------------------------------------- - # Copy the contents of the plugin directories to a place next to - # the application binary - #------------------------------------------------------------------- - for piDir in [ "db_plugins", "lay_plugins", "pymod" ]: - os.makedirs( os.path.join( targetDirM, piDir )) + #------------------------------------------------------------------------------- + # (A) Copy the contents of the plugin directories to a place next to + # the application binary + #------------------------------------------------------------------------------- + for piDir in [ "db_plugins", "lay_plugins" ]: + os.makedirs( os.path.join( targetDirM, piDir ) ) dynamicLinkLibs = glob.glob( os.path.join( MacBinDir, piDir, "*.dylib" ) ) for item in dynamicLinkLibs: if os.path.isfile(item) and not os.path.islink(item): #------------------------------------------------------------------- - # (A) Copy an ordinary *.dylib file here by changing the name + # (B) Copy an ordinary *.dylib file here by changing the name # to style (3) and set its mode to 0755 (sanity check). #------------------------------------------------------------------- fullName = os.path.basename(item).split('.') # e.g. [ 'libklayout_lay', '0', '26', '1', 'dylib' ] nameStyle3 = fullName[0] + "." + fullName[1] + ".dylib" - destPath = os.path.join( targetDirM, piDir, nameStyle3 ) + destPath = os.path.join( targetDirM, piDir, nameStyle3 ) shutil.copy2( item, destPath ) os.chmod( destPath, 0o0755 ) #------------------------------------------------------------------- - # (B) Then get inter-library dependencies + # (C) Then get inter-library dependencies # Note that will pull all dependencies and sort them out later # dropping those which don't have a path entry #------------------------------------------------------------------- otoolCm = "otool -L %s | grep 'dylib'" % destPath otoolOut = os.popen( otoolCm ).read() dependDic = DecomposeLibraryDependency(otoolOut) - depDicOrdinary.update(dependDic) + dependencyDic_1.update(dependDic) #------------------------------------------------------------------- - # (C) This library goes into the plugin dir + # (D) This library goes into the plugin directory #------------------------------------------------------------------- - pathDic[nameStyle3] = "@executable_path/" + piDir + "/" + nameStyle3 + pathDic_1[nameStyle3] = "@executable_path/" + piDir + "/" + nameStyle3 - ''' - PrintLibraryDependencyDictionary( depDicOrdinary, pathDic, "Style (3)" ) - exit() - ''' + if 420 in ToolDebug: + DumpDependencyDicPair( "In [4-2 420]:", dependencyDic_1, pathDic_1 ) - #---------------------------------------------------------------------------------- - # (D) Make a symbolic link - # 'db_plugins' --slink--> ../MacOS/db_plugins/ - # under Frameworks/, which is required for the command line Buddy tools. + print( " [4-3] Making symbolic links from 'Frameworks' to '../MacOS/[db_plugins | lay_plugins | pymod]'..." ) + #-------------------------------------------------------------------------------------------------------------- + # [4-3] Make symbolic links + # 'db_plugins' --slink--> ../MacOS/db_plugins/ + # 'lay_plugins' --slink--> ../MacOS/lay_plugins/ + # 'pymod' --slink--> ../MacOS/pymod/ + # under Frameworks/. # - # Ref. https://github.com/KLayout/klayout/issues/460#issuecomment-571803458 + # 'db_plugins' is required for the command line Buddy tools. + # Ref. https://github.com/KLayout/klayout/issues/460#issuecomment-571803458 # # : # +-- Frameworks/+ # | +-- '*.framework' # | +-- '*.dylib' - # | +-- 'db_plugins' --sym.link--> ../MacOS/db_plugins/ + # | +-- 'db_plugins' --sym.link--> ../MacOS/db_plugins/ + # | +-- 'lay_plugins' --sym.link--> ../MacOS/lay_plugins/ + # | +-- 'pymod' --sym.link--> ../MacOS/pymod/ # +-- MacOS/+ # | +-- 'klayout' # | +-- db_plugins/ # | +-- lay_plugins/ # | +-- pymod/ # : - #---------------------------------------------------------------------------------- - os.chdir( targetDirF ) - os.symlink( "../MacOS/db_plugins/", "./db_plugins" ) + #-------------------------------------------------------------------------------------------------------------- + os.chdir(targetDirF) + os.symlink( "../MacOS/db_plugins/", "./db_plugins" ) + os.symlink( "../MacOS/lay_plugins/", "./lay_plugins" ) + os.symlink( "../MacOS/pymod/", "./pymod" ) + + print( " [4-4] Deeply copying 'pymod/*' to 'MacOS/pymod/*'..." ) + #-------------------------------------------------------------------------------------------------------------- + # [4-4] Deeply copy the 'pymod' directory's contents, if any + # : + # +-- Frameworks/+ + # | +-- '*.framework' + # | +-- '*.dylib' + # | +-- 'db_plugins' --sym.link--> ../MacOS/db_plugins/ + # | +-- 'lay_plugins' --sym.link--> ../MacOS/lay_plugins/ + # | +-- 'pymod' --sym.link--> ../MacOS/pymod/ + # +-- MacOS/+ + # | +-- 'klayout' + # | +-- db_plugins/ + # | +-- lay_plugins/ + # | +-- pymod/+ + # | +-- klayout/+ + # | +-- (*) + # | +-- pya/+ + # | +-- __init__.py + # : + # (*) Example + # -rwxr-xr-x ... QtCore.cpython-311-darwin.so --- (+) + # -rwxr-xr-x ... QtDesigner.cpython-311-darwin.so + # -rwxr-xr-x ... QtGui.cpython-311-darwin.so + # -rwxr-xr-x ... QtMultimedia.cpython-311-darwin.so + # -rwxr-xr-x ... QtNetwork.cpython-311-darwin.so + # -rwxr-xr-x ... QtPrintSupport.cpython-311-darwin.so + # -rwxr-xr-x ... QtSql.cpython-311-darwin.so + # -rwxr-xr-x ... QtSvg.cpython-311-darwin.so + # -rwxr-xr-x ... QtUiTools.cpython-311-darwin.so + # -rwxr-xr-x ... QtWidgets.cpython-311-darwin.so + # -rwxr-xr-x ... QtXml.cpython-311-darwin.so + # -rwxr-xr-x ... QtXmlPatterns.cpython-311-darwin.so + # -rwxr-xr-x ... __init__.py + # drwxr-xr-x ... __pycache__ + # drwxr-xr-x ... db + # -rwxr-xr-x ... dbcore.cpython-311-darwin.so + # drwxr-xr-x ... lay + # -rwxr-xr-x ... laycore.cpython-311-darwin.so + # drwxr-xr-x ... lib + # -rwxr-xr-x ... libcore.cpython-311-darwin.so + # drwxr-xr-x ... pya + # -rwxr-xr-x ... pyacore.cpython-311-darwin.so + # drwxr-xr-x ... rdb + # -rwxr-xr-x ... rdbcore.cpython-311-darwin.so + # drwxr-xr-x ... tl + # -rwxr-xr-x ... tlcore.cpython-311-darwin.so + # + #-------------------------------------------------------------------------------------------------------------- + # (+) Example of the inter-library dependency of pymod's library + # + # % otool -L QtCore.cpython-311-darwin.so + # + # QtCore.cpython-311-darwin.so: + # libQtCore.0.dylib (compatibility version 0.28.0, current version 0.28.17) + # /opt/local/lib/libgit2.1.7.dylib (compatibility version 1.7.0, current version 1.7.2) + # /opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.3.1) + # /usr/local/opt/python@3.11/Frameworks/Python.framework/Versions/3.11/Python (compatibility...) + # libklayout_tl.0.dylib (compatibility version 0.28.0, current version 0.28.17) + # libklayout_gsi.0.dylib (compatibility version 0.28.0, current version 0.28.17) + # libklayout_pya.0.dylib (compatibility version 0.28.0, current version 0.28.17) + # libklayout_QtCore.0.dylib (compatibility version 0.28.0, current version 0.28.17) + # libklayout_QtGui.0.dylib (compatibility version 0.28.0, current version 0.28.17) + # libklayout_QtWidgets.0.dylib (compatibility version 0.28.0, current version 0.28.17) + # /opt/local/libexec/qt5/lib/QtPrintSupport.framework/Versions/5/QtPrintSupport (compatibility...) + # /opt/local/libexec/qt5/lib/QtDesigner.framework/Versions/5/QtDesigner (compatibility...) + # /opt/local/libexec/qt5/lib/QtMultimediaWidgets.framework/Versions/5/QtMultimediaWidgets (compatibility...) + # /opt/local/libexec/qt5/lib/QtSvg.framework/Versions/5/QtSvg (compatibility...) + # /opt/local/libexec/qt5/lib/QtWidgets.framework/Versions/5/QtWidgets (compatibility...) + # /opt/local/libexec/qt5/lib/QtMultimedia.framework/Versions/5/QtMultimedia (compatibility...) + # /opt/local/libexec/qt5/lib/QtGui.framework/Versions/5/QtGui (compatibility...) + # /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility...) + # /System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility...) + # /opt/local/libexec/qt5/lib/QtXml.framework/Versions/5/QtXml (compatibility...) + # /opt/local/libexec/qt5/lib/QtXmlPatterns.framework/Versions/5/QtXmlPatterns (compatibility...) + # /opt/local/libexec/qt5/lib/QtNetwork.framework/Versions/5/QtNetwork (compatibility...) + # /opt/local/libexec/qt5/lib/QtSql.framework/Versions/5/QtSql (compatibility...) + # /opt/local/libexec/qt5/lib/QtCore.framework/Versions/5/QtCore (compatibility...) + # /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility...) + # /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility...) + # /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility...) + # /System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility...) + # /usr/lib/libc++.1.dylib (compatibility...) + # /usr/lib/libSystem.B.dylib (compatibility...) + #-------------------------------------------------------------------------------------------------------------- + os.chdir(AbsMacBinDir) + sourceDirKly = "pymod/klayout" + sourdeDirPya = "pymod/pya" + pymodDirInBin = os.path.isdir(sourceDirKly) and os.path.isdir(sourdeDirPya) + dependencyDic_2 = dict() # inter-library dependency dictionary + pathDic_2 = dict() # paths to insert for each library + dependencyDic_3 = dict() # inter-library dependency dictionary + pathDic_3 = dict() # paths to insert for each library + dependencyDic_4 = dict() # inter-library dependency dictionary + pathDic_4 = dict() # paths to insert for each library + + if pymodDirInBin: + targetDirKly = os.path.join(targetDirM, sourceDirKly) + targetDirPya = os.path.join(targetDirM, sourdeDirPya) + + retK = Deeply_Copy_Dir( sourceDirKly, targetDirKly, excl_pat_list=["__pycache__"] ) + retP = Deeply_Copy_Dir( sourdeDirPya, targetDirPya, excl_pat_list=["__pycache__"] ) + if not (retK and retP): + msg = "!!! Failed to deeply copy the 'pymod' directory's contents !!!" + print(msg) + return 1 + + # <<< Do the remaining job in "MacOS/pymod/klayout/" >>> + os.chdir(targetDirKly) + #------------------------------------------------------------------- + # (A) Prepare regular expressions for the library name-matching + #------------------------------------------------------------------- + # (1) KLayout's self libraries + patSelf = r'^(lib.+[.]dylib)' + regSelf = re.compile(patSelf) + + # (2) Auxiliary libraries such as 'libgit2.1.7.dylib' + libAux_1 = "/opt/local/lib/" + libAux_2 = "/usr/local/lib/" + libAux_3 = "/opt/homebrew/lib/" + libAux_4 = "/Applications/anaconda3/lib/" + patAux = r'^(%s|%s|%s|%s)(lib.+[.]dylib)' % (libAux_1, libAux_2, libAux_3, libAux_4) + regAux = re.compile(patAux) + + # (3) Qt frameworks + # QtLibRoot: + # MacPorts ===> '/opt/local/libexec/qt5/lib' + # Homebrew ===> '/usr/local/opt/qt@5/lib' + # Anaconda3 ===> '/Applications/anaconda3/lib' (not frameworks and won't be embedded) + patQt = r'(%s/)(Qt.+[.]framework.+)' % QtLibRoot + regQt = re.compile(patQt) + + # (4) Python frameworks (only for Homebrew) # in the case of Intel Mac... + libPy3_1 = "%s/" % HBPython311FrameworkPath # /usr/local/opt/python@3.11/Frameworks/Python.framework/ + libPy3_2 = "%s/" % HBPython39FrameworkPath # /usr/local/opt/python@3.9/Frameworks/Python.framework/ + patPy3 = r'^(%s|%s)(.+)' % (libPy3_1, libPy3_2) + regPy3 = re.compile(patPy3) + + #------------------------------------------------------------------------------- + # (B) Copy the contents of the pymod/klayout/ directory to the place next to + # the main application executable + #------------------------------------------------------------------------------- + dynamicLinkLibs = glob.glob( os.path.join( targetDirM, sourceDirKly, "*.so" ) ) + for item in dynamicLinkLibs: + if os.path.isfile(item) and not os.path.islink(item): + #------------------------------------------------------------------- + # (C) Copy an ordinary *.dylib file here by changing the name + # to style (3) and set its mode to 0755 (sanity check). + #------------------------------------------------------------------- + baseName = os.path.basename(item) # 'QtCore.cpython-311-darwin.so' + fullName = os.path.basename(item).split('.') # [ 'QtCore', 'cpython-311-darwin', 'so' ] + nameStyle3 = fullName[0] + "." + fullName[1] + ".so" # == fullName; no need to change! + destPath = os.path.join( targetDirM, sourceDirKly, nameStyle3 ) + # shutil.copy2( item, destPath ) # already deeply copied! + os.chmod( destPath, 0o0755 ) # just for confirmation + + #------------------------------------------------------------------- + # (D) Then get inter-library dependencies + #------------------------------------------------------------------- + otoolCm = "otool -L %s" % destPath + otoolOut = os.popen( otoolCm ).read() + dependDic = DecomposeLibraryDependency(otoolOut) + dicKey = list(dependDic.keys())[0] + dicVal = dependDic[dicKey] + dicValIdx = list( range(0, len(dicVal)) ) + + #------------------------------------------------------------------- + # (E) Dependencies on KLayout's self libraries (always) + # Populate 'dependencyDic_2' and 'pathDic_2' + #------------------------------------------------------------------- + if True: + dependLib_2 = dict() + for idx in dicValIdx: + fname = dicVal[idx] + if regSelf.match(fname): + if idx == 0: + dependLib_2[baseName] = baseName + else: + dependLib_2[fname] = fname + + dependencyDic_2.update( {dicKey: dependLib_2} ) + pathDic_2[nameStyle3] = "@executable_path/" + sourceDirKly + "/" + nameStyle3 + + for libname in dependLib_2.keys(): + if libname == baseName: + continue + else: + pathDic_2[libname] = "@executable_path/../Frameworks/" + dependLib_2[libname] + + #------------------------------------------------------------------- + # (F) Dependencies on Qt and auxiliary libraries (optional) + # Populate 'dependencyDic_3' and 'pathDic_3' + #------------------------------------------------------------------- + if EmbedQt: + dependLib_3 = dict() + for idx in dicValIdx: + fname = dicVal[idx] + if regAux.match(fname): + dependLib_3[fname] = regAux.match(fname).groups()[1] + elif regQt.match(fname): + dependLib_3[fname] = regQt.match(fname).groups()[1] + + dependencyDic_3.update( {dicKey: dependLib_3} ) + pathDic_3[nameStyle3] = "@executable_path/" + sourceDirKly + "/" + nameStyle3 + + for libname in dependLib_3.keys(): + if libname == baseName: + continue + else: + pathDic_3[libname] = "@executable_path/../Frameworks/" + dependLib_3[libname] + + #------------------------------------------------------------------- + # (G) Dependencies on Python framework (optional) + # Populate 'dependencyDic_4' and 'pathDic_4' + #------------------------------------------------------------------- + if EmbedPython3: + dependLib_4 = dict() + for idx in dicValIdx: + fname = dicVal[idx] + if regPy3.match(fname): + dependLib_4[fname] = "Python.framework/%s" % regPy3.match(fname).groups()[1] + + dependencyDic_4.update( {dicKey: dependLib_4} ) + pathDic_4[nameStyle3] = "@executable_path/" + sourceDirKly + "/" + nameStyle3 + + for libname in dependLib_4.keys(): + if libname == baseName: + continue + else: + pathDic_4[libname] = "@executable_path/../Frameworks/" + dependLib_4[libname] + + if 441 in ToolDebug: + DumpDependencyDicPair( "In [4-4 441]:", dependencyDic_2, pathDic_2 ) + + if 442 in ToolDebug: + DumpDependencyDicPair( "In [4-4 442]:", dependencyDic_3, pathDic_3 ) + if 443 in ToolDebug: + DumpDependencyDicPair( "In [4-4 443]:", dependencyDic_4, pathDic_4 ) - print( " [5] Setting and changing the identification names among KLayout's libraries ..." ) + print( " [5-1] Setting and changing the identification names among KLayout's libraries ..." ) #------------------------------------------------------------- - # [5] Set the identification names for KLayout's libraries + # [5-1] Set the identification names for KLayout's libraries # and make the library aware of the locations of libraries # on which it depends; that is, inter-library dependency #------------------------------------------------------------- - ret = SetChangeIdentificationNameOfDyLib( depDicOrdinary, pathDic ) + os.chdir(targetDirF) + ret = SetChangeIdentificationNameOfDyLib( dependencyDic_1, pathDic_1 ) if not ret == 0: - msg = "!!! Failed to set and change to new identification names !!!" + msg = "!!! Failed to set and change to new identification names with (dependencyDic_1, pathDic_1) !!!" print(msg) return 1 + print( " [5-2] Setting and changing the identification names among pymod's libraries ..." ) + #------------------------------------------------------------- + # [5-2] Similarly for the pymod's libraries... + #------------------------------------------------------------- + if not targetDirKly == None: + os.chdir(targetDirKly) + if len(dependencyDic_2) > 0 and len(pathDic_2) > 0: + ret = SetChangeIdentificationNameOfDyLib( dependencyDic_2, pathDic_2 ) + if not ret == 0: + msg = "!!! Failed to set and change to new identification names with (dependencyDic_2, pathDic_2) !!!" + print(msg) + return 1 + + if len(dependencyDic_3) > 0 and len(pathDic_3) > 0: + ret = SetChangeIdentificationNameOfDyLib( dependencyDic_3, pathDic_3 ) + if not ret == 0: + msg = "!!! Failed to set and change to new identification names with (dependencyDic_3, pathDic_3) !!!" + print(msg) + return 1 + + if len(dependencyDic_4) > 0 and len(pathDic_4) > 0: + ret = SetChangeIdentificationNameOfDyLib( dependencyDic_4, pathDic_4 ) + if not ret == 0: + msg = "!!! Failed to set and change to new identification names with (dependencyDic_4, pathDic_4) !!!" + print(msg) + return 1 - print( " [6] Copying built executables and resource files ..." ) + print( " [6] Copying the built executables and resource files ..." ) #------------------------------------------------------------- # [6] Copy some known files in source directories to # relevant target directories @@ -1414,15 +1703,13 @@ def Deploy_Binaries_For_Bundle(config, parameters): sourceDir1 = sourceDir0 + "/MacOS" sourceDir2 = "%s/macbuild/Resources" % ProjectDir sourceDir3 = "%s" % MacBinDir - sourceDir4 = "%s/pymod" % MacBinDir # (A) the main components tmpfileM = ProjectDir + "/macbuild/Resources/Info.plist.template" keydicM = { 'exe': 'klayout', 'icon': 'klayout.icns', 'bname': 'klayout', 'ver': Version } plistM = GenerateInfoPlist( keydicM, tmpfileM ) - file = open( targetDir0 + "/Info.plist", "w" ) - file.write(plistM) - file.close() + with open( targetDir0 + "/Info.plist", "w" ) as file: + file.write(plistM) shutil.copy2( sourceDir0 + "/PkgInfo", targetDir0 ) # this file is not mandatory shutil.copy2( sourceDir1 + "/klayout", targetDirM ) @@ -1441,7 +1728,7 @@ def Deploy_Binaries_For_Bundle(config, parameters): os.chmod( targetDirB + "/" + buddy, 0o0755 ) # (C) the Pymod - if BuildPymod and not pymodDistDir == "": + if BuildPymodWhl and not pymodDistDir == "": for item in glob.glob( pymodDistDir + "/*.whl" ): shutil.copy2( item, targetDirP ) @@ -1471,7 +1758,7 @@ def Deploy_Binaries_For_Bundle(config, parameters): return 1 if DeploymentF: - print( " [8] Finally, deploying Qt's Frameworks ..." ) + print( " [8] Finally, deploying Qt's Frameworks and auxiliary libraries ..." ) #------------------------------------------------------------- # [8] Deploy Qt Frameworks #------------------------------------------------------------- @@ -1831,8 +2118,8 @@ def Deploy_Binaries_For_Bundle(config, parameters): #------------------------------------------------------------- # [10] Special deployment of Ruby3.3 from Homebrew? #------------------------------------------------------------- - deploymentRuby32HB = (ModuleRuby == 'Ruby33Brew') - if deploymentRuby32HB and NonOSStdLang: + deploymentRuby33HB = (ModuleRuby == 'Ruby33Brew') + if deploymentRuby33HB and NonOSStdLang: print( "" ) print( " [10] You have reached optional deployment of Ruby from %s ..." % HBRuby33Path ) @@ -1848,7 +2135,7 @@ def Deploy_Binaries_For_Bundle(config, parameters): else: print( " [8] Skipped deploying Qt's Frameworks and optional Python/Ruby Frameworks..." ) - print( "##### Finished deploying libraries and executables for #####" ) + print( "##### Finished deploying the libraries and executables for #####" ) print("") os.chdir(ProjectDir) return 0 diff --git a/macbuild/build4mac_env.py b/macbuild/build4mac_env.py index feb04bad89..c7d6f2e1f2 100755 --- a/macbuild/build4mac_env.py +++ b/macbuild/build4mac_env.py @@ -6,7 +6,7 @@ # # Here are dictionaries of ... # different modules for building KLayout (http://www.klayout.de/index.php) -# version 0.28.17 or later on different Apple Mac OSX platforms. +# version 0.29.0 or later on different Apple Mac OSX platforms. # # This file is imported by 'build4mac.py' script. #=============================================================================== @@ -72,14 +72,16 @@ # install with 'sudo port install [qt5|qt5-qttools]' # [Key Type Name] = 'Qt5MacPorts' Qt5MacPorts = { 'qmake' : '/opt/local/libexec/qt5/bin/qmake', - 'deploy': '/opt/local/libexec/qt5/bin/macdeployqt' + 'deploy': '/opt/local/libexec/qt5/bin/macdeployqt', + 'libdir': '/opt/local/libexec/qt5/lib' } # Qt5 from Homebrew (https://brew.sh/) # install with 'brew install qt5' # [Key Type Name] = 'Qt5Brew' Qt5Brew = { 'qmake' : '%s/opt/qt@5/bin/qmake' % DefaultHomebrewRoot, - 'deploy': '%s/opt/qt@5/bin/macdeployqt' % DefaultHomebrewRoot + 'deploy': '%s/opt/qt@5/bin/macdeployqt' % DefaultHomebrewRoot, + 'libdir': '%s/opt/qt@5/lib' % DefaultHomebrewRoot } # Qt5 bundled with anaconda3 installed under /Applications/anaconda3/ @@ -87,7 +89,8 @@ # If so, you need to make a symbolic link: /Applications/anaconda3 ---> $HOME/opt/anaconda3/ # [Key Type Name] = 'Qt5Ana3' Qt5Ana3 = { 'qmake' : '/Applications/anaconda3/bin/qmake', - 'deploy': '/Applications/anaconda3/bin/macdeployqt' + 'deploy': '/Applications/anaconda3/bin/macdeployqt', + 'libdir': '/Applications/anaconda3/lib' } #------------------------------------------------------------------------- @@ -97,16 +100,26 @@ # install with 'sudo port install [qt6|qt6-qttools]' # [Key Type Name] = 'Qt6MacPorts' Qt6MacPorts = { 'qmake' : '/opt/local/libexec/qt6/bin/qmake', - 'deploy': '/opt/local/libexec/qt6/bin/macdeployqt' + 'deploy': '/opt/local/libexec/qt6/bin/macdeployqt', + 'libdir': '/opt/local/libexec/qt6/lib' } # Qt6 from Homebrew (https://brew.sh/) # install with 'brew install qt6' # [Key Type Name] = 'Qt6Brew' Qt6Brew = { 'qmake' : '%s/opt/qt@6/bin/qmake' % DefaultHomebrewRoot, - 'deploy': '%s/opt/qt@6/bin/macdeployqt' % DefaultHomebrewRoot + 'deploy': '%s/opt/qt@6/bin/macdeployqt' % DefaultHomebrewRoot, + 'libdir': '%s/opt/qt@6/lib' % DefaultHomebrewRoot } +# Consolidated dictionary kit for Qt[5|6] +Qt56Dictionary = { 'Qt5MacPorts': Qt5MacPorts, + 'Qt5Brew' : Qt5Brew, + 'Qt5Ana3' : Qt5Ana3, + 'Qt6MacPorts': Qt6MacPorts, + 'Qt6Brew' : Qt6Brew + } + #----------------------------------------------------- # [2] Ruby # * Dropped the followings (2023-10-24). @@ -123,31 +136,46 @@ #----------------------------------------------------- # Whereabouts of different components of Ruby #----------------------------------------------------- +# % which ruby +# /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby +# +# % ruby -v +# ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.x86_64-darwin21] +# +# Where is the 'ruby.h' used to build the 'ruby' executable? +# +# % ruby -e "puts File.expand_path('ruby.h', RbConfig::CONFIG['rubyhdrdir'])" +# ===> /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk \ +# /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby.h +# # Bundled with Monterey (12.x) # [Key Type Name] = 'Sys' -MontereySDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" +MontereyXcSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" +MontereyCLTSDK = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" RubyMonterey = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby', - 'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % MontereySDK, - 'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % MontereySDK, - 'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % MontereySDK + 'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % MontereyXcSDK, + 'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % MontereyXcSDK, + 'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % MontereyXcSDK } # Bundled with Ventura (13.x) # [Key Type Name] = 'Sys' -VenturaSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" +VenturaXcSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" +VenturaCLTSDK = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" RubyVentura = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby', - 'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % VenturaSDK, - 'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % VenturaSDK, - 'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % VenturaSDK + 'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % VenturaXcSDK, + 'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % VenturaXcSDK, + 'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % VenturaXcSDK } # Bundled with Sonoma (14.x) # [Key Type Name] = 'Sys' -SonomaSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" +SonomaXcSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" +SonomaCLTSDK = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" RubySonoma = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby', - 'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % SonomaSDK, - 'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % SonomaSDK, - 'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % SonomaSDK + 'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % SonomaXcSDK, + 'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % SonomaXcSDK, + 'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % SonomaXcSDK } # Ruby 3.3 from MacPorts (https://www.macports.org/) diff --git a/macbuild/build4mac_util.py b/macbuild/build4mac_util.py index 8241aebf7c..87980982b7 100755 --- a/macbuild/build4mac_util.py +++ b/macbuild/build4mac_util.py @@ -6,7 +6,7 @@ # # Here are utility functions and classes ... # for building KLayout (http://www.klayout.de/index.php) -# version 0.28.17 or later on different Apple Mac OSX platforms. +# version 0.29.0 or later on different Apple Mac OSX platforms. # # This file is imported by 'build4mac.py' script. #======================================================================================== @@ -16,6 +16,7 @@ import string import subprocess import shutil +import fnmatch #---------------------------------------------------------------------------------------- ## To import global dictionaries of different modules @@ -80,6 +81,9 @@ def PrintLibraryDependencyDictionary( depdic, pathdic, namedic ): # @return 0 on success; non-zero on failure #---------------------------------------------------------------------------------------- def SetChangeIdentificationNameOfDyLib( libdic, pathDic ): + if len(libdic) == 0 or len(pathDic) == 0: + return 0 + cmdNameId = XcodeToolChain['nameID'] cmdNameChg = XcodeToolChain['nameCH'] dependentLibs = libdic.keys() @@ -101,10 +105,9 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ): # [2] Make the library aware of the new identifications of all supporters #------------------------------------------------------------------------- supporters = libdic[lib] - for sup in supporters: - supName = os.path.basename(sup) - if libName != supName and (supName in pathDic): - nameOld = "%s" % sup + for supName in supporters: + if (libName != supName) and (supName in pathDic): + nameOld = "%s" % supName nameNew = pathDic[supName] command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, lib ) if subprocess.call( command, shell=True ) != 0: @@ -133,11 +136,15 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ): # +-- Frameworks/+ # | +-- '*.framework' # | +-- '*.dylib' -# | +-- 'db_plugins' --slink--> ../MacOS/db_plugins/ +# | +-- 'db_plugins' --sym.link--> ../MacOS/db_plugins/ +# | +-- 'lay_plugins' --sym.link--> ../MacOS/lay_plugins/ +# | +-- 'pymod' --sym.link--> ../MacOS/pymod/ # +-- MacOS/+ # | +-- 'klayout' # | +-- db_plugins/ # | +-- lay_plugins/ +# | +-- pymod/ +# | # +-- Buddy/+ # +-- 'strm2cif' # +-- 'strm2dxf' @@ -790,6 +797,79 @@ def Generate_Start_Console_Py( template, pythonver, target ): else: return True +#---------------------------------------------------------------------------------------- +## To deeply copy directory contents +# +# @param[in] src_dir : source directory +# @param[in] dest_dir : destination directory +# @param[in] excl_pat_list: exclude pattern list (default=[]]) +# +# @return True on success, False on failure +#---------------------------------------------------------------------------------------- +def Deeply_Copy_Dir( src_dir, dest_dir, excl_pat_list=[] ): + + def FnameMatch(item): + for excl_pat in excl_pat_list: + if fnmatch.fnmatch( item, excl_pat ): + return True + return False + + if os.path.isfile(dest_dir): + print( "! Destination <%s> is an existing file" % dest_dir, file=sys.stderr ) + return False + + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + + for item in os.listdir(src_dir): + src_item = os.path.join(src_dir, item) + dest_item = os.path.join(dest_dir, item) + + if os.path.isdir(src_item): + if FnameMatch(item): + continue # skip copying if directory name matches the exclusion pattern + Deeply_Copy_Dir( src_item, dest_item, excl_pat_list ) + else: + if FnameMatch(item): + continue # skip copying if the file matches the exclusion pattern + shutil.copy2( src_item, dest_item ) + + return True + +#---------------------------------------------------------------------------------------- +## To dump the contents of a dependency dictionary +# +# @param[in] title: title +# @param[in] depDic: dependency dictionary to dump +# @param[in] pathDic: path dictionary to dump +# +# @return void +#---------------------------------------------------------------------------------------- +def DumpDependencyDicPair( title, depDic, pathDic ): + + print( "### Dependency Dictionary Pair <%s> ###" % title ) + + # depDic + count1 = 0 + for key1 in sorted(depDic.keys()): + count1 += 1 + diclist = depDic[key1] + print( " %3d:%s" % (count1, key1) ) + + count2 = 0 + for dict_file in diclist: + count2 += 1 + print( " %3d:%s" % (count2, dict_file) ) + + # pathDic + print( " ==========" ) + count3 = 0 + for key3 in sorted(pathDic.keys()): + count3 += 1 + print( " %3d:%s: %s" % (count3, key3, pathDic[key3]) ) + + return + #---------------- # End of File #---------------- diff --git a/macbuild/macQAT.py b/macbuild/macQAT.py index f62ffce162..bd73ddcc1d 100755 --- a/macbuild/macQAT.py +++ b/macbuild/macQAT.py @@ -5,7 +5,7 @@ # File: "macbuild/macQAT.py" # # The top Python script to run "ut_runner" after building KLayout -# (http://www.klayout.de/index.php) version 0.28.17 or later on different Apple +# (http://www.klayout.de/index.php) version 0.29.0 or later on different Apple # ßMac OSX platforms. # # This script must be copied to a "*.macQAT/" directory to run. @@ -25,6 +25,7 @@ def SetGlobals(): global ProjectDir # project directory where "ut_runner" exists global RunnerUsage # True to print the usage of 'ut_runner' + global StartKLayout # True to start the KLayout main GUI window global Run # True to run this script global ContinueOnError # True to continue after an error global TestsExcluded # list of tests to exclude @@ -54,6 +55,7 @@ def SetGlobals(): Usage += " -----------------------------------------------------------------+---------------\n" Usage += " [-u|--usage] : print usage of 'ut_runner'and exit | disabled\n" Usage += " |\n" + Usage += " [-k|--klayout] : just start the KLayout main GUI window | disabled\n" Usage += " <-r|--run> : run this script | disabled\n" Usage += " [-s|--stop] : stop on error | disabled\n" Usage += " [-x|--exclude ] : exclude test(s) such as 'pymod,pya' | ''\n" @@ -66,6 +68,7 @@ def SetGlobals(): ProjectDir = os.getcwd() RunnerUsage = False + StartKLayout = False Run = False ContinueOnError = True TestsExcluded = list() @@ -104,6 +107,7 @@ def GetTimeStamp(): def ParseCommandLineArguments(): global Usage global RunnerUsage + global StartKLayout global Run global ContinueOnError global TestsExcluded @@ -117,6 +121,12 @@ def ParseCommandLineArguments(): default=False, help="print usage of 'ut_runner' and exit (false)" ) + p.add_option( '-k', '--klayout', + action='store_true', + dest='start_KLayout', + default=False, + help='just start the KLayout main GUI window (false)' ) + p.add_option( '-r', '--run', action='store_true', dest='runme', @@ -152,6 +162,7 @@ def ParseCommandLineArguments(): help='check usage (false)' ) p.set_defaults( runner_usage = False, + start_KLayout = False, runme = False, stop_on_error = False, exclude_tests = list(), @@ -165,6 +176,7 @@ def ParseCommandLineArguments(): quit() RunnerUsage = opt.runner_usage + StartKLayout = opt.start_KLayout Run = opt.runme ContinueOnError = not opt.stop_on_error if not len(opt.exclude_tests) == 0: @@ -211,6 +223,18 @@ def ExportEnvVariables(): for env in [ 'TESTSRC', 'TESTTMP', 'LD_LIBRARY_PATH' ]: os.environ[env] = MyEnviron[env] +#------------------------------------------------------------------------------- +## Start the KLayout main GUI window +# +#------------------------------------------------------------------------------- +def StartKLatyouGUIWindow(): + if System == "Darwin": + command = "./klayout.app/Contents/MacOS/klayout" + else: + command = "./klayout" + + subprocess.call( command, shell=False ) + #------------------------------------------------------------------------------- ## Run the tester # @@ -255,35 +279,43 @@ def Main(): quit() #------------------------------------------------------- - # [3] Run the unit tester + # [3] Start the KLayout main GUI window + #------------------------------------------------------- + if StartKLayout: + StartKLatyouGUIWindow() + #------------------------------------------------------- - if not Run: - print( "! pass <-r|--run> option to run" ) + # [4] Run the unit tester + #------------------------------------------------------- + if not Run and not StartKLayout: + print( "! pass <-r|--run> option to run the QA tests" ) + print( "! pass <-k|--klayout> option to start the KLayout main GUI window" ) print(Usage) quit() - command = './ut_runner' - if ContinueOnError: - command += " -c" - for item in TestsExcluded: - command += ' -x %s' % item - if not len(Arguments) == 0: - for arg in Arguments: - command += " %s" % arg - - print( "" ) - print( "### Dumping the log to <%s>" % LogFile ) - print( "------------------------------------------------------------------------" ) - print( " Git SHA1 = %s" % GitSHA1 ) - print( " Time stamp = %s" % TimeStamp ) - print( " Command line = %s" % command ) - print( "------------------------------------------------------------------------" ) - if DryRun: - quit() - sleep(1.0) - HidePrivateDir() - RunTester( command, logfile=LogFile ) - ShowPrivateDir() + if Run: + command = './ut_runner' + if ContinueOnError: + command += " -c" + for item in TestsExcluded: + command += ' -x %s' % item + if not len(Arguments) == 0: + for arg in Arguments: + command += " %s" % arg + + print( "" ) + print( "### Dumping the log to <%s>" % LogFile ) + print( "------------------------------------------------------------------------" ) + print( " Git SHA1 = %s" % GitSHA1 ) + print( " Time stamp = %s" % TimeStamp ) + print( " Command line = %s" % command ) + print( "------------------------------------------------------------------------" ) + if DryRun: + quit() + sleep(1.0) + HidePrivateDir() + RunTester( command, logfile=LogFile ) + ShowPrivateDir() #=================================================================================== if __name__ == "__main__": diff --git a/macbuild/makeDMG4mac.py b/macbuild/makeDMG4mac.py index 1b2ae16835..1cdd03cde6 100755 --- a/macbuild/makeDMG4mac.py +++ b/macbuild/makeDMG4mac.py @@ -77,7 +77,7 @@ def SetGlobals(): Usage = "\n" Usage += "---------------------------------------------------------------------------------------------------------\n" Usage += "<< Usage of 'makeDMG4mac.py' >>\n" - Usage += " for making a DMG file of KLayout 0.28.17 or later on different Apple macOS platforms.\n" + Usage += " for making a DMG file of KLayout 0.29.0 or later on different Apple macOS platforms.\n" Usage += "\n" Usage += "$ [python] ./makeDMG4mac.py\n" Usage += " option & argument : descriptions | default value\n" @@ -162,36 +162,50 @@ def SetGlobals(): Item3AppleScript = "" # Populate DicStdLightHeavyW - DicStdLightHeavyW[ "std" ] = dict() # ST-* - DicStdLightHeavyW[ "ports" ] = dict() # LW-* - DicStdLightHeavyW[ "brew" ] = dict() # LW-* - DicStdLightHeavyW[ "ana3" ] = dict() # LW-* - DicStdLightHeavyW[ "brewH" ] = dict() # HW-* - - DicStdLightHeavyW[ "std" ]["zip"] = "macbuild/Resources/script-bundle-S.zip" - DicStdLightHeavyW[ "std" ]["src"] = "script-bundle-S" - DicStdLightHeavyW[ "std" ]["des"] = "MacStdUser-ReadMeFirst" - DicStdLightHeavyW[ "std" ]["item3"] = 'set position of item "MacStdUser-ReadMeFirst" to {700, 400}' - - DicStdLightHeavyW[ "ports" ]["zip"] = "macbuild/Resources/script-bundle-P.zip" - DicStdLightHeavyW[ "ports" ]["src"] = "script-bundle-P" - DicStdLightHeavyW[ "ports" ]["des"] = "MacPortsUser-ReadMeFirst" - DicStdLightHeavyW[ "ports" ]["item3"] = 'set position of item "MacPortsUser-ReadMeFirst" to {700, 400}' - - DicStdLightHeavyW[ "brew" ]["zip"] = "macbuild/Resources/script-bundle-B.zip" - DicStdLightHeavyW[ "brew" ]["src"] = "script-bundle-B" - DicStdLightHeavyW[ "brew" ]["des"] = "HomebrewUser-ReadMeFirst" - DicStdLightHeavyW[ "brew" ]["item3"] = 'set position of item "HomebrewUser-ReadMeFirst" to {700, 400}' - - DicStdLightHeavyW[ "ana3" ]["zip"] = "macbuild/Resources/script-bundle-A.zip" - DicStdLightHeavyW[ "ana3" ]["src"] = "script-bundle-A" - DicStdLightHeavyW[ "ana3" ]["des"] = "Anaconda3User-ReadMeFirst" - DicStdLightHeavyW[ "ana3" ]["item3"] = 'set position of item "Anaconda3User-ReadMeFirst" to {700, 400}' - - DicStdLightHeavyW[ "brewH" ]["zip"] = "macbuild/Resources/script-bundle-H.zip" - DicStdLightHeavyW[ "brewH" ]["src"] = "script-bundle-H" - DicStdLightHeavyW[ "brewH" ]["des"] = "Homebrew-HUser-ReadMeFirst" - DicStdLightHeavyW[ "brewH" ]["item3"] = 'set position of item "Homebrew-HUser-ReadMeFirst" to {700, 400}' + DicStdLightHeavyW[ "std" ] = dict() # ST-* + DicStdLightHeavyW[ "ports" ] = dict() # LW-* + DicStdLightHeavyW[ "brew" ] = dict() # LW-* + DicStdLightHeavyW[ "ana3" ] = dict() # LW-* + DicStdLightHeavyW[ "brewH" ] = dict() # HW-* + # "pbrew" is the alternative of "brew" using MacPorts' Qt and Homebrew's (Ruby, Python) + # "pbrewHW" is the alternative of "brewH" using MacPorts' Qt and Homebrew's Python + DicStdLightHeavyW[ "pbrew" ] = dict() # LW-* + DicStdLightHeavyW[ "pbrewHW" ] = dict() # HW-* + + DicStdLightHeavyW[ "std" ]["zip"] = "macbuild/Resources/script-bundle-S.zip" + DicStdLightHeavyW[ "std" ]["src"] = "script-bundle-S" + DicStdLightHeavyW[ "std" ]["des"] = "MacStdUser-ReadMeFirst" + DicStdLightHeavyW[ "std" ]["item3"] = 'set position of item "MacStdUser-ReadMeFirst" to {700, 400}' + + DicStdLightHeavyW[ "ports" ]["zip"] = "macbuild/Resources/script-bundle-P.zip" + DicStdLightHeavyW[ "ports" ]["src"] = "script-bundle-P" + DicStdLightHeavyW[ "ports" ]["des"] = "MacPortsUser-ReadMeFirst" + DicStdLightHeavyW[ "ports" ]["item3"] = 'set position of item "MacPortsUser-ReadMeFirst" to {700, 400}' + + DicStdLightHeavyW[ "brew" ]["zip"] = "macbuild/Resources/script-bundle-B.zip" + DicStdLightHeavyW[ "brew" ]["src"] = "script-bundle-B" + DicStdLightHeavyW[ "brew" ]["des"] = "HomebrewUser-ReadMeFirst" + DicStdLightHeavyW[ "brew" ]["item3"] = 'set position of item "HomebrewUser-ReadMeFirst" to {700, 400}' + + DicStdLightHeavyW[ "ana3" ]["zip"] = "macbuild/Resources/script-bundle-A.zip" + DicStdLightHeavyW[ "ana3" ]["src"] = "script-bundle-A" + DicStdLightHeavyW[ "ana3" ]["des"] = "Anaconda3User-ReadMeFirst" + DicStdLightHeavyW[ "ana3" ]["item3"] = 'set position of item "Anaconda3User-ReadMeFirst" to {700, 400}' + + DicStdLightHeavyW[ "brewH" ]["zip"] = "macbuild/Resources/script-bundle-H.zip" + DicStdLightHeavyW[ "brewH" ]["src"] = "script-bundle-H" + DicStdLightHeavyW[ "brewH" ]["des"] = "Homebrew-HUser-ReadMeFirst" + DicStdLightHeavyW[ "brewH" ]["item3"] = 'set position of item "Homebrew-HUser-ReadMeFirst" to {700, 400}' + + DicStdLightHeavyW[ "pbrew" ]["zip"] = "macbuild/Resources/script-bundle-B.zip" + DicStdLightHeavyW[ "pbrew" ]["src"] = "script-bundle-B" + DicStdLightHeavyW[ "pbrew" ]["des"] = "HomebrewUser-ReadMeFirst" + DicStdLightHeavyW[ "pbrew" ]["item3"] = 'set position of item "HomebrewUser-ReadMeFirst" to {700, 400}' + + DicStdLightHeavyW[ "pbrewHW" ]["zip"] = "macbuild/Resources/script-bundle-H.zip" + DicStdLightHeavyW[ "pbrewHW" ]["src"] = "script-bundle-H" + DicStdLightHeavyW[ "pbrewHW" ]["des"] = "Homebrew-HUser-ReadMeFirst" + DicStdLightHeavyW[ "pbrewHW" ]["item3"] = 'set position of item "Homebrew-HUser-ReadMeFirst" to {700, 400}' #------------------------------------------------------------------------------ ## To check the contents of the package directory @@ -199,18 +213,15 @@ def SetGlobals(): # The package directory name should look like: # * ST-qt5MP.pkg.macos-Monterey-release-RsysPsys # * LW-qt5Ana3.pkg.macos-Monterey-release-Rana3Pana3 -# * LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311 --- (1) +# * LW-qt6Brew.pkg.macos-Monterey-release-Rhb33Phb311 --- (1) # * LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311 -# * HW-qt5Brew.pkg.macos-Monterey-release-RsysPhb311 +# * HW-qt6Brew.pkg.macos-Monterey-release-RsysPhb311 # # * ST-qt6MP.pkg.macos-Monterey-release-RsysPsys -# * LW-qt6Ana3.pkg.macos-Monterey-release-Rana3Pana3 -# * LW-qt6Brew.pkg.macos-Monterey-release-Rhb33Phb311 # * LW-qt6MP.pkg.macos-Monterey-release-Rmp33Pmp311 -# * HW-qt6Brew.pkg.macos-Monterey-release-RsysPhb311 # # Generated DMG will be, for example, -# (1) ---> LW-klayout-0.28.17-macOS-Monterey-1-qt5Brew-Rhb33Phb311.dmg +# (1) ---> LW-klayout-0.29.0-macOS-Monterey-1-qt6Brew-Rhb33Phb311.dmg # # @return on success, positive integer in [MB] that tells approx. occupied disc space; # on failure, -1 @@ -251,16 +262,13 @@ def CheckPkgDirectory(): # [2] Identify (Qt, Ruby, Python) from PkgDir # * ST-qt5MP.pkg.macos-Monterey-release-RsysPsys # * LW-qt5Ana3.pkg.macos-Monterey-release-Rana3Pana3 - # * LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311 + # * LW-qt6Brew.pkg.macos-Monterey-release-Rhb33Phb311 # * LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311 - # * HW-qt5Brew.pkg.macos-Monterey-release-RsysPhb311 + # * HW-qt6Brew.pkg.macos-Monterey-release-RsysPhb311 # * EX-qt5MP.pkg.macos-Monterey-release-Rhb33Pmp311 # # * ST-qt6MP.pkg.macos-Monterey-release-RsysPsys - # * LW-qt6Ana3.pkg.macos-Monterey-release-Rana3Pana3 - # * LW-qt6Brew.pkg.macos-Monterey-release-Rhb33Phb311 # * LW-qt6MP.pkg.macos-Monterey-release-Rmp33Pmp311 - # * HW-qt6Brew.pkg.macos-Monterey-release-RsysPhb311 #----------------------------------------------------------------------------------------------- patQRP = u'(ST|LW|HW|EX)([-])([qt5|qt6][0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-]release[-])([0-9A-Za-z]+)' regQRP = re.compile(patQRP) @@ -299,7 +307,7 @@ def CheckPkgDirectory(): LatestOSHomebrew = Platform == LatestOS LatestOSHomebrew &= PackagePrefix == "LW" - LatestOSHomebrew &= QtIdentification in [ "qt5Brew", "qt6Brew" ] + LatestOSHomebrew &= QtIdentification in [ "qt5Brew", "qt6Brew", "qt5MP", "qt6MP" ] # "qt[5|6]MP" are the alternatives LatestOSHomebrew &= RubyPythonID in [ "Rhb33Phb311", "Rhb33Phb39", "Rhb33Phbauto" ] LatestOSAnaconda3 = Platform == LatestOS @@ -309,8 +317,8 @@ def CheckPkgDirectory(): LatestOSHomebrewH = Platform == LatestOS LatestOSHomebrewH &= PackagePrefix == "HW" - LatestOSHomebrewH &= QtIdentification in [ "qt5Brew", "qt6Brew" ] - LatestOSHomebrewH &= RubyPythonID in [ "RsysPhb311", "RsysPhb39", "RsysPhbauto" ] # Sys-Homebre hybrid + LatestOSHomebrewH &= QtIdentification in [ "qt5Brew", "qt6Brew", "qt5MP", "qt6MP" ] # "qt[5|6]MP" are the alternatives + LatestOSHomebrewH &= RubyPythonID in [ "RsysPhb311", "RsysPhb39", "RsysPhbauto" ] # Sys-Homebrew hybrid if LatestOSSys: mydic = DicStdLightHeavyW["std"] diff --git a/macbuild/nightlyBuild.py b/macbuild/nightlyBuild.py index 7ffb3d4160..3d6100b309 100755 --- a/macbuild/nightlyBuild.py +++ b/macbuild/nightlyBuild.py @@ -57,15 +57,18 @@ def Test_My_Platform( platforms=[ 'Monterey', 'Ventura', 'Sonoma' ] ): # @return a dictionary; key=integer, value=mnemonic #------------------------------------------------------------------------------ def Get_Build_Target_Dict(): - buildTargetDic = dict() - buildTargetDic[0] = 'std' - buildTargetDic[1] = 'ports' - buildTargetDic[2] = 'brew' - buildTargetDic[3] = 'brewHW' - buildTargetDic[4] = 'ana3' - - buildTargetDic[5] = 'brewA' - buildTargetDic[6] = 'brewAHW' + buildTargetDic = dict() + buildTargetDic[0] = 'std' + buildTargetDic[1] = 'ports' + buildTargetDic[2] = 'brew' + buildTargetDic[3] = 'brewHW' + buildTargetDic[4] = 'ana3' + + buildTargetDic[5] = 'brewA' + buildTargetDic[6] = 'brewAHW' + + buildTargetDic[12] = 'pbrew' # use MacPorts' Qt and Homebrew's (Ruby, Python) + buildTargetDic[13] = 'pbrewHW' # use MacPorts' Qt and Homebrew's Python return buildTargetDic #------------------------------------------------------------------------------ @@ -97,25 +100,32 @@ def Get_Build_Options( targetDic, platform ): buildOp[(qtVer, "ports")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP33', '-p', 'MP311' ] logfile[(qtVer, "ports")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rmp33Pmp311") elif target == "brew": - buildOp[(qtVer, "brew")] = [ '-q', '%sBrew' % qtType, '-r', 'HB33', '-p', 'HB311' ] + buildOp[(qtVer, "brew")] = [ '-q', '%sBrew' % qtType, '-r', 'HB33', '-p', 'HB311' ] logfile[(qtVer, "brew")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb33Phb311") elif target == "brewHW": - buildOp[(qtVer, "brewHW")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311' ] + buildOp[(qtVer, "brewHW")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311' ] logfile[(qtVer, "brewHW")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311") elif target == "ana3": - buildOp[(qtVer, "ana3")] = [ '-q', '%sAna3' % qtType, '-r', 'Ana3', '-p', 'Ana3' ] + buildOp[(qtVer, "ana3")] = [ '-q', '%sAna3' % qtType, '-r', 'Ana3', '-p', 'Ana3' ] logfile[(qtVer, "ana3")] = "%sAna3.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rana3Pana3") elif target == "brewA": - buildOp[(qtVer, "brewA")] = [ '-q', '%sBrew' % qtType, '-r', 'HB33', '-p', 'HBAuto' ] + buildOp[(qtVer, "brewA")] = [ '-q', '%sBrew' % qtType, '-r', 'HB33', '-p', 'HBAuto' ] logfile[(qtVer, "brewA")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb33Phbauto") elif target == "brewAHW": - buildOp[(qtVer, "brewAHW")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto' ] + buildOp[(qtVer, "brewAHW")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto' ] logfile[(qtVer, "brewAHW")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhbauto") + elif target == "pbrew": + buildOp[(qtVer, "pbrew")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB33', '-p', 'HB311' ] + logfile[(qtVer, "pbrew")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb33Phb311") + elif target == "pbrewHW": + buildOp[(qtVer, "pbrewHW")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'HB311' ] + logfile[(qtVer, "pbrewHW")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311") if WithPymod: buildOp[(qtVer,"ports")] = buildOp[(qtVer,"ports")] + ['--buildPymod'] buildOp[(qtVer,"brew")] = buildOp[(qtVer,"brew")] + ['--buildPymod'] buildOp[(qtVer,"ana3")] = buildOp[(qtVer,"ana3")] + ['--buildPymod'] + buildOp[(qtVer,"pbrew")] = buildOp[(qtVer,"pbrew")] + ['--buildPymod'] return (buildOp, logfile) @@ -152,6 +162,10 @@ def Get_QAT_Directory( targetDic, platform ): dirQAT[(qtVer, "brewA")] = '%sBrew.build.macos-%s-release-Rhb33Phbauto.macQAT' % (qtType.lower(), platform) elif target == "brewAHW": dirQAT[(qtVer, "brewAHW")] = '%sBrew.build.macos-%s-release-RsysPhbauto.macQAT' % (qtType.lower(), platform) + elif target == "pbrew": + dirQAT[(qtVer, "pbrew")] = '%sMP.build.macos-%s-release-Rhb33Phb311.macQAT' % (qtType.lower(), platform) + elif target == "pbrewHW": + dirQAT[(qtVer, "pbrewHW")] = '%sMP.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform) return dirQAT @@ -182,19 +196,19 @@ def Get_Package_Options( targetDic, platform, srlDMG, makeflag ): for key in targetDic.keys(): target = targetDic[key] if target == "std": - packOp[(qtVer, "std")] = [ '-p', 'ST-%sMP.pkg.macos-%s-release-RsysPsys' % (qtType.lower(), platform), + packOp[(qtVer, "std")] = [ '-p', 'ST-%sMP.pkg.macos-%s-release-RsysPsys' % (qtType.lower(), platform), '-s', '%d' % srlDMG, '%s' % flag ] elif target == "ports": - packOp[(qtVer, "ports")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rmp33Pmp311' % (qtType.lower(), platform), + packOp[(qtVer, "ports")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rmp33Pmp311' % (qtType.lower(), platform), '-s', '%d' % srlDMG, '%s' % flag ] elif target == "brew": - packOp[(qtVer, "brew")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb33Phb311' % (qtType.lower(), platform), + packOp[(qtVer, "brew")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb33Phb311' % (qtType.lower(), platform), '-s', '%d' % srlDMG, '%s' % flag ] elif target == "brewHW": - packOp[(qtVer, "brewHW")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform), + packOp[(qtVer, "brewHW")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform), '-s', '%d' % srlDMG, '%s' % flag ] elif target == "ana3": - packOp[(qtVer, "ana3")] = [ '-p', 'LW-%sAna3.pkg.macos-%s-release-Rana3Pana3' % (qtType.lower(), platform), + packOp[(qtVer, "ana3")] = [ '-p', 'LW-%sAna3.pkg.macos-%s-release-Rana3Pana3' % (qtType.lower(), platform), '-s', '%d' % srlDMG, '%s' % flag ] elif target == "brewA": packOp[(qtVer, "brewA")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb33Phbauto' % (qtType.lower(), platform), @@ -202,6 +216,12 @@ def Get_Package_Options( targetDic, platform, srlDMG, makeflag ): elif target == "brewAHW": packOp[(qtVer, "brewAHW")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhbauto' % (qtType.lower(), platform), '-s', '%d' % srlDMG, '%s' % flag ] + elif target == "pbrew": + packOp[(qtVer, "pbrew")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rhb33Phb311' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] + elif target == "pbrewHW": + packOp[(qtVer, "pbrewHW")] = [ '-p', 'HW-%sMP.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform), + '-s', '%d' % srlDMG, '%s' % flag ] return packOp #------------------------------------------------------------------------------ @@ -227,7 +247,7 @@ def Parse_CommandLine_Arguments(): if platform in [ "Sonoma", "Ventura", "Monterey" ]: targetopt = "0,1,2,3,4" else: - targetopt = None + targetopt = "" Usage = "\n" Usage += "----------------------------------------------------------------------------------------------------------\n" @@ -240,7 +260,7 @@ def Parse_CommandLine_Arguments(): Usage += " --------------------------------------------------------------------------+--------------\n" Usage += " [--qt ] : 5='qt5', 6='qt6' (migration to Qt6 is ongoing) | 5\n" Usage += " [--target ] : 0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3', | '%s'\n" % targetopt - Usage += " 5='brewA', 6='brewAHW' |\n" + Usage += " 5='brewA', 6='brewAHW', 12='pbrew', 13='pbrewHW' |\n" Usage += " * with --qt=6, use --target='0,1,2,3' (4 is ignored) |\n" Usage += " [--qttarget ] : ex. '5,1' for qt=5, target=1 | disabled\n" Usage += " + This option supersedes, if used, the --qt and --target combination. |\n" @@ -266,7 +286,7 @@ def Parse_CommandLine_Arguments(): Usage += " (3) $ ./nightlyBuild.py --test |\n" Usage += " (4) $ ./nightlyBuild.py --check (confirm the QA Test results) |\n" Usage += " (5) $ ./nightlyBuild.py --makedmg 1 |\n" - Usage += " (6) $ ./nightlyBuild.py --upload '0.28.17' |\n" + Usage += " (6) $ ./nightlyBuild.py --upload '0.29.0' |\n" Usage += " (7) $ ./nightlyBuild.py --cleandmg 1 |\n" Usage += "-----------------------------------------------------------------------------+----------------------------\n" @@ -370,7 +390,7 @@ def Parse_CommandLine_Arguments(): targetDic = Get_Build_Target_Dict() Target = list() for idx in targetIdx: - if idx in range(0, 7): + if idx in [0,1,2,3,4,5,6,12,13]: Target.append( targetDic[idx] ) # Populate QtTarget @@ -393,7 +413,7 @@ def Parse_CommandLine_Arguments(): for i in range(0, len(df)): qt = df.iloc[i,0] idx = df.iloc[i,1] - if (qt == 5 and idx in range(0, 7)) or (qt == 6 and idx in [0,1,2,3, 5,6]): + if (qt == 5 and idx in [0,1,2,3,4,5,6,12,13]) or (qt == 6 and idx in [0,1,2,3, 5,6,12,13]): QtTarget.append( (qt, targetDic[idx]) ) elif len(opt.qt_target) > 0: QtTarget = list() @@ -401,7 +421,7 @@ def Parse_CommandLine_Arguments(): for item in opt.qt_target: qt = int(item.split(",")[0]) idx = int(item.split(",")[1]) - if (qt == 5 and idx in range(0, 7)) or (qt == 6 and idx in [0,1,2,3, 5,6]): + if (qt == 5 and idx in [0,1,2,3,4,5,6,12,13]) or (qt == 6 and idx in [0,1,2,3, 5,6,12,13]): QtTarget.append( (qt, targetDic[idx]) ) else: withqttarget = False @@ -462,7 +482,7 @@ def Build_Deploy(): command1 = [ pyBuilder ] + buildOp[(qttype, key)] - if key in [ "std", "brewHW", "brewAHW" ] : + if key in [ "std", "brewHW", "brewAHW", "pbrewHW" ] : command2 = "time" command2 += " \\\n %s" % pyBuilder for option in buildOp[(qttype, key)]: diff --git a/macbuild/nightlyBuild.sample.csv b/macbuild/nightlyBuild.sample.csv index edc13bc0a7..ed0f996c95 100644 --- a/macbuild/nightlyBuild.sample.csv +++ b/macbuild/nightlyBuild.sample.csv @@ -8,18 +8,21 @@ # qtVer,target # where # qtVer = 5 or 6 -# target = [0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3'] +# target = [0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3', +# 12='pbrew', 13='pbrewHW'] # note that # (qtVer,target)=(6,4) will be omitted #------------------------------------------------------------------------------- qtVer,target 5,0 5,1 -5,2 -5,3 +6,2 +6,13 5,4 #6,0 #6,1 #6,2 #6,3 #6,4 +#6,12 +#6,13 diff --git a/macbuild/python3HB.py b/macbuild/python3HB.py index a04f0e64ad..82046c09b9 100755 --- a/macbuild/python3HB.py +++ b/macbuild/python3HB.py @@ -27,17 +27,17 @@ def SetGlobals(): del System, Node, Release, MacVersion, Machine, Processor Usage = "\n" - Usage += "-------------------------------------------------------------------------------------\n" + Usage += "----------------------------------------------------------------------------------------\n" Usage += "<< Usage of 'python3HB.py' >>\n" Usage += " to setup the standardized directory structures for Homebrew's Python 3.x on Mac\n" Usage += "\n" Usage += " option & argument : descriptions | default value\n" - Usage += " -------------------------------------------------------------------+----------\n" + Usage += " -------------------------------------------------------------------+---------------\n" Usage += " <-v|--version >: in ['3.8', '3.9', '3.10', '3.11', '3.12', | ''\n" - Usage += " '3.13'] | ''\n" + Usage += " '3.13'] |\n" Usage += " [-u|-unlink] : unlink only | disabled\n" Usage += " [-?|--?] : print this usage and exit | disabled\n" - Usage += "----------------------------------------------------------------------+--------------\n" + Usage += "----------------------------------------------------------------------+-----------------\n" #------------------------------------------------------------------------------ # Parse the command line arguments From ca560e4a811c4302edd8565123786d2e8658474d Mon Sep 17 00:00:00 2001 From: Kazunari Sekigawa Date: Wed, 3 Apr 2024 20:54:47 +0900 Subject: [PATCH 2/2] Forcibly change the wheel file name for anaconda3 --- macbuild/build4mac.py | 52 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/macbuild/build4mac.py b/macbuild/build4mac.py index f0ce5ea26e..b5a5129476 100755 --- a/macbuild/build4mac.py +++ b/macbuild/build4mac.py @@ -801,20 +801,24 @@ def Build_pymod_wheel(parameters): addBinPath = "/opt/local/bin" addIncPath = "/opt/local/include" addLibPath = "/opt/local/lib" + whlTarget = "MP3" # Using Homebrew elif PymodDistDir[ModulePython].find('dist-HB3') >= 0: addBinPath = "%s/bin" % DefaultHomebrewRoot # defined in "build4mac_env.py" addIncPath = "%s/include" % DefaultHomebrewRoot # -- ditto -- addLibPath = "%s/lib" % DefaultHomebrewRoot # -- ditto -- + whlTarget = "HB3" # Using Anaconda3 elif PymodDistDir[ModulePython].find('dist-ana3') >= 0: addBinPath = "/Applications/anaconda3/bin" addIncPath = "/Applications/anaconda3/include" addLibPath = "/Applications/anaconda3/lib" + whlTarget = "ana3" else: addBinPath = "" addIncPath = "" addLibPath = "" + whlTarget = "" if not addBinPath == "": try: @@ -898,7 +902,7 @@ def Build_pymod_wheel(parameters): return 0 #----------------------------------------------------- - # [5] Invoke the main Python scripts; takes time:-) + # [5-A] Invoke the main Python scripts; takes time:-) #----------------------------------------------------- myscript = os.path.basename(__file__) ret = subprocess.call( command1, shell=True ) @@ -920,12 +924,12 @@ def Build_pymod_wheel(parameters): return 1 #--------------------------------------------------------------------------------------------------------- - # Copy and relink library dependencies for wheel. - # In this step, the "delocate-wheel" command using the desired Python must be found in the PATH. - # Refer to: https://github.com/Kazzz-S/klayout/issues/49#issuecomment-1432154118 - # https://pypi.org/project/delocate/ + # [5-B] Copy and relink library dependencies for wheel. + # In this step, the "delocate-wheel" command using the desired Python must be found in the PATH. + # Refer to: https://github.com/Kazzz-S/klayout/issues/49#issuecomment-1432154118 + # https://pypi.org/project/delocate/ #--------------------------------------------------------------------------------------------------------- - cmd3_args = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.28.6-cp39-cp39-macosx_12_0_x86_64.whl'] + cmd3_args = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.29.0-cp311-cp311-macosx_12_0_x86_64.whl'] if len(cmd3_args) == 1: command3 = "time" command3 += " \\\n %s \\\n" % deloc_cmd @@ -952,6 +956,42 @@ def Build_pymod_wheel(parameters): print( "", file=sys.stderr ) return 1 + #------------------------------------------------------------------------ + # [5-C] Forcibly change the wheel file name for anaconda3 + # Ref. https://github.com/Kazzz-S/klayout/issues/53 + # original: klayout-0.29.0-cp311-cp311-macosx_12_0_x86_64.whl + # | + # V + # new: klayout-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl + #------------------------------------------------------------------------ + if whlTarget == "ana3": + wheels = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.29.0-cp311-cp311-macosx_12_0_x86_64.whl'] + if not len(wheels) == 1: + print( "", file=sys.stderr ) + print( "-------------------------------------------------------------", file=sys.stderr ) + print( "!!! <%s>: failed to " % myscript, file=sys.stderr ) + print( "-------------------------------------------------------------", file=sys.stderr ) + print( "", file=sys.stderr ) + return 1 + else: + pass + + original = wheels[0] + # 0 1 2 3 4 5 6 *7 8 9 + patwhl = r"(^dist/klayout-)([0-9.]+)(-)(cp[0-9]+)(-)(cp[0-9]+)(-macosx_)([0-9]+_[0-9]+)([a-z0-9_]+)(\.whl)" + regwhl = re.compile(patwhl) + if not regwhl.match(original): + print( "", file=sys.stderr ) + print( "-------------------------------------------------------------", file=sys.stderr ) + print( "!!! <%s>: failed to " % myscript, file=sys.stderr ) + print( "-------------------------------------------------------------", file=sys.stderr ) + print( "", file=sys.stderr ) + return 1 + else: + ver = regwhl.match(original).groups()[7] + new = original.replace( ver, "10_9" ) + os.rename( original, new ) + #----------------------------------------------------- # [6] Rename the "dist/" directory #-----------------------------------------------------