Merge remote-tracking branch 'origin/master' into HEAD

This commit is contained in:
Matthias Koefferlein 2026-08-09 13:32:51 +02:00
commit 223cebaae3
1501 changed files with 44607 additions and 30372 deletions

View File

@ -12,8 +12,39 @@ on:
release:
types: [published]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
name: Check for source changes
runs-on: ubuntu-latest
outputs:
code: ${{ steps.filter.outputs.code == 'true' || github.event_name == 'release' }}
steps:
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v4
id: filter
if: github.event_name != 'release'
with:
filters: |
code:
- '!**/*.md'
- '!docs/**'
- '!samples/**'
- '!Jenkinsfile*'
- '!.travis.yml'
- '!dropbox-deployment.yml'
- '!.gitignore'
- '!LICENSE'
- '!COPYRIGHT'
- '!CONTRIB'
- '!Changelog*'
build:
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
@ -47,9 +78,7 @@ jobs:
haskell: true
large-packages: true
- uses: hmarr/debug-action@v3
- name: Cancel Workflow Action
uses: styfle/cancel-workflow-action@0.13.0
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: ccache
if: matrix.os != 'ubuntu-24.04-arm'
uses: hendrikmuhs/ccache-action@v1.2
@ -66,7 +95,7 @@ jobs:
mkdir -p $HOST_CCACHE_DIR
- name: Build wheels (ARM)
if: matrix.os == 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.3.1
uses: pypa/cibuildwheel@v4.1.1
env:
# override the default CentOS “yum install … ccache” and drop ccache
CIBW_BEFORE_ALL_LINUX: |
@ -81,7 +110,7 @@ jobs:
- name: Build wheels (all other platforms)
if: matrix.os != 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.3.1
uses: pypa/cibuildwheel@v4.1.1
env:
CIBW_BUILD: ${{ matrix.cibuild }}
CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }}
@ -98,7 +127,7 @@ jobs:
mv ./wheelhouse/.ccache $HOST_CCACHE_DIR
ls -la $HOST_CCACHE_DIR
ccache -s
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
with:
name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
@ -106,14 +135,16 @@ jobs:
# The following was taken from https://cibuildwheel.readthedocs.io/en/stable/deliver-to-pypi/
make_sdist:
name: Make SDist
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Build SDist
run: pipx run build --sdist
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v7
with:
name: artifact-sdist
path: dist/*.tar.gz
@ -122,12 +153,12 @@ jobs:
needs: [build, make_sdist]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
merge-multiple: true
path: dist
- uses: pypa/gh-action-pypi-publish@v1.13.0
- uses: pypa/gh-action-pypi-publish@v1.14.2
continue-on-error: true # might fail if we don't bump the version
with:
user: __token__
@ -139,12 +170,12 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v8
with:
merge-multiple: true
path: dist
- uses: pypa/gh-action-pypi-publish@v1.13.0
- uses: pypa/gh-action-pypi-publish@v1.14.2
with:
user: __token__
password: ${{ secrets.pypi_password }}

View File

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Cleanup
run: |

View File

@ -1,582 +0,0 @@
branches:
only:
- /v\d+\.\d+(\.\d+)?(\S*)?$/ # tag format: *v0.25(.8)(dev14)
- staging
- master
matrix:
include:
# python manylinux packages
- name: "cp39-cp39m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.9'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp39-cp39"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp38-cp38m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.8'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp38-cp38"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp37-cp37m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.7-dev'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp37-cp37m"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp36-cp36m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.6'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp36-cp36m"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp35-cp35m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.5'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp35-cp35m"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp27-cp27m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '2.7'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp27-cp27m"
- DOCKER_BUILD=true
- TEST_IN_HOST=false # travis's python 2.7 uses ucs4 (mu), so this test fails.
- MATRIX_EVAL=""
cache:
directories:
- ccache
# python 3 osx
# MacOS 10.15
# MacOS 10.15, Python 3.9
- name: "cp39-cp39m-macosx_10_15_x86_64.whl"
os: osx
osx_image: xcode12 # macOS 10.15
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.9
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.9/bin/python3'; alias pip='/usr/local/opt/python@3.9/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.15, Python 3.8
- name: "cp38-cp38m-macosx_10_15_x86_64.whl"
os: osx
osx_image: xcode12 # macOS 10.15
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.8
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.8/bin/python3'; alias pip='/usr/local/opt/python@3.8/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.15, Python 3.7
- name: "cp37-cp37m-macosx_10_15_x86_64.whl"
os: osx
osx_image: xcode12 # macOS 10.15
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.7
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.7/bin/python3'; alias pip='/usr/local/opt/python@3.7/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.14
# MacOS 10.14, Python 3.9
- name: "cp39-cp39m-macosx_10_14_x86_64.whl"
os: osx
osx_image: xcode11 # macOS 10.14
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.9
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.9/bin/python3'; alias pip='/usr/local/opt/python@3.9/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.14, Python 3.8
- name: "cp38-cp38m-macosx_10_14_x86_64.whl"
os: osx
osx_image: xcode11 # macOS 10.14
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.8
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.8/bin/python3'; alias pip='/usr/local/opt/python@3.8/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.14, Python 3.7
- name: "cp37-cp37m-macosx_10_14_x86_64.whl"
os: osx
osx_image: xcode11 # macOS 10.14
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.7
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.7/bin/python3'; alias pip='/usr/local/opt/python@3.7/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.13
# MacOS 10.13, Python 3.9
- name: "cp39-cp39m-macosx_10_13_x86_64.whl"
os: osx
osx_image: xcode10.1 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.9
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.9/bin/python3'; alias pip='/usr/local/opt/python@3.9/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.13, Python 3.8
- name: "cp38-cp38m-macosx_10_13_x86_64.whl"
os: osx
osx_image: xcode10.1 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.8
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.8/bin/python3'; alias pip='/usr/local/opt/python@3.8/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.13, Python 3.7
- name: "cp37-cp37m-macosx_10_13_x86_64.whl"
os: osx
osx_image: xcode10.1 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.7
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.7/bin/python3'; alias pip='/usr/local/opt/python@3.7/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.12, Python 3.7
# - name: "klayout python3 osx10.12"
- name: "cp37-cp37m-macosx_10_12_x86_64.whl"
os: osx
osx_image: xcode9.2 # macOS 10.12
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python3
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='python3'; alias pip='pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
exclude:
# No XCode 8.2.1 for Mac OS 10.11
# - name: "klayout python3 osx10.11"
- name: "cp37-cp37m-macosx_10_11_x86_64.whl"
os: osx
osx_image: xcode8 # macOS 10.11
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL="brew update; brew config; brew upgrade python; brew postinstall python; ls -l /usr/local/opt/python/libexec/bin/; shopt -s expand_aliases; alias python='/usr/local/opt/python/libexec/bin/python'; alias pip='/usr/local/opt/python/libexec/bin/pip';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# Python 2 is EOL
# python 2 osx
# - name: "klayout python2 osx10.13"
- name: "cp27-cp27m-macosx_10_13_x86_64.whl"
os: osx
osx_image: xcode9.4 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL=""
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# - name: "klayout python2 osx10.12"
- name: "cp27-cp27m-macosx_10_12_x86_64.whl"
os: osx
osx_image: xcode8.3 # macOS 10.12
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL="brew install python2 || brew link --overwrite python@2" # deficient python2 in travis's xcode8.3 (no ssl)
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# - name: "klayout python2 osx10.11"
- name: "cp27-cp27m-macosx_10_11_x86_64.whl"
os: osx
osx_image: xcode8 # macOS 10.11
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL=""
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# KLayout builds not enabled for now on MacOS
# KLayout builds for mac
# Python 3
- name: "KLayout macOS 10.13 with py3.7"
os: osx
osx_image: xcode9.4 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python3
- qt
- ccache
update: true
env:
- MATRIX_EVAL=""
- PYTHON_VERSION=B37
- MACOS_VERSION=HighSierra
- KLAYOUT_BUILD=true
- name: "KLayout macOS 10.12 with py3.7"
os: osx
osx_image: xcode8.3 # macOS 10.12
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python3
- qt
- ccache
update: true
env:
- MATRIX_EVAL=""
- PYTHON_VERSION=B37
- MACOS_VERSION=Sierra
- KLAYOUT_BUILD=true
- name: "KLayout macOS 10.11 with py3.7"
os: osx
osx_image: xcode8 # macOS 10.11
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python3
- ccache
update: true
env:
- MATRIX_EVAL="brew update; brew config; brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/56c500b569c724b049be7ab9e12d9693f85522f9/Formula/qt.rb" # Qt 5.11.2
- PYTHON_VERSION=B37
- MACOS_VERSION=ElCapitan
- KLAYOUT_BUILD=true
# Python 2
- name: "KLayout macOS 10.13 with py2.7"
os: osx
osx_image: xcode9.4 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- qt
- ccache
update: true
env:
- MATRIX_EVAL=""
- PYTHON_VERSION=Sys
- MACOS_VERSION=HighSierra
- KLAYOUT_BUILD=true
- name: "KLayout macOS 10.12 with py2.7"
os: osx
osx_image: xcode8.3 # macOS 10.12
cache:
directories:
- ccache
addons:
homebrew:
packages:
- qt
- ccache
update: true
env:
- MATRIX_EVAL=""
- PYTHON_VERSION=Sys
- MACOS_VERSION=Sierra
- KLAYOUT_BUILD=true
- name: "KLayout macOS 10.11 with py2.7"
os: osx
osx_image: xcode8 # macOS 10.11
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL="brew update; brew config; brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/56c500b569c724b049be7ab9e12d9693f85522f9/Formula/qt.rb" # Qt 5.11.2
- PYTHON_VERSION=Sys
- MACOS_VERSION=ElCapitan
- KLAYOUT_BUILD=true
before_install:
- env
- gem install dropbox-deployment
- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
export PATH="/usr/local/opt/ccache/libexec:$PATH";
mkdir -p ccache;
export CCACHE_DIR="`pwd`/ccache";
ccache -s;
fi
- eval "${MATRIX_EVAL}"
- if [ "${PIP_UPDATE}" == "1" ]; then
pip --version;
pip install --upgrade pip || sudo pip install --upgrade pip;
pip --version;
pip install --upgrade setuptools wheel || sudo pip install --upgrade setuptools wheel;
fi
- python -c "import distutils.sysconfig as sysconfig; print(sysconfig.__file__)"
install:
- if [ "$DOCKER_BUILD" = true ]; then
docker pull $DOCKER_IMAGE;
fi
script:
- if [ "$DOCKER_BUILD" = true ]; then
mkdir -p ccache;
mkdir -p wheelhouse;
docker run --rm -e DOCKER_IMAGE -e PY_VERSION -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD "/io/ci-scripts/docker/docker_build.sh";
klayout_version=$(python -c 'import setup; print(setup.Config().version())');
mkdir -p deploy/dist-pymod/$klayout_version;
cp -a wheelhouse/{klayout-*manylinux2014*.whl,*.zip} deploy/dist-pymod/$klayout_version;
if [ "$TEST_IN_HOST" = true ]; then
pip install klayout --no-index -f ./wheelhouse;
python testdata/pymod/import_db.py;
python testdata/pymod/import_rdb.py;
python testdata/pymod/import_tl.py;
python testdata/pymod/import_lib.py;
python testdata/pymod/pya_tests.py;
fi
fi
- if [ "$PYTHON_BUILD" = true ]; then
python setup.py build;
python setup.py bdist_wheel;
python setup.py install;
python setup.py sdist --formats=zip;
python testdata/pymod/import_db.py;
python testdata/pymod/import_rdb.py;
python testdata/pymod/import_tl.py;
python testdata/pymod/import_lib.py;
python testdata/pymod/pya_tests.py;
klayout_version=$(python -c 'import setup; print(setup.Config().version())');
mkdir -p deploy/dist-pymod/$klayout_version;
cp -a dist/{*.whl,*.zip} deploy/dist-pymod/$klayout_version;
python -c 'import klayout.db as db; print(dir(db))';
python -c 'import klayout.rdb as rdb; print(dir(rdb))';
python -c 'import klayout.tl as tl; print(dir(tl))';
fi
- if [ "$KLAYOUT_BUILD" = true ]; then
./travis-build.sh;
fi
before_cache:
- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
ccache -s;
fi
after_success:
# upload to dropbox
# need DROPBOX_OAUTH_BEARER environment variable
- dropbox-deployment
# uploading to pypi using twine
# need TWINE_USERNAME, TWINE_PASSWORD and TWINE_REPOSITORY_URL env variable
- if [ ! -z "$TRAVIS_TAG" ] && [ ! -z "$TWINE_PASSWORD" ] && [[ "$PYTHON_BUILD" = true || "$DOCKER_BUILD" = true ]]; then
pip install -U twine || sudo pip install -U twine;
twine upload --skip-existing deploy/dist-pymod/$klayout_version/*;
fi

144
Changelog
View File

@ -1,3 +1,147 @@
0.30.10 (2026-07-25):
* Bugfix: %GITHUB%/issues/2384 Deep DRC failure with scaled instances
* Enhancement: %GITHUB%/issues/2391 Grid Features: switching between coarser and finier grids with keyboard shortcut
* Bugfix: %GITHUB%/issues/2392 Wrong PCell instantiations when switching technologies
* Bugfix: %GITHUB%/issues/2396 Python objects did nott support weak refs
* Bugfix: Region#edges was not working properly when used on Regions with properties
enabled. Now, with properties enabled, the regions are consistently formed from polygons with
identical properties before generating the edges.
* Enhancement: %GITHUB%/issues/2398 Clarify 'move to' (and others) scope in window caption
* Enhancement: DRC two-layer checks now do not necessarily require merging for the
first input. This in general speeds up the operation in certain cases.
As a side effect, the error markers may be split according to partial input
polygons, as is the case for plain "space" check already.
* Enhancement: Use multiple drawing workers for image_with_options rendering
* Bugfix: Update layer names in layer list (when used in addition to layer/datatypes) on reload
As a side effect, the layer properties nodes will update the "real source" according to
the actual name if the name is used in addition to layer/datatypes. Also, the layout readers
will update layer names in the layer properties of the Layout object.
* Bugfix: Marek's bug: move interactive/copy interactive did not do anything when the mouse wasn't moved
* Bugfix: Entering '16:17' (or other nonsense) into 'new layout' layer list made KLayout hang
* Bugfix: API: removing a pin from a Circuit does not crash if the pin was not disconnected before.
Now it's disconnected automatically
* Enhancement: Improved the image pixel value display: favors selected image, and follows mouse instantly
* Bugfix: When images are selected and images are turned off, the selection no longer persists
* Bugfix: Using PCell parameter name 'name' won't break PCell's cell_name_impl default implementation
* Enhancement: New API functions
* Create a Layout as a deep copy of a cell (Layout#new)
* PixelBuffer#to_bytes and PixelBuffer#from_bytes for zero-overhead RGBA pixel access
0.30.9 (2026-05-25):
* Bugfix: %GITHUB%/issues/2331 Screen size error (Assertion tlPixelBuffer.cc:260 n < m_height was not true)
* Enhancement: %GITHUB%/issues/2335 Editor Options (F3) behaviour has changed
* Enhancement: %GITHUB%/issues/2337 [Feature Request] Add to_bytes and from_bytes to shape classes
* Bugfix: %GITHUB%/issues/2339 Ruby ReportDatabase merging can lead to internal error
* Bugfix: %GITHUB%/issues/2343 copy_tree implicitly drops collinear points
* Bugfix: %GITHUB%/issues/2344 Instance properties inside a PCell are not preserved in KLayout
* Bugfix: %GITHUB%/issues/2345 Substrate sequestration
Although this ticket was more a discussion, this enhancement fixes the behavior of
empty layers in deep mode, which allows placing device terminals on them.
This eliminates the need for the workaround mentioned in the ticket.
* Bugfix: %GITHUB%/issues/2350 strm2oas klayout/testdata/gds/issue_1835.gds a.oas, a.oas is empty
With this fix, it is possible to save and load layouts that use a single library references
or PCell variants as top cell. This is not strictly a supported use model, but may be
useful in some cases. The "strm2xxx" tools now allow translating such files from OASIS
to GDS and back.
* Bugfix: %GITHUB%/issues/2356 Edit options always shown, even for klayout -ne
* Enhancement: because of issue #2356 and #2335, the "Editor Options" dock was overhauled
* It is called "Tool Options" now as it hosts not just shape editor options
* The Ruler tool got its own page as the original page was not functional and
rulers have other options. This replicates some settings from the "Ruler
and annotations setup" page.
* The dock widget's visibility can be controlled in the same way than the other
docks. The "Edit/Editor Options" entry moved to "View/Tool Options". It turns
on or off the tool options dock, which is populated with pages depending on the tool.
The default key binding (F3) will now show or hide this dock.
* Enhancement: %GITHUB%/issues/2360 Sort ports by name in "make_top_level_pins"
* Enhancement: A feature to compute a density map. It is found inside Tools/Density Map.
The density map can be computed from one or multiple layers, the region can be
specified in terms of visible region, rulers or bounding boxes of certain layers
or the whole cell. The tool will generate an image overlay over the layout
holding the density data.
* Bugfix: using "xkill" on a KLayout instance ended in a crash
* Enhancement: settings of search features are persisted (see discussion 2868)
* Enhancement: when moving rulers with "snap to objects", all points of the
rulers snap to edges or vertexes now.
* Bugfix: false color maps in images where incorrectly interpolated in some
cases.
* Bugfix: small bugfix in L2N/LVSDB reader - default values of parameters were
treated as int.
* Bugfix: properties on objects with properties (such as BoxWithProperties) where
not properly serialized. Specifically their representation did not include the
type. Hence it was not possible to retrieve properties on deserialization with
"from_s". The fix is to annotate the property values with type information.
NOTE: this is a change that is not backward-compatible.
* Bugfix: snapping rulers to edges parallel to the allowed ruler direction resulted
in skew rulers.
* Enhancement: renamed ruler option for more clarity - changed
'snap to objects (unless disabled in template)' to 'never snap to object' (inverted)
* Enhancement: enhancements in the script API
* Cell#is_cold_proxy?: indicates if a cell is a "defunc" library reference
* Cell#library_cell_name: gets the library cell name for a "defunc" reference
* Cell#library_name: gets the library name for a "defunc" reference
* Cell#pcell_name: gets the pcell name for a "defunct" reference
* Layout#delete_cells: now available with a list of cell object
* Layout#delete_cell_rec: now available with a cell object
* Layout#prune_cell: now available with a cell object
* Layout#prune_cells: for pruning multiple cells in one call
* Layout#prune_subcells: now with a cell object
* Layout#prune_subcells: now with multiple root cells
* Layout#flatten: now with a cell object
* Layout#flatten_into: now with cell objects
* Layout#delete_cell: now with a cell object
* Default value "all" for "levels" argument in "prune_subcells" and "prune_cell"
* Default value "all" for "levels" and "true" for "prune" argument in "flatten"
* Default value "all" for "levels" and "unity" for "trans" argument in "flatten_into"
0.30.8 (2026-04-14):
* Enhancement: %GITHUB%/issues/2248 Switch layouts but without losing the handle
* Bugfix: %GITHUB%/issues/2299 gf180mcu DRC performance regression.
This patch contains some other enhancements which improve performance of DRC,
although in certain cases only.
* Enhancement: %GITHUB%/issues/2300 More options to set layout format, specifically on LEF/DEF
* Bugfix: %GITHUB%/2301 UI freezes permanently with open Macro Development window and event listening plugin
* Bugfix: %GITHUB%/issues/2305 Library reloading problems
* Enhancement: %GITHUB%/2306 Feature Request: API-access to a setter for the selected entry in the Netlist Database Browser
* Bugfix: %GITHUB%/2311 Tip window is shown although running in non-GUI modebug
* Bugfix: %GITHUB%/2315 Edit/Layout/Scale causes confusing
* Enhancement: %GITHUB%/2316 Edit/Selection/Make Array: support both pitch and spacing
* Bugfix: %GITHUB%/2320 Failure to read OASIS files with large property strings
* Enhancement: %GITHUB%/2322 Options(text): split text scaling and rotation into two separate options
* Enhancement: %GITHUB%/2326 Fit shouldn't take bbox of "empty" struct into account
* Bugfix: %GITHUB%/2331 Screen size error (Assertion tlPixelBuffer.cc:260 n < m_height was not true)
* Enhancement: "strmrun" now can run DRC and LVS scripts
* Enhancement: "Library#library_by_name" now can be used with "*" for technology
* Bugfix: PCell "coerce_parameters" and "callback" was called with the client layout
instead of definition layout, hence the DBU was incorrect if both layouts have different DBU.
* Enhancement: new config option: disable generation of new layers
The name of the config option is "auto-create-new-layers".
With this option set to true (default), new layers are automatically added to the layer list on
"paste" or placing instances.
* Enhancement: hovering over a pixel of an image with display the pixel's value in
the status bar.
* Enhancement: experimental support for library declaration files to install static
libraries from other places than the "library" folder and with additional metadata.
0.30.7 (2026-03-07):
* Enhancement: %GITHUB%/issues/2248 Switch layouts but without losing the handle
* Bugfix: %GITHUB%/issues/2273 Potential use-after-free bug due to Ruby GC
* Bugfix: %GITHUB%/issues/2277 Poor netlister performance in some cases
* Enhancement: %GITHUB%/issues/2278 Option to hide markers frames
* Bugfix: %GITHUB%/issues/2284 klayoutrc parse issue silently replaces config file
* Bugfix: %GITHUB%/issues/2286 CLI arguments, '--help' / '--version' should be implemented
* Bugfix: %GITHUB%/issues/2293 Layers window: changing the layer order with the arrow buttons deselects the layer to be moved
* Enhancement: ReportDatabase#merge - a method to merge two report databases
* Bugfix: double clicking an edge in partial mode opened the properties dialog, not setting a point
* Bugfix: Python did not support downcasting of PolygonWithProperties to Polygon on Shapes#polygon for example
* Bugfix: OASIS reader was not able to read S_GDS_PROPERTY from file level or under forward reference conditions
* Enhancement: Adding an option '-of|--format' to strmxor and strmclip to specify the output format instead of taking it from the suffix
* Bugfix: 'oasis_read_all_options' as always on in buddy tools
* Bugfix: avoid a crash on application exit when using objects for user property values or names
* Bugfix: Fixing a small glitch: drawing into a ghost cell is possible, but did not turn off the ghost cell flag
* Bugfix: Fixing a potential segfault during layout painting due to a race condition (rare but happened)
* Enhancement: Performance improvement for hierarchical processor - better parallelization in some cases
0.30.6 (2026-02-07):
* Bugfix: %GITHUB%/issues/2210 LayoutView::create_measure_ruler() creates incorrect rulers
* Enhancement: %GITHUB%/issues/2214 Take selection modifer into account on release rather than on click

View File

@ -1,3 +1,31 @@
klayout (0.30.10-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Sat, 25 Jul 2026 09:48:16 +0200
klayout (0.30.9-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Mon, 25 May 2026 19:46:05 +0200
klayout (0.30.8-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Tue, 14 Apr 2026 19:16:26 +0200
klayout (0.30.7-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Sat, 07 Mar 2026 13:30:05 +0100
klayout (0.30.6-1) unstable; urgency=low
* New features and bugfixes

View File

@ -7,6 +7,44 @@ trigger:
tags:
include:
- 'v*'
paths:
exclude:
- '*.md'
- docs/*
- samples/*
- etc/*
- macbuild/*
- ci-scripts/*
- scripts/*
- .travis.yml
- Jenkinsfile*
- .gitignore
- LICENSE
- COPYRIGHT
- CONTRIB
- Changelog*
pr:
branches:
include:
- master
- staging
paths:
exclude:
- '*.md'
- docs/*
- samples/*
- etc/*
- macbuild/*
- ci-scripts/*
- scripts/*
- .travis.yml
- Jenkinsfile*
- .gitignore
- LICENSE
- COPYRIGHT
- CONTRIB
- Changelog*
jobs:
- job: Build
@ -14,18 +52,6 @@ jobs:
vmImage: 'windows-2022' # other options: 'macOS-10.13', 'ubuntu-16.04'
strategy:
matrix:
cp36-cp36m-win_amd64.whl:
python.version: '3.6'
python.architecture: 'x64'
cp37-cp37m-win_amd64.whl:
python.version: '3.7'
python.architecture: 'x64'
cp38-cp38-win_amd64.whl:
python.version: '3.8'
python.architecture: 'x64'
cp39-cp39-win_amd64.whl:
python.version: '3.9'
python.architecture: 'x64'
cp310-cp310-win_amd64.whl:
python.version: '3.10'
python.architecture: 'x64'
@ -41,18 +67,6 @@ jobs:
cp314-cp314-win_amd64.whl:
python.version: '3.14'
python.architecture: 'x64'
cp36-cp36m-win32.whl:
python.version: '3.6'
python.architecture: 'x86'
cp37-cp37m-win32.whl:
python.version: '3.7'
python.architecture: 'x86'
cp38-cp38-win32.whl:
python.version: '3.8'
python.architecture: 'x86'
cp39-cp39-win32.whl:
python.version: '3.9'
python.architecture: 'x86'
cp310-cp310-win32.whl:
python.version: '3.10'
python.architecture: 'x86'
@ -76,6 +90,13 @@ jobs:
versionSpec: '$(python.version)'
architecture: '$(python.architecture)'
- task: Cache@2
inputs:
key: 'microbits | "4.0"'
path: $(Build.SourcesDirectory)/klayout-microbits
cacheHitVar: MICROBITS_CACHED
displayName: 'Cache KLayout microbits'
# Add additional tasks to run using each Python version in the matrix above
# PowerShell
@ -86,9 +107,13 @@ jobs:
#arguments: # Optional
script: | # Required when targetType == Inline
pwd
Invoke-WebRequest -Uri "https://www.klayout.org/downloads/klayout-microbits-4.0.zip" -OutFile klayout-microbits-4.0.zip
dir
Expand-Archive klayout-microbits-4.0.zip -DestinationPath klayout-microbits
if ("$(MICROBITS_CACHED)" -ne "true") {
Write-Host "Cache miss: downloading and extracting KLayout microbits..."
Invoke-WebRequest -Uri "https://www.klayout.org/downloads/klayout-microbits-4.0.zip" -OutFile klayout-microbits-4.0.zip
Expand-Archive klayout-microbits-4.0.zip -DestinationPath klayout-microbits
} else {
Write-Host "Cache hit: restored KLayout microbits from cache."
}
dir klayout-microbits
#errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue
#failOnStderr: false # Optional
@ -174,26 +199,6 @@ jobs:
inputs:
artifactName: 'wheel-3.10.x64'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.9.x64'
inputs:
artifactName: 'wheel-3.9.x64'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.8.x64'
inputs:
artifactName: 'wheel-3.8.x64'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.7.x64'
inputs:
artifactName: 'wheel-3.7.x64'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.6.x64'
inputs:
artifactName: 'wheel-3.6.x64'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.14.x86'
inputs:
@ -219,26 +224,6 @@ jobs:
inputs:
artifactName: 'wheel-3.10.x86'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.9.x86'
inputs:
artifactName: 'wheel-3.9.x86'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.8.x86'
inputs:
artifactName: 'wheel-3.8.x86'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.7.x86'
inputs:
artifactName: 'wheel-3.7.x86'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Build Artifacts wheel-3.6.x86'
inputs:
artifactName: 'wheel-3.6.x86'
downloadPath: '$(System.DefaultWorkingDirectory)'
- task: CopyFiles@2
condition: always()
inputs:

View File

@ -41,6 +41,7 @@ HAVE_CURL=0
HAVE_EXPAT=0
HAVE_GIT2=1
HAVE_LSTREAM=1
HAVE_CPP20=0
RUBYINCLUDE=""
RUBYINCLUDE2=""
@ -217,6 +218,9 @@ while [ "$*" != "" ]; do
-nolstream)
HAVE_LSTREAM=0
;;
-cpp20)
HAVE_CPP20=1
;;
-qt5)
echo "*** WARNING: -qt5 option is ignored - Qt version is auto-detected now."
;;
@ -275,6 +279,7 @@ while [ "$*" != "" ]; do
echo " -libpng Use libpng instead of Qt for PNG generation"
echo " -nolibgit2 Do not include libgit2 for Git package support"
echo " -nolstream Do not include the LStream plugin"
echo " -cpp20 Uses some C++20 features (e.g. atomics)"
echo ""
echo "Environment Variables:"
echo ""
@ -670,6 +675,7 @@ qmake_options=(
HAVE_PNG="$HAVE_PNG"
HAVE_GIT2="$HAVE_GIT2"
HAVE_LSTREAM="$HAVE_LSTREAM"
HAVE_CPP20="$HAVE_CPP20"
PREFIX="$BIN"
RPATH="$RPATH"
KLAYOUT_VERSION="$KLAYOUT_VERSION"

View File

@ -1,4 +0,0 @@
deploy:
dropbox_path: /Builds/klayout # The path to the folder on Dropbox where the files will go
artifacts_path: deploy # can be a single file, or a path
debug: true # if you want to see more logs

View File

@ -1,16 +1,17 @@
Relevant KLayout version: 0.30.5<br>
Relevant KLayout version: 0.30.9<br>
Author: Kazzz-S<br>
Last modified: 2025-11-10<br>
Last modified: 2026-07-26<br>
# 1. Introduction
This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.30.5 or later for different 64-bit macOS, including:
This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.30.9 or later for different 64-bit macOS, including:
* Golden Gate (27.x) : really experimental (on M4 Mac Mini)
* Tahoe (26.x) : really experimental (on M4 Mac Mini)
* Sequoia (15.x) : the primary development environment
* Sonoma (14.x) : experimental
Building KLayout for the previous operating systems listed below has been discontinued.<br>
Pre-built DMG packages are also not provided.<br>
As of today (November 2025), Homebrew classifies macOS Catalina 10.15 - Ventura 13 as Tier 3.
As of November 2025, Homebrew classifies macOS Catalina 10.15 - Ventura 13 as Tier 3.
* Ventura (13.7.8; the build would still be possible)
* Monterey (12.7.6; the build would still be possible)
* Big Sur (11.7.10)
@ -47,7 +48,7 @@ Then, follow the instructions in Section 6F.
The migration work to "Qt6" is ongoing. You can try to use it; however, you might encounter some build or runtime errors.<br>
If you use **Homebrew** to build KLayout >= 0.29.0, you need "Qt6" to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599).<br>
I have also tried migrating to "Python 3.13.x" (earlier, Python 3.12.x) in this version.
I have also tried migrating to "Python 3.14.x" (earlier, Python 3.13.x) in this version.
# 3. Script language support: Ruby and Python
@ -72,7 +73,7 @@ The operating system type is detected automatically.
```
-----------------------------------------------------------------------------------------------------------
<< Usage of 'build4mac.py' >>
for building KLayout 0.30.5 or later on different Apple macOS platforms.
for building KLayout 0.30.9 or later on different Apple macOS platforms.
$ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value
@ -88,27 +89,27 @@ $ [python] ./build4mac.py
: (*) migration to Qt6 is ongoing |
[-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP34', 'HB34', 'Ana3'] | sys
: nil: don't bind Ruby |
: Sys: use [Tahoe|Sequoia|Sonoma]-bundled Ruby 2.6 |
: Sys: use [GoldenGate|Tahoe|Sequoia|Sonoma]-bundled Ruby 2.6 |
: MP34: use Ruby 3.4 from MacPorts |
: HB34: use Ruby 3.4 from Homebrew |
: Ana3: use Ruby 3.4 from Anaconda3 |
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP313', 'HB313', 'Ana3', | sys
: 'MP312', 'MP312', |
: 'MP311', 'HB311', 'HBAuto'] |
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP314', 'HB314', 'Ana3', | sys
: 'MP314', 'HB314', |
: 'HB311', 'HBAuto'] |
: nil: don't bind Python |
: Sys: use [Tahoe|Sequoia|Sonoma]-bundled Python 3.9 |
: MP313: use Python 3.13 from MacPorts |
: HB313: use Python 3.13 from Homebrew |
: Ana3: use Python 3.13 from Anaconda3 |
: MP312: use Python 3.12 from MacPorts |
: HB312: use Python 3.12 from Homebrew |
: MP311: use Python 3.11 from MacPorts |
: Sys: use [GoldenGate|Tahoe|Sequoia|Sonoma]-bundled Python 3.9 |
: MP314: use Python 3.14 from MacPorts |
: HB314: use Python 3.14 from Homebrew |
: Ana3: use Python 3.14 from Anaconda3 |
: MP314: use Python 3.14 from MacPorts |
: HB314: use Python 3.14 from Homebrew |
: HB311: use Python 3.11 from Homebrew (+) |
: (+) required to provide the legacy pip in HW-*.dmg |
: HBAuto: use the latest Python 3.x auto-detected from Homebrew |
[-P|--buildPymod] : build and deploy Pymod (*.whl) for LW-*.dmg | disabled
[-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled
[-u|--noqtuitools] : don't include uitools in Qt binding | disabled
[-s|--no32bitcoord] : don't use short (32bit) coordinates; use long (64bit) instead | disabled
[-g|--nolibgit2] : don't include libgit2 for Git package support | disabled
[-m|--make <option>] : option passed to 'make' | '--jobs=4'
[-d|--debug] : enable debug mode build; AddressSanitizer (ASAN) is linked | disabled
@ -160,27 +161,28 @@ $ ./build4mac.py -q qt5macports -r sys -p sys
$ ./build4mac.py -q qt5macports -r sys -p sys -y
```
The application bundle **`klayout.app`** is located under:<br>
**`ST-qt5MP.pkg.macos-Sequoia-release-RsysPsys`** directory, where
**`ST-qt5MP.pkg.macos-Sequoia-C32-release-RsysPsys`** directory, where
* "ST-" means this is a standard package.
* "qt5MP" means that Qt5 from MacPorts is used.
* "C32" means that the coordinate representation is a 32-bit integer (C64, for 64-bit).
* "RsysPsys" means that Ruby is 2.6 provided by OS; Python is 3.9 provided by OS.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### 6B. Fully MacPorts-flavored build with MacPorts Ruby 3.4 and MacPorts Python 3.13
0. Install MacPorts, then install Qt5, Ruby 3.4, Python 3.13, and libgit2 by
### 6B. Fully MacPorts-flavored build with MacPorts Ruby 3.4 and MacPorts Python 3.14
0. Install MacPorts, then install Qt5, Ruby 3.4, Python 3.14, and libgit2 by
```
$ sudo port install coreutils
$ sudo port install findutils
$ sudo port install qt5
$ sudo port install ruby34
$ sudo port install python313
$ sudo port install py313-pip
$ sudo port install python314
$ sudo port install py314-pip
$ sudo port install libgit2
```
1. Invoke **`build4mac.py`** with the following options:
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5macports -r mp34 -p mp313
$ ./build4mac.py -q qt5macports -r mp34 -p mp314
```
2. Confirm successful build (it will take about one hour, depending on your machine spec).
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
@ -188,50 +190,52 @@ $ ./build4mac.py -q qt5macports -r mp34 -p mp313
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Standalone Python Package (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
```
$ ./build4mac.py -q qt5macports -r mp34 -p mp313 -Y
$ ./build4mac.py -q qt5macports -r mp34 -p mp314 -Y
```
The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5MP.pkg.macos-Sequoia-release-Rmp34Pmp313`** directory, where
**`LW-qt5MP.pkg.macos-Sequoia-C32-release-Rmp34Pmp314`** directory, where
* "LW-" means this is a lightweight package.
* "qt5MP" means that Qt5 from MacPorts is used.
* "Rmp34Pmp313" means that Ruby is 3.4 from MacPorts; Python is 3.13 from MacPorts.
* "C32" means that the coordinate representation is a 32-bit integer (C64, for 64-bit).
* "Rmp34Pmp314" means that Ruby is 3.4 from MacPorts; Python is 3.14 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 3.4 and Homebrew Python 3.13
### 6C. Fully Homebrew-flavored build with Homebrew Ruby 3.4 and Homebrew Python 3.14
> [!IMPORTANT]
> To build KLayout >= 0.29.0, you need "Qt6" >= 6.7.0 to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599).<br>
0. Install Homebrew, then install Qt6, Ruby 3.4, Python 3.13, and libgit2 by
0. Install Homebrew, then install Qt6, Ruby 3.4, Python 3.14, and libgit2 by
```
$ brew install qt@6
$ brew install ruby@3.4
$ brew install python@3.13
$ brew install python@3.14
$ brew install libgit2
$ cd /where/'build.sh'/exists
$ cd macbuild
$ ./python3HB.py -v 3.13
$ ./python3HB.py -v 3.14
```
1. Invoke **`build4mac.py`** with the following options:
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt6brew -r hb34 -p hb313
$ ./build4mac.py -q qt6brew -r hb34 -p hb314
```
2. Confirm successful build (it will take about one hour, depending on your machine spec).
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.<br>
```
$ ./build4mac.py -q qt6brew -r hb34 -p hb313 -Y
$ ./build4mac.py -q qt6brew -r hb34 -p hb314 -Y
```
The application bundle **`klayout.app`** is located under:<br>
**`LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb313`** directory, where
**`LW-qt6Brew.pkg.macos-Sequoia-C32-release-Rhb34Phb314`** directory, where
* "LW-" means this is a lightweight package.
* "qt6Brew" means that Qt6 from Homebrew is used.
* "Rhb34Phb313" means that Ruby is 3.4 from Homebrew; Python is 3.13 from Homebrew.
* "C32" means that the coordinate representation is a 32-bit integer (C64, for 64-bit).
* "Rhb34Phb314" means that Ruby is 3.4 from Homebrew; Python is 3.14 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
> [!WARNING]
> We can no longer use the legacy **pip** command with Homebew Python@3.13, and we will get,
> We can no longer use the legacy **pip** command with Homebew Python@3.12-, and we will get,
> ```
> error: externally-managed-environment
> ```
@ -264,9 +268,10 @@ $ ./build4mac.py -q qt6brew -r sys -p hb311
$ ./build4mac.py -q qt6brew -r sys -p hb311 -y
```
The application bundle **`klayout.app`** is located under:<br>
**`HW-qt6Brew.pkg.macos-Sequoia-release-RsysPhb311`** directory, where
**`HW-qt6Brew.pkg.macos-Sequoia-C32-release-RsysPhb311`** directory, where
* "HW-" means this is a heavyweight package because both Qt6 and Python Frameworks are deployed.
* "qt6Brew" means that Qt6 from Homebrew is used.
* "C32" means that the coordinate representation is a 32-bit integer (C64, for 64-bit).
* "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
> [!IMPORTANT]
@ -306,9 +311,10 @@ $ ./build4mac.py -q qt5macports -r sys -p hb311
$ ./build4mac.py -q qt5macports -r sys -p hb311 -y
```
The application bundle **`klayout.app`** is located under:<br>
**`HW-qt5MP.pkg.macos-Sequoia-release-RsysPhb311`** directory, where
**`HW-qt5MP.pkg.macos-Sequoia-C32-release-RsysPhb311`** directory, where
* "HW-" means this is a heavyweight package because both Qt5 and Python Frameworks are deployed.
* "qt5MP" means that Qt5 from MacPorts is used.
* "C32" means that the coordinate representation is a 32-bit integer (C64, for 64-bit).
* "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
@ -355,9 +361,10 @@ $ ./build4mac.py -q qt6ana3 -r ana3 -p ana3
$ ./build4mac.py -q qt6ana3 -r ana3 -p ana3 -Y
```
The application bundle **`klayout.app`** is located under:<br>
**`LW-qt6Ana3.pkg.macos-Sequoia-release-Rana3Pana3`** directory, where
**`LW-qt6Ana3.pkg.macos-Sequoia-C32-release-Rana3Pana3`** directory, where
* "LW-" means this is a lightweight package.
* "qt6Ana3" means that Qt6 from Anaconda3 is used.
* "C32" means that the coordinate representation is a 32-bit integer (C64, for 64-bit).
* "Rana3Pana3" means that Ruby (3.4) is from Anaconda3; Python (3.13) is from Anaconda3.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
5. Now, you need to create an Automator wrapper app (Launcher) to start the application.
@ -420,11 +427,11 @@ makeDMG4mac.py -> macbuild/makeDMG4mac.py
2. Invoke **`makeDMG4mac.py`** with -p and -m options, for example,
```
$ cd /where/'build.sh'/exists
$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Sequoia-release-Rmp34Pmp313 -m
$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Sequoia-C32-release-Rmp34Pmp314 -m
```
This command will generate the two files below:<br>
* **`LW-klayout-0.30.5-macOS-Sequoia-1-qt5MP-Rmp34Pmp313.dmg`** ---(1) the main DMG file
* **`LW-klayout-0.30.5-macOS-Sequoia-1-qt5MP-Rmp34Pmp313.dmg.md5`** ---(2) MD5-value text file
* **`LW-klayout-0.30.9-macOS-Sequoia-C32-1-qt5MP-Rmp34Pmp314.dmg`** ---(1) the main DMG file
* **`LW-klayout-0.30.9-macOS-Sequoia-C32-1-qt5MP-Rmp34Pmp314.dmg.md5`** ---(2) MD5-value text file
# Known issues
Because we assume some specific versions of non-OS-standard Ruby and Python, updating Homebrew, MacPorts, or Anaconda3 may cause build- and link errors.<br>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -5,7 +5,7 @@
# File: "macbuild/build4mac.py"
#
# The top Python script for building KLayout (http://www.klayout.de/index.php).
# version 0.30.5 or later on different Apple Mac OSX platforms.
# version 0.30.9 or later on different Apple Mac OSX platforms.
#===============================================================================
import sys
import os
@ -36,7 +36,7 @@ from bundle_qtconf import generate_qtconf, QtConfError
# @return (usage, moduleset)-tuple
#-------------------------------------------------------------------------------
def GenerateUsage(platform):
if platform.upper() in [ "TAHOE", "SEQUOIA", "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ]
if platform.upper() in [ "GOLDENGATE", "TAHOE", "SEQUOIA", "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ]
myQt56 = "qt5macports"
myRuby = "sys"
myPython = "sys"
@ -47,7 +47,7 @@ def GenerateUsage(platform):
usage = "\n"
usage += "-----------------------------------------------------------------------------------------------------------\n"
usage += "<< Usage of 'build4mac.py' >>\n"
usage += " for building KLayout 0.30.5 or later on different Apple macOS platforms.\n"
usage += " for building KLayout 0.30.9 or later on different Apple macOS platforms.\n"
usage += "\n"
usage += "$ [python] ./build4mac.py\n"
usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value\n"
@ -63,27 +63,27 @@ def GenerateUsage(platform):
usage += " : (*) migration to Qt6 is ongoing |\n"
usage += " [-r|--ruby <type>] : case-insensitive type=['nil', 'Sys', 'MP34', 'HB34', 'Ana3'] | %s\n" % myRuby
usage += " : nil: don't bind Ruby |\n"
usage += " : Sys: use [Tahoe|Sequoia|Sonoma]-bundled Ruby 2.6 |\n"
usage += " : Sys: use [GoldenGate|Tahoe|Sequoia|Sonoma]-bundled Ruby 2.6 |\n"
usage += " : MP34: use Ruby 3.4 from MacPorts |\n"
usage += " : HB34: use Ruby 3.4 from Homebrew |\n"
usage += " : Ana3: use Ruby 3.4 from Anaconda3 |\n"
usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP313', 'HB313', 'Ana3', | %s\n" % myPython
usage += " : 'MP312', 'MP312', |\n"
usage += " : 'MP311', 'HB311', 'HBAuto'] |\n"
usage += " [-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP314', 'HB314', 'Ana3', | %s\n" % myPython
usage += " : 'MP313', 'HB313', |\n"
usage += " : 'HB311', 'HBAuto'] |\n"
usage += " : nil: don't bind Python |\n"
usage += " : Sys: use [Tahoe|Sequoia|Sonoma]-bundled Python 3.9 |\n"
usage += " : Sys: use [GoldenGate|Tahoe|Sequoia|Sonoma]-bundled Python 3.9 |\n"
usage += " : MP314: use Python 3.14 from MacPorts |\n"
usage += " : HB314: use Python 3.14 from Homebrew |\n"
usage += " : Ana3: use Python 3.13 from Anaconda3 |\n"
usage += " : MP313: use Python 3.13 from MacPorts |\n"
usage += " : HB313: use Python 3.13 from Homebrew |\n"
usage += " : Ana3: use Python 3.13 from Anaconda3 |\n"
usage += " : MP312: use Python 3.12 from MacPorts |\n"
usage += " : HB312: use Python 3.12 from Homebrew |\n"
usage += " : MP311: use Python 3.11 from MacPorts |\n"
usage += " : HB311: use Python 3.11 from Homebrew (+) |\n"
usage += " : (+) required to provide the legacy pip in HW-*.dmg |\n"
usage += " : HBAuto: use the latest Python 3.x auto-detected from Homebrew |\n"
usage += " [-P|--buildPymod] : build and deploy Pymod (*.whl) for LW-*.dmg | disabled\n"
usage += " [-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled\n"
usage += " [-u|--noqtuitools] : don't include uitools in Qt binding | disabled\n"
usage += " [-s|--no32bitcoord] : don't use short (32bit) coordinates; use long (64bit) instead | disabled\n"
usage += " [-g|--nolibgit2] : don't include libgit2 for Git package support | disabled\n"
usage += " [-m|--make <option>] : option passed to 'make' | '--jobs=4'\n"
usage += " [-d|--debug] : enable debug mode build; AddressSanitizer (ASAN) is linked | disabled\n"
@ -123,7 +123,9 @@ def Get_Default_Config():
# Dropped [ElCapitan - BigSur] (2023-10-24).
# See 415b5aa2efca04928f1148a69e77efd5d76f8c1d for the previous states.
#----------------------------------------------------------------------------
if release == 25:
if release == 27:
Platform = "GoldenGate"
elif release == 25:
Platform = "Tahoe"
elif release == 24:
Platform = "Sequoia"
@ -142,7 +144,7 @@ def Get_Default_Config():
if not Machine == "x86_64":
# with an Apple Silicon Chip?
if Machine == "arm64" and Platform in ["Tahoe", "Sequoia", "Sonoma", "Ventura", "Monterey"]:
if Machine == "arm64" and Platform in ["GoldenGate", "Tahoe", "Sequoia", "Sonoma", "Ventura", "Monterey"]:
print("")
print( "### Your Mac equips an Apple Silicon Chip ###" )
print( " Setting QMAKE_APPLE_DEVICE_ARCHS=arm64\n")
@ -160,7 +162,11 @@ def Get_Default_Config():
ToolDebug = list()
# Set the default modules
if Platform == "Tahoe":
if Platform == "GoldenGate":
ModuleQt = "Qt5MacPorts"
ModuleRuby = "Sys"
ModulePython = "Sys"
elif Platform == "Tahoe":
ModuleQt = "Qt5MacPorts"
ModuleRuby = "Sys"
ModulePython = "Sys"
@ -189,6 +195,7 @@ def Get_Default_Config():
NonOSStdLang = False
NoQtBindings = False
NoQtUiTools = False
No32bitCoord = False
NoLibGit2 = False
MakeOptions = "--jobs=4"
DebugMode = False
@ -214,6 +221,7 @@ def Get_Default_Config():
config['NonOSStdLang'] = NonOSStdLang # True if non-OS-standard language is chosen
config['NoQtBindings'] = NoQtBindings # True if not creating Qt bindings for Ruby scripts
config['NoQtUiTools'] = NoQtUiTools # True if not to include QtUiTools in Qt binding
config['No32bitCoord'] = No32bitCoord # True if not use the short (32bit) coordinate
config['NoLibGit2'] = NoLibGit2 # True if not to include libgit2 for Git package support
config['MakeOptions'] = MakeOptions # options passed to `make`
config['DebugMode'] = DebugMode # True if debug mode build
@ -259,6 +267,7 @@ def Parse_CLI_Args(config):
NonOSStdLang = config['NonOSStdLang']
NoQtBindings = config['NoQtBindings']
NoQtUiTools = config['NoQtUiTools']
No32bitCoord = config['No32bitCoord']
NoLibGit2 = config['NoLibGit2']
MakeOptions = config['MakeOptions']
DebugMode = config['DebugMode']
@ -287,7 +296,7 @@ def Parse_CLI_Args(config):
p.add_option( '-p', '--python',
dest='type_python',
help="Python type=['nil', 'Sys', 'MP313', 'HB313', 'Ana3', 'MP312', 'HB312', 'MP311', 'HB311', 'HBAuto']" )
help="Python type=['nil', 'Sys', 'MP314', 'HB314', 'Ana3', 'MP313', 'HB313', 'HB311', 'HBAuto']" )
p.add_option( '-P', '--buildPymod',
action='store_true',
@ -307,6 +316,12 @@ def Parse_CLI_Args(config):
default=False,
help="don't include uitools in Qt binding" )
p.add_option( '-s', '--no32bitcoord',
action='store_true',
dest='no_32_bitcoord',
default=False,
help="don't use short (32bit) coordinates; use long (64bit) instead " )
p.add_option( '-g', '--nolibgit2',
action='store_true',
dest='no_libgit2',
@ -356,13 +371,14 @@ def Parse_CLI_Args(config):
default=False,
help='check usage' )
if Platform.upper() in [ "TAHOE", "SEQUOIA", "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ]
if Platform.upper() in [ "GOLDENGATE", "TAHOE", "SEQUOIA", "SONOMA", "VENTURA", "MONTEREY" ]: # with Xcode [13.1 .. ]
p.set_defaults( type_qt = "qt5macports",
type_ruby = "sys",
type_python = "sys",
build_pymod_whl = False,
no_qt_binding = False,
no_qt_uitools = False,
no_32_bitcoord = False,
no_libgit2 = False,
make_option = "--jobs=4",
debug_build = False,
@ -432,7 +448,9 @@ def Parse_CLI_Args(config):
if choiceRuby == "nil":
ModuleRuby = 'nil'
elif choiceRuby == "Sys":
if Platform == "Tahoe":
if Platform == "GoldenGate":
ModuleRuby = 'RubyGoldenGate'
elif Platform == "Tahoe":
ModuleRuby = 'RubyTahoe'
elif Platform == "Sequoia":
ModuleRuby = 'RubySequoia'
@ -465,12 +483,11 @@ def Parse_CLI_Args(config):
candidates = dict()
candidates['NIL'] = 'nil'
candidates['SYS'] = 'Sys'
candidates['MP314'] = 'MP314'
candidates['HB314'] = 'HB314'
candidates['ANA3'] = 'Ana3'
candidates['MP313'] = 'MP313'
candidates['HB313'] = 'HB313'
candidates['ANA3'] = 'Ana3'
candidates['MP312'] = 'MP312'
candidates['HB312'] = 'HB312'
candidates['MP311'] = 'MP311'
candidates['HB311'] = 'HB311'
candidates['HBAUTO'] = 'HBAuto'
try:
@ -484,7 +501,10 @@ def Parse_CLI_Args(config):
ModulePython = 'nil'
OSPython3FW = None
elif choicePython == "Sys":
if Platform == "Tahoe":
if Platform == "GoldenGate":
ModulePython = 'PythonGoldenGate'
OSPython3FW = GoldenGatePy3FW
elif Platform == "Tahoe":
ModulePython = 'PythonTahoe'
OSPython3FW = TahoePy3FW
elif Platform == "Sequoia":
@ -499,12 +519,12 @@ def Parse_CLI_Args(config):
elif Platform == "Monterey":
ModulePython = 'PythonMonterey'
OSPython3FW = MontereyPy3FW
elif choicePython == "MP313":
ModulePython = 'Python313MacPorts'
elif choicePython == "MP314":
ModulePython = 'Python314MacPorts'
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "HB313":
ModulePython = 'Python313Brew'
elif choicePython == "HB314":
ModulePython = 'Python314Brew'
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "Ana3":
@ -514,12 +534,12 @@ def Parse_CLI_Args(config):
ModulePython = 'PythonAnaconda3V6'
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "MP312":
ModulePython = 'Python312MacPorts'
elif choicePython == "MP313":
ModulePython = 'Python313MacPorts'
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "HB312":
ModulePython = 'Python312Brew'
elif choicePython == "HB313":
ModulePython = 'Python313Brew'
OSPython3FW = None
NonOSStdLang = True
elif choicePython == "HB311":
@ -544,6 +564,7 @@ def Parse_CLI_Args(config):
BuildPymodWhl = opt.build_pymod_whl
NoQtBindings = opt.no_qt_binding
NoQtUiTools = opt.no_qt_uitools
No32bitCoord = opt.no_32_bitcoord
NoLibGit2 = opt.no_libgit2
MakeOptions = opt.make_option
DebugMode = opt.debug_build
@ -630,6 +651,7 @@ def Parse_CLI_Args(config):
config['NonOSStdLang'] = NonOSStdLang
config['NoQtBindings'] = NoQtBindings
config['NoQtUiTools'] = NoQtUiTools
config['No32bitCoord'] = No32bitCoord
config['NoLibGit2'] = NoLibGit2
config['MakeOptions'] = MakeOptions
config['DebugMode'] = DebugMode
@ -674,6 +696,7 @@ def Get_Build_Parameters(config):
ModuleSet = config['ModuleSet']
NoQtBindings = config['NoQtBindings']
NoQtUiTools = config['NoQtUiTools']
No32bitCoord = config['No32bitCoord']
NoLibGit2 = config['NoLibGit2']
MakeOptions = config['MakeOptions']
DebugMode = config['DebugMode']
@ -701,34 +724,46 @@ def Get_Build_Parameters(config):
(qt, ruby, python) = ModuleSet # ( 'qt6Brew', 'Sys', 'Sys' )
ruby_python = "R%sP%s" % ( ruby.lower(), python.lower() )
# (C) Target directories and files
MacPkgDir = "%s%s.pkg.macos-%s-%s-%s" % (PackagePrefix, qt, Platform, mode, ruby_python)
MacBinDir = "%s.bin.macos-%s-%s-%s" % ( qt, Platform, mode, ruby_python)
MacBuildDir = "%s.build.macos-%s-%s-%s" % ( qt, Platform, mode, ruby_python)
MacBuildLog = "%s.build.macos-%s-%s-%s.log" % ( qt, Platform, mode, ruby_python)
# (C) Coordinate size
if No32bitCoord:
coordSize = "C64"
else:
coordSize = "C32"
# (D) Target directories and files
MacPkgDir = "%s%s.pkg.macos-%s-%s-%s-%s" % (PackagePrefix, qt, Platform, coordSize, mode, ruby_python)
MacBinDir = "%s.bin.macos-%s-%s-%s-%s" % ( qt, Platform, coordSize, mode, ruby_python)
MacBuildDir = "%s.build.macos-%s-%s-%s-%s" % ( qt, Platform, coordSize, mode, ruby_python)
MacBuildLog = "%s.build.macos-%s-%s-%s-%s.log" % ( qt, Platform, coordSize, mode, ruby_python)
MacBuildDirQAT = MacBuildDir + ".macQAT"
parameters['bin'] = MacBinDir
parameters['build'] = MacBuildDir
parameters['logfile'] = MacBuildLog
# (D) about Qt[5|6]
# (E) about Qt[5|6]
parameters['qmake'] = Qt56Dictionary[ModuleQt]['qmake']
parameters['deploy_tool'] = Qt56Dictionary[ModuleQt]['deploy']
parameters['qt_lib_root'] = Qt56Dictionary[ModuleQt]['libdir']
# (E) rpath
# (F) rpath
if OSPython3FW in [ MontereyPy3FW, VenturaPy3FW, SonomaPy3FW, SequoiaPy3FW, TahoePy3FW ]:
parameters['rpath'] = OSPython3FW
else:
parameters['rpath'] = "@executable_path/../Frameworks"
# (F) want Qt bindings with Ruby scripts?
# (G) want Qt bindings with Ruby scripts?
parameters['no_qt_bindings'] = NoQtBindings
# (G) want QtUiTools?
# (H) want QtUiTools?
parameters['no_qt_uitools'] = NoQtUiTools
# (H) options to `make` tool
# (I) want 32bitCoord?
parameters['no_32_bitcoord'] = No32bitCoord
# (J) want libgit2?
parameters['no_libgit2'] = NoLibGit2
# (K) options to `make` tool
if not MakeOptions == "":
parameters['make_options'] = MakeOptions
try:
@ -739,7 +774,7 @@ def Get_Build_Parameters(config):
else:
parameters['num_parallel'] = pnum
# (I) about Ruby
# (L) about Ruby
if ModuleRuby != "nil":
parameters['ruby'] = RubyDictionary[ModuleRuby]['exe']
parameters['rbinc'] = RubyDictionary[ModuleRuby]['inc']
@ -747,7 +782,7 @@ def Get_Build_Parameters(config):
if 'inc2' in RubyDictionary[ModuleRuby]:
parameters['rbinc2'] = RubyDictionary[ModuleRuby]['inc2']
# (J) about Python
# (M) about Python
if ModulePython != "nil":
parameters['python'] = PythonDictionary[ModulePython]['exe']
parameters['pyinc'] = PythonDictionary[ModulePython]['inc']
@ -759,28 +794,28 @@ def Get_Build_Parameters(config):
config['MacBuildDirQAT'] = MacBuildDirQAT # relative path to build directory for QATest
config['MacBuildLog'] = MacBuildLog # relative path to build log file
# (K) Extra parameters needed for deployment
# (N) Extra parameters needed for deployment
parameters['project_dir'] = ProjectDir
# (L) Extra parameters needed for <pymod>
# (O) Extra parameters needed for <pymod>
# <pymod> will be built if:
# BuildPymodWhl = True
# Platform = [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]
# ModuleRuby = [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]
# ModulePython = [ 'Python313MacPorts', 'Python312MacPorts', 'Python311MacPorts',
# 'Python313Brew', 'Python312Brew', 'Python311Brew',
# 'PythonAnaconda3' ]
# Platform = [ 'GoldenGate', 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]
# ModuleRuby = [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3V5', 'RubyAnaconda3V6' ]
# ModulePython = [ 'Python314MacPorts', 'Python313MacPorts',
# 'Python314Brew', 'Python313Brew', 'Python311Brew',
# 'PythonAnaconda3V5', 'PythonAnaconda3V6' ]
parameters['BuildPymodWhl'] = BuildPymodWhl
parameters['Platform'] = Platform
parameters['ModuleRuby'] = ModuleRuby
parameters['ModulePython'] = ModulePython
PymodDistDir = dict()
if Platform in [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]:
if Platform in [ 'GoldenGate', 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]:
if ModuleRuby in [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3V5', 'RubyAnaconda3V6' ]:
if ModulePython in [ 'Python313MacPorts', 'Python312MacPorts', 'Python311MacPorts' ]:
if ModulePython in [ 'Python314MacPorts', 'Python313MacPorts' ]:
PymodDistDir[ModulePython] = 'dist-MP3-%s' % ModuleQt
elif ModulePython in [ 'Python313Brew', 'Python312Brew', 'Python311Brew' ]:
elif ModulePython in [ 'Python314Brew', 'Python313Brew', 'Python311Brew' ]:
PymodDistDir[ModulePython] = 'dist-HB3-%s' % ModuleQt
elif ModulePython in [ 'PythonAnaconda3V5', 'PythonAnaconda3V6' ]:
PymodDistDir[ModulePython] = 'dist-ana3-%s' % ModuleQt
@ -799,10 +834,10 @@ def Build_pymod_wheel(parameters):
#-----------------------------------------------------------------------------------------------------------
# [1] <pymod> will be built if:
# BuildPymodWhl = True
# Platform = [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]
# Platform = [ 'GoldenGate', 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]
# ModuleRuby = [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3V5', 'RubyAnaconda3V6' ]
# ModulePython = [ 'Python313MacPorts', 'Python312MacPorts', 'Python311MacPorts',
# 'Python313Brew', 'Python312Brew', 'Python311Brew',
# ModulePython = [ 'Python314MacPorts', 'Python313MacPorts',
# 'Python314Brew', 'Python313Brew', 'Python311Brew',
# 'PythonAnaconda3V5', 'PythonAnaconda3V6' ]
#-----------------------------------------------------------------------------------------------------------
BuildPymodWhl = parameters['BuildPymodWhl']
@ -811,12 +846,12 @@ def Build_pymod_wheel(parameters):
ModulePython = parameters['ModulePython']
if not BuildPymodWhl:
return 0
if not Platform in [ 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]:
if not Platform in [ 'GoldenGate', 'Tahoe', 'Sequoia', 'Sonoma', 'Ventura', 'Monterey' ]:
return 0
elif not ModuleRuby in [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3V5', 'RubyAnaconda3V6' ]:
return 0
elif not ModulePython in [ 'Python313MacPorts', 'Python312MacPorts', 'Python311MacPorts', \
'Python313Brew', 'Python312Brew', 'Python311Brew', \
elif not ModulePython in [ 'Python314MacPorts', 'Python313MacPorts', \
'Python314Brew', 'Python313Brew', 'Python311Brew', \
'PythonAnaconda3V5', 'PythonAnaconda3V6' ]:
return 0
@ -1124,25 +1159,31 @@ def Run_Build_Command(config, parameters):
cmd_args += " \\\n -build %s" % parameters['build']
cmd_args += " \\\n -rpath %s" % parameters['rpath']
# (E) want Qt bindings with Ruby scripts?
# (F) want Qt bindings with Ruby scripts?
if parameters['no_qt_bindings']:
cmd_args += " \\\n -without-qtbinding"
else:
cmd_args += " \\\n -with-qtbinding"
# (F) want QtUiTools?
# (G) want QtUiTools?
if parameters['no_qt_uitools']:
cmd_args += " \\\n -without-qt-uitools"
# (G) don't want to use libgit2?
if NoLibGit2:
cmd_args += " \\\n -libgit2"
# (I) want 32bitCoord?
if parameters['no_32_bitcoord']:
cmd_args += " \\\n -with-64bit-coord" # EXPERIMENTAL FEATURE
else:
cmd_args += " \\\n -without-64bit-coord" # default
# (H) options to `make` tool
# (J) want libgit2?
if parameters['no_libgit2']:
cmd_args += " \\\n -nolibgit2"
# (K) options to `make` tool
if 'make_options' in parameters:
cmd_args += " \\\n -option %s" % parameters['make_options']
# (I) about Ruby
# (L) about Ruby
if 'ruby' in parameters:
cmd_args += " \\\n -ruby %s" % parameters['ruby']
cmd_args += " \\\n -rbinc %s" % parameters['rbinc']
@ -1152,7 +1193,7 @@ def Run_Build_Command(config, parameters):
else:
cmd_args += " \\\n -noruby"
# (J) about Python
# (M) about Python
if 'python' in parameters:
cmd_args += " \\\n -python %s" % parameters['python']
cmd_args += " \\\n -pyinc %s" % parameters['pyinc']
@ -1645,9 +1686,10 @@ def Deploy_Binaries_For_Bundle(config, parameters):
regQt = re.compile(patQt)
# (4) Python frameworks (only for Homebrew) # in the case of Intel Mac...
libPy3_1 = "%s/" % HBPython312FrameworkPath # /usr/local/opt/python@3.12/Frameworks/Python.framework/
libPy3_2 = "%s/" % HBPython311FrameworkPath # /usr/local/opt/python@3.11/Frameworks/Python.framework/
patPy3 = r'^(%s|%s)(.+)' % (libPy3_1, libPy3_2)
libPy3_1 = "%s/" % HBPython314FrameworkPath # /usr/local/opt/python@3.14/Frameworks/Python.framework/
libPy3_2 = "%s/" % HBPython313FrameworkPath # /usr/local/opt/python@3.13/Frameworks/Python.framework/
libPy3_3 = "%s/" % HBPython311FrameworkPath # /usr/local/opt/python@3.11/Frameworks/Python.framework/
patPy3 = r'^(%s|%s|%s)(.+)' % (libPy3_1, libPy3_2, libPy3_3)
regPy3 = re.compile(patPy3)
#-------------------------------------------------------------------------------
@ -1991,7 +2033,25 @@ def Deploy_Binaries_For_Bundle(config, parameters):
cmd06 = "rm -rf %s" % binTarget
cmd07 = "mkdir %s" % sitepackagesTarget
cmd08 = "cp -RL %s/{*distutils*,pip*,pkg_resources,setuptools*,wheel*} %s" % (sitepackagesSource, sitepackagesTarget)
# cmd08 = "cp -RL %s/{*distutils*,pip*,pkg_resources,setuptools*,wheel*} %s" % (sitepackagesSource, sitepackagesTarget)
# Updated for compatibility with modern Python environments (e.g., pip 26.0.1+).
# Since 'pkg_resources' and other legacy paths may be absent in recent setuptools,
# we use a loop to copy only available directories while suppressing errors.
# Define patterns for essential packages to be bundled
patterns = ['*distutils*', 'pip*', 'pkg_resources*', 'setuptools*', 'wheel*']
sources = []
# Collect only existing paths to avoid "No such file or directory" errors,
# especially for 'pkg_resources' which is deprecated and often missing in Python 3.11+.
for p in patterns:
sources.extend(glob.glob(os.path.join(sitepackagesSource, p)))
if sources:
# Join all existing paths into a single string for the 'cp' command
cmd08 = "cp -RL %s %s" % (" ".join(sources), sitepackagesTarget)
else:
# Fallback or log if no packages are found
cmd08 = "echo 'No matching site-packages found to copy; skipping.'"
shell_commands = list()
shell_commands.append(cmd01)
@ -2267,7 +2327,7 @@ def Deploy_Binaries_For_Bundle(config, parameters):
#-------------------------------------------------------------
# [11] Sign the application bundle
#-------------------------------------------------------------
if Platform in ['Tahoe']:
if Platform in ['GoldenGate', 'Tahoe']:
print( " [11] Signing the macOS application bundle (ad-hoc) after all post-build edits (install_name_tool/strip)..." )
appbundle = "%s/klayout.app" % AbsMacPkgDir
res = Sign_App_Bundle(appbundle)

View File

@ -6,7 +6,7 @@
#
# Here are dictionaries of ...
# different modules for building KLayout (http://www.klayout.de/index.php)
# version 0.30.5 or later on different Apple Mac OSX platforms.
# version 0.30.9 or later on different Apple Mac OSX platforms.
#
# This file is imported by the 'build4mac.py' script.
#===============================================================================
@ -255,7 +255,7 @@ Qt56Dictionary = { 'Qt5MacPorts': Qt5MacPorts,
# for the previous states.
#-----------------------------------------------------
RubyNil = [ 'nil' ]
RubySys = [ 'RubyMonterey', 'RubyVentura', 'RubySonoma', 'RubySequoia', 'RubyTahoe' ]
RubySys = [ 'RubyMonterey', 'RubyVentura', 'RubySonoma', 'RubySequoia', 'RubyTahoe', 'RubyGoldenGate' ]
RubyExt = [ 'Ruby34MacPorts', 'Ruby34Brew', 'RubyAnaconda3' ]
Rubies = RubyNil + RubySys + RubyExt
@ -324,11 +324,21 @@ RubyTahoe = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % TahoeXcSDK
}
# Bundled with Golden Gate (27.x)
# [Key Type Name] = 'Sys'
GoldenGateXcSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
GoldenGateCLTSDK = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
RubyGoldenGate = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby',
'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % GoldenGateXcSDK,
'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % GoldenGateXcSDK,
'lib': '%s/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.tbd' % GoldenGateXcSDK
}
# Ruby 3.4 from MacPorts (https://www.macports.org/)
# install with 'sudo port install ruby34'
# [Key Type Name] = 'MP34'
Ruby34MacPorts = { 'exe': '/opt/local/bin/ruby3.4',
'inc': '/opt/local/include/ruby-3.4.7',
'inc': '/opt/local/include/ruby-3.4.10',
'lib': '/opt/local/lib/libruby.3.4.dylib'
}
@ -361,6 +371,7 @@ RubyDictionary = { 'nil' : None,
'RubySonoma' : RubySonoma,
'RubySequoia' : RubySequoia,
'RubyTahoe' : RubyTahoe,
'RubyGoldenGate' : RubyGoldenGate,
'Ruby34MacPorts' : Ruby34MacPorts,
'Ruby34Brew' : Ruby34Brew,
'RubyAnaconda3V5' : RubyAnaconda3V5,
@ -376,9 +387,9 @@ RubyDictionary = { 'nil' : None,
# for the previous states.
#-----------------------------------------------------
PythonNil = [ 'nil' ]
PythonSys = [ 'PythonMonterey', 'PythonVentura', 'PythonSonoma', 'PythonSequoia', 'PythonTahoe' ]
PythonExt = [ 'Python311MacPorts', 'Python312MacPorts', 'Python313MacPorts' ]
PythonExt += [ 'Python311Brew', 'Python312Brew', 'Python313Brew', 'PythonAutoBrew' ]
PythonSys = [ 'PythonMonterey', 'PythonVentura', 'PythonSonoma', 'PythonSequoia', 'PythonTahoe', 'PythonGoldenGate' ]
PythonExt = [ 'Python314MacPorts', 'Python313MacPorts' ]
PythonExt += [ 'Python314Brew', 'Python313Brew', 'Python311Brew', 'PythonAutoBrew' ]
PythonExt += [ 'PythonAnaconda3' ]
Pythons = PythonNil + PythonSys + PythonExt
@ -430,20 +441,21 @@ PythonTahoe = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' %
'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % TahoePy3FW
}
# Python 3.11 from MacPorts (https://www.macports.org/)
# install with 'sudo port install python311'
# [Key Type Name] = 'MP311'
Python311MacPorts = { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.11/bin/python3.11',
'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.11/include/python3.11',
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.11/lib/libpython3.11.dylib'
# Bundled with Golden Gate (27.x)
# [Key Type Name] = 'Sys'
GoldenGatePy3FWXc = "/Applications/Xcode.app/Contents/Developer/Library/Frameworks"
GoldenGatePy3FW = "/Library/Developer/CommandLineTools/Library/Frameworks"
PythonGoldenGate = { 'exe': '%s/Python3.framework/Versions/3.9/bin/python3.9' % GoldenGatePy3FW,
'inc': '%s/Python3.framework/Versions/3.9/include/python3.9' % GoldenGatePy3FW,
'lib': '%s/Python3.framework/Versions/3.9/lib/libpython3.9.dylib' % GoldenGatePy3FW
}
# Python 3.12 from MacPorts (https://www.macports.org/)
# install with 'sudo port install python312'
# [Key Type Name] = 'MP312'
Python312MacPorts = { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12',
'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.12/include/python3.12',
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.12/lib/libpython3.12.dylib'
# Python 3.14 from MacPorts (https://www.macports.org/)
# install with 'sudo port install python314'
# [Key Type Name] = 'MP314'
Python314MacPorts = { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14',
'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.14/include/python3.14',
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.14/lib/libpython3.14.dylib'
}
# Python 3.13 from MacPorts (https://www.macports.org/)
@ -454,22 +466,13 @@ Python313MacPorts = { 'exe': '/opt/local/Library/Frameworks/Python.framework/Ver
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.13/lib/libpython3.13.dylib'
}
# Python 3.11 from Homebrew
# install with 'brew install python@3.11'
# [Key Type Name] = 'HB311'
HBPython311FrameworkPath = '%s/opt/python@3.11/Frameworks/Python.framework' % DefaultHomebrewRoot
Python311Brew = { 'exe': '%s/Versions/3.11/bin/python3.11' % HBPython311FrameworkPath,
'inc': '%s/Versions/3.11/include/python3.11' % HBPython311FrameworkPath,
'lib': '%s/Versions/3.11/lib/libpython3.11.dylib' % HBPython311FrameworkPath
}
# Python 3.12 from Homebrew
# install with 'brew install python@3.12'
# [Key Type Name] = 'HB312'
HBPython312FrameworkPath = '%s/opt/python@3.12/Frameworks/Python.framework' % DefaultHomebrewRoot
Python312Brew = { 'exe': '%s/Versions/3.12/bin/python3.12' % HBPython312FrameworkPath,
'inc': '%s/Versions/3.12/include/python3.12' % HBPython312FrameworkPath,
'lib': '%s/Versions/3.12/lib/libpython3.12.dylib' % HBPython312FrameworkPath
# Python 3.14 from Homebrew
# install with 'brew install python@3.14'
# [Key Type Name] = 'HB314'
HBPython314FrameworkPath = '%s/opt/python@3.14/Frameworks/Python.framework' % DefaultHomebrewRoot
Python314Brew = { 'exe': '%s/Versions/3.14/bin/python3.14' % HBPython314FrameworkPath,
'inc': '%s/Versions/3.14/include/python3.14' % HBPython314FrameworkPath,
'lib': '%s/Versions/3.14/lib/libpython3.14.dylib' % HBPython314FrameworkPath
}
# Python 3.13 from Homebrew
@ -481,6 +484,15 @@ Python313Brew = { 'exe': '%s/Versions/3.13/bin/python3.13' % HBPython313Fram
'lib': '%s/Versions/3.13/lib/libpython3.13.dylib' % HBPython313FrameworkPath
}
# Python 3.11 from Homebrew
# install with 'brew install python@3.11'
# [Key Type Name] = 'HB311'
HBPython311FrameworkPath = '%s/opt/python@3.11/Frameworks/Python.framework' % DefaultHomebrewRoot
Python311Brew = { 'exe': '%s/Versions/3.11/bin/python3.11' % HBPython311FrameworkPath,
'inc': '%s/Versions/3.11/include/python3.11' % HBPython311FrameworkPath,
'lib': '%s/Versions/3.11/lib/libpython3.11.dylib' % HBPython311FrameworkPath
}
# Python 3.13 installed under [/opt|/Applications]/anaconda3/klayout-qt[5|6]
# See the [Qt5] [Qt6] section above.
# [Key Type Name] = 'Ana3'
@ -551,13 +563,13 @@ PythonDictionary = { 'nil' : None,
'PythonSonoma' : PythonSonoma,
'PythonSequoia' : PythonSequoia,
'PythonTahoe' : PythonTahoe,
'PythonGoldenGate' : PythonGoldenGate,
'Python314MacPorts' : Python314MacPorts,
'Python313MacPorts' : Python313MacPorts,
'Python314Brew' : Python314Brew,
'Python313Brew' : Python313Brew,
'PythonAnaconda3V5' : PythonAnaconda3V5,
'PythonAnaconda3V6' : PythonAnaconda3V6,
'Python312MacPorts' : Python312MacPorts,
'Python312Brew' : Python312Brew,
'Python311MacPorts' : Python311MacPorts,
'Python311Brew' : Python311Brew
}
if _have_Homebrew_Python:
@ -567,8 +579,8 @@ if _have_Homebrew_Python:
# [4] KLayout executables including buddy tools
#-----------------------------------------------------
KLayoutExecs = [ 'klayout' ]
KLayoutExecs += [ 'strm2cif', 'strm2dxf', 'strm2gds', 'strm2gdstxt', 'strm2mag', 'strm2oas' ]
KLayoutExecs += [ 'strm2txt', 'strmclip', 'strmcmp', 'strmrun', 'strmxor' ]
KLayoutExecs += [ 'strm2cif', 'strm2dxf', 'strm2gds', 'strm2gdstxt', 'strm2lstr', 'strm2mag' ]
KLayoutExecs += [ 'strm2oas', 'strm2txt', 'strmclip', 'strmcmp', 'strmrun', 'strmxor' ]
#----------------
# End of File

View File

@ -6,7 +6,7 @@
#
# Here are utility functions and classes ...
# for building KLayout (http://www.klayout.de/index.php)
# version 0.30.5 or later on different Apple Mac OSX platforms.
# version 0.30.9 or later on different Apple Mac OSX platforms.
#
# This file is imported by 'build4mac.py' script.
#========================================================================================

View File

@ -78,13 +78,13 @@ def SetGlobals():
Usage = "\n"
Usage += "---------------------------------------------------------------------------------------------------------\n"
Usage += "<< Usage of 'makeDMG4mac.py' >>\n"
Usage += " for making a DMG file of KLayout 0.30.5 or later on different Apple macOS platforms.\n"
Usage += " for making a DMG file of KLayout 0.30.9 or later on different Apple macOS platforms.\n"
Usage += "\n"
Usage += "$ [python] ./makeDMG4mac.py\n"
Usage += " option & argument : descriptions | default value\n"
Usage += " ----------------------------------------------------------------------------------+-----------------\n"
Usage += " <-p|--pkg <dir>> : package directory created by `build4mac.py` with [-y|-Y] | ``\n"
Usage += " : like 'LW-qt5MP.pkg.macos-Sequoia-release-Rmp34Pmp313' | \n"
Usage += " : like 'LW-qt5MP.pkg.macos-Sequoia-C32-release-Rmp34Pmp314' | \n"
Usage += " <-c|--clean> : clean the work directory | disabled\n"
Usage += " <-m|--make> : make a compressed DMG file | disabled\n"
Usage += " : <-c|--clean> and <-m|--make> are mutually exclusive | \n"
@ -106,7 +106,11 @@ def SetGlobals():
release = int( Release.split(".")[0] ) # take the first of ['21', '0', '0']
LatestOS = ""
if release == 25:
if release == 27:
GenOSName = "macOS"
Platform = "GoldenGate"
LatestOS = Platform
elif release == 25:
GenOSName = "macOS"
Platform = "Tahoe"
LatestOS = Platform
@ -135,7 +139,7 @@ def SetGlobals():
if not Machine == "x86_64":
# with an Apple Silicon Chip?
if Machine == "arm64" and Platform in ["Tahoe", "Sequoia", "Sonoma", "Ventura", "Monterey"]:
if Machine == "arm64" and Platform in ["GoldenGate", "Tahoe", "Sequoia", "Sonoma", "Ventura", "Monterey"]:
print("")
print( "### Your Mac equips an Apple Silicon Chip ###" )
print("")
@ -222,17 +226,17 @@ def SetGlobals():
## To check the contents of the package directory
#
# The package directory name should look like:
# * ST-qt5MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sequoia-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb312 --- (1)
# * LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sequoia-release-RsysPhb311
# * ST-qt5MP.pkg.macos-Sequoia-C32-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sequoia-C32-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sequoia-C32-release-Rhb34Phb312 --- (1)
# * LW-qt5MP.pkg.macos-Sequoia-C32-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sequoia-C32-release-RsysPhb311
#
# * ST-qt6MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# * ST-qt6MP.pkg.macos-Sequoia-C32-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sequoia-C32-release-Rmp33Pmp312
#
# Generated DMG will be, for example,
# (1) ---> LW-klayout-0.30.2-macOS-Sequoia-1-qt6Brew-Rhb34Phb312.dmg
# (1) ---> LW-klayout-0.30.2-macOS-Sequoia-C32-1-qt6Brew-Rhb34Phb312.dmg
#
# @return on success, positive integer in [MB] that tells approx. occupied disc space;
# on failure, -1
@ -247,6 +251,7 @@ def CheckPkgDirectory():
global BundleName
global PackagePrefix
global QtIdentification
global CoordSize
global BuildType
global RubyPythonID
global BackgroundPNG
@ -272,18 +277,18 @@ def CheckPkgDirectory():
#-----------------------------------------------------------------------------------------------
# [2] Identify (Qt, Ruby, Python) from PkgDir
# * ST-qt5MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sequoia-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb312
# * LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sequoia-release-RsysPhb311
# * EX-qt5MP.pkg.macos-Sequoia-release-Rhb34Pmp312
# * ST-qt5MP.pkg.macos-Sequoia-C32-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sequoia-C32-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sequoia-C32-release-Rhb34Phb312
# * LW-qt5MP.pkg.macos-Sequoia-C32-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sequoia-C32-release-RsysPhb311
# * EX-qt5MP.pkg.macos-Sequoia-C32-release-Rhb34Pmp312
#
# * ST-qt6MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# * ST-qt6MP.pkg.macos-Sequoia-C32-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sequoia-C32-release-Rmp33Pmp312
#-----------------------------------------------------------------------------------------------
# 0 1 2 3 4 5 6 7
patQRP = r'(ST|LW|HW|EX)([-])([qt5|qt6][0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-])(release|debug)([-])([0-9A-Za-z]+)'
# 0 1 2 3 4 5 6 7 8 9
patQRP = r'(ST|LW|HW|EX)([-])([qt5|qt6][0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-])(C32|C64)([-])(release|debug)([-])([0-9A-Za-z]+)'
regQRP = re.compile(patQRP)
if not regQRP.match(PkgDir):
print( "! Cannot identify (Qt, Ruby, Python) from the package directory name" )
@ -309,14 +314,25 @@ def CheckPkgDirectory():
else:
BackgroundPNG = None
raise Exception( "! neither qt5 nor qt6" )
if pkgdirComponents[5] == 'release':
if pkgdirComponents[5] == 'C32':
CoordSize = 'C32'
elif pkgdirComponents[5] == 'C64':
CoordSize = 'C64'
else:
CoordSize = None
raise Exception( "! neither C32 nor C64" )
RubyPythonID = pkgdirComponents[7]
if pkgdirComponents[7] == 'release':
BuildType = 'release'
elif pkgdirComponents[5] == 'debug':
elif pkgdirComponents[7] == 'debug':
BuildType = 'debug'
else:
BuildType = None
raise Exception( "! neither release nor debug" )
RubyPythonID = pkgdirComponents[7]
RubyPythonID = pkgdirComponents[9]
#-----------------------------------------------------------------------------
# [3] Check if the "LatestOS" with MacPorts / Homebrew / Anaconda3
@ -329,12 +345,12 @@ def CheckPkgDirectory():
LatestOSMacPorts = Platform == LatestOS
LatestOSMacPorts &= PackagePrefix == "LW"
LatestOSMacPorts &= QtIdentification in [ "qt5MP", "qt6MP" ]
LatestOSMacPorts &= RubyPythonID in [ "Rmp34Pmp313", "Rmp34Pmp312", "Rmp34Pmp311" ]
LatestOSMacPorts &= RubyPythonID in [ "Rmp34Pmp314", "Rmp34Pmp313" ]
LatestOSHomebrew = Platform == LatestOS
LatestOSHomebrew &= PackagePrefix == "LW"
LatestOSHomebrew &= QtIdentification in [ "qt5Brew", "qt6Brew", "qt5MP", "qt6MP" ] # "qt[5|6]MP" are the alternatives
LatestOSHomebrew &= RubyPythonID in [ "Rhb34Phb313", "Rhb34Phb312", "Rhb34Phb311", "Rhb34Phbauto" ]
LatestOSHomebrew &= RubyPythonID in [ "Rhb34Phb314", "Rhb34Phb313", "Rhb34Phb311", "Rhb34Phbauto" ]
LatestOSAnaconda3 = Platform == LatestOS
LatestOSAnaconda3 &= PackagePrefix == "LW"
@ -555,8 +571,8 @@ def ParseCommandLineArguments():
if opt.target_dmg != "":
TargetDMG = opt.target_dmg
else:
TargetDMG = "%s-klayout-%s-%s-%s-%d-%s-%s.dmg" \
% (PackagePrefix, KLVersion, GenOSName, Platform, DMGSerialNum, QtIdentification, RubyPythonID)
TargetDMG = "%s-klayout-%s-%s-%s-%s-%d-%s-%s.dmg" \
% (PackagePrefix, KLVersion, GenOSName, Platform, CoordSize, DMGSerialNum, QtIdentification, RubyPythonID)
if Machine == "arm64": # with an Apple Silicon Chip
TargetDMG = Machine + TargetDMG
if BuildType == "debug": # in the case of 'debug' build

View File

@ -30,14 +30,16 @@ import pandas as pd
#
# @return matching platform name on success; "" on failure
#------------------------------------------------------------------------------
def Test_My_Platform( platforms=[ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia', 'Tahoe' ] ):
def Test_My_Platform( platforms=[ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia', 'Tahoe', 'GoldenGate' ] ):
(System, Node, Release, MacVersion, Machine, Processor) = platform.uname()
if not System == "Darwin":
return ""
release = int( Release.split(".")[0] ) # take the first of ['21', '0', '0']
if release == 25:
if release == 27:
Platform = "GoldenGate"
elif release == 25:
Platform = "Tahoe"
elif release == 24:
Platform = "Sequoia"
@ -80,12 +82,13 @@ def Get_Build_Target_Dict():
#
# @param[in] targetDic build target dictionary
# @param[in] platform platform name
# @param[in] coordsize coordinate size (default='C32')
#
# @return (dictionary1, dictionary2)-tupple
# dictionary1: key=(qtVer, mnemonic, bdType), value=build option list
# dictionary2: key=(qtVer, mnemonic, bdType), value=log file name
#------------------------------------------------------------------------------
def Get_Build_Options( targetDic, platform ):
def Get_Build_Options( targetDic, platform, coordsize='C32'):
buildOp = dict()
logfile = dict()
@ -99,49 +102,49 @@ def Get_Build_Options( targetDic, platform ):
target = targetDic[key]
if target == "std":
buildOp[(qtVer, "std", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'sys' ]
logfile[(qtVer, "std", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPsys")
logfile[(qtVer, "std", "r")] = "%sMP.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "RsysPsys")
buildOp[(qtVer, "std", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'sys', '--debug' ]
logfile[(qtVer, "std", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "RsysPsys")
logfile[(qtVer, "std", "d")] = "%sMP.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "RsysPsys")
elif target == "ports":
buildOp[(qtVer, "ports", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP34', '-p', 'MP313' ]
logfile[(qtVer, "ports", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rmp34Pmp313")
buildOp[(qtVer, "ports", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP34', '-p', 'MP313', '--debug' ]
logfile[(qtVer, "ports", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rmp34Pmp313")
buildOp[(qtVer, "ports", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP34', '-p', 'MP314' ]
logfile[(qtVer, "ports", "r")] = "%sMP.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "Rmp34Pmp314")
buildOp[(qtVer, "ports", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'MP34', '-p', 'MP314', '--debug' ]
logfile[(qtVer, "ports", "d")] = "%sMP.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "Rmp34Pmp314")
elif target == "brew":
buildOp[(qtVer, "brew", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HB313' ]
logfile[(qtVer, "brew", "r")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb34Phb313")
buildOp[(qtVer, "brew", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HB313', '--debug' ]
logfile[(qtVer, "brew", "d")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rhb34Phb313")
buildOp[(qtVer, "brew", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HB314' ]
logfile[(qtVer, "brew", "r")] = "%sBrew.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "Rhb34Phb314")
buildOp[(qtVer, "brew", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HB314', '--debug' ]
logfile[(qtVer, "brew", "d")] = "%sBrew.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "Rhb34Phb314")
elif target == "brewHW":
buildOp[(qtVer, "brewHW", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311' ]
logfile[(qtVer, "brewHW", "r")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311")
logfile[(qtVer, "brewHW", "r")] = "%sBrew.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "RsysPhb311")
buildOp[(qtVer, "brewHW", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HB311', '--debug' ]
logfile[(qtVer, "brewHW", "d")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "RsysPhb311")
logfile[(qtVer, "brewHW", "d")] = "%sBrew.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "RsysPhb311")
elif target == "ana3":
buildOp[(qtVer, "ana3", "r")] = [ '-q', '%sAna3' % qtType, '-r', 'Ana3', '-p', 'Ana3' ]
logfile[(qtVer, "ana3", "r")] = "%sAna3.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rana3Pana3")
logfile[(qtVer, "ana3", "r")] = "%sAna3.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "Rana3Pana3")
buildOp[(qtVer, "ana3", "d")] = [ '-q', '%sAna3' % qtType, '-r', 'Ana3', '-p', 'Ana3', '--debug' ]
logfile[(qtVer, "ana3", "d")] = "%sAna3.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rana3Pana3")
logfile[(qtVer, "ana3", "d")] = "%sAna3.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "Rana3Pana3")
elif target == "brewA":
buildOp[(qtVer, "brewA", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HBAuto' ]
logfile[(qtVer, "brewA", "r")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb34Phbauto")
logfile[(qtVer, "brewA", "r")] = "%sBrew.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "Rhb34Phbauto")
buildOp[(qtVer, "brewA", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'HB34', '-p', 'HBAuto', '--debug' ]
logfile[(qtVer, "brewA", "d")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rhb34Phbauto")
logfile[(qtVer, "brewA", "d")] = "%sBrew.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "Rhb34Phbauto")
elif target == "brewAHW":
buildOp[(qtVer, "brewAHW", "r")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto' ]
logfile[(qtVer, "brewAHW", "r")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhbauto")
logfile[(qtVer, "brewAHW", "r")] = "%sBrew.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "RsysPhbauto")
buildOp[(qtVer, "brewAHW", "d")] = [ '-q', '%sBrew' % qtType, '-r', 'sys', '-p', 'HBAuto', '--debug' ]
logfile[(qtVer, "brewAHW", "d")] = "%sBrew.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "RsysPhbauto")
logfile[(qtVer, "brewAHW", "d")] = "%sBrew.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "RsysPhbauto")
elif target == "pbrew":
buildOp[(qtVer, "pbrew", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB34', '-p', 'HB313' ]
logfile[(qtVer, "pbrew", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "Rhb34Phb313")
buildOp[(qtVer, "pbrew", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB34', '-p', 'HB313', '--debug' ]
logfile[(qtVer, "pbrew", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "Rhb34Phb313")
buildOp[(qtVer, "pbrew", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB34', '-p', 'HB314' ]
logfile[(qtVer, "pbrew", "r")] = "%sMP.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "Rhb34Phb314")
buildOp[(qtVer, "pbrew", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'HB34', '-p', 'HB314', '--debug' ]
logfile[(qtVer, "pbrew", "d")] = "%sMP.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "Rhb34Phb314")
elif target == "pbrewHW":
buildOp[(qtVer, "pbrewHW", "r")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'HB311' ]
logfile[(qtVer, "pbrewHW", "r")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "release", "RsysPhb311")
logfile[(qtVer, "pbrewHW", "r")] = "%sMP.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "release", "RsysPhb311")
buildOp[(qtVer, "pbrewHW", "d")] = [ '-q', '%sMacPorts' % qtType, '-r', 'sys', '-p', 'HB311', '--debug' ]
logfile[(qtVer, "pbrewHW", "d")] = "%sMP.build.macos-%s-%s-%s.log" % (qtType.lower(), platform, "debug", "RsysPhb311")
logfile[(qtVer, "pbrewHW", "d")] = "%sMP.build.macos-%s-%s-%s-%s.log" % (qtType.lower(), platform, coordsize, "debug", "RsysPhb311")
if WithPymod:
buildOp[(qtVer, "ports", "r")] = buildOp[(qtVer, "ports", "r")] + ['--buildPymod']
@ -161,10 +164,11 @@ def Get_Build_Options( targetDic, platform ):
#
# @param[in] targetDic build target dictionary
# @param[in] platform platform name
# @param[in] coordsize coordinate size (default='C32')
#
# @return a dictionary; key=(qtVer, mnemonic, bdType), value=".macQAT" directory
#------------------------------------------------------------------------------
def Get_QAT_Directory( targetDic, platform ):
def Get_QAT_Directory( targetDic, platform, coordsize='C32' ):
dirQAT = dict()
for qtVer in [5, 6]:
@ -176,32 +180,32 @@ def Get_QAT_Directory( targetDic, platform ):
for key in targetDic.keys():
target = targetDic[key]
if target == "std":
dirQAT[(qtVer, "std", "r")] = '%sMP.build.macos-%s-release-RsysPsys.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "std", "d")] = '%sMP.build.macos-%s-debug-RsysPsys.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "std", "r")] = '%sMP.build.macos-%s-%s-release-RsysPsys.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "std", "d")] = '%sMP.build.macos-%s-%s-debug-RsysPsys.macQAT' % (qtType.lower(), platform, coordsize)
elif target == "ports":
dirQAT[(qtVer, "ports", "r")] = '%sMP.build.macos-%s-release-Rmp34Pmp313.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "ports", "d")] = '%sMP.build.macos-%s-debug-Rmp34Pmp313.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "ports", "r")] = '%sMP.build.macos-%s-%s-release-Rmp34Pmp314.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "ports", "d")] = '%sMP.build.macos-%s-%s-debug-Rmp34Pmp314.macQAT' % (qtType.lower(), platform, coordsize)
elif target == "brew":
dirQAT[(qtVer, "brew", "r")] = '%sBrew.build.macos-%s-release-Rhb34Phb313.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "brew", "d")] = '%sBrew.build.macos-%s-debug-Rhb34Phb313.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "brew", "r")] = '%sBrew.build.macos-%s-%s-release-Rhb34Phb314.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "brew", "d")] = '%sBrew.build.macos-%s-%s-debug-Rhb34Phb314.macQAT' % (qtType.lower(), platform, coordsize)
elif target == "brewHW":
dirQAT[(qtVer, "brewHW", "r")] = '%sBrew.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "brewHW", "d")] = '%sBrew.build.macos-%s-debug-RsysPhb311.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "brewHW", "r")] = '%sBrew.build.macos-%s-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "brewHW", "d")] = '%sBrew.build.macos-%s-%s-debug-RsysPhb311.macQAT' % (qtType.lower(), platform, coordsize)
elif target == "ana3":
dirQAT[(qtVer, "ana3", "r")] = '%sAna3.build.macos-%s-release-Rana3Pana3.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "ana3", "d")] = '%sAna3.build.macos-%s-debug-Rana3Pana3.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "ana3", "r")] = '%sAna3.build.macos-%s-%s-release-Rana3Pana3.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "ana3", "d")] = '%sAna3.build.macos-%s-%s-debug-Rana3Pana3.macQAT' % (qtType.lower(), platform, coordsize)
elif target == "brewA":
dirQAT[(qtVer, "brewA", "r")] = '%sBrew.build.macos-%s-release-Rhb34Phbauto.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "brewA", "d")] = '%sBrew.build.macos-%s-debug-Rhb34Phbauto.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "brewA", "r")] = '%sBrew.build.macos-%s-%s-release-Rhb34Phbauto.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "brewA", "d")] = '%sBrew.build.macos-%s-%s-debug-Rhb34Phbauto.macQAT' % (qtType.lower(), platform, coordsize)
elif target == "brewAHW":
dirQAT[(qtVer, "brewAHW", "r")] = '%sBrew.build.macos-%s-release-RsysPhbauto.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "brewAHW", "d")] = '%sBrew.build.macos-%s-debug-RsysPhbauto.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "brewAHW", "r")] = '%sBrew.build.macos-%s-%s-release-RsysPhbauto.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "brewAHW", "d")] = '%sBrew.build.macos-%s-%s-debug-RsysPhbauto.macQAT' % (qtType.lower(), platform, coordsize)
elif target == "pbrew":
dirQAT[(qtVer, "pbrew", "r")] = '%sMP.build.macos-%s-release-Rhb34Phb313.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "pbrew", "d")] = '%sMP.build.macos-%s-debug-Rhb34Phb313.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "pbrew", "r")] = '%sMP.build.macos-%s-%s-release-Rhb34Phb314.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "pbrew", "d")] = '%sMP.build.macos-%s-%s-debug-Rhb34Phb314.macQAT' % (qtType.lower(), platform, coordsize)
elif target == "pbrewHW":
dirQAT[(qtVer, "pbrewHW", "r")] = '%sMP.build.macos-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "pbrewHW", "d")] = '%sMP.build.macos-%s-debug-RsysPhb311.macQAT' % (qtType.lower(), platform)
dirQAT[(qtVer, "pbrewHW", "r")] = '%sMP.build.macos-%s-%s-release-RsysPhb311.macQAT' % (qtType.lower(), platform, coordsize)
dirQAT[(qtVer, "pbrewHW", "d")] = '%sMP.build.macos-%s-%s-debug-RsysPhb311.macQAT' % (qtType.lower(), platform, coordsize)
return dirQAT
@ -212,10 +216,11 @@ def Get_QAT_Directory( targetDic, platform ):
# @param[in] platform platform name
# @param[in] srlDMG serial number of DMG
# @param[in] makeflag True to make; False to clean
# @param[in] coordsize coordinate size (default='C32')
#
# @return a dictionary; key=(qtVer, mnemonic, bdType), value=build option list
#------------------------------------------------------------------------------
def Get_Package_Options( targetDic, platform, srlDMG, makeflag ):
def Get_Package_Options( targetDic, platform, srlDMG, makeflag, coordsize='C32' ):
packOp = dict()
if makeflag:
@ -232,49 +237,49 @@ def Get_Package_Options( targetDic, platform, srlDMG, makeflag ):
for key in targetDic.keys():
target = targetDic[key]
if target == "std":
packOp[(qtVer, "std", "r")] = [ '-p', 'ST-%sMP.pkg.macos-%s-release-RsysPsys' % (qtType.lower(), platform),
packOp[(qtVer, "std", "r")] = [ '-p', 'ST-%sMP.pkg.macos-%s-%s-release-RsysPsys' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "std", "d")] = [ '-p', 'ST-%sMP.pkg.macos-%s-debug-RsysPsys' % (qtType.lower(), platform),
packOp[(qtVer, "std", "d")] = [ '-p', 'ST-%sMP.pkg.macos-%s-%s-debug-RsysPsys' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "ports":
packOp[(qtVer, "ports", "r")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rmp34Pmp313' % (qtType.lower(), platform),
packOp[(qtVer, "ports", "r")] = [ '-p', 'LW-%sMP.pkg.macos-%s-%s-release-Rmp34Pmp314' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "ports", "d")] = [ '-p', 'LW-%sMP.pkg.macos-%s-debug-Rmp34Pmp313' % (qtType.lower(), platform),
packOp[(qtVer, "ports", "d")] = [ '-p', 'LW-%sMP.pkg.macos-%s-%s-debug-Rmp34Pmp314' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brew":
packOp[(qtVer, "brew", "r")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb34Phb313' % (qtType.lower(), platform),
packOp[(qtVer, "brew", "r")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-%s-release-Rhb34Phb314' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "brew", "d")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-debug-Rhb34Phb313' % (qtType.lower(), platform),
packOp[(qtVer, "brew", "d")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-%s-debug-Rhb34Phb314' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewHW":
packOp[(qtVer, "brewHW", "r")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform),
packOp[(qtVer, "brewHW", "r")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-%s-release-RsysPhb311' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "brewHW", "d")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-debug-RsysPhb311' % (qtType.lower(), platform),
packOp[(qtVer, "brewHW", "d")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-%s-debug-RsysPhb311' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "ana3":
packOp[(qtVer, "ana3", "r")] = [ '-p', 'LW-%sAna3.pkg.macos-%s-release-Rana3Pana3' % (qtType.lower(), platform),
packOp[(qtVer, "ana3", "r")] = [ '-p', 'LW-%sAna3.pkg.macos-%s-%s-release-Rana3Pana3' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "ana3", "d")] = [ '-p', 'LW-%sAna3.pkg.macos-%s-debug-Rana3Pana3' % (qtType.lower(), platform),
packOp[(qtVer, "ana3", "d")] = [ '-p', 'LW-%sAna3.pkg.macos-%s-%s-debug-Rana3Pana3' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewA":
packOp[(qtVer, "brewA", "r")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-release-Rhb34Phbauto' % (qtType.lower(), platform),
packOp[(qtVer, "brewA", "r")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-%s-release-Rhb34Phbauto' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "brewA", "d")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-debug-Rhb34Phbauto' % (qtType.lower(), platform),
packOp[(qtVer, "brewA", "d")] = [ '-p', 'LW-%sBrew.pkg.macos-%s-%s-debug-Rhb34Phbauto' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "brewAHW":
packOp[(qtVer, "brewAHW", "r")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-release-RsysPhbauto' % (qtType.lower(), platform),
packOp[(qtVer, "brewAHW", "r")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-%s-release-RsysPhbauto' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "brewAHW", "d")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-debug-RsysPhbauto' % (qtType.lower(), platform),
packOp[(qtVer, "brewAHW", "d")] = [ '-p', 'HW-%sBrew.pkg.macos-%s-%s-debug-RsysPhbauto' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "pbrew":
packOp[(qtVer, "pbrew", "r")] = [ '-p', 'LW-%sMP.pkg.macos-%s-release-Rhb34Phb313' % (qtType.lower(), platform),
packOp[(qtVer, "pbrew", "r")] = [ '-p', 'LW-%sMP.pkg.macos-%s-%s-release-Rhb34Phb314' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "pbrew", "d")] = [ '-p', 'LW-%sMP.pkg.macos-%s-debug-Rhb34Phb313' % (qtType.lower(), platform),
packOp[(qtVer, "pbrew", "d")] = [ '-p', 'LW-%sMP.pkg.macos-%s-%s-debug-Rhb34Phb314' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
elif target == "pbrewHW":
packOp[(qtVer, "pbrewHW", "r")] = [ '-p', 'HW-%sMP.pkg.macos-%s-release-RsysPhb311' % (qtType.lower(), platform),
packOp[(qtVer, "pbrewHW", "r")] = [ '-p', 'HW-%sMP.pkg.macos-%s-%s-release-RsysPhb311' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
packOp[(qtVer, "pbrewHW", "d")] = [ '-p', 'HW-%sMP.pkg.macos-%s-debug-RsysPhb311' % (qtType.lower(), platform),
packOp[(qtVer, "pbrewHW", "d")] = [ '-p', 'HW-%sMP.pkg.macos-%s-%s-debug-RsysPhb311' % (qtType.lower(), platform, coordsize),
'-s', '%d' % srlDMG, '%s' % flag ]
return packOp
@ -299,7 +304,7 @@ def Parse_CommandLine_Arguments():
global DryRun # True for dry-run
platform = Test_My_Platform()
if platform in [ "Tahoe", "Sequoia", "Sonoma", "Ventura", "Monterey" ]:
if platform in [ "GoldenGate", "Tahoe", "Sequoia", "Sonoma", "Ventura", "Monterey" ]:
targetopt = "0,1,2,13,4"
else:
targetopt = ""
@ -308,7 +313,7 @@ def Parse_CommandLine_Arguments():
Usage += "----------------------------------------------------------------------------------------------------------\n"
Usage += " nightlyBuild.py [EXPERIMENTAL]\n"
Usage += " << To execute the jobs for making KLayout's DMGs for\n"
Usage += " macOS Monterey, Ventura, Sonoma, or Sequoia >>\n"
Usage += " macOS Monterey, Ventura, Sonoma, Sequoia, Tahoe, or GoldenGate >>\n"
Usage += "\n"
Usage += "$ [python] nightlyBuild.py\n"
Usage += " option & argument : comment on option if any | default value\n"
@ -343,7 +348,7 @@ def Parse_CommandLine_Arguments():
Usage += " (3) $ ./nightlyBuild.py --test |\n"
Usage += " (4) $ ./nightlyBuild.py --check (confirm the QA Test results) |\n"
Usage += " (5) $ ./nightlyBuild.py --makedmg 1 |\n"
Usage += " (6) $ ./nightlyBuild.py --upload '0.30.2' |\n"
Usage += " (6) $ ./nightlyBuild.py --upload '0.30.9' |\n"
Usage += " (7) $ ./nightlyBuild.py --cleandmg 1 |\n"
Usage += "-----------------------------------------------------------------------------+----------------------------\n"
@ -434,7 +439,7 @@ def Parse_CommandLine_Arguments():
print(Usage)
sys.exit(0)
myPlatform = Test_My_Platform( [ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia', 'Tahoe' ] )
myPlatform = Test_My_Platform( [ '', 'Monterey', 'Ventura', 'Sonoma', 'Sequoia', 'Tahoe' ] )
if myPlatform == "":
print( "! Current platform is not [ 'Monterey', 'Ventura', 'Sonoma', 'Sequoia', 'Tahoe' ]" )
print(Usage)

View File

@ -33,7 +33,7 @@ def SetGlobals():
Usage += "\n"
Usage += " option & argument : descriptions | default value\n"
Usage += " -------------------------------------------------------------------+---------------\n"
Usage += " <-v|--version <number>>: in ['3.11', '3.12','3.13'] | ''\n"
Usage += " <-v|--version <number>>: in ['3.11', '3.12','3.13', '3.14'] | ''\n"
Usage += " [-u|-unlink] : unlink only | disabled\n"
Usage += " [-?|--?] : print this usage and exit | disabled\n"
Usage += "----------------------------------------------------------------------+-----------------\n"
@ -49,7 +49,7 @@ def Parse_CLI_Args():
p.add_option( '-v', '--version',
dest='version',
help="python3 version=['3.11', '3.12', '3.13']" )
help="python3 version=['3.11', '3.12', '3.13', '3.14']" )
p.add_option( '-u', '--unlink',
action='store_true',
@ -74,7 +74,7 @@ def Parse_CLI_Args():
Version = opt.version
UnlinkOnly = opt.unlink
if not Version in [ '3.11', '3.12', '3.13' ]:
if not Version in [ '3.11', '3.12', '3.13', '3.14' ]:
print( "! Unsupported Python 3 version <%s>" % Version )
print(Usage)
sys.exit(0)

View File

@ -13,7 +13,7 @@ test-command = [
"TESTSRC={package} python {package}/testdata/pymod/pya_tests.py"
]
# Disable building PyPy wheels on all platforms
skip = "pp* cp36-* cp37-*"
skip = "pp* cp36-* cp37-* cp38-* cp39-*"
[tool.cibuildwheel.linux]
# beware: the before-all script does not persist environment variables!

View File

@ -41,6 +41,9 @@ ubuntu22)
ubuntu24)
depends="libqt5core5t64 (>= 5.15.13), libqt5designer5 (>= 5.15.13), libqt5gui5t64 (>= 5.15.13), libqt5multimedia5 (>= 5.15.13), libqt5multimediawidgets5 (>= 5.15.13), libqt5network5t64 (>= 5.15.13), libqt5opengl5t64 (>= 5.15.13), libqt5printsupport5t64 (>= 5.15.13), libqt5sql5t64 (>= 5.15.13), libqt5svg5 (>= 5.15.13), libqt5widgets5t64 (>= 5.15.13), libqt5xml5t64 (>= 5.15.13), libqt5xmlpatterns5 (>= 5.15.13), zlib1g (>= 1.3), libgit2-1.7 (>= 1.7.2), libruby3.2 (>= 3.2.3), python3 (>= 3.12.3), libpython3.12 (>= 3.12.3), libstdc++6 (>=14), libc6 (>= 2.39)"
;;
ubuntu26)
depends="libqt5core5t64 (>= 5.15.18), libqt5designer5 (>= 5.15.18), libqt5gui5t64 (>= 5.15.18), libqt5multimedia5 (>= 5.15.18), libqt5multimediawidgets5 (>= 5.15.18), libqt5network5t64 (>= 5.15.18), libqt5opengl5t64 (>= 5.15.18), libqt5printsupport5t64 (>= 5.15.18), libqt5sql5t64 (>= 5.15.18), libqt5svg5 (>= 5.15.18), libqt5widgets5t64 (>= 5.15.18), libqt5xml5t64 (>= 5.15.18), libqt5xmlpatterns5 (>= 5.15.18), zlib1g (>= 1.3), libgit2-1.9 (>= 1.9.1), libruby3.3 (>= 3.3.8), python3 (>= 3.14.3), libpython3.14 (>= 3.14.3), libstdc++6 (>=16), libc6 (>= 2.43)"
;;
*)
echo "Unknown target '$target' (given as first argument)"
exit 1

View File

@ -641,18 +641,23 @@ rename "QKeySequence", /QKeySequence::QKeySequence\(QKeySequence::StandardKey/,
# TODO: basically, the layout object only takes ownership over the objects when
# it has a QWidget parent itself. This is not reflected in the following simple scheme
keep_arg "QBoxLayout", /::addLayout/, 0 # will take ownership of layout
keep_arg "QBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::addSpacerItem/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::insertItem/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertLayout/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertSpacerItem/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertWidget/, 1 # will take ownership of item
keep_arg "QHBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QVBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QGridLayout", /::addLayout/, 0 # will take ownership of layout
keep_arg "QGridLayout", /::addItem/, 0 # will take ownership of layout
keep_arg "QGridLayout", /::addWidget/, 0 # will take ownership of layout
keep_arg "QFormLayout", /::addItem/, 0 # will take ownership of layout
keep_arg "QLayout", /::addChildLayout/, 0 # will take ownership of layout
keep_arg "QLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::insertWidget/, 1 # will take ownership of item

View File

@ -840,23 +840,23 @@ rename "QKeySequence", /QKeySequence::QKeySequence\(QKeySequence::StandardKey/,
# TODO: basically, the layout object only takes ownership over the objects when
# it has a QWidget parent itself. This is not reflected in the following simple scheme
keep_arg "QBoxLayout", /::addLayout/, 0 # will take ownership of layout
keep_arg "QBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::addSpacerItem/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::insertItem/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertLayout/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertSpacerItem/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertWidget/, 1 # will take ownership of item
keep_arg "QFormLayout", /::addRow/, 1 # will take ownership of item
keep_arg "QFormLayout", /::addRow\(QWidget\s*\*/, 0 # will take ownership of item
keep_arg "QFormLayout", /::insertRow/, 2 # will take ownership of item
keep_arg "QFormLayout", /::insertRow\(QWidget\s*\*/, 1 # will take ownership of item
keep_arg "QFormLayout", /::setWidget/, 2 # will take ownership of item
keep_arg "QHBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QVBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QGridLayout", /::addLayout/, 0 # will take ownership of layout
keep_arg "QGridLayout", /::addItem/, 0 # will take ownership of layout
keep_arg "QGridLayout", /::addWidget/, 0 # will take ownership of layout
keep_arg "QFormLayout", /::addItem/, 0 # will take ownership of layout
keep_arg "QLayout", /::addChildLayout/, 0 # will take ownership of layout
keep_arg "QLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::insertWidget/, 1 # will take ownership of item
@ -886,9 +886,13 @@ owner_arg "QWidget", /::setParent\(QWidget\s+\*/, 0 # will make self owned by ar
# over ownership of it?
# keep_arg "QTableWidget", /::setItemPrototype\(/, 0 # will take ownership of the child
return_new "QLayout", /::takeAt/ # returns a free object
return_new "QLayout", /::replaceWidget/ # returns a free object
return_new "QBoxLayout", /::takeAt/ # returns a free object
return_new "QHBoxLayout", /::takeAt/ # returns a free object
return_new "QVBoxLayout", /::takeAt/ # returns a free object
# TODO: QFormLayout: takeRow -> needs QFormLayout::TakeRowResult
return_new "QGridLayout", /::takeAt/ # returns a free object
return_new "QFormLayout", /::takeAt/ # returns a free object
return_new "QStackedLayout", /::takeAt/ # returns a free object
return_new "QStandardItem", /::take/ # returns a free object
return_new "QStandardItemModel", /::take/ # returns a free object

View File

@ -1162,23 +1162,23 @@ rename "QKeySequence", /QKeySequence::QKeySequence\(QKeySequence::StandardKey/,
# TODO: basically, the layout object only takes ownership over the objects when
# it has a QWidget parent itself. This is not reflected in the following simple scheme
keep_arg "QBoxLayout", /::addLayout/, 0 # will take ownership of layout
keep_arg "QBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::addSpacerItem/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QBoxLayout", /::insertItem/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertLayout/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertSpacerItem/, 1 # will take ownership of item
keep_arg "QBoxLayout", /::insertWidget/, 1 # will take ownership of item
keep_arg "QFormLayout", /::addRow/, 1 # will take ownership of item
keep_arg "QFormLayout", /::addRow\(QWidget\s*\*/, 0 # will take ownership of item
keep_arg "QFormLayout", /::insertRow/, 2 # will take ownership of item
keep_arg "QFormLayout", /::insertRow\(QWidget\s*\*/, 1 # will take ownership of item
keep_arg "QFormLayout", /::setWidget/, 2 # will take ownership of item
keep_arg "QHBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QVBoxLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QGridLayout", /::addLayout/, 0 # will take ownership of layout
keep_arg "QGridLayout", /::addItem/, 0 # will take ownership of layout
keep_arg "QGridLayout", /::addWidget/, 0 # will take ownership of layout
keep_arg "QFormLayout", /::addItem/, 0 # will take ownership of layout
keep_arg "QLayout", /::addChildLayout/, 0 # will take ownership of layout
keep_arg "QLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::addItem/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::addWidget/, 0 # will take ownership of item
keep_arg "QStackedLayout", /::insertWidget/, 1 # will take ownership of item
@ -1208,11 +1208,20 @@ owner_arg "QWidget", /::setParent\(QWidget\s+\*/, 0 # will make self owned by ar
# over ownership of it?
# keep_arg "QTableWidget", /::setItemPrototype\(/, 0 # will take ownership of the child
return_new "QLayout", /::takeAt/ # returns a free object
return_new "QLayout", /::replaceWidget/ # returns a free object
return_new "QBoxLayout", /::takeAt/ # returns a free object
return_new "QBoxLayout", /::replaceWidget/ # returns a free object
return_new "QHBoxLayout", /::takeAt/ # returns a free object
return_new "QHBoxLayout", /::replaceWidget/ # returns a free object
return_new "QVBoxLayout", /::takeAt/ # returns a free object
return_new "QVBoxLayout", /::replaceWidget/ # returns a free object
return_new "QFormLayout", /::takeAt/ # returns a free object
return_new "QFormLayout", /::replaceWidget/ # returns a free object
return_new "QGridLayout", /::takeAt/ # returns a free object
return_new "QGridLayout", /::replaceWidget/ # returns a free object
# TODO: QFormLayout: takeRow -> needs QFormLayout::TakeRowResult
return_new "QStackedLayout", /::takeAt/ # returns a free object
return_new "QStackedLayout", /::replaceWidget/ # returns a free object
return_new "QStandardItem", /::take/ # returns a free object
return_new "QStandardItemModel", /::take/ # returns a free object
return_new "QTreeWidgetItem", /::take/ # returns a free object

View File

@ -2995,12 +2995,8 @@ END
ofile.puts("static void _call_cbs_#{mn}_#{hk}_#{i_var} (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) ")
ofile.puts("{")
ofile.puts(" __SUPPRESS_UNUSED_WARNING(args);")
if !ant.empty?
ofile.puts(" tl::Heap heap;")
end
ant.each_with_index do |at,ia|
ofile.puts(" #{at.renamed_type(alist[ia]).gsi_decl_arg(decl_obj)} = args.read<#{at.gsi_decl_arg(decl_obj)} > (heap);")
end
produce_arg_read(ofile, decl_obj, func, alist, conf.kept_args(bd))
produce_keep_self(ofile, alist, "(#{cls} *)cls", conf.owner_args(bd))
if !rt.is_void?
ofile.puts(" ret.write<#{rt.gsi_decl_return(decl_obj)} > ((#{rt.gsi_decl_return(decl_obj)})((#{clsn}_Adaptor *)cls)->cbs_#{mn}_#{hk}_#{i_var} (#{alist.join(', ')}));")
else

View File

@ -594,12 +594,12 @@
</widget>
</item>
<item row="10" column="1">
<widget class="QCheckBox" name="t_snap_cbx">
<widget class="QCheckBox" name="t_never_snap_cbx">
<property name="toolTip">
<string>If checked, snap to edges or vertices of objects unless disabled above</string>
</property>
<property name="text">
<string>Snap to objects</string>
<string>Never snap to objects</string>
</property>
</widget>
</item>
@ -893,7 +893,7 @@
<tabstop>style_cb</tabstop>
<tabstop>outline_cb</tabstop>
<tabstop>t_angle_cb</tabstop>
<tabstop>t_snap_cbx</tabstop>
<tabstop>t_never_snap_cbx</tabstop>
</tabstops>
<resources>
<include location="../../icons/icons.qrc"/>

242
src/ant/ant/RulerOptions.ui Normal file
View File

@ -0,0 +1,242 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RulerOptions</class>
<widget class="QWidget" name="RulerOptions">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>446</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>446</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Snapping</string>
</property>
<layout class="QGridLayout" name="_5">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="snap_to_grid_cbx">
<property name="text">
<string>Snap to grid</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="snap_to_objects_cbx">
<property name="text">
<string>Snap to edge/vertex</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Angle Constraints</string>
</property>
<layout class="QGridLayout" name="_2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="1">
<widget class="QComboBox" name="angle_cb">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Any Angle</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal only</string>
</property>
</item>
<item>
<property name="text">
<string>Orthogonal</string>
</property>
</item>
<item>
<property name="text">
<string>Horizontal only</string>
</property>
</item>
<item>
<property name="text">
<string>Vertical only</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Ruler direction</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Note: certain rulers may ignore these options</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>121</width>
<height>70</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>scrollArea</tabstop>
<tabstop>snap_to_grid_cbx</tabstop>
<tabstop>snap_to_objects_cbx</tabstop>
<tabstop>angle_cb</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -14,6 +14,7 @@ FORMS = \
RulerConfigPage3.ui \
RulerConfigPage4.ui \
RulerPropertiesPage.ui \
RulerOptions.ui \
}
@ -22,10 +23,12 @@ FORMS = \
HEADERS = \
antConfigPage.h \
antPropertiesPage.h \
antRulerOptionsPage.h
SOURCES = \
antConfigPage.cc \
antPropertiesPage.cc \
antRulerOptionsPage.cc
# Enabled without Qt:

View File

@ -375,7 +375,7 @@ ConfigPage4::show ()
mp_ui->style_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].style ());
mp_ui->outline_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].outline ());
mp_ui->t_angle_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].angle_constraint ());
mp_ui->t_snap_cbx->setChecked (m_ruler_templates [m_current_template].snap ());
mp_ui->t_never_snap_cbx->setChecked (! m_ruler_templates [m_current_template].snap ());
mp_ui->t_mode_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].mode ());
mp_ui->main_position->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].main_position ());
@ -410,7 +410,7 @@ ConfigPage4::commit ()
ant::Template::ruler_mode_type mode = ant::Template::ruler_mode_type (mp_ui->t_mode_cb->currentIndex ());
m_ruler_templates [m_current_template].set_mode (mode);
m_ruler_templates [m_current_template].snap (mp_ui->t_snap_cbx->isChecked ());
m_ruler_templates [m_current_template].snap (! mp_ui->t_never_snap_cbx->isChecked ());
m_ruler_templates [m_current_template].set_main_position (Object::position_type (mp_ui->main_position->currentIndex ()));
m_ruler_templates [m_current_template].set_main_xalign (Object::alignment_type (mp_ui->main_xalign->currentIndex ()));

