我正在开发一个Net-SNMP子代理,其最终目标将是一个ARM板,所以我使用CMake来更容易地pipe理构build本地和交叉编译的版本。 我开始在我的主机平台(x86_64)上进行testing,并使用CMakeLists.txt文件构build并正常工作:
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})
我使用一个单独的生成目录,所以我不混合构build和源文件,这与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
,它build立得很好:
claudio@slackdev:~/git/snmp_agent/build$ make Scanning dependencies of target snmp_agent [ 25%] Building C object CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.co [ 50%] Building C object CMakeFiles/snmp_agent.dir/snmp_agent.co [100%] Linking C executable snmp_agent [100%] Built target snmp_agent
此snmp_agent
作为agentX子代理运行,连接到主snmpd
主代理,并正确回应针对entPhysicalEntry
标量对象的SNMP请求。
好吧,那么好。 问题是当我试图交叉编译这个,所以它在我的最终目标,这是一个ARMv7的Allwinner A20板做同样的事情。 我已经有一个交叉工具链 – 我使用Crosstool-NG创build的。 它在searchPATH中,其工具以前缀armv7-a20_allwinner-linux-gnueabihf
。 为了使用它,我build立了以下工具链文件 ,名为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应该select指向ARM根文件系统的正确path,这是目标板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运行良好,因为find的path都是相对于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
但是在运行时,在寻找/usr/include/gnu/stubs.h
时候make
让它崩溃。 如果我们试着用VERBOSE=1
重复make
,我们可以看到gcc调用正在使用相对于主机的path:
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.co /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.co -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.co' failed make[2]: *** [CMakeFiles/snmp_agent.dir/scalars/entPhysicalEntry.co] 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
没有任何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机器)查看同一个文件,我们可以猜测它为什么试图findstubs-32.h
(记住我们正在为ARM编译,所以它不会find__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设置的path中search库和包含?
更新(问题还没有完全解决!):
在我接受@Tsyvarev的答案后,我仔细检查了我的CMakeLists.txt文件,并在尝试使其工作时发现我通过手动设置CMAKE_FIND_ROOT_PATH作为每个编译器的前缀包括由networking返回的交换机(-I) -snmp-config ,这显然不是理想的事情。 这和CMAKE_SYSROOT一起工作,但是CMAKE_SYSROOT本身并不包含包含path的前缀:
# 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变量设置为“这里是目标环境”。
与仅用于find_*
命令的CMAKE_FIND_ROOT_PATH变量不同, CMAKE_SYSROOT也用作编译器( --sysroot
选项)的提示,所以编译器会选择正确的包含。
在交叉编译的情况下,变量CMAKE_FIND_ROOT_PATH
用于为find_*
命令提供额外的搜索前缀。 CMAKE_SYSROOT
被自动用作前缀。
最终解决方案:
就像在UPDATE问题中解释的一样,@ Tsyvarev的答案通过向指向ARM根文件系统的编译器提供了--sysroot
开关来修复链接部分,但是从主机系统找到stubs-32.h
的问题不是由此修复。 虽然它是解决方案的一部分,但重要的是要注意,主要的观察还有另外一个原因:include -I
开关被直接用作编译器的CFLAGS
,使得它有效地看待主机系统,因为它们没有加前缀ARM板的根文件系统路径(请记住,标记是由net-snmp-config脚本直接输出的,它报告的是本地 ARM版本的标志,所以使用“普通”路径)。 为了解决这个问题,我使用了一个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})