diff --git a/CMakeLists.txt b/CMakeLists.txt index ac727930470..8df08c78507 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,9 @@ option(BUILD_DOCS "Build documentation" OFF) option(BUILD_CUSTOM_PROTOBUF "If set, build Caffe2's own protobuf under third_party" OFF) option(BUILD_PYTHON "Build Python binaries" ON) option(BUILD_SHARED_LIBS "Build libcaffe2.so" ON) +cmake_dependent_option( + CAFFE2_LINK_LOCAL_PROTOBUF "If set, build protobuf inside libcaffe2.so." ON + "BUILD_SHARED_LIBS AND BUILD_CUSTOM_PROTOBUF" OFF) cmake_dependent_option( CAFFE2_USE_MSVC_STATIC_RUNTIME "Using MSVC static runtime libraries" ON "NOT BUILD_SHARED_LIBS" OFF) diff --git a/caffe2/CMakeLists.txt b/caffe2/CMakeLists.txt index 01267c1d420..2e50bc58065 100644 --- a/caffe2/CMakeLists.txt +++ b/caffe2/CMakeLists.txt @@ -76,12 +76,38 @@ install(FILES ${PROJECT_BINARY_DIR}/caffe2/core/macros.h # ---[ List of libraries to link with - add_library(caffe2_protos $ $) add_dependencies(caffe2_protos Caffe_PROTO Caffe2_PROTO) # TODO: enable using lite protobuf. -target_link_libraries(caffe2_protos PUBLIC protobuf::libprotobuf) +# If we are going to link protobuf locally inside caffe2 libraries, what we will do is +# to create a helper static library that always contains libprotobuf source files, and +# link the caffe2 related dependent libraries to it. target_include_directories(caffe2_protos INTERFACE $) +# Reason for this public dependency is as follows: +# (1) Strictly speaking, we should not expose any Protobuf related functions. We should +# only use function interfaces wrapped with our own public API, and link protobuf +# locally. +# (2) However, currently across the Caffe2 codebase, we have extensive use of protobuf +# functionalities. For example, not only libcaffe2.so uses it, but also other +# binaries such as python extensions etc. As a result, we will have to have a +# transitive dependency to libprotobuf. +# +# Good thing is that, if we specify CAFFE2_LINK_LOCAL_PROTOBUF, then we do not need to +# separately deploy protobuf binaries - libcaffe2.so will contain all functionalities +# one needs. One can verify this via ldd. +# +# Todo item in the future includes: +# (1) Properly define public API that do not directly depend on protobuf itself. +# (2) expose the libprotobuf.a file for dependent libraries to link to. +# (3) build libprotobuf with -fvisibility=hidden to avoid version conflicts. +# +# What it means for users/developers? +# (1) Users: nothing affecting the users, other than the fact that CAFFE2_LINK_LOCAL_PROTOBUF +# avoids the need to deploy protobuf. +# (2) Developers: if one simply uses core caffe2 functionality without using protobuf, +# nothing changes. If one has a dependent library that uses protobuf, then one needs to +# have the right protobuf version as well as linking to libprotobuf.a. +target_link_libraries(caffe2_protos PUBLIC protobuf::libprotobuf) install(TARGETS caffe2_protos EXPORT Caffe2Targets DESTINATION lib) # Compile exposed libraries. diff --git a/cmake/ProtoBuf.cmake b/cmake/ProtoBuf.cmake index 46b83d72491..b85e46022bf 100644 --- a/cmake/ProtoBuf.cmake +++ b/cmake/ProtoBuf.cmake @@ -9,16 +9,33 @@ macro(custom_protobuf_find) # so we turn it off here. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") endif() - # If we are building Caffe2 as shared libs, we will also build protobuf as - # shared libs. - set(protobuf_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) + if (${CAFFE2_LINK_LOCAL_PROTOBUF}) + # If we are going to link protobuf locally, we will need to turn off + # shared libs build for protobuf. + set(protobuf_BUILD_SHARED_LIBS OFF) + else() + # If we are building Caffe2 as shared libs, we will also build protobuf as + # shared libs. + set(protobuf_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) + endif() # We will make sure that protobuf and caffe2 uses the same msvc runtime. set(protobuf_MSVC_STATIC_RUNTIME ${CAFFE2_USE_MSVC_STATIC_RUNTIME}) if (MSVC AND BUILD_SHARED_LIBS) add_definitions(-DPROTOBUF_USE_DLLS) endif() + + if (${CAFFE2_LINK_LOCAL_PROTOBUF}) + # We will need to build protobuf with -fPIC. + set(__caffe2_protobuf_cmake_fpic ${CMAKE_POSITION_INDEPENDENT_CODE}) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + endif() + add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/protobuf/cmake) + if (${CAFFE2_LINK_LOCAL_PROTOBUF}) + set(CMAKE_POSITION_INDEPENDENT_CODE ${__caffe2_protobuf_cmake_fpic}) + endif() + # Protobuf "namespaced" target is only added post protobuf 3.5.1. As a # result, for older versions, we will manually add alias. if (NOT TARGET protobuf::libprotobuf) @@ -32,9 +49,13 @@ endmacro() # coded BUILD_CUSTOM_PROTOBUF, we will hard code the use of custom protobuf # in the submodule. if (ANDROID OR IOS) - message(STATUS - "For Android and iOS cross compilation, I am automatically using " - "custom protobuf under third party.") + if (NOT ${BUILD_CUSTOM_PROTOBUF}) + message(WARNING + "For Android and iOS cross compilation, I am automatically using " + "custom protobuf under third party. Note that this behavior may " + "change in the future, and you will need to specify " + "-DBUILD_CUSTOM_PROTOBUF=ON explicitly.") + endif() custom_protobuf_find() # Unfortunately, new protobuf does not support libprotoc and protoc # cross-compilation so we will need to exclude it. diff --git a/cmake/Summary.cmake b/cmake/Summary.cmake index ee4488a29fe..cd9a8b1e0a4 100644 --- a/cmake/Summary.cmake +++ b/cmake/Summary.cmake @@ -20,6 +20,10 @@ function (caffe2_print_configuration_summary) message(STATUS "") message(STATUS " BUILD_BINARY : ${BUILD_BINARY}") + message(STATUS " BUILD_CUSTOM_PROTOBUF : ${BUILD_CUSTOM_PROTOBUF}") + if (${CAFFE2_LINK_LOCAL_PROTOBUF}) + message(STATUS " CAFFE2_LINK_LOCAL_PROTOBUF : ${CAFFE2_LINK_LOCAL_PROTOBUF}") + endif() message(STATUS " BUILD_DOCS : ${BUILD_DOCS}") message(STATUS " BUILD_PYTHON : ${BUILD_PYTHON}") if (${BUILD_PYTHON})