View File

@ -25,6 +25,7 @@
#include "layConverters.h"
#include "layDispatcher.h"
#include "layAbstractMenu.h"
#include "layLayoutViewBase.h"
#include "tlColor.h"
#include "tlLog.h"
#if defined(HAVE_QT)
@ -139,11 +140,10 @@ PluginDeclaration::create_plugin (db::Manager *manager, lay::Dispatcher *, lay::
}
std::vector<std::string>
PluginDeclaration::additional_editor_options_pages () const
PluginDeclaration::additional_editor_options_pages (lay::LayoutViewBase * /*view*/) const
{
std::vector<std::string> names;
// TODO: provide in a central place instead of borrowing from the edt module
names.push_back ("GenericEditorOptions");
names.push_back ("ant::RulerOptions");
return names;
}

View File

@ -54,7 +54,7 @@ public:
virtual void uninitialize (lay::Dispatcher *);
virtual bool menu_activated (const std::string &symbol) const;
virtual std::vector<std::string> additional_editor_options_pages () const;
virtual std::vector<std::string> additional_editor_options_pages (lay::LayoutViewBase *view) const;
void register_annotation_template (const ant::Template &t, lay::Plugin *plugin = 0);
void unregister_annotation_template (const std::string &category, lay::Plugin *plugin = 0);

