開發一個跨平台的項目的時候,大部分時候都是在VS下進行編碼,所以也就使用了VS的解決方案來管理項目。
因為要跨平台,當時網上看scons
這個工具不錯,所以在linux下就使用了scons
來作為編譯腳本。
linux(gcc)下與windows(vs)下的對於鏈接這一步稍有不同。當目標文件是一個(共享)庫的時候,VS會在鏈接的時候就去解析所有用到的符號,而gcc則不會,只有在生成最終可執行程序的時候才會去解析。
所以在VS下,一直都沒有問題。linux下進行測試的時候也沒有問題(因為不是所有的代碼都被調用了)。
這幾天在一次移植過程中出現了如下的問題
/a.out: symbol lookup error: ./uds_file_storage_module.so: undefined symbol: _ZN8unispace13us_ini_config9from_fileERKNS_10us_ustringEPS0_
錯誤很簡單,就是us_ini_config::from_file
這個函數沒有找到,說明沒有將其添加到動態導出符號表中。(uds_file_storage_module.so是運行時動態加載的,所以編譯的時候沒有提示錯誤)
VC中導出符號需要使用到dllexport
,而gcc下則默認是不需要。所以這個問題很是疑惑。
考慮用的gcc
版本比較高,是不是它將-fvisibility
的參數默認設置為hidden
呢?查看了gnu的wiki之后也沒有發現這個。
https://gcc.gnu.org/wiki/Visibility
這里還是有收獲的,看到一段好的跨平台代碼。
#if defined _WIN32 || defined __CYGWIN__
#ifdef BUILDING_DLL
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllexport))
#else
#define DLL_PUBLIC __declspec(dllexport) // 注記:實際上gcc似乎也支持這種語法。
#endif
#else
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllimport))
#else
#define DLL_PUBLIC __declspec(dllimport) // 注記:實際上gcc似乎也支持這種語法。
#endif
#endif
#define DLL_LOCAL
#else
#if __GNUC__ >= 4
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#define DLL_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define DLL_PUBLIC
#define DLL_LOCAL
#endif
#endif
extern "C" DLL_PUBLIC void function(int a);
class DLL_PUBLIC SomeClass
{
int c;
DLL_LOCAL void privateMethod(); // Only for use within this DSO
public:
Person(int _c) : c(_c) { }
static void foo(int a);
};
然后添加了__attribute__((visibility("default")))
進行修飾,發現編譯的結果沒有改變(md5sun判斷)。
然后又在鏈接的時候添加-rdynamic
參數,將所有符號都添加到動態符號表,編譯結果也沒有變。
在仔細查看了SConstruct
腳本之后,發現問題在於沒有將對應的源文件添加到腳本中。也就是編譯的時候完全就沒有編譯us_ini_config::from_file
所在的源文件。
這樣問題就很清晰明了了,修改腳本之后重新編譯就可以了。
說到這里,就該反思一下了。
這個項目早期確實是直接寫的Makefile
來進行編譯的,使用SOURCES = $(shell find $(SRC_DIR)$(PROJECT)/ -name "*.cpp")
來自動發現cpp文件,這本來是很好的。對於貿然使用自己不熟悉的scons
,又沒有進行有效的學習,這是很不好的。
對於這個項目,還是直接使用qmake
來做跨平台的編譯腳本比較好。