Merge branch 'master' into xor-performance

This commit is contained in:
Matthias Koefferlein 2024-03-09 01:02:07 +01:00
commit 1c836de07e
56 changed files with 1664 additions and 566 deletions

View File

@ -1,3 +1,26 @@
0.28.17 (2024-02-16):
* Enhancement: %GITHUB%/issues/1626 Technology specific grids
0.28.16 (2024-02-12):
* Bugfix: %GITHUB%/issues/1623 Package installation with "-y" from command line does not work for URL or file
* Bugfix: %GITHUB%/issues/1619 Segfault on Fedora-39 with Python 3.12.1
* Bugfix: %GITHUB%/issues/1618 Leaking Python reference causing trouble with iterators
* Bugfix: %GITHUB%/issues/1616 DRC doc typo
* Bugfix: %GITHUB%/issues/1614 LEF/DEF .map should not map boundaries and fill to ALL purpose
* Bugfix: %GITHUB%/issues/1609 Cell.read doesn't read LayoutMetaInfo
* Bugfix: %GITHUB%/issues/1608 CustomResistorExtraction: show faulty device in GUI
* Bugfix: %GITHUB%/issues/1603 Weird parameter dialog resizing when hiding / showing many parameters at once with callback_impl
* Bugfix: %GITHUB%/issues/1602 [Qt6] Cannot normally quit the main application window
* Bugfix: %GITHUB%/issues/1594 "connect" (in flat mode) + multiple top cells give an error message
* Bugfix: %GITHUB%/issues/1592 DXF file parsing error, about spline curve
* Enhancement: DRC enhancements related to touching edges
- New DRC function switches: "without_touching_corners", "without_touching_edges"
to skip width and space errors at kissing corners and touching-edge configurations
- Kissing corners are now detected also in non-collinear edge configurations
* Bugfix: fixed rendering of color selection buttons on High-DPI screens
* Bugfix: cross-hair cursor should not use selection default line width and styles
* Enhancement: OASIS reader errors out on broken OASIS with duplicate CELLNAMEs
0.28.15 (2024-01-04):
* Bugfix: %GITHUB%/issues/1578 Missing strm2*.exe in the Windows portable binary package (*.zip)
* Enhancement: %GITHUB%/issues/1569 Make "toggle (selected) layer" key-bindable

View File

