Compare commits

...

57 Commits

Author SHA1 Message Date
Thomas Krijnen 2fe491b7b5 Merge remote-tracking branch 'origin/v0.8.0' into test-ci 2025-10-27 06:54:10 +01:00
Thomas Krijnen 83f7416679 Revert "[TMP] start a RelWithDebInfo build"
This reverts commit 9c8f3be52a.
2025-10-26 12:04:49 +01:00
Thomas Krijnen dc5ac872e3 Revert "[TEMP] IFCOS_SCHEMAS=4 probably disk is full with RelWithDebInfo"
This reverts commit e05318162d.
2025-10-26 12:04:46 +01:00
Thomas Krijnen e05318162d [TEMP] IFCOS_SCHEMAS=4 probably disk is full with RelWithDebInfo 2025-10-26 11:52:52 +01:00
Thomas Krijnen 9c8f3be52a [TMP] start a RelWithDebInfo build 2025-10-26 10:11:40 +01:00
Thomas Krijnen c4a992a9b5 Merge remote-tracking branch 'origin/v0.8.0' into test-ci 2025-10-26 09:50:01 +01:00
Andrej730 6d4f45c586 ifcwrap cmake - fix issue with debug build after migration to FindPython
In FindPython variable was renamed to Python_LIBRARY_DEBUG
2025-10-24 13:53:22 +05:00
Andrej730 0f9030e33b ifcwrap cmake - avoid using deprecated swig_link_libraries 2025-10-24 13:28:33 +05:00
Andrej730 ab0b44c8a2 ifcwrap cmake - use usual name for wrapper target 2025-10-24 12:52:37 +05:00
Andrej730 37e9a0f4fe cache_dependencies.py - fix tarfile deprecation warnings in 3.12-3.13 2025-10-24 12:33:44 +05:00
Andrej730 0787eabdab ifcwrap cmake - move DEBUG_POSTFIX logic to one place 2025-10-23 20:03:12 +05:00
Andrej730 0a34a37d65 cmake - build without Python libraries on Unix 2025-10-23 20:02:03 +05:00
Andrej730 280976f77c ifcwrap cmake - accomodate bug in cmake makefiles generator 2025-10-23 17:15:17 +05:00
Andrej730 4c53f4359d build-all - use shutil.which instead of custom function
To make it more idiomatic and custom function also was misinterpreting folders with the same name as existing executables.
2025-10-23 17:15:17 +05:00
Andrej730 0a547a14ea build_pyodide - use ccache 2025-10-23 17:15:17 +05:00
Andrej730 206ec8776a build_rocky - switch to main branch ccache 2025-10-23 17:15:16 +05:00
Andrej730 c3b43d573e build_pyodide - use prebuilt pyodide build env 2025-10-23 16:18:10 +05:00
Andrej730 afb6befce6 build-all.py - switch to gmp ftpmirror
previous url is inaccessible
2025-10-22 13:53:17 +05:00
Andrej730 f42efbade2 build_pyodide - display logs in github action output 2025-10-22 13:53:17 +05:00
Andrej730 968aa710ee build_pyodide - upload build logs for debugging 2025-10-22 13:53:17 +05:00
Andrej730 61b03dc60c build_pyodide - pack cached dependencies 2025-10-22 13:53:16 +05:00
Andrej730 e87597b8d7 build_osx - remove setup-python
as it's already preinstalled on the image
2025-10-22 10:37:45 +05:00
Andrej730 b12dddc2f0 build-all.py - print BUILD_DIR 2025-10-22 10:37:44 +05:00
Andrej730 f3076ba038 build_pyodide - no need to adjust version
since it's now inferred automatically
2025-10-22 10:37:44 +05:00
Andrej730 908a001ed8 build-all.py - accomodate pyodide build system automatically
as it's the main way to build the wasm package
2025-10-22 10:37:44 +05:00
Andrej730 1a8c47888a build_pyodide.yaml - move build_pyodide to a separate script 2025-10-22 10:37:44 +05:00
Andrej730 5e46a759a6 Add notes for pyodide building process 2025-10-22 10:37:44 +05:00
Andrej730 0e3f8cba8d build-all.py wasm - move SIDE_MODULE=2 to cmake
Next to `EXPORTED_FUNCTIONS` and avoid possible issues with pyodide in the future (pyodide is using `SIDE_MODULE=1`, but currently not providing it libraries of type `MODULE`, only for shared ones).
2025-10-22 10:37:43 +05:00
Andrej730 0ce12c9297 ifcwrap cmake - provide correct suffix for wasm python extension 2025-10-22 10:37:43 +05:00
Andrej730 ee8433655d pyodide setup.py - add dependencies
so micropip would be able to gather them automatically
2025-10-20 18:36:38 +05:00
Andrej730 29434d623c pyodide setup.py - add a note on not switching to pyproject.toml 2025-10-17 19:26:50 +05:00
Andrej730 092705ae68 pyodide setup.py - ensure we have correct pyodide_2025_0_wasm32 suffix
Adding extension, but avoiding adding sources as we're not actually building.
As a result build will have a correct suffix `pyodide_2025_0_wasm32.whl` that's pyodide is currently using and this is pyodide is plan to use when it's going to release to PyPI, see https://peps.python.org/pep-0783/
2025-10-17 19:26:50 +05:00
Andrej730 5f777691cf build-all.py - add a note about alternative gmp mirror 2025-10-17 17:16:36 +05:00
Andrej730 46913422b3 cmake - deduplicate ccache set_property code 2025-10-17 15:34:11 +05:00
Andrej730 ebcdabff07 build-all.py - don't pass side module flags as usual flags
As they're parsed by emcmake automatically https://github.com/pyodide/pyodide-build/blob/bdd0b1422064de6d92264a08c599e85001a28567/pyodide_build/tools/cmake/Modules/Platform/Emscripten.cmake#L105-L107
2025-10-17 15:34:11 +05:00
Andrej730 2cfa5201ca build-all.py - option to build wasm outside pyodide for debugging 2025-10-16 11:18:41 +05:00
Andrej730 8dafcc6b88 build-all.py - provide exported functions to target explicitly
Otherwise it broke compilation in some cases when cmake would try to test that linker is working.
2025-10-16 11:18:41 +05:00
Andrej730 626bb8b011 build-all - wasm to use packages instead of providing includes/libs paths explicitly
More complicated than I anticipated😅

