ソースを参照

FindRuby: Add support for interpreter and development components

Charlie Savage 2 ヶ月 前
コミット
c5e7cb691c

+ 4 - 0
Help/release/dev/FindRuby-components.rst

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

+ 142 - 13
Modules/FindRuby.cmake

@@ -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(

+ 29 - 0
Tests/RunCMake/FindRuby/System/Ruby.cmake

@@ -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
@@ -19,3 +27,24 @@ foreach(var_CMP0185
     message(FATAL_ERROR "Pre-CMP0185 result variable is set: ${var_CMP0185}")
   endif()
 endforeach()
+
+if(NOT TARGET Ruby::Interpreter)
+  message(SEND_ERROR "Ruby::Interpreter not found")
+endif()
+
+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 "${CMAKE_CURRENT_LIST_DIR}/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 "${CMAKE_CURRENT_LIST_DIR}/ruby_version.c")
+target_link_libraries(ruby_version_tgt Ruby::Ruby)
+add_test(NAME ruby_version_tgt COMMAND ruby_version_tgt)

+ 7 - 0
Tests/RunCMake/FindRuby/System/ruby_version.c

@@ -0,0 +1,7 @@
+#include "ruby.h"
+
+int main(void)
+{
+  ruby_show_version();
+  return 0;
+}