链接第三方库,比如OpenCV
确保库已安装:
先将外部第三方库安装在系统中,或者将库的文件放在指定的路径下。
查找OpenCV软件包:
FIND_PACKAGE(OpenCV 4 PATHS ./libopencv REQUIRED)
OpenCV:此为要查找的软件包名称,也就是要在系统里查找 OpenCV 库。4:这是指定要查找的 OpenCV 版本号。CMake 会去查找版本号为 4.x 的 OpenCV 库,要是系统中不存在符合该版本要求的库,查找操作就会失败。PATHS ./libopencv:PATHS 这个参数用于指定额外的查找路径。在这个例子里,CMake 除了会在系统默认路径中查找 OpenCV 库之外,还会在当前目录下的 ./libopencv 路径中进行查找。REQUIRED:这是一个可选参数。当指定了 REQUIRED 之后,若 CMake 未能找到符合要求的 OpenCV 库,就会停止配置过程并报错,以此确保项目不会在缺少必要依赖的情况下继续构建。
添加可执行文件或库:
使用add_executable或add_library命令添加项目中的可执行文件或库。例如,如果要创建一个名为main的可执行文件,可以使用add_executable(main main.cpp)。
链接OpenCV库:
使用target_link_libraries命令将 OpenCV 库链接到目标可执行文件或库。
target_link_libraries(main ${OpenCV_LIBS})
示例代码 :
cmake_minimum_required(VERSION 3.10)
project(MyOpenCVProject)
# 查找 OpenCV 库
find_package(OpenCV 4 PATHS ./libopencv REQUIRED)
# 检查是否成功找到 OpenCV 库
if(OpenCV_FOUND)
message(STATUS "OpenCV library found: ${OpenCV_VERSION}")
message(STATUS "OpenCV include directories: ${OpenCV_INCLUDE_DIRS}")
message(STATUS "OpenCV libraries: ${OpenCV_LIBS}")
else()
message(FATAL_ERROR "OpenCV library not found.")
endif()
# 添加可执行文件
add_executable(MyOpenCVApp main.cpp)
# 链接 OpenCV 库
target_include_directories(MyOpenCVApp PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(MyOpenCVApp PRIVATE ${OpenCV_LIBS})
链接自己生成的库,其他库
target_link_libraries(EOSAngleCalibrationApp -Wl,--start-group EOS_AngleCalibration
${MULTI_THREAD_LIBS} Interfaces AlgoBase ImageLib FilterLib MathLibs Interactor
boost_filesystem boost_system boost_thread tinyxml2 png jpeg tiff ${OpenCV_LIBS}
${IPP_LIBS} ${MKL_LIBS} ${onnxruntime} ${DB_LIBS} -Wl,--end-group pthread rt -lz dl)
这段 CMake 语句 target_link_libraries 用于将多个库链接到名为 EOSAngleCalibrationApp 的目标(通常是一个可执行文件或库)上。下面详细解释一下这条语句的各个部分:
target_link_libraries:这是 CMake 中的一个命令,用于将库链接到指定的目标上。
EOSAngleCalibrationApp:这是目标的名称,即要链接库的可执行文件或库的名称。
-Wl,--start-group 和 -Wl,--end-group:这是链接器(ld)的选项。-Wl 表示后面的参数是传递给链接器的。--start-group 和 --end-group 之间的库会被链接器特殊处理,以解决库之间的依赖循环问题。这意味着链接器会多次扫描这些库,直到所有的符号都被解析。
EOS_AngleCalibration:这是一个库的名称,会被链接到目标上。
${MULTI_THREAD_LIBS}:这是一个变量,代表一组多线程相关的库。在实际构建时,这个变量会被其实际的值替换。
Interfaces AlgoBase ImageLib FilterLib MathLibs Interactor:这些都是库的名称,会被链接到目标上。
boost_filesystem boost_system boost_thread tinyxml2 png jpeg tiff:这些是第三方库的名称,分别是 Boost 库的文件系统、系统、线程模块,以及用于处理 XML、PNG、JPEG、TIFF 格式的库。
${OpenCV_LIBS}:这是一个变量,代表 OpenCV 库的集合。
${IPP_LIBS}:这是一个变量,代表 Intel Integrated Performance Primitives(IPP)库的集合。
${MKL_LIBS}:这是一个变量,代表 Intel Math Kernel Library(MKL)库的集合。
${onnxruntime}:这是一个变量,代表 ONNX Runtime 库。
${DB_LIBS}:这是一个变量,代表一组数据库相关的库。
pthread rt -lz dl:这些是系统库的名称,分别是 POSIX 线程库(pthread)、实时库(rt)、zlib 压缩库(-lz)和动态链接库(dl)。
总的来说,这条 CMake 语句的作用是将多个自定义库、第三方库和系统库链接到 EOSAngleCalibrationApp 目标上,以确保该目标在运行时能够访问到所需的功能。
生成、链接动态库/静态库的两种方法
1、gcc
生成静态库
静态链接库在编译时会被完整地整合到可执行文件中。
步骤:
编写源文件和头文件:假定有一个简单的数学函数库,包含加法和减法操作
math_operations.h
// math_operations.h
#ifndef MATH_OPERATIONS_H
#define MATH_OPERATIONS_H
int add(int a, int b);
int subtract(int a, int b);
#endif
math_operations.c
// math_operations.c
#include "math_operations.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
编译源文件为目标文件:运用 GCC 把源文件编译成目标文件(.o 文件)
gcc -c math_operations.c -o math_operations.o
gcc -c math_operations.c -o math_operations.o 是一条用于编译 C 语言源文件的命令,下面为你详细解释其各个部分的含义:
gcc :GCC(GNU Compiler Collection)是一个广泛使用的开源编译器套件,支持多种编程语言,包括 C、C++、Fortran 等。在这个命令里,它充当 C 语言的编译器,负责将 C 语言源代码转换为计算机能够理解和执行的机器代码。
-c :这是 GCC 编译器的一个选项,其作用是告诉编译器只进行编译操作,而不进行链接操作。具体来说,编译器会对输入的源文件进行预处理、编译和汇编,生成对应的目标文件,但不会将这些目标文件与其他库文件或目标文件组合成一个可执行程序。目标文件通常以 .o 作为文件扩展名,它包含了源文件编译后的机器代码,但还不能直接运行。
math_operations.c :这是要编译的 C 语言源文件的名称。该文件中包含了用 C 语言编写的程序代码,例如函数定义、变量声明等。编译器会对 math_operations.c 文件中的代码进行处理,将其转换为目标代码。
-o :这也是 GCC 编译器的一个选项,用于指定输出文件的名称。通常情况下,如果不使用 -o 选项,编译器会使用默认的输出文件名规则,例如将源文件名去掉扩展名后加上 .o 作为输出文件名。但通过 -o 选项,你可以明确指定输出文件的名称,方便管理和识别。
math_operations.o :这是指定的输出目标文件的名称。结合 -o 选项,编译器会将 math_operations.c 文件编译后生成的目标代码保存到 math_operations.o 文件中。这个目标文件可以在后续的链接过程中与其他目标文件或库文件一起使用,最终生成可执行程序。
总结:这条命令的整体功能是使用 GCC 编译器对 math_operations.c 源文件进行编译,生成对应的目标文件 math_operations.o,但不进行链接操作。在实际的软件开发过程中,通常会先将多个源文件分别编译成目标文件,然后再将这些目标文件和必要的库文件链接在一起,生成最终的可执行程序。
创建静态链接库:使用 ar 工具将目标文件打包成静态链接库(.a 文件)。静态库是由多个目标文件(.o 文件)打包而成,通常使用 ar 工具来创建
ar rcs libmath_operations.a math_operations.o
ar :ar 是一个用于创建、修改和提取归档文件的工具,全称为 "archive",常被用于创建静态库。静态库是一种将多个目标文件打包成一个单独文件的方式,方便在编译时链接到程序中。
r :r 是 ar 命令的一个选项,代表 "replace"(替换)。其作用是将指定的文件(这里是 math_operations.o)插入到归档文件(这里是 libmath_operations.a)中。如果归档文件不存在,就会创建一个新的归档文件;若归档文件中已经存在同名文件,新文件会替换旧文件。
c :c 同样是 ar 命令的选项,意思是 "create"(创建)。该选项表示在归档文件不存在时,直接创建它,并且不会给出警告信息。也就是说,即使 libmath_operations.a 文件原本不存在,使用此选项后,ar 命令会直接创建该文件,而不会提示你文件不存在的警告。
s :s 选项代表 "write an object file index"(写入对象文件索引)。它会在归档文件中创建一个符号表索引,这个索引可以加快链接器在链接时查找符号的速度。当链接器需要从静态库中查找特定的符号(如函数或变量)时,通过这个索引可以更高效地定位到相应的目标文件。
libmath_operations.a :这是要创建或修改的静态库的名称。在 Unix 系统中,静态库的命名通常遵循 lib
math_operations.o :这是要添加到静态库中的目标文件。在前面的编译过程中,gcc -c math_operations.c -o math_operations.o 命令已经将 math_operations.c 源文件编译成了目标文件 math_operations.o。现在,ar 命令会把这个目标文件打包到 libmath_operations.a 静态库中。
总结:这条命令的作用是将 math_operations.o 目标文件添加到一个名为 libmath_operations.a 的静态库中。如果该静态库不存在,就会创建一个新的库文件;同时,会在库文件中创建符号表索引,以提高链接效率。在后续的编译过程中,其他程序可以链接这个静态库,使用其中包含的函数和变量。
使用静态链接库:编写一个测试程序来使用这个静态链接库
// main.c
#include
#include "math_operations.h"
int main() {
int result_add = add(5, 3);
int result_subtract = subtract(5, 3);
printf("5 + 3 = %d\n", result_add);
printf("5 - 3 = %d\n", result_subtract);
return 0;
}
编译并链接静态库:
gcc main.c -L. -lmath_operations -o test_program
gcc :GCC 即 GNU Compiler Collection,是一个广泛使用的开源编译器套件,它支持 C、C++、Fortran 等多种编程语言。在该命令中,它作为 C 语言的编译器和链接器,负责将 C 语言源代码转化为可执行程序。
main.c :这是要编译的 C 语言源文件。一般来说,main.c 会包含程序的入口函数 main(),程序从这里开始执行。GCC 会对 main.c 文件进行预处理、编译、汇编等操作,生成对应的目标代码。
-L.
-L 是 GCC 编译器的一个选项,其作用是指定库文件的搜索路径。
. 代表当前目录。所以 -L. 表示告诉编译器在当前目录中查找需要链接的库文件。
-lmath_operations
-l 同样是 GCC 编译器的选项,用于指定要链接的库的名称。
math_operations 是库名。编译器会在 -L 选项指定的搜索路径(这里是当前目录)中查找名为 libmath_operations.a(静态库) 或者 libmath_operations.so(动态库) 的库文件,并将其链接到最终的可执行程序里。
-o test_program
-o 是 GCC 编译器的选项,用于指定输出文件的名称。
test_program 是最终生成的可执行程序的文件名。也就是说,编译和链接完成后,生成的可执行程序将被命名为 test_program。
总结:这条命令的功能是:使用 GCC 编译器对 main.c 源文件进行编译和汇编,生成目标代码;接着在当前目录下查找 libmath_operations 库文件,并将其链接到程序中;最后生成一个名为 test_program 的可执行程序。main.c 调用了 libmath_operations 库中定义的函数,通过链接该库,程序就能使用这些函数的功能。
运行程序
./main_program
生成动态库
动态链接库在程序运行时才会被加载。
步骤:
使用之前编写的源文件和头文件:math_operations.h 和 math_operations.c 保持不变。编译源文件为目标文件并生成动态链接库:使用 GCC 编译源文件并生成动态链接库(.so 文件)。
gcc -fPIC -shared math_operations.c -o libmath_operations.so
gcc -fPIC -shared math_operations.c -o libmath_operations.so 是一条用于编译和创建动态链接库(也称为共享库)的命令,下面为你详细解释各部分的含义。
gcc :gcc 是 GNU Compiler Collection 的缩写,是一个广泛使用的开源编译器套件,支持多种编程语言,这里主要用于编译 C 语言代码,将源代码转换为机器可执行的代码。
-fPIC
PIC 是 Position Independent Code(位置无关代码)的缩写。
-fPIC 是 GCC 的一个编译选项,它告诉编译器生成位置无关的代码。在创建共享库时,这是非常必要的。因为共享库可以被多个不同的程序在不同的内存地址加载和使用,如果代码不是位置无关的,那么每次加载到不同的内存地址时都需要进行重定位,这会带来额外的开销和复杂性。使用 -fPIC 选项生成的代码可以在任何内存地址处正确执行,无需进行重定位。
-shared :这是 GCC 的另一个重要选项,它指示编译器创建一个共享库而不是一个可执行文件。共享库是一种可以被多个程序同时使用的库文件,它在程序运行时被动态加载到内存中,这样可以减少内存的使用,并且方便库的更新和维护。
math_operations.c :这是要编译的源文件,它包含了用 C 语言编写的函数和变量定义等代码。编译器会对 math_operations.c 中的代码进行编译处理,生成相应的目标代码。
-o :这是 GCC 用于指定输出文件名称的选项。通过 -o 选项,你可以明确指定编译和链接后生成的文件的名称。
libmath_operations.so :这是指定的输出文件名,也是最终生成的共享库文件的名称。在类 Unix 系统中,共享库文件通常以 .so(shared object)作为文件扩展名。按照惯例,共享库的命名一般遵循 lib
即 ,math_operations。
总结
这条命令的整体作用是使用 GCC 编译器将 math_operations.c 源文件编译成位置无关的代码,并将其打包成一个名为 libmath_operations.so 的共享库。这个共享库可以在后续被其他程序动态链接和使用,以提供 math_operations.c 中定义的函数和功能。
或者
gcc -fPIC -c math_operations.c -o math_operations.o
gcc -shared -o libmath_operations.so math_operations.o
编译流程
gcc -fPIC -shared math_operations.c -o libmath_operations.so:这是一个一步到位的操作,GCC 直接将源文件 math_operations.c 编译并链接成共享库 libmath_operations.so。整个过程中,编译和链接操作一次性完成。
gcc -fPIC -c math_operations.c -o math_operations.o 和 gcc -shared -o libmath_operations.so math_operations.o:这是分两步完成的操作。第一步先将源文件 math_operations.c 编译成目标文件 math_operations.o;第二步再将目标文件 math_operations.o 链接成共享库 libmath_operations.so。
灵活性和可维护性
一步编译:当项目规模较小且源文件较少时,一步编译的方式更加简洁高效,减少了命令的输入量。但如果源文件发生变化,就需要重新编译整个共享库。
两步编译:在大型项目中,分两步编译的优势明显。当某个源文件修改后,只需重新编译该源文件生成新的目标文件,然后再进行链接操作,避免了不必要的重复编译,提高了编译效率。同时,在调试过程中,也可以单独对目标文件进行检查和分析,便于发现问题。
综上所述,一步编译适用于简单项目,而两步编译更适合大型、复杂的项目
使用动态链接库:使用之前编写的 main.c 测试程序编译并链接动态链接库:
gcc main.c -L. -lmath_operations -o test_program
运行程序时,需要设置动态库的搜索路径,否则程序可能找不到动态库。可以通过设置 LD_LIBRARY_PATH 环境变量来指定搜索路径:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./test_program
静态链接库会增加可执行文件的大小,但在运行时不需要额外的库文件;而动态链接库可以减少可执行文件的大小,并且多个程序可以共享同一个动态库。静态链接库在编译时就被完整地整合到可执行文件中,而动态链接库在运行时才会被加载。使用时,编译命令基本相同,但使用动态链接库时需要额外设置动态库的搜索路径。
2、CMake
生成静态库
静态库是在编译时被链接到可执行文件中的,最终可执行文件会包含静态库的所有代码。
步骤:
创建项目目录结构:假设你的项目名为 MyLibrary,创建如下目录结构:
MyLibrary/
├── CMakeLists.txt
├── src/
│ ├── library.cpp
│ └── library.h
编写源文件和头文件:在 src 目录下编写库的实现代码和头文件。
// 头文件 library.h
#ifndef LIBRARY_H
#define LIBRARY_H
int add(int a, int b);
#endif
// 源文件 library.cpp
#include "library.h"
int add(int a, int b) {
return a + b;
}
编写 CMakeLists.txt:在项目根目录下编写 CMakeLists.txt 文件,用于生成静态库。
# 设置 CMake 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(MyLibrary)
# 添加源文件
set(SOURCE_FILES src/library.cpp)
# 生成静态库
add_library(MyStaticLibrary STATIC ${SOURCE_FILES})
# 安装静态库和头文件
install(TARGETS MyStaticLibrary DESTINATION lib)
install(FILES src/library.h DESTINATION include)
生成动态库
动态库是在程序运行时被加载的,可被多个程序共享使用。
步骤:
使用上述的项目目录结构和源文件:源文件和头文件保持不变。修改 CMakeLists.txt:修改 CMakeLists.txt 文件,将生成静态库的部分改为生成动态库。
# 设置 CMake 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(MyLibrary)
# 添加源文件
set(SOURCE_FILES src/library.cpp)
# 生成动态库
add_library(MyDynamicLibrary SHARED ${SOURCE_FILES})
# 安装动态库和头文件
install(TARGETS MyDynamicLibrary DESTINATION lib)
install(FILES src/library.h DESTINATION include)
编译和使用库
编译库:在项目根目录下创建一个 build 目录,进入该目录并运行 CMake 和 make 命令。
mkdir build
cd build
cmake ..
make
使用库:编写一个简单的测试程序来使用生成的库。
MyLibrary/
├── CMakeLists.txt
├── src/
│ ├── library.cpp
│ └── library.h
├── test/
│ ├── main.cpp
│ └── CMakeLists.txt
test/main.cpp
#include
#include "library.h"
int main() {
int result = add(2, 3);
std::cout << "2 + 3 = " << result << std::endl;
return 0;
}
test/CMakeLists.txt
# 设置 CMake 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(TestMyLibrary)
# 添加可执行文件
add_executable(TestMyLibrary main.cpp)
# 查找库
find_library(MY_LIBRARY MyDynamicLibrary PATHS ../build)
# 链接库
target_link_libraries(TestMyLibrary ${MY_LIBRARY})
# 包含头文件目录
target_include_directories(TestMyLibrary PRIVATE ../src)
在 test 目录下创建 build 目录,进入该目录并运行 CMake 和 make 命令来编译测试程序。
mkdir build
cd build
cmake ..
make
./TestMyLibrary
错误分析
gcc EOSAngleCalibrationAPP.cc -L../build/EOS_AngleCalibration -lEOS_AngleCalibration -o main_program
g++ EOSAngleCalibrationAPP.cc -L../build/EOS_AngleCalibration -lEOS_AngleCalibration -lstdc++ -o main_program
g++ EOSAngleCalibrationAPP.cc -L../build/EOS_AngleCalibration -lEOS_AngleCalibration `pkg-config --cflags --libs opencv4` -o main_program