Linux开发工具03:使用GCC、make和CMake编译代码

写在前面

  • 这里主要记录一下如何使用GCC、make和CMake编译代码;

一、GCC

  • g++是GCC下专门用于编译C++项目的编译器;

  • 假设目录结构如下:

    • include:包含分离的.h.cpp文件;
    • src:包含主函数入口main.cpp

图片

  • 参考:
    • http://c.biancheng.net/view/7936.html;
    • gcc/g++常用编译选项和gdb常用调试命令;

1. gcc和g++的区别

  • GCC(GNU Compiler Collection,GNU编译器集合)中,

    • 包含了许多编译器,可以编译包括C、C++、Java等语言;
    • gcc是GCC中的C编译器;
    • g++是GCC中的C++编译器;
  • 区别:

    • (1) 编译语言
      • gcc只能编译C语言;
      • g++可以编译C语言和C++语言;
    • (2) 对待.c.cpp文件的处理
      • gcc会分别处理两种文件,如果.cpp中只有C语法(最好是仅有用extern "C"包含的代码),则gcc也是可以编译的,但不建议;
      • g++会都当作.cpp文件处理;
    • (3) C++链接库
      • gcc不会自动链接C++标准库,需要手动加-libstdc++
      • g++会自动链接C++标准库;
    • (4) 对C++特有功能的支持
      • gcc不支持编译类、多态等C++特性;
      • g++支持C++的所有功能;

2. 使用

2.1 直接编译
  • 主函数如下:
    • 引用的头文件需要显示给出完整的相对路径;
#include <iostream>

#include "../include/add.h"
#include "../include/minus.h"

int main()
{
	std::cout << "Hello world!" << std::endl;
	std::cout << add(2, 3) << std::endl;
	std::cout << minus(2, 3) << std::endl;
	return 0;
}
  • 则使用G++编译代码如下:
    • 所有使用的.cpp都需要显式给出;
    • 所有的.cpp都需要给出完整的查找路径;
# 进入main.cpp所在的src目录
cd xxx/src
# 指明所有的.cpp的路径
g++ main.cpp ../include/add.cpp ../include/minus.cpp -o main
  • 编译的过程如下:

    • 将所有的.cpp编译成.o文件;
    • 通过在系统的头文件搜索路径中搜索和main.cpp中的#include说明,将对应的同名的.o链接到main中;
    • 最后生成输出main
  • 不足:

    • (1) 代码main.cpp中需要显式给出所有使用库的相对路径;
      • 一旦修改了库的位置,则源代码也需要对应修改
      • 可以在编译时添加头文件搜索路径来避免这个问题;
    • (2) 编译命令中需要显式给出所有使用的.cpp文件的相对路径;
      • 如果使用了许多头文件,则所有涉及到的.cpp都需要写,而且相对路径必须给出,所以写法上很繁琐;
      • 可以使用的.cpp文件打包成静态库来避免这个问题;
2.2 使用头文件搜索编译
  • 主函数如下:
    • 引用的头文件不需要显式给出完整的相对路径;
#include <iostream>

#include "add.h"
#include "minus.h"

int main()
{
	std::cout << "Hello world!" << std::endl;
	std::cout << add(2, 3) << std::endl;
	std::cout << minus(2, 3) << std::endl;
	return 0;
}
  • 则使用G++编译代码如下:
    • 所有使用的.cpp都需要显式给出;
    • 所有的.cpp都需要给出完整的查找路径;
# 进入main.cpp所在的src目录
cd xxx/src
# -I用于将头文件所在的目录添加到编译器的搜索路径
# -I后面没有空格,直接跟需要添加的头文件搜索路径
g++ -I../include main.cpp ../include/add.cpp ../include/minus.cpp -o main
  • 编译的过程如下:
    • 将所有的.cpp编译成.o文件;
    • 通过在系统的头文件搜索路径和添加的头文件搜索目录中搜索,将对应的同名的.o链接到main中;
    • 最后生成输出main
2.3 使用静态库编译
  • 首先在include文件夹中将.cpp文件编译成一个静态库;
# 进入include文件夹
cd xxx/include
# 为每个.cpp文件编译.o文件
g++ -c add.cpp minus.cpp
# 打包成静态库libmath.a
ar rcs libmath.a add.o minus.o
  • 然后就可以在编译时使用静态库了:
# 进入main.cpp所在的src目录
cd xxx/src
# 用静态库代替.cpp文件
g++ -I../include main.cpp ../include/libmath.a -o main
  • 注意:
    • 静态库中仅包含.cpp文件;
    • 也就是说,如果要令整个库可以迁移的话,还是要加上头文件一起打包的;
    • 头文件是做指引,静态库(或者说.o文件)是做实际的链接,它们的功能是互斥的;
2.4 设置构建级别编译
  • 默认使用RELEASE级别构建;
  • 添加-g参数可以使用DEBUG级别构建;
    • 如果需要使用gdb调试则需要用该级别构建;
  • 此外,还有代码的优化级别,可以搭配RELEASE级别或者DEBUG级别一起使用,说明如下:

说明

# 进入main.cpp所在的src目录
cd xxx/src
# 指明所有的.cpp的路径
g++ -g main.cpp ../include/add.cpp ../include/minus.cpp -o main

二、make

三、CMake

  • 参考:
    • CMake 入门实战;
    • CMake 输出路径的设置 ,当前目录,安装拷贝 集合;
    • cmake-examples-Chinese;

1. 安装

sudo apt install cmake

2. 使用

  • 一个CMakeList.txt的例子如下:
    • 一些内置变量的说明可以参考官方文档:https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html;
# CMake 最低版本号要求
cmake_minimum_required(VERSION 2.8)

# 项目信息
project(项目名)

# 设置静态库文件目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
file(MAKE_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY})
# 设置动态库文件目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
file(MAKE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
# 设置可执行文件目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
file(MAKE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# 设置中间文件目录
set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

# 设置构建为调试模式
set(CMAKE_BUILD_TYPE Debug)
# 根据编译器信息设置调试信息
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
endif()

# 查找当前目录下的所有源文件,并把名称保存在DIR_SRCS变量
aux_source_directory(. DIR_SRCS)

# 指定main源文件和可执行文件名,需要放在最后,否则前面的设置不会生效
# add_executable(可执行文件名 cpp文件1 cpp文件2 ...)
add_executable(可执行文件名 ${DIR_SRCS})
  • 使用的过程如下:
# 在CMakeList.txt目录下新建一个build文件夹
mkdir build
# 进入该文件夹
cd build
# 调用CMakeList.txt在当前目录下生成makefile及其他中间文件
cmake ..
# 执行make生成可执行文件
make
2.1 使用头文件搜索编译
  • 项目结构如下:

项目结构

  • 主函数如下:
    • 引用的头文件不需要显式给出完整的相对路径;
#include <iostream>

#include "add.h"
#include "minus.h"

int main()
{
	std::cout << "Hello world!" << std::endl;
	std::cout << add(2, 3) << std::endl;
	std::cout << minus(2, 3) << std::endl;
	return 0;
}
  • CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.5)

# 项目名称
project(cmake_project)

# 创建一个变量,包含所有的.cpp文件
set(SOURCES
    src/main.cpp
    include/add.cpp
    include/minus.cpp
)

# 生成可执行文件
add_executable(${PROJECT_NAME} ${SOURCES})

# 设置包含库路径
target_include_directories(${PROJECT_NAME}
    PRIVATE
        ${PROJECT_SOURCE_DIR}/include
)
  • 一些说明:
    • project(cmake_project)表明当前项目名,即设置了${PROJECT_NAME}=cmake_project
    • target_include_directories为当前项目设置包含库路径,所以后面跟的第一个参数是项目的名字;
    • 一些可用的CMake变量说明如下:

说明

2.2 创建静态库并使用静态库编译
  • 创建静态库的CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.5)

