mirror of https://github.com/KLayout/klayout.git
Merge branch 'master' into usability-enhancements
This commit is contained in:
commit
f3668a713d
31
.travis.yml
31
.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
|
||||
|
|
|
|||
|
|
@ -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 <type>] : case-insensitive type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3'] | sys
|
||||
[-p|--python <type>] : 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>] : 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.<br>
|
||||
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:<br>
|
||||
**`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.<br>
|
||||
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:<br>
|
||||
**`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.<br>
|
||||
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:<br>
|
||||
**`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. <br>
|
||||
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:
|
||||
```
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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 <type>] : case-insensitive type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3'] | sys \n"
|
||||
Usage += " [-p|--python <type>] : 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>] : 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." )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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 <tests>] : exclude test(s) such as 'pymod,pya' | '' \n"
|
||||
Usage += " [-a|--args <string>] : 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
|
||||
#---------------
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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<db::cell_index_type, std::pair<db::cell_index_type, std::set<db::Box> > > cm_skipped_variants;
|
||||
std::map<db::cell_index_type, db::HierarchyBuilder::CellMapKey> 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<db::cell_index_type, std::pair<db::cell_index_type, std::set<db::Box> > >::const_iterator icm = cm_skipped_variants.find (var_org);
|
||||
std::map<db::cell_index_type, db::HierarchyBuilder::CellMapKey>::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);
|
||||
|
|
|
|||
|
|
@ -799,10 +799,18 @@ struct cluster_building_receiver
|
|||
typedef std::set<size_t> global_nets;
|
||||
typedef std::pair<shape_vector, global_nets> cluster_value;
|
||||
|
||||
cluster_building_receiver (const db::Connectivity &conn)
|
||||
: mp_conn (&conn)
|
||||
cluster_building_receiver (const db::Connectivity &conn, const tl::equivalence_clusters<size_t> *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<size_t>::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<T> &clusters)
|
||||
|
|
@ -817,7 +825,32 @@ struct cluster_building_receiver
|
|||
cluster->add_attr (s->second.second);
|
||||
}
|
||||
|
||||
cluster->set_global_nets (c->second);
|
||||
std::set<size_t> 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<size_t>::cluster_id_type cl = mp_attr_equivalence->cluster_id (a);
|
||||
if (cl > 0) {
|
||||
std::map<size_t, std::set<size_t> >::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<const T *, typename std::list<cluster_value>::iterator> m_shape_to_clusters;
|
||||
std::map<size_t, typename std::list<cluster_value>::iterator> m_global_to_clusters;
|
||||
std::list<cluster_value> m_clusters;
|
||||
std::map<size_t, std::set<size_t> > m_global_nets_by_attribute_cluster;
|
||||
const tl::equivalence_clusters<size_t> *mp_attr_equivalence;
|
||||
|
||||
void join (typename std::list<cluster_value>::iterator ic1, typename std::list<cluster_value>::iterator ic2)
|
||||
{
|
||||
|
|
@ -1035,7 +1070,7 @@ local_clusters<T>::build_clusters (const db::Cell &cell, const db::Connectivity
|
|||
}
|
||||
}
|
||||
|
||||
cluster_building_receiver<T, box_type> rec (conn);
|
||||
cluster_building_receiver<T, box_type> rec (conn, attr_equivalence);
|
||||
bs.process (rec, 1 /*==touching*/, bc);
|
||||
rec.generate_clusters (*this);
|
||||
|
||||
|
|
@ -1048,36 +1083,27 @@ template <class T>
|
|||
void
|
||||
local_clusters<T>::apply_attr_equivalences (const tl::equivalence_clusters<size_t> &attr_equivalence)
|
||||
{
|
||||
tl::equivalence_clusters<size_t> 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<T>::attr_iterator a0;
|
||||
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
|
||||
if (attr_equivalence.has_attribute (*a)) {
|
||||
if (a0 == typename local_cluster<T>::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<tl::equivalence_clusters<size_t>::cluster_id_type, std::set<size_t> > c2c;
|
||||
|
||||
for (const_iterator c = begin (); c != end (); ++c) {
|
||||
|
||||
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
|
||||
tl::equivalence_clusters<size_t>::cluster_id_type cl = attr_equivalence.cluster_id (*a);
|
||||
if (cl > 0) {
|
||||
c2c [cl].insert (c->id ());
|
||||
}
|
||||
}
|
||||
|
||||
for (typename local_cluster<T>::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<size_t>::cluster_id_type cl = attr_equivalence.cluster_id (a);
|
||||
if (cl > 0) {
|
||||
c2c [cl].insert (c->id ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (std::map<tl::equivalence_clusters<size_t>::cluster_id_type, std::set<size_t> >::const_iterator c = c2c.begin (); c != c2c.end (); ++c) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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<db::cell_index_type, std::set<db::Box> > key (iter->top_cell ()->cell_index (), std::set<db::Box> ());
|
||||
CellMapKey key (iter->top_cell ()->cell_index (), false, std::set<db::Box> ());
|
||||
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<db::cell_index_type, std::set<db::Box> > key (inst.object ().cell_index (), std::set<db::Box> ());
|
||||
|
||||
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::Box> ());
|
||||
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<db::Cell *>::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<db::cell_index_type, std::set<db::Box> > 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<db::Cell *>::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
|
||||
(*c)->insert (new_inst);
|
||||
|
|
|
|||
|
|
@ -225,8 +225,36 @@ class DB_PUBLIC HierarchyBuilder
|
|||
: public db::RecursiveShapeReceiver
|
||||
{
|
||||
public:
|
||||
struct CellMapKey
|
||||
{
|
||||
CellMapKey ()
|
||||
: original_cell (0), inactive (false)
|
||||
{ }
|
||||
|
||||
typedef std::map<std::pair<db::cell_index_type, std::set<db::Box> >, db::cell_index_type> cell_map_type;
|
||||
CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set<db::Box> &_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<db::Box> clip_region;
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<CellMapKey, db::cell_index_type> cell_map_type;
|
||||
typedef std::map<db::cell_index_type, std::vector<db::cell_index_type> > original_target_to_variants_map_type;
|
||||
typedef std::map<db::cell_index_type, db::cell_index_type> 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<db::Layout> mp_target;
|
||||
HierarchyBuilderShapeReceiver *mp_pipe;
|
||||
bool m_initial_pass;
|
||||
|
|
|
|||
|
|
@ -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<size_t> &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<size_t> &eq)
|
||||
{
|
||||
std::map<std::string, std::set<size_t> > 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<db::Text> &text_repository = layout->shape_repository ().repository (db::object_tag<db::Text> ());
|
||||
for (db::repository<db::Text>::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<db::cell_index_type, tl::equivalence_clusters<size_t> > 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<std::pair<std::string, std::string> >::const_iterator m = m_joined_net_names_per_cell.begin (); m != m_joined_net_names_per_cell.end (); ++m) {
|
||||
std::pair<bool, db::cell_index_type> 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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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<db::DeviceClassResistor *>(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<db::DeviceClassResistor> (circuit, cn);
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassResistor> (circuit, cn);
|
||||
|
||||
// Apply multiplier
|
||||
value /= mult;
|
||||
|
||||
} else if (element == "L") {
|
||||
|
||||
if (cn.empty ()) {
|
||||
cn = "IND";
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassInductor *>(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<db::DeviceClassInductor> (circuit, cn);
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassInductor> (circuit, cn);
|
||||
|
||||
// Apply multiplier
|
||||
value /= mult;
|
||||
|
||||
} else if (element == "C") {
|
||||
|
||||
if (cn.empty ()) {
|
||||
cn = "CAP";
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassCapacitor *>(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<db::DeviceClassCapacitor> (circuit, cn);
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassCapacitor> (circuit, cn);
|
||||
|
||||
// Apply multiplier
|
||||
value *= mult;
|
||||
|
||||
} else if (element == "D") {
|
||||
|
||||
if (cn.empty ()) {
|
||||
cn = "DIODE";
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassDiode *>(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<db::DeviceClassDiode> (circuit, cn);
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
|
||||
|
||||
// Apply multiplier to "A"
|
||||
std::map<std::string, double>::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<db::DeviceClassBJT3Transistor> (circuit, cn);
|
||||
} else if (nets.size () == 4) {
|
||||
if (cn.empty ()) {
|
||||
cn = "BJT4";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassBJT4Transistor> (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<db::DeviceClassBJT3Transistor *>(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<db::DeviceClassBJT4Transistor *>(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<db::DeviceClassBJT3Transistor> (circuit, cn);
|
||||
} else {
|
||||
if (cn.empty ()) {
|
||||
cn = "BJT4";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassBJT4Transistor> (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<db::DeviceClassMOS4Transistor *>(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<db::DeviceClassMOS4Transistor> (circuit, cn);
|
||||
|
||||
// Apply multiplier to "W"
|
||||
std::map<std::string, double>::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<db::DeviceClassMOS4Transistor> (circuit, cn);
|
||||
} else {
|
||||
error (tl::to_string (tr ("'M' element needs to have 4 terminals")));
|
||||
}
|
||||
}
|
||||
|
||||
// Apply multiplier to "W"
|
||||
std::map<std::string, double>::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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<size_t> (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<unsigned int> 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<size_t> (mp_cell) & size_t (1)) != 0;
|
||||
}
|
||||
|
||||
void set_inactive (bool a) const
|
||||
{
|
||||
size_t c = reinterpret_cast<size_t> (mp_cell);
|
||||
|
|
|
|||
|
|
@ -1491,6 +1491,7 @@ Class<db::Cell> 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<db::Instance> 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"
|
||||
|
|
|
|||
|
|
@ -1028,11 +1028,12 @@ Class<db::Layout> 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."
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -1283,6 +1283,7 @@ Class<db::Shape> 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"
|
||||
|
|
|
|||
|
|
@ -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<db::CommonReaderOptions> ().layer_map = lmap;
|
||||
options.get_options<db::CommonReaderOptions> ().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<unsigned int> ()));
|
||||
|
||||
std::auto_ptr<db::Region> rbulk (l2n.make_layer ("bulk"));
|
||||
std::auto_ptr<db::Region> rnwell (l2n.make_layer (nwell, "nwell"));
|
||||
std::auto_ptr<db::Region> ractive (l2n.make_layer (active, "active"));
|
||||
std::auto_ptr<db::Region> rpplus (l2n.make_layer (pplus, "pplus"));
|
||||
std::auto_ptr<db::Region> rnplus (l2n.make_layer (nplus, "nplus"));
|
||||
std::auto_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly, "poly"));
|
||||
std::auto_ptr<db::Texts> rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
|
||||
std::auto_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont"));
|
||||
std::auto_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont"));
|
||||
std::auto_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
|
||||
std::auto_ptr<db::Texts> rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
|
||||
std::auto_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1, "via1"));
|
||||
std::auto_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
|
||||
std::auto_ptr<db::Texts> 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<const db::Region *, unsigned int> 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 == "+*"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ cells T*
|
|||
</pre>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
|
@ -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:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
|
|
@ -398,7 +398,7 @@ with shapes on layer 6 from instances of TOP.. do initial_cell.shapes(<10/0&g
|
|||
</pre>
|
||||
|
||||
<p>
|
||||
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).
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ end
|
|||
</pre>
|
||||
</p><p>
|
||||
(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).
|
||||
</p><p>
|
||||
To apply cheats for device extraction, use the following scheme:
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ Predefined device extractors are:
|
|||
<li><a href="/about/drc_ref_global.xml#capacitor_with_bulk">global#capacitor_with_bulk</a> - A capacitor with a separate bulk terminal </li>
|
||||
</ul>
|
||||
</p><p>
|
||||
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.
|
||||
</p><p>
|
||||
|
|
|
|||
|
|
@ -282,19 +282,34 @@ To disable the TOP cell but enabled a hypothetical cell B below the top cell, us
|
|||
code:
|
||||
</p><p>
|
||||
<pre>
|
||||
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)
|
||||
...
|
||||
</pre>
|
||||
</p><p>
|
||||
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.
|
||||
</p><p>
|
||||
The following code will just select "B" without its children, because in the
|
||||
first "-*" selection, all cells including the children of "B" are disabled:
|
||||
</p><p>
|
||||
<pre>
|
||||
layout_with_selection = layout.select("-*", "+B")
|
||||
l1 = layout_with_selection.input(1, 0)
|
||||
layout_with_selection = source.select("-*", "+B")
|
||||
l1 = source.input(1, 0)
|
||||
...
|
||||
</pre>
|
||||
</p><p>
|
||||
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:
|
||||
</p><p>
|
||||
<pre>
|
||||
layout_with_selection = source.select("-", "+B")
|
||||
l1 = source.input(1, 0)
|
||||
...
|
||||
</pre>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@
|
|||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
edited. Instead, a library installed in the system comes with a technology
|
||||
association itself.</li>
|
||||
<li>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.</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
cells with the same name will be used as child cells.
|
||||
</li>
|
||||
<li>
|
||||
<b>Deep copy</b>: In deep copy mode, the cell plus it's child cells are copied. All cells will be carried
|
||||
<b>Deep copy</b>: 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.
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@
|
|||
</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Shallow delete:</b> Just the cell (it's shapes and instances) are deleted, not
|
||||
<li><b>Shallow delete:</b> 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.</li>
|
||||
<li><b>Deep delete:</b> The cell and all it's subcells are deleted, unless the subcells are
|
||||
<li><b>Deep delete:</b> 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.</li>
|
||||
<li><b>Complete delete:</b> The cell and all it's subcells are deleted, even if other cells
|
||||
<li><b>Complete delete:</b> The cell and all its subcells are deleted, even if other cells
|
||||
would reference these subcells.</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<keyword name="Editing"/>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
|
|
@ -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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
are brought up to the current cell's level and removed from the original cell.<br/>
|
||||
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.<br/>
|
||||
<b>Hint:</b> the current implementation removes the selected object from it's original cell. Since it only creates
|
||||
<b>Hint:</b> 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.
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
The Alignment of the labels can be specified too: whether the appear left or right-aligned
|
||||
or centered.
|
||||
</li>
|
||||
<li><b>Style:</b> the style determines how the ruler or it's components are drawn. This can be
|
||||
<li><b>Style:</b> 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.
|
||||
</li>
|
||||
<li><b>Outline:</b> the outline determines how the two points forming the ruler are connected
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
</li>
|
||||
<li><b>Angle constraint:</b> 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.
|
||||
</li>
|
||||
<li><b>Object snapping:</b> 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.
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ Application::instance.main_window.views</pre>
|
|||
to a large degree, so different colors can be used for example. Markers are objects of class
|
||||
Marker (<class_doc href="Marker"/>).
|
||||
</li>
|
||||
<li><b>Local configuration</b>: be default, the layout view pulls it's configuration from the global
|
||||
<li><b>Local configuration</b>: 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.
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ layout.write("my_layout.gds")</pre>
|
|||
</p>
|
||||
|
||||
<p>
|
||||
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")</pre>
|
|||
and "delete_property" also are provided for cells and cell instances (<class_doc href="Cell"/> and <class_doc href="Instance"/>).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Note:</b> 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.
|
||||
</p>
|
||||
|
||||
<h2>The LayerInfo class</h2>
|
||||
|
||||
<keyword name="LayerInfo"/>
|
||||
|
|
@ -813,7 +820,7 @@ end</pre>
|
|||
|
||||
<keyword name="CellInstArray"/>
|
||||
<p>
|
||||
Despite it's name, a <class_doc href="CellInstArray"/> object holds a cell reference which is
|
||||
Despite its name, a <class_doc href="CellInstArray"/> 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</pre>
|
|||
The <class_doc href="Shape"/> 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.
|
||||
</p>
|
||||
|
||||
<h3>General methods</h3>
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ end</pre>
|
|||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
The <class_doc href="Point"/> 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 <class_doc href="Point#x"/> and <class_doc href="Point#y"/> attributes. Points can be added,
|
||||
subtracted, the euclidian distance and it's square can be computed using the <class_doc href="Point#distance"/> and <class_doc href="Point#sq_distance"/>
|
||||
subtracted, the euclidian distance and its square can be computed using the <class_doc href="Point#distance"/> and <class_doc href="Point#sq_distance"/>
|
||||
methods. The <b>*</b> operator used with a factor as the second operand will scale both the x and y coordinates.
|
||||
</p>
|
||||
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
<li><b>Box + Point</b>: 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.
|
||||
</li>
|
||||
<li><b>contains?(Point)</b>: returns true, if the point is inside the box or on it's edges.
|
||||
<li><b>contains?(Point)</b>: returns true, if the point is inside the box or on its edges.
|
||||
</li>
|
||||
<li><b>enlarge(Point)</b>: 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)
|
|||
|
||||
<p>
|
||||
A <class_doc href="Path"/> 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 <class_doc href="Path#is_round?"/>. 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)
|
|||
<class_doc href="EdgePairs"/> edge pair collections, not regions. Each edge pair marks a
|
||||
violation of the given check.
|
||||
</p>
|
||||
<p><class_doc href="Region#extents"/> replaces each polygon with it's bounding box.</p>
|
||||
<p><class_doc href="Region#extents"/> replaces each polygon with its bounding box.</p>
|
||||
<p><class_doc href="Region#grid_check"/> performs an on-grid check returning edge pairs for off-grid markers.</p>
|
||||
<p><class_doc href="Region#holes"/> identifies holes and delivers a new region with the holes as filled polygons.</p>
|
||||
<p><class_doc href="Region#hulls"/> identifies holes and delivers a new region without the holes.</p>
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ end</pre>
|
|||
</p>
|
||||
|
||||
<p>
|
||||
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
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ cell.shapes(layer).insert(box)</pre>
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ end</pre>
|
|||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<VALUE> 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 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = tl unit_tests
|
||||
*bsd* {
|
||||
LIBS += -lexecinfo
|
||||
}
|
||||
|
||||
unit_tests.depends += tl
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,13 @@
|
|||
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#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 ());
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#include <zlib.h>
|
||||
#ifdef _WIN32
|
||||
# include <io.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "tlStream.h"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue