cmake杂记

变量

PROJECT_BINARY_DIR 编译目录,即build目录
PROJECT_SOURCE_DIR 工程目录

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

在哪里 ADD_EXECUTABLE 或 ADD_LIBRARY,如果需要改变目标存放路径,就在哪里加入上述的定义

命令

  • FIND_FILE( name1 path1 path2 …) VAR 变量代表找到的文件全路径,包含文件名
    FIND_LIBRARY( name1 path1 path2 …) VAR 变量表示找到的库全路径,包含库文件名
    FIND_PATH( name1 path1 path2 …) VAR 变量代表包含这个文件的路径。
    FIND_PROGRAM( name1 path1 path2 …) VAR 变量代表包含这个程序的全路径
  • add_library(libsugan ${SRC_LISTS})
    #将集合里的所有的源文件生成一个静态库,该静态库的名字libsugan target_link_libraries(libsugan ${OpenCV_LIBS}
    ${PROJECT_SOURCE_DIR}/lib/libCommonUtilities.so
    ${PROJECT_SOURCE_DIR}/lib/libInuStreams.so ) #链接静态库需要的依赖库
  • 也就是这个两个函数的作用应该差不多
    一个区别是:
    target_link_libraries 要在 add_executable 之后
    link_libraries 要在 add_executable 之前
  • file(GLOB_RECURSE mains ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
    ${CMAKE_CURRENT_SOURCE_DIR}下所有cpp文件名赋值给mains

编译选项

选项说明
-c用于把源码文件编译成 .o 对象文件,不进行链接过程
-o用于连接生成可执行文件,在其后可以指定输出文件的名称
-g用于在生成的目标可执行文件中,添加调试信息,可以使用GDB进行调试
-Idir用于把新目录添加到include路径上,可以使用相对和绝对路径,“-I.”、“-I./include”、“-I/opt/include”
-Wall生成常见的所有告警信息,且停止编译,具体是哪些告警信息,请参见GCC手册,一般用这个足矣!
-w关闭所有告警信息
-O表示编译优化选项,其后可跟优化等级0\1\2\3,默认是0,不优化
-fPIC用于生成位置无关的代码
-v(在标准错误)显示执行编译阶段的命令,同时显示编译器驱动程序,预处理器,编译器的版本号

GCC链接选项LDFLAGS参数

选项说明
-llibrary链接时在标准搜索目录中寻找库文件,搜索名为liblibrary.aliblibrary.so
-Ldir用于把新目录添加到库搜索路径上,可以使用相对和绝对路径,“-L.”、“-L./include”、“-L/opt/include”
-Wl,option把选项 option 传递给连接器,如果 option 中含有逗号,就在逗号处分割成多个选项
-static使用静态库链接生成目标文件,避免使用共享库,生成目标文件会比使用动态链接库大
CFLAGS = -g -Wall -I./include -I./include/tinyalsa -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -lc
LDFLAGS = -L./lib
ALL:
    $(CC) $(CFLAGS) $(LDFLAGS) main.c gfifo.c queue.c usbmonitor.c socket_rcv_360_server.c ./lib/srs_librtmp.a ./lib/libcrypto.a ./lib/libssl.a ./lib/libtinyalsa.a -o media_record -static -ldl -lstdc++ -lm -lpthread
clean:
    rm media_record *.raw *.mp4 *.wav -rf

CFLAGS = -g -Wall -I./include -I./include/tinyalsa -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -lc
-g:生成调试信息
-Wall:输出全部的告警信息
-I./include:添加头文件搜索目录./include
-I./include/tinyalsa:添加头文件搜索目录./include/tinyalsa
-Wl,--whole-archive -lpthread:将libpthread.so及以后的库中的所有符号都链接进可执行目标文件
-Wl,--no-whole-archive:关闭之前的-Wl,--whole-archive -lpthread 选项
LDFLAGS = -L./lib,将makefile当前目录下的、lib文件夹添加到库文件搜索目录

$(CC):makefile隐含变量,默认选择gcc编译器
$(CFLAGS):引用之前定义的CFLAGS变量,即编译选项参数
$(LDFLAGS):引用之前定义的LDFLAGS变量,即链接参数

$(CC) $(CFLAGS) $(LDFLAGS) main.c gfifo.c queue.c usbmonitor.c socket_rcv_360_server.c ./lib/srs_librtmp.a ./lib/libcrypto.a ./lib/libssl.a ./lib/libtinyalsa.a -o media_record -static -ldl -lstdc++ -lm -lpthread
使用定的编译器、编译选项参数、链接选项参数,编译.c文件,并使用静态方式链接制定的库文件,以及编译器目录下的libdl.a、libstdc++.a、libm.a、libpthread.a库文件生成 media_record 可执行目标文件。

privat、public

CMake是一个很强大的跨平台构建系统,但是学习难度也蛮大的

1. 指令说明

  • target_include_directories():指定目标包含的头文件路径。官方文档
  • target_link_libraries():指定目标链接的库。官方文档
  • target_compile_options():指定目标的编译选项。官方文档

目标add_library()add_executable() 生成。

这三个指令类似,这里以 target_include_directories() 为例进行讲解。

2. 指令讲解

测试工程目录结构:

cmake-test/                 工程主目录,main.c 调用 libhello-world.so
├── CMakeLists.txt
├── hello-world             生成 libhello-world.so,调用 libhello.so 和 libworld.so
│   ├── CMakeLists.txt
│   ├── hello               生成 libhello.so 
│   │   ├── CMakeLists.txt
│   │   ├── hello.c
│   │   └── hello.h         libhello.so 对外的头文件
│   ├── hello_world.c
│   ├── hello_world.h       libhello-world.so 对外的头文件
│   └── world               生成 libworld.so
│       ├── CMakeLists.txt
│       ├── world.c
│       └── world.h         libworld.so 对外的头文件
└── main.c

调用关系:

                                 ├────libhello.so
可执行文件────libhello-world.so
                                 ├────libworld.so

三大关键字用法说明:

PRIVATE私有的。生成 libhello-world.so 时,只在 hello_world.c 中包含了 hello.h,libhello-world.so 对外的头文件——hello_world.h 中不包含 hello.h 。而且 main.c 不会调用 hello.c 中的函数,或者说 main.c 不知道 hello.c 的存在,那么在 hello-world/CMakeLists.txt 中应该写入:

target_link_libraries(hello-world PRIVATE hello) // hello-world 表示可执行文件目标
target_include_directories(hello-world PRIVATE hello)

INTERFACE接口,即提供了接口。生成 libhello-world.so 时,只在libhello-world.so 对外的头文件——hello_world.h 中包含 了 hello.h, hello_world.c 中不包含 hello.h,即 libhello-world.so 不使用 libhello.so 提供的功能,只使用 hello.h 中的某些信息,比如结构体。但是 main.c 需要使用 libhello.so 中的功能。那么在 hello-world/CMakeLists.txt 中应该写入:

target_link_libraries(hello-world INTERFACE hello)
target_include_directories(hello-world INTERFACE hello)

PUBLIC:公开的。PUBLIC = PRIVATE + INTERFACE。生成 libhello-world.so 时,在 hello_world.c 和 hello_world.h 中都包含了 hello.h。并且 main.c 中也需要使用 libhello.so 提供的功能。那么在 hello-world/CMakeLists.txt 中应该写入:

target_link_libraries(hello-world PUBLIC hello)
target_include_directories(hello-world PUBLIC hello)

实际上,这三个关键字指定的是目标文件依赖项的使用范围(scope)或者一种传递(propagate)。具体来说:就是对内而言,是否使用?对外而言,是否要传递出去?

本例中:
可执行文件依赖 libhello-world.so, libhello-world.so 依赖 libhello.so 和 libworld.so。

  • main.c 不使用 libhello.so 的任何功能,因此 libhello-world.so 不需要将其依赖—— libhello.so 传递给 main.c,hello-world/CMakeLists.txt 中使用 PRIVATE 关键字;
  • main.c 使用 libhello.so 的功能,但是libhello-world.so 不使用,hello-world/CMakeLists.txt 中使用 INTERFACE 关键字;
  • main.c 和 libhello-world.so 都使用 libhello.so 的功能,hello-world/CMakeLists.txt 中使用 PUBLIC 关键字;

3. include_directories(dir)

target_include_directories() 的功能完全可以使用 include_directories() 实现。但是我还是建议使用 target_include_directories()。为什么?保持清晰!

include_directories(header-dir) 是一个全局包含,向下传递。什么意思呢?就是说如果某个目录的 CMakeLists.txt 中使用了该指令,其下所有的子目录默认也包含了header-dir 目录。

上述例子中,如果在顶层的 cmake-test/CMakeLists.txt 中加入:

include_directories(hello-world)
include_directories(hello-world/hello)
include_directories(hello-world/world)

那么整个工程的源文件在编译时都会增加:

-I hello-world -I hello-world/hello -I hello-world/world ...

这样各级子目录中无需使用 target_include_directories() 或者 include_directories()了。如果此时查看详细的编译过程(make VERBOSE=1)就会发现编译过程是一大坨,很不舒服。

当然了,在最终子目录的 CMakeLists.txt 文件中,使用 include_directories() 和 target_include_directories() 的效果是相同的。

4. 目录划分

每一个目录都是一个模块,目录内部应将对外和对内的头文件进行区分,由模块的调用者决定模块是否被传递(PRIVATE,INTERFACE,PUBLIC)。

调试选项

当然可以加入调试信息的,方法如下:

修改CMakeLists.txt文件,如我修改如下:

 # Version 2.4 might work, but it won't find static libraries with odd names
 cmake_minimum_required(VERSION 2.6)
  
 # CMake used to complain without this
 #if(COMMAND cmake_policy)
 #  cmake_policy(SET CMP0003 NEW)
 #endif(COMMAND cmake_policy)
  
 SET(CMAKE_BUILD_TYPE "Debug")
 SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
 SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

在CMakeLists.txt文件的开头部分增加上面的几个SET语法行,简单解释如下:

在cmake中有一个全局的环境变量,CMAKE_BUILD_TYPE,可以取Release或者Debug等值。然后可以通过设置CMAKE_CXX_FLAGS_DEBUG来设置在debug时的CXXFLAGS,这个值大家肯定都熟悉的哈。如果不需要添加调试信息,就直接修改CMAKE_BUILD_TYPE的值。

gcc和g++

我们知道编译和链接阶段是靠g++和gcc编辑器来完成,这两个编译阶段是相同的,但是链接阶段g++默认链接c++库,所以一般情况下用gcc编译c文件,而g++编译cpp文件。当然g++也可以编译c文件,而gcc编译cpp文件则需要在后面加上参数-lstdc++,作用就是链接c++库。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页