Details
https://discourse.cmake.org/t/provide-absolute-paths-to-dependencies-with-cmake-find-root-path-search-during-cross-compilation-wasm/15241
2025-10-16 11:18:41 +05:00
Andrej730 721f74f045 build-all.py - move unsupported dependencies to script 2025-10-16 11:18:40 +05:00
Andrej730 31ace7c941 build-deps - use nuget to download ccache 2025-10-13 16:55:49 +05:00
Andrej730 612bd0b0a0 build-all.py - print IFCOS_SCHEMAS 2025-10-10 20:46:03 +05:00
Andrej730 3414d3c7ef build-all.py - document required wasm variables 2025-10-10 20:46:03 +05:00
Andrej730 6c1b3bcbdc build-all.py - fix issue building wasm on apple device 2025-10-10 17:48:29 +05:00
Andrej730 ceba8d51c5 build-all.py - remove --host none from wasm args
As it's now obsolete and producing warnings `configure: WARNING: the "none" host is obsolete, use --disable-assembly`.
2025-10-10 17:48:28 +05:00
Andrej730 0c3fbfa2b8 build-all.py - bump freetype to avoid issues on cmake 4+ 2025-10-10 17:48:28 +05:00
Andrej730 05805bb557 build-all.py - add note on --without-xxx option 2025-10-10 17:48:28 +05:00
Andrej730 3edb006c35 Add Python 3.14 builds 2025-10-09 17:13:07 +05:00
Andrej730 b36cd6442e build-deps - set NUGET_EXE from PATH if it's available 2025-10-08 18:37:15 +05:00
Andrej730 fbd6755666 build-all - deduce python components from python path 2025-10-08 18:37:15 +05:00
Andrej730 ffdee45cf9 ifcwrap cmake - migrate to FindPython
Also simplify run-cmake to just prefix the path, instead of providing 3 paths explicitly.
2025-10-08 17:16:34 +05:00
Andrej730 51bf4951b3 build-deps - support overriding PYTHON_VERSION 2025-10-07 11:50:43 +05:00
Andrej730 9c8ae71323 build-deps - download Python using nuget 2025-10-06 19:59:47 +05:00
Andrej730 8a95ec130f build-deps - download nuget
to later download python using it
2025-10-06 19:59:46 +05:00
Andrej730 b2f5e4b11c build-deps - include ccache for convenience 2025-10-06 19:59:46 +05:00
Andrej730 91a2504629 build_win - extend cache size 2025-10-06 19:59:46 +05:00
Andrej730 1eaf110d3d build_win - use fork to resolve cache misses and duplicated cache 2025-10-06 19:59:46 +05:00
Andrej730 9688db67b6 build_win - test ccache 2025-10-06 19:59:46 +05:00
22 changed files with 705 additions and 307 deletions
+4 -5
View File
@@ -34,11 +34,6 @@ jobs:
lfs: true
token: ${{ secrets.BUILD_REPO_TOKEN }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install Dependencies
run: |
brew update
@@ -72,6 +67,10 @@ jobs:
# We don't use gmpcxx, but it comes preinstalled on macos runner as arm64 bottle
# and CGAL detects it and breaks cross compilation.
brew uninstall --ignore-dependencies gmp
# Otherwise Python will fallback to use arm64 `pkg-config`,
# will pick up arm64 libraries ('zstd' in particular),
# and break the build.
/usr/local/bin/brew install pkg-config
# Required by Python.
/usr/local/bin/brew install gettext openssl
fi
+42 -21
View File
@@ -14,33 +14,54 @@ jobs:
submodules: recursive
path: IfcOpenShell
- name: Checkout Pyodide
- name: Checkout Build Repository
uses: actions/checkout@v3
with:
submodules: recursive
repository: pyodide/pyodide
ref: '0.28.0a3'
repository: IfcOpenShell/build-outputs
path: ifcopenshell_build
ref: wasm
lfs: true
token: ${{ secrets.BUILD_REPO_TOKEN }}
path: pyodide
- name: Unpack Dependencies
run: |
cd ifcopenshell_build
python ../IfcOpenShell/pyodide/cache_dependencies.py unpack
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ubuntu-22.04-${{ runner.arch }}
- name: Build
run: |
VERSION=`cat IfcOpenShell/VERSION`
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' >> 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/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}-cp313-cp313-emscripten_4_0_9_wasm32.whl
./IfcOpenShell/pyodide/build_pyodide.sh
FILE=`echo dist/ifcopenshell-*.whl`
NEW_FILE=`echo $FILE | sed "s/-/+${GITHUB_SHA:0:7}-/2"`
mv $FILE $NEW_FILE
- name: Upload Build Logs
if: always()
uses: actions/upload-artifact@v4
with:
name: build-logs-pyodide
path: |
ifcopenshell_build/*/*/logs/*.log
retention-days: 30
- name: Pack Dependencies
run: |
cd ifcopenshell_build
python ../IfcOpenShell/pyodide/cache_dependencies.py pack
- name: Commit and Push Changes to Build Repository
run: |
cd ifcopenshell_build
git config user.name "IfcOpenBot"
git config user.email "ifcopenbot@ifcopenshell.org"
git add */*/install/cache-*.tar.gz
git commit -m "Update build artifacts [skip ci]" || echo "No changes to commit"
git push || echo "Push failed"
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
+2 -2
View File
@@ -48,8 +48,8 @@ jobs:
[ -n "$install_root" ] && find "$install_root" -type f -name 'cache-*.tar.gz' -maxdepth 1 -exec tar -xzf {} -C "$install_root" \; || true
- name: ccache
# Using fork to support Rocky.
uses: Andrej730/ccache-action@main
# Latest release (1.2.19) doesn't support Rocky, so using a specific commit.
uses: hendrikmuhs/ccache-action@eca11c308176d48942455b9e5b1b70ff6950a778
with:
key: ubuntu-22.04-${{ runner.arch }}-rockylinux8
+2 -2
View File
@@ -48,8 +48,8 @@ jobs:
[ -n "$install_root" ] && find "$install_root" -type f -name 'cache-*.tar.gz' -maxdepth 1 -exec tar -xzf {} -C "$install_root" \; || true
- name: ccache
# Using fork to support Rocky.
uses: Andrej730/ccache-action@main
# Latest release (1.2.19) doesn't support Rocky, so using a specific commit.
uses: hendrikmuhs/ccache-action@eca11c308176d48942455b9e5b1b70ff6950a778
with:
key: ubuntu-22.04-${{ runner.arch }}-rockylinux8
+11 -49
View File
@@ -9,7 +9,6 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ['3.9.11', '3.10.3', '3.11.8', '3.12.1', '3.13.0']
arch: ['x64']
steps:
- name: Checkout Repository
@@ -30,14 +29,6 @@ jobs:
run: |
choco install -y sed 7zip.install awscli
- name: Install Python
run: |
$installer = "python-${{ matrix.python }}-amd64.exe"
$url = "https://www.python.org/ftp/python/${{ matrix.python }}/$installer"
Invoke-WebRequest -Uri $url -OutFile $installer
Start-Process -Wait -FilePath .\$installer -ArgumentList '/quiet InstallAllUsers=0 PrependPath=0 Include_test=0 TargetDir=C:\Python\${{ matrix.python }}'
Remove-Item .\$installer
- name: Unpack Dependencies
run: |
cd _deps-vs2022-x64-installed
@@ -45,21 +36,21 @@ jobs:
7z x $_.FullName
}
- name: Run Build Script
- name: ccache
# Use fork to resolve cache misses / duplicated cache entries on Windows.
uses: Andrej730/ccache-action@main
with:
key: win-${{ matrix.arch }}
# Windows ccache needs ~1GB
# and with default 500MB some cache gets deleted, leading to misses.
max-size: 5000MB
- name: Run Build Script And Pack .zip Archives
shell: cmd
run: |
setlocal EnableDelayedExpansion
SET PYTHON_VERSION=${{ matrix.python }}
for /f "tokens=1,2,3 delims=." %%a in ("%PYTHON_VERSION%") do (
set PY_VER_MAJOR_MINOR=%%a%%b
)
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 vs2022-x64 Release
SET PYTHONHOME=C:\Python\${{ matrix.python }}
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
python build-all-win.py
- name: Pack Dependencies
run: |
@@ -81,35 +72,6 @@ jobs:
git commit -m "Update build artifacts [skip ci]" || echo "No changes to commit"
git push || echo "Push failed"
- name: Package .zip Archives
run: |
$VERSION = 'v' + ((Get-Content VERSION).Trim())
$SHA = ${env:GITHUB_SHA}.Substring(0, 7)
$OUTPUT_DIR = "$env:USERPROFILE\output"
New-Item -ItemType Directory -Force -Path $OUTPUT_DIR
if ("${{ matrix.python }}" -eq "3.9.11") {
# only for the first python version the executables are assembled for upload
cd _installed-vs2022-x64/bin
Get-ChildItem -Path . | ForEach-Object {
echo $_
$exe = $_.Name
$baseName = $exe.Substring(0, $exe.Length - 4)
$zipName = "${baseName}-$VERSION-$SHA-win64.zip"
7z a $zipName $exe
}
mv *.zip $OUTPUT_DIR
}
$pyVersion = "${{ matrix.python }}"
$pyVersionMajor = ($pyVersion -split '\.')[0..1] -join ''
cd C:\Python\${{ matrix.python }}\Lib\site-packages
Remove-Item -Recurse -Force ifcopenshell\__pycache__ -ErrorAction SilentlyContinue
Get-ChildItem -Path ifcopenshell -Filter "*.pyc" -Recurse | Remove-Item -Force
$zipName = "ifcopenshell-python-$pyVersionMajor-$VERSION-$SHA-win64.zip"
7z a $zipName ifcopenshell
mv $zipName $OUTPUT_DIR
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
pyver: [py39, py310, py311, py312, py313]
pyver: [py39, py310, py311, py312, py313, py314]
config:
- {
name: "Windows 64bit",
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
strategy:
fail-fast: false
matrix:
pyver: [py39, py310, py311, py312, py313]
pyver: [py39, py310, py311, py312, py313, py314]
config:
- {
name: "Windows 64bit",
+10 -9
View File
@@ -27,12 +27,6 @@ endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
message(STATUS "`ccache` is found, using it as a compiler launcher.")
endif()
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
if(POLICY CMP0141) # 3.25+
@@ -127,8 +121,10 @@ if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND(NOT BUILD_IFCGEOM)
set(BUILD_IFCGEOM ON)
endif()
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
message(STATUS "`ccache` is found, using it as a compiler launcher.")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
if(MSVC)
# By default Visual Studio generators will use /Zi which is not compatible
# with ccache, so tell Visual Studio to use /Z7 instead.
@@ -212,6 +208,8 @@ if(NOT MINIMAL_BUILD)
UNIFY_ENVVARS_AND_CACHE(LIBXML2_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(LIBXML2_LIBRARIES)
UNIFY_ENVVARS_AND_CACHE(PCRE_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(PYTHON_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(PYTHON_LIBRARY)
UNIFY_ENVVARS_AND_CACHE(PYTHON_EXECUTABLE)
UNIFY_ENVVARS_AND_CACHE(HDF5_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(HDF5_LIBRARY_DIR)
@@ -453,8 +451,11 @@ if(NOT MINIMAL_BUILD)
clear_wasm_sysroot()
if(IFCXML_SUPPORT)
if((NOT LIBXML2_INCLUDE_DIR AND NOT LIBXML2_LIBRARIES))
# First try config mode (probably works with vcpkg, Conan, macOS brew installs, but not on ubuntu 22.04?)
find_package(LibXml2 QUIET CONFIG)
# First try config mode (probably works with vcpkg, Conan, macOS brew installs, but not on ubuntu 22.04)
# CONFIG is provided using root path, so no need to clear sysroot here.
restore_wasm_sysroot()
find_package(LibXml2 REQUIRED CONFIG)
clear_wasm_sysroot()
if(NOT LibXml2_FOUND)
# Fallback to CMake's builtin FindLibXml2 module (works on Ubuntu)
+157 -109
View File
@@ -33,6 +33,7 @@ Available arguments:
``-py-313`` - build for specific Python version
(building for all supported Python version by default).
``-wasm`` - compile for wasm
``-without-xxx`` - do not build dependency ``xxx`` (e.g. ``--without-swig``)
``-mac-cross-compile-intel`` - cross compile for Intel Mac on Apple Silicon host
``-shared`` - build shared libraries. By default will build static.
``-diskcleanup`` - clean up build directories after finishing building dependencies
@@ -52,6 +53,13 @@ Used environment variables:
- ``IFCOS_SCHEMAS`` - schemas to be built; defaults to cmake default (IFC2X3; IFC4; IFC4X3_ADD2) - to be supplied as `2x3;4`
- ``USE_OCCT`` - whether to use official Open CASCADE instead of Community Edition
(`true` by default, any other value is considered `false`)
- ``WASM_PYTHON_PATH`` - path to WASM Python installation,
used to deduce `PYVERSION` (e.g. '3.13.2'), `PYTHONINCLUDE`,
`SIDE_MODULE_CFLAGS`, `SIDE_MODULE_LDFLAGS`.
Allows to build wasm without pyodide build environment, which can be useful for debugging build issues.
Example value: 'pyodide/cpython/installs/python-3.13.2'
- ``WASM_TOOLCHAIN_FILE`` - path to emscripten toolchain file from pyodide ('Emscripten.cmake')
needed only if ``WASM_PYTHON_PATH`` is provided.
- ``ADD_COMMIT_SHA`` - if defined with any non-empty value then
`ADD_COMMIT_SHA` and `VERSION_OVERRIDE` will be set to `ON` while configuring IfcOpenShell
@@ -135,7 +143,7 @@ PROJECT_NAME = "IfcOpenShell"
USE_CURRENT_PYTHON_VERSION = os.getenv("USE_CURRENT_PYTHON_VERSION")
ADD_COMMIT_SHA = os.getenv("ADD_COMMIT_SHA")
PYTHON_VERSIONS = ["3.9.11", "3.10.3", "3.11.8", "3.12.1", "3.13.6"]
PYTHON_VERSIONS = ["3.9.11", "3.10.3", "3.11.8", "3.12.1", "3.13.6", "3.14.0"]
JSON_VERSION = "3.11.3"
OCE_VERSION = "0.18.3"
OCCT_VERSION = "7.8.1"
@@ -195,19 +203,40 @@ def cecho(message, color=NO_COLOR):
logger.info(f"{color}{message}\033[0m")
def which(cmd: str) -> Union[str, None]:
PATH = os.getenv("PATH")
assert PATH
for path in PATH.split(":"):
if os.path.exists(path) and cmd in os.listdir(path):
return cmd
return None
# Flags.
APPLE = platform.system() == "Darwin"
MAC_CROSS_COMPILE_INTEL = "mac-cross-compile-intel" in flags
assert platform.system() == "Darwin" or not MAC_CROSS_COMPILE_INTEL
WASM = "wasm" in flags
"""Build WASM outside pyodide build environment."""
WASM_DEBUG = False
if WASM:
if "WASM_PYTHON_PATH" in os.environ:
wasm_python_path = os.environ["WASM_PYTHON_PATH"]
# Deduce version from path, assuming format .../python-X.Y.Z
version_match = re.search(r"python-(\d+)\.(\d+)\.(\d+)", wasm_python_path)
assert version_match, f"Could not deduce python version from '{wasm_python_path}'"
python_version = version_match.group(1)
os.environ["PYVERSION"] = python_version
os.environ["PYTHONINCLUDE"] = f"{wasm_python_path}/include/python{python_version.rpartition('.')[0]}"
os.environ["SIDE_MODULE_CFLAGS"] = ""
# Required, otherwise library will compile as .a, not .so.
os.environ["SIDE_MODULE_LDFLAGS"] = "-s SIDE_MODULE=1"
# We're outside pyodide build environment, so need to provide toolchain file ourselves.
assert "WASM_TOOLCHAIN_FILE" in os.environ, "WASM_TOOLCHAIN_FILE must be set when WASM_PYTHON_PATH is provided"
WASM_DEBUG = True
required_vars = (
# E.g. '3.13.2'.
"PYVERSION",
# 'include' folder in WASM-Python installation.
# e.g. '/pyodide/cpython/installs/python-3.13.2/include/python3.13'
"PYTHONINCLUDE",
"SIDE_MODULE_CFLAGS",
"SIDE_MODULE_LDFLAGS",
)
missing_vars = [v for v in required_vars if v not in os.environ]
assert not missing_vars, f"Some variables required for WASM compilation are missing: {', '.join(missing_vars)}"
# Set defaults for missing empty environment variables
@@ -223,9 +252,11 @@ if platform.system() == "Darwin":
IFCOS_NUM_BUILD_PROCS = os.getenv("IFCOS_NUM_BUILD_PROCS", multiprocessing.cpu_count() + 1)
CMAKE_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "cmake"))
SCRIPT_PATH = Path(__file__).parent
REPO_PATH = SCRIPT_PATH.parent
CMAKE_DIR = (REPO_PATH / "cmake").resolve().__str__()
build_dir = os.environ.get("BUILD_DIR", os.path.join(os.path.dirname(__file__), "..", "build"))
BUILD_DIR = os.environ.get("BUILD_DIR", (REPO_PATH / "build").__str__())
if WASM:
@@ -234,7 +265,7 @@ elif MAC_CROSS_COMPILE_INTEL:
arch = "x86_64"
else:
arch = platform.machine()
DEFAULT_DEPS_DIR = Path(build_dir) / platform.system() / arch
DEFAULT_DEPS_DIR = Path(BUILD_DIR) / platform.system() / arch
if TOOLSET:
DEFAULT_DEPS_DIR = DEFAULT_DEPS_DIR / TOOLSET
@@ -266,6 +297,7 @@ if USE_OCCT:
cecho(" - Compiling against official Open Cascade")
else:
cecho(" - Compiling against Open Cascade Community Edition")
cecho(f"* Build Directory = {BUILD_DIR}", MAGENTA)
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)
@@ -282,6 +314,11 @@ cecho(
""" - How many compiler processes may be run in parallel.
"""
)
cecho(f" * IFCOS_SCHEMAS = '{os.environ.get('IFCOS_SCHEMAS')}'", MAGENTA)
cecho(
""" - IFC Schemas to compile. If not provided, fallback to default provided in cmake.
"""
)
dependency_tree: "dict[str, tuple[str, ...]]" = {
"IfcParse": ("boost", "libxml2", "hdf5", "rocksdb"),
@@ -357,6 +394,23 @@ else:
targets = set(dependency_tree.keys())
targets = set(t for t in targets if "without-%s" % t.lower() not in flags)
if WASM:
SKIP_TARGETS_FOR_WASM = {
"hdf5",
"rocksdb",
"opencollada",
"swig",
"pcre",
"pcre2",
"IfcGeom",
"IfcConvert",
"IfcGeomServer",
}
SKIP_TARGETS_FOR_WASM = {t.lower() for t in SKIP_TARGETS_FOR_WASM}
skip_targets = {t for t in targets if t.lower() in SKIP_TARGETS_FOR_WASM}
if skip_targets:
cecho(f"Skipping targets for wasm build: {', '.join(sorted(skip_targets))}", YELLOW)
targets.difference_update(skip_targets)
print("Building:", *sorted(targets, key=lambda t: len(list(gather_dependencies(t)))))
@@ -365,16 +419,20 @@ yacc = "yacc" # Used during swig building process, installed with `bison` on De
missing_commands: "list[str]" = []
required_commands = [git, bunzip2, tar, cc, cplusplus, autoconf, automake, make, "patch", "cmake", yacc, xz]
if "wasm" in flags:
required_commands.remove(yacc) # yacc not needed for wasm builds
# Skip swig build for WASM.
required_commands.append("swig")
required_commands.remove(yacc)
for cmd in required_commands:
if which(cmd) is None:
if shutil.which(cmd) is None:
missing_commands.append(cmd)
if missing_commands:
raise ValueError(f"Required tools not installed or not added to PATH: {', '.join(missing_commands)}")
MAC_INTEL_BIN_PATH = "/usr/local/bin"
if MAC_CROSS_COMPILE_INTEL:
brew = "/usr/local/bin/brew"
brew = f"{MAC_INTEL_BIN_PATH}/brew"
assert os.path.exists(brew), f"For intel cross compilation the brew path is expected to be '{brew}'."
# identifiers for the download tool (could be less memory consuming as ints, but are more verbose as strings)
@@ -398,6 +456,13 @@ except:
pass
def restore_env(var_name: str, old_value: Union[str, None]) -> None:
if old_value is None:
del os.environ[var_name]
else:
os.environ[var_name] = old_value
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,
@@ -637,7 +702,7 @@ def build_dependency(
if isinstance(patch, str):
patch = [patch]
for p in patch:
patch_abs = os.path.abspath(os.path.join(os.path.dirname(__file__), p))
patch_abs = (SCRIPT_PATH / p).absolute().__str__()
if os.path.exists(patch_abs):
try:
run(["patch", "-p1", "--batch", "--forward", "-i", patch_abs], cwd=extract_dir)
@@ -724,12 +789,13 @@ LDFLAGS = os.environ.get("LDFLAGS", "")
ADDITIONAL_ARGS_STR = " ".join(ADDITIONAL_ARGS)
CXXFLAGS_MINIMAL = f"{CXXFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
CFLAGS_MINIMAL = f"{CFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
if "wasm" in flags:
CFLAGS_MINIMAL = CXXFLAGS_MINIMAL = CFLAGS = CXXFLAGS = os.environ["SIDE_MODULE_CFLAGS"]
LDFLAGS = os.environ["SIDE_MODULE_LDFLAGS"]
# WASM `SIDE_MODULE_` are absorbed by `emcmake` automatically.
CXXFLAGS = CXXFLAGS_MINIMAL
CFLAGS = CFLAGS_MINIMAL
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:
CXXFLAGS = f"{CXXFLAGS} {PIC} -fdata-sections -ffunction-sections -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
CFLAGS = f"{CFLAGS} {PIC} -fdata-sections -ffunction-sections -fvisibility=hidden {ADDITIONAL_ARGS_STR}"
@@ -738,8 +804,6 @@ elif sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev
CFLAGS = CFLAGS_MINIMAL
LDFLAGS = f"{LDFLAGS} -Wl,--gc-sections {ADDITIONAL_ARGS_STR}"
else:
CXXFLAGS_MINIMAL = f"{CXXFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
CFLAGS_MINIMAL = f"{CFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
if BUILD_STATIC:
CXXFLAGS = f"{CXXFLAGS} {PIC} -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
CFLAGS = f"{CFLAGS} {PIC} -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
@@ -819,9 +883,9 @@ if "eigen" in targets:
)
if "pcre" in targets:
OLD_CC, OLD_CCXX = None, None
OLD_CC, OLD_CXX = None, None
if MAC_CROSS_COMPILE_INTEL:
OLD_CC, OLD_CCXX = os.environ.get("CC"), os.environ.get("CXX")
OLD_CC, OLD_CXX = os.environ.get("CC"), os.environ.get("CXX")
os.environ["CC"] = MAC_CROSS_COMPILE_INTEL_CC
os.environ["CXX"] = MAC_CROSS_COMPILE_INTEL_CXX
# Keep it autoconf as OpenCOLLADA is pretty old and might break
@@ -834,14 +898,8 @@ if "pcre" in targets:
download_name=f"pcre-{PCRE_VERSION}.tar.bz2",
)
if MAC_CROSS_COMPILE_INTEL:
if OLD_CC is None:
del os.environ["CC"]
else:
os.environ["CC"] = OLD_CC
if OLD_CCXX is None:
del os.environ["CXX"]
else:
os.environ["CXX"] = OLD_CCXX
restore_env("CC", OLD_CC)
restore_env("CXX", OLD_CXX)
if "pcre2" in targets:
build_dependency(
@@ -873,7 +931,7 @@ if "freetype" in targets:
download_url="https://github.com/freetype/freetype",
download_name="freetype2",
download_tool=download_tool_git,
revision="VER-2-11-1",
revision="VER-2-14-0",
)
if USE_OCCT and "occ" in targets:
@@ -1012,6 +1070,7 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag
# 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: "list[str]" = []
original_path = ""
if platform.system() == "Darwin":
PYTHON_CONFIGURE_ARGS = ["--enable-shared"]
open_ssl_prefix = run([brew, "--prefix", "openssl@3"]).strip()
@@ -1020,6 +1079,10 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag
PYTHON_CONFIGURE_ARGS.append(f"--with-openssl={open_ssl_prefix}")
if MAC_CROSS_COMPILE_INTEL:
original_path = os.environ["PATH"]
# Need to ensure python will pick up intel's `pkg-config`,
# otherwise it might attempt to use ARM libraries (e.g. `zstd`) and fail.
os.environ["PATH"] = f"{MAC_INTEL_BIN_PATH}{os.pathsep}{original_path}"
PYTHON_CONFIGURE_ARGS.extend(["--with-universal-archs=intel-64", "--enable-universalsdk"])
for PYTHON_VERSION in PYTHON_VERSIONS:
@@ -1041,6 +1104,9 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag
if not os.path.exists(os.path.join(DEPS_DIR, "install", f"python-{PYTHON_VERSION}")):
raise e
if MAC_CROSS_COMPILE_INTEL:
assert original_path
os.environ["PATH"] = original_path
os.environ["CPPFLAGS"] = OLD_CPP_FLAGS
os.environ["CXXFLAGS"] = OLD_CXX_FLAGS
os.environ["CFLAGS"] = OLD_C_FLAGS
@@ -1086,8 +1152,15 @@ if "boost" in targets:
if "cgal" in targets:
gmp_args: "list[str]" = []
mpfr_args: "list[str]" = []
if "wasm" in flags:
gmp_args.extend(("--disable-assembly", "--host", "none", "--enable-cxx"))
OLD_HOST_CC = None
if WASM:
if APPLE:
# Override `HOST_CC`, otherwise `emcc` will try to use it's own `clang` which can only build
# wasm executables and build will fail.
os.environ["HOST_CC"] = "clang"
# Disable assembly, otherwise `emcc -c conftest.s` will crash due to assembly mismatch.
gmp_args.extend(("--disable-assembly", "--enable-cxx"))
mpfr_args.extend(("--host", "none"))
OLD_CC = None
@@ -1102,10 +1175,14 @@ if "cgal" in targets:
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/",
# Sometimes ftp.gnu.org is very slow, use ftpmirror.gnu.org as a workaround.
download_url="https://ftpmirror.gnu.org/gnu/gmp/",
download_name=f"gmp-{GMP_VERSION}.tar.bz2",
)
if WASM and APPLE:
restore_env("HOST_CC", OLD_HOST_CC)
build_dependency(
name=f"mpfr-{MPFR_VERSION}",
mode="autoconf",
@@ -1115,10 +1192,7 @@ if "cgal" in targets:
)
if MAC_CROSS_COMPILE_INTEL:
if OLD_CC is None:
del os.environ["CC"]
else:
os.environ["CC"] = OLD_CC
restore_env("CC", OLD_CC)
build_dependency(
name=f"cgal-{CGAL_VERSION}",
@@ -1249,15 +1323,19 @@ def get_cmake_args_prefix_path(additional_paths: "Sequence[str]" = ()) -> "list[
args_prefix_path = cmake_args_prefix_path.copy()
args_prefix_path.extend(additional_paths)
prefix_path = ";".join(args_prefix_path)
return [f"-DCMAKE_PREFIX_PATH={prefix_path}"]
if WASM:
# `emcmake` is disabling search in PATH, so we provide root paths instead.
# Provide '/' to PATH, so it will be combined with provided root paths,
# otherwise, depending on environment, it might not search the root path itself.
return [f"-DCMAKE_FIND_ROOT_PATH={prefix_path}", "-DCMAKE_PREFIX_PATH=//"]
else:
return [f"-DCMAKE_PREFIX_PATH={prefix_path}"]
if "wasm" in flags:
# Boost is built by the build script so should not be found
# inside of the sysroot set by the emscriptem toolchain
cmake_args.append("-DWASM_BUILD=On")
# set Eigen3 path for WASM to avoid find_package issues
cmake_args.append(f"-DEIGEN_DIR={DEPS_DIR}/install/eigen-install-{EIGEN_VERSION}/include/eigen3")
schemas = os.environ.get("IFCOS_SCHEMAS")
if schemas:
@@ -1267,26 +1345,9 @@ if "cgal" in targets:
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/cgal-{CGAL_VERSION}")
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/gmp-{GMP_VERSION}")
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/mpfr-{MPFR_VERSION}")
if "wasm" in flags:
cmake_args.extend(
[
f"-DCGAL_INCLUDE_DIR={DEPS_DIR}/install/cgal-{CGAL_VERSION}/include",
f"-DGMP_INCLUDE_DIR={DEPS_DIR}/install/gmp-{GMP_VERSION}/include",
f"-DGMP_LIBRARY_DIR={DEPS_DIR}/install/gmp-{GMP_VERSION}/lib",
f"-DMPFR_INCLUDE_DIR={DEPS_DIR}/install/mpfr-{MPFR_VERSION}/include",
f"-DMPFR_LIBRARY_DIR={DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib",
]
)
if "occ" in targets and USE_OCCT:
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/occt-{OCCT_VERSION}")
if "wasm" in flags:
cmake_args.extend(
[
f"-DOCC_INCLUDE_DIR={DEPS_DIR}/install/occt-{OCCT_VERSION}/include/opencascade",
f"-DOCC_LIBRARY_DIR={DEPS_DIR}/install/occt-{OCCT_VERSION}/lib",
]
)
elif "occ" in targets:
# We don't support find_package for OCE.
@@ -1307,13 +1368,6 @@ else:
if "libxml2" in targets:
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}")
if "wasm" in flags:
cmake_args.extend(
[
f"-DLIBXML2_INCLUDE_DIR={DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2",
f"-DLIBXML2_LIBRARIES={DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/lib/libxml2.{LIBRARY_EXT}",
]
)
if "hdf5" in targets:
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}")
@@ -1343,7 +1397,7 @@ if "rocksdb" in targets:
]
)
if not explicit_targets or {"IfcGeom", "IfcConvert", "IfcGeomServer"} & set(explicit_targets):
if not WASM and (not explicit_targets or {"IfcGeom", "IfcConvert", "IfcGeomServer"} & set(explicit_targets)):
logger.info("\rConfiguring executables...")
exec_args = [
@@ -1368,7 +1422,10 @@ if "IfcOpenShell-Python" in targets:
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"
ADDITIONAL_ARGS = "-Wl,-undefined,suppress"
# Override CMAKE_TOOLCHAIN_FILE because by default emscripten doesn't support building shared binaries.
if WASM_DEBUG:
os.environ["CMAKE_TOOLCHAIN_FILE"] = os.environ["WASM_TOOLCHAIN_FILE"]
# NOTE: We don't use `CXXFLAGS` for wrappers, so wrapper is compiled with different flags
# (e.g. ` -fdata-sections` is missing, which is set by default for executables)
@@ -1382,40 +1439,47 @@ if "IfcOpenShell-Python" in targets:
os.makedirs(python_dir, exist_ok=True)
def compile_python_wrapper(
python_version: str, python_library: str, python_include: str, python_executable: Union[str, None]
python_version: str,
python_include: Union[str, None] = None,
python_executable: Union[str, None] = None,
python_path: Union[Path, None] = None,
) -> Union[str, None]:
"""
:return: Path to module dir if ``python_executable`` was provided, otherwise ``None``.
"""
assert bool(python_path) ^ bool(python_include)
logger.info(f"\rConfiguring python {python_version} wrapper...")
cache_path = os.path.join(python_dir, "CMakeCache.txt")
if os.path.exists(cache_path):
os.remove(cache_path)
os.environ["PYTHON_LIBRARY_BASENAME"] = os.path.basename(python_library)
swig_prefix_paths: list[str] = []
prefix_paths: list[str] = []
if "swig" in targets:
swig_prefix_paths.append(f"{DEPS_DIR}/install/swig")
prefix_paths.append(f"{DEPS_DIR}/install/swig")
if python_path:
# We couldn't just prefix PATH and have to provide all variables explicitly,
# see ifcwrap/cmake for the details.
python_executable = (Path(python_path) / "bin" / "python3").__str__()
python_include = run(
[
python_executable,
"-c",
"import sysconfig; print(sysconfig.get_config_var('INCLUDEPY'))",
]
)
assert python_include
run_cmake(
"",
cmake_args
+ get_cmake_args_prefix_path(swig_prefix_paths)
+ get_cmake_args_prefix_path(prefix_paths)
+ [
"-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,
# Needed because pyodide is expecting setup.py to be in the root.
*([f"-DPYTHON_MODULE_INSTALL_DIR={REPO_PATH}"] * WASM),
f"-DPYTHON_INCLUDE_DIR={python_include}",
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/ifcopenshell/tmp",
"-DUSERSPACE_PYTHON_PREFIX="
+ ["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}],
@@ -1452,33 +1516,17 @@ if "IfcOpenShell-Python" in targets:
return module_dir
if "wasm" in flags:
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,
)
compile_python_wrapper(os.environ["PYVERSION"], os.environ["PYTHONINCLUDE"])
# Copy setup.py where pyodide build system expects it.
shutil.copy(REPO_PATH / "pyodide" / "setup.py", REPO_PATH)
elif USE_CURRENT_PYTHON_VERSION:
python_info = sysconfig.get_paths()
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("/", ""))
python_lib = os.path.join(*py_path_components)
compile_python_wrapper(platform.python_version(), python_lib, python_info["include"], sys.executable)
compile_python_wrapper(platform.python_version(), python_info["include"], sys.executable)
else:
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]}"
)
module_dir = compile_python_wrapper(python_version, python_library, python_include, python_executable)
python_path = Path(DEPS_DIR) / "install" / f"python-{python_version}"
module_dir = compile_python_wrapper(python_version, python_path=python_path)
assert module_dir
# Not sure why, but added after reading this in the logs
# cp: /Users/runner/work/IfcOpenShell/IfcOpenShell/build/Darwin/x86_64/10.15/install/ifcopenshell/python-3.9.11: No such file or directory
+33
View File
@@ -0,0 +1,33 @@
There are two ways to build pyodide ifcopenshell Python wrapper wheel.
1. Using pyodide build system (`build_pyodide.yml` does it):
- install prebuilt pyodide build and emscripten environment (see `build_pyodide.sh`)
- clone IfcOpenShell to `IfcOpenShell` folder
- create `packages/ifcopenshell` folder that will be used by pyodide build system
- from `IfcOpenShell` move building recipe `pyodide/meta.yaml` to `packages/ifcopenshell`
- run `pyodide build-recipes ifcopenshell --install`, it will
- execute `meta.yaml` recipe - it will:
- copy IfcOpenShell source to build folder `packages/ifcopenhell/build/ifcopenshell-0.8.0`
- build ifcopenshell and its dependencies
- note that rerunning `pyodide build-recipes` will remove previous build folder and rebuild all dependencies.
The way to avoid it, if build fails, is to use `pyodide build-recipes-no-deps ifcopenshell --continue` instead.
- run `setup.py` in `IfcOpenShell` root, producing a wheel in `IfcOpenShell/dist`
- copy that wheel to `packages/ifcopenshell/dist`
- `--install` it to current build envrionment
- copy the wheel next to `dist` folder (in root directory, next to `packages`)
- add wheel to `dist/pyodide-lock.json`
2. Build it outside of pyodide build system.
Building inside pyodide build system should be preferred, option to build it outside is useful for debugging purposes,
since it's pure cmake without any additional moving parts.
- setup pyodide environment, see above
- clone IfcOpenShell repo next to it to `IfcOpenShell` folder
- setup debug build environment using `source pyodide/debug_build_env.sh /path/to/pyodide_root`
- run `python nix/build-all.py -wasm -py-313` in `IfcOpenShell`
- it will produce Python package in `IfcOpenShell/ifcopenshell`
- run `pyodide build`
- it will produce a wheel in `IfcOpenShell/dist`
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/bash
set -ex
# Install uv.
curl -LsSf https://astral.sh/uv/install.sh | sh
uv venv --python 3.13
source .venv/bin/activate
# Install pyodide cross build environment.
# Instructions: https://pyodide.org/en/stable/development/building-packages.html
uv pip install pyodide-build
# `uv run` is required, so xbuildenv would skip using `pip`.
uv run pyodide xbuildenv install
# Emscripten doesn't come with xbuildenv.
git clone https://github.com/emscripten-core/emsdk
pushd emsdk
PYODIDE_EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)
./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
source emsdk_env.sh
which emcc
popd
mkdir -p packages/ifcopenshell
VERSION=`cat IfcOpenShell/VERSION`
cp IfcOpenShell/pyodide/meta.yaml packages/ifcopenshell
sed -i s/0.8.0/$VERSION/g packages/ifcopenshell/meta.yaml
# Use custom build ifcopenshell directory in build-all to make caching simpler
# Otherwise pyodide build path typically includes package version, so cached cmake configs might break.
export BUILD_DIR=`readlink -f ifcopenshell_build`
# Use build-recipes-no-deps first, so logs would be printed to stdout.
pyodide build-recipes-no-deps ifcopenshell
pyodide build-recipes ifcopenshell --install
+64
View File
@@ -0,0 +1,64 @@
"""
Cache built dependencies for builds.
This script is finding common install directory and either
packs each folder into a tar.gz archive, if it wasn't packed before,
or unpacks existing archives.
Usage: python cache_dependencies.py [pack|unpack]
"""
import tarfile
import sys
from pathlib import Path
from typing import Literal
CACHE_PREFIX = "cache-"
def get_install_dir() -> Path:
for data in Path.cwd().glob("*/*/install"):
return data
raise Exception("No install dir found")
def pack_dependencies(install_dir: Path) -> None:
# Process each install_dir
for dependency_path in install_dir.iterdir():
if not dependency_path.is_dir():
continue
dependency_name = dependency_path.name
tar_path = install_dir / f"{CACHE_PREFIX}{dependency_name}.tar.gz"
if tar_path.exists():
print(f"Skipping existing cache: '{tar_path}'")
else:
with tarfile.open(tar_path, "w:gz") as tar:
tar.add(dependency_path, arcname=dependency_path.name)
print(f"Created cache: '{tar_path}'")
def unpack_dependencies(install_dir: Path) -> None:
# `filter` argument was fully introduced in 3.12
# and results in deprecation warnings in 3.12-3.13, if not provided.
tar_filter: "dict[Literal['filter'], Literal['data']]" = (
{"filter": "data"} if bool(sys.version_info >= (3, 12)) else {}
)
for tar_path in install_dir.glob(f"{CACHE_PREFIX}*.tar.gz"):
with tarfile.open(tar_path, "r:gz") as tar:
tar.extractall(path=install_dir, **tar_filter)
print(f"Extracted cache: '{tar_path.name}'.")
if __name__ == "__main__":
if len(sys.argv) != 2 or (action := sys.argv[1].lower()) not in ("pack", "unpack"):
print(__doc__)
sys.exit(1)
install_dir = get_install_dir()
print(f"Found install dir: '{install_dir}'")
if action == "pack":
pack_dependencies(install_dir)
else:
unpack_dependencies(install_dir)
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
if [ $# -ne 1 ]; then
echo "Usage: $0 <pyodide_root>"
exit 1
fi
export PYODIDE_ROOT=$(readlink -f "$1")
export PATH="$PYODIDE_ROOT/emsdk/emsdk:$PYODIDE_ROOT/emsdk/emsdk/node/22.16.0_64bit/bin:$PYODIDE_ROOT/emsdk/emsdk/upstream/emscripten:$PATH"
# build-all.py vars.
export WASM_PYTHON_PATH="$PYODIDE_ROOT/cpython/installs/python-3.13.2"
export WASM_TOOLCHAIN_FILE="$PYODIDE_ROOT/pyodide-build/pyodide_build/tools/cmake/Modules/Platform/Emscripten.cmake"
+2 -3
View File
@@ -3,13 +3,12 @@ package:
version: 0.8.0
source:
# meta.yaml is placed as `packages/ifcopenshell/meta.yaml`.
path: ../../IfcOpenShell
build:
script: |
BUILD_CFG=Release python nix/build-all.py --without-rocksdb --without-hdf5 --without-opencollada --without-swig --without-pcre -v --wasm --py313 IfcOpenShell-Python
mv package/ifcopenshell .
cp pyodide/setup.py .
BUILD_CFG=Release python nix/build-all.py -v --wasm --py313
about:
home: http://ifcopenshell.org
+46 -9
View File
@@ -1,11 +1,48 @@
from setuptools import setup, find_packages
# setup.py is getting deprecated, but we still use it,
# because `tool.setuptools.ext-modules` is still experimental in pyproject.toml
# and we need it to get the wheel suffix right.
import os
from pathlib import Path
setup(name='ifcopenshell',
version='0.8.0',
description='IfcOpenShell is an open source (LGPL) software library for working with the Industry Foundation Classes (IFC) file format.',
author='Thomas Krijnen',
author_email='thomas@aecgeeks.com',
url='http://ifcopenshell.org',
packages=find_packages(),
package_data={'ifcopenshell': ['util/schema/*.json', 'util/schema/*.ifc'], '': ['*.so', '*.json', '*.ifc']},
import tomllib
from setuptools import Extension, setup
REPO_FOLDER = Path(__file__).parent
def get_version() -> str:
if "PKG_VERSION" in os.environ:
# Inside pyodide build environment.
return os.environ["PKG_VERSION"]
return (REPO_FOLDER / "VERSION").read_text().strip()
# Read dependencies from pyproject.toml
def get_dependencies() -> list[str]:
pyproject_toml = REPO_FOLDER / "src" / "ifcopenshell-python" / "pyproject.toml"
pyproject_data = tomllib.loads(pyproject_toml.read_text())
dependencies = pyproject_data["project"]["dependencies"]
return dependencies
setup(
name="ifcopenshell",
version=get_version(),
description=(
"IfcOpenShell is an open source (LGPL) software library "
"for working with the Industry Foundation Classes (IFC) file format."
),
author="Thomas Krijnen",
author_email="thomas@aecgeeks.com",
url="https://ifcopenshell.org",
install_requires=get_dependencies(),
packages=["ifcopenshell"],
package_data={
# "*.so" is needed to include prebuilt binary extension. Otherwise it would try to build it and fail.
"ifcopenshell": ["util/schema/*.json", "util/schema/*.ifc", "*.so"],
"": ["*.json", "*.ifc"],
},
# Has to provide extension to get the correct wheel suffix.
ext_modules=[Extension("ifcopenshell._ifcopenshell_wrapper", sources=[])],
)
+3
View File
@@ -43,6 +43,9 @@ endif
ifeq ($(PYVERSION), py313)
PYNUMBER:=313
endif
ifeq ($(PYVERSION), py314)
PYNUMBER:=314
endif
ifndef PYNUMBER
$(error Unsupported PYVERSION '$(PYVERSION)')
endif
@@ -44,6 +44,8 @@ ZIP packages
+-------------+---------------------------------+-------------------------------+---------------------------------+-----------------------------------+
| Python 3.13 | :ios_python_url:`py313-linux64` | :ios_python_url:`py313-win64` | :ios_python_url:`py313-macos64` | :ios_python_url:`py313-macosm164` |
+-------------+---------------------------------+-------------------------------+---------------------------------+-----------------------------------+
| Python 3.14 | :ios_python_url:`py314-linux64` | :ios_python_url:`py314-win64` | :ios_python_url:`py314-macos64` | :ios_python_url:`py314-macosm164` |
+-------------+---------------------------------+-------------------------------+---------------------------------+-----------------------------------+
2. Unzip the downloaded file and copy the ``ifcopenshell`` directory into your
Python path. If you're not sure where your Python path is, run the following
+1 -1
View File
@@ -30,7 +30,7 @@ import test.bootstrap
# - .github/workflows/ci-ifcopenshell-python.yml
# - .github/workflows/ci-ifcopenshell-python-pypi.yml
# - src/ifcopenshell-python/Makefile (PYVERSION check)
SUPPORTED_PY_VERSIONS = ("39", "310", "311", "312", "313")
SUPPORTED_PY_VERSIONS = ("39", "310", "311", "312", "313", "314")
SUPPORTED_PLATFORMS = ("win64", "linux64", "macos64", "macosm164")
+87 -52
View File
@@ -35,31 +35,39 @@ ENDIF()
include(GNUInstallDirs)
INCLUDE(${SWIG_USE_FILE})
IF(NOT "$ENV{PYTHON_INCLUDE_DIR}" STREQUAL "")
SET(PYTHON_INCLUDE_DIR $ENV{PYTHON_INCLUDE_DIR} CACHE FILEPATH "Python header files")
MESSAGE(STATUS "Looking for Python header files in: ${PYTHON_INCLUDE_DIR}")
IF(NOT "${PYTHON_INCLUDE_DIR}" STREQUAL "")
# Hint for FindPython where to find Python_INCLUDE_DIRS.
set(Python_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
MESSAGE(STATUS "Looking for Python header files in: ${Python_INCLUDE_DIR}")
ENDIF()
IF(NOT "$ENV{PYTHON_LIBRARY}" STREQUAL "")
SET(PYTHON_LIBRARY $ENV{PYTHON_LIBRARY} CACHE FILEPATH "Python library file")
MESSAGE(STATUS "Looking for Python library file in: ${PYTHON_LIBRARY}")
IF(NOT "${PYTHON_LIBRARY}" STREQUAL "")
# Hint for FindPython where to find Python_LIBRARIES.
set(Python_LIBRARY "${PYTHON_LIBRARY}")
MESSAGE(STATUS "Looking for Python library file in: ${Python_LIBRARY}")
ENDIF()
# NOTE PYTHONLIBS_FOUND and PYTHONINTERP_FOUND cannot seem to be trusted so
# we need further checks to see whether the packages were actually found or not.
FIND_PACKAGE(PythonLibs)
IF(NOT PYTHONLIBS_FOUND OR "$PYTHON_INCLUDE_DIRS}" STREQUAL "")
# Development.Module = headers on Unix, headers+libraries on Windows.
# Required variables:
# - Windows - Python_INCLUDE_DIR, Python_LIBRARY (MSVC doesn't allow undefined symbols at link time)
# - Unix - Python_INCLUDE_DIR
# Unfortunately all paths must be always provided explicitly, not by prefixing PATH.
# Otherwise FindPython will break on newer Python versions (list of supported versions is hardcoded).
find_package(Python COMPONENTS Development.Module)
IF(NOT Python_Development.Module_FOUND)
MESSAGE(FATAL_ERROR "BUILD_IFCPYTHON enabled, but unable to find Python lib or header. Disable BUILD_IFCPYTHON or fix Python paths to proceed.")
ENDIF()
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
# Ensure version is saved here, from wasm libraries,
# not from Python interpreter that might be unrelated to pyodide Python version.
set(_python_libs_version "${Python_VERSION_MAJOR}${Python_VERSION_MINOR}")
INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
SET(CMAKE_SWIG_FLAGS ${SWIG_DEFINES})
# NOTE Workaround for most likely missing debug Python libraries on Windows (requires Python built from the source).
# Python 3.5 intaller and onwards will have an option to install the debug libraries too.
# NOTE PYTHON_DEBUG_LIBRARIES appears to be a deprecated variable
IF (WIN32 AND NOT PYTHON_DEBUG_LIBRARIES)
MESSAGE(STATUS "PYTHON_DEBUG_LIBRARIES not found, defining SWIG_PYTHON_INTERPRETER_NO_DEBUG workaround for IfcWrap.")
IF (WIN32 AND NOT Python_LIBRARY_DEBUG)
MESSAGE(STATUS "Python debug libraries were not found, defining SWIG_PYTHON_INTERPRETER_NO_DEBUG workaround for IfcWrap.")
ADD_DEFINITIONS(-DSWIG_PYTHON_INTERPRETER_NO_DEBUG)
ENDIF()
@@ -79,51 +87,80 @@ SET_PROPERTY(
utils/typemaps_out.i
)
# Debug build on Windows add required `_d` suffix to the resulting .pyd file.
# If we won't override -interface name,
# swig py wrapper will try to import `_ifcopenshell_wrapper_d` module and would fail.
set(SWIG_MODULE_ifcopenshell_wrapper_EXTRA_FLAGS "-interface" "_ifcopenshell_wrapper")
# On Windows there is '_d' prefix for debug builds - e.g. `_d.cp311-win_amd64.pyd`.
if(WIN32)
set(Python_DEBUG_POSTFIX "_d")
endif()
if (CMAKE_VERSION VERSION_GREATER_EQUAL "4.2")
# `DEBUG_POSTFIX` argument was added in 4.2.
swig_add_library(ifcopenshell_wrapper LANGUAGE python SOURCES IfcPython.i DEBUG_POSTFIX "${Python_DEBUG_POSTFIX}")
else()
# If we won't override -interface name,
# swig py wrapper will try to import `_ifcopenshell_wrapper_d` module and would fail.
set(SWIG_MODULE_ifcopenshell_wrapper_EXTRA_FLAGS "-interface" "_ifcopenshell_wrapper")
swig_add_library(ifcopenshell_wrapper LANGUAGE python SOURCES IfcPython.i)
set_target_properties(
ifcopenshell_wrapper PROPERTIES
DEBUG_POSTFIX "${Python_DEBUG_POSTFIX}"
)
endif()
swig_add_library(ifcopenshell_wrapper LANGUAGE python SOURCES IfcPython.i)
SET_PROPERTY(TARGET ${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PROPERTY SWIG_DEPENDS ${IFCOPENSHELL_LIBRARIES})
target_link_libraries(ifcopenshell_wrapper PRIVATE Python::Module)
SET_PROPERTY(TARGET ifcopenshell_wrapper PROPERTY SWIG_DEPENDS ${IFCOPENSHELL_LIBRARIES})
if (WASM_BUILD)
# SIDE_MODULE=1 - add to .so all symbols from linked archives (default used by pyodide).
# Since currently libIfcGeom.a seems to be linked twice it results in duplicated symbols and compilation errors.
# Possibly in the future we can clean up linked libs and try `=1`.
# SIDE_MODULE=2 - add to .so only explicitly exported functions (`-s EXPORTED_FUNCTIONS).
# Have to provide it here to override possible `=1` coming from pyodide.
if(CMAKE_GENERATOR MATCHES "Makefile")
# Accomodate bug in cmake makefile generator:
# https://gitlab.kitware.com/cmake/cmake/-/issues/27326
string(
REPLACE "SIDE_MODULE=1" "SIDE_MODULE=2"
CMAKE_MODULE_LINKER_FLAGS
"${CMAKE_MODULE_LINKER_FLAGS}"
)
endif()
target_link_options(
ifcopenshell_wrapper
PRIVATE "SHELL:-s EXPORTED_FUNCTIONS=_PyInit__ifcopenshell_wrapper -s SIDE_MODULE=2"
)
endif()
if("$ENV{LDFLAGS}" MATCHES ".undefined.suppress")
# On osx there is some state in the python dylib. With `-Wl,undefined,suppress` we can ignore the missing symbols at compile time.
SWIG_LINK_LIBRARIES(ifcopenshell_wrapper ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${LIBSVGFILL})
target_link_libraries(ifcopenshell_wrapper PRIVATE ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${LIBSVGFILL})
else()
SWIG_LINK_LIBRARIES(ifcopenshell_wrapper ${IFCOPENSHELL_LIBRARIES} ${PYTHON_LIBRARIES} ${LIBSVGFILL})
target_link_libraries(ifcopenshell_wrapper PRIVATE ${IFCOPENSHELL_LIBRARIES} ${LIBSVGFILL})
endif()
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
SET_INSTALL_RPATHS(${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} "${IFCDIRS};${OCC_LIBRARY_DIR}")
SET_INSTALL_RPATHS(ifcopenshell_wrapper "${IFCDIRS};${OCC_LIBRARY_DIR}")
endif()
# Try to find the Python interpreter to get the site-packages
# directory in which the wrapper can be installed.
FIND_PACKAGE(PythonInterp)
IF((PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "") OR PYTHON_MODULE_INSTALL_DIR)
if (NOT WASM_BUILD)
# Find Python interpreter and get its version
# Not on WASM because we're cross compiling in that case
EXECUTE_PROCESS(
COMMAND ${PYTHON_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))"
OUTPUT_VARIABLE PYTHON_EXTENSION_SUFFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# On Windows there is '_d' prefix for debug builds - e.g. `_d.cp311-win_amd64.pyd`.
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set_target_properties(
${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PROPERTIES
SUFFIX ${PYTHON_EXTENSION_SUFFIX}
DEBUG_POSTFIX "_d"
)
if(NOT ${PYTHON_EXECUTABLE} STREQUAL "")
set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
MESSAGE(STATUS "Looking for Python interpreter in: ${PYTHON_EXECUTABLE}")
endif()
FIND_PACKAGE(Python COMPONENTS Interpreter)
IF(Python_Interpreter_FOUND OR PYTHON_MODULE_INSTALL_DIR)
# Python_SOABI example value is 'cp311-win_amd64'.
if(WASM_BUILD)
# For WASM we usually don't provide interpreter,
# so `find_package` couldn't find `Python_SOABI`.
# So we just hardcode it.
# .cpython-313-wasm32-emscripten.so
set(PYTHON_EXTENSION_SUFFIX ".cpython-${_python_libs_version}-wasm32-emscripten.so")
elseif(WIN32)
set(PYTHON_EXTENSION_SUFFIX ".${Python_SOABI}.pyd")
else()
set_target_properties(
${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME} PROPERTIES
SUFFIX ${PYTHON_EXTENSION_SUFFIX}
)
set(PYTHON_EXTENSION_SUFFIX ".${Python_SOABI}.so")
endif()
endif(NOT WASM_BUILD)
set_target_properties(
ifcopenshell_wrapper PROPERTIES
SUFFIX ${PYTHON_EXTENSION_SUFFIX}
)
if (PYTHON_MODULE_INSTALL_DIR)
set(python_package_dir "${PYTHON_MODULE_INSTALL_DIR}")
else()
@@ -133,10 +170,7 @@ IF((PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "") OR PYTHON_MOD
OUTPUT_VARIABLE python_package_dir
)
ELSE ()
EXECUTE_PROCESS(
COMMAND ${PYTHON_EXECUTABLE} -c "import sys; import sysconfig; sys.stdout.write(sysconfig.get_path('platlib'))"
OUTPUT_VARIABLE python_package_dir
)
set(python_package_dir "${Python_SITEARCH}")
ENDIF()
if (BUILD_PACKAGE)
set(python_package_dir ${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION_MAJOR}/dist-packages/)
@@ -145,6 +179,7 @@ IF((PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "") OR PYTHON_MOD
IF("${python_package_dir}" STREQUAL "")
MESSAGE(WARNING "Unable to locate Python site-package directory, unable to install the Python wrapper")
ELSE()
message(STATUS "Python wrapper will be installed to '${python_package_dir}'.")
FILE(GLOB_RECURSE sourcefiles
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*"
)
@@ -158,10 +193,10 @@ IF((PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "") OR PYTHON_MOD
ENDFOREACH()
INSTALL(FILES "${CMAKE_BINARY_DIR}/ifcwrap/ifcopenshell_wrapper.py"
DESTINATION "${python_package_dir}/ifcopenshell")
INSTALL(TARGETS ${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME}
INSTALL(TARGETS ifcopenshell_wrapper
DESTINATION "${python_package_dir}/ifcopenshell")
if (MSVC)
INSTALL(FILES $<TARGET_PDB_FILE:${SWIG_MODULE_ifcopenshell_wrapper_REAL_NAME}> DESTINATION bin OPTIONAL)
INSTALL(FILES $<TARGET_PDB_FILE:ifcopenshell_wrapper> DESTINATION bin OPTIONAL)
endif()
ENDIF()
ELSE()
+105
View File
@@ -0,0 +1,105 @@
"""
It's not really a full version of nix/build-all.py for Windows,
but serves the similar purpose - build all packages during CI (though by using cmd scripts),
but also archives them to '~/outputs'.
"""
import os
import subprocess
from pathlib import Path
from zipfile import ZipFile
assert Path.cwd() == Path(__file__).parent, "Run this script from the 'win' directory."
PYTHON_VERSIONS = ["3.9.13", "3.10.3", "3.11.8", "3.12.1", "3.13.0", "3.14.0"]
REPO_PATH = Path(__file__).parent.parent
REPO_WIN = REPO_PATH / "win"
VERSION = (REPO_PATH / "VERSION").read_text().strip()
if "GITHUB_SHA" in os.environ:
SHA = os.environ["GITHUB_SHA"][:7]
else:
SHA = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip()
OUTPUT_DIR = Path.home() / "output"
OUTPUT_DIR.mkdir(exist_ok=True)
print("Output directory:", OUTPUT_DIR)
ZIP_TEMPLATE = f"{{package_name}}-v{VERSION}-{SHA}-win64.zip"
def run(command: list[str]) -> None:
print("Running:", command)
subprocess.check_call(command) # nosec B603
def build() -> None:
for python_version in PYTHON_VERSIONS:
os.environ["PYTHON_VERSION"] = python_version
print(f"Building for Python {python_version}...")
subprocess.run(
[str(REPO_WIN / "build-deps.cmd"), "vs2022-x64", "Release"],
check=True,
text=True,
input="y\n",
)
run(
[
str(REPO_WIN / "run-cmake.bat"),
"vs2022-x64",
"-DENABLE_BUILD_OPTIMIZATIONS=ON",
"-DGLTF_SUPPORT=ON",
"-DADD_COMMIT_SHA=ON",
"-DVERSION_OVERRIDE=ON",
"-DSCHEMA_VERSIONS=4",
]
)
run([str(REPO_WIN / "install-ifcopenshell.bat"), "vs2022-x64", "Release"])
def archive_executables() -> None:
# Typically '_installed-vs2022-x64'.
install_dir = next(d for d in REPO_PATH.iterdir() if d.is_dir() and d.name.startswith("_installed"))
for file in (install_dir / "bin").iterdir():
if file.suffix.lower() != ".exe":
continue
zip_name = ZIP_TEMPLATE.format(package_name=file.stem)
with ZipFile(OUTPUT_DIR / zip_name, "w") as zipf:
zipf.write(file, arcname=file.name)
print(f"{file} -> {zip_name}")
def archive_python_package(python_version: str, python_path: Path) -> None:
python_version_major_minor = "".join(python_version.split(".")[:2])
site_packages = python_path / "Lib" / "site-packages"
package_path = site_packages / "ifcopenshell"
# Clean cache.
for file in package_path.rglob("*.pyc"):
file.unlink()
zip_name = ZIP_TEMPLATE.format(package_name=f"ifcopenshell-python-{python_version_major_minor}")
with ZipFile(OUTPUT_DIR / zip_name, "w") as zipf:
for file in package_path.rglob("*"):
arcname = file.relative_to(site_packages)
zipf.write(file, arcname=arcname)
print(f"{package_path} -> {zip_name}")
def archive_python_packages() -> None:
deps_path = REPO_PATH / "_deps"
python_versions: list[str] = []
for d in deps_path.iterdir():
if d.is_dir() and d.name.startswith("python."):
python_version = d.name.partition(".")[2]
python_path = d / "tools"
archive_python_package(python_version, python_path)
python_versions.append(d.name.partition(".")[2])
def main() -> None:
build()
archive_executables()
archive_python_packages()
if __name__ == "__main__":
main()
+78 -38
View File
@@ -90,10 +90,6 @@ 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%
@@ -147,12 +143,12 @@ echo Defaults to RelWithDebInfo if not specified.
IF %BUILD_CFG%==MinSizeRel call cecho.cmd 0 14 " WARNING: MinSizeRel build can suffer from a significant performance loss."
call cecho.cmd 0 13 "* Build Type`t`t= %BUILD_TYPE%"
echo - The used build type for the dependencies (Build, Rebuild, Clean).
echo Defaults to Build if not specified. Rebuild/Clean also uninstalls Python (if it was installed by this script).
echo Defaults to Build if not specified.
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.
echo But then you'll need to set PYTHONHOME env variable to your Python installation before running run-cmake.bat
echo to your Python installation path.
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.
@@ -180,17 +176,17 @@ cd "%DEPS_DIR%"
:: Don't use HDF5 1.13.0, because it has a broken cmake package path.
set HDF5_VERSION=1_13_1
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=%PYTHON_VERSION%
IF DEFINED PYTHON_VERSION (
echo Using overridden PYTHON_VERSION: '%PYTHON_VERSION%'
) else (
set PYTHON_VERSION=3.11.7
)
:: 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 (
set PY_VER_MAJOR_MINOR=%%a%%b
)
IF "%IFCOS_INSTALL_PYTHON%"=="TRUE" (
set PYTHONHOME=%INSTALL_DIR%\Python%PY_VER_MAJOR_MINOR%
set PYTHONHOME=%DEPS_DIR%\python.%PYTHON_VERSION%\tools
)
:: Cache last used CMake generator and configurable dependency dirs for other scripts to use
@@ -201,10 +197,56 @@ echo HDF5_VERSION=%HDF5_VERSION%>>"%~dp0\%BUILD_DEPS_CACHE_PATH%"
echo OCC_INCLUDE_DIR=%OCC_INCLUDE_DIR%>>"%~dp0\%BUILD_DEPS_CACHE_PATH%"
echo OCC_LIBRARY_DIR=%OCC_LIBRARY_DIR%>>"%~dp0\%BUILD_DEPS_CACHE_PATH%"
IF "%IFCOS_INSTALL_PYTHON%"=="TRUE" (
echo PY_VER_MAJOR_MINOR=%PY_VER_MAJOR_MINOR%>>"%~dp0\%BUILD_DEPS_CACHE_PATH%"
echo PYTHONHOME=%PYTHONHOME%>>"%~dp0\%BUILD_DEPS_CACHE_PATH%"
)
:nuget
set DEPENDENCY_NAME=nuget
set NUGET_VERSION=6.14.0
set NUGET_INSTALL_DIR=%DEPS_DIR%\nuget-%NUGET_VERSION%
set NUGET_EXE=%NUGET_INSTALL_DIR%\nuget.exe
where nuget >nul 2>&1
IF %ERRORLEVEL%==0 (
echo Found existing nuget in PATH. Skipping.
for /f "delims=" %%i in ('where nuget') do set "NUGET_EXE=%%i"
goto :ccache
)
IF EXIST "%NUGET_EXE%" (
echo Found existing "%DEPS_DIR%\nuget.exe", skipping
goto :ccache
)
cd %DEPS_DIR%
call :DownloadFile ^
https://dist.nuget.org/win-x86-commandline/v%NUGET_VERSION%/nuget.exe ^
"%NUGET_INSTALL_DIR%" nuget.exe
IF NOT %ERRORLEVEL%==0 GOTO :Error
:ccache
set DEPENDENCY_NAME=ccache
set CCACHE_VERSION=4.12.1
set CCACHE_INSTALL_DIR=%DEPS_DIR%\%DEPENDENCY_NAME%.%CCACHE_VERSION%\tools
set DEPENDENCY_DIR=%CCACHE_INSTALL_DIR%
where ccache >nul 2>&1
IF %ERRORLEVEL%==0 (
echo Found existing ccache in PATH. Skipping.
goto :proj
)
echo CCACHE_INSTALL_DIR=%CCACHE_INSTALL_DIR%>>"%~dp0\%BUILD_DEPS_CACHE_PATH%"
IF EXIST "%DEPENDENCY_DIR%" (
echo Found existing "%DEPENDENCY_DIR%", skipping
goto :proj
)
"%NUGET_EXE%" install ccache -Version %CCACHE_VERSION% -OutputDirectory "%DEPS_DIR%"
IF NOT %ERRORLEVEL%==0 GOTO :Error
:proj
IF EXIST "%INSTALL_DIR%\proj-9.2.1" (
@@ -506,32 +548,29 @@ set PYTHON_AMD64_POSTFIX=-amd64
IF NOT %TARGET_ARCH%==x64 set PYTHON_AMD64_POSTFIX=
set PYTHON_INSTALLER=python-%PYTHON_VERSION%%PYTHON_AMD64_POSTFIX%.exe
IF "%IFCOS_INSTALL_PYTHON%"=="TRUE" (
cd "%DEPS_DIR%"
call :DownloadFile https://www.python.org/ftp/python/%PYTHON_VERSION%/%PYTHON_INSTALLER% "%DEPS_DIR%" %PYTHON_INSTALLER%
IF NOT %ERRORLEVEL%==0 GOTO :Error
REM Uninstall if build Rebuild/Clean used
IF NOT %BUILD_TYPE%==Build (
call cecho.cmd 0 13 "Uninstalling %DEPENDENCY_NAME%. Please be patient, this will take a while."
start /w %PYTHON_INSTALLER% /quiet /uninstall
)
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."
)
) ELSE (
call cecho.cmd 0 13 "IFCOS_INSTALL_PYTHON not true, skipping installation of Python."
IF NOT "%IFCOS_INSTALL_PYTHON%"=="TRUE" (
call cecho.cmd 0 13 "IFCOS_INSTALL_PYTHON not 'TRUE', skipping installation of Python."
goto :SWIG
)
:: nuget doesn't support providing architecture for packages.
if NOT %TARGET_ARCH%==x64 (
call cecho.cmd 0 12 "Automatic insallation of Python for x86 builds is not supported,"
call cecho.cmd 0 12 "please install Python %PYTHON_VERSION% manually and ensure that it is available in PATH."
call cecho.cmd 0 12 "https://www.python.org/ftp/python/%PYTHON_VERSION%/%PYTHON_INSTALLER%"
goto :Error
)
if EXIST "%PYTHONHOME%" (
echo Found existing '%PYTHONHOME%', skipping installation.
goto :SWIG
)
"%NUGET_EXE%" install Python -Version %PYTHON_VERSION% -OutputDirectory "%DEPS_DIR%"
IF NOT %ERRORLEVEL%==0 GOTO :Error
:SWIG
IF EXIST "%INSTALL_DIR%\swigwin" (
@@ -753,6 +792,7 @@ exit /b %IFCOS_SCRIPT_RET%
:: DownloadFile - Downloads a file using PowerShell
:: Params: %1 url, %2 destinationDir, %3 filename
:DownloadFile
mkdir "%2"
pushd "%2"
if not exist "%~3". (
call cecho.cmd 0 13 "Downloading %DEPENDENCY_NAME% into %~2."
+8 -5
View File
@@ -91,13 +91,15 @@ set OPENCOLLADA_INSTALL_DIR=%INSTALL_DIR%\OpenCOLLADA
set LIBXML2_INCLUDE_DIR=%DEPS_DIR%\OpenCOLLADA\Externals\LibXML\include
set LIBXML2_LIBRARIES=%INSTALL_DIR%\OpenCOLLADA\lib\opencollada\xml.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_EXECUTABLE=%PYTHONHOME%\python.exe
for /f "usebackq delims=" %%v in (`
call "%PYTHON_EXECUTABLE%" -c "import sys; print(f'{sys.version_info[0]}{sys.version_info[1]}')"
`) do set "PY_VER_MAJOR_MINOR=%%v"
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_INSTALL_DIR=%INSTALL_DIR%\swigwin
set PATH=%PATH%;%PYTHONHOME%
set JSON_INCLUDE_DIR=%INSTALL_DIR%\json
if not defined ADD_COMMIT_SHA set ADD_COMMIT_SHA=Off
@@ -151,6 +153,7 @@ echo TBB_INCLUDE_DIR = %TBB_INCLUDE_DIR%
echo TBB_LIBRARY_DIR = %TBB_LIBRARY_DIR%
echo ROCKSDB_INSTALL_DIR = %ROCKSDB_INSTALL_DIR%
echo ZSTD_INSTALL_DIR = %ZSTD_INSTALL_DIR%
echo CCACHE_INSTALL_DIR = %CCACHE_INSTALL_DIR%
echo.
echo CMAKE_INSTALL_PREFIX = %CMAKE_INSTALL_PREFIX%
echo.
@@ -162,7 +165,7 @@ echo "Running CMake for %PROJECT_NAME%."
set CMAKE_PREFIX_PATH=%HDF5_INSTALL_DIR%;%OPENCOLLADA_INSTALL_DIR%;%SWIG_INSTALL_DIR%
set CMAKE_PREFIX_PATH=%CMAKE_PREFIX_PATH%;%ROCKSDB_INSTALL_DIR%;%ZSTD_INSTALL_DIR%
set CMAKE_PREFIX_PATH=%CMAKE_PREFIX_PATH%;%BOOST_ROOT%
set CMAKE_PREFIX_PATH=%CMAKE_PREFIX_PATH%;%BOOST_ROOT%;%CCACHE_INSTALL_DIR%
IF NOT "%VS_TOOLSET_HOST%"=="" (
cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% -T %VS_TOOLSET_HOST% ^