CMake使用笔记

一、浅谈能自动生成makefile的几大工具:

  • Autotools
  • cmake

Autotools:

  • Autotools是一个工具集,具有灵活性较大,对用户角度使用较为友好(cmake生成用户权限较多)。
  • 开发步骤太多,配置繁琐 [ autoscan + autoconf + automake ];
  • 通常编译的./configure文件,大多通过由autotools构建的,最终生成Makefile和config.h文件

CMake:

  • CMake是一个跨平台的安装(编译)工具
  • CMake类似make工具功能,用来读取并执行CMakeLists.txt文件的语句,最终生成makefile
  • Cmake语言开发相对简单,易于理解
  • 目前很多项目正在抛弃Autotools、qmake等,转而采用cmake.

二、CMake 环境搭建

以下内容参考自B站:视频去哪了呢?_哔哩哔哩_bilibili

2.1 下载cmake

    下载后,解压即可,无需安装

2.2 配置环境变量

 在系统环境变量下面的Path下面点击编辑,然后新加 C:\D_disk\CMAKE\cmake-3.18.0-rc2-win64-x64\bin 的cmake路径

2.3 配置vscode的编译器和扩展插件

从vscode左下角的齿轮中选择commond palette(快捷键:Ctrl+shift+P), 然后输入框内输入“cmake”,选择下图所示的kit

扩展插件添加 CMake 和CMake Tools,如下图:

2.4 创建CMakeLists.txt

这一步,CMakeLists.txt不需要自己去创建,可以通过vscode自动创建,方法如下:

(1)Ctrl+shift+P打开命令窗,输入cmake,然后会弹出 cmake:quik start, 并选择它。  

                 

下面是我自动生成的一个CMakeLists.txt

cmake_minimum_required(VERSION 3.0.0)
project(cmake_test02 VERSION 0.1.0)

include(CTest)
enable_testing()

# 生成的可执行文件名为Hello, 被编译文件为main.cpp
add_executable(Hello main.cpp)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

2.5 生成

创建好了CMakeLists.txt之后,就可以直接生成了,点击最下端的“生成”按钮,如下图:

此时生成就成功了,然后在终端Terminal上cd build/, 之后ls 即可看到生成的Hello可执行文件。

最后敲入命令:./Hello 即可运行它!

三、 CMake 使用

  •  基本规则

3.1 链接2个以上文件

以如下目录结构为例:

main.cpp需要用到 instrument.h内的类来实例化对象,如下所示为 main.cpp

#include <iostream>
#include "instrument.h"

int main(int, char**) {
    std::cout << "Hello, world!\n";

      Instruments *p = new Instruments(1,2,3);
      int S = p->get_acreage();
      std::cout<<"S = "<<  S<<std::endl;
}

此时的 CMakeLists.txt文件写法非常简单,如下所示:

cmake_minimum_required(VERSION 3.0.0)
project(Hello VERSION 0.1.0)

include(CTest)
enable_testing()

# 设置头文件搜索路径 --- (1)
include_directories(./include)

# 重点是要在这里添加 ./src/instrument.cpp   --- (2)
add_executable(Hello main.cpp ./src/instrument.cpp)



set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

3.2 编译静态库

创建一个Lib的 CMakeLists.txt 的文件

cmake_minimum_required(VERSION 3.0.0)
project(sub_add VERSION 0.1.0)

include(CTest)
enable_testing()

add_library(sub_add sub_add.cpp)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

之后点击生成,在build目录下可以看到 libsub_add.a 文件,如下图:

3.3 编译动态库

生成动态库时,只需要在 CMakeLists.txt 的add_library 里面加上 "SHARED"

将 add_library(sub_add sub_add.cpp) 修改为:

add_library(sub_add SHARED sub_add.cpp)

add_library(sub_add sub_add.cpp)          //编译静态库时
add_library(sub_add SHARED sub_add.cpp)   //编译动态库时

点击生成按钮之后,生成成功后,在build可以看到这三个文件

说明:如果给别人使用的话,只需要将头文件动态库拷给别人用即可!

3.4 链接 “内部” 动/静态库

3.4.1 我先在在代码的根目录下创建了2个文件夹,分别为include 和 src ,如下图:

include 文件夹存放头文件
src 存放源文件

在src内再创建一个CMakeLists.txt文件,用来生成库文件,如下所示:

