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中的内容。
C++服务器框架开发11——编译调试1/cmake学习_第1张图片

ubuntu下的cmake学习

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

简单样例

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

#include 

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。
执行过程:
C++服务器框架开发11——编译调试1/cmake学习_第2张图片
执行后目录下的文件:
C++服务器框架开发11——编译调试1/cmake学习_第3张图片
执行make进行编译。
执行过程:
在这里插入图片描述
执行后目录下多了一个main的可执行文件。
C++服务器框架开发11——编译调试1/cmake学习_第4张图片
运行main看看效果:
C++服务器框架开发11——编译调试1/cmake学习_第5张图片
如果想重新生成,可以先执行make clean来删除已有的main文件。

同一目录下多个源文件

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

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

#include 

#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 
#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:
C++服务器框架开发11——编译调试1/cmake学习_第6张图片
运行:
在这里插入图片描述
如果有很多源文件,这种方法就不太方便。可以使用aux_source_directory(dir var),这个命令将目录dir下的所有源文件存储在变量var中。
例子:
C++服务器框架开发11——编译调试1/cmake学习_第7张图片
main.c:

#include 

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

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

	return 0;
}

testFunc1.c:

/*
** testFunc1.c
*/

#include 
#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,并执行:
C++服务器框架开发11——编译调试1/cmake学习_第8张图片
如果不想把所有源文件都进行编译,可以用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})

不同目录下多个源文件

假设文件目录如下:
C++服务器框架开发11——编译调试1/cmake学习_第9张图片
修改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:可执行文件。
结构调整如下:
C++服务器框架开发11——编译调试1/cmake学习_第10张图片
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
C++服务器框架开发11——编译调试1/cmake学习_第11张图片
在这里插入图片描述
另一种方法,可以把子目录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,最后执行。
C++服务器框架开发11——编译调试1/cmake学习_第12张图片

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

目录结构如下
C++服务器框架开发11——编译调试1/cmake学习_第13张图片
把生成的库放在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
C++服务器框架开发11——编译调试1/cmake学习_第14张图片
查看子目录lib
C++服务器框架开发11——编译调试1/cmake学习_第15张图片

对库进行链接

重新构建如下工程,并把上一小节的生成的库放到./testFunc/lib下。
C++服务器框架开发11——编译调试1/cmake学习_第16张图片
main.c:

#include 

#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,最后执行。
C++服务器框架开发11——编译调试1/cmake学习_第17张图片

查看可执行文件使用了那些库readelf -d ../bin/main
C++服务器框架开发11——编译调试1/cmake学习_第18张图片

添加编译选项

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

添加控制选项

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

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

如下结构:
C++服务器框架开发11——编译调试1/cmake学习_第19张图片
main1.c:

// main1.c
#include 

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

main2.c:

// main2.c
#include 

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目录。
C++服务器框架开发11——编译调试1/cmake学习_第20张图片

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

结构如下:
C++服务器框架开发11——编译调试1/cmake学习_第21张图片
main.c:

#include 

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,最后执行。
C++服务器框架开发11——编译调试1/cmake学习_第22张图片
只想编译WWW2里的内容:
build目录下cmake ../ -DWWW1=OFF -DWWW2=ON, 然后make,最后执行。
C++服务器框架开发11——编译调试1/cmake学习_第23张图片
编译WWW1和WWW2里的内容:
build目录下cmake ../ -DWWW1=ON -DWWW2=ON, 然后make,最后执行。
C++服务器框架开发11——编译调试1/cmake学习_第24张图片
:每次重新编译时,记得把build和bin中的文件删掉,否则build中的文件会影响下次编译,比如我第一次编译设置了WWW1,如果我没有删除build中文件,那么如果我第二次编译时没设置WWW1,WWW1将是上次编译时的设置。

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

回顾图1

C++服务器框架开发11——编译调试1/cmake学习_第25张图片
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原先根本不会,三是这个笔记有点长。

你可能感兴趣的:(分布式服务器开发,c++,服务器,学习)