diff --git a/.travis.yml b/.travis.yml
index 5917ff4cb..34ac00e1e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,6 +7,37 @@ branches:
matrix:
include:
# python manylinux packages
+ - name: "cp39-cp39m-manylinux1_x86_64.whl"
+ os: linux
+ sudo: true
+ language: python
+ python: '3.9'
+ services:
+ - docker
+ env:
+ - DOCKER_IMAGE="quay.io/pypa/manylinux1_x86_64"
+ - PY_VERSION="cp39-cp39"
+ - DOCKER_BUILD=true
+ - TEST_IN_HOST=true
+ - MATRIX_EVAL=""
+ cache:
+ directories:
+ - ccache
+
+ - name: "cp39-cp39m-manylinux1_i686.whl"
+ os: linux
+ sudo: true
+ services:
+ - docker
+ env:
+ - DOCKER_IMAGE="quay.io/pypa/manylinux1_i686"
+ - PY_VERSION="cp39-cp39"
+ - DOCKER_BUILD=true
+ - MATRIX_EVAL=""
+ cache:
+ directories:
+ - ccache
+
- name: "cp38-cp38m-manylinux1_x86_64.whl"
os: linux
sudo: true
diff --git a/macbuild/ReadMe.md b/macbuild/ReadMe.md
index 4f87e9616..09344755b 100644
--- a/macbuild/ReadMe.md
+++ b/macbuild/ReadMe.md
@@ -1,4 +1,4 @@
-Relevant KLayout version: 0.26.5
+Relevant KLayout version: 0.26.7
# 1. Introduction
This directory **`macbuild`** contains different files required for building KLayout (http://www.klayout.de/) version 0.26.1 or later for different 64-bit Mac OSXs including:
@@ -72,16 +72,16 @@ $ [python] ./build4mac.py
: MP26: use Ruby 2.6 from MacPorts |
: HB27: use Ruby 2.7 from Homebrew |
: Ana3: use Ruby 2.5 from Anaconda3 |
- [-p|--python ] : case-insensitive type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3'] | sys
+ [-p|--python ] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3'] | sys
: nil: don't bind Python |
: Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] |
- : MP37: use Python 3.7 from MacPorts |
- : HB37: use Python 3.7 from Homebrew |
- : Ana3: use Python 3.7 from Anaconda3 |
+ : MP38: use Python 3.8 from MacPorts |
+ : HB38: use Python 3.8 from Homebrew |
+ : Ana3: use Python 3.8 from Anaconda3 |
[-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled
[-m|--make ] : option passed to 'make' | '-j4'
[-d|--debug] : enable debug mode build | disabled
- [-c|--checkcom] : check command-line and exit without building | disabled
+ [-c|--checkcom] : check command line and exit without building | disabled
[-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled
[-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled
: from the source code and use the tools in the same machine |
@@ -126,64 +126,64 @@ Then the directory name will be **`LW-qt5MP.pkg.macos-Catalina-release-RsysPsys`
#### If you build KLayout from the source code AND use it on the same machine, "-Y" option is highly recommended. ####
-### 6B. Fully MacPorts-flavored build with MacPorts Ruby 2.6 and MacPorts Python 3.7
+### 6B. Fully MacPorts-flavored build with MacPorts Ruby 2.6 and MacPorts Python 3.8
```
$ cd /where/'build.sh'/exists
-$ ./build4mac.py -q qt5macports -r mp26 -p mp37
+$ ./build4mac.py -q qt5macports -r mp26 -p mp38
```
2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.
The buddy command-line tools (strm*) will also be deployed in this step.
```
-$ ./build4mac.py -q qt5macports -r mp26 -p mp37 -Y
+$ ./build4mac.py -q qt5macports -r mp26 -p mp38 -Y
```
The application bundle **`klayout.app`** is located under:
- **`LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37`** directory, where
+ **`LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38`** directory, where
* "LW-" means that this is a lightweight package.
* "qt5MP" means that Qt5 from MacPorts is used.
-* "Rmp26Pmp37" means that Ruby is 2.6 from MacPorts; Python is 3.7 from MacPorts.
+* "Rmp26Pmp38" means that Ruby is 2.6 from MacPorts; Python is 3.8 from MacPorts.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
-### 6C. Fully Homebrew-flavored build with Homebrew Ruby 2.7 and Homebrew Python 3.7
+### 6C. Fully Homebrew-flavored build with Homebrew Ruby 2.7 and Homebrew Python 3.8
```
$ cd /where/'build.sh'/exists
-$ ./build4mac.py -q qt5brew -r hb27 -p hb37
+$ ./build4mac.py -q qt5brew -r hb27 -p hb38
```
2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.
The buddy command-line tools (strm*) will also be deployed in this step.
```
-$ ./build4mac.py -q qt5brew -r hb27 -p hb37 -Y
+$ ./build4mac.py -q qt5brew -r hb27 -p hb38 -Y
```
The application bundle **`klayout.app`** is located under:
- **`LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb37`** directory, where
+ **`LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb38`** directory, where
* "LW-" means that this is a lightweight package.
* "qt5Brew" means that Qt5 from Homebrew is used.
-* "Rhb27Phb37" means that Ruby is 2.7 from Homebrew; Python is 3.7 from Homebrew.
+* "Rhb27Phb38" means that Ruby is 2.7 from Homebrew; Python is 3.8 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
-### 6D. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.7
+### 6D. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.8
```
$ cd /where/'build.sh'/exists
-$ ./build4mac.py -q qt5brew -r sys -p hb37
+$ ./build4mac.py -q qt5brew -r sys -p hb38
```
2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-y" to deploy executables and libraries (including Qt's frameworks and Python frameworks) under **`klayout.app`** bundle.
The buddy command-line tools (strm*) will also be deployed in this step.
```
-$ ./build4mac.py -q qt5brew -r sys -p hb37 -y
+$ ./build4mac.py -q qt5brew -r sys -p hb38 -y
```
The application bundle **`klayout.app`** is located under:
- **`HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb37`** directory, where
+ **`HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb38`** directory, where
* "HW-" means that this is a heavyweight package because both Qt5 and Python are deployed.
* "qt5Brew" means that Qt5 from Homebrew is used.
-* "RsysPhb37" means that Ruby is OS-bundled; Python is 3.7 from Homebrew.
+* "RsysPhb38" means that Ruby is OS-bundled; Python is 3.8 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### Important ###
So far, deployment of Homebrew Ruby is not supported.
Therefore, if you intend to use "-y" option, you need to use "-r sys" for building.
-### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 2.5 and Anaconda3 Python 3.7
+### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 2.5 and Anaconda3 Python 3.8
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5ana3 -r ana3 -p ana3
@@ -198,7 +198,7 @@ $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 -Y
**`LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3`** directory, where
* "LW-" means that this is a lightweight package.
* "qt5Ana3" means that Qt5 from Anaconda3 is used.
-* "Rana3Pana3" means that Ruby (2.5) is from Anaconda3; Python (3.7) is from Anaconda3.
+* "Rana3Pana3" means that Ruby (2.5) is from Anaconda3; Python (3.8) is from Anaconda3.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
5. You may have to set `PYTHONHOME` environment variable like:
```
diff --git a/macbuild/Resources/script-bundle-A.zip b/macbuild/Resources/script-bundle-A.zip
index 990050af7..eda075de5 100644
Binary files a/macbuild/Resources/script-bundle-A.zip and b/macbuild/Resources/script-bundle-A.zip differ
diff --git a/macbuild/Resources/script-bundle-B.zip b/macbuild/Resources/script-bundle-B.zip
index 253e8ae6b..0a8dfe893 100644
Binary files a/macbuild/Resources/script-bundle-B.zip and b/macbuild/Resources/script-bundle-B.zip differ
diff --git a/macbuild/Resources/script-bundle-P.zip b/macbuild/Resources/script-bundle-P.zip
index d8b11fa06..afdc90968 100644
Binary files a/macbuild/Resources/script-bundle-P.zip and b/macbuild/Resources/script-bundle-P.zip differ
diff --git a/macbuild/build4mac.py b/macbuild/build4mac.py
index d5bc8732a..d9a99e74c 100755
--- a/macbuild/build4mac.py
+++ b/macbuild/build4mac.py
@@ -73,11 +73,11 @@ def SetGlobals():
Usage += " : MP26: use Ruby 2.6 from MacPorts | \n"
Usage += " : HB27: use Ruby 2.7 from Homebrew | \n"
Usage += " : Ana3: use Ruby 2.5 from Anaconda3 | \n"
- Usage += " [-p|--python ] : case-insensitive type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3'] | sys \n"
+ Usage += " [-p|--python ] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3'] | sys \n"
Usage += " : nil: don't bind Python | \n"
Usage += " : Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] | \n"
- Usage += " : MP37: use Python 3.7 from MacPorts | \n"
- Usage += " : HB37: use Python 3.7 from Homebrew | \n"
+ Usage += " : MP38: use Python 3.8 from MacPorts | \n"
+ Usage += " : HB38: use Python 3.8 from Homebrew | \n"
Usage += " : Ana3: use Python 3.7 from Anaconda3 | \n"
Usage += " [-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled \n"
Usage += " [-m|--make ] : option passed to 'make' | '-j4' \n"
@@ -195,7 +195,7 @@ def ParseCommandLineArguments():
p.add_option( '-p', '--python',
dest='type_python',
- help="Python type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3']" )
+ help="Python type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3']" )
p.add_option( '-n', '--noqtbinding',
action='store_true',
@@ -332,8 +332,8 @@ def ParseCommandLineArguments():
candidates = dict()
candidates['NIL'] = 'nil'
candidates['SYS'] = 'Sys'
- candidates['MP37'] = 'MP37'
- candidates['HB37'] = 'HB37'
+ candidates['MP38'] = 'MP38'
+ candidates['HB38'] = 'HB38'
candidates['ANA3'] = 'Ana3'
try:
choicePython = candidates[ opt.type_python.upper() ]
@@ -355,11 +355,11 @@ def ParseCommandLineArguments():
ModulePython = 'PythonSierra'
elif Platform == "ElCapitan":
ModulePython = 'PythonElCapitan'
- elif choicePython == "MP37":
- ModulePython = 'Python37MacPorts'
+ elif choicePython == "MP38":
+ ModulePython = 'Python38MacPorts'
NonOSStdLang = True
- elif choicePython == "HB37":
- ModulePython = 'Python37Brew'
+ elif choicePython == "HB38":
+ ModulePython = 'Python38Brew'
NonOSStdLang = True
elif choicePython == "Ana3":
ModulePython = 'PythonAnaconda3'
@@ -374,12 +374,12 @@ def ParseCommandLineArguments():
# Set of modules chosen
ModuleSet = ( choiceQt5, choiceRuby, choicePython )
- NoQtBindings = opt.no_qt_binding
- MakeOptions = opt.make_option
- DebugMode = opt.debug_build
- CheckComOnly = opt.check_command
- DeploymentF = opt.deploy_full
- DeploymentP = opt.deploy_partial
+ NoQtBindings = opt.no_qt_binding
+ MakeOptions = opt.make_option
+ DebugMode = opt.debug_build
+ CheckComOnly = opt.check_command
+ DeploymentF = opt.deploy_full
+ DeploymentP = opt.deploy_partial
if DeploymentF and DeploymentP:
print("")
@@ -409,9 +409,9 @@ def ParseCommandLineArguments():
if (ModuleRuby in RubySys) and (ModulePython in PythonSys):
PackagePrefix = "ST-"
message += "a standard (ST-) package including Qt5 and using OS-bundled Ruby and Python..."
- elif ModulePython == 'Python37Brew':
+ elif ModulePython == 'Python38Brew':
PackagePrefix = "HW-"
- message += "a heavyweight (HW-) package including Qt5 and Python3.7 from Homebrew..."
+ message += "a heavyweight (HW-) package including Qt5 and Python3.8 from Homebrew..."
else:
PackagePrefix = "EX-"
message += "a package with exceptional (EX-) combinations of different modules..."
@@ -593,6 +593,7 @@ def RunMainBuildBash():
os.remove( tarFile )
os.chdir( "../" )
shutil.copy2( "macbuild/macQAT.sh", MacBuildDirQAT )
+ shutil.copy2( "macbuild/macQAT.py", MacBuildDirQAT )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "### <%s>: prepared the initial *.macQAT/" % myscript, file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
@@ -905,10 +906,6 @@ def DeployBinariesForBundle():
deploytool = Qt5MacPorts['deploy']
app_bundle = "klayout.app"
options = macdepQtOpt + verbose
- # To use Qt5 from Homebrew on Catalina...
- # in "/usr/local/opt/python/lib/"
- # Python.framework -> ../Frameworks/Python.framework/ <=== this symbolic was needed
- # pkgconfig/
elif ModuleQt == 'Qt5Brew':
deploytool = Qt5Brew['deploy']
app_bundle = "klayout.app"
@@ -932,39 +929,52 @@ def DeployBinariesForBundle():
os.chdir(ProjectDir)
return 1
- #-------------------------------------------------------------
- # [9] Special deployment of Python3.7 from Homebrew
- #-------------------------------------------------------------
- deploymentPython37HB = (ModulePython == 'Python37Brew')
- if deploymentPython37HB and NonOSStdLang:
+ #-----------------------------------------------------------------------------------------------
+ # [9] Special deployment of Python3.8 from Homebrew
+ # To use Python3.8 from Homebrew on Catalina...
+ # in "/usr/local/opt/python/lib/"
+ # Python.framework -> ../Frameworks/Python.framework/ <=== this symbolic was needed
+ # pkgconfig/
+ #-----------------------------------------------------------------------------------------------
+ deploymentPython38HB = (ModulePython == 'Python38Brew')
+ if deploymentPython38HB and NonOSStdLang:
from build4mac_util import WalkFrameworkPaths, PerformChanges
- bundlePath = AbsMacPkgDir + '/klayout.app'
- bundleExecPathAbs = '%s/Contents/MacOS/' % bundlePath
+ pythonHBVer = "3.8" # 'pinned' to this version as of KLayout version 0.26.7 (2020-09-13)
+ bundlePath = AbsMacPkgDir + '/klayout.app'
+ bundleExecPathAbs = '%s/Contents/MacOS/' % bundlePath
pythonFrameworkPath = '%s/Contents/Frameworks/Python.framework' % bundlePath
+ testTarget = '%s/Versions/%s/lib/python%s/test' % (pythonFrameworkPath, pythonHBVer, pythonHBVer)
+ resourceTarget1 = '%s/Versions/%s/Resources' % (pythonFrameworkPath, pythonHBVer)
+ resourceTarget2 = '%s/Resources' % pythonFrameworkPath
+ binTarget = '%s/Versions/%s/bin' % (pythonFrameworkPath, pythonHBVer)
+ sitepackagesTarget = '%s/Versions/%s/lib/python%s/site-packages' % (pythonFrameworkPath, pythonHBVer, pythonHBVer)
+ sitepackagesSource = '%s/Versions/%s/lib/python%s/site-packages' % (HBPython38FrameworkPath, pythonHBVer, pythonHBVer)
print( "" )
- print( " [9] Optional deployment of Python from %s ..." % HBPython37FrameworkPath )
+ print( " [9] Optional deployment of Python from %s ..." % HBPython38FrameworkPath )
print( " [9.1] Copying Python Framework" )
- cmd1 = "rm -rf %s" % pythonFrameworkPath
- cmd2 = "rsync -a --safe-links %s/ %s" % (HBPython37FrameworkPath, pythonFrameworkPath)
- cmd3 = "mkdir %s/Versions/3.7/lib/python3.7/site-packages/" % pythonFrameworkPath
- cmd4 = "cp -RL %s/Versions/3.7/lib/python3.7/site-packages/{pip*,pkg_resources,setuptools*,wheel*} " % \
- HBPython37FrameworkPath
- cmd4 += "%s/Versions/3.7/lib/python3.7/site-packages/" % pythonFrameworkPath
- cmd5 = "rm -rf %s/Versions/3.7/lib/python3.7/test" % pythonFrameworkPath
- cmd6 = "rm -rf %s/Versions/3.7/Resources" % pythonFrameworkPath
- cmd7 = "rm -rf %s/Versions/3.7/bin" % pythonFrameworkPath
+ cmd01 = "rm -rf %s" % pythonFrameworkPath
+ cmd02 = "rsync -a --safe-links %s/ %s" % (HBPython38FrameworkPath, pythonFrameworkPath)
+
+ cmd03 = "rm -rf %s" % testTarget
+ cmd04 = "rm -rf %s" % resourceTarget1
+ cmd05 = "unlink %s" % resourceTarget2
+ cmd06 = "rm -rf %s" % binTarget
+
+ cmd07 = "mkdir %s" % sitepackagesTarget
+ cmd08 = "cp -RL %s/{pip*,pkg_resources,setuptools*,wheel*} %s" % (sitepackagesSource, sitepackagesTarget)
shell_commands = list()
- shell_commands.append(cmd1)
- shell_commands.append(cmd2)
- shell_commands.append(cmd3)
- shell_commands.append(cmd4)
- shell_commands.append(cmd5)
- shell_commands.append(cmd6)
- shell_commands.append(cmd7)
+ shell_commands.append(cmd01)
+ shell_commands.append(cmd02)
+ shell_commands.append(cmd03)
+ shell_commands.append(cmd04)
+ shell_commands.append(cmd05)
+ shell_commands.append(cmd06)
+ shell_commands.append(cmd07)
+ shell_commands.append(cmd08)
for command in shell_commands:
if subprocess.call( command, shell=True ) != 0:
@@ -973,14 +983,15 @@ def DeployBinariesForBundle():
sys.exit(1)
shutil.copy2( sourceDir2 + "/start-console.py", targetDirM )
- shutil.copy2( sourceDir2 + "/klayout_console", targetDirM )
- os.chmod( targetDirM + "/klayout_console", 0o0755 )
+ shutil.copy2( sourceDir2 + "/klayout_console", targetDirM )
+ os.chmod( targetDirM + "/start-console.py", 0o0755 )
+ os.chmod( targetDirM + "/klayout_console", 0o0755 )
print(" [9.2] Relinking dylib dependencies inside Python.framework" )
print(" [9.2.1] Patching Python Framework" )
depdict = WalkFrameworkPaths( pythonFrameworkPath )
appPythonFrameworkPath = '@executable_path/../Frameworks/Python.framework/'
- PerformChanges(depdict, [(HBPython37FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
+ PerformChanges(depdict, [(HBPython38FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
print(" [9.2.2] Patching /usr/local/opt/ libs")
usrLocalPath = '/usr/local/opt/'
@@ -989,17 +1000,16 @@ def DeployBinariesForBundle():
depdict = WalkFrameworkPaths(pythonFrameworkPath, search_path_filter=r'\t+/usr/local/(opt|Cellar)')
PerformChanges(depdict, replacePairs, bundleExecPathAbs)
- print(" [9.2.3] Patching openssl, gdbm, readline, sqlite, tcl-tk, xz")
+ print(" [9.2.3] Patching openssl@1.1, gdbm, readline, sqlite, xz")
usrLocalPath = '/usr/local/opt'
appUsrLocalPath = '@executable_path/../Frameworks/'
replacePairs = [(usrLocalPath, appUsrLocalPath, True)]
- replacePairs.extend([(openssl_version, '@executable_path/../Frameworks/openssl', True)
- for openssl_version in glob.glob('/usr/local/Cellar/openssl/*')])
- depdict = WalkFrameworkPaths([pythonFrameworkPath + '/../openssl',
+ replacePairs.extend([(openssl_version, '@executable_path/../Frameworks/openssl@1.1', True)
+ for openssl_version in glob.glob('/usr/local/Cellar/openssl@1.1/*')])
+ depdict = WalkFrameworkPaths([pythonFrameworkPath + '/../openssl@1.1',
pythonFrameworkPath + '/../gdbm',
pythonFrameworkPath + '/../readline',
pythonFrameworkPath + '/../sqlite',
- pythonFrameworkPath + '/../tcl-tk',
pythonFrameworkPath + '/../xz'], search_path_filter=r'\t+/usr/local/(opt|Cellar)')
PerformChanges(depdict, replacePairs, bundleExecPathAbs)
@@ -1007,21 +1017,21 @@ def DeployBinariesForBundle():
print(" [9.3] Relinking dylib dependencies for klayout")
klayoutPath = bundleExecPathAbs
depdict = WalkFrameworkPaths(klayoutPath, filter_regex=r'klayout$')
- PerformChanges(depdict, [(HBPython37FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
+ PerformChanges(depdict, [(HBPython38FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
libKlayoutPath = bundleExecPathAbs + '../Frameworks'
depdict = WalkFrameworkPaths(libKlayoutPath, filter_regex=r'libklayout')
- PerformChanges(depdict, [(HBPython37FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
+ PerformChanges(depdict, [(HBPython38FrameworkPath, appPythonFrameworkPath, False)], bundleExecPathAbs)
print(" [9.4] Patching site.py, pip/, and distutils/")
- site_module = "%s/Versions/3.7/lib/python3.7/site.py" % pythonFrameworkPath
+ site_module = "%s/Versions/%s/lib/python%s/site.py" % (pythonFrameworkPath, pythonHBVer, pythonHBVer)
with open(site_module, 'r') as site:
buf = site.readlines()
with open(site_module, 'w') as site:
import re
for line in buf:
# This will fool pip into thinking it's inside a virtual environment
- # and install new packates to the correct site-packages
+ # and install new packages to the correct site-packages
if re.match("^PREFIXES", line) is not None:
line = line + "sys.real_prefix = sys.prefix\n"
# do not allow installation in the user folder.
@@ -1029,7 +1039,24 @@ def DeployBinariesForBundle():
line = "ENABLE_USER_SITE = False\n"
site.write(line)
- pip_module = "%s/Versions/3.7/lib/python3.7/site-packages/pip/__init__.py" % pythonFrameworkPath
+ #----------------------------------------------------------------------------------
+ # Typical usage of 'pip' after installation of the DMG package
+ #
+ # $ /Applications/klayout.app/Contents/MacOS/start-console.py
+ # Warning: Populating font family aliases took 195 ms. Replace uses of missing font\
+ # family "Monospace" with one that exists to avoid this cost.
+ # Python 3.7.8 (default, Jul 4 2020, 10:17:17)
+ # [Clang 11.0.3 (clang-1103.0.32.62)] on darwin
+ # Type "help", "copyright", "credits" or "license" for more information.
+ # (KLayout Python Console)
+ # >>> import pip
+ # >>> pip.main( ['install', 'numpy'] )
+ # >>> pip.main( ['install', 'scipy'] )
+ # >>> pip.main( ['install', 'pandas'] )
+ # >>> pip.main( ['install', 'matplotlib'] )
+ #----------------------------------------------------------------------------------
+ pip_module = "%s/Versions/%s/lib/python%s/site-packages/pip/__init__.py" % \
+ (pythonFrameworkPath, pythonHBVer, pythonHBVer)
with open(pip_module, 'r') as pip:
buf = pip.readlines()
with open(pip_module, 'w') as pip:
@@ -1039,7 +1066,8 @@ def DeployBinariesForBundle():
line = re.sub("return isolated$", "return isolated or True", line)
pip.write(line)
- distutilsconfig = "%s/Versions/3.7/lib/python3.7/distutils/distutils.cfg" % pythonFrameworkPath
+ distutilsconfig = "%s/Versions/%s/lib/python%s/distutils/distutils.cfg" % \
+ (pythonFrameworkPath, pythonHBVer, pythonHBVer)
with open(distutilsconfig, 'r') as file:
buf = file.readlines()
with open(distutilsconfig, 'w') as file:
@@ -1051,7 +1079,7 @@ def DeployBinariesForBundle():
file.write(line)
#-------------------------------------------------------------
- # [10] Special deployment of Ruby2.6 from Homebrew?
+ # [10] Special deployment of Ruby2.7 from Homebrew?
#-------------------------------------------------------------
deploymentRuby26HB = (ModuleRuby == 'Ruby27Brew')
if deploymentRuby26HB and NonOSStdLang:
@@ -1059,7 +1087,7 @@ def DeployBinariesForBundle():
print( "" )
print( " [10] You have reached optional deployment of Ruby from %s ..." % HBRuby27Path )
print( " [!!!] Sorry, the deployed package will not work properly since deployment of" )
- print( " Ruby2.6 from Homebrew is not yet supported." )
+ print( " Ruby2.7 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 Qt5, Python, and Ruby from Homebrew." )
diff --git a/macbuild/build4mac_env.py b/macbuild/build4mac_env.py
index 033e574d9..b87bb8a9d 100755
--- a/macbuild/build4mac_env.py
+++ b/macbuild/build4mac_env.py
@@ -154,7 +154,7 @@ RubyDictionary = { 'nil' : None,
#-----------------------------------------------------
PythonNil = [ 'nil' ]
PythonSys = [ 'PythonElCapitan', 'PythonSierra', 'PythonHighSierra', 'PythonMojave', 'PythonCatalina' ]
-PythonExt = [ 'Python37MacPorts', 'Python37Brew', 'PythonAnaconda3' ]
+PythonExt = [ 'Python38MacPorts', 'Python38Brew', 'PythonAnaconda3' ]
Pythons = PythonNil + PythonSys + PythonExt
#-----------------------------------------------------
@@ -204,30 +204,30 @@ PythonCatalina = { 'exe': '/System/Library/Frameworks/Python.framework/Versions
'lib': '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib'
}
-# Python 3.7 from MacPorts (https://www.macports.org/) *+*+*+ EXPERIMENTAL *+*+*+
-# install with 'sudo port install python37'
-# [Key Type Name] = 'MP37'
-Python37MacPorts= { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/bin/python3.7m',
- 'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/include/python3.7m',
- 'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/libpython3.7m.dylib'
+# Python 3.8 from MacPorts (https://www.macports.org/) *+*+*+ EXPERIMENTAL *+*+*+
+# install with 'sudo port install python38'
+# [Key Type Name] = 'MP38'
+Python38MacPorts= { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/bin/python3.8',
+ 'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8',
+ 'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/libpython3.8.dylib'
}
-# Python 3.7 from Homebrew *+*+*+ EXPERIMENTAL *+*+*+
+# Python 3.8 from Homebrew *+*+*+ EXPERIMENTAL *+*+*+
# install with 'brew install python'
-# [Key Type Name] = 'HB37'
-HBPython37FrameworkPath = '/usr/local/opt/python3/Frameworks/Python.framework'
-Python37Brew = { 'exe': '%s/Versions/3.7/bin/python3.7m' % HBPython37FrameworkPath,
- 'inc': '%s/Versions/3.7/include/python3.7m' % HBPython37FrameworkPath,
- 'lib': '%s/Versions/3.7/lib/libpython3.7m.dylib' % HBPython37FrameworkPath
+# [Key Type Name] = 'HB38'
+HBPython38FrameworkPath = '/usr/local/opt/python3/Frameworks/Python.framework'
+Python38Brew = { 'exe': '%s/Versions/3.8/bin/python3.8' % HBPython38FrameworkPath,
+ 'inc': '%s/Versions/3.8/include/python3.8' % HBPython38FrameworkPath,
+ 'lib': '%s/Versions/3.8/lib/libpython3.8.dylib' % HBPython38FrameworkPath
}
-# Python 3.7 bundled with anaconda3 installed under /Applications/anaconda3/ *+*+*+ EXPERIMENTAL *+*+*+
+# Python 3.8 bundled with anaconda3 installed under /Applications/anaconda3/ *+*+*+ EXPERIMENTAL *+*+*+
# The standard installation deploys the tool under $HOME/opt/anaconda3/.
# If so, you need to make a symbolic link: /Applications/anaconda3 ---> $HOME/opt/anaconda3/
# [Key Type Name] = 'Ana3'
-PythonAnaconda3 = { 'exe': '/Applications/anaconda3/bin/python3.7m',
- 'inc': '/Applications/anaconda3/include/python3.7m',
- 'lib': '/Applications/anaconda3/lib/libpython3.7m.dylib'
+PythonAnaconda3 = { 'exe': '/Applications/anaconda3/bin/python3.8',
+ 'inc': '/Applications/anaconda3/include/python3.8',
+ 'lib': '/Applications/anaconda3/lib/libpython3.8.dylib'
}
# Consolidated dictionary kit for Python
@@ -237,8 +237,8 @@ PythonDictionary= { 'nil' : None,
'PythonHighSierra': PythonHighSierra,
'PythonMojave' : PythonMojave,
'PythonCatalina' : PythonCatalina,
- 'Python37MacPorts': Python37MacPorts,
- 'Python37Brew' : Python37Brew,
+ 'Python38MacPorts': Python38MacPorts,
+ 'Python38Brew' : Python38Brew,
'PythonAnaconda3' : PythonAnaconda3
}
diff --git a/macbuild/build4mac_util.py b/macbuild/build4mac_util.py
index ea682fe21..3c330f8fd 100755
--- a/macbuild/build4mac_util.py
+++ b/macbuild/build4mac_util.py
@@ -1,7 +1,7 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
-#===============================================================================
+#========================================================================================
# File: "macbuild/build4mac_util.py"
#
# Here are utility functions and classes ...
@@ -9,7 +9,7 @@
# version 0.26.1 or later on different Apple Mac OSX platforms.
#
# This file is imported by 'build4mac.py' script.
-#===============================================================================
+#========================================================================================
from __future__ import print_function # to use print() of Python 3 in Python >= 2.7
import sys
import os
@@ -18,14 +18,14 @@ import string
import subprocess
import shutil
-#-------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
## To import global dictionaries of different modules
-#-------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
mydir = os.path.dirname(os.path.abspath(__file__))
sys.path.append( mydir )
from build4mac_env import *
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
## To decompose strings obtained by 'otool -L <*.dylib>' command and to
# generate a dictionary of KLayout's inter-library dependency.
#
@@ -41,7 +41,7 @@ from build4mac_env import *
# :
#
# @return a dictionary
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
def DecomposeLibraryDependency( depstr ):
alllines = depstr.split('\n')
numlines = len(alllines)
@@ -53,13 +53,13 @@ def DecomposeLibraryDependency( depstr ):
supporters.append(supporter)
return { dependent: supporters }
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
## To print the contents of a library dependency dictionary
#
# @param[in] depdic dictionary
# @param[in] pathdic path dictionary
# @param[in] namedic dictionary name
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
def PrintLibraryDependencyDictionary( depdic, pathdic, namedic ):
keys = depdic.keys()
print("")
@@ -73,13 +73,13 @@ def PrintLibraryDependencyDictionary( depdic, pathdic, namedic ):
if itemName != keyName and (itemName in pathdic):
print( " %s (%s)" % (item, pathdic[itemName]) )
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
## To set and change identification name of KLayout's dylib
#
# @param[in] libdic inter-library dependency dictionary
#
# @return 0 on success; non-zero on failure
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
@@ -115,7 +115,7 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
# for-lib
return 0
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
## To set the identification names of KLayout's libraries to an executable
# and make the application aware of the library locations
#
@@ -146,7 +146,7 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
# +-- 'strmxor'
#
# @return 0 on success; non-zero on failure
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
def SetChangeLibIdentificationName( executable, relativedir ):
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
@@ -181,15 +181,21 @@ def SetChangeLibIdentificationName( executable, relativedir ):
# for-lib
return 0
-def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt'):
- NOTHINGTODO = [] # return empty list if nothing to do.
- cmdNameId = XcodeToolChain['nameID']
- cmdNameChg = XcodeToolChain['nameCH']
- otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex)
- otoolOut = os.popen( otoolCm ).read()
- exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut )
- keys = exedepdic.keys()
- deplibs = exedepdic[ list(keys)[0] ]
+#----------------------------------------------------------------------------------------
+## To make a library dependency dictionary by recursively walk down the lib hierarchy
+#
+# @param[in] dylibPath: dylib path
+# @param[in] depth: hierarchy depth (< 5)
+# @param[in] filter_regex: filter regular expression
+#
+# @return a dictionary
+#----------------------------------------------------------------------------------------
+def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt' ):
+ otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex)
+ otoolOut = os.popen( otoolCm ).read()
+ exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut )
+ keys = exedepdic.keys()
+ deplibs = exedepdic[ list(keys)[0] ]
if depth < 5:
if len(deplibs) > 0:
@@ -201,9 +207,19 @@ def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt'
return deplibs
return exedepdic
else:
- raise RuntimeError("Exceeded maximum recursion depth.")
+ raise RuntimeError( "Exceeded maximum recursion depth." )
-def WalkFrameworkPaths(frameworkPaths, filter_regex=r'\.(so|dylib)$', search_path_filter=r'\t+/usr/local/opt'):
+#----------------------------------------------------------------------------------------
+## To make a library dependency dictionary by recursively walk down the Framework
+#
+# @param[in] frameworkPaths: Framework path
+# @param[in] filter_regex: filter regular expression
+# @param[in] search_path_filter: search path filter regular expression
+#
+# @return a dictionary
+#----------------------------------------------------------------------------------------
+def WalkFrameworkPaths( frameworkPaths, filter_regex=r'\.(so|dylib)$',
+ search_path_filter=r'\t+/usr/local/opt' ):
if isinstance(frameworkPaths, str):
frameworkPathsIter = [frameworkPaths]
@@ -225,7 +241,15 @@ def WalkFrameworkPaths(frameworkPaths, filter_regex=r'\.(so|dylib)$', search_pat
dependency_dict[frameworkPath].append(dict_file)
return dependency_dict
-def WalkDictTree(dependencyDict, visited_files):
+#----------------------------------------------------------------------------------------
+## To make a list of changed libraries
+#
+# @param[in] dependencyDict: library dependency dictionary
+# @param[in] visited_files: list of visited files
+#
+# @return a list
+#----------------------------------------------------------------------------------------
+def WalkDictTree( dependencyDict, visited_files ):
libNameChanges = list()
for lib, dependencies in dependencyDict.items():
if lib in visited_files:
@@ -253,31 +277,69 @@ def WalkDictTree(dependencyDict, visited_files):
visited_files.append(lib)
return libNameChanges
-
-def FindFramework(path, root_path):
+#----------------------------------------------------------------------------------------
+## To find the Framework name from a library name
+#
+# @param[in] path: path to a library
+# @param[in] root_path: root path
+#
+# @return the path to a Framework
+#----------------------------------------------------------------------------------------
+def FindFramework( path, root_path ):
relPath = os.path.relpath(path, root_path)
- return os.path.join(root_path, relPath.split(os.sep)[0])
-def ResolveExecutablePath(path, executable_path):
+ frmPath = os.path.join(root_path, relPath.split(os.sep)[0])
+ #print( "###", frmPath, path, root_path )
+ return frmPath
+
+#----------------------------------------------------------------------------------------
+## To resolve an executable path
+#
+# @param[in] path: a path to resolve
+# @param[in] executable_path: an executable path
+#
+# @return the resolved path
+#----------------------------------------------------------------------------------------
+def ResolveExecutablePath( path, executable_path ):
""" Transforms @executable_path into executable_path"""
p = path.replace("@executable_path", "/%s/" % executable_path)
return p
+#----------------------------------------------------------------------------------------
+## To detect the changed library names
+#
+# @param[in] frameworkDependencyDict: framework dependency dictionary
+#
+# @return a list of changes, each of which is stored in the form of
+# * ('lib.dylib', ['dep1.dylib', ...])
+# OR
+# * ('lib.dylib',)
+#----------------------------------------------------------------------------------------
def DetectChanges(frameworkDependencyDict):
visited_files = list()
libNameChanges = list()
for framework, libraries in frameworkDependencyDict.items():
for libraryDict in libraries:
libNameChanges.extend(WalkDictTree(libraryDict, visited_files))
- # Changes are stored in libNameChanges in the form of ('lib.dylib', ['dep1.dylib', ...])
-
return libNameChanges
-def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_path="/tmp/klayout"):
+#----------------------------------------------------------------------------------------
+## To perform the required changes
+#
+# @param[in] frameworkDependencyDict: framework dependency dictionary
+# @param[in] replaceFromToPairs: (from, to)-pair for replacement
+# @param[in] executable_path: executable path
+#
+# @return 0 on success; > 0 on failure
+#----------------------------------------------------------------------------------------
+def PerformChanges( frameworkDependencyDict, replaceFromToPairs=None, executable_path="/tmp/klayout" ):
libNameChanges = DetectChanges(frameworkDependencyDict)
- cmdNameId = XcodeToolChain['nameID']
+ #print(libNameChanges)
+ cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
- if replaceFromToPairs is not None:
+ if replaceFromToPairs is None:
+ return 0
+ else:
for libNameChange in libNameChanges:
libNameChangeIterator = iter(libNameChange)
lib = next(libNameChangeIterator)
@@ -300,8 +362,8 @@ def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_
destFrameworkPath = ResolveExecutablePath(destFrameworkPath, executable_path)
if not os.path.exists(fileName):
- print (lib.replace(replaceFrom, replaceTo), "DOES NOT EXIST")
- print ("COPY", frameworkPath, " -> ", destFrameworkPath)
+ print( " NOT FOUND:", lib.replace(replaceFrom, replaceTo) )
+ print( " COPYING:", frameworkPath, " -> ", destFrameworkPath )
shutil.copytree(frameworkPath, destFrameworkPath)
nameId = lib.replace(replaceFrom, replaceTo)
@@ -316,8 +378,8 @@ def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_
for dependency in dependencies:
if dependency.find(replaceFrom) >= 0:
- print("\tIn:", fileName)
- print("\tRENAME", dependency, " -> ", dependency.replace(replaceFrom, replaceTo))
+ print( " IN:", fileName )
+ print( " RENAMING:", dependency, " -> ", dependency.replace(replaceFrom, replaceTo) )
# Try changing id first
nameId = dependency.replace(replaceFrom, replaceTo)
@@ -342,15 +404,15 @@ def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr )
return 1
+ return 0
-
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
## To get KLayout's version from a file; most likely from 'version.sh'
#
# @param[in] verfile version file from which version is retrieved
#
# @return version string
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
def GetKLayoutVersionFrom( verfile='version.h' ):
version = "?.?.?"
try:
@@ -372,14 +434,14 @@ def GetKLayoutVersionFrom( verfile='version.h' ):
return version
return version
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
## To generate the contents of "Info.plist" file from a template
#
# @param[in] keydic dictionary of four key words ['exe', 'icon', 'bname', 'ver']
# @param[in] templfile template file ("macbuild/Resources/Info.plist.template")
#
# @return generated strings
-#------------------------------------------------------------------------------
+#----------------------------------------------------------------------------------------
def GenerateInfoPlist( keydic, templfile ):
val_exe = keydic['exe']
val_icon = keydic['icon']
diff --git a/macbuild/macQAT.py b/macbuild/macQAT.py
new file mode 100755
index 000000000..907330f1c
--- /dev/null
+++ b/macbuild/macQAT.py
@@ -0,0 +1,278 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#===============================================================================
+# 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
+# ßMac OSX platforms.
+#
+# This script must be copied to a "*.macQAT/" directory to run.
+#===============================================================================
+from __future__ import print_function # to use print() of Python 3 in Python >= 2.7
+import sys
+import os
+import datetime
+from time import sleep
+import six
+import shutil
+import glob
+import platform
+import optparse
+import subprocess
+
+#-------------------------------------------------------------------------------
+## To set global variables including present directory and platform info.
+#
+#-------------------------------------------------------------------------------
+def SetGlobals():
+ global ProjectDir # project directory where "ut_runner" exists
+ global RunnerUsage # True to print the usage of 'ut_runner'
+ global Run # True to run this script
+ global ContinueOnError # True to continue after an error
+ global TestsExcluded # list of tests to exclude
+ global Arguments # other arguments
+ global GitSHA1 # Git's short SHA1 value of the HEAD
+ global TimeStamp # time stamp
+ global WorkDir # work directory name
+ global LogFile # log file name
+ global Usage # string on usage
+ # auxiliary variables on platform
+ global System # 6-tuple from platform.uname()
+ global Node # - do -
+ global Release # - do -
+ global Version # - do -
+ global Machine # - do -
+ global Processor # - do -
+ global Bit # machine bit-size
+
+ Usage = "\n"
+ Usage += "----------------------------------------------------------------------------------------\n"
+ Usage += "<< Usage of 'macQAT.py' >>\n"
+ Usage += " for running 'ut_runner' after building KLayout.\n"
+ Usage += "\n"
+ Usage += "$ [python] ./macQAT.py \n"
+ Usage += " option & argument : descriptions | default value\n"
+ Usage += " -----------------------------------------------------------------+---------------\n"
+ Usage += " [-u|--usage] : print usage of 'ut_runner'and exit | disabled \n"
+ Usage += " | \n"
+ Usage += " <-r|--run> : run this script | disabled \n"
+ Usage += " [-s|--stop] : stop on error | disabled \n"
+ Usage += " [-x|--exclude ] : exclude test(s) such as 'pymod,pya' | '' \n"
+ Usage += " [-a|--args ] : arguments other than '-x' and '-c' | '' \n"
+ Usage += " [-?|--?] : print this usage and exit | disabled \n"
+ Usage += "--------------------------------------------------------------------+-------------------\n"
+
+ ProjectDir = os.getcwd()
+ RunnerUsage = False
+ Run = False
+ ContinueOnError = True
+ TestsExcluded = list()
+ Arguments = ""
+ GitSHA1 = GetGitShortSHA1()
+ TimeStamp = GetTimeStamp()
+ WorkDir = "QATest_%s_%s__%s" % (GitSHA1, TimeStamp, os.path.basename(ProjectDir) )
+ LogFile = WorkDir + ".log"
+
+ (System, Node, Release, Version, Machine, Processor) = platform.uname()
+
+#-------------------------------------------------------------------------------
+## Get git's short SHA1 value of the HEAD
+#
+# @return SHA1 value string
+#-------------------------------------------------------------------------------
+def GetGitShortSHA1():
+ command = "git rev-parse --short HEAD 2>/dev/null"
+ sha1val = os.popen( command ).read().strip()
+ return sha1val
+
+#-------------------------------------------------------------------------------
+## Get the time stamp
+#
+# @return time stamp string
+#-------------------------------------------------------------------------------
+def GetTimeStamp():
+ ts = datetime.datetime.today()
+ return "%04d_%02d%02d_%02d%02d" % (ts.year, ts.month, ts.day, ts.hour, ts.minute)
+
+#-------------------------------------------------------------------------------
+## To parse the command line arguments
+#
+#-------------------------------------------------------------------------------
+def ParseCommandLineArguments():
+ global Usage
+ global RunnerUsage
+ global Run
+ global ContinueOnError
+ global TestsExcluded
+ global Arguments
+
+ p = optparse.OptionParser( usage=Usage )
+ p.add_option( '-u', '--usage',
+ action='store_true',
+ dest='runner_usage',
+ default=False,
+ help="print usage of 'ut_runner' and exit (false)" )
+
+ p.add_option( '-r', '--run',
+ action='store_true',
+ dest='runme',
+ default=False,
+ help='run this script (false)' )
+
+ p.add_option( '-s', '--stop',
+ action='store_true',
+ dest='stop_on_error',
+ default=False,
+ help='stop on error (false)' )
+
+ p.add_option( '-x', '--exclude',
+ dest='exclude_tests',
+ help="exclude test(s) such as 'pymod,pya' ('')" )
+
+ p.add_option( '-a', '--args',
+ dest='arguments',
+ help="arguments other than '-x' and '-c' ('')" )
+
+ p.add_option( '-?', '--??',
+ action='store_true',
+ dest='checkusage',
+ default=False,
+ help='check usage (false)' )
+
+ p.set_defaults( runner_usage = False,
+ runme = False,
+ stop_on_error = False,
+ exclude_tests = "",
+ arguments = "",
+ checkusage = False )
+
+ opt, args = p.parse_args()
+ if opt.checkusage:
+ print(Usage)
+ quit()
+
+ RunnerUsage = opt.runner_usage
+ Run = opt.runme
+ ContinueOnError = not opt.stop_on_error
+ if not opt.exclude_tests == "":
+ TestsExcluded = [ item.strip() for item in opt.exclude_tests.split(',') ]
+ else:
+ TestsExcluded = []
+ Arguments = opt.arguments
+
+#-------------------------------------------------------------------------------
+## Hide/Show the private directory
+#
+#-------------------------------------------------------------------------------
+def HidePrivateDir():
+ if os.path.isdir( "../private" ):
+ os.rename( "../private", "../private.stash" )
+
+def ShowPrivateDir():
+ if os.path.isdir( "../private.stash" ):
+ os.rename( "../private.stash", "../private" )
+
+#-------------------------------------------------------------------------------
+## Export environment variables for "ut_runner"
+#
+#-------------------------------------------------------------------------------
+def ExportEnvVariables():
+ global ProjectDir
+ global WorkDir
+ global MyEnviron # my environment variables; can be independently used later
+
+ # In older versions of subprocess module, 'env=None' argument is not provided
+ MyEnviron = os.environ.copy()
+ MyEnviron[ 'TESTSRC' ] = ".."
+ MyEnviron[ 'TESTTMP' ] = WorkDir
+ if System == "Darwin":
+ MyEnviron[ 'DYLD_LIBRARY_PATH' ] = "%s:%s/db_plugins" % (ProjectDir, ProjectDir)
+ for env in [ 'TESTSRC', 'TESTTMP', 'DYLD_LIBRARY_PATH' ]:
+ os.environ[env] = MyEnviron[env]
+ else:
+ MyEnviron[ 'LD_LIBRARY_PATH' ] = "%s:%s/db_plugins" % (ProjectDir, ProjectDir)
+ for env in [ 'TESTSRC', 'TESTTMP', 'LD_LIBRARY_PATH' ]:
+ os.environ[env] = MyEnviron[env]
+
+#-------------------------------------------------------------------------------
+## Run the tester
+#
+# @param[in] command command string to run
+# @param[in] logfile log file name
+#-------------------------------------------------------------------------------
+def RunTester( command, logfile="" ):
+ if six.PY3:
+ proc = subprocess.Popen( command.split(), stdout=subprocess.PIPE, \
+ stderr=subprocess.STDOUT, \
+ universal_newlines=True )
+ else:
+ proc = subprocess.Popen( command.split(), stdout=subprocess.PIPE, \
+ stderr=subprocess.STDOUT )
+
+ if not logfile == "":
+ with proc.stdout, open( logfile, 'w' ) as file:
+ for line in proc.stdout:
+ sys.stdout.write(line)
+ file.write(line)
+ proc.wait()
+ else:
+ with proc.stdout:
+ for line in proc.stdout:
+ sys.stdout.write(line)
+ proc.wait()
+
+#-------------------------------------------------------------------------------
+# Main function
+#-------------------------------------------------------------------------------
+def Main():
+ #-------------------------------------------------------
+ # [1] Initialize
+ #-------------------------------------------------------
+ SetGlobals()
+ ParseCommandLineArguments()
+ ExportEnvVariables()
+
+ #-------------------------------------------------------
+ # [2] Print the runner's usage
+ #-------------------------------------------------------
+ if RunnerUsage:
+ command = './ut_runner --help-all'
+ RunTester( command )
+ quit()
+
+ #-------------------------------------------------------
+ # [3] Run the unit tester
+ #-------------------------------------------------------
+ if not Run:
+ print( "! pass <-r|--run> option to run" )
+ print(Usage)
+ quit()
+
+ command = './ut_runner'
+ if ContinueOnError:
+ command += " -c"
+ for item in TestsExcluded:
+ command += ' -x %s' % item
+ if not Arguments == "":
+ command += " %s" % Arguments
+
+ print( "" )
+ print( "### Dumping the log to <%s>" % LogFile )
+ print( "------------------------------------------------------------" )
+ print( " Git SHA1 = %s" % GitSHA1 )
+ print( " Time stamp = %s" % TimeStamp )
+ print( "------------------------------------------------------------" )
+ sleep( 1.0 )
+ HidePrivateDir()
+ RunTester( command, logfile=LogFile )
+ ShowPrivateDir()
+
+#===================================================================================
+if __name__ == "__main__":
+ Main()
+
+#---------------
+# End of file
+#---------------
diff --git a/macbuild/makeDMG4mac.py b/macbuild/makeDMG4mac.py
index c2429c785..04998cb33 100755
--- a/macbuild/makeDMG4mac.py
+++ b/macbuild/makeDMG4mac.py
@@ -179,8 +179,8 @@ def SetGlobals():
# The package directory name should look like:
# * ST-qt5MP.pkg.macos-Catalina-release-RsysPsys --- (1)
# * LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3
-# * LW-qt5Brew.pkg.macos-Catalina-release-Rhb26Phb37
-# * LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37
+# * LW-qt5Brew.pkg.macos-Catalina-release-Rhb26Phb38
+# * LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38
#
# Generated DMG will be, for example,
# (1) ---> ST-klayout-0.26.1-macOS-Catalina-1-qt5MP-RsysPsys.dmg
@@ -221,9 +221,9 @@ def CheckPkgDirectory():
#
# * ST-qt5MP.pkg.macos-Catalina-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3
- # * LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb37
- # * HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb37
- # * EX-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37
+ # * LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb38
+ # * HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb38
+ # * EX-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38
#-----------------------------------------------------------------------------
patQRP = u'(ST|LW|HW|EX)([-])(qt5[0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-]release[-])([0-9A-Za-z]+)'
regQRP = re.compile(patQRP)
@@ -243,12 +243,12 @@ def CheckPkgDirectory():
LatestOSMacPorts = Platform == LatestOS
LatestOSMacPorts &= PackagePrefix == "LW"
LatestOSMacPorts &= QtIdentification == "qt5MP"
- LatestOSMacPorts &= RubyPythonID == "Rmp26Pmp37"
+ LatestOSMacPorts &= RubyPythonID == "Rmp26Pmp38"
LatestOSHomebrew = Platform == LatestOS
LatestOSHomebrew &= PackagePrefix == "LW"
LatestOSHomebrew &= QtIdentification == "qt5Brew"
- LatestOSHomebrew &= RubyPythonID == "Rhb27Phb37"
+ LatestOSHomebrew &= RubyPythonID == "Rhb27Phb38"
LatestOSAnaconda3 = Platform == LatestOS
LatestOSAnaconda3 &= PackagePrefix == "LW"
diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc
index f95667da2..42f72e17d 100644
--- a/src/db/db/dbDeepShapeStore.cc
+++ b/src/db/db/dbDeepShapeStore.cc
@@ -931,7 +931,7 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first;
// collects the cell mappings we skip because they are variants (variant building or box variants)
- std::map > > cm_skipped_variants;
+ std::map cm_skipped_variants;
if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell ()) {
@@ -945,14 +945,14 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
HierarchyBuilder::cell_map_type::const_iterator mm = m;
++mm;
bool skip = original_builder.is_variant (m->second); // skip variant cells
- while (mm != original_builder.end_cell_map () && mm->first.first == m->first.first) {
+ while (mm != original_builder.end_cell_map () && mm->first.original_cell == m->first.original_cell) {
// we have cell (box) variants and cannot simply map
++mm;
skip = true;
}
if (! skip) {
- cm->second.map (m->second, m->first.first);
+ cm->second.map (m->second, m->first.original_cell);
} else {
for (HierarchyBuilder::cell_map_type::const_iterator n = m; n != mm; ++n) {
tl_assert (cm_skipped_variants.find (n->second) == cm_skipped_variants.end ());
@@ -988,17 +988,19 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
db::cell_index_type var_org = original_builder.original_target_for_variant (np->first);
- std::map > >::const_iterator icm = cm_skipped_variants.find (var_org);
+ std::map::const_iterator icm = cm_skipped_variants.find (var_org);
if (icm != cm_skipped_variants.end ()) {
// create the variant clone in the original layout too and delete this cell
- VariantsCollectorBase::copy_shapes (*into_layout, np->second, icm->second.first);
- cells_to_delete.insert (icm->second.first);
+ VariantsCollectorBase::copy_shapes (*into_layout, np->second, icm->second.original_cell);
+ cells_to_delete.insert (icm->second.original_cell);
// forget the original cell (now separated into variants) and map the variants back into the
// DSS layout
original_builder.unmap (icm->second);
- original_builder.map (std::make_pair (np->second, icm->second.second), np->first);
+ db::HierarchyBuilder::CellMapKey k = icm->second;
+ k.original_cell = np->second;
+ original_builder.map (k, np->first);
// forget the variant as now it's a real cell in the source layout
original_builder.unregister_variant (np->first);
diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc
index 41f876b79..0f1e12271 100644
--- a/src/db/db/dbHierNetworkProcessor.cc
+++ b/src/db/db/dbHierNetworkProcessor.cc
@@ -799,10 +799,18 @@ struct cluster_building_receiver
typedef std::set global_nets;
typedef std::pair cluster_value;
- cluster_building_receiver (const db::Connectivity &conn)
- : mp_conn (&conn)
+ cluster_building_receiver (const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence)
+ : mp_conn (&conn), mp_attr_equivalence (attr_equivalence)
{
- // .. nothing yet..
+ if (mp_attr_equivalence) {
+ // cache the global nets per attribute equivalence cluster
+ for (size_t gid = 0; gid < conn.global_nets (); ++gid) {
+ tl::equivalence_clusters::cluster_id_type cl = attr_equivalence->cluster_id (global_net_id_to_attr (gid));
+ if (cl > 0) {
+ m_global_nets_by_attribute_cluster [cl].insert (gid);
+ }
+ }
+ }
}
void generate_clusters (local_clusters &clusters)
@@ -817,7 +825,32 @@ struct cluster_building_receiver
cluster->add_attr (s->second.second);
}
- cluster->set_global_nets (c->second);
+ std::set global_nets = c->second;
+
+ // Add the global nets we derive from the attribute equivalence (join_nets of labelled vs.
+ // global nets)
+ if (mp_attr_equivalence) {
+
+ for (typename shape_vector::const_iterator s = c->first.begin (); s != c->first.end (); ++s) {
+
+ size_t a = s->second.second;
+ if (a == 0) {
+ continue;
+ }
+
+ tl::equivalence_clusters::cluster_id_type cl = mp_attr_equivalence->cluster_id (a);
+ if (cl > 0) {
+ std::map >::const_iterator gn = m_global_nets_by_attribute_cluster.find (cl);
+ if (gn != m_global_nets_by_attribute_cluster.end ()) {
+ global_nets.insert (gn->second.begin (), gn->second.end ());
+ }
+ }
+
+ }
+
+ }
+
+ cluster->set_global_nets (global_nets);
}
}
@@ -913,6 +946,8 @@ private:
std::map::iterator> m_shape_to_clusters;
std::map::iterator> m_global_to_clusters;
std::list m_clusters;
+ std::map > m_global_nets_by_attribute_cluster;
+ const tl::equivalence_clusters *mp_attr_equivalence;
void join (typename std::list::iterator ic1, typename std::list::iterator ic2)
{
@@ -1035,7 +1070,7 @@ local_clusters::build_clusters (const db::Cell &cell, const db::Connectivity
}
}
- cluster_building_receiver rec (conn);
+ cluster_building_receiver rec (conn, attr_equivalence);
bs.process (rec, 1 /*==touching*/, bc);
rec.generate_clusters (*this);
@@ -1048,36 +1083,27 @@ template
void
local_clusters::apply_attr_equivalences (const tl::equivalence_clusters &attr_equivalence)
{
- tl::equivalence_clusters eq;
-
- // collect all local attributes (the ones which are present in attr_equivalence) into "eq"
- // and form equivalences for multi-attribute clusters.
- for (const_iterator c = begin (); c != end (); ++c) {
- typename local_cluster::attr_iterator a0;
- for (typename local_cluster::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
- if (attr_equivalence.has_attribute (*a)) {
- if (a0 == typename local_cluster::attr_iterator ()) {
- a0 = a;
- }
- eq.same (*a0, *a);
- }
- }
- }
-
- // apply the equivalences implied by attr_equivalence
- eq.apply_equivalences (attr_equivalence);
-
// identify the layout clusters joined into one attribute cluster and join them
std::map::cluster_id_type, std::set > c2c;
for (const_iterator c = begin (); c != end (); ++c) {
+
for (typename local_cluster::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
tl::equivalence_clusters::cluster_id_type cl = attr_equivalence.cluster_id (*a);
if (cl > 0) {
c2c [cl].insert (c->id ());
}
}
+
+ for (typename local_cluster::global_nets_iterator g = c->begin_global_nets (); g != c->end_global_nets (); ++g) {
+ size_t a = global_net_id_to_attr (*g);
+ tl::equivalence_clusters::cluster_id_type cl = attr_equivalence.cluster_id (a);
+ if (cl > 0) {
+ c2c [cl].insert (c->id ());
+ }
+ }
+
}
for (std::map::cluster_id_type, std::set >::const_iterator c = c2c.begin (); c != c2c.end (); ++c) {
diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h
index be84db6da..1161ea84a 100644
--- a/src/db/db/dbHierNetworkProcessor.h
+++ b/src/db/db/dbHierNetworkProcessor.h
@@ -1396,11 +1396,11 @@ private:
/**
* @brief A helper function generating an attribute ID from a property ID
- * This function is used to provide a generic attribute wrapping a property ID and a text ID.
+ * This function is used to provide a generic attribute wrapping a property ID, text ID and global net ID
*/
inline size_t prop_id_to_attr (db::properties_id_type id)
{
- return size_t (id) * 2;
+ return size_t (id) * 4;
}
/**
@@ -1408,7 +1408,7 @@ inline size_t prop_id_to_attr (db::properties_id_type id)
*/
inline bool is_prop_id_attr (size_t attr)
{
- return (attr & 1) == 0;
+ return (attr & 3) == 0;
}
/**
@@ -1416,7 +1416,32 @@ inline bool is_prop_id_attr (size_t attr)
*/
inline db::properties_id_type prop_id_from_attr (size_t attr)
{
- return attr / 2;
+ return attr / 4;
+}
+
+/**
+ * @brief A helper function generating an attribute ID from a global net ID
+ * This function is used to provide a generic attribute wrapping a property ID, text ID and global net ID
+ */
+inline size_t global_net_id_to_attr (size_t id)
+{
+ return size_t (id) * 4 + 2;
+}
+
+/**
+ * @brief Gets a value indicating whether the attribute is a global net ID
+ */
+inline bool is_global_net_id_attr (size_t attr)
+{
+ return (attr & 3) == 2;
+}
+
+/**
+ * @brief Gets the global net ID from an attribute
+ */
+inline size_t global_net_id_from_attr (size_t attr)
+{
+ return attr / 4;
}
/**
@@ -1424,9 +1449,18 @@ inline db::properties_id_type prop_id_from_attr (size_t attr)
*/
inline size_t text_ref_to_attr (const db::Text *tr)
{
+ // NOTE: pointers are 32bit aligned, hence the lower two bits are 0
return size_t (tr) + 1;
}
+/**
+ * @brief Gets a value indicating whether the attribute is a StringRef
+ */
+inline bool is_text_ref_attr (size_t attr)
+{
+ return (attr & 3) == 1;
+}
+
/**
* @brief Gets the text value from an attribute ID
*/
diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc
index 0b15c3127..49d597b84 100644
--- a/src/db/db/dbHierarchyBuilder.cc
+++ b/src/db/db/dbHierarchyBuilder.cc
@@ -62,6 +62,14 @@ compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIter
return iter1.max_depth () < iter2.max_depth () ? -1 : 1;
}
+ // take potential selection of cells into account
+ if (iter1.disables () != iter2.disables ()) {
+ return iter1.disables () < iter2.disables () ? -1 : 1;
+ }
+ if (iter1.enables () != iter2.enables ()) {
+ return iter1.enables () < iter2.enables () ? -1 : 1;
+ }
+
// if a region is set, the hierarchical appearance is the same only if the layers and
// complex region are identical
if ((iter1.region () == db::Box::world ()) != (iter2.region () == db::Box::world ())) {
@@ -238,11 +246,11 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter)
return;
}
- std::pair > key (iter->top_cell ()->cell_index (), std::set ());
+ CellMapKey key (iter->top_cell ()->cell_index (), false, std::set ());
m_cm_entry = m_cell_map.find (key);
if (m_cm_entry == m_cell_map.end ()) {
- db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.first));
+ db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.original_cell));
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first;
}
@@ -300,27 +308,47 @@ HierarchyBuilder::leave_cell (const RecursiveShapeIterator * /*iter*/, const db:
m_cell_stack.pop_back ();
}
+db::cell_index_type
+HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, const std::string &cell_name)
+{
+ m_cm_entry = m_cell_map.find (key);
+ m_cm_new_entry = false;
+
+ db::cell_index_type new_cell;
+
+ if (m_cm_entry == m_cell_map.end ()) {
+
+ std::string cn = cell_name;
+ if (! key.clip_region.empty ()) {
+ cn += "$CLIP_VAR";
+ }
+ if (key.inactive) {
+ cn += "$DIS";
+ }
+ new_cell = mp_target->add_cell (cn.c_str ());
+ m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
+ m_cm_new_entry = true;
+ m_cells_to_be_filled.insert (new_cell);
+
+ } else {
+ new_cell = m_cm_entry->second;
+ }
+
+ return new_cell;
+}
+
HierarchyBuilder::new_inst_mode
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
{
if (all) {
- std::pair > key (inst.object ().cell_index (), std::set ());
-
- m_cm_entry = m_cell_map.find (key);
- m_cm_new_entry = false;
-
- if (m_cm_entry == m_cell_map.end ()) {
- db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ()));
- m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
- m_cm_new_entry = true;
- m_cells_to_be_filled.insert (new_cell);
- }
+ CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set ());
+ db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
// for new cells, create this instance
if (m_cell_stack.back ().first) {
db::CellInstArray new_inst (inst, &mp_target->array_repository ());
- new_inst.object () = db::CellInst (m_cm_entry->second);
+ new_inst.object () = db::CellInst (new_cell);
new_inst.transform_into (m_trans);
for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
(*c)->insert (new_inst);
@@ -353,24 +381,12 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
return false;
}
- std::pair > key (inst.object ().cell_index (), clip_variant.second);
- m_cm_entry = m_cell_map.find (key);
- m_cm_new_entry = false;
-
- if (m_cm_entry == m_cell_map.end ()) {
- std::string suffix;
- if (! key.second.empty ()) {
- suffix = "$CLIP_VAR";
- }
- db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ());
- m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
- m_cm_new_entry = true;
- m_cells_to_be_filled.insert (new_cell);
- }
+ CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), clip_variant.second);
+ db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
// for a new cell, create this instance
if (m_cell_stack.back ().first) {
- db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans);
+ db::CellInstArray new_inst (db::CellInst (new_cell), trans);
new_inst.transform_into (m_trans);
for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
(*c)->insert (new_inst);
diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h
index 2ddd6fe22..970f2e2a5 100644
--- a/src/db/db/dbHierarchyBuilder.h
+++ b/src/db/db/dbHierarchyBuilder.h
@@ -225,8 +225,36 @@ class DB_PUBLIC HierarchyBuilder
: public db::RecursiveShapeReceiver
{
public:
+ struct CellMapKey
+ {
+ CellMapKey ()
+ : original_cell (0), inactive (false)
+ { }
- typedef std::map >, db::cell_index_type> cell_map_type;
+ CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region)
+ : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region)
+ { }
+
+ bool operator== (const CellMapKey &other) const
+ {
+ return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region;
+ }
+
+ bool operator< (const CellMapKey &other) const
+ {
+ if (original_cell != other.original_cell) { return original_cell < other.original_cell; }
+ if (inactive != other.inactive) { return inactive < other.inactive; }
+ if (clip_region != other.clip_region) { return clip_region < other.clip_region; }
+ return false;
+ }
+
+ db::cell_index_type original_cell;
+ bool inactive;
+ std::set clip_region;
+ };
+
+
+ typedef std::map cell_map_type;
typedef std::map > original_target_to_variants_map_type;
typedef std::map variant_to_original_target_map_type;
@@ -346,6 +374,8 @@ public:
db::cell_index_type original_target_for_variant (db::cell_index_type ci) const;
private:
+ db::cell_index_type make_cell_variant (const HierarchyBuilder::CellMapKey &key, const std::string &cell_name);
+
tl::weak_ptr mp_target;
HierarchyBuilderShapeReceiver *mp_pipe;
bool m_initial_pass;
diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc
index 7783ffbc6..0067f9517 100644
--- a/src/db/db/dbNetlistExtractor.cc
+++ b/src/db/db/dbNetlistExtractor.cc
@@ -51,7 +51,7 @@ void NetlistExtractor::set_include_floating_subcircuits (bool f)
}
static void
-build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, const std::string &joined_net_names, tl::equivalence_clusters &eq)
+build_net_name_equivalence (const db::Layout *layout, const db::Connectivity &conn, db::property_names_id_type net_name_id, const std::string &joined_net_names, tl::equivalence_clusters &eq)
{
std::map > prop_by_name;
tl::GlobPattern jn_pattern (joined_net_names);
@@ -67,6 +67,14 @@ build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type
}
}
+ // include pseudo-attributes for global nets to implement "join_with" for global nets
+ for (size_t gid = 0; gid < conn.global_nets (); ++gid) {
+ const std::string &gn = conn.global_net_name (gid);
+ if (jn_pattern.match (gn)) {
+ prop_by_name [gn].insert (db::global_net_id_to_attr (gid));
+ }
+ }
+
const db::repository &text_repository = layout->shape_repository ().repository (db::object_tag ());
for (db::repository::iterator t = text_repository.begin (); t != text_repository.end (); ++t) {
std::string nn = t->string ();
@@ -107,12 +115,12 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
std::map > net_name_equivalence;
if (m_text_annot_name_id.first) {
if (! m_joined_net_names.empty ()) {
- build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, m_joined_net_names, net_name_equivalence [hier_clusters_type::top_cell_index]);
+ build_net_name_equivalence (mp_layout, conn, m_text_annot_name_id.second, m_joined_net_names, net_name_equivalence [hier_clusters_type::top_cell_index]);
}
for (std::list >::const_iterator m = m_joined_net_names_per_cell.begin (); m != m_joined_net_names_per_cell.end (); ++m) {
std::pair cp = mp_layout->cell_by_name (m->first.c_str ());
if (cp.first) {
- build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, m->second, net_name_equivalence [cp.second]);
+ build_net_name_equivalence (mp_layout, conn, m_text_annot_name_id.second, m->second, net_name_equivalence [cp.second]);
}
}
}
@@ -313,7 +321,7 @@ void NetlistExtractor::collect_labels (const connected_clusters_type &clusters,
}
- } else {
+ } else if (db::is_text_ref_attr (*a)) {
net_names.insert (db::text_from_attr (*a));
diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc
index 129fc248d..c019ef7c4 100644
--- a/src/db/db/dbNetlistSpiceReader.cc
+++ b/src/db/db/dbNetlistSpiceReader.cc
@@ -104,46 +104,66 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
std::string cn = model;
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (cn);
- if (cls) {
+ if (element == "R") {
- // use given class
-
- } else if (element == "R") {
-
- if (cn.empty ()) {
- cn = "RES";
+ if (cls) {
+ if (! dynamic_cast(cls)) {
+ error (tl::sprintf (tl::to_string (tr ("Class %s not a resistor device class as required by 'R' element")), cn));
+ }
+ } else {
+ if (cn.empty ()) {
+ cn = "RES";
+ }
+ cls = make_device_class (circuit, cn);
}
- cls = make_device_class (circuit, cn);
// Apply multiplier
value /= mult;
} else if (element == "L") {
- if (cn.empty ()) {
- cn = "IND";
+ if (cls) {
+ if (! dynamic_cast(cls)) {
+ error (tl::sprintf (tl::to_string (tr ("Class %s not a inductor device class as required by 'L' element")), cn));
+ }
+ } else {
+ if (cn.empty ()) {
+ cn = "IND";
+ }
+ cls = make_device_class (circuit, cn);
}
- cls = make_device_class (circuit, cn);
// Apply multiplier
value /= mult;
} else if (element == "C") {
- if (cn.empty ()) {
- cn = "CAP";
+ if (cls) {
+ if (! dynamic_cast(cls)) {
+ error (tl::sprintf (tl::to_string (tr ("Class %s not a capacitor device class as required by 'C' element")), cn));
+ }
+ } else {
+ if (cn.empty ()) {
+ cn = "CAP";
+ }
+ cls = make_device_class (circuit, cn);
}
- cls = make_device_class (circuit, cn);
// Apply multiplier
value *= mult;
} else if (element == "D") {
- if (cn.empty ()) {
- cn = "DIODE";
+ if (cls) {
+ if (! dynamic_cast(cls)) {
+ error (tl::sprintf (tl::to_string (tr ("Class %s not a diode device class as required by 'D' element")), cn));
+ }
+ } else {
+ if (cn.empty ()) {
+ cn = "DIODE";
+ }
+ cls = make_device_class (circuit, cn);
}
- cls = make_device_class (circuit, cn);
// Apply multiplier to "A"
std::map::iterator p;
@@ -154,18 +174,30 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} else if (element == "Q") {
- if (nets.size () == 3) {
- if (cn.empty ()) {
- cn = "BJT3";
- }
- cls = make_device_class (circuit, cn);
- } else if (nets.size () == 4) {
- if (cn.empty ()) {
- cn = "BJT4";
- }
- cls = make_device_class (circuit, cn);
- } else {
+ if (nets.size () != 3 && nets.size () != 4) {
error (tl::to_string (tr ("'Q' element needs to have 3 or 4 terminals")));
+ } else if (cls) {
+ if (nets.size () == 3) {
+ if (! dynamic_cast(cls)) {
+ error (tl::sprintf (tl::to_string (tr ("Class %s not a 3-terminal BJT device class as required by 'Q' element")), cn));
+ }
+ } else {
+ if (! dynamic_cast(cls)) {
+ error (tl::sprintf (tl::to_string (tr ("Class %s not a 4-terminal BJT device class as required by 'Q' element")), cn));
+ }
+ }
+ } else {
+ if (nets.size () == 3) {
+ if (cn.empty ()) {
+ cn = "BJT3";
+ }
+ cls = make_device_class (circuit, cn);
+ } else {
+ if (cn.empty ()) {
+ cn = "BJT4";
+ }
+ cls = make_device_class (circuit, cn);
+ }
}
// Apply multiplier to "AE"
@@ -177,22 +209,28 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} else if (element == "M") {
- if (nets.size () == 4) {
- if (cn.empty ()) {
- cn = "MOS4";
+ if (cls) {
+ if (! dynamic_cast(cls)) {
+ error (tl::sprintf (tl::to_string (tr ("Class %s not a 4-terminal MOS device class as required by 'M' element")), cn));
}
- cls = make_device_class (circuit, cn);
-
- // Apply multiplier to "W"
- std::map::iterator p;
- p = params.find ("W");
- if (p != params.end ()) {
- p->second *= mult;
- }
-
} else {
- error (tl::to_string (tr ("'M' element needs to have 4 terminals")));
+ if (nets.size () == 4) {
+ if (cn.empty ()) {
+ cn = "MOS4";
+ }
+ cls = make_device_class (circuit, cn);
+ } else {
+ error (tl::to_string (tr ("'M' element needs to have 4 terminals")));
+ }
}
+
+ // Apply multiplier to "W"
+ std::map::iterator p;
+ p = params.find ("W");
+ if (p != params.end ()) {
+ p->second *= mult;
+ }
+
} else {
error (tl::sprintf (tl::to_string (tr ("Not a known element type: '%s'")), element));
}
diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc
index e3e571721..bbb6e763e 100644
--- a/src/db/db/dbRecursiveShapeIterator.cc
+++ b/src/db/db/dbRecursiveShapeIterator.cc
@@ -864,10 +864,9 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const
m_layer = m_layers.front ();
}
- if (! m_start.empty () && m_start.find (cell_index ()) != m_start.end ()) {
- set_inactive (false);
- } else if (! m_stop.empty () && m_stop.find (cell_index ()) != m_stop.end ()) {
- set_inactive (true);
+ bool new_cell_inactive = is_child_inactive (cell_index ());
+ if (is_inactive () != new_cell_inactive) {
+ set_inactive (new_cell_inactive);
}
new_layer ();
@@ -975,6 +974,18 @@ RecursiveShapeIterator::is_outside_complex_region (const db::Box &box) const
}
}
+bool
+RecursiveShapeIterator::is_child_inactive (db::cell_index_type new_child) const
+{
+ bool inactive = is_inactive ();
+ if (! m_start.empty () && m_start.find (new_child) != m_start.end ()) {
+ inactive = false;
+ } else if (! m_stop.empty () && m_stop.find (new_child) != m_stop.end ()) {
+ inactive = true;
+ }
+ return inactive;
+}
+
void
RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver)
{
diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h
index 40225b8d1..8dc398b95 100644
--- a/src/db/db/dbRecursiveShapeIterator.h
+++ b/src/db/db/dbRecursiveShapeIterator.h
@@ -704,6 +704,19 @@ public:
*/
void push (RecursiveShapeReceiver *receiver);
+ /**
+ * @brief Returns a value indicating whether the current cell is inactive (disabled)
+ */
+ bool is_inactive () const
+ {
+ return (reinterpret_cast (mp_cell) & size_t (1)) != 0;
+ }
+
+ /**
+ * @brief Returns a value indicating whether a new child cell of the current cell will be inactive
+ */
+ bool is_child_inactive (db::cell_index_type new_child) const;
+
private:
std::vector m_layers;
bool m_has_layers;
@@ -760,11 +773,6 @@ private:
bool is_outside_complex_region (const db::Box &box) const;
- bool is_inactive () const
- {
- return (reinterpret_cast (mp_cell) & size_t (1)) != 0;
- }
-
void set_inactive (bool a) const
{
size_t c = reinterpret_cast (mp_cell);
diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc
index 1ca13c811..5c051852e 100644
--- a/src/db/db/gsiDeclDbCell.cc
+++ b/src/db/db/gsiDeclDbCell.cc
@@ -1491,6 +1491,7 @@ Class decl_Cell ("db", "Cell",
"If no property with that key exists, it will create one. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
+ "Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"\n"
"This method has been introduced in version 0.23."
) +
@@ -3453,6 +3454,7 @@ Class decl_Instance ("db", "Instance",
"If no property with that key exists, it will create one. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
+ "Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"Calling this method may invalidate any iterators. It should not be called inside a "
"loop iterating over instances.\n"
"\n"
diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc
index 79574277b..2e85264b4 100644
--- a/src/db/db/gsiDeclDbLayout.cc
+++ b/src/db/db/gsiDeclDbLayout.cc
@@ -1028,11 +1028,12 @@ Class decl_Layout ("db", "Layout",
"This method has been introduced in version 0.24."
) +
gsi::method_ext ("set_property", &set_layout_property, gsi::arg ("key"), gsi::arg ("value"),
- "@brief Set the user property with the given key to the given value\n"
+ "@brief Sets the user property with the given key to the given value\n"
"This method is a convenience method that sets the property with the given key to the given value. "
"If no property with that key exists, it will create one. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
+ "Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"\n"
"This method has been introduced in version 0.24."
) +
diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc
index b0ae59e3b..1062c1374 100644
--- a/src/db/db/gsiDeclDbShape.cc
+++ b/src/db/db/gsiDeclDbShape.cc
@@ -1283,6 +1283,7 @@ Class decl_Shape ("db", "Shape",
"If no property with that key exists, it will create one. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
+ "Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"Calling this method will invalidate any iterators. It should not be called inside a "
"loop iterating over shapes.\n"
"\n"
diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc
index 3e886b16a..b2feaff2a 100644
--- a/src/db/unit_tests/dbLayoutToNetlistTests.cc
+++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc
@@ -3156,3 +3156,232 @@ TEST(12_FlattenCircuitDoesFlattenLayout)
db::compare_layouts (_this, ly, au);
}
+TEST(13_JoinNets)
+{
+ db::Layout ly;
+ db::LayerMap lmap;
+
+ unsigned int nwell = define_layer (ly, lmap, 1);
+ unsigned int active = define_layer (ly, lmap, 2);
+ unsigned int pplus = define_layer (ly, lmap, 10);
+ unsigned int nplus = define_layer (ly, lmap, 11);
+ unsigned int poly = define_layer (ly, lmap, 3);
+ unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
+ unsigned int diff_cont = define_layer (ly, lmap, 4);
+ unsigned int poly_cont = define_layer (ly, lmap, 5);
+ unsigned int metal1 = define_layer (ly, lmap, 6);
+ unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
+ unsigned int via1 = define_layer (ly, lmap, 7);
+ unsigned int metal2 = define_layer (ly, lmap, 8);
+ unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
+
+ {
+ db::LoadLayoutOptions options;
+ options.get_options ().layer_map = lmap;
+ options.get_options ().create_other_layers = false;
+
+ std::string fn (tl::testsrc ());
+ fn = tl::combine_path (fn, "testdata");
+ fn = tl::combine_path (fn, "algo");
+ fn = tl::combine_path (fn, "device_extract_l13.gds");
+
+ tl::InputStream stream (fn);
+ db::Reader reader (stream);
+ reader.read (ly, options);
+ }
+
+ db::Cell &tc = ly.cell (*ly.begin_top_down ());
+ db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ()));
+
+ std::auto_ptr rbulk (l2n.make_layer ("bulk"));
+ std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell"));
+ std::auto_ptr ractive (l2n.make_layer (active, "active"));
+ std::auto_ptr rpplus (l2n.make_layer (pplus, "pplus"));
+ std::auto_ptr rnplus (l2n.make_layer (nplus, "nplus"));
+ std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly"));
+ std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
+ std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont"));
+ std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont"));
+ std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
+ std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
+ std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1"));
+ std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
+ std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl"));
+
+ // derived regions
+
+ db::Region ractive_in_nwell = *ractive & *rnwell;
+ db::Region rpactive = ractive_in_nwell & *rpplus;
+ db::Region rntie = ractive_in_nwell & *rnplus;
+ db::Region rpgate = rpactive & *rpoly;
+ db::Region rpsd = rpactive - rpgate;
+
+ db::Region ractive_outside_nwell = *ractive - *rnwell;
+ db::Region rnactive = ractive_outside_nwell & *rnplus;
+ db::Region rptie = ractive_outside_nwell & *rpplus;
+ db::Region rngate = rnactive & *rpoly;
+ db::Region rnsd = rnactive - rngate;
+
+ // return the computed layers into the original layout and write it for debugging purposes
+
+ unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate
+ unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain
+ unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion
+ unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion
+ unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie
+ unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie
+
+ rpgate.insert_into (&ly, tc.cell_index (), lgate);
+ rngate.insert_into (&ly, tc.cell_index (), lgate);
+ rpsd.insert_into (&ly, tc.cell_index (), lsd);
+ rnsd.insert_into (&ly, tc.cell_index (), lsd);
+ rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
+ rnsd.insert_into (&ly, tc.cell_index (), lndiff);
+ rpsd.insert_into (&ly, tc.cell_index (), lptie);
+ rnsd.insert_into (&ly, tc.cell_index (), lntie);
+
+ db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS");
+ db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS");
+
+ // device extraction
+
+ db::NetlistDeviceExtractor::input_layers dl;
+
+ dl["SD"] = &rpsd;
+ dl["G"] = &rpgate;
+ dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
+ dl["W"] = rnwell.get ();
+ l2n.extract_devices (pmos_ex, dl);
+
+ dl["SD"] = &rnsd;
+ dl["G"] = &rngate;
+ dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
+ dl["W"] = rbulk.get ();
+ l2n.extract_devices (nmos_ex, dl);
+
+ // net extraction
+
+ l2n.register_layer (rpsd, "psd");
+ l2n.register_layer (rnsd, "nsd");
+ l2n.register_layer (rptie, "ptie");
+ l2n.register_layer (rntie, "ntie");
+
+ // Intra-layer
+ l2n.connect (rpsd);
+ l2n.connect (rnsd);
+ l2n.connect (*rnwell);
+ l2n.connect (*rpoly);
+ l2n.connect (*rdiff_cont);
+ l2n.connect (*rpoly_cont);
+ l2n.connect (*rmetal1);
+ l2n.connect (*rvia1);
+ l2n.connect (*rmetal2);
+ l2n.connect (rptie);
+ l2n.connect (rntie);
+ // Inter-layer
+ l2n.connect (rpsd, *rdiff_cont);
+ l2n.connect (rnsd, *rdiff_cont);
+ l2n.connect (*rpoly, *rpoly_cont);
+ l2n.connect (*rpoly_cont, *rmetal1);
+ l2n.connect (*rdiff_cont, *rmetal1);
+ l2n.connect (*rdiff_cont, rptie);
+ l2n.connect (*rdiff_cont, rntie);
+ l2n.connect (*rnwell, rntie);
+ l2n.connect (*rmetal1, *rvia1);
+ l2n.connect (*rvia1, *rmetal2);
+ l2n.connect (*rpoly, *rpoly_lbl); // attaches labels
+ l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels
+ l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels
+ // Global
+ l2n.connect_global (rntie, "VDD");
+ l2n.connect_global (*rnwell, "VDD");
+ l2n.connect_global (rptie, "VSS");
+ l2n.connect_global (*rbulk, "VSS");
+
+ // Extract with joining VSS and VDD
+ l2n.extract_netlist ("{VSS,VDD}");
+
+ // debug layers produced for nets
+ // 201/0 -> Well
+ // 203/0 -> Poly
+ // 204/0 -> Diffusion contacts
+ // 205/0 -> Poly contacts
+ // 206/0 -> Metal1
+ // 207/0 -> Via1
+ // 208/0 -> Metal2
+ // 210/0 -> N source/drain
+ // 211/0 -> P source/drain
+ // 212/0 -> N tie
+ // 213/0 -> P tie
+ std::map dump_map;
+ dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0));
+ dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0));
+ dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0));
+ dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0));
+ dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0));
+ dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0));
+ dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0));
+ dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0));
+ dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0));
+ dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0));
+ dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0));
+ dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0));
+
+ // write nets to layout
+ db::CellMapping cm = l2n.cell_mapping_into (ly, tc);
+ dump_nets_to_layout (l2n, ly, dump_map, cm);
+
+ dump_map.clear ();
+ dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0));
+ dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0));
+ dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0));
+ dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0));
+ dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0));
+ dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0));
+ dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0));
+ dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0));
+ dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0));
+ dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0));
+ dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0));
+ dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0));
+
+ dump_recursive_nets_to_layout (l2n, ly, dump_map, cm);
+
+ // compare netlist as string
+ CHECKPOINT ();
+ db::compare_netlist (_this, *l2n.netlist (),
+ "circuit RINGO ();\n"
+ " subcircuit INV2 $1 (IN=$I7,$2=FB,OUT=OSC,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ " subcircuit INV2 $2 (IN=FB,$2=$I34,OUT=$I17,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ " subcircuit INV2 $3 (IN=$3,$2=$I38,OUT=$I4,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ " subcircuit INV2 $4 (IN=$I2,$2=$I37,OUT=$3,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ " subcircuit INV2 $5 (IN=$I4,$2=$I39,OUT=$I5,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ " subcircuit INV2 $6 (IN=$I5,$2=$I40,OUT=$I6,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ " subcircuit INV2 $7 (IN=$I6,$2=$I41,OUT=$I7,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ " subcircuit INV2 $8 (IN=$I17,$2=$I35,OUT=$I1,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ " subcircuit INV2 $9 (IN=$I1,$2=$I36,OUT=$I2,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
+ "end;\n"
+ "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5,VDD=VDD,VSS=VSS);\n"
+ " device PMOS $1 (S=$2,G=IN,D=$5,B=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
+ " device PMOS $2 (S=$5,G=$2,D=OUT,B=VDD) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
+ " device NMOS $3 (S=$2,G=IN,D=$4,B=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
+ " device NMOS $4 (S=$4,G=$2,D=OUT,B=VSS) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
+ " subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n"
+ " subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n"
+ " subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n"
+ " subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n"
+ "end;\n"
+ "circuit TRANS ($1=$1,$2=$2,$3=$3);\n"
+ "end;\n"
+ );
+
+ // compare the collected test data
+
+ std::string au = tl::testsrc ();
+ au = tl::combine_path (au, "testdata");
+ au = tl::combine_path (au, "algo");
+ au = tl::combine_path (au, "device_extract_au13_circuits.gds");
+
+ db::compare_layouts (_this, ly, au);
+}
+
diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc
index 12f0a97f6..4e4779588 100644
--- a/src/db/unit_tests/dbNetlistReaderTests.cc
+++ b/src/db/unit_tests/dbNetlistReaderTests.cc
@@ -359,28 +359,75 @@ TEST(9_DeviceMultipliers)
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader9.cir");
- db::NetlistSpiceReader reader;
- tl::InputStream is (path);
- reader.read (is, nl);
+ {
+ db::NetlistSpiceReader reader;
+ tl::InputStream is (path);
+ reader.read (is, nl);
- std::string nl_string = nl.to_string ();
- // normalization of exponential representation:
- nl_string = tl::replaced (nl_string, "e-009", "e-09");
+ std::string nl_string = nl.to_string ();
+ // normalization of exponential representation:
+ nl_string = tl::replaced (nl_string, "e-009", "e-09");
- EXPECT_EQ (nl_string,
- "circuit .TOP ();\n"
- " device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
- " device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
- " device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n"
- " device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n"
- " device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
- " device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
- " device DIODE $1 (A='1',C='2') (A=20,P=0);\n"
- " device DIODE $2 (A='3',C='4') (A=10,P=0);\n"
- " device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
- " device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
- "end;\n"
- );
+ EXPECT_EQ (nl_string,
+ "circuit .TOP ();\n"
+ " device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
+ " device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
+ " device RMODEL $3 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
+ " device RMODEL $4 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
+ " device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n"
+ " device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n"
+ " device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
+ " device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
+ " device CMODEL $3 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
+ " device CMODEL $4 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
+ " device IND $1 (A='1',B='2') (L=5e-10);\n"
+ " device IND $2 (A='3',B='4') (L=1e-09);\n"
+ " device LMODEL $3 (A='1',B='2') (L=5e-10);\n"
+ " device LMODEL $4 (A='3',B='4') (L=1e-09);\n"
+ " device DIODE $1 (A='1',C='2') (A=20,P=0);\n"
+ " device DIODE $2 (A='3',C='4') (A=10,P=0);\n"
+ " device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
+ " device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
+ "end;\n"
+ );
+ }
+
+ db::Circuit *top = nl.circuit_by_name (".TOP");
+ nl.remove_circuit (top);
+
+ // read once again, this time with known classes (must not trigger issue-652)
+ {
+ db::NetlistSpiceReader reader;
+ tl::InputStream is (path);
+ reader.read (is, nl);
+
+ std::string nl_string = nl.to_string ();
+ // normalization of exponential representation:
+ nl_string = tl::replaced (nl_string, "e-009", "e-09");
+
+ EXPECT_EQ (nl_string,
+ "circuit .TOP ();\n"
+ " device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
+ " device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
+ " device RMODEL $3 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
+ " device RMODEL $4 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
+ " device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n"
+ " device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n"
+ " device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
+ " device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
+ " device CMODEL $3 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
+ " device CMODEL $4 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
+ " device IND $1 (A='1',B='2') (L=5e-10);\n"
+ " device IND $2 (A='3',B='4') (L=1e-09);\n"
+ " device LMODEL $3 (A='1',B='2') (L=5e-10);\n"
+ " device LMODEL $4 (A='3',B='4') (L=1e-09);\n"
+ " device DIODE $1 (A='1',C='2') (A=20,P=0);\n"
+ " device DIODE $2 (A='3',C='4') (A=10,P=0);\n"
+ " device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
+ " device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
+ "end;\n"
+ );
+ }
}
TEST(10_SubcircuitsNoPins)
diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb
index 6c6b6186a..cdbe786f4 100644
--- a/src/drc/drc/built-in-macros/_drc_engine.rb
+++ b/src/drc/drc/built-in-macros/_drc_engine.rb
@@ -1253,7 +1253,7 @@ module DRC
# @/code
#
# (Technically, the cheat code block is a Ruby Proc and cannot create variables
- # outside it's scope. Hence the results of this code block have to be passed
+ # outside its scope. Hence the results of this code block have to be passed
# through the "cheat" method).
#
# To apply cheats for device extraction, use the following scheme:
@@ -1829,7 +1829,7 @@ CODE
sel.each do |s|
if s == "-"
- iter.unselect_cells(cell.cell_index)
+ iter.unselect_cells([cell_index])
elsif s == "-*"
iter.unselect_all_cells
elsif s == "+*"
diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb
index 2c32640ad..657892e95 100644
--- a/src/drc/drc/built-in-macros/_drc_netter.rb
+++ b/src/drc/drc/built-in-macros/_drc_netter.rb
@@ -162,7 +162,7 @@ module DRC
# @li \global#capacitor_with_bulk - A capacitor with a separate bulk terminal @/li
# @/ul
#
- # Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance
+ # Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs its own instance
# of device extractor. The device extractor beside the algorithm and specific
# extraction settings defines the name of the device to be built.
#
diff --git a/src/drc/drc/built-in-macros/_drc_source.rb b/src/drc/drc/built-in-macros/_drc_source.rb
index c7c846692..8793e93de 100644
--- a/src/drc/drc/built-in-macros/_drc_source.rb
+++ b/src/drc/drc/built-in-macros/_drc_source.rb
@@ -22,14 +22,27 @@ module DRC
@layout_var = layout_var
@path = path
@cell = cell
- @inside = nil
@box = nil
- @layers = nil
@sel = []
@clip = false
@overlapping = false
@tmp_layers = []
end
+
+ # Conceptual deep copy (not including the temp layers)
+ def dup
+ d = DRCSource::new(@engine, @layout, @layout_var, @cell, @path)
+ d._init_internal(@box ? @box.dup : nil, @sel.dup, @clip, @overlapping)
+ d
+ end
+
+ # internal copy initialization
+ def _init_internal(box, sel, clip, overlapping)
+ @box = box
+ @sel = sel
+ @clip = clip
+ @overlapping = overlapping
+ end
# %DRC%
# @name layout
@@ -139,19 +152,34 @@ module DRC
# code:
#
# @code
- # layout_with_selection = layout.select("-TOP", "+B")
- # l1 = layout_with_selection.input(1, 0)
+ # layout_with_selection = source.select("-TOP", "+B")
+ # l1 = source.input(1, 0)
# ...
# @/code
#
# Please note that the sample above will deliver the children of "B" because there is
- # nothing said about how to proceed with cells other than "TOP" or "B".
- # The following code will just select "B" without it's children, because in the
+ # nothing said about how to proceed with cells other than "TOP" or "B". Conceptually,
+ # the instantiation path of a cell will be matched against the different filters in the
+ # order they are given.
+ # A matching negative expression will disable the cell, a matching positive expression
+ # will enable the cell. Hence, every cell that has a "B" in the instantiation path
+ # is enabled.
+ #
+ # The following code will just select "B" without its children, because in the
# first "-*" selection, all cells including the children of "B" are disabled:
#
# @code
- # layout_with_selection = layout.select("-*", "+B")
- # l1 = layout_with_selection.input(1, 0)
+ # layout_with_selection = source.select("-*", "+B")
+ # l1 = source.input(1, 0)
+ # ...
+ # @/code
+ #
+ # The short form "-" will disable the top cell. This code is identical to the first example
+ # and will start with a disabled top cell regardless of its name:
+ #
+ # @code
+ # layout_with_selection = source.select("-", "+B")
+ # l1 = source.input(1, 0)
# ...
# @/code
diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc
index c7d80eb95..11f50376f 100644
--- a/src/drc/unit_tests/drcSimpleTests.cc
+++ b/src/drc/unit_tests/drcSimpleTests.cc
@@ -755,3 +755,44 @@ TEST(16_issue570)
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
+
+// Problems with Source#select
+TEST(17_issue570)
+{
+ std::string rs = tl::testsrc ();
+ rs += "/testdata/drc/drcSimpleTests_17.drc";
+
+ std::string input = tl::testsrc ();
+ input += "/testdata/drc/drcSimpleTests_17.gds";
+
+ std::string au = tl::testsrc ();
+ au += "/testdata/drc/drcSimpleTests_au17.gds";
+
+ std::string output = this->tmp_file ("tmp.gds");
+
+ {
+ // Set some variables
+ lym::Macro config;
+ config.set_text (tl::sprintf (
+ "$drc_test_source = '%s'\n"
+ "$drc_test_target = '%s'\n"
+ , input, output)
+ );
+ config.set_interpreter (lym::Macro::Ruby);
+ EXPECT_EQ (config.run (), 0);
+ }
+
+ lym::Macro drc;
+ drc.load_from (rs);
+ EXPECT_EQ (drc.run (), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::Reader reader (stream);
+ reader.read (layout);
+ }
+
+ db::compare_layouts (_this, layout, au, db::NoNormalization);
+}
diff --git a/src/klayout.pri b/src/klayout.pri
index d9d1fdc41..245b3f12d 100644
--- a/src/klayout.pri
+++ b/src/klayout.pri
@@ -160,7 +160,7 @@ equals(HAVE_QT, "0") {
} else {
DEFINES += HAVE_QT
- QT += core network xml sql
+ QT += core network xml sql widgets
equals(HAVE_QT5, "1") {
QT += designer printsupport
diff --git a/src/lay/lay/doc/about/custom_queries.xml b/src/lay/lay/doc/about/custom_queries.xml
index 2f4f5acb0..02c70381b 100644
--- a/src/lay/lay/doc/about/custom_queries.xml
+++ b/src/lay/lay/doc/about/custom_queries.xml
@@ -104,7 +104,7 @@ cells T*
- A cell expression can already be used by it's own. The report of such a query will simply contain the cells
+ A cell expression can already be used by its own. The report of such a query will simply contain the cells
selected by that expression. If combined with an action, such expressions already provide useful manipulation
functionality.
@@ -243,7 +243,7 @@ cells TOP..A
is used in (including top cells and all child contexts). Used after a cell, it will return the cell
plus all child cells in each possible context. Used before a cell it will deliver all contexts that cell
is used in every top cells.
- The following query will deliver "TOP" plus all it's direct and indirect children:
+ The following query will deliver "TOP" plus all its direct and indirect children:
@@ -398,7 +398,7 @@ with shapes on layer 6 from instances of TOP.. do initial_cell.shapes(<10/0&g
- That expression reads all shapes of cell "TOP" and it's children, inserts them into a new layer 10, datatype 0 and
+ That expression reads all shapes of cell "TOP" and its children, inserts them into a new layer 10, datatype 0 and
transforms the shape after it has been inserted. This expression makes use of the variables "initial_cell" (a Cell object
representing the root cell of the cell query), "shape" (a pointer to the currently selected shape and "path_trans"
(a Trans object representing the transformation of the current shape into the root cell of the query).
diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml
index 5cf550238..1476c277e 100644
--- a/src/lay/lay/doc/about/drc_ref_global.xml
+++ b/src/lay/lay/doc/about/drc_ref_global.xml
@@ -164,7 +164,7 @@ end
(Technically, the cheat code block is a Ruby Proc and cannot create variables
-outside it's scope. Hence the results of this code block have to be passed
+outside its scope. Hence the results of this code block have to be passed
through the "cheat" method).
To apply cheats for device extraction, use the following scheme:
diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml
index 6d4a91380..a0b2b14b3 100644
--- a/src/lay/lay/doc/about/drc_ref_netter.xml
+++ b/src/lay/lay/doc/about/drc_ref_netter.xml
@@ -302,7 +302,7 @@ Predefined device extractors are:
global#capacitor_with_bulk - A capacitor with a separate bulk terminal
-Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance
+Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs its own instance
of device extractor. The device extractor beside the algorithm and specific
extraction settings defines the name of the device to be built.
diff --git a/src/lay/lay/doc/about/drc_ref_source.xml b/src/lay/lay/doc/about/drc_ref_source.xml
index d30545115..7b29857d1 100644
--- a/src/lay/lay/doc/about/drc_ref_source.xml
+++ b/src/lay/lay/doc/about/drc_ref_source.xml
@@ -282,19 +282,34 @@ To disable the TOP cell but enabled a hypothetical cell B below the top cell, us
code:
-layout_with_selection = layout.select("-TOP", "+B")
-l1 = layout_with_selection.input(1, 0)
+layout_with_selection = source.select("-TOP", "+B")
+l1 = source.input(1, 0)
...
Please note that the sample above will deliver the children of "B" because there is
-nothing said about how to proceed with cells other than "TOP" or "B".
-The following code will just select "B" without it's children, because in the
+nothing said about how to proceed with cells other than "TOP" or "B". Conceptually,
+the instantiation path of a cell will be matched against the different filters in the
+order they are given.
+A matching negative expression will disable the cell, a matching positive expression
+will enable the cell. Hence, every cell that has a "B" in the instantiation path
+is enabled.
+
+The following code will just select "B" without its children, because in the
first "-*" selection, all cells including the children of "B" are disabled:
-layout_with_selection = layout.select("-*", "+B")
-l1 = layout_with_selection.input(1, 0)
+layout_with_selection = source.select("-*", "+B")
+l1 = source.input(1, 0)
+...
+
+
+The short form "-" will disable the top cell. This code is identical to the first example
+and will start with a disabled top cell regardless of its name:
+
+
+layout_with_selection = source.select("-", "+B")
+l1 = source.input(1, 0)
...
diff --git a/src/lay/lay/doc/about/layer_mapping.xml b/src/lay/lay/doc/about/layer_mapping.xml
index b9209e016..81d6205db 100644
--- a/src/lay/lay/doc/about/layer_mapping.xml
+++ b/src/lay/lay/doc/about/layer_mapping.xml
@@ -130,7 +130,7 @@
If the match expression includes a numerical range or wildcards
for the layer or datatype number, by default all these layers
- will be combined into a single one, where it's layer or datatype number is derived
+ will be combined into a single one, where its layer or datatype number is derived
from the least permitted number.
diff --git a/src/lay/lay/doc/about/technology_manager.xml b/src/lay/lay/doc/about/technology_manager.xml
index 1cc3fa189..a9c508fa9 100644
--- a/src/lay/lay/doc/about/technology_manager.xml
+++ b/src/lay/lay/doc/about/technology_manager.xml
@@ -37,7 +37,7 @@
edited. Instead, a library installed in the system comes with a technology
association itself.
File format options: technology specific file reader or writer options can be
- given. When a layout is saved, it will use the writer options from it's technology.
+ given. When a layout is saved, it will use the writer options from its technology.
When loading a layout, the reader options from the active technology will be used.
diff --git a/src/lay/lay/doc/manual/copypaste_cell.xml b/src/lay/lay/doc/manual/copypaste_cell.xml
index 519e7ebf5..97d59e700 100644
--- a/src/lay/lay/doc/manual/copypaste_cell.xml
+++ b/src/lay/lay/doc/manual/copypaste_cell.xml
@@ -28,7 +28,7 @@
cells with the same name will be used as child cells.
- Deep copy : In deep copy mode, the cell plus it's child cells are copied. All cells will be carried
+ Deep copy : In deep copy mode, the cell plus its child cells are copied. All cells will be carried
along and when pasting the cell, copies of all children will be created as well.
diff --git a/src/lay/lay/doc/manual/create_instance.xml b/src/lay/lay/doc/manual/create_instance.xml
index 07f6c6897..11b6922ab 100644
--- a/src/lay/lay/doc/manual/create_instance.xml
+++ b/src/lay/lay/doc/manual/create_instance.xml
@@ -57,7 +57,7 @@
KLayout offers a unique feature for the PCell implementation: a PCell can employ "guiding shapes".
"Guiding shapes" are shapes that do not appear as layout themselves but are used by the PCell
- to derive it's geometry from. For example the "rounded path" PCell of the "Basic" library
+ to derive its geometry from. For example the "rounded path" PCell of the "Basic" library
uses a path as a guiding shape. This path is manipulated to obtain the final shape.
diff --git a/src/lay/lay/doc/manual/del_cell.xml b/src/lay/lay/doc/manual/del_cell.xml
index e03b36750..7662c96a7 100644
--- a/src/lay/lay/doc/manual/del_cell.xml
+++ b/src/lay/lay/doc/manual/del_cell.xml
@@ -13,13 +13,13 @@
- Shallow delete: Just the cell (it's shapes and instances) are deleted, not
+ Shallow delete: Just the cell (its shapes and instances) are deleted, not
any cells referenced by this cell. Since cells might no longer be referenced after
that, they may appear as new top cells in the layout.
- Deep delete: The cell and all it's subcells are deleted, unless the subcells are
+ Deep delete: The cell and all its subcells are deleted, unless the subcells are
referenced otherwise (by cells that are not deleted).
In this delete mode a complete hierarchy of cells can be removed without side effects.
- Complete delete: The cell and all it's subcells are deleted, even if other cells
+ Complete delete: The cell and all its subcells are deleted, even if other cells
would reference these subcells.
diff --git a/src/lay/lay/doc/manual/flatten.xml b/src/lay/lay/doc/manual/flatten.xml
index 9238089e8..d01579b59 100644
--- a/src/lay/lay/doc/manual/flatten.xml
+++ b/src/lay/lay/doc/manual/flatten.xml
@@ -8,7 +8,7 @@
- The "flatten cell" operation flattens a cell into all of it's parents.
+ The "flatten cell" operation flattens a cell into all of its parents.
This basically removes a cell by promoting her shapes and instances up in the hierarchy.
@@ -16,7 +16,7 @@
Cell flattening can be applied to single instances or cells as a whole.
When applied to an instance, the individual instance is resolved into shapes. The
instantiated cell will still exist afterwards. When applied to a cell, the cell
- will disappear and replaced by it's contents in all places it is used.
+ will disappear and replaced by its contents in all places it is used.
diff --git a/src/lay/lay/doc/manual/hier_ops.xml b/src/lay/lay/doc/manual/hier_ops.xml
index 7f010a337..326492d5f 100644
--- a/src/lay/lay/doc/manual/hier_ops.xml
+++ b/src/lay/lay/doc/manual/hier_ops.xml
@@ -23,7 +23,7 @@
are brought up to the current cell's level and removed from the original cell.
A non-destructive way of moving a shape up in the hierarchy is to copy and paste the shape. This does an
explicit flattening of the shapes selected when inserting them.
- Hint: the current implementation removes the selected object from it's original cell. Since it only creates
+ Hint: the current implementation removes the selected object from its original cell. Since it only creates
new copies for the selected instances, the object ist lost for all other instances of the cell. This may create undesired
side effects and it is likely that this behaviour will change in future implementations.
diff --git a/src/lay/lay/doc/manual/lvs_tweaks.xml b/src/lay/lay/doc/manual/lvs_tweaks.xml
index 2e631f677..12e4b0bf5 100644
--- a/src/lay/lay/doc/manual/lvs_tweaks.xml
+++ b/src/lay/lay/doc/manual/lvs_tweaks.xml
@@ -101,7 +101,7 @@
act as a wrapper to something else. In layouts, devices are often implemented as PCells and
appear as specific cells for no other reason than being implemented in a subcell. The same
might happen for schematic subcircuits which wrap a device. "Flattening" means that a circuit
- is removed and it's contents are integrated into the calling circuits.
+ is removed and its contents are integrated into the calling circuits.
diff --git a/src/lay/lay/doc/manual/main_window.xml b/src/lay/lay/doc/manual/main_window.xml
index d4a7f6ae6..c98c2ef45 100644
--- a/src/lay/lay/doc/manual/main_window.xml
+++ b/src/lay/lay/doc/manual/main_window.xml
@@ -10,7 +10,7 @@
The main window is divided into three parts: the left area is the hierarchy browser and navigator, the
center part of the canvas and the right part is the layer list with the layer toolbox.
The individual components can be rearranged, so the arrangement described is just the default
- arrangement. You can move a component to a new place by dragging it with it's title bar to
+ arrangement. You can move a component to a new place by dragging it with its title bar to
some other place or detach it from the main window to form a floating separate window.
diff --git a/src/lay/lay/doc/manual/move_sel.xml b/src/lay/lay/doc/manual/move_sel.xml
index 4799fd5d7..72403a30c 100644
--- a/src/lay/lay/doc/manual/move_sel.xml
+++ b/src/lay/lay/doc/manual/move_sel.xml
@@ -40,7 +40,7 @@
In the same way, "Move To" allows one to reposition the selection to a certain point. The point which
is positioned can be chosen relative to the bounding box of the selection. If first example,
the lower-left corner of the selection is picked as the reference point and a certain position
- is given in the dialog, the selection is moved such that the lower left position of it's
+ is given in the dialog, the selection is moved such that the lower left position of its
bounding box will match the given coordinate.
diff --git a/src/lay/lay/doc/manual/panels.xml b/src/lay/lay/doc/manual/panels.xml
index 1abaa5183..265baee06 100644
--- a/src/lay/lay/doc/manual/panels.xml
+++ b/src/lay/lay/doc/manual/panels.xml
@@ -30,7 +30,7 @@
Choose "Pull In Other Layout" to combine other layouts already loaded into the
current panel. Basically, KLayout allows viewsing a layout in multiple panels, either
- on it's own in different configurations or together with other layouts. "Pull In Other Layout"
+ on its own in different configurations or together with other layouts. "Pull In Other Layout"
allows configuration of a panel to show another layout which has been loaded into another panel.
In that sense it's the reverse of closing one layout from a panel showing multiple layouts.
diff --git a/src/lay/lay/doc/manual/ruler_properties.xml b/src/lay/lay/doc/manual/ruler_properties.xml
index bfcababf0..d889bd883 100644
--- a/src/lay/lay/doc/manual/ruler_properties.xml
+++ b/src/lay/lay/doc/manual/ruler_properties.xml
@@ -20,7 +20,7 @@
The Alignment of the labels can be specified too: whether the appear left or right-aligned
or centered.
- Style: the style determines how the ruler or it's components are drawn. This can be
+ Style: the style determines how the ruler or its components are drawn. This can be
"ruler-like" (with ticks), arrow style, a plain line or with cross markers at the end.
Outline: the outline determines how the two points forming the ruler are connected
@@ -31,7 +31,7 @@
Angle constraint: the orientation of the ruler can be restricted in several ways, i.e.
just being horizontal. By default, the ruler uses the global setting. It can however be configured
- to provide it's own constraint.
+ to provide its own constraint.
Object snapping: each ruler can be configure to snap to the closest object edge or vertex.
By default, the rulers use the global setting. It may be disabled however for each ruler.
diff --git a/src/lay/lay/doc/programming/application_api.xml b/src/lay/lay/doc/programming/application_api.xml
index 1fb7914fe..5baee3f8d 100644
--- a/src/lay/lay/doc/programming/application_api.xml
+++ b/src/lay/lay/doc/programming/application_api.xml
@@ -206,7 +206,7 @@ Application::instance.main_window.views
to a large degree, so different colors can be used for example. Markers are objects of class
Marker ( ).
- Local configuration : be default, the layout view pulls it's configuration from the global
+ Local configuration : be default, the layout view pulls its configuration from the global
configuration database. It is possible however to override certain configuration parameters for a
particular view. This allows for example to set the background color for a particular view without
affecting the other views.
@@ -703,7 +703,7 @@ layout_view.insert_layer(lp, props)
Width is a "weak" property. That means that for computing the effective width, child nodes
can override the settings inherited from the parent nodes. A width of 0 is considered "not set" and does not
override parent defined widths. Other properties like visibility are "strong", i.e.
- the parent can override the properties set for it's children. Another form of combination is "additive" where
+ the parent can override the properties set for its children. Another form of combination is "additive" where
the effective property value is the "sum" (or in general combination) of all local properties from parent
to child.
diff --git a/src/lay/lay/doc/programming/database_api.xml b/src/lay/lay/doc/programming/database_api.xml
index 3607af938..07fe4b06a 100644
--- a/src/lay/lay/doc/programming/database_api.xml
+++ b/src/lay/lay/doc/programming/database_api.xml
@@ -161,7 +161,7 @@ layout.write("my_layout.gds")
- A layout can provide and import PCells. PCells are cells that provide it's geometry through program code
+ A layout can provide and import PCells. PCells are cells that provide its geometry through program code
(for example written in Ruby) and provide parameters which can be adjusted to change the appearance of the
cell. For each PCell a "declaration" must be provided which basically contains the code for the
PCell and some information about the parameters provided by the PCell. PCells are stored in the layout
@@ -473,6 +473,13 @@ shape.set_property(1, "NewValue")
and "delete_property" also are provided for cells and cell instances ( and ).
+
+ Note: The GDS format does not have string names for properties. As a result GDS only supports numeric property keys.
+ OASIS, on the other hand, can handle string and numeric property "names". When saving layouts in GDS format, KLayout tries
+ to convert the properties' names into numbers if possible (i.e. if it sees that the string is a number). If it can't, then
+ the property is not saved. When the layout is read by KLayout upon opening, it gets converted to integer.
+
+
The LayerInfo class
@@ -813,7 +820,7 @@ end
- Despite it's name, a object holds a cell reference which is
+ Despite its name, a object holds a cell reference which is
not only an array, but also a single instances. The object represents a raw instance, in contrast to the
Instance object which is basically a pointer to an instance inside the database. CellInstArray objects as raw instances can be created,
copied, modified and stored in the usual containers, but once they are stored inside the Cell object, they can
@@ -1060,7 +1067,7 @@ end
The object provides a unified view to a geometrical primitive inside
a Shapes container. It also plays an important role for addressing geometrical primitives
inside a Shapes container. A Shape object has general methods and specific methods that apply
- depending on it's identity.
+ depending on its identity.
General methods
diff --git a/src/lay/lay/doc/programming/events.xml b/src/lay/lay/doc/programming/events.xml
index ee9b4e657..827102a7c 100644
--- a/src/lay/lay/doc/programming/events.xml
+++ b/src/lay/lay/doc/programming/events.xml
@@ -108,7 +108,7 @@ end
Events are the callback variant which is the easiest one to use. Using an event it is possible
to directly attach a block of code to a callback. An event has a specific signature, i.e.
- the parameters it provides. The block can obtain this parameters by listing them in it's argument list.
+ the parameters it provides. The block can obtain this parameters by listing them in its argument list.
diff --git a/src/lay/lay/doc/programming/geometry_api.xml b/src/lay/lay/doc/programming/geometry_api.xml
index e0628293c..cff528b57 100644
--- a/src/lay/lay/doc/programming/geometry_api.xml
+++ b/src/lay/lay/doc/programming/geometry_api.xml
@@ -36,7 +36,7 @@
The object represents an integer coordinate in the 2-dimensional layout space,
expressed in database units.
That object provides the x and y coordinates through the and attributes. Points can be added,
- subtracted, the euclidian distance and it's square can be computed using the and
+ subtracted, the euclidian distance and its square can be computed using the and
methods. The * operator used with a factor as the second operand will scale both the x and y coordinates.
@@ -113,7 +113,7 @@
Box + Point : computes the box that encloses the box and the point given
as the second operand. It basically enlarges the first box so it includes the second point as well.
- contains?(Point) : returns true, if the point is inside the box or on it's edges.
+ contains?(Point) : returns true, if the point is inside the box or on its edges.
enlarge(Point) : will enlarge the box by the x and y coordinates of the Point. Basically the x value
of the point is subtracted from the left side and added to the right. The same way, the y coordinate is added
@@ -272,7 +272,7 @@ box = RBA::Box::new(dbox)
A object represents a line with a certain width. The figure above depicts the basic properties
- of a path. The basic geometry of a path is defined by it's spine - a sequence of points that the path follows.
+ of a path. The basic geometry of a path is defined by its spine - a sequence of points that the path follows.
By default, a path has rectangular end caps with variable length. The end caps can be round, in which case the extension
should be half the path width to avoid paths which are not compatible with the GDS2 format. Round-ended paths return
true on . An example for such a path is depicted in the following figure:
@@ -641,7 +641,7 @@ ly.top_cell.shapes(ly.layer(100, 0)).insert(r11 & r21)
edge pair collections, not regions. Each edge pair marks a
violation of the given check.
- replaces each polygon with it's bounding box.
+ replaces each polygon with its bounding box.
performs an on-grid check returning edge pairs for off-grid markers.
identifies holes and delivers a new region with the holes as filled polygons.
identifies holes and delivers a new region without the holes.
diff --git a/src/lay/lay/doc/programming/qt_binding.xml b/src/lay/lay/doc/programming/qt_binding.xml
index ae227cfa3..7d5c22b3a 100644
--- a/src/lay/lay/doc/programming/qt_binding.xml
+++ b/src/lay/lay/doc/programming/qt_binding.xml
@@ -165,7 +165,7 @@ end
- When a Qt object is created in Ruby space with a parent, it's
+ When a Qt object is created in Ruby space with a parent, its
ownership is passed to the parent. It is safe however to keep a reference to that object, because
KLayout's Ruby binding employs a special Ruby class internally (a proxy) which keeps track of the lifetime
of the Qt object. If the parent is destroyed, the Qt object is destroyed as well. The internal Qt object will
diff --git a/src/lay/lay/doc/programming/ruby_binding.xml b/src/lay/lay/doc/programming/ruby_binding.xml
index 7449de54a..6ff93b20c 100644
--- a/src/lay/lay/doc/programming/ruby_binding.xml
+++ b/src/lay/lay/doc/programming/ruby_binding.xml
@@ -119,7 +119,7 @@ cell.shapes(layer).insert(box)
An exception to the lifetime control rule given above are Qt objects: a common pattern is to create Qt objects
and add them to a container (i.e. widgets to a dialog). This implies a lifetime control transfer from
Ruby to C++. RBA handles that case by explicitly transferring control when a QObject or one of the
- derived objects is created with a parent reference in Ruby code. Qt implements it's own mechanism of controlling
+ derived objects is created with a parent reference in Ruby code. Qt implements its own mechanism of controlling
the lifetime which includes monitoring of the lifetime of child objects. This feature makes transferring the
control feasible for these kind of objects. For some Qt methods which are know to transfer the ownership
of an object, the ownership is transferred explicitly.
diff --git a/src/lay/lay/doc/programming/ruby_pcells.xml b/src/lay/lay/doc/programming/ruby_pcells.xml
index caabcf7ac..d068d3ef5 100644
--- a/src/lay/lay/doc/programming/ruby_pcells.xml
+++ b/src/lay/lay/doc/programming/ruby_pcells.xml
@@ -310,7 +310,7 @@ end
is used for the cell tree and cell box labels. To avoid confusion, it should start with the name of the
PCell. The bracket notation is not mandatory, but it's always a good idea to follow some common style.
The information delivered by this method should be short but contain enough information so that a
- PCell variant can be distinguished from it's sibling.
+ PCell variant can be distinguished from its sibling.
diff --git a/src/lay/lay/lay.pro b/src/lay/lay/lay.pro
index 9724ba932..f2ba59502 100644
--- a/src/lay/lay/lay.pro
+++ b/src/lay/lay/lay.pro
@@ -190,6 +190,10 @@ win32 {
LIBS += -lpsapi -ldbghelp
}
+*bsd* {
+ LIBS += -lexecinfo
+}
+
# Note: this accounts for UI-generated headers placed into the output folders in
# shadow builds:
INCLUDEPATH += $$DESTDIR/laybasic/laybasic
diff --git a/src/lay/lay/laySignalHandler.cc b/src/lay/lay/laySignalHandler.cc
index f1d7462a0..73da25731 100644
--- a/src/lay/lay/laySignalHandler.cc
+++ b/src/lay/lay/laySignalHandler.cc
@@ -450,7 +450,7 @@ void install_signal_handlers ()
act.sa_sigaction = signal_handler;
sigemptyset (&act.sa_mask);
act.sa_flags = SA_SIGINFO;
-#if !defined(__APPLE__) && !defined(__OpenBSD__)
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
act.sa_restorer = 0;
#endif
diff --git a/src/lym/unit_tests/lymBasicTests.cc b/src/lym/unit_tests/lymBasicTests.cc
index 10a6a7362..d0b2eaf14 100644
--- a/src/lym/unit_tests/lymBasicTests.cc
+++ b/src/lym/unit_tests/lymBasicTests.cc
@@ -28,6 +28,7 @@
#include "gsiInterpreter.h"
#include "rba.h"
#include "pya.h"
+#include "tlFileUtils.h"
class TestCollectorConsole
: public gsi::Console
@@ -52,6 +53,11 @@ private:
std::string m_text;
};
+static std::string np (const std::string &s)
+{
+ return tl::replaced (s, "\\", "/");
+}
+
#if defined(HAVE_RUBY)
TEST(1_BasicRuby)
@@ -120,7 +126,7 @@ TEST(3_RubyInclude)
throw;
}
- EXPECT_EQ (console.text (), "An error in " + tl::testsrc () + "/testdata/lym/b_inc.rb:3\n");
+ EXPECT_EQ (np (console.text ()), np ("An error in " + tl::testsrc () + "/testdata/lym/b_inc.rb:3\n"));
}
TEST(11_DRCBasic)
diff --git a/src/lym/unit_tests/unit_tests.pro b/src/lym/unit_tests/unit_tests.pro
index 8aec7683c..f6947c4d2 100644
--- a/src/lym/unit_tests/unit_tests.pro
+++ b/src/lym/unit_tests/unit_tests.pro
@@ -12,5 +12,17 @@ SOURCES = \
INCLUDEPATH += $$RBA_INC $$PYA_INC $$LYM_INC $$TL_INC $$GSI_INC
DEPENDPATH += $$RBA_INC $$PYA_INC $$LYM_INC $$TL_INC $$GSI_INC
-LIBS += -L$$DESTDIR_UT -lklayout_rba -lklayout_pya -lklayout_lym -lklayout_tl -lklayout_gsi
+LIBS += -L$$DESTDIR_UT -lklayout_lym -lklayout_tl -lklayout_gsi
+
+equals(HAVE_RUBY, "1") {
+ LIBS += -lklayout_rba
+} else {
+ LIBS += -lklayout_rbastub
+}
+
+equals(HAVE_PYTHON, "1") {
+ LIBS += -lklayout_pya
+} else {
+ LIBS += -lklayout_pyastub
+}
diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc
index 3ee9dd964..b3e45d57a 100644
--- a/src/rba/rba/rba.cc
+++ b/src/rba/rba/rba.cc
@@ -1015,11 +1015,24 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor)
if (argc == 0) {
ret = rba_funcall2_checked (self, id_to_enum, 1, &method_sym);
} else {
+#if 0
+ // this solution does not work on MSVC2017 for unknown reasons and
+ // makes the application segfault even without being called
std::vector new_args;
new_args.reserve (size_t (argc + 1));
new_args.push_back (method_sym);
new_args.insert (new_args.end (), argv, argv + argc);
ret = rba_funcall2_checked (self, id_to_enum, argc + 1, new_args.begin ().operator-> ());
+#else
+ VALUE new_args[16];
+ tl_assert (argc + 1 <= sizeof(new_args) / sizeof(new_args[0]));
+ VALUE *a = &new_args[0];
+ *a++ = method_sym;
+ for (int i = 0; i < argc; ++i) {
+ *a++ = argv[i];
+ }
+ ret = rba_funcall2_checked (self, id_to_enum, argc + 1, &new_args[0]);
+#endif
}
} else {
diff --git a/src/tl/tl.pro b/src/tl/tl.pro
index 841b38262..b85db09a0 100644
--- a/src/tl/tl.pro
+++ b/src/tl/tl.pro
@@ -1,6 +1,9 @@
TEMPLATE = subdirs
SUBDIRS = tl unit_tests
+*bsd* {
+ LIBS += -lexecinfo
+}
unit_tests.depends += tl
diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc
index b608139c4..7a8078883 100644
--- a/src/tl/tl/tlFileUtils.cc
+++ b/src/tl/tl/tlFileUtils.cc
@@ -59,6 +59,13 @@
#endif
+#if defined(__FreeBSD__)
+
+#include
+#include
+
+#endif
+
namespace tl
{
@@ -816,6 +823,16 @@ get_inst_path_internal ()
return tl::absolute_path (buffer);
}
+#elif defined (__FreeBSD__)
+
+ char path[PATH_MAX];
+ size_t len = PATH_MAX;
+ const int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+ if (sysctl(&mib[0], 4, &path, &len, NULL, 0) == 0) {
+ return tl::absolute_path(path);
+ }
+ return "";
+
#else
std::string pf = tl::sprintf ("/proc/%d/exe", getpid ());
diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc
index 753e5e16b..36f6a8b3c 100644
--- a/src/tl/tl/tlStream.cc
+++ b/src/tl/tl/tlStream.cc
@@ -32,6 +32,8 @@
#include
#ifdef _WIN32
# include
+#else
+# include
#endif
#include "tlStream.h"
diff --git a/src/tl/unit_tests/tlIncludeTests.cc b/src/tl/unit_tests/tlIncludeTests.cc
index bbb56dc66..924a4510c 100644
--- a/src/tl/unit_tests/tlIncludeTests.cc
+++ b/src/tl/unit_tests/tlIncludeTests.cc
@@ -26,6 +26,11 @@
#include "tlStream.h"
#include "tlInclude.h"
+static std::string np (const std::string &s)
+{
+ return tl::replaced (s, "\\", "/");
+}
+
TEST(1_simple)
{
std::string fn = tl::testsrc () + "/testdata/tl/x.txt";
@@ -48,12 +53,12 @@ TEST(2_single_include)
tl::IncludeExpander ie = tl::IncludeExpander::expand (fn, tl::InputStream (fn).read_all (), et);
EXPECT_EQ (et, "A line\nincluded.1\nAnother line\n");
- EXPECT_EQ (ie.to_string (), "@1*" + tl::testsrc () + "/testdata/tl/x_inc1.txt*0;2*" + tl::testsrc () + "/testdata/tl/inc1.txt*-1;3*" + tl::testsrc () + "/testdata/tl/x_inc1.txt*0;");
+ EXPECT_EQ (np (ie.to_string ()), np ("@1*" + tl::testsrc () + "/testdata/tl/x_inc1.txt*0;2*" + tl::testsrc () + "/testdata/tl/inc1.txt*-1;3*" + tl::testsrc () + "/testdata/tl/x_inc1.txt*0;"));
EXPECT_EQ (tl::IncludeExpander::from_string (ie.to_string ()).to_string (), ie.to_string ());
EXPECT_EQ (ie.translate_to_original (1).first, fn);
EXPECT_EQ (ie.translate_to_original (1).second, 1);
- EXPECT_EQ (ie.translate_to_original (2).first, tl::testsrc () + "/testdata/tl/inc1.txt");
+ EXPECT_EQ (np (ie.translate_to_original (2).first), np (tl::testsrc () + "/testdata/tl/inc1.txt"));
EXPECT_EQ (ie.translate_to_original (2).second, 1);
EXPECT_EQ (ie.translate_to_original (3).first, fn);
EXPECT_EQ (ie.translate_to_original (3).second, 3);
@@ -71,11 +76,11 @@ TEST(3_multi_include)
EXPECT_EQ (ie.translate_to_original (1).first, fn);
EXPECT_EQ (ie.translate_to_original (1).second, 1);
- EXPECT_EQ (ie.translate_to_original (2).first, tl::testsrc () + "/testdata/tl/inc3.txt");
+ EXPECT_EQ (np (ie.translate_to_original (2).first), np (tl::testsrc () + "/testdata/tl/inc3.txt"));
EXPECT_EQ (ie.translate_to_original (2).second, 1);
- EXPECT_EQ (ie.translate_to_original (3).first, tl::testsrc () + "/testdata/tl/inc2.txt");
+ EXPECT_EQ (np (ie.translate_to_original (3).first), np (tl::testsrc () + "/testdata/tl/inc2.txt"));
EXPECT_EQ (ie.translate_to_original (3).second, 1);
- EXPECT_EQ (ie.translate_to_original (5).first, tl::testsrc () + "/testdata/tl/inc3.txt");
+ EXPECT_EQ (np (ie.translate_to_original (5).first), np (tl::testsrc () + "/testdata/tl/inc3.txt"));
EXPECT_EQ (ie.translate_to_original (5).second, 3);
EXPECT_EQ (ie.translate_to_original (6).first, fn);
EXPECT_EQ (ie.translate_to_original (6).second, 3);
@@ -93,11 +98,11 @@ TEST(4_multi_include_interpolate)
EXPECT_EQ (ie.translate_to_original (1).first, fn);
EXPECT_EQ (ie.translate_to_original (1).second, 1);
- EXPECT_EQ (ie.translate_to_original (2).first, tl::testsrc () + "/testdata/tl/inc3.txt");
+ EXPECT_EQ (np (ie.translate_to_original (2).first), np (tl::testsrc () + "/testdata/tl/inc3.txt"));
EXPECT_EQ (ie.translate_to_original (2).second, 1);
- EXPECT_EQ (ie.translate_to_original (3).first, tl::testsrc () + "/testdata/tl/inc2.txt");
+ EXPECT_EQ (np (ie.translate_to_original (3).first), np (tl::testsrc () + "/testdata/tl/inc2.txt"));
EXPECT_EQ (ie.translate_to_original (3).second, 1);
- EXPECT_EQ (ie.translate_to_original (5).first, tl::testsrc () + "/testdata/tl/inc3.txt");
+ EXPECT_EQ (np (ie.translate_to_original (5).first), np (tl::testsrc () + "/testdata/tl/inc3.txt"));
EXPECT_EQ (ie.translate_to_original (5).second, 3);
EXPECT_EQ (ie.translate_to_original (6).first, fn);
EXPECT_EQ (ie.translate_to_original (6).second, 3);
diff --git a/testdata/algo/device_extract_au13_circuits.gds b/testdata/algo/device_extract_au13_circuits.gds
new file mode 100644
index 000000000..131540011
Binary files /dev/null and b/testdata/algo/device_extract_au13_circuits.gds differ
diff --git a/testdata/algo/device_extract_l13.gds b/testdata/algo/device_extract_l13.gds
new file mode 100644
index 000000000..2fad35e6b
Binary files /dev/null and b/testdata/algo/device_extract_l13.gds differ
diff --git a/testdata/algo/nreader9.cir b/testdata/algo/nreader9.cir
index 302b7a0a2..e68e4bd20 100644
--- a/testdata/algo/nreader9.cir
+++ b/testdata/algo/nreader9.cir
@@ -1,10 +1,18 @@
R$1 1 2 1.7k M=2
R$2 3 4 1.7k
+R$3 1 2 1.7k RMODEL M=2
+R$4 3 4 1.7k RMODEL
M$1 1 2 3 4 NMOS W=2u L=7u M=2
M$2 1 2 3 4 PMOS W=2u L=7u
C$1 1 2 1e-9 M=2
C$2 3 4 1e-9
+C$3 1 2 1e-9 CMODEL M=2
+C$4 3 4 1e-9 CMODEL
+L$1 1 2 1e-9 M=2
+L$2 3 4 1e-9
+L$3 1 2 1e-9 LMODEL M=2
+L$4 3 4 1e-9 LMODEL
D$1 1 2 DIODE A=10P M=2
D$2 3 4 DIODE A=10P
Q$1 1 2 3 4 BIP AE=10P M=2
diff --git a/testdata/drc/drcSimpleTests_17.drc b/testdata/drc/drcSimpleTests_17.drc
new file mode 100644
index 000000000..8cdbc5992
--- /dev/null
+++ b/testdata/drc/drcSimpleTests_17.drc
@@ -0,0 +1,42 @@
+
+source($drc_test_source)
+in_cell = source.select("-", "+S*")
+not_in_cell = source.select("-SPECIAL")
+in_cell_local = source.select("-*", "+SPECIAL")
+not_in_cell_local = source.select("+*", "-SPECIAL")
+
+target($drc_test_target)
+
+deep
+
+input(2,0).output(2, 0)
+input(3,0).output(3, 0)
+
+flat
+
+in_cell.input(2, 0).output(102, 0)
+in_cell.input(3, 0).output(103, 0)
+
+not_in_cell.input(2, 0).output(202, 0)
+not_in_cell.input(3, 0).output(203, 0)
+
+in_cell_local.input(2, 0).output(302, 0)
+in_cell_local.input(3, 0).output(303, 0)
+
+not_in_cell_local.input(2, 0).output(402, 0)
+not_in_cell_local.input(3, 0).output(403, 0)
+
+deep
+
+in_cell.input(2, 0).output(502, 0)
+in_cell.input(3, 0).output(503, 0)
+
+not_in_cell.input(2, 0).output(602, 0)
+not_in_cell.input(3, 0).output(603, 0)
+
+in_cell_local.input(2, 0).output(702, 0)
+in_cell_local.input(3, 0).output(703, 0)
+
+not_in_cell_local.input(2, 0).output(802, 0)
+not_in_cell_local.input(3, 0).output(803, 0)
+
diff --git a/testdata/drc/drcSimpleTests_17.gds b/testdata/drc/drcSimpleTests_17.gds
new file mode 100644
index 000000000..0718d5171
Binary files /dev/null and b/testdata/drc/drcSimpleTests_17.gds differ
diff --git a/testdata/drc/drcSimpleTests_au17.gds b/testdata/drc/drcSimpleTests_au17.gds
new file mode 100644
index 000000000..ceeb45db2
Binary files /dev/null and b/testdata/drc/drcSimpleTests_au17.gds differ
diff --git a/testdata/ruby/qtbinding.rb b/testdata/ruby/qtbinding.rb
index f8b7471d4..54b596542 100644
--- a/testdata/ruby/qtbinding.rb
+++ b/testdata/ruby/qtbinding.rb
@@ -679,9 +679,16 @@ class QtBinding_TestClass < TestBase
w = RBA::QObject::new
- on = nil
- w.objectNameChanged do |name|
- on = name
+ if w.respond_to?(:objectNameChanged) # Qt5
+
+ on = nil
+ w.objectNameChanged do |name|
+ on = name
+ end
+
+ w.objectName = "uvw"
+ assert_equal(on, "uvw")
+
end
od = false
@@ -689,12 +696,7 @@ class QtBinding_TestClass < TestBase
od = true
end
- w.objectName = "uvw"
-
- assert_equal(on, "uvw")
-
w._destroy
-
assert_equal(od, true)
end