简单的说:
CMake是一个跨平台的构建系统,用于管理软件项目的构建过程。能够自动生成适用于不同平台和编译器的构建文件。
详细的说:
CMake是一个跨平台的开源构建系统,用于管理软件项目中的构建过程。它使用一种名为“CMakeLists.txt”的特殊脚本语言来描述构建过程,并能够自动生成各种不同平台和编译器所需的构建文件。CMake支持多种编程语言,包括C、C++、Java、Python等。
使用CMake可以将构建过程从特定的编译器和操作系统中解耦出来,使得同一个项目可以在多个平台上进行构建。与传统的构建系统相比,CMake具有更为灵活的配置选项和更强大的模块化能力,方便用户自定义和扩展构建规则。
在ROS中,CMake是ROS的默认构建系统。每个ROS包都包含一个名为CMakeLists.txt的文件,其中包含了编译该包所需的构建规则和依赖关系。通过使用CMake,用户可以轻松创建、编译和运行ROS软件包,实现机器人应用程序的开发和部署。
我们需要在一个空的文件夹中写两个文件hello.cpp和CMakeLists.txt
注意:在CMake中,默认情况下,CMakeLists.txt文件的名字是固定的,必须以这个名称命名。CMake会自动查找当前目录下的CMakeLists.txt文件并加载它。
hello.cpp代码
#include <iostream>
using namespace std;
int main()
{
cout << "hello world" << endl;
return 0;
}
CMakeLists.txt代码
project(HELLO)
set(SRC_LIST hello.cpp)
message(STATUS "This is a BINARY dir" ${HELLO_BINARY_DIR})
message(STATUS "This is a SOURCE dir" ${HELLO_SOURCE_DIR})
add_executable(hello ${SRC_LIST})
代码分析
project(HELLO)
set(SRC_LIST hello.cpp)
set(SRC_LIST hello.cpp)
命令用于将hello.cpp
文件的名称存储在变量中,以便在后续的命令中引用它。
message(STATUS "This is a BINARY dir" ${HELLO_BINARY_DIR})
message(STATUS "This is a SOURCE dir" ${HELLO_SOURCE_DIR})
message
命令用于输出消息到CMake的输出窗口,这里将输出当前二进制目录和源码目录的路径。
add_executable(hello ${SRC_LIST})
add_executable
命令将源代码文件${SRC_LIST}
编译为一个可执行文件hello
${SRC_LIST}是取值的意思
这里的${SRC_LIST}就是与上面的代码set(SRC_LIST hello.cpp)有关系
---------------------------------------------------代码分析完毕-----------------------------------------------------------
使用cmake .(注意:cmake .中的.不要忘记了)
问题:为什么要使用cmake
解答:在终端输入命令cmake
的作用是运行CMake,使用CMakeLists.txt文件中的指令来生成对应的构建系统文件。
具体地说,当你在终端中运行cmake
命令时,CMake将读取当前目录中的CMakeLists.txt文件,并根据其中的指令生成对应的构建系统脚本(如Makefile、Visual Studio项目文件等),以便编译和构建你的项目。
图1 hello文件的初始状态?
图2?使用cmake .?
cmake . 后生成的大多数文件可以不予理会
需要注意的是Makefile文件
生成的Makefile文件是一种构建系统文件,用于指导make工具进行项目的编译和构建。
Makefile中包含了一系列规则和命令,用于描述项目中源文件的依赖关系以及如何生成最终的可执行文件或库文件。通过读取Makefile文件,make工具可以自动地根据这些规则和命令来确定需要重新编译的文件,并执行相应的编译和链接操作。
make命令编译
在终端输入make
命令时,它会根据当前目录中的Makefile文件中所定义的规则和依赖关系,自动编译生成指定的目标文件。
通常情况下,我们在Makefile文件中会定义多个目标,比如可执行文件、静态库或动态库等。在执行make
命令时,如果没有指定目标,则默认会使用Makefile文件中第一个定义的目标作为默认目标进行编译。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图3?使用make编译?
可见到生成了可以运行的hello编译文件
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图4?运行hello结果?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图5?创建文件夹build
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图6?cmake ..
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图7 项目工程初始形态
build?录:把?成的临时?件放在build?录下
src目录:用来放置工程的源代码
问题:为什么src和工程目录下都要有CMakeList.txt
解答:
工程目录下的CMakeLists.txt: 工程目录下的CMakeLists.txt文件主要用于设置整个项目的全局配置和管理。它可以包含多个子目录,并指导CMake如何构建和组织整个项目。这个CMakeLists.txt文件通常用于包含整个项目的编译选项、链接选项、依赖库的查找和添加、生成的可执行文件或库文件的输出位置等。它是整个项目的入口点,负责协调各个子目录的构建过程。
源代码src目录下的CMakeLists.txt: 源代码目录下的CMakeLists.txt文件主要用于描述该目录下的源代码文件,指导CMake如何编译和构建当前目录下的源文件。这个CMakeLists.txt文件通常用于定义当前目录下的编译目标、源文件的编译选项、依赖库的链接等。它是每个源代码目录的入口点,负责管理当前目录下的源代码的构建过程。
工程目录下的CMakeLists.txt文件提供了整个项目的全局配置和管理,而源代码src目录下的CMakeLists.txt文件则主要用于描述该目录下的源代码文件,指导CMake如何编译和构建当前目录下的源文件。
工程目录下的CMakeLists.txt的代码
project(HELLO)
add_subdirectory(src bin)
代码分析
add_subdirectory(src bin)
add_executable(hello hello.cpp)
add_executable
命令将源代码文件${SRC_LIST}
编译为一个可执行文件hello
编译
buid目录中生成了bin目录,可执行文件hello就在bin目录中
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图9?项目工程初始形态
hello.cpp的代码
#include "hello.h"
#include <iostream>
using namespace std;
void HelloFunc()
{
cout << "Hello World" << endl;
}
hello.h的代码
#ifndef HELLO_H
#define HELLO_H
void HelloFunc();
#endif
工程目录下的CMakeLists.txt的代码
project(HELLO)
add_subdirectory(lib bin)
源代码lib目录下的CMakeLists.txt的代码
set(LIBHELLO_SRC hello.cpp)
add_library(hello_static STATIC ${LIBHELLO_SRC})
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
add_library(hello SHARED ${LIBHELLO_SRC})
set_target_properties(hello PROPERTIES OUTPUT_NAME "hello")
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
INSTALL(FILES hello.h DESTINATION include/hello)
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
代码分析
add_library(hello_static STATIC ${LIBHELLO_SRC})
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
add_library(hello SHARED ${LIBHELLO_SRC})
set_target_properties(hello PROPERTIES OUTPUT_NAME "hello")
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
这个代码是为了同时构建名为hello的静态库和动态库。
为什么不能直接用
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
add_library(hello_static STATIC ${LIBHELLO_SRC})
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
设置hello_static库文件只有在执行make clean命令时才会被删除。
默认情况下,CMake会自动将所有生成的库文件和可执行文件都放在构建目录下的一个特定位置。如果你不设置CLEAN_DIRECT_OUTPUT
属性或者将其设置为0,那么在执行make clean命令时,CMake会删除这个目录中的所有内容,包括生成的库文件和可执行文件。
但是,如果你将CLEAN_DIRECT_OUTPUT
属性设置为1,那么在执行make clean命令时,CMake只会删除由该目标直接生成的文件,而不会删除任何其他文件。在这个例子中,它只会删除hello_static库文件,而不会删除hello动态库文件和其他文件。
这个属性通常用于帮助节省时间和资源。因为静态库文件只需要在第一次构建时生成一次,因此在后续构建中,如果不需要重新生成该文件,则可以通过设置CLEAN_DIRECT_OUTPUT
属性来避免不必要的文件操作。
INSTALL(FILES hello.h DESTINATION include/hello)
安装包含在hello.h文件中的头文件到系统路径usr/include/hello下。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
安装生成的动态库和静态库到系统路径lib下。动态库被安装在lib目录下,静态库被安装在usr/lib/static目录下(因为静态库的输出名称已经被修改为hello)。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图10 项目工程初始形态
hello.cpp的代码
#include "hello.h"
void HelloFunc()
{
HelloFunc();
}
工程目录下的CMakeLists.txt的代码
project(HELLO)
add_subdirectory(src bin)
源代码src目录下的CMakeLists.txt的代码
include_directories(/usr/include/hello)
add_executable(hello hello.cpp)
target_link_libraries(hello libhello.so)
分析
include_directories(/usr/include/hello)
将/usr/include/hello
目录添加到包含文件的搜索路径中。这样,在编译时,编译器将在该目录中查找头文件.
target_link_libraries(hello libhello.so)
将 libhello.so
动态库链接到 hello
可执行文件中。这样,在运行 hello
可执行文件时,系统将自动加载并使用 libhello.so
动态库中的函数和符号。
问题:代码中libhello.so是哪里来的为什么不用指明路径
答:TARGET_LINK_LIBRARIES 添加需要链接的共享库。当使用target_link_libraries
函数时,如果要链接的库文件是系统默认的库文件或已在默认的库搜索路径中(例如 /usr/lib
),则可以直接使用库文件名而无需指定完整的路径。
补充:LINK_DIRECTORIES 添加?标准的共享库搜索路径。当使用 LINK_DIRECTORIES
指令时,你需要提供库文件的完整路径。
最后,在build目录中cmake ..和make就行了,就不细讲了
shell脚本的代码
gcc -c linuxrec.c
g++ -g -std=c++11 -o ivw_demo ivw_demo.cpp linuxrec.o -L./libs/ -laikit -lpthread -ldl -Wl,-rpath=lib -lasound
转换成CMakeList.txt中的代码
在 CMakeLists.txt 中添加一个可执行文件目标
add_executable(ivw_demo
src/ivw_demo.cpp
src/linuxrec.o
)
然后,指定该可执行文件需要链接的库文件和编译选项
target_link_libraries(ivw_demo
libaikit
pthread
dl
asound
)
set_target_properties(ivw_demo PROPERTIES
LINK_FLAGS "-Wl,-rpath=lib"
)
分析
set_target_properties(ivw_demo PROPERTIES
LINK_FLAGS "-Wl,-rpath=lib"
)
ivw_demo
的链接属性。具体来说,它使用了 set_target_properties
函数来为目标设置属性,并且属性名称为 LINK_FLAGS
。PROPERTIES
是一个用于设置目标属性的关键字。通过使用 set_target_properties
函数,可以为指定的目标(如可执行文件、库文件等)设置不同的属性。LINK_FLAGS
属性是一个连接器选项,它允许你在链接目标时传递额外的标志给连接器。在这个例子中,-Wl,-rpath=lib
是要传递给连接器的标志。-Wl
:这是 GCC/Clang 编译器的选项,用于将后面的参数传递给连接器。-rpath=lib
:这是连接器选项,用于指定运行时库搜索路径。在这里,它指定了名为?lib
?的目录作为运行时库的搜索路径。ivw_demo
目标时,将 lib
目录添加到运行时库搜索路径中。这样,当运行 ivw_demo
时,系统将在 lib
目录中查找并加载所需的运行时库。最后,设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_LIST_DIR}/../bin_${PLATFORM})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
分析
EXECUTABLE_OUTPUT_PATH
被设置为 ${CMAKE_CURRENT_LIST_DIR}/../bin_${PLATFORM}
。${CMAKE_CURRENT_LIST_DIR}
是 CMakeLists.txt 文件所在的当前目录,而 ${PLATFORM}
是一个变量,表示平台名称。CMAKE_RUNTIME_OUTPUT_DIRECTORY
被设置为 EXECUTABLE_OUTPUT_PATH
。这样就将可执行文件的输出路径设置为 EXECUTABLE_OUTPUT_PATH
。${CMAKE_CURRENT_LIST_DIR}
表示以 CMakeLists.txt 文件所在的目录为基准,../bin_${PLATFORM}
表示将可执行文件放置在 CMakeLists.txt 文件所在目录的上一级目录中的名为 bin_${PLATFORM}
的子目录中。推荐(若有侵权,告知必删):【从零开始详细介绍CMake】https://www.bilibili.com/video/BV1vR4y1u77h?p=10&vd_source=8a8939fa34f1535a58f4146e906da94a