View File

@ -0,0 +1,119 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if defined(HAVE_QT)
#include "antRulerOptionsPage.h"
#include "antConfig.h"
#include "laySnap.h"
#include "layConverters.h"
#include "layDispatcher.h"
#include "tlString.h"
#include "tlClassRegistry.h"
#include "ui_RulerOptions.h"
namespace ant
{
// ------------------------------------------------------------------
// RulerOptionsPage implementation
RulerOptionsPage::RulerOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher)
: lay::EditorOptionsPageWidget (view, dispatcher)
{
mp_ui = new Ui::RulerOptions ();
mp_ui->setupUi (this);
connect (mp_ui->angle_cb, SIGNAL (activated (int)), this, SLOT (edited ()));
connect (mp_ui->snap_to_grid_cbx, SIGNAL (clicked ()), this, SLOT (edited ()));
connect (mp_ui->snap_to_objects_cbx, SIGNAL (clicked ()), this, SLOT (edited ()));
}
RulerOptionsPage::~RulerOptionsPage ()
{
delete mp_ui;
mp_ui = 0;
}
std::string
RulerOptionsPage::title () const
{
return tl::to_string (QObject::tr ("Ruler Options"));
}
// Must match order in angle_cb
static std::vector<lay::angle_constraint_type> s_ac_options =
{
lay::AC_Any,
lay::AC_Diagonal,
lay::AC_DiagonalOnly,
lay::AC_Ortho,
lay::AC_Horizontal,
lay::AC_Vertical
};
void
RulerOptionsPage::apply (lay::Dispatcher *root)
{
lay::angle_constraint_type ac = lay::AC_Any;
int ai = mp_ui->angle_cb->currentIndex ();
if (ai >= 0 && ai < int (s_ac_options.size ())) {
ac = s_ac_options [ai];
}
lay::ACConverter acc;
root->config_set (cfg_ruler_snap_mode, acc.to_string (ac));
root->config_set (cfg_ruler_obj_snap, tl::to_string (mp_ui->snap_to_objects_cbx->isChecked ()));
root->config_set (cfg_ruler_grid_snap, tl::to_string (mp_ui->snap_to_grid_cbx->isChecked ()));
}
void
RulerOptionsPage::setup (lay::Dispatcher *root)
{
lay::ACConverter acc;
lay::angle_constraint_type ac;
ac = lay::AC_Any;
root->config_get (cfg_ruler_snap_mode, ac, acc);
for (int ai = 0; ai < int (s_ac_options.size ()); ++ai) {
if (s_ac_options [ai] == ac) {
mp_ui->angle_cb->setCurrentIndex (ai);
}
}
bool snap_to_grid = false;
root->config_get (cfg_ruler_grid_snap, snap_to_grid);
mp_ui->snap_to_grid_cbx->setChecked (snap_to_grid);
bool snap_to_objects = false;
root->config_get (cfg_ruler_obj_snap, snap_to_objects);
mp_ui->snap_to_objects_cbx->setChecked (snap_to_objects);
}
static tl::RegisteredClass<lay::EditorOptionsPageFactoryBase> s_factory_ruler_options (new lay::EditorOptionsPageFactory<RulerOptionsPage> ("ant::RulerOptions"), 0);
}
#endif