# 内部静态库
# 没有指定SHARED参数,默认静态库
# TEST 是指生成的目标文件,例如main.o
add_library(TEST SHARED test.cpp) 

3.4.2 在代码根目录下的 CMakeLists.txt 按如下步骤操作:

CMakeLists 代码如下:

cmake_minimum_required(VERSION 3.0.0)
project(new_hello VERSION 0.1.0)

include(CTest)
enable_testing()

# 设置头文件的搜索路径
include_directories(./include)

# 设置输出库文件路径,如果不加此句,生成文件会放在/build/src/目录下,导致找不到
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})

add_subdirectory(./src)

add_executable(new_hello main.cpp)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)


target_link_libraries(new_hello TEST)

3.4 链接 “外部” 动/静态库

4. CMake在 Linux 下的使用

参考CSDN资料:多头文件和源文件工程构建CMakeLists写法_正午之阳的博客-CSDN博客_cmake 头文件 源文件

1. 安装CMake工具

sudo apt-get install cmake

2. cmake 以 .o 方式连接多个文件

  • 创建新的目录结构如下:

其中,main.cpp 要调用到 src/a.cpp 的内容。 
main.cpp 以a.o 的方式调用,此时只需要1个CMakeLists.txt文件即可。

CMakeLists.txt 内容如下:

cmake_minimum_required(VERSION 2.8)
project(cmake test)

include_directories(include)

add_executable(LM_CMAKE main.cpp src/a.cpp)

3. cmake 以静态或动态库方式链接多个文件

mkdir lesson_01       //整个源代码的工程目录

touch main.cpp        //源文件
touch CMakeLists.txt  //创建CMake的链接文件

mkdir src             //用来存放.cpp和.h的子模块
mkdir include         //include 目录用来存放 .h 头文件
mkdir build           //build目录用来存放cmake 编译文件的
cd src/
touch modbus.cpp modbus.h
touch CMakeLists.txt   //有子模块时,src 目录下也需要 CMakeLists.txt

main.cpp

#include <iostream>
#include "./src/modbus.h"

using namespace std;

int main()
{
    Modbus myModbus;

    cout<<"Hello World"<<endl;
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)

project(CMake_test)

#include_directories(./include)
#include_directories("${PROJECT_SOURCE_DIR}modbuslib")
#include_directories(./src)
add_subdirectory(src)

# add compile option
if(CMAKE_COMPILER_IS_GNUCXX)
    add_compile_options(-std=c++11)
    message(STATUS "optional:-std=c++11")   
endif(CMAKE_COMPILER_IS_GNUCXX)

aux_source_directory(. DIR_SRCS)  # 用 DIR_SRCS 指代当前源文件所在的目录
add_executable(lm_test ${DIR_SRCS})  # 把当前所在目录下的所有源文件都包含进去

target_link_libraries(lm_test modbuslib) # 添加链接库

src/modbus.h 代码如下:

class Modbus {

public:
    Modbus();

} ;

src/modbus.cpp  代码如下:

#include "modbus.h"
#include <iostream>

Modbus::Modbus()
{

    std::cout<<"modbus construct"<<std::endl;
}

src/CMakeLists.txt  代码如下:

# add_library(modbuslib modbus.cpp)  #create static lib
add_library(modbuslib SHARED modbus.cpp)  #create dynamic lib

说明: 
    当时用.so的动态库时,需要用到 dlopen dlsym 关键字来操作:
    dlopen、dlsym我所理解的是通过dlopen可以动态加载一个so,通过dlsym可以获得该so中某接口的地址,从而实现使用该so中接口的功能;

3. 使用CMake进行链接并生成Makefile

cd build/
cmake ..

链接成功信息如下:

ls build/ 下看到生成了Makefile文件,然后直接执行 make

生成了可执行文件: lm_test

4. 使用std::thread 时,CMakeLists.txt写法:


cmake_minimum_required(VERSION 2.8)
project(LM_cmake_test)  # project name must not be have spaces

include_directories(include)
# add_subdirectory(src)

add_definitions("-lpthread")
add_definitions("-std=c++11")

# add compile option
# if(CMAKE_COMPILER_IS_GNUCXX)
#     add_compile_options(-std=c++11)
#     message(STATUS "optional:-std=c++11")   
# endif(CMAKE_COMPILER_IS_GNUCXX)

