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

Building on Win32 / Visual Studio generates a .dll with no exports #57

Open
olivier-fs opened this issue Nov 19, 2022 · 6 comments
Open

Comments

@olivier-fs
Copy link

Hi!
I succesfuly built fmtlog with CMake on a Windows 10 box, with CMake 3.25.0 and Visual Studio 2019.
Targets fmtlog-static.lib and fmtlog-shared.dll are generated.
Tests targets enc_dec_test.exe, log_test.exe, multithread_test.exe are generated linking against the static library.

But:

fmtlog-shared.dll has no exported symbols, because they are actually not exported i.e. no FMTLOG_API that resolves to __declspec(dllimport) / __declspec(dllexport), with some FMTLOG_EXPORTS compile symbol defined when building the DLL.
(So there is no fmtlog-shared.lib because no symbol is exported)

I know... Stupid Windows linker, GNU ld does not need this...

@MengRao
Copy link
Owner

MengRao commented Nov 22, 2022

Actually it's not mandatory to build shared library, and it's easiler to go with the header-only way: just include fmtlog-inl.h in any of your source files and no library is needed.

@olivier-fs
Copy link
Author

Sure!

But the fmtlog-shared target in CMakeLists.txt is not flaged by an option() that would be enabled by some -D flag in cmake.

If this target is not usable with MSVC may be it should not be enabled/built by default ?

Because then it looks as some linker/config error mad it have no exports... only when reading the actual code does one understand that it can't have exports.

Or add the FMTLOG_API/FMT_EXPORTS classif stuff in MSVC ?

Then when upgrading fmtlog one could choose to only rebuild the DLL. Header only libraries are very useful for sure, but then one must rebuild all targets when upgrading (I already have to do this with inja, nlohmann/json, flatbuffers, etc.)

@MengRao
Copy link
Owner

MengRao commented Nov 28, 2022

For the header only version, actually fmtlog has two files: fmtlog.h: the real header file, and fmtlog-inl.h: the implementation file(can be seen as fmtlog.cc, need to be compiled once but can be included in multiple targets like header files for convenience. It's recommended to include it in one of your source files or add it to your project CMakeLists as a source file, then it's compiled only once). If fmtlog.h is changed, of course all targets need to be rebuilt even if static/shared library is used, but upgrading should be infrequent as fmtlog has became stable.

@gh-andre
Copy link

@olivier-fs

Then when upgrading fmtlog one could choose to only rebuild the DLL. Header only libraries are very useful for sure, but then one must rebuild all targets when upgrading

Even with old-fashioned typical DLLs whose headers contained only function signatures and data structures you always need to rebuild all dependent targets when DLL libraries were upgraded because any of those structures or signatures may have changed with a new DLL release. In rare cases you could get away without rebuilding your targets, typically with C libraries, if you looked up exported functions, but with C++ libraries you pretty much have to rebuild your targets or you'd be seeing all sorts of binary compatibility issues.

A fmtlog DLL would come in handy for a different reason - if you have multiple app components built as DLLs, all using fmtlog, then you would have multiple copies of fmtlog, which is something you'd want to avoid by having it in a DLL or having your own DLL wrapper. The latter would be problematic at this point because fmtlog captures source location at the point where one of its macros was used, which would make all log lines originating from the wrapper DLL.

@olivier-fs
Copy link
Author

Agreed, but
-Wouldn't the macros in the .h still emit the right source location even if the actual impl would be in a DLL ?

  • Obviously if the DLL interface is changed, everything has to be rebuilt ; but I was thinking about security fixes : Sometimes you only need to bring patches/fixes in the .cpp, with no change in the interface.
  • Rebuilding apps / libs implies you redo all testing : I sadly have to admit that there are many use cases for me where not everything will be tested again just for "light" changesets like logging upgrades... Even in big corporationss I work for that still have insufficient CI/CD tools...

Header-only fmtlog is fine for me.
But then I don't understand the purpose of the fmtlog-shared target in CMakeLists.txt.

@gh-andre
Copy link

Wouldn't the macros in the .h still emit the right source location even if the actual impl would be in a DLL ?

These macros capture logger's locations in any setting where the logger is implemented as a separate component, which is typical for server-type applications.

Consider an application that has multiple DLLs, all need to log. If fmtlog is linked in as a static library in these DLLs, each DLL gets its own copy of fmtlog, which means each has its own set of globals and manages its own, possibly conflicting log files, if these DLLs use the same configuration.

A solution to this would be to have a logger DLL, which uses fmtlog to implement logging. This means that logging macros are only replaced within this logger DLL and not in actual source locations - hence the problem. I resolved it by splitting FMTLOG_ONCE to capture the location and pass it into fmtlog, but now I have to merge my changes when I want to take a new version.

but I was thinking about security fixes : Sometimes you only need to bring patches/fixes in the .cpp, with no change in the interface.

Rebuilding apps / libs implies you redo all testing

Dropping in a new DLL may work for internal builds, but a security release from another project will have a different patch number, so you would have to be mindful of their recommendations whether it changes the interface in terms of binary compatibility.

In rare special cases you can advise your production guys or app users to drop in a fixed DLL, but in most cases getting a new set of headers, import libraries and DLLs means your CI/CD pipeline runs and produces a new package, so you have to test it anyway. How much, is another question.

But then I don't understand the purpose of the fmtlog-shared target in CMakeLists.txt.

It is intended for Linux builds. The fmtlog source doesn't have export declarations, as you pointed out earlier, so it won't generate any exported symbols in a DLL.

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

3 participants