Repository navigation
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
270 lines (242 loc) · 12.8 KB
/
Copy pathCMakeLists.txt
File metadata and controls
270 lines (242 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
cmake_minimum_required(VERSION 3.30)
# CMAKE_OSX_SYSROOT is a macOS-specific setting that specifies the SDK path.
if(APPLE)
execute_process(
COMMAND xcrun --show-sdk-path
OUTPUT_VARIABLE CMAKE_OSX_SYSROOT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
# Enable `import std;` in C++23 modules. This is gated behind CMake's
# experimental flag whose activation UUID is pinned to the exact CMake version,
# so the value differs between the terminal's CMake and CLion's bundled CMake.
# Select it by version to keep both environments configuring cleanly. MUST be set
# before project()/CXX language enablement so the toolchain is detected with
# import-std support. Requires the Ninja generator plus a toolchain that ships a
# libc++ `std` module. Configure with: cmake -G Ninja -B build
if(CMAKE_VERSION VERSION_GREATER_EQUAL "4.3" AND CMAKE_VERSION VERSION_LESS "4.4")
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "451f2fe2-a8a2-47c3-bc32-94786d8fc91b")
elseif(CMAKE_VERSION VERSION_GREATER_EQUAL "4.2" AND CMAKE_VERSION VERSION_LESS "4.3")
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
elseif(CMAKE_VERSION VERSION_GREATER_EQUAL "3.30" AND CMAKE_VERSION VERSION_LESS "3.31")
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "0e5b6991-d74f-4b3d-a41c-cf096e0b2508")
else()
message(FATAL_ERROR
"No pinned CMAKE_EXPERIMENTAL_CXX_IMPORT_STD UUID for CMake "
"${CMAKE_VERSION}. Find the value next to the "
"CMAKE_EXPERIMENTAL_CXX_IMPORT_STD symbol in this CMake's binary and add "
"a branch above.")
endif()
# CMake 4.2 locates the libc++ `std` module via a `libc++.modules.json` manifest,
# discovered through `clang++ -print-file-name=libc++.modules.json`. Homebrew LLVM
# ships the manifest under <prefix>/lib/c++/, which is not a -print-file-name search
# path, so discovery returns the bare filename and import-std detection fails with
# "`libc++.modules.json` resource does not exist". Point CMake straight at the
# manifest. (CMake 3.30 didn't require this, so the terminal build was unaffected.)
# Must be set before project()/CXX enablement, like the experimental flag above.
if(APPLE AND NOT CMAKE_CXX_STDLIB_MODULES_JSON)
execute_process(
COMMAND brew --prefix llvm
OUTPUT_VARIABLE _llvm_prefix
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(_llvm_prefix AND EXISTS "${_llvm_prefix}/lib/c++/libc++.modules.json")
set(CMAKE_CXX_STDLIB_MODULES_JSON "${_llvm_prefix}/lib/c++/libc++.modules.json")
endif()
endif()
# On Linux the import-std toolchain is Clang + libc++ (apt.llvm.org), mirroring
# the macOS Homebrew-LLVM setup so both platforms share one story. Stock Clang on
# Linux defaults to libstdc++, which has no usable `std` module, so force libc++
# and locate its module manifest the same way the Apple branch does — Clang's
# `-print-file-name` search path does not include libc++'s install dir, so fall
# back to the well-known apt.llvm.org locations. Gated on the compiler name
# (passed via -DCMAKE_CXX_COMPILER before project()) since the compiler id is not
# known until project()/CXX enablement below. MUST run before project(), like the
# Apple block, so the toolchain is detected with libc++/import-std support.
if(NOT APPLE AND CMAKE_CXX_COMPILER MATCHES "clang")
string(APPEND CMAKE_CXX_FLAGS " -stdlib=libc++")
if(NOT CMAKE_CXX_STDLIB_MODULES_JSON)
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} -stdlib=libc++ -print-file-name=libc++.modules.json
OUTPUT_VARIABLE _libcxx_modules_json
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT (_libcxx_modules_json AND EXISTS "${_libcxx_modules_json}"))
file(GLOB _libcxx_modules_json
/usr/lib/llvm-*/lib/c++/libc++.modules.json
/usr/lib/*/c++/libc++.modules.json
/usr/local/lib/c++/libc++.modules.json)
if(_libcxx_modules_json)
list(GET _libcxx_modules_json 0 _libcxx_modules_json) # first match
endif()
endif()
if(_libcxx_modules_json AND EXISTS "${_libcxx_modules_json}")
set(CMAKE_CXX_STDLIB_MODULES_JSON "${_libcxx_modules_json}")
endif()
endif()
endif()
project(BacktestingEngine)
# Set the C++ standard
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Emit build/compile_commands.json for clangd/IntelliSense. Forced on here (not
# left to a configure-time flag) so the project's own source/ entries — and the
# module flags for loadCommand.cppm — are always present for the editor.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Clang 22 made "reduced BMI" the default for C++20 modules, and reduced BMIs
# drop the predeclared global operator new/delete from the global module
# (llvm/llvm-project#166068). A TU that imports std alongside enough of this
# project's modules then segfaults clang's codegen (EmitBuiltinNewDeleteCall,
# release build's llvm_unreachable) while emitting __builtin_operator_new from
# libc++'s __libcpp_allocate — main.cpp was the victim. Force full BMIs (the
# pre-22 behaviour) until the upstream fix reaches a release. Global
# CMAKE_CXX_FLAGS on purpose: it must also reach CMake's internal `import std`
# target (see the OpenMP note above the flags append near the bottom).
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 22)
string(APPEND CMAKE_CXX_FLAGS " -fno-modules-reduced-bmi")
endif()
# Configure libpqxx build
set(PQXX_LIBRARIES_INSTALL ON)
set(SKIP_BUILD_TEST ON)
set(SKIP_CONFIGURE_LIBPQXX OFF)
# Disable warningsfor external libraries
set(PREV_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
elseif(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /w")
endif()
# Quiet CMAKE output
set(CMAKE_INSTALL_MESSAGE NEVER)
set(CMAKE_MESSAGE_LOG_LEVEL "WARNING")
# Homebrew's libpq is keg-only, so CMake's FindPostgreSQL module can't locate it
# on its own. Without a hint, libpqxx's config.cmake falls back to pkg-config,
# which sets PostgreSQL_FOUND but never creates the PostgreSQL::PostgreSQL imported
# target that src/CMakeLists.txt links against — producing a "target not found"
# generate error. Point FindPostgreSQL at the keg and resolve the target here so it
# exists before libpqxx is configured.
if(APPLE AND NOT PostgreSQL_ROOT)
execute_process(
COMMAND brew --prefix libpq
OUTPUT_VARIABLE PostgreSQL_ROOT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
find_package(PostgreSQL REQUIRED)
# Build libpqxx from source
add_subdirectory(external/libpqxx EXCLUDE_FROM_ALL)
# Collect all .cpp files in the src directory. CONFIGURE_DEPENDS re-runs the
# glob when .cpp files are added/removed (matching the MODULE_INTERFACES glob
# below), so a new translation unit is picked up without a manual reconfigure.
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "source/*.cpp")
# main.cpp belongs to the executable only — keeping it in the library too
# means it gets compiled twice and the lib carries a competing main().
list(REMOVE_ITEM SOURCES ${CMAKE_SOURCE_DIR}/source/main.cpp)
# Create a library of your project's code
add_library(BacktestingEngineLib STATIC ${SOURCES})
# C++23 module interface units. The SOURCES glob only matches *.cpp, so the
# .cppm modules are collected separately. CONFIGURE_DEPENDS re-runs the glob when
# files are added/removed. PUBLIC so linked consumers can `import` them.
file(GLOB_RECURSE MODULE_INTERFACES CONFIGURE_DEPENDS
"${CMAKE_SOURCE_DIR}/source/*.cppm")
target_sources(BacktestingEngineLib
PUBLIC FILE_SET CXX_MODULES FILES
${MODULE_INTERFACES})
# Enable `import std;` for this target only (NOT globally — that would force it
# onto the third-party libpqxx/pqxx targets). Request C++23 as a compile feature
# so import-std support propagates to consumers that link this library.
set_target_properties(BacktestingEngineLib PROPERTIES CXX_MODULE_STD ON)
target_compile_features(BacktestingEngineLib PUBLIC cxx_std_23)
# Include directories — PUBLIC so they propagate to the executable (and any
# other consumer) through target_link_libraries. Every project #include is
# source-relative and path-qualified (e.g. "shared/utilities/decimalJson.hpp",
# "run/reporting/tradingResults.hpp"), so a single source root makes each
# #include / module GMF dependency self-describing. The external root is kept
# only for the vendored header-only nlohmann/json (no CMake target ships it).
target_include_directories(BacktestingEngineLib PUBLIC
${CMAKE_SOURCE_DIR}/source
${CMAKE_SOURCE_DIR}/external # vendored header-only nlohmann/json
)
# Configure OpenMP. On Apple, provide Homebrew libomp hints before discovery.
# brew --prefix resolves the right location on both Apple Silicon
# (/opt/homebrew) and Intel (/usr/local).
if(APPLE)
execute_process(
COMMAND brew --prefix libomp
OUTPUT_VARIABLE LIBOMP_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(OpenMP_C_FLAGS "-Xclang -fopenmp")
set(OpenMP_CXX_FLAGS "-Xclang -fopenmp")
set(OpenMP_C_LIB_NAMES "omp")
set(OpenMP_CXX_LIB_NAMES "omp")
set(OpenMP_omp_LIBRARY ${LIBOMP_PREFIX}/lib/libomp.dylib)
target_include_directories(BacktestingEngineLib PRIVATE ${LIBOMP_PREFIX}/include)
endif()
find_package(OpenMP REQUIRED)
# OpenMP is linked PUBLIC below, so `-Xclang -fopenmp` is applied to every TU in
# the library — including the `.cppm` modules. CMake builds the `import std;`
# standard-library module WITHOUT OpenMP, so importing std from an OpenMP-enabled
# module is rejected by Clang as a BMI config mismatch. That CMake-managed std
# target ignores target_compile_options and linked usage requirements — it honors
# only the global CMAKE_CXX_FLAGS. So put the OpenMP flag there too, making the
# std module's OpenMP configuration match every consumer's. (It then appears twice
# on normal TUs, via this flag and the linked target; a harmless duplicate.)
string(APPEND CMAKE_CXX_FLAGS " ${OpenMP_CXX_FLAGS}")
# Boost (Homebrew) — Boost.Redis is header-only but its implementation is
# compiled into one dedicated TU via <boost/redis/src.hpp>
# (source/shared/redis/connection/boostRedisImpl.cpp), which isolates its
# macro/SSL pollution from the rest of the build. It pulls in Boost.Asio's SSL
# layer, which needs OpenSSL. Boost.Interprocess (the shared-memory control
# channel, source/shared/ipc/engineControl.cpp) is also header-only; on Linux it
# needs librt for shm_open (glibc < 2.34), which is a no-op elsewhere.
find_package(Boost REQUIRED CONFIG)
find_package(OpenSSL REQUIRED)
find_package(Threads REQUIRED)
find_package(CURL REQUIRED)
target_link_libraries(BacktestingEngineLib PUBLIC
Boost::headers
OpenSSL::SSL
OpenSSL::Crypto
Threads::Threads
CURL::libcurl
$<$<PLATFORM_ID:Linux>:rt>)
add_subdirectory(external/boost-decimal)
target_link_libraries(BacktestingEngineLib PUBLIC Boost::decimal)
target_link_libraries(BacktestingEngineLib PUBLIC pqxx OpenMP::OpenMP_CXX)
# AWS SDK for C++, DynamoDB client only — IG session credentials live in the
# MarketDataLive table (shared/aws/dynamoAuth). Deliberately NOT an
# add_subdirectory: the SDK is enormous and must not inherit this project's
# global module/OpenMP flags, so scripts/build_dep.sh builds and installs it
# once (static libs) into external/aws-install and it is consumed here as a
# prebuilt package. AWSSDK_LINK_LIBRARIES resolves to the dynamodb target,
# which chains core + CRT dependencies through its exported config.
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/external/aws-install")
# The static aws-cpp-sdk-core's exported link interface names ZLIB::ZLIB
# (and curl/OpenSSL targets, already found above) but its config does not
# find_dependency them — resolve ZLIB here first.
find_package(ZLIB REQUIRED)
find_package(AWSSDK REQUIRED CONFIG COMPONENTS dynamodb)
target_link_libraries(BacktestingEngineLib PUBLIC ${AWSSDK_LINK_LIBRARIES})
# Main executable
add_executable(BacktestingEngine source/main.cpp)
target_link_libraries(BacktestingEngine BacktestingEngineLib)
# Clang source-based coverage. Opt-in (CI sets -DENABLE_COVERAGE=ON) so local
# builds aren't instrumented. PUBLIC on the library so the flags also reach the
# test executable that links it — that's the TU set we actually measure.
option(ENABLE_COVERAGE "Instrument with Clang source-based coverage" OFF)
if(ENABLE_COVERAGE)
target_compile_options(BacktestingEngineLib PUBLIC -fprofile-instr-generate -fcoverage-mapping)
target_link_options(BacktestingEngineLib PUBLIC -fprofile-instr-generate -fcoverage-mapping)
endif()
# Unit tests (Catch2). include(CTest) defines BUILD_TESTING (default ON) and
# enables `ctest`. Catch2 is a vendored submodule; EXCLUDE_FROM_ALL keeps it out
# of the default `all` build until the test target pulls it in.
include(CTest)
if(BUILD_TESTING)
add_subdirectory(external/Catch2 EXCLUDE_FROM_ALL)
add_subdirectory(tests)
endif()