想玩儿github开源,怎能对make、cmake一知半解?

如果要接触源码,就不可避免地要触及 编译(python这种不需要编译的 解释性语言 源码除外!!!)为了完成编译动作,我们有众多的编译工具,很多情况下,编译工具的调用被集成到了 IDE 当中。然而,如果上手一些开源项目,我们会发现,更常见到的是「奇奇怪怪」的 MakeFileCMakeLists.txt。我们参照 ReadMe 的教程,装这个装那个,然后运行同样奇奇怪怪的 makecmake 命令……折腾半天或许成功完成了编译……但脑子是否还是一片混沌?别说定制修改MakeFileCMakeLists.txt,甚至连自己做了些什么都无法记忆起来……Why?

因为我们没理清Ta们为什么存在!

认识 make & cmake,我们先看看可执行文件是如何生成的

一、从 main.c 到 可执行文件 输出 Hello World

我们编辑一份最简单的 main.c 文件(认真上过1节C语言课的同学该都可以看懂),并期望经过编译将其变为可执行文件,然后运行输出Hello World。

#include <stdio.h>

int main(int argc, char * argv[]) {
    printf("\nHello World!\n");
    return 0;
}

Step1:预处理 Preprocess

预处理即将源文件中的宏、头文件进行 ”展开“

参考命令:
gcc -E main.c -o main_preprocess.c
预处理展开

Step2:汇编 Assembly

汇编可以将预处理后的代码转换为汇编语言,看看下面的汇编语言是不是特别「优美」捏!

参考命令:
gcc -S main_preprocess.c
汇编

Step3:生成机器语言

机器语言(二进制命令语言)即计算机可以识别的语言,汇编代码可以进一步转化为机器语言

参考命令:
gcc -c main.s
汇编

Step4:链接

将多个二进制文件(.o文件,虽然当前只有一个main.o)链接成一个文件,根据需求,可能是一个lib,也可能是一个可执行文件。

参考命令:
gcc  main.o -o main
链接

Step5:执行

向世界问好吧!:)

你好世界

二、用gcc、make、cmake编译同一套代码

2.1:使用gcc编译

GCC 是一个linux下的常用的编译工具。我们拟写了如下的源文件,并尝试用 GCC 对齐进行编译:

- ./main.c -

#include "submodule.h"

int main(int argc, char * argv[]) {
    subTest(10);
    return 0;
}
- ./include/submodule.h -

#include <stdio.h>

int subTest(int a);
- ./submodule/submodule.c - 

#include "submodule.h"

int subTest(int a) {
    printf("\n<%s:%d> Function Called... %d \n\n", __func__, __LINE__, a);
    return 1;
}

gcc的命令很简单,只要如下 4条命令 就能完成可执行文件 main 的编译和调用:

# 1 生成subModel的二进制文件(.o)
gcc ./submodule/submodule.c -c -I ./include -o ./submodule.o

# 2 生成main的二进制文件(.o)
gcc ./main.c -c -I ./include -o ./main.o

# 3 链接二进制文件
gcc ./submodule.o ./mian.o -o ./main

# 4 执行可执行文件
./main

2.2 构造MakeFile文件,使用make编译

我们为什么要用MakeFile?如果是为了封装命令,方便调用,我们完全可以将相关的编译命令放置到一个shell脚本中,MakeFile 有什么其他优势呢?

1)它封装一套简单的指定编译目标的语法,这比写shell的参数解析简单得多
2)藉由这套语法,make封装了编译依赖、增量编译等逻辑。即大型工程进行小范围局部改动时候,重新的编译的速度会非常快。(未涉及改动的内容不会重编)

那么,同样的 mainsubmodule,使用 MakeFile 我们可以编辑两个 MakeFile 文件

- ./MakeFile -

INCLUDE_PATH := ./include
SRCS += $(wildcard ./*.c)
OBJS += $(SRCS:.c=.o)

SUB_DIR = ./submodule
SUB_SRCS = $(wildcard ${SUB_DIR}/*.c)
SUB_OBJS += $(SUB_SRCS:.c=.o)

TARGET := main

all: clean build linkobjs
    
linkobjs:
    gcc ${OBJS} ${SUB_OBJS} -o ${TARGET}
    
build:
    cd ${SUB_DIR} && make build
    gcc -c ${SRCS} -I${INCLUDE_PATH}
    
clean:
    cd ${SUB_DIR} && make clean
    rm -rf ${OBJS}
    rm -rf ${TARGET} 
- ./submodule/MakeFile - 

INCLUDE_PATH := ../include
SRCS += $(wildcard ./*.c)
OBJS += $(wildcard ./*.o)

all: clean build

build:
    gcc -c ${SRCS} -I${INCLUDE_PATH}

clean:
    rm -rf ${OBJS}

然后,在 main.c 所在的目录执行 make all 就好啦

编写好MakeFile,执行make all

关于MakeFile,有几个 tips 可能对大家上手有帮助:
1)其完成支持语法和Shell脚本是有些相似的
2)各个编译目标下可以执行 linux 命令
3)编译目标要执行的命令,前面要加4个空格(这个和 python 的函数语法有些相似)
4)示例中的「all : clean build」表示「make all」等同于顺序执行「make clean」「make build」

2.3 构造CMakeLists.txt,使用 cmake 命令生成MakeFile,再make

cmake 定义了另一套语法来组织 CMakeLists.txt 文件,然后通过 cmake 命令可以结合 CMakeLists.txt 文件的”配置“生成 MakeFile,然后再……make……

最终同样是使用MakeFile,干嘛加一步再让大家学习cmake的语法呢?

原来,不同平台(linux、Windows、Macos……)的编译环境是有差异的,为了应对这种差异,各平台编译所需的 MakeFile 文件也各不相同。而 cmake 抽象了一套上层的编译配置语法,并负责了将Ta针对平台进行 MakeFile 文件翻译的任务。

还是同样的 mainsubmodule,使用 cmake 我们将构造两个 CMakeLists.txt 文件:

- ./CMakeLists.txt -

# cmake最低版本约定
cmake_minimum_required(VERSION 2.8)

# 工程名称
project(main)

# 宏开关
option(DT "Demo Test Switch" OFF)
if(DT)
add_definitions("-DDEMO_TEST=1")
endif()

# include目录
include_directories(./include)

# 子模块文件目录
add_subdirectory(./submodule)

# 查找当前文件夹源文件
aux_source_directory(. SRCS)

# 生成可执行文件
add_executable(main ${SRCS})

# 可执行文件链接静态库
target_link_libraries(main submodule)

- ./submodule/CMakeLists.txt - 

# cmake最低版本约定
cmake_minimum_required(VERSION 2.8)

# include目录
include_directories(../include)

# 查找当前文件夹源文件
aux_source_directory(. SRCS)

# 生成静态链接库
add_library(submodule ${SRCS})

然后,我们创建一个 build 文件夹,并进行 cmake

mkdir build
cd build
cmake ../

build 目录下回生成一系列文件,我们可以理解Ta们都是为了支持 Makefile 存在的就好。👇

cmake生成的MakeFile

那么,在 build 下执行 make 吧!

make

成功编译出我们的目标。👇

目标出现了

有没有发现 cmake 的另一点「优雅」:Ta能将所有的编译信息有效地管理在一个文件夹下!当我们想清理编译数据时,只需要删除build文件夹就好了。

三、草草结束

相关的示例代码我放在了 这里
然后看看之前在 github 上遇到的 MakefileCMakeLists.txt 文件,现在能看懂一些了嘛?
还有问题也可以留言交流哦~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335