From 656fcafe41a0697a0aadb952873ca32c0b877575 Mon Sep 17 00:00:00 2001 From: Alan Frindell Date: Tue, 24 Feb 2026 11:51:48 -0800 Subject: [PATCH] Standalone CMake (#94) Summary: Pull Request resolved: https://github.com/facebookexperimental/moxygen/pull/94 This is an alternate build to getdeps for moxygen. If you have the necessary non-Meta deps installed, you can `cmake -B build -S standalone/ -G Ninja` `cmake --build build --parallel` And it will compile only necessary parts of folly, fizz, wangle, mvfst and proxygen. Reviewed By: kvtsoy, lnicco Differential Revision: D91102518 --- standalone/CMakeLists.txt | 337 ++++++++++++++++++++++++++++++ standalone/README.md | 127 +++++++++++ standalone/install-system-deps.sh | 100 +++++++++ 3 files changed, 564 insertions(+) create mode 100644 standalone/CMakeLists.txt create mode 100644 standalone/README.md create mode 100755 standalone/install-system-deps.sh diff --git a/standalone/CMakeLists.txt b/standalone/CMakeLists.txt new file mode 100644 index 000000000..106e80954 --- /dev/null +++ b/standalone/CMakeLists.txt @@ -0,0 +1,337 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +cmake_minimum_required(VERSION 3.16) +project(moxygen-standalone VERSION 1.0.0 LANGUAGES CXX C) + +# Disable CMake user package registry to prevent picking up stale getdeps builds +set(CMAKE_FIND_USE_PACKAGE_REGISTRY OFF) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Required for glog 0.7+ compatibility +add_compile_definitions(GLOG_USE_GLOG_EXPORT) + +# ============================================================================= +# Options +# ============================================================================= +option(BUILD_TESTS "Build tests" ON) +option(BUILD_SHARED_LIBS "Build shared libraries (discouraged)" OFF) + +# ============================================================================= +# Linking preferences +# ============================================================================= +# Use static Boost (required by folly). Other system libraries use platform +# defaults (shared on Linux) to avoid PIE issues with non-PIC static libs. +# FetchContent deps are built from source with BUILD_SHARED_LIBS=OFF so +# they are already static. +set(Boost_USE_STATIC_LIBS ON CACHE BOOL "Use static Boost libraries" FORCE) +set(BOOST_LINK_STATIC ON CACHE BOOL "Link Boost statically (for folly)" FORCE) + +find_package(ZLIB REQUIRED) +find_library(ZSTD_LIBRARY NAMES zstd) + +# ============================================================================= +# Read pinned commit hashes from github_hashes (same as getdeps uses) +# ============================================================================= +set(GITHUB_HASHES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../build/deps/github_hashes") + +# Helper function to read commit hash from rev.txt file +function(read_rev_hash ORG PROJECT OUT_VAR) + set(REV_FILE "${GITHUB_HASHES_DIR}/${ORG}/${PROJECT}-rev.txt") + if(EXISTS "${REV_FILE}") + file(READ "${REV_FILE}" REV_CONTENT) + # Format: "Subproject commit <40-char-hash>" + string( + REGEX MATCH "Subproject commit ([a-fA-F0-9]+)" + _ "${REV_CONTENT}" + ) + if(CMAKE_MATCH_1) + set(${OUT_VAR} "${CMAKE_MATCH_1}" PARENT_SCOPE) + message(STATUS "Using pinned ${PROJECT} rev: ${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Failed to parse rev from ${REV_FILE}") + endif() + else() + message(FATAL_ERROR "Rev file not found: ${REV_FILE}\n" + "Make sure you cloned moxygen with build/deps/github_hashes/") + endif() +endfunction() + +# Read all pinned revisions +read_rev_hash("facebook" "folly" FOLLY_REV) +read_rev_hash("facebookincubator" "fizz" FIZZ_REV) +read_rev_hash("facebook" "wangle" WANGLE_REV) +read_rev_hash("facebook" "mvfst" MVFST_REV) +read_rev_hash("facebook" "proxygen" PROXYGEN_REV) + +# ============================================================================= +# System Dependencies (must be pre-installed) +# ============================================================================= +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/../build/fbcode_builder/CMake" +) +find_package(Threads REQUIRED) +find_package(OpenSSL REQUIRED) +find_package(gflags CONFIG REQUIRED) +find_package(glog CONFIG REQUIRED) +find_package(fmt CONFIG REQUIRED) +find_package(double-conversion CONFIG REQUIRED) +find_package(Boost 1.70 REQUIRED COMPONENTS + context filesystem program_options regex) +find_package(ZLIB REQUIRED) +find_package(Zstd REQUIRED) +find_package(LibEvent REQUIRED) +find_package(Sodium REQUIRED) + +# Check for libunwind to decide if we need lzma for symbolization +find_package(LibUnwind QUIET) + +# ============================================================================= +# FetchContent: Meta OSS Dependencies (using pinned hashes) +# ============================================================================= +include(FetchContent) +set(FETCHCONTENT_QUIET OFF) + +# googletest 1.12+ required for move-only WillOnce support +# (Ubuntu 22.04 ships 1.11 which doesn't support this) +if(BUILD_TESTS) + # Try to find system googletest first + find_package(GTest 1.12.1 QUIET) + + if(GTest_FOUND) + message(STATUS "Using system googletest ${GTest_VERSION}") + include(GoogleTest) + else() + message(STATUS + "System googletest not found or too old, using FetchContent" + ) + FetchContent_Declare(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.12.1 + ) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + # Required on Linux where GCC defaults to PIE executables; + # without this, static libgmock.a/libgtest.a have non-PIC + # relocations that fail to link into PIE binaries. + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + FetchContent_MakeAvailable(googletest) + # Ensure FetchContent googletest headers take precedence over homebrew + target_include_directories(gtest BEFORE INTERFACE + $) + target_include_directories(gmock BEFORE INTERFACE + $) + # Provide GoogleTest module (for gtest_add_tests) since + # find_package(GTest/GMock) is skipped for FetchContent-provided deps. + include(GoogleTest) + endif() +endif() + +# Save original BUILD_TESTS value, then disable for fetched dependencies +set(_MOXYGEN_BUILD_TESTS ${BUILD_TESTS}) +set(BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + +message(STATUS "Fetching Meta OSS dependencies using pinned revisions...") + +# fast_float is a header-only library required by folly +FetchContent_Declare(fast_float + GIT_REPOSITORY https://github.com/fastfloat/fast_float.git + GIT_TAG v8.0.0 +) +FetchContent_GetProperties(fast_float) +if(NOT fast_float_POPULATED) + FetchContent_Populate(fast_float) +endif() +set(FASTFLOAT_INCLUDE_DIR + "${fast_float_SOURCE_DIR}/include" CACHE PATH "" FORCE) + +# Allow overriding folly repo/rev via -DFOLLY_REPO_OVERRIDE and +# -DFOLLY_REV_OVERRIDE +set(FOLLY_REPO_OVERRIDE "" CACHE STRING "Override folly git repository URL") +set(FOLLY_REV_OVERRIDE "" CACHE STRING "Override folly git revision") +if(FOLLY_REPO_OVERRIDE) + set(_FOLLY_REPO "${FOLLY_REPO_OVERRIDE}") +else() + set(_FOLLY_REPO "https://github.com/facebook/folly.git") +endif() +if(FOLLY_REV_OVERRIDE) + set(_FOLLY_REV "${FOLLY_REV_OVERRIDE}") +else() + set(_FOLLY_REV "${FOLLY_REV}") +endif() + +FetchContent_Declare(folly + GIT_REPOSITORY ${_FOLLY_REPO} + GIT_TAG ${_FOLLY_REV} +) + +# Allow overriding fizz repo/rev via -DFIZZ_REPO_OVERRIDE and +# -DFIZZ_REV_OVERRIDE +set(FIZZ_REPO_OVERRIDE "" CACHE STRING "Override fizz git repository URL") +set(FIZZ_REV_OVERRIDE "" CACHE STRING "Override fizz git revision") +if(FIZZ_REPO_OVERRIDE) + set(_FIZZ_REPO "${FIZZ_REPO_OVERRIDE}") +else() + set(_FIZZ_REPO "https://github.com/facebookincubator/fizz.git") +endif() +if(FIZZ_REV_OVERRIDE) + set(_FIZZ_REV "${FIZZ_REV_OVERRIDE}") +else() + set(_FIZZ_REV "${FIZZ_REV}") +endif() + +FetchContent_Declare(fizz + GIT_REPOSITORY ${_FIZZ_REPO} + GIT_TAG ${_FIZZ_REV} +) + +FetchContent_Declare(wangle + GIT_REPOSITORY https://github.com/facebook/wangle.git + GIT_TAG ${WANGLE_REV} +) + +FetchContent_Declare(mvfst + GIT_REPOSITORY https://github.com/facebook/mvfst.git + GIT_TAG ${MVFST_REV} +) + +# Allow overriding proxygen repo/rev via -DPROXYGEN_REPO_OVERRIDE and +# -DPROXYGEN_REV_OVERRIDE +set(PROXYGEN_REPO_OVERRIDE "" + CACHE STRING "Override proxygen git repository URL") +set(PROXYGEN_REV_OVERRIDE "" CACHE STRING "Override proxygen git revision") +if(PROXYGEN_REPO_OVERRIDE) + set(_PROXYGEN_REPO "${PROXYGEN_REPO_OVERRIDE}") +else() + set(_PROXYGEN_REPO "https://github.com/facebook/proxygen.git") +endif() +if(PROXYGEN_REV_OVERRIDE) + set(_PROXYGEN_REV "${PROXYGEN_REV_OVERRIDE}") +else() + set(_PROXYGEN_REV "${PROXYGEN_REV}") +endif() + +FetchContent_Declare(proxygen + GIT_REPOSITORY ${_PROXYGEN_REPO} + GIT_TAG ${_PROXYGEN_REV} +) + +# Populate and add dependencies with EXCLUDE_FROM_ALL so only moxygen +# targets are in 'all' +# Override find_package for deps we provide via FetchContent, since sub-projects +# (fizz, wangle, mvfst, proxygen) call find_package(folly), +# find_package(fizz), etc. +# which would fail because these aren't installed — they're in-tree targets. +# Also skip optional compression codecs not needed for moxygen. +# Skip lzma unless libunwind is available (lzma is used for symbolization). +set(_FETCHCONTENT_PROVIDED_DEPS folly fizz wangle mvfst proxygen GTest GMock + BZip2 LZ4 Snappy) +if(NOT LibUnwind_FOUND) + list(APPEND _FETCHCONTENT_PROVIDED_DEPS LibLZMA) + message(STATUS "LibUnwind not found, disabling lzma") +else() + message(STATUS "LibUnwind found, enabling lzma for symbolization") +endif() +macro(find_package PKG_NAME) + list(FIND _FETCHCONTENT_PROVIDED_DEPS "${PKG_NAME}" _fc_idx) + if(_fc_idx GREATER -1) + # Skip — already provided by FetchContent add_subdirectory + else() + _find_package(${ARGV}) + endif() +endmacro() + +FetchContent_GetProperties(folly) +if(NOT folly_POPULATED) + FetchContent_Populate(folly) + add_subdirectory(${folly_SOURCE_DIR} ${folly_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() + +FetchContent_GetProperties(fizz) +if(NOT fizz_POPULATED) + FetchContent_Populate(fizz) + add_subdirectory( + ${fizz_SOURCE_DIR}/fizz ${fizz_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() + +FetchContent_GetProperties(wangle) +if(NOT wangle_POPULATED) + FetchContent_Populate(wangle) + add_subdirectory( + ${wangle_SOURCE_DIR}/wangle ${wangle_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() + +FetchContent_GetProperties(mvfst) +if(NOT mvfst_POPULATED) + FetchContent_Populate(mvfst) + add_subdirectory(${mvfst_SOURCE_DIR} ${mvfst_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() + +FetchContent_GetProperties(proxygen) +if(NOT proxygen_POPULATED) + FetchContent_Populate(proxygen) + add_subdirectory( + ${proxygen_SOURCE_DIR} ${proxygen_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() + +# ============================================================================= +# Moxygen +# ============================================================================= +# Re-enable tests for moxygen itself +set(BUILD_TESTS ${_MOXYGEN_BUILD_TESTS} CACHE BOOL "" FORCE) + +# Set MOXYGEN_FBCODE_ROOT to match what main CMakeLists.txt expects +set(MOXYGEN_FBCODE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..) + +# Include the compile options setup +include(CheckCXXCompilerFlag) +list(APPEND _MOXYGEN_COMMON_COMPILE_OPTIONS -Wall -Wextra) +CHECK_CXX_COMPILER_FLAG(-Wno-unused-parameter COMPILER_HAS_W_UNUSED_PARAMETER) +if(COMPILER_HAS_W_UNUSED_PARAMETER) + list(APPEND _MOXYGEN_COMMON_COMPILE_OPTIONS -Wno-unused-parameter) +endif() + +# Add moxygen cmake modules to path and include test helper +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/../cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/../build/fbcode_builder/CMake" +) +include(MoxygenFunctions) +if(BUILD_TESTS) + include(MoxygenTest) +endif() + +# Include moxygen's main CMakeLists.txt +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../moxygen moxygen) + +# ============================================================================= +# Installation (mirrors main CMakeLists.txt) +# ============================================================================= +set(LIB_INSTALL_DIR "lib" CACHE STRING "Library install directory") +set(CMAKE_INSTALL_DIR + "${LIB_INSTALL_DIR}/cmake/moxygen/" + CACHE STRING "CMake config install directory") + +install( + EXPORT moxygen-exports + FILE moxygen-targets.cmake + NAMESPACE moxygen:: + DESTINATION ${CMAKE_INSTALL_DIR} +) + +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/moxygen-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/moxygen-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_DIR} +) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/moxygen-config.cmake + DESTINATION ${CMAKE_INSTALL_DIR} +) diff --git a/standalone/README.md b/standalone/README.md new file mode 100644 index 000000000..e4e08c398 --- /dev/null +++ b/standalone/README.md @@ -0,0 +1,127 @@ +# Moxygen Standalone Build + +This directory provides a standalone build configuration that automatically +fetches Meta's OSS dependencies (folly, fizz, wangle, mvfst, proxygen) using +CMake's FetchContent. + +**Note:** This build method is for the open-source GitHub version only. It reads +pinned commit hashes from `build/deps/github_hashes/` which are included in the +GitHub repository but not in fbsource. + +## Prerequisites + +System dependencies must be installed before building. Run: + +```bash +./standalone/install-system-deps.sh +``` + +Or install manually: +- OpenSSL +- glog +- gflags +- double-conversion +- libevent +- libsodium +- zstd +- boost (context, filesystem, program_options) +- fmt +- c-ares +- gperf +- googletest (for tests) + +## Building + +```bash +# Clone the repo (github_hashes are included) +git clone https://github.com/facebookexperimental/moxygen.git +cd moxygen + +# Install system deps +./standalone/install-system-deps.sh + +# Build using standalone entry point +cmake -B _build -S standalone -G Ninja +cmake --build _build -j$(nproc) + +# Run tests +ctest --test-dir _build +``` + +## How Version Pinning Works + +The standalone build reads commit hashes from `build/deps/github_hashes/`: +```text +build/deps/github_hashes/ +├── facebook/ +│ ├── folly-rev.txt +│ ├── mvfst-rev.txt +│ ├── proxygen-rev.txt +│ └── wangle-rev.txt +└── facebookincubator/ + └── fizz-rev.txt +``` + +These are the same hashes used by getdeps.py, ensuring both build methods +use identical dependency versions. + +## Options + +| Option | Default | Description | +|--------|---------|-------------| +| `BUILD_TESTS` | `ON` | Build test executables | +| `BUILD_SHARED_LIBS` | `OFF` | Build shared libraries (discouraged) | + +## Build Caching + +To avoid re-downloading dependencies on clean builds: + +```bash +cmake -B _build -S standalone \ + -DFETCHCONTENT_BASE_DIR=$HOME/.cmake-fetchcontent +``` + +## Testing Local Changes to Dependencies + +To test local modifications to any dependency (e.g., proxygen, mvfst), use +CMake's `FETCHCONTENT_SOURCE_DIR_` variables. This skips fetching and +uses your local checkout instead: + +```bash +# Test local proxygen changes +cmake -B _build -S standalone \ + -DFETCHCONTENT_SOURCE_DIR_PROXYGEN=/path/to/my/proxygen + +# Test local mvfst changes +cmake -B _build -S standalone \ + -DFETCHCONTENT_SOURCE_DIR_MVFST=/path/to/my/mvfst + +# Test multiple local deps at once +cmake -B _build -S standalone \ + -DFETCHCONTENT_SOURCE_DIR_PROXYGEN=/path/to/proxygen \ + -DFETCHCONTENT_SOURCE_DIR_MVFST=/path/to/mvfst \ + -DFETCHCONTENT_SOURCE_DIR_FOLLY=/path/to/folly +``` + +Available override variables: +- `FETCHCONTENT_SOURCE_DIR_FOLLY` +- `FETCHCONTENT_SOURCE_DIR_FIZZ` +- `FETCHCONTENT_SOURCE_DIR_WANGLE` +- `FETCHCONTENT_SOURCE_DIR_MVFST` +- `FETCHCONTENT_SOURCE_DIR_PROXYGEN` + +**Note:** When using local sources, you're responsible for ensuring version +compatibility between dependencies. + +## Troubleshooting + +### "Rev file not found" +Make sure you cloned from GitHub (not fbsource). The github_hashes +directory is only present in the open-source repository. + +### "Could not find package X" +Ensure system dependencies are installed. Run `./standalone/install-system-deps.sh`. + +### First build is slow +FetchContent downloads ~500MB of source code on first run. Subsequent +builds use the cache. diff --git a/standalone/install-system-deps.sh b/standalone/install-system-deps.sh new file mode 100755 index 000000000..54d109d52 --- /dev/null +++ b/standalone/install-system-deps.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Installs system dependencies required for standalone moxygen build + +set -e + +install_ubuntu() { + echo "Installing dependencies for Ubuntu/Debian..." + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + cmake \ + ninja-build \ + git \ + libssl-dev \ + libunwind-dev \ + libgoogle-glog-dev \ + libgflags-dev \ + libdouble-conversion-dev \ + libevent-dev \ + libsodium-dev \ + libzstd-dev \ + libboost-all-dev \ + libfmt-dev \ + zlib1g-dev \ + libc-ares-dev \ + gperf +} + +install_fedora() { + echo "Installing dependencies for Fedora..." + sudo dnf install -y \ + cmake \ + ninja-build \ + git \ + openssl-devel \ + glog-devel \ + gflags-devel \ + double-conversion-devel \ + libevent-devel \ + libsodium-devel \ + libzstd-devel \ + boost-devel \ + fmt-devel \ + zlib-devel \ + c-ares-devel \ + gperf +} + +install_macos() { + echo "Installing dependencies for macOS..." + brew install \ + cmake \ + ninja \ + openssl@3 \ + glog \ + gflags \ + double-conversion \ + libevent \ + libsodium \ + zstd \ + boost \ + fmt \ + c-ares \ + gperf +} + +# Detect OS - check macOS first +if [[ "$(uname)" == "Darwin" ]]; then + install_macos +elif [[ -f /etc/os-release ]]; then + . /etc/os-release + case "$ID" in + ubuntu|debian) + install_ubuntu + ;; + fedora) + install_fedora + ;; + *) + echo "Unsupported Linux distribution: $ID" + echo "Please install dependencies manually (see README.md)" + exit 1 + ;; + esac +else + echo "Unsupported operating system" + exit 1 +fi + +echo "" +echo "System dependencies installed successfully!" +echo "You can now build moxygen with:" +echo " cmake -B _build -S standalone -G Ninja" +echo " cmake --build _build"