# 项目名称
project(cmake_project)

# 创建一个变量,包含静态库的所有.cpp文件
set(SOURCES
    include/add.cpp
    include/minus.cpp
)

# 生成静态库
add_library(${PROJECT_NAME} ${SOURCES})

# 设置包含库路径
target_include_directories(${PROJECT_NAME}
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)
  • 说明:

    • 其实和创建可执行文件非常像,仅修改为使用add_library,就可以不创建目标文件而改为创建静态库;
    • 调用cmake的方式也和创建可执行文件相同;
    • 创建后的静态库也是在build文件夹中,以.a为后缀;
  • 使用静态库进行编译的CMakeLists.txt如下:

cmake_minimum_required(VERSION 3.5)

# 项目名称
project(cmake_project)

# 创建一个变量,包含静态库的所有.cpp文件
set(SOURCES
    src/main.cpp
)

# 生成静态库
add_executable(${PROJECT_NAME} ${SOURCES})

# 设置包含库路径
target_include_directories(${PROJECT_NAME}
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)

# 设置包含静态库路径
target_link_libraries(${PROJECT_NAME}
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include/libcmake_project.a
)
  • 说明:
    • 即使是使用静态库,头文件还是要加上的;
    • 因为静态库只是.cpp文件的集合,这个在GCC一章中也有说到;
    • 另外,关于PUBLICPRIVATEINTERFACE的说明如下:

说明

2.3 设置构建级别编译
  • 这里的构建模式是指DEBUGRELEASE之类的模式,如下:

    • (1) Debug模式:
      • 用于开发和调试目的的构建级别;
      • 包含调试符号和较少优化;
      • 如果需要使用gdb调试的话,则需要使用该级别构建;
    • (2) RelWithDebInfo
      • 包含优化和调试信息的构建级别;
      • 适合进行性能分析和调试;
    • (3) Release模式(默认):
      • 用于发布和部署目的的构建级别;
      • 几乎不包含调试符号和较多的优化;
      • 使用该级别不能进行gdb调试;
    • (4) MinSizeRel
      • 最小体积和最大程度优化的构建级别;
  • 设置构建级别的CMakeLists.txt如下:

cmake_minimum_required(VERSION 3.5)

# 项目名称
project(cmake_project)

# 创建一个变量,包含静态库的所有.cpp文件
set(SOURCES
    src/main.cpp
)

# 设置构建为调试模式
set(CMAKE_BUILD_TYPE Debug)

# 生成静态库
add_executable(${PROJECT_NAME} ${SOURCES})

# 设置包含库路径
target_include_directories(${PROJECT_NAME}
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)

# 设置包含静态库路径
target_link_libraries(${PROJECT_NAME}
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include/libcmake_project.a
)
  • 说明:
    • 通过设置CMAKE_BUILD_TYPE变量即可更改调试模式;

3. 错误

3.1 CMake Error: The source “/home/jeremy_wsl/projects/CMakeProject/CMakeLists.txt” does not match the source “/home/jeremy_wsl/projects/CMakeProject/src/CMakeLists.txt” used to generate cache. Re-run cmake with a different source directory.
  • 原因

    • 当前的源码目录与之前生成缓存时使用的源码目录不匹配;
    • 更改项目目录结构或者移动 CMakeLists.txt 文件后继续使用之前的缓存文件;
  • 解决方法

    • 删除bulid文件夹下的所有文件;
    • 重新cmake即可;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/146202.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

双点重发布+路由策略实验

一、双点重发布实验 1、实验拓扑图 2、各路由器IP地址、环回地址配置 R1 R2 R3 R4 3、启动RIP和OSPF 4、双向重发布 5、查看路由信息 6、更改网络类型 6、抓取流量 二、路由策略实验 1、实验拓扑图 2、各路由器IP地址的配置 3、启动RIP和OSPF 3、重发布 4、抓取流量 5、创建…

电脑屏幕标记软件——Pointofix

前言 Pointofix是一款由德国人开发的屏幕标记软件&#xff0c;德国人的工匠精神&#xff0c;是出了名的&#xff0c;德国人开发的软件也一样。 Pointofix体积非常小巧&#xff0c;安装包只有1MB大小&#xff0c;使用Pointofix可以直接在屏幕上面写字、画图、标重点。 下面介…

为什么Springboot项目中有些写法继承了SpringBootServletInitializer类?Springboot的两种发布方式

文章目录 一、前言二、SpringBoot的两种发布方式2.1、内置容器运行2.2、外置容器&#xff08;Tomcat&#xff09;运行 三、扩展3.1、如何将 Spring Boot 项目打包成 war 包&#xff1f; 一、前言 在一次SpringBoot源码中看到了启动类中继承了SpringBootServletInitializer&…

数据结构-二叉树的前、中、后序遍历

目录 1. 二叉树的遍历 1.1 前序 1.2 中序 1.3 后序 1.4 遍历的复杂度 2.二叉树节点个数及高度的计算 2.1 二叉树节点个数 2.2 二叉树叶子节点的个数 2.3 二叉树高度 2.4 二叉树第k层节点个数 1. 二叉树的遍历 前面的章节中&#xff0c;我们学习了二叉树的顺序结构&am…

Python入门:一文详解Python列表(List)操作方法

文章目录 前言一、创建一个列表二、访问列表中的值三、更新列表四、删除列表元素六、Python列表截取七、Python列表操作的函数和方法关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②…

【thop.profile】thop.profile计算网络参数量和计算效率

&#x1f34b;&#x1f34b;1.安装thop 安装thop有两种方式。 &#x1f3c6;第一种 pip install thop &#x1f3c6;第二种 用源码编译安装 从官网下载【github】thop安装压缩包下载压缩文件&#xff0c;解压到虚拟环境的site-packages文件下激活进入自己的虚拟环境cd到压缩…

不同类型的软件企业该如何有效的管理好你的软件测试团队?

最近在网上发现一篇记录了2012年《[视频]作为测试经理如何有效管理好你的软件测试团队》的文字内容&#xff0c;感谢记录的人&#xff0c;我也保存一下。顺便将演讲中的PPT重点截图也放上来&#xff0c;一并保存了&#xff01;。由于是现场速记&#xff0c;过度的口语化&#x…

迅为iTOPRK3588开发板系统定制(无法联网)

在上一个小节中讲解了 ubuntu 和 debian 文件系统的定制&#xff0c;但那是在可以运行脚本正常构 建系统的前提下&#xff0c;而本小节则是针对部分特殊用户无法联网的情况。 在 source 目录下存放了已经构建完成的压缩包&#xff0c;如下图所示&#xff1a; 然后使用以下命令…

weblogic控制台登陆console的时候慢

我们在搭建完weblogic后&#xff0c;登录控制台时&#xff0c;会出现等待很长时间的情况。 如下图&#xff1a;怎么解决呢 连接所属服务器,.找到jdk的安装路径 [rootlocalhost lib]# echo $JAVA_HOME/ /usr/java/jdk1.8.0_161/ 进入jre下的lib目录下的security目录&#xff0…

全球市场的新趋势:海外网红营销和私域流量的共同驱动

在数字时代的今天&#xff0c;随着全球互联网的蓬勃发展&#xff0c;网络营销已经不再是一种新鲜事物。然而&#xff0c;随着社交媒体和在线内容创作的兴起&#xff0c;一种新的营销方式崭露头角&#xff0c;它将海外网红营销与私域流量相结合&#xff0c;成为了全球市场的一股…

刘家窑中医院医生王忠:以仁心诠释医者使命

王忠是刘家窑中医院的一名医生&#xff0c;从医多年&#xff0c;积累了丰富的临床经验&#xff0c;挽救了无数病人的生命。他以“想病人之所想&#xff0c;急病人之所急”为自己行医济世的人生格言。 病人说&#xff1a;他是一位可亲可敬的“亲人”。 “医以德为本&#xff0c…