View File

@ -0,0 +1,66 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if defined(HAVE_QT)
#ifndef HDR_antRulerOptionsPage
#define HDR_antRulerOptionsPage
#include "antCommon.h"
#include "layEditorOptionsPageWidget.h"
namespace Ui
{
class RulerOptions;
}
namespace ant
{
/**
* @brief The generic properties page
*/
class RulerOptionsPage
: public lay::EditorOptionsPageWidget
{
Q_OBJECT
public:
RulerOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher);
~RulerOptionsPage ();
virtual std::string title () const;
virtual int order () const { return -10; }
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
private:
Ui::RulerOptions *mp_ui;
};
}
#endif
#endif

View File

@ -1541,6 +1541,19 @@ Service::begin_move (lay::Editable::MoveMode mode, const db::DPoint &p, lay::ang
}
}
static int snap_prio (lay::PointSnapToObjectResult::ObjectSnap os)
{
if (os == lay::PointSnapToObjectResult::ObjectVertex) {
return 3;
} else if (os == lay::PointSnapToObjectResult::ObjectEdge) {
return 2;
} else if (os == lay::PointSnapToObjectResult::ObjectUnspecific) {
return 1;
} else {
return 0;
}
}
void
Service::snap_rulers (lay::angle_constraint_type ac)
{
@ -1566,7 +1579,7 @@ Service::snap_rulers (lay::angle_constraint_type ac)
auto snp = snap2_details (org1, p1, ruler, ac);
double dist = p1.distance (snp.snapped_point);
if (min_dist < 0 || dist < min_dist) {
if (min_dist < 0 || snap_prio (snp.object_snap) > snap_prio (min_snp.object_snap) || (snap_prio (snp.object_snap) == snap_prio (min_snp.object_snap) && dist < min_dist)) {
min_snp = snp;
min_dist = dist;
min_delta = snp.snapped_point - p1;
@ -1575,7 +1588,7 @@ Service::snap_rulers (lay::angle_constraint_type ac)
snp = snap2_details (org2, p2, ruler, ac);
dist = p2.distance (snp.snapped_point);
if (min_dist < 0 || dist < min_dist) {
if (min_dist < 0 || snap_prio (snp.object_snap) > snap_prio (min_snp.object_snap) || (snap_prio (snp.object_snap) == snap_prio (min_snp.object_snap) && dist < min_dist)) {
min_snp = snp;
min_dist = dist;
min_delta = snp.snapped_point - p2;
@ -1597,7 +1610,7 @@ Service::move_transform (const db::DPoint & /*p*/, db::DFTrans tr, lay::angle_co
return;
}
auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac;
auto ac_eff = ac == lay::AC_Global ? lay::AC_Any : ac;
clear_mouse_cursors ();
if (m_move_mode == MoveSelected) {
@ -1621,20 +1634,21 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac)
return;
}
auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac;
clear_mouse_cursors ();
if (m_move_mode == MoveSelected) {
auto ac_eff = ac == lay::AC_Global ? lay::AC_Any : ac;
db::DVector dp = p - m_p1;
dp = lay::snap_angle (dp, ac_eff);
m_trans = db::DTrans (dp + (m_p1 - db::DPoint ()) - m_trans.disp ()) * m_trans * db::DTrans (db::DPoint () - m_p1);
propose_move_transformation (m_trans, 1);
snap_rulers (ac_eff);
propose_move_transformation (m_trans, 1);
for (std::vector<ant::View *>::iterator r = m_rulers.begin (); r != m_rulers.end (); ++r) {
(*r)->transform_by (db::DCplxTrans (m_trans));
}
@ -1643,7 +1657,28 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac)
} else if (m_move_mode != MoveNone) {
db::DPoint ps = snap2_visual (m_p1, p, &m_current, ac);
db::DPoint ps;
if (m_move_mode == MoveP1 && m_current.segments () == 1) {
// when moving p1 in a normal ruler, observe the currently active angle constraints and use p2 as reference
db::DPoint pref = m_current.seg_p2 (m_seg_index);
ps = snap2_visual (pref, p, &m_current, ac);
} else if (m_move_mode == MoveP2 && m_current.segments () == 1) {
// when moving p2 in a normal ruler, observe the currently active angle constraints and use p1 as reference
db::DPoint pref = m_current.seg_p1 (m_seg_index);
ps = snap2_visual (pref, p, &m_current, ac);
} else {
// other move modes use the angle constraint imposed by the modifier buttons
// and the start point for reference
ps = snap2_visual (m_p1, p, &m_current, ac == lay::AC_Global ? lay::AC_Any : ac);
}
m_trans = db::DTrans (ps - m_p1);
apply_partial_move (ps);
@ -1834,6 +1869,7 @@ Service::edit_cancel ()
m_move_mode = MoveNone;
m_selected.clear ();
selection_to_view ();
clear_mouse_cursors ();
}
}

View File

@ -41,7 +41,9 @@ INCLUDEPATH += $$RBA_INC
DEPENDPATH += $$RBA_INC
equals(HAVE_RUBY, "1") {
LIBS += -lklayout_rba
INCLUDEPATH += $$DRC_INC $$LVS_INC
DEPENDPATH += $$DRC_INC $$LVS_INC
LIBS += -lklayout_rba -lklayout_drc -lklayout_lvs
} else {
LIBS += -lklayout_rbastub
}

View File

@ -153,10 +153,10 @@ void clip (ClipData &data)
// write the layout
db::SaveLayoutOptions save_options;
save_options.set_format_from_filename (data.file_out);
std::string of = save_options.set_format_from_filename (data.file_out).second;
data.writer_options.configure (save_options, target_layout);
tl::OutputStream stream (data.file_out);
tl::OutputStream stream (of);
db::Writer writer (save_options);
writer.write (target_layout, stream);
}
@ -176,7 +176,9 @@ BD_PUBLIC int strmclip (int argc, char *argv[])
<< tl::arg ("output", &data.file_out, "The output file",
"The output format is determined from the suffix of the file. If the suffix indicates "
"gzip compression, the file will be compressed on output. Examples for recognized suffixes are "
"\".oas\", \".gds.gz\", \".dxf\" or \".gds2\"."
"\".oas\", \".gds.gz\", \".dxf\" or \".gds2\". You can also use any name, and specify the "
"desired suffix in square brackets after the file name. For example, 'file.clip[oas]' will "
"create an OASIS file called 'file.clip'."
)
<< tl::arg ("-l|--clip-layer=spec", &data, &ClipData::set_clip_layer, "Specifies a layer to take the clip regions from",
"If this option is given, the clip rectangles are taken from the given layer."

View File

@ -33,11 +33,16 @@
#include "pya.h"
#include "gsi.h"
#include "gsiExpression.h"
#include "lymMacro.h"
#include "lymMacroCollection.h"
#include "libForceLink.h"
#include "rdbForceLink.h"
#include "pexForceLink.h"
#include "lymMacro.h"
#include "lymMacroCollection.h"
#if defined(HAVE_RUBY)
# include "drcForceLink.h"
# include "lvsForceLink.h"
#endif
struct RunnerData
{

View File

@ -408,7 +408,9 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
<< tl::arg ("?output", &output, "The output file to which the XOR differences are written",
"This argument is optional. If not given, the exit status alone will indicate whether the layouts "
"are identical or not. The output is a layout file. The format of the file is derived "
"from the file name's suffix (.oas[.gz] for (gzipped) OASIS, .gds[.gz] for (gzipped) GDS2 etc.)."
"from the file name's suffix (.oas for OASIS, .gds[.gz] for (gzipped) GDS2 etc.). "
"You can also use any name, and specify the desired suffix in square brackets after the file name. "
"For example, 'file.xor[oas]' will create an OASIS file called 'file.xor'."
)
<< tl::arg ("-ta|--top-a=name", &top_a, "Specifies the top cell for the first layout",
"Use this option to take a specific cell as the top cell from the first layout. All "
@ -587,10 +589,10 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
if (output_layout.get ()) {
db::SaveLayoutOptions save_options;
save_options.set_format_from_filename (output);
std::string of = save_options.set_format_from_filename (output).second;
writer_options.configure (save_options, *output_layout);
tl::OutputStream stream (output);
tl::OutputStream stream (of);
db::Writer writer (save_options);
writer.write (*output_layout, stream);

View File

@ -25,7 +25,7 @@ INCLUDEPATH += $$RBA_INC
DEPENDPATH += $$RBA_INC
equals(HAVE_RUBY, "1") {
LIBS += -lklayout_rba
LIBS += -lklayout_rba -lklayout_drc -lklayout_lvs
} else {
LIBS += -lklayout_rbastub
}

View File

@ -212,6 +212,34 @@ TEST(7)
db::compare_layouts (this, layout, input, db::NoNormalization);
}
// Small DEF example with explicit format
TEST(8)
{
std::string input = tl::testdata ();
input += "/lefdef/strm2oas_small/in.defok[def]";
std::string input_au = tl::testdata ();
input_au += "/lefdef/strm2oas_small/au.gds";
std::string output = this->tmp_file ("small_def");
const char *argv[] = { "x", input.c_str (), output.c_str () };
EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::LoadLayoutOptions options;
db::Reader reader (stream);
reader.read (layout, options);
EXPECT_EQ (reader.format (), "OASIS");
}
db::compare_layouts (this, layout, input_au, db::WriteGDS2);
}
// Large LEF/DEF to OAS converter test
TEST(10)
{

View File

@ -97,6 +97,34 @@ TEST(2)
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
EXPECT_EQ (reader.format (), "GDS2");
}
db::compare_layouts (this, layout, au, db::NoNormalization);
}
// with explicit output format
TEST(3)
{
std::string input = tl::testdata ();
input += "/bd/strm2clip_in.gds";
std::string au = tl::testdata ();
au += "/bd/strm2clip_au2.gds";
std::string output = this->tmp_file () + "[oas]";
const char *argv[] = { "x", input.c_str (), output.c_str (), "-r=0,-2,9,5", "-t", "INV2", "-x=CLIP_OUT" };
EXPECT_EQ (strmclip (sizeof (argv) / sizeof (argv[0]), (char **) argv), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
EXPECT_EQ (reader.format (), "OASIS");
}
db::compare_layouts (this, layout, au, db::NoNormalization);

View File

@ -28,10 +28,7 @@
TEST(1)
{
#if defined(HAVE_PYTHON)
std::string fp (tl::testsrc ());
fp += "/testdata/bd/strmrun.py";
std::string cmd;
std::string cmd_call;
#if defined(__APPLE__)
// NOTE: because of system integrity, MacOS does not inherit DYLD_LIBRARY_PATH to child
@ -39,19 +36,42 @@ TEST(1)
const char *ldpath_name = "DYLD_LIBRARY_PATH";
const char *ldpath = getenv (ldpath_name);
if (ldpath) {
cmd += std::string (ldpath_name) + "=\"" + ldpath + "\"; export " + ldpath_name + "; ";
cmd_call += std::string (ldpath_name) + "=\"" + ldpath + "\"; export " + ldpath_name + "; ";
}
#endif
cmd += tl::combine_path (tl::get_inst_path (), "strmrun ") + fp;
tl::info << cmd;
cmd_call += tl::combine_path (tl::get_inst_path (), "strmrun");
tl::InputPipe pipe (cmd);
tl::InputStream is (pipe);
std::string data = is.read_all ();
tl::info << data;
{
std::string fp (tl::testsrc ());
fp += "/testdata/bd/strmrun.py";
std::string cmd = cmd_call + " " + fp;
tl::info << cmd;
tl::InputPipe pipe (cmd);
tl::InputStream is (pipe);
std::string data = is.read_all ();
tl::info << data;
EXPECT_EQ (data, "Hello, world (0,-42;42,0)!\n");
}
{
std::string fp (tl::testsrc ());
fp += "/testdata/bd/strmrun.drc";
std::string cmd = cmd_call + " " + fp;
tl::info << cmd;
tl::InputPipe pipe (cmd);
tl::InputStream is (pipe);
std::string data = is.read_all ();
tl::info << data;
EXPECT_EQ (data, "This is DRC.\n");
}
EXPECT_EQ (data, "Hello, world (0,-42;42,0)!\n");
#endif
}

View File

@ -97,6 +97,52 @@ TEST(1A_Flat)
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
EXPECT_EQ (std::string (reader.format ()), "OASIS");
}
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n"
"\n"
" Layer Output Differences (shape count)\n"
" ----------------------------------------------------------------\n"
" 3/0 3/0 30\n"
" 6/0 6/0 41\n"
" 8/1 8/1 1\n"
" 10/0 - (no such layer in first layout)\n"
"\n"
);
}
TEST(1A_FlatWithExplicitOutputFormat)
{
tl::CaptureChannel cap;
std::string input_a = tl::testdata ();
input_a += "/bd/strmxor_in1.gds";
std::string input_b = tl::testdata ();
input_b += "/bd/strmxor_in2.gds";
std::string au = tl::testdata ();
au += "/bd/strmxor_au1.oas";
std::string output = this->tmp_file ("tmp.xxx[oas]");
const char *argv[] = { "x", input_a.c_str (), input_b.c_str (), output.c_str () };
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
EXPECT_EQ (std::string (reader.format ()), "OASIS");
}
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));

View File

@ -289,6 +289,11 @@ module RBA
alias_method :_layout_base, :layout
end
# makes PCellDeclaration's "name" method available
if ! self.method_defined?(:_name_base)
alias_method :_name_base, :name
end
# import the Type... constants from PCellParameterDeclaration
PCellParameterDeclaration.constants.each do |c|
if !const_defined?(c)
@ -595,7 +600,7 @@ module RBA
# default implementation
def cell_name_impl
self.name
_name_base()
end
# default implementation

View File

@ -8,6 +8,7 @@ DEFINES += MAKE_DB_LIBRARY
SOURCES = \
dbArray.cc \
dbBinarySerialize.cc \
dbBox.cc \
dbBoxConvert.cc \
dbBoxScanner.cc \
@ -34,6 +35,7 @@ SOURCES = \
dbEdgeProcessor.cc \
dbEdges.cc \
dbEdgesLocalOperations.cc \
dbFileBasedLibrary.cc \
dbFillTool.cc \
dbFuzzyCellMapping.cc \
dbGenericShapeIterator.cc \
@ -152,6 +154,7 @@ SOURCES = \
gsiDeclDbRecursiveInstanceIterator.cc \
gsiDeclDbRecursiveShapeIterator.cc \
gsiDeclDbRegion.cc \
gsiDeclDbSaveLayoutOptions.cc \
gsiDeclDbShape.cc \
gsiDeclDbShapeProcessor.cc \
gsiDeclDbShapes.cc \
@ -246,6 +249,7 @@ SOURCES = \
HEADERS = \
dbArray.h \
dbBinarySerialize.h \
dbBoxConvert.h \
dbBox.h \
dbBoxScanner.h \
@ -274,6 +278,7 @@ HEADERS = \
dbEdges.h \
dbEdgesLocalOperations.h \
dbEdgesToContours.h \
dbFileBasedLibrary.h \
dbFillTool.h \
dbFuzzyCellMapping.h \
dbGenericShapeIterator.h \

View File

@ -631,14 +631,14 @@ namespace {
class OutputPairHolder
{
public:
OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics)
OutputPairHolder (InteractingOutputMode output_mode, bool merged_semantics, bool min_coherence)
{
if (output_mode == None) {
return;
}
if (output_mode == Positive || output_mode == Negative || output_mode == PositiveAndNegative) {
m_positive.reset (new FlatRegion (merged_semantics));
m_positive.reset (new FlatRegion (merged_semantics, 0.0, 0, min_coherence));
m_results.push_back (& m_positive->raw_polygons ());
} else {
m_results.push_back ((db::Shapes *) 0);
@ -667,7 +667,7 @@ private:
std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode output_mode) const
{
OutputPairHolder oph (output_mode, merged_semantics ());
OutputPairHolder oph (output_mode, merged_semantics (), min_coherence ());
if (output_mode == None) {
return oph.region_pair ();
@ -717,7 +717,7 @@ AsIfFlatRegion::in_and_out_generic (const Region &other, InteractingOutputMode o
std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const
{
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ());
if (output_mode == None) {
return oph.region_pair ();
@ -756,7 +756,7 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOut
std::vector<generic_shape_iterator<db::Edge> > others;
others.push_back (counting ? other.begin_merged () : other.begin ());
std::unique_ptr<FlatRegion> output (new FlatRegion (merged_semantics ()));
std::unique_ptr<FlatRegion> output (new FlatRegion (merged_semantics (), 0.0, 0, min_coherence ()));
std::vector<db::Shapes *> results;
results.push_back (&output->raw_polygons ());
@ -768,7 +768,7 @@ AsIfFlatRegion::selected_interacting_generic (const Edges &other, InteractingOut
std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const
{
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ());
if (output_mode == None) {
return oph.region_pair ();
@ -814,7 +814,7 @@ AsIfFlatRegion::selected_interacting_generic (const Texts &other, InteractingOut
std::pair<RegionDelegate *, RegionDelegate *>
AsIfFlatRegion::selected_interacting_generic (const Region &other, int mode, bool touching, InteractingOutputMode output_mode, size_t min_count, size_t max_count) const
{
OutputPairHolder oph (output_mode, merged_semantics () || is_merged ());
OutputPairHolder oph (output_mode, merged_semantics () || is_merged (), min_coherence ());
if (output_mode == None) {
return oph.region_pair ();
@ -941,7 +941,7 @@ AsIfFlatRegion::pull_generic (const Region &other, int mode, bool touching) cons
std::vector<generic_shape_iterator<db::Polygon> > others;
others.push_back (other.begin_merged ());
std::unique_ptr<FlatRegion> output (new FlatRegion (other.merged_semantics () || other.is_merged ()));
std::unique_ptr<FlatRegion> output (new FlatRegion (other.merged_semantics () || other.is_merged (), 0.0, 0, min_coherence ()));
std::vector<db::Shapes *> results;
results.push_back (&output->raw_polygons ());
@ -1106,7 +1106,7 @@ AsIfFlatRegion::scaled_and_snapped (db::Coord gx, db::Coord mx, db::Coord dx, db
throw tl::Exception (tl::to_string (tr ("Scale and snap requires positive and non-null magnification or divisor values")));
}
std::unique_ptr<FlatRegion> new_region (new FlatRegion (merged_semantics ()));
std::unique_ptr<FlatRegion> new_region (new FlatRegion (merged_semantics (), 0.0, 0, min_coherence ()));
gx = std::max (db::Coord (1), gx);
gy = std::max (db::Coord (1), gy);
@ -1184,13 +1184,7 @@ AsIfFlatRegion::width_check (db::Coord d, const RegionCheckOptions &options) con
EdgePairsDelegate *
AsIfFlatRegion::space_or_isolated_check (db::Coord d, const RegionCheckOptions &options, bool isolated) const
{
if (options.opposite_filter != NoOppositeFilter || options.rect_filter != NoRectFilter || options.shielded) {
// NOTE: we have to use the "foreign" scheme with a filter because only this scheme
// guarantees that all subject shapes are visited.
return run_check (db::SpaceRelation, isolated, foreign_regionptr (), d, options);
} else {
return run_check (db::SpaceRelation, isolated, subject_regionptr (), d, options);
}
return run_check (db::SpaceRelation, isolated, 0, d, options);
}
EdgePairsDelegate *
@ -1239,28 +1233,43 @@ EdgePairsDelegate *
AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const
{
// force different polygons in the different properties case to skip intra-polygon checks
if (pc_always_different (options.prop_constraint)) {
if (! other && pc_always_different (options.prop_constraint)) {
different_polygons = true;
}
bool needs_merged_primary = different_polygons || options.needs_merged ();
bool needs_merged_primary = (! other && different_polygons) || options.needs_merged ();
bool primary_is_merged = is_merged ();
db::RegionIterator polygons;
db::RegionIterator polygons (needs_merged_primary ? begin_merged () : begin ());
bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged ();
if (! merged_semantics ()) {
primary_is_merged = true; // means: don't merge again
needs_merged_primary = false;
polygons = begin ();
} else if (! needs_merged_primary) {
// The implementation may run faster if the primary is not merged
primary_is_merged = false;
polygons = begin_unmerged ();
} else {
primary_is_merged = true;
polygons = begin_merged ();
}
EdgeRelationFilter check (rel, d, options);
std::vector<db::RegionIterator> others;
std::vector<bool> foreign;
bool has_other = false;
bool other_is_merged = true;
if (other == subject_regionptr () || other == foreign_regionptr ()) {
foreign.push_back (other == foreign_regionptr ());
if (! other) {
foreign.push_back (true);
others.push_back (polygons);
other_is_merged = primary_is_merged;
} else {
foreign.push_back (false);
if (! other->merged_semantics ()) {
others.push_back (other->begin ());
other_is_merged = true;
@ -1272,7 +1281,13 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
others.push_back (other->begin ());
other_is_merged = other->is_merged ();
}
has_other = true;
// adds another intruder section to implement subject merging ("primary_intruders")
if (! primary_is_merged) {
foreign.push_back (true);
others.push_back (polygons);
}
}
std::unique_ptr<FlatEdgePairs> output (new FlatEdgePairs ());
@ -1282,7 +1297,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
if (pc_skip (options.prop_constraint)) {
db::check_local_operation<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options);
db::check_local_operation<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, other != 0, other_is_merged, options);
db::local_processor<db::Polygon, db::Polygon, db::EdgePair> proc;
proc.set_base_verbosity (base_verbosity ());
@ -1293,7 +1308,7 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
} else {
db::check_local_operation_with_properties<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, has_other, other_is_merged, options);
db::check_local_operation_with_properties<db::Polygon, db::Polygon> op (check, different_polygons, primary_is_merged, other != 0, other_is_merged, options);
db::local_processor<db::PolygonWithProperties, db::PolygonWithProperties, db::EdgePairWithProperties> proc;
proc.set_base_verbosity (base_verbosity ());
@ -1334,7 +1349,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord
}
RegionDelegate *
AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const
AsIfFlatRegion::merged (bool min_coh, unsigned int min_wc, bool join_properties_on_merge) const
{
if (empty ()) {
@ -1351,8 +1366,8 @@ AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_prope
} else {
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true));
merge_polygons_to (new_region->raw_polygons (), min_coherence, min_wc, join_properties_on_merge);
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0, 0, min_coh));
merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, join_properties_on_merge);
return new_region.release ();
@ -1695,7 +1710,7 @@ AsIfFlatRegion::and_or_not_with (bool is_and, const Region &other, PropertyConst
ep.insert (*p, n);
}
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true));
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op (is_and ? db::BooleanOp::And : db::BooleanOp::ANotB);
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
@ -1772,12 +1787,12 @@ AsIfFlatRegion::andnot_with (const Region &other, PropertyConstraint property_co
ep.insert (*p, n);
}
std::unique_ptr<FlatRegion> new_region1 (new FlatRegion (true));
std::unique_ptr<FlatRegion> new_region1 (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op1 (db::BooleanOp::And);
db::ShapeGenerator pc1 (new_region1->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg1 (pc1, false /*don't resolve holes*/, min_coherence ());
std::unique_ptr<FlatRegion> new_region2 (new FlatRegion (true));
std::unique_ptr<FlatRegion> new_region2 (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op2 (db::BooleanOp::ANotB);
db::ShapeGenerator pc2 (new_region2->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg2 (pc2, false /*don't resolve holes*/, min_coherence ());
@ -1860,7 +1875,7 @@ AsIfFlatRegion::xor_with (const Region &other, PropertyConstraint prop_constrain
ep.insert (*p, n);
}
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true));
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op (db::BooleanOp::Xor);
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());
@ -1916,7 +1931,7 @@ AsIfFlatRegion::or_with (const Region &other, PropertyConstraint /*prop_constrai
ep.insert (*p, n);
}
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true));
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0.0, 0, min_coherence ()));
db::BooleanOp op (db::BooleanOp::Or);
db::ShapeGenerator pc (new_region->raw_polygons (), true /*clear*/);
db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence ());

View File

@ -138,6 +138,11 @@ public:
virtual RegionDelegate *add (const Region &other) const;
virtual RegionDelegate *peel (double /*complexity_factor*/) const
{
return const_cast<AsIfFlatRegion *> (this);
}
virtual RegionDelegate *selected_outside (const Region &other) const
{
return selected_interacting_generic (other, 1, false, Positive, size_t (1), std::numeric_limits<size_t>::max ()).first;

View File

@ -0,0 +1,31 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbBinarySerialize.h"
namespace db
{
// .. nothing yet ..
}

View File

@ -0,0 +1,623 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_dbBinarySerialize
#define HDR_dbBinarySerialize
#include "dbCommon.h"
#include "dbPoint.h"
#include "dbVector.h"
#include "dbEdge.h"
#include "dbEdgePair.h"
#include "dbPolygon.h"
#include "dbBox.h"
#include "dbPath.h"
#include "dbTrans.h"
#include "dbText.h"
#include "dbObjectWithProperties.h"
#include "tlBinaryStream.h"
#include "tlException.h"
namespace db
{
// -----------------------------------------------------------------------------------------
// Stream inserters
template<class C>
tl::BinaryOutputStream &write_coord (tl::BinaryOutputStream &s, C c)
{
return s << c;
}
inline tl::BinaryOutputStream &write_coord (tl::BinaryOutputStream &s, db::Coord c)
{
// NOTE: for compatibility across different builds we use 64 bit coordinates always
return s << (int64_t) c;
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::point<C> &pt)
{
return write_coord (write_coord (s, pt.x ()), pt.y ());
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::vector<C> &v)
{
return write_coord (write_coord (s, v.x ()), v.y ());
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::point<C> &pt)
{
s << (uint16_t) 1; // version
return write_binary (s, pt);
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::vector<C> &v)
{
s << (uint16_t) 1; // version
return write_binary (s, v);
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::edge<C> &e)
{
write_binary (s, e.p1 ());
write_binary (s, e.p2 ());
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::edge<C> &e)
{
s << (uint16_t) 1; // version
return write_binary (s, e);
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::box<C> &b)
{
s << (uint16_t) 1; // version
write_binary (s, b.p1 ());
write_binary (s, b.p2 ());
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::edge_pair<C> &ep)
{
s << (uint16_t) 1; // version
write_binary (s, ep.first ());
write_binary (s, ep.second ());
return s;
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::polygon_contour<C> &c)
{
s << (uint64_t) c.size ();
for (auto i = c.begin (); i != c.end (); ++i) {
write_binary (s, *i);
}
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::polygon<C> &p)
{
s << (uint16_t) 1; // version
s << (uint64_t) (p.holes () + 1);
for (size_t h = 0; h < p.holes () + 1; ++h) {
write_binary (s, p.contour (h));
}
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::simple_polygon<C> &p)
{
// NOTE: the format of polygon and simple polygon are compatible
s << (uint16_t) 1; // version
s << (uint64_t) 1; // number of contours
write_binary (s, p.hull ());
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::unit_trans<C> &)
{
s << (uint16_t) 1; // version
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::disp_trans<C> &t)
{
s << (uint16_t) 1; // version
write_binary (s, t.disp ());
return s;
}
template<class C>
tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::simple_trans<C> &t)
{
s << (uint16_t) t.rot ();
write_binary (s, t.disp ());
return s;
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::simple_trans<C> &t)
{
s << (uint16_t) 1; // version
return write_binary (s, t);
}
template<class C, class D, class R>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::complex_trans<C, D, R> &t)
{
s << (uint16_t) 1; // version
write_binary (s, t.disp ());
return s << (double) t.msin () << (double) t.mcos () << (double) (t.is_mirror () ? -t.mag () : t.mag ());
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::text<C> &t)
{
s << (uint16_t) 1; // version
s << t.string ();
write_binary (s, t.trans ());
write_coord (s, t.size ());
return s << (int32_t) t.font () << (int32_t) t.halign () << (int32_t) t.valign ();
}
template<class C>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::path<C> &p)
{
s << (uint16_t) 1; // version
s << (uint64_t) p.points ();
for (auto i = p.begin (); i != p.end (); ++i) {
write_binary (s, *i);
}
write_coord (s, p.width ());
write_coord (s, p.bgn_ext ());
write_coord (s, p.end_ext ());
s << p.round ();
return s;
}
template<class T>
tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::object_with_properties<T> &o)
{
s << (uint16_t) 1; // version
s << (const T &) o;
const auto &ps = db::properties (o.properties_id ());
s << (uint64_t) ps.size ();
for (auto i = ps.begin (); i != ps.end (); ++i) {
s << db::property_name (i->first) << db::property_value (i->second);
}
return s;
}
// -----------------------------------------------------------------------------------------
// Stream extractors
class FutureBinarySerializationFormatException
: public tl::Exception
{
public:
FutureBinarySerializationFormatException ()
: tl::Exception (tl::to_string (tr ("Binary serialization format version is too new for this build")))
{ }
};
template<class C>
tl::BinaryInputStream &read_coord (tl::BinaryInputStream &s, C &c)
{
return s >> c;
}
inline tl::BinaryInputStream &read_coord (tl::BinaryInputStream &s, db::Coord &c)
{
// NOTE: for compatibility across different builds we use 64 bit coordinates always
int64_t cc = 0;
s >> cc;
c = cc;
return s;
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::point<C> &pt)
{
C x = 0, y = 0;
read_coord (s, x);
read_coord (s, y);
pt = db::point<C> (x, y);
return s;
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::vector<C> &v)
{
C x = 0, y = 0;
read_coord (s, x);
read_coord (s, y);
v = db::vector<C> (x, y);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::point<C> &pt)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return read_binary (s, pt);
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::vector<C> &v)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return read_binary (s, v);
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::edge<C> &v)
{
db::point<C> p1, p2;
read_binary (s, p1);
read_binary (s, p2);
v = db::edge<C> (p1, p2);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge<C> &e)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return read_binary (s, e);
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::box<C> &b)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
db::point<C> p1, p2;
read_binary (s, p1);
read_binary (s, p2);
b = db::box<C> (p1, p2);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge_pair<C> &ep)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
db::edge<C> e1, e2;
read_binary (s, e1);
read_binary (s, e2);
ep = db::edge_pair<C> (e1, e2);
return s;
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::polygon_contour<C> &c, bool hole)
{
uint64_t n = 0;
s >> n;
std::vector<db::point<C> > pts;
pts.reserve (n);
while (n-- > 0) {
pts.push_back (db::point<C> ());
read_binary (s, pts.back ());
}
// NOTE: not normalization or modification is applied
c.assign (pts.begin (), pts.end (), hole, false, false, false);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::polygon<C> &p)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
uint64_t n = 0;
s >> n;
p = db::polygon<C> ();
if (n > 0) {
p.reserve_holes (n - 1);
}
db::polygon_contour<C> ctr;
for (uint64_t h = 0; h < n; ++h) {
read_binary (s, ctr, h > 0);
if (h == 0) {
p.assign_hull (ctr);
} else {
p.insert_hole (ctr);
}
}
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_polygon<C> &p)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
uint64_t n = 0;
s >> n;
tl_assert (n == (uint64_t) 1);
db::polygon_contour<C> ctr;
read_binary (s, ctr, false);
p.assign_hull (ctr);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::unit_trans<C> &)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::disp_trans<C> &t)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
db::vector<C> d;
read_binary (s, d);
t = db::disp_trans<C> (d);
return s;
}
template<class C>
tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::simple_trans<C> &t)
{
uint16_t r = 0;
s >> r;
db::vector<C> d;
read_binary (s, d);
t = db::simple_trans<C> (r, d);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_trans<C> &t)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
return read_binary (s, t);
}
template<class C, class D, class R>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::complex_trans<C, D, R> &t)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
db::vector<R> d;
read_binary (s, d);
double asin, acos, mag;
s >> asin >> acos >> mag;
t = db::complex_trans<C, D, R> (d, asin, acos, mag);
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::text<C> &t)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
std::string txt;
s >> txt;
db::simple_trans<C> tr;
read_binary (s, tr);
C size;
read_coord (s, size);
int32_t font, halign, valign;
s >> font >> halign >> valign;
t = db::text<C> (txt, tr, size, db::Font (font), db::HAlign (halign), db::VAlign (valign));
return s;
}
template<class C>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::path<C> &p)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
uint64_t n = 0;
s >> n;
std::vector<db::point<C> > pts;
pts.reserve (n);
while (n-- > 0) {
pts.push_back (db::point<C> ());
read_binary (s, pts.back ());
}
C width, bgn_ext, end_ext;
read_coord (s, width);
read_coord (s, bgn_ext);
read_coord (s, end_ext);
bool round;
s >> round;
p = db::path<C> (pts.begin (), pts.end (), width, bgn_ext, end_ext, round);
return s;
}
template<class T>
tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::object_with_properties<T> &o)
{
uint16_t fmt = 0;
s >> fmt;
if (fmt > 1) {
throw FutureBinarySerializationFormatException ();
}
s >> (T &) o;
uint64_t n = 0;
s >> n;
db::PropertiesSet ps;
while (n-- > 0) {
tl::Variant name, value;
s >> name >> value;
ps.insert (name, value);
}
o.properties_id (db::properties_id (ps));
return s;
}
// -----------------------------------------------------------------------------------------
// Converters of objects to a binary string
template<class T>
std::vector<char> to_bytes (const T &t)
{
tl::OutputMemoryStream osm;
{
tl::BinaryOutputStream os (osm);
os << t;
}
return std::vector<char> (osm.data (), osm.data () + osm.size ());
}
template<class T>
std::string to_bytes_str (const T &t)
{
tl::OutputMemoryStream osm;
{
tl::BinaryOutputStream os (osm);
os << t;
}
return std::string (osm.data (), osm.size ());
}
template<class T>
void from_bytes (const char *data, size_t n, T &t)
{
tl::InputMemoryStream ism (data, n);
tl::InputStream is (ism);
tl::BinaryInputStream bis (is);
bis >> t;
}
template<class T>
void from_bytes (const std::vector<char> &s, T &t)
{
tl::InputMemoryStream ism (s.begin ().operator-> (), s.size ());
tl::InputStream is (ism);
tl::BinaryInputStream bis (is);
bis >> t;
}
}
#endif

