-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
279 lines (244 loc) · 12.3 KB
/
CMakeLists.txt
File metadata and controls
279 lines (244 loc) · 12.3 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
cmake_minimum_required(VERSION 3.19.2)
# Disable RUNPATH BEFORE project() - native libraries will be extracted and loaded at runtime
# instead of using hardcoded paths from the build environment
set(CMAKE_SKIP_RPATH ON)
set(CMAKE_SKIP_BUILD_RPATH ON)
project("embedded-ruby-vm")
# Enable Position Independent Code for all targets
# Required when linking static libraries into shared libraries
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Add NDEBUG for Release builds (applies to all targets)
# This is needed because COMPILE_FLAGS in subdirectories override CMake's defaults
add_compile_definitions($<$<CONFIG:Release>:NDEBUG>)
# Determine host triplet using compiler's -dumpmachine
# This gives us the proper triplet (e.g., x86_64-linux-gnu, aarch64-linux-android)
# If HOST is already defined (e.g., from toolchain file), use that instead
if(NOT DEFINED HOST OR HOST STREQUAL "")
# For cross-compilation (Android, iOS), -dumpmachine returns the build host
# triple instead of the target. Pass -DHOST=<triplet> explicitly in those cases.
execute_process(
COMMAND ${CMAKE_C_COMPILER} -dumpmachine
OUTPUT_VARIABLE HOST
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
# Fallback if -dumpmachine fails (shouldn't happen with modern compilers)
if(NOT HOST)
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}" HOST)
message(WARNING "Could not determine host triplet from compiler, using fallback: ${HOST}")
endif()
else()
message(STATUS "Using HOST from toolchain/cache: ${HOST}")
endif()
message(NOTICE "Configuring for host: ${HOST}")
# Two-flag build system for fine-grained control:
# - BUILD_WRAPPER_SHARED: Controls output type of libassets and libembedded-ruby
# (ON for JVM/JNI, OFF for Kotlin/Native)
# - BUILD_SHARED_LIBS: Controls which Ruby libraries to link against
# (ON = libruby.so, OFF = libruby-static.a + libruby-ext.a)
option(BUILD_WRAPPER_SHARED "Build libassets and libembedded-ruby as shared libraries (required for JVM/JNI)" OFF)
option(BUILD_SHARED_LIBS "Link against shared Ruby libraries instead of static" OFF)
option(BUILD_JNI "Build JNI binding" OFF)
# Default BUILD_JNI_ANDROID_LOG to ON for Android, OFF for other platforms
if(ANDROID)
option(BUILD_JNI_ANDROID_LOG "Build Android Logcat output" ON)
else()
option(BUILD_JNI_ANDROID_LOG "Build Android Logcat output" OFF)
endif()
option(BUILD_TESTS "Build tests" ON)
# Set build type directory for library resolution based on BUILD_SHARED_LIBS
if(BUILD_SHARED_LIBS)
set(BUILD_TYPE_DIR "shared")
else()
set(BUILD_TYPE_DIR "static")
endif()
# External libraries directory includes build type (shared/static)
# This allows supporting both shared and static Ruby builds
set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/external/lib/${HOST}/${BUILD_TYPE_DIR})
# Fallback: if detected HOST directory doesn't exist, try alternative triplets
if(NOT EXISTS "${EXTERNAL_LIBS_DIR}")
message(WARNING "Directory ${EXTERNAL_LIBS_DIR} not found, trying alternatives...")
# Try x86_64-linux-gnu if HOST is x86_64-linux-linux
if(HOST MATCHES "x86_64-linux")
set(ALT_HOST "x86_64-linux-gnu")
set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/external/lib/${ALT_HOST}/${BUILD_TYPE_DIR})
if(EXISTS "${EXTERNAL_LIBS_DIR}")
message(STATUS "Using alternative host triplet: ${ALT_HOST}")
set(HOST "${ALT_HOST}")
endif()
endif()
# Android NDK clang's -dumpmachine may append the API level (e.g. aarch64-linux-android24).
# The prebuilt libraries use the base triplet without the API level suffix.
if(NOT EXISTS "${EXTERNAL_LIBS_DIR}" AND HOST MATCHES "^([a-z0-9_]+-linux-android)[0-9]+$")
set(ALT_HOST "${CMAKE_MATCH_1}")
set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/external/lib/${ALT_HOST}/${BUILD_TYPE_DIR})
if(EXISTS "${EXTERNAL_LIBS_DIR}")
message(STATUS "Using alternative host triplet (stripped API level): ${ALT_HOST}")
set(HOST "${ALT_HOST}")
endif()
endif()
# macOS clang's -dumpmachine includes a Darwin version suffix (e.g. arm64-apple-darwin24.0.0).
# The prebuilt libraries use the unversioned triplet with aarch64 (aarch64-apple-darwin).
# Step 1: Strip the Darwin version suffix (arm64-apple-darwin24.0.0 → arm64-apple-darwin)
if(NOT EXISTS "${EXTERNAL_LIBS_DIR}" AND HOST MATCHES "^([a-z0-9_]+-apple-darwin)[0-9.]+$")
set(ALT_HOST "${CMAKE_MATCH_1}")
set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/external/lib/${ALT_HOST}/${BUILD_TYPE_DIR})
if(EXISTS "${EXTERNAL_LIBS_DIR}")
message(STATUS "Using alternative host triplet (stripped Darwin version): ${ALT_HOST}")
set(HOST "${ALT_HOST}")
endif()
endif()
# Step 2: Normalize arm64 → aarch64 for consistency with ruby-for-android archives
if(NOT EXISTS "${EXTERNAL_LIBS_DIR}" AND HOST MATCHES "^arm64-apple-darwin$")
set(ALT_HOST "aarch64-apple-darwin")
set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/external/lib/${ALT_HOST}/${BUILD_TYPE_DIR})
if(EXISTS "${EXTERNAL_LIBS_DIR}")
message(STATUS "Using alternative host triplet (arm64 → aarch64): ${ALT_HOST}")
set(HOST "${ALT_HOST}")
endif()
endif()
endif()
# Auto-download Ruby libraries from GitHub Releases if not present
if(NOT EXISTS "${EXTERNAL_LIBS_DIR}" AND NOT SKIP_RUBY_DOWNLOAD)
find_program(BASH_PROGRAM bash)
if(BASH_PROGRAM)
message(STATUS "Ruby libs not found at ${EXTERNAL_LIBS_DIR}, attempting download...")
execute_process(
COMMAND ${BASH_PROGRAM} ${CMAKE_SOURCE_DIR}/scripts/download-ruby-libs.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE DOWNLOAD_RESULT
)
if(NOT DOWNLOAD_RESULT EQUAL 0)
message(WARNING
"Auto-download of Ruby libs failed (exit code: ${DOWNLOAD_RESULT}).\n"
"Please run manually: ./scripts/download-ruby-libs.sh\n"
"Or set -DSKIP_RUBY_DOWNLOAD=ON to skip this step."
)
endif()
else()
message(WARNING
"Ruby libs not found and bash not available for auto-download.\n"
"Please run: ./scripts/download-ruby-libs.sh"
)
endif()
endif()
message(STATUS "Using external libraries from: ${EXTERNAL_LIBS_DIR}")
message(STATUS "Build wrapper as shared: ${BUILD_WRAPPER_SHARED}")
message(STATUS "Link against shared Ruby: ${BUILD_SHARED_LIBS}")
# Add external library directory to linker search path
# This allows CMake to find ruby-static, ssl, crypto, readline, etc.
link_directories(${EXTERNAL_LIBS_DIR})
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")
set(EMBEDDED_RUBY_VM_MAIN_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include)
add_subdirectory(core)
add_subdirectory(assets)
if(BUILD_JNI)
set(JNI_DIR ${CMAKE_SOURCE_DIR}/jni)
add_subdirectory(jni)
endif()
# Create embedded-ruby library with type based on BUILD_WRAPPER_SHARED
# Combine OBJECT libraries (logging, ruby-vm, jni) into a single library
if(BUILD_WRAPPER_SHARED)
# Shared library: Include all OBJECT files directly
add_library(embedded-ruby SHARED
$<TARGET_OBJECTS:logging>
$<TARGET_OBJECTS:ruby-vm>
$<$<BOOL:${BUILD_JNI}>:$<TARGET_OBJECTS:jni>>
$<$<BOOL:${BUILD_JNI_ANDROID_LOG}>:$<TARGET_OBJECTS:jni_logging_android>>
)
else()
# Static library: Include all OBJECT files directly
add_library(embedded-ruby STATIC
$<TARGET_OBJECTS:logging>
$<TARGET_OBJECTS:ruby-vm>
$<$<BOOL:${BUILD_JNI}>:$<TARGET_OBJECTS:jni>>
$<$<BOOL:${BUILD_JNI_ANDROID_LOG}>:$<TARGET_OBJECTS:jni_logging_android>>
)
endif()
# Add the embedded script object file from ruby-vm
# This is generated by a custom command, so we mark it as EXTERNAL_OBJECT
set_source_files_properties(${RUBY_VM_EMBEDDED_SCRIPT_OBJ} PROPERTIES
EXTERNAL_OBJECT TRUE
GENERATED TRUE
)
target_sources(embedded-ruby PRIVATE ${RUBY_VM_EMBEDDED_SCRIPT_OBJ})
# Link dependencies from ruby-vm
# Since we're using OBJECT libraries, all object files are already included
# We just need to link against ruby-vm's external dependencies
# For static builds, use PUBLIC so transitive dependencies propagate to consumers
# (static libraries don't resolve symbols themselves - the final executable needs them)
if(BUILD_WRAPPER_SHARED)
set(_EMBEDDED_RUBY_LINK_VISIBILITY PRIVATE)
else()
set(_EMBEDDED_RUBY_LINK_VISIBILITY PUBLIC)
endif()
target_link_libraries(embedded-ruby ${_EMBEDDED_RUBY_LINK_VISIBILITY} $<TARGET_PROPERTY:ruby-vm,LINK_LIBRARIES>)
# We also need to link system libraries for both static and shared builds
# These are required by Ruby but might not be fully propagated through OBJECT libraries
# macOS provides dl and pthread in libSystem (no separate libraries)
if(APPLE)
target_link_libraries(embedded-ruby ${_EMBEDDED_RUBY_LINK_VISIBILITY} m)
else()
target_link_libraries(embedded-ruby ${_EMBEDDED_RUBY_LINK_VISIBILITY} m dl pthread)
endif()
# Link Android log library when building for Android (needed by debug.h macros)
if(ANDROID)
find_library(log-lib log)
if(log-lib)
target_link_libraries(embedded-ruby PRIVATE ${log-lib})
message(STATUS "Linked Android log library to embedded-ruby target")
else()
message(WARNING "Android log library not found for embedded-ruby")
endif()
endif()
# Propagate include directories from ruby-vm so consumers can access headers
target_include_directories(embedded-ruby PUBLIC
$<TARGET_PROPERTY:ruby-vm,INTERFACE_INCLUDE_DIRECTORIES>
)
# Add explicit dependency relationships so CMake knows build order
add_dependencies(embedded-ruby logging ruby-vm)
if(BUILD_JNI)
add_dependencies(embedded-ruby jni)
endif()
if(BUILD_JNI_ANDROID_LOG)
add_dependencies(embedded-ruby jni_logging_android)
endif()
# This ensures the embedded dependency list is always up-to-date
# Also creates a standalone deps.txt file that can be read without loading the library
# Note: Only extract dependencies for shared wrapper libraries (static libs have no NEEDED entries)
if(BUILD_WRAPPER_SHARED)
add_custom_command(TARGET embedded-ruby POST_BUILD
COMMAND bash -c "${CMAKE_SOURCE_DIR}/core/ruby-vm/extract_deps.sh $<TARGET_FILE:embedded-ruby> ${EXTERNAL_LIBS_DIR} ${CMAKE_BINARY_DIR}/deps_list.txt || true"
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/deps_list.txt ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libembedded-ruby.deps
COMMENT "Extracting library dependencies from libembedded-ruby.so"
)
else()
# For static wrapper builds, create a placeholder deps file
file(WRITE ${CMAKE_BINARY_DIR}/create_static_deps.cmake
"file(WRITE \"${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libembedded-ruby.deps\" \"# Static build - no runtime dependencies\\n\")"
)
add_custom_command(TARGET embedded-ruby POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/create_static_deps.cmake
COMMENT "Creating placeholder deps file for static build"
)
endif()
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# Add distclean target to remove all generated files and CMake cache
# This is particularly important for cross-compilation where cached
# configuration can cause the wrong host triplet to be detected
add_custom_target(distclean
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/CMakeFiles
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/CMakeCache.txt
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/cmake_install.cmake
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/Makefile
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/lib
COMMENT "Removing all generated files and CMake cache to force reconfiguration"
)