From e6699b9b59a12f6fda8f535d370453bb41f3acce Mon Sep 17 00:00:00 2001
From: Julien Marrec <julien.marrec@gmail.com>
Date: Wed, 11 Mar 2020 18:37:19 +0100
Subject: FindRuby: Validate Ruby_EXECUTABLE before accepting it

---
 Modules/FindRuby.cmake        | 40 ++++++++++++++++++++++++++++++++++++++++
 Tests/FindRuby/CMakeLists.txt |  2 +-
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/Modules/FindRuby.cmake b/Modules/FindRuby.cmake
index 1e010bf..699d2b6 100644
--- a/Modules/FindRuby.cmake
+++ b/Modules/FindRuby.cmake
@@ -125,10 +125,50 @@ if(_Ruby_DEBUG_OUTPUT)
   message("_Ruby_POSSIBLE_EXECUTABLE_NAMES=${_Ruby_POSSIBLE_EXECUTABLE_NAMES}")
 endif()
 
+function (_RUBY_VALIDATE_INTERPRETER)
+  if (NOT Ruby_EXECUTABLE)
+    return()
+  endif()
+
+  cmake_parse_arguments (PARSE_ARGV 0 _RVI "EXACT;CHECK_EXISTS" "" "")
+  if (_RVI_UNPARSED_ARGUMENTS)
+    set (expected_version ${_RVI_UNPARSED_ARGUMENTS})
+  else()
+    unset (expected_version)
+  endif()
+
+  if (_RVI_CHECK_EXISTS AND NOT EXISTS "${Ruby_EXECUTABLE}")
+    # interpreter does not exist anymore
+    set (_Ruby_Interpreter_REASON_FAILURE "Cannot find the interpreter \"${Ruby_EXECUTABLE}\"")
+    set_property (CACHE Ruby_EXECUTABLE PROPERTY VALUE "Ruby_EXECUTABLE-NOTFOUND")
+    return()
+  endif()
+
+  # Check the version it returns
+  # executable found must have a specific version
+  execute_process (COMMAND "${Ruby_EXECUTABLE}" -e "puts RUBY_VERSION"
+                   RESULT_VARIABLE result
+                   OUTPUT_VARIABLE version
+                   ERROR_QUIET
+                   OUTPUT_STRIP_TRAILING_WHITESPACE)
+  if (result OR (_RVI_EXACT AND NOT version VERSION_EQUAL expected_version) OR (version VERSION_LESS expected_version))
+    # interpreter not usable or has wrong major version
+    if (result)
+      set (_Ruby_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${Ruby_EXECUTABLE}\"")
+    else()
+      set (_Ruby_Interpreter_REASON_FAILURE "Wrong major version for the interpreter \"${Ruby_EXECUTABLE}\"")
+    endif()
+    set_property (CACHE Ruby_EXECUTABLE PROPERTY VALUE "Ruby_EXECUTABLE-NOTFOUND")
+    return()
+  endif()
+
+endfunction()
+
 find_program (Ruby_EXECUTABLE
   NAMES ${_Ruby_POSSIBLE_EXECUTABLE_NAMES}
   NAMES_PER_DIR
   )
+_RUBY_VALIDATE_INTERPRETER (${Ruby_FIND_VERSION})
 
 if(Ruby_EXECUTABLE AND NOT Ruby_VERSION_MAJOR)
   function(_RUBY_CONFIG_VAR RBVAR OUTVAR)
diff --git a/Tests/FindRuby/CMakeLists.txt b/Tests/FindRuby/CMakeLists.txt
index 193cb4f..aaab839 100644
--- a/Tests/FindRuby/CMakeLists.txt
+++ b/Tests/FindRuby/CMakeLists.txt
@@ -24,7 +24,7 @@ if(CMake_TEST_FindRuby)
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
   set_tests_properties(FindRuby.Fail PROPERTIES
-    PASS_REGULAR_EXPRESSION "Could NOT find Ruby: Found unsuitable version \".*\", but required is.*least \"[0-9]+\\.[0-9]+\\.[0-9]+\" \\(found .*\\)")
+    PASS_REGULAR_EXPRESSION "Could NOT find Ruby.*(Required is at least version \"[0-9]+\\.[0-9]+\\.[0-9]+\")")
 
   # Looks for 1.9.9 EXACTLY, which unlike the "FindRuby" test above will fail on every machine
   # since this version doesn't exist (ruby goes from 1.9.3 to 2.0.0)
-- 
cgit v0.12


