Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linking issue for executables after including doctest in library #224

Closed
bsamseth opened this issue Apr 14, 2019 · 7 comments
Closed

Linking issue for executables after including doctest in library #224

bsamseth opened this issue Apr 14, 2019 · 7 comments

Comments

@bsamseth
Copy link

Note

I suspect this is a simple oversight from my side, and is not belived to be a bug in doctest. This is more of a support question, and I appologise in advance for what probably is a simple fix.

Description

I have written some tests in my library, and successfully compile a test runner executable.

Now I want to make another executable which has nothing to do with tests, and simply uses functionality from the library. However, I cannot seem to get around a linking error which occurs as a result of including doctest in the library.

The error goes like this:

> cmake . -Bbuild
-- The CXX compiler identification is GNU 8.2.0
... (finishes without any issues)

> cmake --build build
Scanning dependencies of target lib
[ 20%] Building CXX object CMakeFiles/lib.dir/lib.cpp.o
[ 20%] Built target lib
Scanning dependencies of target exec
[ 40%] Building CXX object CMakeFiles/exec.dir/exec.cpp.o
[ 60%] Linking CXX executable exec
Undefined symbols for architecture x86_64:
  "doctest::detail::setTestSuite(doctest::detail::TestSuite const&)", referenced from:
      __static_initialization_and_destruction_0(int, int) in lib.cpp.o
  "doctest::detail::TestSuite::TestSuite()", referenced from:
      __static_initialization_and_destruction_0(int, int) in lib.cpp.o
  "doctest::detail::TestSuite::~TestSuite()", referenced from:
      __static_initialization_and_destruction_0(int, int) in lib.cpp.o
  "doctest::detail::TestSuite::operator*(char const*)", referenced from:
      __static_initialization_and_destruction_0(int, int) in lib.cpp.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status
make[2]: *** [exec] Error 1
make[1]: *** [CMakeFiles/exec.dir/all] Error 2
make: *** [all] Error 2

In short, how do I go about setting this up so that users (including myself) of my library can link against it without having to know about doctest?

Steps to reproduce

I've made a minimal example at this repository: https://github.com/bsamseth/doctest-linking-issue
Clone the repository, including submodules, then try the above commands.

Extra information

  • doctest version: master branch (b22d878)
  • Operating System: macOs Mojave 10.14.4
  • Compiler+version: GCC 8.2 and AppleClang 10.0.1.10010046
  • CMake version 3.13.2
@onqtam
Copy link
Member

onqtam commented Apr 14, 2019

If you don't have a test runner implemented somewhere in a binary (.dll/.so/.dylib/.exe) you would get these linker errors - you can use DOCTEST_CONFIG_DISABLE but then why would you include doctest in the first place?

You have tests somewhere, right? The link to the repository you provided is broken - repo seems nonexistent (or is perhaps private).

@bsamseth
Copy link
Author

bsamseth commented Apr 14, 2019

Wow, made the repo private by mistake. Made it public now, hopefully that illuminates things better!
In the example, I have a test in one of the library .cpp files, and another .cpp file which defines the main for doctest. All that works well, but the other .cpp file (with another main function which ignores doctest) is the one that fails. It's probably more clear looking at the repo.

Thanks for the quick response!

@onqtam
Copy link
Member

onqtam commented Apr 14, 2019

@bsamseth seems like you will be writing tests in the library itself - otherwise why would you be including the header there. This is actually what doctest was built for!

But... you will need to supply a test runner even for the exec target, and since you are providing your own main() function you should use DOCTEST_CONFIG_IMPLEMENT somewhere in the exec target - like shown here (and not DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN). You can even skip the actual calling of the tests - but a test runner has to be defined somewhere.

This is the only way if you would want to compile the sources in the library just once (seems that is what you want to achieve with the object library). If you want no test runner in the executable then you would need to define DOCTEST_CONFIG_DISABLE for the entire exec target and that would mean that the .cpp files in the object library will have to be compiled twice - once with DOCTEST_CONFIG_DISABLE for the exec target without a test runner, and once without it for the test executable.

When mixing doctest tests with production code it is assumed that for developer builds there will be a test runner in the production binary, but for final Release builds which will be shipped to customers DOCTEST_CONFIG_DISABLE would be used to remove the tests (100%) and 95%+ of the test runner from the binary.

Does this make sense?

@bsamseth
Copy link
Author

Yes, I wanted to write the tests within the library, as I really like that idea. I also want to avoid having to build the library once per exec., as it takes a while to build the entire thing.

But I think I get it now. I was worried external users would have to add doctest defines in their code, but of course, the version they would be provided could define DOCTEST_CONFIG_DISABLE for them. That makes sense! For my own use I'll just add the required #define DOCTEST_CONFIG_IMPLEMENT and #include "doctest.h" where needed. Got it now!

Thanks for the help. Really liking doctest so far, I'm thinking it'll be my goto going forward :)

@midjji
Copy link

midjji commented Jan 15, 2021

I'm trying to the exact same thing, but am unsure of how you solved it.
I have a library, which is used by a few dozen executables, do I need to include #define DOCTEST_CONFIG_IMPLEMENT in each of them?

Will anyone who depends on the library also have to do so in each and every executable?

@onqtam
Copy link
Member

onqtam commented Jan 17, 2021

@midjji if the tests are always compiled in the library then yes, you will need a test runner linked in every final binary that uses your library.

Is your library static? I assume so.

You could also implement the test runner in the library itself and then users of the library (executables) will only need to create a doctest::Context object and call run() on it to run the tests if they wish to, but they could also not (if they wish not to have a way to run the tests).

You could also have a release configuration in which build your library with DOCTEST_CONFIG_DISABLE (so the tests will actually be disabled) and a test runner inside of it somewhere with DOCTEST_CONFIG_IMPLEMENT (it will be minimal because of DOCTEST_CONFIG_DISABLE) and then users of your library won't need to provide a test runner.

You can also have the test runner in a separate shared object by using DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL so that all executables link to the test runner - have a look at the examples folder.

There are plenty of options - depending on what you want to achieve.

@torokati44
Copy link

Just ran into this exact use case. It's unfortunate that I can't have (only) test cases in DLLs, and then use them from a production binary with no test runner. But I suppose this is a C++ limitation...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants