Cmake Linking Against Shared Library on Windows: Error About Not Finding .Lib File

CMake linking against shared library on windows: error about not finding .lib file

Ah, my problem was I forgot to include a __declspec(dllexport) in suitable places when building the library (can you tell I don't do windows programming a lot?).

Cmake Linking Shared Library: No such file or directory when include a header file from library

You have a couple of issues here.

Propagating headers to users of your target:

Whilst you've added the include file to your library target, you need to let consumers of your library target know how to find the header.

As such, when your app myapp links against your library target test, you need to tell cmake to add ./include to myapp's include search path.

There is a special cmake variable, ${CMAKE_CURRENT_LIST_DIR} which resolves to the path to the directory in which the current CMakeLists.txt being processed is.

In your instance, that is the parent folder of both src and include.

./                    <-- ${CMAKE_CURRENT_LIST_DIR} is this directory
+--- CMakeLists.txt
+--- src/
| +---Test.cpp
| +---ITest.cpp
+--- include/
+---Test.hpp
+---ITest.hpp

In order to tell cmake to add a path to its include search path you use target_include_directories

For this the path will then be ${CMAKE_CURRENT_LIST_DIR}/include

So the syntax you'd be looking for is:

target_include_directories(test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)

Note that this means you don't have to add "include/iTest.hpp" and "include/Test.hpp" to your SRC_LIST glob, as the compiler will be able to find them from the above target_include_directories

Linking to your test library:

Now that you've created your library and added the include directories, to actually use it in your app, you should again use target_link_libraries, but don't specify the path to the generated .so file, instead refer to the name of the library target you created, test

target_link_libraries(myapp test)

Now myapp will know how to find Test.hpp because it will get that information from the "dependency link" you've created between myapp and test

As such, assuming the following directory structure, the following CMakeLists.txt files may work

src/
+--- library/
| +--- < sources for your shared library >
+--- app/
+--- < sources for your application >

src/CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(myapp)

add_subdirectory(library)
add_subdirectory(app)

src/library/CMakeLists.txt

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} 
-std=c++11
-pthread
-O3
-Wall
-ftree-vectorize
-ffast-math
-funroll-loops")

find_package(OpenCV REQUIRED)

add_library(test SHARED "src/iTest.cpp src/Test.cpp")
target_link_libraries(test ${OpenCV_LIBS}) // link opencv libs to libtest.so
target_include_directories(test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)

src/app/CMakeLists.txt

add_executable(myapp main.cpp)
target_link_libraries(myapp test)

missing .lib file when creating shared library with cmake and visual studio 2019 generator

Faced the same problem, I found the solution here: for Visual Studio to export symbols in a .lib file besides the .dll library, you need to set this in your CMake (version>= 3.4) script:

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

Note that the .lib file created this way is a small size file and is not a static library.

CMake manual

Cmake cannot find library using link_directories

Do not use link_directories like this in CMake.

This is a common beginner's mistake, as many other build environments work like this, but in CMake it's just asking for trouble. Even the official documentation specifically advises against it:

Note that this command [link_directories] is rarely necessary. Library locations returned
by find_package() and find_library() are absolute paths. Pass these
absolute library file paths directly to the target_link_libraries()
command. CMake will ensure the linker finds them.

So instead, always pass absolute paths to target_link_libraries and use find_library to resolve the link directory:

find_library(PROTOBUF_LIBRARY protobuf HINTS /usr/lib/x86_64-linux-gnu)
target_link_libraries(test PUBLIC ${PROTOBUF_LIBRARY})

This has the huge benefit that you will probably get a diagnostic at CMake configure time if the expected library cannot be found, instead of a random linker error at compile time. Also, this allows the user to specify a library location via the GUI if the target machine has a non-standard directory layout.

So if it doesn't work right away, be sure to check the result of the find_library call and consult the official documentation to track down why it doesn't find your library as intended.

CMake link shared library on Windows

There are differences between dynamic library linking on different platforms which also needs some additional code. The good news is, that CMake can help you with this. I found the following blog post by Gernot Klingler very useful:

  • Creating and using shared libraries with different compilers on different operating systems

In short you need some "export prefix" defined for whatever is declared in m.h. Otherwise the build process will not generate an "import library" for statically linking named m.lib (see also CMAKE_IMPORT_LIBRARY_SUFFIX).

Here is your code with the modifications needed:

m.h

#include "m_exports.h"

int M_EXPORTS m();

m.c

#include "m.h"
#include <stdio.h>

int m(){
printf("Hello,m!\n");
return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)

include(GenerateExportHeader)

PROJECT("app1")

INCLUDE_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}")
ADD_LIBRARY(m SHARED m.c m.h m_exports.h)
GENERATE_EXPORT_HEADER(m
BASE_NAME m
EXPORT_MACRO_NAME M_EXPORTS
EXPORT_FILE_NAME m_exports.h
STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC)

ADD_EXECUTABLE(myexe main.c)
TARGET_LINK_LIBRARIES(myexe m)

Additional References

  • GenerateExportHeader macro
  • cmake and GenerateExportHeader
  • How do I get CMake to create a dll and its matching lib file?
  • MSDN: Walkthrough: Creating and Using a Dynamic Link Library (C++)

Linking with CMakeLists: ld cannot find library

That's because when linking, the linker doesn't look in the current directory but only in a set of predefined directories.

You need to tell CMake where the library is, for example by giving the full path to the library in the target_link_library command, or adding it as an imported library.



Related Topics



Leave a reply



Submit