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

时间:2019-05-17 15:46:35

标签: cmake

我们可以使用cmake配置文件导入目标。 例如,给定的机械包括 foobarConfig.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}")

用于导出的目标foobar

我们可以做到:

find_package(foobar)

add_executable(usesfoo 
               usesfoo.cpp)
target_link_libraries(usesfoo
               ${FOOBAR_LIBRARY})
target_include_directories(usesfoo PUBLIC
               ${FOOBAR_INCLUDE_DIR})

,通常正常。 但是,我遇到了一个棘手的情况,在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}")

输出为:

foobar include location: /test-import/opt/foobar/include
foobar library location: /test-import/opt/foobar/lib
found foobar
foobar include location2:
foobar library location2:

这可能是怎么回事?

我如何:

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

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

另请参见correctly set the location of imported cmake targets for an installed package。 该问题包含用于重现该问题的代码,类似于该问题的代码。


完成该问题的完整文件集:

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)

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 https://stackoverflow.com/q/56135785/1569204
#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.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

这是@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 https://stackoverflow.com/q/56135785/1569204
#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)

'package'_FOUND由find_package()的实现设置,而不是由它加载的Config.cmake设置。添加check_required_components()出于其他原因是一种好习惯(例如,有人认为该软件包不是非组件化的),但与此问题无关。

2 个答案:

答案 0 :(得分:2)

糟糕。这很尴尬。我已经将生成代码移到了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

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

要回答我自己的问题:

如何找到此问题? 将来避免类似的问题吗? 以安全规范的方式创建这些文件吗?

答案 1 :(得分:0)

check_required_components(Foobar)应该在这种情况下最后被调用。 docs

  

check_required_components()应该在最后调用   FooConfig.cmake文件的名称。这个巨集会检查是否所有要求,   找到了非可选组件,如果不是这种情况,   将Foo_FOUND变量设置为FALSE,以便该包为   被认为找不到。它通过测试   所有要求的必需组件的Foo__FOUND变量。   即使程序包不提供任何宏,也应调用此宏   确保用户未指定组件的组件   错误地。使用NO_CHECK_REQUIRED_COMPONENTS_MACRO选项时,   该宏不会生成到FooConfig.cmake文件中。