0

When target_link_library(foo bar) is called with bar as a target of a SHARED library, CMake will use a static lib libbar.dll.a or bar.lib as an input on Windows. However, MinGW, for example, is capable of linking to a binary file like on Ubuntu. Is it possible to tell cmake to use a dll directly when target_link_library is called?


The obvious workaround is to use generator expressions:
target_link_libraries(foo PRIVATE $<TARGET_FILE:bar>)
Clearly, it has its shortcomings. When you link against a target, you also add its PUBLIC and INTERFACE included directories and linked libraries.

So, in order to fully link in that manner one would have to write something like:

get_target_property(INCLUDE_DIRS bar INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(LINK_LIBS bar INTERFACE_LINK_LIBRARIES)

target_include_directories(foo PRIVATE ${INCLUDE_DIRS})
target_link_libraries(foo PRIVATE ${LINK_LIBS} $<TARGET_FILE:bar>)

So the question is about the possibility to override CMake's default target_link_libraries behavior.

Sergey Kolesnik
  • 3,009
  • 1
  • 8
  • 28
  • _Why_ do you want to do this, rather than letting CMake do what it normally does, given that you already have a first-party, non-IMPORTED, target? _What goes wrong?_ – Alex Reinking Jun 30 '21 at 16:36
  • Also note that a DLL import library is NOT a static library, as you say. Import libraries don't contain code and are just indexes into their associated DLLs. – Alex Reinking Jun 30 '21 at 16:37
  • @AlexReinking why is it NOT a static library? As far as I know it is a general concept to call it a static library. As of *Why I want to do it* - it is because of MinGW not wanting to generate an *import* library. Here is the issue in detail: https://stackoverflow.com/questions/68101661/windows-mingw64-does-not-export-member-static-or-not-functions-defined-within However for the current question I am interested in the possibility to tune cmake's behavior on windows. And "it is not possible" could easily suffice as an aswer. – Sergey Kolesnik Jun 30 '21 at 16:52
  • Because it is _factually_ not a static library. The `.lib` that is generated as part of a shared library on Windows is called an "import library" and it, again, doesn't contain code. It's a build time artifact and is useless on its own. – Alex Reinking Jun 30 '21 at 16:55
  • If your real issue is with symbol visibility, then why not set the `WINDOWS_EXPORT_ALL_SYMBOLS` property on your shared library target? – Alex Reinking Jun 30 '21 at 16:57
  • @AlexReinking it doesn't make any difference. MinGW still would not generate an import library for an executable – Sergey Kolesnik Jun 30 '21 at 17:01
  • Turn on `ENABLE_EXPORTS`. – Alex Reinking Jun 30 '21 at 17:02
  • @AlexReinking I sincerely assure you that I did it all already and **it does not help**. The only thing I haven't tried is using a `.def` file. But it is really ugly when exporting explicit template instantiations. For that topic, please, refer to the link I posted in the comments earlier. This question is explicitly about direct linking to a dll. – Sergey Kolesnik Jun 30 '21 at 17:14

1 Answers1

0

Windows shared object creation works quite differently to on Linux. On Linux, all symbols (functions, classes, etc) are visible to consumers of a shared library by default. On Windows, the opposite is true - nothing is visible by default. This feeds in to shared library creation, because Visual C++ will generate two files per shared library - a .dll and a .lib file. The consumer still links against the lib - not directly against the dll. If you're not explicitly exporting any symbols in your source code, then linking will fail, because no .lib file is generated.

In order to do this, you should basically (on a per-library basis) create a file called "library_api.h" which contains the following:

#ifndef LIBRARY_API_H
#define LIBRARY_API_H

#if defined(WIN32)
  #if defined(MYAPI)
    #define LIBRARY_API __declspec(dllexport)
  #else
    #define LIBRARY_API __declspec(dllimport)
  #endif
#else
  #define LIBRARY_API
#endif

#endif

Then, include that in your header files in which your functions are defined for that library, and prefix them like:

#included "library_api.h"

LIBRARY_API int myfunction();

and in the source file where the definition is, do the same.

Then in CMake, you need to add where the library is made:

add_library(library SHARED source.cpp)
target_compile_definitons(library PUBLIC LIBRARY_API)

Now, the lib file should be generated, and CMake will be able to find it, and hence everything should work.

Note that you can do something similar to the Windows default behaviour on Linux to also hide symbol visibility. You can pass the flag -fvisibility=hidden to GCC, and modify the header slightly using __attribute__ ((visibility ("default"))), and then the behaviour is the same as on Windows. That can be quite useful if your primary development environment is on Linux but you need to build on Windows, because you get the same sort of behaviour and so don't end up with something that works on it but not cross-platform.

  • FYI see https://sourceware.org/binutils/docs/ld/WIN32.html **direct linking to a dll** section – Sergey Kolesnik Jun 30 '21 at 09:24
  • So your issue is only with CMake not picking up the correct file name type? Sorry, I misunderstood. – Ryan Pepper Jun 30 '21 at 09:49
  • No. My issue is that CMake by default uses an `IMPLIB` file of a target. For `SHARED` libraries and executables on Windows it is a `libxxx.dll.a` or `xxx.lib` file, which is a static library. On Ubuntu it would use `lib.so` directly. Since MinGW is capable of direct dll linking, I want CMake to use a `lib.dll` directly on Windows. So I ask if it is possible in CMake at all. – Sergey Kolesnik Jun 30 '21 at 09:54
  • It looks like some of the options suggested here might allow you to modify that behaviour: https://stackoverflow.com/questions/34575066/how-to-prevent-cmake-from-issuing-implib – Ryan Pepper Jun 30 '21 at 11:08
  • Those answers only show how to change output names. That wouldn't help. Making identical names for implib and dll is not an option - in worst case they will just overwrite each other. – Sergey Kolesnik Jun 30 '21 at 11:17