cmake 交叉编译与 sysroot returns 错误的包含路径
cmake cross-compilation with sysroot returns wrong include paths
我在尝试交叉编译(针对 arm 目标)并通过 cmake 包含包时遇到问题。
在本地编译以及在没有包依赖性的情况下进行交叉编译时,编译工作正常。
我的工具链文件是:
cmake_minimum_required(VERSION 3.3)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_FIND_ROOT_PATH /path/to/sysroot/)
set(CMAKE_SYSROOT /path/to/sysroot/)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
而在 CMakeFileList
我有几个 find_packages
:
find_package(pack1 REQUIRED)
find_package(pack2 REQUIRED)
message("--${pack1_INCLUDE_DIRS}--")
message("--${pack2_INCLUDE_DIRS}--")
include_directories(${pack1_INCLUDE_DIRS})
include_directories(${pack2_INCLUDE_DIRS})
显示包含目录变量时,只有列表中的第一个变量带有 sysroot
路径前缀。我得到了这样的东西:
--/path/to/sysroot/usr/include;/usr/include;/usr/include/xmlrpcpp--
当然编译失败了,因为一些头文件与目标系统不对应。
为什么 cmake 没有为整个目录列表添加前缀(它们存在于 sysroot
文件夹中)?
CMAKE_SYSROOT
和 set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
是否足以让 cmake 只在这个目录中查找,或者我在这里遗漏了什么?
注意:在工具链指令中有无set(CMAKE_FIND_ROOT_PATH /path/to/sysroot/)
没有区别。
编辑 1:
明确设置包含路径(${pack1_INCLUDE_DIRS}
和 ${pack2_INCLUDE_DIRS}
)以仅指向 sysroot 路径确实解决了编译问题,但随后导致链接器错误(无明确消息),这表明1) 额外的 /usr/include
条目是问题所在,2) ${pack1_LIBRARIES}
变量也不正确。
编辑 2:
关注@Tsyvarev 的回答解释了为什么这不能直接实现。一种解决方案(有点老套)是从 find_package
中提取要包含的库的名称,并强制 cmake 在 sysroot 文件夹中查找它们。下面的代码适用于我的用例:
#Reset libraries towards sysroot only
foreach(lib_path ${pack1_LIBRARIES} ${pack2_LIBRARIES})
string(REPLACE "/" ";" lib_path_list ${lib_path}) #Breakdown path in list
list(REVERSE lib_path_list)
list(GET lib_path_list 0 lib) #Get last element (library name)
find_library(new_path ${lib} PATHS ${CMAKE_SYSROOT}) #Force to look for it in actual sysroot path
list(APPEND LIBRARIES ${new_path}) #Add it to LIBRARIES list
unset(new_path CACHE) #Clear variable to allow new search
endforeach()
以下配置不会递归地将所有子目录附加到您的包含路径。
set(CMAKE_SYSROOT /path/to/sysroot/)
在您的代码中,include "*.h"
应该相对于 /path/to/sysroot/
。
不使用工具链文件,而是尝试从您的 cmake 命令行传递 --sysroot
,这应该始终有效。
变量 CMAKE_SYSROOT
(和 CMAKE_FIND_ROOT_PATH
)re-roots 仅那些路径,这些路径被不同的 find_*
命令搜索: find_library
、find_path
、find_package
(只修改包文件的搜索路径)。
例如如果 find_library
默认在 /usr/lib
下搜索,那么设置 CMAKE_SYSROOT
后它将在 ${CMAKE_SYSROOT}/usr/lib
下搜索。
但是,如果有人打电话
include_directories("/usr/include")
那么/usr/include
路径即使在cross-compiling时也保持不变。
如果find_package(pack1)
在MODULE
模式下是运行并且使用Findpack1.cmake
脚本定位pack1
包的设置,那么Findpack1.cmake
脚本将使用 find_library
和 find_path
调用。因此,设置 CMAKE_SYSROOT
将强制此脚本在 sysroot 和 return 适当路径下搜索库和 headers。
但是如果find_package(pack1)
在CONFIG
模式下是运行并且使用pack1Config.cmake
脚本定位pack1
包的设置,那么pack1Config.cmake
可以使用库的 绝对路径 和包含目录。在那种情况下,CMake 不会转换这些绝对路径,因此当 cross-compiling.
时它们会出错
实际上,CMake 提供了有助于编写 可重定位 XXXConfig.cmake
脚本的机制和规则。但并非所有配置脚本都遵循这些规则。
如果一个项目pack1
提供 pack1Config.cmake
脚本non-relocatablecross-compiling,那么你需要使用相同的 sysroot 将此项目构建为 cross-compiled。
我在尝试交叉编译(针对 arm 目标)并通过 cmake 包含包时遇到问题。
在本地编译以及在没有包依赖性的情况下进行交叉编译时,编译工作正常。
我的工具链文件是:
cmake_minimum_required(VERSION 3.3)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_FIND_ROOT_PATH /path/to/sysroot/)
set(CMAKE_SYSROOT /path/to/sysroot/)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
而在 CMakeFileList
我有几个 find_packages
:
find_package(pack1 REQUIRED)
find_package(pack2 REQUIRED)
message("--${pack1_INCLUDE_DIRS}--")
message("--${pack2_INCLUDE_DIRS}--")
include_directories(${pack1_INCLUDE_DIRS})
include_directories(${pack2_INCLUDE_DIRS})
显示包含目录变量时,只有列表中的第一个变量带有 sysroot
路径前缀。我得到了这样的东西:
--/path/to/sysroot/usr/include;/usr/include;/usr/include/xmlrpcpp--
当然编译失败了,因为一些头文件与目标系统不对应。
为什么 cmake 没有为整个目录列表添加前缀(它们存在于 sysroot
文件夹中)?
CMAKE_SYSROOT
和 set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
是否足以让 cmake 只在这个目录中查找,或者我在这里遗漏了什么?
注意:在工具链指令中有无set(CMAKE_FIND_ROOT_PATH /path/to/sysroot/)
没有区别。
编辑 1:
明确设置包含路径(${pack1_INCLUDE_DIRS}
和 ${pack2_INCLUDE_DIRS}
)以仅指向 sysroot 路径确实解决了编译问题,但随后导致链接器错误(无明确消息),这表明1) 额外的 /usr/include
条目是问题所在,2) ${pack1_LIBRARIES}
变量也不正确。
编辑 2:
关注@Tsyvarev 的回答解释了为什么这不能直接实现。一种解决方案(有点老套)是从 find_package
中提取要包含的库的名称,并强制 cmake 在 sysroot 文件夹中查找它们。下面的代码适用于我的用例:
#Reset libraries towards sysroot only
foreach(lib_path ${pack1_LIBRARIES} ${pack2_LIBRARIES})
string(REPLACE "/" ";" lib_path_list ${lib_path}) #Breakdown path in list
list(REVERSE lib_path_list)
list(GET lib_path_list 0 lib) #Get last element (library name)
find_library(new_path ${lib} PATHS ${CMAKE_SYSROOT}) #Force to look for it in actual sysroot path
list(APPEND LIBRARIES ${new_path}) #Add it to LIBRARIES list
unset(new_path CACHE) #Clear variable to allow new search
endforeach()
以下配置不会递归地将所有子目录附加到您的包含路径。
set(CMAKE_SYSROOT /path/to/sysroot/)
在您的代码中,include "*.h"
应该相对于 /path/to/sysroot/
。
不使用工具链文件,而是尝试从您的 cmake 命令行传递 --sysroot
,这应该始终有效。
变量 CMAKE_SYSROOT
(和 CMAKE_FIND_ROOT_PATH
)re-roots 仅那些路径,这些路径被不同的 find_*
命令搜索: find_library
、find_path
、find_package
(只修改包文件的搜索路径)。
例如如果 find_library
默认在 /usr/lib
下搜索,那么设置 CMAKE_SYSROOT
后它将在 ${CMAKE_SYSROOT}/usr/lib
下搜索。
但是,如果有人打电话
include_directories("/usr/include")
那么/usr/include
路径即使在cross-compiling时也保持不变。
如果find_package(pack1)
在MODULE
模式下是运行并且使用Findpack1.cmake
脚本定位pack1
包的设置,那么Findpack1.cmake
脚本将使用 find_library
和 find_path
调用。因此,设置 CMAKE_SYSROOT
将强制此脚本在 sysroot 和 return 适当路径下搜索库和 headers。
但是如果find_package(pack1)
在CONFIG
模式下是运行并且使用pack1Config.cmake
脚本定位pack1
包的设置,那么pack1Config.cmake
可以使用库的 绝对路径 和包含目录。在那种情况下,CMake 不会转换这些绝对路径,因此当 cross-compiling.
实际上,CMake 提供了有助于编写 可重定位 XXXConfig.cmake
脚本的机制和规则。但并非所有配置脚本都遵循这些规则。
如果一个项目pack1
提供 pack1Config.cmake
脚本non-relocatablecross-compiling,那么你需要使用相同的 sysroot 将此项目构建为 cross-compiled。