config-file cmake包中变量的奇怪问题

Bru*_*ams 5 cmake

我们可以使用cmake配置文件来导入目标。例如,给定的机械包括foob​​arConfig.cmake.in

set(FOOBAR_VERSION @VERSION@)

@PACKAGE_INIT@

set_and_check(FOOBAR_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
set_and_check(FOOBAR_LIBRARY_DIR "@PACKAGE_LIBRARY_INSTALL_DIR@")
set_and_check(FOOBAR_LIBRARY "@PACKAGE_LIBRARY_INSTALL_DIR@/libfoobar.so")
set_and_check(FOOBAR_STATIC_LIBRARY @PACKAGE_LIBRARY_INSTALL_DIR@/libfoobar.a")
include("${CMAKE_CURRENT_LIST_DIR}/FoobarLibTargets.cmake")

message(STATUS "foobar version: ${FOOBAR_VERSION}")
message(STATUS "foobar include location: ${FOOBAR_INCLUDE_DIR}")
message(STATUS "foobar library location: ${FOOBAR_LIBRARY_DIR}")
Run Code Online (Sandbox Code Playgroud)

用于导出的目标foobar

我们能做的:

find_package(foobar)

add_executable(usesfoo 
               usesfoo.cpp)
target_link_libraries(usesfoo
               ${FOOBAR_LIBRARY})
target_include_directories(usesfoo PUBLIC
               ${FOOBAR_INCLUDE_DIR})
Run Code Online (Sandbox Code Playgroud)

通常正常。但是,我遇到了一个棘手的情况,在find_package之后,Config.cmake中设置的变量不可用。例如给出:

find_package(foobar REQUIRED)
if (foobar_FOUND)
   message(STATUS "found foobar")
endif()

message(STATUS "foobar include location2: ${FOOBAR_INCLUDE_DIR}")
message(STATUS "foobar library location2: ${FOOBAR_LIBRARY_DIR}")
Run Code Online (Sandbox Code Playgroud)

输出为:

foobar include location: /test-import/opt/foobar/include
foobar library location: /test-import/opt/foobar/lib
found foobar
foobar include location2:
foobar library location2:
Run Code Online (Sandbox Code Playgroud)

这可能是怎么回事?

我怎么能够:

  • 发现这个问题?
  • 将来避免类似的问题吗?
  • 以安全规范的方式创建这些文件?

我对尝试调试感到非常困惑,并开始质疑Config程序包应该如何工作。我应该使用导入目标的属性而不是变量吗?find_package在什么范围内运行?我认为这就像一个include()而不是add_subdirectory(),它引入了自己的作用域。这些变量如何变为未设置状态?find_package到底是做什么的?

另请参阅正确设置已安装软件包的导入cmake目标的位置。该问题包含用于重现该问题的代码,该代码类似于该问题的代码。


完整的文件集来重现该问题:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.7)
set(VERSION 1.3.3)

project(FoobarLib VERSION "${VERSION}" LANGUAGES CXX)

SET(CMAKE_INSTALL_PREFIX "/opt/foo")
set(INSTALL_LIB_DIR lib)

add_library(foobar SHARED
   foobar.cpp
)

# Create the distribution package(s)
set(CPACK_PACKAGE_VERSION ${VERSION})
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})

set(CPACK_PACKAGE_NAME "foobar")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")

set(LIBRARY_INSTALL_DIR lib)
set(INCLUDE_INSTALL_DIR include)

INSTALL(TARGETS foobar
  EXPORT FoobarLibTargets
  LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR}
  ARCHIVE DESTINATION ${LIBRARY_INSTALL_DIR}
  INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR})

include(CMakePackageConfigHelpers)
set(ConfigFileInstallDir lib/cmake/FoobarLib)
set(INCLUDE_INSTALL_DIR include CACHE PATH "install path for include files")
set(LIBRARY_INSTALL_DIR lib CACHE PATH "install path for libraries")
configure_package_config_file(FoobarLibConfig.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/FoobarLibConfig.cmake"
  INSTALL_DESTINATION "${ConfigFileInstallDir}"
  PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR
  )
