Cannot Compile Formulae using c++ with gcc-8 (probably gcc >= 6)
kalaxy opened this issue · 21 comments
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:
- Create a fresh prefix:
$ git clone https://github.com/Linuxbrew/brew.git ~/.linuxbrew $ PATH="$HOME/.linuxbrew/bin:$PATH"
- Install gcc@8:
$ brew install gcc@8
- 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
@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?
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.
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?
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
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
@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
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
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.
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.
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.
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 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.
@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.
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
https://github.com/Linuxbrew/homebrew-core/pull/8108 is another solution.
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 ?
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.