View File

@ -42,15 +42,15 @@ namespace db
/**
* @brief A utility class for the box scanner implementation
*/
template <class BoxConvert, class Obj, class Prop, class SideOp>
template <class BoxConvertAdaptor, class Obj, class Prop, class SideOp>
struct bs_side_compare_func
#if __cplusplus < 201703L
: std::binary_function<std::pair<const Obj *, Prop>, std::pair<const Obj *, Prop>, bool>
#endif
{
typedef typename BoxConvert::box_type box_type;
typedef typename BoxConvertAdaptor::box_type box_type;
bs_side_compare_func (const BoxConvert &bc)
bs_side_compare_func (const BoxConvertAdaptor &bc)
: m_bc (bc)
{
// .. nothing yet ..
@ -59,26 +59,26 @@ struct bs_side_compare_func
bool operator() (const std::pair<const Obj *, Prop> &a, const std::pair<const Obj *, Prop> &b) const
{
SideOp sideop;
return sideop (m_bc (*a.first)) < sideop (m_bc (*b.first));
return sideop (m_bc (a)) < sideop (m_bc (b));
}
private:
BoxConvert m_bc;
BoxConvertAdaptor m_bc;
};
/**
* @brief A utility class for the box scanner implementation
*/
template <class BoxConvert, class Obj, class Prop, class SideOp>
template <class BoxConvertAdaptor, class Obj, class Prop, class SideOp>
struct bs_side_compare_vs_const_func
#if __cplusplus < 201703L
: std::unary_function<std::pair<const Obj *, Prop>, bool>
#endif
{
typedef typename BoxConvert::box_type box_type;
typedef typename BoxConvertAdaptor::box_type box_type;
typedef typename box_type::coord_type coord_type;
bs_side_compare_vs_const_func (const BoxConvert &bc, coord_type c)
bs_side_compare_vs_const_func (const BoxConvertAdaptor &bc, coord_type c)
: m_bc (bc), m_c (c)
{
// .. nothing yet ..
@ -87,11 +87,11 @@ struct bs_side_compare_vs_const_func
bool operator() (const std::pair<const Obj *, Prop> &a) const
{
SideOp sideop;
return sideop (m_bc (*a.first)) < m_c;
return sideop (m_bc (a)) < m_c;
}
private:
BoxConvert m_bc;
BoxConvertAdaptor m_bc;
coord_type m_c;
};
@ -186,6 +186,34 @@ public:
typedef std::vector<std::pair<const Obj *, Prop> > container_type;
typedef typename container_type::iterator iterator_type;
template <class BoxConvert>
struct box_convert_adaptor_take_first
{
typedef typename BoxConvert::box_type box_type;
box_convert_adaptor_take_first (const BoxConvert &bc)
: m_bc (bc)
{ }
box_type operator() (const std::pair<const Obj *, Prop> &p) const
{
return m_bc (*p.first);
}
private:
const BoxConvert &m_bc;
};
struct BoxConvertAdaptorTakeSecond
{
typedef Prop box_type;
const box_type &operator() (const std::pair<const Obj *, Prop> &p) const
{
return p.second;
}
};
/**
* @brief Default ctor
*/
@ -288,7 +316,22 @@ public:
bool process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ())
{
rec.initialize ();
bool ret = do_process (rec, enl, bc);
bool ret = do_process (rec, enl, box_convert_adaptor_take_first<BoxConvert> (bc));
rec.finalize (ret);
return ret;
}
/**
* @brief Same as "process", but allows specfying a box convert adaptor
*
* This version is useful for use with BoxConvertAdaptorTakeSecond. In that case, "Prop" needs to be
* a box type and is used directly as the bounding box.
*/
template <class Rec, class BoxConvertAdaptor = BoxConvertAdaptorTakeSecond>
bool process_with_adaptor (Rec &rec, typename BoxConvertAdaptor::box_type::coord_type enl, const BoxConvertAdaptor &bca = BoxConvertAdaptor ())
{
rec.initialize ();
bool ret = do_process (rec, enl, bca);
rec.finalize (ret);
return ret;
}
@ -300,21 +343,21 @@ private:
bool m_report_progress;
std::string m_progress_desc;
template <class Rec, class BoxConvert>
bool do_process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ())
template <class Rec, class BoxConvertAdaptor>
bool do_process (Rec &rec, typename BoxConvertAdaptor::box_type::coord_type enl, const BoxConvertAdaptor &bc = BoxConvertAdaptor ())
{
typedef typename BoxConvert::box_type box_type;
typedef typename BoxConvertAdaptor::box_type box_type;
typedef typename box_type::coord_type coord_type;
typedef bs_side_compare_func<BoxConvert, Obj, Prop, box_bottom<Box> > bottom_side_compare_func;
typedef bs_side_compare_func<BoxConvert, Obj, Prop, box_left<Box> > left_side_compare_func;
typedef bs_side_compare_vs_const_func<BoxConvert, Obj, Prop, box_top<Box> > below_func;
typedef bs_side_compare_vs_const_func<BoxConvert, Obj, Prop, box_right<Box> > left_func;
typedef bs_side_compare_func<BoxConvertAdaptor, Obj, Prop, box_bottom<Box> > bottom_side_compare_func;
typedef bs_side_compare_func<BoxConvertAdaptor, Obj, Prop, box_left<Box> > left_side_compare_func;
typedef bs_side_compare_vs_const_func<BoxConvertAdaptor, Obj, Prop, box_top<Box> > below_func;
typedef bs_side_compare_vs_const_func<BoxConvertAdaptor, Obj, Prop, box_right<Box> > left_func;
// sort out the entries with an empty bbox (we must not put that into sort)
typename container_type::iterator wi = m_pp.begin ();
for (typename container_type::iterator ri = m_pp.begin (); ri != m_pp.end (); ++ri) {
if (! bc (*ri->first).empty ()) {
if (! bc (*ri).empty ()) {
if (wi != ri) {
*wi = *ri;
}
@ -334,9 +377,9 @@ private:
// below m_scanner_thr elements use the brute force approach which is faster in that case
for (iterator_type i = m_pp.begin (); i != m_pp.end (); ++i) {
box_type b1 = bc (*i->first);
box_type b1 = bc (*i);
for (iterator_type j = i + 1; j != m_pp.end (); ++j) {
if (bs_boxes_overlap (b1, bc (*j->first), enl)) {
if (bs_boxes_overlap (b1, bc (*j), enl)) {
rec.add (i->first, i->second, j->first, j->second);
if (rec.stop ()) {
return false;
@ -355,7 +398,7 @@ private:
std::sort (m_pp.begin (), m_pp.end (), bottom_side_compare_func (bc));
coord_type y = bc (*m_pp.front ().first).bottom ();
coord_type y = bc (m_pp.front ()).bottom ();
iterator_type current = m_pp.begin ();
iterator_type future = m_pp.begin ();
@ -389,10 +432,10 @@ private:
typename std::iterator_traits<iterator_type>::difference_type min_band_size = size_t ((future - current) * m_fill_factor);
coord_type yy = y;
do {
yy = bc (*future->first).bottom ();
yy = bc (*future).bottom ();
do {
++future;
} while (future != m_pp.end () && bc (*future->first).bottom () == yy);
} while (future != m_pp.end () && bc (*future).bottom () == yy);
} while (future != m_pp.end () && future - current < min_band_size);
std::sort (current, future, left_side_compare_func (bc));
@ -400,7 +443,7 @@ private:
iterator_type c = current;
iterator_type f = current;
coord_type x = bc (*c->first).left ();
coord_type x = bc (*c).left ();
while (f != future) {
@ -412,10 +455,10 @@ private:
typename std::iterator_traits<iterator_type>::difference_type min_box_size = size_t ((f - c) * m_fill_factor);
coord_type xx = x;
do {
xx = bc (*f->first).left ();
xx = bc (*f).left ();
do {
++f;
} while (f != future && bc (*f->first).left () == xx);
} while (f != future && bc (*f).left () == xx);
} while (f != future && f - c < min_box_size);
if (m_report_progress) {
@ -425,9 +468,13 @@ private:
for (iterator_type i = f0; i != f; ++i) {
for (iterator_type j = c; j < i; ++j) {
if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) {
if (seen.find (std::make_pair (i->first, j->first)) == seen.end () && seen.find (std::make_pair (j->first, i->first)) == seen.end ()) {
seen.insert (std::make_pair (i->first, j->first));
if (bs_boxes_overlap (bc (*i), bc (*j), enl)) {
std::pair<const Obj *, const Obj *> k (i->first, j->first);
if (k.first < k.second) {
std::swap (k.first, k.second);
}
if (seen.find (k) == seen.end ()) {
seen.insert (k);
rec.add (i->first, i->second, j->first, j->second);
if (rec.stop ()) {
return false;
@ -534,6 +581,62 @@ public:
typedef typename container_type1::iterator iterator_type1;
typedef typename container_type2::iterator iterator_type2;
template <class BoxConvert>
struct box_convert_adaptor_take_first1
{
typedef typename BoxConvert::box_type box_type;
box_convert_adaptor_take_first1 (const BoxConvert &bc)
: m_bc (bc)
{ }
box_type operator() (const std::pair<const Obj1 *, Prop1> &p) const
{
return m_bc (*p.first);
}
private:
const BoxConvert &m_bc;
};
template <class BoxConvert>
struct box_convert_adaptor_take_first2
{
typedef typename BoxConvert::box_type box_type;
box_convert_adaptor_take_first2 (const BoxConvert &bc)
: m_bc (bc)
{ }
box_type operator() (const std::pair<const Obj2 *, Prop2> &p) const
{
return m_bc (*p.first);
}
private:
const BoxConvert &m_bc;
};
struct BoxConvertAdaptorTakeSecond1
{
typedef Prop1 box_type;
const box_type &operator() (const std::pair<const Obj1 *, Prop1> &p) const
{
return p.second;
}
};
struct BoxConvertAdaptorTakeSecond2
{
typedef Prop2 box_type;
const box_type &operator() (const std::pair<const Obj2 *, Prop2> &p) const
{
return p.second;
}
};
/**
* @brief Default ctor
*/
@ -678,7 +781,22 @@ public:
bool process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ())
{
rec.initialize ();
bool ret = do_process (rec, enl, bc1, bc2);
bool ret = do_process (rec, enl, box_convert_adaptor_take_first1<BoxConvert1> (bc1), box_convert_adaptor_take_first2<BoxConvert2> (bc2));
rec.finalize (ret);
return ret;
}
/**
* @brief Same as "process", but allows specfying a box convert adaptor
*
* This version is useful for use with BoxConvertAdaptorTakeSecond. In that case, "Prop" needs to be
* a box type and is used directly as the bounding box.
*/
template <class Rec, class BoxConvertAdaptor1 = BoxConvertAdaptorTakeSecond1, class BoxConvertAdaptor2 = BoxConvertAdaptorTakeSecond2>
bool process_with_adaptor (Rec &rec, typename BoxConvertAdaptor1::box_type::coord_type enl, const BoxConvertAdaptor1 &bca1 = BoxConvertAdaptor1 (), const BoxConvertAdaptor2 &bca2 = BoxConvertAdaptor2 ())
{
rec.initialize ();
bool ret = do_process (rec, enl, bca1, bca2);
rec.finalize (ret);
return ret;
}
@ -691,25 +809,25 @@ private:
bool m_report_progress;
std::string m_progress_desc;
template <class Rec, class BoxConvert1, class BoxConvert2>
bool do_process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ())
template <class Rec, class BoxConvertAdaptor1, class BoxConvertAdaptor2>
bool do_process (Rec &rec, typename BoxConvertAdaptor1::box_type::coord_type enl, const BoxConvertAdaptor1 &bc1 = BoxConvertAdaptor1 (), const BoxConvertAdaptor2 &bc2 = BoxConvertAdaptor2 ())
{
typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type
typedef typename BoxConvertAdaptor1::box_type box_type; // must be same as BoxConvert2::box_type
typedef typename box_type::coord_type coord_type;
typedef bs_side_compare_func<BoxConvert1, Obj1, Prop1, box_bottom<Box> > bottom_side_compare_func1;
typedef bs_side_compare_func<BoxConvert1, Obj1, Prop1, box_left<Box> > left_side_compare_func1;
typedef bs_side_compare_vs_const_func<BoxConvert1, Obj1, Prop1, box_top<Box> > below_func1;
typedef bs_side_compare_vs_const_func<BoxConvert1, Obj1, Prop1, box_right<Box> > left_func1;
typedef bs_side_compare_func<BoxConvert2, Obj2, Prop2, box_bottom<Box> > bottom_side_compare_func2;
typedef bs_side_compare_func<BoxConvert2, Obj2, Prop2, box_left<Box> > left_side_compare_func2;
typedef bs_side_compare_vs_const_func<BoxConvert2, Obj2, Prop2, box_top<Box> > below_func2;
typedef bs_side_compare_vs_const_func<BoxConvert2, Obj2, Prop2, box_right<Box> > left_func2;
typedef bs_side_compare_func<BoxConvertAdaptor1, Obj1, Prop1, box_bottom<Box> > bottom_side_compare_func1;
typedef bs_side_compare_func<BoxConvertAdaptor1, Obj1, Prop1, box_left<Box> > left_side_compare_func1;
typedef bs_side_compare_vs_const_func<BoxConvertAdaptor1, Obj1, Prop1, box_top<Box> > below_func1;
typedef bs_side_compare_vs_const_func<BoxConvertAdaptor1, Obj1, Prop1, box_right<Box> > left_func1;
typedef bs_side_compare_func<BoxConvertAdaptor2, Obj2, Prop2, box_bottom<Box> > bottom_side_compare_func2;
typedef bs_side_compare_func<BoxConvertAdaptor2, Obj2, Prop2, box_left<Box> > left_side_compare_func2;
typedef bs_side_compare_vs_const_func<BoxConvertAdaptor2, Obj2, Prop2, box_top<Box> > below_func2;
typedef bs_side_compare_vs_const_func<BoxConvertAdaptor2, Obj2, Prop2, box_right<Box> > left_func2;
// sort out the entries with an empty bbox (we must not put that into sort)
typename container_type1::iterator wi1 = m_pp1.begin ();
for (typename container_type1::iterator ri1 = m_pp1.begin (); ri1 != m_pp1.end (); ++ri1) {
if (! bc1 (*ri1->first).empty ()) {
if (! bc1 (*ri1).empty ()) {
if (wi1 != ri1) {
*wi1 = *ri1;
}
@ -726,7 +844,7 @@ private:
typename container_type2::iterator wi2 = m_pp2.begin ();
for (typename container_type2::iterator ri2 = m_pp2.begin (); ri2 != m_pp2.end (); ++ri2) {
if (! bc2 (*ri2->first).empty ()) {
if (! bc2 (*ri2).empty ()) {
if (wi2 != ri2) {
*wi2 = *ri2;
}
@ -757,9 +875,9 @@ private:
// below m_scanner_thr elements use the brute force approach which is faster in that case
for (iterator_type1 i = m_pp1.begin (); i != m_pp1.end (); ++i) {
box_type b1 = bc1 (*i->first);
box_type b1 = bc1 (*i);
for (iterator_type2 j = m_pp2.begin (); j != m_pp2.end (); ++j) {
if (bs_boxes_overlap (b1, bc2 (*j->first), enl)) {
if (bs_boxes_overlap (b1, bc2 (*j), enl)) {
rec.add (i->first, i->second, j->first, j->second);
if (rec.stop ()) {
return false;
@ -783,7 +901,7 @@ private:
std::sort (m_pp1.begin (), m_pp1.end (), bottom_side_compare_func1 (bc1));
std::sort (m_pp2.begin (), m_pp2.end (), bottom_side_compare_func2 (bc2));
coord_type y = std::min (bc1 (*m_pp1.front ().first).bottom (), bc2 (*m_pp2.front ().first).bottom ());
coord_type y = std::min (bc1 (m_pp1.front ()).bottom (), bc2 (m_pp2.front ()).bottom ());
iterator_type1 current1 = m_pp1.begin ();
iterator_type1 future1 = m_pp1.begin ();
@ -833,16 +951,16 @@ private:
coord_type yy = y;
do {
if (future1 != m_pp1.end () && future2 != m_pp2.end ()) {
yy = std::min (bc1 (*future1->first).bottom (), bc2 (*future2->first).bottom ());
yy = std::min (bc1 (*future1).bottom (), bc2 (*future2).bottom ());
} else if (future1 != m_pp1.end ()) {
yy = bc1 (*future1->first).bottom ();
yy = bc1 (*future1).bottom ();
} else {
yy = bc2 (*future2->first).bottom ();
yy = bc2 (*future2).bottom ();
}
while (future1 != m_pp1.end () && bc1 (*future1->first).bottom () == yy) {
while (future1 != m_pp1.end () && bc1 (*future1).bottom () == yy) {
++future1;
}
while (future2 != m_pp2.end () && bc2 (*future2->first).bottom () == yy) {
while (future2 != m_pp2.end () && bc2 (*future2).bottom () == yy) {
++future2;
}
} while ((future1 != m_pp1.end () || future2 != m_pp2.end ()) && size_t (future1 - current1) + size_t (future2 - current2) < min_band_size);
@ -857,7 +975,7 @@ private:
iterator_type2 c2 = current2;
iterator_type2 f2 = current2;
coord_type x = std::min (bc1 (*c1->first).left (), bc2 (*c2->first).left ());
coord_type x = std::min (bc1 (*c1).left (), bc2 (*c2).left ());
while (f1 != future1 || f2 != future2) {
@ -869,16 +987,16 @@ private:
coord_type xx = x;
do {
if (f1 != future1 && f2 != future2) {
xx = std::min (bc1 (*f1->first).left (), bc2 (*f2->first).left ());
xx = std::min (bc1 (*f1).left (), bc2 (*f2).left ());
} else if (f1 != future1) {
xx = bc1 (*f1->first).left ();
xx = bc1 (*f1).left ();
} else if (f2 != future2) {
xx = bc2 (*f2->first).left ();
xx = bc2 (*f2).left ();
}
while (f1 != future1 && bc1 (*f1->first).left () == xx) {
while (f1 != future1 && bc1 (*f1).left () == xx) {
++f1;
}
while (f2 != future2 && bc2 (*f2->first).left () == xx) {
while (f2 != future2 && bc2 (*f2).left () == xx) {
++f2;
}
} while ((f1 != future1 || f2 != future2) && size_t (f1 - c1) + size_t (f2 - c2) < min_box_size);
@ -886,7 +1004,7 @@ private:
if (c1 != f1 && c2 != f2) {
for (iterator_type1 i = c1; i != f1; ++i) {
for (iterator_type2 j = c2; j < f2; ++j) {
if (bs_boxes_overlap (bc1 (*i->first), bc2 (*j->first), enl)) {
if (bs_boxes_overlap (bc1 (*i), bc2 (*j), enl)) {
if (seen1.insert (std::make_pair (i->first, j->first)).second) {
seen2.insert (std::make_pair (j->first, i->first));
rec.add (i->first, i->second, j->first, j->second);

View File

@ -945,7 +945,7 @@ std::string
Cell::get_display_name () const
{
tl_assert (layout () != 0);
if (is_ghost_cell () && empty ()) {
if (is_real_ghost_cell ()) {
return std::string ("(") + layout ()->cell_name (cell_index ()) + std::string (")");
} else {
return layout ()->cell_name (cell_index ());
@ -959,6 +959,20 @@ Cell::set_name (const std::string &name)
layout ()->rename_cell (cell_index (), name.c_str ());
}
void
Cell::set_ghost_cell (bool g)
{
// NOTE: this change is not undo managed
if (m_ghost_cell != g) {
m_ghost_cell = g;
tl_assert (layout () != 0);
// To trigger a redraw and cell tree rebuild
layout ()->cell_name_changed ();
}
}
void
Cell::check_locked () const
{

View File

@ -857,7 +857,7 @@ public:
void check_locked () const;
/**
* @brief Tell, if this cell is a proxy cell
* @brief Gets a value indicating if this cell is a proxy cell
*
* Proxy cells are such whose layout represents a snapshot of another entity.
* Such cells can be PCell variants or library references for example.
@ -867,6 +867,17 @@ public:
return false;
}
/**
* @brief Gets a value indicating that this cell is a replica that can be skipped
*
* This attribute is evaluated by file writers to skip cell replicas for
* library cells that do not want to replicated.
*/
virtual bool can_skip_replica () const
{
return false;
}
/**
* @brief Sets the cell name
*
@ -925,15 +936,26 @@ public:
return m_ghost_cell;
}
/**
* @brief Gets a value indicating whether the cell is a "real" ghost cell
*
* A ghost cell is a real ghost cell only if the ghost cell flag is set
* and the cell is empty. Only in that case for example the cell is written
* to GDS files as a ghost cell.
*
* Otherwise, the ghost cell flag is mostly ignored.
*/
bool is_real_ghost_cell () const
{
return m_ghost_cell && empty ();
}
/**
* @brief Sets the "ghost cell" flag
*
* See "is_ghost_cell" for a description of this property.
*/
void set_ghost_cell (bool g)
{
m_ghost_cell = g;
}
void set_ghost_cell (bool g);
/**
* @brief Gets a value indicating whether the cell is locked

View File

@ -353,6 +353,7 @@ Pin &Circuit::add_pin (const std::string &name)
void Circuit::remove_pin (size_t id)
{
if (id < m_pin_by_id.size () && ! tl::is_null_iterator (m_pin_by_id [id])) {
connect_pin (id, 0);
m_pins.erase (m_pin_by_id [id]);
m_pin_by_id [id] = pin_list::iterator ();
}

View File

@ -60,14 +60,36 @@ ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOr
}
i->second->push_back (this);
}
layout.register_cold_proxy (this);
}
ColdProxy::~ColdProxy ()
{
if (layout ()) {
layout ()->unregister_cold_proxy (this);
}
delete mp_context_info;
mp_context_info = 0;
}
void
ColdProxy::unregister ()
{
if (layout ()) {
layout ()->unregister_cold_proxy (this);
}
}
void
ColdProxy::reregister ()
{
if (layout ()) {
layout ()->register_cold_proxy (this);
}
}
Cell *
ColdProxy::clone (Layout &layout) const
{

View File

@ -81,6 +81,16 @@ public:
return true;
}
/**
* @brief Reimplemented from Cell: unregisters the proxy at the layout
*/
virtual void unregister ();
/**
* @brief Reimplemented from Cell: reregisters the proxy at the layout
*/
virtual void reregister ();
/**
* @brief Gets a list of cold proxies for a given library name
*/

View File

@ -541,31 +541,58 @@ CommonReaderBase::open_dl_uncached (db::Layout &layout, const LDPair &dl)
}
unsigned int nl = layout.insert_layer (lp);
// change OASIS layer name if a layer already exists by layer/datatype, but has a different name
if (! lp.name.empty () && layout.get_properties (nl).name != lp.name) {
layout.set_properties (nl, lp);
}
m_layer_map_out.map (dl, nl, lp);
m_layers_created.insert (nl);
return std::make_pair (true, nl);
} else if (li.size () == 1) {
m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ()));
return std::make_pair (true, *li.begin ());
} else {
for (std::set<unsigned int>::const_iterator i = li.begin (); i != li.end (); ++i) {
m_layer_map_out.mmap (dl, *i, layout.get_properties (*i));
for (auto i = li.begin (); i != li.end (); ++i) {
// change OASIS layer name if a layer exists by layer/datatype, but has a different name
const tl::interval_map <db::ld_type, std::string> *names_dmap = m_layer_names.mapped (dl.layer);
if (names_dmap != 0) {
const std::string *name = names_dmap->mapped (dl.datatype);
if (name != 0) {
db::LayerProperties lp_out = layout.get_properties (*i);
if (lp_out.name != *name) {
lp_out.name = *name;
layout.set_properties (*i, lp_out);
}
}
}
}
std::map<std::set<unsigned int>, unsigned int>::iterator mmp = m_multi_mapping_placeholders.find (li);
if (mmp == m_multi_mapping_placeholders.end ()) {
// create a placeholder layer
mmp = m_multi_mapping_placeholders.insert (std::make_pair (li, layout.insert_layer ())).first;
}
if (li.size () == 1) {
return std::make_pair (true, mmp->second);
m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ()));
return std::make_pair (true, *li.begin ());
} else {
for (std::set<unsigned int>::const_iterator i = li.begin (); i != li.end (); ++i) {
m_layer_map_out.mmap (dl, *i, layout.get_properties (*i));
}
std::map<std::set<unsigned int>, unsigned int>::iterator mmp = m_multi_mapping_placeholders.find (li);
if (mmp == m_multi_mapping_placeholders.end ()) {
// create a placeholder layer
mmp = m_multi_mapping_placeholders.insert (std::make_pair (li, layout.insert_layer ())).first;
}
return std::make_pair (true, mmp->second);
}
}
}
@ -600,30 +627,7 @@ CommonReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
// A cleanup may be necessary because of the following scenario: if library proxies contain subcells
// which are proxies themselves, the proxy update may make them orphans (the proxies are regenerated).
// The cleanup will removed these.
// Adressing issue #1835 (reading proxy-only GDS file renders empty layout) we do not delete
// the first (non-cold) proxy if there are only proxy top cells.
// We never clean up the top cell if there is a single one. This catches the case of having
// defunct proxies for top cells.
std::set<db::cell_index_type> keep;
if (layout.end_top_cells () - layout.begin_top_down () == 1) {
keep.insert (*layout.begin_top_down ());
} else {
for (auto c = layout.begin_top_down (); c != layout.end_top_cells (); ++c) {
const db::Cell *cptr = &layout.cell (*c);
if (cptr->is_proxy ()) {
if (! dynamic_cast <const db::ColdProxy *> (cptr) && keep.empty ()) {
keep.insert (*c);
}
} else {
keep.clear ();
break;
}
}
}
layout.cleanup (keep);
layout.cleanup ();
return layer_map_out ();
}
@ -692,6 +696,11 @@ public:
return false;
}
virtual bool supports_context () const
{
return false;
}
virtual tl::XMLElementBase *xml_reader_options_element () const
{
return new db::ReaderOptionsXMLElement<db::CommonReaderOptions> ("common",

View File

@ -859,7 +859,7 @@ namespace
struct generic_result_adaptor
{
public:
generic_result_adaptor<T> (std::vector<std::unordered_set<T> > *results)
generic_result_adaptor (std::vector<std::unordered_set<T> > *results)
: mp_results (results)
{
m_intermediate.reserve (results->size ());
@ -1682,13 +1682,9 @@ CompoundRegionCheckOperationNode::computed_dist () const
void
CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout *layout, db::Cell *cell, const shape_interactions<db::PolygonWithProperties, db::PolygonWithProperties> &interactions, std::vector<std::unordered_set<db::EdgePairWithProperties> > &results, const db::LocalProcessorBase *proc) const
{
// consider magnification variants
db::EdgeRelationFilter check = m_check;
check.set_distance (proc->dist_for_cell (cell, check.distance ()));
// TODO: needs a concept to deal with merged/non-merged inputs
bool is_merged = true;
db::check_local_operation<db::PolygonWithProperties, db::PolygonWithProperties> op (check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options);
db::check_local_operation<db::PolygonWithProperties, db::PolygonWithProperties> op (m_check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options);
tl_assert (results.size () == 1);
if (results.front ().empty ()) {
@ -1704,13 +1700,9 @@ CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache
void
CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout *layout, db::Cell *cell, const shape_interactions<db::PolygonRefWithProperties, db::PolygonRefWithProperties> &interactions, std::vector<std::unordered_set<db::EdgePairWithProperties> > &results, const db::LocalProcessorBase *proc) const
{
// consider magnification variants
db::EdgeRelationFilter check = m_check;
check.set_distance (proc->dist_for_cell (cell, check.distance ()));
// TODO: needs a concept to deal with merged/non-merged inputs
bool is_merged = true;
db::check_local_operation<db::PolygonRefWithProperties, db::PolygonRefWithProperties> op (check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options);
db::check_local_operation<db::PolygonRefWithProperties, db::PolygonRefWithProperties> op (m_check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options);
tl_assert (results.size () == 1);
if (results.front ().empty ()) {

View File

@ -1758,7 +1758,7 @@ public:
* Creates a local operation which utilizes the operation tree. "node" is the root of the operation tree.
* Ownership of the node is *not* transferred to the local operation.
*/
compound_local_operation<TS, TI, TR> (CompoundRegionOperationNode *node)
compound_local_operation (CompoundRegionOperationNode *node)
: mp_node (node)
{ }
@ -1802,7 +1802,7 @@ public:
* Creates a local operation which utilizes the operation tree. "node" is the root of the operation tree.
* Ownership of the node is *not* transferred to the local operation.
*/
compound_local_operation_with_properties<TS, TI, TR> (CompoundRegionOperationNode *node, db::PropertyConstraint prop_constraint)
compound_local_operation_with_properties (CompoundRegionOperationNode *node, db::PropertyConstraint prop_constraint)
: mp_node (node), m_prop_constraint (prop_constraint)
{
// .. nothing yet ..

View File

@ -179,8 +179,10 @@ DeepRegion::~DeepRegion ()
DeepRegion::DeepRegion (const DeepRegion &other)
: MutableRegion (other), DeepShapeCollectionDelegateBase (other),
m_merged_polygons_valid (other.m_merged_polygons_valid),
m_merged_polygons_min_coherence (other.m_merged_polygons_min_coherence),
m_merged_polygons_boc_hash (other.m_merged_polygons_boc_hash),
m_is_merged (other.m_is_merged)
m_is_merged (other.m_is_merged),
m_is_merged_min_coherence (other.m_is_merged_min_coherence)
{
if (m_merged_polygons_valid) {
m_merged_polygons = other.m_merged_polygons.copy ();
@ -196,8 +198,10 @@ DeepRegion::operator= (const DeepRegion &other)
DeepShapeCollectionDelegateBase::operator= (other);
m_merged_polygons_valid = other.m_merged_polygons_valid;
m_merged_polygons_min_coherence = other.m_merged_polygons_min_coherence;
m_merged_polygons_boc_hash = other.m_merged_polygons_boc_hash;
m_is_merged = other.m_is_merged;
m_is_merged_min_coherence = other.m_is_merged_min_coherence;
if (m_merged_polygons_valid) {
m_merged_polygons = other.m_merged_polygons.copy ();
}
@ -210,9 +214,11 @@ DeepRegion::operator= (const DeepRegion &other)
void DeepRegion::init ()
{
m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
m_merged_polygons_boc_hash = 0;
m_merged_polygons = db::DeepLayer ();
m_is_merged = false;
m_is_merged_min_coherence = false;
}
RegionDelegate *
@ -228,7 +234,7 @@ void DeepRegion::merged_semantics_changed ()
void DeepRegion::min_coherence_changed ()
{
set_is_merged (false);
// merged status is tracked in separate variables and is validated on inquiry
}
void DeepRegion::join_properties_on_merge_changed ()
@ -401,6 +407,13 @@ DeepRegion::begin_merged () const
}
}
RegionIteratorDelegate *
DeepRegion::begin_unmerged () const
{
ensure_unmerged_polygons_valid ();
return begin ();
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans>
DeepRegion::begin_iter () const
{
@ -445,6 +458,13 @@ DeepRegion::begin_merged_iter () const
}
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans>
DeepRegion::begin_unmerged_iter () const
{
ensure_unmerged_polygons_valid ();
return begin_iter ();
}
bool
DeepRegion::empty () const
{
@ -454,7 +474,7 @@ DeepRegion::empty () const
bool
DeepRegion::is_merged () const
{
return m_is_merged;
return m_is_merged && (m_is_merged_min_coherence == min_coherence ());
}
const db::Polygon *
@ -725,18 +745,21 @@ DeepRegion::merged_deep_layer () const
bool
DeepRegion::merged_polygons_available () const
{
return m_is_merged || (m_merged_polygons_valid && m_merged_polygons_boc_hash == deep_layer ().breakout_cells_hash ());
return (m_is_merged && m_is_merged_min_coherence == min_coherence ()) ||
(m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence () && m_merged_polygons_boc_hash == deep_layer ().breakout_cells_hash ());
}
void
DeepRegion::ensure_merged_polygons_valid () const
{
if (! m_merged_polygons_valid || (! m_is_merged && m_merged_polygons_boc_hash != deep_layer ().breakout_cells_hash ())) {
if (! m_merged_polygons_valid || m_merged_polygons_min_coherence != min_coherence () ||
((! m_is_merged || m_is_merged_min_coherence != min_coherence ()) && m_merged_polygons_boc_hash != deep_layer ().breakout_cells_hash ())) {
if (m_is_merged) {
if (m_is_merged && m_is_merged_min_coherence == min_coherence ()) {
// NOTE: this will reuse the deep layer reference
m_merged_polygons = deep_layer ();
m_merged_polygons_min_coherence = min_coherence ();
} else {
@ -782,15 +805,59 @@ DeepRegion::ensure_merged_polygons_valid () const
}
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = min_coherence ();
m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash ();
}
}
void
DeepRegion::ensure_unmerged_polygons_valid () const
{
if (! m_is_merged ||
(deep_layer ().store ()->max_area_ratio () == 0.0 && deep_layer ().store ()->max_vertex_count () == 0)) {
return;
}
m_merged_polygons = deep_layer ().derived ();
db::DeepLayer &polygons = const_cast<db::DeepLayer &> (deep_layer ());
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = m_is_merged_min_coherence;
m_is_merged = false;
m_is_merged_min_coherence = min_coherence ();
m_merged_polygons_boc_hash = deep_layer ().breakout_cells_hash ();
db::Layout &layout = polygons.layout ();
polygons.swap (m_merged_polygons);
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
const db::Shapes &s = c->shapes (m_merged_polygons.layer ());
db::Shapes &st = c->shapes (deep_layer ().layer ());
db::PolygonRefToShapesGenerator pr (&layout, &st);
db::PolygonSplitter splitter (pr, polygons.store ()->max_area_ratio (), polygons.store ()->max_vertex_count ());
splitter.start ();
for (auto p = s.begin (db::ShapeIterator::All); ! p.at_end (); ++p) {
if (p->is_polygon ()) {
pr.set_prop_id (p->prop_id ());
db::Polygon poly;
p->polygon (poly);
splitter.put (poly);
}
}
splitter.flush ();
}
}
void
DeepRegion::set_is_merged (bool f)
{
m_is_merged = f;
m_is_merged_min_coherence = min_coherence ();
m_merged_polygons_valid = false;
m_merged_polygons_boc_hash = 0;
m_merged_polygons = db::DeepLayer ();
@ -826,6 +893,140 @@ DeepRegion::nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Var
return new db::DeepRegion (result);
}
namespace {
/**
* @brief Implements a boolean, top-down NOT operation with property handling for the "peel" feature
*/
class PushHierLocalOperationWithProperties
: public local_operation<db::object_with_properties<db::PolygonRef>, db::object_with_properties<db::PolygonRef>, db::object_with_properties<db::PolygonRef> >
{
public:
PushHierLocalOperationWithProperties (double complexity_factor)
: local_operation<db::object_with_properties<db::PolygonRef>, db::object_with_properties<db::PolygonRef>, db::object_with_properties<db::PolygonRef> > (),
m_complexity_factor (complexity_factor)
{
// .. nothing yet ..
}
OnEmptyIntruderHint on_empty_intruder_hint () const { return Copy; }
std::string description () const
{
return tl::to_string (tr ("'peel' operation"));
}
virtual void do_compute_local (db::Layout *layout, db::Cell * /*subject_cell*/, const shape_interactions<db::object_with_properties<db::PolygonRef>, db::object_with_properties<db::PolygonRef> > &interactions, std::vector<std::unordered_set<db::object_with_properties<db::PolygonRef> > > &results, const db::LocalProcessorBase *proc) const
{
tl_assert (results.size () == 1);
auto &result = results.front ();
db::EdgeProcessor ep;
for (auto i = interactions.begin (); i != interactions.end (); ++i) {
const auto &subject = interactions.subject_shape (i->first);
db::properties_id_type prop_id = subject.properties_id ();
if (i->second.empty ()) {
result.insert (subject);
} else {
ep.clear ();
const auto &subject = interactions.subject_shape (i->first);
for (auto e = subject.begin_edge (); ! e.at_end(); ++e) {
ep.insert (*e, 0);
}
size_t p2 = 1;
for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) {
const auto &intruder = interactions.intruder_shape (*ii);
for (auto e = intruder.second.begin_edge (); ! e.at_end(); ++e) {
ep.insert (*e, p2);
}
p2 += 2;
}
std::unordered_set<db::object_with_properties<db::PolygonRef> > result1;
db::BooleanOp op (db::BooleanOp::ANotB);
db::polygon_ref_generator_with_properties<db::object_with_properties<db::PolygonRef> > pr (layout, result1, prop_id);
db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ());
db::PolygonGenerator pg (splitter, true, true);
ep.set_base_verbosity (50);
ep.process (pg, op);
if (result1.empty ()) {
// shortcut: nothing to do
} else if (m_complexity_factor < 0.0) {
// no complexity limit
result.insert (result1.begin (), result1.end ());
} else if (m_complexity_factor == 0.0) {
// only remove shape if it is really entirely covered in this case
result.insert (subject);
} else {
size_t vertices_before = subject.vertices ();
size_t vertices_after = 0;
for (auto r = result1.begin (); r != result1.end (); ++r) {
vertices_after += r->vertices ();
}
if (floor (0.5 + m_complexity_factor * vertices_before) >= vertices_after) {
result.insert (result1.begin (), result1.end ());
} else {
result.insert (subject);
}
}
}
}
}
private:
double m_complexity_factor;
};
}
RegionDelegate *
DeepRegion::peel (double complexity_factor) const
{
if (empty ()) {
// we can return "this", as this method is only intended for in-place execution inside Region
return const_cast<DeepRegion *> (this);
}
DeepLayer dl_out (deep_layer ().derived ());
PushHierLocalOperationWithProperties op (complexity_factor);
db::local_processor<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::PolygonRefWithProperties> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), deep_layer ().breakout_cells ());
configure_proc (proc);
proc.set_threads (deep_layer ().store ()->threads ());
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
// with this setting, only top-down interactions are considered
proc.set_top_down (true);
proc.run (&op, deep_layer ().layer (), deep_layer ().layer (), dl_out.layer ());
return new DeepRegion (dl_out);
}
RegionDelegate *
DeepRegion::and_with (const Region &other, PropertyConstraint property_constraint) const
{
@ -922,6 +1123,10 @@ DeepRegion::andnot_with (const Region &other, PropertyConstraint property_constr
DeepLayer
DeepRegion::and_with_impl (const DeepRegion *other, db::PropertyConstraint property_constraint) const
{
// booleans run better on simple polygons
ensure_unmerged_polygons_valid ();
other->ensure_unmerged_polygons_valid ();
DeepLayer dl_out (deep_layer ().derived ());
if (pc_skip (property_constraint)) {
@ -956,6 +1161,10 @@ DeepRegion::and_with_impl (const DeepRegion *other, db::PropertyConstraint prope
DeepLayer
DeepRegion::not_with_impl (const DeepRegion *other, db::PropertyConstraint property_constraint) const
{
// booleans run better on simple polygons
ensure_unmerged_polygons_valid ();
other->ensure_unmerged_polygons_valid ();
DeepLayer dl_out (deep_layer ().derived ());
DeepLayer dl_prep;
@ -1059,6 +1268,10 @@ DeepRegion::not_with_impl (const DeepRegion *other, db::PropertyConstraint prope
std::pair<DeepLayer, DeepLayer>
DeepRegion::and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const
{
// booleans run better on simple polygons
ensure_unmerged_polygons_valid ();
other->ensure_unmerged_polygons_valid ();
DeepLayer dl_out1 (deep_layer ().derived ());
DeepLayer dl_out2 (deep_layer ().derived ());
@ -1179,6 +1392,10 @@ DeepRegion::add_in_place (const Region &other)
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (other_deep) {
// NOTE: as we don't benefit from merged shapes here, we prefer unmerged ones
// for potentially better performance.
other_deep->ensure_unmerged_polygons_valid ();
deep_layer ().add_from (other_deep->deep_layer ());
} else {
@ -1822,6 +2039,7 @@ DeepRegion::merged () const
res->deep_layer ().layer ();
res->set_min_coherence (min_coherence ());
res->set_is_merged (true);
return res.release ();
}
@ -1885,6 +2103,10 @@ DeepRegion::sized (coord_type d, unsigned int mode) const
return clone ();
}
// in case of negative sizing the output polygons will still be merged (on positive sizing they might
// overlap after size and are not necessarily merged)
bool will_be_merged = (d < 0 && (merged_semantics () || is_merged ()));
const db::DeepLayer &polygons = merged_deep_layer ();
db::Layout &layout = const_cast<db::Layout &> (polygons.layout ());
@ -1904,7 +2126,8 @@ DeepRegion::sized (coord_type d, unsigned int mode) const
db::Shapes &st = c->shapes (res->deep_layer ().layer ());
db::PolygonRefToShapesGenerator pr (&layout, &st);
db::PolygonGenerator pg2 (pr, false /*don't resolve holes*/, true /*min. coherence*/);
db::PolygonSplitter splitter (pr, will_be_merged ? 0.0 : polygons.store ()->max_area_ratio (), will_be_merged ? 0 : polygons.store ()->max_vertex_count ());
db::PolygonGenerator pg2 (splitter, false /*don't resolve holes*/, true /*min. coherence*/);
db::SizingPolygonFilter siz (pg2, d_with_mag, d_with_mag, mode);
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
@ -1916,11 +2139,7 @@ DeepRegion::sized (coord_type d, unsigned int mode) const
}
// in case of negative sizing the output polygons will still be merged (on positive sizing they might
// overlap after size and are not necessarily merged)
if (d < 0 && (merged_semantics () || is_merged ())) {
res->set_is_merged (true);
}
res->set_is_merged (will_be_merged);
return res.release ();
}
@ -1938,6 +2157,10 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const
return sized (dx, mode);
}
// in case of negative sizing the output polygons will still be merged (on positive sizing they might
// overlap after size and are not necessarily merged)
bool will_be_merged = (dx < 0 && dy < 0 && (merged_semantics () || is_merged ()));
const db::DeepLayer &polygons = merged_deep_layer ();
db::Layout &layout = const_cast<db::Layout &> (polygons.layout ());
@ -1964,7 +2187,8 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const
db::Shapes &st = c->shapes (res->deep_layer ().layer ());
db::PolygonRefToShapesGenerator pr (&layout, &st);
db::PolygonGenerator pg2 (pr, false /*don't resolve holes*/, true /*min. coherence*/);
db::PolygonSplitter splitter (pr, will_be_merged ? 0.0 : polygons.store ()->max_area_ratio (), will_be_merged ? 0 : polygons.store ()->max_vertex_count ());
db::PolygonGenerator pg2 (splitter, false /*don't resolve holes*/, true /*min. coherence*/);
db::SizingPolygonFilter siz (pg2, dx_with_mag, dy_with_mag, mode);
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
@ -1976,11 +2200,7 @@ DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const
}
// in case of negative sizing the output polygons will still be merged (on positive sizing they might
// overlap after size and are not necessarily merged)
if (dx < 0 && dy < 0 && (merged_semantics () || is_merged ())) {
res->set_is_merged (true);
}
res->set_is_merged (will_be_merged);
return res.release ();
}
@ -2164,48 +2384,75 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
{
if (empty ()) {
return new db::DeepEdgePairs (deep_layer ().derived ());
} else if (other && ! is_subject_regionptr (other) && other->empty () && ! options.negative) {
} else if (other && other->empty () && ! options.negative) {
return new db::DeepEdgePairs (deep_layer ().derived ());
}
// force different polygons in the different properties case to skip intra-polygon checks
if (pc_always_different (options.prop_constraint)) {
// TODO: this forces merged primaries, so maybe that is not a good optimization?
different_polygons = true;
}
// delegate to AsIfFlatRegion if the other region is not a deep one
const db::DeepRegion *other_deep = 0;
unsigned int other_layer = 0;
bool other_is_merged = true;
bool needs_merged_primary = different_polygons || options.needs_merged ();
bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged ();
if (other == subject_regionptr ()) {
other_layer = subject_idlayer ();
other_is_merged = primary_is_merged;
} else if (other == foreign_regionptr ()) {
other_layer = foreign_idlayer ();
other_is_merged = primary_is_merged;
} else {
if (other) {
other_deep = dynamic_cast<const db::DeepRegion *> (other->delegate ());
if (! other_deep) {
return db::AsIfFlatRegion::run_check (rel, different_polygons, other, d, options);
}
}
// force different polygons in the different properties case to skip intra-polygon checks
if (! other_deep && pc_always_different (options.prop_constraint)) {
// TODO: this forces merged primaries, so maybe that is not a good optimization?
different_polygons = true;
}
// primary input
bool needs_merged_primary = (! other_deep && different_polygons) || options.needs_merged ();
bool primary_is_merged = false;
db::DeepLayer polygons;
if (! merged_semantics ()) {
primary_is_merged = true; // means: don't merge again
polygons = deep_layer ();
} else if (! needs_merged_primary) {
// The implementation may run faster if the primary is not merged
primary_is_merged = false;
ensure_unmerged_polygons_valid ();
polygons = deep_layer ();
} else {
primary_is_merged = true;
polygons = merged_deep_layer ();
}
// other input
std::vector<unsigned int> other_layers;
bool other_is_merged = true;
if (! other_deep) {
other_layers.push_back (foreign_idlayer ());
other_is_merged = primary_is_merged;
} else {
if (! other->merged_semantics ()) {
other_layer = other_deep->deep_layer ().layer ();
other_layers.push_back (other_deep->deep_layer ().layer ());
other_is_merged = true;
} else if (options.whole_edges) {
// NOTE: whole edges needs both inputs merged
other_layer = other_deep->merged_deep_layer ().layer ();
other_layers.push_back (other_deep->merged_deep_layer ().layer ());
other_is_merged = true;
} else {
other_layer = other_deep->deep_layer ().layer ();
other_layers.push_back (other_deep->deep_layer ().layer ());
other_is_merged = other->is_merged ();
}
}
const db::DeepLayer &polygons = needs_merged_primary ? merged_deep_layer () : deep_layer ();
// adds another intruder section to implement subject merging ("primary_intruders")
if (! primary_is_merged) {
other_layers.push_back (foreign_idlayer ());
}
}
EdgeRelationFilter check (rel, d, options);
@ -2229,7 +2476,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
configure_proc (proc);
proc.set_threads (polygons.store ()->threads ());
proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ());
proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ());
} else {
@ -2242,7 +2489,7 @@ DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, cons
configure_proc (proc);
proc.set_threads (polygons.store ()->threads ());
proc.run (&op, polygons.layer (), other_layer, res->deep_layer ().layer ());
proc.run (&op, polygons.layer (), other_layers, res->deep_layer ().layer ());
}

View File

@ -66,9 +66,11 @@ public:
virtual RegionIteratorDelegate *begin () const;
virtual RegionIteratorDelegate *begin_merged () const;
virtual RegionIteratorDelegate *begin_unmerged () const;
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const;
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_unmerged_iter () const;
virtual bool empty () const;
virtual bool is_merged () const;
@ -138,6 +140,8 @@ public:
virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type d, int steps, unsigned int mode) const;
virtual RegionDelegate *sized_inside (const Region &inside, bool outside, coord_type dx, coord_type dy, int steps, unsigned int mode) const;
virtual RegionDelegate *peel (double complexity_factor) const;
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
virtual RegionDelegate *nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const std::vector<const Net *> *nets) const;
@ -175,11 +179,14 @@ private:
mutable DeepLayer m_merged_polygons;
mutable bool m_merged_polygons_valid;
mutable bool m_merged_polygons_min_coherence;
mutable size_t m_merged_polygons_boc_hash;
bool m_is_merged;
mutable bool m_is_merged;
mutable bool m_is_merged_min_coherence;
void init ();
void ensure_merged_polygons_valid () const;
void ensure_unmerged_polygons_valid () const;
DeepLayer not_with_impl (const DeepRegion *other, PropertyConstraint property_constraint) const;
DeepLayer and_with_impl (const DeepRegion *other, PropertyConstraint property_constraint) const;
std::pair<DeepLayer, DeepLayer> and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const;

View File

@ -232,6 +232,14 @@ bool DeepLayer::operator== (const DeepLayer &other) const
return true;
}
void
DeepLayer::swap (DeepLayer &other)
{
tl_assert (mp_store.get () == other.mp_store.get ());
std::swap (m_layer, other.m_layer);
std::swap (m_layout, other.m_layout);
}
db::Layout &
DeepLayer::layout ()
{
@ -563,13 +571,13 @@ static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIter
}
DeepShapeStore::DeepShapeStore ()
: m_keep_layouts (true), m_wants_all_cells (false)
: m_keep_layouts (true), m_wants_all_cells (false), m_sparse_array_limit (-1.0)
{
++s_instance_count;
}
DeepShapeStore::DeepShapeStore (const std::string &topcell_name, double dbu)
: m_keep_layouts (true), m_wants_all_cells (false)
: m_keep_layouts (true), m_wants_all_cells (false), m_sparse_array_limit (-1.0)
{
++s_instance_count;
@ -857,6 +865,16 @@ bool DeepShapeStore::wants_all_cells () const
return m_wants_all_cells;
}
void DeepShapeStore::set_sparse_array_limit (double l)
{
m_sparse_array_limit = l;
}
double DeepShapeStore::sparse_array_limit () const
{
return m_sparse_array_limit;
}
void DeepShapeStore::set_reject_odd_polygons (bool f)
{
m_state.set_reject_odd_polygons (f);
@ -1022,6 +1040,7 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
builder.set_wants_all_cells (m_wants_all_cells);
builder.set_sparse_array_limit (m_sparse_array_limit);
unsigned int layer_index = init_layer (layout, si);
builder.set_target_layer (layer_index);
@ -1056,6 +1075,9 @@ DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator
db::Layout &layout = m_layouts[layout_index]->layout;
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
builder.set_wants_all_cells (m_wants_all_cells);
builder.set_sparse_array_limit (m_sparse_array_limit);
unsigned int layer_index = init_layer (layout, si);
builder.set_target_layer (layer_index);
@ -1367,7 +1389,6 @@ namespace
{
public:
DeepShapeStoreToShapeTransformer (const DeepShapeStore &dss, const db::Layout &layout)
: mp_layout (& layout)
{
// gets the text annotation property ID -
// this is how the texts are passed for annotating the net names
@ -1423,7 +1444,6 @@ namespace
private:
std::pair<bool, db::property_names_id_type> m_text_annot_name_id;
const db::Layout *mp_layout;
};
}

View File

@ -120,6 +120,11 @@ public:
*/
bool operator== (const DeepLayer &other) const;
/**
* @brief Swap two layers
*/
void swap (DeepLayer &other);
/**
* @brief Gets the layout object
* The return value is guaranteed to be non-null.
@ -696,6 +701,27 @@ public:
*/
bool wants_all_cells () const;
/**
* @brief Sets the "sparse array" limit
*
* Sparse arrays are instance arrays whose bounding box is no longer a
* good approximation of the covered area. The "sparse array ratio" is
* the area of the bounding box divided by the area of the bounding box
* of a single instance.
*
* Arrays above this limit will be resolved into single instances.
*
* Setting this value to 0 will resolve all arrays. Setting this
* value to a negative value will never split arrays. The latter
* is the default.
*/
void set_sparse_array_limit (double l);
/**
* @brief Gets the "sparse array" limit
*/
double sparse_array_limit () const;
/**
* @brief Sets a flag indicating whether to reject odd polygons
*
@ -853,6 +879,7 @@ private:
std::list<DeepShapeStoreState> m_state_stack;
bool m_keep_layouts;
bool m_wants_all_cells;
double m_sparse_array_limit;
tl::Mutex m_lock;
struct DeliveryMappingCacheKey

View File

@ -119,7 +119,6 @@ public:
virtual bool wants_variants () const { return false; }
private:
bool m_inverted;
db::EdgeAngleChecker m_checker;
};

View File

@ -103,6 +103,13 @@ EdgePairs::EdgePairs (DeepShapeStore &dss)
mp_delegate = new DeepEdgePairs (DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ()));
}
void
EdgePairs::convert_to_deep (const db::DeepLayer &layer)
{
tl_assert (mp_delegate->deep () == 0);
set_delegate (copy_data_id (new db::DeepEdgePairs (layer)));
}
void
EdgePairs::write (const std::string &fn) const
{
@ -113,9 +120,8 @@ EdgePairs::write (const std::string &fn) const
unsigned int li = layout.insert_layer (db::LayerProperties (0, 0));
insert_into (&layout, top.cell_index (), li);
tl::OutputStream os (fn);
db::SaveLayoutOptions opt;
opt.set_format_from_filename (fn);
tl::OutputStream os (opt.set_format_from_filename (fn).second);
db::Writer writer (opt);
writer.write (layout, os);
}

View File

@ -204,6 +204,11 @@ public:
*/
explicit EdgePairs (DeepShapeStore &dss);
/**
* @brief Converts the shape collection to a deep one using the specified layer
*/
virtual void convert_to_deep (const db::DeepLayer &layer);
/**
* @brief Writes the edge pair collection to a file
*

View File

@ -114,6 +114,13 @@ Edges::Edges (DeepShapeStore &dss)
mp_delegate = new DeepEdges (DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ()));
}
void
Edges::convert_to_deep (const db::DeepLayer &layer)
{
tl_assert (mp_delegate->deep () == 0);
set_delegate (copy_data_id (new db::DeepEdges (layer)));
}
const db::RecursiveShapeIterator &
Edges::iter () const
{
@ -145,9 +152,8 @@ Edges::write (const std::string &fn) const
unsigned int li = layout.insert_layer (db::LayerProperties (0, 0));
insert_into (&layout, top.cell_index (), li);
tl::OutputStream os (fn);
db::SaveLayoutOptions opt;
opt.set_format_from_filename (fn);
tl::OutputStream os (opt.set_format_from_filename (fn).second);
db::Writer writer (opt);
writer.write (layout, os);
}

View File

@ -264,6 +264,11 @@ public:
*/
explicit Edges (DeepShapeStore &dss);
/**
* @brief Converts the shape collection to a deep one using the specified layer
*/
virtual void convert_to_deep (const db::DeepLayer &layer);
/**
* @brief Implementation of the ShapeCollection interface
*/

View File

@ -244,7 +244,6 @@ struct DB_PUBLIC EdgeOrientationFilter
}
private:
bool m_inverse;
db::MagnificationAndOrientationReducer m_vars;
EdgeAngleChecker m_checker;
};

View File

@ -46,9 +46,11 @@ public:
virtual RegionIteratorDelegate *begin () const { return 0; }
virtual RegionIteratorDelegate *begin_merged () const { return 0; }
virtual RegionIteratorDelegate *begin_unmerged () const { return 0; }
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); }
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); }
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_unmerged_iter () const { return std::make_pair (db::RecursiveShapeIterator (), db::ICplxTrans ()); }
virtual bool empty () const { return true; }
virtual size_t count () const { return 0; }
@ -109,6 +111,8 @@ public:
virtual RegionDelegate *add_in_place (const Region &other);
virtual RegionDelegate *add (const Region &other) const;
virtual RegionDelegate *peel (double /*complexity_factor*/) const { return new EmptyRegion (); }
virtual RegionDelegate *selected_outside (const Region &) const { return new EmptyRegion (); }
virtual RegionDelegate *selected_not_outside (const Region &) const { return new EmptyRegion (); }
virtual std::pair<RegionDelegate *, RegionDelegate *> selected_outside_pair (const Region &) const { return std::make_pair (new EmptyRegion (), new EmptyRegion ()); }

View File

@ -0,0 +1,146 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbFileBasedLibrary.h"
#include "dbReader.h"
#include "dbCellMapping.h"
#include "tlFileUtils.h"
#include "tlStream.h"
namespace db
{
// -------------------------------------------------------------------------------------------
FileBasedLibrary::FileBasedLibrary (const std::string &path, const std::string &name)
: db::Library (), m_name (name), m_path (path), m_is_loaded (false)
{
set_description (tl::filename (path));
// preliminary name, may be replaced later
if (! name.empty ()) {
set_name (name);
}
}
void
FileBasedLibrary::merge_with_other_layout (const std::string &path)
{
m_other_paths.push_back (path);
if (m_is_loaded) {
merge_impl (path);
}
}
bool
FileBasedLibrary::is_for_path (const std::string &path)
{
return m_other_paths.empty () && m_path == path;
}
bool
FileBasedLibrary::is_for_paths (const std::vector<std::string> &paths)
{
if (paths.size () != m_other_paths.size () + 1) {
return false;
}
// this check is purely based on path strings
std::set<std::string> p1 (paths.begin (), paths.end ());
std::set<std::string> p2;
p2.insert (m_path);
p2.insert (m_other_paths.begin (), m_other_paths.end ());
return p1 == p2;
}
std::string
FileBasedLibrary::load ()
{
if (! m_is_loaded) {
return reload ();
} else {
return get_name ();
}
}
std::string
FileBasedLibrary::reload ()
{
std::string name = m_name.empty () ? tl::basename (m_path) : m_name;
layout ().clear ();
tl::InputStream stream (m_path);
db::Reader reader (stream);
reader.read (layout ());
// Use the libname if there is one
if (m_name.empty ()) {
db::Layout::meta_info_name_id_type libname_name_id = layout ().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator m = layout ().begin_meta (); m != layout ().end_meta (); ++m) {
if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
name = m->second.value.to_string ();
break;
}
}
}
for (auto p = m_other_paths.begin (); p != m_other_paths.end (); ++p) {
merge_impl (*p);
}
m_is_loaded = true;
return name;
}
void
FileBasedLibrary::merge_impl (const std::string &path)
{
db::Layout ly;
tl::InputStream stream (path);
db::Reader reader (stream);
reader.read (ly);
std::vector<db::cell_index_type> target_cells, source_cells;
// collect the cells to pull in (all top cells of the library layout)
// NOTE: cells are not overwritten - the first layout wins, in terms
// of cell names and also in terms of database unit.
for (auto c = ly.begin_top_down (); c != ly.end_top_cells (); ++c) {
std::string cn = ly.cell_name (*c);
if (! layout ().has_cell (cn.c_str ())) {
source_cells.push_back (*c);
target_cells.push_back (layout ().add_cell (cn.c_str ()));
}
}
db::CellMapping cm;
cm.create_multi_mapping_full (layout (), target_cells, ly, source_cells);
layout ().copy_tree_shapes (ly, cm);
}
}

View File

@ -0,0 +1,115 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2026 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_dbFileBasedLibrary
#define HDR_dbFileBasedLibrary
#include "dbLibrary.h"
#include "dbCommon.h"
#include <string>
#include <list>
namespace db
{
/**
* @brief A Library specialization that ties a library to a file
*
* This object supports loading a library from multiple files and merging them into
* a single library (e.g. for loading a directory of cell files).
*/
class DB_PUBLIC FileBasedLibrary
: public db::Library
{
public:
/**
* @brief Creates a file-based library object
*
* @param path The file path
* @param name The library name
*
* If the library name is an empty string, the library name is taken from
* a GDS LIBNAME or from the file name in the path.
*
* Note that you need to call "load" in order to actually load the file.
*/
FileBasedLibrary (const std::string &path, const std::string &name = std::string ());
/**
* @brief Merges another file into this library
*
* If the library was not loaded already, the merge requests are postponed
* until "load" is called.
*/
void merge_with_other_layout (const std::string &path);
/**
* @brief Loads the files
*
* If the files are already loaded, this method does nothing.
* It returns the name of the library derived from the first file
* or the name given in the constructor. The constructor name has
* priority.
*/
std::string load ();
/**
* @brief Implements the reload feature
*/
virtual std::string reload ();
/**
* @brief Set the paths
* This method is provided for test purposes only.
*/
void set_paths (const std::string &path, const std::list<std::string> &other_paths = std::list<std::string> ())
{
m_path = path;
m_other_paths = other_paths;
}
/**
* @brief Gets a value indicating whether the library is for the given path
*/
bool is_for_path (const std::string &path);
/**
* @brief Gets a value indicating whether the library is for the given path
*/
bool is_for_paths (const std::vector<std::string> &paths);
private:
std::string m_name;
std::string m_path;
std::list<std::string> m_other_paths;
bool m_is_loaded;
void merge_impl (const std::string &path);
};
}
#endif

View File

@ -33,41 +33,55 @@ namespace db
// -------------------------------------------------------------------------------------------------------------
// FlatRegion implementation
FlatRegion::FlatRegion ()
: MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false))
FlatRegion::FlatRegion (double area_ratio, size_t max_vertex_count)
: MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
}
FlatRegion::FlatRegion (const FlatRegion &other)
: MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons)
: MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons),
m_area_ratio (other.m_area_ratio), m_max_vertex_count (other.m_max_vertex_count)
{
init ();
set_min_coherence (other.min_coherence ());
m_is_merged = other.m_is_merged;
m_is_merged_min_coherence = other.m_is_merged_min_coherence;
m_merged_polygons_valid = other.m_merged_polygons_valid;
m_merged_polygons_min_coherence = other.m_merged_polygons_min_coherence;
}
FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged)
: MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false))
FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
}
FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged)
: MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false))
FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
transform_generic (trans);
set_merged_semantics (merged_semantics);
}
FlatRegion::FlatRegion (bool is_merged)
: MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false))
FlatRegion::FlatRegion (bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
}
FlatRegion::~FlatRegion ()
@ -78,6 +92,7 @@ FlatRegion::~FlatRegion ()
void FlatRegion::set_is_merged (bool m)
{
m_is_merged = m;
m_is_merged_min_coherence = min_coherence ();
}
void FlatRegion::invalidate_cache ()
@ -90,7 +105,9 @@ void FlatRegion::invalidate_cache ()
void FlatRegion::init ()
{
m_is_merged = false;
m_is_merged_min_coherence = false;
m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
}
void FlatRegion::merged_semantics_changed ()
@ -107,9 +124,7 @@ void FlatRegion::join_properties_on_merge_changed ()
void FlatRegion::min_coherence_changed ()
{
m_is_merged = false;
mp_merged_polygons->clear ();
m_merged_polygons_valid = false;
// merged status is tracked in separate variables and is validated on inquiry
}
void FlatRegion::reserve (size_t n)
@ -117,15 +132,44 @@ void FlatRegion::reserve (size_t n)
mp_polygons->reserve (db::Polygon::tag (), n);
}
bool
FlatRegion::merged_polygons_valid () const
{
return m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence ();
}
bool
FlatRegion::merged_polygons_available () const
{
return is_merged () || merged_polygons_valid ();
}
void
FlatRegion::ensure_merged_polygons_valid () const
{
if (! m_merged_polygons_valid) {
if (! merged_polygons_valid ()) {
merge_polygons_to (*mp_merged_polygons, min_coherence (), 0, join_properties_on_merge ());
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = min_coherence ();
}
}
void
FlatRegion::ensure_unmerged_polygons_valid () const
{
if (! m_is_merged || (m_area_ratio == 0.0 && m_max_vertex_count == 0)) {
return;
}
mp_merged_polygons.reset (new db::Shapes (*mp_polygons));
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = m_is_merged_min_coherence;
m_is_merged = false;
m_is_merged_min_coherence = min_coherence ();
break_polygons (*mp_polygons, m_max_vertex_count, m_area_ratio);
}
RegionIteratorDelegate *FlatRegion::begin () const
{
return new FlatRegionIterator (mp_polygons.get_const ());
@ -133,7 +177,7 @@ RegionIteratorDelegate *FlatRegion::begin () const
RegionIteratorDelegate *FlatRegion::begin_merged () const
{
if (! merged_semantics () || m_is_merged) {
if (! merged_semantics () || is_merged ()) {
return begin ();
} else {
ensure_merged_polygons_valid ();
@ -141,6 +185,12 @@ RegionIteratorDelegate *FlatRegion::begin_merged () const
}
}
RegionIteratorDelegate *FlatRegion::begin_unmerged () const
{
ensure_unmerged_polygons_valid ();
return begin ();
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_iter () const
{
return std::make_pair (db::RecursiveShapeIterator (*mp_polygons), db::ICplxTrans ());
@ -148,7 +198,7 @@ std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_iter ()
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_merged_iter () const
{
if (! merged_semantics () || m_is_merged) {
if (! merged_semantics () || is_merged ()) {
return begin_iter ();
} else {
ensure_merged_polygons_valid ();
@ -156,6 +206,12 @@ std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_merged_i
}
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_unmerged_iter () const
{
ensure_unmerged_polygons_valid ();
return begin_iter ();
}
bool FlatRegion::empty () const
{
return mp_polygons->empty ();
@ -173,7 +229,7 @@ size_t FlatRegion::hier_count () const
bool FlatRegion::is_merged () const
{
return m_is_merged;
return m_is_merged && m_is_merged_min_coherence == min_coherence ();
}
Box FlatRegion::compute_bbox () const
@ -215,6 +271,7 @@ RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter)
mp_merged_polygons->clear ();
invalidate_cache ();
m_is_merged = filter.requires_raw_input () ? false : merged_semantics ();
m_is_merged_min_coherence = min_coherence ();
return this;
}
@ -245,6 +302,7 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter
mp_merged_polygons->clear ();
invalidate_cache ();
m_is_merged = filter.result_is_merged () && merged_semantics ();
m_is_merged_min_coherence = min_coherence ();
if (filter.result_must_not_be_merged ()) {
set_merged_semantics (false);
@ -255,14 +313,15 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter
RegionDelegate *FlatRegion::merged_in_place ()
{
if (! m_is_merged) {
if (! is_merged ()) {
if (m_merged_polygons_valid) {
if (merged_polygons_valid ()) {
db::Shapes &merged_polygons = *mp_merged_polygons;
mp_polygons->swap (merged_polygons);
merged_polygons.clear ();
m_is_merged = true;
m_is_merged_min_coherence = min_coherence ();
return this;
} else {
@ -274,7 +333,7 @@ RegionDelegate *FlatRegion::merged_in_place ()
}
}
RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge)
RegionDelegate *FlatRegion::merged_in_place (bool min_coh, unsigned int min_wc, bool join_properties_on_merge)
{
if (empty ()) {
@ -291,9 +350,11 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi
} else {
invalidate_cache ();
merge_polygons_to (*mp_polygons, min_coherence, min_wc, join_properties_on_merge);
merge_polygons_to (*mp_polygons, min_coh, min_wc, join_properties_on_merge);
set_min_coherence (min_coh);
m_is_merged = true;
m_is_merged_min_coherence = min_coh;
}
@ -302,19 +363,35 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi
RegionDelegate *FlatRegion::merged () const
{
if (! m_is_merged) {
if (! is_merged ()) {
if (m_merged_polygons_valid) {
return new FlatRegion (*mp_merged_polygons, true);
} else {
return AsIfFlatRegion::merged (min_coherence (), 0, join_properties_on_merge ());
if (! merged_polygons_valid ()) {
ensure_merged_polygons_valid ();
}
std::unique_ptr<FlatRegion> new_region (new FlatRegion (*mp_merged_polygons, true, 0, 0, min_coherence ()));
new_region->set_max_vertex_count (m_max_vertex_count);
new_region->set_area_ratio (m_area_ratio);
return new_region.release ();
} else {
return clone ();
}
}
RegionDelegate *FlatRegion::merged (bool min_coh, unsigned int min_wc, bool jp) const
{
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0, 0, min_coh));
merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, jp);
new_region->set_max_vertex_count (m_max_vertex_count);
new_region->set_area_ratio (m_area_ratio);
return new_region.release ();
}
RegionDelegate *FlatRegion::add (const Region &other) const
{
std::unique_ptr<FlatRegion> new_region (new FlatRegion (*this));

View File

@ -51,10 +51,10 @@ public:
typedef db::layer<db::PolygonWithProperties, db::unstable_layer_tag> polygon_layer_wp_type;
typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type;
FlatRegion ();
FlatRegion (const db::Shapes &polygons, bool is_merged = false);
FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false);
FlatRegion (bool is_merged);
FlatRegion (double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (const db::Shapes &polygons, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (bool is_merged, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (const FlatRegion &other);
@ -69,9 +69,11 @@ public:
virtual RegionIteratorDelegate *begin () const;
virtual RegionIteratorDelegate *begin_merged () const;
virtual RegionIteratorDelegate *begin_unmerged () const;
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_iter () const;
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_merged_iter () const;
virtual std::pair<db::RecursiveShapeIterator, db::ICplxTrans> begin_unmerged_iter () const;
virtual bool empty () const;
virtual size_t count () const;
@ -83,10 +85,9 @@ public:
virtual RegionDelegate *merged_in_place ();
virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge);
virtual RegionDelegate *merged () const;
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const
{
return db::AsIfFlatRegion::merged (min_coherence, min_wc, join_properties_on_merge);
}
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const;
bool merged_polygons_available () const;
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter);
virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter);
@ -136,6 +137,7 @@ protected:
virtual Box compute_bbox () const;
void invalidate_cache ();
void set_is_merged (bool m);
bool merged_polygons_valid () const;
private:
friend class AsIfFlatRegion;
@ -143,13 +145,28 @@ private:
FlatRegion &operator= (const FlatRegion &other);
bool m_is_merged;
mutable bool m_is_merged;
mutable bool m_is_merged_min_coherence;
mutable tl::copy_on_write_ptr<db::Shapes> mp_polygons;
mutable tl::copy_on_write_ptr<db::Shapes> mp_merged_polygons;
mutable bool m_merged_polygons_valid;
mutable bool m_merged_polygons_min_coherence;
double m_area_ratio;
size_t m_max_vertex_count;
void init ();
void ensure_merged_polygons_valid () const;
void ensure_unmerged_polygons_valid () const;
void set_area_ratio (double ar)
{
m_area_ratio = ar;
}
void set_max_vertex_count (size_t mc)
{
m_max_vertex_count = mc;
}
template <class Trans>
void transform_generic (const Trans &trans)

View File

@ -20,7 +20,6 @@
*/
#include "dbHierNetworkProcessor.h"
#include "dbShape.h"
#include "dbShapes.h"
@ -1794,7 +1793,7 @@ public:
cell_clusters_box_converter (const db::Layout &layout, const hier_clusters<T> &tree)
: mp_layout (&layout), mp_tree (&tree)
{
// .. nothing yet ..
m_cache.resize (layout.cells (), 0);
}
const box_type &operator() (const db::CellInst &cell_inst) const
@ -1804,10 +1803,10 @@ public:
const box_type &operator() (db::cell_index_type cell_index) const
{
typename std::map<db::cell_index_type, box_type>::const_iterator b = m_cache.find (cell_index);
if (b != m_cache.end ()) {
const box_type *b = m_cache [cell_index];
if (b) {
return b->second;
return *b;
} else {
@ -1820,13 +1819,17 @@ public:
box += inst_array.bbox (*this);
}
return m_cache.insert (std::make_pair (cell_index, box)).first->second;
m_cached_boxes.push_front (box);
b = m_cached_boxes.begin ().operator-> ();
m_cache [cell_index] = b;
return *b;
}
}
private:
mutable std::map<db::cell_index_type, box_type> m_cache;
mutable std::vector<const box_type *> m_cache;
mutable tl::slist<box_type> m_cached_boxes;
const db::Layout *mp_layout;
const hier_clusters<T> *mp_tree;
};
@ -1889,8 +1892,8 @@ namespace
*/
template <class T>
struct hc_receiver
: public db::box_scanner_receiver<db::Instance, unsigned int>,
public db::box_scanner_receiver2<local_cluster<T>, unsigned int, db::Instance, unsigned int>
: public db::box_scanner_receiver<db::Instance, db::Box>,
public db::box_scanner_receiver2<local_cluster<T>, db::Box, db::Instance, db::Box>
{
public:
typedef typename hier_clusters<T>::box_type box_type;
@ -1960,7 +1963,7 @@ public:
/**
* @brief Receiver main event for instance-to-instance interactions
*/
void add (const db::Instance *i1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/)
void add (const db::Instance *i1, db::Box /*p1*/, const db::Instance *i2, db::Box /*p2*/)
{
db::ICplxTrans t;
@ -1973,7 +1976,7 @@ public:
/**
* @brief Single-instance treatment - may be required because of interactions between array members
*/
void finish (const db::Instance *i, unsigned int /*p1*/)
void finish (const db::Instance *i, db::Box /*p1*/)
{
consider_single_inst (*i);
}
@ -1981,7 +1984,7 @@ public:
/**
* @brief Receiver main event for local-to-instance interactions
*/
void add (const local_cluster<T> *c1, unsigned int /*p1*/, const db::Instance *i2, unsigned int /*p2*/)
void add (const local_cluster<T> *c1, db::Box /*p1*/, const db::Instance *i2, db::Box /*p2*/)
{
std::list<ClusterInstanceInteraction> ic;
@ -1993,10 +1996,10 @@ public:
}
/**
* @brief Finally join the clusters in the join set
* @brief Finally generate cluster-to-instance interactions and join the clusters in the join set
*
* This step is postponed because doing this while the iteration happens would
* invalidate the box trees.
* invalidate the box trees and disturb the propagation mechanism.
*/
void finish_cluster_to_instance_interactions ()
{
@ -2081,7 +2084,7 @@ private:
const db::Connectivity *mp_conn;
const std::set<db::cell_index_type> *mp_breakout_cells;
typedef std::list<std::set<id_type> > join_set_list;
std::map<id_type, typename join_set_list::iterator> m_cm2join_map;
std::unordered_map<id_type, typename join_set_list::iterator> m_cm2join_map;
join_set_list m_cm2join_sets;
std::map<std::pair<size_t, size_t>, int> m_soft_connections;
std::list<ClusterInstanceInteraction> m_ci_interactions;
@ -2551,8 +2554,8 @@ private:
return;
}
typename std::map<id_type, typename join_set_list::iterator>::const_iterator x = m_cm2join_map.find (a);
typename std::map<id_type, typename join_set_list::iterator>::const_iterator y = m_cm2join_map.find (b);
typename std::unordered_map<id_type, typename join_set_list::iterator>::const_iterator x = m_cm2join_map.find (a);
typename std::unordered_map<id_type, typename join_set_list::iterator>::const_iterator y = m_cm2join_map.find (b);
if (x == m_cm2join_map.end ()) {
@ -2579,6 +2582,11 @@ private:
} else if (x->second != y->second) {
// the y set should be the smaller one for better efficiency
if (x->second->size () < y->second->size ()) {
std::swap (x, y);
}
// join two superclusters
typename join_set_list::iterator yset = y->second;
x->second->insert (yset->begin (), yset->end ());
@ -2591,12 +2599,12 @@ private:
#if defined(DEBUG_HIER_NETWORK_PROCESSOR)
// concistency check for debugging
for (typename std::map<id_type, typename join_set_list::iterator>::const_iterator j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) {
for (auto j = m_cm2join_map.begin (); j != m_cm2join_map.end (); ++j) {
tl_assert (j->second->find (j->first) != j->second->end ());
}
for (typename std::list<std::set<id_type> >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) {
for (typename std::set<id_type>::const_iterator j = i->begin(); j != i->end(); ++j) {
for (auto i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) {
for (auto j = i->begin(); j != i->end(); ++j) {
tl_assert(m_cm2join_map.find (*j) != m_cm2join_map.end ());
tl_assert(m_cm2join_map[*j] == i);
}
@ -2604,8 +2612,8 @@ private:
// the sets must be disjunct
std::set<id_type> all;
for (typename std::list<std::set<id_type> >::const_iterator i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) {
for (typename std::set<id_type>::const_iterator j = i->begin(); j != i->end(); ++j) {
for (auto i = m_cm2join_sets.begin (); i != m_cm2join_sets.end (); ++i) {
for (auto j = i->begin(); j != i->end(); ++j) {
tl_assert(all.find (*j) == all.end());
all.insert(*j);
}
@ -2700,23 +2708,13 @@ private:
} else if (x1 != x2) {
int soft = ic->soft;
// for instance-to-instance interactions the number of connections is more important for the
// cost of the join operation: make the one with more connections the target
// TODO: this will be SLOW for STL's not providing a fast size()
if (mp_cell_clusters->connections_for_cluster (x1).size () < mp_cell_clusters->connections_for_cluster (x2).size ()) {
std::swap (x1, x2);
soft = -soft;
}
if (soft != 0) {
register_soft_connection (x1, x2, soft);
} else {
mp_cell_clusters->join_cluster_with (x1, x2);
mp_cell_clusters->remove_cluster (x2);
mark_to_join (x1, x2);
}
@ -3081,15 +3079,15 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
static std::string desc = tl::to_string (tr ("Instance to instance treatment"));
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc);
db::box_scanner<db::Instance, unsigned int> bs (true, desc);
db::box_scanner<db::Instance, db::Box> bs (true, desc);
for (std::vector<db::Instance>::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) {
if (! is_breakout_cell (breakout_cells, inst->cell_index ())) {
bs.insert (inst.operator-> (), 0);
bs.insert (inst.operator-> (), cibc (*inst));
}
}
bs.process (*rec, 1 /*touching*/, cibc);
bs.process_with_adaptor (*rec, 1 /*touching*/);
}
// handle local to instance connections
@ -3101,7 +3099,8 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
static std::string desc = tl::to_string (tr ("Local to instance treatment"));
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc);
db::box_scanner2<db::local_cluster<T>, unsigned int, db::Instance, unsigned int> bs2 (true, desc);
local_cluster_box_convert<T> lcbc;
db::box_scanner2<db::local_cluster<T>, db::Box, db::Instance, db::Box> bs2 (true, desc);
for (typename connected_clusters<T>::const_iterator c = local.begin (); c != local.end (); ++c) {
@ -3110,22 +3109,23 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
std::back_insert_iterator<std::list<local_cluster<T> > > iout = std::back_inserter (heap);
size_t n = c->split (area_ratio, iout);
if (n == 0) {
bs2.insert1 (c.operator-> (), 0);
bs2.insert1 (c.operator-> (), lcbc (*c));
} else {
typename std::list<local_cluster<T> >::iterator h = heap.end ();
while (n-- > 0) {
bs2.insert1 ((--h).operator-> (), 0);
--h;
bs2.insert1 (h.operator-> (), lcbc (*h));
}
}
}
for (std::vector<db::Instance>::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) {
if (! is_breakout_cell (breakout_cells, inst->cell_index ())) {
bs2.insert2 (inst.operator-> (), 0);
bs2.insert2 (inst.operator-> (), cibc (*inst));
}
}
bs2.process (*rec, 1 /*touching*/, local_cluster_box_convert<T> (), cibc);
bs2.process_with_adaptor (*rec, 1 /*touching*/);
}

View File

@ -622,21 +622,28 @@ public:
typedef typename local_processor_cell_contexts<TS, TI, TR>::context_key_type interactions_value_type;
typedef std::unordered_map<std::pair<db::cell_index_type, db::ICplxTrans>, interactions_value_type> interactions_type;
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), mp_result (result), m_rt (subject_layout)
interaction_registration_inst2shape (db::Layout *subject_layout, unsigned int subject_layer, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance, interactions_type *result)
: mp_subject_layout (subject_layout), m_subject_layer (subject_layer), m_dist (dist), m_override_distance (override_distance), mp_result (result), m_rt (subject_layout)
{
// nothing yet ..
}
void add (const db::CellInstArray *inst, unsigned int, const TI *ref, unsigned int layer)
{
collect_instance_shape_interactions (inst, layer, *ref, m_dist);
db::Coord d = m_dist;
auto od = m_override_distance.find (layer);
if (od != m_override_distance.end ()) {
d = std::min (d, od->second);
}
collect_instance_shape_interactions (inst, layer, *ref, d);
}
private:
db::Layout *mp_subject_layout;
unsigned int m_subject_layer;
db::Coord m_dist;
std::map<unsigned int, db::Coord> m_override_distance;
interactions_type *mp_result;
db::shape_reference_translator_with_trans<TI, db::ICplxTrans> m_rt;
@ -681,11 +688,11 @@ private:
// LocalProcessorContextComputationTask implementation
template <class TS, class TI, class TR>
local_processor_context_computation_task<TS, TI, TR>::local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist)
local_processor_context_computation_task<TS, TI, TR>::local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance)
: tl::Task (),
mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context),
mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst),
mp_intruder_cell (intruder_cell), m_dist (dist)
mp_intruder_cell (intruder_cell), m_dist (dist), m_override_distance (override_distance)
{
// This is quick, but will take away the intruders from the caller
m_intruders.swap (intruders);
@ -695,7 +702,7 @@ template <class TS, class TI, class TR>
void
local_processor_context_computation_task<TS, TI, TR>::perform ()
{
mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist);
mp_proc->compute_contexts (*mp_contexts, mp_parent_context, mp_subject_parent, mp_subject_cell, m_subject_cell_inst, mp_intruder_cell, m_intruders, m_dist, m_override_distance);
}
// ---------------------------------------------------------------------------------------------
@ -714,7 +721,6 @@ local_processor_result_computation_task<TS, TI, TR>::perform ()
{
mp_cell_contexts->compute_results (*mp_contexts, mp_cell, mp_op, m_output_layers, mp_proc);
// erase the contexts we don't need any longer
{
tl::MutexLocker locker (& mp_contexts->lock ());
@ -734,7 +740,10 @@ local_processor_result_computation_task<TS, TI, TR>::perform ()
}
#endif
mp_contexts->context_map ().erase (mp_cell);
// release some memory
auto ctx = mp_contexts->context_map ().find (mp_cell);
tl_assert (ctx != mp_contexts->context_map ().end ());
ctx->second.cleanup ();
}
}
@ -742,7 +751,7 @@ local_processor_result_computation_task<TS, TI, TR>::perform ()
// LocalProcessorBase implementation
LocalProcessorBase::LocalProcessorBase ()
: m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_boolean_core (false),
: m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_top_down (false), m_boolean_core (false),
m_base_verbosity (30), mp_vars (0), mp_current_cell (0)
{
// .. nothing yet ..
@ -881,15 +890,6 @@ void local_processor<TS, TI, TR>::run (local_operation<TS, TI, TR> *op, unsigned
compute_results (contexts, op, output_layers);
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set<TR> &result) const
{
if (! result.empty ()) {
tl::MutexLocker locker (&cell->layout ()->lock ());
cell->shapes (output_layer).insert (result.begin (), result.end ());
}
}
template <class TS, class TI, class TR>
void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, unsigned int subject_layer, const std::vector<unsigned int> &intruder_layers) const
{
@ -907,8 +907,18 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
contexts.set_intruder_layers (intruder_layers);
contexts.set_subject_layer (subject_layer);
// collect override distances per layer ID
auto od = op->override_distance ();
std::map<unsigned int, db::Coord> override_distance;
for (auto il = intruder_layers.begin (); il != intruder_layers.end (); ++il) {
auto o = od.find ((unsigned int) (il - intruder_layers.begin ()));
if (o != od.end ()) {
override_distance [*il] = o->second;
}
}
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type intruders;
issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ());
issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist (), override_distance);
if (mp_cc_job.get ()) {
mp_cc_job->start ();
@ -929,14 +939,15 @@ void local_processor<TS, TI, TR>::issue_compute_contexts (local_processor_contex
const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell,
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist) const
db::Coord dist,
const std::map<unsigned int, db::Coord> &override_distance) const
{
bool is_small_job = subject_cell->begin ().at_end ();
if (! is_small_job && mp_cc_job.get ()) {
mp_cc_job->schedule (new local_processor_context_computation_task<TS, TI, TR> (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist));
mp_cc_job->schedule (new local_processor_context_computation_task<TS, TI, TR> (this, contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist, override_distance));
} else {
compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist);
compute_contexts (contexts, parent_context, subject_parent, subject_cell, subject_cell_inst, intruder_cell, intruders, dist, override_distance);
}
}
@ -948,7 +959,8 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell,
const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders,
db::Coord dist_top) const
db::Coord dist_top,
const std::map<unsigned int, db::Coord> &override_distance) const
{
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
@ -1041,84 +1053,96 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
}
}
// TODO: can we shortcut this if interactions is empty?
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
// in top-down mode we are not interested in cell-to-cell interactions, nor shape-to-instance interactions
// except local ones (shape-to-child cells), hence we skip this part
if (! top_down ()) {
db::box_convert <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
// TODO: can we shortcut this if interactions is empty?
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il) {
db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner;
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), dist, &interactions);
unsigned int id = 0;
if (subject_cell == intruder_cell) {
// Use the same id's for same instances - this way we can easily detect same instances
// and don't make them self-interacting
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
unsigned int iid = ++id;
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), iid);
}
if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) {
scanner.insert2 (&i->cell_inst (), iid);
}
auto od = override_distance.find (*il);
db::Coord d = dist;
if (od != override_distance.end ()) {
d = std::min (d, od->second);
}
} else {
db::box_convert <db::CellInstArray, true> inst_bci (*mp_intruder_layout, contexts.actual_intruder_layer (*il));
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), ++id);
}
}
db::box_scanner2<db::CellInstArray, int, db::CellInstArray, int> scanner;
interaction_registration_inst2inst<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), mp_intruder_layout, contexts.actual_intruder_layer (*il), contexts.is_foreign (*il), d, &interactions);
if (intruder_cell) {
for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) {
unsigned int id = 0;
if (subject_cell == intruder_cell) {
// Use the same id's for same instances - this way we can easily detect same instances
// and don't make them self-interacting
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
unsigned int iid = ++id;
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), iid);
}
if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) {
scanner.insert2 (&i->cell_inst (), ++id);
scanner.insert2 (&i->cell_inst (), iid);
}
}
} else {
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), ++id);
}
}
if (intruder_cell) {
for (db::Cell::const_iterator i = intruder_cell->begin (); !i.at_end (); ++i) {
if (! inst_bci (i->cell_inst ()).empty () && ! intruder_cell_is_breakout (i->cell_index ())) {
scanner.insert2 (&i->cell_inst (), ++id);
}
}
}
}
for (std::set<db::CellInstArray>::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
if (! inst_bci (*i).empty ()) {
scanner.insert2 (i.operator-> (), ++id);
}
}
scanner.process (rec, dist, inst_bcs, inst_bci);
}
for (std::set<db::CellInstArray>::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
if (! inst_bci (*i).empty ()) {
scanner.insert2 (i.operator-> (), ++id);
if (! intruders.second.empty () || ! intruder_shapes.empty ()) {
db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
db::addressable_object_from_shape<TI> heap;
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, override_distance, &interactions);
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), 0);
}
}
}
scanner.process (rec, dist, inst_bcs, inst_bci);
}
if (! intruders.second.empty () || ! intruder_shapes.empty ()) {
db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
db::addressable_object_from_shape<TI> heap;
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
for (db::Cell::const_iterator i = subject_cell->begin (); !i.at_end (); ++i) {
if (! inst_bcs (i->cell_inst ()).empty () && ! subject_cell_is_breakout (i->cell_index ())) {
scanner.insert1 (&i->cell_inst (), 0);
for (typename std::map<unsigned int, std::set<TI> >::const_iterator il = intruders.second.begin (); il != intruders.second.end (); ++il) {
for (typename std::set<TI>::const_iterator i = il->second.begin (); i != il->second.end (); ++i) {
scanner.insert2 (i.operator-> (), il->first);
}
}
}
for (typename std::map<unsigned int, std::set<TI> >::const_iterator il = intruders.second.begin (); il != intruders.second.end (); ++il) {
for (typename std::set<TI>::const_iterator i = il->second.begin (); i != il->second.end (); ++i) {
scanner.insert2 (i.operator-> (), il->first);
for (std::map<unsigned int, const db::Shapes *>::const_iterator im = intruder_shapes.begin (); im != intruder_shapes.end (); ++im) {
for (db::Shapes::shape_iterator i = im->second->begin (shape_flags<TI> ()); !i.at_end (); ++i) {
scanner.insert2 (heap (*i), im->first);
}
}
}
for (std::map<unsigned int, const db::Shapes *>::const_iterator im = intruder_shapes.begin (); im != intruder_shapes.end (); ++im) {
for (db::Shapes::shape_iterator i = im->second->begin (shape_flags<TI> ()); !i.at_end (); ++i) {
scanner.insert2 (heap (*i), im->first);
}
}
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
}
}
@ -1128,7 +1152,7 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
db::Cell *subject_child_cell = &mp_subject_layout->cell (i->first.first);
db::Cell *intruder_child_cell = (subject_cell == intruder_cell ? subject_child_cell : 0);
issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist);
issue_compute_contexts (contexts, cell_context, subject_cell, subject_child_cell, i->first.second, intruder_child_cell, i->second, dist, override_distance);
}
@ -1248,7 +1272,7 @@ local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, T
typename local_processor_contexts<TS, TI, TR>::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu));
if (cpc != contexts.context_map ().end ()) {
cpc->second.compute_results (contexts, cpc->first, op, output_layers, this);
contexts.context_map ().erase (cpc);
cpc->second.cleanup (); // release some memory
}
}
@ -1261,6 +1285,24 @@ local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, T
}
}
// deliver the results
{
tl::MutexLocker locker (& mp_subject_layout->lock ());
for (auto c = contexts.begin (); c != contexts.end (); ++c) {
db::Cell *cell = c->first;
auto r = c->second.result ().begin ();
auto rend = c->second.result ().end ();
for (auto o = output_layers.begin (); r != rend && o != output_layers.end (); ++o, ++r) {
if (! r->empty ()) {
cell->shapes (*o).insert (r->begin (), r->end ());
}
}
}
}
}
namespace {
@ -1369,7 +1411,8 @@ template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const
{
db::Coord dist = dist_for_cell (subject_cell->cell_index (), op->dist ());
auto override_distance = op->override_distance ();
db::Coord dist_global = dist_for_cell (subject_cell->cell_index (), op->dist ());
const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ());
db::shape_to_object<TS> s2o;
@ -1395,6 +1438,13 @@ local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_conte
unsigned int il_index = 0;
for (std::vector<unsigned int>::const_iterator il = contexts.intruder_layers ().begin (); il != contexts.intruder_layers ().end (); ++il, ++il_index) {
// compute effective distance
db::Coord dist = dist_global;
auto od = override_distance.find (il_index);
if (od != override_distance.end ()) {
dist = std::min (dist, dist_for_cell (subject_cell->cell_index (), od->second));
}
unsigned int ail = contexts.actual_intruder_layer (*il);
bool foreign = contexts.is_foreign (*il);
@ -1406,14 +1456,14 @@ local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_conte
}
}
// local shapes vs. child cell
db::box_convert<db::CellInstArray, true> inst_bci (*mp_intruder_layout, ail);
typename std::map<unsigned int, std::set<TI> >::const_iterator ipl = intruders.second.find (*il);
static std::set<TI> empty_intruders;
if (! subject_shapes->empty () && (intruder_shapes || ipl != intruders.second.end ())) {
// local shapes vs. local shapes
if (! top_down () && ! subject_shapes->empty () && (intruder_shapes || ipl != intruders.second.end ())) {
if (subject_cell == intruder_cell && contexts.subject_layer () == ail && !foreign) {
@ -1428,6 +1478,8 @@ local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_conte
}
// local shapes vs. child cells
if (! subject_shapes->empty () && ! ((! intruder_cell || intruder_cell->begin ().at_end ()) && intruders.first.empty ())) {
db::box_scanner2<TS, int, db::CellInstArray, int> scanner;
@ -1441,7 +1493,7 @@ local_processor<TS, TI, TR>::compute_local_cell (const db::local_processor_conte
unsigned int inst_id = 0;
if (subject_cell == intruder_cell && contexts.subject_layer () == ail && !foreign) {
if (! top_down () && subject_cell == intruder_cell && contexts.subject_layer () == ail && !foreign) {
// Same cell, same layer -> no shape to child instance interactions because this will be taken care of
// by the instances themselves (and their intruders). This also means, we prefer to deal with
@ -1614,15 +1666,16 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
// build the subjects in the intruders list
db::Coord dist = op->dist ();
db::Coord dist_global = op->dist ();
auto override_distance = op->override_distance ();
db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist, dist);
db::Box subjects_box = safe_box_enlarged (subjects.bbox (), dist_global, dist_global);
db::Box intruders_box;
for (typename std::vector<generic_shape_iterator<TI> >::const_iterator il = intruders.begin (); il != intruders.end (); ++il) {
intruders_box += il->bbox ();
}
intruders_box = safe_box_enlarged (intruders_box, dist, dist);
intruders_box = safe_box_enlarged (intruders_box, dist_global, dist_global);
db::Box common_box = intruders_box & subjects_box;
if (common_box.empty () || common_box.width () == 0 || common_box.height () == 0) {
@ -1651,6 +1704,12 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
bool ff = foreign.size () > il_index && foreign [il_index];
db::Coord dist = dist_global;
auto od = override_distance.find (il_index);
if (od != override_distance.end ()) {
dist = std::min (dist, od->second);
}
if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description);
@ -1717,6 +1776,12 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
bool ff = foreign.size () > il_index && foreign [il_index];
db::Coord dist = dist_global;
auto od = override_distance.find (il_index);
if (od != override_distance.end ()) {
dist = std::min (dist, od->second);
}
if (*il == subjects && ! ff) {
interaction_registration_shape1_scanner_combo<TS, TI> scanner (&interactions, il_index, report_progress (), scan_description);
@ -1770,6 +1835,7 @@ local_processor<TS, TI, TR>::run_flat (const generic_shape_iterator<TS> &subject
}
}
}
}

View File

@ -244,9 +244,20 @@ public:
return m_contexts.end ();
}
const std::vector<std::unordered_set<TR> > &result () const
{
return m_result;
}
void cleanup ()
{
m_contexts.clear ();
}
private:
const db::Cell *mp_intruder_cell;
std::unordered_map<context_key_type, db::local_processor_cell_context<TS, TI, TR> > m_contexts;
std::vector<std::unordered_set<TR> > m_result;
};
template <class TS, class TI, class TR>
@ -348,7 +359,7 @@ class DB_PUBLIC local_processor_context_computation_task
: public tl::Task
{
public:
local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist);
local_processor_context_computation_task (const local_processor<TS, TI, TR> *proc, local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, db::Coord> &override_distance);
void perform ();
private:
@ -361,6 +372,7 @@ private:
const db::Cell *mp_intruder_cell;
typename local_processor_cell_contexts<TS, TI, TR>::context_key_type m_intruders;
db::Coord m_dist;
std::map<unsigned int, db::Coord> m_override_distance;
};
template <class TS, class TI, class TR>
@ -454,6 +466,16 @@ public:
return m_nthreads;
}
void set_top_down (bool f)
{
m_top_down = f;
}
bool top_down () const
{
return m_top_down;
}
void set_max_vertex_count (size_t max_vertex_count)
{
m_max_vertex_count = max_vertex_count;
@ -514,6 +536,7 @@ private:
unsigned int m_nthreads;
size_t m_max_vertex_count;
double m_area_ratio;
bool m_top_down;
bool m_boolean_core;
int m_base_verbosity;
const db::VariantsCollectorBase *mp_vars;
@ -556,9 +579,8 @@ private:
void next () const;
size_t get_progress () const;
void compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
void issue_compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
void push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set<TR> &result) const;
void compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, Coord> &override_distance) const;
void issue_compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist, const std::map<unsigned int, Coord> &override_distance) const;
void compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const;
bool subject_cell_is_breakout (db::cell_index_type ci) const

