6

I am using CMake 3.8.2, GNU make 4.2.1 and GCC 6.4.0 for my C++14 project and I noticed a strange behavior when building. I am using CMake for an out-of-source build in a sub-folder called "build" where I run cmake .. followed by make.

CMake runs fine without any errors and make will build all source files like I expect until it is done compiling and starts linking them. It will then fail with an error

[ 83%] ...
[100%] Linking CXX executable myproject
/usr/bin/ld: some-source-file.cc.o: undefined reference to symbol '_ZNKSt7__cxx1118basic_stringstreamIcSt11char_traitsIcESaIcEE3strEv@@GLIBCXX_3.4.21'

Interestingly it doesn't show any compiler warnings up to this point and only shows the above mentioned linker error.

Now when I ignore the error and simply run cmake .. and then make again (just like I did before) I get all the compiler warnings that my code should produce and everything links perfectly fine, even though I didn't change any code or CMake-related files in the meantime.

I can reproduce this behavior by deleting all files in the build dir by running rm -r *.

Here is my CMakeLists.txt file:

# Define minimum required CMake version
cmake_minimum_required(VERSION 3.8.2)

# Setting compiler related settings
set(CMAKE_CXX_COMPILER "${CMAKE_SOURCE_DIR}/toolchain/binary/gcc-6.4.0/bin/gcc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion -O2 -lstdc++")
set(CMAKE_CXX_STANDARD 14)

# Define project name
project(MyProject)

# Find source files
file(GLOB_RECURSE SOURCES application/*.cc)

# Adding third-party sources
set(SOURCES ${SOURCES} "third-party/cpp-base64/base64.cpp")

# Executable to be built from which source files
add_executable(myproject ${SOURCES})

# Find and include and link Botan
find_library(BOTAN botan-2 "third-party/botan/build/lib")
include_directories("third-party/botan/build/include/botan-2")

# Includes that are part of the project
include_directories("${CMAKE_SOURCE_DIR}/application/include")

# Include nlohmann/json
include_directories("third-party/json/src")

# Include cpp-base64 by René Nyffenegger
include_directories("third-party/cpp-base64")

find_package(Boost REQUIRED COMPONENTS program_options)
if(Boost_FOUND)
  include_directories(${Boost_INCLUDE_DIRS})
endif()

# Link third-party libraries
target_link_libraries(myproject ${Boost_LIBRARIES} ${BOTAN})

Note: I am required to check-in the compiler and libraries I am using, which is why I specified them in the CMake file.

comfreak
  • 158
  • 1
  • 7
  • Maybe try to use `link_library(stdc++)` instead of passing the "-lstdc++" compiler flag. It is probable that cmake perform the compilation in 2 steps, one for compiling an object file using `gcc`, and a second to link using `ld`. The `-lstdc++` is passed to `gcc` while it is usefull to `ld`. Using link_library solve this problem. – Oliv Dec 06 '17 at 22:34
  • @Oliv `link_library()` is unknown to my version of CMake, but `link_libraries()` exists. If I use that, CMake will run successfully, but I still get the same behavior, except that the second make now fails with the same error like in the first run. – comfreak Dec 06 '17 at 22:39
  • This should not, try to define CMAKE_EXE_LINKER_FLAGS to stdc++, – Oliv Dec 06 '17 at 22:50
  • @Oliv I added `set(CMAKE_EXE_LINKER_FLAGS "-lstdc++")` but it's still the same original behavior. – comfreak Dec 06 '17 at 22:55
  • This is not normal, this symbol is not exported by libstdc++.so. May be you could grep "extern template" on your source. – Oliv Dec 06 '17 at 23:18
  • @Oliv what exactly do you mean by "grep extern template"? – comfreak Dec 07 '17 at 09:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160702/discussion-between-oliv-and-comfreak). – Oliv Dec 07 '17 at 10:27

1 Answers1

3

If it only works the second time it has to do with cached variables.

So I'm pretty sure that it will work the first time if you modify CMAKE_CXX_COMPILER setting by adding set(... CACHE INTERNAL "") to:

set(CMAKE_CXX_COMPILER "${CMAKE_SOURCE_DIR}/toolchain/binary/gcc-6.4.0/bin/gcc" CACHE INTERNAL "")

And move set(CMAKE_CXX_FLAGS ...) after the project() command.

But please also be noted that you shouldn't put the compiler into your CMakeLists.txt.

References

Florian
  • 39,996
  • 9
  • 133
  • 149
  • In the chat with @Oliv I figured out that my CMake file caused my code to be compiled with the compiler that I declared in the CMake file but linked with the system's libraries which apparently don't match. When I remove the corresponding line from the CMakeLists file, everything compiles on the first run. However, I still only see compiler warnings on the second run. Is this normal? -- Regarding your last sentence, what is a better way to define the compiler settings than putting them in the CMakeLists file? – comfreak Dec 07 '17 at 11:30
  • 1
    @comfreak Ok. The compilers are normally set from the outside. Either with [environment variables](https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html) or [toolchain files](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html). In your case I would probably just reduce your `PATH` environment variable to just point the the compiler toolchain you want and then call `cmake ..`. – Florian Dec 07 '17 at 11:39
  • I do want to mention that simply moving the `project()` command right after defining the minimum version (and therefore before any compiler settings) solved the issue and the code now successfully builds on the first try, including all the compiler warnings I was missing. Thanks! - And thanks for the links, I will check them out too. ;-) – comfreak Dec 07 '17 at 12:13