CMake: linking library on Visual Studio - visual-studio

Im trying to make CMakeklist.txt file on Windows and I have big problem.
I wrote
cmake_minimum_required(VERSION 3.1.2)
project(c_api)
set(INC_PATH target/release/deps)
set(PROJECT_DLL traffic.dll)
set(PROJECT_LIB traffic.dll.lib)
configure_file(${INC_PATH}/${PROJECT_DLL} ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
include_directories(${INC_PATH}/include)
add_library(traffic UNKNOWN IMPORTED)
set_property(TARGET traffic PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_DLL})
set_property(TARGET traffic PROPERTY IMPORTED_IMPLIB ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_LIB})
file(GLOB SOURCES "c_api/examples/ai_module/*.c")
add_executable(ai_module
${SOURCES}
)
target_link_libraries(ai_module traffic )
I run the project that CMake generate i got
LNK1107 invalid or corrupt file: cannot read at 0x2D8
I thought that the problem is add_library(traffic UNKNOWN IMPORTED) but if I change it to add_library(traffic SHARED IMPORTED) I get
LNK2019 unresolved external symbol _traffic_import_osm referenced in function _main ai_module
so I assume that Visual Studio dont see a library.
I run this code on linux and i only change the .dll format for .so and it works fine.
Im using Visual Studio 15 2017 on CMake

According to the documentation of your first error, it appears you are trying to link to the .dll directly. On Windows, shared libraries require two files. The shared .dll, which includes all the definitions of the functions and classes in the library, and the .lib which (when build with the dll) only contains the declarations. The stub .lib file is used at compile time to tell the linker what to expect from the function.
Do you have both the .lib and the .dll? If you do not have the .lib, you cannot link the .dll to your program.

I solve the problem. CMake make project of 32bits and my .dll is 64bit and now its work

Related

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

I have created a personnal C++ library and I try to use it in an other program. To create and compile my library, I use the commands
cmake -G "Visual Studio 16 2019" -A x64 ..
cmake --build . --config Release --target INSTALL
I have no problem with te compilation and the installation, but in the Target-release.cmake install look like this :
#----------------------------------------------------------------
# Generated CMake target import file for configuration "Release".
#----------------------------------------------------------------
# Commands may need to know the format version.
set(CMAKE_IMPORT_FILE_VERSION 1)
# Import target "containers" for configuration "Release"
set_property(TARGET containers APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(containers PROPERTIES
IMPORTED_IMPLIB_RELEASE "C:/Program Files (x86)/containers/lib/containers.lib"
IMPORTED_LOCATION_RELEASE "C:/Program Files (x86)/containers/bin/containers.dll"
)
list(APPEND _IMPORT_CHECK_TARGETS containers )
list(APPEND _IMPORT_CHECK_FILES_FOR_containers "C:/Program Files (x86)/containers/lib/containers.lib" "C:/Program Files (x86)/containers/bin/containers.dll" )
# Commands beyond this point should not need to know the version.
set(CMAKE_IMPORT_FILE_VERSION)
Nothing abnormal, as far as I can see. My problem is : I have no file C:/Program Files (x86)/containers/lib/containers.lib generated during the compilation and the installation. So every time I try to use a find package, I have an error. (It's only with visual studio generator, it works with mingw). Here is my CMakeLists.txt :
cmake_minimum_required(VERSION 3.1)
set(VERSION 1.0.0)
project (containers
VERSION ${VERSION}
DESCRIPTION "Library adding some features to containers of the stl."
LANGUAGES CXX)
option(BUILD_TESTING "Build test programs" OFF)
include(CTest)
# Set the possible values of build type for cmake-gui
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build.")
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
message( STATUS "Sources path : ${PROJECT_SOURCE_DIR}")
message( STATUS "Binary path : ${PROJECT_BINARY_DIR}")
message( STATUS "install path : ${CMAKE_INSTALL_PREFIX}")
message( STATUS "Version : ${PROJECT_VERSION}")
message( STATUS "Version : ${PROJECT_VERSION}")
message( STATUS "Compiler : ${CMAKE_CXX_COMPILER_ID}")
set(SOURCES ${PROJECT_SOURCE_DIR}/src/containers.cpp)
set(HEADERS ${PROJECT_SOURCE_DIR}/include/containers/containers_check.h
${PROJECT_SOURCE_DIR}/include/containers/containers.h
${PROJECT_SOURCE_DIR}/include/containers/append.h
${PROJECT_SOURCE_DIR}/include/containers/contains.h
${PROJECT_SOURCE_DIR}/include/containers/remove.h
${PROJECT_SOURCE_DIR}/include/containers/print.h
)
add_library(containers SHARED ${SOURCES} ${HEADERS})
#add_library(containers INTERFACE)
target_compile_features(containers PRIVATE cxx_std_17)
set_target_properties(containers
PROPERTIES
MSVC_RUNTIME_LIBRARY "MultiThreadedDLL"
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS OFF)
target_include_directories(containers
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_compile_options(containers PRIVATE
# g++
#$<$<CXX_COMPILER_ID:GNU>:$<BUILD_INTERFACE:-Wall>>
#$<$<CXX_COMPILER_ID:GNU>:$<BUILD_INTERFACE:-Wextra>>
#$<$<CXX_COMPILER_ID:GNU>:$<BUILD_INTERFACE:-pedantic>>
#$<$<CXX_COMPILER_ID:GNU>:$<BUILD_INTERFACE:-Werror>>
#$<$<CXX_COMPILER_ID:GNU>:-Wno-reorder>
## Clang
#$<$<CXX_COMPILER_ID:Clang>:$<BUILD_INTERFACE:-Wall>>
##TODO
## MSVC
#$<$<CXX_COMPILER_ID:MSVC>:$<BUILD_INTERFACE:/W4>>
#$<$<CXX_COMPILER_ID:MSVC>:$<BUILD_INTERFACE:/WX>>
)
install(TARGETS containers
EXPORT containersTarget
ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/ COMPONENT Development
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/ COMPONENT Library NAMELINK_COMPONENT Development
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ COMPONENT Runtimes
)
install(FILES ${HEADERS}
DESTINATION ${CMAKE_INSTALL_PREFIX}/include/containers/
COMPONENT headers)
include(CMakePackageConfigHelpers)
# For moteur_de_calculConfig.cmake
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include CACHE PATH "install path for include files")
set(LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "install path for libraries")
#------------------------------------------------------------------------------
# Configure <export_config_name>ConfigVersion.cmake common to build and install tree
set(config_version_file ${PROJECT_BINARY_DIR}/containersConfigVersion.cmake)
write_basic_package_version_file(
${config_version_file}
VERSION "${CMAKE_PROJECT_VERSION}"
COMPATIBILITY ExactVersion
)
#------------------------------------------------------------------------------
# Export '<export_config_name>Target.cmake' for a build tree
export(TARGETS
containers
FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Target.cmake"
)
#------------------------------------------------------------------------------
# Configure '<export_config_name>Config.cmake' for a build tree
set(build_config ${CMAKE_BINARY_DIR}/containersConfig.cmake)
configure_package_config_file(
"containersConfig.cmake.in"
${build_config}
INSTALL_DESTINATION "${PROJECT_BINARY_DIR}"
PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR VERSION
)
#------------------------------------------------------------------------------
# Export '<export_config_name>Target.cmake' for an install tree
install(EXPORT
containersTarget
DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME}"
)
#------------------------------------------------------------------------------
# Configure '<export_config_name>Config.cmake' for a install tree
set(install_config ${PROJECT_BINARY_DIR}/CMakeFiles/containersConfig.cmake)
configure_package_config_file(
containersConfig.cmake.in
${install_config}
INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME}"
PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR VERSION
)
# Install config files
install(
FILES ${config_version_file} ${install_config}
DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/${PROJECT_NAME}"
)
# test
if(BUILD_TESTING)
add_subdirectory(test)
endif()
# uninstall target
# use : make uninstall
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE #ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
My cmake version :
cmake version 3.18.0
What is missing in my CMakeLists.txt for generate the container.lib file ?
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
I'd like to add to the accepted answer here, for others who might find it: although CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS will solve the problem, it could well be a sticking plaster masking a deeper issue going on with your code.
I'm not an expert on the Microsoft compilers, but I do know that if you build a shared library with MSVC, the .lib file should still be produced. However, rather than containing all of your compiled code as it would for a static library, it basically provides the compiler with information about the exported symbols in your shared library. This means that the compiler can automatically link any other targets to your shared library, without you needing to manually load the library from your code using the Windows API functions. If you link an executable to a shared library this way, the Microsoft C runtime will basically call LoadLibrary() for you automatically when your application starts. Useful, huh?
If the compiler does not produce a .lib alongside your shared library, this basically means that there are no exported symbols in the shared library. This is why CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS solves the problem - it forces all the symbols to be exported anyway, thereby causing the compiler to generate the .lib file detailing these exports. However, as you may be able to work out, this is very much the nuclear option. There's probably a lot of stuff in your shared library that really doesn't need to be visible from the outside! So the pertinent question becomes: why is nothing being exported from my library?
In the linked answer referred to previously, underneath all the CMake technicals, the issue was that the OP was not properly marking their symbols for export. It turns out that this was my issue too, but in a more round-about way: I had decided that for my particular library, which could previously be built in shared or static configurations, I now wanted to force it only to be built in a shared configuration. Because of this, I had removed a particular preprocessor definition from my project in CMake which specified whether the build mode was shared or static; this meant that all of the export annotations on my functions compiled down to nothing (as they should do under a static configuration where they are not needed). The upshot was that I accidentally ended up building the shared library with no exported symbols whatsoever, and MSVC just said "Well I guess there's no point building a .lib then", and didn't produce one. This caused the build issues stating that the .lib could not be found on disk.
When I encountered the answer above, I was suspicious as I wondered why I'd not had to set this value before, despite having used CMake to build Windows shared library projects for years. The correct solution in my case was not to switch CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS on - it was actually to remove the C++ preprocessor condition that checked for my "this is a shared build" preprocessor definition. This re-enabled all of the export annotations on my functions, and everything built as it should.
Before you use CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS, do check that there isn't some subtle bug in your scripts that is preventing your symbols from being exported!

