-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
311 lines (281 loc) · 12.4 KB
/
Copy pathCMakeLists.txt
File metadata and controls
311 lines (281 loc) · 12.4 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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
cmake_minimum_required(VERSION 3.16)
# Resolve version from git tag (preferred) or VERSION file (fallback).
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(PineForgeVersion)
pineforge_resolve_version()
project(PineForge
VERSION ${PINEFORGE_VERSION_MMP}
DESCRIPTION "Deterministic PineScript v6 backtest runtime, validated trade-for-trade against TradingView"
LANGUAGES C CXX
)
# Generate <pineforge/version.h> from version.h.in. Lives in the build
# tree; installed alongside the curated headers below.
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/include/pineforge/version.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/include/pineforge/version.h"
@ONLY
)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 11)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release)
endif()
# Build artefact layout
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
option(PINEFORGE_BUILD_TESTS "Build the C++ test suite" ON)
option(PINEFORGE_BUILD_EXAMPLES "Build example consumers (none yet)" OFF)
option(PINEFORGE_BUILD_CORPUS_STRATEGIES
"Build a strategy.so per .pine in corpus/ from its generated.cpp" OFF)
option(PINEFORGE_BUILD_TUTORIAL
"Build the tutorial MACD strategy.so under tutorial/" ON)
option(PINEFORGE_ENABLE_COVERAGE
"Instrument the runtime + tests for source-level coverage (Clang or GCC)" OFF)
# Opt-in AddressSanitizer + UndefinedBehaviorSanitizer. Off by default
# (slows the runtime and is incompatible with bit-exact perf builds).
# Build with:
# cmake -B build -DCMAKE_BUILD_TYPE=Debug -DPINEFORGE_ENABLE_SANITIZERS=ON
option(PINEFORGE_ENABLE_SANITIZERS
"Build with AddressSanitizer + UndefinedBehaviorSanitizer" OFF)
# Opt-in strict conversion warnings. Off by default — would flood output.
# Build with:
# cmake -B build -DPINEFORGE_STRICT_WARNINGS=ON
# to quantify implicit-conversion debt. Not -Werror; never fails the build.
option(PINEFORGE_STRICT_WARNINGS
"Enable -Wconversion / -Wsign-conversion (does not imply -Werror)" OFF)
# === Eigen3 ============================================================
# Used for matrix-typed PineScript (matrix.* API). Tries system install
# first; falls back to FetchContent.
find_package(Eigen3 3.3 QUIET)
if(NOT Eigen3_FOUND)
message(STATUS "System Eigen not found, fetching via FetchContent...")
include(FetchContent)
FetchContent_Declare(
eigen
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz
)
set(EIGEN_BUILD_DOC OFF CACHE BOOL "" FORCE)
set(EIGEN_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(eigen)
else()
message(STATUS "Found system Eigen: ${Eigen3_DIR}")
endif()
# === pineforge library =================================================
add_library(pineforge STATIC
src/c_abi.cpp
src/engine_fills.cpp
src/engine_lower_tf.cpp
src/engine_metrics.cpp
src/engine_orders.cpp
src/engine_path_resolve.cpp
src/engine_report.cpp
src/engine_risk.cpp
src/engine_run.cpp
src/engine_security.cpp
src/engine_stream.cpp
src/engine_strategy_commands.cpp
src/engine_trade_accessors.cpp
src/magnifier.cpp
src/math.cpp
src/matrix.cpp
src/session_time.cpp
src/str_utils.cpp
src/ta_extremes_volume.cpp
src/ta_misc.cpp
src/ta_moving_averages.cpp
src/ta_oscillators.cpp
src/ta_volatility_trend.cpp
src/timeframe.cpp
src/timezone.cpp
)
# Canonical alias for downstream find_package(PineForge) consumers.
add_library(PineForge::pineforge ALIAS pineforge)
# Hidden visibility by default — only symbols explicitly tagged with
# PF_API in <pineforge/pineforge.h> are exported in any .so that
# statically links libpineforge.a. Internal C++ classes (BacktestEngine,
# ta::*, internal::*) remain hidden post-link.
target_compile_options(pineforge PRIVATE
# Optimization level only for non-Debug configs — baking -O2 here
# unconditionally would defeat -DCMAKE_BUILD_TYPE=Debug (no breakpoints
# / inlined-away locals). The FP flags below stay unconditional because
# parity depends on them in every build type.
$<$<NOT:$<CONFIG:Debug>>:-O2>
-fPIC
-fvisibility=hidden
-fvisibility-inlines-hidden
-Wall
-Wextra
-Wpedantic
-Wno-unused-parameter
# Disable floating-point contraction (FMA generation). TradingView's
# Pine runtime executes in JavaScript, which has no FMA primitive —
# every multiply-add is two separately-rounded operations. With the
# default -O2 + clang behaviour (`-ffp-contract=on` for in-statement
# contraction) the engine emits FMA on ARM64 / AVX2, producing
# results 1-2 ULP closer to the true value than Pine. That sounds
# like a win but it's the wrong target — we want bit-exact parity
# with TradingView, not "more accurate". The 1-2 ULP drift compounds
# across hundreds of RMA iterations and can flip RSI threshold
# crossings on close-call bars (see tests/test_ta_rma_warmup.cpp).
# `-ffp-contract=off` forces the compiler to round each * and + to
# IEEE-754 double independently, matching the Pine reference.
-ffp-contract=off
)
target_include_directories(pineforge
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_link_libraries(pineforge PUBLIC Eigen3::Eigen)
# === Coverage instrumentation (opt-in) =================================
# Build with:
# cmake -B build -DCMAKE_BUILD_TYPE=Debug -DPINEFORGE_ENABLE_COVERAGE=ON
# then drive the report via `bash scripts/coverage.sh`. Clang emits
# llvm-profile (.profraw) which `llvm-cov` consumes; GCC emits .gcda /
# .gcno which `gcov` / `gcovr` / `lcov` consume.
if(PINEFORGE_ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(_pf_cov_flags -fprofile-instr-generate -fcoverage-mapping)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(_pf_cov_flags --coverage -fprofile-arcs -ftest-coverage)
else()
message(FATAL_ERROR
"PINEFORGE_ENABLE_COVERAGE requires Clang or GCC (got ${CMAKE_CXX_COMPILER_ID})")
endif()
target_compile_options(pineforge PRIVATE ${_pf_cov_flags})
target_link_options(pineforge PUBLIC ${_pf_cov_flags})
message(STATUS "Coverage instrumentation: ${_pf_cov_flags}")
endif()
# === Sanitizers (opt-in) ==============================================
# ASan + UBSan. Link options are PUBLIC so anything linking libpineforge.a
# (tests, corpus strategy.so) picks up the runtime.
if(PINEFORGE_ENABLE_SANITIZERS)
# Compile options must be PUBLIC, not PRIVATE: a consumer TU (test, strategy
# .so) that links the instrumented library but compiles WITHOUT -fsanitize
# gets a different Eigen alignment view than matrix.cpp, so an Eigen matrix
# allocated in the library and freed (header-inlined) in the consumer hits a
# cross-TU aligned-free mismatch -> ASan "bad-free" (seen on Linux; masked by
# macOS's allocator). PUBLIC keeps instrumentation consistent everywhere.
target_compile_options(pineforge PUBLIC
-fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_options(pineforge PUBLIC -fsanitize=address,undefined)
# Belt-and-suspenders: disable Eigen's manual over-alignment under the
# sanitizer build so Eigen uses plain malloc/free (no offset pointers to
# mis-free). Sanitizer-lane only; normal builds keep full alignment.
target_compile_definitions(pineforge PUBLIC EIGEN_MAX_ALIGN_BYTES=0)
message(STATUS "Sanitizers: -fsanitize=address,undefined (Eigen alignment disabled)")
endif()
# === Strict warnings (opt-in) =========================================
if(PINEFORGE_STRICT_WARNINGS)
target_compile_options(pineforge PRIVATE -Wconversion -Wsign-conversion)
message(STATUS "Strict warnings: -Wconversion -Wsign-conversion")
endif()
# === Tests =============================================================
if(PINEFORGE_BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# === Tutorial strategy (opt-out) =======================================
if(PINEFORGE_BUILD_TUTORIAL)
add_subdirectory(tutorial)
endif()
# === Corpus strategies (opt-in) ========================================
if(PINEFORGE_BUILD_CORPUS_STRATEGIES)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/corpus/CMakeLists.txt")
add_subdirectory(corpus)
else()
message(FATAL_ERROR
"PINEFORGE_BUILD_CORPUS_STRATEGIES=ON but corpus/CMakeLists.txt is missing. "
"Init the private validation submodule (maintainers): "
" git submodule update --init corpus\n"
"See CONTRIBUTING.md.")
endif()
endif()
# === Bench strategies (opt-in) ========================================
# Builds one strategy.dylib/.so per bench strategy from committed generated.cpp.
# Requires the benchmarks/assets submodule to be initialised:
# git submodule update --init benchmarks/assets
# Build with:
# cmake -B build -DPINEFORGE_BUILD_BENCH_STRATEGIES=ON
# cmake --build build --target bench_strategies -j
option(PINEFORGE_BUILD_BENCH_STRATEGIES
"Build benchmarks bench strategies (.dylib/.so per strategy)" OFF)
if(PINEFORGE_BUILD_BENCH_STRATEGIES)
set(_bench_strat_cmake
"${CMAKE_CURRENT_SOURCE_DIR}/benchmarks/assets/strategies/CMakeLists.txt")
if(EXISTS "${_bench_strat_cmake}")
add_subdirectory(benchmarks/assets/strategies)
else()
message(FATAL_ERROR
"PINEFORGE_BUILD_BENCH_STRATEGIES=ON but "
"benchmarks/assets/strategies/CMakeLists.txt is missing.\n"
"Init the assets submodule:\n"
" git submodule update --init benchmarks/assets\n"
"See CONTRIBUTING.md.")
endif()
endif()
# === Speed bench (opt-in) =============================================
# Google Benchmark target for per-strategy PineForge timing.
# Build with: cmake -B build -DPINEFORGE_BUILD_SPEED_BENCH=ON
# Requires bench-built strategy dylibs alongside generated.cpp in assets/strategies/.
# Build bench strategies first: cmake --build build --target bench_strategies
option(PINEFORGE_BUILD_SPEED_BENCH "Build benchmarks/speed GBench target" OFF)
if(PINEFORGE_BUILD_SPEED_BENCH)
add_subdirectory(benchmarks/speed)
endif()
# === Install / package config =========================================
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
install(TARGETS pineforge
EXPORT PineForgeTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(DIRECTORY include/pineforge
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp"
)
# Generated version header — installed alongside the curated ones.
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/pineforge/version.h"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pineforge
)
install(EXPORT PineForgeTargets
FILE PineForgeTargets.cmake
NAMESPACE PineForge::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/PineForge
)
configure_package_config_file(
cmake/PineForgeConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/PineForgeConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/PineForge
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/PineForgeConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/PineForgeConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/PineForgeConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/PineForge
)
# === Summary ===========================================================
message(STATUS "")
message(STATUS "PineForge ${PINEFORGE_VERSION_FULL}")
message(STATUS " build type: ${CMAKE_BUILD_TYPE}")
message(STATUS " C++ standard: ${CMAKE_CXX_STANDARD}")
message(STATUS " install prefix: ${CMAKE_INSTALL_PREFIX}")
message(STATUS " build tests: ${PINEFORGE_BUILD_TESTS}")
message(STATUS " build tutorial: ${PINEFORGE_BUILD_TUTORIAL}")
message(STATUS " build corpus: ${PINEFORGE_BUILD_CORPUS_STRATEGIES}")
message(STATUS " build bench strat: ${PINEFORGE_BUILD_BENCH_STRATEGIES}")
message(STATUS "")