C实战:项目构建Make,Automake,CMake
C實戰:項目構建Make,Automake,CMake
在本系列文章《C實戰:強大的程序調試工具GDB》中我們簡要學習了流行的調試工具GDB的使用方法。本文繼續“C實戰”的主題,對同樣非常流行的構建工具Make的用法和原理一探究竟,并順便看一下一些高級衍生產品。
1.Make基礎
首先我們編寫一個簡單的C項目,以此項目在實戰中學習Make的相關知識。更全面的介紹請參考官方手冊。
cdai@vm /syspace/2-ccpp/24-pragmatic/build-tool/make $ tree . ├── hello.c ├── hello.h ├── main.c └── Makefile0 directories, 4 files整個程序的邏輯非常簡單:main.c中包含一個main方法,調用了hello.c中的sayHello()函數,打印了一句話到控制臺上。
// 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 基本語法
一個簡單的Makefile包含很多規則(Rule),每一條規則的語法結構由目標(Target)、先決條件(Prerequisite)、動作(Recipe)三部分組成:
- 目標:通常有兩種命名方法,一是與要生成的可執行文件或目標文件同名,二是說明動作的目的,例如最常見的clean清理規則。對于第二種規則命名,為了避免與同名文件沖突,可以將目標名加入到.PHONY偽目標列表中。默認情況下,make執行Makefile中的第一個規則,此規則被稱為最終目標
- 先決條件:先決條件是用來創建目標的輸入文件,一個目標可以依賴多個先決條件
- 動作:動作由Make命令負責執行,可以包含多個命令,每個命令可以另起一行。一定要注意的是:命令必須以TAB開頭!
下面就看一下示例項目的Makefile是什么樣子的。在Makefile中有3個規則,其中目標main依賴于main.o和hello.o,利用gcc執行鏈接,這與我們的代碼結構是相對應的。而main.o和hello.o則分別依賴于各自的源代碼.c文件和hello.h頭文件,并利用gcc -c執行編譯。
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 實現原理
Make看似非常智能,其實它的原理就像其語法規則一樣簡單。
4.1 先決條件不存在,則執行規則中的命令
4.2 先決條件存在,且至少一個比目標“更新”,則執行規則中的命令重新生成
4.3 先決條件存在,且都比目標“更舊”, 則什么都不做
了解了Make的原理,就看一下我們的示例項目Make的執行過程。可以看到,Make以第一個目標main作為構建目標,從關系鏈底部的main.o和hello.o開始構建,最終生成了可執行文件main。接下來就執行main,可以看到控制臺輸出了”Hello, Make!”,證明我們構建成功了!
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!再次執行make會看到“‘main’ is up to date.”的提示,說明Make檢測到了沒有發生任何修改。如果我們做一點改動,例如修改以下sayHello()函數中的輸出,再執行Make就能看到hello.o和main被重新構建,而main.o規則的命令沒有被執行。
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進階
2.1 變量
在Makefile中,我們可以用變量來替換重復出現在先決條件或動作中的字符串。例如,對于前面我們的示例Makefile,最明顯的問題就是gcc和main目標依賴的main.o和hello.o出現了多次,我們可以用變量將它們提取出來。同樣地,我們也經常將鏈接和編譯選項做成變量。
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執行一下make可以看到效果,我們提取出來的變量在執行之前都被替換到了正確的位置。
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 隱式規則
使用Make編譯.c源文件時,規則的命令和先決條件都可以簡化,對于命令,我們不用明確指出,Make能夠自動將.c編譯成.o;對于先決條件,Make還會自動尋找.o對應的.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的規則都做了簡化,執行一下可以看到Make自動執行了cc -c,并根據目標找到了對應的源文件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 模式規則
隱式規則雖然很方便,但有時我們還想自己控制規則,這時我們可以使用模式規則。老Make支持.c.o這種規則定義,而新Make一般推薦使用模式規則,因為它支持模式匹配,更靈活、更強大!例如,我們定義目標名匹配%.o和先決條件匹配%.c的話,就執行編譯命令。這樣main.o和hello.o被簡化的同時,我們還對其進行了精確的控制。
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執行一下看看效果。
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的流行也帶動起一批自動生成Makefile的工具,目的就是進一步減輕項目構建中的工作量,讓我們程序員全身心投入到開發之中。在這些工具中,不得不提Automake和CMake。
3.1 Automake
Automake其實是一系列工具集Autotools中的一員,要想發揮Automake的威力,需要配合使用Autotools中的其他工具,例如autoscan、aclocal、autoconf和autoheader。在下面的Automake構建流程中,能看到這些工具的身影。
這樣Makefile就生成好了,看一下當前目錄發現已經這么多文件了!如果想清理一下怎么辦呢?其實Automake早為我們想好了,它生成的Makefile功能很多:
+ make:編譯源代碼,生成目標文件
+ make clean:清理之前make產生的臨時文件
+ make install:將編譯好的可執行文件安裝到系統目錄,一般為/usr/local/bin
+ make dist:生成軟件發布包,將可執行文件及相關文件打包成”PACKAGE-VERSION.tar.gz”的tarball。其中PACKAGE和VERSION可以在configure.in中通過AM_INIT_AUTOMAKE(PACKAGE, VERSION)定義。對于我們的例子,執行后會生成main-1.0.tar.gz
+ make distcheck:查看發布包是否正確,解壓開執行configure和make來確認
+ make distclean:不僅將make產生的文件,同時將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
前面我們已經見識了Automake的強大和復雜。現在我們重新用CMake生成Makefile,Automake中的9步被壓縮到了只需要2步!
3.2.1 CMakeLists.txt
對于我們示例中這種簡單的項目,CMakeLists.txt簡單得不能再簡單了。指定好項目名稱和最終生成的可執行文件名稱后,就完成了!
# CMake 最低版本號要求 cmake_minimum_required (VERSION 2.8)# 項目信息 project (main)# 查找當前目錄下的所有源文件 # 并將名稱保存到 DIR_SRCS 變量 aux_source_directory(. DIR_SRCS)# 指定生成目標 add_executable(main ${DIR_SRCS})3.2.2 cmake
現在執行cmake .就能得到一個CMake為我們自動生成的Makefile。這個Makefile比我們手寫的要復雜得多,這里就不深入分析了。除了Makefile外,CMake還產生了一些緩存文件和臨時文件,目前還不清楚具體是做什么的。
[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附:參考資料
轉載于:https://www.cnblogs.com/xiaomaohai/p/6157594.html
總結
以上是生活随笔為你收集整理的C实战:项目构建Make,Automake,CMake的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为了使界面组件更圆滑,Swing,且跨系
- 下一篇: Oracle Partition Out