ContinuumIO / anaconda-issues

Anaconda issue tracking
648 stars 222 forks source link

Ruby can't install gems with native extensions, missing anaconda compilers. #9863

Open asford opened 6 years ago

asford commented 6 years ago

Actual Behavior

The current ruby package (2.5.1 h070849d_0) can not install gems with native extension components, as gem attempts to compile the extension using the anaconda compilers used to compile the ruby package.

Eg:

make: /tmp/build/80754af9/ruby_1532875114928/_build_env/bin/x86_64-conda_cos6-linux-gnu-cc: Command not found
Makefile:241: recipe for target 'arena.o' failed
make: *** [arena.o] Error 127

make failed, exit code 2

This is a relatively serious error as many gems contain native extensions. For example, this blocks the installation of jekyll, which is a critical component for local previews of github pages.

Expected Behavior

This does not occur when using the conda-forge ruby package, which successfully identifies and uses the system compilers.

I would expect that either:

  1. The ruby package is configured to identify and use system compilers to install gems with native extensions.
  2. The ruby package specifies dependencies on the appropriate anaconda compiler packages needed to install native extensions.

Steps to Reproduce

For a minimal repro:

conda create -y -p .conda ruby
conda activate ./.conda
gem install commonmarker

Full log:

+fordas@localhost:~/workspace/ruby-build-failure$ conda create -y -p .conda ruby
Solving environment: done

## Package Plan ##

  environment location: /home/fordas/workspace/ruby-build-failure/.conda

  added / updated specs: 
    - ruby

The following NEW packages will be INSTALLED:

    ca-certificates: 2018.03.07-0     
    gmp:             6.1.2-h6c8ec71_1 
    libgcc-ng:       7.2.0-hdf63c60_3 
    libstdcxx-ng:    7.2.0-hdf63c60_3 
    ncurses:         6.1-hf484d3e_0   
    openssl:         1.0.2o-h20670df_0
    readline:        7.0-ha6073c6_4   
    ruby:            2.5.1-h070849d_0 
    tk:              8.6.7-hc745277_3 
    yaml:            0.1.7-had09818_2 
    zlib:            1.2.11-ha838bed_2

Preparing transaction: done
Verifying transaction: done
Executing transaction: done
#
# To activate this environment, use
#
#     $ conda activate /home/fordas/workspace/ruby-build-failure/.conda
#
# To deactivate an active environment, use
#
#     $ conda deactivate

+fordas@localhost:~/workspace/ruby-build-failure$ source activate ./.conda

+fordas@localhost:~/workspace/ruby-build-failure$ conda list
# packages in environment at /home/fordas/workspace/ruby-build-failure/.conda:
#
# Name                    Version                   Build  Channel
ca-certificates           2018.03.07                    0  
gmp                       6.1.2                h6c8ec71_1  
libgcc-ng                 7.2.0                hdf63c60_3  
libstdcxx-ng              7.2.0                hdf63c60_3  
ncurses                   6.1                  hf484d3e_0  
openssl                   1.0.2o               h20670df_0  
readline                  7.0                  ha6073c6_4  
ruby                      2.5.1                h070849d_0  
tk                        8.6.7                hc745277_3  
yaml                      0.1.7                had09818_2  
zlib                      1.2.11               ha838bed_2  

+fordas@localhost:~/workspace/ruby-build-failure$ which gem
/home/fordas/workspace/ruby-build-failure/.conda/bin/gem

+fordas@localhost:~/workspace/ruby-build-failure$ gem install commonmarker
Building native extensions. This could take a while...
ERROR:  Error installing commonmarker:
        ERROR: Failed to build gem native extension.

    current directory: /home/fordas/workspace/ruby-build-failure/.conda/lib/ruby/gems/2.5.0/gems/commonmarker-0.17.9/ext/commonmarker
/home/fordas/workspace/ruby-build-failure/.conda/bin/ruby -r ./siteconf20180802-18453-13y3oko.rb extconf.rb
creating Makefile

current directory: /home/fordas/workspace/ruby-build-failure/.conda/lib/ruby/gems/2.5.0/gems/commonmarker-0.17.9/ext/commonmarker
make "DESTDIR=" clean

current directory: /home/fordas/workspace/ruby-build-failure/.conda/lib/ruby/gems/2.5.0/gems/commonmarker-0.17.9/ext/commonmarker
make "DESTDIR="
compiling arena.c
make: /tmp/build/80754af9/ruby_1532875114928/_build_env/bin/x86_64-conda_cos6-linux-gnu-cc: Command not found
Makefile:241: recipe for target 'arena.o' failed
make: *** [arena.o] Error 127

make failed, exit code 2

Gem files will remain installed in /home/fordas/workspace/ruby-build-failure/.conda/lib/ruby/gems/2.5.0/gems/commonmarker-0.17.9 for inspection.
Results logged to /home/fordas/workspace/ruby-build-failure/.conda/lib/ruby/gems/2.5.0/extensions/x86_64-linux/2.5.0/commonmarker-0.17.9/gem_make.out
Operating System:

Ubuntu 16.04

conda info
+(/home/fordas/workspace/ruby-build-failure/.conda) fordas@localhost:~/workspace/ruby-build-failure$ conda info

     active environment : /home/fordas/workspace/ruby-build-failure/.conda
    active env location : /home/fordas/workspace/ruby-build-failure/.conda
            shell level : 1
       user config file : /home/fordas/.condarc
 populated config files : 
          conda version : 4.5.6
    conda-build version : 3.10.9
         python version : 3.6.3.final.0
       base environment : /home/fordas/.conda  (writable)
           channel URLs : https://repo.anaconda.com/pkgs/main/linux-64
                          https://repo.anaconda.com/pkgs/main/noarch
                          https://repo.anaconda.com/pkgs/free/linux-64
                          https://repo.anaconda.com/pkgs/free/noarch
                          https://repo.anaconda.com/pkgs/r/linux-64
                          https://repo.anaconda.com/pkgs/r/noarch
                          https://repo.anaconda.com/pkgs/pro/linux-64
                          https://repo.anaconda.com/pkgs/pro/noarch
          package cache : /home/fordas/.conda/pkgs
       envs directories : /home/fordas/.conda/envs
               platform : linux-64
             user-agent : conda/4.5.6 requests/2.18.4 CPython/3.6.3 Linux/4.4.127-13890-g6c206f1711d4 ubuntu/16.04 glibc/2.23
                UID:GID : 1000:1000
             netrc file : None
           offline mode : False
conda list --show-channel-urls
# packages in environment at /home/fordas/workspace/ruby-build-failure/.conda:
#
# Name                    Version                   Build  Channel
ca-certificates           2018.03.07                    0    defaults
gmp                       6.1.2                h6c8ec71_1    defaults
libgcc-ng                 7.2.0                hdf63c60_3    defaults
libstdcxx-ng              7.2.0                hdf63c60_3    defaults
ncurses                   6.1                  hf484d3e_0    defaults
openssl                   1.0.2o               h20670df_0    defaults
readline                  7.0                  ha6073c6_4    defaults
ruby                      2.5.1                h070849d_0    defaults
tk                        8.6.7                hc745277_3    defaults
yaml                      0.1.7                had09818_2    defaults
zlib                      1.2.11               ha838bed_2    defaults
CyanoKobalamyne commented 6 years ago

In case anyone else finds this thread from Google: I solved this problem by temporarily symlinking the locations referenced in the build log to the build tools in my conda environment (the system tools probably also work).

ln -s /home/.../miniconda3/envs/.../bin/x86_64-conda_cos6-linux-gnu-gcc /tmp/build/80754af9/ruby_1532875114928/_build_env/bin/x86_64-conda_cos6-linux-gnu-cc

lmrodriguezr commented 5 years ago

Is there any update on this issue? It appears the GCC compiler was hardcoded to some temporal file at the building time. I tried several other channels offering ruby packages for conda and all of them had the same issue.

The workaround that @CyanoKobalamyne provides gets me through, but I cannot systematically add this into a new conda package that depends on ruby 🤕

msarahan commented 5 years ago

Sorry for the sub-optimal behavior here. Unfortunately, I think it might be a while before the Anaconda team can get to this. If you'd like to submit a PR, the recipe for our package build is at: https://github.com/anacondarecipes/ruby-feedstock

The precedent for what we think is the "right" way to do things is with Python here: https://github.com/AnacondaRecipes/python-feedstock/tree/master/recipe/sysconfigdata

Basically, the idea is that we patch Python to look for an environment variable to set the correct compiler stuff. If that isn't present, we fall back to one that should be compatible with system compilers. I'm not sure if Ruby has some equivalent.

If you think it's an adequate workaround, you could also just alter the recipe to include:

requirements:
  run:
    - {{ compiler("c") }}
    - {{ compiler("cxx") }}

That should give you the correct compilers, though it seems pretty awful to need to do that.

In general, using 2 package managers leads to headache, since they are often not aware of each other's metadata. We don't have much experience with ruby/gem - mostly pip for python and R. In the long term, we'd like to have conda be able to talk with the upstream repos and handle all installation, but that will take some time to figure out. R and Python are first, but hopefully they set a good pattern that can extend to ruby soon after.

lmrodriguezr commented 5 years ago

Thanks a lot for the update and the pointers @msarahan !

I think that's doable, I'll play with it a bit and try to come up with a PR to start solving the issue. I'm almost sure a similar solution to Python's can be accomplished with rbconfig.rb.

msarahan commented 5 years ago

One thing I neglected to mention: the thing that sets the env var is an activation script is the compiler package: https://github.com/AnacondaRecipes/aggregate/blob/700b189155e27744dd0006acf90f58f2ed0fd42f/ctng-compilers-activation-feedstock/recipe/activate-gcc.sh#L142

That takes effect when an env containing the compiler package is activated.

You'll want a similar thing for Ruby in that recipe. You really want to use the anaconda compilers when building packages that interact with other conda packages. It helps a lot with binary compatibility when all the bits are built with the same toolchain.

lmrodriguezr commented 5 years ago

I saw the Python's build re-sets several ENV variables as VAR=$(basename $VAR) before compiling, and I think that would solve the issue if we're thorough on the cleanup. Do you see a problem with this approach? In most cases that should still use conda's compiler thanks to the compilers naming conventions, but I don't know if it'd be the proper conda way (sorry, I'm not very familiar with conda yet).

In the meantime, if anyone's looking for a monkey-patch that doesn't require much manual intervention, here you are:

cd $(gem environment gemdir)
cd ../../$(basename $PWD)/$(gem environment platform | perl -pe 's/.*://‘)
mv rbconfig.rb rbconfig.rb.bu
perl -pe 's/\/\S*?\/_build_env\/bin\///g' rbconfig.rb.bu > rbconfig.rb
lmrodriguezr commented 5 years ago

These are the variable I think should be modified:

CC
CPP
STRIP
NM
OBJDUMP
AS
AR
RANLIB
LD

I don't see any other variables that need attention.

msarahan commented 5 years ago

Those variables are all modified by the activation script that I pointed to above. If you unset them, yes, you'll nullify our activation script, but I don't see the point of doing that: Ruby will still be hardcoded to look for the prefixed compiler names?

phil-blain commented 5 years ago

A relevant pull request on conda-forge: https://github.com/conda-forge/ruby-feedstock/pull/18

phil-blain commented 5 years ago

On my machine (Ubuntu 14.04), the following line of the quick fix provided by lmrodriguezr above does not work

cd ../../$(basename $PWD)/$(gem environment platform | perl -pe 's/.*://‘)

however, it works with sed:

cd ../../$(basename $PWD)/$(gem environment platform | sed -e 's/.*://')
lmrodriguezr commented 5 years ago

It seems https://github.com/conda-forge/ruby-feedstock/pull/23 resolved this issue.

The version of ruby in conda-forge is currently behind that of the default packages (so conda install ruby won't install a ruby with the fix). You can just install the specific version/build:

conda install -c conda-forge ruby=2.4.5=h28fbfb0_1003 # For osx-64
conda install -c conda-forge ruby=2.4.5=h5148a6c_1003 # For linux-ppc64le
conda install -c conda-forge ruby=2.4.5=h48a8d5d_1003 # For linux-64
conda install -c conda-forge ruby=2.4.5=h48a8d5d_1003 # For linux-aarch64