天天看點

OpenWRT開發之——對C++的支援(解決庫依賴問題)

c++是本人的強項。如果在openwrt中不能用c++進行開發,那就有點大失所望了。

接下來将與大家一起來嘗試寫一個c++程式,并把它做成 ipk 包,并試運作。

在 sdk/package/ 路徑下建立 cpp-demo 目錄,并生成一個非常簡單的 cpp 程式

形成如下目錄結構:

package/cpp-demo/src/main.cpp内容:

package/cpp-demo/src/makefile内容:

注意:上面用的是cxx,而不是cc,就這點差別。

package/makefile内容:

在sdk目錄下 make v=s,結果報錯:

說是要依賴 libstdc++.so.6 這個庫檔案?

那好吧,在本地機上安裝一個便是了。

說明本地已安排了libstdc++了的。難不成是版本對不上号?

我用 locate指令找了一下 libstdc++這個檔案,發現存在 /usr/lib/libstdc++.so.6 檔案。這應該就是上面需要的庫吧。

應該在ld_library_path環境變量中沒有将 /usr/lib 加入到其中。如下:

這次,報:

不湊效?!

不過靜下心來想想,我編譯cpp-demo需要的是開發機的動态庫呢,還是目标機的動态庫?

動态庫是程式在運作起來後加載的庫,那麼cpp-demo在目标機上運作起來後,加載的動态庫應該是目标機的,不應該是開發機的。是以,剛剛我在整開發機上的動态庫,與這個應該是兩碼事兒。

在sdk路徑下搜所有的動态庫檔案:

有很多,也看到了 libstdc++.so.6

應該是在連結時将 libstdc++.so.6 所在的路徑加進來吧。就像:

這樣。

我們檢查編譯調試資訊列印出來的ldflags的值:

這說明,libstdc++.so.6已在ldflags的路徑範圍之下,編譯不應該出錯。

還是好好研究一下錯誤提示:

從前3行看來,cpp-demo的編譯好像是正常通過了的。

檢視 build_dir/target-mips

在這個路徑下已經成功生成了cpp-demo可執行程式。證明,編譯是通過了的。

為什麼 find 執行完就報錯?

怎麼入手?從錯誤提示入手吧,看看是在哪裡提示"package cpp-demo is missing dependencies for the following libraries"的。

在sdk目錄下執行 grep 指令進行查找。

打開 include/package-ipkg.mk 檔案看個究竟。

OpenWRT開發之——對C++的支援(解決庫依賴問題)

在74行輸入錯誤提示資訊,條件是存在檔案 $(pkg_info_dir)/$(1).missing 這個檔案。那問題來了,這個檔案是怎麼産生的呢?

行70~71行的意思是:如果在 $(pkg_info_dir)/$(1).provides 中沒有找到$file的,就把$file寫入到 $(pkg_info_dir)/$(1).missing

其中 $(1)值為cpp-demo,相應的 cpp-demo.provides 與 cpp-demo.missing 都是在 sdk/staging_dir/target-mips_34kc_uclibc-0.9.33.2/pkginfo/ 路徑下的。

那麼 xxx.provides 檔案裡的内容代碼什麼呢?

63~68行為一個執行程序,它用管道與while語句連接配接起來。也就是說while裡面的file變量就是63~68行标準輸出的結果。

65,66,67是在根據平台設定環境變量的值,然後在68行執行 sdk/scripts/gen-dependencies.sh。

我猜gen-dependencise.sh執行輸出的一定是cpp-demo所依賴的庫名稱。

我們打開 sdk/scripts/gen-dependencise.sh 檔案,其中最核心的一段:

從targets目錄下檢視到所有的檔案。對每個檔案并用file指令輸入該檔案的基本資訊。用sed對file的輸出進行分析,找出有"elf"且有"executable"或"shared object"關鍵字的檔案。

再對這個檔案用 readelf -d 指令輸入elf檔案的資訊,再用awk從中提取出所依賴的 *.so 檔案名,并列印到标準輸出。

我親手用readelf檢視一下cpp-demo依賴什麼:

它确實依賴 libstdc++.so.6, libm.so.6, libc.so.6 這3個動态庫檔案。

現在 package-ipk.mk 檔案中過程理清楚了。cpp-demo所依賴的庫檔案,在cpp-demo.provides 檔案中找不到就會寫入到 cpp-demo.missing。

如果純粹是想讓它打包成功的話,那就把差的檔案寫入到cpp-demo.provides檔案就了事兒了。

這樣做會不會有後有後遺症?

試試再說~,結果還是不成功。原因是cpp-demo.provides是自動生成的。就算是我手動改了它,下次make的時候還是會還原的。

那新問題是:who在生成這個cpp-demo.provides檔案?它是根據什麼生成這個檔案的?

那就grep一下關鍵字"$(1).provides",應該就可以找到。結果還是在 package-ipk.mk 檔案裡:

OpenWRT開發之——對C++的支援(解決庫依賴問題)

l185,将資料進行排序了之後寫入到 cpp-demo.provides。資料由l179~184産生。

l179,将idir_$(1)目錄下的所有 lib*.so*, *.ko 檔案名列印出來,作為已有的庫檔案,由l185寫入cpp-demo.provides檔案。

idir_$(1)的定義如下:

OpenWRT開發之——對C++的支援(解決庫依賴問題)

l180~184這個for循環不太好了解。

OpenWRT開發之——對C++的支援(解決庫依賴問題)

$(patsubst %,$(pkg_info_dir)/%.provides,$(idepend_$(1))) 傳回的結果是:将$(idepend_$(1))中的每個結果 item,變成:$(pkg_info_dir)/$(item).provides傳回。

那idepend_$(1)是什麼?

OpenWRT開發之——對C++的支援(解決庫依賴問題)
OpenWRT開發之——對C++的支援(解決庫依賴問題)

部落客深深地感到腦容量不夠了。有哪位高人知道的,請指點一下。

跳出這個洞,回到l180~184,的問題。它就是把其它的某個相關的 xxxx.provides 檔案裡的内容輸出來。

部落客通過經驗推斷,xxxx.provides 代表的是是與cpp-demo依賴的元件所依賴的庫。

注意:l184,是 $(package/$(1)/extra_provides)。也就是說,我們可以在 package/cpp-demo/makefile 檔案中定義 package/cpp-demo/extra_provides 宏來強制性地将那幾個庫加進去。比如:

經過試驗,正确的寫法如下:

這樣寫果然湊效,再 make v=s,能夠打包成功。

但是,有點我們必須明确的是:在打包中生成的ipk檔案裡,是沒有libstdc++, libc, libm這3個庫的。如果所安裝的openwrt系統裡也沒有這3個庫,那麼我們安裝的應用程式是不能正常使用的。

相當于是在騙ipk工具,我們已具備了上面這3個庫檔案。

比較穩妥的方法是采用方案二,如下:

還有另一個方法,注意l176,$(call package/$(1)/install, $$(idir_$(1))),這個就是引用了我們在makefile裡寫的 package/cpp-demo/install 宏麼?

我們可以在這個宏裡,将它需要的幾個庫檔案複制到 $(1) 對應的目錄下。

如下修改:

其中,install_data 與 toolchain_dir 這兩個變量在 rules.mk 檔案中定義。

之是以選用 toolchain_dir,是因為libstdc++.so.6這個檔案就在這個變量所對應的路徑下。不信,你可以用 find 指令查找一下。

好了,這樣再make v=s,就能正常打包了。

經部落客親自嘗試,是ok的。

不過,為什麼是叫libstdcpp而不是libstdc++呢?這個有待研究一下。

将生成的 sdk/bin/ar71xx/packages/base/cpp-demo_1_ar71xx.ipk 檔案用 scp 傳到目标機上進行安裝試用。

然後用 ssh 登陸到目标機上去安裝。

試執行一下 cpp-demo

oops~  運作不起來。

檢查一下那3個依賴的庫檔案,都還在。那為什麼不能運作起來呢?

之前部落客在排查上面庫依賴的問題的時候已看出了端倪。

在 sdk/build_dir/target-mips_34kc_uclibc-0.9.33.2/cpp-demo/ipkg-ar71xx/cpp-demo/ 路徑下的所有内容都是直接打包的檔案。而這裡面的 bin/cpp-demo 可執行檔案在開發機上居然都正常運作。

這明顯不對,bin/cpp-demo 應該是運作在目标機上的elf檔案,怎麼可以在開發機上運作?開發機與目标機都不是同類型的cpu。一定是這裡出了問題!

部落客在想,是不是在交叉編譯的時候,選用了本機的gcc,而非目标機的gcc。如下為 sdk/package/cpp-demo/src/makefile :

為了知道cxx到底是哪一個編譯器。我特地加了 echo "cxx = $(cxx)" 調試資訊。

把 sdk/build_dir 删了重新 make。為了友善分析,

分析列印的資訊:

咦?nothing to be done for 'all',表示沒有什麼需要再編譯的了。

我趕緊檢視

發現cpp-demo已經生成,而且在開發機上可運作。這個cpp-demo是怎麼來的?難不出是從 package/cpp-demo/src裡來的?

我突然想到,我之前進入 package/cpp-demo/src/ 路徑下,執行過一次 make,那時一定生成了一個 cpp-demo 可執行程式。檢視之:

果然有它!在make時,由于它的存在,編譯元件就認為已經有了就不必再編譯,是以才導緻了上述的問題。

删除cpp-demo以及main.o。回到sdk,重新make

根據調試資訊可以知道cpp-demo是用mips-openwrt-linux-uclibs-g++來編譯的。這下應該就對了!

再次嘗試将ipk檔案傳到目标機上并安裝。運作效果如下:

成功了!

這一次,一個小小的cpp-demo實驗,經曆了太多波折。不過在嘗試解決這些問題的過程中,我們可以更深入地了解其中的編譯機制。

首先,我們知道了建立c++應用與c應用沒什麼差別,莫非就是在makefile中将cc改成cxx。

然後我們了解到了在打包的時候,它是如何查找一個elf檔案依賴哪些動态包。并知道如何将所依賴的包加入到我們的ipk中。

最後,我們了解到了在rules.mk中定義了大量有用的變量,在include/package-ipk.mk檔案中了解到打包的過程。

總之,這次折騰也算是蠻有收獲的。

我會堅持天天學習,并寫博文。如果小夥伴們想一定來研究,那就關注我吧~

上一篇: vsftp
下一篇: css 其他

繼續閱讀