Skip to content

Commit c73a230

Browse files
committed
Document the CMakeLists.txt file.
1 parent ea11bd2 commit c73a230

File tree

1 file changed

+137
-15
lines changed

1 file changed

+137
-15
lines changed

ext/CMakeLists.txt

Lines changed: 137 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,63 @@
1-
cmake_minimum_required (VERSION 3.26)
1+
# =============================================================================
2+
# CMakeLists.txt - Building a Ruby Extension with Rice
3+
# =============================================================================
4+
#
5+
# This CMake configuration demonstrates how to build a Ruby native extension
6+
# using the Rice library (https://github.com/ruby-rice/rice). Rice provides
7+
# a clean C++ interface for creating Ruby bindings.
8+
#
9+
# This file serves as an educational example and can be adapted for your
10+
# own Rice-based Ruby extensions.
11+
#
12+
# =============================================================================
13+
14+
# -----------------------------------------------------------------------------
15+
# CMake Version and C++ Standard
16+
# -----------------------------------------------------------------------------
17+
# CMake 3.26+ is required for modern FetchContent features and improved
18+
# Ruby detection. Rice requires C++17 for features like std::string_view,
19+
# structured bindings, and if-constexpr.
20+
21+
cmake_minimum_required(VERSION 3.26)
222

323
set(CMAKE_CXX_STANDARD 17)
424
set(CMAKE_CXX_STANDARD_REQUIRED ON)
525

6-
# Setup ruby extension
26+
# -----------------------------------------------------------------------------
27+
# Project Definition
28+
# -----------------------------------------------------------------------------
29+
# Define the project name and specify that we only need a C++ compiler.
30+
# The project name is used throughout via ${CMAKE_PROJECT_NAME}.
31+
732
project(BitmapPlusPlus LANGUAGES CXX)
8-
add_library (${CMAKE_PROJECT_NAME} MODULE)
933

10-
# Link to Rice git repo
34+
# -----------------------------------------------------------------------------
35+
# Create the Extension Library
36+
# -----------------------------------------------------------------------------
37+
# IMPORTANT: Use MODULE instead of SHARED for Ruby extensions!
38+
#
39+
# - MODULE: Creates a loadable plugin that cannot be linked against.
40+
# This is correct for Ruby extensions loaded via require/dlopen.
41+
# - SHARED: Creates a shared library that can be linked against.
42+
# On macOS, this creates a .dylib which Ruby cannot load.
43+
#
44+
# Using SHARED on macOS will result in: "LoadError: cannot load such file"
45+
# because Ruby expects a .bundle file, not a .dylib.
46+
47+
add_library(${CMAKE_PROJECT_NAME} MODULE)
48+
49+
# -----------------------------------------------------------------------------
50+
# Fetch Rice from GitHub
51+
# -----------------------------------------------------------------------------
52+
# Rice is a header-only library, so we use FetchContent to download it
53+
# automatically. This eliminates the need for users to manually install Rice.
54+
#
55+
# FetchContent downloads the repository at configure time and makes it
56+
# available as if it were part of your project.
57+
#
58+
# Note: For production gems, you may want to pin to a specific release tag
59+
# instead of 'dev' for reproducible builds.
60+
1161
include(FetchContent)
1262
FetchContent_Declare(
1363
rice
@@ -16,37 +66,109 @@ FetchContent_Declare(
1666
)
1767
FetchContent_MakeAvailable(rice)
1868

19-
# Use FindRuby.cmake from Rice repo for now while upstream is being updated to support
20-
# the new Ruby targets
69+
# -----------------------------------------------------------------------------
70+
# Configure Ruby Detection
71+
# -----------------------------------------------------------------------------
72+
# Rice provides an enhanced FindRuby.cmake that creates proper CMake targets
73+
# (Ruby::Ruby, Ruby::Module) instead of just setting variables. We prepend
74+
# Rice's module path so CMake finds this improved version.
75+
#
76+
# The upstream CMake FindRuby.cmake is being updated to support these targets,
77+
# but until that lands, we use Rice's version.
78+
2179
list(PREPEND CMAKE_MODULE_PATH "${rice_SOURCE_DIR}")
2280

23-
# Find Ruby
81+
# -----------------------------------------------------------------------------
82+
# Find Ruby Installation
83+
# -----------------------------------------------------------------------------
84+
# find_package(Ruby) locates the Ruby installation and sets up:
85+
# - Ruby::Ruby - Target for embedding Ruby (links to libruby)
86+
# - Ruby::Module - Target for extensions (links to Ruby headers only)
87+
#
88+
# For extensions, always link to Ruby::Module, not Ruby::Ruby!
89+
# Extensions are loaded into an already-running Ruby process, so they
90+
# should not link against libruby (which could cause symbol conflicts).
91+
2492
find_package(Ruby REQUIRED)
93+
2594
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
2695
Ruby::Module
2796
)
2897

