C++服务器框架开发11——编译调试1/cmake学习

该专栏记录了在学习一个开发项目的过程中遇到的疑惑和问题。
其教学视频见:[C++高级教程]从零开始开发服务器框架(sylar)

上一篇:C++服务器框架开发10——日志系统1~9代码

C++服务器框架开发11——编译调试1/cmake学习

  • 目前进度
  • ubuntu下的cmake学习
    • 简单样例
    • 同一目录下多个源文件
    • 不同目录下多个源文件
    • 正式一点的组织结构
    • 动态库和静态库的编译控制
    • 对库进行链接
    • 添加编译选项
    • 添加控制选项
      • 本来要生成多个bin或库文件,现在只想生成部分指定的bin或库文件
      • 对于同一个bin文件,只想编译其中部分代码(使用宏来控制)
  • 回顾图1

目前进度

学习到第6个视频的00:59,由于不了解编译,这次先学习下cmake。下图是CMakeLists.txt中的内容。
在这里插入图片描述

ubuntu下的cmake学习

参考自文章1
“CMake是一个跨平台的编译(Build)工具, 不同平台之间的编译方式遵循不同的规则,彼此不通用。因此 Cmake被提出,他统一了一套规则, 来描述所有平台的编译过程。
它允许开发者编写一种平台无关的 CMakeLists.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。”
以下内容基本上都是学习的文章1。

简单样例

在这里插入图片描述
在文件目录下创建这两个文件,内容如下:
main.c,输出一个字符串。

#include <stdio.h>

int main(void)
{
	printf("Hello World\n");

	return 0;
}

CMakeLists.txt,其中各行的意思都有进行注释说明。

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo
project (demo)
# 将名为 main.c 的源文件编译成一个名称为 main 的可执行文件
add_executable(main main.c)

在该目录下打开一个终端,执行cmake ./得到makefile。
执行过程:
在这里插入图片描述
执行后目录下的文件:
在这里插入图片描述
执行make进行编译。
执行过程:
在这里插入图片描述
执行后目录下多了一个main的可执行文件。
在这里插入图片描述
运行main看看效果:
在这里插入图片描述
如果想重新生成,可以先执行make clean来删除已有的main文件。

同一目录下多个源文件

上面的例子是只有一个源文件的,如果有多个的话,如下:
在这里插入图片描述

main.c调用调用testFunc.h里声明的函数func()。
main.c:

#include <stdio.h>

#include "testFunc.h"

int main(void)
{
	func(100);

	return 0;
}

testFunc.h:

/*
** testFunc.h
*/

#ifndef _TEST_FUNC_H_
#define _TEST_FUNC_H_

void func(int data);

#endif

testFunc.c:

/*
** testFunc.c
*/

#include <stdio.h>
#include "testFunc.h"

void func(int data)
{
	printf("data is %d\n", data);
}

CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)
# 将名为 main.c 和testFunc.c的源文件编译成一个名称为 main 的可执行文件
add_executable(main main.c testFunc.c)

cmake 和make:
在这里插入图片描述
运行:
在这里插入图片描述
如果有很多源文件,这种方法就不太方便。可以使用aux_source_directory(dir var),这个命令将目录dir下的所有源文件存储在变量var中。
例子:
在这里插入图片描述
main.c:

#include <stdio.h>

#include "testFunc.h"
#include "testFunc1.h"

int main(void)
{
	func(100);
	func1(200);

	return 0;
}

testFunc1.c:

/*
** testFunc1.c
*/

#include <stdio.h>
#include "testFunc1.h"

void func1(int data)
{
	printf("data is %d\n", data);
}

testFunc1.h:

/*
** testFunc1.h
*/

#ifndef _TEST_FUNC1_H_
#define _TEST_FUNC1_H_

void func1(int data);

#endif

CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)
#把当前目录下的源文件存放到变量SRC_LIST里
aux_source_directory(. SRC_LIST)
# 将SRC_LIST里的源文件编译成一个名称为 main 的可执行文件
add_executable(main ${SRC_LIST})

cmake 和make,并执行:
在这里插入图片描述
如果不想把所有源文件都进行编译,可以用set来指定想要编译的源文件,对应的CMakeLists.txt可以修改为如下:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)
#把当前目录下的源文件存放到变量SRC_LIST里
set( SRC_LIST
	 ./main.c
	 ./testFunc1.c
	 ./testFunc.c)