Library's CMake generates DLL. Application's CMake wants LIB

My library has minimal, straightforward CMake code with the pertinent lines
add_library(MyLib <sources>)
install(
TARGETS MyLib
LIBRARY DESTINATION ${destination}/lib
RUNTIME DESTINATION ${destination}/lib
COMPONENT Libraries)
install(
FILES mylib.h
DESTINATION ${destination}/include
COMPONENT Headers)
When executed under Windows, the system generates mylib.dll in ...\build\Release, and mylib.lib and mylib.exp (what's that?) in ...\build\lib\Release. It only installs mylib.dll.
My application has minimal, straightforward CMake code to search for my library:
find_path(MyLib_INCLUDE_DIR mylib.h)
find_library(MyLib_LIBRARIES NAMES MyLib)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MyLib DEFAULT_MSG MyLib_LIBRARIES MyLib_INCLUDE_DIR)
Which works under Linux, but under Windows results in
-- Could NOT find MyLib (missing: MyLib_LIBRARIES)
From experimentation I know that this error occurs whenever there is only a .DLL file, and no associated .LIB import library.
Shall I correct MyLib to install mylib.lib? How?
Or is it possible to modify my application so that it is satisfied with mylib.dll only? How?
Research done so far
This is not about static vs dynamic linking (DLL and LIB files - what and why?, cmake link against dll/lib): I want dynamic linking; if a .LIB file is required, it has nothing to do with static linking.
This link cmake : shared library : how to get the .lib name instead of .dll name? may be pertinent, but is not explicit enough. Two other questions CMake generated VS project expecting lib instead of dll, Linking dll/lib to a cmake project seem related, but have no answer.
Command install classifies .lib file for a shared library as ARCHIVE. This is explicitly stated in the documentation:
For DLL platforms (all Windows-based systems including Cygwin), the DLL import library is treated as an ARCHIVE target.
So you need to add ARCHIVE clause to install() for install .lib file as well:
install(
TARGETS MyLib
ARCHIVE DESTINATION ${destination}/lib
LIBRARY DESTINATION ${destination}/lib
RUNTIME DESTINATION ${destination}/bin
COMPONENT Libraries)
Not also, that RUNTIME DESTINATION is usually specified as bin, the same as destination for executables. This helps the executables on Windows to locate the shared libraries (.dll).

CMake command target_link_libraries in Visual Studio

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?
#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.

Boost VS2017 linking to the wrong DLL

I have a CMake file which does this:
find_package(Boost COMPONENTS system filesystem)
add_library(MyModule MODULE main.cpp)
target_include_directories(MyModule PUBLIC ${Boost_INCLUDE_DIRS})
target_link_libraries(MyModule Boost::system Boost::filesystem)
I'm using VS 2017 as my generator. When I generate the project file with cmake, it finds boost_system-vc141-mt-1_63.lib and I can see that it is in the linking rules of the vcxproj. However, when I try to compile I get this error:
LINK : fatal error LNK1104: cannot open file 'libboost_system-vc140-mt-1_63.lib
Note the different generators (vc140 vs vc141). I know my compiler has output the right values because I built boost from source, so I tried to just rename vc141 to vc140, but the error stayed the same. I also confirmed that vc140 is not referenced in the project file.
What's going on? How can I force boost to link to the correct version?
When building with Visual Studio, boost has some pragma statements which do the linking for you. This is called "Auto-linking" and it over-rides any command-line arguments you may be passing to the linker.
The solution is to define BOOST_ALL_NO_LIB. This can be done in two ways:
In source code before including boost headers as #define BOOST_ALL_NO_LIB.
It could be added to your cmake file as: add_definitions("-DBOOST_ALL_NO_LIB").
As of CMake 3.5: Use the disable_autolinking imported target:
target_link_libraries(MyModule Boost::system Boost::filesystem Boost::disable_autolinking)

Compiling FFMpeg libraries for Visual Studio

I'm trying to use the FFMpeg libraries in a Windows application. I use MingW-w64 to compile FFMpeg with static libraries with architecture once with x86 and once with x86_64.
Currently I'm trying to link the 32bit x86 libraries with my VS2008 application.
The libraries are added to the library path and VS does not complain about being unable to load a .a file.
However I get several uneresolved symbol errors like
ait_rtp_receiver.lib(ait_decoder_lib.obj) : error LNK2001: unresolved external symbol _av_free
I import the FFMpeg header files as extern C and I can see the symbols in the .a without the leading underscore.
What can I do to make the name decoration of Mingw-w64's gcc and of the VS compiler suite compatible?
After spending miserable days trying to get the GCC-Mingw based version of building FFMpeg to work, I reverted to using the Visual Studio or Windows SDK compiler and linker. Using the --toolchain=msvc of the configure script made the build process generate static .a files that are basically .lib files that can be loaded within Visual Studio projects.
I wrote the build steps up and here is a link

Resources