2

I am trying to build a cross-platform project using CMake and Visual C++ 2017 toolchain.

CMakeLists.txt:

cmake_minimum_required(VERSION 3.0.0)

project(CMakeLibTest)

add_executable(mainApp App.cpp)

target_include_directories(mainApp PRIVATE ${PROJECT_SOURCE_DIR}/../Lib)
target_link_libraries(mainApp -L${PROJECT_SOURCE_DIR}/../Win32/Debug -lLib)

Lib.lib is some static library. It is located in the folder ../Win32/Debug relative to the location of CMakeLists.txt and App.cpp.

When I start the project build I see strange options in the linker command line:

-LC:/Users/UserName/source/repos/CMakeLibTest/App/../Win32/Debug -lLib.lib

The linker cannot recognize these options and shows warnings:

warning LNK4044: unrecognized option '/LC:/Users/UserName/source/repos/CMakeLibTest/App/../Win32/Debug'; ignored
warning LNK4044: unrecognized option '/lLib.lib'; ignored

And finally it fails:

error LNK2019: unresolved external symbol "void __cdecl f(void)" (?f@@YAXXZ) referenced in function main

Expected correct linker command line options:

/LIBPATH:"C:\Users\UserName\source\repos\CMakeLibTest\Win32\Debug\" "Lib.lib"

What I am doing wrong? What is the correct way to link libraries in CMake compatible with Visual Studio? Or maybe it is a bug in CMake?

Kevin
  • 16,549
  • 8
  • 60
  • 74
Andrey Epifantsev
  • 976
  • 1
  • 11
  • 27
  • 1
    No need to specify `-l` flag in `target_link_libraries`, especially for Visual Studio where options `-l` (and `/l`) are meaningless. Just use `target_link_libraries(mainApp lib)`. Similar about the link directory: options `-L` and `/L` are meaningless for Visual Studio. Use `link_directories(${PROJECT_SOURCE_DIR}/../Win32/Debug)` instead. See [that question](https://stackoverflow.com/questions/8774593/cmake-link-to-external-library) about possible ways for link to external libraries in CMake. – Tsyvarev Jul 26 '19 at 08:57
  • link_directories is not recommended to use: "Note This command is rarely necessary and should be avoided where there are other choices." https://cmake.org/cmake/help/v3.13/command/link_directories.html – Andrey Epifantsev Jul 26 '19 at 11:20

1 Answers1

1

@Tsyvarev is correct; there is no need to bother with the -l or -L flags when using target_link_libraries in CMake. The other linked question here proposes making the other DLL dependency an imported library. Here is what it would look like for your example:

# Add the static library 'Lib.lib', marking it as an IMPORT.
add_library(MyLib STATIC IMPORTED)
# Define the location of the library dependency.
set(MYLIB_FILE_PATH "C:/Users/UserName/source/repos/CMakeLibTest/Win32/Debug/Lib.lib")
# Tell CMake where to find the library.
set_target_properties(MyLib PROPERTIES IMPORTED_LOCATION ${MYLIB_FILE_PATH})

Note that if Win32 is your CMake build directory, you can just use CMAKE_BINARY_DIR or CMAKE_CURRENT_BINARY_DIR to get location of Lib.lib.

# Add the static library 'Lib.lib', marking it as an IMPORT.
add_library(MyLib STATIC IMPORTED)
# Tell CMake where to find the library.
set_target_properties(MyLib PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/Debug/Lib.lib)

I encourage checking out the Debug/Release options for IMPORTED_LOCATION, since you are using Visual Studio.

Now, you can use target_link_libraries to link the imported library:

target_link_libraries(mainApp MyLib)

An important note: If you used CMake to configure and build Lib.lib in the same source tree as mainApp, you can skip the IMPORTED steps, and simply call target_link_libraries as shown above.

Kevin
  • 16,549
  • 8
  • 60
  • 74
  • 3 commands to add 1 .lib file? If this library consists of several .lib files I need to repeat these 3 commands for each file? In case of using link_directories and target_link_libraries it is enough 2 commands for all files: the first to add path to the library and the second to add all files. – Andrey Epifantsev Jul 26 '19 at 14:36
  • Yes, you need to import each individually, but you can make a list of the libraries and iterate over each of them using CMake's `foreach`. Also, you can link all of them to your application using `target_link_libraries(mainApp MyLib1 MyLib2 ... )`. – Kevin Jul 26 '19 at 14:40
  • @AndreyEpifantsev You can shorten it to two commands as I have shown in the answer. Regarding `link_directories`, the CMake documentation for [`link_directories`](https://cmake.org/cmake/help/latest/command/link_directories.html) warns against using it due to its relative path use, and they suggest using the `add_library` approach (as I've described), or using `find_library` instead. If you want to try out the `find_library` approach, check out this [response](https://stackoverflow.com/a/41909627/3987854). – Kevin Jul 26 '19 at 14:51
  • As with most problems, there are multiple solutions. Some are just safer than others. In this case, using `add_library` is safer than using `link_directories`. – Kevin Jul 26 '19 at 14:54