# Copyright (c) Facebook, Inc. and its affiliates. include(FBCMakeParseArgs) # Generate a C++ library from a thrift file # # Parameters: - SERVICES [ ...] The names of the services defined # in the thrift file. - DEPENDS [ ...] A list of other thrift C++ # libraries that this library depends on. - OPTIONS [ ...] A list # of options to pass to the thrift compiler. - INCLUDE_DIR The # sub-directory where generated headers will be installed. Defaults to "include" # if not specified. The caller must still call install() to install the thrift # library if desired. - THRIFT_INCLUDE_DIR The sub-directory where # generated headers will be installed. Defaults to "${INCLUDE_DIR}/thrift-files" # if not specified. The caller must still call install() to install the thrift # library if desired. function(add_fbthrift_cpp_library LIB_NAME THRIFT_FILE) # Parse the arguments set(one_value_args INCLUDE_DIR THRIFT_INCLUDE_DIR) set(multi_value_args SERVICES DEPENDS OPTIONS) fb_cmake_parse_args(ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}") if(NOT DEFINED ARG_INCLUDE_DIR) set(ARG_INCLUDE_DIR "include") endif() if(NOT DEFINED ARG_THRIFT_INCLUDE_DIR) set(ARG_THRIFT_INCLUDE_DIR "${ARG_INCLUDE_DIR}/thrift-files") endif() get_filename_component(base ${THRIFT_FILE} NAME_WE) get_filename_component(output_dir ${CMAKE_CURRENT_BINARY_DIR}/${THRIFT_FILE} DIRECTORY) # Generate relative paths in #includes file(RELATIVE_PATH include_prefix "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${THRIFT_FILE}") get_filename_component(include_prefix ${include_prefix} DIRECTORY) if(NOT "${include_prefix}" STREQUAL "") list(APPEND ARG_OPTIONS "include_prefix=${include_prefix}") endif() # CMake 3.12 is finally getting a list(JOIN) function, but until then treating # the list as a string and replacing the semicolons is good enough. string(REPLACE ";" "," GEN_ARG_STR "${ARG_OPTIONS}") # Compute the list of generated files list( APPEND generated_headers "${output_dir}/gen-cpp2/${base}_constants.h" "${output_dir}/gen-cpp2/${base}_types.h" "${output_dir}/gen-cpp2/${base}_types.tcc" "${output_dir}/gen-cpp2/${base}_types_custom_protocol.h" "${output_dir}/gen-cpp2/${base}_metadata.h") list( APPEND generated_sources "${output_dir}/gen-cpp2/${base}_constants.cpp" "${output_dir}/gen-cpp2/${base}_data.h" "${output_dir}/gen-cpp2/${base}_data.cpp" "${output_dir}/gen-cpp2/${base}_types.cpp" "${output_dir}/gen-cpp2/${base}_metadata.cpp") foreach(service IN LISTS ARG_SERVICES) list( APPEND generated_headers "${output_dir}/gen-cpp2/${service}.h" "${output_dir}/gen-cpp2/${service}.tcc" "${output_dir}/gen-cpp2/${service}AsyncClient.h" "${output_dir}/gen-cpp2/${service}_custom_protocol.h") list( APPEND generated_sources "${output_dir}/gen-cpp2/${service}.cpp" "${output_dir}/gen-cpp2/${service}AsyncClient.cpp" "${output_dir}/gen-cpp2/${service}_processmap_binary.cpp" "${output_dir}/gen-cpp2/${service}_processmap_compact.cpp") endforeach() # This generator expression gets the list of include directories required for # all of our dependencies. It requires using COMMAND_EXPAND_LISTS in the # add_custom_command() call below. COMMAND_EXPAND_LISTS is only available in # CMake 3.8+ If we really had to support older versions of CMake we would # probably need to use a wrapper script around the thrift compiler that could # take the include list as a single argument and split it up before invoking # the thrift compiler. if(NOT POLICY CMP0067) message(FATAL_ERROR "add_fbthrift_cpp_library() requires CMake 3.8+") endif() set(thrift_include_options "-I;$,;-I;>" ) # Emit the rule to run the thrift compiler add_custom_command( OUTPUT ${generated_headers} ${generated_sources} COMMAND_EXPAND_LISTS COMMAND "${CMAKE_COMMAND}" -E make_directory "${output_dir}" COMMAND "${FBTHRIFT_COMPILER}" # TODO: this flag does not exist in all fbthrift versions. --legacy-strict --gen "mstch_cpp2:${GEN_ARG_STR}" "${thrift_include_options}" -I "${FBTHRIFT_INCLUDE_DIR}" -o "${output_dir}" "${CMAKE_CURRENT_SOURCE_DIR}/${THRIFT_FILE}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" MAIN_DEPENDENCY "${THRIFT_FILE}" DEPENDS ${ARG_DEPENDS} "${FBTHRIFT_COMPILER}") # Now emit the library rule to compile the sources if(BUILD_SHARED_LIBS) set(LIB_TYPE SHARED) else() set(LIB_TYPE STATIC) endif() add_library("${LIB_NAME}" ${LIB_TYPE} ${generated_sources}) target_include_directories( "${LIB_NAME}" PUBLIC "$" "$") target_link_libraries( "${LIB_NAME}" PUBLIC ${ARG_DEPENDS} Folly::folly FBThrift::thriftcpp2 # TODO: these symbols require other dependencies that need to be # correctly handle by Velox's build system before they can be # enabled. mvfst::mvfst_server_async_tran mvfst::mvfst_server ) # Add ${generated_headers} to the PUBLIC_HEADER property for ${LIB_NAME} # # This allows callers to install it using "install(TARGETS ${LIB_NAME} # PUBLIC_HEADER)" However, note that CMake's PUBLIC_HEADER behavior is rather # inflexible, and does have any way to preserve header directory structure. # Callers must be careful to use the correct PUBLIC_HEADER DESTINATION # parameter when doing this, to put the files the correct directory # themselves. We define a HEADER_INSTALL_DIR property with the include # directory prefix, so typically callers should specify the PUBLIC_HEADER # DESTINATION as "$" set_property(TARGET "${LIB_NAME}" PROPERTY PUBLIC_HEADER ${generated_headers}) # Define a dummy interface library to help propagate the thrift include # directories between dependencies. add_library("${LIB_NAME}.thrift_includes" INTERFACE) target_include_directories( "${LIB_NAME}.thrift_includes" INTERFACE "$" "$") foreach(dep IN LISTS ARG_DEPENDS) target_link_libraries("${LIB_NAME}.thrift_includes" INTERFACE "${dep}.thrift_includes") endforeach() set_target_properties( "${LIB_NAME}" PROPERTIES EXPORT_PROPERTIES "THRIFT_INSTALL_DIR" THRIFT_INSTALL_DIR "${ARG_THRIFT_INCLUDE_DIR}/${include_prefix}" HEADER_INSTALL_DIR "${ARG_INCLUDE_DIR}/${include_prefix}/gen-cpp2") endfunction()