Search code examples
c++unit-testinglinkerlinker-errorscatch-unit-test

C++ Catch framework 'multiple definition' linker errors with Makefile


Submitting my own answer for Google traffic. Consider the Makefile

  SHELL := /bin/bash                                                              

  run-tests: catch.o                                                              
      for x in *.cpp; do g++ $$x -o $$x.o catch.o && ./$$x.o; done                

  catch.o: catch.hpp                                                              
      g++ main.cxx -o catch.o

I am compiling unit tests for Catch v1.9.5. Compare the below against the example given by their docs:

// main.cxx
#define CATCH_CONFIG_MAIN
#include "catch.hpp"

// meta.cpp
#include "catch.hpp"

int foo(int a) {
    return -a;
}

TEST_CASE("foo", "[example]") {
    REQUIRE(foo(0) == 0);
    REQUIRE(foo(2) == -2);
    REQUIRE(foo(-2) == 2);
}

make yields:

catch.o:(.rodata+0x0): multiple definition of `_IO_stdin_used'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o:(.rodata.cst4+0x0): first defined here
catch.o:(.rodata+0x8): multiple definition of `__dso_handle'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/crtbegin.o:(.rodata+0x0): first defined here
catch.o: In function `_fini':
(.fini+0x0): multiple definition of `_fini'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crti.o:(.fini+0x0): first defined here
catch.o: In function `_start':
(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o:(.text+0x0): first defined here
catch.o: In function `_init':
(.init+0x0): multiple definition of `_init'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crti.o:(.init+0x0): first defined here
catch.o: In function `data_start':
(.data+0x0): multiple definition of `__data_start'
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o:(.data+0x0): first defined here
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/crtend.o:(.tm_clone_table+0x0): multiple definition of `__TMC_END__'
catch.o:(.data+0x98): first defined here
/usr/bin/ld: error in catch.o(.eh_frame); no .eh_frame_hdr table will be created.
collect2: error: ld returned 1 exit status
Makefile:5: recipe for target 'run-tests' failed
make: *** [run-tests] Error 1

Removing reference to catch.o in run-tests causes the linker to complain about undefined references. What's wrong with catch.o and main.cxx that causes the redundant definitions?


Solution

  • The catch.o Make target is missing the -c flag, so GCC went ahead and ran the linker step too early. Attempting to link once more with the actual test suites caused GCC to generate the definitions from Catch a second time, causing the above errors. The fix is to simply add -c to g++ main.cxx -o catch.o so it compiles Catch, but waits to link until it actually has tests to compile.