From 02d3410253dd0f0268ef1e1a5161fb82a38acbb2 Mon Sep 17 00:00:00 2001 From: elliejs Date: Thu, 25 Jun 2026 03:44:42 +0000 Subject: [PATCH] Add FreeBSD platform support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add build-job-freebsd.yml CI workflow for generating and compiling OCP bindings on FreeBSD 15.0 using vmactions/freebsd-vm. FreeBSD has no conda-forge packages, so all dependencies come from the FreeBSD pkg system (latest repo). The workflow follows the same two-phase structure (generate → compile) as the other platforms, with source caching between phases and a combined sources+stubs artifact upload. The clang Python bindings are pip-installed because the FreeBSD pkg version (11.0) is too old to drive libclang 22 from llvm-devel. Add a [FreeBSD] platform section to ocp.toml with system include paths and a FreeBSD symbol manifest. Update dump_symbols.py to support lief <0.14 (FreeBSD pkg ships 0.12.3), which uses lief.EXE_FORMATS instead of lief.Binary.FORMATS. The existing code used p.FORMATS which only works on 0.14+. The fallback (getattr + or) now supports both. Also reorder the lief.parse() None check before accessing p.format (was a crash on parse failure), add an empty-string guard for the semicolon-split lib list, and detect FreeBSD as a distinct ELF platform via platform.system(). Add FreeBSD case to CMakeLists.txt platform detection and FreeBSD job entry to bindings.yml. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/bindings.yml | 6 ++ .github/workflows/build-job-freebsd.yml | 130 ++++++++++++++++++++++++ CMakeLists.txt | 2 + dump_symbols.py | 19 +++- ocp.toml | 8 ++ 5 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/build-job-freebsd.yml diff --git a/.github/workflows/bindings.yml b/.github/workflows/bindings.yml index acc88b052..95d9712e8 100644 --- a/.github/workflows/bindings.yml +++ b/.github/workflows/bindings.yml @@ -39,3 +39,9 @@ jobs: platform: OSX secrets: ANACONDA_TOKEN: ${{ secrets.ANACONDA_TOKEN }} + + FreeBSD: + uses: ./.github/workflows/build-job-freebsd.yml + with: + name: FreeBSD + platform: FreeBSD diff --git a/.github/workflows/build-job-freebsd.yml b/.github/workflows/build-job-freebsd.yml new file mode 100644 index 000000000..229a7eac4 --- /dev/null +++ b/.github/workflows/build-job-freebsd.yml @@ -0,0 +1,130 @@ +name: Build Bindings (FreeBSD) + +# FreeBSD cannot use micromamba/conda-forge (no FreeBSD packages exist). +# Uses vmactions/freebsd-vm to run in a FreeBSD bhyve VM on a Linux runner, +# with system packages (pkg "latest" repo) instead of conda environments. + +on: + workflow_call: + inputs: + name: + required: true + type: string + platform: + required: true + type: string +env: + FBSD_PKGS: >- + opencascade llvm-devel cmake ninja pybind11 py311-pybind11 vtk9 + rapidjson libfmt python311 py311-numpy py311-pandas py311-joblib + py311-Jinja2 py311-click py311-toml py311-pyparsing py311-tqdm + py311-lief py311-pip py311-logzero py311-path py311-schema + py311-toposort py311-pybind11-stubgen gcc + +jobs: + # --------------------------------------------------------------------------- + # Phase 1: Generate bindings + # --------------------------------------------------------------------------- + generate: + name: Generate ${{ inputs.name }} + runs-on: ubuntu-latest + timeout-minutes: 120 + + steps: + - uses: actions/checkout@v5 + with: + submodules: true + + - name: Restore OCP_src cache + id: cache-ocp-src-restore + uses: actions/cache/restore@v4 + with: + path: build/OCP + key: OCP-src-${{ inputs.platform }}- + + - name: Generate bindings on FreeBSD + if: steps.cache-ocp-src-restore.outputs.cache-hit != 'true' + uses: vmactions/freebsd-vm@v1 + with: + release: '15.0' + usesh: true + prepare: | + pkg install -y ${{ env.FBSD_PKGS }} + python3.11 -m pip install clang + + run: | + set -ex + cmake -S . -B build -G Ninja \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/local/llvm-devel" + cmake --build build + + - name: Cache OCP_src + if: steps.cache-ocp-src-restore.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: build/OCP + key: ${{ steps.cache-ocp-src-restore.outputs.cache-primary-key }} + + # --------------------------------------------------------------------------- + # Phase 2: Compile + test + stubs + # --------------------------------------------------------------------------- + compile: + name: Compile ${{ inputs.name }} (3.${{ matrix.py_min }}) + needs: generate + runs-on: ubuntu-latest + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + py_min: ['11'] + + steps: + - uses: actions/checkout@v5 + with: + submodules: true + + - name: Restore OCP_src cache + uses: actions/cache/restore@v4 + with: + path: build/OCP + key: OCP-src-${{ inputs.platform }}- + + - name: Compile and test on FreeBSD + uses: vmactions/freebsd-vm@v1 + with: + release: '15.0' + usesh: true + prepare: | + pkg install -y ${{ env.FBSD_PKGS }} + + run: | + set -ex + + cmake -B build_native -S build/OCP -G Ninja \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/local/llvm-devel" \ + -DCMAKE_BUILD_TYPE=Release + + ninja -C build_native -j 2 -k 0 + + # --- Test --- + cd build_native && python3.11 -c "import OCP; print('OCP import OK')" + + # --- Generate stubs --- + cd ${GITHUB_WORKSPACE}/build_native + python3.11 -m pybind11_stubgen -o . OCP + # --- Prepare upload (sources + stubs) --- + mkdir -p ${GITHUB_WORKSPACE}/upload + cp -r ${GITHUB_WORKSPACE}/build/OCP ${GITHUB_WORKSPACE}/upload/ + cp -r OCP ${GITHUB_WORKSPACE}/upload/OCP-stubs + + - name: Upload Compilation + uses: actions/upload-artifact@v5 + with: + name: OCP_${{ inputs.platform }}_py3${{ matrix.py_min }} + path: build_native/OCP.cpython-*.so + + - name: Upload Sources and Stubs + uses: actions/upload-artifact@v5 + with: + name: OCP_src_stubs_${{ inputs.platform }}_py3${{ matrix.py_min }} + path: upload diff --git a/CMakeLists.txt b/CMakeLists.txt index 12fef27a1..dc81fc98c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,8 @@ else() set(PLATFORM Windows) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") set(PLATFORM OSX) + elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + set(PLATFORM FreeBSD) else() set(PLATFORM Linux) endif() diff --git a/dump_symbols.py b/dump_symbols.py index 0c2ce9fc9..25c57ae2d 100755 --- a/dump_symbols.py +++ b/dump_symbols.py @@ -7,21 +7,30 @@ exported_symbols = [] +# lief <0.14 uses lief.EXE_FORMATS; 0.14+ uses lief.Binary.FORMATS +FORMATS = getattr(lief, 'EXE_FORMATS', None) or lief.Binary.FORMATS + for lib in libs: logger.info(f'Analyzing {lib}') + lib = lib.strip() + if not lib: + continue + p = lief.parse(lib) - format = p.format if p is None: - continue + raise RuntimeError(f'lief.parse failed for {lib!r}') + + format = p.format - if format==p.FORMATS.ELF: - name = "linux" + if format == FORMATS.ELF: + import platform as _platform + name = "freebsd" if _platform.system() == "FreeBSD" else "linux" for s in p.exported_symbols: exported_symbols.append(f'{s.name}\n') - elif format==p.FORMATS.MACHO: + elif format == FORMATS.MACHO: name = "mac" for s in p.symbols: if s.raw_type>1: diff --git a/ocp.toml b/ocp.toml index ce5a38533..f740589ba 100644 --- a/ocp.toml +++ b/ocp.toml @@ -390,6 +390,14 @@ class Adaptor3d_Surface; exclude_classes = ["Handle_*"] symbols = "symbols_mangled_win.dat" +[FreeBSD] + modules = ["Xw"] + symbols = "symbols_mangled_freebsd.dat" + includes = ['/usr/include/c++/v1', + '/usr/local/llvm-devel/lib/clang/22/include', + '/usr/local/include', + '/usr/include'] + [OSX] modules = ["Cocoa"] symbols = "symbols_mangled_mac.dat"