8

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 ?

Kafka
  • 720
  • 6
  • 21
  • 2
    Looks like you forgot to `dllexport` functions/variable in your library. With no exported symbols `.lib` file is not created. (In case of shared libraries, `.lib` is essentially an *import* file). – Tsyvarev Sep 27 '20 at 11:50
  • hum ok, I am not familiar with the concept, but I while check that. Thank you. – Kafka Sep 27 '20 at 11:54

2 Answers2

12

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

Sorush
  • 3,275
  • 4
  • 28
  • 42
3

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!

NoodleCollie
  • 855
  • 7
  • 24