write_basic_package_version_file(
  "${CMAKE_CURRENT_BINARY_DIR}/FoobarLibConfigVersion.cmake"
  VERSION "${VERSION}"
  COMPATIBILITY SameMajorVersion)

EXPORT(EXPORT FoobarLibTargets
  FILE FoobarLibTargets.cmake)

INSTALL(FILES
  "${CMAKE_CURRENT_BINARY_DIR}/FoobarLibConfig.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/FoobarLibConfigVersion.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/FoobarLibTargets.cmake"
  DESTINATION "${ConfigFileInstallDir}")

include(CPack)
Run Code Online (Sandbox Code Playgroud)

FoobarLibConfig.cmake.in:

set(FoobarLib_VERSION @VERSION@)

@PACKAGE_INIT@

INCLUDE("${CMAKE_CURRENT_LIST_DIR}/FoobarLibTargets.cmake")

SET_AND_CHECK(FoobarLib_LIB_DIR "@PACKAGE_LIBRARY_INSTALL_DIR@")

message(STATUS "Foobar library version: ${FoobarLib_VERSION}")
message(STATUS "Foobar library location: ${FoobarLib_LIB_DIR}")

# workaround incorrect setting of location for import targets when package is installed
# see /sf/ask/3929504981/
#set_target_properties(foobar PROPERTIES
#                      IMPORTED_LOCATION_NOCONFIG "@PACKAGE_LIBRARY_INSTALL_DIR@/libfoobar.so"
#                      IMPORTED_LOCATION_RELEASE "@PACKAGE_LIBRARY_INSTALL_DIR@/libfoobar.so"
#                      IMPORTED_LOCATION_DEBUG "@PACKAGE_LIBRARY_INSTALL_DIR@/libfoobar.so")

check_required_components(FoobarLib)
Run Code Online (Sandbox Code Playgroud)

run.sh:

#!/bin/sh

SRC=`pwd`
mkdir -p ./target/debug && \
cd ./target/debug &&
cmake -DCMAKE_BUILD_TYPE=Debug ../../ &&
make &&
cpack -G TGZ 

cd ../..

rm -rf foo
mkdir foo

TGZ=`pwd`/target/debug/foobar-1.3.3.tar.gz

cd foo
tar -xvzf $TGZ
cat - >CMakeLists.txt <<EOF
cmake_minimum_required(VERSION 3.7)
project(useFoo VERSION 1.2.3)

find_package(FoobarLib ${MIN_FOOBARLIB_VERSION}
  HINTS "${WSDIR}/opt/foo"
  PATHS /opt/foo
  REQUIRED)

message(STATUS "Foobar library version: ${FOOBARLIB_VERSION}")
message(STATUS "Foobar library location: ${FOOBARLIB_LIB_DIR}")


message(STATUS "FoobarLib_FOUND=${FoobarLib_FOUND}")
message(STATUS "FoobarLib_PATH=${FOOBARLIB_PATH}")
message(STATUS "FoobarLib_DIR=${FoobarLib_DIR}")

message(STATUS "FOOBARLIB_FOUND=${FoobarLib_FOUND}")
message(STATUS "FOOBARLIB_PATH=${FOOBARLIB_PATH}")
message(STATUS "FOOBARLIB_DIR=${FoobarLib_DIR}")

file(GENERATE OUTPUT foobar-loc CONTENT "<TARGET_FILE:foobar>=$<TARGET_FILE:foobar>\n")

EOF
export CMAKE_PREFIX_PATH=`pwd`/opt/foo/lib/cmake:`pwd`/opt/foo/lib/cmake/
cmake . && make VERBOSE=1
echo pwd=`pwd`

# critical - check the location of the target is relative to the installation
grep $WSDIR/opt/foo/lib/libfoobar.so foobar-loc
if [ $? -ne 0 ]; then
   echo "FAIL: location of imported target 'foobar' is incorect" >&2
   cat foobar-loc >&2
   exit 1
fi
Run Code Online (Sandbox Code Playgroud)

这是@havogt请求的生成的Config.cmake,我认为这没有帮助,因为它是标准生成的代码:

# CMake configuration file for the FoobarLib package
# Use with the find_package command in config-mode to find information about
# the FoobarLib package.
#

set(FoobarLib_VERSION 1.3.3)


####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was FoobarLibConfig.cmake.in                            ########

get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)

macro(set_and_check _var _file)
  set(${_var} "${_file}")
  if(NOT EXISTS "${_file}")
    message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
  endif()
endmacro()

macro(check_required_components _NAME)
  foreach(comp ${${_NAME}_FIND_COMPONENTS})
    if(NOT ${_NAME}_${comp}_FOUND)
      if(${_NAME}_FIND_REQUIRED_${comp})
        set(${_NAME}_FOUND FALSE)
      endif()
    endif()
  endforeach()
endmacro()

####################################################################################

INCLUDE("${CMAKE_CURRENT_LIST_DIR}/FoobarLibTargets.cmake")

SET_AND_CHECK(FoobarLib_LIB_DIR "${PACKAGE_PREFIX_DIR}/lib")

message(STATUS "Foobar library version: ${FoobarLib_VERSION}")
message(STATUS "Foobar library location: ${FoobarLib_LIB_DIR}")

# workaround incorrect setting of location for import targets when package is installed
# see /sf/ask/3929504981/
#set_target_properties(foobar PROPERTIES
#                      IMPORTED_LOCATION_NOCONFIG "${PACKAGE_PREFIX_DIR}/lib/libfoobar.so"
#                      IMPORTED_LOCATION_RELEASE "${PACKAGE_PREFIX_DIR}/lib/libfoobar.so"
#                      IMPORTED_LOCATION_DEBUG "${PACKAGE_PREFIX_DIR}/lib/libfoobar.so")

check_required_components(FoobarLib)
Run Code Online (Sandbox Code Playgroud)

“ package” _FOUND由find_package()的实现设置,而不是由它加载的Config.cmake设置。由于其他原因(选择某人认为该软件包不是非组件化的),添加check_required_components()是一个好习惯,但与该问题无关。

Bru*_*ams 3

哎呀。这很尴尬。我已将生成代码移至 shell 脚本中,但忘记转义变量!

cat - >CMakeLists.txt <<EOF
cmake_minimum_required(VERSION 3.7)
project(useFoo VERSION 1.2.3)

find_package(FoobarLib ${MIN_FOOBARLIB_VERSION}
  HINTS "${WSDIR}/opt/foo"
  PATHS /opt/foo
  REQUIRED)

message(STATUS "Foobar library version: ${FOOBARLIB_VERSION}")
message(STATUS "Foobar library location: ${FOOBARLIB_LIB_DIR}")


message(STATUS "FoobarLib_FOUND=${FoobarLib_FOUND}")
message(STATUS "FoobarLib_PATH=${FOOBARLIB_PATH}")
message(STATUS "FoobarLib_DIR=${FoobarLib_DIR}")

message(STATUS "FOOBARLIB_FOUND=${FoobarLib_FOUND}")
message(STATUS "FOOBARLIB_PATH=${FOOBARLIB_PATH}")
message(STATUS "FOOBARLIB_DIR=${FoobarLib_DIR}")

file(GENERATE OUTPUT foobar-loc CONTENT "<TARGET_FILE:foobar>=$<TARGET_FILE:foobar>\n")

EOF
Run Code Online (Sandbox Code Playgroud)

不过,该问题对于提供相关问题的来源仍然有用。

回答我自己的问题:

我怎样才能找到这个问题?避免以后出现类似问题?以安全且规范的方式创建这些文件?