TL;DR
An unintended behavior when using ruby-install
when both MacPorts and Homebrew are installed, a.k.a. “three hours lost in my life”.
Installation failure
It has been a few weeks since I last wrote some Ruby code, and my Ruby (installed with ruby-install
) doesn’t work, because MacPorts were updated. This is pretty expected, and I went ahead to reinstall the version of Ruby. When I run ruby-install
for that, I hit an compilation error:
compiling compile.c
In file included from compile.c:40:
./vm_callinfo.h:175:9: error: use of undeclared identifier 'RUBY_FUNCTION_NAME_STRING'
rp(ci);
^
./internal.h:94:72: note: expanded from macro 'rp'
#define rp(obj) rb_obj_info_dump_loc((VALUE)(obj), __FILE__, __LINE__, RUBY_FUNCTION_NAME_STRING)
In file included from compile.c:40:
./vm_callinfo.h:216:16: error: use of undeclared identifier 'RUBY_FUNCTION_NAME_STRING'
if (debug) rp(ci);
^
./internal.h:94:72: note: expanded from macro 'rp'
#define rp(obj) rb_obj_info_dump_loc((VALUE)(obj), __FILE__, __LINE__, RUBY_FUNCTION_NAME_STRING)
^
2 errors generated.
make: *** [compile.o] Error 1
!!! Compiling ruby 3.1.2 failed!
My quick reaction was to Google this error message. Everyone said that their issue were fixed by reinstall the Xcode CLT (Command Line Tool). Tried it, didn’t help; so I had to actually dig into the issue.
Understanding the Ruby configuration
From the affected C code, RUBY_FUNCTION_NAME_STRING
looks like a macro. I searched the source code for the macro, and found that it was defined here:
AC_CACHE_CHECK(for function name string predefined identifier,
rb_cv_function_name_string,
[AS_CASE(["$target_os"],[openbsd*],[
rb_cv_function_name_string=__func__
],[
rb_cv_function_name_string=no
RUBY_WERROR_FLAG([
for func in __func__ __FUNCTION__; do
AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <stdio.h>]],
[[puts($func);]])],
[rb_cv_function_name_string=$func
break])
done
])])]
)
AS_IF([test "$rb_cv_function_name_string" != no], [
AC_DEFINE_UNQUOTED(RUBY_FUNCTION_NAME_STRING, [$rb_cv_function_name_string])
])
This file (configure.ac
) is a GNU Autoconf script. This section of the script sets the value of the macro:
- If compiling for OpenBSD, set it to
__func__
. - Otherwise, check whether the compiler supports the magic
__func__
, by compiling a minimal C program with it. If it does, set the macro to__func__
. - Otherwise, do the same with
__FUNCTION__
. - If none of the above works, then leave the macro unset.
Indeed the output of ruby-install
(actually ./configure
) contains
checking for function name string predefined identifier... no
So it did fall into the last branch, leaving the macro unset. I tried compiling the same piece of C code; my compiler do support __func__
. My next thoughts were: anyway for me to force it skipping the detection and use __func__
anyway?
./configure --help
gives the answer: it supports --enable-FLAG=VALUE
to override any feature detection. So I ran
./configure --enable-rb_cv_function_name_string=__func__
And indeed that line of log became
checking for function name string predefined identifier...
__func__
In addition, subsequent make compiler.o
succeeded without complaint. So the workaround sounds simple: just pass this flag as a “configure option” to ruby-install
, like
ruby-install ruby-3.1.2 -- --enable-rb_cv_function_name_string=__func__
right?
Finding the next (real) broken thing
Nope, it didn’t work; the feature was set back to “no”. After scratching my head and reading the source code of ruby-install
, I noticed that it supports a flag: -D
/--debug
, which prints the command it runs. I ran ruby-install
again with that flag, and it printed
[DEBUG] ./configure --prefix=/Users/franklinyu/.rubies/ruby-3.1.2 --with-opt-dir=/Users/franklinyu/.local/opt/openssl@1.1:/Users/franklinyu/.local/opt/readline:/Users/franklinyu/.local/opt/libyaml:/Users/franklinyu/.local/opt/gdbm --enable-rb_cv_function_name_string=__func__
So it did have the --enable-xxx
flags that I wanted; the --prefix
looks legit. But the --with-opt-dir
looks suspicious, because those paths didn’t actually exist on my system. I tried running this ./configure
command, and I could reproduce the problem of feature set to “no”. I tried running with only the --prefix
flag, and the feature was set to __func__
even without my --enable-xxx
hack. So the root cause was actually the --with-opt-dir
. Why was it set?
Reading the source code again, the relevant part is this:
case "$package_manager" in
brew)
opt_dir="$(brew --prefix readline):$(brew --prefix libyaml):$(brew --prefix gdbm)"
openssl_dir="$(brew --prefix openssl@1.1)"
;;
port)
opt_dir="/opt/local"
;;
esac
run ./configure --prefix="$install_dir" \
"${opt_dir:+--with-opt-dir="$opt_dir"}" \
"${openssl_dir:+--with-openssl-dir="$openssl_dir"}" \
"${configure_opts[@]}" || return $?
I didn’t set --package_manager
for ruby-install
because I did set --no-install-deps
, and I thought it is sufficient. The problem was finally resolved by replacing --no-install-deps
with --package_manager port
. (Note: ruby-install
doesn’t support the flag format --key=val
.)
Background
I use MacPorts to manage FOSS on my Mac. I switched from Homebrew to MacPorts because Homebrew doesn’t record whether a package is “explicitly installed” or pulled in as dependencies; also Homebrew doesn’t support “virtual packages”. However, MacPorts seems to be mainly (only?) for FOSS, and is not interested in distributing binaries compiled upstream. This is includes Docker for Mac, coconutBattery, Acrobat Reader, Vagrant, and VirtualBox. Managing them via Homebrew Cask allows clean uninstallation, which is important for me, so I’d keep them coexist for a while.
Top comments (0)