C实战:项目构建Make,Automake,CMake
C實(shí)戰(zhàn):項(xiàng)目構(gòu)建Make,Automake,CMake
在本系列文章《C實(shí)戰(zhàn):強(qiáng)大的程序調(diào)試工具GDB》中我們簡要學(xué)習(xí)了流行的調(diào)試工具GDB的使用方法。本文繼續(xù)“C實(shí)戰(zhàn)”的主題,對(duì)同樣非常流行的構(gòu)建工具M(jìn)ake的用法和原理一探究竟,并順便看一下一些高級(jí)衍生產(chǎn)品。
1.Make基礎(chǔ)
首先我們編寫一個(gè)簡單的C項(xiàng)目,以此項(xiàng)目在實(shí)戰(zhàn)中學(xué)習(xí)Make的相關(guān)知識(shí)。更全面的介紹請(qǐng)參考官方手冊(cè)。
cdai@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ tree . ├── hello.c ├── hello.h ├── main.c └── Makefile0 directories, 4 files整個(gè)程序的邏輯非常簡單:main.c中包含一個(gè)main方法,調(diào)用了hello.c中的sayHello()函數(shù),打印了一句話到控制臺(tái)上。
// cat main.c hello.h hello.c // ----- main.c ----- #include "hello.h"int main(void) {sayHello("Make");return 1; }// ----- hello.h ----- #ifndef _HELLO_H_ #define _HELLO_H_void sayHello(char *name);#endif// ----- hello.c ----- #include "hello.h" #include <stdio.h>void sayHello(char *name) {printf("Hello, %s!\n", name); }1.1 基本語法
一個(gè)簡單的Makefile包含很多規(guī)則(Rule),每一條規(guī)則的語法結(jié)構(gòu)由目標(biāo)(Target)、先決條件(Prerequisite)、動(dòng)作(Recipe)三部分組成:
- 目標(biāo):通常有兩種命名方法,一是與要生成的可執(zhí)行文件或目標(biāo)文件同名,二是說明動(dòng)作的目的,例如最常見的clean清理規(guī)則。對(duì)于第二種規(guī)則命名,為了避免與同名文件沖突,可以將目標(biāo)名加入到.PHONY偽目標(biāo)列表中。默認(rèn)情況下,make執(zhí)行Makefile中的第一個(gè)規(guī)則,此規(guī)則被稱為最終目標(biāo)
- 先決條件:先決條件是用來創(chuàng)建目標(biāo)的輸入文件,一個(gè)目標(biāo)可以依賴多個(gè)先決條件
- 動(dòng)作:動(dòng)作由Make命令負(fù)責(zé)執(zhí)行,可以包含多個(gè)命令,每個(gè)命令可以另起一行。一定要注意的是:命令必須以TAB開頭!
下面就看一下示例項(xiàng)目的Makefile是什么樣子的。在Makefile中有3個(gè)規(guī)則,其中目標(biāo)main依賴于main.o和hello.o,利用gcc執(zhí)行鏈接,這與我們的代碼結(jié)構(gòu)是相對(duì)應(yīng)的。而main.o和hello.o則分別依賴于各自的源代碼.c文件和hello.h頭文件,并利用gcc -c執(zhí)行編譯。
main: main.o hello.ogcc -o main main.o hello.omain.o: main.c hello.hgcc -c main.c -o main.ohello.o: hello.c hello.hgcc -c hello.c -o hello.o1.2 實(shí)現(xiàn)原理
Make看似非常智能,其實(shí)它的原理就像其語法規(guī)則一樣簡單。
4.1 先決條件不存在,則執(zhí)行規(guī)則中的命令
4.2 先決條件存在,且至少一個(gè)比目標(biāo)“更新”,則執(zhí)行規(guī)則中的命令重新生成
4.3 先決條件存在,且都比目標(biāo)“更舊”, 則什么都不做
了解了Make的原理,就看一下我們的示例項(xiàng)目Make的執(zhí)行過程。可以看到,Make以第一個(gè)目標(biāo)main作為構(gòu)建目標(biāo),從關(guān)系鏈底部的main.o和hello.o開始構(gòu)建,最終生成了可執(zhí)行文件main。接下來就執(zhí)行main,可以看到控制臺(tái)輸出了”Hello, Make!”,證明我們構(gòu)建成功了!
cdai@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ make gcc -c main.c -o main.o gcc -c hello.c -o hello.o gcc -o main main.o hello.ocdai@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ ./main Hello, Make!再次執(zhí)行make會(huì)看到“‘main’ is up to date.”的提示,說明Make檢測到了沒有發(fā)生任何修改。如果我們做一點(diǎn)改動(dòng),例如修改以下sayHello()函數(shù)中的輸出,再執(zhí)行Make就能看到hello.o和main被重新構(gòu)建,而main.o規(guī)則的命令沒有被執(zhí)行。
cdai@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ make make: 'main' is up to date.cdai@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ make gcc -c hello.c -o hello.o gcc -o main main.o hello.ocdai@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ ./main Hello111, Make!2.Make進(jìn)階
2.1 變量
在Makefile中,我們可以用變量來替換重復(fù)出現(xiàn)在先決條件或動(dòng)作中的字符串。例如,對(duì)于前面我們的示例Makefile,最明顯的問題就是gcc和main目標(biāo)依賴的main.o和hello.o出現(xiàn)了多次,我們可以用變量將它們提取出來。同樣地,我們也經(jīng)常將鏈接和編譯選項(xiàng)做成變量。
LD = gcc CC = gcc CFLAGS = -Wall OBJECTS = main.o hello.oall: mainmain: $(OBJECTS)$(LD) -o main $(OBJECTS)main.o: main.c hello.h$(CC) $(CFLAGS) -c $< -o $@hello.o: hello.c hello.h$(CC) $(CFLAGS) -c hello.c -o hello.o執(zhí)行一下make可以看到效果,我們提取出來的變量在執(zhí)行之前都被替換到了正確的位置。
cdaih@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ make gcc -Wall -c main.c -o main.o gcc -Wall -c hello.c -o hello.o gcc -o main main.o hello.o2.2 隱式規(guī)則
使用Make編譯.c源文件時(shí),規(guī)則的命令和先決條件都可以簡化,對(duì)于命令,我們不用明確指出,Make能夠自動(dòng)將.c編譯成.o;對(duì)于先決條件,Make還會(huì)自動(dòng)尋找.o對(duì)應(yīng)的.c源文件,我們只需給出頭文件即可。
LD = gcc OBJECTS = main.o hello.oall: mainmain: $(OBJECTS)$(LD) -o main $(OBJECTS)main.o: hello.h hello.o: hello.h我們將main.o和hell.o的規(guī)則都做了簡化,執(zhí)行一下可以看到Make自動(dòng)執(zhí)行了cc -c,并根據(jù)目標(biāo)找到了對(duì)應(yīng)的源文件main.c和hello.c。
cdaih@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ make cc -c -o main.o main.c cc -c -o hello.o hello.c gcc -o main main.o hello.o2.3 模式規(guī)則
隱式規(guī)則雖然很方便,但有時(shí)我們還想自己控制規(guī)則,這時(shí)我們可以使用模式規(guī)則。老Make支持.c.o這種規(guī)則定義,而新Make一般推薦使用模式規(guī)則,因?yàn)樗С帜J狡ヅ?#xff0c;更靈活、更強(qiáng)大!例如,我們定義目標(biāo)名匹配%.o和先決條件匹配%.c的話,就執(zhí)行編譯命令。這樣main.o和hello.o被簡化的同時(shí),我們還對(duì)其進(jìn)行了精確的控制。
LD = gcc CC = gcc CFLAGS = -Wall OBJECTS = main.o hello.o%.o: %.c$(CC) $(CFLAGS) -c $< -o $@all: mainmain: $(OBJECTS)$(LD) -o main $(OBJECTS)main.o: main.c hello.h hello.o: hello.c hello.h執(zhí)行一下看看效果。
cdaih@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ make gcc -Wall -c main.c -o main.o gcc -Wall -c hello.c -o hello.o gcc -o main main.o hello.o3.Makefile生成工具
Make的流行也帶動(dòng)起一批自動(dòng)生成Makefile的工具,目的就是進(jìn)一步減輕項(xiàng)目構(gòu)建中的工作量,讓我們程序員全身心投入到開發(fā)之中。在這些工具中,不得不提Automake和CMake。
3.1 Automake
Automake其實(shí)是一系列工具集Autotools中的一員,要想發(fā)揮Automake的威力,需要配合使用Autotools中的其他工具,例如autoscan、aclocal、autoconf和autoheader。在下面的Automake構(gòu)建流程中,能看到這些工具的身影。
這樣Makefile就生成好了,看一下當(dāng)前目錄發(fā)現(xiàn)已經(jīng)這么多文件了!如果想清理一下怎么辦呢?其實(shí)Automake早為我們想好了,它生成的Makefile功能很多:
+ make:編譯源代碼,生成目標(biāo)文件
+ make clean:清理之前make產(chǎn)生的臨時(shí)文件
+ make install:將編譯好的可執(zhí)行文件安裝到系統(tǒng)目錄,一般為/usr/local/bin
+ make dist:生成軟件發(fā)布包,將可執(zhí)行文件及相關(guān)文件打包成”PACKAGE-VERSION.tar.gz”的tarball。其中PACKAGE和VERSION可以在configure.in中通過AM_INIT_AUTOMAKE(PACKAGE, VERSION)定義。對(duì)于我們的例子,執(zhí)行后會(huì)生成main-1.0.tar.gz
+ make distcheck:查看發(fā)布包是否正確,解壓開執(zhí)行configure和make來確認(rèn)
+ make distclean:不僅將make產(chǎn)生的文件,同時(shí)將configure生成的文件也都刪除,包括Makefile
測試一下,看看Automake生成的Makefile是否能正常工作。
[root@vm automaketest]# make make all-am make[1]: Entering directory '/root/Temp/automaketest' gcc -DHAVE_CONFIG_H -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c mv -f .deps/main.Tpo .deps/main.Po gcc -DHAVE_CONFIG_H -I. -g -O2 -MT hello.o -MD -MP -MF .deps/hello.Tpo -c -o hello.o hello.c mv -f .deps/hello.Tpo .deps/hello.Po gcc -g -O2 -o main main.o hello.o make[1]: Leaving directory '/root/Temp/automaketest'[root@vm automaketest]# ./main Hello, Make!3.2 CMake
前面我們已經(jīng)見識(shí)了Automake的強(qiáng)大和復(fù)雜。現(xiàn)在我們重新用CMake生成Makefile,Automake中的9步被壓縮到了只需要2步!
3.2.1 CMakeLists.txt
對(duì)于我們示例中這種簡單的項(xiàng)目,CMakeLists.txt簡單得不能再簡單了。指定好項(xiàng)目名稱和最終生成的可執(zhí)行文件名稱后,就完成了!
# CMake 最低版本號(hào)要求 cmake_minimum_required (VERSION 2.8)# 項(xiàng)目信息 project (main)# 查找當(dāng)前目錄下的所有源文件 # 并將名稱保存到 DIR_SRCS 變量 aux_source_directory(. DIR_SRCS)# 指定生成目標(biāo) add_executable(main ${DIR_SRCS})3.2.2 cmake
現(xiàn)在執(zhí)行cmake .就能得到一個(gè)CMake為我們自動(dòng)生成的Makefile。這個(gè)Makefile比我們手寫的要復(fù)雜得多,這里就不深入分析了。除了Makefile外,CMake還產(chǎn)生了一些緩存文件和臨時(shí)文件,目前還不清楚具體是做什么的。
[root@vm cmaketest]# cmake . -- The C compiler identification is GNU 4.4.7 -- The CXX compiler identification is GNU 4.4.7 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Configuring done -- Generating done -- Build files have been written to: /root/Temp/cmaketest[root@vm cmaketest]# tree -L 1 . ├── CMakeCache.txt ├── CMakeFiles ├── cmake_install.cmake ├── CMakeLists.txt ├── hello.c ├── hello.h ├── main.c └── Makefile1 directory, 7 files[root@vm cmaketest]# make Scanning dependencies of target main [ 50%] Building C object CMakeFiles/main.dir/main.c.o [100%] Building C object CMakeFiles/main.dir/hello.c.o Linking C executable main [100%] Built target main附:參考資料
轉(zhuǎn)載于:https://www.cnblogs.com/xiaomaohai/p/6157594.html
總結(jié)
以上是生活随笔為你收集整理的C实战:项目构建Make,Automake,CMake的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为了使界面组件更圆滑,Swing,且跨系
- 下一篇: Oracle Partition Out