Skip to content
This repository has been archived by the owner on Mar 12, 2019. It is now read-only.

Cannot Compile Formulae using c++ with gcc-8 (probably gcc >= 6) #724

Closed
kalaxy opened this issue Jun 8, 2018 · 21 comments
Closed

Cannot Compile Formulae using c++ with gcc-8 (probably gcc >= 6) #724

kalaxy opened this issue Jun 8, 2018 · 21 comments
Assignees
Labels

Comments

@kalaxy
Copy link

kalaxy commented Jun 8, 2018

I'm trying to compile formulae using the gcc@8 package. When I try to compile formulae which use the c++ stdlib, I get build failures. Steps to reproduce:

  1. Create a fresh prefix:
    $ git clone https://github.com/Linuxbrew/brew.git ~/.linuxbrew
    $ PATH="$HOME/.linuxbrew/bin:$PATH"
  2. Install gcc@8:
    $ brew install gcc@8
  3. Install a formula, compiling from source, which uses c++ stdlib using gcc-8 as the compiler. For example, berkeley-db:
    $ brew install berkeley-db --cc=gcc-8

This fails for me:

==> Downloading https://download.oracle.com/berkeley-db/db-6.2.32.tar.gz
Already downloaded: /home/kmills/.cache/Homebrew/berkeley-db-6.2.32.tar.gz
==> ../dist/configure --disable-debug --prefix=/home/kmills/.linuxbrew/Cellar/berkeley-db/6.2.32 --mandir=/home/kmills/.linuxbrew/Cellar/berkeley-db/6.2.32/share/man --enable-cxx --enable-compat185
Last 15 lines from /home/kmills/.cache/Homebrew/Logs/berkeley-db/01.configure:
checking if the linker (/home/kmills/.linuxbrew/Library/Homebrew/shims/linux/super/ld -m elf_x86_64) is GNU ld... yes
checking whether the g++-8 linker (/home/kmills/.linuxbrew/Library/Homebrew/shims/linux/super/ld -m elf_x86_64) supports shared libraries... yes
checking for g++-8 option to produce PIC... -fPIC -DPIC
checking if g++-8 PIC flag -fPIC -DPIC works... yes
checking if g++-8 static flag -static works... yes
checking if g++-8 supports -c -o file.o... yes
checking if g++-8 supports -c -o file.o... (cached) yes
checking whether the g++-8 linker (/home/kmills/.linuxbrew/Library/Homebrew/shims/linux/super/ld -m elf_x86_64) supports shared libraries... yes
checking dynamic linker characteristics... (cached) GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking SOSUFFIX from libtool... .so
checking MODSUFFIX from libtool... .so
checking JMODSUFFIX from libtool... .so
checking for main in -ldl... yes
checking whether the C++ compiler supports templates for STL... configure: error: no

After digging in the config.log I found this failure:

configure:19261: g++-8 -c -O3   -D_GNU_SOURCE -D_REENTRANT conftest.cpp >&5
In file included from /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/ext/string_conversions.h:41,
                 from /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/bits/basic_string.h:6361,
                 from /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/string:52,
                 from /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/bits/locale_classes.h:40,
                 from /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/bits/ios_base.h:41,
                 from /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/ios:42,
                 from /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/ostream:38,
                 from /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/iostream:39,
                 from conftest.cpp:23:
/home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/cstdlib:75:15: fatal error: stdlib.h: No such file or directory
 #include_next <stdlib.h>
               ^~~~~~~~~~
compilation terminated.

I tracked this down to a behavior change in gcc's -isystem handling. See gcc bug 70129. It appears that since gcc 6, but I haven't tried 6 or 7. Essentially, adding an include path which is already part of the default include paths causes issues when resolving c++ stdlib includes.

brew appears to automatically add -isystem=$(brew --prefix)/include even though it is already encoded as a default search path for gcc.

Note the default search paths in gcc-8 already include $(brew --prefix)/include.

$ echo | gcc-8 -E -Wp,-v -
ignoring nonexistent directory "/home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/bin/../lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../../../x86_64-pc-linux-gnu/include"
ignoring duplicate directory "/home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/bin/../lib/gcc/8/gcc/../../../../lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/include"
ignoring nonexistent directory "/home/kmills/.linuxbrew/nonexistent/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/home/kmills/.linuxbrew/nonexistent/usr/local/include"
ignoring duplicate directory "/home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/bin/../lib/gcc/8/gcc/../../../../lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/include-fixed"
ignoring nonexistent directory "/home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/bin/../lib/gcc/8/gcc/../../../../lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../../../x86_64-pc-linux-gnu/include"
ignoring nonexistent directory "/home/kmills/.linuxbrew/nonexistent/usr/include/x86_64-linux-gnu"
ignoring nonexistent directory "/home/kmills/.linuxbrew/nonexistent/usr/include"
#include "..." search starts here:
#include <...> search starts here:
 /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/bin/../lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/include
 /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/bin/../lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/include-fixed
 /home/kmills/.linuxbrew/Cellar/gcc@8/8.1.0/bin/../lib/gcc/8/gcc/../../../../include
 /home/kmills/.linuxbrew/include