View File

@ -446,7 +446,12 @@ local_processor_cell_contexts<TS, TI, TR>::compute_results (const local_processo
proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, res);
}
if (common.empty ()) {
bool common_empty = true;
for (auto c = common.begin (); common_empty && c != common.end (); ++c) {
common_empty = c->empty ();
}
if (common_empty) {
CRONOLOGY_COMPUTE_BRACKET(event_propagate)
for (std::vector<unsigned int>::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) {
@ -519,9 +524,30 @@ local_processor_cell_contexts<TS, TI, TR>::compute_results (const local_processo
}
for (std::vector<unsigned int>::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) {
size_t oi = o - output_layers.begin ();
proc->push_results (cell, *o, common[oi]);
// store the results
bool common_empty = true;
for (auto c = common.begin (); common_empty && c != common.end (); ++c) {
common_empty = c->empty ();
}
if (! common_empty) {
if (m_result.empty ()) {
m_result.swap (common);
} else {
if (m_result.size () < common.size ()) {
m_result.resize (common.size ());
}
auto t = m_result.begin ();
for (auto s = common.begin (); s != common.end (); ++s, ++t) {
if (t->empty ()) {
t->swap (*s);
} else {
t->insert (s->begin (), s->end ());
s->clear ();
}
}
}
}
}

View File

@ -152,14 +152,14 @@ static std::pair<bool, std::set<db::Box> > compute_clip_variant (const db::Box &
}
HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
: mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans)
: mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans), m_sparse_array_limit (-1.0)
{
set_shape_receiver (pipe);
reset ();
}
HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe)
: mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans)
: mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans), m_sparse_array_limit (-1.0)
{
set_shape_receiver (pipe);
reset ();
@ -396,13 +396,56 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn
// for new cells, create this instance
if (m_cell_stack.back ().first || m_cm_new_entry) {
db::CellInstArray new_inst (inst, &mp_target->array_repository ());
new_inst.object () = db::CellInst (new_cell);
new_inst.transform (always_apply);
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);
// check if the cell array is "sparse" according to
// "sparse_array_limit" and resolve into single instances if so
bool resolve = false;
if (m_sparse_array_limit >= 0.0 && inst.size () > 1) {
if (m_sparse_array_limit == 0.0) {
resolve = true;
} else {
db::box_convert<db::CellInst> bc (*iter->layout ());
auto a1 = bc (inst.object ()).area ();
auto aa = inst.bbox (bc).area ();
if (a1 * m_sparse_array_limit * inst.size () < aa) {
resolve = true;
}
}
}
if (resolve) {
// resolve the instances of the array
for (auto i = inst.begin (); !i.at_end (); ++i) {
db::CellInstArray new_inst;
if (inst.is_complex ()) {
new_inst = db::CellInstArray (db::CellInst (new_cell), inst.complex_trans (*i));
} else {
new_inst = db::CellInstArray (db::CellInst (new_cell), *i);
}
new_inst.transform (always_apply);
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);
}
}
} else {
db::CellInstArray new_inst (inst, &mp_target->array_repository ());
new_inst.object () = db::CellInst (new_cell);
new_inst.transform (always_apply);
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);
}
}
}
// To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array.

