ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.74k stars 2.48k forks source link

zig c++ errors with boost #18770

Open marchelbling-aqemia opened 7 months ago

marchelbling-aqemia commented 7 months ago

Zig Version

0.12.0-dev.2341+92211135f

Steps to Reproduce and Observed Behavior

I am trying to build some scientific tooling using zig so that I can have static binaries for a couple of architectures (and I'm pretty excited about zig for this!).

I am using the following Dockerfile [updated]:

# Usage: docker build -t smina:latest .

FROM debian:bullseye-slim as build
ARG BOOST_VERSION=1.75.0
ARG BESSENTIAL_VERSION=12.9
ARG OPENBABEL_VERSION=3.1.1
ARG EIGEN3_VERSION=3.3.9-2
ARG ZSTD_VERSION=v1.5.5
ARG ZLIB_VERSION=1.3.1
ARG SMINA_TAG=7369fc0
ARG ZIG_VERSION=0.12.0-dev.2341+92211135f
ARG ZIG_TARGET="x86_64-linux-gnu"
ENV DEBIAN_FRONTEND noninteractive

RUN mkdir /src \
 && apt-get update \
 && apt-get upgrade -y \
 && apt-get install -y -q \
        git \
        build-essential=${BESSENTIAL_VERSION} \
        cmake \
        cmake-curses-gui \
        wget \
        neovim \
        libeigen3-dev=${EIGEN3_VERSION}

WORKDIR /src
RUN git clone --recursive --depth 1 --branch "${ZSTD_VERSION}" https://github.com/facebook/zstd \
 && wget https://www.zlib.net/zlib-${ZLIB_VERSION}.tar.gz && tar -xf zlib-${ZLIB_VERSION}.tar.gz && mv zlib-${ZLIB_VERSION} zlib \
 && git clone --recursive --depth 1 --branch "boost-${BOOST_VERSION}" https://github.com/boostorg/boost \
 && git clone --depth 1 --branch "openbabel-$( echo ${OPENBABEL_VERSION} | tr '.' '-' )" https://github.com/openbabel/openbabel \
 && git clone --depth 1 git://git.code.sf.net/p/smina/code smina

WORKDIR /src
RUN wget https://ziglang.org/builds/zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && tar -xf zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && rm zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && mv zig-linux-$(uname -m)-${ZIG_VERSION} zig-${ZIG_VERSION}

ENV PATH "${PATH}:/src/zig-${ZIG_VERSION}"
ENV CC "zig cc -pthread -target ${ZIG_TARGET}"
ENV CXX "zig c++ -pthread -target ${ZIG_TARGET}"

WORKDIR /src/zstd
RUN mkdir tmp && cd tmp \
 && cmake \
        -DCMAKE_BUILD_TYPE=Release \
        -DBUILD_SHARED=OFF \
        -DCMAKE_INSTALL_PREFIX=/usr/local \
        -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
    ../build/cmake \
 && make -j8 install

WORKDIR /src/zlib
RUN mkdir build && cd build \
 && cmake \
        -DCMAKE_BUILD_TYPE=Release \
        -DBUILD_SHARED=OFF \
        -DCMAKE_INSTALL_PREFIX=/usr/local \
        -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
    .. \
 && make -j8 install

WORKDIR /src/boost
RUN ./bootstrap.sh --prefix=/usr/local --with-libraries=iostreams,timer,system,regex,thread,serialization,filesystem,program_options,date_time \
 && ./b2 variant=release link=static runtime-link=static install

WORKDIR /src/openbabel
RUN mkdir build && cd build \
 && cmake \
        -DBUILD_SHARED=OFF \
        -DCMAKE_INSTALL_PREFIX=/usr/local \
        -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
        # note: we fake PTHREAD_LIBRARY that will handled by the compiler correctly \
        -DPTHREAD_LIBRARY=/usr/local/lib \
    .. \
 && make -j8 install

WORKDIR /src/smina
RUN mkdir build && cd build \
 && sed -i "s/\bunordered_map</boost::unordered_map</" ../src/lib/CommandLine2/CommandLine.cpp \
 && cmake \
        -DCMAKE_BUILD_TYPE=Release \
        -DBUILD_SHARED_LIBS=OFF \
        -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
        -DBoost_INCLUDE_DIR=/usr/local/include \
        -DBoost_LIBRARY_DIR_RELEASE=/usr/local/lib \
        -DBOOST_TIMER_ENABLE_DEPRECATED=ON \
        -DBoost_USE_STATIC_LIBS=ON \
        -DOPENBABEL3_INCLUDE_DIR=/usr/local/include/openbabel3 \
        -DOPENBABEL3_LIBRARIES='/usr/local/lib/libopenbabel.a;/usr/local/lib/libz.a;/usr/local/lib/libzstd.a' \
    .. \
  && make smina -j8
  1. if I don't add the sed replacement before the last cmake, the compilation fails with zig (gcc (Debian 10.2.1-6) 10.2.1 20210110 compiles just fine)

    /src/smina/src/lib/CommandLine2/CommandLine.cpp:1089:10: error: reference to 'unordered_map' is ambiguous
    1089 |     for (unordered_map<string, Option*>::iterator I = OptMap.begin(), E = OptMap.end();

    Looking at the source (link), since <unordered_map> is not included, I would not expect the compiler to fail here, even if the code is

    using namespace std;
    using namespace boost;
  2. with the sed replacement, compilation works fine however the linker fails:

    101.2 [100%] Linking CXX executable smina
    102.2 ld.lld: error: undefined symbol: boost::program_options::option_description::long_name() const
    102.2 >>> referenced by main.cpp:514 (/src/smina/src/main/main.cpp:514)
    102.2 >>>               CMakeFiles/smina.dir/src/main/main.cpp.o:(get_occurrence(boost::program_options::variables_map&, boost::program_options::options_description&))
    102.2 >>> referenced by main.cpp:531 (/src/smina/src/main/main.cpp:531)
    102.2 >>>               CMakeFiles/smina.dir/src/main/main.cpp.o:(check_occurrence(boost::program_options::variables_map&, boost::program_options::options_description&))
    102.2
    ...
    102.2 ld.lld: error: undefined symbol: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_create(unsigned long&, unsigned long)
    102.2 >>> referenced by chrono.cpp
    102.2 >>>               chrono.o:(boost::wrapexcept<boost::system::system_error>::clone() const) in archive /usr/local/lib/libboost_chrono.a
    102.2 >>> referenced by chrono.cpp
    102.2 >>>               chrono.o:(boost::wrapexcept<boost::system::system_error>::wrapexcept(boost::wrapexcept<boost::system::system_error> const&)) in archive /usr/local/lib/libboost_chrono.a
    102.2 >>> referenced by convert.cpp
    102.2 >>>               convert.o:(boost::program_options::to_internal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&)) in archive /usr/local/lib/libboost_program_options.a
    102.2 >>> referenced 202 more times
    ...

Expected Behavior

When relying on gcc (i.e. after removing the following lines:)

ENV PATH "${PATH}:/zig-${ZIG_VERSION}"
ENV CC "zig cc -pthread -target ${ZIG_TARGET}"
ENV CXX "zig c++ -pthread -target ${ZIG_TARGET}"

the build succeeds.

It seems that both issues somehow relate to the use of boost. I may be missing something obvious however I would expect that zig cc and zig c++ could be used as drop in replacements for gcc.

mikdusan commented 7 months ago

Looking at the source since <unordered_map> is not included, I would not expect the compiler to fail here

It's included (implicitly) somewhere, maybe even by libc++ implementation, otherwise the ambiguity error wouldn't present. This is probably explained by a difference between libc++ vs libstdc++ .

however the linker fails:

It's been a while since I used Dockerfile but what are you doing here? Earlier the versions are defined, and then just before they are used, the values get undefined? Dockerfile fails because clone command lines have empty arguments.

ARG ZSTD_VERSION
ARG ZLIB_VERSION
ARG BOOST_VERSION
ARG OPENBABEL_VERSION
WORKDIR /src
RUN git clone ........

here too:

ARG ZIG_ARCH
ARG ZIG_VERSION
WORKDIR /src
RUN wget https://ziglang.org/builds/zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && tar -xf zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && rm zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && mv zig-linux-$(uname -m)-${ZIG_VERSION} zig-${ZIG_VERSION}

ARG ZIG_VERSION
ARG ZIG_TARGET
ENV PATH "${PATH}:/src/zig-${ZIG_VERSION}"
ENV CC "zig cc -pthread -target ${ZIG_TARGET}"
ENV CXX "zig c++ -pthread -target ${ZIG_TARGET}"

... if that all works out, it may help if you provide the verbose link command line that is failing.

marchelbling-aqemia commented 7 months ago

Thank you for the quick reply @mikdusan!

It's included (implicitly) somewhere, maybe even by libc++ implementation, otherwise the ambiguity error wouldn't present. This is probably explained by a difference between libc++ vs libstdc++ .

It's been a while since I dealt with C++. I thought that libc++ and libstdc++ should be pretty much entirely interchangeable so I would have expected that headers "layout" would be compatible and only actual implementation details would change.

Earlier the versions are defined, and then just before they are used, the values get undefined? Dockerfile fails because clone command lines have empty arguments.

Sorry, you are right that the ARGs do not have to be repeated (I believe that should not have been an issue, as the first occurrence provides the default value, at least it was not causing docker build issues on my end). I changed my actual Dockerfile before posting to ease testing and improve layers caching as the whole build is somewhat lengthy, sorry about that. I've updated the Dockerfile in my initial message for better readibility.

The issue(s) I have both are in the last build (following WORKDIR /src/smina). The cmake call is fine and it's the make smina call that fails.

All the other programs before (zstd, zlib, boost, openbabel) compile/link just fine with zig.

mikdusan commented 7 months ago

The issue(s) I have both are in the last build

The boost build process does not respect env vars CC or CXX. You'll have to find another way to override which compiler boost selects. The link is currently failing because gcc was used to build boost.

One way would be to create bin/ path and create your own { cc, c++ } or { gcc, g++ } shell scripts which call zig.

Another way is the following snippet, create a user config file for boost b2; but this only gets me so far. It seems to break down not building iostreams with zlib extensions, maybe it can't find zlib? Or maybe it's a clang thing.

As an aside, I ended up with using gcc instead of using clang because b2 inserts a --target command line option, and it doesn't match zig's target spec, causing error. I'm unaware of how to disable or override that.

WORKDIR /src/boost
RUN ./bootstrap.sh --prefix=/usr/local --with-libraries=iostreams,timer,system,regex,thread,serialization,filesystem,program_options,date_time \
 && echo "using gcc : : ${CXX} ;" > zig.jam \
 && ./b2 --user-config=zig.jam variant=release link=static runtime-link=static install
marchelbling-aqemia commented 7 months ago

Thank you very much for following up! I clearly overlooked boost compilation configuration. I'll have to tinker with your inputs and other resources to make this work. Is it okay to let this issue open until I (or someone else) come up with a working solution to compile boost with zig? Anyway, definitively not a zig bug!

marchelbling-aqemia commented 2 months ago

Alright, I have been back at this and made some progress:

FROM debian:bullseye-slim as build
ARG BESSENTIAL_VERSION=12.9
ARG BOOST_VERSION=1.75.0
ARG OPENBABEL_VERSION=3.1.1
ARG EIGEN3_VERSION=3.3.9-2
ARG ZSTD_VERSION=v1.5.5
ARG ZLIB_VERSION=1.3.1
ARG SMINA_TAG=7369fc0
ARG ZIG_VERSION=0.13.0
ARG ZIG_TARGET="x86_64-linux-gnu"
ENV DEBIAN_FRONTEND noninteractive

RUN mkdir /src \
 && apt-get update \
 && apt-get upgrade -y \
 && apt-get install -y -q \
        git \
        build-essential=${BESSENTIAL_VERSION} \
        cmake \
        cmake-curses-gui \
        wget \
        libeigen3-dev=${EIGEN3_VERSION}

WORKDIR /src
RUN git clone --recursive --depth 1 --branch "${ZSTD_VERSION}" https://github.com/facebook/zstd \
 && wget https://www.zlib.net/zlib-${ZLIB_VERSION}.tar.gz && tar -xf zlib-${ZLIB_VERSION}.tar.gz && mv zlib-${ZLIB_VERSION} zlib \
 && git clone --recursive --depth 1 --branch "boost-${BOOST_VERSION}" https://github.com/boostorg/boost \
 && git clone --depth 1 --branch "openbabel-$( echo ${OPENBABEL_VERSION} | tr '.' '-' )" https://github.com/openbabel/openbabel \
 && git clone --depth 1 git://git.code.sf.net/p/smina/code smina

WORKDIR /src
RUN wget https://ziglang.org/download/${ZIG_VERSION}/zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && tar -xf zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && rm zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz \
 && mv zig-linux-$(uname -m)-${ZIG_VERSION} zig-${ZIG_VERSION}

ENV PATH "${PATH}:/src/zig-${ZIG_VERSION}"
ENV CC "zig cc -pthread -target ${ZIG_TARGET}"
ENV CXX "zig c++ -std=c++11 -pthread -target ${ZIG_TARGET} -Wno-deprecated-declarations -I/src/zig-0.13.0/lib/libcxx/include/"

WORKDIR /src/zstd
RUN mkdir tmp && cd tmp \
 && cmake \
        -DCMAKE_BUILD_TYPE=Release \
        -DBUILD_SHARED=OFF \
        -DCMAKE_INSTALL_PREFIX=/usr/local \
    ../build/cmake \
 && make -j8 install

WORKDIR /src/zlib
RUN mkdir build && cd build \
 && cmake \
        -DCMAKE_BUILD_TYPE=Release \
        -DBUILD_SHARED=OFF \
        -DCMAKE_INSTALL_PREFIX=/usr/local \
    .. \
 && make -j8 install

WORKDIR /src/boost
RUN echo "using clang : zig++ : ${CXX} ;" >> user-config.jam \
 && echo "using zlib : ${ZLIB_VERSION} : <include>\"/usr/local/include\" <search>\"/usr/local/lib\" ;" >> user-config.jam \
 && ./bootstrap.sh --prefix=/usr/local --with-libraries=atomic,iostreams,timer,system,regex,thread,serialization,filesystem,program_options,date_time \
 && ./b2 --user-config=user-config.jam variant=release link=static runtime-link=static install

WORKDIR /src/openbabel
RUN mkdir build && cd build \
 && cmake \
        -DBUILD_SHARED=OFF \
        -DCMAKE_INSTALL_PREFIX=/usr/local \
        -DCMAKE_FIND_LIBRARY_SUFFIXES=".a" \
        # note: we fake PTHREAD_LIBRARY that will handled by the compiler correctly \
        -DPTHREAD_LIBRARY=/usr/local/lib \
    .. \
 && make -j8 install

WORKDIR /src/smina
RUN mkdir build && cd build \
 && sed -i "s/\bunordered_map</boost::unordered_map</" ../src/lib/CommandLine2/CommandLine.cpp \
 && sed -i "s/filesystem::/boost::filesystem::/g" ../src/lib/CommandLine2/CommandLine.cpp \
 && cmake \
        -DCMAKE_BUILD_TYPE=Release \
        -DBUILD_SHARED_LIBS=OFF \
        -DBoost_INCLUDE_DIR=/usr/local/include \
        -DBoost_LIBRARY_DIR_RELEASE=/usr/local/lib \
        -DBOOST_TIMER_ENABLE_DEPRECATED=ON \
        -DBoost_USE_STATIC_LIBS=ON \
        -DOPENBABEL3_INCLUDE_DIR=/usr/local/include/openbabel3 \
        -DOPENBABEL3_LIBRARIES='/usr/local/lib/libopenbabel.a;/usr/local/lib/libz.a;/usr/local/lib/libzstd.a' \
    .. \
  && make smina -j8

The issue with boost was that some headers were not found and then boost 1.75 leverages std::unary_function which is deprecated and both problems seems fixable by updating CXX.

This script now terminates without errors. However the binaries are not fully static for a reason that I don't understand

$ docker run --rm -t my_image:latest bash -c 'echo "smina: $(ldd /src/smina/build/smina)"; echo "obabel: $(ldd /src/openbabel/build/bin/obabel)"'
smina:  linux-vdso.so.1 (0x00007fff22787000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x000079645392c000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x000079645390a000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000796453736000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x0000796453730000)
        /lib64/ld-linux-x86-64.so.2 (0x0000796453a76000)
obabel:         linux-vdso.so.1 (0x00007fffd8f76000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x000078428278a000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x0000784282768000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000784282594000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x000078428258e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007842828d4000)

I am probably missing something obvious here.

kassane commented 2 weeks ago

Hi @marchelbling-aqemia,

I don't know if you've tried using build.zig. If it's feasible, I'd like to hear your feedback.

https://github.com/allyourcodebase/boost-libraries-zig

marchelbling-aqemia commented 2 weeks ago

@kassane I did not. Tbh, I didn't find many resources on using build.zig for non-Zig code and I didn't want to introduce a new "language" (vs just a compiler) at my workplace. Now that sounds like something I could give it a try on my free time, thank you for suggesting and making this project. I'll let you know how it goes!