如果要接觸源碼,就不可避免地要觸及 編譯(python這種不需要編譯的 解釋性語言 源碼除外V及_中馈!)為了完成編譯動作轨帜,我們有眾多的編譯工具魄咕,很多情況下,編譯工具的調(diào)用被集成到了 IDE 當(dāng)中蚌父。然而哮兰,如果上手一些開源項目,我們會發(fā)現(xiàn)梢什,更常見到的是「奇奇怪怪」的 MakeFile 和 CMakeLists.txt奠蹬。我們參照 ReadMe 的教程朝聋,裝這個裝那個嗡午,然后運行同樣奇奇怪怪的 make、cmake 命令……折騰半天或許成功完成了編譯……但腦子是否還是一片混沌冀痕?別說定制修改MakeFile和CMakeLists.txt荔睹,甚至連自己做了些什么都無法記憶起來……Why?
因為我們沒理清Ta們?yōu)槭裁创嬖冢?/em>
認(rèn)識 make & cmake,我們先看看可執(zhí)行文件是如何生成的
一言蛇、從 main.c 到 可執(zhí)行文件 輸出 Hello World
我們編輯一份最簡單的 main.c 文件(認(rèn)真上過1節(jié)C語言課的同學(xué)該都可以看懂)僻他,并期望經(jīng)過編譯將其變?yōu)榭蓤?zhí)行文件,然后運行輸出Hello World腊尚。
#include <stdio.h>
int main(int argc, char * argv[]) {
printf("\nHello World!\n");
return 0;
}
Step1:預(yù)處理 Preprocess
預(yù)處理即將源文件中的宏吨拗、頭文件進(jìn)行 ”展開“。
參考命令:
gcc -E main.c -o main_preprocess.c
Step2:匯編 Assembly
匯編可以將預(yù)處理后的代碼轉(zhuǎn)換為匯編語言婿斥,看看下面的匯編語言是不是特別「優(yōu)美」捏劝篷!
參考命令:
gcc -S main_preprocess.c
Step3:生成機(jī)器語言
機(jī)器語言(二進(jìn)制命令語言)即計算機(jī)可以識別的語言,匯編代碼可以進(jìn)一步轉(zhuǎn)化為機(jī)器語言
參考命令:
gcc -c main.s
Step4:鏈接
將多個二進(jìn)制文件(.o文件民宿,雖然當(dāng)前只有一個main.o)鏈接成一個文件娇妓,根據(jù)需求,可能是一個lib活鹰,也可能是一個可執(zhí)行文件哈恰。
參考命令:
gcc main.o -o main
Step5:執(zhí)行
向世界問好吧V还馈:)
二、用gcc着绷、make蛔钙、cmake編譯同一套代碼
2.1:使用gcc編譯
GCC 是一個linux下的常用的編譯工具。我們擬寫了如下的源文件荠医,并嘗試用 GCC 對齊進(jìn)行編譯:
- ./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條命令 就能完成可執(zhí)行文件 main 的編譯和調(diào)用:
# 1 生成subModel的二進(jìn)制文件(.o)
gcc ./submodule/submodule.c -c -I ./include -o ./submodule.o
# 2 生成main的二進(jìn)制文件(.o)
gcc ./main.c -c -I ./include -o ./main.o
# 3 鏈接二進(jìn)制文件
gcc ./submodule.o ./mian.o -o ./main
# 4 執(zhí)行可執(zhí)行文件
./main
2.2 構(gòu)造MakeFile文件,使用make編譯
我們?yōu)槭裁匆?strong>MakeFile子漩?如果是為了封裝命令豫喧,方便調(diào)用,我們完全可以將相關(guān)的編譯命令放置到一個shell腳本中幢泼,MakeFile 有什么其他優(yōu)勢呢紧显?
1)它封裝一套簡單的指定編譯目標(biāo)的語法,這比寫shell的參數(shù)解析簡單得多
2)藉由這套語法缕棵,make封裝了編譯依賴孵班、增量編譯等邏輯。即大型工程進(jìn)行小范圍局部改動時候招驴,重新的編譯的速度會非掣莩蹋快。(未涉及改動的內(nèi)容不會重編)
那么别厘,同樣的 main 和 submodule虱饿,使用 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 所在的目錄執(zhí)行 make all 就好啦
關(guān)于MakeFile氮发,有幾個 tips 可能對大家上手有幫助:
1)其完成支持語法和Shell腳本是有些相似的
2)各個編譯目標(biāo)下可以執(zhí)行 linux 命令
3)編譯目標(biāo)要執(zhí)行的命令,前面要加4個空格(這個和 python 的函數(shù)語法有些相似)
4)示例中的「all : clean build」表示「make all」等同于順序執(zhí)行「make clean」「make build」
2.3 構(gòu)造CMakeLists.txt冗懦,使用 cmake 命令生成MakeFile爽冕,再make
cmake 定義了另一套語法來組織 CMakeLists.txt 文件,然后通過 cmake 命令可以結(jié)合 CMakeLists.txt 文件的”配置“生成 MakeFile披蕉,然后再……make……
最終同樣是使用MakeFile颈畸,干嘛加一步再讓大家學(xué)習(xí)cmake的語法呢?
原來没讲,不同平臺(linux眯娱、Windows、Macos……)的編譯環(huán)境是有差異的食零,為了應(yīng)對這種差異困乒,各平臺編譯所需的 MakeFile 文件也各不相同。而 cmake 抽象了一套上層的編譯配置語法贰谣,并負(fù)責(zé)了將Ta針對平臺進(jìn)行 MakeFile 文件翻譯的任務(wù)娜搂。
還是同樣的 main 和 submodule迁霎,使用 cmake 我們將構(gòu)造兩個 CMakeLists.txt 文件:
- ./CMakeLists.txt -
# cmake最低版本約定
cmake_minimum_required(VERSION 2.8)
# 工程名稱
project(main)
# 宏開關(guān)
option(DT "Demo Test Switch" OFF)
if(DT)
add_definitions("-DDEMO_TEST=1")
endif()
# include目錄
include_directories(./include)
# 子模塊文件目錄
add_subdirectory(./submodule)
# 查找當(dāng)前文件夾源文件
aux_source_directory(. SRCS)
# 生成可執(zhí)行文件
add_executable(main ${SRCS})
# 可執(zhí)行文件鏈接靜態(tài)庫
target_link_libraries(main submodule)
- ./submodule/CMakeLists.txt -
# cmake最低版本約定
cmake_minimum_required(VERSION 2.8)
# include目錄
include_directories(../include)
# 查找當(dāng)前文件夾源文件
aux_source_directory(. SRCS)
# 生成靜態(tài)鏈接庫
add_library(submodule ${SRCS})
然后,我們創(chuàng)建一個 build 文件夾百宇,并進(jìn)行 cmake
mkdir build
cd build
cmake ../
build 目錄下回生成一系列文件考廉,我們可以理解Ta們都是為了支持 Makefile 存在的就好。??
那么携御,在 build 下執(zhí)行 make 吧昌粤!
make
成功編譯出我們的目標(biāo)。??
有沒有發(fā)現(xiàn) cmake 的另一點「優(yōu)雅」:Ta能將所有的編譯信息有效地管理在一個文件夾下啄刹!當(dāng)我們想清理編譯數(shù)據(jù)時涮坐,只需要刪除build文件夾就好了。
三誓军、草草結(jié)束
相關(guān)的示例代碼我放在了 這里
然后看看之前在 github 上遇到的 Makefile袱讹、CMakeLists.txt 文件,現(xiàn)在能看懂一些了嘛昵时?
還有問題也可以留言交流哦~