2014-05-19 52 views
1

我正在嘗試使用Boost :: Test爲庫創建C庫和C++測試程序。我在這個問題中發佈的代碼是對我的代碼的簡化,它顯示了完全相同的問題。請幫忙!與靜態庫鏈接時未定義的引用,但與src編譯時成功鏈接

這是目錄結構。 childlib是我正在創建的庫,test是測試程序。

> tree 
. 
`-- src 
    |-- main 
    | |-- c 
    | | `-- childlib.c 
    | |-- childlib.h 
    | `-- makefile 
    `-- test 
     |-- cpp 
     | `-- test.cpp 
     `-- makefile 

5 directories, 5 files 

我可以讓childlib成爲一個成功的靜態庫:

> cd src/main  
> make 
gcc -c -fPIC -o c/childlib.o c/childlib.c 
ar rcs libchildlib.a c/childlib.o 
ranlib libchildlib.a 

但我不能用它連接使我的測試程序:

> cd ../test/ 
> make 
g++ -I. -I../main -Imy_boost_install_dir/include -c -std=c++11 -o cpp/test.o cpp/test.cpp 
g++ -L../main -Lmy_boost_install_dir/lib -lchildlib -lboost_unit_test_framework -Wl,-rpath=my_boost_install_dir/lib -o test_childlib cpp/test.o 
cpp/test.o: In function `test_func1::test_method()': 
test.cpp:(.text+0x15e7e): undefined reference to `childlib_func1()' 
collect2: error: ld returned 1 exit status 
make: *** [test_childlib] Error 1 

。另一方面,如果我通過刪除對靜態庫的引用並將childlib.c添加爲源文件來手動運行編譯,我可以成功完成測試程序:

> g++ -Lmy_boost_install_dir/lib -lboost_unit_test_framework -Wl,-rpath=my_boost_install_dir/lib -o test_childlib cpp/test.o ../main/c/childlib.c 
> ./test_childlib 
Running 1 test case... 

*** No errors detected 

下面是各種源文件。 TEST.CPP:

#include <cstdlib> 
#include <stdint.h> 

#define BOOST_TEST_MODULE childlib test 
#include <boost/test/unit_test.hpp> 
#include <boost/test/included/unit_test.hpp> 

#include "childlib.h" 

BOOST_AUTO_TEST_CASE(test_func1) { 
    childlib_func1(); 

    BOOST_CHECK(true); 
} 

childlib.h:

#ifndef CHILDLIB_H_ 
#define CHILDLIB_H_ 

void childlib_func1(); 

#endif /* CHILDLIB_H_ */ 

終於childlib.c:

#include <stdint.h> 

void childlib_func1() { 
    return; 
} 

兩個makefile文件如下。

childlib生成文件:最後,我也想創建一個動態庫,但現在它註釋掉的make all

# childlib 

CC = gcc 

SRCS    = $(wildcard c/*.c) 
OBJS    = $(SRCS:.c=.o) 
HDRS    = $(wildcard *.h) 
MODULE_BASE_NAME = childlib 
MODULE_LIB_A  = lib$(MODULE_BASE_NAME).a 
MODULE_LIB_SO = lib$(MODULE_BASE_NAME).so 
INCLUDE_DIRS  = . 
INCLUDE   = $(addprefix -I,$(INCLUDE_DIRS)) 
CFLAGS   = -c -fPIC 


all: $(MODULE_LIB_A) # $(MODULE_LIB_SO) 

$(MODULE_LIB_A): $(OBJS) 
    ar rcs [email protected] $^ 
    ranlib [email protected] 

$(MODULE_LIB_SO): $(OBJS) 
    $(CC) -shared -o [email protected] $^ 

.c.o: 
    $(CC) $(CFLAGS) -o [email protected] $^ 

clean: 
    -rm -f $(MODULE_LIB_A) $(MODULE_LIB_SO) $(OBJS) 
    -find . -name '*~' -delete 

這裏的測試工具生成文件:

# test 

CC  = g++ 

SRCS    = $(wildcard cpp/*.cpp) 
OBJS    = $(SRCS:.cpp=.o) 
MODULE_EXE  = test_childlib 

MAIN_DIR   = ../main 

BOOST_DIR  = my_boost_install_dir 
BOOST_INC_DIR = $(BOOST_DIR)/include 
BOOST_LIB_DIR = $(BOOST_DIR)/lib 
BOOST_LIBS  = boost_unit_test_framework 
INCLUDE   = $(addprefix -I,. $(MAIN_DIR) $(BOOST_INC_DIR)) 
LIB_DIRS   = $(addprefix -L,$(MAIN_DIR) $(BOOST_LIB_DIR)) 
LIBS    = $(addprefix -l,childlib $(BOOST_LIBS)) 
LINKER_OPTS  = -Wl,-rpath=$(BOOST_LIB_DIR) 

CFLAGS   = -c -std=c++11 


all: test 

test: $(MODULE_EXE) 
    ./$(MODULE_EXE) 


$(MODULE_EXE): $(OBJS) 
    $(CC) $(LIB_DIRS) $(LIBS) $(LINKER_OPTS) -o [email protected] $^ 

.cpp.o: 
    $(CC) $(INCLUDE) $(CFLAGS) -o [email protected] $^ 

clean: 
    -rm -f $(MODULE_EXE) $(OBJS) 
    -find . -name '*~' -delete 

我用gcc & g ++ 4.8.1。我也使用gcc 4.7.2編譯的boost 1.54.0。

看來我需要在測試makefile中爲g ++提供正確的選項,但我不知道它們是什麼。有人可以幫我連接childlib庫和我的測試程序嗎?

+0

'-fPIC'標誌將與__shared__庫一起使用,而不是靜態的。 – Chnossos

+0

@Chnossos - 我試圖從childlib makefile中刪除-fPIC,然後清理/重建這兩個模塊。使測試裝置以同樣的方式失敗。 –

+0

我將childlib的編譯器更改爲g ++而不是gcc,並且我可以鏈接到測試工具!我仍然想將childlib編譯爲C庫而不是C++,但也許這有助於指出答案。我將開始研究如何將C庫與C++ exe鏈接起來。 –

回答

2

我還沒有看到您的所有代碼,但問題很可能是由靜態鏈接如何工作引起的(http://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking/)。

嘗試改變庫的順序,並且具體地,嘗試修改此:到這個

$(MODULE_EXE): $(OBJS) 
    $(CC) $(LIB_DIRS) $(LIBS) $(LINKER_OPTS) -o [email protected] $^ 

$(MODULE_EXE): $(OBJS) 
    $(CC) $(LIB_DIRS) $(LINKER_OPTS) -o [email protected] $^ $(LIBS) 
+0

感謝您的建議。我只是嘗試了它,以及交換庫命令,並得到相同的失敗('g ++ -L ../ main -Lmy_boost_install_dir/lib -Wl,-rpath = my_boost_install_dir/lib -o test_childlib cpp/test.o -lboost_unit_test_framework -lchildlib'和'g ++ -L ../main -Lmy_boost_install_dir/lib -Wl,-rpath = my_boost_install_dir/lib -o test_childlib cpp/test.o -lchildlib -lboost_unit_test_framework') –

+0

我將此標記爲答案,因爲這個是答案的一部分。另一部分是用extern「C」包裝頭文件:「extern」C「{#include」childlib.h「};' –

2

庫和對象在鏈接命令行上出現的順序被處理:總是包含目標文件(或轉換成目標文件後的源文件)。他們未定義的符號被添加到要解析的符號列表中。檢查庫是否會遇到任何未定義的符號。如果它定義了至少一個到目前爲止尚未定義的符號,則包含正在調查的庫中的任何對象文件。一旦處理完畢,圖書館就會被遺忘。

tl; dr:將目標文件放在第一位,庫放到最後。

2

在「另一方面」的情況下,您編譯childlibg++

在最初的情況下,你編譯它與gcc

C和C++是不同的語言。您應該決定使用哪種語言並且堅持使用childlib,而不是嘗試編寫符合這兩種語言通用子集的代碼。

只要指定C++對象文件應該使用C兼容格式,或者它們需要鏈接的函數名稱在一個C生成的目標文件。

假設您想保留childlib.c作爲C代碼。後者更爲常用,其實現方法是確保childlib.h中的所有聲明和定義都包含在extern "C" { ......代碼中...... }只要包含在C++源文件中。實現這一目標的方法之一是有頭包含:

#ifdef __cplusplus 
extern "C" { 
#endif 

// the code 

#ifdef __cplusplus 
} 
#endif 

然後頭文件始終工作在兩種語言,而不必依靠記得做在包括它的所有文件extern "C" {#include...。此外,它還允許您在extern區塊之外擁有其他內容,例如包括系統標題。

+0

我想用C寫成childlib。我試着用C++編譯childlib,因爲我已經嘗試了我所知道的一切(不是很多),但沒有成功。這個實驗使我走上了將include封裝在'extern「C」塊中的最終答案。但這種方式看起來更好,所以我會做到這一點。 –