29-
# Rice headers
98+
# -----------------------------------------------------------------------------
99+
# Link to Rice
100+
# -----------------------------------------------------------------------------
101+
# The rice::rice target provides:
102+
# - Include paths to Rice headers
103+
# - Required compiler flags for Rice
104+
#
105+
# Rice is header-only, so this doesn't add any link-time dependencies.
106+
30107
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
31108
rice::rice
32109
)
33110

34-
# BitmapPlusPlus.hpp headers
111+
# -----------------------------------------------------------------------------
112+
# Include Directories for Project Headers
113+
# -----------------------------------------------------------------------------
114+
# Add the current directory (ext/) to the include path so we can find
115+
# our project's header files (BitmapPlusPlus.hpp, etc.)
116+
35117
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE .)
36118

37-
# Define project properties
119+
# -----------------------------------------------------------------------------
120+
# Configure Extension Output
121+
# -----------------------------------------------------------------------------
122+
# Ruby extensions have specific naming requirements that vary by platform:
123+
#
124+
# Extension Suffix (from Ruby configuration):
125+
# - Linux: .so (e.g., bitmap_plus_plus_ruby.so)
126+
# - macOS: .bundle (e.g., bitmap_plus_plus_ruby.bundle)
127+
# - Windows: .so (e.g., bitmap_plus_plus_ruby.so)
128+
#
129+
# Note: Windows uses .so (not .dll) for Ruby extensions by convention.
130+
#
131+
# PREFIX "": Ruby extensions have no 'lib' prefix (unlike regular shared libs)
132+
#
133+
# Visibility Settings:
134+
# - CXX_VISIBILITY_PRESET hidden: Hide all symbols by default
135+
# - VISIBILITY_INLINES_HIDDEN ON: Hide inline function symbols
136+
# - WINDOWS_EXPORT_ALL_SYMBOLS OFF: Don't auto-export on Windows
137+
#
138+
# These settings ensure only the Init_* function is exported, reducing
139+
# binary size and avoiding symbol conflicts with other extensions.
140+
#
141+
# Output Directories:
142+
# - RUNTIME_OUTPUT_DIRECTORY: Where Windows puts .dll/.so files
143+
# - LIBRARY_OUTPUT_DIRECTORY: Where Unix puts .so/.bundle files
144+
#
145+
# On Windows, we place the extension in a Ruby version-specific subdirectory
146+
# (e.g., lib/3.3/) to support multiple Ruby versions simultaneously.
147+
38148
get_target_property(RUBY_EXT_SUFFIX Ruby::Ruby INTERFACE_RUBY_EXTENSION_SUFFIX)
149+
39150
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES
40151
PREFIX ""
41152
SUFFIX "${RUBY_EXT_SUFFIX}"
42153
OUTPUT_NAME "bitmap_plus_plus_ruby"
43154
CXX_VISIBILITY_PRESET hidden
44155
VISIBILITY_INLINES_HIDDEN ON
45156
WINDOWS_EXPORT_ALL_SYMBOLS OFF
46-
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/../lib/${Ruby_VERSION_MAJOR}.${Ruby_VERSION_MINOR}" # Windows
47-
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/../lib") # Not Windows
157+
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/../lib/${Ruby_VERSION_MAJOR}.${Ruby_VERSION_MINOR}"
158+
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/../lib"
159+
)
160+
161+
# -----------------------------------------------------------------------------
162+
# Source Files
163+
# -----------------------------------------------------------------------------
164+
# List all source files for the extension. The naming convention used here is:
165+
# - ProjectName-rb.hpp: Header declaring the Init function
166+
# - ProjectName-rb.cpp: Implementation with Rice bindings
167+
#
168+
# The Init_<name> function in the .cpp file is the entry point Ruby calls
169+
# when the extension is loaded via 'require'.
48170

49-
# Source code
50171
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
51-
"${CMAKE_PROJECT_NAME}-rb.hpp"
52-
"${CMAKE_PROJECT_NAME}-rb.cpp")
172+
"${CMAKE_PROJECT_NAME}-rb.hpp"
173+
"${CMAKE_PROJECT_NAME}-rb.cpp"
174+
)

0 commit comments

Comments
 (0)