End of search list.
...

I tried reproducing this on homebrew for mac and couldn't. That said, I was using the default install location rather than a custom location, and it looks like there might be hooks in place detecting the default location and acting differently in that case.

Anyway, I believe to make this work brew needs to forgo adding -isystem$(brew --prefix)/include for gcc 6 and later when it is already in the include path. But I'm not sure how to do that. Is this something easily fixable?

For info:

$ brew config
HOMEBREW_VERSION: >=1.4.0 (shallow or no git repository)
ORIGIN: https://github.com/Linuxbrew/brew.git
HEAD: b9917814b14a54406a1e8588f4157e97cdc88c32
Last commit: 3 days ago
Core tap ORIGIN: https://github.com/Linuxbrew/homebrew-core
Core tap HEAD: 39bae62cca3c05ae051f11111b2e303f0261f89a
Core tap last commit: 89 minutes ago
HOMEBREW_PREFIX: /home/kmills/.linuxbrew
HOMEBREW_REPOSITORY: /home/kmills/.linuxbrew
HOMEBREW_CELLAR: /home/kmills/.linuxbrew/Cellar
HOMEBREW_CACHE: /home/kmills/.cache/Homebrew
HOMEBREW_NO_ANALYTICS_THIS_RUN: 1
CPU: octa-core 64-bit haswell
Homebrew Ruby: 2.3.3 => /home/kmills/.linuxbrew/Library/Homebrew/vendor/portable-ruby/2.3.3_2/bin/ruby
Clang: N/A
Git: 1.8.3.1 => /bin/git
Curl: 7.29.0 => /usr/bin/curl
Java: N/A
Kernel: Linux 3.10.0-693.21.1.el7.x86_64 x86_64 GNU/Linux
OS: CentOS Linux release 7.4.1708 (Core) 
Host glibc: 2.17
/usr/bin/gcc: 4.8.5
glibc: 2.23
gcc: 5.5.0_4
xorg: N/A

Unrelated, but since the output was asked for...

$ brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!

Warning: An outdated version (1.8.3.1) of Git was detected in your PATH.
Git 1.8.5 or newer is required to perform checkouts over HTTPS from GitHub and
to support the 'git -C <path>' option.
Please upgrade:
  brew install git

Warning: Homebrew's sbin was not found in your PATH but you have installed
formulae that put executables in /home/kmills/.linuxbrew/sbin.
Consider setting the PATH for example like so
  echo 'export PATH="/home/kmills/.linuxbrew/sbin:$PATH"' >> ~/.bash_profile
@iMichka
Copy link
Member

iMichka commented Jun 12, 2018

@xu-cheng @sjackman thoughts?

@sjackman
Copy link
Member

@kalaxy Thanks for the bug report, Kalon. Linuxbrew modified the default GCC specs file to add $HOMEBREW_PREFIX/include to the default search path; it otherwise would not be part of the default search path. See these two files:

/home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/specs.orig
/home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/specs

I wasn't able to reproduce this issue using…

docker run -it --rm linuxbrew/linuxbrew:1.6.7
brew install gcc@8
brew install hello --cc=gcc-8
…
🍺  /home/linuxbrew/.linuxbrew/Cellar/hello/2.10: 53 files, 312.8KB, built in 1 minute 9 seconds

Are you able to reproduce this issue in a Docker image?

@kalaxy
Copy link
Author

kalaxy commented Jun 13, 2018

Hi @sjackman, thanks for starting to look at this. In order to see the failure you need to install a formula that uses the c++ std library. It looks like hello is only C. Installing hello won't reproduce it on my machine either. Sorry that was unclear; I mentioned it in step 3 but didn't make it stick out enough.

Yeah, adding $HOMEBREW_PREFIX/include to the default search path when building GCC seems like the right thing to do. It probably just shouldn't be later added as an -isystem path when using the compiler to build formulae.

@sjackman
Copy link
Member

Ah, my mistake. Thanks for clearing that up, Kalon. I tried installing berkeley-db, but I wasn't able to reproduce the issue.

docker run -it --rm linuxbrew/linuxbrew:1.6.8
brew install gcc@8
brew install berkeley-db --cc=gcc-8
…
🍺  /home/linuxbrew/.linuxbrew/Cellar/berkeley-db/6.2.32: 5,632 files, 126.4MB, built in 8 minutes 37 seconds

Are you able to reproduce this issue in a Docker image?

@kalaxy
Copy link
Author

kalaxy commented Jun 14, 2018

Gotcha, sorry about that. I'm not that familiar with Docker so was avoiding it. (Yeah I know I'm behind on the times.)

No, as you found, I am not able to replicate it from a the linuxbrew/linuxbrew docker container.

I am, however, able to get this to reproduce with a CentOS docker container. That is what matches my system. Does that help?

docker run -it --rm centos:7
yum install -y bzip2 ca-certificates curl file fonts-dejavu-core g++ git locales make openssh-client patch sudo uuid-runtime
localedef -i en_US -f UTF-8 en_US.UTF-8 && useradd -m -s /bin/bash linuxbrew && echo 'linuxbrew ALL=(ALL) NOPASSWD:ALL' >>/etc/sudoers
sudo su - linuxbrew
git clone https://github.com/Linuxbrew/brew.git ~/.linuxbrew
PATH="$HOME/.linuxbrew/bin:$PATH"
brew install gcc@8
brew install berkeley-db --cc=gcc-8
...
checking whether the C++ compiler supports templates for STL... configure: error: no

Debugging a bit, It appears that the packages installed are a little different. For instance on the CentOS container I after installing gcc@8 I have

$ brew list
binutils  gcc  gcc@8  glibc  gmp  isl  isl@0.18  libmpc  linux-headers  m4  mpfr  patchelf  zlib

But in the linuxbrew docker container I only have

$ brew list
gcc@8  gmp  isl  libmpc  mpfr  patchelf  zlib

So something about the env is changing how things are installed. Maybe it is also affecting the -isystem flags? But I'm just grasping at straws now. HTH

@kalaxy
Copy link
Author

kalaxy commented Jun 14, 2018

I just noticed that you already have a Dockerfile for CentOS7 which includes some of the setup that I did above. It also reproduces the problem.

docker build -t linuxbrew-centos7 https://raw.githubusercontent.com/Linuxbrew/docker/master/centos7/Dockerfile
docker run -it --rm linuxbrew-centos7
brew install gcc@8
brew install berkeley-db --cc=gcc-8
...
checking whether the C++ compiler supports templates for STL... configure: error: no

@xu-cheng
Copy link
Contributor

@xu-cheng @sjackman thoughts

Sorry, I am in travel this week. I can only look into this issue next week.

@sjackman
Copy link
Member

@kalaxy It's installing glibc that's breaking gcc@8. I can reproduce this issue in the linuxbrew/linuxbrew Docker image.

docker run -it --rm linuxbrew/linuxbrew:1.6.8
brew install glibc
brew install gcc@8
brew install berkeley-db --cc=gcc-8
…
checking whether the C++ compiler supports templates for STL... configure: error: no

@sjackman
Copy link
Member

Removing the specs file created by Linuxbrew works around this issue.

docker run -it --rm linuxbrew/linuxbrew:1.6.8
brew install glibc
brew install gcc@8
rm /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/lib/gcc/8/gcc/x86_64-pc-linux-gnu/8.1.0/specs
brew install berkeley-db --cc=gcc-8
…
🍺  /home/linuxbrew/.linuxbrew/Cellar/berkeley-db/6.2.32: 5,632 files, 126.4MB, built in 8 minutes 18 seconds

@xu-cheng
Copy link
Contributor

xu-cheng commented Jun 16, 2018

I am still in the travelling. @sjackman Could you help to test whether it can be workaround using std env instead of super env?

I suspect that this is caused by passing -isystem=HOMEBREW_PREFIX/include by homebrew superenv. As a result, it will invalid the -idirafter=HOMEBREW_PREFIX/include from the gcc spec file, which supports to tell gcc to search HOMEBREW_PREFIX/include with the lowest priority. The invalidation happening is because that gcc treats the second HOMEBREW_PREFIX/include (from -idirafter in spec file) as duplication and ignores it.

After the -idirafter=HOMEBREW_PREFIX/include is invalided, it means include_next <stdlib.h> wouldn't be able to find header files from glibc. Please note the special meaning of include_next instead of the standard include. Ref: https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html

Removing the specs file created by Linuxbrew works around this issue.

I think this is because without the spec file, gcc will search glibc header file in /usr/include. However, this is not a desirable behaviour. As in the system with old glibc, we want brew to ignore the system glibc and use brew glibc. That is the gcc should use the glibc header files from brew glibc.

As for solutions, one way is to add -idirafter HOMEBREW_PREFIX/opt/glibc/include in the end of gcc spec file for the users who use brew glibc. Another way is using -idirafter HOMEBREW_PREFIX/include instead of -isystem=HOMEBREW_PREFIX/include in the superenv wrapper.

@sjackman
Copy link
Member

The -isystem and -idirafter options also mark the directory as a system directory, so that it gets the same special treatment that is applied to the standard system directories.

https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html

Sounds like using -idirafter consistently should resolve this issue. I'll test it out this week.

