буст-тест — ошибки «неопределенная ссылка»

У меня есть два простых файла:

runner.cpp:

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

и 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()

Для компиляции я использую:

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

Я получаю следующую ошибку:

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

Я использую g ++ 4.7.2 на MinGW, с надстройкой 1.52.0.

Я получаю те же ошибки, когда только пытаюсь скомпилировать test1.cpp — кроме «множественного основного определения».

Я долгое время просматривал официальную документацию, но в ней мало информации о возможностях ссылок. Когда я скомпилировал буст-библиотеки, кроме unit_test_frameworkЯ тоже получил prg_exec_monitor а также test_exec_monitor; возможно я должен связать это как-то? Я перепробовал много комбинаций, но все они привели к какой-то неопределенной ошибке ссылочного компоновщика.

Полный список буст-сгенерированных библиотек — у меня есть все они в корне проекта:

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

6

Решение

С помощью @llonesmiz, был выявлен ряд проблем.

1. Библиотеки должны быть указаны после объекты и источники, которые их используют.

Как описано Вот:

Традиционное поведение компоновщиков заключается в поиске внешних функций из
слева направо в библиотеках, указанных в командной строке. Это означает, что
библиотека, содержащая определение функции, должна появляться после любого источника
файлы или объектные файлы, которые его используют. Это включает в себя библиотеки, указанные с
опция short-cut -l, как показано в следующей команде:

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

С некоторыми компоновщиками обратный порядок (размещение опции -lm перед файлом
который использует его) приведет к ошибке,

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

потому что нет библиотеки или объектного файла, содержащего sqrt после «calc.c».
опция -lm должна появиться после файла «calc.c»

2. Библиотечные пути должны быть указаны явно.

Если пути к библиотекам не указаны, компоновщик может искать библиотеки в серии
папок по умолчанию, таким образом, загружая другую библиотеку, чем предполагалось. Это то, что
случилось в моем случае — я хотел связать boost_unit_test_framework, но не сделал
указать путь, потому что я предполагал, что компоновщик будет смотреть в текущей папке.
Это то, что происходит во время выполнения, в конце концов — если dll находится в той же папке
с exe, он найдет это.

Мне показалось немного странным, что компоновщик найдет библиотеку, так как это было
названный ibboost_unit_test_framework-mgw47-mt-1_52.dll, Когда я пытался связаться с
компоновщик пожаловался на несуществующую библиотеку, поэтому я предположил, что это не
выпускать и MinGW Линкер игнорирует эти суффиксы.

После еще нескольких исследований я обнаружил, эта статья о путях библиотеки MinGW.
Папки, которые MinGW ищет для поиска библиотек, можно найти в выводе gcc -print-search-dirs,
Статья также содержит некоторые bash магия, чтобы понять смысл этого вывода:

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

Это напечатает хороший список этих папок. gcc будут не, по умолчанию,
посмотрите в текущем каталоге для библиотек. Я посмотрел в каждом из них, и нашел
загружаемая библиотека — libboost_unit_test_framework.aстатическая библиотека.

Это выявляет еще одну проблему, о которой стоит упомянуть:

3. Статическое и динамическое связывание

Я не уточнил, хочу ли я boost_unit_test_framework связаны статически или динамически.
В этом случае, gcc предпочитает динамическое связывание:

Из-за этих преимуществ gcc компилирует программы для использования общих библиотек
по умолчанию на большинстве систем, если они доступны. Всякий раз, когда статическая библиотека
«LibNAME.a» будет использоваться для связи с опцией -lNAME компилятора
сначала проверяет альтернативную разделяемую библиотеку с тем же именем и символом «.so»
расширение.

(so расширение для динамических библиотек в Unix — в Windows эквивалент dll.)

Итак, что случилось, что gcc искал libboost_unit_test_framework.dll
во всех папках по умолчанию, но не смог найти его. Затем он искал
libboost_unit_test_framework.aи статически это связано. Это привело к
ошибки связывания, потому что источники имеют #define BOOST_TEST_DYN_LINK, а также
поэтому ожидайте, что библиотека будет динамически связана.

Для обеспечения статического или динамического связывания -Wl,-Bstatic а также -Wl,-Bdynamic
варианты линкера вступают в игру, описаны Вот.

Если я скажу компоновщику, что я хочу динамическое связывание:

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

Это не удастся, потому что компоновщик не сможет найти dll,

4.Summary

Проблемы были:

  1. библиотеки, где указаны до источников, которые использовали их
  2. путь к lib не указан
  3. тип ссылки не указан
  4. название библиотеки было неверным

Итоговая, рабочая команда:

$ 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
18

Другие решения

Других решений пока нет …