diff --git a/.github/workflows/build_osx.yml b/.github/workflows/build_osx.yml index 4b968c8516..0ada497388 100644 --- a/.github/workflows/build_osx.yml +++ b/.github/workflows/build_osx.yml @@ -44,7 +44,11 @@ jobs: brew update brew install git bison autoconf automake libffi cmake findutils echo "$(brew --prefix findutils)/libexec/gnubin" >> $GITHUB_PATH - + + - name: Install aws cli + run: | + python -m pip install awscli + - name: Unpack Dependencies run: | install_root=$(find ./build -maxdepth 4 -name install) @@ -117,20 +121,6 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_UPLOAD_SECRET_ACCESS_KEY }} aws-region: us-east-1 - - name: Overwrite aws cli - run: | - # Error: The `brew link` step did not complete successfully - # The formula built, but is not symlinked into /usr/local - # Could not symlink bin/idle3 - # Target /usr/local/bin/idle3 - # already exists. You may want to remove it: - # rm '/usr/local/bin/idle3' - # - # To force the link and overwrite all conflicting files: - # brew link --overwrite python@3.13 - # https://github.com/rust-lang/rustup/pull/3989/files - brew install --overwrite awscli | true - - name: Upload .zip archives to S3 run: | aws s3 cp ~/output s3://ifcopenshell-builds/ --recursive diff --git a/.github/workflows/build_pyodide.yml b/.github/workflows/build_pyodide.yml index be8a73666e..14bb047333 100644 --- a/.github/workflows/build_pyodide.yml +++ b/.github/workflows/build_pyodide.yml @@ -19,7 +19,7 @@ jobs: with: submodules: recursive repository: pyodide/pyodide - ref: '0.26.4' + ref: '0.28.0a3' token: ${{ secrets.BUILD_REPO_TOKEN }} path: pyodide @@ -29,18 +29,18 @@ jobs: sed -i s/0.8.0/$VERSION/g IfcOpenShell/pyodide/meta.yaml sed -i s/0.8.0/$VERSION/g IfcOpenShell/pyodide/setup.py echo '#!/usr/bin/bash' > script.sh - echo 'cd pyodide' > script.sh - echo 'make && pip install ./pyodide-build' >> script.sh + echo 'cd pyodide' >> script.sh + echo 'make' >> script.sh echo 'cd ..' >> script.sh echo 'mkdir -p packages/ifcopenshell' >> script.sh echo 'cp IfcOpenShell/pyodide/meta.yaml packages/ifcopenshell' >> script.sh echo 'PYODIDE_ROOT=/src/pyodide \' >> script.sh - echo 'PATH=/src/pyodide/emsdk/emsdk:/src/pyodide/emsdk/emsdk/node/20.18.0_64bit/bin:/src/pyodide/emsdk/emsdk/upstream/emscripten:$PATH \' >> script.sh + echo 'PATH=/src/pyodide/emsdk/emsdk:/src/pyodide/emsdk/emsdk/node/22.16.0_64bit/bin:/src/pyodide/emsdk/emsdk/upstream/emscripten:$PATH \' >> script.sh echo 'pyodide build-recipes ifcopenshell --install' >> script.sh chmod +x script.sh sed -i s/--tty// pyodide/run_docker pyodide/run_docker ./script.sh - mv dist/ifcopenshell-$VERSION-py3-none-any.whl dist/ifcopenshell-$VERSION+${GITHUB_SHA:0:7}-cp312-cp312-emscripten_3_1_58_wasm32.whl + mv dist/ifcopenshell-$VERSION-py3-none-any.whl dist/ifcopenshell-$VERSION+${GITHUB_SHA:0:7}-cp313-cp313-emscripten_4_0_9_wasm32.whl - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/build_rocky.yml b/.github/workflows/build_rocky.yml index 45732124dc..4b88f7e90b 100644 --- a/.github/workflows/build_rocky.yml +++ b/.github/workflows/build_rocky.yml @@ -16,7 +16,8 @@ jobs: 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 + findutils xz + python3 -m pip install typing_extensions git config --global --add safe.directory '*' - name: Install aws cli diff --git a/.github/workflows/build_rocky_arm.yml b/.github/workflows/build_rocky_arm.yml index 64c22a3331..aa6c689ff9 100644 --- a/.github/workflows/build_rocky_arm.yml +++ b/.github/workflows/build_rocky_arm.yml @@ -16,7 +16,8 @@ jobs: 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 + findutils xz + python3 -m pip install typing_extensions git config --global --add safe.directory '*' - name: Install aws cli diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index 8c11be8537..5767d006f1 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -5,7 +5,7 @@ on: jobs: build_ifcopenshell: - runs-on: windows-2019 + runs-on: windows-2022 strategy: fail-fast: false matrix: @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v3 with: repository: IfcOpenShell/build-outputs - path: _deps-vs2019-x64-installed + path: _deps-vs2022-x64-installed ref: windows-${{ matrix.arch }} lfs: true token: ${{ secrets.BUILD_REPO_TOKEN }} @@ -40,7 +40,7 @@ jobs: - name: Unpack Dependencies run: | - cd _deps-vs2019-x64-installed + cd _deps-vs2022-x64-installed Get-ChildItem -Path . -Filter 'cache-*.zip' | ForEach-Object { 7z x $_.FullName } @@ -53,17 +53,17 @@ jobs: for /f "tokens=1,2,3 delims=." %%a in ("%PYTHON_VERSION%") do ( set PY_VER_MAJOR_MINOR=%%a%%b ) - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" SET IFCOS_INSTALL_PYTHON=FALSE cd win - echo y | call build-deps.cmd vs2019-x64 Release + echo y | call build-deps.cmd vs2022-x64 Release SET PYTHONHOME=C:\Python\${{ matrix.python }} - call run-cmake.bat vs2019-x64 -DENABLE_BUILD_OPTIMIZATIONS=On -DGLTF_SUPPORT=ON -DADD_COMMIT_SHA=ON -DVERSION_OVERRIDE=ON - call install-ifcopenshell.bat vs2019-x64 Release + call run-cmake.bat vs2022-x64 -DENABLE_BUILD_OPTIMIZATIONS=On -DGLTF_SUPPORT=ON -DADD_COMMIT_SHA=ON -DVERSION_OVERRIDE=ON + call install-ifcopenshell.bat vs2022-x64 Release - name: Pack Dependencies run: | - cd _deps-vs2019-x64-installed + cd _deps-vs2022-x64-installed Get-ChildItem -Path . -Directory | ForEach-Object { $cacheFile = "cache-$($_.Name).zip" echo $cacheFile @@ -74,7 +74,7 @@ jobs: - name: Commit and Push Changes to Build Repository run: | - cd _deps-vs2019-x64-installed + cd _deps-vs2022-x64-installed git config user.name "IfcOpenBot" git config user.email "ifcopenbot@ifcopenshell.org" git add *.zip @@ -90,7 +90,7 @@ jobs: if ("${{ matrix.python }}" -eq "3.9.11") { # only for the first python version the executables are assembled for upload - cd _installed-vs2019-x64/bin + cd _installed-vs2022-x64/bin Get-ChildItem -Path . | ForEach-Object { echo $_ $exe = $_.Name @@ -124,4 +124,7 @@ jobs: AWS_MAX_ATTEMPTS: 3 run: | dir "$env:USERPROFILE\output" - aws s3 cp "$env:USERPROFILE\output" s3://ifcopenshell-builds/ --recursive --debug + foreach ($zip in Get-ChildItem -Path "$env:USERPROFILE\output" -Filter *.zip) { + aws s3 cp "$($zip.FullName)" s3://ifcopenshell-builds/ --debug + Start-Sleep -Seconds 5 + } diff --git a/.github/workflows/ci-bcf-pypi.yml b/.github/workflows/ci-bcf-pypi.yml index 75034acc2f..011babfe2b 100644 --- a/.github/workflows/ci-bcf-pypi.yml +++ b/.github/workflows/ci-bcf-pypi.yml @@ -34,7 +34,7 @@ jobs: cd src/bcf && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-black-formatting.yaml b/.github/workflows/ci-black-formatting.yaml index b1d3295c1b..e0531f6f5b 100644 --- a/.github/workflows/ci-black-formatting.yaml +++ b/.github/workflows/ci-black-formatting.yaml @@ -4,9 +4,6 @@ on: push: pull_request: -env: - PYTHON_VERSION: "3.12" - jobs: lint-formatting: runs-on: ubuntu-latest @@ -17,57 +14,51 @@ jobs: - name: Action - install python uses: actions/setup-python@v5.3.0 with: - python-version: "${{ env.PYTHON_VERSION }}" + python-version: "3.9" - - name: Step 1 - install dependencies - shell: bash + - name: Action - install python + uses: actions/setup-python@v5.3.0 + with: + python-version: "3.11" + + - name: Install dependencies run: | - python3 -m pip install --upgrade pip - python3 -m pip install 'black>=24.10.0' + curl -LsSf https://astral.sh/uv/install.sh | sh + uv tool install ruff + uv tool install black - # NOTE: This would suffice, however it is less informative in terms of the 3 possible outcomes - # - name: QA Step - check linting - # shell: bash - # id: linting - # run: | - # python3 -m black . - - # QA STEP - - name: QA Step - check linting - shell: bash - id: linting + # black doesn't catch all syntax errors, so we check them explicitly. + - name: Check syntax errors + id: syntax-errors run: | - python3 -m black --check . \ - && exit 0 \ - || (echo "exit_code=$?" >> "$GITHUB_OUTPUT" && exit 1); + ERROR=0 + python3.9 -W error -m compileall -q src/ifcopenshell-python || ERROR=1 + python3.11 -W error -m compileall -q src/bonsai || ERROR=1 + exit $ERROR continue-on-error: true - # OUTCOME 1 of QA STEP - - name: QA Step - no linting errors - if: steps.linting.outcome == 'success' - shell: bash - run: |- - echo "::notice::QA step linting succeeded" - exit 0; + - name: Black formatter + id: black + run: | + uvx black --diff --check . + continue-on-error: true - # OUTCOME 2i of QA STEP - - name: QA Step - unprettified code with no syntax errors - if: steps.linting.outputs.exit_code == 1 - shell: bash - run: |- - echo "::group::QA step succeeded with warnings" - echo "::warning::one or more files contains unformatted code but no syntax errors"; - echo "::notice::please run the linter before pushing!"; - echo "::endgroup::" - exit 0; + - name: Ruff check + id: ruff + run: | + uvx ruff check + continue-on-error: true - # OUTCOME 2ii of QA STEP - - name: QA Step - code contains syntax errors - if: steps.linting.outputs.exit_code == 123 - shell: bash - run: |- - echo "::group::QA step failed" - echo "::error::one or more files contains syntax errors"; - echo "::notice::please run the linter and fix syntax errors before pushing!"; - echo "::endgroup::" - exit 1; + - name: Final check + run: | + ERROR=0 + if [ "${{ steps.syntax-errors.outcome }}" != "success" ]; then + echo "::error::Syntax errors check failed, see 'syntax-errors' step for the details." && ERROR=1 + fi + if [ "${{ steps.black.outcome }}" != "success" ]; then + echo "::error::Black formatting check failed, see 'black' step for the details." && ERROR=1 + fi + if [ "${{ steps.ruff.outcome }}" != "success" ]; then + echo "::error::Ruff check failed, see 'ruff' step for the details." && ERROR=1 + fi + exit $ERROR diff --git a/.github/workflows/ci-bonsai-daily.yml b/.github/workflows/ci-bonsai-daily.yml index 29fd8490a7..dfa6be9a00 100644 --- a/.github/workflows/ci-bonsai-daily.yml +++ b/.github/workflows/ci-bonsai-daily.yml @@ -96,7 +96,7 @@ jobs: uses: actions/checkout@v2 with: repository: IfcOpenShell/bonsai_unstable_repo - token: ${{ secrets.IOS_TO_BLENDER_REPO }} + token: ${{ secrets.IFCOPENBOT_TOKEN }} path: bonsai_unstable_repo - name: Download Blender and run critical tests @@ -104,7 +104,7 @@ jobs: # Ensure Bonsai and ifcsverchok enable/disable works before uploading to extensions repo. # Download Blender. - wget -q -O blender.tar.xz https://download.blender.org/release/Blender4.4/blender-4.4.0-linux-x64.tar.xz + wget -q -O blender.tar.xz https://download.blender.org/release/Blender4.5/blender-4.5.0-linux-x64.tar.xz tar -xf blender.tar.xz # Setup Blender. @@ -175,6 +175,7 @@ jobs: cd IfcOpenShell/src/bonsai pip install pytest-blender + pip install pytest-bdd blender --background --python scripts/setup_pytest.py blender --python-expr "import bonsai; print(bonsai.bbim_semver); import ifcopenshell; print(ifcopenshell.version)" --background make test diff --git a/.github/workflows/ci-bsdd-pypi.yaml b/.github/workflows/ci-bsdd-pypi.yaml index a1f68bf236..b59ddbf2e4 100644 --- a/.github/workflows/ci-bsdd-pypi.yaml +++ b/.github/workflows/ci-bsdd-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/bsdd && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifc4d-pypi.yaml b/.github/workflows/ci-ifc4d-pypi.yaml index d2fa648ddb..6ecd2c37d2 100644 --- a/.github/workflows/ci-ifc4d-pypi.yaml +++ b/.github/workflows/ci-ifc4d-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/ifc4d && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifc5d-pypi.yaml b/.github/workflows/ci-ifc5d-pypi.yaml index c4669e1af2..6c545f603d 100644 --- a/.github/workflows/ci-ifc5d-pypi.yaml +++ b/.github/workflows/ci-ifc5d-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/ifc5d && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifccityjson-pypi.yaml b/.github/workflows/ci-ifccityjson-pypi.yaml index f3c921b094..19b7a8e9d7 100644 --- a/.github/workflows/ci-ifccityjson-pypi.yaml +++ b/.github/workflows/ci-ifccityjson-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/ifccityjson && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifcclash-pypi.yaml b/.github/workflows/ci-ifcclash-pypi.yaml index e53c12ab67..adae45e008 100644 --- a/.github/workflows/ci-ifcclash-pypi.yaml +++ b/.github/workflows/ci-ifcclash-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/ifcclash && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifccsv-pypi.yaml b/.github/workflows/ci-ifccsv-pypi.yaml index d1a40f94ab..b62f372bf6 100644 --- a/.github/workflows/ci-ifccsv-pypi.yaml +++ b/.github/workflows/ci-ifccsv-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/ifccsv && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifcdiff-pypi.yaml b/.github/workflows/ci-ifcdiff-pypi.yaml index 6fc88a6059..6059d7a3dd 100644 --- a/.github/workflows/ci-ifcdiff-pypi.yaml +++ b/.github/workflows/ci-ifcdiff-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/ifcdiff && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifcfm-pypi.yaml b/.github/workflows/ci-ifcfm-pypi.yaml index 576d150dc4..50e8707796 100644 --- a/.github/workflows/ci-ifcfm-pypi.yaml +++ b/.github/workflows/ci-ifcfm-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/ifcfm && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifcopenshell-conda-daily.yml b/.github/workflows/ci-ifcopenshell-conda-daily.yml index bad86bf2aa..c1bdb874ab 100644 --- a/.github/workflows/ci-ifcopenshell-conda-daily.yml +++ b/.github/workflows/ci-ifcopenshell-conda-daily.yml @@ -35,11 +35,11 @@ jobs: - name: Version + date str id: verdate - run: echo "verdate='${{ steps.version.outputs.version }}alpha${{ steps.date.outputs.date }}'" >> $GITHUB_OUTPUT + run: echo "verdate=${{ steps.version.outputs.version }}alpha${{ steps.date.outputs.date }}" >> $GITHUB_OUTPUT test: - name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}-${{ matrix.variant }} + name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }} needs: activate runs-on: ${{ matrix.platform.distver }} defaults: @@ -48,57 +48,67 @@ jobs: strategy: fail-fast: false matrix: - variant: [novtk, all] pyver: [ -# { name: py311, distver: '3.11'}, { name: py312, distver: '3.12'} ] platform: [ { name: win, distver: windows-latest, pkg_dir: 'win-64' }, { name: linux, distver: ubuntu-latest, pkg_dir: 'linux-64' }, - { name: macOS, distver: macos-latest, pkg_dir: 'osx-64' } + { name: macOS-arm, distver: macos-latest, pkg_dir: 'osx-arm64' }, + { name: macOS-x86, distver: macos-13, pkg_dir: 'osx-64' } ] steps: - - name: Set up swap space + - name: Set Swap Space if: runner.os == 'Linux' - uses: pierotofy/set-swap-space@v1.0 + uses: pierotofy/set-swap-space@master with: swap-size-gb: 10 - + + - name: set ARTIFACTS ENV vars + shell: bash + run: | + pwd + if [[ "$RUNNER_OS" == "Windows" ]]; then + echo "ARTIFACTS_DIR=D:/a/artifacts" >> $GITHUB_ENV + elif [[ "$RUNNER_OS" == "macOS" ]]; then + echo "ARTIFACTS_DIR=/Users/runner/work/artifacts" >> $GITHUB_ENV + elif [[ "$RUNNER_OS" == "Linux" ]]; then + echo "ARTIFACTS_DIR=/home/runner/work/artifacts" >> $GITHUB_ENV + fi + - uses: actions/checkout@v4 with: submodules: recursive - name: Download and extract MacOSX SDK - if: ${{ runner.os == 'macOS' }} + if: ${{ matrix.platform.name == 'macOS-x86' }} run: | curl -L https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.13.sdk.tar.xz | tar -xvJf - -C /Users/runner/work/ - - uses: mamba-org/setup-micromamba@v1 # https://github.com/mamba-org/setup-micromamba + - uses: mamba-org/setup-micromamba@v2 # https://github.com/mamba-org/setup-micromamba with: environment-name: test-env create-args: >- - python=3.11 + python=3.12 anaconda-client - boa + rattler-build - name: create conda package dist dir run: | - mkdir -p ${{ github.workspace }}/dist + mkdir -p ${{ env.ARTIFACTS_DIR }} - name: build & test ifcopenshell run: | - conda mambabuild . --python ${{ matrix.pyver.distver }} --no-remove-work-dir --output-folder '${{ github.workspace }}/dist' --variants='{variant: ${{ matrix.variant }}}' - working-directory: ./conda + rattler-build build -r conda/recipe.yaml --output-dir '${{ env.ARTIFACTS_DIR }}' env: VERSION_OVERRIDE: ${{ needs.activate.outputs.verdate }} - name: upload to anaconda if: ${{ matrix.platform.name == 'win' }} run: | - anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force --user ifcopenshell '${{ github.workspace }}/dist/${{ matrix.platform.pkg_dir }}/*.tar.bz2' + anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force --user ifcopenshell '${{ env.ARTIFACTS_DIR }}/${{ matrix.platform.pkg_dir }}/*.conda' - name: upload to anaconda if: ${{ matrix.platform.name != 'win' }} run: | - anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force --user ifcopenshell ${{ github.workspace }}/dist/${{ matrix.platform.pkg_dir }}/*.tar.bz2 + anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force --user ifcopenshell ${{ env.ARTIFACTS_DIR }}/${{ matrix.platform.pkg_dir }}/*.conda diff --git a/.github/workflows/ci-ifcopenshell-docker.yml b/.github/workflows/ci-ifcopenshell-docker.yml index e03bd8a929..cdc2e173b4 100644 --- a/.github/workflows/ci-ifcopenshell-docker.yml +++ b/.github/workflows/ci-ifcopenshell-docker.yml @@ -44,6 +44,7 @@ jobs: cmake \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_STANDARD=17 \ -DCMAKE_INSTALL_PREFIX=$PWD/install/ \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH=/usr \ diff --git a/.github/workflows/ci-ifcopenshell-python-pypi.yml b/.github/workflows/ci-ifcopenshell-python-pypi.yml index 73199521bb..0389f1c84f 100644 --- a/.github/workflows/ci-ifcopenshell-python-pypi.yml +++ b/.github/workflows/ci-ifcopenshell-python-pypi.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - pyver: [py39, py310, py311, py312] + pyver: [py39, py310, py311, py312, py313] config: - { name: "Windows 64bit", @@ -44,6 +44,8 @@ jobs: } steps: - uses: actions/checkout@v2 + with: + submodules: recursive - uses: actions/setup-python@v2 # 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 @@ -59,7 +61,7 @@ jobs: cd src/ifcopenshell_${{ matrix.config.short_name }}_${{ matrix.pyver }} && make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }} IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifcopenshell-python.yml b/.github/workflows/ci-ifcopenshell-python.yml index 46f7074fe0..6ebefeb5d7 100644 --- a/.github/workflows/ci-ifcopenshell-python.yml +++ b/.github/workflows/ci-ifcopenshell-python.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - pyver: [py39, py310, py311, py312] + pyver: [py39, py310, py311, py312, py313] config: - { name: "Windows 64bit", @@ -39,6 +39,8 @@ jobs: } steps: - uses: actions/checkout@v2 + with: + submodules: recursive - uses: actions/setup-python@v2 # 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 diff --git a/.github/workflows/ci-ifcpatch-pypi.yaml b/.github/workflows/ci-ifcpatch-pypi.yaml index cdeb78c27d..312fe49f74 100644 --- a/.github/workflows/ci-ifcpatch-pypi.yaml +++ b/.github/workflows/ci-ifcpatch-pypi.yaml @@ -28,7 +28,7 @@ jobs: cd src/ifcpatch && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci-ifctester-pypi.yml b/.github/workflows/ci-ifctester-pypi.yml index 51835418e7..6b594509ef 100644 --- a/.github/workflows/ci-ifctester-pypi.yml +++ b/.github/workflows/ci-ifctester-pypi.yml @@ -28,7 +28,7 @@ jobs: cd src/ifctester && make dist IS_STABLE=TRUE - name: Publish a Python distribution to PyPI - uses: ortega2247/pypi-upload-action@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5845b9d8d7..aed1223646 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,7 @@ jobs: cmake \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_STANDARD=17 \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH=/usr \ -DCMAKE_SYSTEM_PREFIX_PATH=/usr \ @@ -127,12 +128,21 @@ jobs: cd ../src/ifcopenshell-python mv ifcopenshell ifcopenshell-local # Force testing on installed module pip install -e ../ifcpatch --no-deps # Needed for sql.py tests. - make test - cd ../bcf && make test + ERROR=0 + make test || ERROR=1 + cd ../bcf && make test || ERROR=1 pip install requests - cd ../bsdd && make test + cd ../bsdd && make test || ERROR=1 pip install deepdiff - cd ../ifcdiff && make test - cd ../ifcpatch && make test + cd ../ifcdiff && make test || ERROR=1 + cd ../ifcpatch && make test || ERROR=1 pip install -e ../ifctester --no-deps - cd ../ifctester && make test + cd ../ifctester && make test || ERROR=1 + # Run mathutils related tests at the end to ensure no other code is relying on mathutils. + cd ../ifcopenshell-python + pip install mathutils + make test-mathutils || ERROR=1 + if [ $ERROR -ne 0 ]; then + echo "One or more tests failed"; + exit 1; + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5690af06b5..93ecaf11d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,6 +46,7 @@ jobs: mkdir build && cd build cmake \ -DCMAKE_INSTALL_PREFIX=$PWD/install/ \ + -DCMAKE_CXX_STANDARD=17 \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_PREFIX_PATH=/usr \ -DCMAKE_SYSTEM_PREFIX_PATH=/usr \ diff --git a/.gitignore b/.gitignore index d87b9eca58..608fdd3e74 100644 --- a/.gitignore +++ b/.gitignore @@ -86,6 +86,7 @@ src/bonsai/bonsai/bim/data/build/ src/bonsai/bonsai/bim/data/gantt/index.html src/bonsai/bonsai/bim/data/gantt/jsgantt.js src/bonsai/bonsai/bim/data/gantt/jsgantt.css +src/bonsai/bonsai/bim/data/webui/static/js/jquery.min.js src/bonsai/drawings src/bonsai/layouts @@ -108,4 +109,4 @@ src/bonsai/bonsai/bim/schema/Brick.ttl bonsaiDecoratorForLoads.code-workspace dev_environment.bat -.pixi/ +.pixi/ \ No newline at end of file diff --git a/VERSION b/VERSION index ee94dd834b..b60d71966a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.3 +0.8.4 diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 7bb1eedc23..6c535a23a5 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -18,7 +18,12 @@ ################################################################################ cmake_minimum_required(VERSION 3.21) -set(CMAKE_CXX_STANDARD 17) +if (NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() +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 set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -29,13 +34,12 @@ endif() add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR) -cmake_policy(SET CMP0048 NEW) -cmake_policy(SET CMP0074 NEW) -cmake_policy(SET CMP0078 NEW) -cmake_policy(SET CMP0086 NEW) -if (POLICY CMP0144) +if(POLICY CMP0144) # 3.27 cmake_policy(SET CMP0144 NEW) # find_package() uses upper-case _ROOT variables. endif() +if(POLICY CMP0167) # 3.30 + cmake_policy(SET CMP0167 OLD) +endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") @@ -64,7 +68,7 @@ option(BUILD_IFCPYTHON "Build IfcPython." ON) option(BUILD_CONVERT "Build IfcConvert executable." ON) option(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF) option(BUILD_EXAMPLES "Build example applications." ON) -option(BUILD_GEOMSERVER "Build IfcGeomServer executable." ON) +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_QTVIEWER "Build IfcOpenShell Qt GUI Viewer" OFF) # QtViewer requires Qt6 option(BUILD_PACKAGE "" OFF) @@ -84,6 +88,12 @@ option(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only ins option(ADD_COMMIT_SHA "Add commit sha and branch in version number, warning results in many rebuilds, requires git" OFF) option(VERSION_OVERRIDE "Override the version defined in IfcParse.h with the file VERSION in the repository root" OFF) +set( + PYTHON_MODULE_INSTALL_DIR + "" CACHE PATH + "Directory to install IfcPython package to. By default package is installed in found Python's site-packages." +) + if (VERSION_OVERRIDE) file(READ "../VERSION" "RELEASE_VERSION_") string(STRIP "${RELEASE_VERSION_}" RELEASE_VERSION) @@ -217,6 +227,10 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) endmacro() if(WITH_CGAL) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT EXISTS "${CGAL_INCLUDE_DIR}") + message(FATAL_ERROR "CGAL_INCLUDE_DIR ('${CGAL_INCLUDE_DIR}') is not provided or doesn't exist.") + endif() + add_definitions(-DIFOPSH_WITH_CGAL) set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_CGAL) @@ -250,7 +264,7 @@ if(GLTF_SUPPORT OR CITYJSON_SUPPORT) set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_GLTF) endif() -# Add USD support to serializers +# Add USD support to serializers if(USD_SUPPORT) UNIFY_ENVVARS_AND_CACHE(USD_INCLUDE_DIR) UNIFY_ENVVARS_AND_CACHE(USD_LIBRARY_DIR) @@ -273,13 +287,13 @@ if(USD_SUPPORT) endif() set(USD_LIBRARIES - usd_usd + usd_usd usd_usdGeom - usd_usdShade - usd_usdLux - usd_vt - usd_sdf - usd_tf + usd_usdShade + usd_usdLux + usd_vt + usd_sdf + usd_tf usd_gf ) @@ -353,7 +367,9 @@ message(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}") if(NOT MINIMAL_BUILD) # libxml2 is required for IFCXML (optional) and SVGFILL (mandatory) clear_wasm_sysroot() - find_package(LibXml2 REQUIRED) + if(IFCXML_SUPPORT) + find_package(LibXml2 REQUIRED) + endif() restore_wasm_sysroot() endif() @@ -369,44 +385,85 @@ if(BUILD_IFCGEOM) # Open CASCADE if(WITH_OPENCASCADE) - if("${OCC_INCLUDE_DIR}" STREQUAL "") + + # Fallback for missing OCC_INCLUDE_DIR. + if(NOT OCC_INCLUDE_DIR) clear_wasm_sysroot() find_path(OCC_INCLUDE_DIR Standard_Version.hxx PATHS /usr/include/occt /usr/include/oce /usr/include/opencascade - REQUIRED ) restore_wasm_sysroot() if(OCC_INCLUDE_DIR) message(STATUS "Found Open CASCADE include files in: ${OCC_INCLUDE_DIR}") - else() - message(FATAL_ERROR "Unable to find Open CASCADE include directory, specify OCC_INCLUDE_DIR manually.") endif() else() set(OCC_INCLUDE_DIR ${OCC_INCLUDE_DIR} CACHE FILEPATH "Open CASCADE header files") message(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}") endif() - if(OCC_INCLUDE_DIR) - file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAJOR - REGEX "#define OCC_VERSION_MAJOR.*" + if("${OCC_LIBRARY_DIR}" STREQUAL "") + find_library(OCC_LIBRARY TKernel + PATHS + /usr/lib ) - string(REGEX MATCH "[0-9]+" OCC_MAJOR ${OCC_MAJOR}) - file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MINOR - REGEX "#define OCC_VERSION_MINOR.*" - ) - string(REGEX MATCH "[0-9]+" OCC_MINOR ${OCC_MINOR}) - file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAINT - REGEX "#define OCC_VERSION_MAINTENANCE.*" - ) - string(REGEX MATCH "[0-9]+" OCC_MAINT ${OCC_MAINT}) - set(OCC_VERSION_STRING "${OCC_MAJOR}.${OCC_MINOR}.${OCC_MAINT}") - endif(OCC_INCLUDE_DIR) - set(OPENCASCADE_LIBRARY_NAMES + if(OCC_LIBRARY) + GET_FILENAME_COMPONENT(OCC_LIBRARY_DIR ${OCC_LIBRARY} PATH) + message(STATUS "Found Open CASCADE library files in: ${OCC_LIBRARY_DIR}") + endif() + else() + set(OCC_LIBRARY_DIR ${OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files") + message(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}") + endif() + + # No specific paths specified, try to find package. + if(OCC_INCLUDE_DIR AND OCC_LIBRARY_DIR) + message( + STATUS + "Using provided OCCT_INCLUDE_DIR ('${OCC_INCLUDE_DIR}') " + "and OCC_LIBRARY_DIR ('${OCC_LIBRARY_DIR}')." + ) + elseif(NOT OCC_INCLUDE_DIR AND NOT OCC_LIBRARY_DIR) + find_package(OpenCASCADE CONFIG REQUIRED) + get_target_property(OCC_INCLUDE_DIR TKernel INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(TKERNEL_LIB_PATH TKernel LOCATION) + get_filename_component(OCC_LIBRARY_DIR "${TKERNEL_LIB_PATH}" DIRECTORY) + message( + STATUS + "Found Open CASCADE package at '${OpenCASCADE_DIR}', " + "deducing from it OCC_INCLUDE_DIR: '${OCC_INCLUDE_DIR}' " + "and OCC_LIBRARY_DIR: '${OCC_LIBRARY_DIR}'." + ) + else() + message( + FATAL_ERROR + "Couldn't find Open CASCADE installation. " + "Either both OCC_INCLUDE_DIR ('${OCC_INCLUDE_DIR}') and OCC_LIBRARY_DIR ('${OCC_LIBRARY_DIR}') " + "must be specified or OpenCASCADE package should be discoverable." + ) + endif() + + # Parse OCC_VERSION_STRING. + file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAJOR + REGEX "#define OCC_VERSION_MAJOR.*" + ) + string(REGEX MATCH "[0-9]+" OCC_MAJOR ${OCC_MAJOR}) + file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MINOR + REGEX "#define OCC_VERSION_MINOR.*" + ) + string(REGEX MATCH "[0-9]+" OCC_MINOR ${OCC_MINOR}) + file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAINT + REGEX "#define OCC_VERSION_MAINTENANCE.*" + ) + string(REGEX MATCH "[0-9]+" OCC_MAINT ${OCC_MAINT}) + set(OCC_VERSION_STRING "${OCC_MAJOR}.${OCC_MINOR}.${OCC_MAINT}") + + set( + OPENCASCADE_LIBRARY_NAMES TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet TKXSBase TKOffset TKHLR @@ -420,24 +477,6 @@ if(BUILD_IFCGEOM) list(APPEND OPENCASCADE_LIBRARY_NAMES TKDESTEP TKDEIGES) endif(OCC_VERSION_STRING VERSION_LESS 7.8.0) - if("${OCC_LIBRARY_DIR}" STREQUAL "") - find_library(OCC_LIBRARY TKernel - PATHS - /usr/lib - REQUIRED - ) - - if(OCC_LIBRARY) - GET_FILENAME_COMPONENT(OCC_LIBRARY_DIR ${OCC_LIBRARY} PATH) - message(STATUS "Found Open CASCADE library files in: ${OCC_LIBRARY_DIR}") - else() - message(FATAL_ERROR "Unable find Open CASCADE library directory, specify OCC_LIBRARY_DIR manually.") - endif() - else() - set(OCC_LIBRARY_DIR ${OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files") - message(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}") - endif() - clear_wasm_sysroot() find_library(libTKernel NAMES TKernel TKerneld PATHS ${OCC_LIBRARY_DIR} NO_DEFAULT_PATH) restore_wasm_sysroot() @@ -476,7 +515,7 @@ if(BUILD_IFCGEOM) if(OCCT_STATIC) find_package(Threads) - + if(WASM_BUILD) set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) else() @@ -488,7 +527,7 @@ if(BUILD_IFCGEOM) set(OPENCASCADE_LIBRARIES -Wl,--start-group ${OPENCASCADE_LIBRARIES} -Wl,--end-group ${CMAKE_THREAD_LIBS_INIT}) endif() endif() - + if(NOT APPLE AND NOT WIN32) set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt") endif() @@ -649,14 +688,27 @@ if(HDF5_SUPPORT) endif() if(NOT HDF5_LIBRARIES) - # debian default - set(HDF5_LIBRARIES - /usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5_cpp.so - /usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5.so - /usr/lib/x86_64-linux-gnu/libsz.so - /usr/lib/x86_64-linux-gnu/libaec.so - z dl - ) + if(NOT WIN32 AND NOT APPLE) + # debian default + set(HDF5_LIBRARIES + /usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5_cpp.so + /usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5.so + /usr/lib/x86_64-linux-gnu/libsz.so + /usr/lib/x86_64-linux-gnu/libaec.so + z dl + ) + else() + find_package(HDF5 CONFIG) + if(NOT HDF5_DIR) + message( + FATAL_ERROR + "HDF5_LIBRARY_DIR is not provided (current value: '${HDF5_LIBRARY_DIR}'). " + "Also could not find HDF5 package." + ) + endif() + message(STATUS "Found HDF5 package: '${HDF5_DIR}'.") + set(HDF5_LIBRARIES hdf5_cpp-static) + endif() endif() add_definitions(-DWITH_HDF5) @@ -773,6 +825,21 @@ else() endif() endif(MSVC) + +# Ensure other dependencies are provided. +if(NOT EXISTS "${EIGEN_DIR}") + find_package(Eigen3 CONFIG) + if(NOT Eigen3_DIR) + message( + FATAL_ERROR + "EIGEN_DIR is not provided or provided folder doesn't exist (current value: '${EIGEN_DIR}'). " + "Also couldn't find Eigen3 as a package." + ) + endif() + link_libraries(Eigen3::Eigen) +endif() + + include_directories(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR} ${JSON_INCLUDE_DIR} ${HDF5_INCLUDE_DIR} ${EIGEN_DIR} ${CGAL_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${MPFR_INCLUDE_DIR} ${USD_INCLUDE_DIR} @@ -873,7 +940,7 @@ if(BUILD_IFCGEOM) if(WASM_BUILD) set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) else() - set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) + set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) endif() endif() @@ -962,7 +1029,7 @@ if(BUILD_IFCGEOM) file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/kernels/${kernel}/*.h) file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/kernels/${kernel}/*.cpp) set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES}) - + add_library(geometry_kernel_${kernel} ${IFCGEOM_FILES}) set_property(TARGET geometry_kernel_${kernel} APPEND PROPERTY COMPILE_FLAGS "-DIFC_GEOM_EXPORTS") # needed? @@ -970,7 +1037,7 @@ if(BUILD_IFCGEOM) # endif() target_link_libraries(geometry_kernel_${kernel} ${${KERNEL_UPPER}_LIBRARIES}) list(APPEND kernel_libraries geometry_kernel_${kernel}) - + if(${kernel} STREQUAL "cgal") set_property(TARGET geometry_kernel_${kernel} APPEND_STRING PROPERTY COMPILE_FLAGS " -DCGAL_HAS_THREADS") @@ -990,7 +1057,7 @@ if(BUILD_IFCGEOM) file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/mapping/*.h) file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/mapping/*.cpp) set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES} ${IFCGEOM_I_FILES}) - + add_library(geometry_mapping_ifc${schema} STATIC ${IFCGEOM_FILES}) set_target_properties(geometry_mapping_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}") target_link_libraries(geometry_mapping_ifc${schema} IfcParse) @@ -1029,7 +1096,7 @@ if(BUILD_CONVERT OR BUILD_IFCPYTHON) foreach(schema ${SCHEMA_VERSIONS}) add_library(Serializers_ifc${schema} STATIC ${SERIALIZERS_S_FILES}) set_target_properties(Serializers_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}") - + if(WASM_BUILD) target_link_libraries(Serializers_ifc${schema} ${HDF5_LIBRARIES}) else() @@ -1121,6 +1188,11 @@ endif(BUILD_CONVERT) # IfcGeomServer if(BUILD_GEOMSERVER) + + if(NOT WITH_OPENCASCADE) + message(FATAL_ERROR "Open CASCADE is required to build IfcGeomServer.") + endif() + file(GLOB CPP_FILES ../src/ifcgeomserver/*.cpp) file(GLOB H_FILES ../src/ifcgeomserver/*.h) set(SOURCE_FILES ${CPP_FILES} ${H_FILES}) @@ -1209,7 +1281,7 @@ if(BUILD_IFCMAX) endif() if(WITH_CGAL) - add_subdirectory(../src/svgfill svgfill) + add_subdirectory(../src/svgfill svgfill) endif() if(BUILD_QTVIEWER) @@ -1267,11 +1339,13 @@ if(BUILD_CONVERT) endif(BUILD_CONVERT) if(BUILD_CONVERT OR BUILD_IFCPYTHON) - install(TARGETS geometry_serializer ${geometry_serializer_libraries} - ARCHIVE DESTINATION ${LIBDIR} - LIBRARY DESTINATION ${LIBDIR} - RUNTIME DESTINATION ${BINDIR} - ) + if(WITH_OPENCASCADE) + install(TARGETS geometry_serializer ${geometry_serializer_libraries} + ARCHIVE DESTINATION ${LIBDIR} + LIBRARY DESTINATION ${LIBDIR} + RUNTIME DESTINATION ${BINDIR} + ) + endif() endif(BUILD_CONVERT OR BUILD_IFCPYTHON) # Cmake uninstall target @@ -1289,6 +1363,8 @@ endif() 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}") diff --git a/cmake/utilities.cmake b/cmake/utilities.cmake index fe7c4fa20d..590c5e5c1d 100644 --- a/cmake/utilities.cmake +++ b/cmake/utilities.cmake @@ -121,4 +121,4 @@ function(files_for_ifc_version IFC_VERSION RESULT_NAME) ${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.cpp PARENT_SCOPE ) -endfunction() \ No newline at end of file +endfunction() diff --git a/conda/bld.bat b/conda/build.bat similarity index 92% rename from conda/bld.bat rename to conda/build.bat index 07e546052d..388e5cd746 100644 --- a/conda/bld.bat +++ b/conda/build.bat @@ -9,6 +9,7 @@ set LIBXML2="%LIBRARY_PREFIX%/lib/libxml2.lib" cmake -G "Ninja" ^ -D SCHEMA_VERSIONS="2x3;4;4x1;4x3_add2" ^ -D CMAKE_BUILD_TYPE:STRING=Release ^ + -D CMAKE_CXX_STANDARD=17 ^ -D CMAKE_INSTALL_PREFIX:FILEPATH="%LIBRARY_PREFIX%" ^ -D CMAKE_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^ -D CMAKE_SYSTEM_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^ @@ -49,3 +50,7 @@ if errorlevel 1 exit 1 ninja install -j 1 if errorlevel 1 exit 1 + +python %RECIPE_DIR%/update_version_init.py %PKG_VERSION% %SP_DIR%/ifcopenshell/__init__.py + +if errorlevel 1 exit 1 \ No newline at end of file diff --git a/conda/build.sh b/conda/build.sh index c0d447a6f4..b17bc87159 100644 --- a/conda/build.sh +++ b/conda/build.sh @@ -16,6 +16,7 @@ cmake ${CMAKE_ARGS} -G Ninja \ -DSCHEMA_VERSIONS="2x3;4;4x1;4x3_add2" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ + -DCMAKE_CXX_STANDARD=17 \ ${CMAKE_PLATFORM_FLAGS[@]} \ -DCMAKE_PREFIX_PATH=$PREFIX \ -DCMAKE_SYSTEM_PREFIX_PATH=$PREFIX \ @@ -46,4 +47,6 @@ cmake ${CMAKE_ARGS} -G Ninja \ ninja -ninja install -j 1 \ No newline at end of file +ninja install -j 1 + +python "${RECIPE_DIR}/update_version_init.py" "${PKG_VERSION}" "${SP_DIR}/ifcopenshell/__init__.py" \ No newline at end of file diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml index 2b600d14be..47b843ee31 100644 --- a/conda/conda_build_config.yaml +++ b/conda/conda_build_config.yaml @@ -1,6 +1,5 @@ -variant: - - novtk - - all +python: + - 3.12 occt: - 7.8.1 @@ -24,14 +23,15 @@ cxx_compiler_version: - '12' # [linux] - '16' # [osx] c_stdlib_version: -- '2.12' # [linux] -- '10.13' # [osx] + - 2.17 # [linux] + - 10.13 # [osx and x86_64] + - 11.0 # [osx and arm64] hdf5: -- 1.14.3 +- 1.14.6 libboost_devel: -- '1.84' +- '1.86' libxml2: -- '2' +- 2.13 mpfr: - '4' gmp: @@ -47,12 +47,12 @@ target_platform: - win-64 # [win] - linux-64 # [linux] - osx-64 # [osx] -macos_machine: -- x86_64-apple-darwin13.4.0 -MACOSX_DEPLOYMENT_TARGET: -- '10.13' -MACOSX_SDK_VERSION: -- '10.13' +macos_machine: # [osx] + - x86_64-apple-darwin13.4.0 # [osx and x86_64] + - arm64-apple-darwin20.0.0 # [osx and arm64] +MACOSX_DEPLOYMENT_TARGET: # [osx] + - 11.0 # [osx and arm64] + - 10.13 # [osx and x86_64] -CONDA_BUILD_SYSROOT: - - "/Users/runner/work/MacOSX10.13.sdk" # [osx] \ No newline at end of file +CONDA_BUILD_SYSROOT: # [osx] + - "/Users/runner/work/MacOSX10.13.sdk" # [osx and x86_64] diff --git a/conda/meta.yaml b/conda/recipe.yaml similarity index 86% rename from conda/meta.yaml rename to conda/recipe.yaml index 53ad90be3f..aeddedbe07 100644 --- a/conda/meta.yaml +++ b/conda/recipe.yaml @@ -1,64 +1,78 @@ -{% set version = environ.get('VERSION_OVERRIDE', '0.8.0') %} -{% set build = 0 %} - -# Higher number -> Always prioritize "novtk" variant over "all" variant -{% set build = build + 200 %} # [variant == "novtk"] -{% set build = build + 100 %} # [variant == "all"] +context: + version: ${{ env.get("VERSION_OVERRIDE", default="0.8.2.1") }} + build: 1 package: name: ifcopenshell - version: {{ version }} + version: ${{ version }} source: - path: ../ + - path: .. build: - number: {{ build }} - skip: true # [py<39] - binary_relocation: false # [osx] - string: py{{ CONDA_PY }}_{{ variant }}_h{{ PKG_HASH }}_{{ build }} - run_exports: - - {{ pin_subpackage('ifcopenshell', max_pin='x.x.x') }} *{{ variant }}* - + number: ${{ build }} + dynamic_linking: + binary_relocation: ${{ true if osx }} requirements: build: - - cmake + - if: build_platform != target_platform + then: + - python + - cross-python_${{ target_platform }} + - cmake <4 - ninja - swig >=4.1.1 - - {{ stdlib("c") }} - - {{ compiler('c') }} - - {{ compiler('cxx') }} + - ${{ stdlib("c") }} + - ${{ compiler('c') }} + - ${{ compiler('cxx') }} host: - python - libboost-devel - - occt *=*{{ variant }}* + - occt - libxml2 - cgal-cpp - hdf5 - eigen - mpfr - nlohmann_json - - gmp # [unix] - - mpir # [win] + - gmp - zlib run: - python - - {{ pin_compatible('occt', max_pin='x.x.x') }} *{{ variant }}* - - {{ pin_compatible('cgal-cpp', max_pin='x.x') }} - - -test: - imports: - - ifcopenshell + - shapely + - typing_extensions + - ${{ pin_compatible('occt', upper_bound='x.x.x') }} + - ${{ pin_compatible('cgal-cpp', upper_bound='x.x') }} + run_exports: + - ${{ pin_subpackage('ifcopenshell', upper_bound='x.x.x') }} +tests: + - python: + imports: + - ifcopenshell + pip_check: false + - script: + - python -c "import ifcopenshell; assert ifcopenshell.version == '${{ version }}', 'print(ifcopenshell.version)'" + requirements: + run: + - occt * *novtk* # Ensure that even though compiled against OCCT with VTK, it can still run with OCCT without VTK + - pytest + - python-dateutil + - xmlschema + - xsdata + - lxml + - isodate + - lark + - networkx + - tabulate + - shapely about: - home: https://ifcopenshell.org license: LGPL-3.0-or-later license_file: COPYING - summary: 'IfcOpenShell is a library to support the IFC file format' + summary: IfcOpenShell is a library to support the IFC file format description: | IfcOpenShell ============ @@ -293,8 +307,9 @@ about: [MSYS2]: https://msys2.github.io/ "MSYS2" [win/readme.md]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/win/readme.md "win/readme.md" [nix/build-all.py]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/nix/build-all.py "nix/build-all.py" - doc_url: https://ifcopenshell.org/ - dev_url: https://github.com/IfcOpenShell/IfcOpenShell + homepage: https://ifcopenshell.org + repository: https://github.com/IfcOpenShell/IfcOpenShell + documentation: https://ifcopenshell.org/ extra: recipe-maintainers: diff --git a/conda/update_version_init.py b/conda/update_version_init.py new file mode 100644 index 0000000000..e5a7a6fd22 --- /dev/null +++ b/conda/update_version_init.py @@ -0,0 +1,32 @@ +import re +import argparse +from pathlib import Path + +def update_version(file_path: str, version: str) -> None: + """Update the version string in the given __init__.py file.""" + file_path = Path(file_path) + + # Read the file and replace the version + file_contents = file_path.read_text(encoding="utf-8") + new_contents = re.sub(r'version = "0\.0\.0"', f'version = "{version}"', file_contents) + + # Write the updated contents back to the file + file_path.write_text(new_contents, encoding="utf-8") + + print(f"Updated version in {file_path} to {version}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Update the version string in a Python file.") + parser.add_argument( + "version", + type=str, + help="The version string to replace '0.0.0' with (e.g., '1.2.3')." + ) + parser.add_argument( + "file", + type=str, + help="The path to the __init__.py file where the version will be updated." + ) + + args = parser.parse_args() + update_version(args.file, args.version) diff --git a/nix/build-all.py b/nix/build-all.py index 5f2f2f0a79..bcf224f5ab 100644 --- a/nix/build-all.py +++ b/nix/build-all.py @@ -18,6 +18,32 @@ # # ############################################################################### +""" +Example usage: + # Build all targets by default. + python build-all.py + + # Build just the provided targets. + python build-all.py IfcParse IfcOpenShell-Python + + +Available arguments: + ``-py-313`` - build for specific Python version + (building for all supported Python version by default). + ``-wasm`` - compile for wasm + ``-shared`` - build shared libraries. By default will build static. + ``-diskcleanup`` - clean up build directories after finishing building dependencies + ``-v`` - enable verbose logs + + +Used environment variables: + - ``CXXFLAGS``, ``CPPFLAGS``, ``CFLAGS``, ``LDFLAGS`` + - ``BUILD_DIR`` - build directory. By default will use "build" folder in IfcOpenShell repository. + - ``DEPS_DIR`` - dependencies directory. By default will create automatic folder in build directory. + - ``BUILD_CFG`` - build configuration, 'RelWithDebInfo' by default. +""" + + ############################################################################### # # # This script builds IfcOpenShell and its dependencies # @@ -67,15 +93,24 @@ import sysconfig # @todo temporary for expired mpfr.org certificate on 2023-04-08 import ssl + ssl._create_default_https_context = ssl._create_unverified_context +import time from urllib.request import urlretrieve +from collections.abc import Generator, Sequence +from pathlib import Path +try: + from typing import Union, Literal +except: + # python 3.6 compatibility for rocky 8 + from typing import Union + from typing_extensions import Literal logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) ch = logging.StreamHandler() -ch.setLevel(logging.INFO) logger.addHandler(ch) PROJECT_NAME = "IfcOpenShell" @@ -88,14 +123,14 @@ OCE_VERSION = "0.18.3" OCCT_VERSION = "7.8.1" BOOST_VERSION = "1.86.0" PCRE_VERSION = "8.41" -LIBXML2_VERSION = "2.9.11" +LIBXML2_VERSION = "2.13.8" SWIG_VERSION = "4.0.2" OPENCOLLADA_VERSION = "v1.6.68" HDF5_VERSION = "1.12.1" GMP_VERSION = "6.2.1" -MPFR_VERSION = "3.1.6" # latest is 4.1.0 -CGAL_VERSION = "5.3" +MPFR_VERSION = "3.1.6" # latest is 4.1.0 +CGAL_VERSION = "5.6.x-branch" USD_VERSION = "23.05" TBB_VERSION = "2021.9.0" @@ -116,21 +151,27 @@ wget = "wget" strip = "strip" explicit_targets = [s for s in sys.argv[1:] if not s.startswith("-")] -flags = set(s.lstrip('-') for s in sys.argv[1:] if s.startswith("-")) +"""Targets provided by CLI.""" +flags = set(s.lstrip("-") for s in sys.argv[1:] if s.startswith("-")) +"""CLI flags.""" # Helper function for coloured printing -NO_COLOR = "\033[0m" # http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux +NO_COLOR = ( + "\033[0m" # http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux +) BLACK_ON_WHITE = "\033[0;30;107m" RED = "\033[31m" GREEN = "\033[32m" YELLOW = "\033[33m" MAGENTA = "\033[35m" + def cecho(message, color=NO_COLOR): """Logs message `message` in color `color`.""" logger.info(f"{color}{message}\033[0m") + def which(cmd): for path in os.getenv("PATH").split(":"): if os.path.exists(path) and cmd in os.listdir(path): @@ -154,10 +195,10 @@ CMAKE_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "cmak build_dir = os.environ.get("BUILD_DIR", os.path.join(os.path.dirname(__file__), "..", "build")) -path = [build_dir, platform.system(), "wasm" if "wasm" in flags else platform.machine()] +DEFAULT_DEPS_DIR = Path(build_dir) / platform.system() / ("wasm" if "wasm" in flags else platform.machine()) if TOOLSET: - path.append(TOOLSET) -DEFAULT_DEPS_DIR = os.path.realpath(os.path.join(*path)) + DEFAULT_DEPS_DIR = DEFAULT_DEPS_DIR / TOOLSET +DEFAULT_DEPS_DIR = os.path.realpath(DEFAULT_DEPS_DIR) DEPS_DIR = os.getenv("DEPS_DIR", DEFAULT_DEPS_DIR) @@ -169,11 +210,17 @@ BUILD_CFG = os.getenv("BUILD_CFG", "RelWithDebInfo") # Print build configuration information -cecho (f"""This script fetches and builds {PROJECT_NAME} and its dependencies -""", BLACK_ON_WHITE) -cecho("""Script configuration: +cecho( + f"""This script fetches and builds {PROJECT_NAME} and its dependencies +""", + BLACK_ON_WHITE, +) +cecho( + """Script configuration: -""", GREEN) +""", + GREEN, +) cecho(f"""* USE_OCCT = {USE_OCCT}""", MAGENTA) if USE_OCCT: cecho(" - Compiling against official Open Cascade") @@ -182,45 +229,53 @@ else: cecho(f"* Dependency Directory = {DEPS_DIR}", MAGENTA) cecho(f" - The directory where {PROJECT_NAME} dependencies are installed.") cecho(f"* Build Config Type = {BUILD_CFG}", MAGENTA) -cecho(""" - The used build configuration type for the dependencies. - Defaults to RelWithDebInfo if not specified.""") +cecho( + """ - The used build configuration type for the dependencies. + Defaults to RelWithDebInfo if not specified.""" +) if BUILD_CFG == "MinSizeRel": cecho(" WARNING: MinSizeRel build can suffer from a significant performance loss.", RED) cecho(f"* IFCOS_NUM_BUILD_PROCS = {IFCOS_NUM_BUILD_PROCS}", MAGENTA) -cecho(""" - How many compiler processes may be run in parallel. -""") +cecho( + """ - How many compiler processes may be run in parallel. +""" +) -dependency_tree = { - 'IfcParse': ('boost', 'libxml2', 'hdf5'), - 'IfcGeom': ('IfcParse', 'occ', 'json', 'cgal', 'eigen'), - 'IfcConvert': ('IfcGeom',), - 'OpenCOLLADA': ('libxml2', 'pcre'), - 'IfcGeomServer': ('IfcGeom',), - 'IfcOpenShell-Python': ('python', 'swig', 'IfcGeom'), - 'swig': ('pcre',), - 'boost': (), - 'libxml2': (), - 'python': (), - 'occ': ('freetype',), - 'pcre': (), - 'json': (), - 'hdf5': (), - 'cgal': (), - 'eigen': (), - 'freetype': (), +dependency_tree: "dict[str, tuple[str, ...]]" = { + "IfcParse": ("boost", "libxml2", "hdf5"), + "IfcGeom": ("IfcParse", "occ", "json", "cgal", "eigen"), + "IfcConvert": ("IfcGeom",), + "OpenCOLLADA": ("libxml2", "pcre"), + "IfcGeomServer": ("IfcGeom",), + "IfcOpenShell-Python": ("python", "swig", "IfcGeom"), + "swig": ("pcre",), + "boost": (), + "libxml2": (), + "python": (), + "occ": ("freetype",), + "pcre": (), + "json": (), + "hdf5": (), + "cgal": (), + "eigen": (), + "freetype": (), # 'usd': ('boost', 'oneTBB') } -def v(dep): - yield dep - for d in dependency_tree[dep]: - for x in v(d): - yield x + +def gather_dependencies(dep: str) -> "Generator[str]": + yield dep + for d in dependency_tree[dep]: + for x in gather_dependencies(d): + yield x + if "v" in flags: logger.setLevel(logging.DEBUG) + formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") + ch.setFormatter(formatter) else: logger.setLevel(logging.INFO) @@ -229,21 +284,21 @@ BUILD_STATIC = "shared" not in flags ENABLE_FLAG = "--enable-static" if BUILD_STATIC else "--enable-shared" DISABLE_FLAG = "--disable-shared" if BUILD_STATIC else "--disable-static" LINK_TYPE = "static" if BUILD_STATIC else "shared" -LINK_TYPE_UCFIRST = LINK_TYPE[0].upper() + LINK_TYPE[1:] +LINK_TYPE_UCFIRST = LINK_TYPE.capitalize() LIBRARY_EXT = "a" if BUILD_STATIC else "so" PIC = "-fPIC" if BUILD_STATIC else "" if any(f.startswith("py-") for f in flags): - PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if "py-%s" % "".join(pyv.split('.')[0:2]) in flags] + PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if f"py-{''.join(pyv.split('.')[:2])}" in flags] -if len(explicit_targets): - targets = set(sum((list(v(target)) for target in explicit_targets), [])) +if explicit_targets: + targets = {dep for target in explicit_targets for dep in gather_dependencies(target)} else: targets = set(dependency_tree.keys()) - -targets = set(t for t in targets if 'without-%s' % t.lower() not in flags) -print("Building:", *sorted(targets, key=lambda t: len(list(v(t))))) +targets = set(t for t in targets if "without-%s" % t.lower() not in flags) + +print("Building:", *sorted(targets, key=lambda t: len(list(gather_dependencies(t))))) # Check that required tools are in PATH @@ -267,11 +322,12 @@ logger.info(f"using command log file '{LOG_FILE}'") # Causing havoc in python 3.11 build try: - del os.environ['__PYVENV_LAUNCHER__'] -except: pass + del os.environ["__PYVENV_LAUNCHER__"] +except: + pass -def run(cmds, cwd=None, can_fail=False): +def run(cmds: "Sequence[str]", cwd: "Union[str, None]" = None, can_fail: bool = False) -> str: """ Wraps `subprocess.Popen.communicate()` and logs the command being executed, sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout @@ -295,6 +351,7 @@ def run(cmds, cwd=None, can_fail=False): return stdout.strip() + if platform.system() == "Darwin": if run(["sw_vers", "-productVersion"]) >= "11.": # Apparently not supported @@ -312,34 +369,59 @@ BOOST_LOCATION = f"https://github.com/boostorg/boost/releases/download/boost-{BO # Helper functions -def run_autoconf(arg1, configure_args, cwd): +def run_autoconf(arg1: str, configure_args: "list[str]", cwd: str) -> None: configure_path = os.path.realpath(os.path.join(cwd, "..", "configure")) if not os.path.exists(configure_path): - run([bash, "./autogen.sh"], cwd=os.path.realpath(os.path.join(cwd, ".."))) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things - # Using `sh` over `bash` fixes issues with building swig + run( + [bash, "./autogen.sh"], cwd=os.path.realpath(os.path.join(cwd, "..")) + ) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things + # Using `sh` over `bash` fixes issues with building swig prefix = os.path.realpath(f"{DEPS_DIR}/install/{arg1}") wasm = [] if "wasm" in flags: wasm.append("emconfigure") - run([*wasm, "/bin/sh", "../configure"] + configure_args + [f"--prefix={prefix}"], cwd=cwd) + run( + [ + *wasm, + "/bin/sh", + "../configure", + *(["--host=wasm32"] if "wasm" in flags and not any(s.startswith("--host") for s in configure_args) else []), + *configure_args, + f"--prefix={prefix}", + ], + cwd=cwd, + ) -def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None): +def run_cmake(arg1, cmake_args: "list[str]", cmake_dir: Union[str, None] = None, cwd: Union[str, None] = None): if cmake_dir is None: P = ".." else: P = cmake_dir - + wasm = [] if "wasm" in flags: wasm.append("emcmake") - - run([*wasm, "cmake", P, *cmake_args, f"-DCMAKE_BUILD_TYPE={BUILD_CFG}", f"-DBUILD_SHARED_LIBS={OFF_ON[not BUILD_STATIC]}"], cwd=cwd) + + run( + [ + *wasm, + "cmake", + P, + *cmake_args, + f"-DCMAKE_BUILD_TYPE={BUILD_CFG}", + f"-DBUILD_SHARED_LIBS={OFF_ON[not BUILD_STATIC]}", + f"-DCMAKE_CXX_FLAGS='{os.environ['CXXFLAGS']}'", + f"-DCMAKE_C_FLAGS='{os.environ['CFLAGS']}'", + f"-DCMAKE_SHARED_LINKER_FLAGS={os.environ['LDFLAGS']}", + ], + cwd=cwd, + ) -def git_clone_or_pull_repository(clone_url, target_dir, revision=None): +def git_clone_or_pull_repository(clone_url: str, target_dir: str, revision: Union[str, None] = None) -> None: """Lazily clones the `git` repository denoted by `clone_url` into the `target_dir` or pulls latest changes if the `target_dir` exists (naively assumes that a working clone exists there) and optionally checks out a revision @@ -362,11 +444,35 @@ def git_clone_or_pull_repository(clone_url, target_dir, revision=None): run([git, "checkout", revision], cwd=target_dir) -def build_dependency(name, mode, build_tool_args, download_url, download_name, download_tool=download_tool_default, revision=None, patch=None, additional_files={}, no_append_name=False, **kwargs): +def build_dependency( + name: str, + mode: Literal[ + "cmake", + "autoconf", + "ctest", + "bjam", + ], + build_tool_args: "list[str]", + download_url: str, + download_name: str, + download_tool: Literal["py", "git"] = download_tool_default, + revision: "Union[str, None]" = None, + patch: "Union[str, list[str], None]" = None, + shell=None, + pre_compile_subs: "Sequence[tuple[str, str, str]]" = (), + additional_files: "Union[dict[str, str], None]" = None, + no_append_name=False, + **kwargs, +) -> None: """Handles building of dependencies with different tools (which are distinguished with the `mode` argument. `build_tool_args` is expected to be a list which is necessary in order to not mess up quoting of compiler and - linker flags.""" + linker flags. + + :param pre_compile_subs: A sequence of ``(fn, before, after)`` + :param additional_files: Mapping path->url. + :param kwargs: Additional ``mode`` related kwargs. + """ check_dir = os.path.join(DEPS_DIR, "install", name) if os.path.exists(check_dir): logger.info(f"Found existing {name}, skipping") @@ -374,27 +480,36 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d build_dir = os.path.join(DEPS_DIR, "build") if not os.path.exists(build_dir): os.makedirs(build_dir) - + logger.info(f"\rFetching {name}... ") - + if download_tool == download_tool_py: if no_append_name: url = download_url else: url = os.path.join(download_url, download_name) - + download_path = os.path.join(build_dir, download_name) if not os.path.exists(download_path): - urlretrieve(url, os.path.join(build_dir, download_path)) + for _ in range(3): + try: + urlretrieve(url, os.path.join(build_dir, download_path)) + break + except ConnectionError as e: + print(e, "... retrying...") + time.sleep(30.0) + continue else: - logger.info(f"Download '{download_path}' already exists, assuming it's an undamaged download and that it has been extracted if possible, skipping") + logger.info( + f"Download '{download_path}' already exists, assuming it's an undamaged download and that it has been extracted if possible, skipping" + ) elif download_tool == download_tool_git: logger.info(f"\rChecking {name}... ") git_clone_or_pull_repository(download_url, target_dir=os.path.join(build_dir, download_name), revision=revision) else: raise ValueError(f"download tool '{download_tool}' is not supported") download_dir = os.path.join(build_dir, download_name) - + if os.path.isdir(download_dir): extract_dir_name = download_name extract_dir = os.path.join(build_dir, extract_dir_name) @@ -404,39 +519,51 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d compr = "gz" elif download_name.endswith(".tar.bz2"): compr = "bz2" + elif download_name.endswith(".tar.xz"): + compr = "xz" else: raise RuntimeError("fix source for new download type") download_tarfile = tarfile.open(name=download_tarfile_path, mode=f"r:{compr}") # tarfile seriously doesn't have a function to retrieve the root directory more easily extract_dir_name = os.path.commonprefix([x for x in download_tarfile.getnames() if x != "."]) - #run([tar, "--exclude=\"*/*\"", "-tf", download_name], cwd=build_dir).strip() no longer works + # run([tar, "--exclude=\"*/*\"", "-tf", download_name], cwd=build_dir).strip() no longer works if extract_dir_name is None: - extract_dir_name = run([bash, "-c", f"tar -tf {download_name} 2> /dev/null | head -n 1 | cut -f1 -d /"], cwd=build_dir) + extract_dir_name = run( + [bash, "-c", f"tar -tf {download_name} 2> /dev/null | head -n 1 | cut -f1 -d /"], cwd=build_dir + ) extract_dir = os.path.join(build_dir, extract_dir_name) if not os.path.exists(extract_dir): run([tar, "-xf", download_name], cwd=build_dir) - - for path, url in additional_files.items(): - if not os.path.exists(path): - urlretrieve(url, os.path.join(extract_dir, path)) - + + if additional_files: + for path, url in additional_files.items(): + if not os.path.exists(path): + urlretrieve(url, os.path.join(extract_dir, path)) + if patch is not None: if isinstance(patch, str): patch = [patch] for p in patch: patch_abs = os.path.abspath(os.path.join(os.path.dirname(__file__), p)) if os.path.exists(patch_abs): - try: run(["patch", "-p1", "--batch", "--forward", "-i", patch_abs], cwd=extract_dir) + try: + run(["patch", "-p1", "--batch", "--forward", "-i", patch_abs], cwd=extract_dir) except Exception as e: # Assert that the patch has already been applied run(["patch", "-p1", "--batch", "--reverse", "--dry-run", "-i", patch_abs], cwd=extract_dir) - + + if shell is not None: + sp.run(shell, shell=True, check=True, cwd=extract_dir) + if mode == "ctest": - run(["ctest", "-S", "HDF5config.cmake,BUILD_GENERATOR=Unix", "-C", BUILD_CFG, "-V", "-O", "hdf5.log"], cwd=extract_dir) - run([tar, "-xf", kwargs["ctest_result"] + ".tar.gz"], cwd=os.path.join(extract_dir, 'build')) + run( + ["ctest", "-S", "HDF5config.cmake,BUILD_GENERATOR=Unix", "-C", BUILD_CFG, "-V", "-O", "hdf5.log"], + cwd=extract_dir, + ) + run([tar, "-xf", kwargs["ctest_result"] + ".tar.gz"], cwd=os.path.join(extract_dir, "build")) shutil.copytree( os.path.join(extract_dir, "build", kwargs["ctest_result"], kwargs["ctest_result_path"]), - os.path.join(DEPS_DIR, "install", name) + os.path.join(DEPS_DIR, "install", name), ) elif mode != "bjam": extract_build_dir = os.path.join(extract_dir, "build") @@ -451,23 +578,32 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d run_cmake(name, build_tool_args, cwd=extract_build_dir) else: raise ValueError() + for fn, before, after in pre_compile_subs: + with open(os.path.join(extract_dir, fn), "r") as f: + s = f.read() + s = s.replace(before, after) + with open(os.path.join(extract_dir, fn), "w") as f: + f.write(s) logger.info(f"\rBuilding {name}... ") run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "VERBOSE=1"], cwd=extract_build_dir) logger.info(f"\rInstalling {name}... ") run([make, "install"], cwd=extract_build_dir) logger.info(f"\rInstalled {name} \n") - else: + else: # bjam logger.info(f"\rConfiguring {name}...") run([bash, "./bootstrap.sh"], cwd=extract_dir) logger.info(f"\rBuilding {name}... ") run(["./b2", f"-j{IFCOS_NUM_BUILD_PROCS}"] + build_tool_args, cwd=extract_dir, can_fail="wasm" in flags) logger.info(f"\rInstalling {name}... ") - shutil.copytree(os.path.join(extract_dir, "boost"), os.path.join(DEPS_DIR, "install", f"boost-{BOOST_VERSION}", "boost")) + shutil.copytree( + os.path.join(extract_dir, "boost"), os.path.join(DEPS_DIR, "install", f"boost-{BOOST_VERSION}", "boost") + ) logger.info(f"\rInstalled {name} \n") if "diskcleanup" in flags: shutil.rmtree(build_dir, ignore_errors=True) + cecho("Collecting dependencies:", GREEN) # Set compiler flags for 32bit builds on 64bit system @@ -478,9 +614,6 @@ ADDITIONAL_ARGS = [] if platform.system() == "Darwin": ADDITIONAL_ARGS = [f"-mmacosx-version-min={TOOLSET}"] + ADDITIONAL_ARGS -if "wasm" in flags: - ADDITIONAL_ARGS.extend(("-sWASM_BIGINT", "-fwasm-exceptions")) - # If the linker supports GC sections, set it up to reduce binary file size # -fPIC is required for the shared libraries to work @@ -491,7 +624,11 @@ CFLAGS = os.environ.get("CFLAGS", "") LDFLAGS = os.environ.get("LDFLAGS", "") ADDITIONAL_ARGS_STR = " ".join(ADDITIONAL_ARGS) -if "wasm" not in flags and sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev/null"]) != 0: + +if "wasm" in flags: + CFLAGS_MINIMAL = CXXFLAGS_MINIMAL = CFLAGS = CXXFLAGS = os.environ["SIDE_MODULE_CFLAGS"] + LDFLAGS = os.environ["SIDE_MODULE_LDFLAGS"] +elif sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev/null"]) != 0: CXXFLAGS_MINIMAL = f"{CXXFLAGS} {PIC} {ADDITIONAL_ARGS_STR}" CFLAGS_MINIMAL = f"{CFLAGS} {PIC} {ADDITIONAL_ARGS_STR}" if BUILD_STATIC: @@ -508,8 +645,8 @@ else: CXXFLAGS = f"{CXXFLAGS} {PIC} -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}" CFLAGS = f"{CFLAGS} {PIC} -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}" else: - CXXFLAGS=CXXFLAGS_MINIMAL - CFLAGS=CFLAGS_MINIMAL + CXXFLAGS = CXXFLAGS_MINIMAL + CFLAGS = CFLAGS_MINIMAL LDFLAGS = f"{LDFLAGS} {ADDITIONAL_ARGS_STR}" if "lto" in flags: @@ -517,6 +654,7 @@ if "lto" in flags: locals()[f] += f" -flto={IFCOS_NUM_BUILD_PROCS}" os.environ["CXXFLAGS"] = CXXFLAGS +os.environ["CPPFLAGS"] = CXXFLAGS os.environ["CFLAGS"] = CFLAGS os.environ["LDFLAGS"] = LDFLAGS @@ -524,7 +662,7 @@ os.environ["LDFLAGS"] = LDFLAGS # @tfk: this is no longer needed # build_dependency(name="cmake-%s" % (CMAKE_VERSION,), mode="autoconf", build_tool_args=[], download_url="https://cmake.org/files/v%s" % (CMAKE_VERSION_2,), download_name="cmake-%s.tar.gz" % (CMAKE_VERSION,)) -if 'hdf5' in targets: +if "hdf5" in targets: # not supported orig = [os.environ[f] for f in compiler_flags] for f in compiler_flags: @@ -538,12 +676,12 @@ if 'hdf5' in targets: download_url=f"https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-{HDF5_MAJOR}/hdf5-{HDF5_VERSION}/src/", download_name=f"CMake-hdf5-{HDF5_VERSION}.tar.gz", ctest_result=f"HDF5-{HDF5_VERSION}-{platform.system()}", - ctest_result_path=f"HDF_Group/HDF5/{HDF5_VERSION}" + ctest_result_path=f"HDF_Group/HDF5/{HDF5_VERSION}", ) - + for f, o in zip(compiler_flags, orig): os.environ[f] = o - + if "json" in targets: json_url = f"https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp" @@ -552,9 +690,11 @@ if "json" in targets: os.makedirs(os.path.dirname(json_install_path)) if not os.path.exists(json_install_path): urlretrieve(json_url, json_install_path) - + if "eigen" in targets: - git_clone_or_pull_repository("https://gitlab.com/libeigen/eigen.git", f"{DEPS_DIR}/install/eigen-3.3.9", revision="3.3.9") + git_clone_or_pull_repository( + "https://gitlab.com/libeigen/eigen.git", f"{DEPS_DIR}/install/eigen-3.3.9", revision="3.3.9" + ) if "pcre" in targets: build_dependency( @@ -562,7 +702,7 @@ if "pcre" in targets: mode="autoconf", build_tool_args=[DISABLE_FLAG], download_url=f"https://downloads.sourceforge.net/project/pcre/pcre/{PCRE_VERSION}/", - download_name=f"pcre-{PCRE_VERSION}.tar.bz2" + download_name=f"pcre-{PCRE_VERSION}.tar.bz2", ) # An issue exists with swig-1.3 and python >= 3.2 @@ -575,36 +715,34 @@ if "swig" in targets: download_url="https://github.com/swig/swig.git", download_name="swig", download_tool=download_tool_git, - revision=f"rel-{SWIG_VERSION}" + revision=f"rel-{SWIG_VERSION}", ) - + if "freetype" in targets: build_dependency( name=f"freetype", mode="cmake", - build_tool_args=[ - f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/freetype" - ], - download_url = "https://github.com/freetype/freetype", - download_name = "freetype2", + build_tool_args=[f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/freetype"], + download_url="https://github.com/freetype/freetype", + download_name="freetype2", download_tool=download_tool_git, - revision="VER-2-11-1" + revision="VER-2-11-1", ) if USE_OCCT and "occ" in targets: patches = [] if OCCT_VERSION < "7.4": patches.append("./patches/occt/enable-exception-handling.patch") - + if OCCT_VERSION == "7.7.1": patches.append("./patches/occt/no_ExpToCasExe.patch") - + if OCCT_VERSION == "7.7.2": patches.append("./patches/occt/no_ExpToCasExe_7_7_2.patch") if OCCT_VERSION == "7.8.1": patches.append("./patches/occt/no_ExpToCasExe_7_8_1.patch") - + if "wasm" in flags: patches.append("./patches/occt/no_em_js.patch") @@ -616,13 +754,13 @@ if USE_OCCT and "occ" in targets: f"-DBUILD_LIBRARY_TYPE={LINK_TYPE_UCFIRST}", "-DBUILD_MODULE_Draw=0", "-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off", - f"-D3RDPARTY_FREETYPE_DIR={DEPS_DIR}/install/freetype" + f"-D3RDPARTY_FREETYPE_DIR={DEPS_DIR}/install/freetype", ], - download_url = "https://github.com/Open-Cascade-SAS/OCCT", - download_name = "occt", + download_url="https://github.com/Open-Cascade-SAS/OCCT", + download_name="occt", download_tool=download_tool_git, patch=patches, - revision="V" + OCCT_VERSION.replace('.', '_') + revision="V" + OCCT_VERSION.replace(".", "_"), ) elif "occ" in targets: build_dependency( @@ -635,12 +773,12 @@ elif "occ" in targets: "-DOCE_DISABLE_X11=ON", "-DOCE_VISUALISATION=OFF", "-DOCE_OCAF=OFF", - f"-DOCE_INSTALL_PREFIX={DEPS_DIR}/install/oce-{OCE_VERSION}" + f"-DOCE_INSTALL_PREFIX={DEPS_DIR}/install/oce-{OCE_VERSION}", ], download_url="https://github.com/tpaviot/oce/archive/", - download_name=f"OCE-{OCE_VERSION}.tar.gz" + download_name=f"OCE-{OCE_VERSION}.tar.gz", ) - + if "libxml2" in targets: build_dependency( f"libxml2-{LIBXML2_VERSION}", @@ -651,12 +789,12 @@ if "libxml2" in targets: DISABLE_FLAG, "--without-zlib", "--without-iconv", - "--without-lzma" + "--without-lzma", ], - download_url="ftp://xmlsoft.org/libxml2/", - download_name=f"libxml2-{LIBXML2_VERSION}.tar.gz" + download_url=f"https://download.gnome.org/sources/libxml2/{'.'.join(LIBXML2_VERSION.split('.')[0:2])}/", + download_name=f"libxml2-{LIBXML2_VERSION}.tar.xz", ) - + if "OpenCOLLADA" in targets: patches = ["./patches/opencollada/pr622_and_disable_subdirs.patch"] @@ -676,25 +814,27 @@ if "OpenCOLLADA" in targets: f"-DPCRE_INCLUDE_DIR={DEPS_DIR}/install/pcre-{PCRE_VERSION}/include", f"-DPCRE_PCREPOSIX_LIBRARY={DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib/libpcreposix.{LIBRARY_EXT}", f"-DPCRE_PCRE_LIBRARY={DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib/libpcre.{LIBRARY_EXT}", - f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/OpenCOLLADA/" + f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/OpenCOLLADA/", ], download_url="https://github.com/KhronosGroup/OpenCOLLADA.git", download_name="OpenCOLLADA", download_tool=download_tool_git, patch=patches, - revision=OPENCOLLADA_VERSION + revision=OPENCOLLADA_VERSION, ) if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flags: # Python should not be built with -fvisibility=hidden, from experience that introduces segfaults + OLD_CPP_FLAGS = os.environ["CPPFLAGS"] OLD_CXX_FLAGS = os.environ["CXXFLAGS"] OLD_C_FLAGS = os.environ["CFLAGS"] os.environ["CXXFLAGS"] = CXXFLAGS_MINIMAL + os.environ["CPPFLAGS"] = CXXFLAGS_MINIMAL os.environ["CFLAGS"] = CFLAGS_MINIMAL # On OSX a dynamic python library is built or it would not be compatible # with the system python because of some threading initialization - PYTHON_CONFIGURE_ARGS = [] + PYTHON_CONFIGURE_ARGS: "list[str]" = [] if platform.system() == "Darwin": PYTHON_CONFIGURE_ARGS = ["--enable-shared"] @@ -705,7 +845,7 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag "autoconf", PYTHON_CONFIGURE_ARGS, f"http://www.python.org/ftp/python/{PYTHON_VERSION}/", - f"Python-{PYTHON_VERSION}.tgz" + f"Python-{PYTHON_VERSION}.tgz", ) except RuntimeError as e: # Sometimes setting up modules such as pip/lzma can cause @@ -714,11 +854,10 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag # are installed correctly. This is all we need so we catch # the exception and only reraise if a partially successful # install is not detected. - if not os.path.exists( - os.path.join(DEPS_DIR, "install", f"python-{PYTHON_VERSION}") - ): + if not os.path.exists(os.path.join(DEPS_DIR, "install", f"python-{PYTHON_VERSION}")): raise e + os.environ["CPPFLAGS"] = OLD_CPP_FLAGS os.environ["CXXFLAGS"] = OLD_CXX_FLAGS os.environ["CFLAGS"] = OLD_C_FLAGS @@ -741,21 +880,27 @@ if "boost" in targets: "--with-filesystem", f"link={LINK_TYPE}", *toolset, - *map(str_concat("cxxflags"), CXXFLAGS.strip().split(' ')), - *map(str_concat("linkflags"), LDFLAGS.strip().split(' ')), - "stage", "-s", "NO_BZIP2=1"], + *map(str_concat("cxxflags"), CXXFLAGS.strip().split(" ")), + *map(str_concat("linkflags"), LDFLAGS.strip().split(" ")), + "stage", + "-s", + "NO_BZIP2=1", + ], download_url=BOOST_LOCATION, # don't remember what this is, but fail on 1.86 # patch="./patches/boost/boostorg_regex_62.patch", - download_name=f"boost-{BOOST_VERSION}-b2-nodocs.tar.gz" + download_name=f"boost-{BOOST_VERSION}-b2-nodocs.tar.gz", ) if "wasm" in flags: # only supported on nix for now - run(("find", ".", "-name", "*.bc", "-exec", "bash", "-c", "emar q ${1%.bc}.a $1", "bash", "{}", ";"), cwd=f"{DEPS_DIR}/install/boost-{BOOST_VERSION}/lib") - + run( + ("find", ".", "-name", "*.bc", "-exec", "bash", "-c", "emar q ${1%.bc}.a $1", "bash", "{}", ";"), + cwd=f"{DEPS_DIR}/install/boost-{BOOST_VERSION}/lib", + ) + if "cgal" in targets: - gmp_args = [] - mpfr_args = [] + gmp_args: "list[str]" = [] + mpfr_args: "list[str]" = [] if "wasm" in flags: gmp_args.extend(("--disable-assembly", "--host", "none", "--enable-cxx")) mpfr_args.extend(("--host", "none")) @@ -764,49 +909,49 @@ if "cgal" in targets: name=f"gmp-{GMP_VERSION}", mode="autoconf", build_tool_args=[ENABLE_FLAG, DISABLE_FLAG, "--with-pic", *gmp_args], + pre_compile_subs=( + [("build/config.h", "HAVE_OBSTACK_VPRINTF 1", "HAVE_OBSTACK_VPRINTF 0")] if "wasm" in flags else [] + ), download_url="https://ftp.gnu.org/gnu/gmp/", - download_name=f"gmp-{GMP_VERSION}.tar.bz2" + download_name=f"gmp-{GMP_VERSION}.tar.bz2", ) - + build_dependency( name=f"mpfr-{MPFR_VERSION}", mode="autoconf", build_tool_args=[ENABLE_FLAG, DISABLE_FLAG, *mpfr_args, f"--with-gmp={DEPS_DIR}/install/gmp-{GMP_VERSION}"], download_url=f"http://www.mpfr.org/mpfr-{MPFR_VERSION}/", - download_name=f"mpfr-{MPFR_VERSION}.tar.bz2" + download_name=f"mpfr-{MPFR_VERSION}.tar.bz2", ) - + build_dependency( name=f"cgal-{CGAL_VERSION}", mode="cmake", build_tool_args=[ f"-DGMP_LIBRARIES={DEPS_DIR}/install/gmp-{GMP_VERSION}/lib/libgmp.{LIBRARY_EXT}", f"-DGMP_INCLUDE_DIR={DEPS_DIR}/install/gmp-{GMP_VERSION}/include", - f"-DMPFR_LIBRARIES={DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib/libmpfr.{LIBRARY_EXT}" , + f"-DMPFR_LIBRARIES={DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib/libmpfr.{LIBRARY_EXT}", f"-DMPFR_INCLUDE_DIR={DEPS_DIR}/install/mpfr-{MPFR_VERSION}/include", f"-DBoost_INCLUDE_DIR={DEPS_DIR}/install/boost-{BOOST_VERSION}", f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/cgal-{CGAL_VERSION}/", - "-DCGAL_HEADER_ONLY=On", - "-DBUILD_SHARED_LIBS=Off" + "-DCGAL_HEADER_ONLY=On", + "-DBUILD_SHARED_LIBS=Off", ], download_url="https://github.com/CGAL/cgal.git", download_name="cgal", download_tool=download_tool_git, - revision=f"v{CGAL_VERSION}" + revision=CGAL_VERSION, ) if "usd" in targets: build_dependency( name=f"oneTBB-{TBB_VERSION}", mode="cmake", - build_tool_args=[ - f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/tbb-{TBB_VERSION}", - f"-DTBB_TEST=OFF" - ], + build_tool_args=[f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/tbb-{TBB_VERSION}", f"-DTBB_TEST=OFF"], download_url="https://github.com/oneapi-src/oneTBB", download_name="oneTBB", download_tool=download_tool_git, - revision=f"v{TBB_VERSION}" + revision=f"v{TBB_VERSION}", ) build_dependency( @@ -822,12 +967,12 @@ if "usd" in targets: f"-DPXR_BUILD_TUTORIALS=FALSE", f"-DPXR_BUILD_EXAMPLES=FALSE", f"-DPXR_BUILD_USD_TOOLS=FALSE", - f"-DPXR_BUILD_TESTS=FALSE" + f"-DPXR_BUILD_TESTS=FALSE", ], download_url="https://github.com/PixarAnimationStudios/USD", download_name="USD", download_tool=download_tool_git, - revision=f"v{USD_VERSION}" + revision=f"v{USD_VERSION}", ) cecho("Building IfcOpenShell:", GREEN) @@ -842,17 +987,19 @@ os.makedirs(executables_dir, exist_ok=True) cmake_args = [ - "-DUSE_MMAP=" "OFF", - "-DBUILD_EXAMPLES=" "OFF", - "-DBUILD_SHARED_LIBS=" +OFF_ON[not BUILD_STATIC], - "-DBOOST_ROOT=" f"{DEPS_DIR}/install/boost-{BOOST_VERSION}", - "-DGLTF_SUPPORT=" "ON", - "-DJSON_INCLUDE_DIR=" f"{DEPS_DIR}/install/json", - "-DEIGEN_DIR=" f"{DEPS_DIR}/install/eigen-3.3.9", - "-DBoost_NO_BOOST_CMAKE=" "On", - "-DADD_COMMIT_SHA=" +("On" if ADD_COMMIT_SHA else "Off"), - "-DVERSION_OVERRIDE=" +("On" if ADD_COMMIT_SHA else "Off") + "-DCMAKE_CXX_STANDARD=17", + "-DUSE_MMAP=OFF", + "-DBUILD_EXAMPLES=OFF", + "-DBUILD_SHARED_LIBS=" + OFF_ON[not BUILD_STATIC], + f"-DBOOST_ROOT={DEPS_DIR}/install/boost-{BOOST_VERSION}", + "-DGLTF_SUPPORT=ON", + f"-DJSON_INCLUDE_DIR={DEPS_DIR}/install/json", + f"-DEIGEN_DIR={DEPS_DIR}/install/eigen-3.3.9", + "-DBoost_NO_BOOST_CMAKE=On", + "-DADD_COMMIT_SHA=" + ("On" if ADD_COMMIT_SHA else "Off"), + "-DVERSION_OVERRIDE=" + ("On" if ADD_COMMIT_SHA else "Off"), ] +"""Default CMake args to use for all CMake configs.""" if "wasm" in flags: # Boost is built by the build script so should not be found @@ -860,77 +1007,81 @@ if "wasm" in flags: cmake_args.append("-DWASM_BUILD=On") if "cgal" in targets: - cmake_args.extend([ - "-DCGAL_INCLUDE_DIR=" f"{DEPS_DIR}/install/cgal-{CGAL_VERSION}/include", - "-DGMP_INCLUDE_DIR=" f"{DEPS_DIR}/install/gmp-{GMP_VERSION}/include", - "-DGMP_LIBRARY_DIR=" f"{DEPS_DIR}/install/gmp-{GMP_VERSION}/lib", - "-DMPFR_INCLUDE_DIR=" f"{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/include", - "-DMPFR_LIBRARY_DIR=" f"{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib", - ]) - -if "occ" in targets and USE_OCCT: - occ_include_dir = f"{DEPS_DIR}/install/occt-{OCCT_VERSION}/include/opencascade" - occ_library_dir = f"{DEPS_DIR}/install/occt-{OCCT_VERSION}/lib" - cmake_args.extend([ - "-DOCC_INCLUDE_DIR=" +occ_include_dir, - "-DOCC_LIBRARY_DIR=" +occ_library_dir - ]) - -elif "occ" in targets: - occ_include_dir = f"{DEPS_DIR}/install/oce-{OCE_VERSION}/include/oce" - occ_library_dir = f"{DEPS_DIR}/install/oce-{OCE_VERSION}/lib" - cmake_args.extend([ - "-DOCC_INCLUDE_DIR=" +occ_include_dir, - "-DOCC_LIBRARY_DIR=" +occ_library_dir - ]) - -if "OpenCOLLADA" in targets: - cmake_args.extend([ - "-DOPENCOLLADA_INCLUDE_DIR=" f"{DEPS_DIR}/install/OpenCOLLADA/include/opencollada", - "-DOPENCOLLADA_LIBRARY_DIR=" f"{DEPS_DIR}/install/OpenCOLLADA/lib/opencollada" - ]) -else: - cmake_args.extend([ - "-DCOLLADA_SUPPORT=" "Off", - ]) - -if "pcre" in targets: - cmake_args.append( - "-DPCRE_LIBRARY_DIR=" f"{DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib" + cmake_args.extend( + [ + "-DCGAL_INCLUDE_DIR=" f"{DEPS_DIR}/install/cgal-{CGAL_VERSION}/include", + "-DGMP_INCLUDE_DIR=" f"{DEPS_DIR}/install/gmp-{GMP_VERSION}/include", + "-DGMP_LIBRARY_DIR=" f"{DEPS_DIR}/install/gmp-{GMP_VERSION}/lib", + "-DMPFR_INCLUDE_DIR=" f"{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/include", + "-DMPFR_LIBRARY_DIR=" f"{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib", + ] ) +if "occ" in targets and USE_OCCT: + occ_include_dir = f"{DEPS_DIR}/install/occt-{OCCT_VERSION}/include/opencascade" + occ_library_dir = f"{DEPS_DIR}/install/occt-{OCCT_VERSION}/lib" + cmake_args.extend(["-DOCC_INCLUDE_DIR=" + occ_include_dir, "-DOCC_LIBRARY_DIR=" + occ_library_dir]) + +elif "occ" in targets: + occ_include_dir = f"{DEPS_DIR}/install/oce-{OCE_VERSION}/include/oce" + occ_library_dir = f"{DEPS_DIR}/install/oce-{OCE_VERSION}/lib" + cmake_args.extend(["-DOCC_INCLUDE_DIR=" + occ_include_dir, "-DOCC_LIBRARY_DIR=" + occ_library_dir]) + +if "OpenCOLLADA" in targets: + cmake_args.extend( + [ + "-DOPENCOLLADA_INCLUDE_DIR=" f"{DEPS_DIR}/install/OpenCOLLADA/include/opencollada", + "-DOPENCOLLADA_LIBRARY_DIR=" f"{DEPS_DIR}/install/OpenCOLLADA/lib/opencollada", + ] + ) +else: + cmake_args.extend( + [ + "-DCOLLADA_SUPPORT=" "Off", + ] + ) + +if "pcre" in targets: + cmake_args.append("-DPCRE_LIBRARY_DIR=" f"{DEPS_DIR}/install/pcre-{PCRE_VERSION}/lib") + if "libxml2" in targets: - cmake_args.extend([ - "-DLIBXML2_INCLUDE_DIR=" f"{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2", - "-DLIBXML2_LIBRARIES=" f"{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}" - ]) + cmake_args.extend( + [ + "-DLIBXML2_INCLUDE_DIR=" f"{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2", + "-DLIBXML2_LIBRARIES=" f"{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}", + ] + ) if "hdf5" in targets: - cmake_args.extend([ - "-DHDF5_INCLUDE_DIR=" f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/include", - "-DHDF5_LIBRARY_DIR=" f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/lib" - ]) + cmake_args.extend( + [ + "-DHDF5_INCLUDE_DIR=" f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/include", + "-DHDF5_LIBRARY_DIR=" f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/lib", + ] + ) else: cmake_args.append("-DHDF5_SUPPORT=Off") if "usd" in targets: - cmake_args.extend([ - "-DUSD_SUPPORT=" "On", - "-DUSD_INCLUDE_DIR=" f"{DEPS_DIR}/install/usd-{USD_VERSION}/include", - "-DUSD_LIBRARY_DIR=" f"{DEPS_DIR}/install/usd-{USD_VERSION}/lib" - ]) + cmake_args.extend( + [ + "-DUSD_SUPPORT=" "On", + "-DUSD_INCLUDE_DIR=" f"{DEPS_DIR}/install/usd-{USD_VERSION}/include", + "-DUSD_LIBRARY_DIR=" f"{DEPS_DIR}/install/usd-{USD_VERSION}/lib", + ] + ) if not explicit_targets or {"IfcGeom", "IfcConvert", "IfcGeomServer"} & set(explicit_targets): logger.info("\rConfiguring executables...") exec_args = [ - "-DBUILD_IFCGEOM=" +OFF_ON["IfcGeom" in targets], - "-DBUILD_GEOMSERVER=" +OFF_ON["IfcGeomServer" in targets], - "-DBUILD_CONVERT=" +OFF_ON["IfcConvert" in targets], - "-DBUILD_IFCPYTHON=" "OFF", - "-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell", + "-DBUILD_IFCGEOM=" + OFF_ON["IfcGeom" in targets], + "-DBUILD_GEOMSERVER=" + OFF_ON["IfcGeomServer" in targets], + "-DBUILD_CONVERT=" + OFF_ON["IfcConvert" in targets], + "-DBUILD_IFCPYTHON=" "OFF", + "-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell", ] - + run_cmake("", exec_args + cmake_args, cmake_dir=CMAKE_DIR, cwd=executables_dir) logger.info("\rBuilding executables... ") @@ -943,10 +1094,11 @@ if "IfcOpenShell-Python" in targets: ADDITIONAL_ARGS = "" if platform.system() == "Darwin": ADDITIONAL_ARGS = "-Wl,-flat_namespace,-undefined,suppress" - + if "wasm" in flags: ADDITIONAL_ARGS = f"-Wl,-undefined,suppress -sSIDE_MODULE=2 -sEXPORTED_FUNCTIONS=_PyInit__ifcopenshell_wrapper" - + + os.environ["CPPFLAGS"] = f"{CXXFLAGS_MINIMAL} {ADDITIONAL_ARGS}" os.environ["CXXFLAGS"] = f"{CXXFLAGS_MINIMAL} {ADDITIONAL_ARGS}" os.environ["CFLAGS"] = f"{CFLAGS_MINIMAL} {ADDITIONAL_ARGS}" os.environ["LDFLAGS"] = f"{LDFLAGS} {ADDITIONAL_ARGS}" @@ -954,7 +1106,12 @@ if "IfcOpenShell-Python" in targets: python_dir = os.path.join(IFCOS_DIR, "pythonwrapper") os.makedirs(python_dir, exist_ok=True) - def compile_python_wrapper(python_version, python_library, python_include, python_executable): + def compile_python_wrapper( + python_version: str, python_library: str, python_include: str, python_executable: Union[str, None] + ) -> Union[str, None]: + """ + :return: Path to module dir if ``python_executable`` was provided, otherwise ``None``. + """ logger.info(f"\rConfiguring python {python_version} wrapper...") cache_path = os.path.join(python_dir, "CMakeCache.txt") @@ -967,17 +1124,30 @@ if "IfcOpenShell-Python" in targets: if "swig" in targets: swig_when_built.append(f"-DSWIG_EXECUTABLE={DEPS_DIR}/install/swig/bin/swig") - run_cmake("", - cmake_args + [ - "-DPYTHON_LIBRARY=" +python_library, + run_cmake( + "", + cmake_args + + [ + "-DPYTHON_LIBRARY=" + python_library, *([f"-DPYTHON_EXECUTABLE={python_executable}"] if python_executable else []), # *([f"-DPYTHON_MODULE_INSTALL_DIR={os.environ['PYTHONPATH']}/ifcopenshell"] if "wasm" in flags else []), - *(["-DPYTHON_MODULE_INSTALL_DIR="+os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "package"))] if "wasm" in flags else []), - "-DPYTHON_INCLUDE_DIR=" +python_include, - "-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell/tmp", - "-DUSERSPACE_PYTHON_PREFIX=" +["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}], - *swig_when_built], - cmake_dir=CMAKE_DIR, cwd=python_dir) + *( + [ + "-DPYTHON_MODULE_INSTALL_DIR=" + + os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "package")) + ] + if "wasm" in flags + else [] + ), + "-DPYTHON_INCLUDE_DIR=" + python_include, + "-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell/tmp", + "-DUSERSPACE_PYTHON_PREFIX=" + + ["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}], + *swig_when_built, + ], + cmake_dir=CMAKE_DIR, + cwd=python_dir, + ) logger.info(f"\rBuilding python {python_version} wrapper... ") @@ -985,14 +1155,16 @@ if "IfcOpenShell-Python" in targets: run([make, "install/local"], cwd=os.path.join(python_dir, "ifcwrap")) if python_executable: - module_dir = os.path.dirname(run([python_executable, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"])) + module_dir = os.path.dirname( + run([python_executable, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"]) + ) if platform.system() != "Darwin": if BUILD_CFG == "Release": # TODO: This symbol name depends on the Python version? so = glob.glob(os.path.join(module_dir, "_ifcopenshell_wrapper*.so"))[0] if "wasm" in flags: - run(['wasm-strip', so, '-k', "dylink.0"]) + run(["wasm-strip", so, "-k", "dylink.0"]) else: run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", so], cwd=module_dir) @@ -1002,20 +1174,17 @@ if "IfcOpenShell-Python" in targets: compile_python_wrapper( f"{os.environ['PYMAJOR']}.{os.environ['PYMINOR']}.{os.environ['PYMICRO']}", f"{os.environ['TARGETINSTALLDIR']}/lib/libpython{os.environ['PYMAJOR']}.{os.environ['PYMINOR']}.a", - os.environ['PYTHONINCLUDE'], - None + os.environ["PYTHONINCLUDE"], + None, ) - + elif USE_CURRENT_PYTHON_VERSION: python_info = sysconfig.get_paths() - py_path_components = [ - sysconfig.get_config_var('LIBDIR'), - sysconfig.get_config_var("INSTSONAME") - ] + py_path_components = [sysconfig.get_config_var("LIBDIR"), sysconfig.get_config_var("INSTSONAME")] - if sysconfig.get_config_var('multiarchsubdir'): - py_path_components.insert(1, sysconfig.get_config_var('multiarchsubdir').replace("/", "")) + if sysconfig.get_config_var("multiarchsubdir"): + py_path_components.insert(1, sysconfig.get_config_var("multiarchsubdir").replace("/", "")) python_lib = os.path.join(*py_path_components) @@ -1024,9 +1193,12 @@ if "IfcOpenShell-Python" in targets: for python_version in PYTHON_VERSIONS: python_library = run([bash, "-c", f"ls {DEPS_DIR}/install/python-{python_version}/lib/libpython*.*"]) python_include = run([bash, "-c", f"ls -d {DEPS_DIR}/install/python-{python_version}/include/python*"]) - python_executable = os.path.join(DEPS_DIR, "install", f"python-{python_version}", "bin", f"python{python_version[0]}") + python_executable = os.path.join( + DEPS_DIR, "install", f"python-{python_version}", "bin", f"python{python_version[0]}" + ) module_dir = compile_python_wrapper(python_version, python_library, python_include, python_executable) + assert module_dir run([cp, "-R", module_dir, os.path.join(DEPS_DIR, "install", "ifcopenshell", f"python-{python_version}")]) logger.info("\rBuilt IfcOpenShell...\n\n") diff --git a/pyodide/meta.yaml b/pyodide/meta.yaml index b3b3be0187..9e8acd5512 100644 --- a/pyodide/meta.yaml +++ b/pyodide/meta.yaml @@ -7,7 +7,7 @@ source: build: script: | - BUILD_CFG=Release python nix/build-all.py --without-hdf5 --without-opencollada --without-swig --without-pcre -v --wasm --py312 IfcOpenShell-Python + BUILD_CFG=Release python nix/build-all.py --without-hdf5 --without-opencollada --without-swig --without-pcre -v --wasm --py313 IfcOpenShell-Python mv package/ifcopenshell . cp pyodide/setup.py . diff --git a/pyproject.toml b/pyproject.toml index 8864121f3b..e3f82db534 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,28 +1,8 @@ [tool.black] line-length = 120 include = ''' - src/( - bcf - |bcfserver - |bonsai - |bsdd - |foundationserver - |ifc2ca - |ifc4d - |ifc5d - |ifcbimtester - |ifcblender - |ifccityjson - |ifcclash - |ifccsv - |ifcdiff - |ifcfm - |ifcpatch - |ifctester - |ifcopenshell-python - |ifcsverchok - |opencdeserver - )/.*.py$ + src/.*.pyi?$ + |nix/.*.pyi?$ ''' extend-exclude = ''' src/ifcopenshell-python/ifcopenshell/express/* @@ -30,9 +10,57 @@ extend-exclude = ''' |src/ifcopenshell-python/ifcopenshell/simple_spf/* |src/ifc2ca/templates/* |src/ifcconvert/cityjson/* + |src/svgfill + |src/exterior-shell-extractor + |src/pyodide ''' [tool.pyright] reportInvalidTypeForm = false disableBytesTypePromotions = true reportUnnecessaryTypeIgnoreComment = true + +# Define here general ruff settings, +# then they will be inherited projects .toml files. +# This allows using assuming different Python version for different projects. +[tool.ruff] +exclude = [ + # Submodules. + "src/ifcconvert/cityjson", + "src/ifcopenshell-python/ifcopenshell/express", + "src/ifcopenshell-python/ifcopenshell/mvd", + "src/ifcopenshell-python/ifcopenshell/simple_spf", + "src/svgfill", + # + # Unformatted. + "src/exterior-shell-extractor", + # Incompatible with linter. + "src/ifc2ca/templates", +] +[tool.ruff.lint] +select = [ + # Default Ruff rules. + # "E4", # imports + # "E7", # statements + "E9", # io errors + # "F", # pyflakes + # + "FA", # future annotations + "UP", # pyupgrade + "RUF015", # next() > list_comprehension[0] + "RUF022", # sort __all__ +] +ignore = [ + "FA100", # Conflicts with Blender using annotations for props definitions. + # Maybe will enable later: + "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 + + # Deprecated Ruff rules. + "UP038", # Use X | Y in isinstance +] diff --git a/src/bcf/bcf/agnostic/extensions.py b/src/bcf/bcf/agnostic/extensions.py index c5d4a7f556..20ecd621d8 100644 --- a/src/bcf/bcf/agnostic/extensions.py +++ b/src/bcf/bcf/agnostic/extensions.py @@ -20,7 +20,7 @@ def get_extensions_attributes(extensions: Extensions) -> dict[str, AttributeData - subattribute name""" possible_attributes = {} for field in fields(type(extensions)): - field_type = field.type.__args__[0] # type: ignore [reportAttributeAccessIssue] + field_type = field.type.__args__[0] # pyright: ignore [reportAttributeAccessIssue] subfield = next(iter(fields(field_type))) xsd_name = subfield.metadata["name"] possible_attributes[field.name] = AttributeData(field_type, subfield.name, xsd_name) diff --git a/src/bcf/bcf/inmemory_zipfile.py b/src/bcf/bcf/inmemory_zipfile.py index 2a4d4117ce..8bcbbc6cf8 100644 --- a/src/bcf/bcf/inmemory_zipfile.py +++ b/src/bcf/bcf/inmemory_zipfile.py @@ -6,6 +6,7 @@ Copyright (c) 2017-2020 Anthon van der Neut, Ruamel bvba original idea from https://stackoverflow.com/a/19722365/1307905 """ +from __future__ import annotations import zipfile from io import BytesIO from os import PathLike @@ -47,7 +48,7 @@ class InMemoryZipFile: def data(self) -> bytes: return self.in_memory_data.getvalue() - def __enter__(self) -> "InMemoryZipFile": + def __enter__(self) -> InMemoryZipFile: return self def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: diff --git a/src/bcf/bcf/v2/bcfxml.py b/src/bcf/bcf/v2/bcfxml.py index b15199f4dc..039f1cbe09 100644 --- a/src/bcf/bcf/v2/bcfxml.py +++ b/src/bcf/bcf/v2/bcfxml.py @@ -1,5 +1,6 @@ """BCF XML V2 handler.""" +from __future__ import annotations import uuid import warnings import zipfile @@ -31,7 +32,7 @@ class BcfXml: self._extension_schema: Optional[bytes] = None self._zip_file = self._load_zip_file() - def __enter__(self) -> "BcfXml": + def __enter__(self) -> BcfXml: return self def __exit__(self, *args: Any) -> None: @@ -99,7 +100,7 @@ class BcfXml: extensions = mdl_extensions.Extensions() xs = "{http://www.w3.org/2001/XMLSchema}" - root = etree.parse(io.BytesIO((self.extension_schema))) + root = etree.parse(io.BytesIO(self.extension_schema)) attrs = bcf.agnostic.extensions.get_extensions_attributes(extensions) xsd_to_attrs = {v.subattr_xsd_name: k for k, v in attrs.items()} @@ -148,7 +149,7 @@ class BcfXml: return topics @classmethod - def load(cls, filename: Path, xml_handler: Optional[AbstractXmlParserSerializer] = None) -> Optional["BcfXml"]: + def load(cls, filename: Path, xml_handler: Optional[AbstractXmlParserSerializer] = None) -> Optional[BcfXml]: """ Create a BcfXml object from a file. @@ -172,7 +173,7 @@ class BcfXml: cls, project_name: Optional[str] = None, xml_handler: Optional[AbstractXmlParserSerializer] = None, - ) -> "BcfXml": + ) -> BcfXml: """ Create a new BcfXml object. @@ -261,7 +262,7 @@ class BcfXml: ) # region Deprecated methods - def new_project(self) -> "BcfXml": + def new_project(self) -> BcfXml: """Deprecated method.""" warnings.warn("new_project is deprecated, use create_new instead.", DeprecationWarning) return self.create_new() diff --git a/src/bcf/bcf/v2/model/__init__.py b/src/bcf/bcf/v2/model/__init__.py index 3bb55fd922..fa66907968 100644 --- a/src/bcf/bcf/v2/model/__init__.py +++ b/src/bcf/bcf/v2/model/__init__.py @@ -36,20 +36,10 @@ from bcf.v2.model.visinfo import ( __all__ = [ "BimSnippet", - "Comment", - "CommentViewpoint", - "Header", - "HeaderFile", - "Markup", - "Topic", - "TopicDocumentReference", - "TopicRelatedTopic", - "ViewPoint", - "Project", - "ProjectExtension", - "Version", "BitmapFormat", "ClippingPlane", + "Comment", + "CommentViewpoint", "Component", "ComponentColoring", "ComponentColoringColor", @@ -58,10 +48,20 @@ __all__ = [ "ComponentVisibilityExceptions", "Components", "Direction", + "Header", + "HeaderFile", "Line", + "Markup", "OrthogonalCamera", "PerspectiveCamera", "Point", + "Project", + "ProjectExtension", + "Topic", + "TopicDocumentReference", + "TopicRelatedTopic", + "Version", + "ViewPoint", "ViewSetupHints", "VisualizationInfo", "VisualizationInfoBitmap", diff --git a/src/bcf/bcf/v2/model/extensions.py b/src/bcf/bcf/v2/model/extensions.py index fb25a47c64..bf28512100 100644 --- a/src/bcf/bcf/v2/model/extensions.py +++ b/src/bcf/bcf/v2/model/extensions.py @@ -14,16 +14,20 @@ # Currently extensions support for v2 is only read-only. +import sys from dataclasses import dataclass, field, fields -from typing import List, NamedTuple, Optional +from typing import Optional -@dataclass(slots=True, kw_only=True) +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} + + +@dataclass(**DATACLASS_KWARGS) class ExtensionsPriorities: class Meta: global_type = False - priority: List[str] = field( + priority: list[str] = field( default_factory=list, metadata={ "name": "Priority", @@ -35,12 +39,12 @@ class ExtensionsPriorities: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsSnippetTypes: class Meta: global_type = False - snippet_type: List[str] = field( + snippet_type: list[str] = field( default_factory=list, metadata={ "name": "SnippetType", @@ -52,12 +56,12 @@ class ExtensionsSnippetTypes: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsStages: class Meta: global_type = False - stage: List[str] = field( + stage: list[str] = field( default_factory=list, metadata={ "name": "Stage", @@ -69,12 +73,12 @@ class ExtensionsStages: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsTopicLabels: class Meta: global_type = False - topic_label: List[str] = field( + topic_label: list[str] = field( default_factory=list, metadata={ "name": "TopicLabel", @@ -86,12 +90,12 @@ class ExtensionsTopicLabels: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsTopicStatuses: class Meta: global_type = False - topic_status: List[str] = field( + topic_status: list[str] = field( default_factory=list, metadata={ "name": "TopicStatus", @@ -103,12 +107,12 @@ class ExtensionsTopicStatuses: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsTopicTypes: class Meta: global_type = False - topic_type: List[str] = field( + topic_type: list[str] = field( default_factory=list, metadata={ "name": "TopicType", @@ -120,12 +124,12 @@ class ExtensionsTopicTypes: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsUsers: class Meta: global_type = False - user: List[str] = field( + user: list[str] = field( default_factory=list, metadata={ "name": "UserIdType", @@ -137,7 +141,7 @@ class ExtensionsUsers: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Extensions: topic_types: Optional[ExtensionsTopicTypes] = field( default=None, diff --git a/src/bcf/bcf/v2/model/markup.py b/src/bcf/bcf/v2/model/markup.py index 0b8fd987aa..f91d8fe8dc 100644 --- a/src/bcf/bcf/v2/model/markup.py +++ b/src/bcf/bcf/v2/model/markup.py @@ -1,10 +1,13 @@ +import sys from dataclasses import dataclass, field -from typing import List, Optional +from typing import Optional from xsdata.models.datatype import XmlDateTime +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} -@dataclass(slots=True, kw_only=True) + +@dataclass(**DATACLASS_KWARGS) class BimSnippet: reference: str = field( metadata={ @@ -38,7 +41,7 @@ class BimSnippet: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class CommentViewpoint: class Meta: global_type = False @@ -53,7 +56,7 @@ class CommentViewpoint: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class HeaderFile: class Meta: global_type = False @@ -109,7 +112,7 @@ class HeaderFile: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicDocumentReference: class Meta: global_type = False @@ -147,7 +150,7 @@ class TopicDocumentReference: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicRelatedTopic: class Meta: global_type = False @@ -162,7 +165,7 @@ class TopicRelatedTopic: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ViewPoint: viewpoint: Optional[str] = field( default=None, @@ -198,7 +201,7 @@ class ViewPoint: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Comment: date: XmlDateTime = field( metadata={ @@ -258,9 +261,9 @@ class Comment: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Header: - file: List[HeaderFile] = field( + file: list[HeaderFile] = field( default_factory=list, metadata={ "name": "File", @@ -271,9 +274,9 @@ class Header: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Topic: - reference_link: List[str] = field( + reference_link: list[str] = field( default_factory=list, metadata={ "name": "ReferenceLink", @@ -305,7 +308,7 @@ class Topic: "namespace": "", }, ) - labels: List[str] = field( + labels: list[str] = field( default_factory=list, metadata={ "name": "Labels", @@ -385,7 +388,7 @@ class Topic: "namespace": "", }, ) - document_reference: List[TopicDocumentReference] = field( + document_reference: list[TopicDocumentReference] = field( default_factory=list, metadata={ "name": "DocumentReference", @@ -393,7 +396,7 @@ class Topic: "namespace": "", }, ) - related_topic: List[TopicRelatedTopic] = field( + related_topic: list[TopicRelatedTopic] = field( default_factory=list, metadata={ "name": "RelatedTopic", @@ -425,7 +428,7 @@ class Topic: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Markup: header: Optional[Header] = field( default=None, @@ -443,7 +446,7 @@ class Markup: "required": True, } ) - comment: List[Comment] = field( + comment: list[Comment] = field( default_factory=list, metadata={ "name": "Comment", @@ -451,7 +454,7 @@ class Markup: "namespace": "", }, ) - viewpoints: List[ViewPoint] = field( + viewpoints: list[ViewPoint] = field( default_factory=list, metadata={ "name": "Viewpoints", diff --git a/src/bcf/bcf/v2/model/project.py b/src/bcf/bcf/v2/model/project.py index e86fb9b009..a27afb7cd6 100644 --- a/src/bcf/bcf/v2/model/project.py +++ b/src/bcf/bcf/v2/model/project.py @@ -1,8 +1,11 @@ +import sys from dataclasses import dataclass, field from typing import Optional +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} -@dataclass(slots=True, kw_only=True) + +@dataclass(**DATACLASS_KWARGS) class Project: name: Optional[str] = field( default=None, @@ -21,7 +24,7 @@ class Project: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ProjectExtension: project: Optional[Project] = field( default=None, diff --git a/src/bcf/bcf/v2/model/version.py b/src/bcf/bcf/v2/model/version.py index 9f55ad5bf9..ea9331bc36 100644 --- a/src/bcf/bcf/v2/model/version.py +++ b/src/bcf/bcf/v2/model/version.py @@ -1,8 +1,11 @@ +import sys from dataclasses import dataclass, field from typing import Optional +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} -@dataclass(slots=True, kw_only=True) + +@dataclass(**DATACLASS_KWARGS) class Version: detailed_version: Optional[str] = field( default=None, diff --git a/src/bcf/bcf/v2/model/visinfo.py b/src/bcf/bcf/v2/model/visinfo.py index e1f13d3a37..3615609881 100644 --- a/src/bcf/bcf/v2/model/visinfo.py +++ b/src/bcf/bcf/v2/model/visinfo.py @@ -1,6 +1,9 @@ +import sys from dataclasses import dataclass, field from enum import Enum -from typing import List, Optional +from typing import Optional + +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} class BitmapFormat(Enum): @@ -8,7 +11,7 @@ class BitmapFormat(Enum): JPG = "JPG" -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Component: originating_system: Optional[str] = field( default=None, @@ -35,7 +38,7 @@ class Component: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Direction: x: float = field( metadata={ @@ -60,7 +63,7 @@ class Direction: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Point: x: float = field( metadata={ @@ -85,7 +88,7 @@ class Point: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ViewSetupHints: spaces_visible: Optional[bool] = field( default=None, @@ -110,7 +113,7 @@ class ViewSetupHints: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ClippingPlane: location: Point = field( metadata={ @@ -128,12 +131,12 @@ class ClippingPlane: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentColoringColor: class Meta: global_type = False - component: List[Component] = field( + component: list[Component] = field( default_factory=list, metadata={ "name": "Component", @@ -151,9 +154,9 @@ class ComponentColoringColor: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentSelection: - component: List[Component] = field( + component: list[Component] = field( default_factory=list, metadata={ "name": "Component", @@ -163,12 +166,12 @@ class ComponentSelection: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentVisibilityExceptions: class Meta: global_type = False - component: List[Component] = field( + component: list[Component] = field( default_factory=list, metadata={ "name": "Component", @@ -178,7 +181,7 @@ class ComponentVisibilityExceptions: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Line: start_point: Point = field( metadata={ @@ -196,7 +199,7 @@ class Line: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class OrthogonalCamera: """ Attributes @@ -236,7 +239,7 @@ class OrthogonalCamera: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class PerspectiveCamera: """ Attributes @@ -281,7 +284,7 @@ class PerspectiveCamera: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class VisualizationInfoBitmap: class Meta: global_type = False @@ -330,9 +333,9 @@ class VisualizationInfoBitmap: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentColoring: - color: List[ComponentColoringColor] = field( + color: list[ComponentColoringColor] = field( default_factory=list, metadata={ "name": "Color", @@ -342,7 +345,7 @@ class ComponentColoring: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentVisibility: exceptions: Optional[ComponentVisibilityExceptions] = field( default=None, @@ -360,12 +363,12 @@ class ComponentVisibility: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class VisualizationInfoClippingPlanes: class Meta: global_type = False - clipping_plane: List[ClippingPlane] = field( + clipping_plane: list[ClippingPlane] = field( default_factory=list, metadata={ "name": "ClippingPlane", @@ -374,12 +377,12 @@ class VisualizationInfoClippingPlanes: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class VisualizationInfoLines: class Meta: global_type = False - line: List[Line] = field( + line: list[Line] = field( default_factory=list, metadata={ "name": "Line", @@ -389,7 +392,7 @@ class VisualizationInfoLines: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Components: view_setup_hints: Optional[ViewSetupHints] = field( default=None, @@ -421,7 +424,7 @@ class Components: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class VisualizationInfo: """ VisualizationInfo documentation. @@ -462,7 +465,7 @@ class VisualizationInfo: "type": "Element", }, ) - bitmap: List[VisualizationInfoBitmap] = field( + bitmap: list[VisualizationInfoBitmap] = field( default_factory=list, metadata={ "name": "Bitmap", diff --git a/src/bcf/bcf/v2/topic.py b/src/bcf/bcf/v2/topic.py index 0a26cbcc93..7ea1c89772 100644 --- a/src/bcf/bcf/v2/topic.py +++ b/src/bcf/bcf/v2/topic.py @@ -1,5 +1,6 @@ """BCF XML V2 Topic handler.""" +from __future__ import annotations import datetime import tempfile import uuid @@ -152,7 +153,7 @@ class TopicHandler: topic_type: str = "", topic_status: str = "", xml_handler: Optional[AbstractXmlParserSerializer] = None, - ) -> "TopicHandler": + ) -> TopicHandler: """ Create a new BCF topic. diff --git a/src/bcf/bcf/v2/visinfo.py b/src/bcf/bcf/v2/visinfo.py index 359fbc0ab9..d0056ec407 100644 --- a/src/bcf/bcf/v2/visinfo.py +++ b/src/bcf/bcf/v2/visinfo.py @@ -1,6 +1,7 @@ import uuid import zipfile -from typing import Any, Iterable, Optional, Literal, Union +from typing import Any, Optional, Literal, Union +from collections.abc import Iterable import numpy as np from ifcopenshell import entity_instance diff --git a/src/bcf/bcf/v3/bcfapi.py b/src/bcf/bcf/v3/bcfapi.py index 7303ed8d7e..5c66cee067 100644 --- a/src/bcf/bcf/v3/bcfapi.py +++ b/src/bcf/bcf/v3/bcfapi.py @@ -21,11 +21,11 @@ import http.server import os import tempfile import time -import urllib +import urllib.parse import uuid import webbrowser from re import A -from typing import Any, Optional, Tuple +from typing import Any, Optional import requests @@ -40,7 +40,7 @@ class OAuthReceiver(http.server.BaseHTTPRequestHandler): self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() - self.wfile.write("You have now authenticated :) You may now close this browser window.".encode("utf-8")) + self.wfile.write(b"You have now authenticated :) You may now close this browser window.") class FoundationClient: @@ -174,7 +174,7 @@ class BcfClient: except requests.exceptions.HTTPError as e: print(f"message: {response.reason}' '{response.status_code}' '{ e }") - def post(self, endpoint: str, data: Any = None, params: Any = None) -> Tuple[int, str]: + def post(self, endpoint: str, data: Any = None, params: Any = None) -> tuple[int, str]: headers = { "Authorization": f"Bearer {self.foundation_client.get_access_token()}", "Content-type": "application/json", @@ -192,7 +192,7 @@ class BcfClient: print(f"message: {response.reason}' '{response.status_code}, {errh}") return response.status_code, response.reason - def put(self, endpoint: str, data: Any = None, params: Any = None) -> Tuple[int, str]: + def put(self, endpoint: str, data: Any = None, params: Any = None) -> tuple[int, str]: headers = { "Authorization": f"Bearer {self.foundation_client.get_access_token()}", "Content-type": "application/json", @@ -210,7 +210,7 @@ class BcfClient: print(f"message: {response.reason}' '{response.status_code}, {errh}") return response.status_code, response.reason - def delete(self, endpoint: str, params: Any = None) -> Tuple[int, str]: + def delete(self, endpoint: str, params: Any = None) -> tuple[int, str]: headers = { "Authorization": f"Bearer {self.foundation_client.get_access_token()}", "Content-type": "application/json", @@ -237,7 +237,7 @@ class BcfClient: }, ) - def update_project(self, project_id: str = "", data: Any = None) -> Tuple[int, str]: + def update_project(self, project_id: str = "", data: Any = None) -> tuple[int, str]: url = f"{self.baseurl}/projects/{project_id}" headers = {"Authorization": f"Bearer {self.foundation_client.get_access_token()}"} resp = requests.put(url, headers=headers, data=data) @@ -276,16 +276,16 @@ class BcfClient: }, ) - def create_topic(self, project_id: str = "", data: Any = None) -> Tuple[int, str]: + def create_topic(self, project_id: str = "", data: Any = None) -> tuple[int, str]: return self.post(f"/projects/{project_id}/topics", data=data) - def update_topic(self, project_id: str = "", topic_id: str = "", data: Any = None) -> Tuple[int, str]: + def update_topic(self, project_id: str = "", topic_id: str = "", data: Any = None) -> tuple[int, str]: return self.put(f"/projects/{project_id}/topics/{topic_id}", data=data) - def delete_topic(self, project_id: str = "", topic_id: str = "") -> Tuple[int, str]: + def delete_topic(self, project_id: str = "", topic_id: str = "") -> tuple[int, str]: return self.delete(f"/projects/{project_id}/topics/{topic_id}") - def get_snippet(self, project_id: str = "", topic_id: str = "") -> Tuple[int, str]: + def get_snippet(self, project_id: str = "", topic_id: str = "") -> tuple[int, str]: headers = { "Authorization": f"Bearer {self.foundation_client.get_access_token()}", "Content-type": "application/octet-stream", @@ -332,7 +332,7 @@ class BcfClient: topic_id: str = "", data: Any = None, params: Any = None, - ) -> Tuple[int, str]: + ) -> tuple[int, str]: return self.put( f"/projects/{project_id}/topics/{topic_id}/files", data=data, @@ -347,7 +347,7 @@ class BcfClient: topic_id: str = "", data: Any = None, params: Any = None, - ) -> Tuple[int, str]: + ) -> tuple[int, str]: return self.post( f"/projects/{project_id}/topics/{topic_id}/comments", data=data, @@ -363,7 +363,7 @@ class BcfClient: }, ) - def delete_comment(self, project_id: str = "", topic_id: str = "", comment_id: str = "") -> Tuple[int, str]: + def delete_comment(self, project_id: str = "", topic_id: str = "", comment_id: str = "") -> tuple[int, str]: return self.delete(f"/projects/{project_id}/topics/{topic_id}/comments/{comment_id}") def update_comment( @@ -372,7 +372,7 @@ class BcfClient: topic_id: str = "", comment_id: str = "", data: Any = None, - ) -> Tuple[int, str]: + ) -> tuple[int, str]: return self.put( f"/projects/{project_id}/topics/{topic_id}/comments/{comment_id}", data=data, @@ -387,7 +387,7 @@ class BcfClient: }, ) - def create_viewpoints(self, project_id: str = "", topic_id: str = "", data: Any = None) -> Tuple[int, str]: + def create_viewpoints(self, project_id: str = "", topic_id: str = "", data: Any = None) -> tuple[int, str]: return self.post( f"/projects/{project_id}/topics/{topic_id}/viewpoints", data=data, @@ -408,7 +408,7 @@ class BcfClient: project_id: str = "", topic_id: str = "", viewpoint_id: str = "", - ) -> Tuple[int, str]: + ) -> tuple[int, str]: return self.delete( f"/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}", ) @@ -478,7 +478,7 @@ class BcfClient: project_id: str = "", topic_id: str = "", data: Any = None, - ) -> Tuple[int, str]: + ) -> tuple[int, str]: return self.put( f"/projects/{project_id}/topics/{topic_id}/related_topics", data=data, @@ -498,7 +498,7 @@ class BcfClient: project_id: str = "", topic_id: str = "", data: Any = None, - ) -> Tuple[int, str]: + ) -> tuple[int, str]: return self.post( f"/projects/{project_id}/topics/{topic_id}/document_references", data=data, @@ -510,7 +510,7 @@ class BcfClient: topic_id: str = "", document_reference_id: str = "", data: Any = None, - ) -> Tuple[int, str]: + ) -> tuple[int, str]: return self.put( f"/projects/{project_id}/topics/{topic_id}/document_references/{document_reference_id}", data=data, @@ -543,7 +543,7 @@ class BcfClient: return response.status_code - def get_document(self, project_id: str = "", topic_id: str = "", document_id: str = "") -> Tuple[int, str]: + def get_document(self, project_id: str = "", topic_id: str = "", document_id: str = "") -> tuple[int, str]: headers = { "Authorization": f"Bearer {self.foundation_client.get_access_token()}", "Content-type": "application/octet-stream", diff --git a/src/bcf/bcf/v3/bcfxml.py b/src/bcf/bcf/v3/bcfxml.py index 16d920737a..872efe5db9 100644 --- a/src/bcf/bcf/v3/bcfxml.py +++ b/src/bcf/bcf/v3/bcfxml.py @@ -1,5 +1,6 @@ """BCF XML V3 handlers.""" +from __future__ import annotations import uuid import warnings import zipfile @@ -30,7 +31,7 @@ class BcfXml: self._documents: Optional[DocumentsHandler] = None self._zip_file = self._load_zip_file() - def __enter__(self) -> "BcfXml": + def __enter__(self) -> BcfXml: return self def __exit__(self, *args: Any) -> None: @@ -116,7 +117,7 @@ class BcfXml: return self._documents @classmethod - def load(cls, filename: Path, xml_handler: Optional[AbstractXmlParserSerializer] = None) -> Optional["BcfXml"]: + def load(cls, filename: Path, xml_handler: Optional[AbstractXmlParserSerializer] = None) -> Optional[BcfXml]: """ Create a BcfXml object from a file. @@ -141,7 +142,7 @@ class BcfXml: project_name: Optional[str] = None, extensions: Optional[mdl.Extensions] = None, xml_handler: Optional[AbstractXmlParserSerializer] = None, - ) -> "BcfXml": + ) -> BcfXml: """ Create a new BcfXml object. @@ -239,7 +240,7 @@ class BcfXml: ) # region Deprecated methods - def new_project(self) -> "BcfXml": + def new_project(self) -> BcfXml: """Deprecated method.""" warnings.warn("new_project is deprecated, use create_new instead.", DeprecationWarning) return self.create_new() diff --git a/src/bcf/bcf/v3/model/__init__.py b/src/bcf/bcf/v3/model/__init__.py index ed74e66dcc..7ccfa02d0b 100644 --- a/src/bcf/bcf/v3/model/__init__.py +++ b/src/bcf/bcf/v3/model/__init__.py @@ -55,40 +55,12 @@ from bcf.v3.model.visinfo import ( ) __all__ = [ - "Document", - "DocumentInfo", - "DocumentInfoDocuments", - "Extensions", - "ExtensionsPriorities", - "ExtensionsSnippetTypes", - "ExtensionsStages", - "ExtensionsTopicLabels", - "ExtensionsTopicStatuses", - "ExtensionsTopicTypes", - "ExtensionsUsers", "BimSnippet", - "Comment", - "CommentViewpoint", - "DocumentReference", - "File", - "Header", - "HeaderFiles", - "Markup", - "Topic", - "TopicComments", - "TopicDocumentReferences", - "TopicLabels", - "TopicReferenceLinks", - "TopicRelatedTopics", - "TopicRelatedTopicsRelatedTopic", - "TopicViewpoints", - "ViewPoint", - "Project", - "ProjectInfo", - "Version", "Bitmap", "BitmapFormat", "ClippingPlane", + "Comment", + "CommentViewpoint", "Component", "ComponentColoring", "ComponentColoringColor", @@ -98,10 +70,38 @@ __all__ = [ "ComponentVisibilityExceptions", "Components", "Direction", + "Document", + "DocumentInfo", + "DocumentInfoDocuments", + "DocumentReference", + "Extensions", + "ExtensionsPriorities", + "ExtensionsSnippetTypes", + "ExtensionsStages", + "ExtensionsTopicLabels", + "ExtensionsTopicStatuses", + "ExtensionsTopicTypes", + "ExtensionsUsers", + "File", + "Header", + "HeaderFiles", "Line", + "Markup", "OrthogonalCamera", "PerspectiveCamera", "Point", + "Project", + "ProjectInfo", + "Topic", + "TopicComments", + "TopicDocumentReferences", + "TopicLabels", + "TopicReferenceLinks", + "TopicRelatedTopics", + "TopicRelatedTopicsRelatedTopic", + "TopicViewpoints", + "Version", + "ViewPoint", "ViewSetupHints", "VisualizationInfo", "VisualizationInfoBitmaps", diff --git a/src/bcf/bcf/v3/model/documents.py b/src/bcf/bcf/v3/model/documents.py index 1b18758d0e..2e2de1f5ea 100644 --- a/src/bcf/bcf/v3/model/documents.py +++ b/src/bcf/bcf/v3/model/documents.py @@ -1,8 +1,11 @@ +import sys from dataclasses import dataclass, field -from typing import List, Optional +from typing import Optional + +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Document: filename: str = field( metadata={ @@ -34,12 +37,12 @@ class Document: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class DocumentInfoDocuments: class Meta: global_type = False - document: List[Document] = field( + document: list[Document] = field( default_factory=list, metadata={ "name": "Document", @@ -49,7 +52,7 @@ class DocumentInfoDocuments: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class DocumentInfo: documents: Optional[DocumentInfoDocuments] = field( default=None, diff --git a/src/bcf/bcf/v3/model/extensions.py b/src/bcf/bcf/v3/model/extensions.py index c9c47495ce..12d3d4dbfd 100644 --- a/src/bcf/bcf/v3/model/extensions.py +++ b/src/bcf/bcf/v3/model/extensions.py @@ -1,13 +1,16 @@ +import sys from dataclasses import dataclass, field -from typing import List, Optional +from typing import Optional + +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsPriorities: class Meta: global_type = False - priority: List[str] = field( + priority: list[str] = field( default_factory=list, metadata={ "name": "Priority", @@ -19,12 +22,12 @@ class ExtensionsPriorities: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsSnippetTypes: class Meta: global_type = False - snippet_type: List[str] = field( + snippet_type: list[str] = field( default_factory=list, metadata={ "name": "SnippetType", @@ -36,12 +39,12 @@ class ExtensionsSnippetTypes: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsStages: class Meta: global_type = False - stage: List[str] = field( + stage: list[str] = field( default_factory=list, metadata={ "name": "Stage", @@ -53,12 +56,12 @@ class ExtensionsStages: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsTopicLabels: class Meta: global_type = False - topic_label: List[str] = field( + topic_label: list[str] = field( default_factory=list, metadata={ "name": "TopicLabel", @@ -70,12 +73,12 @@ class ExtensionsTopicLabels: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsTopicStatuses: class Meta: global_type = False - topic_status: List[str] = field( + topic_status: list[str] = field( default_factory=list, metadata={ "name": "TopicStatus", @@ -87,12 +90,12 @@ class ExtensionsTopicStatuses: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsTopicTypes: class Meta: global_type = False - topic_type: List[str] = field( + topic_type: list[str] = field( default_factory=list, metadata={ "name": "TopicType", @@ -104,12 +107,12 @@ class ExtensionsTopicTypes: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ExtensionsUsers: class Meta: global_type = False - user: List[str] = field( + user: list[str] = field( default_factory=list, metadata={ "name": "User", @@ -121,7 +124,7 @@ class ExtensionsUsers: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Extensions: topic_types: Optional[ExtensionsTopicTypes] = field( default=None, diff --git a/src/bcf/bcf/v3/model/markup.py b/src/bcf/bcf/v3/model/markup.py index 4121b04abe..96b656b09d 100644 --- a/src/bcf/bcf/v3/model/markup.py +++ b/src/bcf/bcf/v3/model/markup.py @@ -1,10 +1,13 @@ +import sys from dataclasses import dataclass, field -from typing import List, Optional +from typing import Optional from xsdata.models.datatype import XmlDateTime +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} -@dataclass(slots=True, kw_only=True) + +@dataclass(**DATACLASS_KWARGS) class BimSnippet: reference: str = field( metadata={ @@ -44,7 +47,7 @@ class BimSnippet: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class CommentViewpoint: class Meta: global_type = False @@ -59,7 +62,7 @@ class CommentViewpoint: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class DocumentReference: document_guid: Optional[str] = field( default=None, @@ -100,7 +103,7 @@ class DocumentReference: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class File: filename: Optional[str] = field( default=None, @@ -157,12 +160,12 @@ class File: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicLabels: class Meta: global_type = False - label: List[str] = field( + label: list[str] = field( default_factory=list, metadata={ "name": "Label", @@ -174,12 +177,12 @@ class TopicLabels: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicReferenceLinks: class Meta: global_type = False - reference_link: List[str] = field( + reference_link: list[str] = field( default_factory=list, metadata={ "name": "ReferenceLink", @@ -191,7 +194,7 @@ class TopicReferenceLinks: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicRelatedTopicsRelatedTopic: class Meta: global_type = False @@ -206,7 +209,7 @@ class TopicRelatedTopicsRelatedTopic: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ViewPoint: viewpoint: Optional[str] = field( default=None, @@ -246,7 +249,7 @@ class ViewPoint: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Comment: date: XmlDateTime = field( metadata={ @@ -312,12 +315,12 @@ class Comment: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class HeaderFiles: class Meta: global_type = False - file: List[File] = field( + file: list[File] = field( default_factory=list, metadata={ "name": "File", @@ -327,12 +330,12 @@ class HeaderFiles: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicDocumentReferences: class Meta: global_type = False - document_reference: List[DocumentReference] = field( + document_reference: list[DocumentReference] = field( default_factory=list, metadata={ "name": "DocumentReference", @@ -342,12 +345,12 @@ class TopicDocumentReferences: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicRelatedTopics: class Meta: global_type = False - related_topic: List[TopicRelatedTopicsRelatedTopic] = field( + related_topic: list[TopicRelatedTopicsRelatedTopic] = field( default_factory=list, metadata={ "name": "RelatedTopic", @@ -357,12 +360,12 @@ class TopicRelatedTopics: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicViewpoints: class Meta: global_type = False - view_point: List[ViewPoint] = field( + view_point: list[ViewPoint] = field( default_factory=list, metadata={ "name": "ViewPoint", @@ -372,7 +375,7 @@ class TopicViewpoints: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Header: files: Optional[HeaderFiles] = field( default=None, @@ -384,12 +387,12 @@ class Header: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class TopicComments: class Meta: global_type = False - comment: List[Comment] = field( + comment: list[Comment] = field( default_factory=list, metadata={ "name": "Comment", @@ -399,7 +402,7 @@ class TopicComments: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Topic: reference_links: Optional[TopicReferenceLinks] = field( default=None, @@ -596,7 +599,7 @@ class Topic: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Markup: header: Optional[Header] = field( default=None, diff --git a/src/bcf/bcf/v3/model/project.py b/src/bcf/bcf/v3/model/project.py index 76a6922925..ff07fa2c08 100644 --- a/src/bcf/bcf/v3/model/project.py +++ b/src/bcf/bcf/v3/model/project.py @@ -1,8 +1,11 @@ +import sys from dataclasses import dataclass, field from typing import Optional +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} -@dataclass(slots=True, kw_only=True) + +@dataclass(**DATACLASS_KWARGS) class Project: name: Optional[str] = field( default=None, @@ -25,7 +28,7 @@ class Project: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ProjectInfo: project: Project = field( metadata={ diff --git a/src/bcf/bcf/v3/model/version.py b/src/bcf/bcf/v3/model/version.py index f3a44d2ab3..dd8cc713e3 100644 --- a/src/bcf/bcf/v3/model/version.py +++ b/src/bcf/bcf/v3/model/version.py @@ -1,7 +1,10 @@ +import sys from dataclasses import dataclass, field +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} -@dataclass(slots=True, kw_only=True) + +@dataclass(**DATACLASS_KWARGS) class Version: version_id: str = field( metadata={ diff --git a/src/bcf/bcf/v3/model/visinfo.py b/src/bcf/bcf/v3/model/visinfo.py index 1a1dcb72c5..43a00f33d3 100644 --- a/src/bcf/bcf/v3/model/visinfo.py +++ b/src/bcf/bcf/v3/model/visinfo.py @@ -1,6 +1,10 @@ +import sys from dataclasses import dataclass, field from enum import Enum -from typing import List, Optional +from typing import Optional + + +DATACLASS_KWARGS = {} if sys.version_info < (3, 10) else {"slots": True, "kw_only": True} class BitmapFormat(Enum): @@ -8,7 +12,7 @@ class BitmapFormat(Enum): JPG = "jpg" -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Component: originating_system: Optional[str] = field( default=None, @@ -39,7 +43,7 @@ class Component: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Direction: x: float = field( metadata={ @@ -64,7 +68,7 @@ class Direction: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Point: x: float = field( metadata={ @@ -89,7 +93,7 @@ class Point: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ViewSetupHints: spaces_visible: bool = field( default=False, @@ -114,7 +118,7 @@ class ViewSetupHints: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Bitmap: format: BitmapFormat = field( metadata={ @@ -162,7 +166,7 @@ class Bitmap: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ClippingPlane: location: Point = field( metadata={ @@ -180,12 +184,12 @@ class ClippingPlane: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentColoringColorComponents: class Meta: global_type = False - component: List[Component] = field( + component: list[Component] = field( default_factory=list, metadata={ "name": "Component", @@ -195,9 +199,9 @@ class ComponentColoringColorComponents: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentSelection: - component: List[Component] = field( + component: list[Component] = field( default_factory=list, metadata={ "name": "Component", @@ -206,12 +210,12 @@ class ComponentSelection: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentVisibilityExceptions: class Meta: global_type = False - component: List[Component] = field( + component: list[Component] = field( default_factory=list, metadata={ "name": "Component", @@ -220,7 +224,7 @@ class ComponentVisibilityExceptions: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Line: start_point: Point = field( metadata={ @@ -238,7 +242,7 @@ class Line: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class OrthogonalCamera: """ Attributes @@ -288,7 +292,7 @@ class OrthogonalCamera: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class PerspectiveCamera: """ Attributes @@ -344,7 +348,7 @@ class PerspectiveCamera: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentColoringColor: class Meta: global_type = False @@ -366,7 +370,7 @@ class ComponentColoringColor: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentVisibility: view_setup_hints: Optional[ViewSetupHints] = field( default=None, @@ -391,12 +395,12 @@ class ComponentVisibility: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class VisualizationInfoBitmaps: class Meta: global_type = False - bitmap: List[Bitmap] = field( + bitmap: list[Bitmap] = field( default_factory=list, metadata={ "name": "Bitmap", @@ -405,12 +409,12 @@ class VisualizationInfoBitmaps: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class VisualizationInfoClippingPlanes: class Meta: global_type = False - clipping_plane: List[ClippingPlane] = field( + clipping_plane: list[ClippingPlane] = field( default_factory=list, metadata={ "name": "ClippingPlane", @@ -419,12 +423,12 @@ class VisualizationInfoClippingPlanes: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class VisualizationInfoLines: class Meta: global_type = False - line: List[Line] = field( + line: list[Line] = field( default_factory=list, metadata={ "name": "Line", @@ -433,9 +437,9 @@ class VisualizationInfoLines: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class ComponentColoring: - color: List[ComponentColoringColor] = field( + color: list[ComponentColoringColor] = field( default_factory=list, metadata={ "name": "Color", @@ -444,7 +448,7 @@ class ComponentColoring: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class Components: selection: Optional[ComponentSelection] = field( default=None, @@ -469,7 +473,7 @@ class Components: ) -@dataclass(slots=True, kw_only=True) +@dataclass(**DATACLASS_KWARGS) class VisualizationInfo: """ VisualizationInfo documentation. diff --git a/src/bcf/bcf/v3/topic.py b/src/bcf/bcf/v3/topic.py index b0cfa4268d..38fea3de7e 100644 --- a/src/bcf/bcf/v3/topic.py +++ b/src/bcf/bcf/v3/topic.py @@ -1,5 +1,6 @@ """BCF XML V3 Topic handler.""" +from __future__ import annotations import datetime import uuid import zipfile @@ -91,12 +92,12 @@ class TopicHandler: self._bim_snippet = value @property - def viewpoints(self) -> dict[str, "VisualizationInfoHandler"]: + def viewpoints(self) -> dict[str, VisualizationInfoHandler]: if self._viewpoints is None: self._viewpoints = self._load_viewpoints() return self._viewpoints - def _load_viewpoints(self) -> dict[str, "VisualizationInfoHandler"]: + def _load_viewpoints(self) -> dict[str, VisualizationInfoHandler]: if self._topic_dir and self.topic.viewpoints and (viewpoints := self.topic.viewpoints.view_point): return VisualizationInfoHandler.from_topic_viewpoints(self._topic_dir, viewpoints) return {} @@ -139,7 +140,7 @@ class TopicHandler: topic_type: str = "", topic_status: str = "", xml_handler: Optional[AbstractXmlParserSerializer] = None, - ) -> "TopicHandler": + ) -> TopicHandler: """ Create a new BCF topic. diff --git a/src/bcf/bcf/v3/visinfo.py b/src/bcf/bcf/v3/visinfo.py index 6fb2be6a39..1cb442aa83 100644 --- a/src/bcf/bcf/v3/visinfo.py +++ b/src/bcf/bcf/v3/visinfo.py @@ -1,6 +1,7 @@ import uuid import zipfile -from typing import Any, Iterable, Optional, Literal, Union +from typing import Any, Optional, Literal, Union +from collections.abc import Iterable import numpy as np from ifcopenshell import entity_instance diff --git a/src/bcf/bcf/xml_parser.py b/src/bcf/bcf/xml_parser.py index a5dbd3b71c..e53debb853 100644 --- a/src/bcf/bcf/xml_parser.py +++ b/src/bcf/bcf/xml_parser.py @@ -1,6 +1,6 @@ """XML Parser and Serializer factories.""" -from typing import Optional, Protocol, Type, TypeVar +from typing import Optional, Protocol, TypeVar from xsdata.formats.dataclass.context import XmlContext from xsdata.formats.dataclass.parsers import XmlParser @@ -29,7 +29,7 @@ T = TypeVar("T") class AbstractXmlParserSerializer(Protocol): """XML Parser and serializer wrapper.""" - def parse(self, xml: bytes, clazz: Type[T]) -> T: + def parse(self, xml: bytes, clazz: type[T]) -> T: """ Parse an XML file to an object. @@ -61,7 +61,7 @@ class XmlParserSerializer: self.parser = build_xml_parser(self.context) self.serializer = build_serializer(self.context) - def parse(self, xml: bytes, clazz: Type[T]) -> T: + def parse(self, xml: bytes, clazz: type[T]) -> T: """ Parse an XML file to an object. diff --git a/src/bcf/pyproject.toml b/src/bcf/pyproject.toml index b5ec969a93..7047595ce1 100644 --- a/src/bcf/pyproject.toml +++ b/src/bcf/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "xsdata>=24.4", "numpy", "ifcopenshell", + "requests", ] version = "0.0.0" classifiers = [ diff --git a/src/bonsai/Makefile b/src/bonsai/Makefile index 1d57431109..5bc39cbd84 100644 --- a/src/bonsai/Makefile +++ b/src/bonsai/Makefile @@ -87,7 +87,7 @@ BLENDER_PLATFORM:=windows-x64 endif # Current build commit hash. -OLD:=2db6b86 +OLD:=358395c .PHONY: bump bump: ifndef NEW @@ -138,7 +138,7 @@ endif # odfpy doesn't come with its own wheel, so whee'l (get it?) create it ourselves! cd build && . env/$(VENV_ACTIVATE) && $(PIP) wheel odfpy --wheel-dir=./wheels # Required by IFCCityJSON - cd build && . env/$(VENV_ACTIVATE) && $(PIP) download cjio --dest=./wheels + cd build && . env/$(VENV_ACTIVATE) && $(PIP) download "cjio >=0.8, <0.10" --dest=./wheels # Required in general for sorting all sorts of stuff in a nice way cd build && . env/$(VENV_ACTIVATE) && $(PIP) download natsort --dest=./wheels # Provides express rule validation for ifcopenshell.validate @@ -193,13 +193,15 @@ else cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pyradiance $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels endif + # jQuery is required for web UI functionality + cd build/bonsai/bim/data/webui/static/js/ && wget https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js + # Provides jsgantt-improved supports for web-based construction sequencing gantt charts cd build/bonsai/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js cd build/bonsai/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css # Provides IFCJSON functionality - # TODO: replace with main repo if https://github.com/IFCJSON-Team/IFC2JSON_python/pull/3 is merged. - cd build && wget -O ifc2json.zip https://github.com/Moult/IFC2JSON_python/archive/refs/heads/feature-ios-v0.8.0.zip + cd build && wget -O ifc2json.zip https://github.com/IFCJSON-Team/IFC2JSON_python/archive/refs/heads/master.zip cd build && unzip ifc2json.zip && rm ifc2json.zip # IFCJSON doesn't have pyproject.toml, so we use python command. cd build && . env/$(VENV_ACTIVATE) && cd IFC2JSON_python-*/file_converters && \ diff --git a/src/bonsai/bonsai/__init__.py b/src/bonsai/bonsai/__init__.py index 6ea48885b3..ea0c4ca753 100644 --- a/src/bonsai/bonsai/__init__.py +++ b/src/bonsai/bonsai/__init__.py @@ -28,6 +28,10 @@ IN_BLENDER = sys.modules.get("bpy", None) if IN_BLENDER: import bpy +# This file is executed twice - first as a bonsai-extension +# and then as a bonsai-package. +IN_PACKAGE = __package__ == "bonsai" + import re import platform import traceback @@ -36,7 +40,8 @@ import uuid import shutil from collections import deque from pathlib import Path -from typing import Union, Any, Generator +from typing import Union, Any +from collections.abc import Generator last_commit_hash = "8888888" @@ -218,17 +223,21 @@ if IN_BLENDER: info["binary_python_version"] = version return info - try: - import git + def update_commit_data() -> None: + try: + import git - # We can't just use __file__ as bonsai/__init__.py is typically not symlinked - # as Blender have errors symlinking main addon package file. - path = Path(__file__).resolve().parent - repo = git.Repo(str(path), search_parent_directories=True) - last_commit_hash = repo.head.object.hexsha - last_commit_date = repo.head.object.committed_datetime.isoformat() - except: - pass + global last_commit_hash + global last_commit_date + path = Path(__file__).resolve().parent + repo = git.Repo(str(path), search_parent_directories=True) + last_commit_hash = repo.head.object.hexsha + last_commit_date = repo.head.object.committed_datetime.isoformat() + except: + pass + + if IN_PACKAGE: + update_commit_data() try: import ifcopenshell.api @@ -244,6 +253,12 @@ if IN_BLENDER: ifcopenshell.api.add_pre_listener("*", "action_logger", log_api) + def purge_cache(): + """Purge cache left from previous session (e.g. after reload or update).""" + import bonsai.tool as tool + + tool.Blender.get_bonsai_version.cache_clear() + def register(): if platform.system() == "Windows": clean_up_dlls_safe_links() @@ -261,6 +276,7 @@ if IN_BLENDER: bonsai.REINSTALLED_BBIM_VERSION = current_version bonsai.bim.register() + purge_cache() def unregister(): if platform.system() == "Windows": diff --git a/src/bonsai/bonsai/bim/__init__.py b/src/bonsai/bonsai/bim/__init__.py index 2a86f92f74..79fc5fdd54 100644 --- a/src/bonsai/bonsai/bim/__init__.py +++ b/src/bonsai/bonsai/bim/__init__.py @@ -22,7 +22,8 @@ import bpy.utils.previews import importlib from bpy_extras.io_utils import ImportHelper, ExportHelper from . import handler, ui, prop, operator -from typing import Callable, Union +from typing import Union +from collections.abc import Callable try: from bonsai.translations import translations_dict @@ -96,17 +97,20 @@ for name in modules.keys(): classes = [ - operator.AddIfcFile, operator.BIM_OT_add_section_plane, operator.BIM_OT_delete_object, operator.BIM_OT_remove_section_plane, operator.BIM_OT_select_entity, + operator.BIM_OT_select_entity_by_guid, operator.BIM_OT_select_object, operator.BIM_OT_show_description, operator.BIM_OT_multiple_file_selector, + operator.BIM_OT_attribute_add_subitem, + operator.BIM_OT_attribute_remove_subitem, operator.ClippingPlaneCutWithCappings, operator.CloseBlendWarning, operator.CloseError, + operator.CreateMacBonsaiApp, operator.CopyTextToClipboard, operator.EditBlenderCollection, operator.FileAssociate, @@ -115,24 +119,25 @@ classes = [ operator.OpenUpstream, operator.OpenUri, operator.ReloadIfcFile, - operator.RemoveIfcFile, operator.RevertClippingPlaneCut, operator.SelectDir, operator.SelectIfcFile, operator.SelectURIAttribute, operator.SetTab, operator.SwitchTab, + operator.ShowSystemInfo, prop.StrProperty, operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty + operator.BIM_OT_attribute_search_values, prop.ObjProperty, prop.MultipleFileSelect, prop.Attribute, + prop.ISODuration, prop.BIMAreaProperties, prop.BIMTabProperties, prop.BIMProperties, prop.IfcParameter, prop.PsetQto, - prop.GlobalId, prop.BIMObjectProperties, prop.BIMCollectionProperties, prop.BIMMeshProperties, @@ -142,7 +147,7 @@ classes = [ prop.BIMSnapGroups, ui.BIM_UL_clipping_plane, ui.BIM_UL_generic, - ui.BIM_UL_topics, + ui.DocPreferences, ui.BIM_ADDON_preferences, # Tabs panel ui.BIM_PT_tabs, @@ -150,10 +155,10 @@ classes = [ ui.BIM_PT_tab_new_project_wizard, ui.BIM_PT_tab_project_info, ui.BIM_PT_tab_spatial, + ui.BIM_PT_tab_grouping_and_filtering, ui.BIM_PT_tab_project_setup, ui.BIM_PT_tab_geometry, ui.BIM_PT_tab_stakeholders, - ui.BIM_PT_tab_grouping_and_filtering, # Object information ui.BIM_PT_tab_object_metadata, ui.BIM_PT_tab_misc, diff --git a/src/bonsai/bonsai/bim/data/assets/shading_styles.json b/src/bonsai/bonsai/bim/data/assets/shading_styles.json index dd7ded4423..602b8a652e 100644 --- a/src/bonsai/bonsai/bim/data/assets/shading_styles.json +++ b/src/bonsai/bonsai/bim/data/assets/shading_styles.json @@ -47,8 +47,6 @@ "scene.render.simplify_gpencil_shader_fx": true, "scene.render.simplify_gpencil_tint": true, "scene.render.simplify_gpencil_view_fill": true, - "scene.render.simplify_shadows": 1.0, - "scene.render.simplify_shadows_render": 1.0, "scene.render.simplify_subdivision": 6, "scene.render.simplify_subdivision_render": 6, "scene.render.simplify_volumes": 1.0, @@ -147,7 +145,7 @@ "scene.display.shading.use_scene_world_render": false, "scene.display.shading.use_studiolight_view_rotation": true, "scene.display.shading.use_world_space_lighting": false, - "scene.display.shading.wireframe_color_type": "MATERIAL", + "scene.display.shading.wireframe_color_type": "THEME", "scene.display.shading.xray_alpha": 0.5, "scene.display.shading.xray_alpha_wireframe": 0.0, "scene.display.light_direction": [ @@ -162,7 +160,6 @@ "scene.display.shadow_focus": 0.0, "scene.display.shadow_shift": 0.10000000149011612, "scene.display.viewport_aa": "FXAA", - "space.overlay.backwire_opacity": 0.5, "space.overlay.bone_wire_alpha": 1.0, "space.overlay.display_handle": "SELECTED", "space.overlay.fade_inactive_alpha": 0.4000000059604645, @@ -189,7 +186,6 @@ "space.overlay.show_edge_crease": true, "space.overlay.show_edge_seams": true, "space.overlay.show_edge_sharp": true, - "space.overlay.show_edges": false, "space.overlay.show_extra_edge_angle": false, "space.overlay.show_extra_edge_length": false, "space.overlay.show_extra_face_angle": false, @@ -208,7 +204,6 @@ "space.overlay.show_motion_paths": true, "space.overlay.show_object_origins": false, "space.overlay.show_object_origins_all": false, - "space.overlay.show_occlude_wire": false, "space.overlay.show_onion_skins": false, "space.overlay.show_ortho_grid": true, "space.overlay.show_outline_selected": true, @@ -298,8 +293,6 @@ "scene.render.simplify_gpencil_shader_fx": true, "scene.render.simplify_gpencil_tint": true, "scene.render.simplify_gpencil_view_fill": true, - "scene.render.simplify_shadows": 1.0, - "scene.render.simplify_shadows_render": 1.0, "scene.render.simplify_subdivision": 6, "scene.render.simplify_subdivision_render": 6, "scene.render.simplify_volumes": 1.0, @@ -398,7 +391,7 @@ "scene.display.shading.use_scene_world_render": false, "scene.display.shading.use_studiolight_view_rotation": true, "scene.display.shading.use_world_space_lighting": false, - "scene.display.shading.wireframe_color_type": "MATERIAL", + "scene.display.shading.wireframe_color_type": "THEME", "scene.display.shading.xray_alpha": 0.5, "scene.display.shading.xray_alpha_wireframe": 0.0, "scene.display.light_direction": [ @@ -413,7 +406,6 @@ "scene.display.shadow_focus": 0.0, "scene.display.shadow_shift": 0.10000000149011612, "scene.display.viewport_aa": "FXAA", - "space.overlay.backwire_opacity": 0.5, "space.overlay.bone_wire_alpha": 1.0, "space.overlay.display_handle": "SELECTED", "space.overlay.fade_inactive_alpha": 0.4000000059604645, @@ -440,7 +432,6 @@ "space.overlay.show_edge_crease": true, "space.overlay.show_edge_seams": true, "space.overlay.show_edge_sharp": true, - "space.overlay.show_edges": false, "space.overlay.show_extra_edge_angle": false, "space.overlay.show_extra_edge_length": false, "space.overlay.show_extra_face_angle": false, @@ -459,7 +450,6 @@ "space.overlay.show_motion_paths": true, "space.overlay.show_object_origins": false, "space.overlay.show_object_origins_all": false, - "space.overlay.show_occlude_wire": false, "space.overlay.show_onion_skins": false, "space.overlay.show_ortho_grid": true, "space.overlay.show_outline_selected": true, @@ -509,6 +499,10 @@ 0.05087608844041824, 0.05087608844041824 ], + "scene.eevee.use_shadows": true, + "scene.eevee.shadow_ray_count": 1, + "scene.eevee.shadow_step_count": 6, + "scene.eevee.shadow_resolution_scale": 1.0, "scene.render.bake_bias": 0.0010000000474974513, "scene.render.bake_margin": 16, "scene.render.bake_margin_type": "ADJACENT_FACES", @@ -520,7 +514,7 @@ "scene.render.border_min_x": 0.0, "scene.render.border_min_y": 0.0, "scene.render.dither_intensity": 1.0, - "scene.render.engine": "BLENDER_EEVEE", + "scene.render.engine": "BLENDER_EEVEE_NEXT", "scene.render.film_transparent": false, "scene.render.filter_size": 1.5, "scene.render.fps": 24, @@ -549,8 +543,6 @@ "scene.render.simplify_gpencil_shader_fx": true, "scene.render.simplify_gpencil_tint": true, "scene.render.simplify_gpencil_view_fill": true, - "scene.render.simplify_shadows": 1.0, - "scene.render.simplify_shadows_render": 1.0, "scene.render.simplify_subdivision": 6, "scene.render.simplify_subdivision_render": 6, "scene.render.simplify_volumes": 1.0, @@ -649,7 +641,7 @@ "scene.display.shading.use_scene_world_render": false, "scene.display.shading.use_studiolight_view_rotation": true, "scene.display.shading.use_world_space_lighting": false, - "scene.display.shading.wireframe_color_type": "MATERIAL", + "scene.display.shading.wireframe_color_type": "THEME", "scene.display.shading.xray_alpha": 0.5, "scene.display.shading.xray_alpha_wireframe": 0.0, "scene.display.light_direction": [ @@ -664,7 +656,6 @@ "scene.display.shadow_focus": 0.0, "scene.display.shadow_shift": 0.10000000149011612, "scene.display.viewport_aa": "FXAA", - "space.overlay.backwire_opacity": 0.5, "space.overlay.bone_wire_alpha": 1.0, "space.overlay.display_handle": "SELECTED", "space.overlay.fade_inactive_alpha": 0.4000000059604645, @@ -691,7 +682,6 @@ "space.overlay.show_edge_crease": true, "space.overlay.show_edge_seams": true, "space.overlay.show_edge_sharp": true, - "space.overlay.show_edges": false, "space.overlay.show_extra_edge_angle": false, "space.overlay.show_extra_edge_length": false, "space.overlay.show_extra_face_angle": false, @@ -710,7 +700,6 @@ "space.overlay.show_motion_paths": true, "space.overlay.show_object_origins": true, "space.overlay.show_object_origins_all": false, - "space.overlay.show_occlude_wire": false, "space.overlay.show_onion_skins": false, "space.overlay.show_ortho_grid": true, "space.overlay.show_outline_selected": true, diff --git a/src/bonsai/bonsai/bim/data/icons/dm_extend_height.png b/src/bonsai/bonsai/bim/data/icons/dm_extend_height.png new file mode 100644 index 0000000000..93ffd71099 Binary files /dev/null and b/src/bonsai/bonsai/bim/data/icons/dm_extend_height.png differ diff --git a/src/bonsai/bonsai/bim/data/icons/lm_extend_height.png b/src/bonsai/bonsai/bim/data/icons/lm_extend_height.png new file mode 100644 index 0000000000..1f75a6cd1c Binary files /dev/null and b/src/bonsai/bonsai/bim/data/icons/lm_extend_height.png differ diff --git a/src/bonsai/bonsai/bim/data/pset/EPset_Drawing.ifc b/src/bonsai/bonsai/bim/data/pset/EPset_Drawing.ifc index f7ea3fd10c..a516b9765d 100644 --- a/src/bonsai/bonsai/bim/data/pset/EPset_Drawing.ifc +++ b/src/bonsai/bonsai/bim/data/pset/EPset_Drawing.ifc @@ -14,7 +14,7 @@ DATA; #7=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); #8=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); #9=IFCSIMPLEPROPERTYTEMPLATE('2NPPxuABv1huDTVh32TFgw',$,'GlobalReferencing','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); -#10=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.); +#10=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','Comma separated list of selector expressions to evaluate for each drawing elementand add results to their ''class'' attribute.\X2\000A\X0\E.g. ''Name, id'' would add to ''class'' value similar to ''Name-Wall id-1220''.\X2\000A\X0\Then it can be used to applied css styles based on the resulting class.\X2\000A\X0\If attribute is not present on the element, then it won''t be added to it''s ''class''.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.); #11=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','Selector expression to include ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.); #12=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','Selector expression to exclude ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.); #13=IFCSIMPLEPROPERTYTEMPLATE('0c1$8NpYDEaBiJrj16jHIo',$,'Stylesheet','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.); diff --git a/src/bonsai/bonsai/bim/data/templates/titleblocks/README.md b/src/bonsai/bonsai/bim/data/templates/titleblocks/README.md new file mode 100644 index 0000000000..e2e2ebeb35 --- /dev/null +++ b/src/bonsai/bonsai/bim/data/templates/titleblocks/README.md @@ -0,0 +1 @@ +SVG [mustache](https://mustache.github.io/) templates that will be used for sheets and fill be filled with infromation from the sheet's IfcDocumentInformation attributes (e.g. Identification, Name, Revision, etc). diff --git a/src/bonsai/bonsai/bim/data/webui/templates/costing.html b/src/bonsai/bonsai/bim/data/webui/templates/costing.html index 0351bd978c..6e076203c3 100644 --- a/src/bonsai/bonsai/bim/data/webui/templates/costing.html +++ b/src/bonsai/bonsai/bim/data/webui/templates/costing.html @@ -21,7 +21,7 @@ /> - + + + + + + + + \ No newline at end of file diff --git a/src/pyodide/demo-app/wheels b/src/pyodide/demo-app/wheels index 6b5bfb4bdc..d1126cec5b 160000 --- a/src/pyodide/demo-app/wheels +++ b/src/pyodide/demo-app/wheels @@ -1 +1 @@ -Subproject commit 6b5bfb4bdc364f859643a624bd69bf9f471d45c0 +Subproject commit d1126cec5b6deec97575e7f66fcb429eb7db605c diff --git a/src/qtviewer/CMakeLists.txt b/src/qtviewer/CMakeLists.txt index 0e83f1a386..978d5f116b 100644 --- a/src/qtviewer/CMakeLists.txt +++ b/src/qtviewer/CMakeLists.txt @@ -86,14 +86,14 @@ target_link_libraries(${targetName} ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENSCENEGRAPH_LIBRARIES} - Qt${QT_VERSION}::Core - Qt${QT_VERSION}::Gui - Qt${QT_VERSION}::OpenGL - Qt${QT_VERSION}::OpenGLWidgets + Qt${QT_VERSION}::Core + Qt${QT_VERSION}::Gui + Qt${QT_VERSION}::OpenGL + Qt${QT_VERSION}::OpenGLWidgets Qt${QT_VERSION}::Widgets ) -target_include_directories(${targetName} PUBLIC +target_include_directories(${targetName} PUBLIC ${QT_DIR}/include ${OPENSCENEGRAPH_INCLUDE_DIRS} ) diff --git a/src/serializers/GltfSerializer.cpp b/src/serializers/GltfSerializer.cpp index 8b1c0ec03b..b8487a5fb7 100644 --- a/src/serializers/GltfSerializer.cpp +++ b/src/serializers/GltfSerializer.cpp @@ -106,7 +106,10 @@ int GltfSerializer::writeMaterial(const ifcopenshell::geometry::taxonomy::style: base[3] = 1. - style->transparency; } - json_["materials"].push_back({ {"name", style->name}, {"doubleSided", true}, {"pbrMetallicRoughness", {{"baseColorFactor", base}, {"metallicFactor", 0}}}}); + if (style->has_specularity()) + json_["materials"].push_back({ {"name", style->name}, {"doubleSided", true}, {"pbrMetallicRoughness", {{"baseColorFactor", base}, {"metallicFactor", 0}, {"roughnessFactor", 1.0 / style->specularity}}}}); + else + json_["materials"].push_back({ {"name", style->name}, {"doubleSided", true}, {"pbrMetallicRoughness", {{"baseColorFactor", base}, {"metallicFactor", 0}}}}); if (style->transparency == style->transparency && style->transparency > 1.e-9) { json_["materials"].back()["alphaMode"] = "BLEND"; diff --git a/src/serializers/HdfSerializer.cpp b/src/serializers/HdfSerializer.cpp index 7dd8c18fc4..2fceb0eec0 100644 --- a/src/serializers/HdfSerializer.cpp +++ b/src/serializers/HdfSerializer.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef USE_BINARY #define write_shape write_binary @@ -233,21 +234,25 @@ namespace { } } -void HdfSerializer::read_surface_style(surface_style_serialization& s, const ifcopenshell::geometry::taxonomy::style::ptr& gss_) { - auto& gss = *gss_; +void HdfSerializer::read_surface_style(const surface_style_serialization& s, + ifcopenshell::geometry::taxonomy::style& gss, + IfcParse::IfcFile& f) { if (strlen(s.name) || s.id) { - if (s.diffuse[0] == s.diffuse[0]) { + if (!std::isnan(s.diffuse[0])) { gss.diffuse = ifcopenshell::geometry::taxonomy::colour(s.diffuse[0], s.diffuse[1], s.diffuse[2]); } - if (s.specular[0] == s.specular[0]) { + if (!std::isnan(s.specular[0])) { gss.specular = ifcopenshell::geometry::taxonomy::colour(s.specular[0], s.specular[1], s.specular[2]); } - if (s.transparency == s.transparency) { + if (!std::isnan(s.transparency)) { gss.transparency = s.transparency; } - if (s.specularity == s.specularity) { + if (!std::isnan(s.specularity)) { gss.specularity = s.specularity; } + if (s.id != 0) { + gss.instance = f.instance_by_id(s.id)->as(); + } } } @@ -356,7 +361,7 @@ IfcGeom::Element* HdfSerializer::read(IfcParse::IfcFile& f, const std::string& g matrix->components() << Eigen::Map(&part.matrix[0][0]); auto style_ptr = ifcopenshell::geometry::taxonomy::make(); - read_surface_style(part.surface_style, style_ptr); + read_surface_style(part.surface_style, *style_ptr, f); shapes.push_back(IfcGeom::ConversionResult(part.id, matrix, new ifcopenshell::geometry::OpenCascadeShape(shp), style_ptr)); } @@ -421,7 +426,8 @@ IfcGeom::Element* HdfSerializer::read(IfcParse::IfcFile& f, const std::string& g std::vector surface_style_ptrs(surface_styles.size()); for (size_t i = 0; i < surface_styles.size(); ++i) { - read_surface_style(surface_styles[i], surface_style_ptrs[i]); + surface_style_ptrs[i] = ifcopenshell::geometry::taxonomy::make(); + read_surface_style(surface_styles[i], *surface_style_ptrs[i], f); } triangulation_geometry = boost::shared_ptr(new IfcGeom::Representation::Triangulation( @@ -566,6 +572,16 @@ void HdfSerializer::write_style(surface_style_serialization& data, const ifcopen void HdfSerializer::write(const IfcGeom::BRepElement* o) { + // Currenly we only support OpenCascade shapes. + for (auto it = o->geometry().begin(); it != o->geometry().end(); ++it) { + auto shape_ptr = std::dynamic_pointer_cast(it->Shape()); + if (shape_ptr == nullptr) { + std::cerr << "WARNING. Only OpenCascade shapes support caching. " + << "Skipping caching for item #" << it->ItemId() << "." << std::endl; + return; + } + } + static auto nan = std::numeric_limits::quiet_NaN(); auto element_group = write((const IfcGeom::Element*)o); @@ -610,6 +626,7 @@ void HdfSerializer::write(const IfcGeom::BRepElement* o) { } brep_strings.emplace_back(); + // OpenCascadeShape type ensured by the check at the method start. write_shape(std::static_pointer_cast(it->Shape())->shape(), brep_strings.back()); parts[i].surface_style = { "", "", 0, {nan,nan,nan}, {nan,nan,nan}, nan, nan }; @@ -672,6 +689,7 @@ void HdfSerializer::write(const IfcGeom::TriangulationElement* o) { write_dataset(meshGroup, DATASET_NAME_UVCOORDS, mesh.uvs(), 2); write_dataset(meshGroup, DATASET_NAME_MATERIAL_IDS, mesh.material_ids(), 1); write_dataset(meshGroup, DATASET_NAME_ITEM_IDS, mesh.item_ids(), 1); + write_dataset(meshGroup, DATASET_NAME_EDGES_ITEM_IDS, mesh.edges_item_ids(), 1); { auto& ts = mesh.materials(); diff --git a/src/serializers/HdfSerializer.h b/src/serializers/HdfSerializer.h index d5846337f6..6dab05eaad 100644 --- a/src/serializers/HdfSerializer.h +++ b/src/serializers/HdfSerializer.h @@ -90,7 +90,9 @@ private: std::map group_cache_; H5::Group createRepresentationGroup(const H5::Group& element_group, const std::string& gid); - void read_surface_style(surface_style_serialization& sss, const ifcopenshell::geometry::taxonomy::style::ptr& style_ptr); + void read_surface_style(const surface_style_serialization& s, + ifcopenshell::geometry::taxonomy::style& gss, + IfcParse::IfcFile& f); void write_style(surface_style_serialization& data, const ifcopenshell::geometry::taxonomy::style::ptr& s); public: diff --git a/src/serializers/StepSerializer.h b/src/serializers/StepSerializer.h index 19144a35cc..da3335bde4 100644 --- a/src/serializers/StepSerializer.h +++ b/src/serializers/StepSerializer.h @@ -54,7 +54,7 @@ public: void setUnitNameAndMagnitude(const std::string& /*name*/, float magnitude) { const char* symbol = getSymbolForUnitMagnitude(magnitude); if (symbol) { - Interface_Static::SetCVal("xstep.cascade.unit", symbol); + // Interface_Static::SetCVal("xstep.cascade.unit", symbol); Interface_Static::SetCVal("write.step.unit", symbol); } } diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index 02de511db4..496660fca6 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -646,6 +646,8 @@ void SvgSerializer::write(const IfcGeom::BRepElement* brep_obj) { view_box_3d_.emplace(); BRepBndLib::AddOBB(compound_unmirrored, *view_box_3d_, false, false, false); #endif + } else { + Logger::Error("Failed to box or edge from drawing annotation"); } std::vector props; diff --git a/src/serializers/TtlWktSerializer.cpp b/src/serializers/TtlWktSerializer.cpp index b6b7698e2a..e9383e44b5 100644 --- a/src/serializers/TtlWktSerializer.cpp +++ b/src/serializers/TtlWktSerializer.cpp @@ -177,7 +177,7 @@ namespace { } std::string escape_for_turtle(const std::u32string& input) { - std::wostringstream escaped; + std::ostringstream escaped; escaped << "\""; for (auto& c : input) { @@ -200,7 +200,7 @@ namespace { default: if (c < 0x20 || c > 0x7E) { escaped << "\\u" - << std::hex << std::setw(4) << std::setfill(L'0') + << std::hex << std::setw(4) << std::setfill('0') << (c & 0xFFFF); } else { escaped.put(c); @@ -209,7 +209,7 @@ namespace { } } escaped << "\""; - return IfcUtil::convert_utf8(escaped.str()); + return escaped.str(); } template @@ -262,16 +262,16 @@ void TtlWktSerializer::write(const IfcGeom::TriangulationElement* o) { filename_.stream << ttl_object_id(o) << " a geo:Feature ;\n"; filename_.stream << " dcterms:identifier " << escape_for_turtle( - IfcUtil::convert_utf8_to_utf32(o->guid())) << " ;\n"; + IfcUtil::convert_utf8(o->guid())) << " ;\n"; filename_.stream << " rdfs:label " << escape_for_turtle( - IfcUtil::convert_utf8_to_utf32(o->name()) + IfcUtil::convert_utf8(o->name()) ) << " ;\n"; filename_.stream << " geo:hasGeometry " << ttl_object_id(o, "_geometry") << " .\n\n"; if (!o->geometry().polyhedral_faces_with_holes().empty()) { filename_.stream << ttl_object_id(o, "_geometry") << " a geo:Geometry ;\n"; filename_.stream << " geo:asWKT " << escape_for_turtle( - IfcUtil::convert_utf8_to_utf32( + IfcUtil::convert_utf8( capture_output( emit_polyhedral_surface, o->geometry().verts(), @@ -314,7 +314,7 @@ void TtlWktSerializer::write(const IfcGeom::TriangulationElement* o) filename_.stream << ttl_object_id(o) << " geo:hasGeometry " << ttl_object_id(o, "_footprint_geometry") << " .\n\n"; filename_.stream << ttl_object_id(o, "_footprint_geometry") << " a geo:Geometry ;\n"; filename_.stream << " geo:asWKT " << escape_for_turtle( - IfcUtil::convert_utf8_to_utf32( + IfcUtil::convert_utf8( capture_output( // @nb this is line_component, because this is the linestring // from a faceboundary, not the edges as pairs of indices. @@ -341,7 +341,7 @@ void TtlWktSerializer::write(const IfcGeom::TriangulationElement* o) } } filename_.stream << " geo:asWKT " << escape_for_turtle( - IfcUtil::convert_utf8_to_utf32( + IfcUtil::convert_utf8( capture_output( emit_line_strings, o->geometry().verts(), @@ -355,9 +355,9 @@ void TtlWktSerializer::write(const IfcGeom::BRepElement* brep_obj) { #ifdef IFOPSH_WITH_OPENCASCADE filename_.stream << ttl_object_id(brep_obj) << " a geo:Feature ;\n"; filename_.stream << " dcterms:identifier " << escape_for_turtle( - IfcUtil::convert_utf8_to_utf32(brep_obj->guid())) << " ;\n"; + IfcUtil::convert_utf8(brep_obj->guid())) << " ;\n"; filename_.stream << " rdfs:label " << escape_for_turtle( - IfcUtil::convert_utf8_to_utf32(brep_obj->name()) + IfcUtil::convert_utf8(brep_obj->name()) ) << " .\n"; // @todo unify logic with SVG serializer @@ -391,7 +391,12 @@ void TtlWktSerializer::write(const IfcGeom::BRepElement* brep_obj) { double x1, y1, zmin, x2, y2, zmax; bb.Get(x1, y1, zmin, x2, y2, zmax); - gp_Pln pln(gp_Pnt(0, 0, zmin + 1.), gp::DZ()); + auto height = zmax - zmin; + auto section_height = (height < (1. + 1.e-5)) ? (height / 2.0) : 1.0; + + filename_.stream << ttl_object_id(brep_obj) << " geo:hasMetricLength " << height << " .\n\n"; + + gp_Pln pln(gp_Pnt(0, 0, zmin + section_height), gp::DZ()); Handle(TopTools_HSequenceOfShape) wires = new TopTools_HSequenceOfShape(); @@ -431,7 +436,7 @@ void TtlWktSerializer::write(const IfcGeom::BRepElement* brep_obj) { filename_.stream << ttl_object_id(brep_obj) << " geo:hasGeometry " << ttl_object_id(brep_obj, postfix.c_str()) << " .\n\n"; filename_.stream << ttl_object_id(brep_obj, postfix.c_str()) << " a geo:Geometry ;\n"; filename_.stream << " geo:asWKT " << escape_for_turtle( - IfcUtil::convert_utf8_to_utf32( + IfcUtil::convert_utf8( capture_output( emit_line_component, loop_coords, diff --git a/src/serializers/schema_dependent/XmlSerializer.cpp b/src/serializers/schema_dependent/XmlSerializer.cpp index e98b0fbe4e..2a72f42637 100644 --- a/src/serializers/schema_dependent/XmlSerializer.cpp +++ b/src/serializers/schema_dependent/XmlSerializer.cpp @@ -362,7 +362,7 @@ ptree* descend(ifcopenshell::geometry::abstract_mapping* mapping, IfcSchema::Ifc } } -#ifdef SCHEMA_HAS_IfcAlignmentSegment +#if defined(SCHEMA_HAS_IfcAlignmentSegment) && defined(SCHEMA_IfcAlignmentSegment_HAS_DesignParameters) if (auto* als = product->as()) { ptree node; format_entity_instance(mapping, als->DesignParameters(), node, child, false); diff --git a/src/serializers/util.h b/src/serializers/util.h index cb7ad3e446..24ffd9485b 100644 --- a/src/serializers/util.h +++ b/src/serializers/util.h @@ -22,6 +22,8 @@ #include #include +#include +#include #include @@ -50,7 +52,7 @@ namespace util { void assign(const double& d) { this->d = d; } const double& value() const { return d; } double& value() { return d; } - std::string str() const { std::stringstream ss; ss << d; return ss.str(); } + std::string str() const { std::stringstream ss; ss << std::setprecision(std::numeric_limits::max_digits10) << d; return ss.str(); } virtual ~float_item() {}; }; private: diff --git a/src/svgfill b/src/svgfill index ff00026cd3..29fbc17ede 160000 --- a/src/svgfill +++ b/src/svgfill @@ -1 +1 @@ -Subproject commit ff00026cd359d83c8aebe43b550f7f75a04a359a +Subproject commit 29fbc17edec61b4f774ba8e87d4308983a75fc90 diff --git a/win/build-deps.cmd b/win/build-deps.cmd index 3e0874ba25..888a94533e 100644 --- a/win/build-deps.cmd +++ b/win/build-deps.cmd @@ -87,6 +87,11 @@ IF %VS_VER%==2008 set PATH=C:\Windows\Microsoft.NET\Framework\v3.5;%PATH% :: User-configurable build options IF NOT DEFINED IFCOS_INSTALL_PYTHON set IFCOS_INSTALL_PYTHON=TRUE +set PYTHON_VERSION=3.11 +py -%PYTHON_VERSION% --version 2>&1>NUL +IF %ERRORLEVEL%==0 set IFCOS_INSTALL_PYTHON=EXISTS +set PYTHON_VERSION=%PYTHON_VERSION%.7 + IF NOT DEFINED IFCOS_NUM_BUILD_PROCS set IFCOS_NUM_BUILD_PROCS=%NUMBER_OF_PROCESSORS% :: For subroutines @@ -143,6 +148,8 @@ echo Defaults to Build if not specified. Rebuild/Clean also uninstalls Pytho call cecho.cmd 0 13 "* IFCOS_INSTALL_PYTHON`t= %IFCOS_INSTALL_PYTHON%" echo - Download and install Python. echo Set to something other than TRUE if you wish to use an already installed version of Python. +echo EXISTS value is set automatically if same Python version is already found on the system +echo and we won't be able to install it again. call cecho.cmd 0 13 "* IFCOS_NUM_BUILD_PROCS`t= %IFCOS_NUM_BUILD_PROCS%" echo - How many MSBuild.exe processes may be run in parallel. echo Defaults to NUMBER_OF_PROCESSORS. Used also by other IfcOpenShell build scripts. @@ -167,16 +174,16 @@ echo. cd "%DEPS_DIR%" :: VERSIONS -set HDF5_VERSION=1.8.22 -set HDF5_VERSION_MAJOR=1.8 +set HDF5_VERSION=1.12.1 +set HDF5_VERSION_MAJOR=1.12 set OCCT_VERSION=7.8.1 :: NOTE If updating the default Python version, change PY_VER_MAJOR_MINOR accordingly in run-cmake.bat -set PYTHON_VERSION=3.11.7 +set PYTHON_VERSION=%PYTHON_VERSION% :: VERSION DERIVATIONS set OCC_INCLUDE_DIR=%INSTALL_DIR%\opencascade-%OCCT_VERSION%\inc>>"%~dp0\%BUILD_DEPS_CACHE_PATH%" set OCC_LIBRARY_DIR=%INSTALL_DIR%\opencascade-%OCCT_VERSION%\win%ARCH_BITS%\lib>>"%~dp0\%BUILD_DEPS_CACHE_PATH%" -for /f "tokens=1,2,3 delims=." %%a in ("%PYTHON_VERSION%") do ( +for /f "tokens=1,2,3 delims=." %%a in ("%PYTHON_VERSION%") do ( set PY_VER_MAJOR_MINOR=%%a%%b ) IF "%IFCOS_INSTALL_PYTHON%"=="TRUE" ( @@ -253,7 +260,7 @@ git clean -fdx REM There probably need to be quotes here around the filename powershell -c "get-content %~dp0patches\mpir.patch | %%{$_ -replace \"sdk\",\"%UCRTVersion%\"} | %%{$_ -replace \"fn\",\"lib_mpir_cxx\"}" | git apply --unidiff-zero --ignore-whitespace IF NOT %ERRORLEVEL%==0 GOTO :Error -powershell -c "get-content %~dp0patches\mpir.patch | %%{$_ -replace \"sdk\",\"%UCRTVersion%\"} | %%{$_ -replace \"fn\",\"lib_mpir_gc\"}" | git apply --unidiff-zero --ignore-whitespace +powershell -c "get-content %~dp0patches\mpir.patch | %%{$_ -replace \"sdk\",\"%UCRTVersion%\"} | %%{$_ -replace \"fn\",\"lib_mpir_gc\"}" | git apply --unidiff-zero --ignore-whitespace IF NOT %ERRORLEVEL%==0 GOTO :Error if NOT "%USE_STATIC_RUNTIME%"=="FALSE" git apply "%~dp0patches\mpir_runtime.patch" --unidiff-zero --ignore-whitespace IF NOT %ERRORLEVEL%==0 GOTO :Error @@ -301,26 +308,33 @@ IF NOT %ERRORLEVEL%==0 GOTO :Error :HDF5 set DEPENDENCY_NAME=hdf5 -set DEPENDENCY_DIR=%DEPS_DIR% +set DEPENDENCY_DIR=%DEPS_DIR%\hdf5-%HDF5_VERSION% cd "%DEPENDENCY_DIR%" -set HDF5_CMAKE_ZIP=CMake-hdf5-%HDF5_VERSION%.zip -set HDF5_INSTALL_ZIP_NAME=HDF5-%HDF5_VERSION%-win%ARCH_BITS% +set HDF5_CMAKE_ZIP=hdf5-%HDF5_VERSION%.zip +set HDF5_INSTALL_NAME=HDF5-%HDF5_VERSION%-win%ARCH_BITS% -IF EXIST "%INSTALL_DIR%\%HDF5_INSTALL_ZIP_NAME%" ( - echo Found existing "%INSTALL_DIR%\%HDF5_INSTALL_ZIP_NAME%", skipping +IF EXIST "%INSTALL_DIR%\%HDF5_INSTALL_NAME%" ( + echo Found existing "%INSTALL_DIR%\%HDF5_INSTALL_NAME%", skipping goto :Boost ) if "%ARCH_BITS%"=="64" set ARCH_BITS_64=64 -call :DownloadFile http://support.hdfgroup.org/ftp/HDF5/releases/hdf5-%HDF5_VERSION_MAJOR%/hdf5-%HDF5_VERSION%/src/CMake-hdf5-%HDF5_VERSION%.zip "%DEPS_DIR%" %HDF5_CMAKE_ZIP% +call :DownloadFile ^ + http://support.hdfgroup.org/ftp/HDF5/releases/hdf5-%HDF5_VERSION_MAJOR%/hdf5-%HDF5_VERSION%/src/%HDF5_CMAKE_ZIP% ^ + "%DEPS_DIR%" %HDF5_CMAKE_ZIP% IF NOT %ERRORLEVEL%==0 GOTO :Error -call :ExtractArchive %HDF5_CMAKE_ZIP% "%DEPS_DIR%" "%DEPS_DIR%\CMake-hdf5-%HDF5_VERSION%" +call :ExtractArchive %HDF5_CMAKE_ZIP% "%DEPS_DIR%" "%DEPS_DIR%\hdf5-%HDF5_VERSION%" +IF NOT %ERRORLEVEL%==0 GOTO :Error +pushd "%DEPS_DIR%\hdf5-%HDF5_VERSION%" +call :RunCMake -DCMAKE_INSTALL_PREFIX="%INSTALL_DIR%\%HDF5_INSTALL_NAME%" ^ + -DHDF5_ENABLE_Z_LIB_SUPPORT=OFF -DBUILD_TESTING=OFF ^ + -DHDF5_BUILD_TOOLS=OFF -DHDF5_BUILD_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DHDF5_BUILD_UTILS=OFF ^ + -DHDF5_BUILD_CPP_LIB=ON +IF NOT %ERRORLEVEL%==0 GOTO :Error +call :BuildSolution "%DEPENDENCY_DIR%\%BUILD_DIR%\HDF5.sln" %DEBUG_OR_RELEASE% +IF NOT %ERRORLEVEL%==0 GOTO :Error +call :InstallCMakeProject "%DEPENDENCY_DIR%\%BUILD_DIR%" %DEBUG_OR_RELEASE% IF NOT %ERRORLEVEL%==0 GOTO :Error -pushd "%DEPS_DIR%\CMake-hdf5-%HDF5_VERSION%" -git apply %~dp0patches\hdf5vs2022.patch --ignore-whitespace -rem It is not checked whether this patch is applied successfully! -ctest -S HDF5config.cmake,BUILD_GENERATOR=VS%VS_VER%%ARCH_BITS_64% -C %BUILD_CFG% -V -O hdf5.log -call :ExtractArchive %HDF5_INSTALL_ZIP_NAME%.zip "%INSTALL_DIR%" "%INSTALL_DIR%\%HDF5_INSTALL_ZIP_NAME%" popd :: Note all of the dependencies have appropriate label so that user can easily skip something if wanted @@ -360,7 +374,7 @@ call cecho.cmd 0 13 "Building %DEPENDENCY_NAME% %BOOST_LIBS% Please be patient, IF EXIST "%DEPENDENCY_DIR%\bin.v2\project-cache.jam" del "%DEPS_DIR%\boost\bin.v2\project-cache.jam" call .\b2 toolset=%BOOST_TOOLSET% runtime-link=shared address-model=%ARCH_BITS% --abbreviate-paths -j%IFCOS_NUM_BUILD_PROCS% ^ - variant=%DEBUG_OR_RELEASE_LOWERCASE% %BOOST_WIN_API% %BOOST_LIBS% stage --stagedir=stage/%GEN_SHORTHAND% + variant=%DEBUG_OR_RELEASE_LOWERCASE% %BOOST_WIN_API% %BOOST_LIBS% stage --stagedir=stage/%GEN_SHORTHAND% IF NOT %ERRORLEVEL%==0 GOTO :Error @@ -427,7 +441,7 @@ if not %ERRORLEVEL%==0 goto :Error call :BuildSolution "%DEPENDENCY_DIR%\%BUILD_DIR%\freetype.sln" %BUILD_CFG% if not %ERRORLEVEL%==0 goto :Error call :InstallCMakeProject "%DEPENDENCY_DIR%\%BUILD_DIR%" %BUILD_CFG% -if not %ERRORLEVEL%==0 goto :Error +if not %ERRORLEVEL%==0 goto :Error set DEPENDENCY_NAME=Open CASCADE %OCCT_VERSION% set DEPENDENCY_DIR=%DEPS_DIR%\occt_git @@ -500,6 +514,12 @@ IF "%IFCOS_INSTALL_PYTHON%"=="TRUE" ( IF NOT EXIST "%PYTHONHOME%". ( call cecho.cmd 0 13 "Installing %DEPENDENCY_NAME%. Please be patient, this will take a while." start /w %PYTHON_INSTALLER% /quiet TargetDir="%PYTHONHOME%" + if errorlevel 1 ( + :: Standard installer doesn't support installing same Python version twice, + :: but we skip installation during IFCOS_INSTALL_PYTHON initialization. + call cecho.cmd 0 12 "Failed to install Python. Error code: !ERRORLEVEL!." + GOTO :Error + ) ) ELSE ( call cecho.cmd 0 13 "%DEPENDENCY_NAME% already installed. Skipping." ) @@ -539,7 +559,7 @@ IF EXIST "%INSTALL_DIR%\cgal" ( set DEPENDENCY_NAME=cgal set DEPENDENCY_DIR=%DEPS_DIR%\cgal -call :GitCloneAndCheckoutRevision https://github.com/CGAL/cgal.git "%DEPENDENCY_DIR%" v5.2.3 +call :GitCloneAndCheckoutRevision https://github.com/CGAL/cgal.git "%DEPENDENCY_DIR%" v5.5.5 IF NOT %ERRORLEVEL%==0 GOTO :Error cd "%DEPENDENCY_DIR%" git reset --hard @@ -561,6 +581,11 @@ IF NOT %ERRORLEVEL%==0 GOTO :Error :Eigen set DEPENDENCY_NAME=Eigen set DEPENDENCY_DIR=%INSTALL_DIR%\%DEPENDENCY_NAME% + +IF EXIST "%INSTALL_DIR%\%DEPENDENCY_NAME%" ( + echo Found existing "%INSTALL_DIR%\%DEPENDENCY_NAME%", skipping + goto :Successful +) call :GitCloneAndCheckoutRevision https://gitlab.com/libeigen/eigen.git "%DEPENDENCY_DIR%" 3.3.9 :: :tbb @@ -576,7 +601,7 @@ call :GitCloneAndCheckoutRevision https://gitlab.com/libeigen/eigen.git "%DEPEND :: IF NOT %ERRORLEVEL%==0 GOTO :Error :: call :InstallCMakeProject "%DEPENDENCY_DIR%\%BUILD_DIR%" %BUILD_CFG% :: IF NOT %ERRORLEVEL%==0 GOTO :Error -:: +:: :: :usd :: set DEPENDENCY_NAME=usd :: set DEPENDENCY_DIR=%DEPS_DIR%\usd @@ -709,7 +734,7 @@ call git checkout %3 set RET=%ERRORLEVEL% popd exit /b %RET% - + :: RunCMake - Runs CMake for a CMake-based project :: Params: %* cmakeOptions :: NOTE cd to root CMakeLists.txt folder before calling this if the CMakeLists.txt is not in the repo root. @@ -718,7 +743,7 @@ call cecho.cmd 0 13 "Running CMake for %DEPENDENCY_NAME%." IF NOT EXIST %BUILD_DIR%. mkdir %BUILD_DIR% IF NOT %ERRORLEVEL%==0 GOTO :Error pushd %BUILD_DIR% -:: TODO make deleting cache a parameter for this subroutine? We probably want to delete the +:: TODO make deleting cache a parameter for this subroutine? We probably want to delete the :: cache always e.g. when we've had new changes in the repository. IF %BUILD_TYPE%==Rebuild IF EXIST CMakeCache.txt. del CMakeCache.txt diff --git a/win/patches/hdf5vs2022.patch b/win/patches/hdf5vs2022.patch deleted file mode 100644 index e5a803f47f..0000000000 --- a/win/patches/hdf5vs2022.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- HDF5config.cmake 2021-02-05 14:38:05.000000000 +0000 -+++ HDF5config.cmake 2022-01-21 10:41:19.705646800 +0000 -@@ -110,7 +110,19 @@ - if (WIN32 AND NOT MINGW) - set (SITE_OS_NAME "Windows") - set (SITE_OS_VERSION "WIN10") -- if (BUILD_GENERATOR STREQUAL "VS201964") -+ if (BUILD_GENERATOR STREQUAL "VS202264") -+ set (CTEST_CMAKE_GENERATOR "Visual Studio 17 2022") -+ set (CMAKE_GENERATOR_ARCHITECTURE "x64") -+ set (SITE_OS_BITS "64") -+ set (SITE_COMPILER_NAME "vs2022") -+ set (SITE_COMPILER_VERSION "17") -+ elseif (BUILD_GENERATOR STREQUAL "VS2022") -+ set (CTEST_CMAKE_GENERATOR "Visual Studio 17 2022") -+ set (CMAKE_GENERATOR_ARCHITECTURE "Win32") -+ set (SITE_OS_BITS "32") -+ set (SITE_COMPILER_NAME "vs2022") -+ set (SITE_COMPILER_VERSION "17") -+ elseif (BUILD_GENERATOR STREQUAL "VS201964") - set (CTEST_CMAKE_GENERATOR "Visual Studio 16 2019") - set (CMAKE_GENERATOR_ARCHITECTURE "x64") - set (SITE_OS_BITS "64") diff --git a/win/run-cmake.bat b/win/run-cmake.bat index abadbdf96c..c1ad00beef 100755 --- a/win/run-cmake.bat +++ b/win/run-cmake.bat @@ -91,15 +91,14 @@ set OPENCOLLADA_INCLUDE_DIR=%INSTALL_DIR%\OpenCOLLADA\include\opencollada set OPENCOLLADA_LIBRARY_DIR=%INSTALL_DIR%\OpenCOLLADA\lib\opencollada set LIBXML2_INCLUDE_DIR=%DEPS_DIR%\OpenCOLLADA\Externals\LibXML\include set LIBXML2_LIBRARIES=%INSTALL_DIR%\OpenCOLLADA\lib\opencollada\xml.lib -set HDF5_INCLUDE_DIR=%INSTALL_DIR%\HDF5-%HDF5_VERSION%-win%ARCH_BITS%\include -set HDF5_LIBRARY_DIR=%INSTALL_DIR%\HDF5-%HDF5_VERSION%-win%ARCH_BITS%\lib +set HDF5_INSTALL_DIR=%INSTALL_DIR%\HDF5-%HDF5_VERSION%-win%ARCH_BITS% if not defined PY_VER_MAJOR_MINOR set PY_VER_MAJOR_MINOR=311 if not defined PYTHONHOME set PYTHONHOME=%INSTALL_DIR%\Python%PY_VER_MAJOR_MINOR% set PYTHON_INCLUDE_DIR=%PYTHONHOME%\include set PYTHON_LIBRARY=%PYTHONHOME%\libs\python%PY_VER_MAJOR_MINOR%.lib set PYTHON_EXECUTABLE=%PYTHONHOME%\python.exe -set SWIG_DIR=%INSTALL_DIR%\swigwin -set PATH=%PATH%;%SWIG_DIR%;%PYTHONHOME% +set SWIG_EXECUTABLE=%SWIG_DIR%\swig.exe +set PATH=%PATH%;%PYTHONHOME% set JSON_INCLUDE_DIR=%INSTALL_DIR%\json if not defined ADD_COMMIT_SHA set ADD_COMMIT_SHA=Off @@ -131,8 +130,7 @@ echo OPENCOLLADA_INCLUDE_DIR = %OPENCOLLADA_INCLUDE_DIR% echo OPENCOLLADA_LIBRARY_DIR = %OPENCOLLADA_LIBRARY_DIR% echo LIBXML2_INCLUDE_DIR = %LIBXML2_INCLUDE_DIR% echo LIBXML2_LIBRARIES = %LIBXML2_LIBRARIES% -echo HDF5_INCLUDE_DIR = %HDF5_INCLUDE_DIR% -echo HDF5_LIBRARY_DIR = %HDF5_LIBRARY_DIR% +echo HDF5_INSTALL_DIR = %HDF5_INSTALL_DIR% echo PYTHONHOME = %PYTHONHOME% echo PYTHON_INCLUDE_DIR = %PYTHON_INCLUDE_DIR% echo PYTHON_LIBRARY = %PYTHON_LIBRARY% @@ -161,9 +159,15 @@ if not (%1)==() if exist CMakeCache.txt. del /Q CMakeCache.txt call cecho.cmd 0 13 "Running CMake for %PROJECT_NAME%." IF NOT "%VS_TOOLSET_HOST%"=="" ( - cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% -T %VS_TOOLSET_HOST% -DCMAKE_INSTALL_PREFIX="%CMAKE_INSTALL_PREFIX%" -DBoost_NO_BOOST_CMAKE=On -DADD_COMMIT_SHA=%ADD_COMMIT_SHA% %ARGUMENTS% + cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% -T %VS_TOOLSET_HOST% ^ + -DCMAKE_INSTALL_PREFIX="%CMAKE_INSTALL_PREFIX%" -DBoost_NO_BOOST_CMAKE=ON ^ + -DCMAKE_PREFIX_PATH="%HDF5_INSTALL_DIR%" ^ + -DADD_COMMIT_SHA=%ADD_COMMIT_SHA% %ARGUMENTS% ) ELSE ( - cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% -DCMAKE_INSTALL_PREFIX="%CMAKE_INSTALL_PREFIX%" -DBoost_NO_BOOST_CMAKE=On -DADD_COMMIT_SHA=%ADD_COMMIT_SHA% %ARGUMENTS% + cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% ^ + -DCMAKE_INSTALL_PREFIX="%CMAKE_INSTALL_PREFIX%" -DBoost_NO_BOOST_CMAKE=ON ^ + -DCMAKE_PREFIX_PATH="%HDF5_INSTALL_DIR%" ^ + -DADD_COMMIT_SHA=%ADD_COMMIT_SHA% %ARGUMENTS% ) IF NOT %ERRORLEVEL%==0 GOTO :Error diff --git a/win/vs-cfg.cmd b/win/vs-cfg.cmd index 8aee1508e9..b086d6db5a 100644 --- a/win/vs-cfg.cmd +++ b/win/vs-cfg.cmd @@ -110,7 +110,7 @@ IF "!GENERATOR!"=="" IF NOT "%VisualStudioVersion%"=="" ( set GENERATOR_CHECK=%GENERATOR: Win64=% cmake --help | findstr /c:%GENERATOR_CHECK% >nul if not %ERRORLEVEL%==0 ( -call utils\cecho.cmd 0 12 "%~nx0: The used CMake version does not support '`"!GENERATOR!`'"- cannot proceed." +call utils\cecho.cmd 0 12 "%~nx0: The used CMake version does not support generator '`"!GENERATOR!`'"- cannot proceed." exit /b 1 )