一、浅谈能自动生成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文档: