9

I have two simple files:

runner.cpp:

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Main
#include <boost/test/unit_test.hpp>

and test1.cpp:

#define BOOST_TEST_DYN_LINK
#ifdef STAND_ALONE
#   define BOOST_TEST_MODULE Main
#endif
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE( Foo)

BOOST_AUTO_TEST_CASE( TestSomething )
{
    BOOST_CHECK( true );
}

BOOST_AUTO_TEST_SUITE_END()

To compile, I'm using:

$ g++ -I/e/code/boost_1_52_0 -o runner -lboost_unit_test_framework runner.cpp test1.cpp

I get the following error:

C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text+0x8c): multiple definition of `main'
c:/pdev/mingw/bin/../lib/gcc/i686-pc-mingw32/4.7.2/../../../libboost_unit_test_framework.a(unit_test_main.o):unit_test_main.cpp:(.text.startup+0x0): first defined here
c:/pdev/mingw/bin/../lib/gcc/i686-pc-mingw32/4.7.2/../../../libboost_unit_test_framework.a(unit_test_main.o):unit_test_main.cpp:(.text.startup+0x14): undefined reference to `init_unit_test_suite(int, char**)'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text+0x52): undefined reference to `_imp___ZN5boost9unit_test9framework17master_test_suiteEv'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text+0xb0): undefined reference to `_imp___ZN5boost9unit_test14unit_test_mainEPFbvEiPPc'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text$_ZN5boost9unit_test13test_observerD2Ev[__ZN5boost9unit_test13test_observerD2Ev]+0xe): undefined reference to `_imp___ZTVN5boost9unit_test13test_observerE'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text$_ZN5boost9unit_test13test_observerC2Ev[__ZN5boost9unit_test13test_observerC2Ev]+0xe): undefined reference to `_imp___ZTVN5boost9unit_test13test_observerE'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccU0cDSz.o:runner.cpp:(.text$_ZN5boost9unit_test15unit_test_log_tC1Ev[__ZN5boost9unit_test15unit_test_log_tC1Ev]+0x22): undefined reference to `_imp___ZTVN5boost9unit_test15unit_test_log_tE'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x88): undefined reference to `_imp___ZN5boost9unit_test15unit_test_log_t14set_checkpointENS0_13basic_cstringIKcEEjS4_'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x136): undefined reference to `_imp___ZN5boost10test_tools9tt_detail10check_implERKNS0_16predicate_resultERKNS_9unit_test12lazy_ostreamENS5_13basic_cstringIKcEEjNS1_10tool_levelENS1_10check_typeEjz'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x21d): undefined reference to `_imp___ZN5boost9unit_test9ut_detail24auto_test_unit_registrarC1ENS0_13basic_cstringIKcEE'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x284): undefined reference to `_imp___ZN5boost9unit_test9ut_detail24auto_test_unit_registrarC1EPNS0_9test_caseEm'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text+0x2a4): undefined reference to `_imp___ZN5boost9unit_test9ut_detail24auto_test_unit_registrarC1Ei'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text$_ZN5boost9unit_test14make_test_caseERKNS0_9callback0INS0_9ut_detail6unusedEEENS0_13basic_cstringIKcEE[__ZN5boost9unit_test14make_test_caseERKNS0_9callback0INS0_9ut_detail6unusedEEENS0_13basic_cstringIKcEE]+0x1d): undefined reference to `_imp___ZN5boost9unit_test9ut_detail24normalize_test_case_nameENS0_13basic_cstringIKcEE'
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\cciSdkmB.o:test1.cpp:(.text$_ZN5boost9unit_test14make_test_caseERKNS0_9callback0INS0_9ut_detail6unusedEEENS0_13basic_cstringIKcEE[__ZN5boost9unit_test14make_test_caseERKNS0_9callback0INS0_9ut_detail6unusedEEENS0_13basic_cstringIKcEE]+0x5b): undefined reference to `_imp___ZN5boost9unit_test9test_caseC1ENS0_13basic_cstringIKcEERKNS0_9callback0INS0_9ut_detail6unusedEEE'
collect2.exe: error: ld returned 1 exit status

