mirror of
https://github.com/facebook/proxygen.git
synced 2026-10-24 01:33:54 +03:00
Summary: Pull Request resolved: https://github.com/facebook/proxygen/pull/601 The granular CMake refactoring in D91009064 broke shared library builds. 1. `proxygen_add_library()` creates INTERFACE targets for shared builds, but `lib/CMakeLists.txt` called `target_include_directories(... PUBLIC ...)` on them. CMake only allows INTERFACE properties on INTERFACE targets. These calls were redundant since `proxygen_add_library()` already sets the same include directories. 2. In `proxygen_resolve_deferred_dependencies()`, `_obj` targets could fall through to linking against plain INTERFACE wrapper targets, which link to the monolithic `proxygen` shared library. Since `proxygen` is built from those same `_obj` targets, this creates a dependency cycle. Skip deps without an `_obj` version for `_obj` targets in shared builds. Reviewed By: jbeshay Differential Revision: D96253183 fbshipit-source-id: 1b0a1728c014b8475834e31ff09bbed7bd912948
476 lines
16 KiB
CMake
476 lines
16 KiB
CMake
# 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.
|
|
|
|
# Initialize global properties for tracking targets and deferred dependencies
|
|
set_property(GLOBAL PROPERTY PROXYGEN_COMPONENT_TARGETS)
|
|
set_property(GLOBAL PROPERTY PROXYGEN_DEFERRED_DEPS)
|
|
set_property(GLOBAL PROPERTY PROXYGEN_GRANULAR_INTERFACE_TARGETS)
|
|
|
|
# Define a granular proxygen library that:
|
|
# 1. Compiles sources ONCE via OBJECT library
|
|
# 2. Creates a STATIC library for individual linking (static builds)
|
|
# 3. Creates an INTERFACE library linking to monolithic proxygen (shared builds)
|
|
# 4. Defers internal proxygen deps to be resolved later
|
|
# 5. Tracks OBJECT target for monolithic aggregation
|
|
# 6. Creates proxygen:: namespace alias
|
|
#
|
|
# Usage:
|
|
# proxygen_add_library(proxygen_http_message
|
|
# SRCS HTTPMessage.cpp
|
|
# DEPS proxygen_http_headers # Private dependencies
|
|
# EXPORTED_DEPS Folly::folly_io_iobuf # Public dependencies (propagated)
|
|
# EXCLUDE_FROM_MONOLITH # Don't include in monolithic proxygen library
|
|
# )
|
|
function(proxygen_add_library _target_name)
|
|
cmake_parse_arguments(
|
|
PROXYGEN_LIB
|
|
"EXCLUDE_FROM_MONOLITH" # Options (boolean flags)
|
|
"" # Single-value args
|
|
"SRCS;DEPS;EXPORTED_DEPS" # Multi-value args
|
|
${ARGN}
|
|
)
|
|
|
|
set(_sources ${PROXYGEN_LIB_SRCS})
|
|
if(NOT _sources)
|
|
# Legacy support: if no SRCS keyword, treat remaining args as sources
|
|
set(_sources ${PROXYGEN_LIB_UNPARSED_ARGUMENTS})
|
|
endif()
|
|
|
|
# Object library name - used for monolithic aggregation
|
|
set(_obj_target "${_target_name}_obj")
|
|
|
|
# Skip if no sources (header-only library)
|
|
list(LENGTH _sources _src_count)
|
|
if(_src_count EQUAL 0)
|
|
# Header-only: create INTERFACE library
|
|
add_library(${_target_name} INTERFACE)
|
|
target_include_directories(${_target_name}
|
|
INTERFACE
|
|
$<BUILD_INTERFACE:${PROXYGEN_FBCODE_ROOT}>
|
|
$<BUILD_INTERFACE:${PROXYGEN_GENERATED_ROOT}>
|
|
$<INSTALL_INTERFACE:include/>
|
|
)
|
|
|
|
# Link exported deps for INTERFACE libraries
|
|
if(PROXYGEN_LIB_EXPORTED_DEPS)
|
|
target_link_libraries(${_target_name} INTERFACE ${PROXYGEN_LIB_EXPORTED_DEPS})
|
|
endif()
|
|
|
|
install(TARGETS ${_target_name} EXPORT proxygen-exports)
|
|
add_library(proxygen::${_target_name} ALIAS ${_target_name})
|
|
return()
|
|
endif()
|
|
|
|
# 1. Create OBJECT library (compiles sources once)
|
|
add_library(${_obj_target} OBJECT ${_sources})
|
|
|
|
# Ensure generated headers are built before any proxygen sources
|
|
# This is needed because transitive deps don't carry build-order dependencies
|
|
if(TARGET proxygen-generated)
|
|
add_dependencies(${_obj_target} proxygen-generated)
|
|
endif()
|
|
|
|
if(DEFINED PACKAGE_VERSION)
|
|
set_property(TARGET ${_obj_target} PROPERTY VERSION ${PACKAGE_VERSION})
|
|
endif()
|
|
|
|
if(BUILD_SHARED_LIBS)
|
|
set_property(TARGET ${_obj_target} PROPERTY POSITION_INDEPENDENT_CODE ON)
|
|
endif()
|
|
|
|
target_include_directories(${_obj_target}
|
|
PUBLIC
|
|
$<BUILD_INTERFACE:${PROXYGEN_FBCODE_ROOT}>
|
|
$<BUILD_INTERFACE:${PROXYGEN_GENERATED_ROOT}>
|
|
$<INSTALL_INTERFACE:include/>
|
|
)
|
|
|
|
target_compile_options(${_obj_target}
|
|
PRIVATE
|
|
${_PROXYGEN_COMMON_COMPILE_OPTIONS}
|
|
)
|
|
|
|
target_compile_features(${_obj_target} PUBLIC cxx_std_20)
|
|
|
|
# Separate proxygen internal deps (defer) from external deps (link immediately)
|
|
# Also separate utility deps (like proxygen-generated) that need add_dependencies
|
|
set(_immediate_deps "")
|
|
set(_proxygen_deps "")
|
|
set(_utility_deps "")
|
|
foreach(_dep IN LISTS PROXYGEN_LIB_EXPORTED_DEPS)
|
|
if(_dep STREQUAL "proxygen-generated")
|
|
# Utility target - use add_dependencies, not target_link_libraries
|
|
list(APPEND _utility_deps ${_dep})
|
|
elseif(_dep MATCHES "^proxygen_")
|
|
list(APPEND _proxygen_deps ${_dep})
|
|
else()
|
|
# Folly::*, fizz::*, mvfst::*, wangle::*, external libs - link immediately
|
|
list(APPEND _immediate_deps ${_dep})
|
|
endif()
|
|
endforeach()
|
|
|
|
# Add build-order dependencies on utility targets (like proxygen-generated)
|
|
if(_utility_deps)
|
|
add_dependencies(${_obj_target} ${_utility_deps})
|
|
endif()
|
|
|
|
# Link non-proxygen deps immediately - they provide include paths needed at compile time
|
|
if(_immediate_deps)
|
|
target_link_libraries(${_obj_target} PUBLIC ${_immediate_deps})
|
|
endif()
|
|
|
|
# For shared builds: link Folly::folly, fizz::fizz, wangle::wangle to OBJECT libraries
|
|
# to get transitive includes
|
|
if(BUILD_SHARED_LIBS)
|
|
target_link_libraries(${_obj_target} PUBLIC Folly::folly fizz::fizz wangle::wangle)
|
|
endif()
|
|
|
|
# Defer internal proxygen dependencies until all targets are created
|
|
# This is needed for both static and shared builds because:
|
|
# - Static: avoid circular dependencies during library creation
|
|
# - Shared: OBJECT libraries need include paths from deps that may not exist yet
|
|
if(_proxygen_deps)
|
|
list(JOIN _proxygen_deps "," _deps_str)
|
|
set_property(GLOBAL APPEND PROPERTY PROXYGEN_DEFERRED_DEPS
|
|
"${_obj_target}|PUBLIC|${_deps_str}"
|
|
)
|
|
endif()
|
|
if(PROXYGEN_LIB_DEPS)
|
|
# Separate proxygen internal deps (defer) from external deps (link immediately)
|
|
set(_private_immediate_deps "")
|
|
set(_private_proxygen_deps "")
|
|
foreach(_dep IN LISTS PROXYGEN_LIB_DEPS)
|
|
if(_dep MATCHES "^proxygen_")
|
|
list(APPEND _private_proxygen_deps ${_dep})
|
|
else()
|
|
list(APPEND _private_immediate_deps ${_dep})
|
|
endif()
|
|
endforeach()
|
|
|
|
if(_private_immediate_deps)
|
|
target_link_libraries(${_obj_target} PRIVATE ${_private_immediate_deps})
|
|
endif()
|
|
|
|
if(_private_proxygen_deps)
|
|
list(JOIN _private_proxygen_deps "," _deps_str)
|
|
set_property(GLOBAL APPEND PROPERTY PROXYGEN_DEFERRED_DEPS
|
|
"${_obj_target}|PRIVATE|${_deps_str}"
|
|
)
|
|
endif()
|
|
endif()
|
|
|
|
# Track OBJECT target for monolithic aggregation (unless excluded)
|
|
if(NOT PROXYGEN_LIB_EXCLUDE_FROM_MONOLITH)
|
|
set_property(GLOBAL APPEND PROPERTY PROXYGEN_COMPONENT_TARGETS ${_obj_target})
|
|
endif()
|
|
|
|
# 2. Create the granular library target
|
|
if(BUILD_SHARED_LIBS AND NOT PROXYGEN_LIB_EXCLUDE_FROM_MONOLITH)
|
|
# For shared builds: create INTERFACE library that will link to monolithic proxygen
|
|
add_library(${_target_name} INTERFACE)
|
|
|
|
target_include_directories(${_target_name}
|
|
INTERFACE
|
|
$<BUILD_INTERFACE:${PROXYGEN_FBCODE_ROOT}>
|
|
$<BUILD_INTERFACE:${PROXYGEN_GENERATED_ROOT}>
|
|
$<INSTALL_INTERFACE:include/>
|
|
)
|
|
|
|
# Track this target to link to proxygen after monolithic library is created
|
|
set_property(GLOBAL APPEND PROPERTY PROXYGEN_GRANULAR_INTERFACE_TARGETS ${_target_name})
|
|
|
|
install(TARGETS ${_target_name} EXPORT proxygen-exports)
|
|
elseif(BUILD_SHARED_LIBS AND PROXYGEN_LIB_EXCLUDE_FROM_MONOLITH)
|
|
# For excluded targets in shared builds: create SHARED library with actual code
|
|
# These are NOT in the monolithic proxygen, so they need their own implementation
|
|
add_library(${_target_name} SHARED $<TARGET_OBJECTS:${_obj_target}>)
|
|
|
|
if(DEFINED PACKAGE_VERSION)
|
|
set_property(TARGET ${_target_name} PROPERTY VERSION ${PACKAGE_VERSION})
|
|
endif()
|
|
|
|
target_include_directories(${_target_name}
|
|
PUBLIC
|
|
$<BUILD_INTERFACE:${PROXYGEN_FBCODE_ROOT}>
|
|
$<BUILD_INTERFACE:${PROXYGEN_GENERATED_ROOT}>
|
|
$<INSTALL_INTERFACE:include/>
|
|
)
|
|
|
|
target_compile_features(${_target_name} PUBLIC cxx_std_20)
|
|
|
|
# Add build-order dependencies on utility targets
|
|
if(_utility_deps)
|
|
add_dependencies(${_target_name} ${_utility_deps})
|
|
endif()
|
|
|
|
# Link non-proxygen deps immediately
|
|
if(_immediate_deps)
|
|
target_link_libraries(${_target_name} PUBLIC ${_immediate_deps})
|
|
endif()
|
|
|
|
# Defer internal proxygen dependencies
|
|
if(_proxygen_deps)
|
|
list(JOIN _proxygen_deps "," _deps_str)
|
|
set_property(GLOBAL APPEND PROPERTY PROXYGEN_DEFERRED_DEPS
|
|
"${_target_name}|PUBLIC|${_deps_str}"
|
|
)
|
|
endif()
|
|
if(PROXYGEN_LIB_DEPS)
|
|
set(_priv_imm "")
|
|
set(_priv_prox "")
|
|
foreach(_dep IN LISTS PROXYGEN_LIB_DEPS)
|
|
if(_dep MATCHES "^proxygen_")
|
|
list(APPEND _priv_prox ${_dep})
|
|
else()
|
|
list(APPEND _priv_imm ${_dep})
|
|
endif()
|
|
endforeach()
|
|
if(_priv_imm)
|
|
target_link_libraries(${_target_name} PRIVATE ${_priv_imm})
|
|
endif()
|
|
if(_priv_prox)
|
|
list(JOIN _priv_prox "," _deps_str)
|
|
set_property(GLOBAL APPEND PROPERTY PROXYGEN_DEFERRED_DEPS
|
|
"${_target_name}|PRIVATE|${_deps_str}"
|
|
)
|
|
endif()
|
|
endif()
|
|
|
|
install(
|
|
TARGETS ${_target_name}
|
|
EXPORT proxygen-exports
|
|
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
|
|
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
|
|
)
|
|
else()
|
|
# For static builds: create STATIC library
|
|
add_library(${_target_name} STATIC $<TARGET_OBJECTS:${_obj_target}>)
|
|
|
|
if(DEFINED PACKAGE_VERSION)
|
|
set_property(TARGET ${_target_name} PROPERTY VERSION ${PACKAGE_VERSION})
|
|
endif()
|
|
|
|
target_include_directories(${_target_name}
|
|
PUBLIC
|
|
$<BUILD_INTERFACE:${PROXYGEN_FBCODE_ROOT}>
|
|
$<BUILD_INTERFACE:${PROXYGEN_GENERATED_ROOT}>
|
|
$<INSTALL_INTERFACE:include/>
|
|
)
|
|
|
|
target_compile_features(${_target_name} PUBLIC cxx_std_20)
|
|
|
|
# Add build-order dependencies on utility targets (reuse _utility_deps from above)
|
|
if(_utility_deps)
|
|
add_dependencies(${_target_name} ${_utility_deps})
|
|
endif()
|
|
|
|
# Link non-proxygen deps immediately (reuse _immediate_deps computed above)
|
|
if(_immediate_deps)
|
|
target_link_libraries(${_target_name} PUBLIC ${_immediate_deps})
|
|
endif()
|
|
|
|
# Defer internal proxygen dependencies for STATIC library too (reuse _proxygen_deps)
|
|
if(_proxygen_deps)
|
|
list(JOIN _proxygen_deps "," _deps_str)
|
|
set_property(GLOBAL APPEND PROPERTY PROXYGEN_DEFERRED_DEPS
|
|
"${_target_name}|PUBLIC|${_deps_str}"
|
|
)
|
|
endif()
|
|
if(PROXYGEN_LIB_DEPS)
|
|
set(_priv_imm "")
|
|
set(_priv_prox "")
|
|
foreach(_dep IN LISTS PROXYGEN_LIB_DEPS)
|
|
if(_dep MATCHES "^proxygen_")
|
|
list(APPEND _priv_prox ${_dep})
|
|
else()
|
|
list(APPEND _priv_imm ${_dep})
|
|
endif()
|
|
endforeach()
|
|
if(_priv_imm)
|
|
target_link_libraries(${_target_name} PRIVATE ${_priv_imm})
|
|
endif()
|
|
if(_priv_prox)
|
|
list(JOIN _priv_prox "," _deps_str)
|
|
set_property(GLOBAL APPEND PROPERTY PROXYGEN_DEFERRED_DEPS
|
|
"${_target_name}|PRIVATE|${_deps_str}"
|
|
)
|
|
endif()
|
|
endif()
|
|
|
|
install(
|
|
TARGETS ${_target_name}
|
|
EXPORT proxygen-exports
|
|
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
|
|
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
|
|
)
|
|
endif()
|
|
|
|
# Create alias for the library
|
|
add_library(proxygen::${_target_name} ALIAS ${_target_name})
|
|
endfunction()
|
|
|
|
# Create a backwards-compatible alias target
|
|
# This creates an INTERFACE library with the old name that links to the new target
|
|
function(proxygen_add_compat_alias _old_name _new_name)
|
|
if(NOT TARGET ${_new_name})
|
|
message(WARNING "Cannot create compat alias ${_old_name}: target ${_new_name} does not exist")
|
|
return()
|
|
endif()
|
|
|
|
add_library(${_old_name} INTERFACE)
|
|
target_link_libraries(${_old_name} INTERFACE ${_new_name})
|
|
install(TARGETS ${_old_name} EXPORT proxygen-exports)
|
|
add_library(proxygen::${_old_name} ALIAS ${_old_name})
|
|
endfunction()
|
|
|
|
# Create the monolithic proxygen library from all component OBJECT libraries
|
|
# Call this after all add_subdirectory() calls, before proxygen_resolve_deferred_dependencies()
|
|
function(proxygen_create_monolithic_library)
|
|
get_property(_component_targets GLOBAL PROPERTY PROXYGEN_COMPONENT_TARGETS)
|
|
|
|
if(NOT _component_targets)
|
|
message(STATUS "No component targets found, skipping monolithic library creation")
|
|
return()
|
|
endif()
|
|
|
|
# Collect all object files from component targets
|
|
set(_all_objects)
|
|
foreach(_target IN LISTS _component_targets)
|
|
list(APPEND _all_objects $<TARGET_OBJECTS:${_target}>)
|
|
endforeach()
|
|
|
|
# Create the monolithic library
|
|
add_library(proxygen ${_all_objects})
|
|
|
|
if(BUILD_SHARED_LIBS)
|
|
set_property(TARGET proxygen PROPERTY POSITION_INDEPENDENT_CODE ON)
|
|
if(DEFINED PACKAGE_VERSION)
|
|
set_target_properties(proxygen PROPERTIES VERSION ${PACKAGE_VERSION})
|
|
endif()
|
|
endif()
|
|
|
|
target_include_directories(proxygen
|
|
PUBLIC
|
|
$<BUILD_INTERFACE:${PROXYGEN_FBCODE_ROOT}>
|
|
$<BUILD_INTERFACE:${PROXYGEN_GENERATED_ROOT}>
|
|
$<INSTALL_INTERFACE:include/>
|
|
)
|
|
|
|
target_compile_features(proxygen PUBLIC cxx_std_20)
|
|
|
|
# Link all dependencies
|
|
target_link_libraries(proxygen
|
|
PUBLIC
|
|
Folly::folly
|
|
fizz::fizz
|
|
wangle::wangle
|
|
${ZSTD_LIBRARIES}
|
|
ZLIB::ZLIB
|
|
${OPENSSL_LIBRARIES}
|
|
Threads::Threads
|
|
cares
|
|
PRIVATE
|
|
glog::glog
|
|
${GFLAG_DEPENDENCIES}
|
|
${CMAKE_DL_LIBS}
|
|
)
|
|
|
|
# Create alias for consistency
|
|
add_library(proxygen::proxygen ALIAS proxygen)
|
|
|
|
# For shared builds: link all granular INTERFACE targets to the monolithic library
|
|
if(BUILD_SHARED_LIBS)
|
|
cmake_policy(SET CMP0079 NEW)
|
|
get_property(_interface_targets GLOBAL PROPERTY PROXYGEN_GRANULAR_INTERFACE_TARGETS)
|
|
foreach(_target IN LISTS _interface_targets)
|
|
target_link_libraries(${_target} INTERFACE proxygen)
|
|
endforeach()
|
|
endif()
|
|
endfunction()
|
|
|
|
# Resolve all deferred dependencies after all targets have been created
|
|
# Call this after all add_subdirectory() calls
|
|
function(proxygen_resolve_deferred_dependencies)
|
|
# Allow linking targets defined in other directories
|
|
cmake_policy(SET CMP0079 NEW)
|
|
|
|
# For shared builds: link all granular INTERFACE targets to the monolithic library
|
|
# This is needed because proxygen_add_library creates INTERFACE libraries for shared builds
|
|
# that need to link to the monolithic proxygen target
|
|
if(BUILD_SHARED_LIBS)
|
|
get_property(_interface_targets GLOBAL PROPERTY PROXYGEN_GRANULAR_INTERFACE_TARGETS)
|
|
foreach(_target IN LISTS _interface_targets)
|
|
if(TARGET ${_target} AND TARGET proxygen)
|
|
target_link_libraries(${_target} INTERFACE proxygen)
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
get_property(_deferred_deps GLOBAL PROPERTY PROXYGEN_DEFERRED_DEPS)
|
|
|
|
foreach(_spec IN LISTS _deferred_deps)
|
|
# Parse the spec: "target|visibility|dep1,dep2,..."
|
|
string(REPLACE "|" ";" _parts "${_spec}")
|
|
list(LENGTH _parts _len)
|
|
if(_len LESS 3)
|
|
continue()
|
|
endif()
|
|
|
|
list(GET _parts 0 _target)
|
|
list(GET _parts 1 _visibility)
|
|
list(GET _parts 2 _deps_str)
|
|
|
|
# Split deps by comma
|
|
string(REPLACE "," ";" _deps "${_deps_str}")
|
|
|
|
# Filter to only existing targets (skip deps that weren't generated)
|
|
# For shared builds and OBJECT targets, prefer linking to _obj version for include paths
|
|
set(_valid_deps "")
|
|
foreach(_dep IN LISTS _deps)
|
|
# For shared builds: if target is an OBJECT library (*_obj) and dep has an _obj version,
|
|
# link to _obj for include path propagation
|
|
if(BUILD_SHARED_LIBS AND _target MATCHES "_obj$" AND TARGET ${_dep}_obj)
|
|
list(APPEND _valid_deps ${_dep}_obj)
|
|
elseif(BUILD_SHARED_LIBS AND _target MATCHES "_obj$")
|
|
# No _obj version exists; skip to avoid cycle through monolithic library
|
|
elseif(TARGET ${_dep})
|
|
list(APPEND _valid_deps ${_dep})
|
|
endif()
|
|
endforeach()
|
|
|
|
if(_valid_deps)
|
|
target_link_libraries(${_target} ${_visibility} ${_valid_deps})
|
|
endif()
|
|
endforeach()
|
|
endfunction()
|
|
|
|
# =============================================================================
|
|
# Header installation function
|
|
# =============================================================================
|
|
# Install headers preserving directory structure relative to rootDir
|
|
# Usage: proxygen_install_headers(proxygen ${CMAKE_CURRENT_SOURCE_DIR} ${HEADERS})
|
|
function(proxygen_install_headers rootName rootDir)
|
|
file(TO_CMAKE_PATH "${rootDir}" rootDir)
|
|
string(LENGTH "${rootDir}" rootDirLength)
|
|
foreach(fil ${ARGN})
|
|
file(TO_CMAKE_PATH "${fil}" filePath)
|
|
string(FIND "${filePath}" "/" rIdx REVERSE)
|
|
if(rIdx EQUAL -1)
|
|
continue()
|
|
endif()
|
|
string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)
|
|
|
|
string(LENGTH "${filePath}" filePathLength)
|
|
string(FIND "${filePath}" "${rootDir}" rIdx)
|
|
if(rIdx EQUAL 0)
|
|
math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")
|
|
string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)
|
|
install(FILES ${fil}
|
|
DESTINATION ${INCLUDE_INSTALL_DIR}/${rootName}${fileGroup})
|
|
endif()
|
|
endforeach()
|
|
endfunction()
|