@ -1,3 +1,17 @@
klayout (0.28.17-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Fri, 16 Feb 2024 15:52:09 +0100
klayout (0.28.16-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Mon, 12 Feb 2024 00:00:00 +0100
klayout (0.28.15-1) unstable; urgency=low
* New features and bugfixes

View File

@ -1,9 +1,9 @@
Relevant KLayout version: 0.28.15<br>
Relevant KLayout version: 0.28.17<br>
Author: Kazzz-S<br>
Last modified: 2024-01-05<br>
Last modified: 2024-02-16<br>
# 1. Introduction
This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.28.13 or later for different 64-bit macOS, including:
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:
* Monterey (12.x) : the primary development environment
* Ventura (13.x) : experimental
* Sonoma (14.x) : -- ditto --
@ -51,6 +51,8 @@ Some typical use cases are described in Section 6.
# 4. Prerequisites
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
#### For matching versions of Ruby and Python, please also refer to `build4mac_env.py`.
@ -63,12 +65,12 @@ The operating system type is detected automatically.
```
---------------------------------------------------------------------------------------------------------
<< Usage of 'build4mac.py' >>
for building KLayout 0.28.13 or later on different Apple macOS platforms.
for building KLayout 0.28.17 or later on different Apple macOS platforms.
$ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value
--------------------------------------------------------------------------------------+---------------
[-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', | qt5brew
[-q|--qt <type>] : case-insensitive type=['Qt5MacPorts', 'Qt5Brew', 'Qt5Ana3', | qt5macports
: 'Qt6MacPorts', 'Qt6Brew'] |
: Qt5MacPorts: use Qt5 from MacPorts |
: Qt5Brew: use Qt5 from Homebrew |
@ -76,20 +78,21 @@ $ [python] ./build4mac.py
: Qt6MacPorts: use Qt6 from MacPorts (*) |
: Qt6Brew: use Qt6 from Homebrew (*) |
: (*) migration to Qt6 is ongoing |
[-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP32', 'HB32', 'Ana3'] | hb32
[-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP33', 'HB33', 'Ana3'] | sys
: nil: don't bind Ruby |
: Sys: use [Sonoma|Ventura|Monterey]-bundled Ruby 2.6 |
: MP32: use Ruby 3.2 from MacPorts |
: HB32: use Ruby 3.2 from Homebrew |
: MP33: use Ruby 3.3 from MacPorts |
: HB33: use Ruby 3.3 from Homebrew |
: Ana3: use Ruby 3.2 from Anaconda3 |
[-p|--python <type>] : case-insensitive type=['nil', 'MP311', 'HB311', 'Ana3', | hb311
: 'MP39', 'hb311', 'HBAuto'] |
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP311', 'HB311', 'Ana3', | sys
: 'MP39', 'HB39', 'HBAuto'] |
: nil: don't bind Python |
: Sys: use [Sonoma|Ventura|Monterey]-bundled Python 3.9 |
: MP311: use Python 3.11 from MacPorts |
: HB311: use Python 3.11 from Homebrew |
: Ana3: use Python 3.11 from Anaconda3 |
: MP39: use Python 3.9 from MacPorts (+) |
: hb311: use Python 3.9 from Homebrew (+) |
: HB39: use Python 3.9 from Homebrew (+) |
: (+) for the backward compatibility tests |
: HBAuto: use the latest Python 3.x auto-detected from Homebrew |
[-P|--buildPymod] : build and deploy Pymod (*.whl) for LW-*.dmg | disabled
@ -118,24 +121,55 @@ $ [python] ./build4mac.py
In this section, the actual file and directory names are those obtained on macOS Monterey.<br>
On different OS, those names differ accordingly.
### 6A. Standard build using the OS-bundled Ruby and Python with MacPorts
This build has been discontinued.
### 6A. Standard build using the OS-bundled Ruby and Python with MacPorts Qt
0. Install MacPorts, then install Qt5 and libgit2 by
```
$ sudo port install coreutils
$ sudo port install findutils
$ sudo port install qt5
$ sudo port install libgit2
```
### 6B. Fully Homebrew-flavored build with Homebrew Ruby 3.2 and Homebrew Python 3.11
0. Install Homebrew, then install Qt5, Ruby 3.2, Python 3.11, and libgit2 by
Confirm that you have:
```
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/*
```
As of this writing, the provided Python version is `3.9.6`.
1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default values for Monterey, Ventura, and Sonoma.
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5macports -r sys -p sys
```
2. Confirm successful build (it will take about one hour, depending on your machine spec).
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.<br>
```
$ ./build4mac.py -q qt5macports -r sys -p sys -y
```
The application bundle **`klayout.app`** is located under:<br>
**`ST-qt5MP.pkg.macos-Monterey-release-RsysPsys`** directory, where
* "ST-" means this is a standard package.
* "qt5MP" means that Qt5 from MacPorts is used.
* "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
```
$ brew install qt@5
$ brew install ruby@3.2
$ 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
```
1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default values for Monterey, Ventura, and Sonoma.
1. Invoke **`build4mac.py`** with the following options:
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5brew -r hb32 -p hb311
$ ./build4mac.py -q qt5brew -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 under **`klayout.app`** bundle.<br>
@ -143,13 +177,13 @@ $ ./build4mac.py -q qt5brew -r hb32 -p hb311
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/**.
```
$ ./build4mac.py -q qt5brew -r hb32 -p hb311 -Y
$ ./build4mac.py -q qt5brew -r hb33 -p hb311 -Y
```
The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5Brew.pkg.macos-Monterey-release-Rhb32Phb311`** directory, where
**`LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311`** directory, where
* "LW-" means this is a lightweight package.
* "qt5Brew" means that Qt5 from Homebrew is used.
* "Rhb32Phb311" means that Ruby is 3.2 from Homebrew; Python is 3.11 from Homebrew.
* "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.
### 6C. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.11
@ -184,13 +218,13 @@ $ ./build4mac.py -q qt5brew -r sys -p hb311 -y
So far, the deployment of Homebrew Ruby is not supported.<br>
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.2 and MacPorts Python 3.11
0. Install MacPorts, then install Qt5, Ruby 3.2, Python 3.11, and libgit2 by
### 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
```
$ sudo port install coreutils
$ sudo port install findutils
$ sudo port install qt5
$ sudo port install ruby32
$ sudo port install ruby33
$ sudo port install python311
$ sudo port install py311-pip
$ sudo port install libgit2
@ -198,7 +232,7 @@ $ sudo port install libgit2
1. Invoke **`build4mac.py`** with the following options:
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5macports -r mp32 -p mp311
$ ./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.<br>
@ -206,13 +240,13 @@ $ ./build4mac.py -q qt5macports -r mp32 -p mp311
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/**.
```
$ ./build4mac.py -q qt5macports -r mp32 -p mp311 -Y
$ ./build4mac.py -q qt5macports -r mp33 -p mp311 -Y
```
The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311`** directory, where
**`LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311`** directory, where
* "LW-" means this is a lightweight package.
* "qt5MP" means that Qt5 from MacPorts is used.
* "Rmp32Pmp311" means that Ruby is 3.2 from MacPorts; Python is 3.11 from MacPorts.
* "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.
### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 3.2 and Anaconda3 Python 3.11
@ -273,11 +307,11 @@ makeDMG4mac.py -> macbuild/makeDMG4mac.py
2. Invoke **`makeDMG4mac.py`** with -p and -m options, for example,
```
$ cd /where/'build.sh'/exists
$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311 -m
$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311 -m
```
This command will generate the two files below:<br>
* **`LW-klayout-0.28.13-macOS-Monterey-1-qt5MP-Rmp32Pmp311.dmg`** ---(1) the main DMG file
* **`LW-klayout-0.28.13-macOS-Monterey-1-qt5MP-Rmp32Pmp311.dmg.md5`** ---(2) MD5-value text file
* **`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
# Known issues
Because we assume some specific versions of non-OS-standard Ruby and Python, updating Homebrew, MacPorts, or Anaconda3 may cause build- and link errors.<br>

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

View File

@ -5,7 +5,7 @@
# File: "macbuild/build4mac.py"
#
# The top Python script for building KLayout (http://www.klayout.de/index.php)
# version 0.28.13 or later on different Apple Mac OSX platforms.
# version 0.28.17 or later on different Apple Mac OSX platforms.
#===============================================================================
import sys
import os
@ -35,17 +35,17 @@ from build4mac_util import *
#-------------------------------------------------------------------------------
def GenerateUsage(platform):
if platform.upper() in [ "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ]
myQt56 = "qt5brew"
myRuby = "hb32"
myPython = "hb311"
moduleset = ('qt5Brew', 'HB32', 'HB311')
myQt56 = "qt5macports"
myRuby = "sys"
myPython = "sys"
moduleset = ('Qt5MacPorts', 'Sys', 'Sys')
else: # too obsolete
raise Exception( "! Too obsolete platform <%s>" % platform )
usage = "\n"
usage += "---------------------------------------------------------------------------------------------------------\n"
usage += "<< Usage of 'build4mac.py' >>\n"
usage += " for building KLayout 0.28.13 or later on different Apple macOS platforms.\n"
usage += " for building KLayout 0.28.17 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"
@ -58,15 +58,16 @@ def GenerateUsage(platform):
usage += " : Qt6MacPorts: use Qt6 from MacPorts (*) |\n"
usage += " : Qt6Brew: use Qt6 from Homebrew (*) |\n"
usage += " : (*) migration to Qt6 is ongoing |\n"
usage += " [-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP32', 'HB32', 'Ana3'] | %s\n" % myRuby
usage += " [-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP33', 'HB33', 'Ana3'] | %s\n" % myRuby
usage += " : nil: don't bind Ruby |\n"
usage += " : Sys: use [Sonoma|Ventura|Monterey]-bundled Ruby 2.6 |\n"
usage += " : MP32: use Ruby 3.2 from MacPorts |\n"
usage += " : HB32: use Ruby 3.2 from Homebrew |\n"
usage += " : MP33: use Ruby 3.3 from MacPorts |\n"
usage += " : HB33: use Ruby 3.3 from Homebrew |\n"
usage += " : Ana3: use Ruby 3.2 from Anaconda3 |\n"
usage += " [-p|--python <type>] : case-insensitive type=['nil', 'MP311', 'HB311', 'Ana3', | %s\n" % myPython
usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP311', 'HB311', 'Ana3', | %s\n" % myPython
usage += " : 'MP39', 'HB39', 'HBAuto'] |\n"
usage += " : nil: don't bind Python |\n"
usage += " : Sys: use [Sonoma|Ventura|Monterey]-bundled Python 3.9 |\n"
usage += " : MP311: use Python 3.11 from MacPorts |\n"
usage += " : HB311: use Python 3.11 from Homebrew |\n"
usage += " : Ana3: use Python 3.11 from Anaconda3 |\n"
@ -149,19 +150,19 @@ def Get_Default_Config():
# Set the default modules
if Platform == "Sonoma":
ModuleQt = "Qt5Brew"
ModuleRuby = "Ruby32Brew"
ModulePython = "Python311Brew"
ModuleQt = "Qt5MacPorts"
ModuleRuby = "Sys"
ModulePython = "Sys"
elif Platform == "Ventura":
ModuleQt = "Qt5Brew"
ModuleRuby = "Ruby32Brew"
ModulePython = "Python311Brew"
ModuleQt = "Qt5MacPorts"
ModuleRuby = "Sys"
ModulePython = "Sys"
elif Platform == "Monterey":
ModuleQt = "Qt5Brew"
ModuleRuby = "Ruby32Brew"
ModulePython = "Python311Brew"
ModuleQt = "Qt5MacPorts"
ModuleRuby = "Sys"
ModulePython = "Sys"
else:
ModuleQt = "Qt5Brew"
ModuleQt = "Qt5MacPorts"
ModuleRuby = "nil"
ModulePython = "nil"
@ -179,6 +180,7 @@ def Get_Default_Config():
DeployVerbose = 1
Version = GetKLayoutVersionFrom( "./version.sh" )
HBPythonIs39 = False # because ModulePython == "Python311Brew" by default
OSPython3FW = None # system Python3 frameworks in [ None, MontereyPy3FW, VenturaPy3FW, SonomaPy3FW ]
config = dict()
config['ProjectDir'] = ProjectDir # project directory where "build.sh" exists
@ -204,6 +206,7 @@ def Get_Default_Config():
config['ModuleSet'] = ModuleSet # (Qt, Ruby, Python)-tuple
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 ]
# auxiliary variables on platform
config['System'] = System # 6-tuple from platform.uname()
config['Node'] = Node # - do -
@ -246,6 +249,7 @@ def Parse_CLI_Args(config):
ModuleSet = config['ModuleSet']
ToolDebug = config['ToolDebug']
HBPythonIs39 = config['HBPythonIs39']
OSPython3FW = config['OSPython3FW']
#-----------------------------------------------------
# [2] Parse the CLI arguments
@ -257,7 +261,7 @@ def Parse_CLI_Args(config):
p.add_option( '-r', '--ruby',
dest='type_ruby',
help="Ruby type=['nil', 'Sys', 'MP32', 'HB32', 'Ana3']" )
help="Ruby type=['nil', 'Sys', 'MP33', 'HB33', 'Ana3']" )
p.add_option( '-p', '--python',
dest='type_python',
@ -331,9 +335,9 @@ def Parse_CLI_Args(config):
help='check usage' )
if Platform.upper() in [ "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ]
p.set_defaults( type_qt = "qt5brew",
type_ruby = "hb32",
type_python = "hb311",
p.set_defaults( type_qt = "qt5macports",
type_ruby = "sys",
type_python = "sys",
build_pymod = False,
no_qt_binding = False,
no_qt_uitools = False,
@ -390,8 +394,8 @@ def Parse_CLI_Args(config):
candidates = dict()
candidates['NIL'] = 'nil'
candidates['SYS'] = 'Sys'
candidates['MP32'] = 'MP32'
candidates['HB32'] = 'HB32'
candidates['MP33'] = 'MP33'
candidates['HB33'] = 'HB33'
candidates['ANA3'] = 'Ana3'
try:
choiceRuby = candidates[ opt.type_ruby.upper() ]
@ -403,18 +407,17 @@ def Parse_CLI_Args(config):
if choiceRuby == "nil":
ModuleRuby = 'nil'
elif choiceRuby == "Sys":
choiceRuby = "Sys"
if Platform == "Sonoma":
ModuleRuby = 'RubySonoma'
elif Platform == "Ventura":
ModuleRuby = 'RubyVentura'
elif Platform == "Monterey":
ModuleRuby = 'RubyMonterey'
elif choiceRuby == "MP32":
ModuleRuby = 'Ruby32MacPorts'
elif choiceRuby == "MP33":
ModuleRuby = 'Ruby33MacPorts'
NonOSStdLang = True
elif choiceRuby == "HB32":
ModuleRuby = 'Ruby32Brew'
elif choiceRuby == "HB33":
ModuleRuby = 'Ruby33Brew'
NonOSStdLang = True
elif choiceRuby == "Ana3":
ModuleRuby = 'RubyAnaconda3'
@ -429,6 +432,7 @@ def Parse_CLI_Args(config):
# (C) Determine the Python type
candidates = dict()
candidates['NIL'] = 'nil'
candidates['SYS'] = 'Sys'
candidates['MP311'] = 'MP311'
candidates['HB311'] = 'HB311'
candidates['ANA3'] = 'Ana3'
@ -445,29 +449,49 @@ def Parse_CLI_Args(config):
if choicePython == "nil":
ModulePython = 'nil'
HBPythonIs39 = None
OSPython3FW = None
elif choicePython == "Sys":
if Platform == "Sonoma":
ModulePython = 'PythonSonoma'
HBPythonIs39 = None
OSPython3FW = SonomaPy3FW
elif Platform == "Ventura":
ModulePython = 'PythonVentura'
HBPythonIs39 = None
OSPython3FW = VenturaPy3FW
elif Platform == "Monterey":
ModulePython = 'PythonMonterey'
HBPythonIs39 = None
OSPython3FW = MontereyPy3FW
elif choicePython == "MP311":
ModulePython = 'Python311MacPorts'
HBPythonIs39 = None
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "HB311":
ModulePython = 'Python311Brew'
HBPythonIs39 = False
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "Ana3":
ModulePython = 'PythonAnaconda3'
HBPythonIs39 = None
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "MP39":
ModulePython = 'Python39MacPorts'
HBPythonIs39 = None
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "HB39":
ModulePython = 'Python39Brew'
HBPythonIs39 = True
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "HBAuto":
ModulePython = 'PythonAutoBrew'
HBPythonIs39 = (HBPythonAutoVersion == "3.9")
OSPython3FW = None
NonOSStdLang = True
if ModulePython == '':
print("")
@ -524,7 +548,7 @@ def Parse_CLI_Args(config):
else:
message += "a lightweight (LW-) package with Pymod excluding Qt5, Ruby, and Python..."
elif DeploymentF:
if (ModuleRuby in RubySys) and (ModulePython in PythonSys): # won't meet this condition any more!
if (ModuleRuby in RubySys) and (ModulePython in PythonSys):
PackagePrefix = "ST-"
message += "a standard (ST-) package including Qt[5|6] and using OS-bundled Ruby and Python..."
elif ModulePython in ['Python311Brew', 'Python39Brew', 'PythonAutoBrew']:
@ -568,6 +592,7 @@ def Parse_CLI_Args(config):
config['ModuleSet'] = ModuleSet
config['ToolDebug'] = ToolDebug
config['HBPythonIs39'] = HBPythonIs39
config['OSPython3FW'] = OSPython3FW
if CheckComOnly:
pp = pprint.PrettyPrinter( indent=4, width=140 )
@ -606,6 +631,7 @@ def Get_Build_Parameters(config):
DeploymentF = config['DeploymentF']
DeploymentP = config['DeploymentP']
PackagePrefix = config['PackagePrefix']
OSPython3FW = config['OSPython3FW']
#-----------------------------------------------------
# [2] Set parameters passed to the main Bash script
@ -652,7 +678,10 @@ def Get_Build_Parameters(config):
parameters['bin'] = MacBinDir
parameters['build'] = MacBuildDir
parameters['rpath'] = "@executable_path/../Frameworks"
if OSPython3FW in [ MontereyPy3FW, VenturaPy3FW, SonomaPy3FW ]:
parameters['rpath'] = OSPython3FW
else:
parameters['rpath'] = "@executable_path/../Frameworks"
# (E) want Qt bindings with Ruby scripts?
parameters['no_qt_bindings'] = NoQtBindings
@ -698,7 +727,7 @@ def Get_Build_Parameters(config):
# <pymod> will be built if:
# BuildPymod = True
# Platform = [ 'Sonoma', 'Ventura', 'Monterey']
# ModuleRuby = [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ]
# ModuleRuby = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ]
# ModulePython = [ 'Python311MacPorts', 'Python39MacPorts',
# 'Python311Brew', Python39Brew', 'PythonAutoBrew',
# 'PythonAnaconda3' ]
@ -709,13 +738,13 @@ def Get_Build_Parameters(config):
PymodDistDir = dict()
if Platform in [ 'Sonoma', 'Ventura', 'Monterey' ]:
if ModuleRuby in [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ]:
if ModuleRuby in [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ]:
if ModulePython in [ 'Python311MacPorts', 'Python39MacPorts' ]:
PymodDistDir[ModulePython] = 'dist-MP3'
PymodDistDir[ModulePython] = 'dist-MP3-%s' % ModuleQt
elif ModulePython in [ 'Python311Brew', 'Python39Brew', 'PythonAutoBrew' ]:
PymodDistDir[ModulePython] = 'dist-HB3'
PymodDistDir[ModulePython] = 'dist-HB3-%s' % ModuleQt
elif ModulePython in [ 'PythonAnaconda3' ]:
PymodDistDir[ModulePython] = 'dist-ana3'
PymodDistDir[ModulePython] = 'dist-ana3-%s' % ModuleQt
parameters['pymod_dist'] = PymodDistDir
return parameters
@ -732,7 +761,7 @@ def Build_pymod(parameters):
# [1] <pymod> will be built if:
# BuildPymod = True
# Platform = [ 'Sonoma', 'Ventura', 'Monterey']
# ModuleRuby = [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ]
# ModuleRuby = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ]
# ModulePython = [ 'Python311MacPorts', 'Python39MacPorts',
# 'Python311Brew', Python39Brew', 'PythonAutoBrew',
# 'PythonAnaconda3' ]
@ -745,7 +774,7 @@ def Build_pymod(parameters):
return 0
if not Platform in [ 'Sonoma', 'Ventura', 'Monterey' ]:
return 0
elif not ModuleRuby in [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ]:
elif not ModuleRuby in [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ]:
return 0
elif not ModulePython in [ 'Python311MacPorts', 'Python39MacPorts', 'PythonAnaconda3', \
'Python311Brew', 'Python39Brew', 'PythonAutoBrew' ]:
@ -758,17 +787,17 @@ def Build_pymod(parameters):
#--------------------------------------------------------------------
PymodDistDir = parameters['pymod_dist']
# Using MacPorts
if PymodDistDir[ModulePython] == 'dist-MP3':
if PymodDistDir[ModulePython].find('dist-MP3') >= 0:
addBinPath = "/opt/local/bin"
addIncPath = "/opt/local/include"
addLibPath = "/opt/local/lib"
# Using Homebrew
elif PymodDistDir[ModulePython] == 'dist-HB3':
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 --
# Using Anaconda3
elif PymodDistDir[ModulePython] == 'dist-ana3':
elif PymodDistDir[ModulePython].find('dist-ana3') >= 0:
addBinPath = "/Applications/anaconda3/bin"
addIncPath = "/Applications/anaconda3/include"
addLibPath = "/Applications/anaconda3/lib"
@ -1149,7 +1178,7 @@ def Deploy_Binaries_For_Bundle(config, parameters):
if BuildPymod:
try:
PymodDistDir = parameters['pymod_dist']
pymodDistDir = PymodDistDir[ModulePython] # [ 'dist-MP3', 'dist-HB3', 'dist-ana3' ]
pymodDistDir = PymodDistDir[ModulePython] # [ 'dist-MP3-${ModuleQt}', 'dist-HB3-${ModuleQt}', 'dist-ana3-${ModuleQt}' ]
except KeyError:
pymodDistDir = ""
else:
@ -1800,15 +1829,15 @@ def Deploy_Binaries_For_Bundle(config, parameters):
return 1
#-------------------------------------------------------------
# [10] Special deployment of Ruby3.2 from Homebrew?
# [10] Special deployment of Ruby3.3 from Homebrew?
#-------------------------------------------------------------
deploymentRuby32HB = (ModuleRuby == 'Ruby32Brew')
deploymentRuby32HB = (ModuleRuby == 'Ruby33Brew')
if deploymentRuby32HB and NonOSStdLang:
print( "" )
print( " [10] You have reached optional deployment of Ruby from %s ..." % HBRuby32Path )
print( " [10] You have reached optional deployment of Ruby from %s ..." % HBRuby33Path )
print( " [!!!] Sorry, the deployed package will not work properly since deployment of" )
print( " Ruby3.2 from Homebrew is not yet supported." )
print( " Ruby3.3 from Homebrew is not yet supported." )
print( " Since you have Homebrew development environment, there two options:" )
print( " (1) Retry to make a package with '-Y|--DEPLOY' option." )
print( " This will not deploy any of Qt[5|6], Python, and Ruby from Homebrew." )

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python3
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#===============================================================================
@ -6,7 +6,7 @@
#
# Here are dictionaries of ...
# different modules for building KLayout (http://www.klayout.de/index.php)
# version 0.28.13 or later on different Apple Mac OSX platforms.
# version 0.28.17 or later on different Apple Mac OSX platforms.
#
# This file is imported by 'build4mac.py' script.
#===============================================================================
@ -117,7 +117,7 @@ Qt6Brew = { 'qmake' : '%s/opt/qt@6/bin/qmake' % DefaultHomebrewRoot,
#-----------------------------------------------------
RubyNil = [ 'nil' ]
RubySys = [ 'RubyMonterey', 'RubyVentura', 'RubySonoma' ]
RubyExt = [ 'Ruby32MacPorts', 'Ruby32Brew', 'RubyAnaconda3' ]
RubyExt = [ 'Ruby33MacPorts', 'Ruby33Brew', 'RubyAnaconda3' ]
Rubies = RubyNil + RubySys + RubyExt
#-----------------------------------------------------
@ -150,21 +150,21 @@ RubySonoma = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % SonomaSDK
}
# Ruby 3.2 from MacPorts (https://www.macports.org/)
# install with 'sudo port install ruby32'
# [Key Type Name] = 'MP32'
Ruby32MacPorts = { 'exe': '/opt/local/bin/ruby3.2',
'inc': '/opt/local/include/ruby-3.2.2',
'lib': '/opt/local/lib/libruby.3.2.dylib'
# Ruby 3.3 from MacPorts (https://www.macports.org/)
# install with 'sudo port install ruby33'
# [Key Type Name] = 'MP33'
Ruby33MacPorts = { 'exe': '/opt/local/bin/ruby3.3',
'inc': '/opt/local/include/ruby-3.3.0',
'lib': '/opt/local/lib/libruby.3.3.dylib'
}
# Ruby 3.2 from Homebrew
# install with 'brew install ruby@3.2'
# [Key Type Name] = 'HB32'
HBRuby32Path = '%s/opt/ruby@3.2' % DefaultHomebrewRoot
Ruby32Brew = { 'exe': '%s/bin/ruby' % HBRuby32Path,
'inc': '%s/include/ruby-3.2.0' % HBRuby32Path,
'lib': '%s/lib/libruby.3.2.dylib' % HBRuby32Path
# Ruby 3.3 from Homebrew
# install with 'brew install ruby@3.3'
# [Key Type Name] = 'HB33'
HBRuby33Path = '%s/opt/ruby@3.3' % DefaultHomebrewRoot
Ruby33Brew = { 'exe': '%s/bin/ruby' % HBRuby33Path,
'inc': '%s/include/ruby-3.3.0' % HBRuby33Path,
'lib': '%s/lib/libruby.3.3.dylib' % HBRuby33Path
}
# Ruby 3.2 bundled with anaconda3 installed under /Applications/anaconda3/
@ -181,8 +181,8 @@ RubyDictionary = { 'nil' : None,
'RubyMonterey' : RubyMonterey,
'RubyVentura' : RubyVentura,
'RubySonoma' : RubySonoma,
'Ruby32MacPorts': Ruby32MacPorts,
'Ruby32Brew' : Ruby32Brew,
'Ruby33MacPorts': Ruby33MacPorts,
'Ruby33Brew' : Ruby33Brew,
'RubyAnaconda3' : RubyAnaconda3
}
@ -195,7 +195,7 @@ RubyDictionary = { 'nil' : None,
# for the previous states.
#-----------------------------------------------------
PythonNil = [ 'nil' ]
PythonSys = [ ]
PythonSys = [ 'PythonMonterey', 'PythonVentura', 'PythonSonoma' ]
PythonExt = [ 'Python39MacPorts', 'Python39Brew' ]
PythonExt += [ 'Python311MacPorts', 'Python311Brew' ]
PythonExt += [ 'PythonAnaconda3', 'PythonAutoBrew' ]
@ -204,6 +204,33 @@ Pythons = PythonNil + PythonSys + PythonExt
#-----------------------------------------------------
# Whereabouts of different components of Python
#-----------------------------------------------------
# Bundled with Monterey (12.x)
# [Key Type Name] = 'Sys'
MontereyPy3FWXc = "/Applications/Xcode.app/Contents/Developer/Library/Frameworks"
MontereyPy3FW = "/Library/Developer/CommandLineTools/Library/Frameworks"
PythonMonterey = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' % MontereyPy3FW,
'inc': '%s/Python3.framework/Versions/3.9/include/python3.9' % MontereyPy3FW,
'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % MontereyPy3FW
}
# Bundled with Ventura (13.x)
# [Key Type Name] = 'Sys'
VenturaPy3FWXc = "/Applications/Xcode.app/Contents/Developer/Library/Frameworks"
VenturaPy3FW = "/Library/Developer/CommandLineTools/Library/Frameworks"
PythonVentura = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' % VenturaPy3FW,
'inc': '%s/Python3.framework/Versions/3.9/include/python3.9' % VenturaPy3FW,
'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % VenturaPy3FW
}
# Bundled with Sonoma (14.x)
# [Key Type Name] = 'Sys'
SonomaPy3FWXc = "/Applications/Xcode.app/Contents/Developer/Library/Frameworks"
SonomaPy3FW = "/Library/Developer/CommandLineTools/Library/Frameworks"
PythonSonoma = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' % SonomaPy3FW,
'inc': '%s/Python3.framework/Versions/3.9/include/python3.9' % SonomaPy3FW,
'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % SonomaPy3FW
}
# Python 3.9 from MacPorts (https://www.macports.org/)
# install with 'sudo port install python39'
# [Key Type Name] = 'MP39'
@ -299,6 +326,9 @@ else:
# Consolidated dictionary kit for Python
PythonDictionary = { 'nil' : None,
'PythonMonterey' : PythonMonterey,
'PythonVentura' : PythonVentura,
'PythonSonoma' : PythonSonoma,
'Python39MacPorts' : Python39MacPorts,
'Python311MacPorts': Python311MacPorts,
'Python39Brew' : Python39Brew,
@ -312,7 +342,7 @@ if _have_Homebrew_Python:
# [4] KLayout executables including buddy tools
#-----------------------------------------------------
KLayoutExecs = [ 'klayout' ]
KLayoutExecs += [ 'strm2cif', 'strm2dxf', 'strm2gds', 'strm2gdstxt', 'strm2oas' ]
KLayoutExecs += [ 'strm2cif', 'strm2dxf', 'strm2gds', 'strm2gdstxt', 'strm2mag', 'strm2oas' ]
KLayoutExecs += [ 'strm2txt', 'strmclip', 'strmcmp', 'strmrun', 'strmxor' ]
#----------------

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python3
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#========================================================================================
@ -6,7 +6,7 @@
#
# Here are utility functions and classes ...
# for building KLayout (http://www.klayout.de/index.php)
# version 0.26.1 or later on different Apple Mac OSX platforms.
# version 0.28.17 or later on different Apple Mac OSX platforms.
#
# This file is imported by 'build4mac.py' script.
#========================================================================================

View File

@ -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.26.1 or later on different Apple
# (http://www.klayout.de/index.php) version 0.28.17 or later on different Apple
# ßMac OSX platforms.
#
# This script must be copied to a "*.macQAT/" directory to run.

View File

@ -4,7 +4,7 @@
# File: "macbuild/macQAT.sh"
#
# The top Bash script to run "ut_runner" after building KLayout
# (http://www.klayout.de/index.php) version 0.26.1 or later on different Apple
# (http://www.klayout.de/index.php) version 0.25.17 or later on different Apple
# Mac OSX platforms.
#
# This script must be copied to a "*.macQAT/" directory to run.

View File

@ -4,7 +4,7 @@
# File: "macbuild/macQAT2.sh"
#
# The top Bash script to run "ut_runner" after building KLayout
# (http://www.klayout.de/index.php) version 0.26.1 or later on different Apple
# (http://www.klayout.de/index.php) version 0.28.17 or later on different Apple
# Mac OSX platforms.
#
# This script must be copied to a directory that can be found in $PATH.

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python3
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#=============================================================================================
@ -63,7 +63,7 @@ def SetGlobals():
global LatestOSHomebrew # True if 'LatestOS with Homebrew' and targeting LW-*
global LatestOSAnaconda3 # True if 'LatestOS with Anaconda3' and targeting LW-*
global LatestOSHomebrewH # True if 'LatestOS with Homebrew' and targeting HW-*
global DicLightHeavyW # dictionary for LW-* and HW-* packages
global DicStdLightHeavyW # dictionary for LW-* and HW-* packages
global Item3AppleScript # ITEM_3 in the Apple script
# auxiliary variables on platform
global System # 6-tuple from platform.uname()
@ -77,13 +77,13 @@ def SetGlobals():
Usage = "\n"
Usage += "---------------------------------------------------------------------------------------------------------\n"
Usage += "<< Usage of 'makeDMG4mac.py' >>\n"
Usage += " for making a DMG file of KLayout 0.28.13 or later on different Apple macOS platforms.\n"
Usage += " for making a DMG file of KLayout 0.28.17 or later on different Apple macOS platforms.\n"
Usage += "\n"
Usage += "$ [python] ./makeDMG4mac.py\n"
Usage += " option & argument : descriptions | default value\n"
Usage += " ----------------------------------------------------------------------------------+-----------------\n"
Usage += " <-p|--pkg <dir>> : package directory created by `build4mac.py` with [-y|-Y] | ``\n"
Usage += " : like 'LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311' | \n"
Usage += " : like 'LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311' | \n"
Usage += " <-c|--clean> : clean the work directory | disabled\n"
Usage += " <-m|--make> : make a compressed DMG file | disabled\n"
Usage += " : <-c|--clean> and <-m|--make> are mutually exclusive | \n"
@ -158,51 +158,59 @@ def SetGlobals():
LatestOSHomebrew = False
LatestOSAnaconda3 = False
LatestOSHomebrewH = False
DicLightHeavyW = dict()
DicStdLightHeavyW = dict()
Item3AppleScript = ""
# Populate DicLightHeavyW
DicLightHeavyW[ "ports" ] = dict() # LW-*
DicLightHeavyW[ "brew" ] = dict() # LW-*
DicLightHeavyW[ "ana3" ] = dict() # LW-*
DicLightHeavyW[ "brewH" ] = dict() # HW-*
# Populate DicStdLightHeavyW
DicStdLightHeavyW[ "std" ] = dict() # ST-*
DicStdLightHeavyW[ "ports" ] = dict() # LW-*
DicStdLightHeavyW[ "brew" ] = dict() # LW-*
DicStdLightHeavyW[ "ana3" ] = dict() # LW-*
DicStdLightHeavyW[ "brewH" ] = dict() # HW-*
DicLightHeavyW[ "ports" ]["zip"] = "macbuild/Resources/script-bundle-P.zip"
DicLightHeavyW[ "ports" ]["src"] = "script-bundle-P"
DicLightHeavyW[ "ports" ]["des"] = "MacPortsUser-ReadMeFirst"
DicLightHeavyW[ "ports" ]["item3"] = 'set position of item "MacPortsUser-ReadMeFirst" to {700, 400}'
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}'
DicLightHeavyW[ "brew" ]["zip"] = "macbuild/Resources/script-bundle-B.zip"
DicLightHeavyW[ "brew" ]["src"] = "script-bundle-B"
DicLightHeavyW[ "brew" ]["des"] = "HomebrewUser-ReadMeFirst"
DicLightHeavyW[ "brew" ]["item3"] = 'set position of item "HomebrewUser-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}'
DicLightHeavyW[ "ana3" ]["zip"] = "macbuild/Resources/script-bundle-A.zip"
DicLightHeavyW[ "ana3" ]["src"] = "script-bundle-A"
DicLightHeavyW[ "ana3" ]["des"] = "Anaconda3User-ReadMeFirst"
DicLightHeavyW[ "ana3" ]["item3"] = 'set position of item "Anaconda3User-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}'
DicLightHeavyW[ "brewH" ]["zip"] = "macbuild/Resources/script-bundle-H.zip"
DicLightHeavyW[ "brewH" ]["src"] = "script-bundle-H"
DicLightHeavyW[ "brewH" ]["des"] = "Homebrew-HUser-ReadMeFirst"
DicLightHeavyW[ "brewH" ]["item3"] = 'set position of item "Homebrew-HUser-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}'
#------------------------------------------------------------------------------
## To check the contents of the package directory
#
# 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-Rhb32Phb311 --- (1)
# * LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311
# * LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311 --- (1)
# * LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311
# * HW-qt5Brew.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-Rhb32Phb311
# * LW-qt6MP.pkg.macos-Monterey-release-Rmp32Pmp311
# * 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.13-macOS-Monterey-1-qt5Brew-Rhb32Phb311.dmg
# (1) ---> LW-klayout-0.28.17-macOS-Monterey-1-qt5Brew-Rhb33Phb311.dmg
#
# @return on success, positive integer in [MB] that tells approx. occupied disc space;
# on failure, -1
@ -223,7 +231,7 @@ def CheckPkgDirectory():
global LatestOSHomebrew
global LatestOSAnaconda3
global LatestOSHomebrewH
global DicLightHeavyW
global DicStdLightHeavyW
global Item3AppleScript
#-----------------------------------------------------------------------------
@ -241,18 +249,20 @@ 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-Rhb32Phb311
# * LW-qt5MP.pkg.macos-Monterey-release-Rmp32Pmp311
# * LW-qt5Brew.pkg.macos-Monterey-release-Rhb33Phb311
# * LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311
# * HW-qt5Brew.pkg.macos-Monterey-release-RsysPhb311
# * EX-qt5MP.pkg.macos-Monterey-release-Rhb32Pmp311
# * 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-Rhb32Phb311
# * LW-qt6MP.pkg.macos-Monterey-release-Rmp32Pmp311
# * LW-qt6Brew.pkg.macos-Monterey-release-Rhb33Phb311
# * LW-qt6MP.pkg.macos-Monterey-release-Rmp33Pmp311
# * HW-qt6Brew.pkg.macos-Monterey-release-RsysPhb311
#-----------------------------------------------------------------------------------------------
patQRP = u'(LW|HW|EX)([-])([qt5|qt6][0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-]release[-])([0-9A-Za-z]+)'
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)
if not regQRP.match(PkgDir):
print( "! Cannot identify (Qt, Ruby, Python) from the package directory name" )
@ -277,15 +287,20 @@ def CheckPkgDirectory():
#-----------------------------------------------------------------------------
# [3] Check if the "LatestOS" with MacPorts / Homebrew / Anaconda3
#-----------------------------------------------------------------------------
LatestOSSys = Platform == LatestOS
LatestOSSys &= PackagePrefix == "ST"
LatestOSSys &= QtIdentification in [ "qt5MP", "qt6MP" ]
LatestOSSys &= RubyPythonID in [ "RsysPsys" ]
LatestOSMacPorts = Platform == LatestOS
LatestOSMacPorts &= PackagePrefix == "LW"
LatestOSMacPorts &= QtIdentification in [ "qt5MP", "qt6MP" ]
LatestOSMacPorts &= RubyPythonID in [ "Rmp32Pmp311", "Rmp32Pmp39" ]
LatestOSMacPorts &= RubyPythonID in [ "Rmp33Pmp311", "Rmp33Pmp39" ]
LatestOSHomebrew = Platform == LatestOS
LatestOSHomebrew &= PackagePrefix == "LW"
LatestOSHomebrew &= QtIdentification in [ "qt5Brew", "qt6Brew" ]
LatestOSHomebrew &= RubyPythonID in [ "Rhb32Phb311", "Rhb32Phb39", "Rhb32Phbauto" ]
LatestOSHomebrew &= RubyPythonID in [ "Rhb33Phb311", "Rhb33Phb39", "Rhb33Phbauto" ]
LatestOSAnaconda3 = Platform == LatestOS
LatestOSAnaconda3 &= PackagePrefix == "LW"
@ -297,8 +312,23 @@ def CheckPkgDirectory():
LatestOSHomebrewH &= QtIdentification in [ "qt5Brew", "qt6Brew" ]
LatestOSHomebrewH &= RubyPythonID in [ "RsysPhb311", "RsysPhb39", "RsysPhbauto" ] # Sys-Homebre hybrid
if LatestOSSys:
mydic = DicStdLightHeavyW["std"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref:
zip_ref.extractall(PkgDir)
os.rename( srcDir, desDir )
if OpClean:
if os.path.isdir(srcDir):
shutil.rmtree(srcDir)
if os.path.isdir(desDir):
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
if LatestOSMacPorts:
mydic = DicLightHeavyW["ports"]
mydic = DicStdLightHeavyW["ports"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
@ -313,7 +343,7 @@ def CheckPkgDirectory():
Item3AppleScript = mydic["item3"]
if LatestOSHomebrew:
mydic = DicLightHeavyW["brew"]
mydic = DicStdLightHeavyW["brew"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
@ -328,7 +358,7 @@ def CheckPkgDirectory():
Item3AppleScript = mydic["item3"]
if LatestOSAnaconda3:
mydic = DicLightHeavyW["ana3"]
mydic = DicStdLightHeavyW["ana3"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
@ -343,7 +373,7 @@ def CheckPkgDirectory():
Item3AppleScript = mydic["item3"]
if LatestOSHomebrewH:
mydic = DicLightHeavyW["brewH"]
mydic = DicStdLightHeavyW["brewH"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python3
#!/Applications/anaconda3/bin/python3
# -*- coding: utf-8 -*-
import sys
@ -8,6 +8,20 @@ import glob
import platform
import optparse
import subprocess
#------------------------------------------------------------------------------
# In general, avoid setting the first line to '#!/usr/bin/env python3'.
# If so, when this script is invoked in the 'KLayoutNightlyBuild.app' script
# bundle created by Automator, the python3 will be the macOS-bundled python3,
# where pandas is not included by default.
# Therefore, it is better to use one of:
# 1) #!/Applications/anaconda3/bin/python3 (Anaconda3)
# 2) #!/usr/local/bin/python3 (Homebrew needs 'pip3 install pandas')
# 3) #!/opt/local/bin/python3 (MacPorts needs 'sudo pip3 install pandas')
#
# However, if we install 'pandas' and its dependencies to the system Python
# environment, we can also set '#!/usr/bin/env python3'.
#------------------------------------------------------------------------------
import pandas as pd
#------------------------------------------------------------------------------
## To test if the platform is a member of valid platforms
@ -44,7 +58,7 @@ def Test_My_Platform( platforms=[ 'Monterey', 'Ventura', 'Sonoma' ] ):
#------------------------------------------------------------------------------
def Get_Build_Target_Dict():
buildTargetDic = dict()
# buildTargetDic[0] = 'std'
buildTargetDic[0] = 'std'
buildTargetDic[1] = 'ports'
buildTargetDic[2] = 'brew'
buildTargetDic[3] = 'brewHW'
@ -61,45 +75,47 @@ def Get_Build_Target_Dict():
# @param[in] platform platform name
#
# @return (dictionary1, dictionary2)-tupple
# dictionary1: key=mnemonic, value=build option list
# dictionary2: key=mnemonic, value=log file name
# dictionary1: key=(qtVer, mnemonic), value=build option list
# dictionary2: key=(qtVer, mnemonic), value=log file name
#------------------------------------------------------------------------------
def Get_Build_Options( targetDic, platform ):
if QtType == 5:
qtType = "Qt5"
else:
qtType = "Qt6"
buildOp = dict()
logfile = dict()
for key in targetDic.keys():
target = targetDic[key]
if target == "std": # use 'Qt5MacPorts' that provides Qt 5.15.2~ to run on "Big Sur", too
buildOp["std"] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'sys' ]
logfile["std"] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPsys")
elif target == "ports":
buildOp["ports"] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP32', '-p', 'MP311' ]
logfile["ports"] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rmp32Pmp311")
elif target == "brew":
buildOp["brew"] = [ '-q', '%sBrew' % qtType, '-r', 'HB32', '-p', 'HB311' ]
logfile["brew"] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb32Phb311")
elif target == "brewHW":
buildOp["brewHW"] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311' ]
logfile["brewHW"] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311")
elif target == "ana3":
buildOp["ana3"] = [ '-q', '%sAna3' % qtType, '-r', 'Ana3', '-p', 'Ana3' ]
logfile["ana3"] = "%sAna3.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rana3Pana3")
elif target == "brewA":
buildOp["brewA"] = [ '-q', '%sBrew' % qtType, '-r', 'HB32', '-p', 'HBAuto' ]
logfile["brewA"] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb32Phbauto")
elif target == "brewAHW":
buildOp["brewAHW"] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto' ]
logfile["brewAHW"] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhbauto")
if WithPymod:
buildOp["ports"] = buildOp["ports"] + ['--buildPymod']
buildOp["brew"] = buildOp["brew"] + ['--buildPymod']
buildOp["ana3"] = buildOp["ana3"] + ['--buildPymod']
for qtVer in [5, 6]:
if qtVer == 5:
qtType = "Qt5"
elif qtVer == 6:
qtType = "Qt6"
for key in targetDic.keys():
target = targetDic[key]
if target == "std":
buildOp[(qtVer, "std")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'sys' ]
logfile[(qtVer, "std")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPsys")
elif target == "ports":
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' ]
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' ]
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' ]
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' ]
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' ]
logfile[(qtVer, "brewAHW")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhbauto")
if WithPymod:
buildOp[(qtVer,"ports")] = buildOp[(qtVer,"ports")] + ['--buildPymod']
buildOp[(qtVer,"brew")] = buildOp[(qtVer,"brew")] + ['--buildPymod']
buildOp[(qtVer,"ana3")] = buildOp[(qtVer,"ana3")] + ['--buildPymod']
return (buildOp, logfile)
@ -109,29 +125,34 @@ def Get_Build_Options( targetDic, platform ):
# @param[in] targetDic build target dictionary
# @param[in] platform platform name
#
# @return a dictionary; key=mnemonic, value=".macQAT" directory
# @return a dictionary; key=(qtVer, mnemonic), value=".macQAT" directory
#------------------------------------------------------------------------------
def Get_QAT_Directory( targetDic, platform ):
if QtType == 5:
qtType = "qt5"
else:
qtType = "qt6"
dirQAT = dict()
for key in targetDic.keys():
target = targetDic[key]
if target == "ports":
dirQAT["ports"] = '%sMP.build.macos-%s-release-Rmp32Pmp311.macQAT' % (qtType, platform)
elif target == "brew":
dirQAT["brew"] = '%sBrew.build.macos-%s-release-Rhb32Phb311.macQAT' % (qtType, platform)
elif target == "brewHW":
dirQAT["brewHW"] = '%sBrew.build.macos-%s-release-RsysPhb311.macQAT' % (qtType, platform)
elif target == "ana3":
dirQAT["ana3"] = '%sAna3.build.macos-%s-release-Rana3Pana3.macQAT' % (qtType, platform)
elif target == "brewA":
dirQAT["brewA"] = '%sBrew.build.macos-%s-release-Rhb32Phbauto.macQAT' % (qtType, platform)
elif target == "brewAHW":
dirQAT["brewAHW"] = '%sBrew.build.macos-%s-release-RsysPhbauto.macQAT' % (qtType, platform)
for qtVer in [5, 6]:
if qtVer == 5:
qtType = "Qt5"
elif qtVer == 6:
qtType = "Qt6"
for key in targetDic.keys():
target = targetDic[key]
if target == "std":
dirQAT[(qtVer, "std")] = '%sMP.build.macos-%s-release-RsysPsys.macQAT' % (qtType.lower(), platform)
elif target == "ports":
dirQAT[(qtVer, "ports")] = '%sMP.build.macos-%s-release-Rmp33Pmp311.macQAT' % (qtType.lower(), platform)
elif target == "brew":
dirQAT[(qtVer, "brew")] = '%sBrew.build.macos-%s-release-Rhb33Phb311.macQAT' % (qtType.lower(), platform)
elif target == "brewHW":
dirQAT[(qtVer, "brewHW")] = '%sBrew.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform)
elif target == "ana3":
dirQAT[(qtVer, "ana3")] = '%sAna3.build.macos-%s-release-Rana3Pana3.macQAT' % (qtType.lower(), platform)
elif target == "brewA":
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)
return dirQAT
#------------------------------------------------------------------------------
@ -142,40 +163,45 @@ def Get_QAT_Directory( targetDic, platform ):
# @param[in] srlDMG serial number of DMG
# @param[in] makeflag True to make; False to clean
#
# @return a dictionary; key=mnemonic, value=build option list
# @return a dictionary; key=(qtVer, mnemonic), value=build option list
#------------------------------------------------------------------------------
def Get_Package_Options( targetDic, platform, srlDMG, makeflag ):
if QtType == 5:
qtType = "qt5"
else:
qtType = "qt6"
packOp = dict()
if makeflag:
flag = '-m'
else:
flag = '-c'
packOp = dict()
for key in targetDic.keys():
target = targetDic[key]
if target == "ports":
packOp["ports"] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rmp32Pmp311' % (qtType, platform),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brew":
packOp["brew"] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb32Phb311' % (qtType, platform),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewHW":
packOp["brewHW"] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhb311' % (qtType, platform),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "ana3":
packOp["ana3"] = [ '-p', 'LW-%sAna3.pkg.macos-%s-release-Rana3Pana3' % (qtType, platform),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewA":
packOp["brewA"] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb32Phbauto' % (qtType, platform),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewAHW":
packOp["brewAHW"] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhbauto' % (qtType, platform),
'-s', '%d' % srlDMG, '%s' % flag ]
for qtVer in [5, 6]:
if qtVer == 5:
qtType = "Qt5"
elif qtVer == 6:
qtType = "Qt6"
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),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "ports":
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),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewHW":
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),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewA":
packOp[(qtVer, "brewA")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb33Phbauto' % (qtType.lower(), platform),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewAHW":
packOp[(qtVer, "brewAHW")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhbauto' % (qtType.lower(), platform),
'-s', '%d' % srlDMG, '%s' % flag ]
return packOp
#------------------------------------------------------------------------------
@ -185,6 +211,7 @@ def Parse_CommandLine_Arguments():
global Usage # usage
global QtType # Qt type
global Target # target list
global QtTarget # list of (Qt, target)-tuple
global Build # operation flag
global WithPymod # operation flag
global QATest # operation flag
@ -198,9 +225,9 @@ def Parse_CommandLine_Arguments():
platform = Test_My_Platform()
if platform in [ "Sonoma", "Ventura", "Monterey" ]:
targetopt = "1,2,3,4"
targetopt = "0,1,2,3,4"
else:
targetopt = "0"
targetopt = None
Usage = "\n"
Usage += "----------------------------------------------------------------------------------------------------------\n"
@ -209,33 +236,39 @@ def Parse_CommandLine_Arguments():
Usage += " macOS Monterey, Ventura, or Sonoma >>\n"
Usage += "\n"
Usage += "$ [python] nightlyBuild.py\n"
Usage += " option & argument : comment on option if any | default value\n"
Usage += " ------------------------------------------------------------------------+--------------\n"
Usage += " [--qt <type>] : 5='qt5', 6='qt6' (migration to Qt6 is ongoing) | 5\n"
Usage += " [--target <list>] : 1='ports', 2='brew', 3='brewHW', 4='ana3', | '%s'\n" % targetopt
Usage += " 5='brewA', 6='brewAHW' |\n"
Usage += " * with --qt=6, use --target='2,3' (4 is ignored) |\n"
Usage += " [--build] : build and deploy | disabled\n"
Usage += " [--pymod] : build and deploy Pymod, too | disabled\n"
Usage += " [--test] : run the QA Test | disabled\n"
Usage += " [--check] : check the QA Test results | disabled\n"
Usage += " [--makedmg|--cleandmg <srlno>] : make or clean DMGs | disabled\n"
Usage += " [--upload <dropbox>] : upload DMGs to $HOME/Dropbox/klayout/<dropbox> | disabled\n"
Usage += " [--dryrun] : dry-run for --build option | disabled\n"
Usage += " [-?|--?] : print this usage and exit | disabled\n"
Usage += " |\n"
Usage += " To use this script, make a symbolic link in the project root by: |\n"
Usage += " $ ln -s ./macbuild/nightlyBuild.py . |\n"
Usage += " |\n"
Usage += " Regular sequence for using this script: |\n"
Usage += " (1) $ ./nightlyBuild.py --build --pymod |\n"
Usage += " (2) (confirm the build results) |\n"
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.12' |\n"
Usage += " (7) $ ./nightlyBuild.py --cleandmg 1 |\n"
Usage += "---------------------------------------------------------------------------+------------------------------\n"
Usage += " option & argument : comment on option if any | default value\n"
Usage += " --------------------------------------------------------------------------+--------------\n"
Usage += " [--qt <type>] : 5='qt5', 6='qt6' (migration to Qt6 is ongoing) | 5\n"
Usage += " [--target <list>] : 0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3', | '%s'\n" % targetopt
Usage += " 5='brewA', 6='brewAHW' |\n"
Usage += " * with --qt=6, use --target='0,1,2,3' (4 is ignored) |\n"
Usage += " [--qttarget <tuple list>] : ex. '5,1' for qt=5, target=1 | disabled\n"
Usage += " + This option supersedes, if used, the --qt and --target combination. |\n"
Usage += " + You can use this option multiple times. |\n"
Usage += " + Or you can pass those list by the 'nightlyBuild.csv' file. |\n"
Usage += " A sample file 'macbuild/nightlyBuild.sample.csv' is available. |\n"
Usage += " [--build] : build and deploy | disabled\n"
Usage += " [--pymod] : build and deploy Pymod, too | disabled\n"
Usage += " [--test] : run the QA Test | disabled\n"
Usage += " [--check] : check the QA Test results | disabled\n"
Usage += " [--makedmg|--cleandmg <srlno>] : make or clean DMGs | disabled\n"
Usage += " [--upload <dropbox>] : upload DMGs to $HOME/Dropbox/klayout/<dropbox> | disabled\n"
Usage += " [--dryrun] : dry-run for --build option | disabled\n"
Usage += " [-?|--?] : print this usage and exit | disabled\n"
Usage += " |\n"
Usage += " To use this script, make a symbolic link in the project root by: |\n"
Usage += " $ ln -s ./macbuild/nightlyBuild.py . |\n"
Usage += " + edit and save ./macbuild/nightlyBuild.csv (optional) |\n"
Usage += " |\n"
Usage += " Regular sequence for using this script: |\n"
Usage += " (1) $ ./nightlyBuild.py --build --pymod |\n"
Usage += " (2) (confirm the build results) |\n"
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 += " (7) $ ./nightlyBuild.py --cleandmg 1 |\n"
Usage += "-----------------------------------------------------------------------------+----------------------------\n"
p = optparse.OptionParser( usage=Usage )
p.add_option( '--qt',
@ -246,6 +279,11 @@ def Parse_CommandLine_Arguments():
dest='targets',
help='build target list' )
p.add_option( '--qttarget',
action='append',
dest='qt_target',
help='(Qt, target)-tuple' )
p.add_option( '--build',
action='store_true',
dest='build',
@ -296,6 +334,7 @@ def Parse_CommandLine_Arguments():
p.set_defaults( qt_type = "5",
targets = "%s" % targetopt,
qt_target = list(),
build = False,
with_pymod = False,
qa_test = False,
@ -331,9 +370,51 @@ def Parse_CommandLine_Arguments():
targetDic = Get_Build_Target_Dict()
Target = list()
for idx in targetIdx:
if idx in range(1, 7):
if idx in range(0, 7):
Target.append( targetDic[idx] )
# Populate QtTarget
QtTarget = list()
for target in Target:
QtTarget.append( (QtType, target) )
QtType = None
Target = None
print( "# The --qt and --target combination specifies..." )
print(QtTarget)
if len(opt.qt_target) == 1 and opt.qt_target[0] == "nightlyBuild.csv": # reserved file name
QtTarget = list()
withqttarget = True
df = pd.read_csv( opt.qt_target[0], comment="#" )
if len(df) == 0:
print( "! --qttarget==nightlyBuild.csv is used but DataFrame is empty" )
print(Usage)
quit()
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]):
QtTarget.append( (qt, targetDic[idx]) )
elif len(opt.qt_target) > 0:
QtTarget = list()
withqttarget = True
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]):
QtTarget.append( (qt, targetDic[idx]) )
else:
withqttarget = False
if withqttarget:
if len(QtTarget) > 0:
print( "# The --qttarget option superseded the --qt and --target combination" )
print(QtTarget)
else:
print( "! --qttarget is used but there is no valid (Qt, target)-tuple" )
print(Usage)
quit()
Build = opt.build
WithPymod = opt.with_pymod
QATest = opt.qa_test
@ -373,17 +454,18 @@ def Build_Deploy():
myPlatform = Test_My_Platform()
buildOp, logfile = Get_Build_Options( Get_Build_Target_Dict(), myPlatform )
for key in Target:
if key == "ana3" and QtType == 6: # anaconda3 does not provide Qt6 so far
for qttype, key in QtTarget:
if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far
continue
deplog = logfile[key].replace( ".log", ".dep.log" )
command1 = [ pyBuilder ] + buildOp[key]
deplog = logfile[(qttype, key)].replace( ".log", ".dep.log" )
if key in [ "brewHW", "brewAHW" ] :
command1 = [ pyBuilder ] + buildOp[(qttype, key)]
if key in [ "std", "brewHW", "brewAHW" ] :
command2 = "time"
command2 += " \\\n %s" % pyBuilder
for option in buildOp[key]:
for option in buildOp[(qttype, key)]:
command2 += " \\\n %s" % option
command2 += " \\\n %s" % '-y'
command2 += " 2>&1 | tee %s; \\\n" % deplog
@ -391,7 +473,7 @@ def Build_Deploy():
else:
command2 = "time"
command2 += " \\\n %s" % pyBuilder
for option in buildOp[key]:
for option in buildOp[(qttype, key)]:
command2 += " \\\n %s" % option
command2 += " \\\n %s" % '-Y'
command2 += " 2>&1 | tee %s; \\\n" % deplog
@ -442,14 +524,14 @@ def Run_QATest( exclude ):
myPlatform = Test_My_Platform()
dirQAT = Get_QAT_Directory( Get_Build_Target_Dict(), myPlatform )
for key in Target:
if key == 4 and QtType == 6: # anaconda3 does not provide Qt6 so far
for qttype, key in QtTarget:
if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far
continue
command1 = [ pyRunnerQAT ] + [ '--run', '--exclude', '%s' % exclude ]
print( dirQAT[key], command1 )
print( dirQAT[(qttype, key)], command1 )
#continue
os.chdir( dirQAT[key] )
os.chdir( dirQAT[(qttype, key)] )
if subprocess.call( command1, shell=False ) != 0:
print( "", file=sys.stderr )
@ -477,14 +559,14 @@ def Check_QATest_Results( lines ):
myPlatform = Test_My_Platform()
dirQAT = Get_QAT_Directory( Get_Build_Target_Dict(), myPlatform )
for key in Target:
if key == 4 and QtType == 6: # anaconda3 does not provide Qt6 so far
for qttype, key in QtTarget:
if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far
continue
os.chdir( dirQAT[key] )
os.chdir( dirQAT[(qttype, key)] )
logfile = glob.glob( "*.log" )
command1 = [ tailCommand ] + [ '-n', '%d' % lines ] + logfile
print( dirQAT[key], command1 )
print( dirQAT[(qttype, key)], command1 )
#continue
if subprocess.call( command1, shell=False ) != 0:
@ -518,11 +600,11 @@ def DMG_Make( srlDMG ):
shutil.rmtree( stashDMG )
os.mkdir( stashDMG )
for key in Target:
if key == 4 and QtType == 6: # anaconda3 does not provide Qt6 so far
for qttype, key in QtTarget:
if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far
continue
command1 = [ pyDMGmaker ] + packOp[key]
command1 = [ pyDMGmaker ] + packOp[(qttype, key)]
print(command1)
#continue
@ -558,11 +640,11 @@ def DMG_Clean( srlDMG ):
if os.path.isdir( stashDMG ):
shutil.rmtree( stashDMG )
for key in Target:
if key == 4 and QtType == 6: # anaconda3 does not provide Qt6 so far
for qttype, key in QtTarget:
if key == "ana3" and qttype == 6: # anaconda3 does not provide Qt6 so far
continue
command1 = [ pyDMGmaker ] + packOp[key]
command1 = [ pyDMGmaker ] + packOp[(qttype, key)]
print(command1)
#continue

View File

@ -0,0 +1,25 @@
#-------------------------------------------------------------------------------
# File: 'nightlyBuild.csv'
#
# Descriptions:
# This file (located where "build.sh" exists) is to be passed to
# 'nightlyBuild.py' via the --qttarget option.
#
# qtVer,target
# where
# qtVer = 5 or 6
# target = [0='std', 1='ports', 2='brew', 3='brewHW', 4='ana3']
# note that
# (qtVer,target)=(6,4) will be omitted
#-------------------------------------------------------------------------------
qtVer,target
5,0
5,1
5,2
5,3
5,4
#6,0
#6,1
#6,2
#6,3
#6,4
Can't render this file because it contains an unexpected character in line 5 and column 30.

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python3
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#==============================================================================

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>668</width>
<height>410</height>
<width>745</width>
<height>438</height>
</rect>
</property>
<property name="windowTitle">
@ -845,7 +845,12 @@
</item>
<item>
<property name="text">
<string>Angle measurement (three mouse clicks)</string>
<string>Auto measure along edge (points will be set automatically)</string>
</property>
</item>
<item>
<property name="text">
<string>Angle or radius measurement (three mouse clicks)</string>
</property>
</item>
<item>

View File

@ -247,6 +247,8 @@ RulerModeConverter::to_string (ant::Template::ruler_mode_type m)
return "single_click";
} else if (m == ant::Template::RulerAutoMetric) {
return "auto_metric";
} else if (m == ant::Template::RulerAutoMetricEdge) {
return "auto_metric_edge";
} else if (m == ant::Template::RulerMultiSegment) {
return "multi_segment";
} else if (m == ant::Template::RulerThreeClicks) {
@ -266,6 +268,8 @@ RulerModeConverter::from_string (const std::string &s, ant::Template::ruler_mode
a = ant::Template::RulerSingleClick;
} else if (t == "auto_metric") {
a = ant::Template::RulerAutoMetric;
} else if (t == "auto_metric_edge") {
a = ant::Template::RulerAutoMetricEdge;
} else if (t == "multi_segment") {
a = ant::Template::RulerMultiSegment;
} else if (t == "angle") {

View File

@ -60,6 +60,9 @@ static std::vector<ant::Template> make_standard_templates ()
templates.push_back (ant::Template (tl::to_string (tr ("Measure")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure"));
templates.back ().set_mode (ant::Template::RulerAutoMetric);
templates.push_back (ant::Template (tl::to_string (tr ("Measure edge")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure_edge"));
templates.back ().set_mode (ant::Template::RulerAutoMetricEdge);
templates.push_back (ant::Template (tl::to_string (tr ("Angle")), "", "", "$(sprintf('%.5g',G))°", ant::Object::STY_line, ant::Object::OL_angle, true, lay::AC_Global, "_angle"));
templates.back ().set_mode (ant::Template::RulerThreeClicks);

View File

@ -1056,8 +1056,18 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
m_drawing (false), m_current (),
m_move_mode (MoveNone),
m_seg_index (0),
m_current_template (0)
{
m_current_template (0),
m_hover (false),
m_hover_wait (false),
m_hover_buttons (0),
m_mouse_in_window (false)
{
#if defined(HAVE_QT)
m_timer.setInterval (100 /*hover time*/);
m_timer.setSingleShot (true);
connect (&m_timer, SIGNAL (timeout ()), this, SLOT (timeout ()));
#endif
mp_view->annotations_changed_event.add (this, &Service::annotations_changed);
}
@ -1809,9 +1819,36 @@ Service::mouse_double_click_event (const db::DPoint & /*p*/, unsigned int button
return false;
}
lay::TwoPointSnapToObjectResult
Service::auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl)
{
// for auto-metric we need some cutline constraint - any or global won't do.
if (ac == lay::AC_Global) {
ac = tpl.angle_constraint ();
}
if (ac == lay::AC_Global) {
ac = m_snap_mode;
}
if (ac == lay::AC_Global) {
ac = lay::AC_Diagonal;
}
db::DVector g;
if (m_grid_snap) {
g = db::DVector (m_grid, m_grid);
}
double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (m_snap_range);
snap_range *= 0.5;
return lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0);
}
bool
Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
hover_reset ();
if (prio && (buttons & lay::LeftButton) != 0) {
const ant::Template &tpl = current_template ();
@ -1852,27 +1889,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
} else if (tpl.mode () == ant::Template::RulerAutoMetric) {
// for auto-metric we need some cutline constraint - any or global won't do.
lay::angle_constraint_type ac = ac_from_buttons (buttons);
if (ac == lay::AC_Global) {
ac = tpl.angle_constraint ();
}
if (ac == lay::AC_Global) {
ac = m_snap_mode;
}
if (ac == lay::AC_Global) {
ac = lay::AC_Diagonal;
}
db::DVector g;
if (m_grid_snap) {
g = db::DVector (m_grid, m_grid);
}
double snap_range = ui ()->mouse_event_trans ().inverted ().ctrans (m_snap_range);
snap_range *= 0.5;
lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0);
lay::TwoPointSnapToObjectResult ee = auto_measure (p, ac_from_buttons (buttons), tpl);
if (ee.any) {
// begin the transaction
@ -1893,6 +1910,29 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
}
} else if (tpl.mode () == ant::Template::RulerAutoMetricEdge) {
lay::PointSnapToObjectResult snap_details = snap1_details (p, true);
if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge) {
// begin the transaction
if (manager ()) {
tl_assert (! manager ()->transacting ());
manager ()->transaction (tl::to_string (tr ("Create ruler")));
}
m_current = ant::Object (snap_details.object_ref.p1 (), snap_details.object_ref.p2 (), 0, tpl);
show_message ();
insert_ruler (m_current, true);
// end the transaction
if (manager ()) {
manager ()->commit ();
}
}
} else {
m_p1 = snap1 (p, m_obj_snap && tpl.snap ()).second;
@ -1968,21 +2008,33 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type
bool
Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
if (prio) {
if (! prio) {
return false;
}
lay::PointSnapToObjectResult snap_details;
if (m_drawing) {
snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons));
} else {
const ant::Template &tpl = current_template ();
snap_details = snap1_details (p, m_obj_snap && tpl.snap ());
}
if (! m_drawing && m_mouse_in_window && view ()->transient_selection_mode ()) {
mouse_cursor_from_snap_details (snap_details);
// Restart hover timer
m_hover_wait = true;
#if defined(HAVE_QT)
m_timer.start ();
#endif
m_hover_point = p;
m_hover_buttons = buttons;
}
if (m_drawing && prio) {
lay::PointSnapToObjectResult snap_details;
if (m_drawing) {
snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons));
} else {
const ant::Template &tpl = current_template ();
snap_details = snap1_details (p, m_obj_snap && tpl.snap () && (tpl.mode () != ant::Template::RulerAutoMetricEdge || ! view ()->transient_selection_mode ()));
}
mouse_cursor_from_snap_details (snap_details);
if (m_drawing) {
set_cursor (lay::Cursor::cross);
@ -2284,6 +2336,84 @@ Service::click_proximity (const db::DPoint &pos, lay::Editable::SelectionMode mo
}
}
bool
Service::enter_event (bool /*prio*/)
{
m_mouse_in_window = true;
return false;
}
bool
Service::leave_event (bool)
{
m_mouse_in_window = false;
hover_reset ();
return false;
}
void
Service::hover_reset ()
{
if (m_hover_wait) {
#if defined(HAVE_QT)
m_timer.stop ();
#endif
m_hover_wait = false;
}
if (m_hover) {
// as we use the transient selection for the hover ruler, we have to remove it here
clear_transient_selection ();
m_hover = false;
}
}
#if defined(HAVE_QT)
void
Service::timeout ()
{
m_hover_wait = false;
m_hover = true;
// as we use the transient selection for the hover ruler, we have to remove it here
clear_transient_selection ();
// transiently create an auto-metric ruler if requested
ant::Object *ruler = 0;
const ant::Template &tpl = current_template ();
if (tpl.mode () == ant::Template::RulerAutoMetric) {
lay::TwoPointSnapToObjectResult ee = auto_measure (m_hover_point, ac_from_buttons (m_hover_buttons), tpl);
if (ee.any) {
m_current = ant::Object (ee.first, ee.second, 0, tpl);
ruler = &m_current;
}
} else if (tpl.mode () == ant::Template::RulerAutoMetricEdge) {
lay::PointSnapToObjectResult snap_details = snap1_details (m_hover_point, true);
if (snap_details.object_snap == lay::PointSnapToObjectResult::ObjectEdge) {
m_current = ant::Object (snap_details.object_ref.p1 (), snap_details.object_ref.p2 (), 0, tpl);
ruler = &m_current;
}
}
if (ruler) {
// HINT: there is no special style for "transient selection on rulers"
mp_transient_ruler = new ant::View (this, ruler, true /*not selected*/);
if (! editables ()->has_selection ()) {
display_status (true);
}
}
}
#endif
bool
Service::transient_select (const db::DPoint &pos)
{

View File

@ -39,6 +39,11 @@
#include <map>
#include <vector>
#if defined (HAVE_QT)
# include <QTimer>
# include <QObject>
#endif
namespace ant {
class LayoutViewBase;
@ -177,12 +182,19 @@ private:
// -------------------------------------------------------------
class ANT_PUBLIC Service
: public lay::EditorServiceBase,
class ANT_PUBLIC Service :
#if defined (HAVE_QT)
public QObject,
#endif
public lay::EditorServiceBase,
public lay::Drawing,
public db::Object
{
public:
#if defined (HAVE_QT)
Q_OBJECT
#endif
public:
typedef lay::AnnotationShapes::iterator obj_iterator;
/**
@ -341,6 +353,21 @@ public:
*/
virtual db::DBox selection_bbox ();
/**
* @brief Implementation of the editables API
*/
virtual bool enter_event (bool);
/**
* @brief Implementation of the editables API
*/
virtual bool leave_event (bool);
/**
* @brief Implementation of the editables API
*/
virtual void hover_reset ();
/**
* @brief Transform the selection (reimplementation of lay::Editable interface)
*/
@ -506,6 +533,11 @@ public:
*/
tl::Event annotation_selection_changed_event;
#if defined (HAVE_QT)
public slots:
void timeout ();
#endif
private:
// Ruler display and snapping configuration
tl::Color m_color;
@ -551,10 +583,22 @@ private:
std::vector<ant::Template> m_ruler_templates;
unsigned int m_current_template;
// Hover detector
bool m_hover;
bool m_hover_wait;
db::DPoint m_hover_point;
unsigned int m_hover_buttons;
#if defined (HAVE_QT)
QTimer m_timer;
#endif
bool m_mouse_in_window;
std::pair<bool, db::DPoint> snap1 (const db::DPoint &p, bool obj_snap);
lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap);
std::pair<bool, db::DPoint> snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
lay::TwoPointSnapToObjectResult auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl);
const ant::Template &current_template () const;

View File

@ -64,15 +64,20 @@ public:
*/
RulerAutoMetric = 2,
/**
* @brief The ruler is auto-metric along an edge: a single click will place a ruler and the ruler will extend to the edge below
*/
RulerAutoMetricEdge = 3,
/**
* @brief The ruler an angle type (two segments, three mouse clicks) for angle and circle radius measurements
*/
RulerThreeClicks = 3,
RulerThreeClicks = 4,
/**
* @brief The ruler is a multi-segment type
*/
RulerMultiSegment = 4
RulerMultiSegment = 5
};
/**

View File

@ -434,6 +434,11 @@ static int ruler_mode_auto_metric ()
return ant::Template::RulerAutoMetric;
}
static int ruler_mode_auto_metric_edge ()
{
return ant::Template::RulerAutoMetricEdge;
}
static int ruler_mode_three_clicks ()
{
return ant::Template::RulerThreeClicks;
@ -525,6 +530,12 @@ gsi::Class<AnnotationRef> decl_Annotation (decl_BasicAnnotation, "lay", "Annotat
"\n"
"This constant has been introduced in version 0.25"
) +
gsi::method ("RulerModeAutoMetricEdge", &gsi::ruler_mode_auto_metric_edge,
"@brief Specifies edge-sensitive auto-metric ruler mode for the \\register_template method\n"
"In auto-metric mode, a ruler can be placed with a single click and p1/p2 will be determined from the edge it is placed on.\n"
"\n"
"This constant has been introduced in version 0.29"
) +
gsi::method ("RulerThreeClicks", &gsi::ruler_mode_three_clicks,
"@brief Specifies three-click ruler mode for the \\register_template method\n"
"In this ruler mode, two segments are created for angle and circle radius measurements. Three mouse clicks are required.\n"

View File

@ -154,6 +154,10 @@ CommonReaderBase::rename_cell (db::Layout &layout, size_t id, const std::string
common_reader_error (tl::sprintf (tl::to_string (tr ("Cell named %s with ID %ld was already given name %s")), cn, id, iid->second.first));
}
if (iname != m_name_map.end () && iname->second.first != null_id && iname->second.first != id) {
common_reader_error (tl::sprintf (tl::to_string (tr ("Same cell name %s, but different IDs: %ld and %ld")), cn, id, iname->second.first));
}
if (iid != m_id_map.end () && iname != m_name_map.end ()) {
if (iname->second.second != iid->second.second) {

View File

@ -1566,23 +1566,26 @@ compute_rounded (const db::DPolygon &polygon, double rinner, double router, unsi
}
// -------------------------------------------------------------------------
// Implementation of AreaMap
// Implementation of area_map
AreaMap::AreaMap ()
template <class C>
area_map<C>::area_map ()
: m_nx (0), m_ny (0)
{
mp_av = 0;
}
AreaMap::AreaMap (const AreaMap &other)
template <class C>
area_map<C>::area_map (const area_map &other)
: m_nx (0), m_ny (0)
{
mp_av = 0;
operator= (other);
}
AreaMap &
AreaMap::operator= (const AreaMap &other)
template <class C>
area_map<C> &
area_map<C>::operator= (const area_map &other)
{
if (this != &other) {
// TODO: this could be copy on write
@ -1594,21 +1597,24 @@ AreaMap::operator= (const AreaMap &other)
return *this;
}
AreaMap::AreaMap (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny)
template <class C>
area_map<C>::area_map (const area_map::point_type &p0, const area_map::vector_type &d, size_t nx, size_t ny)
: m_p0 (p0), m_d (d), m_p (d), m_nx (nx), m_ny (ny)
{
mp_av = new area_type [nx * ny];
clear ();
}
AreaMap::AreaMap (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny)
template <class C>
area_map<C>::area_map (const area_map::point_type &p0, const area_map::vector_type &d, const area_map::vector_type &p, size_t nx, size_t ny)
: m_p0 (p0), m_d (d), m_p (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())), m_nx (nx), m_ny (ny)
{
mp_av = new area_type [nx * ny];
clear ();
}
AreaMap::~AreaMap ()
template <class C>
area_map<C>::~area_map ()
{
if (mp_av) {
delete[] mp_av;
@ -1616,18 +1622,20 @@ AreaMap::~AreaMap ()
mp_av = 0;
}
template <class C>
void
AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny)
area_map<C>::reinitialize (const area_map::point_type &p0, const area_map::vector_type &d, size_t nx, size_t ny)
{
reinitialize (p0, d, d, nx, ny);
}
template <class C>
void
AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny)
area_map<C>::reinitialize (const area_map::point_type &p0, const area_map::vector_type &d, const area_map::vector_type &p, size_t nx, size_t ny)
{
m_p0 = p0;
m_d = d;
m_p = db::Vector (std::min (d.x (), p.x ()), std::min (d.y (), p.y ()));
m_p = vector_type (std::min (d.x (), p.x ()), std::min (d.y (), p.y ()));
if (nx != m_nx || ny != m_ny) {
@ -1645,8 +1653,9 @@ AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, const db::Vecto
clear ();
}
template <class C>
void
AreaMap::clear ()
area_map<C>::clear ()
{
if (mp_av) {
area_type *a = mp_av;
@ -1656,8 +1665,9 @@ AreaMap::clear ()
}
}
template <class C>
void
AreaMap::swap (AreaMap &other)
area_map<C>::swap (area_map &other)
{
std::swap (m_p0, other.m_p0);
std::swap (m_d, other.m_d);
@ -1667,8 +1677,9 @@ AreaMap::swap (AreaMap &other)
std::swap (mp_av, other.mp_av);
}
AreaMap::area_type
AreaMap::total_area () const
template <class C>
typename area_map<C>::area_type
area_map<C>::total_area () const
{
area_type asum = 0;
if (mp_av) {
@ -1680,16 +1691,21 @@ AreaMap::total_area () const
return asum;
}
db::Box
AreaMap::bbox () const
template <class C>
typename area_map<C>::box_type
area_map<C>::bbox () const
{
if (m_nx == 0 || m_ny == 0) {
return db::Box ();
return box_type ();
} else {
return db::Box (m_p0, m_p0 + db::Vector (db::Coord (m_nx - 1) * m_d.x () + m_p.x (), db::Coord (m_ny - 1) * m_d.y () + m_p.y ()));
return box_type (m_p0, m_p0 + vector_type (C (m_nx - 1) * m_d.x () + m_p.x (), C (m_ny - 1) * m_d.y () + m_p.y ()));
}
}
// explicit instantiations
template class area_map<db::Coord>;
template class area_map<db::DCoord>;
// -------------------------------------------------------------------------
// Implementation of rasterize
@ -1707,29 +1723,69 @@ static bool edge_is_partially_left_of (const db::Edge &e, const db::Edge &e_orig
}
}
bool
rasterize (const db::Polygon &polygon, db::AreaMap &am)
static bool edge_is_partially_left_of (const db::DEdge &e, const db::DEdge &e_original, db::DCoord x)
{
typedef db::AreaMap::area_type area_type;
db::Box box = am.bbox ();
db::Box pbox = polygon.box ();
DCoord xmin = db::edge_xmin (e);
if (db::coord_traits<db::DCoord>::less (xmin, x)) {
return true;
} else if (db::coord_traits<db::DCoord>::equal (xmin, x) && ! db::coord_traits<db::DCoord>::equal (e_original.dx (), 0)) {
// the skew edge is cut partially rendering a straight vertical line (due to rounding)
// which we will count as "left of"
return true;
} else {
return false;
}
}
static size_t npixels_floor (db::Coord d, db::Coord p)
{
return size_t (std::max (db::Coord (0), d / p));
}
static size_t npixels_ceil (db::Coord d, db::Coord p)
{
return size_t (std::max (db::Coord (0), (d + p - 1) / p));
}
static size_t npixels_floor (db::DCoord d, db::DCoord p)
{
return size_t (std::max (db::DCoord (0), floor (d / p + db::epsilon)));
}
static size_t npixels_ceil (db::DCoord d, db::DCoord p)
{
return size_t (std::max (db::DCoord (0), ceil (d / p - db::epsilon)));
}
template <class C>
static
bool
rasterize_impl (const db::polygon<C> &polygon, db::area_map<C> &am)
{
typedef typename db::area_map<C>::area_type area_type;
typedef db::box<C> box_type;
typedef db::edge<C> edge_type;
box_type box = am.bbox ();
box_type pbox = polygon.box ();
// check if the polygon overlaps the rasterization area. Otherwise, we simply do nothing.
if (! pbox.overlaps (box)) {
return false;
}
db::Coord ymin = box.bottom (), ymax = box.top ();
db::Coord dy = am.d ().y (), dx = am.d ().x ();
db::Coord py = am.p ().y (), px = am.p ().x ();
db::Coord y0 = am.p0 ().y (), x0 = am.p0 ().x ();
C ymin = box.bottom (), ymax = box.top ();
C dy = am.d ().y (), dx = am.d ().x ();
C py = am.p ().y (), px = am.p ().x ();
C y0 = am.p0 ().y (), x0 = am.p0 ().x ();
size_t ny = am.ny (), nx = am.nx ();
size_t iy0 = std::min (ny, size_t (std::max (db::Coord (0), (pbox.bottom () - am.p0 ().y ()) / am.d ().y ())));
size_t iy1 = std::min (ny, size_t (std::max (db::Coord (0), (pbox.top () - am.p0 ().y () + am.d ().y () - 1) / am.d ().y ())));
size_t iy0 = std::min (ny, npixels_floor (pbox.bottom () - am.p0 ().y (), am.d ().y ()));
size_t iy1 = std::min (ny, npixels_ceil (pbox.top () - am.p0 ().y (), am.d ().y ()));
size_t ix0 = std::min (nx, size_t (std::max (db::Coord (0), (pbox.left () - am.p0 ().x ()) / am.d ().x ())));
size_t ix1 = std::min (nx, size_t (std::max (db::Coord (0), (pbox.right () - am.p0 ().x () + am.d ().x () - 1) / am.d ().x ())));
size_t ix0 = std::min (nx, npixels_floor (pbox.left () - am.p0 ().x (), am.d ().x ()));
size_t ix1 = std::min (nx, npixels_ceil (pbox.right () - am.p0 ().x (), am.d ().x ()));
// no scanning required (i.e. degenerated polygon) -> do nothing
if (iy0 == iy1 || ix0 == ix1) {
@ -1738,26 +1794,26 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
// collect edges
size_t n = 0;
for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) {
for (typename db::polygon<C>::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) {
if ((*e).dy () != 0 && db::edge_ymax (*e) > ymin && db::edge_ymin (*e) < ymax) {
++n;
}
}
std::vector <db::Edge> edges;
std::vector <edge_type> edges;
edges.reserve (n);
for (db::Polygon::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) {
for (typename db::polygon<C>::polygon_edge_iterator e = polygon.begin_edge (); ! e.at_end (); ++e) {
if ((*e).dy () != 0 && db::edge_ymax (*e) > ymin && db::edge_ymin (*e) < ymax) {
edges.push_back (*e);
}
}
// sort edges
std::sort (edges.begin (), edges.end (), db::edge_ymin_compare<db::Coord> ());
std::sort (edges.begin (), edges.end (), db::edge_ymin_compare<C> ());
std::vector <db::Edge>::iterator c = edges.begin ();
typename std::vector <edge_type>::iterator c = edges.begin ();
db::Coord y = y0 + dy * db::Coord (iy0);
C y = y0 + dy * C (iy0);
while (c != edges.end () && db::edge_ymax (*c) <= y) {
++c;
@ -1767,36 +1823,36 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
return false;
}
std::vector <db::Edge>::iterator f = c;
typename std::vector <edge_type>::iterator f = c;
for (size_t iy = iy0; iy < iy1; ++iy) {
db::Coord yy = y + py;
C yy = y + py;
while (f != edges.end () && db::edge_ymin (*f) < yy) {
++f;
}
std::sort (c, f, db::edge_xmin_compare <db::Coord> ());
std::sort (c, f, db::edge_xmin_compare <C> ());
db::Coord x = x0 + dx * db::Coord (ix0);
db::Coord xl = pbox.left ();
C x = x0 + dx * C (ix0);
C xl = pbox.left ();
area_type a = 0;
std::vector <db::Edge>::iterator cc = c;
typename std::vector <edge_type>::iterator cc = c;
while (cc != edges.end () && cc != f && db::edge_xmax (*cc) <= x) {
db::Coord y1 = std::max (y, std::min (yy, cc->p1 ().y ()));
db::Coord y2 = std::max (y, std::min (yy, cc->p2 ().y ()));
C y1 = std::max (y, std::min (yy, cc->p1 ().y ()));
C y2 = std::max (y, std::min (yy, cc->p2 ().y ()));
a += area_type (px) * area_type (y2 - y1);
++cc;
}
std::vector <db::Edge>::iterator ff = cc;
typename std::vector <edge_type>::iterator ff = cc;
for (size_t ix = ix0; ix < ix1; ++ix) {
db::Coord xx = x + px;
db::Coord xxx = x + dx;
C xx = x + px;
C xxx = x + dx;
// TODO: edge_xmin_at_interval(y, yy) and edge_xmax.. would be more efficient in the
// all-angle case. However, it is crucial that the edge clipping produces
@ -1807,7 +1863,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
++ff;
}
std::vector <db::Edge>::iterator fff = ff;
typename std::vector <edge_type>::iterator fff = ff;
if (xx < xxx) {
while (fff != f && db::edge_xmin (*fff) < xxx) {
@ -1818,11 +1874,11 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
if (xl < x) {
// consider all edges or parts of those left of the first cell
db::Box left (xl, y, x, yy);
box_type left (xl, y, x, yy);
for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
for (typename std::vector <edge_type>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (left);
std::pair<bool, edge_type> ec = e->clipped (left);
if (ec.first && edge_is_partially_left_of (ec.second, *e, x)) {
a += area_type (ec.second.dy ()) * area_type (px);
}
@ -1835,11 +1891,11 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
if (dx == py) {
db::Box cell (x, y, xx, yy);
box_type cell (x, y, xx, yy);
for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
for (typename std::vector <edge_type>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (cell);
std::pair<bool, edge_type> ec = e->clipped (cell);
if (ec.first && edge_is_partially_left_of (ec.second, *e, xx)) {
aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2;
a += area_type (ec.second.dy ()) * area_type (px);
@ -1849,22 +1905,22 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
} else {
db::Box cell (x, y, xx, yy);
box_type cell (x, y, xx, yy);
for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
for (typename std::vector <edge_type>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (cell);
std::pair<bool, edge_type> ec = e->clipped (cell);
if (ec.first && edge_is_partially_left_of (ec.second, *e, xx)) {
aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2;
}
}
db::Box wide_cell (x, y, x + dx, yy);
box_type wide_cell (x, y, x + dx, yy);
for (std::vector <db::Edge>::iterator e = cc; e != fff; ++e) {
for (typename std::vector <edge_type>::iterator e = cc; e != fff; ++e) {
std::pair<bool, db::Edge> wide_ec = e->clipped (wide_cell);
std::pair<bool, edge_type> wide_ec = e->clipped (wide_cell);
if (wide_ec.first && edge_is_partially_left_of (wide_ec.second, *e, x + dx)) {
a += area_type (wide_ec.second.dy ()) * area_type (px);
}
@ -1880,7 +1936,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
ff = fff;
for (std::vector <db::Edge>::iterator ccx = cc; ccx != ff; ++ccx) {
for (typename std::vector <edge_type>::iterator ccx = cc; ccx != ff; ++ccx) {
if (db::edge_xmax (*ccx) <= x) {
std::swap (*ccx, *cc);
++cc;
@ -1898,7 +1954,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
y = yy;
for (std::vector <db::Edge>::iterator cx = c; cx != f; ++cx) {
for (typename std::vector <edge_type>::iterator cx = c; cx != f; ++cx) {
if (db::edge_ymax (*cx) <= y) {
std::swap (*cx, *c);
++c;
@ -1910,6 +1966,18 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
return true;
}
bool
rasterize (const db::Polygon &polygon, db::AreaMap &am)
{
return rasterize_impl<db::Coord> (polygon, am);
}
bool
rasterize (const db::DPolygon &polygon, db::DAreaMap &am)
{
return rasterize_impl<db::DCoord> (polygon, am);
}
// -------------------------------------------------------------------------
// Implementation of Minkowski sum

View File

@ -493,55 +493,59 @@ bool DB_PUBLIC is_non_orientable_polygon (const db::Polygon &poly, std::vector<d
* It is used for example by the rasterize function to collect area values
* on a per-pixel basis.
*/
class DB_PUBLIC AreaMap
template <class C>
class DB_PUBLIC area_map
{
public:
typedef db::coord_traits<db::Coord>::area_type area_type;
typedef typename db::coord_traits<C>::area_type area_type;
typedef db::point<C> point_type;
typedef db::vector<C> vector_type;
typedef db::box<C> box_type;
/**
* @brief Constructor
*/
AreaMap ();
area_map ();
/**
* @brief Copy constructor
*/
AreaMap (const AreaMap &);
area_map (const area_map &);
/**
* @brief Constructor
*/
AreaMap (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny);
area_map (const point_type &p0, const vector_type &d, size_t nx, size_t ny);
/**
* @brief Constructor with pixel size
*/
AreaMap (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny);
area_map (const point_type &p0, const vector_type &d, const vector_type &p, size_t nx, size_t ny);
/**
* @brief Destructor
*/
~AreaMap ();
~area_map ();
/**
* @brief Assignment
*/
AreaMap &operator= (const AreaMap &);
area_map &operator= (const area_map &);
/**
* @brief Reinitialize
*/
void reinitialize (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny);
void reinitialize (const point_type &p0, const vector_type &d, size_t nx, size_t ny);
/**
* @brief Reinitialize with pixel size
*/
void reinitialize (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny);
void reinitialize (const point_type &p0, const vector_type &d, const vector_type &p, size_t nx, size_t ny);
/**
* @brief Swap of two maps
*/
void swap (AreaMap &other);
void swap (area_map &other);
/**
* @brief Get the area of one pixel
@ -578,7 +582,7 @@ public:
/**
* @brief The origin
*/
const db::Point &p0 () const
const point_type &p0 () const
{
return m_p0;
}
@ -586,7 +590,7 @@ public:
/**
* @brief Move the origin
*/
void move (const db::Vector &d)
void move (const vector_type &d)
{
m_p0 += d;
}
@ -594,7 +598,7 @@ public:
/**
* @brief The per-pixel displacement vector (pixel size)
*/
const db::Vector &d () const
const vector_type &d () const
{
return m_d;
}
@ -602,7 +606,7 @@ public:
/**
* @brief The pixel size (must be less than d)
*/
const db::Vector &p () const
const vector_type &p () const
{
return m_p;
}
@ -610,7 +614,7 @@ public:
/**
* @brief Compute the bounding box of the area map
*/
db::Box bbox () const;
box_type bbox () const;
/**
* @brief Compute the total area
@ -632,12 +636,15 @@ public:
private:
area_type *mp_av;
db::Point m_p0;
db::Vector m_d;
db::Vector m_p;
point_type m_p0;
vector_type m_d;
vector_type m_p;
size_t m_nx, m_ny;
};
typedef area_map<db::Coord> AreaMap;
typedef area_map<db::DCoord> DAreaMap;
/**
* @brief Rasterize the polygon into the given area map
*
@ -648,6 +655,16 @@ private:
*/
bool DB_PUBLIC rasterize (const db::Polygon &polygon, db::AreaMap &am);
/**
* @brief Rasterize the polygon into the given area map (double version)
*
* This will decompose the polygon and produce per-pixel area values for the given
* polygon. The area contributions will be added to the given area map.
*
* Returns a value indicating whether the map will be non-empty.
*/
bool DB_PUBLIC rasterize (const db::DPolygon &polygon, db::DAreaMap &am);
/**
* @brief Minkowski sum of an edge and a polygon
*/

View File

@ -788,6 +788,40 @@ size_dvm (db::Region *region, const db::Vector &dv, unsigned int mode)
return *region;
}
static std::vector<std::vector<double> >
rasterize2 (const db::Region *region, const db::Point &origin, const db::Vector &pixel_distance, const db::Vector &pixel_size, unsigned int nx, unsigned int ny)
{
db::DAreaMap am (db::DPoint (origin), db::DVector (pixel_distance), db::DVector (pixel_size), nx, ny);
auto pi = region->begin ();
pi = pi.confined (db::Box (am.bbox ()), false /*not overlapping*/);
while (! pi.at_end ()) {
db::DPolygon dp (*pi);
db::rasterize (dp, am);
++pi;
}
std::vector<std::vector<double> > result;
result.reserve (ny);
for (unsigned int y = 0; y < ny; ++y) {
result.push_back (std::vector<double> ());
std::vector<double> &row = result.back ();
row.reserve (nx);
for (unsigned int x = 0; x < nx; ++x) {
row.push_back (am.get (x, y));
}
}
return result;
}
static std::vector<std::vector<double> >
rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector &pixel_size, unsigned int nx, unsigned int ny)
{
return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny);
}
static db::Point default_origin;
// provided by gsiDeclDbPolygon.cc:
@ -3095,6 +3129,36 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"@brief Converts the region to a string\n"
"This version allows specification of the maximum number of polygons contained in the string."
) +
method_ext ("rasterize", &rasterize1, gsi::arg ("origin"), gsi::arg ("pixel_size"), gsi::arg ("nx"), gsi::arg ("ny"),
"@brief A grayscale rasterizer delivering the area covered per pixel\n"
"@param origin The lower-left corner of the lowest-left pixel\n"
"@param pixel_size The dimension of each pixel (the x component gives the width, the y component the height)\n"
"@param nx The number of pixels in horizontal direction\n"
"@param ny The number of pixels in vertical direction\n"
"The method will create a grayscale, high-resolution density map of a rectangular region.\n"
"The scan region is defined by the origin, the pixel size and the number of pixels in horizontal (nx) and\n"
"vertical (ny) direction. The resulting array will contain the area covered by polygons from the region\n"
"in square database units.\n"
"\n"
"For non-overlapping polygons, the maximum density value is px*py. Overlapping polygons are counted multiple\n"
"times, so the actual values may be larger. If you want overlaps removed, you have to\n"
"merge the region before. Merge semantics does not apply for the 'rasterize' method.\n"
"\n"
"The resulting area values are precise within the limits of double-precision floating point arithmetics.\n"
"\n"
"A second version exists that allows specifying an active pixel size which is smaller than the\n"
"pixel distance hence allowing pixels samples that do not cover the full area, but leave gaps between the pixels.\n"
"\n"
"This method has been added in version 0.29.\n"
) +
method_ext ("rasterize", &rasterize2, gsi::arg ("origin"), gsi::arg ("pixel_distance"), gsi::arg ("pixel_size"), gsi::arg ("nx"), gsi::arg ("ny"),
"@brief A version of 'rasterize' that allows a pixel step distance which is larger than the pixel size\n"
"This version behaves like the first variant of 'rasterize', but the pixel distance (pixel-to-pixel step raster)\n"
"can be specified separately from the pixel size. Currently, the pixel size must be equal or smaller than the\n"
"pixel distance - i.e. the pixels must not overlap.\n"
"\n"
"This method has been added in version 0.29.\n"
) +
method ("enable_progress", &db::Region::enable_progress, gsi::arg ("label"),
"@brief Enable progress reporting\n"
"After calling this method, the region will report the progress through a progress bar while "

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,12 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>
<title>DRC Reference: Global Functions</title>
<keyword name="global"/>
<p>
Some functions are available on global level and can be used without any object.
Most of them are convenience functions that basically act on some default object
or provide function-like alternatives for the methods.
</p>
<h2-index/>
<a name="angle"/><h2>"angle" - In universal DRC context: selects edges based on their orientation</h2>
<keyword name="angle"/>
@ -828,7 +833,7 @@ The following example selects all shapes which are rectangles and
whose area is larger than 0.5 square micrometers:
</p><p>
<pre>
out = in.drc(if_all(area &gt; 0.5, rectangle))
out = in.drc(if_all(area &gt; 0.5, rectangles))
</pre>
</p><p>
The condition expressions may be of any type (edges, edge pairs and polygons).

View File

@ -1,12 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>
<title>DRC Reference: Layer Object</title>
<keyword name="Layer"/>
<p>
The layer object represents a collection of polygons, edges or edge pairs.
</p>
<h2-index/>
<a name="&amp;"/><h2>"&amp;" - Boolean AND operation</h2>
<keyword name="&amp;"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "klayout_doc.dtd">
<!-- generated by /home/matthias/klayout/master/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- generated by /home/matthias/klayout/0.28/scripts/drc_lvs_doc/extract_doc.rb -->
<!-- DO NOT EDIT! -->
<doc>

View File

@ -555,7 +555,7 @@ module DRC
# whose area is larger than 0.5 square micrometers:
#
# @code
# out = in.drc(if_all(area > 0.5, rectangle))
# out = in.drc(if_all(area > 0.5, rectangles))
# @/code
#
# The condition expressions may be of any type (edges, edge pairs and polygons).

View File

@ -105,15 +105,22 @@ MouseTracker::mouse_move_event (const db::DPoint &p, unsigned int /*buttons*/, b
double max_coord = 1e30; // big enough I guess
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set_line_style (m_cursor_line_style);
mp_markers.back ()->set_color (m_cursor_color);
mp_markers.back ()->set (db::DEdge (db::DPoint (tp.x (), -max_coord), db::DPoint (tp.x (), max_coord)));
for (int i = 0; i < 2; ++i) {
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set_line_style (m_cursor_line_style);
mp_markers.back ()->set_color (m_cursor_color);
mp_markers.back ()->set (db::DEdge (db::DPoint (-max_coord, tp.y ()), db::DPoint (max_coord, tp.y ())));
mp_markers.push_back (new lay::DMarker (mp_view));
mp_markers.back ()->set_line_style (m_cursor_line_style);
mp_markers.back ()->set_line_width (1);
mp_markers.back ()->set_halo (false);
mp_markers.back ()->set_dither_pattern (1);
mp_markers.back ()->set_color (m_cursor_color.is_valid () ? m_cursor_color : mp_view->canvas ()->foreground_color ());
if (i == 0) {
mp_markers.back ()->set (db::DEdge (db::DPoint (tp.x (), -max_coord), db::DPoint (tp.x (), max_coord)));
} else {
mp_markers.back ()->set (db::DEdge (db::DPoint (-max_coord, tp.y ()), db::DPoint (max_coord, tp.y ())));
}
}
}

View File

@ -305,10 +305,7 @@ MoveService::handle_click (const db::DPoint &p, unsigned int buttons, bool drag_
if (mp_editables->begin_move (p, ac_from_buttons (buttons))) {
lay::SelectionService *selector = mp_view->selection_service ();
if (selector) {
selector->hover_reset ();
}
ui ()->hover_reset ();
mp_view->clear_transient_selection ();

View File

@ -69,14 +69,7 @@ public:
virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual bool mouse_double_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
virtual bool wheel_event (int delta, bool horizontal, const db::DPoint &p, unsigned int buttons, bool prio);
/**
* @brief Reset the hover timer for the transient selection
*
* This method may be used by other services (in particular Move) to avoid the transient to
* be triggered from a move operation.
*/
void hover_reset ();
virtual void hover_reset ();
#if defined (HAVE_QT)
public slots:

View File

@ -1051,7 +1051,15 @@ ViewObjectUI::drag_cancel ()
}
}
namespace
void
ViewObjectUI::hover_reset ()
{
for (service_iterator svc = begin_services (); svc != end_services (); ++svc) {
(*svc)->hover_reset ();
}
}
namespace
{
struct z_order_compare_f
{

View File

@ -147,6 +147,17 @@ public:
virtual bool drop_event (const db::DPoint & /*p*/, const DragDropDataBase * /*data*/) { return false; }
#endif
/**
* @brief Hover reset request
*
* This event is issued for services providing some "hover" mode - i.e. capture
* mouse move events and start a timer on them.
*
* The implementation of this event should cancel this timer and
* not raise a hover condition.
*/
virtual void hover_reset () { }
/**
* @brief Mouse press event handler
*
@ -605,6 +616,11 @@ public:
*/
void drag_cancel ();
/**
* @brief Calls hover_reset on all services
*/
void hover_reset ();
/**
* @brief CanvasPlane rendering
*

View File

@ -55,9 +55,6 @@
</property>
<item row="1" column="1">
<widget class="lay::ColorButton" name="color_pb">
<property name="toolTip">
<string>The color in which the rulers are drawn</string>
</property>
<property name="text">
<string/>
</property>
@ -151,9 +148,6 @@
</item>
<item row="0" column="1">
<widget class="lay::ColorButton" name="color_chc">
<property name="toolTip">
<string>The color in which the rulers are drawn</string>
</property>
<property name="text">
<string/>
</property>

View File

@ -972,17 +972,31 @@ SimpleColorButton::set_color_internal (QColor c)
m_color = c;
QFontMetrics fm (font (), this);
QRect rt (fm.boundingRect (QObject::tr ("Auto"))); // dummy text to be compliant with the other color button
QPixmap pxmp (rt.width () + 24, rt.height ());
QRect rt (fm.boundingRect (QObject::tr ("XXXXXXX")));
#if QT_VERSION >= 0x050000
double dpr = devicePixelRatio ();
#else
double dpr = 1.0;
#endif
QPixmap pxmp (rt.width () * dpr, rt.height () * dpr);
#if QT_VERSION >= 0x050000
pxmp.setDevicePixelRatio (dpr);
#endif
QPainter pxpainter (&pxmp);
QColor text_color = palette ().color (QPalette::Active, QPalette::Text);
pxpainter.setPen (QPen (text_color));
pxpainter.setBrush (QBrush (c.isValid () ? c : QColor (128, 128, 128)));
QRect r (0, 0, pxmp.width () - 1, pxmp.height () - 1);
QPen frame_pen (text_color);
frame_pen.setWidthF (1.0);
frame_pen.setJoinStyle (Qt::MiterJoin);
pxpainter.setPen (frame_pen);
int dpri = int (dpr);
QRectF r ((dpri / 2) / dpr, (dpri / 2) / dpr, rt.width () - 1.0, rt.height () - 1.0);
pxpainter.drawRect (r);
setIconSize (pxmp.size ());
setIconSize (QSize (rt.width (), rt.height ()));
setIcon (QIcon (pxmp));
}
@ -1216,22 +1230,25 @@ ColorButton::set_color_internal (QColor c)
#if QT_VERSION >= 0x50000
pixmap.setDevicePixelRatio (dpr);
#endif
pixmap.fill (QColor (0, 0, 0, 0));
QColor text_color = palette ().color (QPalette::Active, QPalette::Text);
QPainter pxpainter (&pixmap);
pxpainter.setPen (QPen (text_color));
QPen frame_pen (text_color);
frame_pen.setWidthF (1.0);
frame_pen.setJoinStyle (Qt::MiterJoin);
pxpainter.setPen (frame_pen);
int dpri = int (dpr);
QRectF r ((dpri / 2) / dpr, (dpri / 2) / dpr, rt.width () - 1.0, rt.height () - 1.0);
if (! m_color.isValid ()) {
pxpainter.setFont (font ());
QRectF r (0, 0, rt.width () - pxpainter.pen ().widthF (), rt.height () - pxpainter.pen ().widthF ());
pxpainter.drawText (r, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, QObject::tr ("Auto"));
} else {
pxpainter.setBrush (QBrush (c));
QRectF r (0, 0, rt.width () - pxpainter.pen ().widthF (), rt.height () - pxpainter.pen ().widthF ());
pxpainter.drawRect (r);
}

View File

@ -602,3 +602,21 @@ TEST(Bug_1474)
EXPECT_EQ (ex.msg (), "Cell named ADDHX2 with ID 4 was already given name SEDFFTRX2 (position=763169, cell=)");
}
}
TEST(DuplicateCellname)
{
db::Manager m (false);
db::Layout layout (&m);
try {
tl::InputStream file (tl::testdata () + "/oasis/duplicate_cellname.oas");
db::OASISReader reader (file);
reader.read (layout);
EXPECT_EQ (false, true);
} catch (tl::CancelException &ex) {
// Seen when private test data is not installed
throw;
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "Same cell name TOP, but different IDs: 3 and 0 (position=1070, cell=)");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -7204,12 +7204,12 @@ class Macro:
@overload
def __eq__(self, other: object) -> bool:
r"""
@brief Compares an enum with an integer value
@brief Compares two enums
"""
@overload
def __eq__(self, other: object) -> bool:
r"""
@brief Compares two enums
@brief Compares an enum with an integer value
"""
@overload
def __init__(self, i: int) -> None:
@ -7234,12 +7234,12 @@ class Macro:
@overload
def __ne__(self, other: object) -> bool:
r"""
@brief Compares an enum with an integer for inequality
@brief Compares two enums for inequality
"""
@overload
def __ne__(self, other: object) -> bool:
r"""
@brief Compares two enums for inequality
@brief Compares an enum with an integer for inequality
"""
def __repr__(self) -> str:
r"""
@ -7331,12 +7331,12 @@ class Macro:
@overload
def __ne__(self, other: object) -> bool:
r"""
@brief Compares an enum with an integer for inequality
@brief Compares two enums for inequality
"""
@overload
def __ne__(self, other: object) -> bool:
r"""
@brief Compares two enums for inequality
@brief Compares an enum with an integer for inequality
"""
def __repr__(self) -> str:
r"""

BIN
testdata/oasis/duplicate_cellname.oas vendored Normal file

Binary file not shown.

View File

@ -1226,6 +1226,36 @@ class DBRegion_TestClass < TestBase
end
# rasterize
def test_rasterize
r = RBA::Region::new()
r.insert(RBA::Polygon::new([[0, 0], [100, 100], [150, 0]]))
r.insert(RBA::Polygon::new(RBA::Box::new([0, 200], [100, 300])))
pd = RBA::Vector::new(50, 50)
ps = RBA::Vector::new(25, 25)
sum = 0
2.times do |ix|
2.times do |iy|
am = r.rasterize(RBA::Point::new(-50 + ix * ps.x, -20 + iy * ps.y), pd, ps, 7, 7)
sum += am.collect { |r| r.inject(:+) }.inject(:+)
end
end
assert_equal("%.12g" % sum, "%.12g" % (7.0 * pd.x * pd.y))
tot = 0.0
pd = RBA::Vector::new(50, 50)
am = r.rasterize(RBA::Point::new(-50, -20), pd, 7, 7)
sum = am.collect { |r| r.inject(:+) }.inject(:+)
assert_equal("%.12g" % sum, "%.12g" % (7.0 * pd.x * pd.y))
end
end
load("test_epilogue.rb")

View File

@ -2,10 +2,10 @@
# This script is sourced to define the main version parameters
# The main version
KLAYOUT_VERSION="0.28.15"
KLAYOUT_VERSION="0.28.17"
# The version used for PyPI (don't use variables here!)
KLAYOUT_PYPI_VERSION="0.28.15"
KLAYOUT_PYPI_VERSION="0.28.17"
# The build date
KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")