View File

@ -314,6 +314,25 @@ public:
m_wants_all_cells = f;
}
/**
* @brief Sets the "sparse array" limit
*
* Sparse arrays are instance arrays whose bounding box is no longer a
* good approximation of the covered area. The "sparse array ratio" is
* the area of the bounding box divided by the area of the bounding box
* of a single instance.
*
* Arrays above this limit will be resolved into single instances.
*
* Setting this value to 0 will resolve all arrays. Setting this
* value to a negative value will never split arrays. The latter
* is the default.
*/
void set_sparse_array_limit (double l)
{
m_sparse_array_limit = l;
}
/**
* @brief Reset the builder - performs a new initial pass
*/
@ -440,6 +459,8 @@ private:
db::Cell *mp_initial_cell;
db::ICplxTrans m_trans;
double m_sparse_array_limit;
};
}

View File

@ -190,7 +190,7 @@ static bool read_ld (tl::Extractor &ex, ld_type &l, bool with_relative)
}
}
void
bool
LayerProperties::read (tl::Extractor &ex, bool as_target)
{
layer = db::any_ld ();
@ -207,6 +207,8 @@ LayerProperties::read (tl::Extractor &ex, bool as_target)
layer = l;
datatype = d;
return true;
} else if (ex.try_read_word_or_quoted (name)) {
if (ex.test ("(")) {
@ -223,6 +225,10 @@ LayerProperties::read (tl::Extractor &ex, bool as_target)
}
return true;
} else {
return false;
}
}

View File

@ -91,8 +91,10 @@ struct DB_PUBLIC LayerProperties
* relative layer/datatype specifications in the format "*+1" or "*-100".
* "*" for layer or datatype is for "don't care" (on input) or "leave as is"
* (for output).
*
* Returns true, if a layer was read successfully.
*/
void read (tl::Extractor &ex, bool as_target = false);
bool read (tl::Extractor &ex, bool as_target = false);
/**
* @brief "Logical" equality

View File

@ -337,6 +337,37 @@ private:
// -----------------------------------------------------------------
// Implementation of the ProxyContextInfo class
bool
LayoutOrCellContextInfo::operator== (const LayoutOrCellContextInfo &other) const
{
return lib_name == other.lib_name &&
cell_name == other.cell_name &&
pcell_name == other.pcell_name &&
pcell_parameters == other.pcell_parameters &&
meta_info == other.meta_info;
}
bool
LayoutOrCellContextInfo::operator< (const LayoutOrCellContextInfo &other) const
{
if (lib_name != other.lib_name) {
return lib_name < other.lib_name;
}
if (cell_name != other.cell_name) {
return cell_name < other.cell_name;
}
if (pcell_name != other.pcell_name) {
return pcell_name < other.pcell_name;
}
if (pcell_parameters != other.pcell_parameters) {
return pcell_parameters < other.pcell_parameters;
}
if (meta_info != other.meta_info) {
return meta_info < other.meta_info;
}
return false;
}
LayoutOrCellContextInfo
LayoutOrCellContextInfo::deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to)
{
@ -540,6 +571,7 @@ Layout::clear ()
m_pcell_ids.clear ();
m_lib_proxy_map.clear ();
m_cold_proxy_map.clear ();
m_meta_info.clear ();
}
@ -568,6 +600,7 @@ Layout::operator= (const Layout &d)
}
m_lib_proxy_map = d.m_lib_proxy_map;
m_cold_proxy_map = d.m_cold_proxy_map;
m_cell_ptrs.resize (d.m_cell_ptrs.size (), 0);
@ -674,14 +707,12 @@ Layout::set_technology_name (const std::string &tech)
for (db::Layout::iterator c = begin (); c != end (); ++c) {
std::map<db::lib_id_type, db::lib_id_type>::const_iterator m;
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (&*c);
if (! lib_proxy) {
continue;
}
if ((m = mapping.find (lib_proxy->lib_id ())) != mapping.end ()) {
if (mapping.find (lib_proxy->lib_id ()) != mapping.end ()) {
db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ());
db::Cell *lib_cell = &lib->layout ().cell (lib_proxy->library_cell_index ());
@ -712,7 +743,10 @@ Layout::set_technology_name (const std::string &tech)
db::cell_index_type ci = lp->first->Cell::cell_index ();
db::PCellVariant *lib_pcell = lp->second;
std::pair<bool, pcell_id_type> pn = lib_pcell->layout ()->pcell_by_name (lp->first->get_basic_name ().c_str ());
db::Library *new_lib = db::LibraryManager::instance ().lib (mapping [lp->first->lib_id ()]);
tl_assert (new_lib != 0);
std::pair<bool, pcell_id_type> pn = new_lib->layout ().pcell_by_name (lp->first->get_basic_name ().c_str ());
if (! pn.first) {
@ -723,8 +757,6 @@ Layout::set_technology_name (const std::string &tech)
} else {
db::Library *new_lib = db::LibraryManager::instance ().lib (mapping [lp->first->lib_id ()]);
const db::PCellDeclaration *old_pcell_decl = lib_pcell->layout ()->pcell_declaration (lib_pcell->pcell_id ());
const db::PCellDeclaration *new_pcell_decl = new_lib->layout ().pcell_declaration (pn.second);
if (! old_pcell_decl || ! new_pcell_decl) {
@ -809,6 +841,7 @@ Layout::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat
db::mem_stat (stat, purpose, cat, m_pcells, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_pcell_ids, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_lib_proxy_map, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_cold_proxy_map, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_meta_info, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_shape_repository, true, (void *) this);
db::mem_stat (stat, purpose, cat, m_array_repository, true, (void *) this);
@ -1417,7 +1450,7 @@ Layout::add_cell (const char *name)
if (cm != m_cell_map.end ()) {
const db::Cell &c= cell (cm->second);
if (c.is_ghost_cell () && c.empty ()) {
if (c.is_real_ghost_cell ()) {
// ghost cells are available as new cells - the idea is to
// treat them as non-existing.
return cm->second;
@ -1505,6 +1538,11 @@ Layout::register_cell_name (const char *name, cell_index_type ci)
void
Layout::rename_cell (cell_index_type id, const char *name)
{
static const char *anonymous_name = "";
if (! name) {
name = anonymous_name;
}
tl_assert (id < m_cell_names.size ());
if (strcmp (m_cell_names [id], name) != 0) {
@ -1521,7 +1559,10 @@ Layout::rename_cell (cell_index_type id, const char *name)
delete [] m_cell_names [id];
m_cell_names [id] = cp;
m_cell_map.insert (std::make_pair (cp, id));
// NOTE: anonymous cells (empty name string) are not registered in the cell name map
if (*cp != 0) {
m_cell_map.insert (std::make_pair (cp, id));
}
// to enforce a redraw and a rebuild
cell_name_changed ();
@ -1681,6 +1722,84 @@ Layout::refresh ()
}
}
std::set<db::cell_index_type>
Layout::cells_to_cleanup (const std::set<db::cell_index_type> &keep_always) const
{
std::set<db::cell_index_type> to_clean;
// Adressing issue #1835 (reading proxy-only GDS file renders empty layout) we do not delete
// the first (non-cold) proxy if there are only proxy top cells.
// We never clean up the top cell if there is a single one. This catches the case of having
// defunct proxies for top cells.
if (end_top_cells () - begin_top_down () > 1) {
std::set<db::cell_index_type> keep;
for (auto c = begin_top_down (); c != end_top_cells (); ++c) {
const db::Cell *cptr = &cell (*c);
if (cptr->is_proxy ()) {
if (! dynamic_cast <const db::ColdProxy *> (cptr) && keep.empty ()) {
keep.insert (*c);
}
} else {
keep.clear ();
break;
}
}
for (auto c = begin_top_down (); c != end_top_cells (); ++c) {
if (cell (*c).is_proxy () && keep.find (*c) == keep.end () && keep_always.find (*c) == keep_always.end ()) {
to_clean.insert (*c);
}
}
}
// determine all cells that can be deleted as well because they are called
// by cells registered for cleanup and nowhere else
if (! to_clean.empty ()) {
// collect the called cells
std::set <db::cell_index_type> all_called;
for (auto c = to_clean.begin (); c != to_clean.end (); ++c) {
cell (*c).collect_called_cells (all_called, -1);
}
// remove all called cells which are not proxies - we don't want to
// clean up those and their children.
std::set <db::cell_index_type> called;
for (auto c = all_called.begin (); c != all_called.end (); ++c) {
if (cell (*c).is_proxy ()) {
called.insert (*c);
}
}
// From these cells erase all cells that have parents outside the subtree of our cell.
// Make sure this is done recursively by doing this top-down.
for (auto c = begin_top_down (); c != end_top_down (); ++c) {
if (called.find (*c) != called.end ()) {
const db::Cell &ccref = cell (*c);
for (db::Cell::parent_cell_iterator pc = ccref.begin_parent_cells (); pc != ccref.end_parent_cells (); ++pc) {
if (to_clean.find (*pc) == to_clean.end () && called.find (*pc) == called.end ()) {
// we have a parent outside the subset considered currently (either the cell was never in or
// it was removed itself already): remove this cell from the set of valid subcells.
called.erase (*c);
break;
}
}
}
}
to_clean.insert (called.begin (), called.end ());
}
return to_clean;
}
void
Layout::cleanup (const std::set<db::cell_index_type> &keep)
{
@ -1690,27 +1809,109 @@ Layout::cleanup (const std::set<db::cell_index_type> &keep)
return;
}
// deleting cells may create new top cells which need to be deleted as well, hence we iterate
// until there are no more cells to delete
while (true) {
if (tl::verbosity () >= 30) {
tl::info << "Cleaning up layout ..";
}
// delete all cells that are top cells and are proxies. Those cells are proxies no longer required.
std::set<cell_index_type> cells_to_delete;
for (top_down_iterator c = begin_top_down (); c != end_top_cells (); ++c) {
if (cell (*c).is_proxy ()) {
cells_to_delete.insert (*c);
// Do some polishing of the proxies - sometimes, specifically when resolving indirect library references,
// different proxies to the same library object exist. We can identify them and clean them up, so there
// is a single reference. We can also try to ensure that cell names reflect the library cell names.
// The latter is good for LVS for example.
{
update();
db::LayoutLocker locker (this);
// join library proxies pointing to the same object
for (auto c = m_lib_proxy_map.begin (); c != m_lib_proxy_map.end (); ) {
auto c0 = c++;
size_t n = 1;
while (c != m_lib_proxy_map.end () && c->first == c0->first) {
++n;
++c;
}
if (n > 1) {
auto cc = c0;
++cc;
while (cc != c) {
if (keep.find (cc->second) == keep.end ()) {
if (tl::verbosity () >= 30) {
tl::info << "Joining lib proxy " << cell_name (cc->second) << " into " << cell_name (c0->second);
}
replace_instances_of (cc->second, c0->second);
}
++cc;
}
}
}
for (std::set<db::cell_index_type>::const_iterator k = keep.begin (); k != keep.end (); ++k) {
cells_to_delete.erase (*k);
// join cold proxies pointing to the same object
for (auto c = m_cold_proxy_map.begin (); c != m_cold_proxy_map.end (); ) {
auto c0 = c++;
size_t n = 1;
while (c != m_cold_proxy_map.end () && c->first == c0->first) {
++n;
++c;
}
if (n > 1) {
auto cc = c0;
++cc;
while (cc != c) {
if (keep.find (cc->second) == keep.end ()) {
if (tl::verbosity () >= 30) {
tl::info << "Joining cold proxy " << cell_name (cc->second) << " into " << cell_name (c0->second);
}
replace_instances_of (cc->second, c0->second);
}
++cc;
}
}
}
if (cells_to_delete.empty ()) {
break;
}
// Remove all cells that are targeted for cleanup
{
std::set<cell_index_type> cells_to_delete = cells_to_cleanup (keep);
if (! cells_to_delete.empty ()) {
delete_cells (cells_to_delete);
}
}
// Try to ensure that cell names reflect the library cell names. The latter is good for LVS for example.
for (auto c = m_lib_proxy_map.begin (); c != m_lib_proxy_map.end (); ++c) {
std::string bn = cell (c->second).get_basic_name ();
if (bn != cell_name (c->second) && ! cell_by_name (bn.c_str ()).first) {
if (tl::verbosity () >= 30) {
tl::info << "Renaming lib proxy " << cell_name (c->second) << " to " << bn;
}
rename_cell (c->second, bn.c_str ());
}
delete_cells (cells_to_delete);
}
for (auto c = m_cold_proxy_map.begin (); c != m_cold_proxy_map.end (); ++c) {
std::string bn = cell (c->second).get_basic_name ();
if (bn != cell_name (c->second) && ! cell_by_name (bn.c_str ()).first) {
if (tl::verbosity () >= 30) {
tl::info << "Renaming cold proxy " << cell_name (c->second) << " to " << bn;
}
rename_cell (c->second, bn.c_str ());
}
}
}
@ -2830,12 +3031,7 @@ Layout::has_context_info (cell_index_type cell_index) const
}
}
const db::Cell &cref = cell (cell_index);
if (cref.is_proxy () && ! cref.is_top ()) {
return true;
} else {
return false;
}
return cell (cell_index).is_proxy ();
}
bool
@ -2987,6 +3183,14 @@ Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrC
void
Layout::restore_proxies (ImportLayerMapping *layer_mapping)
{
if (restore_proxies_without_cleanup (layer_mapping)) {
cleanup ();
}
}
bool
Layout::restore_proxies_without_cleanup (ImportLayerMapping *layer_mapping)
{
std::vector<db::ColdProxy *> cold_proxies;
@ -3004,9 +3208,7 @@ Layout::restore_proxies (ImportLayerMapping *layer_mapping)
}
}
if (needs_cleanup) {
cleanup ();
}
return needs_cleanup;
}
bool
@ -3145,13 +3347,32 @@ Layout::variant_name (cell_index_type cell_index) const
void
Layout::register_lib_proxy (db::LibraryProxy *lib_proxy)
{
m_lib_proxy_map.insert (std::make_pair (std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ()), lib_proxy->Cell::cell_index ()));
auto key = std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ());
auto l = m_lib_proxy_map.find (key);
while (l != m_lib_proxy_map.end () && l->first == key) {
if (l->second == lib_proxy->Cell::cell_index ()) {
return;
}
++l;
}
m_lib_proxy_map.insert (std::make_pair (key, lib_proxy->Cell::cell_index ()));
}
void
Layout::unregister_lib_proxy (db::LibraryProxy *lib_proxy)
{
m_lib_proxy_map.erase (std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ()));
auto key = std::make_pair (lib_proxy->lib_id (), lib_proxy->library_cell_index ());
auto l = m_lib_proxy_map.find (key);
while (l != m_lib_proxy_map.end () && l->first == key) {
if (l->second == lib_proxy->Cell::cell_index ()) {
m_lib_proxy_map.erase (l);
break;
}
++l;
}
}
void
@ -3171,9 +3392,13 @@ Layout::get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_t
cell_index_type
Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
{
lib_proxy_map::const_iterator lp = m_lib_proxy_map.find (std::make_pair (lib->get_id (), cell_index));
if (lp != m_lib_proxy_map.end ()) {
auto key = std::make_pair (lib->get_id (), cell_index);
lib_proxy_map::const_iterator lp = m_lib_proxy_map.find (key);
if (lp != m_lib_proxy_map.end () && lp->first == key) {
return lp->second;
} else {
// create a new unique name
@ -3204,35 +3429,82 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
}
}
void
Layout::register_cold_proxy (db::ColdProxy *cold_proxy)
{
auto l = m_cold_proxy_map.find (cold_proxy->context_info ());
while (l != m_cold_proxy_map.end () && l->first == cold_proxy->context_info ()) {
if (l->second == cold_proxy->Cell::cell_index ()) {
return;
}
++l;
}
m_cold_proxy_map.insert (std::make_pair (cold_proxy->context_info (), cold_proxy->Cell::cell_index ()));
}
void
Layout::unregister_cold_proxy (db::ColdProxy *cold_proxy)
{
auto l = m_cold_proxy_map.find (cold_proxy->context_info ());
while (l != m_cold_proxy_map.end () && l->first == cold_proxy->context_info ()) {
if (l->second == cold_proxy->Cell::cell_index ()) {
m_cold_proxy_map.erase (l);
break;
}
++l;
}
}
std::pair<bool, cell_index_type>
Layout::find_cold_proxy (const db::LayoutOrCellContextInfo &info)
{
cold_proxy_map::const_iterator lp = m_cold_proxy_map.find (info);
if (lp != m_cold_proxy_map.end () && lp->first == info) {
return std::make_pair (true, lp->second);
} else {
return std::make_pair (false, 0);
}
}
cell_index_type
Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info)
{
// create a new unique name
std::string b;
if (! info.cell_name.empty ()) {
b = info.cell_name;
} else if (! info.pcell_name.empty ()) {
b = info.pcell_name;
cold_proxy_map::const_iterator lp = m_cold_proxy_map.find (info);
if (lp != m_cold_proxy_map.end () && lp->first == info) {
return lp->second;
} else {
// create a new unique name
std::string b;
if (! info.cell_name.empty ()) {
b = info.cell_name;
} else if (! info.pcell_name.empty ()) {
b = info.pcell_name;
}
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
b = uniquify_cell_name (b.c_str ());
}
// create a new cell (a LibraryProxy)
cell_index_type new_index = allocate_new_cell ();
ColdProxy *proxy = new ColdProxy (new_index, *this, info);
m_cells.push_back_ptr (proxy);
m_cell_ptrs [new_index] = proxy;
// enter its index and cell_name
register_cell_name (b.c_str (), new_index);
if (manager () && manager ()->transacting ()) {
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
}
return new_index;
}
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
b = uniquify_cell_name (b.c_str ());
}
// create a new cell (a LibraryProxy)
cell_index_type new_index = allocate_new_cell ();
ColdProxy *proxy = new ColdProxy (new_index, *this, info);
m_cells.push_back_ptr (proxy);
m_cell_ptrs [new_index] = proxy;
// enter its index and cell_name
register_cell_name (b.c_str (), new_index);
if (manager () && manager ()->transacting ()) {
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
}
return new_index;
}
void

View File

@ -62,6 +62,7 @@ class PCellDeclaration;
class PCellHeader;
class Library;
class LibraryProxy;
class ColdProxy;
class CellMapping;
class LayerMapping;
class Region;
@ -427,6 +428,9 @@ struct DB_PUBLIC LayoutOrCellContextInfo
std::map<std::string, tl::Variant> pcell_parameters;
std::map<std::string, std::pair<tl::Variant, std::string> > meta_info;
bool operator== (const LayoutOrCellContextInfo &other) const;
bool operator< (const LayoutOrCellContextInfo &other) const;
static LayoutOrCellContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
void serialize (std::vector<std::string> &strings);
@ -469,7 +473,8 @@ public:
typedef db::pcell_id_type pcell_id_type;
typedef std::map<std::string, pcell_id_type> pcell_name_map;
typedef pcell_name_map::const_iterator pcell_iterator;
typedef std::map<std::pair<lib_id_type, cell_index_type>, cell_index_type> lib_proxy_map;
typedef std::multimap<std::pair<lib_id_type, cell_index_type>, cell_index_type> lib_proxy_map;
typedef std::multimap<db::LayoutOrCellContextInfo, cell_index_type> cold_proxy_map;
typedef LayerIterator layer_iterator;
typedef size_t meta_info_name_id_type;
typedef std::map<meta_info_name_id_type, MetaInfo> meta_info_map;
@ -1003,6 +1008,12 @@ public:
*/
void get_lib_proxy_as (Library *lib, cell_index_type cell_index, cell_index_type target_cell_index, ImportLayerMapping *layer_mapping = 0, bool retain_layout = false);
/**
* @brief Find an existing cold proxy for a given context
* @return A pair of success flag and cell index of the proxy
*/
std::pair<bool, cell_index_type> find_cold_proxy (const db::LayoutOrCellContextInfo &info);
/**
* @brief Creates a cold proxy representing the given context information
*/
@ -1115,7 +1126,16 @@ public:
* Library updates may enabled lost connections which are help in cold proxies. This method will recover
* these connections.
*/
void restore_proxies(ImportLayerMapping *layer_mapping = 0);
void restore_proxies (ImportLayerMapping *layer_mapping = 0);
/**
* @brief Restores proxies as far as possible, no cleanup included
*
* This method is equivalent to "restore_proxies", but does not include a cleanup.
* Instead it returns a value of true, indicating that something got changed
* and a cleanup is required.
*/
bool restore_proxies_without_cleanup (ImportLayerMapping *layer_mapping = 0);
/**
* @brief Replaces the given cell index with the new cell
@ -1662,6 +1682,19 @@ public:
*/
void cleanup (const std::set<db::cell_index_type> &keep = std::set<db::cell_index_type> ());
/**
* @brief Gets a list of cells which are cleanup candidates
*
* The returned list also includes cells that are indirectly cleaned up.
* The cleanup only removes top cells which represent unused proxies
* with some exceptions - for example proxies that are single top cells
* are retained.
*
* However, removing the top cells should not produce new top cells
* of proxies, so these cells need to be removed as well.
*/
std::set<db::cell_index_type> cells_to_cleanup (const std::set<db::cell_index_type> &keep = std::set<db::cell_index_type> ()) const;
/**
* @brief Calls "update" on all cells of the layout
*
@ -1830,6 +1863,20 @@ public:
*/
void unregister_lib_proxy (db::LibraryProxy *lib_proxy);
/**
* @brief Register a cold proxy
*
* This method is used by ColdProxy to register itself.
*/
void register_cold_proxy (db::ColdProxy *cold_proxy);
/**
* @brief Unregister a cold proxy
*
* This method is used by ColdProxy to unregister itself.
*/
void unregister_cold_proxy (db::ColdProxy *cold_proxy);
/**
* @brief Gets the editable status of this layout
*
@ -2144,6 +2191,7 @@ private:
std::vector<pcell_header_type *> m_pcells;
pcell_name_map m_pcell_ids;
lib_proxy_map m_lib_proxy_map;
cold_proxy_map m_cold_proxy_map;
bool m_do_cleanup;
bool m_editable;
std::map<std::string, meta_info_name_id_type> m_meta_info_name_map;

View File

@ -183,6 +183,14 @@ public:
return m_busy;
}
/**
* @brief Issue a "cell name changed event"
*/
void cell_name_changed ()
{
cell_name_changed_event ();
}
protected:
friend class PropertiesRepository;
@ -191,14 +199,6 @@ protected:
*/
virtual void do_update () { }
/**
* @brief Issue a "prop id's changed event"
*/
void cell_name_changed ()
{
cell_name_changed_event ();
}
/**
* @brief Issue a "layer properties changed event"
*/

View File

@ -458,7 +458,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
std::string param_name;
read_word_or_quoted (param_name);
int primary = read_int ();
int default_value = read_double ();
double default_value = read_double ();
if (! dc->has_parameter_with_name (param_name)) {
db::DeviceParameterDefinition pd;
pd.set_name (param_name);

View File

@ -218,6 +218,7 @@ copy_or_propagate_shapes (db::Layout &target,
db::Cell &target_cell = target.cell (cm->second);
transformer->insert_transformed (target_cell.shapes (target_layer), source_cell.shapes (source_layer), trans * propagate_trans);
}
}

View File

@ -26,20 +26,24 @@
#include "dbPCellDeclaration.h"
#include "dbPCellVariant.h"
#include "dbLibraryManager.h"
#include "tlTimer.h"
#include <limits>
namespace db
{
Library::Library()
: m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (true)
Library::Library ()
: m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (true), m_replicate (true)
{
m_layout.set_library (this);
}
Library::Library(const Library &d)
: gsi::ObjectBase (), tl::Object (), m_name (d.m_name), m_description (d.m_description), m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (d.m_layout)
Library::Library (const Library &d)
: gsi::ObjectBase (), tl::Object (),
m_name (d.m_name), m_description (d.m_description),
m_id (std::numeric_limits<lib_id_type>::max ()), m_layout (d.m_layout),
m_replicate (d.m_replicate)
{
m_layout.set_library (this);
}
@ -55,7 +59,7 @@ Library::~Library ()
bool
Library::is_for_technology (const std::string &name) const
{
return m_technologies.find (name) != m_technologies.end ();
return (! m_technologies.empty () && name == "*") || m_technologies.find (name) != m_technologies.end ();
}
bool
@ -64,6 +68,12 @@ Library::for_technologies () const
return ! m_technologies.empty ();
}
void
Library::set_technologies (const std::set<std::string> &t)
{
m_technologies = t;
}
void
Library::set_technology (const std::string &t)
{
@ -108,10 +118,12 @@ Library::unregister_proxy (db::LibraryProxy *lib_proxy, db::Layout *ly)
if (! --c->second) {
db::cell_index_type ci = c->first;
m_refcount.erase (c);
// remove cells which are itself proxies and are no longer used
db::Cell *lib_cell = &layout ().cell (ci);
if (lib_cell && lib_cell->is_proxy () && lib_cell->parent_cells () == 0) {
layout ().delete_cell (ci);
if (layout ().is_valid_cell_index (ci)) {
// remove cells which are itself proxies and are no longer used
db::Cell *lib_cell = &layout ().cell (ci);
if (lib_cell && lib_cell->is_proxy () && lib_cell->parent_cells () == 0) {
layout ().delete_cell (ci);
}
}
}
retired_state_changed_event ();
@ -145,6 +157,12 @@ Library::is_retired (const db::cell_index_type library_cell_index) const
return (i != m_refcount.end () && j != m_retired_count.end () && i->second == j->second);
}
void
Library::set_replicate (bool f)
{
m_replicate = f;
}
void
Library::rename (const std::string &name)
{
@ -164,122 +182,175 @@ Library::rename (const std::string &name)
void
Library::refresh ()
{
std::string name = reload ();
rename (name);
refresh_without_restore ();
layout ().refresh ();
// proxies need to be restored in a special case when a library has a proxy to itself.
// The layout readers will not be able to create library references in this case.
layout ().restore_proxies ();
}
void
Library::refresh_without_restore ()
{
db::LibraryManager::instance ().unregister_lib (this);
try {
m_name = reload ();
} catch (...) {
// fallback - leave the library, but with empty layout
layout ().clear ();
db::LibraryManager::instance ().register_lib (this);
throw;
}
// re-register, potentially under the new name
db::LibraryManager::instance ().register_lib (this);
// This is basically only needed for coerce_parameters in a persistent way.
// During "register_lib", this is already called in PCell update, but cannot be persisted then.
remap_to (this);
}
void
Library::remap_to (db::Library *other)
Library::remap_to (db::Library *other, db::Layout *original_layout)
{
if (! original_layout) {
original_layout = &layout ();
}
// Hint: in the loop over the referrers we might unregister (delete from m_referrers) a referrer because no more cells refer to us.
// Hence we must not directly iterate of m_referrers.
std::vector<std::pair<db::Layout *, int> > referrers;
std::vector<db::Layout *> referrers;
for (std::map<db::Layout *, int>::const_iterator r = m_referrers.begin (); r != m_referrers.end (); ++r) {
referrers.push_back (*r);
referrers.push_back (r->first);
}
// Sort for deterministic order of resolution
std::sort (referrers.begin (), referrers.end (), tl::sort_by_id ());
// Remember the layouts that will finally need a cleanup
std::set<db::Layout *> needs_cleanup;
for (std::vector<std::pair<db::Layout *, int> >::const_iterator r = referrers.begin (); r != referrers.end (); ++r) {
// NOTE: resolution may create new references due to replicas.
// Hence, loop until no further references are resolved.
bool any = true;
while (any) {
std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map;
std::vector<db::LibraryProxy *> lib_cells_to_map;
any = false;
for (db::Layout::iterator c = r->first->begin (); c != r->first->end (); ++c) {
for (std::vector<db::Layout *>::const_iterator r = referrers.begin (); r != referrers.end (); ++r) {
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (&*c);
if (lib_proxy && lib_proxy->lib_id () == get_id ()) {
std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> > pcells_to_map;
std::vector<db::LibraryProxy *> lib_cells_to_map;
for (auto c = (*r)->begin (); c != (*r)->end (); ++c) {
db::LibraryProxy *lib_proxy = dynamic_cast<db::LibraryProxy *> (c.operator-> ());
if (lib_proxy && lib_proxy->lib_id () == get_id ()) {
if (! original_layout->is_valid_cell_index (lib_proxy->library_cell_index ())) {
// safety feature, should not happen
continue;
}
db::Cell *lib_cell = &original_layout->cell (lib_proxy->library_cell_index ());
db::PCellVariant *lib_pcell = dynamic_cast <db::PCellVariant *> (lib_cell);
if (lib_pcell) {
pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell));
} else {
lib_cells_to_map.push_back (lib_proxy);
}
needs_cleanup.insert (*r);
db::Cell *lib_cell = &layout ().cell (lib_proxy->library_cell_index ());
db::PCellVariant *lib_pcell = dynamic_cast <db::PCellVariant *> (lib_cell);
if (lib_pcell) {
pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell));
} else {
lib_cells_to_map.push_back (lib_proxy);
}
needs_cleanup.insert (r->first);
}
}
// We do PCell resolution before the library proxy resolution. The reason is that
// PCells may generate library proxies in their instantiation. Hence we must instantiate
// the PCells before we can resolve them.
for (std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> >::const_iterator lp = pcells_to_map.begin (); lp != pcells_to_map.end (); ++lp) {
// We do PCell resolution before the library proxy resolution. The reason is that
// PCells may generate library proxies in their instantiation. Hence we must instantiate
// the PCells before we can resolve them.
for (std::vector<std::pair<db::LibraryProxy *, db::PCellVariant *> >::const_iterator lp = pcells_to_map.begin (); lp != pcells_to_map.end (); ++lp) {
db::cell_index_type ci = lp->first->Cell::cell_index ();
db::PCellVariant *lib_pcell = lp->second;
db::cell_index_type ci = lp->first->Cell::cell_index ();
db::PCellVariant *lib_pcell = lp->second;
std::pair<bool, pcell_id_type> pn (false, 0);
if (other) {
pn = other->layout ().pcell_by_name (original_layout->cell (lp->first->library_cell_index ()).get_basic_name ().c_str ());
}
std::pair<bool, pcell_id_type> pn (false, 0);
if (other) {
pn = other->layout ().pcell_by_name (lp->first->get_basic_name ().c_str ());
}
if (! pn.first) {
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
} else {
const db::PCellDeclaration *old_pcell_decl = layout ().pcell_declaration (lib_pcell->pcell_id ());
const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second);
if (! old_pcell_decl || ! new_pcell_decl) {
if (! pn.first) {
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
(*r)->get_context_info (ci, info);
(*r)->create_cold_proxy_as (info, ci);
} else {
db::pcell_parameters_type new_parameters = new_pcell_decl->map_parameters (lib_pcell->parameters_by_name ());
const db::PCellDeclaration *old_pcell_decl = original_layout->pcell_declaration (lib_pcell->pcell_id ());
const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second);
if (! old_pcell_decl || ! new_pcell_decl) {
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
(*r)->get_context_info (ci, info);
(*r)->create_cold_proxy_as (info, ci);
} else {
db::pcell_parameters_type new_parameters = new_pcell_decl->map_parameters (lib_pcell->parameters_by_name ());
// coerce the new parameters if requested
try {
db::pcell_parameters_type plist = new_parameters;
new_pcell_decl->coerce_parameters (other->layout (), plist);
plist.swap (new_parameters);
} catch (tl::Exception &ex) {
// ignore exception - we will do that again on update() to establish an error message
tl::error << ex.msg ();
}
lp->first->remap (other->get_id (), other->layout ().get_pcell_variant (pn.second, new_parameters));
// coerce the new parameters if requested
try {
db::pcell_parameters_type plist = new_parameters;
new_pcell_decl->coerce_parameters (other->layout (), plist);
plist.swap (new_parameters);
} catch (tl::Exception &ex) {
// ignore exception - we will do that again on update() to establish an error message
tl::error << ex.msg ();
}
lp->first->remap (other->get_id (), other->layout ().get_pcell_variant (pn.second, new_parameters));
}
}
}
for (std::vector<db::LibraryProxy *>::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) {
for (std::vector<db::LibraryProxy *>::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) {
db::cell_index_type ci = (*lp)->Cell::cell_index ();
db::cell_index_type ci = (*lp)->Cell::cell_index ();
std::pair<bool, cell_index_type> cn (false, 0);
if (other) {
cn = other->layout ().cell_by_name (original_layout->cell_name ((*lp)->library_cell_index ()));
}
std::pair<bool, cell_index_type> cn (false, 0);
if (other) {
cn = other->layout ().cell_by_name ((*lp)->get_basic_name ().c_str ());
}
if (! cn.first) {
if (! cn.first) {
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
(*r)->get_context_info (ci, info);
(*r)->create_cold_proxy_as (info, ci);
// substitute by a cold proxy
db::LayoutOrCellContextInfo info;
r->first->get_context_info (ci, info);
r->first->create_cold_proxy_as (info, ci);
} else {
} else {
if ((*lp)->lib_id () != other->get_id () || (*lp)->library_cell_index () != cn.second) {
// we potentially need another iteration
any = true;
}
(*lp)->remap (other->get_id (), cn.second);
(*lp)->remap (other->get_id (), cn.second);
}
}

View File

@ -53,12 +53,12 @@ public:
/**
* @brief The constructor
*/
Library();
Library ();
/**
* @brief Copy constructor
*/
Library(const Library &);
Library (const Library &);
/**
* @brief The destructor
@ -138,6 +138,13 @@ public:
*/
bool for_technologies () const;
/**
* @brief Sets the technology names this library is associated with
*
* This will reset the list of technologies to this set.
*/
void set_technologies (const std::set<std::string> &t);
/**
* @brief Sets the technology name this library is associated with
*
@ -172,6 +179,27 @@ public:
m_description = description;
}
/**
* @brief Sets a value indicating whether the library produces replicas
*
* If this value is true (the default), layout written will include the
* actual layout of a library cell (replica). With this, it is possible
* to regenerate the layout without actually having the library at the
* cost of additional bytes in the file.
*
* Setting this flag to false avoids this replication, but a layout
* cannot be regenerated without having this library.
*/
void set_replicate (bool f);
/**
* @brief Gets a value indicating whether the library produces replicas
*/
bool replicate () const
{
return m_replicate;
}
/**
* @brief Getter for the library Id property
*/
@ -225,6 +253,13 @@ public:
*/
void refresh ();
/**
* @brief Refreshes the library on all clients without restoring proxies
*
* This method is intended to be used internally for bulk refreshes.
*/
void refresh_without_restore ();
/**
* @brief Renames the library
*
@ -237,8 +272,11 @@ public:
* @brief Remap the library proxies to a different library
*
* After remapping, "other" can replace "this".
* When calling with "other=this", a pointer to the original
* layout needs to be supplied, because in that case, the
* layout of "this" is already replaced.
*/
void remap_to (db::Library *other);
void remap_to (db::Library *other, Layout *original_layout = 0);
/**
* @brief This event is fired if proxies get retired on unretired
@ -253,6 +291,7 @@ private:
db::Layout m_layout;
std::map<db::Layout *, int> m_referrers;
std::map<db::cell_index_type, int> m_refcount, m_retired_count;
bool m_replicate;
// no copying.
Library &operator=(const Library &);

View File

@ -38,7 +38,7 @@ namespace db
static LibraryManager *sp_instance (0);
LibraryManager &
DB_PUBLIC LibraryManager &
LibraryManager::instance ()
{
if (sp_instance == 0) {
@ -249,13 +249,38 @@ LibraryManager::register_lib (Library *library)
// "restore_proxies" takes care not to re-substitute cold proxies.
const tl::weak_collection<db::ColdProxy> &cold_proxies = db::ColdProxy::cold_proxies_per_lib_name (library->get_name ());
std::set<db::Layout *> to_refresh;
std::set<db::Layout *> to_refresh_set;
for (tl::weak_collection<db::ColdProxy>::const_iterator p = cold_proxies.begin (); p != cold_proxies.end (); ++p) {
to_refresh.insert (const_cast<db::Layout *> (p->layout ()));
to_refresh_set.insert (const_cast<db::Layout *> (p->layout ()));
}
for (std::set<db::Layout *>::const_iterator l = to_refresh.begin (); l != to_refresh.end (); ++l) {
(*l)->restore_proxies (0);
// Sort for deterministic order of resolution
std::vector<db::Layout *> to_refresh (to_refresh_set.begin (), to_refresh_set.end ());
std::sort (to_refresh.begin (), to_refresh.end (), tl::sort_by_id ());
std::set<db::Layout *> needs_cleanup;
// NOTE: "restore proxies" can create new proxies, because indirect references.
// Hence we need to repeat the process.
bool any = true;
while (any) {
any = false;
for (auto l = to_refresh.begin (); l != to_refresh.end (); ++l) {
if ((*l)->restore_proxies_without_cleanup ()) {
any = true;
needs_cleanup.insert (*l);
}
}
}
// do the cleanup
for (auto l = needs_cleanup.begin (); l != needs_cleanup.end (); ++l) {
(*l)->cleanup ();
}
// issue the change notification
@ -284,9 +309,27 @@ LibraryManager::lib_internal (lib_id_type id) const
void
LibraryManager::refresh_all ()
{
// NOTE: libraries may get deleted during the refresh, so better use weak pointers here
// to track lifetime. Libraries appearing are not considered.
std::vector<tl::weak_ptr<Library> > libs;
libs.reserve (m_libs.size ());
for (auto l = m_libs.begin (); l != m_libs.end (); ++l) {
libs.push_back (tl::weak_ptr<Library> (*l));
}
for (auto l = libs.begin (); l != libs.end (); ++l) {
if (l->get ()) {
(*l)->refresh_without_restore ();
}
}
// restore proxies on all libraries - as the refresh happens in random order, dependencies
// are not necessarily resolved. This is done here.
for (std::vector<Library *>::iterator l = m_libs.begin (); l != m_libs.end (); ++l) {
if (*l) {
(*l)->refresh ();
(*l)->layout ().restore_proxies ();
}
}
}

View File

@ -22,6 +22,7 @@
#include "dbLibraryProxy.h"
#include "dbColdProxy.h"
#include "dbLibraryManager.h"
#include "dbLibrary.h"
#include "dbLayout.h"
@ -187,25 +188,6 @@ LibraryProxy::get_layer_indices (db::Layout &layout, db::ImportLayerMapping *lay
return m_layer_indices;
}
class LibraryCellIndexMapper
{
public:
LibraryCellIndexMapper (Layout &layout, Library *lib)
: mp_lib (lib), mp_layout (&layout)
{
// .. nothing yet ..
}
cell_index_type operator() (cell_index_type cell_index_in_lib)
{
return mp_layout->get_lib_proxy (mp_lib, cell_index_in_lib);
}
private:
Library *mp_lib;
Layout *mp_layout;
};
void
LibraryProxy::update (db::ImportLayerMapping *layer_mapping)
{
@ -215,32 +197,69 @@ LibraryProxy::update (db::ImportLayerMapping *layer_mapping)
Library *lib = LibraryManager::instance ().lib (lib_id ());
const db::Cell &source_cell = lib->layout ().cell (library_cell_index ());
db::ICplxTrans tr;
bool need_transform = false;
if (fabs (layout ()->dbu () - lib->layout ().dbu ()) > 1e-6) {
need_transform = true;
tr = db::ICplxTrans (lib->layout ().dbu () / layout ()->dbu ());
}
clear_shapes ();
clear_insts ();
for (unsigned int l = 0; l < lib->layout ().layers (); ++l) {
if (layer_indices [l] >= 0) {
shapes ((unsigned int) layer_indices [l]).assign_transformed (source_cell.shapes (l), tr);
shapes ((unsigned int) layer_indices [l]).assign_transformed (source_cell.shapes (l), db::ICplxTrans (lib->layout ().dbu () / layout ()->dbu ()));
}
}
LibraryCellIndexMapper cell_index_mapper (*layout (), lib);
for (Cell::const_iterator i = source_cell.begin (); ! i.at_end (); ++i) {
for (Cell::const_iterator inst = source_cell.begin (); !inst.at_end (); ++inst) {
db::Instance new_inst = insert (*inst, cell_index_mapper);
if (need_transform) {
replace (new_inst, new_inst.cell_inst ().transformed_into (tr));
db::CellInstArray inst = i->cell_inst ();
Library *real_lib = lib;
cell_index_type real_cil = inst.object ().cell_index ();
// use the "final lib", so we refer to the actual lib instead
// of building chains of lib references
LibraryProxy *lp;
while ((lp = dynamic_cast<LibraryProxy *> (&real_lib->layout ().cell (real_cil))) != 0) {
real_cil = lp->library_cell_index ();
real_lib = db::LibraryManager::instance ().lib (lp->lib_id ());
}
ColdProxy *cp = dynamic_cast<ColdProxy *> (&real_lib->layout ().cell (real_cil));
if (cp) {
// The final item is a cold proxy ("<defunct>" cell) - treat it like
// a library proxy as it may become one in the future, but replace it
// by a cold proxy now.
auto p = layout ()->find_cold_proxy (cp->context_info ());
if (p.first) {
// reuse existing proxy
inst.object ().cell_index (p.second);
} else {
// create a new proxy, reusing the first library proxy's replica
inst.object ().cell_index (layout ()->get_lib_proxy (real_lib, real_cil));
layout ()->create_cold_proxy_as (cp->context_info (), inst.object ().cell_index ());
}
} else {
inst.object ().cell_index (layout ()->get_lib_proxy (real_lib, real_cil));
}
inst.transform_into (db::ICplxTrans (lib->layout ().dbu () / layout ()->dbu ()));
if (i->has_prop_id ()) {
insert (db::CellInstArrayWithProperties (inst, i->prop_id ()));
} else {
insert (inst);
}
}
}
bool
LibraryProxy::can_skip_replica () const
{
const Library *lib = LibraryManager::instance ().lib (lib_id ());
return lib && ! lib->replicate ();
}
std::string
LibraryProxy::get_basic_name () const
{

View File

@ -93,6 +93,14 @@ public:
return true;
}
/**
* @brief Gets a value indicating that this cell is a replica that can be skipped
*
* This attribute is evaluated by file writers to skip cell replicas for
* library cells that do not want to replicated.
*/
virtual bool can_skip_replica () const;
/**
* @brief Gets the basic name
*
@ -129,12 +137,12 @@ public:
/**
* @brief Reimplemented from Cell: unregisters the proxy at the layout
*/
void unregister ();
virtual void unregister ();
/**
* @brief Reimplemented from Cell: reregisters the proxy at the layout
*/
void reregister ();
virtual void reregister ();
private:
lib_id_type m_lib_id;

Some files were not shown because too many files have changed in this diff Show More