【Proteus仿真】【STM32单片机】拔河游戏设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用按键、LED、动态数码管模块等。 主要功能&#xff1a; 系统运行后&#xff0c;指示灯处于中间位置&#xff0c;数码管显示得分0&#xff0c;当…

MySQL主从复制、读写分离(利用Amoeba和Mycat)、完全同步

目录 一、主从复制 1、概念 1.1主从复制延迟问题&#xff1a; 1.2、MySQL安全和性能配置&#xff1a; 1.3、主从复制的工作过程&#xff1a; 1.4、mysql主从复制注意点&#xff1a; 1.5、MySQL的主从复制的模式&#xff1a; 2、主从复制实验&#xff1a; 二、读写分离&…

【ccf-csp题解】第11次csp认证-第三题-Json查询超详细讲解

此题思路来源于acwing ccfcsp认证辅导课 题目描述 思路分析 此题的难点在于对输入的内容进行解析&#xff0c;题目中说除了保证字符串内容不会有空格存在之外&#xff0c;其它的任意地方都可能出现空格&#xff0c;甚至在某些地方还会出现空行&#xff0c;这样的话&#xff0…

由于找不到msvcp140.dll无法继续执行代码有哪些解决方法

msvcp140.dll是Microsoft Visual C 2015 Redistributable的一个组件&#xff0c;它是运行许多Windows应用程序所必需的。当msvcp140.dll丢失或损坏时&#xff0c;可能会导致以下问题&#xff1a; 1. 程序无法启动或崩溃。 2. 系统出现错误提示&#xff0c;如“找不到msvcp140…

离散卡尔曼滤波器算法详解及重要参数(Q、R、P)的讨论

公开数据集中文版详细描述参考前文&#xff1a;https://editor.csdn.net/md/?not_checkout1&spm1011.2124.3001.6192神经元Spike信号分析参考前文&#xff1a;https://blog.csdn.net/qq_43811536/article/details/134359566?spm1001.2014.3001.5501神经元运动调制分析参考…

Java学习之路 —— 异常、集合

文章目录 1. 异常2. 集合2.1 遍历2.1.1 迭代器2.1.2 增强for循环2.1.3 Lambda 2.2 List2.3 Set2.3.1 HashSet2.3.2 LinkedHashSet2.3.3 TreeSet 2.4 Map 1. 异常 Exception&#xff1a;叫异常&#xff0c;是程序员可以捕捉的。异常又分为了2类&#xff1a; 运行时异常&#x…

SpringBoot整合WebSocket实现订阅消息推送

目录 一、什么是WebSocket1.HTTP协议2.WebSocket协议 二、WebSocket使用场景1.消息推送2.实时聊天3.弹幕4.实时数据更新 三、SpringBoot整合WebSocket&#xff08;以实现消息推送为例&#xff09;1.添加依赖2.创建消息类2.WebSocket配置类3.工具类4.测试连接5.服务调用 一、什么…

人工智能 :一种现代的方法 第七章 逻辑智能体

文章目录 前言人工智能 &#xff1a;一种现代的方法 第七章 逻辑智能体7.1 基于知识的智能体7.2 Wumpus世界7.4 命题逻辑7.5 命题逻辑定理证明7.5.1推导和证明7.5.2 归结原理7.5.3 horn子句和限定子句7.5.4 前向链接和后向链接 7.6 有效命题逻辑模型求解7.6.1完备的回溯算法7.6…

MySQL主从延时问题

过线上 MySQL 维护经验的童鞋都知道&#xff0c;主从延迟往往是一个让人头疼不已的问题。 不仅仅是其造成的潜在问题比较严重&#xff0c;而且主从延迟原因的定位尤其考量 DBA 的综合能力&#xff1a;既要熟悉复制的内部原理&#xff0c;又能解读主机层面的资源使用情况&#…