FindRuby: Add support for interpreter and development components

This commit is contained in:
Charlie Savage 2025-12-20 21:53:02 -08:00
parent f27f610e49
commit 09abef62ae
3 changed files with 173 additions and 17 deletions

View File

@ -0,0 +1,4 @@
FindRuby-components
-------------------
* The :module:`FindRuby` module now provides imported targets.

View File

@ -9,12 +9,50 @@ Finds Ruby installation and the locations of its include files and libraries:
.. code-block:: cmake
find_package(Ruby [<version>] [...])
find_package(Ruby [<version>] [COMPONENTS <components>...] [...])
Ruby is a general-purpose programming language. This module supports Ruby
1.8 through 3.4. Virtual environments, such as RVM or RBENV, are also
2.0 through 4.0. Virtual environments, such as RVM or RBENV, are also
supported.
Components
^^^^^^^^^^
.. versionadded:: 4.3
This module supports the following components:
``Interpreter``
The Ruby interpreter executable.
``Development``
Headers and libraries needed to build Ruby extensions or embed Ruby.
If no components are specified, both ``Interpreter`` and ``Development``
are searched for.
Imported Targets
^^^^^^^^^^^^^^^^
.. versionadded:: 4.3
This module defines the following :prop_tgt:`IMPORTED` targets:
``Ruby::Interpreter``
Ruby interpreter. Target defined if component ``Interpreter`` is found.
``Ruby::Ruby``
Ruby library for embedding Ruby in C/C++ applications.
Target defined if component ``Development`` is found.
``Ruby::Module``
Ruby library for building Ruby extension modules.
Target defined if component ``Development`` is found.
Use this target when creating native extensions that will be
loaded into Ruby via ``require``. On most platforms, extension
modules do not link directly to libruby. Includes appropriate
symbol visibility settings.
Result Variables
^^^^^^^^^^^^^^^^
@ -427,9 +465,6 @@ if (Ruby_VERSION_MAJOR)
set(_Ruby_VERSION_SHORT_NODOT "${Ruby_VERSION_MAJOR}${Ruby_VERSION_MINOR}")
endif ()
# FIXME: Currently we require both the interpreter and development components to be found
# in order to use either. See issue #20474.
# Save CMAKE_FIND_FRAMEWORK
set(_Ruby_CMAKE_FIND_FRAMEWORK_ORIGINAL ${CMAKE_FIND_FRAMEWORK})
@ -450,11 +485,6 @@ find_library(Ruby_LIBRARY
NAMES "${_Ruby_SO_NAME}"
HINTS ${_Ruby_POSSIBLE_LIB_DIR})
set(_Ruby_REQUIRED_VARS Ruby_EXECUTABLE Ruby_INCLUDE_DIR Ruby_LIBRARY)
if (_Ruby_VERSION_SHORT_NODOT GREATER 18)
list(APPEND _Ruby_REQUIRED_VARS Ruby_CONFIG_INCLUDE_DIR)
endif ()
# Restore CMAKE_FIND_FRAMEWORK
set(CMAKE_FIND_FRAMEWORK ${_Ruby_CMAKE_FIND_FRAMEWORK_ORIGINAL})
@ -475,12 +505,111 @@ message(DEBUG "Ruby_ARCH_DIR: ${Ruby_ARCH_DIR}")
message(DEBUG "Ruby_ARCHHDR_DIR: ${Ruby_ARCHHDR_DIR}")
message(DEBUG "--------------------")
# Components
#
# If the caller does not request components, preserve legacy behavior
if (NOT Ruby_FIND_COMPONENTS)
set(Ruby_FIND_COMPONENTS Interpreter Development)
endif ()
set(_Ruby_WANT_INTERPRETER FALSE)
set(_Ruby_WANT_DEVELOPMENT FALSE)
set(_Ruby_REQUIRED_VARS "")
foreach (component IN LISTS Ruby_FIND_COMPONENTS)
if (component STREQUAL "Interpreter")
set(_Ruby_WANT_INTERPRETER TRUE)
list(APPEND _Ruby_REQUIRED_VARS Ruby_EXECUTABLE)
elseif (component STREQUAL "Development")
set(_Ruby_WANT_DEVELOPMENT TRUE)
list(APPEND _Ruby_REQUIRED_VARS Ruby_INCLUDE_DIR Ruby_CONFIG_INCLUDE_DIR)
if (WIN32)
list(APPEND _Ruby_REQUIRED_VARS Ruby_LIBRARY)
endif ()
else ()
message(FATAL_ERROR
"FindRuby: Unsupported component '${component}'. Supported components are: Interpreter, Development")
endif ()
endforeach ()
# Set component found flags
if (Ruby_EXECUTABLE)
set(Ruby_Interpreter_FOUND TRUE)
else ()
set(Ruby_Interpreter_FOUND FALSE)
endif ()
if (Ruby_INCLUDE_DIR AND Ruby_CONFIG_INCLUDE_DIR AND (Ruby_LIBRARY OR NOT WIN32))
set(Ruby_Development_FOUND TRUE)
else ()
set(Ruby_Development_FOUND FALSE)
endif ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Ruby REQUIRED_VARS ${_Ruby_REQUIRED_VARS}
VERSION_VAR Ruby_VERSION)
find_package_handle_standard_args(Ruby
REQUIRED_VARS ${_Ruby_REQUIRED_VARS}
VERSION_VAR Ruby_VERSION
HANDLE_COMPONENTS)
if (Ruby_FOUND)
set(Ruby_LIBRARIES ${Ruby_LIBRARY})
if (NOT TARGET Ruby::Interpreter)
add_executable(Ruby::Interpreter IMPORTED GLOBAL)
set_target_properties(Ruby::Interpreter PROPERTIES
IMPORTED_LOCATION "${Ruby_EXECUTABLE}"
)
endif ()
if (Ruby_Development_FOUND)
set(Ruby_LIBRARIES ${Ruby_LIBRARY})
if (Ruby_LIBRARY AND NOT TARGET Ruby::Ruby)
add_library(Ruby::Ruby UNKNOWN IMPORTED)
set_target_properties(Ruby::Ruby PROPERTIES
IMPORTED_LOCATION "${Ruby_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${Ruby_INCLUDE_DIRS}"
# Custom property for extension suffix (with dot), e.g. ".so", ".bundle"
INTERFACE_RUBY_EXTENSION_SUFFIX ".${_Ruby_DLEXT}"
)
endif ()
# Ruby::Module - For building Ruby extension modules
if (NOT TARGET Ruby::Module)
if (WIN32)
add_library(Ruby::Module UNKNOWN IMPORTED)
set_target_properties(Ruby::Module PROPERTIES
IMPORTED_LOCATION "${Ruby_LIBRARY}"
)
else ()
add_library(Ruby::Module INTERFACE IMPORTED)
endif ()
set_target_properties(Ruby::Module PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Ruby_INCLUDE_DIRS}"
# Custom property for extension suffix (with dot), e.g. ".so", ".bundle"
INTERFACE_RUBY_EXTENSION_SUFFIX ".${_Ruby_DLEXT}"
INTERFACE_C_VISIBILITY_PRESET hidden
INTERFACE_CXX_VISIBILITY_PRESET hidden
INTERFACE_VISIBILITY_INLINES_HIDDEN ON
)
# macOS: allow unresolved Ruby API symbols; resolved when Ruby loads the bundle.
if (APPLE)
target_link_options(Ruby::Module INTERFACE
"LINKER:-undefined,dynamic_lookup"
)
endif ()
# Linux (and other ELF platforms):
# Normally undefined Ruby API symbols are allowed in shared objects and resolved at dlopen().
# But if the toolchain/preset adds -Wl,--no-undefined, linking will fail.
# This counteracts that.
if (UNIX AND NOT APPLE)
target_link_options(Ruby::Module INTERFACE
"LINKER:--unresolved-symbols=ignore-all"
)
endif ()
endif ()
endif ()
endif ()
mark_as_advanced(

View File

@ -9,6 +9,14 @@ if (NOT Ruby_FOUND)
message (FATAL_ERROR "Failed to find Ruby >=1.9.9")
endif()
if (NOT Ruby_Interpreter_FOUND)
message (FATAL_ERROR "Failed to find Ruby 'Interpreter' component")
endif()
if (NOT Ruby_Development_FOUND)
message (FATAL_ERROR "Failed to find Ruby 'Development' component")
endif()
foreach(var_CMP0185
RUBY_EXECUTABLE
RUBY_INCLUDE_DIRS
@ -20,8 +28,23 @@ foreach(var_CMP0185
endif()
endforeach()
add_executable(ruby_version ruby_version.c)
target_include_directories(ruby_version PRIVATE ${Ruby_INCLUDE_DIRS})
target_link_libraries(ruby_version PRIVATE ${Ruby_LIBRARIES})
if(NOT TARGET Ruby::Interpreter)
message(SEND_ERROR "Ruby::Interpreter not found")
endif()
add_test(NAME ruby_version COMMAND ruby_version)
if (NOT TARGET Ruby::Ruby)
message(SEND_ERROR "Ruby::Ruby not found")
endif()
if (NOT TARGET Ruby::Module)
message(SEND_ERROR "Ruby::Module not found")
endif()
add_executable(ruby_version_var ruby_version.c)
target_include_directories(ruby_version_var PRIVATE ${Ruby_INCLUDE_DIRS})
target_link_libraries(ruby_version_var PRIVATE ${Ruby_LIBRARIES})
add_test(NAME ruby_version_var COMMAND ruby_version_var)
add_executable(ruby_version_tgt ruby_version.c)
target_link_libraries(ruby_version_tgt Ruby::Ruby)
add_test(NAME ruby_version_tgt COMMAND ruby_version_tgt)