@sjackman
Copy link
Member

When I change Brew's -isystem to -idirafter, I get this error:

configure:19128: checking whether the C++ compiler supports templates for STL
configure:19419: g++-8 -c -O3   -D_GNU_SOURCE -D_REENTRANT conftest.cpp >&5
In file included from /home/linuxbrew/.linuxbrew/include/errno.h:35,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/cerrno:42,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/ext/string_conversions.h:44,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/bits/basic_string.h:6361,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/string:52,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/bits/locale_classes.h:40,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/bits/ios_base.h:41,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/ios:42,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/ostream:38,
                 from /home/linuxbrew/.linuxbrew/Cellar/gcc@8/8.1.0/include/c++/8.1.0/iostream:39,
                 from conftest.cpp:24:
/home/linuxbrew/.linuxbrew/include/bits/errno.h:24:11: fatal error: linux/errno.h: No such file or directory
 # include <linux/errno.h>
           ^~~~~~~~~~~~~~~
compilation terminated.
configure:19419: $? = 1

linux-headers was not installed. Currently the dependency of glibc on linux-headers is :build. Perhaps it should not be :build. I'm trying again with linux-headers installed.

@sjackman
Copy link
Member

Success!

diff --git a/Library/Homebrew/shims/super/cc b/Library/Homebrew/shims/super/cc
index d6085a3..55e9e27 100755
--- a/Library/Homebrew/shims/super/cc
+++ b/Library/Homebrew/shims/super/cc
@@ -270,7 +270,7 @@ class Cmd
   end
 
   def cppflags
-    path_flags("-isystem", isystem_paths) + path_flags("-I", include_paths)
+    path_flags("-idirafter", isystem_paths) + path_flags("-I", include_paths)
   end
 
   def ldflags_mac(args)

@sjackman
Copy link
Member

Here's a PR to hopefully address this issue: #733

@xu-cheng
Copy link
Contributor

xu-cheng commented Jun 19, 2018

@sjackman Personally, I would prefer to fix this in gcc@6 gcc@7 and gcc@8 formula.

Further, I suspect the root issue is a bug from gcc >= 6 rather than Linuxbrew. For example in Ubuntu Bionic docker image, I can reproduce this bug as following:

root@11e517c8810c:/# cat /tmp/test.cpp
#include <cstdlib>

int main()
{
	return 0;
}
root@11e517c8810c:/# g++-8 /tmp/test.cpp -o /tmp/test
root@11e517c8810c:/# g++-8 /tmp/test.cpp -isystem=/usr/include/ -o /tmp/test
In file included from /tmp/test.cpp:1:
/usr/include/c++/8/cstdlib:75:15: fatal error: stdlib.h: No such file or directory
 #include_next <stdlib.h>
               ^~~~~~~~~~
compilation terminated.

Another reason to fix it in formula is that this bug can be trigger by users who use gcc formula as shown in the above.

@kalaxy
Copy link
Author

kalaxy commented Jun 19, 2018

@xu-cheng, fyi see gcc bug 70129. It appears that they have decided it is working as designed. It sounds like their suggestion is to just not add the -isystem path when it is already included in other ways, for example, via the specs file.

Though others, such as comment 5, on that thread seem to have good reasons it should be allowed. So it may be worth pressing the gcc team again.

@sjackman
Copy link
Member

I commented on the closed GCC bug to mention that it affects Linuxbrew as well. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129#c7

@sjackman sjackman self-assigned this Jun 19, 2018
@sjackman sjackman added the bug label Jun 19, 2018
xu-cheng added a commit to xu-cheng/linuxbrew-core that referenced this issue Jun 20, 2018
For gcc >= 6, passing -isystem flag will result to the include path
search order change. This causes issues for brew superenv, which passes
-isystem HOMEBREW_PREFIX/include to the compiler. For more detail,
please refer to issue Linuxbrew/brew#724

Related upstream issue can be found at
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129

This workarounds this issue by passing brew glibc include path to the
end of search path.
@xu-cheng
Copy link
Contributor

https://github.com/Linuxbrew/homebrew-core/pull/8108 is another solution.

@sjackman
Copy link
Member

sjackman commented Jun 20, 2018

This fix PR https://github.com/Linuxbrew/homebrew-core/pull/8108 looks good to me. Thanks again, @xu-cheng! Shall I close this PR in favour of PR https://github.com/Linuxbrew/homebrew-core/pull/8108 ?

@kalaxy
Copy link
Author

kalaxy commented Jun 27, 2018

I can confirm that the changes you made work for me. Thanks so much for your help diagnosing and coming to a solution!

Closing, as the desired workflow works now.

@kalaxy kalaxy closed this as completed Jun 27, 2018
@sjackman
Copy link
Member

Thanks for following up, Kalon. Thanks again, @xu-cheng !

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants