GNU C 和 C++ 编译器分别称为gcc和g++,GCC 是所谓的“ GNU 工具链”的关键组件,用于开发应用程序和编写操作系统。
- C++ 有多种标准:
- C++98
- C++11(又名 C++0x)
- C++14(又名 C++1y)
- C++17(又名 C++1z)
- C++2a(2020 年计划的下一个标准)
-
- ' 对于 6.1 之前的 GCC 版本,默认模式是 C++98,对于 GCC 6.1 及更高版本,默认模式是 C++14 '
-
- '您可以使用命令行标志-std显式指定 C++ 标准。例如:'
- -std=c++98, 或-std=gnu++98(带有 GNU 扩展的 C++98)
- -std=c++11, 或-std=gnu++11(带有 GNU 扩展的 C++11)
- -std=c++14,或-std=gnu++14(带有 GNU 扩展的 C++14),GCC 6.1 及更高版本的默认模式
- -std=c++17,或-std=gnu++17(带有 GNU 扩展的 C++17),实验性的
- -std=c++2a,或-std=gnu++2a(带有 GNU 扩展的 C++2a),实验性的
常用的 GCC 编译器选项:
- g++ -Wall -g -o Hello.exe Hello.cpp
- -o: 指定输出的可执行文件名
- -Wall: 打印“ all”警告信息
- -g: 生成附加的符号调试信息以供gdb调试器使用
1.预处理: 宏定义展开, 头文件展开, 条件编译
2.编译: 检查语法, 生成编译文件
3.汇编: 将汇编文件生成目标文件二进制文件
4.链接: 将目标文件链接成目标程序,此阶段由于动态链接的优势,GCC 默认链接到共享库


上面的编译步骤针对个别的几个文件还能应付,但如果源文件太多,一个一个编译就会特别麻烦,为什么不批处理编译源文件呢,于是就有了make工具,它是一个自动化编译工具,可以使用一条命令实现完全编译。还可以指定文件编译的顺序。但是使用make编译源码,需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile,所以编写makefile文件也是一个程序员所必备的技能。
对于一个大工程,编写makefile实在是件复杂的事,于是人们又想,为什么不设计一个工具,读入所有源文件之后,自动生成makefile呢,于是就出现了cmake工具,它能够输出各种各样的makefile或者project文件,从而帮助程序员减轻负担。但是随之而来也就是编写cmakelist文件,它是cmake所依据的规则。
总体的顺序就是:原文件--camkelist —cmake —makefile —make —生成可执行文件
所以CMake是一个高级编译配置工具,并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces):若没有CMake,就需要使用gcc或者g++对文件进行逐个编译,有了CMake,所有的编译操作都可以通过编译配置CMakeList.txt这个文件完成。
CMake 指令不分大小写,变量区分
变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名
有关双引号:
- " SET(SRC_LIST main.c)也可以写成 SET(SRC_LIST “main.c”) 是没有区别的,"
- 但是假设一个源文件的文件名是 fu nc.c(文件名中间包含了空格)。这时候就必须使用双引号,
- " 如果写成了SET(SRC_LIST fu nc.c),就会出现错误"提示你找不到 fu 文件和 nc.c 文件。
- 这种情况,就必须写成:SET(SRC_LIST “fu nc.c”)
内部构建:产生的各种中间文件都位于源文件的同级目录下,产生很多中间的垃圾文件,干扰源文件的工作目录。
而外部构建产生的中间文件对原有的工程结构没有任何影响,所有动作全部发生在编译目录。
CMakeLists.txt的文件配置顺序很重要,否则可能无法正确编译,示例请看:项目实战示例2或者CMake语法调用顺序。
- cmake_minimum_required() Required CMake Version
- project() Package Name
- find_package() Find other CMake/Catkin packages needed for build
- catkin_python_setup() Enable Python module support
- add_message_files(), add_service_files(), add_action_files() Message/Service/Action Generators
- generate_messages() message/service/action generation
- catkin_package() Specify package build info export
- add_library()/add_executable()/target_link_libraries() Libraries/Executables to build
- install()
- catkin_add_gtest(), catkin_add_nosetests() , add_rostest() , add_rostest_gtest() Tests to build
- PROJECT_NAME '工程名' -->当前'CMakeList.txt里'设置的project_name
- PROJECT_SOURCE_DIR 工程的根目录
- PROJECT_BINARY_DIR 运行cmake的目录,通常是${PROJECT_SOURCE_DIR}/build
-
- CMAKE_CURRENT_SOURCE_DIR: '当前正处理'的源码路径 --> 当前'CMakeLists.txt' 所在的路径
- CMAKE_CURRENT_BINARY_DIR: 当前正在处理的'构建目录'
- 备注: 每个由'add_subdirectory添加'的目录将会在'构建树(Build Tree)'中创建一个'构建目录'
- 补充: 对于直接在'源码目录中编译'的情况,当前正在处理的构建目录就是'当前源码'所在的目录-->'基本不会'
- CMAKE_CURRENT_LIST_DIR: 当前处理的'cmake文件'所在的目录
- 备注:这里的cmake文件可能是'.camke'文件或'CMakeListst.txt'文
- CMAKE_CURRENT_LIST_FILE: 输出调用这个变量的 CMakeLists.txt 的完整路径
- CMAKE_CURRENT_LIST_LINE: 输出这个变量所在的行-->'定位报错'
- CMAKE_PROJECT_NAME: '整个项目'配置的project_name
-
- 'LIBRARY'_OUTPUT_DIR、'BINARY'_OUTPUT_DIR: '库'和'可执行'的最终存放目录
- CMAKE_MODULE_PATH 用来定义自己的 cmake 模块所在的路径




- PROJECT(HELLO CXX) 指定了工程的名字,并且支持语是C++,同时将项目名称存储在变量 PROJECT_NAME中
- PROJECT(HELLO C CXX) 指定了工程的名字,并且支持语是C和C++ 想要支持Java就再往后追加
该指令隐式定义了两个CMAKE的变量:
又定义两个预定义变量:PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,这两个变量和HELLO_BINARY_DIR, HELLO_SOURCE_DIR是一致的,所以改了工程名也没有关系。
- SET(SRC_LIST main.cpp t1.cpp t2.cpp) SRC_LIST变量就包含了main.cpp t1.cpp t2.cpp
- SET(EXECUTABLE_OUTPUT_PATH${PROJECT_BINARY_DIR}/bin) 更改生成的可执行文件路径
- SET(LIBRARY_OUTPUT_PATH${PROJECT_SOURCE_DIR}/lib) 设置库文件输出到代码源路径下的lib文件夹中
- SET(CMAKE_CXX_STANDARD 11) 设置C++标准为C++11
-
- set(ENV{} []) 设置环境变量
- $ENV{HOME}:获取当前系统的home目录,并在编译时打印出这个路径
- MESSAGE(STATUS "HOME Directory: $ENV{HOME}") 使用$ENV{}就可以调用系统的环境变量了
- 主要包含三种信息:
-
- SEND_ERROR 产生错误,成功过程被跳过
- SATUS "输出前缀为--的信息,常用"
- FATAL_ERROR 立即终止所有 cmake 过程
-
- 例子:MESSAGE(SATUS "\n #### I love China! #### \n")
version: 版本合适
EXACT: 版本必须一致
两者的区别:version和EXACT: 都是可选的,version指定的是版本,如果指定就必须检查找到的包的版本是否和version兼容。如果指定EXACT则表示必须完全匹配的版本而不是兼容版本就可以。
QUIET: 没找到包也不会报错,既如果查找失败,不会在屏幕进行输出(但是如果指定了REQUIRED字段,则QUIET无效,仍然会输出查找失败提示语)
REQUIRED: 必须找到该包,否则立即停掉整个cmake。而如果不指定REQUIRED则cmake会继续执行。
COMPONENTS:可选字段,表示查找的包中必须要找到的组件(components),如果有任何一个找不到就算失败,类似于REQUIRED,导致cmake停止执行。
OPTIONAL_COMPONENTS和components:可选的模块,找不到也不会让cmake停止执行。[引用]
- 例子:
-
- find_package(OpenCV 4 REQUIRED)
- include_directories(${OpenCV_INCLUDE_DIRS})
- add_executable(main src/main.cpp)
- target_link_libraries(main ${OpenCV_LIBRARIES})
-
- find_package从目录中寻找OpenCV,找到后将头文件目录设置为${OpenCV_INCLUDE_DIRS},
- 库文件设为${OpenCV_LIBRARIES},然后在工程中包含OpenCV头文件目录,生成可执行文件,
- 最后链接OpenCV库。
向工程添加多个指定头文件的搜索路径,路径之间用空格分隔,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面,可以通过两种方式来进行控制搜索路径添加的方式:
1. CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过 SET 这个 cmake 变量为 on,可以将添加的头文件搜索路径放在已有路径的前面。
2. 通过 AFTER 或者 BEFORE 参数,也可以控制是追加还是置前。
如果使用了find_package(x)去查找到了某一个库,那么就可以使用这个命令去包含这个库的头文件
- 用法:
- include_directories(“/opt/MATLAB/R2012a/extern/include”)
- include_directories(include ${catkin_INCLUDE_DIRS})
- include_directories(${Boost_INCLUDE_DIRS})
- catkin_package(
- INCLUDE_DIRS include 导出包的include路径'(声明给其它包的include路径)'
- LIBRARIES my_dep_app 导出项目中的库'(声明给其它包的库)'
- CATKIN_DEPENDS my_dep '该项目'依赖的其他catkin项目
- DEPENDS system_lib '该项目'所依赖的非catkin CMake项目,如普通的opencv库
- CFG_EXTRAS 其他配置参数
- )
对于DEPENDS依赖项,会将DEPENDS的头文件路径和库添加到本功能包下的include路径和库。假设当前我的DEPENS包含了Boost库,当其他功能包调用这个功能包也需要Boost库的时候,就可以不需要再find_package(Boost),但是如果功能包明确需要使用库的话,仍然建议显式寻找库find_package(Boost),而不是通过包含其他功能包隐式寻找,避免其他功能包修改依赖项导致功能包编译失败。
- find_library(TESTFUNC_LIB testFunc HINTS ${PROJECT_SOURCE_DIR}/testFunc/lib)
- 第一个参数是变量名称,第二个参数是库名称,第三个参数是HINTS,第4个参数是路径
-
- 使用find_library的好处是在执行cmake ..时就会去查找库是否存在,这样可以提前发现错误,不用等到链接时
注意:find_library(TESTFUNC_LIB testFunc ...默认是查找动态库,如果想直接指定使用动态库还是静态库,可以写成find_library(TESTFUNC_LIB libtestFunc.so ...或者find_library(TESTFUNC_LIB libtestFunc.a ...
hello:就是正常的库名,生成的名字前面会加上lib, 最终产生的文件是libhello.so
SHARED:生成动态库,STATIC:生成静态库 如果不写,默认是静态库
${LIBHELLO_SRC}:源文件
- 例子:
-
- ADD_LIBRARY (hello SHARED ${LIBHELLO_SRC}) # 添加动态库
- ADD_LIBRARY (hello STATIC ${LIBHELLO_SRC}) # 添加静态库
-
- '仍然用hello作为target名时,是不能成功创建所需的静态库的,因为hello作为一个target是不能重名的,
- 故把上面的hello修改为hello_static,同理不需要写全libhello_static.a 只需要填写hello_static即可,
- cmake系统会自动为你生成 libhello_static.a '
-
- ADD_LIBRARY (hello_static STATIC ${LIBHELLO_SRC})
-
- '按照一般的习惯,静态库名字跟动态库名字应该是一致的,只是扩展名不同;即:静态库名为 libhello.a;
- 动态库名为libhello.so;所以,希望 "hello_static" 在输出时,不是"hello_static",而是以"hello"
- 的名字显示,故设置如下:'
-
- SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")
- GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME)
-
- ${
- 生成默认库的开关选项:BUILD_SHARED_LIBS
- 这个开关用来控制默认的库编译方式,如果不进行设置,使用 ADD_LIBRARY 并没有指定库类型的情况下,
- 默认编译生成的库都是静态库,如果 SET BUILD_SHARED_LIBS ON后,默认生成的为动态
- }
在使用ROS的message、service、action时注意添加,如下:
添加对其它package的依赖,前提是已经通过find_package()引入了这个package
添加对本package的依赖 add_dependencies(my_target ${${PROJECT_NAME}_EXPORTED_TARGETS})
${catkin_EXPORTED_TARGETS} 和 ${${PROJECT_NAME}_EXPORTED_TARGETS}的使用时机:
catkin_EXPORTED_TARGETS:如果有一个目标(甚至是过渡性的)依赖于需要建立消息/服务/动作的其他目标,需要在目标catkin_EXPORTED_TARGETS上添加显式依赖项,以使它们按照正确的顺序编译。这种情况几乎总是适用,除非你的软件包真的不使用ROS的任何部分。不幸的是,这种依赖不能自动传播。
${PROJECT_NAME}_EXPORTED_TARGETS:如果有编译消息和/或服务的软件包以及使用这些软件的可执行文件,则需要在自动生成的消息目标上创建明确的依赖关系,以便它们按正确的顺序编译。
- 理解:
-
- 假设我们需要生成一个可执行文件,该文件生成需要链接a.so b.so c.so d.so四个动态库,
- 正常来讲我们只需要以下两条指令即可:
-
- ADD_EXECUTABLE(main main.cpp)
- TARGET_LINK_LIBRARIES(main a.so b.so c.so d.so)
-
- '但是编译的时候报错,一些符号的定义找不到,而这些符号恰恰就在这几个库中,假设在a.so 和
- b.so中,在上述两条指令之间加上一条指令即可编译通过:'
-
- ADD_DEPENDENCIES(main a.so b.so)
-
- '原因很简单,生成main需要依赖a.so和b.so中的符号定义,然而a.so和b.so库的生成是在main
- 编译生成之后的,添加这条语句就是提醒编译器需要先生成main的依赖(a.so,b.so),然后再去生成main。'
link_directories(directory1 directory2 …)
- TARGET_LINK_LIBRARIES(targetlibrary1
library2 …) -
- 用法:用于链接库文件。根据调库原则,一是要有库的头文件,二是要有库生成的共享或者静态链接库,
- include_directories命令已经包含了头文件,所以需要用target_link_libraries包含库文件,
- '第一个参数表示的是可执行文件的名称,这个文件执行需要链接那些库,就全写在后面。'
- ADD_EXECUTABLE(hello ${SRC_LIST})
- 生成可执行文件名是hello,源文件读取变量SRC_LIST中的内容,同样可以传入多个源文件
作用:将指定的文件夹加到build任务列表中,这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置,
source_dir:必选参数,该参数指定一个子目录,子目录下应该包含CMakeLists.txt文件和代码文件。子目录可以是相对路径也可以是绝对路径,如果是相对路径,则是相对当前目录的一个相对路径。
binary_dir:可选参数。该参数指定一个目录,用于存放输出文件。可以是相对路径也可以是绝对路径,如果是相对路径,则是相对当前输出目录的一个相对路径。如果该参数没有指定,则默认的输出目录使用source_dir,例子:add_subdirectory(src)
EXCLUDE_FROM_ALL:是将写的目录从编译中排除,如程序中的example
- add_subdirectory(src)
- ' 这里指定src目录下存放了源文件,当执行cmake时,就会进入src目录去找CMakeLists.txt,
- 所以在src目录下也建立一个CMakeLists.txt '
-
- ADD_SUBDIRECTORY(src bin):
- ' 将src子目录加入工程并指定编译输出(包含编译中间结果)路径为bin 目录(bin目录自动创建)
- 如果不进行bin目录的指定,那么编译结果(包括中间结果)都将存放在build/src目录 '
用于比较字符串,相同返回 true
SUBDIRS(dir1 dir2 ...[EXCLUDE_FROM_ALL exclude_dir1 exclude_dir2 ...] [PREORDER] )
SUBDIRS()和ADD_SUBDIRECTORY()的区别:ADD_SUBDIRECTORY()在调用子目录时处理子目录,而SUBDIRS()将目录推送到在当前CMakeLists文件末尾处理的列表上,这是旧行为,已被弃用。
用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本
- SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
- ' VERSION 指代动态库版本,SOVERSION 指代 API 版本 '
-
- 在 build/lib 目录会生成:
- libhello.so.1.2
- libhello.so.1->libhello.so.1.2
- libhello.so -> libhello.so.1
在cmake脚本中,设置编译选项可以通过add_compile_options命令,也可以通过set命令修改CMAKE_CXX_FLAGS或CMAKE_C_FLAGS。使用这两种方式在有的情况下效果是一样的,但请注意它们还是有区别的:add_compile_options命令添加的编译选项是针对所有编译器的(包括c和c++编译器),而set命令设置CMAKE_C_FLAGS或CMAKE_CXX_FLAGS变量则是分别只针对c和c++编译器的。
- if(CMAKE_COMPILER_IS_GNUCXX) #判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
- add_compile_options(-std=c++11)
- message(STATUS "optional:-std=c++11")
- endif(CMAKE_COMPILER_IS_GNUCXX)
使用add_compile_options添加-std=c++11选项,是想在编译c++代码时加上c++11支持选项。但是因为add_compile_options是针对所有类型编译器的,所以在编译c代码时,就会产生如下warning:
- [ 50%] Building C object libb64/CMakeFiles/b64.dir/libb64-1.2.1/src/cdecode.c.obj
- cc1.exe: warning: command line option ‘-std=c++11’ is valid for C++/ObjC++ but not for C
- [100%] Building C object libb64/CMakeFiles/b64.dir/libb64-1.2.1/src/cencode.c.obj
- cc1.exe: warning: command line option ‘-std=c++11’ is valid for C++/ObjC++ but not for C
- Linking C static library libb64.a
- [100%] Built target b64
要消除这个warning,就不能使用add_compile_options,而是只针对c++编译器添加这个option。所以如下修改代码,则警告消除。
- if(CMAKE_COMPILER_IS_GNUCXX)
- set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
- message(STATUS "optional:-std=c++11")
- endif(CMAKE_COMPILER_IS_GNUCXX)
-
- 同样:add_definitions这个命令也是针对所有编译器
第一个参数是这个option的名字,第二个参数是字符串,用来描述这个option是来干嘛的,第三个是option的值,ON或OFF,也可以不写,不写就是默认OFF。
- if(MYDEBUG) add_executable(main2 main2.c)
- else() message(STATUS "Currently is not in debug mode")
- endif()
-
- '使用了if-else来根据option来决定是否编译main2.c'
get_filename_component()
- get_filename_component(ANDROID_NDK_EXPECTED_PATH "d:/ahello.c" ABSOLUTE)
- '原封不动取目录或包含路径的文件 如果不填写路径会自动 添加上自己项目路径,也就是CMAKE_CURRENT_LIST_DIR和它拼接'
-
- get_filename_component(ANDROID_NDK_EXPECTED_PATH "d:/ahello.c" NAME)
- '取文件名,不包含路径'
-
- get_filename_component(ANDROID_NDK_EXPECTED_PATH "d:/ahello.c" EXT)
- '取扩展名'
-
- get_filename_component(ANDROID_NDK_EXPECTED_PATH "d:/ahello.c" REALPATH)
- '和ABSOLUTE 一样'
-
- get_filename_component(ANDROID_NDK_EXPECTED_PATH "d:/ahello.c" PATH)
- '只保留路径(如果第二个参数传入的是路径,那么取该路径的上一层路径)'
收集指定目录中所有源文件的名称,并将列表存储在提供的变量中
- file(WRITE filename "message to write"... )
- 'WRITE :向文件 filename 中写入一个信息. 存在则覆盖,不存在则创建'
-
- file(APPEND filename "message to write"... )
- 'APPEND :同 WRITE, 但会加到文件末尾'
-
- file(REMOVE [file1 ...])
- 'REMOVE :删除给定文件,包括子路径'
-
- file(REMOVE_RECURSE [file1 ...])
- 'REMOVE_RECURSE : 删除给定文件和路径,包括非空路径'
-
- file(RELATIVE_PATH variable directory file)
- 'RELATIVE_PATH : 确定路径到确定文件的相对路径'
- install(TARGETS MyLib
- EXPORT MyLibTargets
- LIBRARY DESTINATION lib '动态库安装路径'
- ARCHIVE DESTINATION lib '静态库安装路径'
- RUNTIME DESTINATION bin '可执行文件安装路径'
- PUBLIC_HEADER DESTINATION include '头文件安装路径'
- )
-
- '执行完cmake后,执行命令make install来执行cmake里install的动作'
- #STEP:安装cmake模块文件:这行代码会将cmake_module/目录下的所有内容(假设这是一个包含自定义CMake模块的目录)安装到工作空间编译后的共享位置。
- # ${CATKIN_PACKAGE_SHARE_DESTINATION}通常指向一个类似于devel/share/
/cmake的路径,在那里其他项目可以通过find_package命令找到并使用你的CMake模块。 - #NOTE:DIRECTORY: 指定要安装的目录及其内容。例如,INSTALL(DIRECTORY src/ DESTINATION include) 将指定的“src”目录下的所有文件复制到安装路径下的“include”目录。
- #NOTE:DESTINATION: 定义了安装的目标位置。它是相对于系统安装根目录(通常是 /usr/local 或者通过 -DCMAKE_INSTALL_PREFIX 设置的位置)的一个路径。例如,DESTINATION bin 表示将文件安装到系统的 bin 目录下。
- install(DIRECTORY cmake_module/
- DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
- )
- #STEP:安装头文件:这个指令会将include/目录下所有的头文件安装到目标系统的相应头文件搜索路径。
- # ${CATKIN_PACKAGE_INCLUDE_DESTINATION}通常指向类似include/
的位置,这样其他项目在编译时能够通过#include 的方式引用你的头文件。 - install(DIRECTORY include/
- DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
- )
- #STEP:安装库和可执行文件:这段代码指定了项目的主目标(通常是库或可执行文件),将其分别按静态库、动态库和可执行文件类型安装到相应的目录。
- #静态库和动态库会被安装到${CATKIN_PACKAGE_LIB_DESTINATION},这通常是一个像lib/
/这样的路径; - #而可执行文件会被安装到${CATKIN_PACKAGE_BIN_DESTINATION},如bin目录下。
- #NOTE:RUNTIME: 在install(TARGETS ...)语境下,RUNTIME指的是可执行文件或者动态链接库(.dll, .dylib, .so等)
- #NOTE:LIBRARY: 在install(TARGETS ...)语境下,LIBRARY指向的是需要安装的库文件,通常指动态库
- #NOTE:ARCHIVE: 在install(TARGETS ...)语境下,ARCHIVE特指静态库文件(.a或.lib)
- install(TARGETS ${PROJECT_NAME}
- ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} #static lib
- LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} #dynamic lib
- RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} #executable file
- )
- #STEP:单独安装特定的可执行文件:这行代码专门针对名为charge_node的目标进行安装,并且仅安装其运行时组件(即可执行文件),同样安装到${CATKIN_PACKAGE_BIN_DESTINATION}目录下。
- install(TARGETS charge_node
- RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
- )
- #STEP:安装其他资源文件:这里指定将launch、cfg和scripts三个目录中的所有文件和子目录安装到${CATKIN_PACKAGE_SHARE_DESTINATION},
- #这些通常包括启动文件、配置文件以及脚本文件等,便于其他节点在运行时访问和使用。这个路径通常为share/
。 - install (DIRECTORY launch cfg scripts
- DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
- )
使用方式一:单独使用,在生成目标文件(使用 add_executable() 或 add_library() 命令生成的文件)时自动执行 add_custom_command 指定的命令。
- add_custom_command(TARGET
- PRE_BUILD | PRE_LINK | POST_BUILD
- COMMAND command1 [ARGS] [args1...]
- [COMMAND command2 [ARGS] [args2...] ...]
- [BYPRODUCTS [files...]]
- [WORKING_DIRECTORY dir]
- [COMMENT comment]
- [VERBATIM] [USES_TERMINAL]
- [COMMAND_EXPAND_LISTS])
-
- 'TARGET:由 add_executable 或 add_library 生成的目标文件名称'
- 'PRE_BUILD | PRE_LINK | POST_BUILD:分别表示编译之前执行命令,链接之前执行命令,生成目标文件后执行命令;
- 具体的pre_build就是在cmake 命令之后, make命令之前。 post_build是在make 出可执行文件后执行'
- 'COMMAND:需要执行的命令'
- 例子:
-
- cmake_minimum_required(VERSION 3.5)
-
- project(test)
-
- add_executable(${PROJECT_NAME} main.c)
-
- add_custom_command(TARGET ${PROJECT_NAME}
- POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E echo compile finish
- VERBATIM )
-
- ${使用-E标志可以以与操作系统无关的方式,运行许多公共操作}
-
- '该工程会生成可执行程序:test,生成 test 后,会在终端输出 compile finish。如下图:'

- CMAKE_MAJOR_VERSION,CMAKE 主版本号,比如 2.4.6 中的 2
- CMAKE_MINOR_VERSION,CMAKE 次版本号,比如 2.4.6 中的 4
- CMAKE_PATCH_VERSION,CMAKE 补丁等级,比如 2.4.6 中的 6
- CMAKE_SYSTEM 系统名称,比如 Linux-2.6.22
- CMAKE_SYSTEM_NAME 不包含版本的系统名,比如 Linux
- CMAKE_SYSTEM_VERSION 系统版本,比如 2.6.22
- CMAKE_SYSTEM_PROCESSOR 处理器名称,比如 i686.
- UNIX 在所有的类 UNIX 平台为 TRUE,包括 OS X 和 cygwin
- WIN32 在所有的 win32 平台为 TRUE,包括 cygwin


补充:gcc编译链中i686和x86-64有什么区别:

图中有很多种交叉编译器,只需关注红色方框中的两个,这个编译链带的i686或者x86_64和Linux开发板没关系,和宿主的Ubuntu是64还是32有关系。
向 C/C++编译器添加-D 定义,比如: ADD_DEFINITIONS(-DENABLE_DEBUG),多个参数时,用空格分割。 如果你的代码中定义了#ifdef ENABLE_DEBUG #endif,这个代码块就会生效。如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量设置。
用来载入 CMakeLists.txt 文件,也用于载入预定义的 cmake 模块. INCLUDE(file1 [OPTIONAL]) INCLUDE(module [OPTIONAL]) OPTIONAL 参数的作用是文件不存在也不会产生错误。 你可以指定载入一个文件,如果定义的是一个模块,那么将在 CMAKE_MODULE_PATH 中搜索这个模块并载入。 载入的内容将在处理到 INCLUDE 语句是直接执行。
- CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS '用来控制 IF ELSE 语句的书写方式'
-
- BUILD_SHARED_LIBS
- '这个开关用来控制默认的库编译方式,如果不进行设置,使用 ADD_LIBRARY 并没有指定库类型的情况下,
- 默认编译生成的库都是静态库,如果 SET BUILD_SHARED_LIBS ON后,默认生成的为动态'
-
- CMAKE_C_FLAGS '设置 C 编译选项,也可以通过指令 ADD_DEFINITIONS添加'
-
- CMAKE_CXX_FLAGS '设置 C++编译选项,也可以通过指令 ADD_DEFINITIONS添加'
- IF(expression)
- COMMAND1(ARGS ...)
- COMMAND2(ARGS ...)
- ...
- ELSE(expression)
- COMMAND1(ARGS ...)
- COMMAND2(ARGS ...)
- ...
- ENDIF(expression)
另外一个指令是 ELSEIF,总体把握一个原则,凡是出现 IF 的地方一定要有对应的ENDIF,出现 ELSEIF 的地方,ENDIF 是可选的。
- #一个小例子,用来判断平台差异:
-
- IF (WIN32)
- MESSAGE(STATUS “This is windows.”)
- ELSE (WIN32)
- MESSAGE(STATUS “This is not windows”)
- ENDIF (WIN32)
上述代码用来控制在不同的平台进行不同的控制,但是,阅读起来却并不是那么舒服,ELSE(WIN32)之类的语句很容易引起歧义,可以SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON),这时候就可以写成如下代码形式:
- IF (WIN32)
- ELSE ()
- ENDIF ()
-
- "配合ELSEIF使用,可能的写法是这样:"
-
- IF (WIN32)
- #do something related to WIN32
- ELSEIF (UNIX)
- #do something related to UNIX
- ELSEIF(APPLE)
- #do something related to APPLE
- ENDIF (WIN32)
- WHILE(condition) "判断逻辑同if"
- COMMAND1(ARGS ...)
- COMMAND2(ARGS ...)
- ...
- ENDWHILE(condition)
总参考:
暂时就先这样,以后还有什么命令再来补充。