I'm trying to create a C++ library that can be re-used via CMake. It's failing when I try to install the export files for the project. I don't understand why. Here's the error I get.
Target "Proj_LibA" INTERFACE_SOURCES property contains path:
"C:/projects/cmake_temp/src/libA/include/liba.hpp"
which is prefixed in the source directory.
Reading the CMake documents and this other stackoverflow post imply that there's something wrong with how I setup source file paths and/or the include directory. Here's a SSCE that reproduces my issue.
Folder structure
cmake_temp/
/build
/install
/src/
/CMakeLists.txt
/src/libA/
/include/liba.hpp
/CMakeLists.txt
/liba.cpp
/LibAConfig.cmake.in
/src/CMakeLists.txt
cmake_minimum_required (VERSION 3.15)
project("TestProj")
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
set(include_install_dir ${CMAKE_INSTALL_PREFIX}/include/)
set(export_cmake ${CMAKE_INSTALL_PREFIX}/cmake)
set(lib_install_dir ${CMAKE_INSTALL_PREFIX}/lib)
set(bin_install_dir ${CMAKE_INSTALL_PREFIX}/bin)
add_subdirectory(libA)
#add_subdirectory(exec)
liba/include/liba.hpp
#ifndef liba
#define liba
#include "LibA_export.hpp"
class PROJ_LIBA_EXPORT Foo
{
public:
Foo(const int bias);
int add(int a, int b);
private:
int mBias;
};
#endif //liba
src/liba/liba.cpp
#include "liba.hpp"
Foo::Foo(const int bias) : mBias(bias) {}
int Foo::add(int a, int b) { return a + b + mBias; }
src/liba/CMakeLists.txt
# Setup alias to support add_subdirectory, find_package, and fetchcontent usage
add_library(Proj_LibA SHARED)
add_library(proj::liba ALIAS Proj_LibA)
set_target_properties(Proj_LibA PROPERTIES
EXPORT_NAME LibA
POSITION_INDEPENDENT_CODE TRUE)
target_sources(Proj_LibA
PUBLIC
include/liba.hpp
PRIVATE
liba.cpp)
target_include_directories(Proj_LibA
PUBLIC
$<INSTALL_INTERFACE:${include_install_dir}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
#PRIVATE
)
# Generate symbol export macros and add to source
include(GenerateExportHeader)
set(export_file "${CMAKE_CURRENT_BINARY_DIR}/LibA_export.hpp")
generate_export_header(Proj_LibA EXPORT_FILE_NAME ${export_file})
target_sources(Proj_LibA PUBLIC ${export_file})
# Install everything and mark it as part of the 'sdk' export package
install(TARGETS Proj_LibA
EXPORT sdk
ARCHIVE DESTINATION ${lib_install_dir}
LIBRARY DESTINATION ${lib_install_dir}
RUNTIME DESTINATION ${bin_install_dir}
)
# Install header files for package consumers
INSTALL(DIRECTORY include/ DESTINATION ${include_install_dir})
# Create the LibAConfig.cmake file for find_package
include(CMakePackageConfigHelpers)
configure_package_config_file(LibAConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/LibAConfig.cmake
INSTALL_DESTINATION ${export_cmake}
PATH_VARS include_install_dir)
# Create the LibAConfigVersion.cmake file for find_package
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/LibAConfigVersion.cmake
VERSION 1.2.3
COMPATIBILITY SameMajorVersion )
# Install the LibAConfig*.cmake files
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/LibAConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/LibAConfigVersion.cmake
DESTINATION ${export_cmake})
# Install the auto-generated export support/find_package scripts
install(EXPORT sdk
DESTINATION ${export_cmake}
NAMESPACE proj::)
# HELP: The above command triggers the following errors
#CMake Error in libA/CMakeLists.txt:
# Target "Proj_LibA" INTERFACE_SOURCES property contains path:
#
# "C:/projects/cmake_temp/src/libA/include/liba.hpp"
#
# which is prefixed in the source directory.
#
#
#CMake Error in libA/CMakeLists.txt:
# Target "Proj_LibA" INTERFACE_SOURCES property contains path:
#
# "C:/projects/cmake_temp/build/libA/LibA_export.hpp"
#
# which is prefixed in the build directory.
src/liba/LibAConfig.cmake.in
set(LibA_VERSION 1.2.3)
@PACKAGE_INIT@
set_and_check(LibA_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
check_required_components(LibA)
Does anyone have suggestions on what I should look at to troubleshoot this error when trying to export my library?
I also hit this problem yesterday. Reading through docs and a blog post of "co-maintainer of CMake" helped to shed some light on this (tldr; see the Complications For Installing paragraph).
The issue here comes from from your liba.hpp
being added as a PUBLIC
target source, which defines an absolute path to your header file. This is valid in your build tree on your local machine, but likely to be different when it is installed, using find_package()
or copying your library into another source tree. I think a lot of people, including myself, make assumptions that PUBLIC
or INTERFACE
is associated with a public header file, which is not the case. Quote from the blog post above:
[...] do not confuse the PRIVATE , PUBLIC and INTERFACE keywords with whether a header is part of the public API for the library or not, the keywords are specifically for controlling which target(s) the sources are added to
Just like in the SO link that you gave (same as in your code) the answer points to BUILD_INTERFACE
and INSTALL_INTERFACE
generator expressions to fix this problem for an include directory. One possible solution is to be more explicit and use same expressions in your target_sources()
target_sources(Proj_LibA
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/liba.hpp>
$<INSTALL_INTERFACE:include/liba.hpp>
PRIVATE
liba.cpp)
This is arguably not a very pretty solution as this has to be done for each header file. Another option is to simply move your headers into the PRIVATE
scope. Yet another one is to use PUBLIC_HEADER
target property to define your headers and specify an install destination, I haven't actually tried this, but see this SO.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With