淺談如何使用clang替換gcc進行編譯


經過多年的發展,LLVM事實上已經對大部分語言進行了支持,其完備的功能和好的模塊化和輕耦合的特性得到了很多人的認可,但是在很多傳統領域,實際上的編譯器還是gcc(基礎設施),大部分人如果想使用LLVM對gcc進行替換時,會遇到一些麻煩。Clang的官網上對這個地方有個說明:The 'clang' driver is designed to work as closely to GCC as possible to maximize portability. The only major difference between the two is that Clang defaults to gnu99 mode while GCC defaults to gnu89 mode. If you see weird link-time errors relating to inline functions, try passing -std=gnu89 to clang.。本文想從一個普通開發者的角度去解釋一些傳統gcc編譯領域使用clang來進行替換的一些實際步驟。

我想根據project的特點,把LLVM替換gcc的編譯過程分為以下三個類型(不完全是,簡單分類):

  1. 測試型的project,例如自己寫的testcase,基本上以一個main函數,調用幾個簡單的cpp文件為主,使用shell腳本進行編譯
  2. 傳統的使用configure生成Makefile文件,然后進行編譯的project
  3. 大型project,往往使用cmake生成編譯腳本,然后編譯的project

1. 測試型的project

    對於這種類型,一般就是幾條簡單的gcc或者g++編譯命令,這種project建議完全復制,粘貼時將gcc替換為clang就能解決問題,大部分gcc支持option,clang都進行了支持,甚至支持的更好,一般用戶很少能寫出gcc支持而clang不支持的命令

2. 傳統configure類型的project

    對於這種類型的project,是目前一般用戶見到的最多的。我這里以一個開源的bird-2.0.8來進行說明。(首先說明,非網絡專業用戶,僅測試使用)某度百科上的詞條介紹是:BIRD是一個類UNIX系統的動態路由守護進程。它支持當代互聯網中所用所有路由協議,如BGP、OSPF、RIP和這些協議的IPv6的變種(除OSPFv3目前尚在發展)。其實做什么的,我們並不關心,我們只關心如何得到一個正確的編譯結果。

    要想知道如何得到一個正確的結果,正常使用gcc搞這類project的流程是使用configure進行配置,然后make,后邊make install進行安裝,其中configure是用來生成Makefile文件的,也會進行很多配置的檢測。這里我采用的思路是首先用默認配置來一遍,然后再換編譯器。

從這里路徑下載代碼后,解壓進入source文件夾。https://bird.network.cz/download/bird-2.0.8.tar.gz

./configure

會遇到

 的問題,按照提示安裝libreadline或者直接使用

./configure --disable-client
make -j 32

就可以發現編譯完成了,非常簡單。

現在我們開始換編譯器。先看下configure文件,發現有如下的介紹:

也就是說剛才在配置的時候,通過設置CC,CPP等環境變量就可以實現切換編譯器的過程,我這里采用最暴力的方法,直接替換(其實也差不了太多)。

找到Makefile文件,查找CC的變量直接替換為CC=clang, 然后make,發現問題:

