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
:
Is 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
Enabling¶
To build your extension using CMake
, add the following to your Gemspec (assuming the extension directory is ext
):
Gem::Specification.new do |spec|
spec.extensions = ["ext/CMakeLists.txt"]
end
Unfortunately, RubyGems
support for CMake is fairly poor. Therefore, Rice includes an improved RubyGem CMakeBuilder. This updated code has been submitted upstream.
Rice uses RubyGem’s plugin system to install the updated CMakeBuilder
code. To use the improved builder include Rice
in your gemspec like this:
Gem::Specification.new do |spec|
spec.add_runtime_dependency('rice')
end
Example¶
Below is an example CMakeLists.txt
file to build a Ruby C++ extension:
cmake_minimum_required (VERSION 3.26)
project(MyExtensionLibrary)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package("Ruby")
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
add_compile_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_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 "Clang")
add_compile_definitions(-D_CRT_SECURE_NO_WARNINGS)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-ftemplate-backtrace-limit=0)
# https://github.com/doxygen/doxygen/issues/9269#issuecomment-1094975328
add_compile_options(unittest PRIVATE -Wa,-mbig-obj)
endif ()
add_library (MyExtension SHARED
"MyExtension.cpp")
target_include_directories(MyExtensionLibrary PRIVATE ${Ruby_INCLUDE_DIR} ${Ruby_CONFIG_INCLUDE_DIR})
target_include_directories(MyExtensionLibrary PRIVATE ${PROJECT_SOURCE_DIR})
target_link_libraries(MyExtensionLibrary ${Ruby_LIBRARY})
# Add in Rice headers
target_include_directories(MyExtensionLibrary PRIVATE <path>)
# Add in the library you are wrapping headers and libraries
target_include_directories(MyExtensionLibrary PRIVATE <path>)
target_link_libraries(MyExtensionLibrary <path>})
Currently you will need to manually specify where the Rice Header Files header files are located.
Compiler Settings¶
See Compiler Settings for details about compiler settings.
FindRuby¶
Notice the inclusion of the following line in the above CMakeLists.txt
file:
find_package("Ruby")
This will find a locally installed Ruby, whether it is the system Ruby or a RVM or RbENV installed Ruby.
If you are using an older version of CMake, you can use the FindRuby.cmake) script included in Rice. In that case, the syntax would be:
include("./FindRuby.cmake")
This sets the following CMake CACHE variables:
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 |