<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [原文地址](https://www.jianshu.com/p/442e71755643) ## 簡介 ` `makefile文件用于管理和組織代碼工程的編譯和鏈接,其不是可執行文件,其被make工具解析并完成相關動作。 ` `Makefile文件里描述的是編譯的時候依賴關系,宏定義,編譯參數,鏈接生成的程序名字等等。 ` `等Makefile文件寫好后,需要用make程序來執行Makefile,所以需要先安裝make程序。 ``` sudo apt install make -y ``` ## 基本語法 ### 文件包含 ` `語法:include 文件名 ` `作用:將其它makefile文件包含進來,組成一個更大的makefile文件,這樣有利于makefile模塊化編程。通常我們將一些配置選項分開成一個獨立的makefile文件,這樣有利于makefile文件的管理,或將模塊代碼的依賴關系和需要編譯的文件信息獨自寫到一個 makefile文件中,最終通過include命令形成一個頂層makefile文件來完成整個工程代碼的編譯和鏈接。 ### 變量定義 ` `語法:變量名 := 變量值 ` `在makefile中,經常先定義一個變量,然后往該變量中追加新的值(通過+=符號),比如先定義一個C_SRCS變量(該值可以為空),然后將代碼文件test1.c和test2.c添加到C_SRCS中,其代碼如下所示: ``` C_SRCS := C_SRCS += test1.c test2.c ``` ` `在makefile中有一類特殊的變量,其名稱為 **自動變量**,自動變量的值會依據規則中的target 和 prerequisites自動計算其值,自動變量一般以開頭$為起始,下面將列出一些常見的自動變量: ``` $@ 為規則中的target名稱。 $< 為規則中第一個prerequisite名稱 ``` ### 內置命令 ` `Makefile中內置了一些常用的命令。 * 有字符串處理函數 ``` 1. subst 2. patsubst 3. strip 4. findstring 5. filter 6. filter-out 7. sort 8. word 9. wordlist 10. words 11. firstword 12. lastword ``` * 文件名處理函數 ``` 1. dir 2. notdir 3. suffix 4. basename 5. addsuffix 6. addprefix 7. join 8. wildcard 9. realpath 10. abspath ``` * 條件處理函數`if`; * 循環處理函數`foreach`等 **以下是一些常用的函數:** * [ ] **wildcard 函數**:其語法為$(wildcard pattern),pattern為匹配的模式,比如$(wildcard %.c) 為查找當前路徑下面文件名以.c結尾的文件。 * [ ] foreach 函數:其語法為$(foreach var,list,text),每循環一次var從list中按順序取值一個,然后執行一次text代碼并記錄結果,最終返回所用text代碼運行的結果。比如 dirs := C_DIR S_DIR file := $(foreach dir,$(dirs),$(wildcard $(dir)/*)) 將C_DIR和S_DIR文件夾下面的所有文件添加到file變量中。 * [ ] **dir 函數**:其語法為$(dir names…),用于獲取names中文件夾路徑,比如 $(dir src/foo.c hacks) 將獲得文件夾路徑 src/ ./ * [ ] **notdir 函數**:其語法為$(notdir names…),用于獲取names中除去路徑的信息,比如 $(notdir src/foo.c hacks) 將獲得文件信息 foo.c hacks * [ ] **basename 函數**:其語法為$(basename names…),用于獲取names中除去后綴信息,比如 $(basename src/foo.c src-1.0/bar hacks) 將獲得信息 src/foo src-1.0/bar hacks * [ ] **addsuffix 函數**:其語法為$(addsuffix suffix,names…),用于往names中添加后綴信息suffix,比如 $(addsuffix .c,foo bar) 將獲得文件信息 foo.c bar.c * [ ] **addprefix 函數**:其語法為$(addprefix prefix,names…),用于往names中添加前綴信息prefix,比如 $(addprefix src/,foo bar) 將獲得信息src/foo src/bar patsubst 函數:其語法為$(patsubst pattern,replacement,text),根據 pattern信息將text替換成replacement,比如 objects = foo.o bar.o baz.o files = $(patsubst %.o,%.c,$( objects)) 將獲得信息 foo.c bar.c baz.c 其可以簡單寫成 objects = foo.o bar.o baz.o files = $(objects:.o=.c) ## 規則定義 ` `規則是makefile中最重要的概念,其告訴make 目標文件的依賴關系,以及如何生成及更新這些目標文件。在makefile文件規則有2種,一種是顯式規則,另一種是隱式規則。 ` `顯式規則用于說明 何時及如何重新生成目標,其列出了目標依賴的文件信息,并通過調用命令來創建或更新目標,其語法一般為: ``` targets : prerequisites ??????? recipe ??????? … ``` ` `targets為要生成或更新的目標,prerequisites為目標依賴的關系,recipe為生成目標的命令,一個規則可以有多條recipe,比如 ``` foo.o : foo.c defs.h ??????? cc -c -g foo.c 其中foo.o為target,foo.c defs.h 為prerequisites,cc -c -g foo.c為recipe。 ``` ` `隱式規則用于說明 何時及如何來重新生成一類目標文件根據其名稱,其描述了目標是如何依賴于名稱相似的文件(一般來說除去后綴信息,其目標與依賴文件的名稱是一樣的),并調用命令來創建或更新目標,比如 ``` %.o : %.c ??????? $(CC) -c $(CFLAGS) $< -o $@ ``` ` `? 這個隱式規則說明了.o的目標文件依賴于同名的.c文件,其中$< 及 $@為自動變量,$<為第一個prerequisites條件,也就是 目標名稱.c,$@為目標,也就是 目標名稱.o。 ` `在makefile中,我們通常要編寫3種隱式規則,第1種為代碼鏈接規則,第2種為源代碼編譯規則,第3種為匯編代碼編譯規則。 ## 文件搜索路徑設置 ` `? Make命令默認會在當前路徑中搜索prerequisites中的文件,比如頭文件,但我們在寫程序時,經常將頭文件和源文件隔開放在不同的文件夾下,這種該怎么處理呢?1、我們可以通過VPATH變量來解決,2、我們可以通過vpath指令來解決。 **VPATH變量** ` `VPATH變量為所有的prerequisites指定文件路徑,路徑之間可以通過 :或空格隔開,比如 ``` **VPATH變量** VPATH變量為所有的prerequisites指定文件路徑,路徑之間可以通過 :或空格隔開,比如 ``` **vpath指令** ` `vpath指令的作用與變量VPATH的作用差不多,但vpath有更多的靈活性,其語法為: vpath pattern directories ` `pattern為需要查找的文件匹配模式信息,directories為要查找的文件路徑,比如 ``` vpath %.h ../headers ``` ` `其代表在上一層文件夾headers中查找 .h頭文件信息。 ## 實例說明 ` `看個最簡單的Makefile的例子,把下面的內容保存到文件Makefile里: ~~~ # 最簡單的Makefile hello: @echo "hello makefile" ~~~ **注意**: 1. makefile文件中命令行的行首不能用空格,而要用Tab鍵 2. makefile文件中的字符格式有要求,必須是英文字符,不能有中文字符。 ` `然后make一下,如果make的時候當前目錄有叫Makefile的文件,默認執行這個Makefile,如果需要指定其他文件名,需要用make -f。 ![](https://img.kancloud.cn/3c/1f/3c1fd36193b85d705900aa714def303e_599x206.png) ` `上面Makefile文件里的hello表示目標,這個目標的執行動作是打印一行字符串"hello Makefile"。 我們可以有多個目標,修改Makefile,加入目標hello2: ``` # 最簡單的Makefile hello: @echo "hello makefile" hello2: @echo "hello linux" ``` ![](https://img.kancloud.cn/a1/94/a1949f24fba3de59fd20c64cf76ac794_569x93.png) ` `每個目標可以有相應的依賴項,看下面的例子 ``` # 最簡單的Makefile hello: ready @echo "hello makefile" hello2: @echo "hello linux" ready: @echo "i am ready" ``` ![](https://img.kancloud.cn/fd/3d/fd3d167c6fb0c05c3d1c776b1bf25bba_551x130.png) ` `目標可以執行一個空的動作,修改Makefile為下面的內容: ``` # 最簡單的Makefile all :hello hello2 hello: ready @echo "hello makefile" hello2: @echo "hello linux" ready: @echo "i am ready" ``` ![](https://img.kancloud.cn/0e/d7/0ed738cccebb2a29b8aa4f2eca5c73c9_495x94.png) ` `們可以看出Makefile實際上非常簡單,就是一個個目標與依賴結合起來的有序的解析過程。 ` `了解了Makefile的執行過程,我們只要把目標的動作改為編譯和鏈接就可以了,下面我們看執行一個編譯動作的命令。 ` `linux下編譯c++代碼用g++,c++代碼的文件后綴為.cpp或.cc,編譯c代碼用gcc,c代碼的文件后綴為.c。 鏈接程序也用g++。 ** g++的常用參數說明** * -c 表示編譯代碼 * -o 表示指定生成的文件名 * -g 表示編譯時候加入調試符號信息,debug時候需要這些信息 * -I (大寫i)表示設置頭文件路徑,-I./表示頭文件路徑為./ * -Wall 表示輸出所有警告信息 * -D 表示設置宏定義,-DDEBUG表示編譯debug版本,-DNDEBUG表示編譯release版本 * -O 表示編譯時候的優化級別,有4個級別-O0,-O1,-O2 -O3,-O0表示不優化,-O3表示最高優化級別 * -shared 表示生成動態庫 * -L 指定庫路徑,如-L.表示庫路徑為當前目錄 * -l (小寫L)指定庫名,如-lc表示引用libc.so ` `新裝的ubuntu可能沒有g++,可以先在bash里輸入g++ -v試一下,如果沒有安裝會提示先安裝,這時候跟著提示apt install g++就可以了。 安裝gcc也一樣。 ` `看一下最簡單的編譯.cpp代碼的Makefile怎么寫 ~~~c 1 2 main : main.o 3 g++ -o $@ $^ 4 5 .cpp.o: 6 g++ -c -o $@ $< ~~~ ` `解釋一下上面的Makefile: * 第2行main表示一個目標名為main,依賴為main.o,.o是代碼編譯后生成的obj文件 * 第3行表示目標main的執行動作, 就是執行鏈接程序的命令。 * 第5行.cpp.o表示這是一個目標,作用是把.cpp文件編譯為.o文件 * 第6行是該目標的具體編譯命令,-c表示編譯, -o @表示指定生成文件名 * 看下面的測試: ![](https://img.kancloud.cn/94/a1/94a18d80ff4dd88f053eba858a12c144_536x131.png) > 小知識 Makefile里的echo和rm前面帶了@表示不要打印執行該命令時候命令本身的輸出,比如 rm -rf \*.o main在執行的時候會輸出這句命令"rm -rf \*.o main" 如果把rm改為@rm, 再make clean的時候就不會輸出"rm -rf \*.o main"命令本身了。 ` `看到這里基本上再回頭看開完整Makefile就能看懂了,需要重點說的是編譯的參數,頭文件路徑和庫引用的寫法,下面我們一步一步寫上注釋 ``` ~~~c # 這句是鏈接時候的命令,在g++前面加入了@echo linking $@ # 這樣在鏈接時候就會先輸出linking xxx, # 這行直接寫g++也是沒有任何問題的 LINK = @echo linking $@ && g++ # 編譯c++代碼時候用的,一樣會顯示compiling xxx GCC = @echo compiling $@ && g++ # 編譯c代碼時候用gcc GC = @echo compiling $@ && gcc # 生成靜態庫時候用ar命令 AR = @echo generating static library $@ && ar crv # 這是編譯時候的參數設置,下面-g表示編譯時候加入調試信息, # -DDEBUG表示編譯debug版本 # -W -Wall表示輸出所有警告 # -fPIC是生成dll時候用的 FLAGS = -g -DDEBUG -W -Wall -fPIC GCCFLAGS = DEFINES = # 這里指出頭文件的目錄為./ HEADER = -I./ # 需要引用的庫文件 LIBS = LINKFLAGS = # 更多頭文件可以用 += 加進來 #HEADER += -I./ # 如果需要librt.so,就把-lrt加進來 #LIBS += -lrt # 如果需要寫多線程程序,就需要引用-pthread #LIBS += -pthread # 這里是主要需要修改的地方,每一個.c或.cpp對應于這里的一項, # 如main.cpp對應于main.o # 多個.o可以用空格分開,也可以像下面這樣用"\"換行,然后寫在新一行 OBJECT := main.o \ # 下面舉個例子,這里編譯表示兩個代碼文件 # OBJECT := main.o \ # other.o BIN_PATH = ./ TARGET = main # 鏈接用的,可以看到后面加了$(LIBS),因為只有鏈接時候才需要引用庫文件 $(TARGET) : $(OBJECT) $(LINK) $(FLAGS) $(LINKFLAGS) -o $@ $^ $(LIBS) # 編譯cpp代碼用這個目標 .cpp.o: $(GCC) -c $(HEADER) $(FLAGS) $(GCCFLAGS) -fpermissive -o $@ $< # 編譯c代碼用這個 .c.o: $(GC) -c $(HEADER) $(FLAGS) -fpermissive -o $@ $< # 把生成的$(TARGET)拷貝到$(BIN_PATH)下 install: $(TARGET) cp $(TARGET) $(BIN_PATH) clean: rm -rf $(TARGET) *.o *.so *.a ~~~ ``` ## 生成動態鏈接庫 ` `linux動態鏈接庫的后綴為 .so,生成動態鏈接庫也比windows要方便的多,只要在link的時候加上-shared參數就可以了,下面我們看個例子。 先來看一下完整測試的目錄結構,最上層目錄是test_makefile_so ~~~c test_makefile_so ├── Makefile #這個是總的Makefile,管理所有子目錄的Makefile ├── bin │ ├── libfun.so │ └── main └── src ├── Makefile ├── fun │ ├── Makefile │ ├── fun.cpp │ └── fun.h └── main.cpp 3 directories, 8 files ~~~ ` `main.cpp的內容 ~~~c #include <stdio.h> #include "fun.h" int main(int, char**){ printf("hello so\n"); int a = 1; int b = 2; int c = sum( a, b ); printf( "sum: %d + %d = %d\n", a, b, c ); return 0; } ~~~ ` `src/Makefile的內容 ~~~c LINK = @echo linking $@ && g++ GCC = @echo compiling $@ && g++ GC = @echo compiling $@ && gcc AR = @echo generating static library $@ && ar crv FLAGS = -g -DDEBUG -W -Wall -fPIC GCCFLAGS = DEFINES = HEADER = -I./ LIBS = LINKFLAGS = HEADER += -I./fun #LIBS += -lrt #LIBS += -pthread #這里表示鏈接的時候從bin目錄下找libfun.so LIBS += -L../bin -lfun OBJECT := main.o \ #這里加了bin的相對路徑,編完的main會install到bin目錄下 BIN_PATH = ../bin/ TARGET = main $(TARGET) : $(OBJECT) $(LINK) $(FLAGS) $(LINKFLAGS) -o $@ $^ $(LIBS) .cpp.o: $(GCC) -c $(HEADER) $(FLAGS) $(GCCFLAGS) -fpermissive -o $@ $< .c.o: $(GC) -c $(HEADER) $(FLAGS) -fpermissive -o $@ $< install: $(TARGET) cp $(TARGET) $(BIN_PATH) clean: rm -rf $(TARGET) *.o *.so ~~~ ` `fun.h的內容 ~~~c #ifndef __fun_h__ #define __fun_h__ int sum(int a, int b); #endif//__fun_h__ ~~~ ` `fun.cpp的內容 ~~~c #include "fun.h" int sum(int a, int b){ return a+b; } ~~~ ` `fun/Makefile的內容 ~~~c LINK = @echo linking $@ && g++ GCC = @echo compiling $@ && g++ GC = @echo compiling $@ && gcc FLAGS = -g -DDEBUG -W -Wall -fPIC GCCFLAGS = DEFINES = HEADER = -I./ LIBS = #修改的地方1: 這里加了-shared,表示生成動態庫 LINKFLAGS = -shared #HEADER += -I./ #LIBS += -lrt #LIBS += -pthread OBJECT := fun.o \ #修改的地方2: 表示編譯fun.cpp #修改的地方3: 指出了bin的相對路徑 BIN_PATH = ../../bin/ #修改的地方3,生成的文件名叫libfun.so,動態庫一般以lib為前綴 TARGET = libfun.so $(TARGET) : $(OBJECT) $(LINK) $(FLAGS) $(LINKFLAGS) -o $@ $^ $(LIBS) .cpp.o: $(GCC) -c $(HEADER) $(FLAGS) $(GCCFLAGS) -fpermissive -o $@ $< .c.o: $(GC) -c $(HEADER) $(FLAGS) -fpermissive -o $@ $< install: $(TARGET) cp $(TARGET) $(BIN_PATH) clean: rm -rf $(TARGET) *.o *.so ~~~ 先編譯動態庫libfun.so,因為main程序要依賴libfun.so。 在fun目錄下先make install一下,會生成libfun.so,并且自動拷貝到bin目錄下 ~~~c bash$ cd fun bash$ make install compiling fun.o linking libfun.so cp libfun.so ../../bin/ ~~~ 切換到src目錄下,再編譯main程序 ~~~c bash$ make clean; make install rm -rf main *.o *.so compiling main.o linking main cp main ../bin/ ~~~ 然后切到bin目錄下,運行main程序 ~~~c bash$ cd ../bin # bin目錄下現在有兩個文件,libfun.so和main bash$ ls libfun.so main # 運行main程序 bash$ ./main mac:bin tpf$ ./main hello so sum: 1 + 2 = 3 #這行是由libfun.so里的函數執行的 ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看