find_package (Threads)  //使用了std:thread线程时

add_executable(LM_CMAKE main.cpp src/a.cpp)

target_link_libraries (LM_CMAKE ${CMAKE_THREAD_LIBS_INIT})  //使用了std:thread线程时

5.  使用std::thread 和arm 交叉编译工具链时,CMakeLists.txt写法如下:

方法1:CrossCompile.cmake方式,太繁琐,不好用

参考知乎:cmake:交叉编译 - 知乎

关于CMake 配置交叉编译器这个功能,搞了2天!!

配置方法:

  • 在CMakeLists.txt目录下创建 CrossCompile.cmake 文件
  • CrossCompile.cmake 内容如下:
set(CMAKE_SYSTEM_NAME Linux)

set(tools /usr/local/arm/arm-2014.05)  # /usr/local/arm/arm-2014.0 是我ubuntu上的交叉工具链目录
set(CMAKE_C_COMPILER ${tools}/bin/arm-none-linux-gnueabi-gcc-4.8.3)
set(CMAKE_CXX_COMPILER ${tools}/bin/arm-none-linux-gnueabi-g++)

CMakeLists.txt 里,我需要编译 c++11 以及用到 std::thread 等特性,其内容如下:

cmake_minimum_required(VERSION 2.8.12)
project(cpp_process)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ldl -lpthread")
set(CMAKE_NO_SYSTEM_FROM_IMPORTED 1)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# add_definitions(-std=c++11)
# add_definitions(-lpthread)

include_directories(include)
add_executable(cpp_process cppHandle.cpp src/my_socket.cpp)

最后一步,链接和编译,也是最重要的一步:

cd build/
cmake -DCMAKE_TOOLCHAIN_FILE=../CrossCompile.cmake ..
make

之前我们链接时都是执行 cmake .. , 现在交叉编译需要加上  -DCMAKE_TOOLCHAIN_FILE=../CrossCompile.cmake .. 选项。

交叉编译 方法2: 参考自CMake官方文档: cmake-toolchains(7) — CMake 3.23.0-rc4 Documentation

官方文档方法非常简单,如下所示。

Demo 测试文件结构如下:

CMakeLists.txt 写法如下

cmake_minimum_required(VERSION 2.8)

#切记,指定交叉编译工具链这几句话一定要放在project之前才起作用!!!
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(tools /usr/local/arm/arm-2014.05/bin)  #定义编译器路径宏
set(CMAKE_C_COMPILER ${tools}/arm-none-linux-gnueabi-gcc)   #指定gcc交叉编译器路径
set(CMAKE_CXX_COMPILER ${tools}/arm-none-linux-gnueabi-g++) #指定g++交叉编译器路径

project(CMake_test)

include_directories(include)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

add_executable(LM_test main.cpp src/modbus.cpp)

摘自官方CMake文档:

libmodbus是一个开源的MODBUS通信库,支持命令响应方式和异步IO方式两种通信方式。libmodbus提供了一系列C函数,可用于在Linux或者Windows系统中来开发应用程序,来实现MODBUS通信。 在使用libmodbus时,首先要了解设备的地址(slave ID),通信方式(RTU或TCP),通信端口、串口参数等信息。对于串口通信方式,需要配置通信波特率、数据位、停止位、奇偶校验位等串口参数。对于TCP通信方式,需要连接到设备的IP地址和端口号。 使用libmodbus时,可以使用modbus_new_rtua和modbus_new_tcp函数创建一个新的modbus_t对象,该对象管理MODBUS通信(包括初始化,发送和接收命令等)。对于RTU通信方式,使用modbus_rtu_set_serial_mode可以设置通信的RS-485模式,设置为ModbusRTU或者ModbusASCII。 libmodbus提供了一组针对命令的API,如写操作写单个寄存器modbus_write_register、读操作读多个寄存器modbus_read_registers等。通过这些API,可以实现单向或者双向通信。 使用libmodbus时,还需要注意枚举类型modbus_error和它所对应的错误码值,它们对应着不同的错误类型。例如,当返回值为-1的时候,表示MODBUS通信出现异常。 总之,使用libmodbus时,需要注意设备的地址、通信方式、串口参数和API函数的使用方法等细节问题,仔细地阅读官方的文档和示例程序,熟练掌握这些知识才能够成功实现MODBUS通信。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值