Skip to content

Commit

Permalink
Merge pull request puppetlabs#181 from peterhuene/rbenv
Browse files Browse the repository at this point in the history
(CFACT-122, CFACT-127) Fix ruby detection under rbenv and Fedora.
  • Loading branch information
Michael Smith committed Oct 14, 2014
2 parents 42261f4 + 80a7416 commit 3bfb8ad
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 67 deletions.
7 changes: 3 additions & 4 deletions lib/inc/facter/ruby/api.hpp
Expand Up @@ -510,10 +510,10 @@ namespace facter { namespace ruby {
bool equals(VALUE first, VALUE second) const;

/**
* Disables cleanup of the Ruby VM.
* This is only needed in forked child processes.
* Global flag to disable Ruby VM cleanup.
* This should be set to false in any forked child processes.
*/
void disable_cleanup();
static bool cleanup;

private:
explicit api(facter::util::dynamic_library&& library);
Expand All @@ -534,7 +534,6 @@ namespace facter { namespace ruby {
VALUE _nil;
VALUE _true;
VALUE _false;
bool _cleanup;
bool _initialized;
};

Expand Down
6 changes: 1 addition & 5 deletions lib/src/execution/posix/execution.cc
Expand Up @@ -12,7 +12,6 @@
using namespace std;
using namespace facter::util;
using namespace facter::util::posix;
using namespace facter::ruby;
using namespace facter::logging;
using namespace boost::filesystem;

Expand Down Expand Up @@ -176,10 +175,7 @@ namespace facter { namespace execution {
// Disable Ruby cleanup
// Ruby doesn't play nice with being forked
// The VM records the parent pid, so it doesn't like having ruby_cleanup called from a child process
auto ruby = api::instance();
if (ruby) {
ruby->disable_cleanup();
}
ruby::api::cleanup = false;

if (dup2(stdin_read, STDIN_FILENO) == -1) {
throw execution_exception("failed to redirect child stdin.");
Expand Down
11 changes: 4 additions & 7 deletions lib/src/ruby/api.cc
Expand Up @@ -25,6 +25,9 @@ namespace facter { namespace ruby {
#define LOAD_ALIASED_SYMBOL(x, y) x(reinterpret_cast<decltype(x)>(library.find_symbol(#x, true, #y)))
#define LOAD_OPTIONAL_SYMBOL(x) x(reinterpret_cast<decltype(x)>(library.find_symbol(#x)))

// Default to cleaning up the VM on shutdown
bool api::cleanup = true;

api::api(dynamic_library&& library) :
LOAD_SYMBOL(rb_intern),
LOAD_SYMBOL(rb_const_get),
Expand Down Expand Up @@ -93,14 +96,13 @@ namespace facter { namespace ruby {
LOAD_SYMBOL(ruby_options),
LOAD_SYMBOL(ruby_cleanup),
_library(move(library)),
_cleanup(_library.first_load()),
_initialized(false)
{
}

api::~api()
{
if (_initialized && _cleanup) {
if (_initialized && _library.first_load() && cleanup) {
ruby_cleanup(0);
}
}
Expand Down Expand Up @@ -391,9 +393,4 @@ namespace facter { namespace ruby {
return is_true(rb_funcall(first, rb_intern("eql?"), 1, second));
}

void api::disable_cleanup()
{
_cleanup = false;
}

}} // namespace facter::ruby
107 changes: 56 additions & 51 deletions lib/src/ruby/posix/api.cc
Expand Up @@ -37,25 +37,40 @@ namespace facter { namespace ruby {
}
}

// Next search for where ruby is installed
string ruby = execution::which("ruby");
// First try supporting rbenv
string ruby;
auto result = execution::execute("rbenv", { "which", "ruby" });
if (result.first) {
ruby = result.second;
}

if (ruby.empty()) {
LOG_DEBUG("ruby could not be found on the PATH.");
return library;
// Next search the PATH
ruby = execution::which("ruby");
if (ruby.empty()) {
LOG_DEBUG("ruby could not be found on the PATH.");
return library;
}
}

LOG_DEBUG("ruby was found at \"%1%\"; searching for libruby.", ruby);
LOG_DEBUG("ruby was found at \"%1%\".", ruby);

// Usually a ruby can be found at "../lib" from where the binary is
path parent_ruby_dir = path(ruby).remove_filename() / "..";
vector<path> search_paths;
search_paths.push_back(parent_ruby_dir / "lib");
path parent_path = path(ruby).remove_filename() / "..";
path lib_path = parent_path / "lib64";

// To support rbenv, look for installed versions
directory::each_subdirectory((parent_ruby_dir / "versions").string(), [&](string const& dir) {
search_paths.push_back(path(dir) / "lib");
return true;
});
boost::system::error_code ec;
path search_path = canonical(lib_path, ec);
if (ec) {
LOG_DEBUG("ruby library was not found at %1%: %2%.", lib_path, ec.message());
lib_path = parent_path / "lib";
search_path = canonical(lib_path, ec);
if (ec) {
LOG_DEBUG("ruby library was not found at %1%: %2%.", lib_path, ec.message());
return library;
}
}

LOG_DEBUG("searching %1% for ruby libraries.", search_path);

int major = 0, minor = 0, patch = 0;
string libruby;
Expand All @@ -68,45 +83,35 @@ namespace facter { namespace ruby {
re_adapter regex("libruby(?:-.*)?\\.so(?:\\.(\\d+))?(?:\\.(\\d+))?(?:\\.(\\d+))?$");
#endif

for (auto const& search_path : search_paths) {
boost::system::error_code ec;
path dir = canonical(search_path, ec);
if (ec) {
LOG_DEBUG("ruby library was not found at %1%: %2%.", search_path, ec.message());
continue;
directory::each_file(search_path.string(), [&](string const &file) {
// Ignore symlinks
if (is_symlink(file, ec)) {
return true;
}
LOG_DEBUG("searching %1% for ruby libraries.", dir);

directory::each_file(dir.string(), [&](string const &file) {
// Ignore symlinks
if (is_symlink(file, ec)) {
return true;
}

// Extract the version from the file name
int current_major = 0, current_minor = 0, current_patch = 0;
if (!re_search(file, regex, &current_major, &current_minor, &current_patch)) {
return true;
}

if (current_major == 1 && current_minor == 8) {
LOG_DEBUG("ruby library at \"%1%\" will be skipped: ruby 1.8 is not supported.", file);
return true;
}

// Check to see if the given version is greater than or equal to the current version
// This is done so that if all strings are empty (i.e. we've found only libruby.so),
// we set libruby to the file that was found.
if (tie(current_major, current_minor, current_patch) >= tie(major, minor, patch)) {
tie(major, minor, patch) = tie(current_major, current_minor, current_patch);
libruby = file;
LOG_DEBUG("found candidate ruby library \"%1%\".", file);
} else {
LOG_DEBUG("ruby library \"%1%\" has a higher version number than \"%2%\".", libruby, file);
}

// Extract the version from the file name
int current_major = 0, current_minor = 0, current_patch = 0;
if (!re_search(file, regex, &current_major, &current_minor, &current_patch)) {
return true;
}, "libruby.*\\.(?:so|dylib)");
}
}

if (current_major == 1 && current_minor == 8) {
LOG_DEBUG("ruby library at \"%1%\" will be skipped: ruby 1.8 is not supported.", file);
return true;
}

// Check to see if the given version is greater than or equal to the current version
// This is done so that if all strings are empty (i.e. we've found only libruby.so),
// we set libruby to the file that was found.
if (tie(current_major, current_minor, current_patch) >= tie(major, minor, patch)) {
tie(major, minor, patch) = tie(current_major, current_minor, current_patch);
libruby = file;
LOG_DEBUG("found candidate ruby library \"%1%\".", file);
} else {
LOG_DEBUG("ruby library \"%1%\" has a higher version number than \"%2%\".", libruby, file);
}
return true;
}, "libruby.*\\.(?:so|dylib)");

// If we found a ruby, attempt to load it
if (!libruby.empty()) {
Expand Down

0 comments on commit 3bfb8ad

Please sign in to comment.