本文檔主要總結了集中編譯器的使用方法:主要包括bazel,gcc,g++
1.gcc
編譯語言:C/C++/FORTRAN/JAVA/OBJC/ADA
- 語法:gcc(選項) (參數)
- 選項:
-o:指定生成的輸出文件; -E:僅執行編譯預處理; -S:將C代碼轉換為匯編代碼; -wall:顯示警告信息; -c:僅執行編譯操作,不進行連接操作。
- 參數:
C源文件:指定C語言源代碼文件。
- 實例
#假設源程序文件名為test.c #無選項編譯鏈接 gcc test.c #將test.c預處理,匯編,編譯並鏈接形成可執行文件,這里未指定輸出文件,默認輸出為a.out。 #選項 -o gcc test.c -o test #將test.c預處理,匯編,編譯並鏈接形成可執行文件test。 #-o選項用來指定輸出的文件名。 #選項 -E gcc -E test.c -o test.i #將test.c預處理輸出test.i文件。 #選項-S gcc -S test.i #將預處理輸出文件test.i匯編成test.s文件。 #選項-c gcc -c test.s #將匯編輸出文件test.s鏈接成最終可執行文件test。 #無選項鏈接 gcc test.o -o test #將編譯出文件test.o鏈接成最終可執行文件test。 #選項 -O gcc -O1 test.c -o test #使用編譯優化級別1編譯程序。級別為1~3,級別越大優化效果越好,但是編譯時間越長。
- 多源文件的編譯方法
如果有多個源文件,有兩種編譯方法:
#方法一:多個文件一起編譯 gcc testfun.c test.c -o test #將testfun.c和test.c分別編譯后鏈接成test可執行文件。 #方法二:分別編譯各個源文件,之后對編譯后輸出的目標文件鏈接。 gcc -c testfun.c #將testfun.c編譯成testfun.o gcc -c test.c #將test.c編譯成test.o gcc -o testfun.o test.o -o test #將testfun.c和test.c鏈接成test
注:第一種方法編譯時需要所有文件重新編譯
第二種方法可以重新編譯修改的文件,未修改的文件不要重新編譯。
2.g++
- g++是linux下c++的編譯器,在執行編譯工作的時候,總共需要4步:
(1)預處理,生成*.i的文件;
(2)將預處理后的文件不轉換成匯編語言,生成文件*.s;
(3)由匯編變為目標代碼(機器代碼)生成*.o的文件;
(4)連接目標代碼,生成可執行程序。
- g++編譯c++經常使用的參數
#-c:只編譯,不連接 g++ -c helloworld.cpp #只生成helloworld.o不連接 #-o:指定輸出文件名 g++ -c helloworld -o abc.o #默認是生成helloworld.o,用-o abc.o以后,就生成就是abc.o #-I:附加一個包含頭文件的路徑。 g++ helloworld.cpp -I"/usr/helloworld/include" #-l(小L):附一個庫 g++ helloworld.cpp -labc #使用libabc.so # -L:添加一個庫的路徑 g++ hello.cpp -L"/usr/hello/lib" -labc #-shared:生成動態庫文件 g++ -shared hello.cpp -o libhello.so
- 調用動態庫的時候有幾個問題會經常用到,有時候,明明已經庫的頭文件所在目錄通過include進來了,庫所在文件通過“-L”參數引導,並指定了“-l”的庫名,但通過ldd命令查看時,就是死活找不到你指定鏈接的
so文件。其實編譯鏈接上了共享庫不代表執行時可以找到。所以“-L“什么的對執行沒有用,你需要指明共享庫的路徑。方法有三個:
(1)修改LD_LIBRARY_PATH,指明共享庫的路徑。LD_LIBRARY_PATH:這個環境變量指示動態連接器可以裝載動態庫的路徑。在終端下使用如下命令:
export LD_LIBRARY_PATH = your lib dir export
(2)通過/etc/ld.so.conf文件來指定動態庫的目錄。然后運行ldconfig命令更新搜索共享庫的路徑。通常這個做法就可以解決庫無法鏈接的問題並且一勞永逸。
(3)把庫文件拷貝到/lib下,然后ldconfig就ok了。這個方法有的取巧,且破壞原庫文件的純潔性,不推薦。
- linux下文件的類型是不依賴於其后綴名的,但是一般講來:
*.o:是目標文件,相當於windows中的*.obj文件
*.so為共享庫,是shared object,用於動態連接的,和dll差不多
*.a為靜態庫,是好多個*.o合在一起,用於靜態連接
*.la為libtool自動生成的一些共享庫,主要是記錄了一些配置信息
3.bazel
參考:http://blog.163.com/wujiaxing009@126/blog/static/71988399201751110532137/
- 特點:
(1)多語言支持
JAVA/Objective-C和C++
(2)高級構建描述語言
項目是使用一種叫BUILD的語言來描述的,它是一種簡簡潔的文本語言,它把一個項目是為一個集合,這個集合由一些互相關聯的庫,二進制文件和測試用例組成。相反,像Make這樣的工具,需要去描述每一個文件如何調用編譯器。
(3)多平台支持
同一套工具和相同的BUILD文件可以用來為不同的體系結構構建軟件,甚至是不同的平台。在google,bazel被同時用在數據中心系統中的服務器應用和手機端的移動應用上。
(4)可重復性
在BUILD文件中,每個庫,測試用例和二進制文件都需要明確指定它們的依賴關系。當一個源文件被修改時,bazel憑這些依賴關系來判斷哪些部分需要重新構建,以及那些任務可以並行進行。這就是意味着所有構建都是增量的,並且相同構建總是產生一樣的結果。
(5)可伸縮性
bazel可以處理大型項目,在google,一個服務器軟件由十幾萬行代碼是很常見的,在什么都不改的前提下重新構建這樣一個項目,大概需要200毫秒。
- bazel構建C++項目
(1)使用工作區
bazel builds應該在一個工作區內運行,這個工作區應該包括源代碼和build輸出目錄的符號鏈接(如:bazel-bin、bazel-out)。工作區目錄的位置是可以隨意的,但是工作區的根目錄必須包含一個名為WORKSPACE的工作區配置問價,工作區配置文件可以是一個空文件,也可以包含引用外表不構建輸出所需的依賴關系,在一個工作區內,可根據需求共享多個項目。
(2)創建Build文件
bazel通過檢查BUILD文件可以知道哪些目標文件被創建在項目中,這些BUILD文件采用與Python相似的語法所寫,這種語言通常是一系列規則的聲明,每個規則制定相應的輸入、輸出以及實現輸入到輸出的方法。
(3)通過一個例子學習
1)建立工作區
假設有個項目,對應於~/gitroot/my-project/目錄,先創建一個空的~/gitroot/my-project/WORKSPACE工作區配置文件,用於表示這是Bazel項目對應的根目錄。創立一個hello world工程。
└── my-project ├── lib │ ├── BUILD │ ├── hello-greet.cc │ └── hello-greet.h ├── main │ ├── BUILD │ ├── hello-time.cc │ ├── hello-time.h │ └── hello-world.cc └── WORKSPACE
2)創建源文件
1 $ # If you're not already there, move to your workspace directory. 2 $ cd ~/gitroot/my-project #進入項目根目錄 3 $ mkdir ./main #創建一個main 的文件夾 4 $ cat > main/hello-world.cc <<'EOF' #在main文件夾中創建一個名為hello-world.cc的文件,並以EOF作為Linux創建文件結束的標志 5 #include "lib/hello-greet.h" #include lib中的hello-greet.h 6 #include "main/hello-time.h" #include main文件夾中的hello-time.h,而hello-time.cc則為hello-time.h的實現 7 #include <iostream> #標准庫 8 #include <string> 9 10 int main(int argc, char** argv) { 11 std::string who = "world"; 12 if (argc > 1) { 13 who = argv[1]; 14 } 15 std::cout << get_greet(who) <<std::endl; 16 print_localtime(); 17 return 0; 18 } 19 EOF 20 $ cat > main/hello-time.h <<'EOF' #聲明hello-time.cc是干什么事情的 21 #ifndef MAIN_HELLO_TIME_H_ 22 #define MAIN_HELLO_TIME_H_ 23 24 void print_localtime(); 25 26 #endif 27 EOF 28 $ cat > main/hello-time.cc <<'EOF'#具體hello-time.cc是怎么實現的 29 #include "main/hello-time.h" 30 #include <ctime> 31 #include <iostream> 32 33 void print_localtime() { 34 std::time_t result = std::time(nullptr); 35 std::cout << std::asctime(std::localtime(&result)); 36 } 37 EOF 38 $ mkdir ./lib 39 $ cat > lib/hello-greet.h <<'EOF' 40 #ifndef LIB_HELLO_GREET_H_ 41 #define LIB_HELLO_GREET_H_ 42 43 #include <string> 44 45 std::string get_greet(const std::string &thing); 46 47 #endif 48 EOF 49 $ cat > lib/hello-greet.cc <<'EOF' 50 #include "lib/hello-greet.h" 51 #include <string> 52 53 std::string get_greet(const std::string& who) { 54 return "Hello " + who; 55 } 56 EOF
3)添加BUILD文件
從上面的源代碼可知,main/hello-world.cc要用到lib/hello-greet.h和main/hello-time.h。首先在lib目錄下為hello-greet.cc創建BUILD.
1 cc_library( 2 name = "hello-greet", #要BUILD的文件的名字 3 srcs = ["hello-greet.cc"],#腳本 4 hdrs = ["hello-greet.h"],#頭文件 5 visibility = ["//main:__pkg__"], 6 )
注意:visibility = ["//main:__pkg__"]表示hello-greet對於main/BUILD是可見的。
接下來在main目錄下創建BUILD文件:
1 cc_library( 2 name = "hello-time", 3 srcs = ["hello-time.cc"], 4 hdrs = ["hello-time.h"], 5 ) 6 7 cc_binary( 8 name = "hello-world", 9 srcs = ["hello-world.cc"], 10 deps = [ 11 ":hello-time", 12 "//lib:hello-greet", 13 ], 14 )
注意:當依賴的包在同一個目錄下,只需要用:hello-time,當依賴的包在不同的目錄下,需要用全路徑://lib:hello-greet
現在可以建立hello world的c++二進制程序了:
1 $ bazel build main:hello-world 2 INFO: Found 1 target... 3 Target //main:hello-world up-to-date: 4 bazel-bin/main/hello-world 5 INFO: Elapsed time: 2.869s, Critical Path: 1.00s 6 $ ./bazel-bin/main/hello-world 7 Hello world 8 Thu Jun 23 18:51:46 2016 9 $ ./bazel-bin/main/hello-world Bazel 10 Hello Bazel 11 Thu Jun 23 18:52:10 2016
ok!成功建立了第一個bazel項目!
4)傳遞依賴(Transitive includes)
如果一個文件包含一個頭文件,那么這個文件的規則也應該依賴與頭文件的庫,相反的,只有直接依賴需要被指定為依賴。例如,假設sandwich.h包括bread.h,而且bread.h包括flour.h,sandwich.h不包括flour.h,因此這個BUILD文件應該是這樣:
1 cc_library( 2 name = "sandwich", 3 srcs = ["sandwich.cc"], 4 hdrs = ["sandwich.h"], 5 deps = [":bread"], 6 ) 7 8 cc_library( 9 name = "bread", 10 srcs = ["bread.cc"], 11 hdrs = ["bread.h"], 12 deps = [":flour"], 13 ) 14 15 cc_library( 16 name = "flour", 17 srcs = ["flour.cc"], 18 hdrs = ["flour.h"], 19 )
在這里,sandwich庫依賴於bread庫,而bread庫依賴於flour庫。
5)添加包含路徑(adding include paths)
有時候不能(或者不願意)讓依賴的文件包含咋工作區根目錄的路徑,現有的庫可能已經擁有了包括與工作空間不匹配路徑的目錄。例如,假設有如下的目錄結構:
1 └── my-project 2 ├── third_party 3 │ └── some_lib 4 │ ├── BUILD 5 │ ├── include 6 │ │ └── some_lib.h 7 │ └── some_lib.cc 8 └── WORKSPACE
bazel希望some_lib.h被包含在third_party/some_lib/include/some_lib.h中,但假定some_lib.cc要依賴於include/some_lib.h。為了使包含路徑有效, third_party/some_lib/BUILD需要指定some_lib是一個包含目錄:
1 cc_library( 2 name = "some_lib", 3 srcs = ["some_lib.cc"], 4 hdrs = ["some_lib.h"], 5 copts = ["-Ithird_party/some_lib"], 6 )
這對外部的依賴尤其有用,因為它們的頭文件,否則必須被包含於外部/ [庫名稱]/前綴。
6)包含外部庫
7)編譯並運行c++測試程序
8)在預編譯上添加依賴