# 将SRC_LIST里的源文件编译成一个名称为 main 的可执行文件
add_executable(main ${SRC_LIST})

不同目录下多个源文件

假设文件目录如下:
在这里插入图片描述
修改CMakeLists.txt

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)
#由于main.c中include了两个.h文件,所以需要添加头文件的搜索路径
include_directories (test_func test_func1)
#把两个目录下的源文件分别存放到变量SRC_LIST和SRC_LIST1里
aux_source_directory (test_func SRC_LIST)
aux_source_directory (test_func1 SRC_LIST1)
# 将SRC_LIST里的源文件编译成一个名称为 main 的可执行文件
add_executable(main ${SRC_LIST} ${SRC_LIST1})

其中的include_directories (test_func test_func1)作用已经在注释里了。

正式一点的组织结构

src:源文件目录
include:头文件目录
build:生成的对象文件
bin:可执行文件。
结构调整如下:
在这里插入图片描述
CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)
#添加并构建子目录src
add_subdirectory(./src)

其中的add_subdirectory(src)指定src为源文件的存放位置,camke时回去src目录进行编译。
src/CMakeLists.txt:


#添加头文件的搜索路径
include_directories (../include)
#把源文件存放到变量SRC_LIST里
aux_source_directory (. SRC_LIST)
# 将SRC_LIST里的源文件编译成一个名称为 main 的可执行文件
add_executable(main ${SRC_LIST} )
#设置存放可执行文件的位置
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

其中set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)把存放可执行文件的位置设置为工程根目录下的bin目录。

为了让生成的对象文件放在build里,需要再build目录下打开终端,然后执行cmake ..,接着make
在这里插入图片描述
在这里插入图片描述
另一种方法,可以把子目录src中的cmakelists内容放到外层目录中来,只用一个camkelists。如下:
CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)

#添加头文件的搜索路径
include_directories (./include)
#把源文件存放到变量SRC_LIST里
aux_source_directory (./src SRC_LIST)
# 将SRC_LIST里的源文件编译成一个名称为 main 的可执行文件
add_executable(main ${SRC_LIST} )
#设置存放可执行文件的位置
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

build目录下cmake ../, 然后make,最后执行。
在这里插入图片描述

动态库和静态库的编译控制

目录结构如下
在这里插入图片描述
把生成的库放在lib里。

CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)

#把文件testFunc.c存放到变量SRC_LIST里
set (SRC_LIST ${PROJECT_SOURCE_DIR}/testFunc/testFunc.c)

#生成库,库名字为testFunc_shared,设置为SHARED(动态库,不设置的话默认为静态),源文件为SRC_LIST
add_library (testFunc_shared SHARED ${SRC_LIST})
#生成库,库名字为testFunc_static,设置为STATIC(静态库,不设置的话默认为静态),源文件为SRC_LIST
add_library (testFunc_static STATIC ${SRC_LIST})

#设置动态库testFunc_shared文件名称为libtestFunc.so
set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
#设置静态库testFunc_static文件名称为libtestFunc.a
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")

#设置库文件的输出路径为./lib
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

add_library: 生成动态库或静态库。
set_target_properties: 设置最终生成的库的名称。
LIBRARY_OUTPUT_PATH: 库文件的默认输出路径。

build目录下cmake ../, 然后make
在这里插入图片描述
查看子目录lib
在这里插入图片描述

对库进行链接

重新构建如下工程,并把上一小节的生成的库放到./testFunc/lib下。
在这里插入图片描述
main.c:

#include <stdio.h>

#include "testFunc.h"

int main(void)
{
    func(100);
    
    return 0;
}

testFunc.h:

/*
** testFunc.h
*/

#ifndef _TEST_FUNC_H_
#define _TEST_FUNC_H_

void func(int data);

#endif

CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)

#设置可执行文件的输出路径为./bin
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#把文件main.c存放到变量SRC_LIST里
set (SRC_LIST ${PROJECT_SOURCE_DIR}/src/main.c)
# 添加头文件的搜索路径
include_directories (${PROJECT_SOURCE_DIR}/testFunc/inc)
#在指定目录./testFunc/lib查找制定库testFunc
find_library(TESTFUNC_LIB testFunc HINTS ${PROJECT_SOURCE_DIR}/testFunc/lib)
#将SRC_LIST里的源文件编译成一个名称为 main 的可执行文件
add_executable (main ${SRC_LIST})
#把目标文件与库文件进行链接
target_link_libraries (main ${TESTFUNC_LIB})


find_library: 在指定目录下查找指定库。
target_link_libraries: 把目标文件与库文件进行链接。

build目录下cmake ../, 然后make,最后执行。
在这里插入图片描述

查看可执行文件使用了那些库readelf -d ../bin/main
在这里插入图片描述

添加编译选项

有时编译程序时想添加一些编译选项,如-Wall-std=c++11等,就可以使用add_compile_options来进行操作。
这里可以去看原文章的简单示例。

添加控制选项

如果想要在编译代码时只编译一些指定的源码,可以使用cmake的option命令。

本来要生成多个bin或库文件,现在只想生成部分指定的bin或库文件

如下结构:
在这里插入图片描述
main1.c:

// main1.c
#include <stdio.h>

int main(void)
{
    printf("hello, this main1\n");
    
    return 0;
}

main2.c:

// main2.c
#include <stdio.h>

int main(void)
{
    printf("hello, this main2\n");
    
    return 0;
}

CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)
#设置一个option,名称为MYDEBUG,提示为"enable debug compilation",第3个参数的默认值是OFF。
option(MYDEBUG "enable debug compilation" OFF)
#设置存放可执行文件的位置
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#添加并构建子目录src
add_subdirectory(./src)

./src/CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)

#将源文件main1.c编译成一个名称为 main1 的可执行文件
add_executable(main1 main1.c)
#如果MYDEBUG为NO则将源文件main2.c编译成一个名称为 main2 的可执行文件
if (MYDEBUG)
    add_executable(main2 main2.c)
else()
    message(STATUS "Currently is not in debug mode")    
endif()

build目录下cmake ../, 然后make,最后查看bin目录。
在这里插入图片描述

对于同一个bin文件,只想编译其中部分代码(使用宏来控制)

结构如下:
在这里插入图片描述
main.c:

#include <stdio.h>

int main(void)
{
#ifdef WWW1
    printf("hello world1\n");
#endif    

#ifdef WWW2     
    printf("hello world2\n");
#endif

    return 0;
}

CMakeLists.txt:

# 指定所需 CMake 的最低版本
cmake_minimum_required (VERSION 2.8)
# 项目的名称 demo  
project (demo)

#设置存放可执行文件的位置
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#设置一个option,名称为WWW1,提示为"print one message",第3个参数的默认值是OFF。
option(WWW1 "print one message" OFF)
#设置一个option,名称为WWW2,提示为"print another message",第3个参数的默认值是OFF。
option(WWW2 "print another message" OFF)

#使用add_definitions()函数控制代码的开启和关闭
if (WWW1)
    add_definitions(-DWWW1)
endif()

if (WWW2)
    add_definitions(-DWWW2)
endif()
#将源文件main.c编译成一个名称为 main 的可执行文件
add_executable(main main.c)

只想编译WWW1里的内容:
build目录下cmake ../ -DWWW1=ON -DWWW2=OFF, 然后make,最后执行。
在这里插入图片描述
只想编译WWW2里的内容:
build目录下cmake ../ -DWWW1=OFF -DWWW2=ON, 然后make,最后执行。
在这里插入图片描述
编译WWW1和WWW2里的内容:
build目录下cmake ../ -DWWW1=ON -DWWW2=ON, 然后make,最后执行。
在这里插入图片描述
:每次重新编译时,记得把build和bin中的文件删掉,否则build中的文件会影响下次编译,比如我第一次编译设置了WWW1,如果我没有删除build中文件,那么如果我第二次编译时没设置WWW1,WWW1将是上次编译时的设置。

这是我对cmake的所有学习记录,学习资料来自https://blog.csdn.net/whahu1989/article/details/82078563。这篇文章非常详细,我基本上理解清楚了cmake的基本用法。

回顾图1

在这里插入图片描述
set(CMAKE_VERBOSE_MAKEFILE ON)是显示原始编译信息,用于定位链接报错。
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O3 -fPIC -ggdb -std=c++11 -Wall -Wno-deprecated -Werror -Wno-unused-function")是编译选项设置,具体可以看这两篇文章:2和3。
add_dependencies(test sylar)是因为,编译可执行文件test时需要链接依sylar,而sylar也是通过编译得到的。所以设置这个后,当检测到依赖库sylar还没编译时,会先编译依赖库,然后再编译test。

这次笔记时隔很久,一是其他事情耽搁了,二是camke原先根本不会,三是这个笔记有点长。

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

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

相关文章

使用Django数据库模型中的ForeignKey()形成数据表记录的父子层次结构

可以把ForeignKey()的第1个参数设置为值 “self” 实际形成数据表记录的父子层次结构。 下面是一个简单的实例&#xff1a; 在文件 E:\Python_project\P_001\myshop-test\myshop\app1\models.py 中写入下面的代码&#xff1a; from django.db import models# Create your mod…

创建型模式

创建型模式&#xff08;Creational Pattern&#xff09;关注对象的创建过程&#xff0c;是一类最常用的设计模式&#xff0c;在软件开发中应用非常广泛。创建型模式将对象的创建和使用分离&#xff0c;在使用对象时无须关心对象的创建细节&#xff0c;从而降低系统的耦合度&…

叮,您有一份《C语言思维导图》,请注意查收

目录导航 &#x1f680; 前言&#x1f4fa;配套教程推荐&#x1f530;文章列表&#x1f4da;Part 1&#xff1a;初识C语言&#x1f4da;Part 2&#xff1a;分支和循环语句&#x1f4da;Part 3&#xff1a;函数&#x1f4da;Part 4&#xff1a;数组&#x1f4da;Part 5&#xff…

【数据结构】手撕排序NO.1----排序初识

目录 一. 前言 二. 排序的概念及运用 2.1 排序的概念 2.2 排序的运用 2.3 常见的排序算法 三. 冒泡and选择排序 3.1 冒泡排序 3.2 选择排序 四. 各大排序算法的复杂度和稳定性 一. 前言 从本期开始&#xff0c;我们的数据结构将迎来一个新的篇章&#xff1a;排序篇&#xff…

基于RASC的keil电子时钟制作(瑞萨RA)(1)----安装RASC

基于RASC的keil电子时钟制作_瑞萨RA_1安装RASC 概述硬件准备视频教程RA Smart Configurator软件下载RASC安装Keil下Renesas RA pack包安装 概述 RA Smart Configurator"是一种基于"灵活组合软件"概念的代码生成辅助工具。它可以自动生成微控制器的初始配置程序…

看见未来:定位咨询如何预测行业趋势

商业竞争时代&#xff0c;变化无处不在。科技日新月异&#xff0c;消费者需求日益多元&#xff0c;市场环境更加动态不定。在这个快速发展的时代&#xff0c;如果企业想要继续领先&#xff0c;就必须有能力预见未来&#xff0c;适应并驾驭这些变化&#xff0c;这就是定位咨询的…

【ElasticSearch】ES集群搭建、监控、故障转移

文章目录 1、ES集群介绍2、搭建ES集群3、集群状态监控4、集群职责及脑裂5、分布式新增和查询流程6、ES故障转移 1、ES集群介绍 单机的ES做数据存储与搜索&#xff0c;必然面临两个问题&#xff1a; 海量数据存储问题单点故障问题 因此&#xff0c;考虑使用ES集群&#xff1a…

LCD-STM32液晶显示中英文-(5.字符编码)

目录 字符编码 字符编码说明参考网站 字符编码 ASCII编码 ASCII编码介绍 ASCII编码表 中文编码 1. GB2312标准 区位码 2. GBK编码 3. GB18030 各个标准的对比说明 4. Big5编码 字符编码 字符编码说明参考网站 字符编码及转换测试&#xff1a;导航菜单 - 千千秀字 …

学习AJAX

AJAX &#x1f680; HTTP请求报文响应报文 &#x1f684; express框架&#x1f6ac; express基本使用 &#x1f692; 原生AJAX&#x1f6ac; GET.HTML&#x1f6ac; POST.HTML&#x1f6ac; JSON.HTML&#x1f6ac; nodemon工具可以帮助重启服务&#x1f6ac; IE缓存问题&#…

