Compare commits

..

1 Commits

Author SHA1 Message Date
Petru Conduraru 2c6082d94c Bonsai: stop deleting manually-created Level (Section) annotations
sync_references() treats every IfcAnnotation with ObjectType in
(GRID, SECTION, ELEVATION, SECTION_LEVEL) as a system-managed drawing
reference via is_auto_annotation(), and deletes any of them lacking a
product assignment as an "orphan". Unlike GRID/SECTION/ELEVATION,
SECTION_LEVEL is also a regular annotation the Annotation tool exposes
for manual authoring ("Level (Section)"), and manually created ones
never get that assignment, so they were being wiped out the next time
a drawing was activated. is_auto_annotation() now only classifies a
SECTION_LEVEL annotation as auto-managed if it is actually tied to a
referenced product, matching how PLAN_LEVEL (never auto-managed) is
already handled.

Fixes #7450.

Generated with the assistance of an AI coding tool.
2026-07-16 16:56:55 +03:00
1481 changed files with 388533 additions and 473873 deletions
+1
View File
@@ -1,4 +1,5 @@
Checks: 'bugprone-*,cert-*,clang-analyzer-*,readability-*'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: none
+2 -4
View File
@@ -19,8 +19,6 @@
# normalize the line endings of the following files
*.bat text eol=crlf
*.cmd text eol=crlf
*.cpp text
*.css text
*.csv text
@@ -30,20 +28,20 @@
*.gitkeep
*.h text
*.html text
*.i text
*.ifc text
*.json text
*.md text
*.po text
*.pot text
*.py text
*.sh text eol=lf
*.txt text
# files not normalized ATM
# bat
# bnf
# blend
# i
# ico
# mo
# mpass
@@ -1,46 +0,0 @@
# Lint/test gate for the bonsaiviewer-autodesk crate — nothing here ships.
# The connector binary that reaches users is built by the platform pipelines
# (build_rocky.yml, build_rocky_arm.yml, build_win.yml, build_osx.yml), each
# of which runs packaging/build.py itself and bundles dist/autodesk/ into the
# Bonsai Viewer archive.
name: Test Bonsai Viewer Autodesk Connector
on:
workflow_dispatch:
push:
paths:
- 'src/bonsaiviewer-autodesk/**'
- '.github/workflows/build-bonsaiviewer-autodesk.yml'
pull_request:
paths:
- 'src/bonsaiviewer-autodesk/**'
- '.github/workflows/build-bonsaiviewer-autodesk.yml'
jobs:
test:
name: cargo-test
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/bonsaiviewer-autodesk
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
# cargo-target reuse across runs. Massive cold-build speedup,
# cheap on the GitHub Actions cache budget.
- uses: Swatinem/rust-cache@v2
with:
workspaces: src/bonsaiviewer-autodesk
- name: cargo fmt --check
run: cargo fmt --all -- --check
- name: cargo clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: cargo test
run: cargo test --all-features
+17 -88
View File
@@ -10,11 +10,10 @@ jobs:
fail-fast: false
matrix:
include:
# x64 (Intel cross-compile) dropped while wgpu Qt is required:
# the runner is arm64 so `brew --prefix qt` returns the arm64
# prefix; we'd need a separate x86_64 Qt install under
# /usr/local to cross-build BonsaiViewer. Revisit if Intel-Mac
# demand resurfaces.
- os: macos
runner: macos-14
arch: x64
oldarch:
- os: macos
runner: macos-14
arch: arm64
@@ -22,12 +21,12 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
submodules: recursive
- name: Checkout Build Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/build-outputs
path: ./build
@@ -35,28 +34,15 @@ jobs:
lfs: true
token: ${{ secrets.BUILD_REPO_TOKEN }}
- name: Set up uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install Dependencies
run: |
brew update
# preinstalled: xz, cmake
brew install git bison autoconf automake libffi findutils
# qt brings in Qt6 + Svg; nix/build-all.py honours pre-set
# QT_DIR so BonsaiViewer doesn't try to aqtinstall (which is
# Linux-only).
brew install qt
echo "$(brew --prefix findutils)/libexec/gnubin" >> $GITHUB_PATH
# Mac is using bison 2.5 by default, but we need 3.5+ for swig.
echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH
# The bonsaiviewer-autodesk connector is a Rust crate; the "Package
# .zip archives" step below runs `cargo build --release` via
# packaging/build.py. Match the dedicated connector workflow's stable
# toolchain, rather than whatever Rust the runner image happens to ship.
- uses: dtolnay/rust-toolchain@stable
- name: Install aws cli
run: |
python -m pip install awscli
@@ -64,7 +50,7 @@ jobs:
- name: Unpack Dependencies
run: |
cd build
uv run ../nix/cache_dependencies.py unpack
python ../nix/cache_dependencies.py unpack
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.23
@@ -91,21 +77,8 @@ jobs:
/usr/local/bin/brew install gettext openssl
fi
set -o pipefail
export QT_DIR="$(brew --prefix qt)"
# --shared mirrors build_rocky.yml after 27249770e: builds
# IfcOpenShell as shared libs so each plug-in dylib references
# libIfcParse / libIfcGeom via @rpath instead of statically
# embedding them — the dominant size win for BonsaiViewer.app
# (per-plugin libs go from ~30-50 MB to a few MB).
#
# IfcOpenShell-Python is back on after the ifcwrap rpath fix:
# INSTALL_RPATH "$ORIGIN" is a Linux-ism that macOS dyld bakes
# in as a literal string, so `@rpath/ifcopenshell.document.rdb
# .dylib` failed to resolve at import time. ifcwrap now sets
# INSTALL_RPATH to "@loader_path" on Apple.
CXXFLAGS="-O3" CFLAGS="-O3 ${DARWIN_C_SOURCE}" ADD_COMMIT_SHA=1 BUILD_CFG=Release \
BUILD_BONSAIVIEWER=ON QT_DIR="${QT_DIR}" \
uv run ./nix/build-all.py -v --diskcleanup --ifcopenshell-shared ${MAC_INTEL} \
python3 ./nix/build-all.py -v --diskcleanup ${MAC_INTEL} \
| tee build.log
- name: Upload Build Logs
@@ -122,7 +95,7 @@ jobs:
- name: Pack Dependencies
run: |
cd build
uv run ../nix/cache_dependencies.py pack
python ../nix/cache_dependencies.py pack
- name: Commit and Push Changes to Build Repository
run: |
@@ -136,28 +109,8 @@ jobs:
- name: Package .zip archives
run: |
VERSION=v`cat VERSION`
# packaging/build.py stages the connector binary + connector.json
# into dist/autodesk/; the .app loop below copies that folder into
# the bundle. Same on-disk shape as the Linux and Windows builds.
uv run src/bonsaiviewer-autodesk/packaging/build.py
autodesk_connector_dir="$PWD/src/bonsaiviewer-autodesk/dist/autodesk"
test -d "$autodesk_connector_dir"
cd ./build/`uname`/*/10.15/install/ifcopenshell
mkdir -p ~/output
install_root="$PWD"
stage_runtime_payload() {
dest="$1"
while IFS= read -r runtime_file; do
cp -L "$runtime_file" "$dest/"
done < <(
for runtime_dir in "$install_root/bin" "$install_root/lib" "$install_root/lib64"; do
[ -d "$runtime_dir" ] || continue
find "$runtime_dir" -type f \( -name "*.so" -o -name "*.so.*" -o -name "*.dylib" -o -name "*.dll" \)
done
)
}
mkdir ~/output
ls -d python-* | while read py_version; do
postfix=`echo ${py_version: -1} | sed s/[0-9]//`
@@ -172,42 +125,18 @@ jobs:
fi
[ -d ifcopenshell/__pycache__ ] && rm -rf ifcopenshell/__pycache__
find ifcopenshell -name "*.pyc" -delete
stage_runtime_payload ifcopenshell
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip ifcopenshell
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip ifcopenshell/*
mv *.zip ~/output
popd > /dev/null
done
find "$install_root/bin" -maxdepth 1 -type f -perm /111 ! -name "*.zip" ! -name "*.so" ! -name "*.so.*" ! -name "*.dylib" ! -name "*.dll" | while read exe_path; do
exe=`basename "$exe_path"`
package_dir="$install_root/.package-${exe}"
rm -rf "$package_dir"
mkdir -p "$package_dir"
cp "$exe_path" "$package_dir/"
stage_runtime_payload "$package_dir"
pushd "$package_dir" > /dev/null
zip -qq -r "$HOME/output/${exe}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip" .
popd > /dev/null
rm -rf "$package_dir"
done
# .app bundles (e.g. BonsaiViewer.app) live at the install-prefix
# root because their install rule uses `BUNDLE DESTINATION "."` —
# that's the layout Qt's macdeployqt expects. macdeployqt has
# already embedded the Qt frameworks inside each bundle during
# install/strip, so the only thing left to stage is the connector.
find "$install_root" -maxdepth 1 -type d -name "*.app" | while read app_path; do
app=`basename "$app_path" .app`
if [ "$app" = "BonsaiViewer" ]; then
# ConnectorDiscovery looks in applicationDirPath()/connectors,
# which for a bundle is Contents/MacOS.
mkdir -p "$app_path/Contents/MacOS/connectors"
cp -a "$autodesk_connector_dir" "$app_path/Contents/MacOS/connectors/"
fi
pushd "$install_root" > /dev/null
zip -qq -r "$HOME/output/${app}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip" "$(basename "$app_path")"
popd > /dev/null
cd bin
rm *.zip || true
ls | while read exe; do
zip -qq -r ${exe}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip $exe
done
mv *.zip ~/output
cd ..
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
+17 -34
View File
@@ -8,21 +8,14 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Set up uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install Python
# Installs latest Python version so it's preferred by uv over system Python.
run: uv python install
- name: Checkout Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
submodules: recursive
path: IfcOpenShell
- name: Checkout Build Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/build-outputs
path: ifcopenshell_build
@@ -33,7 +26,7 @@ jobs:
- name: Unpack Dependencies
run: |
cd ifcopenshell_build
uv run ../IfcOpenShell/nix/cache_dependencies.py unpack
python ../IfcOpenShell/nix/cache_dependencies.py unpack
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.23
@@ -56,10 +49,23 @@ jobs:
ifcopenshell_build/*/*/logs/*.log
retention-days: 30
- name: Run wheel tests
run: |
cp -r IfcOpenShell/pyodide/test test
# venv set up in build_pyodide.sh.
source .venv/bin/activate
uv pip install pytest-pyodide
PYODIDE_ROOT_DIST=`pyodide config get pyodide_root`/dist
# `pytest-pyodide` requires pyodide in 'pyodide' directory in cwd, when running `pytest`.
cp -r $PYODIDE_ROOT_DIST test/pyodide
cp dist/ifcopenshell-*.whl test/pyodide
cd test
pytest --capture=no
- name: Pack Dependencies
run: |
cd ifcopenshell_build
uv run ../IfcOpenShell/nix/cache_dependencies.py pack
python ../IfcOpenShell/nix/cache_dependencies.py pack
- name: Commit and Push Changes to Build Repository
run: |
@@ -70,28 +76,6 @@ jobs:
git commit -m "Update build artifacts [skip ci]" || echo "No changes to commit"
git push || echo "Push failed"
- name: Order wheel shared objects
run: |
uv run ./IfcOpenShell/pyodide/order_pyodide_wheel_shared_objects.py dist/ifcopenshell-*.whl
- name: Split packages
run: |
VERSION=v`cat ./IfcOpenShell/VERSION`
mkdir -p dist-modular
uv run ./IfcOpenShell/pyodide/split_pyodide_ifcopenshell_wheel.py dist/ifcopenshell-*.whl ./dist-modular
cd dist-modular
zip -r -qq ifcopenshell-modular-${VERSION}-${GITHUB_SHA:0:7}-pyodide.zip *.whl
- name: Run wheel tests
run: |
# venv set up in build_pyodide.sh.
source .venv/bin/activate
ln -s "$PWD/dist" IfcOpenShell/dist
ln -s "$PWD/dist-modular" IfcOpenShell/dist-modular
cd IfcOpenShell/pyodide
./run_pytest.py setup
./run_pytest.py run
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
with:
@@ -102,4 +86,3 @@ jobs:
- name: Upload .zip archives to S3
run: |
aws s3 cp dist s3://ifcopenshell-builds/ --recursive --exclude "*" --include "*.whl"
aws s3 cp dist-modular s3://ifcopenshell-builds/ --recursive --exclude "*" --include "*.zip"
+13 -168
View File
@@ -19,29 +19,13 @@ jobs:
- name: Install Dependencies
run: |
dnf update -y
dnf install -y epel-release
# --enablerepo=crb: libstdc++-static (libsupc++.a, needed by the
# bundled FLTK link) lives in Rocky's CodeReady Builder repo, which
# is disabled by default.
dnf install -y --enablerepo=crb gcc gcc-c++ git autoconf automake bison make zip cmake \
dnf install -y gcc gcc-c++ git autoconf automake bison make zip cmake python3 python3-pip \
bzip2 patch mesa-libGL-devel libffi-devel fontconfig-devel \
sqlite-devel bzip2-devel zlib-devel openssl-devel xz-devel \
readline-devel ncurses-devel libffi-devel libuuid-devel git-lfs \
findutils xz byacc patchelf libxkbcommon-devel \
dbus-devel \
libXext-devel libXinerama-devel libXcursor-devel libXrender-devel \
libXfixes-devel libXft-devel pango-devel cairo-devel libstdc++-static
findutils xz byacc
git config --global --add safe.directory '*'
- name: Install Rust
# The bonsaiviewer-autodesk connector is a Rust crate; the "Package
# .zip archives" step below runs `cargo build --release` via
# packaging/build.py. Match the dedicated connector workflow's stable
# toolchain (dtolnay/rust-toolchain@stable).
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Install aws cli
run: |
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
@@ -51,12 +35,12 @@ jobs:
aws --version
- name: Checkout Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
submodules: recursive
- name: Checkout Build Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/build-outputs
path: ./build
@@ -78,10 +62,7 @@ jobs:
shell: bash
run: |
set -o pipefail
CXXFLAGS="-O3" CFLAGS="-O3" ADD_COMMIT_SHA=1 BUILD_CFG=Release BUILD_BONSAIVIEWER=ON \
uv run --with aqtinstall ./nix/build-all.py \
-v --diskcleanup --ifcopenshell-shared 2>&1 \
| tee build.log
CXXFLAGS="-O3" CFLAGS="-O3 ${DARWIN_C_SOURCE}" ADD_COMMIT_SHA=1 BUILD_CFG=Release uv run ./nix/build-all.py -v --diskcleanup 2>&1 | tee build.log
- name: Upload Build Logs
if: always()
@@ -108,131 +89,11 @@ jobs:
git push || true
- name: Package .zip archives
shell: bash
run: |
VERSION=v`cat VERSION`
# bonsaiviewer-autodesk is now a Rust connector. packaging/build.py
# invokes `cargo build --release` and stages the binary +
# connector.json into dist/autodesk/. Same on-disk shape as the
# old PyInstaller flow so the symlink + zip steps below
# continue to work unchanged.
uv run src/bonsaiviewer-autodesk/packaging/build.py
autodesk_connector_dir="$PWD/src/bonsaiviewer-autodesk/dist/autodesk"
test -d "$autodesk_connector_dir"
cd ./build/`uname`/*/install/ifcopenshell
mkdir -p ~/output
install_root="$PWD"
QT6_VERSION="${QT6_VERSION:-6.8.3}"
mkdir ~/output
if [ -z "${QT_DIR:-}" ]; then
for qt_candidate in "$(dirname "$install_root")"/qt6-${QT6_VERSION}-*/${QT6_VERSION}/*; do
if [ -d "$qt_candidate/lib" ]; then
QT_DIR="$qt_candidate"
break
fi
done
fi
# Ensure that all shared libraries in provided dest `$1`
# are present using their SONAMEs (at least as symlinks).
ensure_soname_links() {
dest="$1"
find "$dest" -maxdepth 1 -type f -name "*.so*" | while IFS= read -r shared_object; do
# TODO: actual pattern is "Library soname" instead of "Shared library"?
soname=$(readelf -d "$shared_object" 2>/dev/null | sed -n 's/.*(SONAME).*Shared library: \[\(.*\)\].*/\1/p' | head -n 1)
[ -n "$soname" ] || continue
[ -e "$dest/$soname" ] && continue
ln -s "$(basename "$shared_object")" "$dest/$soname"
done
}
# Copy all libs from `install/ifcopenshell` to the provided `$1`.
# Set `$2` to `0` to skip including geometry writers.
stage_runtime_payload() {
dest="$1"
include_geometry_writers="${2:-1}"
while IFS= read -r runtime_file; do
if [ "$include_geometry_writers" != "1" ] && [[ "$(basename "$runtime_file")" == ifcopenshell.geometry.writer.* ]]; then
continue
fi
cp -P "$runtime_file" "$dest/"
done < <(
for runtime_dir in "$install_root/bin" "$install_root/lib" "$install_root/lib64"; do
[ -d "$runtime_dir" ] || continue
find "$runtime_dir" \( -type f -o -type l \) \( -name "*.so" -o -name "*.so.*" -o -name "*.dylib" -o -name "*.dll" \)
done
)
ensure_soname_links "$dest"
}
# Copy all libs from `QT_DIR` to the provided `$2`.
stage_qt_runtime_payload() {
exe_path="$1"
dest="$2"
[ -n "${QT_DIR:-}" ] && [ -d "$QT_DIR/lib" ] || return 0
# Skip executables that don't depend on QT (don't have `libQt6` referenced).
if ! LD_LIBRARY_PATH="$QT_DIR/lib:${LD_LIBRARY_PATH:-}" ldd "$exe_path" 2>/dev/null | grep -q "libQt6"; then
return 0
fi
# Copy all QT libs to `dest`.
find "$QT_DIR/lib" -maxdepth 1 \( -type f -o -type l \) -name "*.so*" -exec cp -P {} "$dest/" \;
ensure_soname_links "$dest"
# Copy QT plugins.
if [ -d "$QT_DIR/plugins" ]; then
pushd "$QT_DIR/plugins" > /dev/null
find . \( -type f -o -type l \) -name "*.so*" | while IFS= read -r plugin_file; do
mkdir -p "$dest/plugins/$(dirname "$plugin_file")"
cp -P "$plugin_file" "$dest/plugins/$plugin_file"
done
popd > /dev/null
# Point plugins rpath to `$dest`.
if [ -d "$dest/plugins" ]; then
find "$dest/plugins" -type f -name "*.so*" -exec patchelf --set-rpath '$ORIGIN/../..:$ORIGIN' {} \;
fi
fi
find "$dest" -maxdepth 1 -type f -name "*.so*" -exec patchelf --set-rpath '$ORIGIN' {} \;
printf "[Paths]\nPrefix = .\n" > "$dest/qt.conf"
}
# Check all binaries in the dest `$1`
# and report if they're still missing dependencies or are static.
check_runtime_dependencies() {
package_dir="$1"
missing=0
# Iterate over all .so files.
while IFS= read -r binary_file; do
# Skip non-binaries.
readelf -h "$binary_file" >/dev/null 2>&1 || continue
# Report non-dynamic binaries.
if ! env -u LD_LIBRARY_PATH ldd "$binary_file" > "$package_dir/.ldd.out" 2>&1; then
echo "ldd failed for $binary_file"
cat "$package_dir/.ldd.out"
missing=1
continue
fi
# Report missing dependencies.
if grep -q "not found" "$package_dir/.ldd.out"; then
echo "Missing runtime dependencies for $binary_file"
grep "not found" "$package_dir/.ldd.out"
missing=1
fi
done < <(find "$package_dir" -type f \( -perm /111 -o -name "*.so" -o -name "*.so.*" \))
rm -f "$package_dir/.ldd.out"
# TODO: should error?
if [ "$missing" -ne 0 ]; then
echo "Runtime dependency check found issues; continuing packaging."
fi
return 0
}
# Iterate over all built Python wrappers in `install/ifcopenshell/python-x.y.z`.
# and zip them, bundling all dynamic libs from `lib`.
ls -d python-* | while read py_version; do
postfix=`echo ${py_version: -1} | sed s/[0-9]//`
numbers=`echo $py_version | grep -oE '[0-9]+\.[0-9]+' | tr -d '.'`
@@ -246,34 +107,18 @@ jobs:
fi
[ -d ifcopenshell/__pycache__ ] && rm -rf ifcopenshell/__pycache__
find ifcopenshell -name "*.pyc" -delete
# TODO: packs qt libs also?
stage_runtime_payload ifcopenshell
zip -y -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-linux64.zip ifcopenshell
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-linux64.zip ifcopenshell/*
mv *.zip ~/output
popd > /dev/null
done
# Iterate over all executables in `install/ifcopenshell/bin` and zip them.
# Each zip bundles dynamic libs from `lib` and also qt libs.
find "$install_root/bin" -maxdepth 1 -type f -perm /111 ! -name "*.zip" ! -name "*.so" ! -name "*.so.*" ! -name "*.dylib" ! -name "*.dll" | while read exe_path; do
exe=`basename "$exe_path"`
package_dir="$install_root/.package-${exe}"
rm -rf "$package_dir"
mkdir -p "$package_dir"
cp "$exe_path" "$package_dir/"
patchelf --set-rpath '$ORIGIN' "$package_dir/$exe"
stage_runtime_payload "$package_dir" 0
stage_qt_runtime_payload "$exe_path" "$package_dir"
if [ "$exe" = "BonsaiViewer" ]; then
mkdir -p "$package_dir/connectors"
cp -a "$autodesk_connector_dir" "$package_dir/connectors/"
fi
check_runtime_dependencies "$package_dir"
pushd "$package_dir" > /dev/null
zip -y -qq -r "$HOME/output/${exe}-${VERSION}-${GITHUB_SHA:0:7}-linux64.zip" .
popd > /dev/null
rm -rf "$package_dir"
cd bin
rm *.zip || true
ls | while read exe; do
zip -qq -r ${exe}-${VERSION}-${GITHUB_SHA:0:7}-linux64.zip $exe
done
mv *.zip ~/output
cd ..
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
+15 -160
View File
@@ -6,19 +6,7 @@ on:
jobs:
build_ifcopenshell:
runs-on: ubuntu-22.04-arm
# Rocky 10 (glibc 2.39) — aqt's official Qt6 ARM binaries are built
# against glibc 2.38, which Rocky 9 (glibc 2.34) can't run (moc fails to
# load). The legacy arm64v8/rockylinux Docker image stopped at 9; Rocky 10
# is published under the rockylinux/rockylinux namespace.
container:
image: rockylinux/rockylinux:10
# The community rockylinux image omits PATH from its config (the old
# Docker Official arm64v8/rockylinux set it), so GitHub Actions `run:`
# steps fail with `exec: "sh": not found` — docker exec has no /usr/bin
# to resolve the shell. Restore a standard PATH; GITHUB_PATH prepends
# (uv, cargo) are still layered on top by the runner.
env:
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
container: arm64v8/rockylinux:9
steps:
- name: Set up uv
@@ -31,29 +19,13 @@ jobs:
- name: Install Dependencies
run: |
dnf update -y
dnf install -y epel-release
# --enablerepo=crb: libstdc++-static (libsupc++.a, needed by the
# bundled FLTK link) lives in Rocky's CodeReady Builder repo, which
# is disabled by default.
dnf install -y --enablerepo=crb gcc gcc-c++ git autoconf automake bison make zip cmake \
dnf install -y gcc gcc-c++ git autoconf automake bison make zip cmake python3 python3-pip \
bzip2 patch mesa-libGL-devel libffi-devel fontconfig-devel \
sqlite-devel bzip2-devel zlib-devel openssl-devel xz-devel \
readline-devel ncurses-devel libffi-devel libuuid-devel git-lfs \
findutils xz byacc patchelf libxkbcommon-devel \
dbus-devel \
libXext-devel libXinerama-devel libXcursor-devel libXrender-devel \
libXfixes-devel libXft-devel pango-devel cairo-devel libstdc++-static
findutils xz byacc
git config --global --add safe.directory '*'
- name: Install Rust
# The bonsaiviewer-autodesk connector is a Rust crate; the "Package
# .zip archives" step below runs `cargo build --release` via
# packaging/build.py. Match the dedicated connector workflow's stable
# toolchain (dtolnay/rust-toolchain@stable).
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Install aws cli
run: |
curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"
@@ -63,12 +35,12 @@ jobs:
aws --version
- name: Checkout Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
submodules: recursive
- name: Checkout Build Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/build-outputs
path: ./build
@@ -84,16 +56,13 @@ jobs:
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.23
with:
key: ubuntu-22.04-${{ runner.arch }}-rockylinux10
key: ubuntu-22.04-${{ runner.arch }}-rockylinux9
- name: Run Build Script
shell: bash
run: |
set -o pipefail
CXXFLAGS="-O3" CFLAGS="-O3" ADD_COMMIT_SHA=1 BUILD_CFG=Release BUILD_BONSAIVIEWER=ON \
uv run --with aqtinstall ./nix/build-all.py \
-v --diskcleanup --ifcopenshell-shared 2>&1 \
| tee build.log
CXXFLAGS="-O3" CFLAGS="-O3 ${DARWIN_C_SOURCE}" ADD_COMMIT_SHA=1 BUILD_CFG=Release uv run ./nix/build-all.py -v --diskcleanup 2>&1 | tee build.log
- name: Upload Build Logs
if: always()
@@ -120,111 +89,10 @@ jobs:
git push || true
- name: Package .zip archives
shell: bash
run: |
VERSION=v`cat VERSION`
# bonsaiviewer-autodesk is now a Rust connector. packaging/build.py
# invokes `cargo build --release` and stages the binary +
# connector.json into dist/autodesk/. Same on-disk shape as the
# old PyInstaller flow so the symlink + zip steps below
# continue to work unchanged.
uv run src/bonsaiviewer-autodesk/packaging/build.py
autodesk_connector_dir="$PWD/src/bonsaiviewer-autodesk/dist/autodesk"
test -d "$autodesk_connector_dir"
cd ./build/`uname`/*/install/ifcopenshell
mkdir -p ~/output
install_root="$PWD"
QT6_VERSION="${QT6_VERSION:-6.8.3}"
if [ -z "${QT_DIR:-}" ]; then
for qt_candidate in "$(dirname "$install_root")"/qt6-${QT6_VERSION}-*/${QT6_VERSION}/*; do
if [ -d "$qt_candidate/lib" ]; then
QT_DIR="$qt_candidate"
break
fi
done
fi
ensure_soname_links() {
dest="$1"
find "$dest" -maxdepth 1 -type f -name "*.so*" | while IFS= read -r shared_object; do
soname=$(readelf -d "$shared_object" 2>/dev/null | sed -n 's/.*(SONAME).*Shared library: \[\(.*\)\].*/\1/p' | head -n 1)
[ -n "$soname" ] || continue
[ -e "$dest/$soname" ] && continue
ln -s "$(basename "$shared_object")" "$dest/$soname"
done
}
stage_runtime_payload() {
dest="$1"
include_geometry_writers="${2:-1}"
while IFS= read -r runtime_file; do
if [ "$include_geometry_writers" != "1" ] && [[ "$(basename "$runtime_file")" == ifcopenshell.geometry.writer.* ]]; then
continue
fi
cp -P "$runtime_file" "$dest/"
done < <(
for runtime_dir in "$install_root/bin" "$install_root/lib" "$install_root/lib64"; do
[ -d "$runtime_dir" ] || continue
find "$runtime_dir" \( -type f -o -type l \) \( -name "*.so" -o -name "*.so.*" -o -name "*.dylib" -o -name "*.dll" \)
done
)
ensure_soname_links "$dest"
}
stage_qt_runtime_payload() {
exe_path="$1"
dest="$2"
[ -n "${QT_DIR:-}" ] && [ -d "$QT_DIR/lib" ] || return 0
if ! LD_LIBRARY_PATH="$QT_DIR/lib:${LD_LIBRARY_PATH:-}" ldd "$exe_path" 2>/dev/null | grep -q "libQt6"; then
return 0
fi
find "$QT_DIR/lib" -maxdepth 1 \( -type f -o -type l \) -name "*.so*" -exec cp -P {} "$dest/" \;
ensure_soname_links "$dest"
if [ -d "$QT_DIR/plugins" ]; then
pushd "$QT_DIR/plugins" > /dev/null
find . \( -type f -o -type l \) -name "*.so*" | while IFS= read -r plugin_file; do
mkdir -p "$dest/plugins/$(dirname "$plugin_file")"
cp -P "$plugin_file" "$dest/plugins/$plugin_file"
done
popd > /dev/null
if [ -d "$dest/plugins" ]; then
find "$dest/plugins" -type f -name "*.so*" -exec patchelf --set-rpath '$ORIGIN/../..:$ORIGIN' {} \;
fi
fi
find "$dest" -maxdepth 1 -type f -name "*.so*" -exec patchelf --set-rpath '$ORIGIN' {} \;
printf "[Paths]\nPrefix = .\n" > "$dest/qt.conf"
}
check_runtime_dependencies() {
package_dir="$1"
missing=0
while IFS= read -r binary_file; do
readelf -h "$binary_file" >/dev/null 2>&1 || continue
if ! env -u LD_LIBRARY_PATH ldd "$binary_file" > "$package_dir/.ldd.out" 2>&1; then
echo "ldd failed for $binary_file"
cat "$package_dir/.ldd.out"
missing=1
continue
fi
if grep -q "not found" "$package_dir/.ldd.out"; then
echo "Missing runtime dependencies for $binary_file"
grep "not found" "$package_dir/.ldd.out"
missing=1
fi
done < <(find "$package_dir" -type f \( -perm /111 -o -name "*.so" -o -name "*.so.*" \))
rm -f "$package_dir/.ldd.out"
if [ "$missing" -ne 0 ]; then
echo "Runtime dependency check found issues; continuing packaging."
fi
return 0
}
mkdir ~/output
ls -d python-* | while read py_version; do
postfix=`echo ${py_version: -1} | sed s/[0-9]//`
@@ -239,31 +107,18 @@ jobs:
fi
[ -d ifcopenshell/__pycache__ ] && rm -rf ifcopenshell/__pycache__
find ifcopenshell -name "*.pyc" -delete
stage_runtime_payload ifcopenshell
zip -y -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-linuxarm64.zip ifcopenshell
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-linuxarm64.zip ifcopenshell/*
mv *.zip ~/output
popd > /dev/null
done
find "$install_root/bin" -maxdepth 1 -type f -perm /111 ! -name "*.zip" ! -name "*.so" ! -name "*.so.*" ! -name "*.dylib" ! -name "*.dll" | while read exe_path; do
exe=`basename "$exe_path"`
package_dir="$install_root/.package-${exe}"
rm -rf "$package_dir"
mkdir -p "$package_dir"
cp "$exe_path" "$package_dir/"
patchelf --set-rpath '$ORIGIN' "$package_dir/$exe"
stage_runtime_payload "$package_dir" 0
stage_qt_runtime_payload "$exe_path" "$package_dir"
if [ "$exe" = "BonsaiViewer" ]; then
mkdir -p "$package_dir/connectors"
cp -a "$autodesk_connector_dir" "$package_dir/connectors/"
fi
check_runtime_dependencies "$package_dir"
pushd "$package_dir" > /dev/null
zip -y -qq -r "$HOME/output/${exe}-${VERSION}-${GITHUB_SHA:0:7}-linuxarm64.zip" .
popd > /dev/null
rm -rf "$package_dir"
cd bin
rm *.zip || true
ls | while read exe; do
zip -qq -r ${exe}-${VERSION}-${GITHUB_SHA:0:7}-linuxarm64.zip $exe
done
mv *.zip ~/output
cd ..
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
+3 -21
View File
@@ -27,12 +27,12 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
submodules: recursive
- name: Checkout Build Repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/build-outputs
path: ${{ matrix.deps_dir }}
@@ -48,11 +48,7 @@ jobs:
run: |
cd ${{ matrix.deps_dir }}
Get-ChildItem -Path . -Filter 'cache-*.zip' | ForEach-Object {
Write-Host "Extracting $($_.Name)"
7z x -bso0 -bsp0 $_.FullName
if ($LASTEXITCODE -ne 0) {
throw "Failed to extract $($_.Name) with 7z exit code $LASTEXITCODE."
}
7z x $_.FullName
}
- name: ccache
@@ -63,20 +59,6 @@ jobs:
# and with default 500MB some cache gets deleted, leading to misses.
max-size: 5000MB
- name: Set up Python for connector build
uses: actions/setup-python@v6
with:
python-version: '3.12'
# Build the Autodesk connector before the C++ build: build-all-win.py
# bundles it next to BonsaiViewer.exe while archiving the executables.
# bonsaiviewer-autodesk is a Rust connector; packaging/build.py runs
# `cargo build --release` and stages the binary + connector.json into
# dist/autodesk/.
- name: Build Autodesk connector
working-directory: src/bonsaiviewer-autodesk
run: python packaging/build.py
- name: Run Build Script And Pack .zip Archives
shell: cmd
env:
+2 -2
View File
@@ -19,8 +19,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7 # https://github.com/actions/checkout
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6 # https://github.com/actions/checkout
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-tags: true
fetch-depth: 0
+4 -4
View File
@@ -65,14 +65,14 @@ jobs:
config:
short_name: macos
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
python-version: '3.11'
- name: Get current version
id: version
run: echo "version=$(sed -E 's/[[:alpha:]]+[0-9]+$//' VERSION)" >> $GITHUB_OUTPUT
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Compile
run: |
cd src/bonsai && make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
@@ -98,7 +98,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout bonsai_unstable_repo repository
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/bonsai_unstable_repo
token: ${{ secrets.IFCOPENBOT_TOKEN }}
+2 -2
View File
@@ -48,8 +48,8 @@ jobs:
config:
short_name: macos
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
python-version: '3.11'
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -37,8 +37,8 @@ jobs:
short_name: macosm164
}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Compile
@@ -21,15 +21,13 @@ jobs:
date: ${{ steps.date.outputs.date }}
verdate: ${{ steps.verdate.outputs.verdate }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Set env
run: echo ok go
- name: Get current version
id: version
# Strip any trailing prerelease label and number; the dated alpha
# suffix is added below.
run: echo "version=$(sed -E 's/[[:alpha:]]+[0-9]+$//' VERSION)" >> $GITHUB_OUTPUT
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Get current date
id: date
@@ -77,7 +75,7 @@ jobs:
echo "ARTIFACTS_DIR=/home/runner/work/artifacts" >> $GITHUB_ENV
fi
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
+4 -3
View File
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-22.04
needs: activate
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -31,7 +31,7 @@ jobs:
sudo apt-get install --no-install-recommends \
git cmake gcc g++ libboost-all-dev python3-all-dev swig libpcre3-dev libxml2-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
libcgal-dev nlohmann-json3-dev libeigen3-dev
libhdf5-dev libcgal-dev nlohmann-json3-dev libeigen3-dev
-
name: ccache
@@ -60,6 +60,7 @@ jobs:
-DMPFR_INCLUDE_DIR=/usr/include \
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
-DGLTF_SUPPORT=On \
-DJSON_INCLUDE_DIR=/usr/include \
-DEIGEN_DIR=/usr/include/eigen3 \
@@ -85,7 +86,7 @@ jobs:
name: Docker Build, Tag, Push
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
lfs: true
@@ -47,10 +47,10 @@ jobs:
short_name: macosm164
}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
+2 -2
View File
@@ -38,10 +38,10 @@ jobs:
short_name: macosm164
}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Compile
+3 -3
View File
@@ -25,14 +25,14 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
python-version: '3.11'
- name: Get current version
id: version
run: echo "version=$(sed -E 's/[[:alpha:]]+[0-9]+$//' VERSION)" >> $GITHUB_OUTPUT
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Get current date
id: date
run: echo "date=$(date +'%y%m%d')" >> $GITHUB_OUTPUT
+2 -2
View File
@@ -19,8 +19,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
python-version: '3.11'
+2 -2
View File
@@ -10,9 +10,9 @@ jobs:
publish_website:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Checkout ifctester_org_static_html
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/ifctester_org_static_html
token: ${{ secrets.IFCOPENBOT_TOKEN }}
+2 -2
View File
@@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
- uses: actions/checkout@v6
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
with:
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
- name: Compile
+5 -16
View File
@@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -43,7 +43,6 @@ jobs:
sudo apt update
sudo apt-get install --no-install-recommends -y \
cmake \
bison \
gcc \
g++ \
libboost-date-time-dev \
@@ -62,23 +61,13 @@ jobs:
libocct-ocaf-dev \
libocct-visualization-dev \
libpcre3-dev \
libpcre2-dev \
libtbb-dev \
libxml2-dev \
libxi-dev \
occt-misc \
tcl-dev \
tk-dev
- name: Build SWIG
# IfcOpenShell requires SWIG 4.1+, ubuntu-22.04 ships 4.0.2.
run: |
sudo apt-get remove --purge -y swig swig4.0
git clone https://github.com/swig/swig --branch v4.2.1 --depth 1
cmake -S swig -B swig/build -DCMAKE_BUILD_TYPE=Release
cmake --build swig/build -j "$(nproc)"
sudo cmake --install swig/build
swig -version
tk-dev \
swig
- name: Configure minimal IfcOpenShell
run: |
@@ -96,7 +85,7 @@ jobs:
cmake --build build-ifcopenshell --target install -j "$(nproc)"
- name: Set up Python 3.11
uses: actions/setup-python@v7
uses: actions/setup-python@v6
with:
python-version: 3.11
@@ -131,7 +120,7 @@ jobs:
PY
- name: Set up Python 3.12
uses: actions/setup-python@v7
uses: actions/setup-python@v6
with:
python-version: 3.12
+10 -19
View File
@@ -12,15 +12,15 @@ jobs:
MIN_BLENDER_PY_VERSION: "3.11"
steps:
- name: Action - checkout repository
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Action - install python
uses: actions/setup-python@v7
uses: actions/setup-python@v6
with:
python-version: ${{ env.MIN_IOS_PY_VERSION }}
- name: Action - install python
uses: actions/setup-python@v7
uses: actions/setup-python@v6
with:
python-version: ${{ env.MIN_BLENDER_PY_VERSION }}
@@ -55,17 +55,11 @@ jobs:
black --diff --check . | black-codeclimate | python .github/workflows/black_to_github_annotations.py
continue-on-error: true
- name: ty check (venv setup)
run: poe ty-venv
- name: ty check (bonsai)
id: ty-bonsai
run: poe ty-bonsai
continue-on-error: true
- name: ty check (ios)
id: ty-ios
run: poe ty-ios
- name: ty check
id: ty
run: |
poe ty-venv
poe ty
continue-on-error: true
- name: Ruff check
@@ -115,10 +109,7 @@ jobs:
if [ "${{ steps.ruff.outcome }}" != "success" ]; then
echo "::error::Ruff check failed, see Summary or 'ruff' step for the details." && ERROR=1
fi
if [ "${{ steps.ty-bonsai.outcome }}" != "success" ]; then
echo "::error::ty check (bonsai) failed, see 'ty check (bonsai)' step for the details." && ERROR=1
fi
if [ "${{ steps.ty-ios.outcome }}" != "success" ]; then
echo "::error::ty check (ios) failed, see 'ty check (ios)' step for the details." && ERROR=1
if [ "${{ steps.ty.outcome }}" != "success" ]; then
echo "::error::ty check failed, see 'ty check' step for the details." && ERROR=1
fi
exit $ERROR
@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout IfcOpenShell
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
@@ -25,7 +25,7 @@ jobs:
echo "name=$(basename $WHEEL)" >> $GITHUB_OUTPUT
- name: Checkout wasm-wheels
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/wasm-wheels
path: wasm-wheels
+29 -17
View File
@@ -18,6 +18,7 @@ on:
- 'src/ifcparse/**'
- 'src/ifcquery/**'
- 'src/ifcwrap/**'
- 'src/qtviewer/**'
- 'src/svgfill/**'
- 'src/serializers/**'
- 'conda/**'
@@ -37,21 +38,17 @@ jobs:
compile-and-test:
runs-on: ubuntu-22.04
needs: activate
strategy:
fail-fast: false
matrix:
build_shared_libs: [ON, OFF]
env:
# Colored output for cmake.
CLICOLOR_FORCE: "1"
CMAKE_COLOR_DIAGNOSTICS: "ON"
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v7
uses: actions/setup-python@v6
with:
python-version: 3.11
@@ -83,7 +80,7 @@ jobs:
libtbb-dev nlohmann-json3-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
${OCCT_CMAKE_DEPS} \
libcgal-dev libeigen3-dev
libhdf5-dev libcgal-dev libeigen3-dev
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.23
@@ -121,7 +118,7 @@ jobs:
cd OpenCOLLADA
git checkout v1.6.68
patch -p1 --batch --forward -i ../nix/patches/opencollada/pr622_and_disable_subdirs.patch
patch -p1 --batch --forward -i ../nix/patches/opencollada/config_select_libs_by_use_shared.patch
patch -p1 --batch --forward -i ../nix/patches/opencollada/allow_static_libraries_config_on_unix.patch
mkdir build && cd build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
@@ -163,7 +160,7 @@ jobs:
# Remove default swig to avoid conflicts.
sudo apt remove --purge swig swig4.0
sudo apt-get install -y libpcre2-dev bison
git clone https://github.com/swig/swig --branch v4.2.1 --depth 1
git clone https://github.com/swig/swig --branch v4.1.0 --depth 1
cd swig
mkdir build && cd build
cmake .. \
@@ -185,7 +182,6 @@ jobs:
-DPYTHON_EXECUTABLE:FILEPATH=${{ env.pythonLocation }}/bin/python \
-DPYTHON_INCLUDE_DIR:PATH=${{ env.pythonLocation }}/include/python3.11 \
-DUSE_MMAP=On \
-DBUILD_SHARED_LIBS=${{ matrix.build_shared_libs }} \
"-DSCHEMA_VERSIONS=2x3;4;4x3_add2" \
-DGLTF_SUPPORT=On \
-DWITH_ROCKSDB=On \
@@ -221,11 +217,29 @@ jobs:
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
cmake --build .
./arbitrary_open_profile_def && test -f arbitrary_open_profile_def.ifc
./composite_profile_def && test -f composite_profile_def.ifc
./csg_primitive && test -f csg_primitive.ifc
./ellipse_pies && test -f ellipse_pies.ifc
./faces && test -f faces.ifc
./ifc_curve_rebar && test -f ifc_curve_rebar.ifc
./profiles
test -f IfcUShapeProfileDef.ifc
test -f IfcTShapeProfileDef.ifc
test -f IfcZShapeProfileDef.ifc
test -f IfcEllipseProfileDef.ifc
test -f IfcIShapeProfileDef.ifc
test -f IfcLShapeProfileDef.ifc
test -f IfcCShapeProfileDef.ifc
test -f IfcCircleProfileDef.ifc
test -f IfcRectangleProfileDef.ifc
test -f IfcTrapeziumProfileDef.ifc
./IfcParseExamples "../IfcParseExamples_test.ifc"
./IfcOpenHouse && test -f IfcOpenHouse.ifc
./IfcParseExamples IfcOpenHouse.ifc
./IfcAdvancedHouse && test -f IfcAdvancedHouse.ifc
./IfcAlignment && test -f FHWA_Bridge_Geometry_Alignment_Example.ifc
./IfcSimplifiedAlignment && test -f FHWA_Bridge_Geometry_Alignment_Example_Simplified.ifc
./IfcAlignment && test -f IfcAlignment.ifc
./IfcSimplifiedAlignment && test -f IfcSimplifiedAlignment.ifc
./triangulated_faceset && test -f triangulated_faceset.ifc
- name: Test ifcopenshell-python
run: |
@@ -243,15 +257,13 @@ jobs:
cd ../ifcdiff && make test || ERROR=1
cd ../ifcpatch && make test || ERROR=1
pip install -e ../ifc5d --no-deps
pip install odfpy openpyxl
pip install odfpy xlsxwriter
cd ../ifc5d && make test || ERROR=1
pip install -e ../ifcquery --no-deps
cd ../ifcquery && make test || ERROR=1
pip install -e ../ifcedit --no-deps
cd ../ifcedit && make test || ERROR=1
# Pinned <2: mcp 2.0.0 renamed mcp.server.fastmcp.FastMCP to
# mcp.server.mcpserver.MCPServer, which ifcmcp doesn't support yet.
pip install "mcp>=1.0,<2"
pip install mcp
pip install -e ../ifcmcp --no-deps
cd ../ifcmcp && make test || ERROR=1
pip install -e ../ifctester --no-deps
@@ -11,10 +11,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v7
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v7
uses: actions/setup-python@v6
with:
python-version: '3.x'
+3 -3
View File
@@ -27,12 +27,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout (recursive)
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0
- name: Checkout intermediate Pages repo
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/aichat_ifcopenshell_org_static_html
ref: gh-pages
@@ -42,7 +42,7 @@ jobs:
run: |
rsync -av --delete --exclude='.git/' src/ifcchat/ output/
- name: Setup Python
uses: actions/setup-python@v7
uses: actions/setup-python@v6
with:
python-version: "3.x"
- name: Download wheels
@@ -7,7 +7,7 @@ jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
@@ -1,87 +0,0 @@
# This file was generated with the assistance of an AI coding tool.
name: Publish C++ API documentation
on:
push:
branches:
- v0.9.0
paths:
- '.github/workflows/publish-cpp-api-docs.yml'
- 'docs/cpp-api/**'
- 'src/ifcgeom/**'
- 'src/ifcparse/**'
- 'src/serializers/**'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: publish-cpp-api-docs
cancel-in-progress: false
jobs:
publish:
if: github.repository == 'IfcOpenShell/IfcOpenShell'
runs-on: ubuntu-24.04
steps:
- name: Checkout IfcOpenShell
uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: '3.10'
- name: Install documentation dependencies
run: |
sudo apt-get update
sudo apt-get install --yes doxygen graphviz
python -m pip install --requirement docs/cpp-api/requirements.txt
- name: Build C++ API documentation
working-directory: docs/cpp-api
run: |
export PROJECT_NUMBER="$(git rev-parse --short HEAD)"
python -m sphinx -M html . output -W --keep-going
- name: Checkout documentation repository
uses: actions/checkout@v7
with:
repository: IfcOpenShell/cpp_docs
ref: master
path: published-docs
token: ${{ secrets.BUILD_REPO_TOKEN }}
- name: Replace published documentation
run: |
publish_tree="${RUNNER_TEMP}/published-docs-tree"
mkdir -p "${publish_tree}/v0.9.0-latest"
rsync --archive docs/cpp-api/output/html/ "${publish_tree}/v0.9.0-latest/"
touch "${publish_tree}/.nojekyll"
if [[ -f published-docs/CNAME ]]; then
cp published-docs/CNAME "${publish_tree}/CNAME"
fi
rsync --archive --delete --exclude='.git/' "${publish_tree}/" published-docs/
- name: Commit and push if changed
working-directory: published-docs
env:
SOURCE_SHA: ${{ github.sha }}
run: |
git config user.name 'IfcOpenBot'
git config user.email 'IfcOpenBot@users.noreply.github.com'
git add --all
if git diff --cached --quiet; then
echo "No changes to commit"
exit 0
fi
git commit -m "Update C++ API docs from ${SOURCE_SHA:0:7}"
git push origin master
@@ -27,12 +27,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout (recursive)
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0
- name: Checkout intermediate Pages repo
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
repository: IfcOpenShell/wasm_ifcopenshell_org_static_html
ref: gh-pages
+3 -2
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
submodules: recursive
- name: Install C++ dependencies
@@ -36,7 +36,7 @@ jobs:
swig libpcre3-dev libxml2-dev \
libtbb-dev nlohmann-json3-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
libcgal-dev opencollada-dev
libhdf5-dev libcgal-dev opencollada-dev
- name: Build
env:
@@ -64,6 +64,7 @@ jobs:
-DMPFR_INCLUDE_DIR=/usr/include \
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
-DOPENCOLLADA_INCLUDE_DIR=/usr/include/opencollada \
-DOPENCOLLADA_LIBRARY_DIR=/usr/lib/opencollada/ \
../cmake
+1 -18
View File
@@ -15,10 +15,8 @@
/src/examples/out/
/src/ifcmax/out/
/src/ifcwrap/out/
/src/qtviewer/out/
/src/ifctester/webapp/public/pyodide/
# pyodide wheels
/dist/
/dist-modular/
/win/BuildDepsCache*.txt
@@ -110,15 +108,6 @@ src/bonsai/bonsai/bim/data/webui/running_pid.json
src/ifcopenshell-python/ifcopenshell/_ifcopenshell_wrapper*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
# plugins
src/ifcopenshell-python/ifcopenshell/ifcopenshell_document_*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell_geometry_*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell_parse_schema*.so
src/ifcopenshell-python/ifcopenshell/libifcopenshell.geometry.so
src/ifcopenshell-python/ifcopenshell/libifcopenshell.geometry.writer.so
src/ifcopenshell-python/ifcopenshell/libifcopenshell.parse.so
src/ifcopenshell-python/ifcopenshell/libifcopenshell.plugin.so
# apple
.DS_Store
@@ -126,9 +115,6 @@ src/ifcopenshell-python/ifcopenshell/libifcopenshell.plugin.so
.clangd
# clangd cache
.cache
# Useful for symlinking json compilation database from cmake,
# allowing clang commands without `-p path/to/build`.
/compile_commands.json
# Brickschema
src/bonsai/bonsai/bim/schema/Brick.ttl
@@ -148,6 +134,3 @@ CLAUDE.local.md
*.py.tmp*
*.json.tmp*
# bonsaiviewer-autodesk connector build artifacts
/src/bonsaiviewer-autodesk/build/
/src/bonsaiviewer-autodesk/dist/
+3
View File
@@ -8,6 +8,9 @@
[submodule "src/ifcopenshell-python/test/Sample-BIM-Files"]
path = src/ifcopenshell-python/test/Sample-BIM-Files
url = https://github.com/IfcOpenShell/ids-test-files
[submodule "docs/cpp-api/assets/doxygen-awesome-css"]
path = docs/cpp-api/assets/doxygen-awesome-css
url = https://github.com/jothepro/doxygen-awesome-css.git
[submodule "src/ifcopenshell-python/ifcopenshell/simple_spf"]
path = src/ifcopenshell-python/ifcopenshell/simple_spf
url = https://github.com/IfcOpenShell/step-file-parser
+1 -1
View File
@@ -27,7 +27,7 @@ RUN echo "deb http://archive.ubuntu.com/ubuntu focal-proposed main restricted" |
libboost-all-dev \
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev \
libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
python3-pytest ; \
libhdf5-serial-dev python3-pytest ; \
rm -rf /var/lib/apt/lists/* ;
COPY . /home/IfcOpenShell/
+2 -1
View File
@@ -18,7 +18,7 @@ and many other libraries, CLI apps, and more. Support is also provided for auxil
For more information, see:
* [IfcOpenShell Website](https://ifcopenshell.org)
* [IfcOpenShell Website](http://ifcopenshell.org)
* [IfcOpenShell Documentation](https://docs.ifcopenshell.org)
* [IfcOpenShell C++ Installation](https://docs.ifcopenshell.org/ifcopenshell/installation.html)
* [IfcOpenShell Python Installation](https://docs.ifcopenshell.org/ifcopenshell-python/installation.html)
@@ -70,6 +70,7 @@ The IfcOpenShell C++ codebase is split into multiple interal libraries:
| ifcjni | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
| ifcparse | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
| ifcwrap | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
| qtviewer | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
| serializers | Internal library for IfcOpenShell | LGPL-3.0-or-later\* |
[LGPL]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/COPYING.LESSER "LGPL-3.0-or-later"
+1 -1
View File
@@ -1 +1 @@
0.9.0alpha0
0.8.6
+1 -1
View File
@@ -3,7 +3,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>blenderbim-nightly</id>
<version>blenderbim_build_version</version>
<version>blenderbim_build_version-alpha</version>
<packageSourceUrl>https://github.com/IfcOpenShell/IfcOpenShell</packageSourceUrl>
<owners>fbpyr</owners>
<!-- == SOFTWARE SPECIFIC SECTION == -->
+11 -14
View File
@@ -3,12 +3,11 @@
apt update && apt install git wget curl ptpython mono-devel micro
mkdir -p /home/runner/work/IfcOpenShell && cd /home/runner/work/IfcOpenShell
git clone https://github.com/IfcOpenShell/IfcOpenShell
cd /home/runner/work/IfcOpenShell/IfcOpenShell/choco/bonsai/
cd /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/
micro choco_release.py # paste this script, comment out push command
export CHOCO_TOKEN="secret_choco_release_token"
python3 choco_release.py
"""
import datetime
import hashlib
import os
@@ -29,7 +28,7 @@ def get_repo_tag_names() -> list[str]:
def request_repo_info(url: str):
req = request.Request(url)
req = request.Request(url)
resp = request.urlopen(req)
if not resp.status == 200:
print(f"[ERROR] could not contact server: {url}")
@@ -86,15 +85,13 @@ def run(command: str) -> None:
start = datetime.datetime.now()
URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
URL_BLENDER_CMAKE = (
"https://raw.githubusercontent.com/blender/blender/{}/build_files/cmake/Modules/FindPythonLibsUnix.cmake"
)
RE_BLENDER_VERSION_MIN_MAJ = r"Latest Version.+<span>Blender (\d+\.\d+)\..+</span>"
RE_BLENDER_VERSION_MIN_MAJ_PAT = r"Latest Version.+<span>Blender (\d+\.\d+\.\d+)</span>"
URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
URL_BLENDER_CMAKE = "https://raw.githubusercontent.com/blender/blender/{}/build_files/cmake/Modules/FindPythonLibsUnix.cmake"
RE_BLENDER_VERSION_MIN_MAJ = r"Latest Version.+<span>Blender (\d+\.\d+)\..+</span>"
RE_BLENDER_VERSION_MIN_MAJ_PAT = r"Latest Version.+<span>Blender (\d+\.\d+\.\d+)</span>"
RE_BLENDER_PYTHON_VERSION_MAJ_MIN = r"\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)"
BLENDERBIM_DIR = pathlib.Path("/home/runner/work/IfcOpenShell/IfcOpenShell/choco/bonsai/")
BLENDERBIM_DIR = pathlib.Path("/home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/")
print("_____ check choco release needed?")
@@ -151,7 +148,7 @@ print(f"{blender_python_version_maj_min=}")
python_version = f"py{found[0].replace('.', '')}"
print(f"{python_version=}")
blenderbim_build_version = target_release_tag.replace("bonsai-", "")
blenderbim_build_version = target_release_tag.replace("blenderbim-", "")
# url_blenderbim_py3x_win_zip
release_zip_file_name, url_blenderbim_py3x_win_zip = get_release_zip(target_release_tag)
@@ -169,15 +166,15 @@ topics = {
"path": HERE_DIR / "blenderbim.nuspec",
"key_values": {
"latest_blender_version_maj_min_pat": latest_blender_release_maj_min_pat,
"blenderbim_build_version": blenderbim_build_version,
"blenderbim_build_version" : blenderbim_build_version,
},
},
"install": {
"path": HERE_DIR / "tools" / "chocolateyinstall.ps1",
"key_values": {
"url_blenderbim_py3x_win_zip": url_blenderbim_py3x_win_zip,
"url_blenderbim_py3x_win_zip" : url_blenderbim_py3x_win_zip,
"sha256sum_blenderbim_py3x_win_zip": sha256sum_blenderbim_py3x_win_zip,
"latest_blender_version_maj_min": blender_version_min_maj,
"latest_blender_version_maj_min" : blender_version_min_maj,
},
},
"uninstall": {
+199 -249
View File
@@ -18,10 +18,10 @@
################################################################################
cmake_minimum_required(VERSION 3.21)
if (NOT DEFINED CMAKE_CXX_STANDARD)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
if (CMAKE_CXX_STANDARD LESS 17)
if(CMAKE_CXX_STANDARD LESS 17)
message(FATAL_ERROR "C++17 or newer is required.")
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
@@ -36,18 +36,11 @@ file(READ "../VERSION" "RELEASE_VERSION_")
string(STRIP "${RELEASE_VERSION_}" RELEASE_VERSION)
message(STATUS "Detected version '${RELEASE_VERSION}'")
# CMake's project(VERSION) only accepts numeric components. Keep the complete
# release identifier for build information, but use its numeric release part
# for PROJECT_VERSION, SOVERSION, and generated CMake package metadata.
string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" PROJECT_VERSION_NUMERIC "${RELEASE_VERSION}")
if(NOT PROJECT_VERSION_NUMERIC)
message(FATAL_ERROR "VERSION must start with a numeric major.minor.patch version: '${RELEASE_VERSION}'")
endif()
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
if(POLICY CMP0141) # 3.25+
cmake_policy(SET CMP0141 NEW) # Support for `CMAKE_MSVC_DEBUG_INFORMATION_FORMAT`.
# Has to be set before `project` to take effect.
cmake_policy(SET CMP0141 NEW) # Support for `CMAKE_MSVC_DEBUG_INFORMATION_FORMAT`.
endif()
if(POLICY CMP0144) # 3.27
cmake_policy(SET CMP0144 NEW) # find_package() uses upper-case <PACKAGENAME>_ROOT variables.
@@ -56,6 +49,8 @@ if(POLICY CMP0167) # 3.30
cmake_policy(SET CMP0167 OLD)
endif()
project(IfcOpenShell VERSION ${RELEASE_VERSION})
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
@@ -63,106 +58,75 @@ endif()
# Include utility macros and functions
include(utilities.cmake)
# Use a SemVer-compatible spelling for CPack artifact names. A trailing
# alphabetic label and number is separated from the numeric version by a
# hyphen: for example, 0.9.0alpha0 becomes 0.9.0-alpha0.
# use extra version to make pre-release using eg semver
if(NOT DEFINED EXTRA_VERSION)
if(RELEASE_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+([A-Za-z]+)([0-9]+)$")
set(EXTRA_VERSION "-${CMAKE_MATCH_1}${CMAKE_MATCH_2}")
else()
set(EXTRA_VERSION "")
endif()
set(EXTRA_VERSION "-alpha.3")
endif()
option(MINIMAL_BUILD "The build is to make a minimal version of IFC converter from OCCT into IFC." OFF)
option(WASM_BUILD "Build a WebAssembly binary." OFF)
option(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF)
option(BUILD_SHARED_LIBS "Build IfcOpenShell as shared libraries (required)." ON)
option(
ENABLE_BUILD_OPTIMIZATIONS
"Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds."
OFF
)
option(BUILD_SHARED_LIBS "Build IfcParse and IfcGeom as shared libs (SO/DLL)." OFF)
option(MSVC_PARALLEL_BUILD "Multi-threaded compilation in Microsoft Visual Studio (/MP)" OFF)
option(USE_VLD "Use Visual Leak Detector for debugging memory leaks, MSVC-only." OFF)
option(USE_MMAP "Adds a command line options to parse IFC files from memory mapped files using Boost.Iostreams" OFF)
option(NO_WARN "Disable all warnings" OFF)
option(CREATE_BUNDLE "Copy .so files and don't create RPATHS or SOVERSION symlinks" )
option(BUILD_IFCGEOM "Build IfcGeom." ON)
option(BUILD_IFCPYTHON "Build IfcPython." ON)
option(BUILD_IFCPARSE_EXPERIMENTAL_WRAPPER "Build the experimental Clang-generated ifcparse Python wrapper." OFF)
option(BUILD_CONVERT "Build IfcConvert executable." ON)
option(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
option(BUILD_EXAMPLES "Build example applications." ON)
option(BUILD_EXAMPLES "Build example applications." OFF)
option(BUILD_GEOMSERVER "Build IfcGeomServer executable (Open CASCADE is required)." ON)
option(BUILD_IFCMAX "Build IfcMax, a 3ds Max plug-in, Windows-only." OFF)
option(BUILD_IFCMODEL_UI "Build minimal Qt IFC model UI prototype" OFF)
option(BUILD_BONSAIVIEWER "Build Bonsai Viewer" OFF) # Requires Qt6 + OpenGL 4.5
option(BUILD_IFCOPENSHELL_PARSE_TESTS "Build C++ unit tests for IfcParse (fetches Catch2 v3)" OFF)
option(BUILD_IFCOPENSHELL_GEOMETRY_TESTS "Build C++ unit tests for IfcGeom (fetches Catch2 v3)" OFF)
option(BUILD_BONSAIVIEWER_TESTS "Build unit tests for Bonsai Viewer core (fetches Catch2 v3)" OFF)
option(BUILD_BONSAIVIEWER_WGPU "Build the experimental wgpu backend (fetches wgpu-native binary release)" OFF)
# IfcViewer (the GL static lib) now links against IfcViewerWgpu because
# SceneLoader drives the wgpu viewport. Auto-enable the wgpu subproject
# whenever BUILD_BONSAIVIEWER is on so the link target exists.
if(BUILD_BONSAIVIEWER AND NOT BUILD_BONSAIVIEWER_WGPU)
message(STATUS "BUILD_BONSAIVIEWER implies BUILD_BONSAIVIEWER_WGPU "
"(SceneLoader uses ViewportWindow); auto-enabling.")
set(BUILD_BONSAIVIEWER_WGPU ON)
endif()
option(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer" OFF) # QtViewer requires Qt6
option(BUILD_PACKAGE "" OFF)
# Most users probably need just common schemas,
# but we're keeping it `OFF` by default to avoid disruption
# (e.g. all Python distribution would need to adapt this option to be set).
option(
IFCOPENSHELL_DEPLOY_QT_RUNTIME
"Deploy Qt runtime dependencies for installed Qt applications."
ON
)
option(
IFCOPENSHELL_DEPLOY_QT_TRANSLATIONS
"Deploy Qt translation catalogs with installed Qt applications."
BUILD_ONLY_COMMON_SCHEMAS
"Build only common IFC schemas (2x3, 4, 4x3_add2). By default all schemas will be built."
OFF
)
option(SCHEMA_VERSIONS "Explicitly specify schemas to build." "")
option(WITH_OPENCASCADE "Enable geometry interpretation using Open CASCADE" ON)
option(WITH_CGAL "Enable geometry interpretation using CGAL" ON)
option(WITH_MANIFOLD "Enable geometry interpretation using Manifold" OFF)
option(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLLADA)." ON)
option(GLTF_SUPPORT "Build IfcConvert with glTF support (requires json.hpp)." OFF)
option(HDF5_SUPPORT "Enable HDF5 support (requires HDF5, zlib)" ON)
option(WITH_PROJ "Enable output of Earth-Centered Earth-Fixed glTF output using the PROJ library" OFF)
option(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." ON)
option(USD_SUPPORT "Build IfcConvert with USD support (requires pixar's USD library)." OFF)
option(WITH_RELATIONSHIP_VALIDATION "Build IfcConvert with option to validate geometrical relationships." OFF)
option(WITH_ROCKSDB "Support a RocksDB key-value store as a file backend in IfcOpenShell" OFF)
option(WITH_ZSTD "Use Zstd compression in RocksDB writes" OFF)
option(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only instead of system-wide." OFF)
option(USE_DEBUG_PYTHON "Use debug binaries when building Debug IfcPython on Windows." OFF)
option(ADD_COMMIT_SHA "Add commit sha and branch in version number, requires git" OFF)
option(VERSION_OVERRIDE "Use VERSION as the branch label when commit information is embedded" OFF)
option(
VERSION_OVERRIDE
"Override the version defined in buildinfo.cpp with the file VERSION in the repository root"
OFF
)
option(USE_CCACHE "Enable use of ccache if it's available from PATH." ON)
set(
PYTHON_MODULE_INSTALL_DIR
"" CACHE PATH
set(PYTHON_MODULE_INSTALL_DIR
""
CACHE PATH
"Directory to install IfcPython package to. By default package is installed in found Python's site-packages."
)
project(IfcOpenShell VERSION ${PROJECT_VERSION_NUMERIC})
# Make sure CMake modules in this project are found first
list(PREPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
# Catch2 is fetched only when an explicit C++ test family is enabled, so the
# default build remains offline-capable.
if(BUILD_IFCOPENSHELL_PARSE_TESTS OR BUILD_IFCOPENSHELL_GEOMETRY_TESTS OR BUILD_BONSAIVIEWER_TESTS)
include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.5.4
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
enable_testing()
endif()
if(MINIMAL_BUILD)
message(STATUS "Setting options for minimal build")
set(BUILD_GEOMSERVER OFF)
@@ -170,16 +134,18 @@ if(MINIMAL_BUILD)
set(WITH_CGAL OFF)
set(COLLADA_SUPPORT OFF)
set(GLTF_SUPPORT OFF)
set(HDF5_SUPPORT OFF)
set(IFCXML_SUPPORT OFF)
set(USD_SUPPORT OFF)
endif()
if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND(NOT BUILD_IFCGEOM))
if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND (NOT BUILD_IFCGEOM))
message(STATUS "'IfcGeom' is required with current outputs")
set(BUILD_IFCGEOM ON)
endif()
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
if(USE_CCACHE AND CCACHE_FOUND)
message(STATUS "`ccache` is found, using it as a compiler launcher.")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
if(MSVC)
@@ -188,17 +154,15 @@ if(CCACHE_FOUND)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>")
# Not needed for Ninja.
if(CMAKE_GENERATOR MATCHES "Visual Studio")
file(COPY_FILE
${CCACHE_FOUND} ${CMAKE_BINARY_DIR}/cl.exe
ONLY_IF_DIFFERENT)
set(CMAKE_VS_GLOBALS
"CLToolExe=cl.exe"
"CLToolPath=${CMAKE_BINARY_DIR}"
"UseMultiToolTask=true"
)
file(COPY_FILE ${CCACHE_FOUND} ${CMAKE_BINARY_DIR}/cl.exe ONLY_IF_DIFFERENT)
set(CMAKE_VS_GLOBALS "CLToolExe=cl.exe" "CLToolPath=${CMAKE_BINARY_DIR}" "UseMultiToolTask=true")
endif()
endif()
endif()
mark_as_advanced(CCACHE_FOUND)
# Variable to accumulate swig definitions from various submodules.
set(SWIG_DEFINES "")
if(MSVC AND MSVC_PARALLEL_BUILD)
add_definitions("/MP")
@@ -216,23 +180,26 @@ include(GNUInstallDirs)
set(IFCOPENSHELL_EXPORT_TARGETS "${PROJECT_NAME}Targets")
if(NOT INCLUDEDIR)
set(INCLUDEDIR include)
# On Windows Release and Debug binaries are not compatible.
# So we add a postfix to avoid issues and allow release and debug installations to coexist.
if(WIN32)
set(CMAKE_DEBUG_POSTFIX "_d")
endif()
if(NOT IS_ABSOLUTE ${INCLUDEDIR})
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
endif()
message(STATUS "INCLUDEDIR: ${INCLUDEDIR}")
if(MSVC)
message(WARNING "Building DLLs against the static VC run-time. This is not recommended if the DLLs are to be redistributed.")
# C4521: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
add_definitions(-wd4251)
if(BUILD_SHARED_LIBS)
add_definitions(-DIFC_SHARED_BUILD)
if(MSVC)
message(
WARNING
"Building DLLs against the static VC run-time. This is not recommended if the DLLs are to be redistributed."
)
# C4521: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
add_definitions(-wd4251)
endif()
endif()
UNIFY_ENVVARS_AND_CACHE(BOOST_ROOT)
UNIFY_ENVVARS_AND_CACHE(BOOST_LIBRARYDIR)
if(NOT MINIMAL_BUILD)
UNIFY_ENVVARS_AND_CACHE(PYTHON_INCLUDE_DIR)
@@ -248,64 +215,47 @@ foreach(option_flag IN LISTS option_flags)
convert_env_var_to_bool("${option_flag}")
endforeach()
if(WITH_CGAL)
find_package(CGAL REQUIRED)
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_CGAL)
list(APPEND GEOMETRY_KERNELS cgal)
endif()
if(BUILD_IFCGEOM AND WITH_OPENCASCADE)
find_package(OpenCASCADE REQUIRED)
# Map OpenCASCADE_LIBRARIES variable from OpenCASCADEConfig.cmake to OpenCASCADE_LIBRARIES used by kernel generic cmake file
set(OpenCASCADE_LIBRARIES ${OpenCASCADE_LIBRARIES})
add_definitions(-DIFOPSH_WITH_OPENCASCADE)
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_OPENCASCADE)
list(APPEND GEOMETRY_KERNELS opencascade)
endif()
message(STATUS "BUILD_IFCGEOM WITH_MANIFOLD: ${BUILD_IFCGEOM} ${WITH_MANIFOLD}")
if(BUILD_IFCGEOM AND WITH_MANIFOLD)
find_package(manifold CONFIG REQUIRED)
if(TARGET manifold::manifold)
set(MANIFOLD_LIBRARIES manifold::manifold)
elseif(TARGET manifold)
set(MANIFOLD_LIBRARIES manifold)
else()
message(FATAL_ERROR "Unable to determine manifold target")
endif()
list(APPEND GEOMETRY_KERNELS manifold)
endif()
if(BUILD_IFCGEOM)
list(APPEND GEOMETRY_KERNELS passthrough)
endif()
set(GLTF_LIBRARIES "")
if(GLTF_SUPPORT)
find_package(nlohmann_json REQUIRED)
set(GLTF_LIBRARIES nlohmann_json::nlohmann_json)
add_definitions(-DWITH_GLTF)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_GLTF)
endif()
# Add USD support to serializers
set(USD_LIBRARIES "")
if(USD_SUPPORT)
find_package(USD REQUIRED)
set(USD_LIBRARIES pxr::USD)
endif(USD_SUPPORT)
if (WITH_ROCKSDB)
set(ROCKSDB_LIBRARIES "")
if(WITH_ROCKSDB)
# Temporaily mess with CMAKE_FIND_PACKAGE_PREFER_CONFIG to help RocksDB
# find it's zstd dependency on Windows.
# Only do it on Windows, otherwise it might create problems as
# findzstd and zstd-config target names do not match.
# https://github.com/facebook/rocksdb/pull/13975
if(WIN32)
set(TEMP CMAKE_FIND_PACKAGE_PREFER_CONFIG)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
endif()
find_package(RocksDB CONFIG REQUIRED)
mark_as_advanced(RocksDB_DIR)
if(WIN32)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ${TEMP})
endif()
message(STATUS "RocksDB: found at '${RocksDB_DIR}'.")
add_library(IFCOPENSHELL_RocksDB INTERFACE)
set(IFCOPENSHELL_ROCKSDB_TARGET IFCOPENSHELL_RocksDB)
set(ROCKSDB_LIBRARIES "IFCOPENSHELL_RocksDB")
target_compile_definitions(IFCOPENSHELL_RocksDB INTERFACE IFOPSH_WITH_ROCKSDB)
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_ROCKSDB)
@@ -318,12 +268,14 @@ if (WITH_ROCKSDB)
message(FATAL_ERROR "RocksDB found but neither RocksDB::rocksdb nor RocksDB::rocksdb-shared target exists")
endif()
if (WITH_ZSTD)
if(WITH_ZSTD)
# @todo do we actually need the zstd include dir or rather just pass
# the libzstd.a along with the rocksdb library when needed and feature
# detect based on rocksdb API?
find_package(zstd CONFIG REQUIRED)
mark_as_advanced(zstd_DIR)
message(STATUS "zstd: found at '${zstd_DIR}'.")
target_link_libraries(IFCOPENSHELL_RocksDB INTERFACE zstd::libzstd_static)
endif()
install(TARGETS IFCOPENSHELL_RocksDB EXPORT ${IFCOPENSHELL_EXPORT_TARGETS})
@@ -331,7 +283,7 @@ endif()
# Find Boost: On win32 the (hardcoded) default is to use static libraries and
# runtime, when doing running conda-build we pick what conda prepared for us.
if(WIN32 AND("$ENV{CONDA_BUILD}" STREQUAL ""))
if(WIN32 AND NOT DEFINED ENV{CONDA_BUILD})
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_USE_MULTITHREADED ON)
@@ -362,8 +314,18 @@ if(WASM_BUILD)
else()
# @todo review this, shouldn't this be all possible header-only now?
# ... or rewritten using C++17 features?
# set(BOOST_COMPONENTS system program_options regex thread date_time iostreams)
set(BOOST_COMPONENTS program_options regex thread date_time iostreams)
# Boost.System has been header-only since 1.69 and its compiled stub library
# was dropped in newer Boost, so requesting it as a component makes
# find_package fail on Boost 1.70 and up (for example Boost 1.90). It is
# still pulled in transitively by thread / iostreams where needed, so do not
# request it explicitly.
set(BOOST_COMPONENTS
program_options
regex
thread
date_time
iostreams
)
endif()
if(USE_MMAP)
@@ -373,6 +335,17 @@ if(USE_MMAP)
else()
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} iostreams)
endif()
add_definitions(-DUSE_MMAP)
endif()
# Handle CGAL after Boost settings are set, since CGAL will use them too.
# Do `find_package(Boost)` only after this, to make sure `FindBoost` finds correct components.
# Otherwise it will find components needed for CGAL and we might some libraries.
if(WITH_CGAL)
find_package(CGAL REQUIRED)
set(CGAL_LIBRARIES IFCOPENSHELL_CGAL)
list(APPEND GEOMETRY_KERNELS cgal)
endif()
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
@@ -381,8 +354,17 @@ message(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}")
if(COLLADA_SUPPORT)
find_package(OpenCOLLADA REQUIRED)
add_definitions(-DWITH_OPENCOLLADA)
endif()
if(HDF5_SUPPORT)
find_package(HDF5 REQUIRED COMPONENTS C CXX)
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} hdf5::hdf5_cpp)
add_definitions(-DWITH_HDF5)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_HDF5)
endif(HDF5_SUPPORT)
if(ENABLE_BUILD_OPTIMIZATIONS)
if(MSVC)
# NOTE: RelWithDebInfo and Release use O2 (= /Ox /Gl /Gy/ = Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy) by default,
@@ -395,12 +377,20 @@ if(ENABLE_BUILD_OPTIMIZATIONS)
# Linker
# /OPT:REF enables also /OPT:ICF and disables INCREMENTAL
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
set(LINKER_FLAGS_RELEASE "/LTCG /OPT:REF")
# /OPT:NOICF is recommended when /DEBUG is used (http://msdn.microsoft.com/en-us/library/xe4t6fc1.aspx)
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
set(LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /OPT:NOICF")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
"${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}"
)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}")
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
set(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
"${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}"
)
else()
# GCC-like: Release should use O3 but RelWithDebInfo 02 so enforce 03. Anything other useful that could be added here?
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
@@ -449,7 +439,7 @@ if(MSVC)
endif()
# Enforce standards-conformance on VS > 2015, older Boost versions fail to compile with this
if(MSVC_VERSION GREATER 1900 AND(Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
if(MSVC_VERSION GREATER 1900 AND (Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
add_definitions(-permissive-)
endif()
@@ -468,11 +458,11 @@ if(MSVC)
# endforeach()
# endif()
add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE)
# See #5158.
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.40)
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
endif()
add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE)
# See #5158.
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.40)
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
endif()
else()
add_definitions(-Wall -Wextra)
@@ -482,7 +472,10 @@ else()
add_definitions(-Wno-maybe-uninitialized)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 9.0))
if(
CMAKE_CXX_COMPILER_ID MATCHES "GNU"
AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 9.0)
)
# OpenCascade spews a lot of deprecated-copy warnings
add_definitions(-Wno-deprecated-copy)
endif()
@@ -493,28 +486,45 @@ else()
endif()
endif(MSVC)
include_directories(${INCLUDE_DIRECTORIES}
${Boost_INCLUDE_DIRS}
${CGAL_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${MPFR_INCLUDE_DIR}
)
include_directories(${OPENCOLLADA_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${HDF5_INCLUDE_DIR})
if(NOT SCHEMA_VERSIONS)
if(WASM_BUILD)
# super arbitrarily try to keep size down at least a little bit
# `WASM_BUILD` - super arbitrarily try to keep size down at least a little bit
if(BUILD_ONLY_COMMON_SCHEMAS OR WASM_BUILD)
set(SCHEMA_VERSIONS "2x3" "4" "4x3_add2")
else()
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3" "4x3_tc1" "4x3_add1" "4x3_add2")
set(SCHEMA_VERSIONS
"2x3"
"4"
"4x1"
"4x2"
"4x3"
"4x3_tc1"
"4x3_add1"
"4x3_add2"
)
endif()
endif()
message(STATUS "IFC SCHEMA_VERSIONS that will be used for the build: ${SCHEMA_VERSIONS}.")
set(SCHEMA_DEFINITIONS "")
foreach(schema ${SCHEMA_VERSIONS})
list(APPEND SCHEMA_DEFINITIONS "-DHAS_SCHEMA_${schema}")
endforeach()
string(REPLACE ";" ")(" schema_version_seq "(${SCHEMA_VERSIONS})")
list(APPEND SCHEMA_DEFINITIONS "-DSCHEMA_SEQ=${schema_version_seq}")
if(COMPILE_SCHEMA)
# @todo, this appears to be untested at the moment
find_package(PythonInterp)
if(NOT PYTHONINTERP_FOUND)
message(FATAL_ERROR "A Python interpreter is necessary when COMPILE_SCHEMA is enabled. Disable COMPILE_SCHEMA or fix Python paths to proceed.")
message(
FATAL_ERROR
"A Python interpreter is necessary when COMPILE_SCHEMA is enabled. Disable COMPILE_SCHEMA or fix Python paths to proceed."
)
endif()
set(IFC_RELEASE_NOT_USED ${SCHEMA_VERSIONS})
@@ -534,7 +544,10 @@ if(COMPILE_SCHEMA)
if("${PYPARSING_FOUND}" STREQUAL "-1")
message(STATUS "Installing pyparsing")
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip "install" --user pyparsing RESULT_VARIABLE SUCCESS)
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -m pip "install" --user pyparsing
RESULT_VARIABLE SUCCESS
)
if(NOT "${SUCCESS}" STREQUAL "0")
execute_process(COMMAND pip "install" --user pyparsing RESULT_VARIABLE SUCCESS)
@@ -553,7 +566,8 @@ if(COMPILE_SCHEMA)
COMMAND ${PYTHON_EXECUTABLE} bootstrap.py
WORKING_DIRECTORY ../src/ifcopenshell-python/ifcopenshell/express
OUTPUT_FILE express_parser.py
RESULT_VARIABLE SUCCESS)
RESULT_VARIABLE SUCCESS
)
if(NOT "${SUCCESS}" STREQUAL "0")
message(FATAL_ERROR "Failed to bootstrap parser. Make sure pyparsing is installed")
@@ -563,7 +577,8 @@ if(COMPILE_SCHEMA)
execute_process(
COMMAND ${PYTHON_EXECUTABLE} ../ifcopenshell-python/ifcopenshell/express/express_parser.py ../../${COMPILE_SCHEMA}
WORKING_DIRECTORY ../src/ifcparse
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME)
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME
)
# Prevent the schema that had just been compiled from being excluded
foreach(schema ${SCHEMA_VERSIONS})
@@ -578,52 +593,17 @@ if(NOT Boost_VERSION LESS 105800)
add_definitions(-DBOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE)
endif()
add_subdirectory(../src/plugin plugin)
add_subdirectory(../src/ifcparse ifcparse)
set(IFCOPENSHELL_LIBRARIES IfcParse)
if(BUILD_IFCOPENSHELL_PARSE_TESTS)
add_subdirectory(../src/ifcparse/tests ifcparse/tests)
endif()
if(BUILD_EXAMPLES OR BUILD_BONSAIVIEWER)
add_subdirectory(../src/helpers helpers)
endif()
if(BUILD_IFCPARSE_EXPERIMENTAL_WRAPPER)
add_subdirectory(../src/wrappergen wrappergen)
endif()
if(BUILD_IFCGEOM)
# CGAL::CGAL target already has dependencies resolved.
if(WITH_CGAL AND CGAL_DIR)
set(CGAL_LIBRARIES CGAL::CGAL)
message(STATUS "Using found CGAL package at '${CGAL_DIR}'")
elseif(WITH_CGAL AND NOT CGAL_DIR)
find_library(libGMP NAMES gmp mpir PATHS ${GMP_LIBRARY_DIR} NO_DEFAULT_PATH)
find_library(libMPFR NAMES mpfr PATHS ${MPFR_LIBRARY_DIR} NO_DEFAULT_PATH)
if(NOT libGMP)
message(FATAL_ERROR "Unable to find GMP library files, aborting")
endif()
if(NOT libMPFR)
message(FATAL_ERROR "Unable to find MPFR library files, aborting")
endif()
list(APPEND CGAL_LIBRARIES "${libMPFR}")
list(APPEND CGAL_LIBRARIES "${libGMP}")
endif()
add_subdirectory(../src/ifcgeom ifcgeom)
if(BUILD_IFCOPENSHELL_GEOMETRY_TESTS)
add_subdirectory(../src/ifcgeom/tests ifcgeom/tests)
endif()
elseif(BUILD_IFCOPENSHELL_GEOMETRY_TESTS)
message(FATAL_ERROR "BUILD_IFCOPENSHELL_GEOMETRY_TESTS requires BUILD_IFCGEOM=ON.")
endif(BUILD_IFCGEOM)
if(BUILD_CONVERT OR BUILD_IFCPYTHON OR BUILD_BONSAIVIEWER)
if(BUILD_CONVERT OR BUILD_IFCPYTHON)
add_subdirectory(../src/serializers serializers)
endif(BUILD_CONVERT OR BUILD_IFCPYTHON OR BUILD_BONSAIVIEWER)
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} ${SERIALIZER_SCHEMA_LIBRARIES})
endif(BUILD_CONVERT OR BUILD_IFCPYTHON)
if(BUILD_CONVERT)
add_subdirectory(../src/ifcconvert ifcconvert)
@@ -642,8 +622,8 @@ if(ADD_COMMIT_SHA)
endif()
if(GIT_FOUND)
if (VERSION_OVERRIDE)
set (git_branch ${RELEASE_VERSION})
if(VERSION_OVERRIDE)
set(git_branch ${RELEASE_VERSION})
else()
message("git found: ${GIT_EXECUTABLE} with version ${GIT_VERSION_STRING}")
execute_process(
@@ -655,8 +635,8 @@ if(ADD_COMMIT_SHA)
string(REPLACE "\n" ";" git_branch_list "${git_branches}")
foreach(git_branch_candidate IN ITEMS ${git_branch_list})
string(REPLACE "*" "" git_branch_candidate_temp "${git_branch_candidate}")
string(STRIP "${git_branch_candidate_temp}" git_branch_candidate_2)
string(REPLACE "*" "" git_branch_candidate_temp "${git_branch_candidate}")
string(STRIP "${git_branch_candidate_temp}" git_branch_candidate_2)
if(NOT git_branch_candidate_2 MATCHES "^HEAD$")
string(REPLACE "/" ";" git_branch_candidate_2_list "${git_branch_candidate_2}")
list(GET git_branch_candidate_2_list -1 git_branch)
@@ -674,13 +654,13 @@ if(ADD_COMMIT_SHA)
message(STATUS "IfcOpenShell branch: \"${git_branch}\"")
message(STATUS "IfcOpenShell commit: \"${git_sha}\"")
if ("${git_branch}" STREQUAL "" OR "${git_sha}" STREQUAL "")
if("${git_branch}" STREQUAL "" OR "${git_sha}" STREQUAL "")
message(FATAL_ERROR "Unable to determine commit sha and/or branch")
endif()
target_compile_definitions(IfcParse PRIVATE
-DIFCOPENSHELL_BRANCH=${git_branch}
-DIFCOPENSHELL_COMMIT=${git_sha}
target_compile_definitions(
IfcParse
PRIVATE -DIFCOPENSHELL_BRANCH=${git_branch} -DIFCOPENSHELL_COMMIT=${git_sha}
)
endif()
endif(ADD_COMMIT_SHA)
@@ -699,7 +679,12 @@ endif()
# Documentation
if(BUILD_DOCUMENTATION)
add_subdirectory(../docs/cpp-api docs/cpp-api)
set(CMAKE_MODULE_PATH "../docs/cmake")
add_subdirectory(../docs docs)
endif()
if(BUILD_IFCPYTHON)
add_subdirectory(../src/ifcwrap ifcwrap)
endif()
if(BUILD_EXAMPLES)
@@ -714,48 +699,8 @@ if(BUILD_IFCPYTHON AND WITH_CGAL)
add_subdirectory(../src/svgfill svgfill)
endif()
if(BUILD_IFCPYTHON)
add_subdirectory(../src/ifcwrap ifcwrap)
endif()
if(BUILD_IFCMODEL_UI)
add_subdirectory(../src/ifcmodel-ui ifcmodel-ui)
endif()
if(BUILD_IFCGEOM)
# install(FILES ${IFCGEOM_H_FILES}
# DESTINATION ${INCLUDEDIR}/ifcgeom
# )
install(FILES ${SCHEMA_AGNOSTIC_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcgeom
)
file(GLOB SERIALIZATION_H_FILES ../src/ifcgeom/serialization/*.h)
install(FILES ${SERIALIZATION_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcgeom/serialization
)
foreach(kernel ${GEOMETRY_KERNELS})
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/kernels/${kernel}/*.h)
install(FILES ${IFCGEOM_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcgeom/kernels/${kernel}
)
endforeach()
install(
TARGETS ${IFCGEOM_SCHEMA_LIBRARIES} ${kernel_libraries} IfcGeom
EXPORT ${IFCOPENSHELL_EXPORT_TARGETS}
)
endif(BUILD_IFCGEOM)
if(BUILD_BONSAIVIEWER)
# IfcViewer is the unified scene + render lib since the wgpu/ifcviewer
# merge — wgpu-native is fetched inside its CMakeLists.txt.
add_subdirectory(../src/ifcviewer ifcviewer)
if(BUILD_BONSAIVIEWER_WGPU)
add_subdirectory(../src/ifcviewer-minimal ifcviewer-minimal)
endif()
add_subdirectory(../src/bonsaiviewer bonsaiviewer)
if(BUILD_QTVIEWER)
add_subdirectory(../src/qtviewer qtviewer)
endif()
# Cmake uninstall target
@@ -763,23 +708,23 @@ if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
IMMEDIATE
@ONLY
)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
# Packaging
list(APPEND CPACK_SOURCE_IGNORE_FILES
"/\\\\.git"
"/build/"
"/.pytest_cache/"
"/__pycache__/"
)
list(APPEND CPACK_SOURCE_IGNORE_FILES "/\\\\.git" "/build/" "/.pytest_cache/" "/__pycache__/")
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR}/..;/")
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}")
set(CPACK_PACKAGE_NAME
"${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}"
)
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}${EXTRA_VERSION}")
SET(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}")
set(CPACK_PACKAGE_FILE_NAME
"${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}"
)
set(CPACK_PACKAGE_DIRECTORY "${PROJECT_BINARY_DIR}/assets")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IfcOpenShell")
set(CPACK_PACKAGE_DESCRIPTION "IfcOpenShell.")
@@ -792,6 +737,7 @@ set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_GENERATOR "TGZ;DEB")
set(CPACK_SOURCE_GENERATOR "TGZ")
set(BOOST_DEPS "")
foreach(COMPONENT IN ITEMS ${BOOST_COMPONENTS})
string(REPLACE "_" "-" COMP ${COMPONENT})
set(BOOST_DEPS "${BOOST_DEPS}, libboost-${COMP}-dev")
@@ -799,12 +745,16 @@ endforeach(COMPONENT)
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_CONTACT}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3, libxml2, libocct-foundation-dev, libocct-modeling-algorithms-dev, libocct-modeling-data-dev, libocct-ocaf-dev, libocct-visualization-dev, libocct-data-exchange-dev, libpython3-dev, python3-pytest ${BOOST_DEPS}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS
"python3, libxml2, libocct-foundation-dev, libocct-modeling-algorithms-dev, libocct-modeling-data-dev, libocct-ocaf-dev, libocct-visualization-dev, libocct-data-exchange-dev, libhdf5-serial-dev, libpython3-dev, python3-pytest ${BOOST_DEPS}"
)
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_SECTION "science")
set(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}${EXTRA_VERSION}")
set(CPACK_DEBIAN_PACKAGE_VERSION
"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}${EXTRA_VERSION}"
)
set(CPACK_DEBIAN_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
# set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/cmake/debian/postinst")
+4 -1
View File
@@ -15,6 +15,7 @@
"BUILD_CONVERT": "ON",
"BUILD_IFCMAX": "OFF",
"IFCXML_SUPPORT": "ON",
"HDF5_SUPPORT": "ON",
"SCHEMA_VERSIONS": "4x3_add2",
"CMAKE_GENERATOR_PLATFORM": "",
"CMAKE_GENERATOR_TOOLSET": ""
@@ -49,6 +50,8 @@
"MPFR_LIBRARY_DIR": "$env{LIBRARY_PREFIX}/lib",
"Boost_LIBRARY_DIR": "$env{LIBRARY_PREFIX}/lib",
"Boost_INCLUDE_DIR": "$env{LIBRARY_PREFIX}/include",
"HDF5_INCLUDE_DIR": "$env{LIBRARY_PREFIX}/include",
"HDF5_LIBRARY_DIR": "$env{LIBRARY_PREFIX}/lib",
"ZLIB_INCLUDE_DIR": "$env{LIBRARY_PREFIX}/include"
}
},
@@ -107,4 +110,4 @@
}
}
]
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
# - `GMP_LIBRARY_DIR`
# - `MPFR_INCLUDE_DIR`
# - `MPFR_LIBRARY_DIR`
# If input variables are not specified, try to find CGAL config.
# If input variables are not specified, try to find HDF5 config.
# Input variables could also be provided as environment variables.
#
# Output targets:
+117
View File
@@ -0,0 +1,117 @@
#
# Input variables:
# - `HDF5_INCLUDE_DIR`
# - `HDF5_LIBRARY_DIR`
# - `HDF5_LIBRARIES`
# If input variables are not specified, try to find HDF5 config.
# Input variables could also be provided as environment variables.
#
# Output variables:
# - `HDF5_INCLUDE_DIR`
# - `HDF5_LIBRARY_DIR`
# - `HDF5_LIBRARIES`
#
UNIFY_ENVVARS_AND_CACHE(HDF5_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(HDF5_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(HDF5_LIBRARIES)
# To avoid cyclic calls to this file
list(REMOVE_ITEM CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR})
if(NOT HDF5_INCLUDE_DIR)
message(STATUS "No HDF5 include directory specified")
else()
set(HDF5_INCLUDE_DIR "${HDF5_INCLUDE_DIR}" CACHE FILEPATH "HDF5 header files")
endif()
if(NOT HDF5_LIBRARY_DIR)
message(STATUS "No HDF5 library directory specified")
else()
set(HDF5_LIBRARY_DIR "${HDF5_LIBRARY_DIR}" CACHE FILEPATH "HDF5 library files")
endif()
if(HDF5_LIBRARY_DIR)
# result of the HDF5 ctest package
# Find zlib using cmake find_library. How should this be implemented?
# FIND_LIBRARY(NAMES z libz libz_debug PATHS ... NO_DEFAULT_PATH)
if(NOT DEFINED ENV{CONDA_BUILD})
# result of the HDF5 ctest package
if(WIN32)
set(zlib_post lib)
set(lib_ext lib)
else()
set(lib_ext a)
endif()
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(debug_postfix "_debug")
endif()
set(HDF5_LIBRARIES
"${HDF5_LIBRARY_DIR}/libhdf5_cpp${debug_postfix}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libhdf5${debug_postfix}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libz${zlib_post}${debug_postfix}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libsz${debug_postfix}.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libaec${debug_postfix}.${lib_ext}"
)
else()
message(STATUS "Packaging hdf5 and zlib for conda distribution")
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# macOS
set(zlib_post libz)
set(lib_ext dylib)
set(HDF5_LIBRARIES
"${HDF5_LIBRARY_DIR}/libhdf5_cpp.${lib_ext}"
"${HDF5_LIBRARY_DIR}/libhdf5.${lib_ext}"
"${HDF5_LIBRARY_DIR}/${zlib_post}.${lib_ext}"
)
else()
# linux and windows
# Find HDF5 package
find_package(HDF5 REQUIRED COMPONENTS C CXX)
# Find ZLIB package
find_package(ZLIB REQUIRED)
# Include directories
include_directories(${HDF5_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS})
# Link libraries
set(HDF5_LIBRARIES ${HDF5_LIBRARIES} ${ZLIB_LIBRARIES})
message(STATUS "HDF5 libraries: ${HDF5_LIBRARIES}")
endif()
endif()
endif()
if(NOT HDF5_INCLUDE_DIR OR NOT HDF5_LIBRARY_DIR)
# First try to find it as a config.
find_package(HDF5 CONFIG)
mark_as_advanced(HDF5_DIR)
if(HDF5_DIR)
message(STATUS "HDF5: found config at '${HDF5_DIR}'.")
if(TARGET hdf5_cpp-static)
set(HDF5_LIBRARIES hdf5_cpp-static)
elseif(TARGET hdf5_cpp-shared)
set(HDF5_LIBRARIES hdf5_cpp-shared)
elseif(TARGET hdf5::hdf5_cpp-shared)
set(HDF5_LIBRARIES hdf5::hdf5_cpp-shared)
else()
find_package(HDF5 REQUIRED COMPONENTS CXX)
endif()
else()
# If it failed, still try to find as a module.
# E.g. on Ubuntu `libhdf5-dev` doesn't provie hdf5-config.cmake.
# Will automatically fill HDF5_LIBRARIES and HDF5_INCLUDE_DIR.
find_package(HDF5 COMPONENTS CXX)
if(NOT HDF5_INCLUDE_DIR)
message(
FATAL_ERROR
"HDF5_INCLUDE_DIR is not provided (current value: '${HDF5_INCLUDE_DIR}'). "
"HDF5_LIBRARY_DIR is not provided (current value: '${HDF5_LIBRARY_DIR}'). "
"Also could not find HDF5 package (neither module or config)."
)
endif()
endif()
endif()
# Restore module path.
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR})
+3 -4
View File
@@ -139,8 +139,7 @@ if(NOT OpenCOLLADA_DIR)
endif()
endif(NOT OpenCOLLADA_DIR)
if(OPENCOLLADA_FOUND AND NOT TARGET OpenCOLLADA::OpenCOLLADA)
add_library(OpenCOLLADA::OpenCOLLADA INTERFACE IMPORTED)
target_include_directories(OpenCOLLADA::OpenCOLLADA INTERFACE ${OPENCOLLADA_INCLUDE_DIRS})
target_link_libraries(OpenCOLLADA::OpenCOLLADA INTERFACE ${OPENCOLLADA_LIBRARIES})
if(OPENCOLLADA_FOUND)
add_definitions(-DWITH_OPENCOLLADA)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_OPENCOLLADA)
endif()
+6 -12
View File
@@ -6,7 +6,7 @@
# Input variables could also be provided as environment variables.
#
# Output targets:
# - `proj::proj`
# - `PROJ::proj`
#
# To avoid cyclic calls to this file
@@ -34,12 +34,10 @@ if((NOT PROJ_INCLUDE_DIR AND NOT PROJ_LIBRARIES))
message(FATAL_ERROR "Unable to find PROJ libraries in: ${PROJ_LIBRARY_DIR}")
endif()
if(NOT TARGET proj::proj)
add_library(proj::proj INTERFACE IMPORTED)
target_include_directories(proj::proj INTERFACE "${PROJ_INCLUDE_DIR}")
target_link_libraries(proj::proj INTERFACE ${PROJ_LIBRARIES})
target_link_directories(proj::proj INTERFACE "${PROJ_LIBRARY}")
endif()
add_library(PROJ::proj INTERFACE IMPORTED)
target_include_directories(PROJ::proj INTERFACE "${PROJ_INCLUDE_DIR}")
target_link_libraries(PROJ::proj INTERFACE ${PROJ_LIBRARIES})
target_link_directories(PROJ::proj INTERFACE "${PROJ_LIBRARY}")
endif()
else()
find_library(PROJ_LIBRARY NAMES proj PATHS ${PROJ_LIBRARY_DIR})
@@ -52,11 +50,7 @@ else()
set(PROJ_INCLUDE_DIR ${PROJ_INCLUDE_DIR} CACHE FILEPATH "PROJ header files")
message(STATUS "Looking for PROJ include files in: ${PROJ_INCLUDE_DIR}")
if(NOT TARGET proj::proj)
add_library(proj::proj INTERFACE IMPORTED)
target_include_directories(proj::proj INTERFACE "${PROJ_INCLUDE_DIR}")
target_link_libraries(proj::proj INTERFACE ${PROJ_LIBRARIES})
endif()
include_directories(${PROJ_INCLUDE_DIR})
endif()
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR})
+3
View File
@@ -64,6 +64,7 @@ set(USD_LIBRARIES
find_library(USD_LIBRARY NAMES ${USD_LIBRARIES} PATHS ${USD_LIBRARY_DIR})
if(USD_LIBRARY)
message(STATUS "USD libraries ${USD_LIBRARIES} found in: ${USD_LIBRARY_DIR}")
link_directories(${USD_LIBRARY_DIR})
else()
message(FATAL_ERROR "Unable to find USD libraries in: ${USD_LIBRARY_DIR}")
endif()
@@ -81,3 +82,5 @@ if(MSVC)
endif()
target_compile_definitions(pxr::USD INTERFACE PXR_STATIC WITH_USD)
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_USD)
-2
View File
@@ -1,7 +1,5 @@
@PACKAGE_INIT@
set_and_check(IFCOPENSHELL_LIBRARY_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
# Variable to inspect installed schema versions.
set(IFCOPENSHELL_SCHEMA_VERSIONS @SCHEMA_VERSIONS@)
-1
View File
@@ -17,7 +17,6 @@ configure_package_config_file(
${CONFIG_PACKAGE_INPUT}
${CONFIG_PACKAGE_OUTPUT}
INSTALL_DESTINATION ${CONFIG_PACKAGE_LOCATION}
PATH_VARS CMAKE_INSTALL_LIBDIR
)
install(FILES "${CONFIG_PACKAGE_OUTPUT}" "${CONFIG_VERSION_OUTPUT}" DESTINATION ${CONFIG_PACKAGE_LOCATION})
-116
View File
@@ -41,122 +41,6 @@ macro(SET_INSTALL_RPATHS _target _paths)
set_target_properties(${_target} PROPERTIES INSTALL_RPATH "${${_target}_rpaths}")
endmacro()
macro(SET_INSTALL_SELF_RPATH _target)
if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
SET_INSTALL_RPATHS(${_target} "${CMAKE_INSTALL_LIBDIR}")
elseif(APPLE)
SET_INSTALL_RPATHS(${_target} "@loader_path")
else()
SET_INSTALL_RPATHS(${_target} "$ORIGIN")
endif()
endmacro()
function(ifcopenshell_plugin_target TARGET)
# Plug-ins are loaded by exact filename and should not receive a platform library prefix.
set_target_properties(${TARGET} PROPERTIES PREFIX "")
if((NOT WIN32) AND BUILD_SHARED_LIBS AND NOT WASM_BUILD AND NOT CREATE_BUNDLE AND NOT CMAKE_INSTALL_RPATH AND COMMAND SET_INSTALL_SELF_RPATH)
SET_INSTALL_SELF_RPATH(${TARGET})
endif()
endfunction()
function(ifcopenshell_wasm_plugin_link_options TARGET REGISTRATION_SYMBOL)
ifcopenshell_plugin_target(${TARGET})
if(NOT WASM_BUILD)
return()
endif()
cmake_parse_arguments(PLUGIN "" "OPTIMIZATION" "" ${ARGN})
if(NOT PLUGIN_OPTIMIZATION)
set(PLUGIN_OPTIMIZATION -O1)
endif()
set(plugin_symbols
ifcopenshell_plugin_abi_v1
ifcopenshell_plugin_metadata_v1
${REGISTRATION_SYMBOL}
)
target_link_options(${TARGET} PRIVATE "SHELL:-s SIDE_MODULE=2" ${PLUGIN_OPTIMIZATION})
foreach(symbol IN LISTS plugin_symbols)
target_link_options(${TARGET} PRIVATE "LINKER:--export=${symbol}")
endforeach()
endfunction()
function(ifcopenshell_deploy_qt_runtime TARGET)
if(NOT IFCOPENSHELL_DEPLOY_QT_RUNTIME)
return()
endif()
if(NOT TARGET ${TARGET})
message(FATAL_ERROR "Cannot deploy Qt runtime for unknown target '${TARGET}'.")
endif()
get_target_property(target_type ${TARGET} TYPE)
if(NOT target_type STREQUAL "EXECUTABLE")
message(FATAL_ERROR "Qt runtime deployment target '${TARGET}' is not an executable.")
endif()
if(NOT DEFINED QT_DEFAULT_MAJOR_VERSION)
if(DEFINED QT_VERSION)
set(QT_DEFAULT_MAJOR_VERSION ${QT_VERSION})
else()
set(QT_DEFAULT_MAJOR_VERSION 6)
endif()
endif()
if(NOT TARGET Qt${QT_DEFAULT_MAJOR_VERSION}::Core)
set(qt_find_args Qt${QT_DEFAULT_MAJOR_VERSION} COMPONENTS Core REQUIRED)
if(DEFINED QT_DIR AND NOT QT_DIR STREQUAL "")
list(APPEND qt_find_args PATHS ${QT_DIR})
endif()
find_package(${qt_find_args})
endif()
if(COMMAND _qt_internal_setup_deploy_support)
if(NOT DEFINED QT_CMAKE_EXPORT_NAMESPACE AND TARGET Qt${QT_DEFAULT_MAJOR_VERSION}::Core)
set(QT_CMAKE_EXPORT_NAMESPACE Qt${QT_DEFAULT_MAJOR_VERSION})
endif()
if(QT_DEFAULT_MAJOR_VERSION EQUAL 6 AND TARGET Qt6::Core)
get_target_property(qt_core_type Qt6::Core TYPE)
if(qt_core_type STREQUAL "SHARED_LIBRARY")
set(QT6_IS_SHARED_LIBS_BUILD ON)
else()
set(QT6_IS_SHARED_LIBS_BUILD OFF)
endif()
endif()
_qt_internal_setup_deploy_support()
endif()
set(deploy_args
TARGET ${TARGET}
OUTPUT_SCRIPT deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)
if(NOT IFCOPENSHELL_DEPLOY_QT_TRANSLATIONS)
list(APPEND deploy_args NO_TRANSLATIONS)
endif()
list(APPEND deploy_args ${ARGN})
if(COMMAND qt_generate_deploy_app_script)
qt_generate_deploy_app_script(${deploy_args})
elseif(COMMAND qt6_generate_deploy_app_script)
qt6_generate_deploy_app_script(${deploy_args})
else()
message(WARNING
"Qt runtime deployment requested for '${TARGET}', but this Qt version "
"does not provide qt_generate_deploy_app_script()."
)
return()
endif()
install(SCRIPT ${deploy_script})
endfunction()
# Get a list of all OPTION flags from the CMakeLists.txt and store in an output LIST
function(get_all_option_flags output_list)
# Read the contents of the CMakeLists.txt
+4
View File
@@ -22,6 +22,9 @@ cmake -G "Ninja" ^
-D GMP_LIBRARY_DIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
-D MPFR_LIBRARY_DIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
-D COLLADA_SUPPORT=OFF ^
-D HDF5_SUPPORT=ON ^
-D HDF5_INCLUDE_DIR="%LIBRARY_PREFIX%\include" ^
-D HDF5_LIBRARY_DIR="%LIBRARY_PREFIX%\lib" ^
-D JSON_INCLUDE_DIR="%LIBRARY_PREFIX%\include" ^
-D PYTHON_INCLUDE_DIR=%PREFIX%\include ^
-D PYTHON_EXECUTABLE:FILEPATH=%PREFIX%\python.exe ^
@@ -34,6 +37,7 @@ cmake -G "Ninja" ^
-D GLTF_SUPPORT:BOOL=ON ^
-D BUILD_CONVERT:BOOL=ON ^
-D BUILD_IFCMAX:BOOL=OFF ^
-D IFCXML_SUPPORT:BOOL=ON ^
-D Boost_LIBRARY_DIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
-D Boost_INCLUDE_DIR:FILEPATH="%LIBRARY_PREFIX%\include" ^
-D Boost_USE_STATIC_LIBS:BOOL=OFF ^
+5 -1
View File
@@ -24,6 +24,9 @@ cmake ${CMAKE_ARGS} -G Ninja \
-DMPFR_LIBRARY_DIR=$PREFIX/lib \
-DOCC_INCLUDE_DIR=$PREFIX/include/opencascade \
-DOCC_LIBRARY_DIR=$PREFIX/lib \
-DHDF5_SUPPORT:BOOL=ON \
-DHDF5_INCLUDE_DIR=$PREFIX/include \
-DHDF5_LIBRARY_DIR=$PREFIX/lib \
-DJSON_INCLUDE_DIR=$PREFIX/include \
-DCGAL_INCLUDE_DIR=$PREFIX/include \
-DLIBXML2_INCLUDE_DIR=$PREFIX/include/libxml2 \
@@ -31,6 +34,7 @@ cmake ${CMAKE_ARGS} -G Ninja \
-DEIGEN_DIR:FILEPATH=$PREFIX/include/eigen3 \
-DCOLLADA_SUPPORT:BOOL=OFF \
-DBUILD_EXAMPLES:BOOL=OFF \
-DIFCXML_SUPPORT:BOOL=ON \
-DGLTF_SUPPORT:BOOL=ON \
-DBUILD_CONVERT:BOOL=ON \
-DBUILD_IFCPYTHON:BOOL=ON \
@@ -43,4 +47,4 @@ ninja
ninja install -j 1
python "${RECIPE_DIR}/update_version_init.py" "${PKG_VERSION}" "${SP_DIR}/ifcopenshell/__init__.py"
python "${RECIPE_DIR}/update_version_init.py" "${PKG_VERSION}" "${SP_DIR}/ifcopenshell/__init__.py"
+2
View File
@@ -26,6 +26,8 @@ c_stdlib_version:
- 2.17 # [linux]
- 10.13 # [osx and x86_64]
- 11.0 # [osx and arm64]
hdf5:
- 1.14.6
libboost_devel:
- '1.86'
libxml2:
+6
View File
@@ -33,6 +33,7 @@ requirements:
- occt
- libxml2
- cgal-cpp
- hdf5
- eigen
- mpfr
- nlohmann_json
@@ -284,6 +285,11 @@ about:
<td>Internal library for IfcOpenShell</td>
<td>LGPL-3.0-or-later*</td>
</tr>
<tr>
<td>qtviewer</td>
<td>Internal library for IfcOpenShell</td>
<td>LGPL-3.0-or-later*</td>
</tr>
<tr>
<td>serializers</td>
<td>Internal library for IfcOpenShell</td>
+34 -14
View File
@@ -1,15 +1,35 @@
find_package(Doxygen REQUIRED)
find_program(
SPHINX_EXECUTABLE
NAMES sphinx-build
REQUIRED
DOC "Path to the sphinx-build executable"
)
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE NAMES sphinx-build DOC "Path to sphinx-build executable")
add_custom_target(
cpp_api_docs
COMMAND ${SPHINX_EXECUTABLE} -M html . output -W --keep-going
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating the IfcOpenShell C++ API documentation"
VERBATIM
)
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx "Failed to find sphinx-build executable" SPHINX_EXECUTABLE)
find_package(Doxygen REQUIRED)
#find_package(Sphinx REQUIRED)
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx)
message(STATUS "SPHINX BUILD ${CMAKE_CURRENT_BINARY_DIR}")
file(MAKE_DIRECTORY ./output/doxygen)
if(DOXYGEN_FOUND)
add_custom_target(
Sphinx
ALL
COMMAND ${SPHINX_EXECUTABLE} -v -T -b html ${SPHINX_SOURCE} ${CMAKE_CURRENT_SOURCE_DIR}/output
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output
COMMENT "Generating documentation with Sphinx"
)
# add_custom_target(ifcopenshell_python_docs ALL
# COMMAND make html
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
# OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
# COMMENT "Generating documentation with Sphinx")
else(DOXYGEN_FOUND)
message("Doxygen need to be installed to generate the doxygen documentation")
endif(DOXYGEN_FOUND)
+22 -63
View File
@@ -68,7 +68,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = ./output/doxygen
OUTPUT_DIRECTORY = ./output
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
@@ -852,7 +852,7 @@ WARNINGS = YES
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = NO
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as documenting some parameters in
@@ -901,7 +901,7 @@ WARN_IF_UNDOC_ENUM_VAL = NO
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# The default value is: NO.
WARN_AS_ERROR = FAIL_ON_WARNINGS
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
@@ -944,6 +944,7 @@ WARN_LOGFILE =
# Note: If this tag is empty the current directory is searched.
INPUT = ../../src/ifcgeom \
../../src/ifcgeom_schema_agnostic \
../../src/ifcparse \
../../src/serializers \
@@ -1000,7 +1001,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = ../../src/ifcparse/schemas
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@@ -1024,33 +1025,7 @@ EXCLUDE_PATTERNS =
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
EXCLUDE_SYMBOLS = "ifcopenshell::geom::opaque_number::*" \
ifcopenshell::entity::attribute_by_name_cmp \
ifcopenshell::impl::rocks_db_file_storage::rocksdb_types_iterator \
ifcopenshell::impl::in_memory_file_storage::type_iterator \
"util::string_buffer::*_item" \
util::string_buffer::item \
ifcopenshell::geom::layer_filter::wildcards_match \
ifcopenshell::paged_file_impl::entry \
ifcopenshell::token \
attribute_value::pointer_type \
INCLUDE_PARENT_PARENT_DIR \
POSTFIX_SCHEMA_ \
POSTFIX_SCHEMA__ \
STRINGIFY_ \
MAKE_INIT_FN_ \
MAKE_INIT_FN__ \
key_from_string \
add_ \
subtract_ \
multiply_ \
divide_ \
equals_ \
less_than_ \
negate_ \
ifcopenshell::geom::utils::create_cube \
ifcopenshell::geom::utils::create_polyhedron \
ifcopenshell::geom::utils::create_nef_polyhedron
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
@@ -1261,7 +1236,7 @@ IGNORE_PREFIX =
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES.
GENERATE_HTML = NO
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -1336,7 +1311,7 @@ HTML_STYLESHEET =
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_STYLESHEET = assets/doxygen-awesome-css/doxygen-awesome.css
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
@@ -2191,7 +2166,7 @@ MAN_LINKS = NO
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = YES
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -2328,7 +2303,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = YES
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -2336,7 +2311,7 @@ MACRO_EXPANSION = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = YES
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
@@ -2369,17 +2344,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = IFC_PARSE_API= \
IFC_SCHEMA_API= \
IFC_GEOM_API= \
IFC_GEOMLIBRARY_API= \
IFC_GEOMSERIALIZATION_API= \
SERIALIZERS_API= \
"POSTFIX_SCHEMA(name)=name##_Schema" \
"Handle(name):=opencascade::handle<name>" \
kernel_=kernel \
Simplekernel_=Simplekernel \
inline=
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@@ -2388,22 +2353,7 @@ PREDEFINED = IFC_PARSE_API= \
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED = kernel_ \
cgal_shape \
cgal_kernel \
cgal_placement \
cgal_point \
cgal_direction \
cgal_vector \
cgal_plane \
cgal_curve \
cgal_wire \
cgal_face \
cgal_polyhedron \
cgal_vertex_descriptor \
cgal_face_descriptor \
create_cube \
create_polyhedron
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
@@ -2781,6 +2731,15 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
+18 -41
View File
@@ -1,56 +1,33 @@
# IfcOpenShell C++ API documentation
This directory contains the Sphinx, Doxygen, Breathe, and Exhale configuration
for the IfcOpenShell C++ API reference. During a Sphinx build, Exhale runs
Doxygen, Breathe consumes the generated XML, and Exhale creates the API pages.
## Prerequisites
- Python 3.10 or newer
- [Doxygen](https://www.doxygen.nl/)
- [Graphviz](https://graphviz.org/)
Install the Python dependencies from this directory:
```shell
python -m pip install -r requirements.txt
```
Both `doxygen` and `dot` must be available on `PATH`. For the standard Windows
install locations, this can be done for the current PowerShell session with:
```powershell
$env:Path = "C:\Program Files\doxygen\bin;C:\Program Files\Graphviz\bin;$env:Path"
```
This folder contains the setup to build the IfcOpenShell C++ API documentation from the source code.
## Generating the documentation
From this directory, run:
> Prerequisites:
>
> Make sure to have [Doxygen](https://www.doxygen.nl) and [Graphviz](https://graphviz.org) installed into your `$PATH` variable.
>
> The documentation also use the [doxygen-awesome](https://jothepro.github.io/doxygen-awesome-css) theme as a git submodule.
Build with the command (from within the `/docs/cpp-api` folder):
```shell
python -m sphinx -M html . output -W --keep-going
$ doxygen
```
To include the current Git commit in Doxygen's project metadata, set
`PROJECT_NUMBER` before building. For example, in PowerShell:
```powershell
$env:PROJECT_NUMBER = git rev-parse --short HEAD
python -m sphinx -M html . output -W --keep-going
```
Or in a POSIX shell:
To include the current git commit hash into the build documentation, use the following command:
```shell
PROJECT_NUMBER=$(git rev-parse --short HEAD) python -m sphinx -M html . output -W --keep-going
$ PROJECT_NUMBER=$(git rev-parse --short HEAD) doxygen
```
Alternatively, configure the main CMake project with
`-DBUILD_DOCUMENTATION=ON` and build the `cpp_api_docs` target.
This will extract the current commit hash in short version and sets the propper ENV variable used by doxygen.
The generated documentation is written to `output/html/index.html`. The
generated Doxygen XML and Exhale sources are kept under `output/` as build
artifacts.
The generation of the documentation might take a while depending on your systems hardware, as it is configured to generate the Class graphs using .
The generated headers under `src/ifcparse/schemas` are intentionally excluded
from this documentation build.
The resulting documentation is located unter `/cpp-api/output/html` and can be directly accessed with your browser:
```shell
$ open ./output/html/index.html
```
-59
View File
@@ -1,59 +0,0 @@
# This file was generated with the assistance of an AI coding tool.
import warnings
from pathlib import Path
from shutil import rmtree
from sphinx.deprecation import RemovedInSphinx90Warning
warnings.filterwarnings("ignore", category=RemovedInSphinx90Warning, module=r"exhale\.configs")
generated_directories = (
Path(__file__).parent / "output" / "api",
Path(__file__).parent / "output" / "doxygen",
)
for generated_directory in generated_directories:
if generated_directory.is_dir():
rmtree(generated_directory)
project = "IfcOpenShell"
copyright = "2020, IfcOpenShell"
extensions = [
"breathe",
"exhale",
]
primary_domain = "cpp"
highlight_language = "cpp"
html_theme = "alabaster"
breathe_projects = {
"IfcOpenShell": "./output/doxygen/xml",
}
breathe_default_project = "IfcOpenShell"
exhale_args = {
"containmentFolder": "./output/api",
"rootFileName": "library_root.rst",
"rootFileTitle": "IfcOpenShell C++ API",
"doxygenStripFromPath": "../..",
"createTreeView": False,
"exhaleExecutesDoxygen": True,
"exhaleUseDoxyfile": True,
}
cpp_id_attributes = [
"IFC_PARSE_API",
"IFC_SCHEMA_API",
"IFC_GEOM_API",
"IFC_GEOMLIBRARY_API",
"IFC_GEOMSERIALIZATION_API",
"SERIALIZERS_API",
]
exclude_patterns = [
"output/doctrees",
"output/doxygen",
"output/html",
]
-9
View File
@@ -1,9 +0,0 @@
.. This file was generated with the assistance of an AI coding tool.
IfcOpenShell C++ API
====================
.. toctree::
:maxdepth: 2
output/api/library_root
-5
View File
@@ -1,5 +0,0 @@
# This file was generated with the assistance of an AI coding tool.
Sphinx==8.1.3
breathe==4.36.0
exhale==0.3.7
-317
View File
@@ -1,317 +0,0 @@
# Build fix: remove `boost_system` from CMake components
`Boost.System` became header-only in Boost 1.69. Boost 1.90.0 no longer ships a compiled library or CMake config for it, so `find_package(Boost REQUIRED COMPONENTS system ...)` fails.
## Fix
`cmake/CMakeLists.txt`:
```diff
- set(BOOST_COMPONENTS system program_options regex thread date_time iostreams)
+ set(BOOST_COMPONENTS program_options regex thread date_time iostreams)
```
The headers are still available; no linking is needed.
# Build fix: add `template` keyword for dependent template member calls
Calling a template member function through a dependent expression (e.g. `storage->has_attribute_value<T>(...)` where `storage`'s type depends on a template parameter) requires the `template` keyword to disambiguate from a less-than comparison.
## Error
```
src/ifcparse/IfcParse.cpp:1856:67: error: expected primary-expression before '>' token
1856 | if (storage->has_attribute_value<express::Base>(attr_index)) {
| ^
```
Six identical errors at lines 1856, 1865, 1896, 1905, 1934, 1943.
## Fix
`src/ifcparse/IfcParse.cpp`:
```diff
-storage->has_attribute_value<express::Base>(attr_index)
+storage->template has_attribute_value<express::Base>(attr_index)
-storage->has_attribute_value<Blank>(attr_index)
+storage->template has_attribute_value<Blank>(attr_index)
```
Applied at all six call sites in `in_memory_file_storage::read_from_stream`.
# Linker fix: missing explicit template instantiations for `InstanceStreamer`
`InstanceStreamer` is a class template with methods defined in `IfcParse.cpp`, not the header. Without explicit instantiations, the linker can't find the symbols when the SWIG wrapper loads.
## Error
```
ImportError: undefined symbol: _ZN8IfcParse16InstanceStreamerINS_10FileReaderINS_14FullBufferImplEEEEC1EPS3_PNS_7IfcFileE
(IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(FileReader<FullBufferImpl>*, IfcFile*))
```
## Fix
Cannot use `template class InstanceStreamer<...>` because some constructors have `static_assert` guards that reject certain reader types. Instead, instantiate each member function individually per reader type, only including the constructors valid for that type.
`src/ifcparse/IfcParse.cpp` (after the last `InstanceStreamer` method definition):
```cpp
// FullBufferImpl
template IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(IfcParse::IfcFile*);
template IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(const std::string&, bool, IfcParse::IfcFile*);
template IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(void*, int, IfcParse::IfcFile*);
template IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(FileReader<FullBufferImpl>*, IfcParse::IfcFile*);
// ... plus ensure_header, initialize_header, hasSemicolon, semicolonCount,
// pushPage, bypassTypes, readInstance
// PushedSequentialImpl — same pattern, different valid constructors
// MMapFileReader (ifdef USE_MMAP) — same pattern
```
# Linker fix: `FullBufferImpl` missing buffer constructor
SWIG's `stream_from_string` calls `InstanceStreamer<FileReader<FullBufferImpl>>(void*, int, IfcFile*)`, but the `(void*, int)` constructor previously hit a `static_assert` for `FullBufferImpl` — it only allowed `PushedSequentialImpl`.
## Error
```
ImportError: undefined symbol: _ZN8IfcParse16InstanceStreamerINS_10FileReaderINS_14FullBufferImplEEEEC1EPviPNS_7IfcFileE
(InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(void*, int, IfcFile*))
```
## Fix
Three changes to make `FullBufferImpl` support buffer-based and default construction:
`src/ifcparse/FileReader.h` — add buffer constructor to `FullBufferImpl`:
```diff
class IFC_PARSE_API FullBufferImpl {
public:
explicit FullBufferImpl(const std::string& fn);
+ FullBufferImpl(void* data, size_t length);
```
`src/ifcparse/FileReader.h` — add `FileReader(void*, size_t)` forwarding constructor:
```diff
+ FileReader(void* data, size_t length)
+ : cursor_(0) {
+ if constexpr (std::is_same_v<Impl, FullBufferImpl>) {
+ impl_ = std::make_shared<Impl>(data, length);
+ } else {
+ static_assert(...);
+ }
+ }
```
`src/ifcparse/FileReader.cpp` — implement the constructor:
```cpp
FullBufferImpl::FullBufferImpl(void* data, size_t length)
: buf_(static_cast<char*>(data), static_cast<char*>(data) + length)
, size_(length) {
}
```
`src/ifcparse/IfcParse.cpp` — extend the two `InstanceStreamer` constructors to accept `FullBufferImpl`:
```diff
// InstanceStreamer(IfcFile*):
+ } else if constexpr (std::is_same_v<Reader, FileReader<FullBufferImpl>>) {
+ owned_stream_ = std::make_unique<Reader>(nullptr, (size_t)0);
// InstanceStreamer(void*, int, IfcFile*):
+ } else if constexpr (std::is_same_v<Reader, FileReader<FullBufferImpl>>) {
+ owned_stream_ = std::make_unique<Reader>(data, (size_t)length);
```
# Runtime fix: segfault in `parse_context::push()` due to vector reallocation
`parse_context_pool` stores nodes in a `std::vector<parse_context>`. During parsing, `load()` takes a `parse_context&` parameter and calls `context.push()`, which calls `pool_->make()`. If the pool's vector reallocates (via `emplace_back`), all existing references into the vector — including the `context` reference held by the caller — become dangling. Subsequent access through the dangling reference causes a segfault.
Triggered by larger IFC files (e.g. `ISSUE_159_kleine_Wohnung_R22.ifc`, 9.5 MB) that cause enough pool growth to trigger reallocation.
## Error
```
Thread 1 received signal SIGSEGV, Segmentation fault.
0x... in IfcParse::parse_context::push()
#1 in_memory_file_storage::load(...) // context& is dangling after reallocation
#2 in_memory_file_storage::load(...) // parent call
#3 InstanceStreamer::readInstance()
```
## Fix
`src/ifcparse/storage.h` — change the pool container from `std::vector` to `std::deque`, which does not invalidate references on `push_back`/`emplace_back`:
```diff
+#include <deque>
struct parse_context_pool {
- std::vector<parse_context> nodes_;
+ std::deque<parse_context> nodes_;
```
# Runtime fix: `express::Base` comparison operators throw on null/expired instances
`express::Base::operator<` and `operator==` called `data()`, which throws `std::runtime_error("Trying to access deleted instance reference")` when the internal `weak_ptr` is expired. A default-constructed `express::Base` (the value-type equivalent of a null pointer) always has an expired `weak_ptr`.
## Why this model triggers it
The bug requires two conditions to coincide:
1. A representation is shared by **more than one product** (via `IfcRepresentationMap` / `IfcMappedItem`).
2. At least one of those products has **no material association**, so `get_single_material_association()` returns `express::Base{}` (the null equivalent).
In `advanced_model.ifc`, Body representations like `#449` (Body/Brep) have a single `IfcRepresentationMap` (`#453`) with 13 `IfcMappedItem` usages, meaning 13 products share the geometry. Some of those products (e.g. `IfcFlowTerminal` instances) have no `IfcRelAssociatesMaterial`, so `get_single_material_association` returns `express::Base{}`.
Smaller or simpler models don't hit this because either:
- Every representation maps to only 1 product → `reuse_ok_` short-circuits at `products.size() == 1` before reaching the material check.
- Every product has a material association → no null `express::Base` is ever inserted into the set.
## Exact call sequence
```
Iterator::initialize()
try {
mapping::get_representations(reps, filters_)
addRepresentationsFromDefaultContexts(representations)
→ collects reps from subcontexts in order:
Axis (#115): 143 reps
Body (#117): 7550 reps
FootPrint (#119): 12 reps
for (auto representation : representations):
── Axis reps (indices 0142) ──────────────────────────
products_represented_by(rep, rmap)
→ OfProductRepresentation: 1 product each
filter_products(products, filters) → 1 product
reuse_ok_(ifcproducts)
→ products.size() == 1 → return true ← SHORT-CIRCUIT, no material check
representation_mapped_to(rep) → null (no MappedItem)
→ task created. 143 tasks accumulated.
── First Body rep #449 (Body/Brep) ────────────────────
products_represented_by(#449, rmap)
→ OfProductRepresentation: empty
→ RepresentationMap: 1 map (#453)
→ MapUsage: 13 MappedItems → traces through to 13 IfcProducts
filter_products(products, filters) → 13 products
reuse_ok_(ifcproducts) ← CRASH HERE
→ products.size() == 1? NO (13 products)
→ for each product:
find_openings(product) → OK
get_single_material_association(product)
→ some products have no IfcRelAssociatesMaterial
→ returns express::Base{} (expired weak_ptr)
associated_single_materials.insert(result)
→ std::set::insert calls operator<
→ operator< calls data()
→ data() calls data_.lock() → expired → THROWS
"Trying to access deleted instance reference"
} catch (const std::exception& e) {
Logger::Error(e) ← exception caught here, get_representations aborted
}
→ reps contains only the 143 Axis tasks created before the throw
→ all 143 Axis reps have Curve2D geometry → map(representation) returns null
→ no valid elements produced → initialize() returns false
```
In the old pointer-based code, `reuse_ok_` used `std::set<const IfcUtil::IfcBaseEntity*>` and `get_single_material_association` returned `nullptr`. Inserting `nullptr` into a `std::set<T*>` is a plain pointer comparison — no dereference, no throw. The refactoring to `std::set<express::Base>` changed the comparison from pointer comparison to `express::Base::operator<`, which unconditionally dereferences through `data()`.
## Error
```
[Error] Trying to access deleted instance reference
[Notice] Created 143 tasks for 143 products ← only Axis reps; all Body reps lost
initialize() returned: False
```
## Fix
`src/ifcparse/express.h` — use `weak_ptr::lock().get()` instead of `data()` so that expired pointers compare as `nullptr` (matching old raw-pointer semantics):
```diff
bool operator<(const Base& other) const {
- return data() < other.data();
+ auto a = data_.lock();
+ auto b = other.data_.lock();
+ return a.get() < b.get();
}
bool operator==(const Base& other) const {
- return data() == other.data();
+ auto a = data_.lock();
+ auto b = other.data_.lock();
+ return a.get() == b.get();
}
```
# Runtime fix: `entity_instance` missing `get_inverse` due to SWIG `%rename` collision
Accessing inverse attributes (e.g. `element.IsDecomposedBy`) on any entity raises `AttributeError: entity instance of type 'IFC2X3.IfcProject' has no attribute 'get_inverse'`.
## Why
`entity_instance_mixin.__getattr__` (line 106 of `entity_instance.py`) calls `self.get_inverse(name)` when it detects an inverse attribute. Since the mixin inherits into the SWIG-generated `entity_instance` class (via the `object = custom_base` hack in `IfcParseWrapper.i:936`), `self.get_inverse` must resolve to a method on the SWIG class.
However, `IfcParseWrapper.i:70` has a global rename:
```
%rename("get_inverses_by_declaration") get_inverse;
```
This was intended for `ifcopenshell::file::get_inverse` (which takes an entity + declaration and returns instances by reference), but SWIG `%rename` is global — it also renames the `%extend express::Base` method `get_inverse(const std::string& a)` at line 551. So the Python-side `entity_instance` class exposes the method as `get_inverses_by_declaration`, not `get_inverse`.
The old code (`v0.8.0`) didn't hit this because `__getattr__` called `self.wrapped_data.get_inverse(name)` on an inner `ifcopenshell_wrapper.entity_instance` object — but in that old layout, the inner object was constructed differently and the rename didn't apply the same way (or the method had a different path). In the new mixin approach, `self` **is** the SWIG object, so the rename is directly visible.
## Fix
`src/ifcwrap/IfcParseWrapper.i` — override the global rename specifically for `express::Base::get_inverse`, restoring the original name on entity instances:
```diff
+%rename("get_inverse") express::Base::get_inverse;
%rename("get_inverses_by_declaration") get_inverse;
```
Add this line **before** the global rename (or anywhere before the `%extend express::Base` block). This scoped rename takes precedence for `express::Base`, so:
- `entity_instance.get_inverse(name)` works as the mixin expects
- `file.get_inverses_by_declaration(...)` keeps its intended name
## Python-side workaround
`entity_instance.py:106` — call the method by its SWIG-renamed name:
```diff
- vs = self.get_inverse(name)
+ vs = self.get_inverses_by_declaration(name)
```
# Runtime fix: `entity_instance` class no longer importable from `entity_instance` module
The class rename from `entity_instance` to `entity_instance_mixin` broke external code that does `from ifcopenshell.entity_instance import entity_instance`.
## Error
```
ImportError: cannot import name 'entity_instance' from 'ifcopenshell.entity_instance'
```
Triggered at import time via `ifcopenshell.util.pset` (and likely other modules).
## Fix
`src/ifcopenshell-python/ifcopenshell/entity_instance.py` — add a backwards-compatible alias at the bottom of the module:
```python
entity_instance = entity_instance_mixin
```
+200 -343
View File
File diff suppressed because it is too large Load Diff
@@ -1,17 +0,0 @@
# This file was generated with the assistance of an AI coding tool.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 42e403b..764562f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -249,11 +249,6 @@ set_source_files_properties(
PROPERTIES GENERATED TRUE
)
-# If it's an EMSCRIPTEN build, we're done
-if(EMSCRIPTEN)
- return()
-endif()
-
# CMake exports
configure_file(
cmake/manifoldConfig.cmake.in
@@ -7,7 +7,7 @@ index d765b108..6800c2a7 100644
# Set the library variable
-if(UNIX)
+if(@USE_SHARED@)
+if(0)
set(OPENCOLLADA_LIBRARIES
ftoa_shared
buffer_shared
+112
View File
@@ -34,6 +34,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hdbfa832_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_7.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.4-nompi_h2d575fe_105.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/imath-3.1.12-h7955e40_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.conda
@@ -148,6 +149,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-64/freeimage-3.18.0-h7cd8ba8_22.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.12.1-h60636b9_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/gmp-6.3.0-hf036a51_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.4-nompi_h1607680_105.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/imath-3.1.12-h2016aa1_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/jxrlib-1.1-h10d778d_3.conda
@@ -241,6 +243,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/win-64/freeimage-3.18.0-h8310ca0_22.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.12.1-hdaf720e_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/gmp-6.3.0-hfeafd45_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.4-nompi_hd5d9e70_105.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.1.12-hbb528cf_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/jxrlib-1.1-hcfcfb64_3.conda
@@ -346,6 +349,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hdbfa832_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_7.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.4-nompi_h2d575fe_105.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/imath-3.1.12-h7955e40_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.conda
@@ -460,6 +464,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-64/freeimage-3.18.0-h7cd8ba8_22.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.12.1-h60636b9_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/gmp-6.3.0-hf036a51_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.4-nompi_h1607680_105.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/imath-3.1.12-h2016aa1_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/jxrlib-1.1-h10d778d_3.conda
@@ -553,6 +558,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/win-64/freeimage-3.18.0-h8310ca0_22.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.12.1-hdaf720e_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/gmp-6.3.0-hfeafd45_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.4-nompi_hd5d9e70_105.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.1.12-hbb528cf_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/jxrlib-1.1-hcfcfb64_3.conda
@@ -737,6 +743,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hdbfa832_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_7.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.4-nompi_h2d575fe_105.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/imath-3.1.12-h7955e40_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.conda
@@ -851,6 +858,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-64/freeimage-3.18.0-h7cd8ba8_22.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.12.1-h60636b9_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/gmp-6.3.0-hf036a51_2.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.4-nompi_h1607680_105.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/imath-3.1.12-h2016aa1_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/jxrlib-1.1-h10d778d_3.conda
@@ -944,6 +952,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/win-64/freeimage-3.18.0-h8310ca0_22.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/freetype-2.12.1-hdaf720e_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/gmp-6.3.0-hfeafd45_2.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.4-nompi_hd5d9e70_105.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/imath-3.1.12-hbb528cf_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/jxrlib-1.1-hcfcfb64_3.conda
@@ -1059,6 +1068,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-14.3.0-he663afc_4.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-14.3.0-ha7acb78_11.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda
@@ -1229,6 +1239,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-64/geos-3.13.1-h502464c_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/gmp-6.3.0-hf036a51_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc8237f9_103.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda
@@ -1369,6 +1380,7 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.13.1-h9ea8674_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/gmp-6.3.0-hfeafd45_2.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda
- conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.6-nompi_he30205f_103.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda
@@ -2978,6 +2990,106 @@ packages:
- pkg:pypi/h2?source=compressed-mapping
size: 95967
timestamp: 1756364871835
- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.4-nompi_h2d575fe_105.conda
sha256: 93d2bfc672f3ee0988d277ce463330a467f3686d3f7ee37812a3d8ca11776d77
md5: d76fff0092b6389a12134ddebc0929bd
depends:
- __glibc >=2.17,<3.0.a0
- libaec >=1.1.3,<2.0a0
- libcurl >=8.10.1,<9.0a0
- libgcc >=13
- libgfortran
- libgfortran5 >=13.3.0
- libstdcxx >=13
- libzlib >=1.3.1,<2.0a0
- openssl >=3.4.0,<4.0a0
license: BSD-3-Clause
license_family: BSD
size: 3950601
timestamp: 1733003331788
- conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda
sha256: 4f173af9e2299de7eee1af3d79e851bca28ee71e7426b377e841648b51d48614
md5: c74d83614aec66227ae5199d98852aaf
depends:
- __glibc >=2.17,<3.0.a0
- libaec >=1.1.4,<2.0a0
- libcurl >=8.14.1,<9.0a0
- libgcc >=14
- libgfortran
- libgfortran5 >=14.3.0
- libstdcxx >=14
- libzlib >=1.3.1,<2.0a0
- openssl >=3.5.1,<4.0a0
license: BSD-3-Clause
license_family: BSD
purls: []
size: 3710057
timestamp: 1753357500665
- conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.4-nompi_h1607680_105.conda
sha256: 56500937894b1ca917e1ae1bea64b873a9eec57d581173579189d0b1f590db26
md5: 12ebafc40b10d4bf519e4c2074c52aef
depends:
- __osx >=10.13
- libaec >=1.1.3,<2.0a0
- libcurl >=8.10.1,<9.0a0
- libcxx >=18
- libgfortran 5.*
- libgfortran5 >=13.2.0
- libzlib >=1.3.1,<2.0a0
- openssl >=3.4.0,<4.0a0
license: BSD-3-Clause
license_family: BSD
size: 3732340
timestamp: 1733003702265
- conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc8237f9_103.conda
sha256: e41d22f672b1fbe713d22cf69630abffaee68bdb38a500a708fc70e6f639357f
md5: 3f1df98f96e0c369d94232712c9b87d0
depends:
- __osx >=10.13
- libaec >=1.1.4,<2.0a0
- libcurl >=8.14.1,<9.0a0
- libcxx >=19
- libgfortran
- libgfortran5 >=14.3.0
- libgfortran5 >=15.1.0
- libzlib >=1.3.1,<2.0a0
- openssl >=3.5.1,<4.0a0
license: BSD-3-Clause
license_family: BSD
purls: []
size: 3522832
timestamp: 1753358062940
- conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.4-nompi_hd5d9e70_105.conda
sha256: e8ced65c604a3b9e4803758a25149d71d8096f186fe876817a0d1d97190550c0
md5: 4381be33460283890c34341ecfa42d97
depends:
- libaec >=1.1.3,<2.0a0
- libcurl >=8.10.1,<9.0a0
- libzlib >=1.3.1,<2.0a0
- openssl >=3.4.0,<4.0a0
- ucrt >=10.0.20348.0
- vc >=14.2,<15
- vc14_runtime >=14.29.30139
license: BSD-3-Clause
license_family: BSD
size: 2048450
timestamp: 1733003052575
- conda: https://conda.anaconda.org/conda-forge/win-64/hdf5-1.14.6-nompi_he30205f_103.conda
sha256: 0a90263b97e9860cec6c2540160ff1a1fff2a609b3d96452f8716ae63489dac5
md5: f1f7aaf642cefd2190582550eaca4658
depends:
- libaec >=1.1.4,<2.0a0
- libcurl >=8.14.1,<9.0a0
- libzlib >=1.3.1,<2.0a0
- openssl >=3.5.1,<4.0a0
- ucrt >=10.0.20348.0
- vc >=14.3,<15
- vc14_runtime >=14.44.35208
license: BSD-3-Clause
license_family: BSD
purls: []
size: 2031491
timestamp: 1753357255237
- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba
md5: 0a802cb9888dd14eeefc611f05c40b6e
+1
View File
@@ -31,6 +31,7 @@ occt = { version = "*", build = "*novtk*" }
cgal-cpp = "*"
numpy = "*"
lark = "*"
hdf5 = "*"
eigen = "*"
mpfr = "*"
gmp = "*"
+2 -10
View File
@@ -28,13 +28,5 @@ since it's pure cmake without any additional moving parts.
- clone IfcOpenShell repo next to it to `IfcOpenShell` folder
- run `python nix/build-all.py -wasm -py-313` in `IfcOpenShell`
- it will produce Python package in `IfcOpenShell/ifcopenshell`
- run `python pyodide/build-all-pack-wheel-local.py`, it will
- clean up previous wheels
- run `pyodide build`
- prepare standalone and modular wheels
- produce final wheels in `IfcOpenShell/dist` and `IfcOpenshell/dist-modular`
- testing:
- ensure you're in pyodide environment
- `cd IfcOpenshell/pyodide`
- `./run_pytest.py setup`
- `./run_pytest.py run`
- run `pyodide build`
- it will produce a wheel in `IfcOpenShell/dist`
-38
View File
@@ -1,38 +0,0 @@
#!/usr/bin/env python3
"""Intended to be run after nix/build-all.py has finished the wasm build."""
import shutil
import subprocess
from pathlib import Path
def get_repo_root() -> Path:
output = subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text=True)
return Path(output.strip())
def run(cmd: list[str], **kwargs) -> None:
print("$", " ".join(cmd))
subprocess.check_call(cmd, **kwargs)
def main() -> None:
repo_root = get_repo_root()
shutil.rmtree(repo_root / "dist", ignore_errors=True)
shutil.rmtree(repo_root / "dist_modular", ignore_errors=True)
run(["pyodide", "build"], cwd=repo_root)
shutil.rmtree(repo_root / "ifcopenshell", ignore_errors=True)
(repo_root / "setup.py").unlink(missing_ok=True)
run(["git", "restore", "pyproject.toml"], cwd=repo_root)
wheel = next((repo_root / "dist").glob("ifcopenshell-*.whl"))
run(["uv", "run", "pyodide/order_pyodide_wheel_shared_objects.py", str(wheel)], cwd=repo_root)
run(
["uv", "run", "pyodide/split_pyodide_ifcopenshell_wheel.py", str(wheel), "dist-modular/"],
cwd=repo_root,
)
if __name__ == "__main__":
main()
+8 -17
View File
@@ -1,8 +1,10 @@
#!/usr/bin/bash
set -ex
PYODIDE_VERSION=0.29.4
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
PYODIDE_VERSION=0.29.3
PYODIDE_BUILD_VERSION=0.33.0
PYODIDE_XBUILDENV_ROOT="${HOME}/.cache/.pyodide-xbuildenv-${PYODIDE_BUILD_VERSION}"
PYODIDE_XBUILDENV="${PYODIDE_XBUILDENV_ROOT}/${PYODIDE_VERSION}"
# Script is assuming that it will be possible to execute it multiple times
# therefore we're clearing venv each time and ignoring existing 'emsdk' folder.
@@ -14,36 +16,25 @@ source .venv/bin/activate
# Install pyodide cross build environment.
# Instructions: https://pyodide.org/en/stable/development/building-packages.html
uv pip install -r "${SCRIPT_DIR}/requirements.txt"
uv pip install "pyodide-build==${PYODIDE_BUILD_VERSION}"
# `uv run` is required, so xbuildenv would skip using `pip`.
uv run pyodide xbuildenv install "${PYODIDE_VERSION}"
uv run pyodide xbuildenv install-emscripten
# Cache path includes a hash segment that varies by pyodide-build version,
# so query it instead of constructing it manually.
EMSDK_ROOT=$(uv run pyodide config get emsdk_dir)
[ -f "${EMSDK_ROOT}/emsdk_env.sh" ] && source "${EMSDK_ROOT}/emsdk_env.sh"
[ -f "${EMSDK_ROOT}/../../emsdk_env.sh" ] && source "${EMSDK_ROOT}/../../emsdk_env.sh"
EMSDK_ROOT="${PYODIDE_XBUILDENV}/emsdk"
source "${EMSDK_ROOT}/emsdk_env.sh"
which emcc
emcc --version
mkdir -p packages/ifcopenshell
VERSION=`cat IfcOpenShell/VERSION`
# Normalize to the canonical PEP 440 form (e.g. 0.9.0alpha0 -> 0.9.0a0).
VERSION=`python3 -c "from packaging.version import Version; print(Version('$VERSION'))"`
cp IfcOpenShell/pyodide/meta.yaml packages/ifcopenshell
sed -i s/9.9.9/$VERSION/g packages/ifcopenshell/meta.yaml
sed -i s/0.8.0/$VERSION/g packages/ifcopenshell/meta.yaml
# Use custom build ifcopenshell directory in build-all to make caching simpler
# Otherwise pyodide build path typically includes package version, so cached cmake configs might break.
export BUILD_DIR=`readlink -f ifcopenshell_build`
# Sat, 25 Apr 2026 12:11:39 GMT 2026-04-25 12:11:39,173 - DEBUG - running
# command `make -j5 ifcopenshell_wrapper VERBOSE=1` in directory
# '/home/runner/work/IfcOpenShell/IfcOpenShell/ifcopenshell_build/Linux/wasm/build/ifcopenshell/build'
# Sat, 25 Apr 2026 12:18:01 GMT Error: Process completed with exit code 143.
export IFCOS_NUM_BUILD_PROCS=1
# Use build-recipes-no-deps first, so logs would be printed to stdout.
pyodide build-recipes-no-deps ifcopenshell
pyodide build-recipes ifcopenshell --install
+1 -2
View File
@@ -1,7 +1,6 @@
package:
name: ifcopenshell
# Placeholder, replaced by build_pyodide.sh with the actual version from VERSION file.
version: 9.9.9
version: 0.8.0
source:
# meta.yaml is placed as `packages/ifcopenshell/meta.yaml`.
@@ -1,143 +0,0 @@
#!/usr/bin/env python3
# /// script
# ///
# This file was generated with the assistance of an AI coding tool.
"""Order Pyodide wheel shared objects so wasm side modules load safely.
Pyodide's package loader loads a wheel's bundled ``.so`` files in the order
they appear in the wheel's zip.
If a ``.so`` that depends on symbols from another ``.so`` is loaded first,
loading fails with errors like
- "Failed to load dynamic library"
- "Dynamic linking error: cannot resolve symbol"
This is a known issue upstream - https://github.com/pyodide/pyodide/issues/6020.
"""
from __future__ import annotations
import argparse
import os
import re
import tempfile
import zipfile
from pathlib import Path
SCHEMA_ORDER = {
"ifc2x3": 0,
"ifc4": 1,
"ifc4x1": 2,
"ifc4x2": 3,
"ifc4x3": 4,
"ifc4x3_add1": 5,
"ifc4x3_add2": 6,
}
MAIN_SHARED_OBJECT_RE = re.compile(r"^_ifcopenshell_wrapper(?:\.|$)")
SCHEMA_PLUGIN_RE = re.compile(r"^ifcopenshell_parse_schema_(.+)\.so$")
MAPPING_PLUGIN_RE = re.compile(r"^ifcopenshell_geometry_mapping_(.+)\.so$")
DOCUMENT_PLUGIN_RE = re.compile(r"^ifcopenshell_document_[a-z0-9]+(?:_(.+))?\.so$")
GEOMETRY_SERIALIZATION_PLUGIN_RE = re.compile(r"^ifcopenshell_geometry_writer_(.+)\.so$")
def schema_key(schema: str) -> tuple[int, str]:
schema = schema.lower()
return SCHEMA_ORDER.get(schema, len(SCHEMA_ORDER)), schema
def shared_object_sort_key(filename: str, index: int) -> tuple[int, tuple[int, str], str, int]:
basename = Path(filename).name
if MAIN_SHARED_OBJECT_RE.match(basename):
return 0, schema_key(""), basename, index
if match := SCHEMA_PLUGIN_RE.match(basename):
return 1, schema_key(match.group(1)), basename, index
if match := MAPPING_PLUGIN_RE.match(basename):
return 2, schema_key(match.group(1)), basename, index
if match := DOCUMENT_PLUGIN_RE.match(basename):
return 3, schema_key(match.group(1)), basename, index
if match := GEOMETRY_SERIALIZATION_PLUGIN_RE.match(basename):
return 4, schema_key(match.group(1)), basename, index
return 5, schema_key(""), basename, index
def ordered_infos(infos: list[zipfile.ZipInfo]) -> list[zipfile.ZipInfo]:
shared_infos = [(index, info) for index, info in enumerate(infos) if info.filename.endswith(".so")]
ordered_shared_infos = [
info for index, info in sorted(shared_infos, key=lambda item: shared_object_sort_key(item[1].filename, item[0]))
]
ordered_shared_iter = iter(ordered_shared_infos)
return [next(ordered_shared_iter) if info.filename.endswith(".so") else info for info in infos]
def zip_info_for_write(source: zipfile.ZipInfo) -> zipfile.ZipInfo:
info = zipfile.ZipInfo(source.filename)
info.date_time = source.date_time
info.compress_type = source.compress_type
info.comment = source.comment
info.create_system = source.create_system
info.external_attr = source.external_attr
info.extra = source.extra
return info
def shared_object_names(infos: list[zipfile.ZipInfo]) -> list[str]:
return [info.filename for info in infos if info.filename.endswith(".so")]
def rewrite_wheel(wheel: Path, ordered: list[zipfile.ZipInfo]) -> None:
fd, temp_name = tempfile.mkstemp(prefix=f".{wheel.name}.", suffix=".tmp", dir=wheel.parent)
os.close(fd)
temp_path = Path(temp_name)
try:
with zipfile.ZipFile(wheel) as zin, zipfile.ZipFile(
temp_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9
) as zout:
for info in ordered:
zout.writestr(zip_info_for_write(info), zin.read(info))
os.replace(temp_path, wheel)
finally:
if temp_path.exists():
temp_path.unlink()
def order_wheel(wheel: Path, check: bool) -> bool:
wheel = wheel.resolve()
if wheel.suffix != ".whl":
raise ValueError(f"not a wheel: {wheel}")
with zipfile.ZipFile(wheel) as zf:
infos = zf.infolist()
ordered = ordered_infos(infos)
changed = shared_object_names(infos) != shared_object_names(ordered)
if check:
if changed:
print(f"{wheel}: shared object order needs updating")
return False
print(f"{wheel}: shared object order is already valid")
return True
if changed:
rewrite_wheel(wheel, ordered)
print(f"{wheel}: reordered shared objects")
else:
print(f"{wheel}: shared object order is already valid")
return True
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("wheel", type=Path, help="Wheel to rewrite in place")
parser.add_argument("--check", action="store_true", help="Only validate the current shared object order")
args = parser.parse_args()
return 0 if order_wheel(args.wheel, args.check) else 1
if __name__ == "__main__":
raise SystemExit(main())
-1
View File
@@ -1 +0,0 @@
pyodide-build==0.39.0
-62
View File
@@ -1,62 +0,0 @@
#!/usr/bin/env python3
import argparse
import shlex
import shutil
import subprocess
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
DIST_DIRS = (
SCRIPT_DIR / "test/pyodide",
SCRIPT_DIR / "test/pyodide-modular",
)
WHEEL_SRCS = (
SCRIPT_DIR / "../dist",
SCRIPT_DIR / "../dist-modular",
)
def run(cmd: list, **kwargs) -> None:
print("$", shlex.join(str(part) for part in cmd))
subprocess.check_call(cmd, **kwargs)
def setup() -> None:
run(["uv", "pip", "install", "pytest-pyodide"])
# Copy pyodide installation so we can modify it locally just for tests.
pyodide_root = subprocess.check_output(["pyodide", "config", "get", "pyodide_root"], text=True).strip()
pyodide_root_dist = Path(pyodide_root) / "dist"
for dist_dir in DIST_DIRS:
if dist_dir.exists():
shutil.rmtree(dist_dir)
shutil.copytree(pyodide_root_dist, dist_dir)
def run_tests() -> None:
for dist_dir, wheel_src in zip(DIST_DIRS, WHEEL_SRCS):
if not wheel_src.exists():
raise RuntimeError(f"error: {wheel_src} does not exist")
# Clean up previous wheels.
for whl in dist_dir.glob("ifcopenshell*.whl"):
whl.unlink()
# Symlink new ones.
for whl in wheel_src.glob("ifcopenshell*.whl"):
(dist_dir / whl.name).symlink_to(whl.resolve())
for dist_dir in DIST_DIRS:
run(["pytest", f"--dist-dir={dist_dir}", "--capture=no"], cwd=SCRIPT_DIR)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("command", choices=["setup", "run"])
args = parser.parse_args()
if args.command == "setup":
setup()
else:
run_tests()
-311
View File
@@ -1,311 +0,0 @@
#!/usr/bin/env python3
# /// script
# ///
"""Split optional IfcOpenShell Pyodide payloads into separate wheels.
The main wheel bundles per-schema plugin ``.so`` files and pure Python
subpackages that most browser sessions probably don't need.
This splits each of those out into its own installable wheel,
so a Pyodide app can fetch just the base wheel plus whichever schema/plugin wheels it actually needs.
Resulting wheels (roughly):
- ifcopenshell.whl (main ifcopenshell.py files + _ifcopenshell_wrapper)
- ifcopenshell_pure_python.whl (api, express, python files only)
- splitted wheels with a single .so binary - e.g. `ifcopenshell_parse_schema_ifc4.whl`
"""
from __future__ import annotations
import argparse
import base64
import csv
import hashlib
import io
import os
import re
import sys
import time
import zipfile
from email.parser import Parser
from pathlib import Path
MAIN_SHARED_OBJECT_RE = re.compile(r"(^|/)_ifcopenshell_wrapper(?:\.|$)")
PURE_PYTHON_PACKAGE_NAME = "ifcopenshell-pure-python"
PURE_PYTHON_PREFIXES = (
"ifcopenshell/api/",
"ifcopenshell/express/",
"ifcopenshell/mvd/",
"ifcopenshell/simple_spf/",
)
def wheel_parts(path: Path) -> tuple[str, str, str, str, str]:
if path.suffix != ".whl":
raise ValueError(f"not a wheel: {path}")
stem = path.name[:-4]
left, py_tag, abi_tag, platform_tag = stem.rsplit("-", 3)
dist, version = left.rsplit("-", 1)
return dist, version, py_tag, abi_tag, platform_tag
def safe_name(name: str) -> str:
return re.sub(r"[-_.]+", "-", name).lower().strip("-")
def wheel_escape(value: str) -> str:
return re.sub(r"[^\w\d.]+", "_", value, flags=re.UNICODE)
def wheel_version_escape(value: str) -> str:
return re.sub(r"[^\w\d.+]+", "_", value, flags=re.UNICODE)
def dist_info_dir(name: str, version: str) -> str:
return f"{wheel_escape(name)}-{wheel_version_escape(version)}.dist-info"
def sha256_record_value(data: bytes) -> str:
digest = hashlib.sha256(data).digest()
return "sha256=" + base64.urlsafe_b64encode(digest).decode("ascii").rstrip("=")
def make_info(name: str, *, source: zipfile.ZipInfo | None = None, mode: int | None = None) -> zipfile.ZipInfo:
info = zipfile.ZipInfo(name)
if source is not None:
info.date_time = source.date_time
info.external_attr = source.external_attr
info.comment = source.comment
info.create_system = source.create_system
else:
info.date_time = time.localtime(time.time())[:6]
info.external_attr = ((mode if mode is not None else 0o644) & 0xFFFF) << 16
info.create_system = 3
info.compress_type = zipfile.ZIP_DEFLATED
return info
def write_record(zf: zipfile.ZipFile, entries: dict[str, bytes | None], record_name: str) -> None:
rows: list[list[str]] = []
for name in sorted(entries):
data = entries[name]
if name == record_name:
rows.append([name, "", ""])
elif data is None:
raise ValueError(f"missing bytes for RECORD entry {name}")
else:
rows.append([name, sha256_record_value(data), str(len(data))])
buf = io.StringIO(newline="")
writer = csv.writer(buf, lineterminator="\n")
writer.writerows(rows)
zf.writestr(make_info(record_name), buf.getvalue().encode("utf-8"))
def read_original_metadata(zf: zipfile.ZipFile) -> tuple[str, str, str]:
metadata_names = [n for n in zf.namelist() if n.endswith(".dist-info/METADATA")]
wheel_names = [n for n in zf.namelist() if n.endswith(".dist-info/WHEEL")]
record_names = [n for n in zf.namelist() if n.endswith(".dist-info/RECORD")]
if len(metadata_names) != 1 or len(wheel_names) != 1 or len(record_names) != 1:
raise ValueError("expected exactly one METADATA, WHEEL, and RECORD in the source wheel")
return metadata_names[0], wheel_names[0], record_names[0]
def shared_package_name(so_path: str) -> str:
stem = Path(so_path).name.removesuffix(".so")
stem = re.sub(r"[^A-Za-z0-9]+", "-", stem).strip("-")
return safe_name(stem)
def is_pure_python_split_path(path: str) -> bool:
return any(path.startswith(prefix) for prefix in PURE_PYTHON_PREFIXES)
def build_wheel(
output_dir: Path,
package_name: str,
version: str,
tag: str,
root_is_purelib: bool,
summary: str,
payloads: list[tuple[zipfile.ZipInfo, bytes]],
license_files: dict[str, bytes],
) -> Path:
di = dist_info_dir(package_name, version)
wheel_name = f"{wheel_escape(package_name)}-{wheel_version_escape(version)}-{tag}.whl"
out = output_dir / wheel_name
record_name = f"{di}/RECORD"
entries: dict[str, bytes | None] = {}
metadata = (
"Metadata-Version: 2.4\n"
f"Name: {package_name}\n"
f"Version: {version}\n"
f"Summary: {summary}\n"
"License-File: COPYING\n"
"License-File: COPYING.LESSER\n"
"\n"
).encode()
wheel = (
"Wheel-Version: 1.0\n"
"Generator: split_pyodide_ifcopenshell_wheel.py\n"
f"Root-Is-Purelib: {str(root_is_purelib).lower()}\n"
f"Tag: {tag}\n"
"\n"
).encode()
with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
for info, data in payloads:
zf.writestr(make_info(info.filename, source=info), data)
entries[info.filename] = data
metadata_name = f"{di}/METADATA"
wheel_meta_name = f"{di}/WHEEL"
zf.writestr(make_info(metadata_name), metadata)
zf.writestr(make_info(wheel_meta_name), wheel)
entries[metadata_name] = metadata
entries[wheel_meta_name] = wheel
for basename, data in license_files.items():
name = f"{di}/licenses/{basename}"
zf.writestr(make_info(name), data)
entries[name] = data
entries[record_name] = None
write_record(zf, entries, record_name)
print(f"Splitting wheel to '{out}'.")
return out
def rewrite_main_wheel(source: Path, target: Path, split_paths: set[str]) -> None:
with zipfile.ZipFile(source) as zin, zipfile.ZipFile(
target, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9
) as zout:
_, _, record_name = read_original_metadata(zin)
entries: dict[str, bytes | None] = {}
for info in zin.infolist():
if info.filename in split_paths or info.filename == record_name:
continue
data = zin.read(info.filename)
zout.writestr(make_info(info.filename, source=info), data)
entries[info.filename] = data
entries[record_name] = None
write_record(zout, entries, record_name)
def verify_wheel(path: Path) -> None:
with zipfile.ZipFile(path) as zf:
zf.testzip()
metadata_name, wheel_name, record_name = read_original_metadata(zf)
Parser().parsestr(zf.read(metadata_name).decode("utf-8"))
wheel_text = zf.read(wheel_name).decode("utf-8")
if "Wheel-Version:" not in wheel_text or "Tag:" not in wheel_text:
raise ValueError(f"invalid WHEEL metadata in {path}")
record_rows = list(csv.reader(io.StringIO(zf.read(record_name).decode("utf-8"))))
names = {row[0] for row in record_rows}
missing = set(zf.namelist()) - names
if missing:
raise ValueError(f"{path} RECORD is missing entries: {sorted(missing)[:5]}")
for name, digest, size in record_rows:
if name == record_name:
continue
data = zf.read(name)
if digest != sha256_record_value(data) or size != str(len(data)):
raise ValueError(f"{path} RECORD mismatch for {name}")
def split_wheel(wheel_path: Path, output_dir: Path) -> None:
wheel_path = wheel_path.expanduser().resolve()
if not wheel_path.exists():
raise FileNotFoundError(wheel_path)
output_dir = output_dir.expanduser().resolve()
output_dir.mkdir(parents=True, exist_ok=True)
main_wheel_path = output_dir / wheel_path.name
if main_wheel_path.resolve(strict=False) == wheel_path:
raise ValueError("output directory must not point to the input wheel location")
_, version, py_tag, abi_tag, platform_tag = wheel_parts(wheel_path)
binary_tag = f"{py_tag}-{abi_tag}-{platform_tag}"
pure_tag = "py3-none-any"
with zipfile.ZipFile(wheel_path) as zf:
file_infos = [info for info in zf.infolist() if not info.is_dir()]
so_infos = [info for info in file_infos if info.filename.endswith(".so")]
split_so_infos = [info for info in so_infos if not MAIN_SHARED_OBJECT_RE.search(Path(info.filename).name)]
pure_python_infos = [info for info in file_infos if is_pure_python_split_path(info.filename)]
if not split_so_infos and not pure_python_infos:
raise RuntimeError("no secondary .so files or pure Python subpackages found to split")
license_files = {
Path(info.filename).name: zf.read(info.filename)
for info in file_infos
if ".dist-info/licenses/" in info.filename
}
split_so_payloads = [(info, zf.read(info.filename)) for info in split_so_infos]
pure_python_payloads = [(info, zf.read(info.filename)) for info in pure_python_infos]
created_wheels: list[Path] = []
for info, data in split_so_payloads:
package_name = shared_package_name(info.filename)
created_wheels.append(
build_wheel(
output_dir,
package_name,
version,
binary_tag,
False,
f"Pyodide shared library split from IfcOpenShell ({Path(info.filename).name}).",
[(info, data)],
license_files,
)
)
if pure_python_payloads:
created_wheels.append(
build_wheel(
output_dir,
PURE_PYTHON_PACKAGE_NAME,
version,
pure_tag,
True,
"Pure Python subpackages split from IfcOpenShell.",
pure_python_payloads,
license_files,
)
)
temp_main_wheel = output_dir / f".{wheel_path.name}.tmp"
try:
rewrite_main_wheel(
wheel_path,
temp_main_wheel,
{info.filename for info, _ in split_so_payloads + pure_python_payloads},
)
verify_wheel(temp_main_wheel)
for created in created_wheels:
verify_wheel(created)
os.replace(temp_main_wheel, main_wheel_path)
finally:
if temp_main_wheel.exists():
temp_main_wheel.unlink()
def parse_args(argv: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Extract optional IfcOpenShell Pyodide payloads into separate wheel artifacts."
)
parser.add_argument("wheel", help="IfcOpenShell Pyodide wheel to split")
parser.add_argument("output_dir", help="Directory for generated wheels")
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
args = parse_args(sys.argv[1:] if argv is None else argv)
split_wheel(Path(args.wheel), Path(args.output_dir))
return 0
if __name__ == "__main__":
raise SystemExit(main())
-2
View File
@@ -1,2 +0,0 @@
pyodide
pyodide-modular
View File
+11 -24
View File
@@ -1,33 +1,19 @@
import zipfile
from pathlib import Path
from ..order_pyodide_wheel_shared_objects import shared_object_sort_key
WHEEL_FILENAME = next(
p.name for p in (Path.cwd() / "pyodide").iterdir() if p.name.startswith("ifcopenshell-") and p.suffix == ".whl"
)
def _first_so_name(wheel_path: Path) -> str:
with zipfile.ZipFile(wheel_path) as zf:
for name in zf.namelist():
if name.endswith(".so"):
return Path(name).name
return wheel_path.name
def test_ifcopenshell_import(selenium, request):
dist_dir = Path(request.config.getoption("--dist-dir"))
wheel_paths = list(dist_dir.glob("ifcopenshell*.whl"))
wheel_paths.sort(key=lambda path: shared_object_sort_key(_first_so_name(path), 0))
WHEEL_NAMES = tuple(path.name for path in wheel_paths)
def test_ifcopenshell_import(selenium):
selenium.load_package("micropip")
selenium.run_async(f"""
# Important to test it with `micropip.install`
# without any dependencies loaded to ensure micropip will load them automatically.
selenium.run_async(
f"""
import micropip
wheel_filenames = {WHEEL_NAMES!r}
for wheel_filename in wheel_filenames:
print(f"Loading {{wheel_filename}}...")
await micropip.install(f"./{{wheel_filename}}")
await micropip.install(f"./{WHEEL_FILENAME}")
import ifcopenshell
from pathlib import Path
ifcopenshell.set_plugin_search_paths([str(Path(ifcopenshell.__file__).parent)])
ifc_file = ifcopenshell.file()
wall = ifc_file.create_entity("IfcWall")
wall1 = ifc_file.by_type("IfcWall")[0]
@@ -36,4 +22,5 @@ def test_ifcopenshell_import(selenium, request):
wall.Name = "Test"
assert wall.Name == "Test", f"Entity name wasn't changed: {{wall}}"
print(wall)
""")
"""
)
+16 -23
View File
@@ -9,7 +9,6 @@ line-length = 120
include = '''
src/.*.pyi?$
|nix/.*.pyi?$
|pyodide/.*.pyi?$
'''
extend-exclude = '''
src/ifcopenshell-python/ifcopenshell/express/rules/*
@@ -63,36 +62,28 @@ select = [
#
"FA", # future annotations
"UP", # pyupgrade
"unnecessary-iterable-allocation-for-first-element",
"unsorted-dunder-all",
"RUF015", # next() > list_comprehension[0]
"RUF022", # sort __all__
"I", # import sorting
"unused-noqa",
"rule-codes-in-selectors",
"noqa-comments",
"rule-codes-in-suppression-comments",
# General util rules.
"invalid-rule-code",
"redirected-noqa",
"invalid-pyproject-toml",
"invalid-suppression-comment",
]
ignore = [
# Conflicts with Blender using annotations for props definitions.
"future-rewritable-type-annotation",
"FA100", # Conflicts with Blender using annotations for props definitions.
# Maybe will enable later:
"non-pep604-annotation-union", # Union[X,Y] to X | Y
"non-pep604-annotation-optional", # Optional to X | None
"redundant-open-modes", # Unnecessary mode argument
"yield-in-for-loop", # yield for -> yield from
"format-literals", # implicit references for positional format fields
"printf-string-formatting", # Replace % with .format
"f-string", # Replace .format with f-string
"UP007", # Optional to X | Y
"UP045", # Optional to X | None
"UP015", # Unnecessary mode argument
"UP028", # yield for -> yield from
"UP030", # implicit references for positional format fields
"UP031", # Replace % with .format
"UP032", # Replace .format with f-string
]
[tool.ty.rules]
all = "error"
# Structural rules (no deep type inference needed, easier to adapt).
# Has false positives due to ty walrus operator bug.
possibly-unresolved-reference = "ignore"
# Maybe later, requires to specify element types for all generics.
missing-type-argument = "ignore"
# Conflicts with `bpy` props defined using annotations.
@@ -120,6 +111,7 @@ no-matching-overload = "ignore"
not-subscriptable = "ignore"
unsupported-dynamic-base = "ignore"
unsupported-operator = "ignore"
type-assertion-failure = "ignore"
[tool.ty.environment]
extra-paths = [
@@ -167,8 +159,7 @@ exclude = [
[tool.poe.tasks]
dev-setup.sequence = [
# 3.13 is chosen because it's the version used in the latest Bonsai.
{cmd = "uv sync --python 3.13"},
{cmd = "uv sync"},
{cmd = "uv pip install -e ./src/bsdd/"},
{cmd = "uv pip install -e ./src/ifcopenshell-python/[advanced,dev]"},
{cmd = "uv pip install -e ./src/ifcedit/"},
@@ -204,6 +195,7 @@ format.sequence = ["black", "ruff"]
cmake-format = "gersemi . --in-place"
[tool.poe.tasks.ty-ios]
# --ignore unresolved-reference: walrus operator false positives in ty.
cmd = """
ty check
nix/
@@ -221,6 +213,7 @@ cmd = """
src/ifcpatch
src/ifctester
--python=src/ifcopenshell-python/.venv
--ignore unresolved-reference
"""
[tool.poe.tasks.bonsai-deps]
+3 -3
View File
@@ -1,5 +1,5 @@
black==26.3.1
ruff==0.16.0
ruff==0.15.12
poethepoet
ty==0.0.63
gersemi==0.28.0
ty==0.0.59
gersemi==0.26.1
+1 -1
View File
@@ -35,7 +35,7 @@ def camera_vectors_from_target_position(
"""
camera_offset = np.array((5, 5, 5)) if offset is None else offset
camera_position = target_position + camera_offset
camera_direction = unit_vector(-camera_offset)
camera_direction = unit_vector(-camera_offset) # pylint: disable=invalid-unary-operand-type
camera_right = unit_vector(np.cross(np.array([0.0, 0.0, 1.0]), camera_direction))
camera_up = unit_vector(np.cross(camera_direction, camera_right))
return camera_position.tolist(), camera_direction.tolist(), camera_up.tolist()
+1 -1
View File
@@ -316,7 +316,7 @@ def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
Returns:
The BCF viewpoint definition.
"""
ifc_file = element.file
ifc_file = element.wrapped_data.file
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
elem_placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
elem_placement[:3, 3] *= unit_scale
+2 -8
View File
@@ -96,10 +96,10 @@ class FoundationClient:
webbrowser.open(f"{auth_endpoint}?{query}")
server.timeout = 100
server.handle_request()
if server.auth_code and server.auth_state == state:
if server.auth_code and server.auth_state == state: # pylint: disable=E1101
data = {
"grant_type": "authorization_code",
"code": server.auth_code,
"code": server.auth_code, # pylint: disable=E1101 type:ignore
"redirect_uri": f"http://localhost:{server.server_address[1]}/{self.redirect_subdir}",
}
headers = self._get_access_token_headers()
@@ -188,8 +188,6 @@ class BcfClient:
response.raise_for_status()
return response.status_code, response.text
except requests.exceptions.HTTPError as errh:
response = errh.response
assert response is not None
print(f"message: {response.reason}' '{response.status_code}, {errh}")
return response.status_code, response.reason
@@ -208,8 +206,6 @@ class BcfClient:
response.raise_for_status()
return response.status_code, response.text
except requests.exceptions.HTTPError as errh:
response = errh.response
assert response is not None
print(f"message: {response.reason}' '{response.status_code}, {errh}")
return response.status_code, response.reason
@@ -226,8 +222,6 @@ class BcfClient:
response.raise_for_status()
return response.status_code, response.text
except requests.exceptions.HTTPError as errh:
response = errh.response
assert response is not None
print(f"message: {response.reason}' '{response.status_code}, {errh}")
return response.status_code, response.reason
+1 -1
View File
@@ -316,7 +316,7 @@ def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
Returns:
The BCF viewpoint definition.
"""
ifc_file = element.file
ifc_file = element.wrapped_data.file
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
elem_placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
elem_placement[:3, 3] *= unit_scale
+82 -4
View File
@@ -10,7 +10,7 @@ name = "bcf-client"
# author = "IfcOpenShell"
description = "BCF-XML file handler."
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.8"
keywords = ["IFC", "BCF", "BIM"]
dependencies = [
"xsdata>=24.4",
@@ -31,6 +31,14 @@ classifiers = [
Source = "https://github.com/IfcOpenShell/IfcOpenShell"
Issues = "https://github.com/IfcOpenShell/IfcOpenShell/issues"
[tool.black]
line-length = 120
extend-exclude = "model"
[tool.isort]
profile = "black"
extend_skip_glob = ["src/bcf/*/model/*"]
[tool.coverage.paths]
source = ["src"]
@@ -52,7 +60,7 @@ exclude_lines = [
[tool.tox]
legacy_tox_ini = """
[tox]
env_list = py3{10,11}
env_list = lint, type, py3{10,11}
skip_missing_interpreters = true
[testenv]
@@ -61,10 +69,80 @@ deps =
pytest-cov
coverage
commands = pytest --cov --cov-report=term tests
[testenv:lint]
description = run linters
skip_install = true
deps =
black
isort
pylint
commands =
black {posargs:.}
isort {posargs:.}
pylint {posargs:.} --output-format=colorized
[testenv:type]
description = run type checks
deps =
mypy>=0.991
commands =
- mypy {posargs:src}
"""
[tool.mypy]
check_untyped_defs = true
disallow_any_generics = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_defs = true
no_implicit_optional = true
#no_implicit_reexport = true
show_column_numbers = true
show_error_codes = true
show_error_context = true
strict_equality = true
strict_optional = true
warn_redundant_casts = true
#warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
warn_unused_ignores = true
exclude= "src/bcf/v(2|3)/model"
plugins = "numpy.typing.mypy_plugin"
[[tool.mypy.overrides]]
module = "tests"
disallow_untyped_decorators = false
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = [
"pytest",
"pytest_mock",
"ifcopenshell",
"ifcopenshell.*",
]
ignore_missing_imports = true
[tool.pylint.main]
ignore = ["model"]
ignored-modules = ["bcf.v2.model", "bcf.v3.model", "xsdata"]
jobs = 0
disable="all"
enable="E" # B,B9,BLK,C,D,E,F,I,N,S,W
[tool.pylint.design]
max-args = 10
max-attributes = 10
[tool.pylint.format]
expected-line-ending-format = "LF"
max-line-length = 120
[tool.ruff]
extend = "../../pyproject.toml"
lint.extend-select = [
"unused-import", # unused imports
lint.select = [
"F401", # unused imports
]
+6
View File
@@ -0,0 +1,6 @@
black
mypy
pylint
isort
xsdata
tox==3.27.1
+16 -13
View File
@@ -42,12 +42,10 @@ endif
IS_STABLE:=FALSE
VERSION:=$(shell cat ../../VERSION)
VERSION_BASE:=$(shell sed -E 's/[[:alpha:]]+[0-9]+$$//' ../../VERSION)
VERSION_PYTHON:=$(shell sed 's/alpha/a/' ../../VERSION)
VERSION_SEMVER:=$(shell sed -E 's/([[:alpha:]]+)([0-9]+)$$/-\\1\\2/' ../../VERSION)
VERSION_MAJOR:=$(shell cat '../../VERSION' | cut -d '.' -f 1)
VERSION_MINOR:=$(shell cat '../../VERSION' | cut -d '.' -f 2)
VERSION_PATCH:=$(shell cat '../../VERSION' | cut -d '.' -f 3)
VERSION_DATE:=$(shell date '+%y%m%d')
VERSION_DAILY:=$(VERSION_BASE)a$(VERSION_DATE)
VERSION_SEMVER_DAILY:=$(VERSION_BASE)-alpha$(VERSION_DATE)
LAST_COMMIT_HASH:=$(shell git rev-parse HEAD)
LAST_COMMIT_DATE:=$(shell git show -s --format=%cI)
LAST_GIT_BRANCH:=$(shell git rev-parse --abbrev-ref HEAD)
@@ -262,14 +260,14 @@ endif
cp pyproject.toml build/
ifeq ($(IS_STABLE), TRUE)
$(SED) "s/0.0.0/$(VERSION_SEMVER)/" build/bonsai/blender_manifest.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION_PYTHON)"/' build/pyproject.toml
$(SED) "s/0.0.0/$(VERSION)/" build/bonsai/blender_manifest.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
else
$(SED) "s/0.0.0/$(VERSION_SEMVER_DAILY)/" build/bonsai/blender_manifest.toml
$(SED) "s/0.0.0/$(VERSION)-alpha$(VERSION_DATE)/" build/bonsai/blender_manifest.toml
$(SED) "s/8888888/$(LAST_COMMIT_HASH)/" build/bonsai/__init__.py
$(SED) "s/9999999/$(LAST_COMMIT_DATE)/" build/bonsai/__init__.py
$(SED) "s/7777777/$(LAST_GIT_BRANCH)/" build/bonsai/__init__.py
$(SED) 's/version = "0.0.0"/version = "$(VERSION_DAILY)"/' build/pyproject.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/pyproject.toml
endif
# Blender 5.1+ requires Python 3.13.
@@ -281,9 +279,9 @@ endif
# Provides bonsai Add-on functionality
ifeq ($(IS_STABLE), TRUE)
$(SED) 's/version = "0.0.0"/version = "$(VERSION_PYTHON)"/' build/pyproject.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
else
$(SED) 's/version = "0.0.0"/version = "$(VERSION_DAILY)"/' build/pyproject.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)a$(VERSION_DATE)"/' build/pyproject.toml
endif
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
cp build/dist/*.whl build/wheels/
@@ -317,9 +315,9 @@ endif
rm -rf build/bonsai/libs/
ifeq ($(IS_STABLE), TRUE)
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION_SEMVER)-$(BLENDER_PLATFORM).zip ./bonsai
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION)-$(BLENDER_PLATFORM).zip ./bonsai
else
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION_SEMVER_DAILY)-$(BLENDER_PLATFORM).zip ./bonsai
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION)-alpha$(VERSION_DATE)-$(BLENDER_PLATFORM).zip ./bonsai
endif
mv build/bonsai*.zip dist/
@@ -372,6 +370,11 @@ test-modal:
test-reregister:
blender --python scripts/reregister_bonsai.py
.PHONY: qa
qa:
black .
pylint ./* --output-format=colorized --disable all --enable E --disable import-error
.PHONY: coverage
coverage:
coverage run --source bonsai.core -m pytest -p no:pytest-blender test/core
+4 -22
View File
@@ -24,28 +24,10 @@ a text, a tspan { fill: blue !important; text-decoration: underline;}
a:hover { cursor: pointer; }
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; fill-rule: evenodd; }
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
/* SVG edge classification (issue #3668): see edge-classification.md. These select directly on
the <path> element (each classified projection edge carries its own class), so they win over
the inherited .projection rule above regardless of specificity. */
path.outline { stroke: black; stroke-width: 0.35; stroke-opacity: 1; }
path.boundary { stroke: black; stroke-width: 0.3; stroke-opacity: 0.9; }
path.crease { stroke: black; stroke-width: 0.25; stroke-opacity: 0.85; }
path.sharp { stroke: black; stroke-width: 0.18; stroke-opacity: 0.7; }
path.flush { stroke: black; stroke-width: 0.1; stroke-opacity: 0.4; }
/* Debug CSS for troubleshooting edge classification */
/*
path.outline { stroke: black; stroke-width: 0.35; stroke-opacity: 1; }
path.boundary { stroke: orange; stroke-width: 0.3; stroke-opacity: 0.9; }
path.crease { stroke: green; stroke-width: 0.25; stroke-opacity: 0.85; }
path.sharp { stroke: red; stroke-width: 0.18; stroke-opacity: 0.7; }
path.flush { stroke: blue; stroke-width: 0.1; stroke-opacity: 0.4; }
*/
.surface {fill: white; stroke-width: 0.1;}
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.3; }
.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.3; }
/* .IfcGeographicElement { fill: none; stroke: rgb(150, 150, 150); stroke-linecap: 'round'; stroke-dasharray: 1, 2;} */
.surface { stroke: none; fill: #fff; fill-rule: evenodd; }
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.IfcGeographicElement { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 1; }
.PredefinedType-LINEWORK { stroke: black; stroke-width: 0.25; }
.PredefinedType-LINEWORK.dashed { stroke-dasharray: 3, 2; }
.PredefinedType-LINEWORK.fine { stroke-width: 0.18; stroke: #777777; }
+1
View File
@@ -0,0 +1 @@
This cache folder contains .h5 files. These files cache IFC geometry for performance only. You may safely clear the contents of this cache folder without losing data.
@@ -5,7 +5,7 @@ FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',$,$,'EPset_Drawing','EPset_D
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation/DRAWING',(#23,#22,#27,#24,#29,#30,#19,#12,#26,#9,#8,#7,#6,#4,#18,#11,#5,#20,#25,#14,#10,#17,#28,#16,#3,#21,#13,#15,#2,#31,#32,#33,#34,#35,#36,#37));
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation/DRAWING',(#23,#22,#27,#24,#29,#30,#19,#12,#26,#9,#8,#7,#6,#4,#18,#11,#5,#20,#25,#14,#10,#17,#28,#16,#3,#21,#13,#15,#2));
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','The scale of this drawing represented as a numerator and denominator, such as 1/100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','The scale of this drawing in human readable format, such as 1:100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
@@ -35,12 +35,5 @@ DATA;
#28=IFCSIMPLEPROPERTYTEMPLATE('1YSnFzurrEyRNtoLdmmddP',$,'BringToFront','The objects with these SVG classes will render in front of all other objects.Ex: IfcBeam, IfcColumn',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#29=IFCSIMPLEPROPERTYTEMPLATE('0lP6Y8q9v2QhDnR4sT7uVx',$,'PerspectiveShiftX','Horizontal perspective camera shift stored as drawing metadata using Blender camera shift units.',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
#30=IFCSIMPLEPROPERTYTEMPLATE('2mR8b1NcW5EoFyG7hJ9kLp',$,'PerspectiveShiftY','Vertical perspective camera shift stored as drawing metadata using Blender camera shift units.',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
#31=IFCSIMPLEPROPERTYTEMPLATE('1cFVJnqT13m8ItkMHaI1tp',$,'UseEdgeClassification','Enable the boundary/outline/sharp/crease/flush SVG edge classification scheme (issue #3668). When false, drawings use the original unclassified linework.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#32=IFCSIMPLEPROPERTYTEMPLATE('2kB$mxBgnBUvhjh0Ti0c4P',$,'RenderCreases','Whether to render ''crease'' (concave) edges. Only relevant when UseEdgeClassification is enabled.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#33=IFCSIMPLEPROPERTYTEMPLATE('3MSIJNW$T8r9Hl12kk0BY$',$,'ValleyAngleMinDegrees','Minimum concave dihedral deviation from flat, in degrees, for a projection edge to be classified as ''crease'' rather than ''flush''.',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
#34=IFCSIMPLEPROPERTYTEMPLATE('2epSGfC4bFM9gb1X7zBIp4',$,'RenderSharp','Whether to render ''sharp'' (convex) edges. Only relevant when UseEdgeClassification is enabled.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#35=IFCSIMPLEPROPERTYTEMPLATE('3TZwsEjkr5WRDKcgrYzSIA',$,'RidgeAngleMinDegrees','Minimum convex dihedral deviation from flat, in degrees, for a projection edge to be classified as ''sharp'' rather than ''flush''.',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
#36=IFCSIMPLEPROPERTYTEMPLATE('2Jua$lO754vgZOkBoHM2gA',$,'RenderFlush','Whether to render ''flush'' edges (dihedral deviation below both ridge/valley thresholds). Only relevant when UseEdgeClassification is enabled.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#37=IFCSIMPLEPROPERTYTEMPLATE('1zM9sia2L8RQDnWZxgUwlZ',$,'JoinClasses','Comma separated list of IFC classes whose cut linework will be joined together when they meet (e.g. mitred at a corner).\X2\000A\X0\Defaults to ''IfcWall,IfcSlab'' if not set. Override to also join other classes, such as ''IfcWall,IfcSlab,IfcCovering''.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
+6 -52
View File
@@ -10,7 +10,6 @@ if bonsai_lib_path:
import argparse
import base64
import json
import urllib.parse
import xml.etree.ElementTree as ET
import pystache
@@ -19,53 +18,8 @@ from aiohttp import web
sio_port = 8080 # default port
def get_asset_version() -> str:
"""A cache-busting token appended to locally served static asset URLs.
Browsers otherwise keep serving a stale cached copy of static/js and
static/css after Bonsai ships a code change, until the user does a hard
refresh. Using the Bonsai version (which includes the build's commit
hash) means the token changes on every shipped update.
"""
if bonsai_version:
return urllib.parse.quote(bonsai_version, safe="")
# Fallback for standalone runs without BONSAI_VERSION set (e.g. running
# sioserver.py directly outside of Blender): derive a token from the
# newest mtime among the static assets, so it still changes whenever the
# shipped files change.
static_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
latest_mtime = 0
for root, _dirs, files in os.walk(static_dir):
for name in files:
latest_mtime = max(latest_mtime, int(os.path.getmtime(os.path.join(root, name))))
return f"dev-{latest_mtime}"
asset_version = get_asset_version()
@web.middleware
async def no_cache_static_middleware(request: web.Request, handler):
"""Force revalidation of locally served static assets.
Query-string version stamping (see `asset_version`) busts the cache for
the HTML-referenced entry points, but JS files that statically import
other local modules (e.g. cost.js/gantt.js importing utilities/costui.js)
reference those modules by an un-stamped relative path. Marking all
/static/ and /jsgantt/ responses as no-cache makes browsers always
revalidate with the server (a cheap conditional GET / 304 when nothing
changed), so nested imports also pick up shipped changes without
requiring a hard refresh.
"""
response = await handler(request)
if request.path.startswith("/static/") or request.path.startswith("/jsgantt/"):
response.headers["Cache-Control"] = "no-cache, must-revalidate"
return response
sio = socketio.AsyncServer(cors_allowed_origins="*", async_mode="aiohttp", max_http_buffer_size=10000000)
app = web.Application(middlewares=[no_cache_static_middleware])
app = web.Application()
sio.attach(app)
@@ -245,28 +199,28 @@ class BlenderNamespace(socketio.AsyncNamespace):
async def schedules(request):
with open("templates/index.html", "r") as f:
template = f.read()
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version})
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
return web.Response(text=html_content, content_type="text/html")
async def costing(request):
with open("templates/costing.html", "r") as f:
template = f.read()
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version})
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
return web.Response(text=html_content, content_type="text/html")
async def sequencing(request):
with open("templates/gantt.html", "r") as f:
template = f.read()
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version})
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
return web.Response(text=html_content, content_type="text/html")
async def documentation(request):
with open("templates/drawings.html", "r") as f:
template = f.read()
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version})
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
return web.Response(text=html_content, content_type="text/html")
@@ -275,7 +229,7 @@ async def documentation(request):
async def demo(request):
with open("templates/demo.html", "r") as f:
template = f.read()
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version})
html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version})
return web.Response(text=html_content, content_type="text/html")

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