From 905d5667e8b49a931078a1bcea26deffe703453b Mon Sep 17 00:00:00 2001
From: Julien Marrec <julien.marrec@gmail.com>
Date: Mon, 23 Mar 2020 13:16:39 +0100
Subject: FindRuby: Add support for RVM installations

---
 Modules/FindRuby.cmake               | 98 ++++++++++++++++++++++++++++++++++--
 Tests/FindRuby/CMakeLists.txt        | 13 +++++
 Tests/FindRuby/Rvm/CMakeLists.txt    | 75 +++++++++++++++++++++++++++
 Tests/FindRuby/Rvm/RvmDefault.cmake  | 17 +++++++
 Tests/FindRuby/Rvm/RvmOnly.cmake     | 41 +++++++++++++++
 Tests/FindRuby/Rvm/RvmStandard.cmake |  9 ++++
 6 files changed, 248 insertions(+), 5 deletions(-)
 create mode 100644 Tests/FindRuby/Rvm/CMakeLists.txt
 create mode 100644 Tests/FindRuby/Rvm/RvmDefault.cmake
 create mode 100644 Tests/FindRuby/Rvm/RvmOnly.cmake
 create mode 100644 Tests/FindRuby/Rvm/RvmStandard.cmake

diff --git a/Modules/FindRuby.cmake b/Modules/FindRuby.cmake
index 699d2b6..1bdee60 100644
--- a/Modules/FindRuby.cmake
+++ b/Modules/FindRuby.cmake
@@ -22,6 +22,9 @@ standard syntax, e.g.
 
 It also determines what the name of the library is.
 
+Virtual environments such as RVM are handled as well, by passing
+the argument ``Ruby_FIND_VIRTUALENV``
+
 Result Variables
 ^^^^^^^^^^^^^^^^
 
@@ -49,6 +52,28 @@ Also:
 
 ``Ruby_INCLUDE_PATH``
   same as Ruby_INCLUDE_DIRS, only provided for compatibility reasons, don't use it
+
+Hints
+^^^^^
+
+``Ruby_ROOT_DIR``
+  Define the root directory of a Ruby installation.
+
+``Ruby_FIND_VIRTUALENV``
+  This variable defines the handling of virtual environments managed by
+  ``rvm``. It is meaningful only when a virtual environment
+  is active (i.e. the ``rvm`` script has been evaluated or at least the
+  ``MY_RUBY_HOME`` environment variable is set).
+  The ``Ruby_FIND_VIRTUALENV`` variable can be set to empty or
+  one of the following:
+
+  * ``FIRST``: The virtual environment is used before any other standard
+    paths to look-up for the interpreter. This is the default.
+  * ``ONLY``: Only the virtual environment is used to look-up for the
+    interpreter.
+  * ``STANDARD``: The virtual environment is not used to look-up for the
+    interpreter (assuming it isn't still in the PATH...)
+
 #]=======================================================================]
 
 # Backwards compatibility
@@ -121,8 +146,32 @@ if(NOT Ruby_FIND_VERSION_EXACT)
   list(REMOVE_DUPLICATES _Ruby_POSSIBLE_EXECUTABLE_NAMES)
 endif()
 
+# virtual environments handling (eg RVM)
+if (DEFINED ENV{MY_RUBY_HOME})
+  if(_Ruby_DEBUG_OUTPUT)
+    message("My ruby home is defined: $ENV{MY_RUBY_HOME}")
+  endif()
+
+  if (DEFINED Ruby_FIND_VIRTUALENV)
+    if (NOT Ruby_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY|STANDARD)$")
+      message (AUTHOR_WARNING "FindRuby: ${Ruby_FIND_VIRTUALENV}: invalid value for 'Ruby_FIND_VIRTUALENV'. 'FIRST', 'ONLY' or 'STANDARD' expected. 'FIRST' will be used instead.")
+      set (_Ruby_FIND_VIRTUALENV "FIRST")
+    else()
+      set (_Ruby_FIND_VIRTUALENV ${Ruby_FIND_VIRTUALENV})
+    endif()
+  else()
+    set (_Ruby_FIND_VIRTUALENV FIRST)
+  endif()
+else()
+  if (DEFINED Ruby_FIND_VIRTUALENV)
+    message("Environment variable MY_RUBY_HOME isn't set, defaulting back to Ruby_FIND_VIRTUALENV=STANDARD")
+  endif()
+  set (_Ruby_FIND_VIRTUALENV STANDARD)
+endif()
+
 if(_Ruby_DEBUG_OUTPUT)
   message("_Ruby_POSSIBLE_EXECUTABLE_NAMES=${_Ruby_POSSIBLE_EXECUTABLE_NAMES}")
+  message("_Ruby_FIND_VIRTUALENV=${_Ruby_FIND_VIRTUALENV}")
 endif()
 
 function (_RUBY_VALIDATE_INTERPRETER)
@@ -164,11 +213,48 @@ function (_RUBY_VALIDATE_INTERPRETER)
 
 endfunction()
 
-find_program (Ruby_EXECUTABLE
-  NAMES ${_Ruby_POSSIBLE_EXECUTABLE_NAMES}
-  NAMES_PER_DIR
-  )
-_RUBY_VALIDATE_INTERPRETER (${Ruby_FIND_VERSION})
+while(1)
+  # Virtual environments handling
+  if(_Ruby_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
+    if(_Ruby_DEBUG_OUTPUT)
+      message("Inside Matches")
+    endif()
+    find_program (Ruby_EXECUTABLE
+                  NAMES ${_Ruby_POSSIBLE_EXECUTABLE_NAMES}
+                  NAMES_PER_DIR
+                  PATHS ENV MY_RUBY_HOME
+                  PATH_SUFFIXES bin Scripts
+                  NO_CMAKE_PATH
+                  NO_CMAKE_ENVIRONMENT_PATH
+                  NO_SYSTEM_ENVIRONMENT_PATH
+                  NO_CMAKE_SYSTEM_PATH)
+
+    if(_Ruby_DEBUG_OUTPUT)
+      message("Ruby_EXECUTABLE=${Ruby_EXECUTABLE}")
+    endif()
+
+    _RUBY_VALIDATE_INTERPRETER (${Ruby_FIND_VERSION}})
+    if(Ruby_EXECUTABLE)
+      break()
+    endif()
+    if(NOT _Ruby_FIND_VIRTUALENV STREQUAL "ONLY")
+      break()
+    endif()
+  elseif(_Ruby_DEBUG_OUTPUT)
+    message("_Ruby_FIND_VIRTUALENV doesn't match: ${_Ruby_FIND_VIRTUALENV}")
+  endif()
+
+  # try using standard paths
+  find_program (Ruby_EXECUTABLE
+                NAMES ${_Ruby_POSSIBLE_EXECUTABLE_NAMES}
+                NAMES_PER_DIR)
+  _RUBY_VALIDATE_INTERPRETER (${Ruby_FIND_VERSION})
+  if (Ruby_EXECUTABLE)
+    break()
+  endif()
+
+  break()
+endwhile()
 
 if(Ruby_EXECUTABLE AND NOT Ruby_VERSION_MAJOR)
   function(_RUBY_CONFIG_VAR RBVAR OUTVAR)
@@ -306,6 +392,8 @@ if(Ruby_VERSION_MAJOR)
   set(_Ruby_NODOT_VERSION "${Ruby_VERSION_MAJOR}${Ruby_VERSION_MINOR}${Ruby_VERSION_PATCH}")
 endif()
 
+# FIXME: Currently we require both the interpreter and development components to be found
+# in order to use either.  See issue #20474.
 find_path(Ruby_INCLUDE_DIR
   NAMES ruby.h
   HINTS
diff --git a/Tests/FindRuby/CMakeLists.txt b/Tests/FindRuby/CMakeLists.txt
index aaab839..3f4807c 100644
--- a/Tests/FindRuby/CMakeLists.txt
+++ b/Tests/FindRuby/CMakeLists.txt
@@ -41,4 +41,17 @@ if(CMake_TEST_FindRuby)
   set_tests_properties(FindRuby.FailExact PROPERTIES
     PASS_REGULAR_EXPRESSION "Could NOT find Ruby: Found unsuitable version \".*\", but required is.*exact version \"[0-9]+\\.[0-9]+\\.[0-9]+\" \\(found .*\\)")
 
+  # RVM specific test
+  if(CMake_TEST_FindRuby_RVM)
+    add_test(NAME FindRuby.Rvm COMMAND
+      ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/FindRuby/Rvm"
+      "${CMake_BINARY_DIR}/Tests/FindRuby/Rvm"
+      ${build_generator_args}
+      --build-project TestRVM
+      --build-options ${build_options}
+      --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+      )
+  endif()
 endif()
diff --git a/Tests/FindRuby/Rvm/CMakeLists.txt b/Tests/FindRuby/Rvm/CMakeLists.txt
new file mode 100644
index 0000000..545fc94
--- /dev/null
+++ b/Tests/FindRuby/Rvm/CMakeLists.txt
@@ -0,0 +1,75 @@
+cmake_minimum_required(VERSION 3.17)
+project(TestRVM LANGUAGES NONE)
+
+include(CTest)
+
+# To run this test, you need to have at least one RVM ruby installed
+# and to ensure that the env variable 'MY_RUBY_HOME' is set to a valid RVM ruby when you run the test
+# (which is the case if you have done `rvm use x.y.z`, but could be manually set too)
+
+# Properly using rvm would require sourcing a shell script, eg `source "$HOME/.rvm/scripts/rvm"`
+# Instead, I'll just rely on the env variable MY_RUBY_HOME
+set(MY_RUBY_HOME "$ENV{MY_RUBY_HOME}")
+if(NOT MY_RUBY_HOME)
+  message(FATAL_ERROR "Env variable MY_RUBY_HOME should be set to a valid RVM ruby location, or you should call `rvm use x.y.z` before")
+endif()
+execute_process (COMMAND "${MY_RUBY_HOME}/bin/ruby" -e "puts RUBY_VERSION"
+                 RESULT_VARIABLE result
+                 OUTPUT_VARIABLE RVM_RUBY_VERSION
+                 ERROR_QUIET
+                 OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if (result)
+  message (FATAL_ERROR "Unable to detect RVM ruby version from `${MY_RUBY_HOME}/bin/ruby`: ${RVM_RUBY_VERSION}")
+endif()
+
+execute_process(COMMAND "${CMAKE_COMMAND}" -E env --unset=MY_RUBY_HOME --unset=PATH
+                        "which" "ruby"
+                 RESULT_VARIABLE result
+                 OUTPUT_VARIABLE SYSTEM_RUBY
+                 ERROR_QUIET
+                 OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if (SYSTEM_RUBY MATCHES "^${MY_RUBY_HOME}/.+")
+  message(FATAL_ERROR "Unable to find system ruby, found ${SYSTEM_RUBY} which is part of MY_RUBY_HOME=${MY_RUBY_HOME}")
+endif()
+
+# Check version of the system ruby executable.
+execute_process (COMMAND "${SYSTEM_RUBY}" -e "puts RUBY_VERSION"
+                 RESULT_VARIABLE result
+                 OUTPUT_VARIABLE SYSTEM_RUBY_VERSION
+                 ERROR_QUIET
+                 OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if (result)
+  message (FATAL_ERROR "Unable to detect system ruby version from '${SYSTEM_RUBY}': ${SYSTEM_RUBY_VERSION}")
+endif()
+
+if(SYSTEM_RUBY_VERSION VERSION_EQUAL RVM_RUBY_VERSION)
+  message(FATAL_ERROR "Your RVM Ruby Version and your System ruby version are the same (${RVM_RUBY_VERSION}).")
+endif()
+
+message("Found System Ruby (${SYSTEM_RUBY_VERSION}): ${SYSTEM_RUBY}")
+message("Found  RVM   Ruby (${RVM_RUBY_VERSION}): ${MY_RUBY_HOME}/bin/ruby")
+
+add_test(NAME FindRuby.RvmDefault
+         COMMAND "${CMAKE_COMMAND}" -E env "MY_RUBY_HOME=${MY_RUBY_HOME}"
+                 "${CMAKE_COMMAND}" "-DRUBY_HOME=${MY_RUBY_HOME}"
+                 -P "${CMAKE_CURRENT_LIST_DIR}/RvmDefault.cmake")
+
+add_test(NAME FindRuby.RvmOnly
+  COMMAND "${CMAKE_COMMAND}" -E env --unset=PATH
+                                    "MY_RUBY_HOME=${MY_RUBY_HOME}"
+          "${CMAKE_COMMAND}" "-DRUBY_HOME=${MY_RUBY_HOME}"
+                 "-DRVM_RUBY_VERSION=${RVM_RUBY_VERSION}" "-DSYSTEM_RUBY_VERSION=${SYSTEM_RUBY_VERSION}"
+                 -P "${CMAKE_CURRENT_LIST_DIR}/RvmOnly.cmake")
+add_test(NAME FindRuby.UnsetRvmOnly
+         COMMAND "${CMAKE_COMMAND}" -E env --unset=MY_RUBY_HOME "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
+                 "${CMAKE_COMMAND}" "-DRVM_RUBY_VERSION=${RVM_RUBY_VERSION}" "-DSYSTEM_RUBY_VERSION=${SYSTEM_RUBY_VERSION}"
+                 -P "${CMAKE_CURRENT_LIST_DIR}/RvmOnly.cmake")
+
+add_test(NAME FindRuby.RvmStandard
+         COMMAND "${CMAKE_COMMAND}" -E env "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
+                                           "MY_RUBY_HOME=${MY_RUBY_HOME}"
+                 "${CMAKE_COMMAND}" "-DRUBY_HOME=${MY_RUBY_HOME}"
+                 -P "${CMAKE_CURRENT_LIST_DIR}/RvmStandard.cmake")
diff --git a/Tests/FindRuby/Rvm/RvmDefault.cmake b/Tests/FindRuby/Rvm/RvmDefault.cmake
new file mode 100644
index 0000000..a66b911
--- /dev/null
+++ b/Tests/FindRuby/Rvm/RvmDefault.cmake
@@ -0,0 +1,17 @@
+set(CMAKE_FIND_LIBRARY_PREFIXES "")
+set(CMAKE_FIND_LIBRARY_SUFFIXES "")
+
+find_package (Ruby 2.1.1 REQUIRED)
+if (NOT RUBY_EXECUTABLE MATCHES "^${RUBY_HOME}/.+")
+  message (FATAL_ERROR "Failed to use RVM environment: ${RUBY_EXECUTABLE}, ${RUBY_HOME}")
+endif()
+
+find_package (Ruby 2.1 REQUIRED)
+if (NOT RUBY_EXECUTABLE MATCHES "^${RUBY_HOME}/.+")
+  message (FATAL_ERROR "Failed to use RVM environment: ${RUBY_EXECUTABLE}, ${RUBY_HOME}")
+endif()
+
+find_package (Ruby REQUIRED)
+if (NOT RUBY_EXECUTABLE MATCHES "^${RUBY_HOME}/.+")
+  message (FATAL_ERROR "Failed to use RVM environment: ${RUBY_EXECUTABLE}, ${RUBY_HOME}")
+endif()
diff --git a/Tests/FindRuby/Rvm/RvmOnly.cmake b/Tests/FindRuby/Rvm/RvmOnly.cmake
new file mode 100644
index 0000000..3851a7c
--- /dev/null
+++ b/Tests/FindRuby/Rvm/RvmOnly.cmake
@@ -0,0 +1,41 @@
+set(CMAKE_FIND_LIBRARY_PREFIXES "")
+set(CMAKE_FIND_LIBRARY_SUFFIXES "")
+
+set(Ruby_FIND_VIRTUALENV ONLY)
+
+# Test: FindRuby.RvmOnly
+if (RUBY_HOME)
+  # => Trying to find exactly system ruby using ONLY virtual environment should fail
+  find_package (Ruby ${SYSTEM_RUBY_VERSION} EXACT QUIET)
+  if(Ruby_FOUND)
+    message (FATAL_ERROR "Ruby unexpectedly found.")
+  endif()
+  # And should work to find the rvm version
+  find_package (Ruby ${RVM_RUBY_VERSION} EXACT QUIET)
+  if(Ruby_FOUND)
+    message (FATAL_ERROR "Ruby unexpectedly found.")
+  endif()
+endif()
+
+
+# Test: FindRuby.UnsetRvmOnly
+if (NOT RUBY_HOME)
+
+  # If ENV{MY_RUBY_HOME} isn't defined, it should default back to "STANDARD"
+  # At which point:
+
+  # It shouldn't find the RVM ruby
+  find_package (Ruby ${RVM_RUBY_VERSION} EXACT QUIET)
+  if(Ruby_FOUND)
+    message(FATAL_ERROR "Found RVM ruby when expecting system")
+  endif()
+
+  # it should find the system ruby
+  find_package (Ruby ${SYSTEM_RUBY_VERSION} EXACT QUIET)
+  if(NOT Ruby_FOUND)
+    message (FATAL_ERROR "Ruby not found.")
+  endif()
+  if (Ruby_FOUND MATCHES "^${RUBY_HOME}/.+")
+    message(FATAL_ERROR "Failed to find system ruby")
+  endif()
+endif()
diff --git a/Tests/FindRuby/Rvm/RvmStandard.cmake b/Tests/FindRuby/Rvm/RvmStandard.cmake
new file mode 100644
index 0000000..26befdb7
--- /dev/null
+++ b/Tests/FindRuby/Rvm/RvmStandard.cmake
@@ -0,0 +1,9 @@
+set(CMAKE_FIND_LIBRARY_PREFIXES "")
+set(CMAKE_FIND_LIBRARY_SUFFIXES "")
+
+set (Ruby_FIND_VIRTUALENV STANDARD)
+find_package (Ruby REQUIRED)
+
+if (RUBY_EXECUTABLE MATCHES "^${RUBY_HOME}/.+")
+  message (FATAL_ERROR "RVM ruby unexpectedly found at ${RUBY_EXECUTABLE}, matches ${RUBY_HOME}")
+endif()
-- 
cgit v0.12