LD -pthread -flto=4 -g -o bird obj/conf/cf-parse.tab.o obj/conf/cf-lex.o obj/conf/conf.o obj/filter/filter.o obj/filter/data.o obj/filter/f-util.o obj/filter/tree.o obj/filter/trie.o obj/filter/inst-gen.o obj/lib/bitmap.o obj/lib/bitops.o obj/lib/checksum.o obj/lib/event.o obj/lib/flowspec.o obj/lib/idm.o obj/lib/ip.o obj/lib/lists.o obj/lib/mac.o obj/lib/md5.o obj/lib/mempool.o obj/lib/net.o obj/lib/patmatch.o obj/lib/printf.o obj/lib/resource.o obj/lib/sha1.o obj/lib/sha256.o obj/lib/sha512.o obj/lib/slab.o obj/lib/slists.o obj/lib/strtoul.o obj/lib/tbf.o obj/lib/timer.o obj/lib/xmalloc.o obj/nest/a-path.o obj/nest/a-set.o obj/nest/cli.o obj/nest/cmds.o obj/nest/iface.o obj/nest/locks.o obj/nest/neighbor.o obj/nest/password.o obj/nest/proto.o obj/nest/rt-attr.o obj/nest/rt-dev.o obj/nest/rt-fib.o obj/nest/rt-show.o obj/nest/rt-table.o obj/proto/bfd/bfd.o obj/proto/bfd/io.o obj/proto/bfd/packets.o obj/proto/babel/babel.o obj/proto/babel/packets.o obj/proto/bgp/attrs.o obj/proto/bgp/bgp.o obj/proto/bgp/packets.o obj/proto/mrt/mrt.o obj/proto/ospf/dbdes.o obj/proto/ospf/hello.o obj/proto/ospf/iface.o obj/proto/ospf/lsack.o obj/proto/ospf/lsalib.o obj/proto/ospf/lsreq.o obj/proto/ospf/lsupd.o obj/proto/ospf/neighbor.o obj/proto/ospf/ospf.o obj/proto/ospf/packet.o obj/proto/ospf/rt.o obj/proto/ospf/topology.o obj/proto/perf/perf.o obj/proto/pipe/pipe.o obj/proto/radv/packets.o obj/proto/radv/radv.o obj/proto/rip/packets.o obj/proto/rip/rip.o obj/proto/rpki/rpki.o obj/proto/rpki/packets.o obj/proto/rpki/tcp_transport.o obj/proto/rpki/ssh_transport.o obj/proto/rpki/transport.o obj/proto/static/static.o obj/sysdep/linux/netlink.o obj/sysdep/unix/io.o obj/sysdep/unix/krt.o obj/sysdep/unix/log.o obj/sysdep/unix/main.o obj/sysdep/unix/random.o
clang-7: error: unsupported argument '4' to option 'flto='
Makefile:159: recipe for target 'bird' failed
make: *** [bird] Error 1

我這里使用的LLVM 7.0,所以會提示clang-7,后邊意思就是clang-7不支持gcc的-flto=4的option,man gcc查看該option的用法,是一個用於link級別的選項,再man clang就能發現clang在這個選項,要求使用 -flto -O2這種標准的用法,這里編譯替換就好:

CFLAGS=$(CPPFLAGS) -g -O2 -pthread -fno-strict-aliasing -fno-strict-overflow -flto -Wall -Wextra -Wstrict-prototypes -Wno-parentheses -Wno-pointer-sign -Wno-missing-field-initializers
LDFLAGS= -pthread -flto -O2 -g

既然是LD提示的錯誤,直接給LDFLAG修改就好。

再次編譯,會出現/usr/bin/ld: /home/daily_learning/oldLLVM/build/bin/../lib/LLVMgold.so: error loading plugin: /home/daily_learning/oldLLVM/build/bin/../lib/LLVMgold.so: cannot open shared object file: No such file or directory

也就是LLVMgold.so找不到的問題,到那個文件下,發現確實沒有這個so文件,這是因為加了flto選項需要引入LLVMgold庫,這個庫是需要放在源碼中進行編譯的,網上的教程很多,不再贅述,解決掉這里后,發現就能得到和gcc一樣的結果。整個替換過程比較簡單。

相對直接用於test的project來說,一般project的configure和編譯過程相對比較復雜,整個編譯流程也分為了預編譯、編譯、鏈接三個過程,預編譯一般沒有問題,編譯過程中可能遇到option的問題,鏈接過程可能遇到庫的兼容性問題,相對來說,有點復雜也需要一點經驗。但是對於想入手學習的人來說,還是非常有必要的。

對於想要進行學習交流的來說,很多時候需要獲得的不是最終的鏈接結果,需要的是中間的bc文件,這個時候其實完全不需要考慮什么-c的編譯過程中添加什么-emit-llvm什么選項這些復雜的問題,直接在CFLAGS后邊添加-save-temps,你就可以獲得中間文件了,雖然有點多。

3. 大型project

   這部分其實和上邊差不多,不過這種功能一般就不能暴力修改Makefile文件來進行了,需要預先配置編譯器和環境變量。我這里給出一個我使用的環境變量,其他人可以對照修改(不保證完全夠):

export LLVM_HOME=/home/daily_learning/oldLLVM
export PATH=/home/local/bin:$LLVM_HOME/build/bin:$PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LLVM_HOME/build/lib
export C_INCLUDE_PATH=$C_INCLUDE_PATH:$LLVM_HOME/build/include
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:$LLVM_HOME/build/include

 說實話,非常想給出一個完整的例子。主要問題是,一個真實的project,想要全流程的介紹和切換,一般是稍微需要一點時間和耐心的,而且在短的篇幅內把遇到的問題都說明白,也非常考驗project自身,所以找一個合適的project非常重要,待有機會再進行補充這部分內容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM