4

TL;DR

Is there a way to force cmake to use the c++ linker when linking a static c++ library to a c executable?


I have a static library that consists of 2 objects, a C++ file and a C wrapper to the functions in that file (Constructor, Destructor, and Print function), similar to this SO answer. The last paragraph states:

The fun part is now ensuring that you get all the required C++ libraries linked into you larger library correctly. For gcc (or clang) that means just doing the final link stage using g++.

I can verify this with my MCVE. Replacing gcc with g++ fixes the problem and everything works

$ gcc -static main.c -L. -lCPPclass -o main
./libCPPclass.a(CInt.o): In function `newCINT':
CInt.cpp:(.text+0xd): undefined reference to `operator new(unsigned long)'
CInt.cpp:(.text+0x28): undefined reference to `operator delete(void*)'
./libCPPclass.a(CInt.o): In function `delCINT':
CInt.cpp:(.text+0x5e): undefined reference to `operator delete(void*)'
./libCPPclass.a(CInt.o):(.eh_frame+0x13): undefined reference to `__gxx_personality_v0'
./libCPPclass.a(CPPclass.o): In function `CPPclass::print_success()':
CPPclass.cpp:(.text+0x26): undefined reference to `std::cout'
CPPclass.cpp:(.text+0x2b): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
./libCPPclass.a(CPPclass.o): In function `__static_initialization_and_destruction_0(int, int)':
CPPclass.cpp:(.text+0x54): undefined reference to `std::ios_base::Init::Init()'
CPPclass.cpp:(.text+0x63): undefined reference to `std::ios_base::Init::~Init()'
collect2: error: ld returned 1 exit status
$
$#REPLACE gcc with g++
$
$ g++ -static main.c -L. -lCPPclass -o main
$ ./main
Success!

My real code, however, is built using CMake, so I'm trying to get this MCVE built with CMake, which takes me back to my original question. My CMakeLists.txt file is as follows:

cmake_minimum_required(VERSION 2.8)
project(Cmain C CXX)

add_library(CPPclass STATIC IMPORTED)
set_property(TARGET CPPclass PROPERTY IMPORTED_LOCATION ./libCPPclass.a)
add_executable(main main.c)
target_link_libraries(main CPPclass)

However, when I run cmake . and then make I get the same errors as above

$ cmake .
-- Configuring done
-- Generating done
-- Build files have been written to: /home/me/temp
$ make
Linking C executable main
./libCPPclass.a(CInt.o): In function `newCINT':
CInt.cpp:(.text+0xd): undefined reference to `operator new(unsigned long)'
CInt.cpp:(.text+0x28): undefined reference to `operator delete(void*)'

etc. Of course if I renamed main.c to main.cpp, CMake would compile the executable with g++ and target_link_libraries would execute without error, but it kind of defeats the purpose of the c wrapper and doesn't work in my real use case.

Community
  • 1
  • 1
user3288829
  • 1,266
  • 15
  • 26
  • `g++` doesn't use a different linker -- it just adds some default libraries to link to the list of libraries to link. Try adding the `libstdc++` library to your `target_link_libraries`. – Billy ONeal Feb 25 '15 at 20:14
  • @BillyONeal Thanks for clarifying that for me. Your suggestion fixed it for both my MCVE and my real use case. If you think the question is still valid and useful, rewrite your comment as an answer, otherwise I'm willing to retract it. – user3288829 Feb 25 '15 at 20:22
  • feel free to post the specific thing you did in an answer -- you can answer your own things. May help someone in the future. I didn't write it as an answer 'cause I've never actually done it; was just a shot in the dark. – Billy ONeal Feb 25 '15 at 20:23
  • @BillyONeal Will do, just trying to give credit where credit is due. – user3288829 Feb 25 '15 at 20:26
  • 1
    I have enough meaningless internet points :) – Billy ONeal Feb 25 '15 at 20:27

3 Answers3

3

Do this:

set_target_properties(CPPclass PROPERTIES
  IMPORTED_LOCATION ./libCPPclass.a
  IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
)
steveire
  • 10,694
  • 1
  • 37
  • 48
  • Any reasons this is a better answer than the other, or is it just another way of doing things? – user3288829 Feb 26 '15 at 00:20
  • This is the better answer because its what the cmake commands `export()` and `install(EXPORT)` generate, and because it is an abstraction, not an implementation detail (as the stdlib to link is - the g++ driver does much more than just that). This answer is correct. – steveire Feb 26 '15 at 01:24
3

Set linker language to CXX on your target

set_target_properties(my_target PROPERTIES LINKER_LANGUAGE CXX)

I think this is better than the stdc++ answer because it is general (I am currently dealing with clang's libc++). It is maybe not better than the IMPORTED_LINK_INTERFACE_LANGUAGES answer, but it is certainly more direct.

user7610
  • 25,267
  • 15
  • 124
  • 150
0

As @BillyONeal mentioned in the comments above, I don't need a different linker to get the additional libraries needed, I just had to add libstdc++ to the list of libraries to be linked.

cmake_minimum_required(VERSION 2.8)
project(Cmain C CXX)

add_library(CPPclass STATIC IMPORTED)
set_property(TARGET CPPclass PROPERTY IMPORTED_LOCATION ./libCPPclass.a)
add_executable(main main.c)
target_link_libraries(main CPPclass stdc++)
user3288829
  • 1,266
  • 15
  • 26