Standalone builder that produces a header-only igl.xcframework for macOS
(arm64) from a tagged libigl release, bundled with a pinned Eigen.
libigl is header-only by default; this builder skips libigl's CMake and just
stages the include/igl/ + Eigen/ trees into a library-form xcframework
(stub static archive + Headers/). Library-form (vs. framework-form) is
deliberate so that <igl/...> and <Eigen/...> both resolve in consumer
C++ TUs.
Sibling project to pdal-xcframework-builder. Output is consumed by
SwiftIGL (Swift Package).
- macOS with Xcode command-line tools (
xcodebuild,clang,ar) cmake,git- (optional)
ghformake release
cp config.sh.example config.sh
$EDITOR config.sh # set SWIFT_PACKAGE_FRAMEWORKS_DIR if mirroring locallymake LIBIGL_VERSION=2.5.0Or directly:
./build.sh 2.5.0Phases (numbered in build.sh):
- Fetch libigl — shallow clone at tag
v<version>(falls back to<version>) intowork/libigl-<version>/libigl-src. - Fetch Eigen — shallow clone Eigen at
EIGEN_VERSION(default3.4.0, matching libigl's pinned FetchContent tag). - Stage
include/— copyinclude/igl/,Eigen/, andunsupported/intostage/include/. License files are copied tostage/include/.licenses/. - Stub static archive — compile a single no-op
.c, archive intolibigl_stub.a. xcframeworks need a real Mach-O attached to headers. - xcframework —
xcodebuild -create-xcframework -library libigl_stub.a -headers stage/include -output output/igl.xcframework. - Codesign (optional) — only when
CODESIGN_IDENTITYis set. - Mirror (optional) — copy into
SWIFT_PACKAGE_FRAMEWORKS_DIRso SwiftIGL can pick it up via path-based binaryTarget during development. - Zip + checksum —
dittozip andswift package compute-checksum.
RELEASE=1 ./build.sh <version> (or make release) additionally creates a
GitHub release via gh if GH_RELEASE_REPO is set.
Releases are published to mnmly/SwiftIGL (configurable via
GH_RELEASE_REPO in config.sh). To ship a new libigl version:
# Build, mirror to SwiftIGL/Frameworks (for local iteration), zip,
# and gh release create with the zip attached.
make LIBIGL_VERSION=2.6.0 releaseThe release notes generated by phase 9 include the exact
.binaryTarget(url:, checksum:) snippet — paste it into
mnmly/SwiftIGL/Package.swift, commit, push.
The build refuses to overwrite an existing tag — bump LIBIGL_VERSION,
or delete the prior release first. If Eigen's pin moved in upstream
libigl/cmake/recipes/external/eigen.cmake, bump EIGEN_VERSION in
config.sh alongside.
igl.xcframework/
├── Info.plist
└── macos-arm64/
├── libigl_stub.a
└── Headers/
├── igl/ # libigl headers
├── Eigen/ # Eigen core
├── unsupported/ # Eigen unsupported (optional)
└── .licenses/
.binaryTarget(
name: "igl",
path: "Frameworks/igl.xcframework" // or url: + checksum: for releases
),
.target(
name: "CxxIGL",
dependencies: ["igl"],
cxxSettings: [.headerSearchPath("include")]
),C++ TUs in CxxIGL can then #include <igl/read_triangle_mesh.h> and
#include <Eigen/Core>.
libigl exposes 600+ headers, each of which is its own translation unit when
LIBIGL_USE_STATIC_LIBRARY=OFF (the default). A Clang umbrella module that
sweeps all of them in is both slow and prone to template-instantiation
explosions. Library-form xcframeworks expose headers via plain
-I<headers>, which is what we want — consumers include only what they use.
- iOS / Catalyst / x86_64 (untested).
- libigl
copyleft/andrestricted/modules (CGAL, comiso, embree, matlab, mosek, predicates). They have non-trivial external deps; add as a v2 if needed. LIBIGL_USE_STATIC_LIBRARY=ONprecompiled-instantiations build. Currently we ship pure headers — slower app builds, but no template-coverage gaps.