[Boost.Test] Linker chooses “wrong” main function
Hi, I have a really strange problem. When using Boost.Test, there is generally no need to define a main() function, since Boost.Test provides one itself. I recently had to convert my project to use static linking of 3rd party libraries (on VS2010). Naturally, I had to link to multiple .libs so that the build succeeds, and my build ran just fine. However, when I ran my test project, something really strange happened. It seems that one of the 3rd party .libs (libpng), required by one of my dependent libraries, contained a test file with a main() function defined within (pngtest.c). Since my project did not have a main() function, The linker chose that one as my "test" application. Thus, non of my tests run. Does anyone know how I prevent this from happening? How can I tell the linker/compiler to use the Boost.Test main()? Thanks, Adi
[Please do not mail me a copy of your followup]
boost-users@lists.boost.org spake the secret code
Since my project did not have a main() function, [...]
Well, obviously your project has a main function, otherwise you would
get a link error.
What boost.test does is define a main() function depending on how it
is compiled. In my projects, I have a main.cpp that looks like this:
#define BOOST_TEST_MAIN
#include
The linker chose that one as my "test" application. Thus, non of my tests run.
You don't say how you are linking with Boost.Test, but if you are linking with a static library then what you need to do is make sure Boost.Test is listed in the linker search order before the png library. However, libpng doesn't include a main(): shell 47> nm .libs/libpng15.a | fgrep -i main shell 48> nm .libs/libpng15.so | fgrep -i main So, wherever you're getting your implementation of main(), doesn't come from libpng. If you're on unix, use nm to find out who is supplying main. If you're on Windows, use dumpbin to find out who is supplying main. The key to understanding how to resolve unexpected linker errors is to understand that the linker is really not a complicated program and only does things in response to the command line arguments given to it. The linker only does two things: i) identify unresolved symbols ii) resolve unresolved symbols If you don't supply a definition of a referenced symbol, then i) issues an undefined symbol error. If you violate the one definition rule, then ii) issues a multiply defined symbol error. That you *don't* get a multiple undefined symbol error, but instead get "the wrong main", tells me that however you're linking against Boost.Test you aren't getting a main from it. When linking against libraries, you need to understand how step ii) works: The linker examines each supplied link input, in the order supplied, and looks for an object defining the desired symbol. So when the linker looks for a definition of main (which it always will when you are linking a final executable and not a library), it starts examining the inputs you've given it, one by one, starting at the first and proceeding towards the last until it finds a definition. Since your link succeeded, it found one. Look at the command-line for your linker step (the WHOLE command line, not some simplified view of it) and use nm/dumpbin to examine the inputs one-by-one until you find the one that is supplying main. -- "The Direct3D Graphics Pipeline" free book http://tinyurl.com/d3d-pipeline The Computer Graphics Museum http://computergraphicsmuseum.org The Terminals Wiki http://terminals.classiccmp.org Legalize Adulthood! (my blog) http://legalizeadulthood.wordpress.com
Hi Richard,
Richard
[Please do not mail me a copy of your followup]
boost-users <at> lists.boost.org spake the secret code
thusly:
Since my project did not have a main() function, [...]
Well, obviously your project has a main function, otherwise you would get a link error.
Exactly. Imagine my surprise when my app suddenly started running this particular main()!
What boost.test does is define a main() function depending on how it is compiled. In my projects, I have a main.cpp that looks like this:
#define BOOST_TEST_MAIN #include
and this provides the canned implementation of main().
I didn't know/use this macro, but my code has BOOST_TEST_MODULE, which apparently achieves the same thing.
The linker chose that one as my "test" application. Thus, non of my tests run.
You don't say how you are linking with Boost.Test, but if you are linking with a static library then what you need to do is make sure Boost.Test is listed in the linker search order before the png library.
Actually, I did say that I am using static linking, I guess I was not clear enough. As I said, I just recently switched to static linking, and rebuilt. I didn't actually specify *any *boost libs, as these are (somehow) linked automatically (on VS2010, probably with some #pragma calls). Perhaps this gets done *after* the rest of the explicitly stated libs. When I manually specified - *libboost_unit_test_framework-vc100-mt-s-1_51.lib* for Release - *libboost_unit_test_framework-vc100-mt-sgd-1_51.lib* for Debug The appropriate main was found and all the tests ran as expected. Is there a way to get the correct libs to properly and automatically link before any other libs? Is there some MACRO or other way to get the name of the correct libraries so that I don't need to explicitly state it for each boost version?
However, libpng doesn't include a main():
shell 47> nm .libs/libpng15.a | fgrep -i main shell 48> nm .libs/libpng15.so | fgrep -i main
So, wherever you're getting your implementation of main(), doesn't come from libpng. If you're on unix, use nm to find out who is supplying main. If you're on Windows, use dumpbin to find out who is supplying main.
I'm actually using OpenCV which builds/requires libpng as part of it. Seems like they inadvertently pulled in the test (with *.c) file into the .lib . Here's the begining CMakeLists.txt file: project(${PNG_LIBRARY}) # List of C++ files: ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}" ${ZLIB_INCLUDE_DIR}) file(GLOB lib_srcs *.c) file(GLOB lib_hdrs *.h) and, indeed, when running 'dumpbin libpng.lib /symbols | grep -i main' I get: 0D2 00000000 SECT29 notype () External | _main (No surprise)
The key to understanding how to resolve unexpected linker errors is to understand that the linker is really not a complicated program and only does things in response to the command line arguments given to it.
The linker only does two things: i) identify unresolved symbols ii) resolve unresolved symbols
If you don't supply a definition of a referenced symbol, then i) issues an undefined symbol error.
If you violate the one definition rule, then ii) issues a multiply defined symbol error. That you *don't* get a multiple undefined symbol error, but instead get "the wrong main", tells me that however you're linking against Boost.Test you aren't getting a main from it.
When linking against libraries, you need to understand how step ii) works: The linker examines each supplied link input, in the order supplied, and looks for an object defining the desired symbol.
So when the linker looks for a definition of main (which it always will when you are linking a final executable and not a library), it starts examining the inputs you've given it, one by one, starting at the first and proceeding towards the last until it finds a definition.
Since your link succeeded, it found one. Look at the command-line for your linker step (the WHOLE command line, not some simplified view of it) and use nm/dumpbin to examine the inputs one-by-one until you find the one that is supplying main.
Yes, I understand this :-).
Thanks!
Adi
On Tue, Aug 28, 2012 at 9:20 PM, Adi Shavit
Hi,
I have a really strange problem.
When using Boost.Test, there is generally no need to define a main() function, since Boost.Test provides one itself. I recently had to convert my project to use static linking of 3rd party libraries (on VS2010). Naturally, I had to link to multiple .libs so that the build succeeds, and my build ran just fine.
However, when I ran my test project, something really strange happened. It seems that one of the 3rd party .libs (libpng), required by one of my dependent libraries, contained a test file with a main() function defined within (pngtest.c). Since my project did not have a main() function, The linker chose that one as my "test" application. Thus, non of my tests run.
Does anyone know how I prevent this from happening? How can I tell the linker/compiler to use the Boost.Test main()?
Thanks, Adi
[Please do not mail me a copy of your followup]
boost-users@lists.boost.org spake the secret code
Richard
writes: What boost.test does is define a main() function depending on how it is compiled. In my projects, I have a main.cpp that looks like this:
#define BOOST_TEST_MAIN #include
and this provides the canned implementation of main().
I didn't know/use this macro, but my code has BOOST_TEST_MODULE, which apparently achieves the same thing.
In both these cases it only provides an implementation of main() if dynamic linking to the test library is used: http://www.boost.org/doc/libs/1_50_0/libs/test/doc/html/utf/compilation.html...
Actually, I did say that I am using static linking, I guess I was not clear enough.
Sorry, I missed that. When you said you switched to static linking, I thought you were only referring to 3rd party libraries like libpng.
I didn't actually specify *any *boost libs, as these are (somehow) linked automatically (on VS2010, probably with some #pragma calls).
Yes.
Perhaps this gets done *after* the rest of the explicitly stated libs.
http://msdn.microsoft.com/en-us/library/7f0aews7.aspx says that #pragma comment(lib, "xxx") places a search record entry for xxx at the end of the default search record entries.
Is there a way to get the correct libs to properly and automatically link before any other libs?
It would be better to identify where your errant main() is coming from and prune it at the source.
Is there some MACRO or other way to get the name of the correct libraries so that I don't need to explicitly state it for each boost version?
I'm not aware of anything, but look at the above URL for the #pragma and check the mentioned command-line arguments.
Here's the begining CMakeLists.txt file:
project(${PNG_LIBRARY}) # List of C++ files: ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}" ${ZLIB_INCLUDE_DIR}) file(GLOB lib_srcs *.c) file(GLOB lib_hdrs *.h)
Ugh. PNG already has it's own build logic that handles everything properly; there was no need for OpenCV to go reinvent the libpng build logic. But hey, some people insist on reinventing the wheel because *theirs* is rounder. I'd make a patch to OpenCV that fixes their globby-ness and submit it back to them. -- "The Direct3D Graphics Pipeline" free book http://tinyurl.com/d3d-pipeline The Computer Graphics Museum http://computergraphicsmuseum.org The Terminals Wiki http://terminals.classiccmp.org Legalize Adulthood! (my blog) http://legalizeadulthood.wordpress.com
Richard
Perhaps this gets done *after* the rest of the explicitly stated libs.
http://msdn.microsoft.com/en-us/library/7f0aews7.aspx says that #pragma comment(lib, "xxx") places a search record entry for xxx at the end of the default search record entries.
Got it.
I'd make a patch to OpenCV that fixes their globby-ness and submit it back to them.
Yes. I already submitted a bug report. Thanks, Adi
participants (2)
-
Adi Shavit
-
legalize+jeeves@mail.xmission.com