DEV Community

Franklin Yu
Franklin Yu

Posted on

Error of “RUBY_FUNCTION_NAME_STRING” when compiling Ruby

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!
Enter fullscreen mode Exit fullscreen mode

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])
])
Enter fullscreen mode Exit fullscreen mode

This file (configure.ac) is a GNU Autoconf script. This section of the script sets the value of the macro:

  1. If compiling for OpenBSD, set it to __func__.
  2. 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__.
  3. Otherwise, do the same with __FUNCTION__.
  4. 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__
Enter fullscreen mode Exit fullscreen mode

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__
Enter fullscreen mode Exit fullscreen mode

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__
Enter fullscreen mode Exit fullscreen mode

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 $?
Enter fullscreen mode Exit fullscreen mode

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. 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)