Background

When I cloned a project and tried to run bundle install, I receive the error

ERROR: Failed to build gem native extension.
...
fatal error: 'climits' file not found #include <climits>

The error is raised when bundler tries to require libv8, which is in turn required by therubyracer.

If you are reading this article, I believe I can assume that you are having a similar problem. I am going to share my findings here.

In this blog, we will try to answer the following questions:

  1. Why do we need libv8?
  2. Why does installation through bundle install fails?
  3. How do we fix it?

Why do we need libv8?

Before we talk about libv8, let’s first take a look what is V8

V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++. It is used in Chrome and in Node.js, among others.

So, basically, V8 is the Javascript engine in Chrome. And libv8 is the ruby interface for the V8 engine used by therubyracer.

The therubyracer gem embeds the V8 Javascript Interpreter into ruby. With it, we can call Javascript functions or use Javascript objects directly in ruby runtime.

Why does installation through bundle install fails?

The installation failed as therubyracer gem calls the libv8 gem, and it fails to build a native extension, i.e. v8. I am able to repeat the same bug with:

$ gem install therubyracer
Building native extensions. This could take a while...
ERROR:  Error installing therubyracer:
    ERROR: Failed to build gem native extension.

    current directory: /Users/billykong/.rvm/gems/ruby-2.6.5/gems/libv8-3.16.14.19/ext/libv8
...
Compiling v8 for x64
Using python 2.7.16
Using compiler: c++ (clang version 11.0.0)
Unable to find a compiler officially supported by v8.
It is recommended to use GCC v4.4 or higher
Beginning compilation. This will take some time.
...

The error in your case may be different but still there is one interesting finding here. The therubyracer gem installation script is trying to compile v8 from source! I am not saying it is impossible, but I got a feeling that the compilation is the cause of issues for many of us. Just the first few posts I found when I google “libv8 error”:

Compiling something requires all the dependencies to be present. Since v8 is written in C++ and probably many of us don’t have a C++ development environment setup ready for use. Perhaps it is easier just to download a suitable binary of v8 instead of building it from source.

How do we fix it?

# 1. Install v8 ourselves
$ brew install v8-315
# 2. Install libv8 using the v8 binary we just installed
$ gem install libv8 -v '3.16.14.19' -- --with-system-v8
# 3. Install therubyracer using the v8 binary we just installed
$ gem install therubyracer -- --with-v8-dir=/usr/local/opt/v8@315 # your path could be different, e.g. /usr/local/opt/v8@3.15
# 4. Install the remaining dependencies
$ bundle install

Note: The -- before the --with-system-v8 and --with-v8-dir=/usr/local/opt/v8@315 is necessary when we supply library paths for native extensions, i.e. using specified libraries/binaries instead of building them.

Conclusion

therubyracer or some other gems may requires native extensions to function properly. While some tries to build the extension from source code, it often fails because the dependencies required by the native extensions are not readily installed. We may solve the issues by supplying our own binaries.

Hope that you have find the information useful. If you find any error or mistake in the above content, please do feel free to leave a comment below.

Reference

  1. https://v8.dev/docs
  2. https://github.com/rubyjs/libv8
  3. https://github.com/rubyjs/therubyracer
  4. https://gist.github.com/fernandoaleman/868b64cd60ab2d51ab24e7bf384da1ca
  5. https://guides.rubygems.org/command-reference/
  6. https://stackoverflow.com/questions/19673714/error-installing-libv8-error-failed-to-build-gem-native-extension
  7. https://gist.github.com/simonqian/f67c862d06c94fe315d0ba97b711571f
  8. https://stackoverflow.com/questions/19577759/installing-libv8-gem-on-os-x-10-9?noredirect=1&lq=1