Building from source¶
How to build the Apple Silicon port from source. Distilled from the
repo-root BUILD.md.
For the upfront installation story see the User Guide.
Prerequisites¶
System¶
- macOS 14.0+ (Sonoma or newer).
CMAKE_OSX_DEPLOYMENT_TARGET = 14.0is pinned inCMakeLists.txt. - Xcode 26+ with the Metal toolchain. Verify with
xcrun metal --versionandxcrun metallib --version. - Apple Silicon arm64.
CMAKE_OSX_ARCHITECTURES = arm64is forced; the build warns onx86_64. - CMake ≥ 3.30 (root
cmake_minimum_required). - Homebrew.
AV_USE_HOMEBREW_DEPS=ON(default) shells out tobrew --prefixand prepends it toCMAKE_PREFIX_PATH.
Homebrew packages¶
For the kernel-only build (no upstream tree, no pipeline binaries) you need only Eigen3:
For the full pipeline build (-DAV_BUILD_UPSTREAM=ON
-DAV_BUILD_UPSTREAM_DEPTHMAP=ON) the upstream dependency tree pulls in:
brew install \
boost ceres-solver openimageio openexr imath zlib \
libomp pkgconf alembic assimp geogram imath lemon \
nanoflann onnxruntime open-mesh
(The Homebrew lemon package is the SQLite parser-generator — not the
COIN-OR graph library — so we vendor LEMON 1.3.1 under third_party/lemon/
and patch it for C++17. See memory/mental_note.md §8a.)
One-shot build¶
Build artifacts¶
build/
├── src/av_gpu/libav_gpu.a Layer 1 (Metal abstraction)
├── src/depth_map_metal/libav_depth_map_metal.a Layer 2
├── src/shaders/default.metallib 35 kernel entries
├── tests/test_* 37 test executables
└── (with AV_BUILD_UPSTREAM=ON)
aliceVision_cameraInit
aliceVision_featureExtraction
aliceVision_imageMatching
aliceVision_featureMatching
aliceVision_incrementalSfM
aliceVision_prepareDenseScene
aliceVision_depthMapEstimation
aliceVision_depthMapFiltering
aliceVision_meshing
aliceVision_meshFiltering
aliceVision_texturing
aliceVision_importMiddlebury
default.metallib (staged here too)
Build options¶
All options are at the top of the root CMakeLists.txt. Full reference
at CMake options.
| Option | Default | When to flip |
|---|---|---|
AV_USE_METAL |
ON |
Always ON on Apple. |
AV_USE_CUDA |
OFF |
CMakeLists.txt aborts if you flip it. |
AV_BUILD_TESTS |
ON |
OFF for distribution/CI. |
AV_BUILD_HELLO_METAL |
ON |
OFF to skip test_metal_hello + test_texture_smoke. |
AV_BUILD_UPSTREAM |
OFF |
ON to build the 12 pipeline binaries. |
AV_BUILD_UPSTREAM_DEPTHMAP |
OFF |
ON to build the 12-module upstream dep subset. |
AV_USE_HOMEBREW_DEPS |
ON |
OFF only with pre-populated CMAKE_PREFIX_PATH. |
AV_PROFILE_ADAPTER |
OFF |
ON to enable per-forwarder timing (see Performance profiling). |
Running tests¶
cd build
ctest # 37/37 expected
ctest -j1 # serialize (use this if a test is flaky on -j8)
ctest -R test_depth_pipeline --output-on-failure -V # specific test
Notable end-to-end tests:
| Test | What it validates |
|---|---|
test_metal_hello |
metal-cpp wiring + SAXPY smoke. |
test_texture_smoke |
RAII textures, bilinear, mipmap cascade. |
test_sgm_pipeline |
init_sim → compute_similarity → optimize → retrieve_best_depth. |
test_refine_pipeline |
init_refine → refine_similarity → refine_best_depth. |
test_depth_pipeline |
Full SGM → Bridge → Refine → Optimize chain. |
test_multi_t_aggregation |
WTA + FP16 additive across multiple T cameras. |
test_device_mipmap_image |
DeviceMipmapImage end-to-end. |
test_device_cache |
LRUCache + DeviceCache eviction. |
test_device_stream_manager |
Multi-queue parallel dispatch. |
test_volume_optimize_adaptive_p2 |
S31 adaptive-P2 path. |
test_upstream_adapter |
Adapter forwarder smoke. |
Path C (AV_BUILD_UPSTREAM_DEPTHMAP=ON)¶
This builds the 32 upstream module subdirectories that the depthMap host
code and the surrounding photogrammetry pipeline depend on. Path C lives
in cmake/UpstreamShim.cmake and provides shim implementations of
upstream's CMake macros (alicevision_add_library, alicevision_add_test,
alicevision_add_interface, alicevision_add_software,
alicevision_swig_add_library) so each per-module CMakeLists.txt composes
without pulling in upstream's install rules, SOVERSION dance, or Windows
.rc generation.
The upstream tree itself is never edited on disk. Quirks accommodated at CMake time:
Boost::systemstubbed asINTERFACE IMPORTED(modern Boost made it header-only; Homebrew dropped the separate config).Coin::Clp/Coin::CoinUtils/Coin::Osistubbed as empty INTERFACE targets (Coin-OR not on Homebrew;linearProgrammingis INTERFACE-only anyway and depthMap doesn't solve LPs at runtime).ALICEVISION_ROTATION_AVERAGING_WITH_BOOSTdefined globally somultiview/rotationAveraginguses Boost.Graph instead of LEMON.multiview/rotationAveraging/l1.cppis patched at configure time viafile(READ … REPLACE … WRITE …)to dropconstfrom three default- initializedEigen::Matrixdeclarations (clang 21 enforces the [dcl.init] rule that const non-user-defined-default-ctor class objects must have an initializer). Patched copy →build/upstream-patched/.
Full rationale in PORTING_NOTES.md §10.
Troubleshooting¶
Boost.System missing¶
Modern Boost (≥1.86) made it header-only; the Boost::system interface stub
in the root CMakeLists.txt handles this for AV_BUILD_UPSTREAM_DEPTHMAP=ON.
If you see it elsewhere, mirror the pattern:
if(NOT TARGET Boost::system)
add_library(Boost::system INTERFACE IMPORTED)
if(TARGET Boost::headers)
set_target_properties(Boost::system PROPERTIES
INTERFACE_LINK_LIBRARIES Boost::headers)
endif()
endif()
Metal license not accepted¶
default.metallib fails to load at test runtime¶
Check:
build/src/shaders/default.metallibexists.build/tests/default.metallibexists (staged byav_install_metallib).xcrun metallib --versionruns without licence prompts.
For ad-hoc debug, load by absolute path:
MTL::Device::default_device().load_library("/path/to/default.metallib").
Clean rebuild¶
There are no generated files outside build/. Even the l1.cpp patch lands
in build/upstream-patched/, not in source. upstream/ stays read-only.
One-shot DMG build (scripts/build_dmg.sh)¶
For producing a distributable .app + DMG without driving each step
yourself, the repo ships a single orchestrator:
Runs five steps with live + per-step file logging to
build/release/logs/<NN>_<step>.log:
cmake configurewith-DAV_BUILD_UPSTREAM=ON -DAV_BUILD_UPSTREAM_DEPTHMAP=ON -DAV_BUILD_PYALICEVISION=ON.cmake --build build -j$(sysctl -n hw.ncpu).scripts/package_macos_app.sh→build/release/Meshroom.app.scripts/codesign_macos_app.sh(ad-hoc by default; pass--identityfor Developer ID).scripts/make_dmg.sh→build/release/Meshroom-<version>-arm64.dmg.
After step 2 the script asserts at least 50 of the expected 60
aliceVision_* binaries built; after step 3 it sweeps the bundle with
otool -L | grep /opt/homebrew/ and warns if any leaked references
remain. Any step that exits non-zero halts the pipeline and echoes the
last 30 lines of its log to stderr.
Final output is build/release/SUMMARY.md with timings, file sizes,
dylib counts, and the resolved flags — so the build is reproducible.
Common flags¶
| Flag | When to use |
|---|---|
--identity "Developer ID Application: NAME (TEAMID)" |
Production builds. Defaults to - (ad-hoc). |
--skip-cmake-configure |
Re-using an already-configured build/. |
--skip-cmake-build |
Re-packaging without rebuilding the binaries. |
--skip-package |
.app already at build/release/Meshroom.app. |
--skip-codesign |
Signing handled externally. |
--clean |
Remove old Meshroom.app and DMGs before starting. |
--jobs N |
Ninja parallelism (default: hw.ncpu). |
--compression {udzo,udzo-max,ulfo,ulmo} |
DMG compression. Default ulmo (best size, ~34 % smaller than legacy). |
DMG compression¶
The orchestrator passes --compression through to
scripts/make_dmg.sh, which dispatches to hdiutil create -format.
Defaults to ULMO based on the benchmark below.
Benchmark (186 MB fixture: 60 aliceVision_* binaries + Homebrew dylibs)¶
--compression |
hdiutil flags |
DMG size | vs UDZO | Wall-time | Min macOS |
|---|---|---|---|---|---|
udzo |
-format UDZO (zlib L1) |
69.2 MB | baseline | 6 s | 10.0 |
udzo-max |
-format UDZO -imagekey zlib-level=9 |
62.5 MB | -9.7 % | 13 s | 10.0 |
ulfo |
-format ULFO (lzfse) |
61.2 MB | -11.5 % | 6 s | 10.11 |
ulmo (default) |
-format ULMO (lzma) |
45.7 MB | -33.9 % | 24 s | 10.15 |
ULMO wins decisively on size and the Mac port already requires
macOS 14+, so 10.15's ULMO requirement is irrelevant. On the full ~2.7
GB Meshroom.app bundle this projects to a ~0.9 GB DMG vs the ~1.4 GB
the old UDZO default produced — a ~470 MB saving per release.
Safety: post-build verification + auto-fallback¶
scripts/make_dmg.sh always runs hdiutil verify <dmg> after creation
to validate checksums + that the image mounts. If the chosen format
fails either create or verify, the script automatically retries with
UDZO (the legacy-safe default that works back to macOS 10.0) and prints
a warning to stderr rather than leaving the operator empty-handed. The
DMG name and path are preserved across the fallback, and the orchestrator
records the actually-used compression in build/release/SUMMARY.md so
post-mortems are easy. Use --no-verify on make_dmg.sh to skip the
verify step (not recommended for distribution builds).
Notarization compatibility¶
All four UDIF formats above are accepted by Apple's notarytool and
stapler. ULMO has been GA since macOS 10.15 Catalina (2019) and is
well-tested in production distribution workflows.
After: notarize the DMG (Developer ID only)¶
xcrun notarytool submit build/release/Meshroom-*.dmg \
--apple-id you@example.com --team-id TEAMID \
--password APP-SPECIFIC-PWD --wait
xcrun stapler staple build/release/Meshroom-*.dmg
notarytool wants the DMG, not the .app. The staple writes the
notarization ticket into the DMG so the user's Mac can verify it
offline.