2

For my CMake project i have an external library, consisting of a header file and several .lib/.dll pairs.

The header file selectively links to one of the .lib/.dll pair, take this example:

#ifdef DEBUG
    #pragma comment(lib "exampled.lib")
#elif
    #pragma comment(lib "example.lib")
#endif

In full, theres a .lib and matching .dll for 32/64 bit and Debug/Release, so 4 pairs in total. Inside the header file is the proper #ifdef-logic to link the right library. As i said it's an external library, so i don't want to change that header.

What is the right way to teach this to CMake?

For compile time (i.e. include directories) i can use target_include_directories() which works fine. I can also create an imported target with an interface include directory, this also works fine.

The problem starts at link-time:

  • target_link_libraries() forces me to specify one of the .lib/.dll files. I could duplicate the #ifdef logic, but this feels wrong. Adding all of the files is incorrect as well, as always only one is needed.
  • Imported targets allow me to add .dll and .lib files as well, but again, i would either have to duplicate the logic or link all libraries.
  • link_directories() works on a global scale. Which feels unnecessary if only a few targets actually need it.
  • adding the given directory to the PATH also seems "to global", i.e. since the library is currently distributed along with the code.

So what i am looking for is something like target_link_directories() or similar solutions. Obviously to actually load the .dll at runtime i would have to take further steps, so if your solution includes this, it would be very welcome.

Anedar
  • 4,235
  • 1
  • 23
  • 41
  • Is passing the lib-name through a `-D` parameter to the compiler an option? – Stephan Lechner Jan 12 '18 at 18:24
  • Doesn't that also include duplicating the logic in the header file? Library names are different for 32 and 64 bit e.g. – Anedar Jan 12 '18 at 18:29
  • #pragma comment(lib...) is a toolchain item, nothing at all to do with the build system (other than making sure the file can be found), why do you feel you need to tell Make any thing more than the library search path? Unless you are also trying to port this to a compiler-linker pair that doesn't understand #pragma comment(lib...) but that sounds a lot harder than just using CMake. The only thing I can see is that you may need to add /D command line options based on build configuration. – SoronelHaetir Jan 12 '18 at 18:44
  • @SoronelHaetir It's exactly what i am looking for: Telling CMake the library search path in a target specific way instead of a global one like `link_directories()` – Anedar Jan 12 '18 at 23:17

4 Answers4

1

target_link_libraries() forces me to specify one of the .lib/.dll files

This is not true. You can provide keywords to indicate what build type the libraries belong to.

From the CMake documentation on target_link_libraries

A debug, optimized, or general keyword indicates that the library immediately following it is to be used only for the corresponding build configuration. The debug keyword corresponds to the Debug configuration (or to configurations named in the DEBUG_CONFIGURATIONS global property if it is set)

Example: target_link_libraries( my_target optimized example.lib debug exampled.lib

This is then controlled by the CMAKE_BUILD_TYPE variable. See the cmake documentation for more info on this variable

As for 32-bit vs 64-bit you will need to add if-blocks checking the architecture, I'll admit this is a limitation.

mascoj
  • 1,313
  • 14
  • 27
  • Those keywords are nice, but it's still somehow "duplicating the logic already present in the header file". But unless someone can come up with a clever solution to only provide the search path, thats probably what i have to do. – Anedar Jan 12 '18 at 23:19
1

To check for 32 vs 64 bit variations, you can use CMAKE_SIZEOF_VOID_P EQUAL 8

if(CMAKE_SIZEOF_VOID_P EQUAL 8)
// Set 64 bit stuff
else()
// Set 32 bit stuff
endif(CMAKE_SIZEOF_VOID_P EQUAL 8)

And since mascoj already pointed out what I was going to, that target_link_libraries accepts general, optimized, and debug keywords and that should help get whatever libraries you need.

Chris
  • 2,254
  • 8
  • 22
1

Keeping the #pragmas is asking for trouble. You should deactivate them if possible.

There are a lot of libraries that do this (eg. Boost on Windows), but this really doesn't make sense when used with CMake. These pragmas are non-standard and will not work on most compilers. The whole point of a CMake build though is to be portable to all platforms, so you will need to move the logic for linking to CMake anyway if you want to be portable. Even if you don't care about portability, CMake builds usually don't work very well with such files (as you experienced yourself). CMake is responsible for the build, so you should move all concerns of linking to CMake.

So first thing is to find a way to deactivate the pragmas. Usually, this can be done through a preprocessor define or similar (for Boost, you could target_link_libraries(... Boost::disable_autolinking ), which will set the required preprocessor defines).

You will need to duplicate the logic that you now have in the header file, yes. This is just how it works, the build system needs to know what to link against. Using generator expressions, you can still hide all of that away in a single imported target, so at least it will be convenient to use.

There is no good solution to the .dll problem. If you want to have the dlls available in the build directory, you need to manually copy them there. CMake does not offer any special support here, but if you already found the .libs for linking, finding the .dlls is usually straightforward.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
0

link_directories() doesn't work for me at all for #pragma comment (lib)

To search pragma's lib I use

if (MSVC)
 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}"
     "/LIBPATH:<path to my libs>")
endif()

For example

if (MSVC)
     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" 
         "/LIBPATH:${CMAKE_CURRENT_BINARY_DIR}/../src_lib/")
endif()