4 CMake 练习
书写一个由 cmake 组织的 C++ 工程,要求如下:
- include/hello.h 和 src/hello.c 构成了 libhello.so 库。hello.c 中提供一个函数 sayHello(),调用此 函数时往屏幕输出一行“Hello SLAM”。我们已经为你准备了 hello.h 和 hello.c 这两个文件,见 “code/”目录下。
- 文件 useHello.c 中含有一个 main 函数,它可以编译成一个可执行文件,名为“sayhello”
- 默认用 Release 模式编译这个工程。
- 如果用户使用 sudo make install,那么将 hello.h 放至/usr/local/include/下,将 libhello.so 放 至/usr/local/lib/下。
- 为你的库提供 FindHello.cmake 文件,让其他用户可以通过 find_package 命令找到你的库,并实际测试你的程序确实可以这样做。
1.建立共享库(hello_lib)
在hello_lib文件夹下创建三个文件夹:build存放编译临时文件,include存放hello.h,src存放hello.cpp。
hello.h
#pragma once
void sayHello();
hello.cpp
#include "hello.h"
#include <iostream>
using namespace std;
void sayHello(){
cout << "Hello World!"<< endl;
}
hello_lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(hello_lib)
include_directories(${PROJECT_SOURCE_DIR}/include)
add_subdirectory(src)
hello_lib/src/CMakeLists.txt
add_library(hello SHARED hello.cpp)
install(FILES ${PROJECT_SOURCE_DIR}/include/hello.h DESTINATION /usr/local/include)
install(TARGETS hello LIBRARY DESTINATION /usr/local/lib)
此段代码目的是将hello.h(头文件)与hello(共享库)安装到本机。
可以在build目录下运行cmake … 编译并调用sudo make install安装
2.使用共享库(say_hello)
在say_hello文件夹下创建两个个文件夹:build存放编译临时文件,src存放useHello.cpp。
useHello.cpp
#include "hello.h"
using namespace std;
int main(int agrc,char **argv){
sayHello();
return 0;
}
say_hello/CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(sayHello)
add_subdirectory(src)
say_hello/src/CMakeLists.txt
include_directories(/usr/local/include)
link_directories(/usr/local/lib)
add_executable(sayHello useHello.cpp)
target_link_libraries(sayHello hello)
5 gflags, glog, gtest 的使用
glog 是日志打印工具,gflags 是参数管理工具,而 gtest 是单元测试工具。当我们在开发自己的应用时,可以灵活地使用这些三方库,加快算法的开发效率。
-
请自行寻找这三个库的说明文档,并在系统中安装它们。请说明你是如何安装的。
(1)进入clone后的目录,在此目录中创建build目录:mkdir build
(2)在build目录下执行cmake …
(3)执行make 编译
(4)执行安装命令:make install(可能需要root权限,su root即可进入root)
PS:需要先安装glog再安装gflags,否则glog将无法安装。
-
将上一题中的打印改为使用 glog 的打印方式,以替代 std::cout 的输出方式。
1.共享库文件修改
hello.h
#pragma once #include <glog/logging.h> void sayHello();
hello.cpp
#include "hello.h" #include <iostream> #include <glog/logging.h> using namespace std; void sayHello(){ LOG(INFO) << "Hello World!"; }
hello_lib/src/CMakeLists.txt
add_library(hello SHARED hello.cpp) include_directories(/usr/local/include/glog) link_directories(/usr/local/lib) target_link_libraries(hello glog) install(FILES ${PROJECT_SOURCE_DIR}/include/hello.h DESTINATION /usr/local/include) install(TARGETS hello LIBRARY DESTINATION /usr/local/lib/lib)
useHello.cpp
#include "hello.h" using namespace std; int main(int argc,char **argv){ sayHello(); return 0; }
2.运行结果
WARNING: Logging before InitGoogleLogging() is written to STDERR I20220423 16:14:39.055696 10549 hello.cpp:6] Hello World!
3.解决WARNING
根据警告提示修改useHello.cpp
#include "hello.h" #include <glog/logging.h> using namespace std; int main(int agrc,char **argv){ google::InitGoogleLogging(argv[0]); google::SetStderrLogging(google::GLOG_INFO); sayHello(); return 0; }
修改say_hello/src/CMakeLists.txt文件
include_directories(/usr/local/include) include_directories(/usr/local/include/glog) link_directories(/usr/local/lib) link_directories(/usr/local/lib/lib) add_executable(sayHello useHello.cpp) target_link_libraries(sayHello hello glog)
编译运行:
I20220423 16:55:50.654726 13485 hello.cpp:6] Hello World!
sayHello的链接情况:
ldd src/sayHello
libhello.so => /usr/local/lib/lib/libhello.so libglog.so.1 => /usr/local/lib/libglog.so.1
注:使用glog之前必须先初始化库,要生成日志文件只需在开始log之前调用一次:
google::InitGoogleLogging(argv[0]); //括号内是程序名
当要结束glog时必须关闭库,否则会内存溢出:
google::ShutdownGoogleLogging();
3.在 useHello.cpp 中增加一个 gflags 以指明打印的次数 print_times,默认为 1。当用户传递该参数时, 即打印多少遍 Hello SLAM。
主要修改say_hello
(1)say_hello/src/useHello.cpp修改
#include "hello.h"
#include <glog/logging.h>
#include <gflags/gflags.h>
using namespace std;
DEFINE_int32(print_times,1,"number of times for output");
int main(int argc,char **argv){
gflags::ParseCommandLineFlags(&argc,&argv,true);
google::InitGoogleLogging(argv[0]);
google::SetStderrLogging(google::GLOG_INFO);
//将输入的字符串转化为数字
if(argv[1])
{
FLAGS_print_times = stoi(argv[1]);
}
for(int i = 0;i < FLAGS_print_times;i++)
{
sayHello();
}
return 0;
}
(2)say_hello/src/CMakeLists.txt修改
include_directories(/usr/local/include)
include_directories(/usr/local/include/glog)
include_directories(/usr/local/include/gflags)
link_directories(/usr/local/lib)
link_directories(/usr/local/lib/lib)
add_executable(sayHello useHello.cpp)
target_link_libraries(sayHello hello glog gflags)
(3)运行
在 ./sayHello 后面添加数字即可重复打印。
./sayHello 3
I20220423 20:03:10.555475 17494 hello.cpp:6] Hello World!
I20220423 20:03:10.555738 17494 hello.cpp:6] Hello World!
I20220423 20:03:10.555755 17494 hello.cpp:6] Hello World!
3.书写一个 gtest 单元测试程序来测试你的工程能够正常运行。修改你的 CMakeLists.txt 来增加这 个单元测试。
(1)创建test文件夹,在其中创建testUsehello.cpp与CMakeLists.txt
testUseHello.cpp
#include <gtest/gtest.h>
#include <glog/logging.h>
#include "hello.h"
TEST(sayHelloTest,sayHelloNoThrow)
{
EXPECT_NO_THROW(sayHello());
}
int main(int argc, char *argv[])
{
google::InitGoogleLogging(argv[0]);
google::SetStderrLogging(google::GLOG_INFO);
::testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
CMakeLists.txt
include_directories(/usr/local/include)
include_directories(/usr/local/include/glog)
include_directories(/usr/local/include/gflags)
include_directories(/usr/local/include/gtest)
include_directories(/usr/local/include/gmock)
link_directories(/usr/local/lib)
link_directories(/usr/local/lib/lib)
add_executable(testUseHello testUseHello.cpp)
target_link_libraries(testUseHello hello glog gflags gtest gmock pthread)
say_hello/CMakeLists.txt中需添加:
add_subdirectory(test)
(2)编译运行
结果如下:
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from sayHelloTest
[ RUN ] sayHelloTest.sayHelloNoThrow
I20220424 14:51:29.943917 8962 hello.cpp:6] Hello World!
[ OK ] sayHelloTest.sayHelloNoThrow (0 ms)
[----------] 1 test from sayHelloTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.