CMake¶
For more complex C++ extensions, you may wish to use CMake to build your extension. CMake has a lot of advantages over extconf.rb:
- It is likely the build system the library you are wrapping uses
- Provides built-in functionality for finding installed modules (like Ruby!)
- Provides much more control over the build process
- Parallelizes builds resulting in much faster compilation times compared to
make
CMakePresets.json¶
Rice includes a CMakePresets.json file that provides pre-configured build settings for various platforms and compilers. Using presets is highly recommended as they encode the correct compiler flags for each platform.
The available presets include:
linux-debug/linux-release- Linux with GCCmacos-debug/macos-release- macOS with Clangmsvc-debug/msvc-release- Windows with MSVCclang-windows-debug/clang-windows-release- Windows with Clangmingw-debug/mingw-release- Windows with MinGW
To use a preset:
# Configure using a preset
cmake --preset macos-debug
# Build using a preset
cmake --build --preset macos-debug
You can copy this file to your own project and customize it as needed. Using presets ensures consistent builds and makes it easy to switch between debug and release configurations.
Enabling¶
To build your extension using CMake, add the following to your Gemspec (assuming the extension directory is ext):
RubyGems 4.0 and later include full CMake support, with improvements contributed by the Rice project.
If you are using an older version of RubyGems (< 4.0), Rice will automatically patch RubyGems to add improved CMake support. This is done via the rubygems_plugin.rb file which uses RubyGem's plugin system. To enable this, include Rice as a dependency in your gemspec:
Example¶
Below is an example CMakeLists.txt file to build a Ruby C++ extension:
cmake_minimum_required(VERSION 3.26)
project(MyExtension)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Ruby REQUIRED)
# Platform-specific compiler settings
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_definitions(_CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_DEPRECATE)
add_compile_options(/bigobj /utf-8)
# The default of /EHsc crashes Ruby when calling longjmp with optimizations on (/O2)
string(REGEX REPLACE "/EHsc" "/EHs" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-ftemplate-backtrace-limit=0)
endif ()
add_library(MyExtension SHARED
"MyExtension.cpp")
# Link to Ruby using the modern imported target
target_link_libraries(MyExtension PRIVATE Ruby::Module)
# Add in Rice headers
target_include_directories(MyExtension PRIVATE <path-to-rice-headers>)
# Add in the library you are wrapping (headers and libraries)
target_include_directories(MyExtension PRIVATE <path-to-library-headers>)
target_link_libraries(MyExtension PRIVATE <library-name>)
Currently you will need to manually specify where the Rice header files are located.
Note that using the Ruby::Module imported target is preferred over manually specifying include directories and libraries, as it automatically sets up all necessary include paths and compiler settings for building Ruby extensions.
Compiler Settings¶
See compiler settings for details about compiler settings.
FindRuby¶
Notice the inclusion of the following line in the above CMakeLists.txt file:
This will find a locally installed Ruby, whether it is the system Ruby or a RVM or RbENV installed Ruby.
macOS with Homebrew¶
CMake's built-in FindRuby module does not currently support finding Ruby installed via Homebrew on macOS. If you are using Homebrew-installed Ruby, you will need to use Rice's FindRuby.cmake script instead.
Rice's FindRuby.cmake includes support for:
- RVM virtual environments
- rbenv virtual environments
- Homebrew-installed Ruby (both Apple Silicon and Intel Macs)
- System Ruby
To use Rice's FindRuby script, copy it to your project and include it before calling find_package:
# Use Rice's FindRuby which supports Homebrew
include("${CMAKE_CURRENT_SOURCE_DIR}/FindRuby.cmake")
# Now find_package will use the included module
find_package(Ruby REQUIRED)
Alternatively, you can add the directory containing FindRuby.cmake to your module path:
Rice's FindRuby.cmake is also useful for older versions of CMake that lack good Ruby support.
FindRuby Imported Targets¶
Rice's FindRuby.cmake provides modern CMake imported targets:
| Target | Description |
|---|---|
Ruby::Interpreter |
Ruby interpreter executable |
Ruby::Module |
For building Ruby extension modules. Use this for gems with native extensions. On Unix, does not link to libruby (symbols resolved at load time). Includes hidden visibility settings. |
Ruby::Ruby |
For embedding Ruby in C/C++ applications. Links to libruby. |
Embedding Ruby (including Rice's C++ test runner) requires a shared Ruby library to be available.
Extension-only modules should link Ruby::Module and can build on macOS/Linux without a shared
libruby.
For building Ruby extensions, use Ruby::Module:
FindRuby Variables¶
The following CMake variables are set by FindRuby:
| Variable | Description |
|---|---|
| Ruby_VERSION_MAJOR | Ruby major version |
| Ruby_VERSION_MINOR | Ruby minor version |
| Ruby_VERSION_PATCH | Ruby patch version |
| Ruby_ARCH_DIR | Ruby arch dir |
| Ruby_HDR_DIR | Ruby header dir (1.9+) |
| Ruby_ARCHHDR_DIR | Ruby arch header dir (2.0+) |
| Ruby_RUBY_LIB_DIR | Ruby ruby-lib dir |
| Ruby_SITEARCH_DIR | Ruby site arch dir |
| Ruby_SITELIB_DIR | Ruby site lib dir |
| Ruby_EXECUTABLE | Ruby interpreter |
| Ruby_LIBRARY | Ruby shared library |
| Ruby_INCLUDE_DIR | Ruby include directory |
| Ruby_CONFIG_INCLUDE_DIR | Ruby config include directory |
| Ruby_INCLUDE_DIRS | Include and config directories |
Rice CMake Target¶
Rice provides the Rice::Rice imported target for including Rice headers:
This target:
- Adds Rice include directories
- Requires C++17 or higher
Complete Example¶
For a complete, real-world example of using CMake with Rice, see BitmapPlusPlus-ruby.
This example demonstrates:
- Fetching Rice via
FetchContent - Using
Ruby::Modulefor extension modules - Using
Rice::Ricefor Rice headers - Setting the correct extension suffix
- Configuring output directories