尽管使用了正确的工具链文件,但为什么 CMake 在交叉编译 Net-SNMP 代理时明显引用主机系统文件?
Why is CMake apparently referring to host system files while cross-compiling a Net-SNMP agent despite a proper toolchain file is being used?
我正在开发 Net-SNMP subagent whose final target will be an ARM board, so I'm using CMake 以便更轻松地管理构建本机和交叉编译版本。我开始在我的主机平台 (x86_64) 上测试它,它可以构建并与这个 CMakeLists.txt 文件一起正常工作:
cmake_minimum_required (VERSION 2.6)
project (snmp_agent C)
set(snmp_agent_VERSION_MAJOR 1)
set(snmp_agent_VERSION_MINOR 0)
# Defines path to the net-snmp-config script
set(NETSNMPCONFIG "${CMAKE_FIND_ROOT_PATH}/usr/bin/net-snmp-config")
# Gets compiling flags and libs linked to Net-SNMP
execute_process(COMMAND "${NETSNMPCONFIG}" "--base-cflags" OUTPUT_VARIABLE NETSNMPCFLAGS)
execute_process(COMMAND "${NETSNMPCONFIG}" "--agent-libs" OUTPUT_VARIABLE NETSNMPLIBS)
# Removes leading/trailing spaces from net-snmp-config output
string(STRIP ${NETSNMPCFLAGS} NETSNMPCFLAGS)
string(STRIP ${NETSNMPLIBS} NETSNMPLIBS)
# Prints compilation and linker flags used in Net-SNMP package
message("Net-SNMP package CFLAGS: ${NETSNMPCFLAGS}")
message("Net-SNMP package LIBS: ${NETSNMPLIBS}")
# Setting libs and compilation flags variables
set(LIBS "${NETSNMPLIBS}")
set(STRICT_FLAGS "-Wall -Wstrict-prototypes")
set(CFLAGS "-I. ${STRICT_FLAGS} ${NETSNMPCFLAGS}")
# Sets prefix for files created by 'mib2c' for the wanted MIB
set(ENT_PHYSICAL_ENTRY "scalars/entPhysicalEntry")
# Source files created by 'mib2c' and then user customized
set(USER_SRCS
${ENT_PHYSICAL_ENTRY}.c
)
# Setting subagent sources
set(SRCS ${USER_SRCS}
${CMAKE_PROJECT_NAME}.c
)
# Finds the required Net-SNMP lib paths and assigns them to variables
find_library(NETSNMPAGENT "netsnmpagent")
message("Found ${NETSNMPAGENT}")
find_library(NETSNMPMIBS "netsnmpmibs")
message("Found ${NETSNMPMIBS}")
find_library(NETSNMP "netsnmp")
message("Found ${NETSNMP}")
# Sets the flags to be used for compiling and linking the executable
set_source_files_properties(${SRCS} COMPILE_FLAGS ${CFLAGS})
add_executable(${CMAKE_PROJECT_NAME} ${SRCS})
target_link_libraries(${CMAKE_PROJECT_NAME} ${NETSNMPAGENT} ${NETSNMPMIBS} ${NETSNMP})
我使用一个单独的构建目录,所以我不会混合构建和源文件,它们与 CMakeLists.txt 文件一起,所以这些的输出命令...
cd ~/git/snmp_agent # CMakeLists.txt is in here along with source files
mkdir build
cd build
cmake ..
...如下:
claudio@slackdev:~/git/snmp_agent/build$ cmake ..
-- The C compiler identification is GNU 5.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
Net-SNMP package CFLAGS: -DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -O2 -fPIC -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -I/usr/lib64/perl5/CORE -I/usr/include/libnl3 -I/usr/include
Net-SNMP package LIBS: -L/usr/lib64 -lnetsnmpmibs -lsensors -lpci -ldl -lnetsnmpagent -lwrap -lnsl -Wl,-E -Wl,-rpath,/usr/lib64/perl5/CORE -lnetsnmp -lcrypto -lnl-3 -lm
Found /usr/lib64/libnetsnmpagent.so
Found /usr/lib64/libnetsnmpmibs.so
Found /usr/lib64/libnetsnmp.so
-- Configuring done
-- Generating done
-- Build files have been written to: /home/claudio/git/snmp_agent/build
运行 make
,构建得很好:
claudio@slackdev:~/git/snmp_agent/build$ make
Scanning dependencies of target snmp_agent
[ 25%] Building C object CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o
[ 50%] Building C object CMakeFiles/snmp_agent.dir/snmp_agent.c.o
[100%] Linking C executable snmp_agent
[100%] Built target snmp_agent
此 snmp_agent
作为 agentX 子代理运行,连接到主 snmpd
主代理并正确响应针对 entPhysicalEntry
标量对象的 SNMP 请求。
好的,到目前为止一切顺利。问题是当我尝试交叉编译它时它会在我的最终目标上做同样的事情,这是一个 ARMv7 Allwinner A20 板。我已经有一个可用的交叉工具链——我使用 Crosstool-NG. It is in the search PATH and its tools begin with the prefix armv7-a20_allwinner-linux-gnueabihf
. In order to use it, I set up the following toolchain file 创建的,它被命名为 armv7-a20_allwinner-linux-gnueabihf.cmake
:
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Linux)
# which C and C++ compiler to use
SET(CMAKE_C_COMPILER armv7-a20_allwinner-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER armv7-a20_allwinner-linux-gnueabihf-g++)
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH "$ENV{HOME}/arm_rootfs")
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
对 ARM 版本使用不同的构建目录,CMake 应该选择指向 ARM 根文件系统的正确路径,这是目标板 SD 卡上内容的副本:
cd ~/git/snmp_agent # CMakeLists.txt and armv7-a20_allwinner-linux-gnueabihf.cmake are here
mkdir build-arm
cd build-arm
cmake -DCMAKE_TOOLCHAIN_FILE=../armv7-a20_allwinner-linux-gnueabihf.cmake ..
请注意,显然 CMake 运行良好,因为找到的路径都是相对于 ARM 根文件系统的:
claudio@slackdev:~/git/snmp_agent/build-arm$ cmake -DCMAKE_TOOLCHAIN_FILE=../cmake_defs/armv7-a20_allwinner-linux-gnueabihf.cmake ..
-- The C compiler identification is GNU 6.3.0
-- Check for working C compiler: /home/claudio/x-tools/armv7-a20_allwinner-linux-gnueabihf/bin/armv7-a20_allwinner-linux-gnueabihf-gcc
-- Check for working C compiler: /home/claudio/x-tools/armv7-a20_allwinner-linux-gnueabihf/bin/armv7-a20_allwinner-linux-gnueabihf-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
Net-SNMP package CFLAGS: -DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -O2 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -I/usr/lib/perl5/CORE -I/usr/include/libnl3 -I/usr/include
Net-SNMP package LIBS: -L/usr/lib -lnetsnmpmibs -lsensors -lpci -ldl -lnetsnmpagent -lwrap -lnsl -Wl,-E -Wl,-rpath,/usr/lib/perl5/CORE -lnetsnmp -lcrypto -lnl-3 -lm
Found /home/claudio/arm_rootfs/usr/lib/libnetsnmpagent.so
Found /home/claudio/arm_rootfs/usr/lib/libnetsnmpmibs.so
Found /home/claudio/arm_rootfs/usr/lib/libnetsnmp.so
-- Configuring done
-- Generating done
-- Build files have been written to: /home/claudio/git/snmp_agent/build-arm
但是当 运行 make
它在寻找 /usr/include/gnu/stubs.h
时崩溃了。如果我们尝试用 VERBOSE=1
重复 make
,我们可以看到 gcc 调用正在使用相对于主机的路径:
claudio@slackdev:~/git/snmp_agent/build-arm$ make VERBOSE=1
/usr/bin/cmake -H/home/claudio/git/snmp_agent -B/home/claudio/git/snmp_agent/build-arm --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/claudio/git/snmp_agent/build-arm/CMakeFiles /home/claudio/git/snmp_agent/build-arm/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory 'snmp_agent/build-arm'
make -f CMakeFiles/snmp_agent.dir/build.make CMakeFiles/snmp_agent.dir/depend
make[2]: Entering directory 'snmp_agent/build-arm'
cd /home/claudio/git/snmp_agent/build-arm && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/claudio/git/snmp_agent /home/claudio/git/snmp_agent /home/claudio/git/snmp_agent/build-arm /home/claudio/git/snmp_agent/build-arm /home/claudio/git/snmp_agent/build-arm/CMakeFiles/snmp_agent.dir/DependInfo.cmake --color=
make[2]: Leaving directory 'snmp_agent/build-arm'
make -f CMakeFiles/snmp_agent.dir/build.make CMakeFiles/snmp_agent.dir/build
make[2]: Entering directory 'snmp_agent/build-arm'
[ 25%] Building C object CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o
/home/claudio/x-tools/armv7-a20_allwinner-linux-gnueabihf/bin/armv7-a20_allwinner-linux-gnueabihf-gcc -I. -Wall -Wstrict-prototypes -DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -O2 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -I/usr/lib/perl5/CORE -I/usr/include/libnl3 -I/usr/include -o CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o -c /home/claudio/git/snmp_agent/scalars/entPhysicalEntry.c
In file included from /usr/include/features.h:392:0,
from /usr/include/stdio.h:27,
from /usr/include/net-snmp/net-snmp-includes.h:14,
from /home/claudio/git/snmp_agent/scalars/entPhysicalEntry.c:7:
/usr/include/gnu/stubs.h:7:27: fatal error: gnu/stubs-32.h: No such file or directory
# include <gnu/stubs-32.h>
^
compilation terminated.
CMakeFiles/snmp_agent.dir/build.make:62: recipe for target 'CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o' failed
make[2]: *** [CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o] Error 1
make[2]: Leaving directory 'snmp_agent/build-arm'
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/snmp_agent.dir/all' failed
make[1]: *** [CMakeFiles/snmp_agent.dir/all] Error 2
make[1]: Leaving directory 'snmp_agent/build-arm'
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
奇怪的是来自 ARM 根文件系统的 /usr/include/gnu/stubs.h
与 stubs-32.h
:
无关
/* This file is automatically generated.
This file selects the right generated file of `__stub_FUNCTION' macros
based on the architecture being compiled for. */
#if !defined __ARM_PCS_VFP
# include <gnu/stubs-soft.h>
#endif
#if defined __ARM_PCS_VFP
# include <gnu/stubs-hard.h>
#endif
但是如果您从主机系统查看同一个文件 - 这是一台 x86_64 机器 - 我们可以猜测它为什么试图找到 stubs-32.h
(记住我们正在尝试为 ARM 编译, 所以它不会找到 __x86_64__
符号定义):
/* This file is automatically generated.
This file selects the right generated file of `__stub_FUNCTION' macros
based on the architecture being compiled for. */
#if !defined __x86_64__
# include <gnu/stubs-32.h>
#endif
#if defined __x86_64__ && defined __LP64__
# include <gnu/stubs-64.h>
#endif
#if defined __x86_64__ && defined __ILP32__
# include <gnu/stubs-x32.h>
#endif
为什么会发生这种情况,因为工具链文件明确指定仅在 CMAKE_FIND_ROOT_PATH 设置的路径中搜索库和包含项?
更新(问题还没有完全解决!):
在我接受了@Tsyvarev 的回答后,我仔细检查了我的 CMakeLists.txt 文件,我发现在尝试让它工作时我已经破解了手动设置 CMAKE_FIND_ROOT_PATH 作为每个编译器的前缀包括 net-snmp-config 返回的开关 (-I),这显然不是理想的做法。那与 CMAKE_SYSROOT 一起使它工作,但是 CMAKE_SYSROOT 本身并没有为包含路径添加前缀:
# This is the manually hacked line:
set(NETSNMPCFLAGS "-DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -O2 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I${CMAKE_FIND_ROOT_PATH}/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -I${CMAKE_FIND_ROOT_PATH}/usr/lib/perl5/CORE -I${CMAKE_FIND_ROOT_PATH}/usr/include/libnl3 -I${CMAKE_FIND_ROOT_PATH}/usr/include")
set(LIBS "${NETSNMPLIBS}")
set(STRICT_FLAGS "-Wall -Wstrict-prototypes")
set(CFLAGS "-I. ${STRICT_FLAGS} ${NETSNMPCFLAGS}")
您需要设置 CMAKE_SYSROOT 变量以供参考 "here is the target environment located"。
与 CMAKE_FIND_ROOT_PATH 变量不同,后者在 find_*
命令中 仅 使用,CMAKE_SYSROOT
也用作对编译器的提示(--sysroot
选项),因此编译器将选择正确的包含。
在交叉编译的情况下,变量 CMAKE_FIND_ROOT_PATH
用于为 find_*
命令提供 额外的 搜索前缀。 CMAKE_SYSROOT
自动用作前缀。
最终解决方案:
就像在问题 UPDATE 上解释的那样,@Tsyvarev 的回答通过向编译器提供指向 ARM 根文件系统的 --sysroot
开关来修复链接部分,但是从主机系统中找到 stubs-32.h
的问题并没有因此而得到解决。尽管它是解决方案的一部分,但重要的是要注意主要观察结果还有另一个原因:include -I
开关直接用作编译器的 CFLAGS
,使其能够有效地查看主机系统,因为它们没有以 ARM 板的根文件系统路径为前缀(记住标志是由 net-snmp-config 脚本直接输出的,它报告 native ARM 构建,因此使用 "normal" 路径)。为了解决这个问题,我使用了 CMake 字符串命令从 NETSNMPCFLAGS
变量中删除所有 -I
开关:
# Gets compiling flags and libs linked to Net-SNMP
execute_process(COMMAND "${NETSNMPCONFIG}" "--base-cflags" OUTPUT_VARIABLE NETSNMPCFLAGS)
execute_process(COMMAND "${NETSNMPCONFIG}" "--agent-libs" OUTPUT_VARIABLE NETSNMPLIBS)
# removes the include dir switches "-I" from the NETSNMPCFLAGS, since we don't want
# the compiler to include paths relative to the host system in the compilation
string(REGEX REPLACE "-I[a-zA-Z0-9/]*" "" NETSNMPCFLAGS ${NETSNMPCFLAGS})
set(STRICT_FLAGS "-Wall -Wstrict-prototypes")
set(CFLAGS "-I. ${STRICT_FLAGS} ${NETSNMPCFLAGS}")
并将根文件系统放在 include_directories 指令中:
# Sets the flags to be used for compiling and linking the executable
set_source_files_properties(${SRCS} COMPILE_FLAGS ${CFLAGS})
include_directories(${CMAKE_FIND_ROOT_PATH}/usr/include)
add_executable(${CMAKE_PROJECT_NAME} ${SRCS})
target_link_libraries(${CMAKE_PROJECT_NAME} ${NETSNMPAGENT} ${NETSNMPMIBS} ${NETSNMP})
cmake
引用主机文件的原因可能与 pkg-config
有关。
参见this document关于交叉编译和pkg-config
。
我用以下内容更新了我的 toolchain.cmake
文件:
set(CMAKE_SYSROOT /home/windel/rpi-sysroot)
# Pkg config tweaks:
SET(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/aarch64-linux-gnu/pkgconfig)
SET(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
我正在开发 Net-SNMP subagent whose final target will be an ARM board, so I'm using CMake 以便更轻松地管理构建本机和交叉编译版本。我开始在我的主机平台 (x86_64) 上测试它,它可以构建并与这个 CMakeLists.txt 文件一起正常工作:
cmake_minimum_required (VERSION 2.6)
project (snmp_agent C)
set(snmp_agent_VERSION_MAJOR 1)
set(snmp_agent_VERSION_MINOR 0)
# Defines path to the net-snmp-config script
set(NETSNMPCONFIG "${CMAKE_FIND_ROOT_PATH}/usr/bin/net-snmp-config")
# Gets compiling flags and libs linked to Net-SNMP
execute_process(COMMAND "${NETSNMPCONFIG}" "--base-cflags" OUTPUT_VARIABLE NETSNMPCFLAGS)
execute_process(COMMAND "${NETSNMPCONFIG}" "--agent-libs" OUTPUT_VARIABLE NETSNMPLIBS)
# Removes leading/trailing spaces from net-snmp-config output
string(STRIP ${NETSNMPCFLAGS} NETSNMPCFLAGS)
string(STRIP ${NETSNMPLIBS} NETSNMPLIBS)
# Prints compilation and linker flags used in Net-SNMP package
message("Net-SNMP package CFLAGS: ${NETSNMPCFLAGS}")
message("Net-SNMP package LIBS: ${NETSNMPLIBS}")
# Setting libs and compilation flags variables
set(LIBS "${NETSNMPLIBS}")
set(STRICT_FLAGS "-Wall -Wstrict-prototypes")
set(CFLAGS "-I. ${STRICT_FLAGS} ${NETSNMPCFLAGS}")
# Sets prefix for files created by 'mib2c' for the wanted MIB
set(ENT_PHYSICAL_ENTRY "scalars/entPhysicalEntry")
# Source files created by 'mib2c' and then user customized
set(USER_SRCS
${ENT_PHYSICAL_ENTRY}.c
)
# Setting subagent sources
set(SRCS ${USER_SRCS}
${CMAKE_PROJECT_NAME}.c
)
# Finds the required Net-SNMP lib paths and assigns them to variables
find_library(NETSNMPAGENT "netsnmpagent")
message("Found ${NETSNMPAGENT}")
find_library(NETSNMPMIBS "netsnmpmibs")
message("Found ${NETSNMPMIBS}")
find_library(NETSNMP "netsnmp")
message("Found ${NETSNMP}")
# Sets the flags to be used for compiling and linking the executable
set_source_files_properties(${SRCS} COMPILE_FLAGS ${CFLAGS})
add_executable(${CMAKE_PROJECT_NAME} ${SRCS})
target_link_libraries(${CMAKE_PROJECT_NAME} ${NETSNMPAGENT} ${NETSNMPMIBS} ${NETSNMP})
我使用一个单独的构建目录,所以我不会混合构建和源文件,它们与 CMakeLists.txt 文件一起,所以这些的输出命令...
cd ~/git/snmp_agent # CMakeLists.txt is in here along with source files
mkdir build
cd build
cmake ..
...如下:
claudio@slackdev:~/git/snmp_agent/build$ cmake ..
-- The C compiler identification is GNU 5.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
Net-SNMP package CFLAGS: -DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -O2 -fPIC -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -I/usr/lib64/perl5/CORE -I/usr/include/libnl3 -I/usr/include
Net-SNMP package LIBS: -L/usr/lib64 -lnetsnmpmibs -lsensors -lpci -ldl -lnetsnmpagent -lwrap -lnsl -Wl,-E -Wl,-rpath,/usr/lib64/perl5/CORE -lnetsnmp -lcrypto -lnl-3 -lm
Found /usr/lib64/libnetsnmpagent.so
Found /usr/lib64/libnetsnmpmibs.so
Found /usr/lib64/libnetsnmp.so
-- Configuring done
-- Generating done
-- Build files have been written to: /home/claudio/git/snmp_agent/build
运行 make
,构建得很好:
claudio@slackdev:~/git/snmp_agent/build$ make
Scanning dependencies of target snmp_agent
[ 25%] Building C object CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o
[ 50%] Building C object CMakeFiles/snmp_agent.dir/snmp_agent.c.o
[100%] Linking C executable snmp_agent
[100%] Built target snmp_agent
此 snmp_agent
作为 agentX 子代理运行,连接到主 snmpd
主代理并正确响应针对 entPhysicalEntry
标量对象的 SNMP 请求。
好的,到目前为止一切顺利。问题是当我尝试交叉编译它时它会在我的最终目标上做同样的事情,这是一个 ARMv7 Allwinner A20 板。我已经有一个可用的交叉工具链——我使用 Crosstool-NG. It is in the search PATH and its tools begin with the prefix armv7-a20_allwinner-linux-gnueabihf
. In order to use it, I set up the following toolchain file 创建的,它被命名为 armv7-a20_allwinner-linux-gnueabihf.cmake
:
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Linux)
# which C and C++ compiler to use
SET(CMAKE_C_COMPILER armv7-a20_allwinner-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER armv7-a20_allwinner-linux-gnueabihf-g++)
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH "$ENV{HOME}/arm_rootfs")
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
对 ARM 版本使用不同的构建目录,CMake 应该选择指向 ARM 根文件系统的正确路径,这是目标板 SD 卡上内容的副本:
cd ~/git/snmp_agent # CMakeLists.txt and armv7-a20_allwinner-linux-gnueabihf.cmake are here
mkdir build-arm
cd build-arm
cmake -DCMAKE_TOOLCHAIN_FILE=../armv7-a20_allwinner-linux-gnueabihf.cmake ..
请注意,显然 CMake 运行良好,因为找到的路径都是相对于 ARM 根文件系统的:
claudio@slackdev:~/git/snmp_agent/build-arm$ cmake -DCMAKE_TOOLCHAIN_FILE=../cmake_defs/armv7-a20_allwinner-linux-gnueabihf.cmake ..
-- The C compiler identification is GNU 6.3.0
-- Check for working C compiler: /home/claudio/x-tools/armv7-a20_allwinner-linux-gnueabihf/bin/armv7-a20_allwinner-linux-gnueabihf-gcc
-- Check for working C compiler: /home/claudio/x-tools/armv7-a20_allwinner-linux-gnueabihf/bin/armv7-a20_allwinner-linux-gnueabihf-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
Net-SNMP package CFLAGS: -DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -O2 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -I/usr/lib/perl5/CORE -I/usr/include/libnl3 -I/usr/include
Net-SNMP package LIBS: -L/usr/lib -lnetsnmpmibs -lsensors -lpci -ldl -lnetsnmpagent -lwrap -lnsl -Wl,-E -Wl,-rpath,/usr/lib/perl5/CORE -lnetsnmp -lcrypto -lnl-3 -lm
Found /home/claudio/arm_rootfs/usr/lib/libnetsnmpagent.so
Found /home/claudio/arm_rootfs/usr/lib/libnetsnmpmibs.so
Found /home/claudio/arm_rootfs/usr/lib/libnetsnmp.so
-- Configuring done
-- Generating done
-- Build files have been written to: /home/claudio/git/snmp_agent/build-arm
但是当 运行 make
它在寻找 /usr/include/gnu/stubs.h
时崩溃了。如果我们尝试用 VERBOSE=1
重复 make
,我们可以看到 gcc 调用正在使用相对于主机的路径:
claudio@slackdev:~/git/snmp_agent/build-arm$ make VERBOSE=1
/usr/bin/cmake -H/home/claudio/git/snmp_agent -B/home/claudio/git/snmp_agent/build-arm --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/claudio/git/snmp_agent/build-arm/CMakeFiles /home/claudio/git/snmp_agent/build-arm/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory 'snmp_agent/build-arm'
make -f CMakeFiles/snmp_agent.dir/build.make CMakeFiles/snmp_agent.dir/depend
make[2]: Entering directory 'snmp_agent/build-arm'
cd /home/claudio/git/snmp_agent/build-arm && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/claudio/git/snmp_agent /home/claudio/git/snmp_agent /home/claudio/git/snmp_agent/build-arm /home/claudio/git/snmp_agent/build-arm /home/claudio/git/snmp_agent/build-arm/CMakeFiles/snmp_agent.dir/DependInfo.cmake --color=
make[2]: Leaving directory 'snmp_agent/build-arm'
make -f CMakeFiles/snmp_agent.dir/build.make CMakeFiles/snmp_agent.dir/build
make[2]: Entering directory 'snmp_agent/build-arm'
[ 25%] Building C object CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o
/home/claudio/x-tools/armv7-a20_allwinner-linux-gnueabihf/bin/armv7-a20_allwinner-linux-gnueabihf-gcc -I. -Wall -Wstrict-prototypes -DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -O2 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -I/usr/lib/perl5/CORE -I/usr/include/libnl3 -I/usr/include -o CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o -c /home/claudio/git/snmp_agent/scalars/entPhysicalEntry.c
In file included from /usr/include/features.h:392:0,
from /usr/include/stdio.h:27,
from /usr/include/net-snmp/net-snmp-includes.h:14,
from /home/claudio/git/snmp_agent/scalars/entPhysicalEntry.c:7:
/usr/include/gnu/stubs.h:7:27: fatal error: gnu/stubs-32.h: No such file or directory
# include <gnu/stubs-32.h>
^
compilation terminated.
CMakeFiles/snmp_agent.dir/build.make:62: recipe for target 'CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o' failed
make[2]: *** [CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.c.o] Error 1
make[2]: Leaving directory 'snmp_agent/build-arm'
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/snmp_agent.dir/all' failed
make[1]: *** [CMakeFiles/snmp_agent.dir/all] Error 2
make[1]: Leaving directory 'snmp_agent/build-arm'
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
奇怪的是来自 ARM 根文件系统的 /usr/include/gnu/stubs.h
与 stubs-32.h
:
/* This file is automatically generated.
This file selects the right generated file of `__stub_FUNCTION' macros
based on the architecture being compiled for. */
#if !defined __ARM_PCS_VFP
# include <gnu/stubs-soft.h>
#endif
#if defined __ARM_PCS_VFP
# include <gnu/stubs-hard.h>
#endif
但是如果您从主机系统查看同一个文件 - 这是一台 x86_64 机器 - 我们可以猜测它为什么试图找到 stubs-32.h
(记住我们正在尝试为 ARM 编译, 所以它不会找到 __x86_64__
符号定义):
/* This file is automatically generated.
This file selects the right generated file of `__stub_FUNCTION' macros
based on the architecture being compiled for. */
#if !defined __x86_64__
# include <gnu/stubs-32.h>
#endif
#if defined __x86_64__ && defined __LP64__
# include <gnu/stubs-64.h>
#endif
#if defined __x86_64__ && defined __ILP32__
# include <gnu/stubs-x32.h>
#endif
为什么会发生这种情况,因为工具链文件明确指定仅在 CMAKE_FIND_ROOT_PATH 设置的路径中搜索库和包含项?
更新(问题还没有完全解决!):
在我接受了@Tsyvarev 的回答后,我仔细检查了我的 CMakeLists.txt 文件,我发现在尝试让它工作时我已经破解了手动设置 CMAKE_FIND_ROOT_PATH 作为每个编译器的前缀包括 net-snmp-config 返回的开关 (-I),这显然不是理想的做法。那与 CMAKE_SYSROOT 一起使它工作,但是 CMAKE_SYSROOT 本身并没有为包含路径添加前缀:
# This is the manually hacked line:
set(NETSNMPCFLAGS "-DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -O2 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I${CMAKE_FIND_ROOT_PATH}/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -I${CMAKE_FIND_ROOT_PATH}/usr/lib/perl5/CORE -I${CMAKE_FIND_ROOT_PATH}/usr/include/libnl3 -I${CMAKE_FIND_ROOT_PATH}/usr/include")
set(LIBS "${NETSNMPLIBS}")
set(STRICT_FLAGS "-Wall -Wstrict-prototypes")
set(CFLAGS "-I. ${STRICT_FLAGS} ${NETSNMPCFLAGS}")
您需要设置 CMAKE_SYSROOT 变量以供参考 "here is the target environment located"。
与 CMAKE_FIND_ROOT_PATH 变量不同,后者在 find_*
命令中 仅 使用,CMAKE_SYSROOT
也用作对编译器的提示(--sysroot
选项),因此编译器将选择正确的包含。
在交叉编译的情况下,变量 CMAKE_FIND_ROOT_PATH
用于为 find_*
命令提供 额外的 搜索前缀。 CMAKE_SYSROOT
自动用作前缀。
最终解决方案:
就像在问题 UPDATE 上解释的那样,@Tsyvarev 的回答通过向编译器提供指向 ARM 根文件系统的 --sysroot
开关来修复链接部分,但是从主机系统中找到 stubs-32.h
的问题并没有因此而得到解决。尽管它是解决方案的一部分,但重要的是要注意主要观察结果还有另一个原因:include -I
开关直接用作编译器的 CFLAGS
,使其能够有效地查看主机系统,因为它们没有以 ARM 板的根文件系统路径为前缀(记住标志是由 net-snmp-config 脚本直接输出的,它报告 native ARM 构建,因此使用 "normal" 路径)。为了解决这个问题,我使用了 CMake 字符串命令从 NETSNMPCFLAGS
变量中删除所有 -I
开关:
# Gets compiling flags and libs linked to Net-SNMP
execute_process(COMMAND "${NETSNMPCONFIG}" "--base-cflags" OUTPUT_VARIABLE NETSNMPCFLAGS)
execute_process(COMMAND "${NETSNMPCONFIG}" "--agent-libs" OUTPUT_VARIABLE NETSNMPLIBS)
# removes the include dir switches "-I" from the NETSNMPCFLAGS, since we don't want
# the compiler to include paths relative to the host system in the compilation
string(REGEX REPLACE "-I[a-zA-Z0-9/]*" "" NETSNMPCFLAGS ${NETSNMPCFLAGS})
set(STRICT_FLAGS "-Wall -Wstrict-prototypes")
set(CFLAGS "-I. ${STRICT_FLAGS} ${NETSNMPCFLAGS}")
并将根文件系统放在 include_directories 指令中:
# Sets the flags to be used for compiling and linking the executable
set_source_files_properties(${SRCS} COMPILE_FLAGS ${CFLAGS})
include_directories(${CMAKE_FIND_ROOT_PATH}/usr/include)
add_executable(${CMAKE_PROJECT_NAME} ${SRCS})
target_link_libraries(${CMAKE_PROJECT_NAME} ${NETSNMPAGENT} ${NETSNMPMIBS} ${NETSNMP})
cmake
引用主机文件的原因可能与 pkg-config
有关。
参见this document关于交叉编译和pkg-config
。
我用以下内容更新了我的 toolchain.cmake
文件:
set(CMAKE_SYSROOT /home/windel/rpi-sysroot)
# Pkg config tweaks:
SET(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/aarch64-linux-gnu/pkgconfig)
SET(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})