File size: 7,822 Bytes
3374e90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
cmake_minimum_required(VERSION 3.16)
project(bexcli LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# ── Release Optimization Flags ────────────────────────────────────────────
# Full static binary, size-optimized, no debug symbols in release mode.
if(CMAKE_BUILD_TYPE STREQUAL "Release")
    # Strip all debug symbols from the final binary
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffunction-sections -fdata-sections")
    set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s -Wl,--gc-sections -Wl,--strip-all")
    # Optimize for size
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os")
endif()

# ── Build Configuration ──────────────────────────────────────────────────
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

# ── Bex Engine Integration ───────────────────────────────────────────────
# This CMake is designed to be self-contained and reusable.
# Copy this directory into any C++ project and it will automatically
# find and link the Rust Bex Engine static library.
#
# Requirements:
#   1. Build the Rust library first: cargo build -p bex-runtime --release
#   2. Set BEX_ENGINE_ROOT if this directory is not inside the bex-engine repo
#
# The Rust library exports a pure C ABI via bex_engine.h β€” no bridge crate needed.

# Find the Bex Engine root directory
if(NOT BEX_ENGINE_ROOT)
    # Default: assume we're inside bex-engine/cpp-cli/
    get_filename_component(_BEX_ROOT "${CMAKE_SOURCE_DIR}/.." ABSOLUTE)
    if(EXISTS "${_BEX_ROOT}/Cargo.toml")
        set(BEX_ENGINE_ROOT "${_BEX_ROOT}")
    else()
        message(FATAL_ERROR
            "Cannot find Bex Engine root. Set -DBEX_ENGINE_ROOT=/path/to/bex-engine\n"
            "The bex-engine directory should contain Cargo.toml and the crates/ directory."
        )
    endif()
endif()

message(STATUS "Bex Engine root: ${BEX_ENGINE_ROOT}")

# ── Rust Target Directory ────────────────────────────────────────────────
# CMake uses "Release" / "Debug", but Rust uses "release" / "debug"
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
    set(RUST_PROFILE "release")
else()
    set(RUST_PROFILE "debug")
endif()

set(RUST_TARGET_DIR "${BEX_ENGINE_ROOT}/target/${RUST_PROFILE}")

# ── Detect the Rust library name ─────────────────────────────────────────
# bex-runtime compiles as both cdylib and staticlib. The produced filename
# differs by platform/toolchain: Windows (MSVC) -> "bex_runtime.lib"/.dll,
# GNU toolchain -> "libbex_runtime.a"/.so. Prefer static when available.

if(WIN32)
    set(RUST_STATIC_LIB "${RUST_TARGET_DIR}/bex_runtime.lib")
    set(RUST_SHARED_LIB "${RUST_TARGET_DIR}/bex_runtime.dll")
else()
    set(RUST_STATIC_LIB "${RUST_TARGET_DIR}/libbex_runtime.a")
    set(RUST_SHARED_LIB "${RUST_TARGET_DIR}/libbex_runtime.so")
endif()

if(EXISTS "${RUST_STATIC_LIB}")
    set(BEX_LINK_MODE "static")
    set(BEX_LIB_PATH "${RUST_STATIC_LIB}")
    message(STATUS "Found Rust static library: ${BEX_LIB_PATH}")
elseif(EXISTS "${RUST_SHARED_LIB}")
    set(BEX_LINK_MODE "shared")
    set(BEX_LIB_PATH "${RUST_SHARED_LIB}")
    message(STATUS "Found Rust shared library: ${BEX_LIB_PATH}")
else()
    message(WARNING
        "Rust library not found at ${RUST_STATIC_LIB} or ${RUST_SHARED_LIB}\n"
        "Build it first: cd ${BEX_ENGINE_ROOT} && cargo build -p bex-runtime --release"
    )
    set(BEX_LINK_MODE "static")
    set(BEX_LIB_PATH "${RUST_STATIC_LIB}")
endif()

if(WIN32)
    set(BEX_WINDOWS_SYS_LIBS bcrypt userenv ntdll advapi32)
endif()

# ── C Header Location ────────────────────────────────────────────────────
set(BEX_INCLUDE_DIR "${CMAKE_SOURCE_DIR}")

# ── Bex Engine Library (Imported Target) ─────────────────────────────────
# Create an INTERFACE library target so other projects can simply do:
#   target_link_libraries(myapp PRIVATE bex::engine)
#   target_include_directories(myapp PRIVATE ${BEX_INCLUDE_DIR})
add_library(bex_engine_lib INTERFACE)
add_library(bex::engine ALIAS bex_engine_lib)

target_include_directories(bex_engine_lib INTERFACE "${BEX_INCLUDE_DIR}")

if(BEX_LINK_MODE STREQUAL "static")
    if(WIN32)
        target_link_libraries(bex_engine_lib INTERFACE
            "${BEX_LIB_PATH}"
            ${BEX_WINDOWS_SYS_LIBS}
        )
    else()
        target_link_libraries(bex_engine_lib INTERFACE
            "${BEX_LIB_PATH}"
            dl
            pthread
            m
        )
        target_link_directories(bex_engine_lib INTERFACE "${RUST_TARGET_DIR}/deps")
    endif()
else()
    if(WIN32)
        target_link_libraries(bex_engine_lib INTERFACE
            "${BEX_LIB_PATH}"
        )
    else()
        target_link_libraries(bex_engine_lib INTERFACE
            "${BEX_LIB_PATH}"
            dl
            pthread
            m
        )
    endif()
endif()

# ── Executable: bexcli ───────────────────────────────────────────────────
add_executable(bexcli bexcli.cpp)

target_include_directories(bexcli PRIVATE "${BEX_INCLUDE_DIR}")

target_link_libraries(bexcli PRIVATE bex_engine_lib)

# ── Static Linking for Release ────────────────────────────────────────────
# Produce a fully static binary in release mode (no runtime dependencies).
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32)
    target_link_options(bexcli PRIVATE -static)
endif()

# ── Helper Target: Build Rust Library ────────────────────────────────────
add_custom_target(rustlib
    COMMAND ${CMAKE_COMMAND} -E env cargo build -p bex-runtime --release
    WORKING_DIRECTORY ${BEX_ENGINE_ROOT}
    COMMENT "Building Rust bex-runtime library (pure C ABI)..."
    VERBATIM
)

# Make bexcli depend on rustlib if the library file doesn't exist yet
if(NOT EXISTS "${BEX_LIB_PATH}")
    add_dependencies(bexcli rustlib)
endif()

# ── Install Rules (for integration into other projects) ──────────────────
install(TARGETS bexcli RUNTIME DESTINATION bin)
install(FILES
    "${CMAKE_SOURCE_DIR}/bex_engine.h"
    DESTINATION include/bex
)

# ── Print Configuration Summary ──────────────────────────────────────────
message(STATUS "")
message(STATUS "=== Bex CLI Configuration (Pure C ABI) ===")
message(STATUS "  Build type:       ${CMAKE_BUILD_TYPE}")
message(STATUS "  Rust profile:     ${RUST_PROFILE}")
message(STATUS "  Engine root:      ${BEX_ENGINE_ROOT}")
message(STATUS "  Rust target dir:  ${RUST_TARGET_DIR}")
message(STATUS "  Link mode:        ${BEX_LINK_MODE}")
message(STATUS "  Library path:     ${BEX_LIB_PATH}")
message(STATUS "  Include dir:      ${BEX_INCLUDE_DIR}")
message(STATUS "  FFI:              Pure C ABI (bex_engine.h)")
message(STATUS "==========================================")
message(STATUS "")