I'm using g++ 4.7.2 on MinGW, with boost 1.52.0.

I get the same errors when only trying to compile test1.cpp - except the "multiple main definition" one.

I perused the official documentation for quite a while, but its scarce on details regarding linking options. When I compiled the boost libs, besides unit_test_framework, I also got prg_exec_monitor and test_exec_monitor; perhaps I should link these somehow ? I tried many combinations, but all resulted in some kind of undefined reference linker error.

Complete list of boost generated libraries - I have them all in the project root:

libboost_prg_exec_monitor-mgw47-mt-1_52.a
libboost_prg_exec_monitor-mgw47-mt-1_52.dll
libboost_prg_exec_monitor-mgw47-mt-1_52.dll.a
libboost_prg_exec_monitor-mgw47-mt-d-1_52.a
libboost_prg_exec_monitor-mgw47-mt-d-1_52.dll
libboost_prg_exec_monitor-mgw47-mt-d-1_52.dll.a
libboost_test_exec_monitor-mgw47-mt-1_52.a
libboost_test_exec_monitor-mgw47-mt-d-1_52.a
libboost_unit_test_framework-mgw47-mt-1_52.a
libboost_unit_test_framework-mgw47-mt-1_52.dll
libboost_unit_test_framework-mgw47-mt-1_52.dll.a
libboost_unit_test_framework-mgw47-mt-d-1_52.a
libboost_unit_test_framework-mgw47-mt-d-1_52.dll
libboost_unit_test_framework-mgw47-mt-d-1_52.dll.a
Mihai Rotaru
  • 1,953
  • 3
  • 26
  • 28
  • 1
    With g++ you need to put your libraries (-lwhatever) after your source or object files (i.e. `g++ -I/e/code/boost_1_52_0 -o runner runner.cpp test1.cpp -lboost_unit_test_framework`). Link [here](http://www.network-theory.co.uk/docs/gccintro/gccintro_18.html). It's possible you'll also need to add a `-L/path/to/libraries` analogous to your `-I/e/code/boost_1_52_0`. –  Dec 07 '12 at 18:18
  • 1
    [This answer](http://stackoverflow.com/a/2907582/1252091) seems to be very similar to what you are doing. –  Dec 07 '12 at 18:33
  • @llonesmiz indeed, putting the lib after objects works. `-L.` is also needed, and the lib needs to passed as `-lboost_unit_test_framework-mgw47-mt-1_52`. Passing it as `-lboost_unit_test_framework` results in linking errors. However, when I suspected it might not find the lib, I gave something like `-lfoo` and it complained no such lib exists. After consulting [this page](http://www.mingw.org/wiki/HOWTO_Specify_the_Location_of_External_Libraries_for_use_with_MinGW) about default MinGW lib paths, turns out I had another `libboost_unit_test_framework.a` in one of those folders. – Mihai Rotaru Dec 07 '12 at 20:59
  • @llonesmiz if you could summarize these issues in an answer I'll gladly accept it. – Mihai Rotaru Dec 07 '12 at 21:01
  • my advice only took you half the way. Put the answer yourself and I'll upvote it. –  Dec 08 '12 at 08:16
  • @llonesmiz - done. A bit longer than I expected, but I did learn quite a few useful things and though I'd keep them here for future reference. – Mihai Rotaru Dec 08 '12 at 13:03

1 Answers1

24

With help from @llonesmiz, a number of issues were identified.

1. Libraries need to be specified after objects and sources which use them.

As described here:

The traditional behavior of linkers is to search for external functions from left to right in the libraries specified on the command line. This means that a library containing the definition of a function should appear after any source files or object files which use it. This includes libraries specified with the short-cut -l option, as shown in the following command:

$ gcc -Wall calc.c -lm -o calc (correct order)

With some linkers the opposite ordering (placing the -lm option before the file which uses it) would result in an error,

$ cc -Wall -lm calc.c -o calc (incorrect order)
main.o: In function 'main':
main.o(.text+0xf): undefined reference to 'sqrt'

because there is no library or object file containing sqrt after ‘calc.c’. The option -lm should appear after the file ‘calc.c’

2. Library paths should be explicitly specified.

If no lib paths are specified, the linker might look for the libs in a series of default folders, thus loading a different library then intended. This is what happened in my case - I wanted to link boost_unit_test_framework, but did not specify a path because I assumed the linker would look in the current folder. That's what happens at runtime, after all - if the dll is in the same folder with the exe, it will find it.

I found it a little bit strange the linker would find the lib, since it was named ibboost_unit_test_framework-mgw47-mt-1_52.dll. When I tried to link to a non-existing lib, the linker complained though, so I assumed this isn't an issue, and MinGW 's linker ignores those suffixes.

After some more research, I found this article about MinGW library paths. The folders MinGW searches for libs can be found in the output of gcc -print-search-dirs. The article also contains some bash magic to make sense of that output:

gcc -print-search-dirs | sed '/^lib/b 1;d;:1;s,/[^/.][^/]*/\.\./,/,;t 1;s,:[^=]*=,:;,;s,;,;  ,g' | tr \; \\012 | grep -v '^ */'

This will print a nice list of those folders. gcc will not, by default, look in the current directory for libs. I looked in each of them, and found the lib that was being loaded - libboost_unit_test_framework.a, a static lib.

This brings into light another issue worth mentioning:

3. Static versus dynamic linking

I did not specify whether I want boost_unit_test_framework linked statically or dynamically. In this case, gcc prefers dynamic linking:

Because of these advantages gcc compiles programs to use shared libraries by default on most systems, if they are available. Whenever a static library ‘libNAME.a’ would be used for linking with the option -lNAME the compiler first checks for an alternative shared library with the same name and a ‘.so’ extension.

(so is the extension for dynamic libraries on Unix - on Windows, the equivalent is dll.)

So, what happened is that gcc looked for libboost_unit_test_framework.dll in all it's default folders, but couldn't find it. Then it looked for libboost_unit_test_framework.a, and statically linked that. This resulted in linking errors because the sources have #define BOOST_TEST_DYN_LINK, and therefore expect to have the lib dynamically linked.

To enforce static or dynamic linking, the -Wl,-Bstatic and -Wl,-Bdynamic linker options come into play, described here.

If I tell the linker that I want dynamic linking:

$ g++ -I/e/code/boost_1_52_0 runner.cpp test1.cpp -o runner -Wl,Bdynamic -lboost_unit_test_framework

This will fail, because the linker will not be able to find the dll.

4.Summary

The issues were:

  1. libraries where specified before the sources which used them
  2. the lib path wasn't specified
  3. the type of linking wasn't specified
  4. the name of the library was not correct

Final, working command:

$ g++ -I/e/code/boost_1_52_0 -o runner runner.cpp test1.cpp -L. -Wl,-Bdynamic -lboost_unit_test_framework-mgw47-mt-1_52
Community
  • 1
  • 1
Mihai Rotaru
  • 1,953
  • 3
  • 26
  • 28
  • 1
    Excellent answer. Was the problem of the multiple definition of main also solved with this? I plan to use something similar in the near future and I'm sure this will be of great help. –  Dec 08 '12 at 13:41
  • @llonesmiz, yes it was. I'm still not sure why it occurred; couldn't reproduce with the newer libs. – Mihai Rotaru Dec 08 '12 at 14:20
  • Your directory shows two version of each library. One with a `-d-` in the middle of the name and one without. For example, `libboost_unit_test_framework-mgw47-mt-1_52.a` and `libboost_unit_test_framework-mgw47-mt-d-1_52.a`. How did you know which of those to use? – Brick Mar 14 '17 at 17:55
  • @Brick if I remember correctly, the difference is that the ones with `-d-` include debugging symbols - but I'm not sure to be honest. – Mihai Rotaru Mar 14 '17 at 20:44