Search code examples
c++cmake

cmake add_library with set_property can not found fmt dll on windows


My enviorment: Windows 10 + MinGW

I m a newer to CMake, and I try to figure out how to import 3rd libs to my project, my trial project structure is list as follow, which location is: D:/CodeDraft/helloworld

D:.
│  CMakeLists.txt
│  main.cpp
│  run_cmake.ps1
│
├─3rdparty
│  └─fmt_x64-mingw-dynamic
│      │  BUILD_INFO
│      │  CONTROL
│      │
│      ├─bin
│      │      libfmt.dll
│      │
│      ├─debug
│      │  ├─bin
│      │  │      libfmtd.dll
│      │  │
│      │  └─lib
│      │      │  libfmtd.dll.a
│      │      │
│      │      └─pkgconfig
│      │              fmt.pc
│      │
│      ├─include
│      │  └─fmt  

and main.cpp:

#include <fmt/core.h>
int main()
{
    fmt::print("Hello World!\n");
    return 0;
}

As you can see I download fmt libs from vcpkg, for some reason I decide to build project by native way instead of using vcpkg function.

according to Importing and Exporting Guide and reference some other projects, I try to link fmt dll on my CMakeLists.txt like this:

cmake_minimum_required(VERSION 3.10)

project(HelloWorld)
message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")


add_executable(HelloWorld main.cpp)


add_library(fmt SHARED IMPORTED)
set_property(TARGET fmt PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-mingw-dynamic/bin/libfmt.dll)
target_include_directories(fmt INTERFACE ${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-mingw-dynamic/include)

target_link_libraries(HelloWorld fmt)

add_custom_command(TARGET HelloWorld POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_RUNTIME_DLLS:HelloWorld> $<TARGET_FILE_DIR:HelloWorld>
  COMMAND_EXPAND_LISTS
  )

and my build script is : run_cmake.ps1

cmake -G "MinGW Makefiles" -B build
cd build
make
.\HelloWorld.exe
cd ..

I run the run_cmake.ps1, the cmake compile is success, but when it cames to make it throw error and say fmt lib is not found:

CMAKE_SOURCE_DIR: D:/CodeDraft/helloworld
-- Configuring done (0.2s)
CMake Warning (dev) in CMakeLists.txt:
  Policy CMP0111 is not set: An imported target missing its location property
  fails during generation.  Run "cmake --help-policy CMP0111" for policy
  details.  Use the cmake_policy command to set the policy and suppress this
  warning.

  IMPORTED_IMPLIB not set for imported target "fmt".
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Generating done (0.1s)
-- Build files have been written to: D:/CodeDraft/helloworld/build
make[2]: *** No rule to make target 'fmt-NOTFOUND', needed by 'HelloWorld.exe'.  Stop.
make[1]: *** [CMakeFiles\Makefile2:82: CMakeFiles/HelloWorld.dir/all] Error 2
make: *** [Makefile:90: all] Error 2

For hours of searching I still not idea the causes, I also try compile as the same way on ubuntu, strangely it works:

cmake_minimum_required(VERSION 3.10)

project(HelloWorld)

add_executable(HelloWorld main.cpp)


add_library(fmt SHARED IMPORTED GLOBAL)
set_target_properties(fmt PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-linux-dynamic/lib/libfmt.so)
target_include_directories(fmt INTERFACE ${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-linux-dynamic/include)

target_link_libraries(HelloWorld fmt)

I definitely sure not dll itself problem because I'm successful compile and run the project with g++ native command:

g++ -g .\main.cpp -o .\main.exe -I.\3rdparty\fmt_x64-mingw-dynamic\include\ -L.\3rdparty\fmt_x64-mingw-dynamic\lib\ -lfmt
# then copy the libfmt.dll along with the main.exe

either not path problem, because I compile and run successfully to write CMakesList.txt another way, the path of include and lib is identical:

cmake_minimum_required(VERSION 3.10)

project(HelloWorld)
message("CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}")

include_directories(${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-mingw-dynamic/include)
link_directories(${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-mingw-dynamic/bin)    # -Lpath
link_libraries(fmt) #-lfmt
add_executable(HelloWorld main.cpp)

add_custom_command(TARGET HelloWorld POST_BUILD     #-for copy libs in windows
             COMMAND ${CMAKE_COMMAND} -E copy_if_different 
             ${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-mingw-dynamic/bin/libfmt.dll 
             ${CMAKE_SOURCE_DIR}/build)


so what's wrong?


Solution

  • On Windows a .dll file is used only at runtime (when your executable is run). At build time (while creating an executable), the linker uses .lib file.

    When represent a library with SHARED IMPORTED library target, on Windows:

    • property IMPORTED_LOCATION should contain the path to the .dll file
    • property IMPORTED_IMPLIB property should contain the path to the import library (.lib file).

    In the warning message CMake tells you, that you forgot to set IMPORTED_IMPLIB property. Without that property the linker doesn't know which library to use for linking.

    Correct:

    add_library(fmt SHARED IMPORTED)
    set_property(TARGET fmt PROPERTY
      IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-mingw-dynamic/bin/libfmt.dll
      IMORTED_IMPLIB ${CMAKE_SOURCE_DIR}/3rdparty/fmt_x64-mingw-dynamic/lib/libfmt.lib
    )