Devops系列五(CI篇之pipeline libraray)jenkins将gitlab helm yaml和argocd 串联,自动部署到K8S

一、说在前面的话 本文是CI篇的上文&#xff0c;因为上一篇已经作了总体设计&#xff0c;就不再赘述&#xff0c;有需要的请看前文。 我们将演示&#xff0c;使用CI工具–jenkins&#xff0c;怎么和CD工具–argocd串联&#xff0c;重点是在Jenkins该怎么做。准备工作和argocd等…

Java springBoot项目报LDAP health check failed

报错内容如下&#xff1a; 在bootstrap.yml文件里加 management:health:ldap:enabled: false 配置。 或者在application.properties文件里加&#xff1a; management.health.ldap.enabledfalse 参考答案&#xff1a;LDAP health check failed 难道没有人遇到这样的问题吗&…

TCP/IP基础知识笔记

应用层&#xff1a;为用户提供应用功能&#xff0c;比如 HTTP、FTP、Telnet、DNS、SMTP等。 应用层是工作在操作系统中的用户态&#xff0c;传输层及以下则工作在内核态。 传输层&#xff1a;为应用层提供网络支持。 *TCP包含众多特性比如流量控制、超时重传、拥塞控制等因此可…

【CPU】关于x86、x86_64/x64、amd64和arm64/aarch64

为什么叫x86和x86_64和AMD64? 为什么大家叫x86为32位系统&#xff1f; 为什么软件版本会注明 for amd64版本&#xff0c;不是intel64呢&#xff1f; x86是指intel的开发的一种32位指令集&#xff0c;从386开始时代开始的&#xff0c;一直沿用至今&#xff0c;是一种cisc指令…

LinkNet分割模型搭建

原论文&#xff1a;LinkNet: Exploiting Encoder Representations for Efficient Semantic Segmentation 直接步入正题~~~ 一、LinkNet 1.decoder模块 class DecoderBlock(nn.Module):def __init__(self, in_channels, n_filters): #512, 256super(DecoderBlock, self).__in…

linux kernel单独编译某项驱动

linux内核经常涉及编译某一项驱动代码的场景&#xff0c;本次以网卡驱动e1000为例说明整个步骤流程。 首先编译内核驱动不必要编译整个内核&#xff0c;但编译的驱动代码必须要和要安装的内核版本保持一致&#xff0c;否则经常会出现无法加载模块。 在编译驱动前&#xff0c;最…

大坝安全监测中需要做好检查监测

大坝安全监测是人们了解大坝运行状态和安全状况的有效手段和方法。它的目的主要是了解大坝安全状况及其发展态势&#xff0c;是一个包括由获取各种环境、水文、结构、安全信息到经过识别、计算、判断等步骤&#xff0c;最终给出一个大坝安全 程度的全过程。 此过程包括&#xf…

layui增删改查的实现

前言 在前三篇layui博客的基础上继续完善&#xff0c;这篇博客增加了数据表格来实现增删改查 这里要注意layui需要使用2.6以上的版本 dao方法的编写 package com.zking.dao;import java.util.List; import java.util.Map;import com.zking.entity.User; import com.zking.uti…

进销不匹配将被严查,增值税高怎么办?

进销不匹配将被严查&#xff0c;增值税高怎么办&#xff1f; 《税筹顾问》专注于园区招商、企业税务筹划&#xff0c;合理合规助力企业节税&#xff01; 金税四期是通过对企业所得税、增值税、个人所得税等各类税种的统一管理&#xff0c;实现对企业财务活动的全面监管和规范&…

探索uni-app:构建跨平台应用的神奇工具

文章目录 &#x1f4da;1. 视图容器类组件⚡ <view>&#xff1a;视图容器&#xff0c;类似于div元素⚡<scroll-view>&#xff1a;可滚动的视图容器 &#x1f4da;2. 基础内容类组件⚡<text>&#xff1a;文本内容&#xff0c;类似于span元素⚡<icon>&am…

单例模式之常见模式详解

单例模式之常见模式详解 单例模式的定义单例模式的分类饿汉模式懒汉模式 单例模式的主要特点单例模式的应用场景总结 单例模式的定义 单例模式是一种设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取该实例。 在单例模式中&#xff0c;类…