日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

笨办法学C 练习28:Makefile 进阶

發布時間:2024/4/13 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 笨办法学C 练习28:Makefile 进阶 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

練習28:Makefile 進階

原文:Exercise 28: Intermediate Makefiles

譯者:飛龍

在下面的三個練習中你會創建一個項目的目錄框架,用于構建之后的C程序。這個目錄框架會在這本書中剩余的章節中使用,并且這個練習中我會涉及到Makefile便于你理解它。

這個結構的目的是,在不憑借配置工具的情況下,使構建中等規模的程序變得容易。如果完成了它,你會學到很多GNU make和一些小型shell腳本方面的東西。

基本的項目結構

首先要做的事情是創建一個C的目錄框架,并且放置一些多續項目都擁有的,基本的文件和目錄。這是我的目錄:

$ mkdir c-skeleton $ cd c-skeleton/ $ touch LICENSE README.md Makefile $ mkdir bin src tests $ cp dbg.h src/ # this is from Ex20 $ ls -l total 8 -rw-r--r-- 1 zedshaw staff 0 Mar 31 16:38 LICENSE -rw-r--r-- 1 zedshaw staff 1168 Apr 1 17:00 Makefile -rw-r--r-- 1 zedshaw staff 0 Mar 31 16:38 README.md drwxr-xr-x 2 zedshaw staff 68 Mar 31 16:38 bin drwxr-xr-x 2 zedshaw staff 68 Apr 1 10:07 build drwxr-xr-x 3 zedshaw staff 102 Apr 3 16:28 src drwxr-xr-x 2 zedshaw staff 68 Mar 31 16:38 tests $ ls -l src total 8 -rw-r--r-- 1 zedshaw staff 982 Apr 3 16:28 dbg.h $

之后你會看到我執行了ls -l,所以你會看到最終結果。

下面是每個文件所做的事情:

LICENSE

如果你在項目中發布源碼,你會希望包含一份協議。如果你不這么多,雖然你有代碼的版權,但是通常沒有人有權使用。

README.md

對你項目的簡要說明。它以.md結尾,所以應該作為Markdown來解析。

Makefile

這個項目的主要構建文件。

bin/

放置可運行程序的地方。這里通常是空的,Makefile會在這里生成程序。

build/

當值庫和其它構建組件的地方。通常也是空的,Makefile會在這里生成這些東西。

src/

放置源碼的地方,通常是.c和.h文件。

tests/

放置自動化測試的地方。

src/dbg.h

我將練習20的dbg.h復制到了這里。

我剛才分解了這個項目框架的每個組件,所以你應該明白它們怎么工作。

Makefile

我要講到的第一件事情就是Makefile,因為你可以從中了解其它東西的情況。這個練習的Makeile比之前更加詳細,所以我會在你輸入它之后做詳細的分解。

CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS) LIBS=-ldl $(OPTLIBS) PREFIX?=/usr/localSOURCES=$(wildcard src/**/*.c src/*.c) OBJECTS=$(patsubst %.c,%.o,$(SOURCES))TEST_SRC=$(wildcard tests/*_tests.c) TESTS=$(patsubst %.c,%,$(TEST_SRC))TARGET=build/libYOUR_LIBRARY.a SO_TARGET=$(patsubst %.a,%.so,$(TARGET))# The Target Build all: $(TARGET) $(SO_TARGET) testsdev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS) dev: all$(TARGET): CFLAGS += -fPIC $(TARGET): build $(OBJECTS)ar rcs $@ $(OBJECTS)ranlib $@$(SO_TARGET): $(TARGET) $(OBJECTS)$(CC) -shared -o $@ $(OBJECTS)build:@mkdir -p build@mkdir -p bin# The Unit Tests .PHONY: tests tests: CFLAGS += $(TARGET) tests: $(TESTS)sh ./tests/runtests.shvalgrind:VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)# The Cleaner clean:rm -rf build $(OBJECTS) $(TESTS)rm -f tests/tests.logfind . -name "*.gc*" -exec rm {} \;rm -rf `find . -name "*.dSYM" -print`# The Install install: allinstall -d $(DESTDIR)/$(PREFIX)/lib/install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/# The Checker BADFUNCS='[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)' check:@echo Files with potentially dangerous functions.@egrep $(BADFUNCS) $(SOURCES) || true

要記住你應該使用一致的Tab字符來縮進Makefile。你的編輯器應該知道怎么做,但是如果不是這樣你可以換個編輯器。沒有程序員會使用一個連如此簡單的事情都做不好的編輯器。

頭部

這個Makefile設計用于構建一個庫,我們之后會用到它,并且通過使用GNU make的特殊特性使它在任何平臺上都可用。我會在這一節拆分它的每一部分,先從頭部開始。

Makefile:1

這是通常的CFLAGS,幾乎每個項目都會設置,但是帶有用于構建庫的其它東西。你可能需要為不同平臺調整它。要注意最后的OPTFLAGS變量可以讓使用者按需擴展構建選項。

Makefile:2

用于鏈接庫的選項,同樣也允許其它人使用OPTFLAGS變量擴展鏈接選項。

Makefile:3

設置一個叫做PREFIX的可選變量,它只在沒有PREFIX設置的平臺上運行Makefile時有效。這就是?=的作用。

Makefile:5

這神奇的一行通過執行wildcard搜索在src/中所有*.c文件來動態創建SOURCES變量。你需要提供src/**/*.c和src/*.c以便GNU make能夠包含src目錄及其子目錄的所有此類文件。

Makefile:6

一旦你創建了源文件列表,你可以使用patsubst命令獲取*.c文件的SOURCES來創建目標文件的新列表。你可以告訴patsubst把所有%.c擴展為%.o,并將它們賦給OBJECTS。

Makefile:8

再次使用wildcard來尋找所有用于單元測試的測試源文件。它們存放在不同的目錄中。

Makefile:9

之后使用相同的patsubst技巧來動態獲得所有TEST目標。其中我去掉了.c后綴,使整個程序使用相同的名字創建。之前我將.c替換為.o來創建目標文件。

Makefile:11

最后,我將最終目標設置為build/libYOUR_LIBRARY.a,你可以為你實際構建的任何庫來修改它。

這就是Makefile的頭部了,但是我應該對“讓其他人擴展構建”做個解釋。你在運行它的時候可以這樣做:

# WARNING! Just a demonstration, won't really work right now. # this installs the library into /tmp $ make PREFIX=/tmp install # this tells it to add pthreads $ make OPTFLAGS=-pthread

如果你傳入匹配Makefile中相同名稱的變量,它們會在構建中生效。你可以利用它來修改Makefile的運行方式。第一條命令改變了PREFIX,使它安裝到/tmp。第二條設置了OPTFLAGS,為之添加了pthread選項。

構建目標

我會繼續Makefile的分解,這一部分用于構建目標文件(object file)和目標(target):

Makefile:14

要記住在沒有提供目標時make會默認運行第一個目標。這里它叫做all:,并且它提供了$(TARGET) tests作為構建目標。查看TARGET變量,你會發現這就是庫文件,所以all:首先會構建出庫文件。之后,tests目標會構建單元測試。

Makefile:16

另一個用于執行“開發者構建”的目標,它介紹了一種為單一目標修改選項的技巧,如果我執行“開發構建”,我希望CFLAGS包含類似Wextra這樣用于發現bug的選項。如果你將它們放到目標的那行中,并再編寫一行來指向原始目標(這里是all),那么它就會將改為你設置的選項。我通常將它用于在不同的平臺上設置所需的不同選項。

Makefile:19

構建TARGET庫,然而它同樣使用了15行的技巧,向一個目標提供選項來為當前目標修改它們。這里我通過適用+=語法為庫的構建添加了-fPIC。

Makefile:20

現在這一真實目標首先創建build目錄,之后編譯所有OBJECTS。

Makefile:21

運行實際創建TARGET的ar的命令。$@ $(OBJECTS)語法的意思是,將當前目標的名稱放在這里,并把OBJECTS的內容放在后面。這里$@的值為19行的$(TARGET),它實際上為build/libYOUR_LIBRARY.a。看起來在這一重定向中它做了很多跟蹤工作,它也有這個功能,并且你可以通過修改頂部的TARGET,來構建一個全新的庫。

Makefile:22

最后,在TARGET上運行ranlib來構建這個庫。

Makefile:24-24

用于在build/和bin/目錄不存在的條件下創建它們。之后它被19行引用,那里提供了build目標來確保build/目錄已創建。

你現在擁有了用于構建軟件的所需的所有東西。之后我們會創建用于構建和運行單元測試的東西,來執行自動化測試。

單元測試

C不同于其他語言,因為它更易于為每個需要測試的東西創建小型程序。一些測試框架試圖模擬其他語言中的模塊概念,并且執行動態加載,但是它在C中并不適用。這也不是必要的,因為你可以僅僅編寫一個程序用于每個測試。

我接下來會涉及到Makefile的這一部分,并且你會看到test/目錄中真正起作用的內容。

Makefile:29

如果你擁有一個不是“真實”的目標,只有有個目錄或者文件叫這個名字,你需要使用g.PHONY:標簽來標記它,以便make忽略該文件。

Makefile:30

我使用了與修改CFLAGS變量相同的技巧,并且將TARGET添加到構建中,于是每個測試程序都會鏈接TARGET庫。這里它會添加build/libYOUR_LIBRARY.a用于鏈接。

Makefile:31

之后我創建了實際的test:目錄,它依賴于所有在TESTS變量中列出的程序。這一行實際上說,“Make,請使用你已知的程序構建方法,以及當前CFLAGS設置的內容來構建TESTS中的每個程序。”

Makefile:32

最后,所有TESTS構建完之后,會運行一個我稍后創建的簡單shell腳本,它知道如何全部運行他們并報告它們的輸出、這一行實際上運行它來讓你看到測試結果。

Makefile:34-35

為了能夠動態使用Valgrind重復運行測試,我創建了valgrind:標簽,它設置了正確的變量并且再次運行它。它會將Valgrind的日志放到/tmp/valgrind-*.log,你可以查看并了解發生了什么。之后tests/runtests.sh看到VALGRIND變量時,它會明白要在Valgrind下運行測試程序。

你需要為單元測試創建一個小型的shell腳本,它知道如何運行程序。我們開始創建這個tests/runtests.sh腳本:

echo "Running unit tests:"for i in tests/*_tests doif test -f $ithenif $VALGRIND ./$i 2>> tests/tests.logthenecho $i PASSelseecho "ERROR in test $i: here's tests/tests.log"echo "------"tail tests/tests.logexit 1fifi doneecho ""

當我提到單元測試如何工作時,我會在之后用到它。

清理工具

我已經有了用于單元測試的工具,所以下一步就是創建需要重置時的清理工具。

Makefile:38

clean:目標在我需要清理這個項目的任何時候都會執行清理。

Makefile:39-42

這會清理不同編譯器和工具留下的多數垃圾。它也會移除build/目錄并且使用了一個技巧來清理XCode為調試目的而留下的*.dSYM。

如果你碰到了想要執行清理的垃圾,你只需要簡單地擴展需要刪除的文件列表。

安裝

然后,我會需要一種安裝項目的方法,對Makefile來說就是把構建出來的庫放到通常的PREFIX目錄下,它通常是/usr/local/lib。

Makefile:45

它會使install:依賴于all:目錄,所以當你運行make install之后也會先確保一切都已構建。

Makefile:46

接下來我使用install程序來創建lib目標的目錄。其中我通過使用兩個為安裝者提供便利的變量,嘗試讓安裝盡可能靈活。DESTDIR交給安裝者,便于在安全或者特定的目錄里執行自己的構建。PREFIX在別人想要將項目安裝到其它目錄而不是/user/local時會被使用。

Makefile:47

在此之后我使用insyall來實際安裝這個庫,到它需要安裝的地方。

install程序的目的是確保這些事情都設置了正確的權限。當你運行make install時你通常使用root權限來執行,所以通常的構建過程應為make && sudo make install。

檢查工具

Makefile的最后一部分是個額外的部分,我把它包含在我的C項目中用于發現任何使用C中“危險”函數的情況。這些函數是字符串函數和另一些“不保護棧”的函數。

Makefile:50

設置變量,它是個稍大的正則表達式,用于檢索類似strcpy的危險函數。

Makefile:51

這是check:目標,使你能夠隨時執行檢查。

Makefile:52

它只是一個打印信息的方式,使用了@echo來告訴make不要打印命令,只需打印輸出。

Makefile:53

對源文件運行egrep命令來尋找任何危險的字符串。最后的|| true是一種方法,用于防止make認為egrep沒有找到任何東西是執行失敗。

當你執行它之后,它會表現得十分奇怪,如果沒有任何危險的函數,你會得到一個錯誤。

你會看到什么

我在完成這個項目框架目錄的構建之前,還設置了兩個額外的練習。下面這是我對Makefile特性的測試結果:

$ make clean rm -rf build rm -f tests/tests.log find . -name "*.gc*" -exec rm {} \; rm -rf `find . -name "*.dSYM" -print` $ make check Files with potentially dangerous functions. ^Cmake: *** [check] Interrupt: 2$ make ar rcs build/libYOUR_LIBRARY.a ar: no archive members specified usage: ar -d [-TLsv] archive file ...ar -m [-TLsv] archive file ...ar -m [-abiTLsv] position archive file ...ar -p [-TLsv] archive [file ...]ar -q [-cTLsv] archive file ...ar -r [-cuTLsv] archive file ...ar -r [-abciuTLsv] position archive file ...ar -t [-TLsv] archive [file ...]ar -x [-ouTLsv] archive [file ...] make: *** [build/libYOUR_LIBRARY.a] Error 1 $ make valgrind VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" make ar rcs build/libYOUR_LIBRARY.a ar: no archive members specified usage: ar -d [-TLsv] archive file ...ar -m [-TLsv] archive file ...ar -m [-abiTLsv] position archive file ...ar -p [-TLsv] archive [file ...]ar -q [-cTLsv] archive file ...ar -r [-cuTLsv] archive file ...ar -r [-abciuTLsv] position archive file ...ar -t [-TLsv] archive [file ...]ar -x [-ouTLsv] archive [file ...] make[1]: *** [build/libYOUR_LIBRARY.a] Error 1 make: *** [valgrind] Error 2 $

當我運行clean:目標時它會生效,但是由于我在src/目錄中并沒有任何源文件,其它命令并沒有真正起作用。我會在下個練習中補完它。

附加題

  • 嘗試通過將源文件和頭文件添加進src/,來使Makefile真正起作用,并且構建出庫文件。在源文件中不應該需要main函數。

  • 研究check:目標會使用BADFUNCS的正則表達式來尋找什么函數。

  • 如果你沒有做過自動化測試,查詢有關資料為以后做準備。

總結

以上是生活随笔為你收集整理的笨办法学C 练习28:Makefile 进阶的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产精品传媒在线 | 国产三级午夜理伦三级 | 台湾佬成人中文网222vvv | 福利社午夜影院 | 黄色av大片| 91直接看| 欧美视频在线一区二区三区 | 欧美一区二区三区四区在线观看 | 欧美又粗又大aaa片 老熟妇仑乱视频一区二区 亚洲妇女体内精汇编 | 亚洲短视频 | 手机看片午夜 | 国产精品变态另类虐交 | 欧美综合色区 | 日韩三级观看 | 超碰日韩| 国产精品xx | 丁香婷婷亚洲 | 一级看片免费视频 | 台湾150部性三级 | 国产欧美日韩成人 | 成人一二三区 | 国产激情在线视频 | 国产一二三四在线 | 淫语对白| 一区二区三区免费网站 | 久久九九久精品国产免费直播 | 精品综合久久久久 | 大号bbwassbigav女 | 国产精品久久婷婷六月丁香 | 国产色在线 | 日本精品99 | 久草不卡 | 国产精品91一区二区 | 亚洲一区二区三区成人 | xxxx久久| 在线麻豆av| 97成人超碰 | 青青青青青青青青草 | 国产欧美日韩在线视频 | 日韩亚洲欧美一区二区三区 | 精品无码久久久久国产 | 国产不卡一区二区视频 | 另类天堂 | 日韩欧美一区二区三区在线观看 | 日韩经典三级 | 超碰97在线资源 | 大片视频免费观看视频 | 日本精品久久久久中文字幕 | 三级黄色生活片 | 久热国产精品视频 | 99视屏| 久久99婷婷 | 欧美性免费 | 色丁香av | 狠狠操夜夜 | 成人福利片| 久久国产成人精品 | 1024视频在线 | 国产在线一区二区视频 | 亚洲黄视频| 国产淫片 | 久久久久久福利 | 日本伦理片在线播放 | 男女激情网址 | 日韩中文字幕在线视频 | 精品人妻一区二区三区四区久久 | 91麻豆精品国产 | 日韩在线免费av | 影音先锋伦理片 | 日韩高清中文字幕 | 天天射夜夜骑 | 国产精品theporn| 欧美激情国产精品免费 | 五月天丁香网 | 最新中文字幕 | 人妻丰满熟妇无码区免费 | 美女av在线免费观看 | caopeng在线 | 奇米视频在线 | 日本免费在线观看视频 | 国产在线看片 | 日韩中文字幕在线不卡 | 九一天堂| 国产免费一区 | 日韩成人久久 | 欧美日韩国产片 | 日日夜夜天天 | 国产三级精品三级 | av丝袜在线 | 这里只有精品22 | 精品一卡二卡 | 超碰av在线 | 日本美女全裸 | 日本一级免费视频 | 亚洲欧洲免费 | 人妻一区二区三区免费 | 爆操91| 女人扒开腿让男人桶爽 | 亚洲视频一区 |