Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 73 additions & 23 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Required for Apple Silicon support.
cmake_minimum_required(VERSION 3.19)
include(CMakeDependentOption)

project(
LaunchDarklyCPPSDKs
Expand All @@ -13,22 +14,63 @@ project(

include(GNUInstallDirs)

option(BUILD_TESTING "Top-level switch for testing. Turn off to disable unit and contract tests." ON)

option(LD_BUILD_SHARED_LIBS "Build the SDKs as shared libraries" OFF)

cmake_dependent_option(LD_BUILD_UNIT_TESTS
"Build the C++ unit tests."
ON # default to enabling unit tests
BUILD_TESTING;NOT LD_BUILD_SHARED_LIBS # only exposed if top-level switch is on, and also only when building
# static libs. This is because we have hidden visibility of symbols by default (to only expose our C API.)
OFF # otherwise, off
)

# If you want to run the unit tests with valgrind, then LD_TESTING_SANITIZERS must of OFF.
cmake_dependent_option(LD_TESTING_SANITIZERS
"Enable sanitizers for unit tests."
ON # default to enabling sanitizers
LD_BUILD_UNIT_TESTS # only expose if unit tests enabled..
OFF # otherwise, off
)

cmake_dependent_option(LD_BUILD_CONTRACT_TESTS
"Build contract test service."
OFF # default to disabling contract tests, since they require running a service
BUILD_TESTING # only expose if top-level switch is on..
OFF # otherwise, off
)

# The general strategy is to produce a fat artifact containing all of our dependencies so users
# only have a single thing to link. We should support this either being a static or shared library.
# Because OpenSSL is a large, and security relevant dependency, we should have a separate option
# to link against that statically or dynamically.

option(LD_DYNAMIC_LINK_OPENSSL
"Dynamically link OpenSSL instead of building with static library"
OFF # default to linking OpenSSL statically
)

option(LD_BUILD_EXAMPLES "Build hello-world examples." ON)


Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing a warning when someone tries to build shared lib with unit tests enabled. This won't work since we hide symbols by default.

if (LD_BUILD_SHARED_LIBS AND LD_BUILD_UNIT_TESTS)
message(WARNING "LaunchDarkly: unit testing isn't supported while building shared libraries. Switch to static libraries or disable unit tests.")
endif ()

# All projects in this repo should share the same version of 3rd party depends.
# It's the only way to remain sane.
set(CMAKE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set(CMAKE_CXX_STANDARD 17)

set(CMAKE_POSITION_INDEPENDENT_CODE ON)

option(BUILD_TESTING "Enable C++ unit tests." ON)

# If you want to run the unit tests with valgrind, then TESTING_SANITIZERS must of OFF.
option(TESTING_SANITIZERS "Enable sanitizers for unit tests." ON)

if (BUILD_TESTING)
if (LD_BUILD_UNIT_TESTS)
message(STATUS "LaunchDarkly: building unit tests")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG")
add_compile_definitions(LAUNCHDARKLY_USE_ASSERT)
if (TESTING_SANITIZERS)
if (LD_TESTING_SANITIZERS)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize=undefined -fsanitize=leak")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
Expand All @@ -53,13 +95,22 @@ if (BUILD_TESTING)
enable_testing()
endif ()

set(OPENSSL_USE_STATIC_LIBS ON)
if (LD_DYNAMIC_LINK_OPENSSL)
message(STATUS "LaunchDarkly: searching for shared OpenSSL library")
set(OPENSSL_USE_STATIC_LIBS OFF)
else ()
message(STATUS "LaunchDarkly: searching for static OpenSSL library")
set(OPENSSL_USE_STATIC_LIBS ON)
endif ()

find_package(OpenSSL REQUIRED)
message(STATUS "LaunchDarkly: using OpenSSL v${OPENSSL_VERSION}")

# Even though the main SDK might be a static or shared lib, boost should always statically
# linked into the binary.
set(Boost_USE_STATIC_LIBS ON)

if (BUILD_SHARED_LIBS)
if (NOT LD_BUILD_STATIC_LIBS)
# When building a shared library we hide all symbols
# aside from this we have specifically exported for the C-API.
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not right now, but we probably need an option for this. For those that build themselves and want to build a C++ dll.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(If they build all their own stuff, then the ABI issue isn't really an SDK issue.)

Expand All @@ -71,25 +122,24 @@ set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.81 REQUIRED COMPONENTS json url coroutine)
message(STATUS "LaunchDarkly: using Boost v${Boost_VERSION}")

add_subdirectory(libs/client-sdk)

set(ORIGINAL_BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS}")
set(BUILD_SHARED_LIBS OFF)
include(${CMAKE_FILES}/certify.cmake)
add_subdirectory(vendor/foxy)

# Always build the common libraries as static libs.
# Common, internal, and server-sent-events are built as "object" libraries.
add_subdirectory(libs/common)
add_subdirectory(libs/internal)
add_subdirectory(libs/server-sent-events)

set(ORIGINAL_BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS}")

set(BUILD_TESTING OFF)
include(${CMAKE_FILES}/certify.cmake)
add_subdirectory(vendor/foxy)

set(BUILD_TESTING "${ORIGINAL_BUILD_TESTING}")
# Built as static or shared depending on LD_BUILD_STATIC_LIBS variable.
# This target "links" in common, internal, and sse as object libraries.
add_subdirectory(libs/client-sdk)

set(BUILD_SHARED_LIBS "${ORIGINAL_BUILD_SHARED_LIBS}")
if (LD_BUILD_CONTRACT_TESTS)
message(STATUS "LaunchDarkly: building contract tests")
add_subdirectory(contract-tests)
endif ()

add_subdirectory(contract-tests)
add_subdirectory(examples)
if (LD_BUILD_EXAMPLES)
message(STATUS "LaunchDarkly: building examples")
add_subdirectory(examples)
endif ()
2 changes: 1 addition & 1 deletion examples/hello-c-client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

add_executable(hello-c main.c)
target_link_libraries(hello-c PRIVATE launchdarkly::client launchdarkly::sse launchdarkly::common Threads::Threads)
target_link_libraries(hello-c PRIVATE launchdarkly::client Threads::Threads)
2 changes: 1 addition & 1 deletion libs/client-sdk/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ include(FetchContent)
# Add main SDK sources.
add_subdirectory(src)

if (BUILD_TESTING)
if (LD_BUILD_UNIT_TESTS)
add_subdirectory(tests)
endif ()
38 changes: 38 additions & 0 deletions libs/client-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,44 @@ gcc -I $(pwd)/include -Llib -fPIE -g main.c liblaunchdarkly-cpp-client.so
The examples here are to help with getting started, but generally speaking the SDK should be incorporated using your
build system (CMake for instance).

### CMake Usage

First, add the SDK to your project:

```cmake
add_subdirectory(path-to-sdk-repo)
```

Currently `find_package` is not yet supported.

This will expose the `launchdarkly::client` target. Next, link the target to your executable or library:

```cmake
target_link_libraries(my-target PRIVATE launchdarkly::client)
```

Various CMake options are available to customize the SDK build.

| Option | Description | Default | Requires |
|---------------------------|----------------------------------------------------------------------------------------|--------------------|-------------------------------------------|
| `BUILD_TESTING` | Coarse-grained switch; turn off to disable all testing and only build the SDK targets. | On | N/A |
| `LD_BUILD_UNIT_TESTS` | Whether C++ unit tests are built. | On | `BUILD_TESTING; NOT LD_BUILD_SHARED_LIBS` |
| `LD_TESTING_SANITIZERS` | Whether sanitizers should be enabled. | On | `LD_BUILD_UNIT_TESTS` |
| `LD_BUILD_CONTRACT_TESTS` | Whether the contract test service (used in CI) is built. | Off | `BUILD_TESTING` |
| `LD_BUILD_EXAMPLES` | Whether example apps (hello world) are built. | On | N/A |
| `LD_BUILD_SHARED_LIBS` | Whether the SDK is built as a static or shared library. | Off (static lib) | N/A |
| `LD_DYNAMIC_LINK_OPENSSL` | Whether OpenSSL be dynamically linked. | Off (static link) | N/A |

**Note:** _if building the SDK as a shared library, then unit tests won't be able to link correctly since the SDK's C++
symbols aren't exposed. To run unit tests, build the SDK as a static library._

Example usage:

```bash
# Build the SDK as a shared library
cmake -GNinja .. -DLD_BUILD_SHARED_LIBS=On
```

Learn more
-----------

Expand Down
18 changes: 13 additions & 5 deletions libs/client-sdk/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ file(GLOB HEADER_LIST CONFIGURE_DEPENDS
"${LaunchDarklyCPPClient_SOURCE_DIR}/include/launchdarkly/client_side/*.hpp"
)

# Automatic library: static or dynamic based on user config.
if (LD_BUILD_SHARED_LIBS)
message(STATUS "LaunchDarkly: building client-sdk as shared library")
add_library(${LIBNAME} SHARED)
else ()
message(STATUS "LaunchDarkly: building client-sdk as static library")
add_library(${LIBNAME} STATIC)
endif ()

add_library(${LIBNAME}
target_sources(${LIBNAME} PRIVATE
${HEADER_LIST}
data_sources/streaming_data_source.cpp
data_sources/data_source_event_handler.cpp
Expand Down Expand Up @@ -43,14 +49,16 @@ add_library(${LIBNAME}
serialization/json_all_flags.cpp
flag_manager/flag_manager.cpp
flag_manager/flag_persistence.cpp
bindings/c/sdk.cpp)
bindings/c/sdk.cpp
)


if (MSVC OR (NOT BUILD_SHARED_LIBS))
if (MSVC OR LD_BUILD_STATIC_LIBS)
target_link_libraries(${LIBNAME}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to note this MSVC condition in the readme.

Unless we want to figure out how to make it actually work with dynamically linked boost on windows (world of pain).

PUBLIC launchdarkly::common
PRIVATE Boost::headers Boost::json Boost::url launchdarkly::sse launchdarkly::internal foxy)
else ()
# The default static lib builds, for linux, are positition independent.
# The default static lib builds, for linux, are position independent.
# So they do not link into a shared object without issues. So, when
# building shared objects do not link the static libraries and instead
# use the "src.hpp" files for required libraries.
Expand Down
2 changes: 1 addition & 1 deletion libs/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ include(${CMAKE_FILES}/expected.cmake)
# Add main SDK sources.
add_subdirectory(src)

if (BUILD_TESTING)
if (LD_BUILD_UNIT_TESTS)
add_subdirectory(tests)
endif ()
2 changes: 1 addition & 1 deletion libs/internal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ include(FetchContent)
# Add main SDK sources.
add_subdirectory(src)

if (BUILD_TESTING)
if (LD_BUILD_UNIT_TESTS)
add_subdirectory(tests)
endif ()
2 changes: 1 addition & 1 deletion libs/server-sent-events/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ include(FetchContent)

add_subdirectory(src)

if (BUILD_TESTING)
if (LD_BUILD_UNIT_TESTS)
add_subdirectory(tests)
endif ()
2 changes: 1 addition & 1 deletion scripts/build-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ cd ..
# Build a dynamic release.
mkdir -p build-dynamic && cd build-dynamic
mkdir -p release
cmake -G Ninja -D CMAKE_BUILD_TYPE=Release -D BUILD_TESTING=OFF -D BUILD_SHARED_LIBS=ON -D CMAKE_INSTALL_PREFIX=./release ..
cmake -G Ninja -D CMAKE_BUILD_TYPE=Release -D BUILD_TESTING=OFF -D LD_BUILD_SHARED_LIBS=ON -D CMAKE_INSTALL_PREFIX=./release ..

cmake --build . --target "$1"
cmake --install .
Expand Down
2 changes: 1 addition & 1 deletion scripts/build-windows.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ cd ..
# Build a dynamic debug release.
mkdir -p build-dynamic-debug && cd build-dynamic-debug
mkdir -p release
cmake -G Ninja -D CMAKE_BUILD_TYPE=Debug -D BUILD_TESTING=OFF -D BUILD_SHARED_LIBS=ON -D CMAKE_INSTALL_PREFIX=./release ..
cmake -G Ninja -D CMAKE_BUILD_TYPE=Debug -D BUILD_TESTING=OFF -D LD_BUILD_SHARED_LIBS=ON -D CMAKE_INSTALL_PREFIX=./release ..

cmake --build . --target "$1"
cmake --install .
4 changes: 2 additions & 2 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# ./scripts/build.sh my-build-target ON
#
# $1 the name of the target. For example "launchdarkly-cpp-common".
# $2 ON/OFF which enables/disables building in a test configuration.
# $2 ON/OFF which enables/disables building in a test configuration (unit tests + contract tests.)

function cleanup {
cd ..
Expand All @@ -17,6 +17,6 @@ cd build
# script ends.
trap cleanup EXIT

cmake -G Ninja -DCMAKE_COMPILE_WARNING_AS_ERROR=TRUE -D BUILD_TESTING="$2" ..
cmake -G Ninja -D CMAKE_COMPILE_WARNING_AS_ERROR=TRUE -D BUILD_TESTING="$2" -D LD_BUILD_UNIT_TESTS="$2" -D LD_BUILD_CONTRACT_TESTS="$2" ..

cmake --build . --target "$1"