-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
237 lines (216 loc) · 10.1 KB
/
Copy pathCMakeLists.txt
File metadata and controls
237 lines (216 loc) · 10.1 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
cmake_minimum_required(VERSION 3.15)
file(STRINGS "${CMAKE_SOURCE_DIR}/VERSION" UNI_VERSION)
# VERSION may carry a SemVer pre-release/build suffix (e.g. 0.2.0-rc.1); strip it
# for project(), which only accepts a numeric major[.minor[.patch[.tweak]]].
string(REGEX REPLACE "[-+].*$" "" UNI_VERSION_NUMERIC "${UNI_VERSION}")
project(UNI VERSION ${UNI_VERSION_NUMERIC} LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Build with TLS (uWS::SSLApp) by default. Turn OFF for plain HTTP when a host
# terminates TLS at its edge and proxies HTTP to the container (e.g. Render):
# cmake --preset conan-release -DUNI_ENABLE_SSL=OFF
option(UNI_ENABLE_SSL "Build the server with TLS (uWS::SSLApp)" ON)
# --- Contract code generation (hatchbed/asyncapi_gencpp) ---
# Fetches the generator at configure time, then runs it as a pre-build step.
# Source of truth: contract/asyncapi.yaml
# Output: ${CMAKE_BINARY_DIR}/generated/ws/messages.h (namespace ws::)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
include(FetchContent)
FetchContent_Declare(
asyncapi_gencpp
GIT_REPOSITORY https://github.com/hatchbed/asyncapi_gencpp.git
GIT_TAG main
GIT_SHALLOW TRUE
)
# Populate only (no add_subdirectory), the package is ROS-specific and only
# the Python generator script is needed here. MakeAvailable would call
# add_subdirectory and pull in its ROS-specific CMake, so the single-argument
# FetchContent_Populate is intentional; CMP0169 OLD allows it without warnings.
if(POLICY CMP0169)
cmake_policy(SET CMP0169 OLD)
endif()
FetchContent_GetProperties(asyncapi_gencpp)
if(NOT asyncapi_gencpp_POPULATED)
FetchContent_Populate(asyncapi_gencpp)
endif()
set(WS_CONTRACT_YAML "${CMAKE_SOURCE_DIR}/contract/asyncapi.yaml")
set(WS_GEN_SCRIPT "${asyncapi_gencpp_SOURCE_DIR}/src/asyncapi_gencpp.py")
# The script writes: {outdir}/{prefix}/messages.h (one .h per schema + master messages.h)
set(WS_MESSAGES_H "${CMAKE_BINARY_DIR}/generated/ws/messages.h")
add_custom_command(
OUTPUT "${WS_MESSAGES_H}"
COMMAND ${CMAKE_COMMAND} -E echo ""
COMMAND ${CMAKE_COMMAND} -E echo "-- Generating WebSocket payload structs from asyncapi.yaml"
COMMAND "${Python3_EXECUTABLE}" "${WS_GEN_SCRIPT}"
"${WS_CONTRACT_YAML}" "ws" "${CMAKE_BINARY_DIR}/generated"
DEPENDS "${WS_CONTRACT_YAML}" "${WS_GEN_SCRIPT}"
)
add_custom_target(gen_ws_messages DEPENDS "${WS_MESSAGES_H}")
# --- Validation constants (common/contract.hpp) ---
# Derives <common/contract.hpp> (namespace contract::) from the same AsyncAPI
# constraint schemas the frontend reads, so the backend's validation limits are
# never hand-maintained. Mirrors the ws-message generation above.
# Source of truth: contract/asyncapi.yaml
# Output: ${CMAKE_BINARY_DIR}/generated/common/contract.hpp
set(CONTRACT_HPP_SCRIPT "${CMAKE_SOURCE_DIR}/scripts/generate_contract_hpp.py")
set(CONTRACT_HPP_OUT "${CMAKE_BINARY_DIR}/generated/common/contract.hpp")
add_custom_command(
OUTPUT "${CONTRACT_HPP_OUT}"
COMMAND ${CMAKE_COMMAND} -E echo ""
COMMAND ${CMAKE_COMMAND} -E echo "-- Generating validation constants from asyncapi.yaml"
COMMAND "${Python3_EXECUTABLE}" "${CONTRACT_HPP_SCRIPT}"
"${WS_CONTRACT_YAML}" "${CONTRACT_HPP_OUT}"
DEPENDS "${WS_CONTRACT_YAML}" "${CONTRACT_HPP_SCRIPT}"
)
add_custom_target(gen_contract_hpp DEPENDS "${CONTRACT_HPP_OUT}")
# --- WebSocket action constants (ws/actions.hpp) ---
# Derives <ws/actions.hpp> (namespace ws::) from the channel publish/subscribe
# lists in the AsyncAPI contract. Replaces the hand-maintained ClientAction
# struct and ServerAction enum that previously lived in include/common/ws.hpp.
# Source of truth: contract/asyncapi.yaml
# Output: ${CMAKE_BINARY_DIR}/generated/ws/actions.hpp
set(WS_ACTIONS_HPP_SCRIPT "${CMAKE_SOURCE_DIR}/scripts/generate_ws_hpp.py")
set(WS_ACTIONS_HPP_OUT "${CMAKE_BINARY_DIR}/generated/ws/actions.hpp")
add_custom_command(
OUTPUT "${WS_ACTIONS_HPP_OUT}"
COMMAND ${CMAKE_COMMAND} -E echo ""
COMMAND ${CMAKE_COMMAND} -E echo "-- Generating WebSocket action constants from asyncapi.yaml"
COMMAND "${Python3_EXECUTABLE}" "${WS_ACTIONS_HPP_SCRIPT}"
"${WS_CONTRACT_YAML}" "${WS_ACTIONS_HPP_OUT}"
DEPENDS "${WS_CONTRACT_YAML}" "${WS_ACTIONS_HPP_SCRIPT}"
)
add_custom_target(gen_ws_actions_hpp DEPENDS "${WS_ACTIONS_HPP_OUT}")
find_package(OpenSSL REQUIRED)
find_package(ZLIB REQUIRED)
find_package(libuv REQUIRED)
find_package(uwebsockets REQUIRED)
find_package(SQLite3 REQUIRED)
find_package(jwt-cpp REQUIRED)
find_package(nlohmann_json REQUIRED)
# --- Main Executable ---
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(uni_server ${SOURCES})
# --- Fix for Winsock conflicts on Windows ---
if(WIN32)
target_compile_definitions(uni_server PRIVATE WIN32_LEAN_AND_MEAN)
endif()
target_compile_definitions(uni_server PRIVATE UNI_ENABLE_SSL=$<BOOL:${UNI_ENABLE_SSL}>)
target_include_directories(uni_server PRIVATE include src "${CMAKE_BINARY_DIR}/generated")
add_dependencies(uni_server gen_ws_messages gen_contract_hpp gen_ws_actions_hpp)
# --- Linking ---
target_link_libraries(uni_server PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
ZLIB::ZLIB
libuv::uv_a
uwebsockets::uwebsockets
SQLite::SQLite3
jwt-cpp::jwt-cpp
nlohmann_json::nlohmann_json
)
# --- Copy the runtime assets next to the executable ---
# The server resolves the certificates, .env file and public/ folder starting from
# the current working directory. By copying them next to the binary, the executable can
# be launched either from the project root or directly from its own build folder
# (e.g. build/Release/).
#
# Note: the presence of the files is evaluated at configuration time (CMake), so
# the certificates, .env and public/ must be generated BEFORE `cmake --preset`. The setup
# script creates them in the correct order.
set(RUNTIME_FILES cert.pem key.pem .env)
foreach(asset IN LISTS RUNTIME_FILES)
if(EXISTS "${CMAKE_SOURCE_DIR}/${asset}")
add_custom_command(TARGET uni_server POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo ""
COMMAND ${CMAKE_COMMAND} -E echo "-- Copying ${asset}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/${asset}"
"$<TARGET_FILE_DIR:uni_server>/${asset}"
)
else()
message(WARNING "Runtime asset '${asset}' not found: it will not be copied next to the executable.")
endif()
endforeach()
if(EXISTS "${CMAKE_SOURCE_DIR}/public")
# Symlink rather than copy: a `copy_directory` snapshot only refreshes on
# the next `cmake --build`, so `npm run watch` rebuilds (which write
# straight into the root public/) never reached the binary's runtime
# public/ without manually rebuilding/restarting the server. A symlink
# means the running server always sees whatever's on disk at
# ${CMAKE_SOURCE_DIR}/public live, no rebuild needed. `rm -rf` first
# since create_symlink refuses to overwrite an existing path (e.g. a
# leftover directory copy from before this change).
add_custom_target(sync_public ALL
COMMAND ${CMAKE_COMMAND} -E echo ""
COMMAND ${CMAKE_COMMAND} -E echo "-- Symlinking public/ next to the executable"
COMMAND ${CMAKE_COMMAND} -E rm -rf "$<TARGET_FILE_DIR:uni_server>/public"
COMMAND ${CMAKE_COMMAND} -E create_symlink
"${CMAKE_SOURCE_DIR}/public"
"$<TARGET_FILE_DIR:uni_server>/public"
)
add_dependencies(sync_public uni_server)
else()
message(WARNING "Folder 'public/' not found: build the frontend first (cd frontend && npm run build).")
endif()
# --- Unit tests (doctest) ---
# On by default so the test TUs land in compile_commands.json and clangd/LSP can
# resolve <doctest/doctest.h>. Configure with -DUNI_BUILD_TESTS=OFF for a lighter
# build (skips the doctest dependency and the uni_tests target). Each tested
# module is compiled into uni_tests alongside the test sources (the main
# executable's TU carries main(), so it cannot be linked here).
option(UNI_BUILD_TESTS "Build the C++ unit tests (doctest)" ON)
if(UNI_BUILD_TESTS)
find_package(doctest REQUIRED)
enable_testing()
file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS "tests/unit/*.cpp")
add_executable(uni_tests
${TEST_SOURCES}
src/common/rate_limiter.cpp
src/common/login_throttle.cpp
src/common/lobby.cpp
src/match/match_instance.cpp
src/match/rules/standard.cpp
src/match/rules/70.cpp
src/match/rules/draw_stacking.cpp
src/match/rules/force_play.cpp
src/match/rules/jump_in.cpp
src/match/rules/no_bluffing.cpp
src/match/rules/progressive.cpp
src/match/effects/standard.cpp
src/match/effects/decide_swap_target.cpp
src/match/effects/pass_hands.cpp
src/controllers/lobby_controller.cpp
src/controllers/match_controller.cpp
src/controllers/auth_controller.cpp
src/controllers/stats_controller.cpp
src/controllers/friend_controller.cpp
src/controllers/chat_controller.cpp
src/services/auth_service.cpp
src/services/stats_service.cpp
src/services/friend_service.cpp
src/services/chat_service.cpp
src/action_router.cpp
src/http_router.cpp
src/common/http_utils.cpp
src/database.cpp
src/transport/uws_broadcaster.cpp
src/transport/uws_timer_service.cpp
src/transport/presence_registry.cpp
)
target_include_directories(uni_tests PRIVATE include src "${CMAKE_BINARY_DIR}/generated")
target_link_libraries(uni_tests PRIVATE
doctest::doctest
nlohmann_json::nlohmann_json
uwebsockets::uwebsockets
usockets::usockets
libuv::uv_a
OpenSSL::SSL OpenSSL::Crypto
SQLite::SQLite3
ZLIB::ZLIB
jwt-cpp::jwt-cpp
)
add_dependencies(uni_tests gen_ws_messages gen_contract_hpp gen_ws_actions_hpp)
add_test(NAME unit COMMAND uni_tests)
endif()
message(STATUS "Configuration complete for system: ${CMAKE_SYSTEM_NAME}")