萬能makefile寫法詳解,一步一步寫一個實用的makefile,詳解 sed 's,\($*\)\.o[ :]*,\1.o [emai
一
目的:編寫一個實用的makefile,能自動編譯當前目錄下所有.c/.cpp原始檔,支援二者混合編譯。並且當某個.c/.cpp、.h或依賴的原始檔被修改後,僅重編涉及到的原始檔,未涉及的不編譯。
二
要達到這個目的,用到的技術有:
1-使用wildcard函式來獲得當前目錄下所有.c/.cpp檔案的列表。
2-make的多目標規則。
3-make的模式規則。
4-用gcc -MM命令得到一個.c/.cpp檔案include了哪些檔案。
5-用sed命令對gcc -MM命令的結果作修改。
6-用include命令包含依賴描述檔案.d。
三 準備知識
(一)多目標
對makefile裡下面2行,可看出多目標特徵,執行make bigoutput或make littleoutput可看到結果:
bigoutput littleoutput: defs.h pub.h
@echo [email protected] $(subst output,OUTPUT,[email protected]) $^ # [email protected]指這個規則裡所有目標的集合,$^指這個規則裡所有依賴的集合。該行是把目標(bigoutput或littleoutput)裡所有子串output替換成大寫的OUTPUT
(二)隱含規則
對makefile裡下面4行,可看出make的隱含規則,執行foo可看到結果:
第3、4行表示由.c得到.o,第1、2行表示由.o得到可執行檔案。
如果把第3、4行註釋的話,效果一樣。
即不寫.o來自.c的規則,它會自動執行gcc -c -o foo.o foo.c這條命令,由.c編譯出.o(其中-c表示只編譯不連結),然後自動執行gcc -o foo foo.o連結為可執行檔案。
foo:foo.o
gcc -o foo foo.o; ./foo
foo.o:foo.c #註釋該行看效果
gcc -c foo.c -o foo.o #註釋該行看效果
(三)定義模式規則
下面定義了一個模式規則,即如何由.c檔案生成.d檔案的規則。
foobar: foo.d bar.d
@echo complete generate foo.d and bar.d
%.d: %.c #make會對當前目錄下每個.c檔案,依次做一次裡面的命令,從而由每個.c檔案生成對應.d檔案。
@echo from $< to [email protected]
g++ -MM $< > [email protected]
假定當前目錄下有2個.c檔案:foo.c和bar.c(檔案內容隨意)。
驗證方法有2種,都可:
1-執行make foo.d(或make bar.d),表示想要生成foo.d這個目標。
根據規則%.d: %.c,這時%匹配foo,這樣%.c等於foo.c,即foo.d這個目標依賴於foo.c。
此時會自動執行該規則裡的命令gcc -MM foo.c > foo.d,來生成foo.d這個目標。
2-執行make foobar,因為foobar依賴於foo.d和bar.d這2個檔案,即會一次性生成這2個檔案。
四
下面詳述如何自動生成依賴性,從而實現本例的makefile。
(一)
本例使用了makefile的模式規則,目的是對當前目錄下每個.c檔案,生成其對應的.d檔案,例如由main.c生成的.d檔案內容為:
main.o : main.c command.h
這裡指示了main.o目標依賴於哪幾個原始檔,我們只要把這一行的內容,通過make的include指令包含到makefile檔案裡,即可在其任意一個依賴檔案被修改後,重新編譯目標main.o。
下面詳解如何生成這個.d檔案。
(二)
gcc/g++編譯器有一個-MM選項,可以對某個.c/.cpp檔案,分析其依賴的原始檔,例如假定main.c的內容為:
#include <stdio.h>//標準標頭檔案(以<>方式包含的),被-MM選項忽略,被-M選項收集
#include "stdlib.h"//標準標頭檔案(以""方式包含的),被-MM選項忽略,被-M選項收集
#include "command.h"
int main()
{
printf("##### Hello Makefile #####\n");
return 0;
}
則執行gcc -MM main.c後,螢幕輸出:
main.o: main.c command.h
執行gcc -M main.c後,螢幕輸出:
main.o: main.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/stdlib.h /usr/include/sys/types.h /usr/include/time.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/sys/select.h \
/usr/include/bits/select.h /usr/include/bits/sigset.h \
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h command.h
(三)
可見,只要把這些行挪到makefile裡,就能自動定義main.c的依賴是哪些檔案了,做法是把命令的輸出重定向到.d檔案裡:gcc -MM main.c > main.d,再把這個.d檔案include到makefile裡。
如何include當前目錄每個.c生成的.d檔案:
sources:=$(wildcard *.c) #使用$(wildcard *.cpp)來獲取工作目錄下的所有.c檔案的列表。
dependence=$(sources:.c=.d) #這裡,dependence是所有.d檔案的列表.即把串sources串裡的.c換成.d。
include $(dependence) #include後面可以跟若干個檔名,用空格分開,支援萬用字元,例如include foo.make *.mk。這裡是把所有.d檔案一次性全部include進來。注意該句要放在終極目標all的規則之後,否則.d檔案裡的規則會被誤當作終極規則了。
(四)
現在main.c command.h這幾個檔案,任何一個改了都會重編main.o。但是這裡還有一個問題,如果修改了command.h,在command.h中加入#include "pub.h",這時:
1-再make,由於command.h改了,這時會重編main.o,並且會使用新加的pub.h,看起來是正常的。
2-這時開啟main.d檢視,發現main.d中未加入pub.h,因為根據模式規則%.d: %.c中的定義,只有依賴的.c檔案變了,才會重新生成.d,而剛才改的是command.h,不會重新生成main.d、及在main.d中加入對pub.h的依賴關係,這會導致問題。
3-修改新加的pub.h的內容,再make,果然問題出現了,make報告up to date,沒有像期望那樣重編譯main.o。
現在問題在於,main.d裡的某個.h檔案改了,沒有重新生成main.d。進一步說,main.d裡給出的每個依賴檔案,任何一個改了,都要重新生成這個main.d。
所以main.d也要作為一個目標來生成,它的依賴應該是main.d裡的每個依賴檔案,也就是說make裡要有這樣的定義:
main.d: main.c command.h
這時我們發現,main.d與main.o的依賴是完全相同的,可以利用make的多目標規則,把main.d與main.o這兩個目標的定義合併為一句:
main.o main.d: main.c command.h
現在,main.o: main.c command.h這一句我們已經有了,如何進一步得到main.o main.d: main.c command.h呢?
(五)
解決方法是行內字串替換,對main.o,取出其中的子串main,加上.d字尾得到main.d,再插入到main.o後面。能實現這種替換功能的命令是sed。
實現的時候,先用gcc -MM命令生成臨時檔案main.d.temp,再用sed命令從該臨時檔案中讀出內容(用<重定向輸入)。做替換後,再用>輸出到最終檔案main.d。
命令可以這麼寫:
g++ -MM main.c > main.d.temp
sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g' < main.d.temp > main.d
其中:
sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g',是sed命令。
< main.d.temp,指示sed命令從臨時檔案main.d.temp讀取輸入,作為命令的來源字串。
> main.d,把行內替換結果輸出到最終檔案main.d。
(六)
這條sed命令的結構是s/match/replace/g。有時為了清晰,可以把每個/寫成逗號,即這裡的格式s,match,replace,g。
該命令表示把源串內的match都替換成replace,s指示match可以是正則表示式。
g表示把每行內所有match都替換,如果去掉g,則只有每行的第1處match被替換(實際上不需要g,因為一個.d檔案中,只會在開頭有一個main.o:)。
這裡match是正則式\(main\)\.o[ :]*,它分成3段:
第1段是\(main\),在sed命令裡把main用\(和\)括起來,使接下來的replace中可以用\1引用main。
第2段是\.o,表示匹配main.o,(這裡\不知何意,去掉也是可以的)。
第3段是正則式[ :]*,表示若干個空格或冒號,(其實一個.d裡只會有一個冒號,如果這裡寫成[ ]*:,即匹配若干個空格後跟一個冒號,也是可以的)。
總體來說match用來匹配'main.o :'這樣的串。
這裡的replace是\1.o main.d :,其中\1會被替換為前面第1個\(和\)括起的內容,即main,這樣replace值為main.o main.d :
這樣該sed命令就實現了把main.o :替換為main.o main.d :的目的。
這兩行實現了把臨時檔案main.d.temp的內容main.o : main.c command.h改為main.o main.d : main.c command.h,並存入main.d檔案的功能。
(七)
進一步修改,採用自動化變數。使得當前目錄下有多個.c檔案時,make會依次對每個.c檔案執行這段規則,生成對應的.d:
gcc -MM $< > [email protected];
sed 's,\($*\)\.o[ :]*,\1.o [email protected] : ,g' < [email protected] > [email protected];
(八)
現在來看上面2行的執行流程:
第一次make,假定這時從來沒有make過,所有.d檔案不存在,這時鍵入make:
1-include所有.d檔案的命令無效果。
2-首次編譯所有.c檔案。每個.c檔案中若#include了其它標頭檔案,會由編譯器自動讀取。由於這次是完整編譯,不存在什麼依賴檔案改了不會重編的問題。
3-對每個.c檔案,會根據依賴規則%.d: %.c,生成其對應的.d檔案,例如main.c生成的main.d檔案為:
main.o main.d: main.c command.h
第二次make,假定改了command.h、在command.h中加入#include "pub.h",這時再make:
1-include所有.d檔案,例如include了main.d後,得到依賴規則:
main.o main.d: main.c command.h
注意所有include命令是首先執行的,make會先把所有include進來,再生成依賴規則關係。
2-此時,根據依賴規則,由於command.h的檔案戳改了,要重新生成main.o和main.d檔案。
3-先呼叫gcc -c main.c -o main.o生成main.o,
再呼叫gcc -MM main.c > main.d重新生成main.d。
此時main.d的依賴檔案裡增加了pub.h:
main.o main.d: main.c command.h pub.h
4-對其它依賴檔案沒改的.c(由其.d檔案得到),不會重新編譯.o和生成其.d。
5-最後會執行gcc $(objects) -o main生成最終可執行檔案。
第三次make,假定改了pub.h,再make。由於第二遍中,已把pub.h加入了main.d的依賴,此時會重編main.c,重新生成main.o和main.d。
這樣便實現了當前目錄下任一原始檔改了,自動編譯涉及它的.c。
(九)
進一步修改,得到目前大家普遍使用的版本:
set -e; rm -f [email protected]; \
$(CC) -MM $(CPPFLAGS) $< > [email protected]$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o [email protected] : ,g' < [email protected]$$$$ > [email protected]; \
rm -f [email protected]$$$$
第一行,set -e表示,如果某個命令的返回引數非0,那麼整個程式立刻退出。
rm -f用來刪除上一次make時生成的.d檔案,因為現在要重新生成這個.d,老的可以刪除了(不刪也可以)。
第二行:前面臨時檔案是用固定的.d.temp作為字尾,為了防止重名覆蓋掉有用的檔案,這裡把temp換成一個隨機數,該數可用$$得到,$$的值是當前程序號。
由於$是makefile特殊符號,一個$要用$$來轉義,所以2個$要寫成$$$$(你可以在makefile裡用echo $$$$來顯示程序號的值)。
第三行:sed命令的輸入也改成該臨時檔案.$$。
每個shell命令的程序號通常是不同的,為了每次呼叫$$時得到的程序號相同,必須把這4行放在一條命令中,這裡用分號把它們連線成一條命令(在書寫時為了易讀,用\拆成了多行),這樣每次.$$便是同一個檔案了。
你可以在makefile裡用下面命令來比較:
echo $$$$
echo $$$$; echo $$$$
第四行:當make完後,每個臨時檔案.d.$$,已經不需要了,刪除之。
但每個.d檔案要在下一次make時被include進來,要保留。
(十)
綜合前面的分析,得到我們的makefile檔案:
#使用$(wildcard *.c)來獲取工作目錄下的所有.c檔案的列表
sources:=$(wildcard *.c)
objects:=$(sources:.c=.o)
#這裡,dependence是所有.d檔案的列表.即把串sources串裡的.c換成.d
dependence:=$(sources:.c=.d)
#所用的編譯工具
CC=gcc
#當$(objects)列表裡所有檔案都生成後,便可呼叫這裡的 $(CC) $^ -o [email protected] 命令生成最終目標all了
#把all定義成第1個規則,使得可以把make all命令簡寫成make
all: $(objects)
$(CC) $^ -o [email protected]
#這段是make的模式規則,指示如何由.c檔案生成.o,即對每個.c檔案,呼叫gcc -c XX.c -o XX.o命令生成對應的.o檔案。
#如果不寫這段也可以,因為make的隱含規則可以起到同樣的效果
%.o: %.c
$(CC) -c $< -o [email protected]
include $(dependence) #注意該句要放在終極目標all的規則之後,否則.d檔案裡的規則會被誤當作終極規則了
%.d: %.c
set -e; rm -f [email protected]; \
$(CC) -MM $(CPPFLAGS) $< > [email protected]$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o [email protected] : ,g' < [email protected]$$$$ > [email protected]; \
rm -f [email protected]$$$$
.PHONY: clean #之所以把clean定義成偽目標,是因為這個目標並不對應實際的檔案
clean:
rm -f all $(objects) $(dependence) #清除所有臨時檔案:所有.o和.d。.$$已在每次使用後立即刪除。-f引數表示被刪檔案不存在時不報錯
(十一)
上面這個makefile已經能正常工作了(編譯C程式),但如果要用它編譯C++,變數CC值要改成g++,每個.c都要改成.cpp,有點繁瑣。
現在我們繼續完善它,使其同時支援C和C++,並支援二者的混合編譯。
#一個實用的makefile,能自動編譯當前目錄下所有.c/.cpp原始檔,支援二者混合編譯
#並且當某個.c/.cpp、.h或依賴的原始檔被修改後,僅重編涉及到的原始檔,未涉及的不編譯
#詳解文件:http://blog.csdn.net/huyansoft/article/details/8924624
#author:胡彥 2013-5-21
#----------------------------------------------------------
#編譯工具用g++,以同時支援C和C++程式,以及二者的混合編譯
CC=g++
#使用$(winldcard *.c)來獲取工作目錄下的所有.c檔案的列表
#sources:=main.cpp command.c
#變數sources得到當前目錄下待編譯的.c/.cpp檔案的列表,兩次呼叫winldcard、結果連在一起即可
sources:=$(wildcard *.c) $(wildcard *.cpp)
#變數objects得到待生成的.o檔案的列表,把sources中每個檔案的副檔名換成.o即可。這裡兩次呼叫patsubst函式,第1次把sources中所有.cpp換成.o,第2次把第1次結果裡所有.c換成.o
objects:=$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(sources)))
#變數dependence得到待生成的.d檔案的列表,把objects中每個副檔名.o換成.d即可。也可寫成$(patsubst %.o,%.d,$(objects))
dependence:=$(objects:.o=.d)
#----------------------------------------------------------
#當$(objects)列表裡所有檔案都生成後,便可呼叫這裡的 $(CC) $^ -o [email protected] 命令生成最終目標all了
#把all定義成第1個規則,使得可以把make all命令簡寫成make
all: $(objects)
$(CC) $(CPPFLAGS) $^ -o [email protected]
@./[email protected] #編譯後立即執行
#這段使用make的模式規則,指示如何由.c檔案生成.o,即對每個.c檔案,呼叫gcc -c XX.c -o XX.o命令生成對應的.o檔案
#如果不寫這段也可以,因為make的隱含規則可以起到同樣的效果
%.o: %.c
$(CC) $(CPPFLAGS) -c $< -o [email protected]
#同上,指示如何由.cpp生成.o,可省略
%.o: %.cpp
$(CC) $(CPPFLAGS) -c $< -o [email protected]
#----------------------------------------------------------
include $(dependence) #注意該句要放在終極目標all的規則之後,否則.d檔案裡的規則會被誤當作終極規則了
#因為這4行命令要多次凋用,定義成命令包以簡化書寫
define gen_dep
set -e; rm -f [email protected]; \
$(CC) -MM $(CPPFLAGS) $< > [email protected]$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o [email protected] : ,g' < [email protected]$$$$ > [email protected]; \
rm -f [email protected]$$$$
endef
#指示如何由.c生成其依賴規則檔案.d
#這段使用make的模式規則,指示對每個.c檔案,如何生成其依賴規則檔案.d,呼叫上面的命令包即可
%.d: %.c
$(gen_dep)
#同上,指示對每個.cpp,如何生成其依賴規則檔案.d
%.d: %.cpp
$(gen_dep)
#----------------------------------------------------------
#清除所有臨時檔案(所有.o和.d)。之所以把clean定義成偽目標,是因為這個目標並不對應實際的檔案
.PHONY: clean
clean: #.$$已在每次使用後立即刪除。-f引數表示被刪檔案不存在時不報錯
rm -f all $(objects) $(dependence)
echo: #除錯時顯示一些變數的值
@echo sources=$(sources)
@echo objects=$(objects)
@echo dependence=$(dependence)
@echo CPPFLAGS=$(CPPFLAGS)
#提醒:當混合編譯.c/.cpp時,為了能夠在C++程式裡呼叫C函式,必須把每一個要呼叫的C函式,其宣告都包括在extern "C"{}塊裡面,這樣C++連結時才能成功連結它們。
五
makefile學習體會:
剛學過C語言的讀者,可能會覺得makefile有點難,因為makefile不像C語言那樣,一招一式都那麼清晰明瞭。
在makefile裡到處是“潛規則”,都是一些隱晦的東西,要弄明白只有搞清楚這些“潛規則”。
基本的規則無非是“一個依賴改了,去更新哪些目標”。
正因為隱晦動作較多,寫成一個makefile才不需要那麼多篇幅,畢竟專案程式碼才是主體。只要知道makefile的框架,往它的套路里填就行了。
較好的學習資料是《跟我一起寫Makefile.pdf》這篇文件(下載包裡已經附帶了),比較詳細,適合初學者。
我們學習的目的是,能夠編寫一個像本文這樣的makefile,以滿足簡單專案的基本需求,這要求理解前面makefile幾個關鍵點:
1-多目標
2-隱含規則
3-定義模式規則
4-自動生成依賴性
可惜的是,這篇文件雖然比較全面,卻沒有以一個完整的例子為引導,對幾處要點沒有突出指明,尤其是“定義模式規則”在最後不顯眼的位置(第十一部分第五點),導致看了“自動生成依賴性”一節後還比較模糊。
所以,看了《跟我一起寫Makefile.pdf》後,再結合本文針對性的講解,會有更實際的收穫。
另一個學習資料是《GNU make v3.80中文手冊v1.5.pdf》,這個手冊更詳細,但較枯燥,不適合完整學習,通常是遇到問題再去查閱。
[END]
相關推薦
萬能makefile寫法詳解,一步一步寫一個實用的makefile,詳解 sed 's,\($*\)\.o[ :]*,\1.o <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a084e0">[emai
一 目的:編寫一個實用的makefile,能自動編譯當前目錄下所有.c/.cpp原始檔,支援二者混合編譯。並且當某個.c/.cpp、.h或依賴的原始檔被修改後,僅重編涉及到的原始檔,未涉及的不編譯。 二 要達到這個目的,用到的技術有: 1-使用wildcard函式來獲得當
<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4c3f3c3e25222b2e2323380c09222d2e20290d3938230f23222a252b393e2d38252322">[emai
首先講解一下springboot中@EnableAutoConfiguration 註解的作用,見名知意,就是開啟自動配置.此時有些人肯定會想,這麼簡單的問題,你tm是xxx吧,哈哈,逗比了哈,開啟了
[ 轉]Shell中引數($0,$1,$#,$NF,<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4b6f0b">[email protected]a>等)的含義
Shell中引數($0,$1,$#,$NF,[email protected]等)的含義 釋出時間:2018-01-19 來源:網路 上傳者:使用者 &nbs
makefile下$^,<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="cbef8b">[email protected]a>,$?,$<,$(@D),$(@F)定義使用詳解
每次看makefile的時候,總會遇到一些變數記不住,就需要去查詢資料,今天有時間,就順便把幾個常用的變數學習了下,順便總結了下記憶方法,感覺記住它們並不難,特把方法分享給大家。變數定義:$^所有的依賴目標的集合。以空格分隔。如果在依賴目標中有多個重複的,那個這個變數
<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5400313a273b2632383b2379142032">[email protected]a>_export詳解
Tensorflow經常看到定義的函式前面加了“@tf_export”。例如,tensorflow/python/platform/app.py中有: @tf_export('app.run') def run(main=None, argv=None): """Runs the progr
Makefile有三個非常有用的變數。分別是<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a480e4">[email protected]a>,$^,$
原文地址:https://blog.csdn.net/u013774102/article/details/79043559 假設我們有下面這樣的一個程式,原始碼如下: /* main.c */ #include "mytool1.h" #include "mytool2.h" i
kafka 消費者優化及配置詳解 <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4c3f3c3e25222b2e232338670c072d2a272d00253f382922293e">[email
自定義屬性和執行工廠 public KafkaListenerContainerFactory<?> batchFactory(){ ConcurrentKafkaListenerContainerFactory<Integer, Stri
【Spring】定時任務詳解例項<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="92bfd2c1f1faf7f6e7fef7f6">[email protected]a>
最近在做專案,時間比較緊張,也有比較久沒寫部落格了。 現在專案的Redis快取需要用到定時任務,就學習了一下Spring 的@Scheduled註解。使用起來很簡單。 這個例子是建立在之前我的一篇部落格的例項上面的。 也就是架好了SSM框架。 SSM
Makefile <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="321672">[email protected]a>,$^,$ 作用
/* main.c */
unix中shell 非一般變數$0 $n $* <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a581e5">[email protected]a> $! $?的詳解
$0:獲取當前執行指令碼的檔名,包括路徑。 [[email protected] script]# cat 0.sh #!/bin/bash echo $0 [[email protected] script]# sh 0.sh 0.sh [[email protected
linux命令提示符[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c8baa7a7bc88a4a7aba9a4a0a7bbbc">[email protected]a> ~]#詳解
[[email protected] ~]# root代表當前登入的使用者,在Linux中管理員賬戶是root localhost當前計
makefile的特殊變數,類似<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0a2e4a">[email protected]a>, $+等 (copied)
來源:http://hi.baidu.com/jingweiyoung/item/dea74399c40eb24cf14215cf Makefile 特殊變數 常用特殊變數 例: %.o:%.c ¥(CC) -c $(CFLAGS) $&l
<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="31624143585f561c7147505d4454">[email protected]a>用法詳解
為了簡化讀取properties檔案中的配置值,spring支援@value註解的方式來獲取,這種方式大大簡化了專案配置,提高業務中的靈活性。 一、兩種使用方法 1、@Value("#{configProperties['key']}") 2、@Value("${key}"
Makefile單字尾,雙字尾,以及<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="113551">[email protected]a>的意.
均是根據《跟我一起寫Makefile》寫的,只不過是具體解釋 關於Makefile的單字尾,也就是.c: $(CC) -c $< $(CFLAGS) $(INCDIRS)相當於%:%.c $(CC) -c $< $(CFLAGS) $(INCDIRS)我
annotation(@<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="75271001101b011c1a1b35211407121001">[email protected]a>)詳解
一、註解:深入理解JAVA註解 要深入學習註解,我們就必須能定義自己的註解,並使用註解,在定義自己的註解之前,我們就必須要了解Java為我們提供的元註解和相關定義註解的語法。 1、元註解(meta-annotation): 元註解的作用就是負責註解其他註解。Java
啟動hbase後,出現ERROR [<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7a4b4f49484b4d4d4a4c493a0b0e0a574f4d494b4a4842424b5748">[em
2018-11-07 09:28:54,771 INFO [master:16000.activeMasterManager] util.FSUtils: Waiting for dfs to exit safe mode... 2018-11-07 09:29:04,783 INFO [mas
無法解析的外部符號 <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1e41497770537f77705e2f28">[email protected]a>,該符號在函式 ___tmai
#include using namespace std; int main() { cout <<“This is a C++ program.”; return 0; } 1>------ 已啟動生成: 專案: hello1, 配置: Debug Win32 ---
面試題:一個字串包含英文和特殊字元,特殊字元不變,英文順序反過來,比如string str="<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2b4d0a4f5c6b">[email
public class Reverse { public static void main(String[] args) { String str = "[email protected]!tk"; char[] chars = str.toCh
SpringBoot 進階系列一 定義全域性異常@<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="284b47465c5a4744444d5a694c5e414b4d03686d504b4d585c41
此方式優點是不用再control層進行try catch了 此方式的缺點恰恰也是隻能反饋control層的相關異常 首先我們定義一下,建立全域性異常控制類,並在類頭上添加註解:@controllerAdvice @ControllerAdvice public class GlobalE
error LNK2019: 無法解析的外部符號 <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="702f031f131b1504304142">[email protected]a>,該
Reason: 學習使用socket,在stdafx.h檔案加了#include ,編譯 #include "stdafx.h" #include using namespace std; int _tmain(int argc