2016-02-19 51 views
2

LS,故障鏈接到靜態庫在Windows上CMa​​ke的

我試圖構建一個可用於與兩個動態和靜態鏈接的靜態和動態庫。我希望無論使用何種編譯器,庫都可以在儘可能多的平臺上運行。爲了構建庫和一些測試程序,我使用CMake在Linux和Windows上分別使用g ++和MSVC++構建庫eeg。

在Linux上,動態庫和靜態庫看起來都像我懷疑的那樣工作,在Windows上.dll似乎完美鏈接,我的測試程序運行。但是,使用靜態庫的程序會抱怨鏈接錯誤。我真的很想念我做錯了什麼,它可能是我的CMakeLists.txt中的東西,也可能是我的庫的設置。 下面我做了一個最小化的程序,它使用我的庫來演示我遇到的問題。該庫包含兩個C++文件,一個用於導出C++文件中代碼的C API,一個使用C API的C++程序以及一個可以構建除使用靜態庫的程序之外的所有程序的CMakeList.txt。這一切產生了一個奇妙的「你好,世界!」。

我知道我展示了很多代碼,但至少它是一個最小的項目,它演示了我的鏈接到靜態庫的問題。我希望有人善意地看看這個項目,並向我解釋我做錯了什麼。

此致

hetepeperfan

的C++文件PriCpp.cpp

#include "PriCpp.h" 

using namespace std; 
string PriMessageBuilder::message() const { 
    return "Hello, World!"; 
} 

頭PriCpp.h

#ifndef PRICPP_H 
#define PRICPP_H 

#include <string> 

class PriMessageBuilder{ 
public: 
    std::string message() const; 
}; 

#endif 

的C API是: mycapi.h

#ifndef MYCAPI_H 
#define MYCAPI_H 
#include "builder_export.h" 

#ifdef __cplusplus 
extern "C" { 
#endif 

typedef struct {} message_builder; 

BUILDER_EXPORT message_builder* message_builder_new(); 
BUILDER_EXPORT void    message_builder_destroy(
             message_builder* builder 
             ); 
BUILDER_EXPORT char*   message_builder_message(
             message_builder* builder 
             ); 
#ifdef __cplusplus 
} 
#endif 

#endif 

mycapi.cpp是:

#include "mycapi.h" 
#include "PriCpp.h" 
#include <cstring> 
#include <cstdlib> 

message_builder* message_builder_new() { 
    PriMessageBuilder* ret = NULL; 
    try { 
     ret = new PriMessageBuilder(); 
    } catch (...) { 
    } 
    return reinterpret_cast<message_builder*>(ret); 
} 

void message_builder_destroy(message_builder* builder) 
{ 
    PriMessageBuilder* b = reinterpret_cast<PriMessageBuilder*>(builder); 
    delete b; 
} 

char* message_builder_message(message_builder* builder) 
{ 
    PriMessageBuilder* b = reinterpret_cast<PriMessageBuilder*>(builder); 
    return strdup(b->message().c_str()); 
} 

,我結合使用與上面的庫中的程序。 program.cpp

#include <iostream> 
#include <cstdlib> 
#include <string> 
#include "mycapi.h" 

using namespace std; 

class MessageBuilder { 
public: 
    MessageBuilder() : m_builder(message_builder_new()) { 
    } 
    ~MessageBuilder() { 
     message_builder_destroy(m_builder); 
    } 
    string MessageBuilder::message() { 
     char* msg = message_builder_message(m_builder); 
     string m(msg); 
     free (msg); 
     return m; 
    } 
private: 
    message_builder* m_builder; 
}; 

int main(int, char**) { 
    MessageBuilder m; 
    cout << m.message() << endl; 
    return 0; 
} 

最後的CMakeLists.txt建立一個makefile文件或Visual Studio解決方案

#setting up project 
cmake_minimum_required(VERSION 2.6) 
project(libbuilder CXX) 
include (GenerateExportHeader) 

include_directories("${PROJECT_BINARY_DIR}") 

#creating library 
add_library(builder SHARED PriCpp.cpp PriCpp.h mycapi.cpp mycapi.h) 
add_library(builder-static STATIC PriCpp.cpp PriCpp.h mycapi.cpp mycapi.h) 

generate_export_header(builder) 

set_target_properties(builder PROPERTIES COMPILE_FLAGS -DBUILD_BUILDER_SHARED) 
set_target_properties(builder-static PROPERTIES COMPILE_FLAGS -DBUILDER_STATIC_DEFINE) 

#creating programs 
add_executable(program program.cpp) 
add_executable(program-static program.cpp) 

target_link_libraries(program builder) 
target_link_libraries(program-static builder-static) 

如果我構建解決方案我得到這些鏈接器錯誤:

1>program.obj : error LNK2019: unresolved external symbol __imp__message_builder_new referenced in function "public: __thiscall MessageBuilder::MessageBuilder(void)" ([email protected]@[email protected]) 
1>program.obj : error LNK2019: unresolved external symbol __imp__message_builder_destroy referenced in function "public: __thiscall MessageBuilder::~MessageBuilder(void)" ([email protected]@[email protected]) 
1>program.obj : error LNK2019: unresolved external symbol __imp__message_builder_message referenced in function "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall MessageBuilder::message(void)" ([email protected]@@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@XZ) 
1>K:\duijn119\programming\cmake\staticsharedlib\build\Debug\program-static.exe : fatal error LNK1120: 3 unresolved externals 

CMake生成的包含標頭爲:

#ifndef BUILDER_EXPORT_H 
#define BUILDER_EXPORT_H 

#ifdef BUILDER_STATIC_DEFINE 
# define BUILDER_EXPORT 
# define BUILDER_NO_EXPORT 
#else 
# ifndef BUILDER_EXPORT 
# ifdef builder_EXPORTS 
     /* We are building this library */ 
#  define BUILDER_EXPORT __declspec(dllexport) 
# else 
     /* We are using this library */ 
#  define BUILDER_EXPORT __declspec(dllimport) 
# endif 
# endif 

# ifndef BUILDER_NO_EXPORT 
# define BUILDER_NO_EXPORT 
# endif 
#endif 

#ifndef BUILDER_DEPRECATED 
# define BUILDER_DEPRECATED __declspec(deprecated) 
#endif 

#ifndef BUILDER_DEPRECATED_EXPORT 
# define BUILDER_DEPRECATED_EXPORT BUILDER_EXPORT BUILDER_DEPRECATED 
#endif 

#ifndef BUILDER_DEPRECATED_NO_EXPORT 
# define BUILDER_DEPRECATED_NO_EXPORT BUILDER_NO_EXPORT BUILDER_DEPRECATED 
#endif 

#define DEFINE_NO_DEPRECATED 0 
#if DEFINE_NO_DEPRECATED 
# define BUILDER_NO_DEPRECATED 
#endif 

#endif 
+0

不能確定,但​​你真的需要'的extern 「C」'在頭,因爲你有C++實現? – dmi

+0

@dmi是的我很確定我需要extern「C」。我想使用C調用約定,所以沒有名稱調整。這樣可以更輕鬆地從C程序中調用庫中的函數。 C API還通過dll邊界隱藏了標準C++庫中的類。這保持了不同C/C++編譯器之間的ABI穩定性。 – hetepeperfan

+0

如果刪除所有非靜態庫和可執行文件,問題依然存在嗎?如果是這樣,請在沒有它​​們的情況下發布'CMakeLists.txt'。 – Tsyvarev

回答

2

您需要在add_executable(program-static program.cpp)後添加set_target_properties(program-static PROPERTIES COMPILE_FLAGS -DBUILDER_STATIC_DEFINE)。目前您的靜態庫得到建立正確的,但應用程序獲取庫的頭用錯declspec(當然,應該沒有declspec都和你有一個)

而且,它似乎更適合使用以下概念:

set_target_properties(program-static PROPERTIES COMPILE_DEFINITIONS BUILDER_STATIC_DEFINE) 

COMPILE_DEFINITIONS,而不是COMPILE_FLAGS

+0

優秀的芒。猜猜我還是一個CMake Noob。當鏈接到靜態庫時,define -DBUILDER_STATIC_DEFINE明確地將它作爲我的庫的文檔。我不認爲我自己會想出這一個,儘管當有人指出它時,它是如此符合邏輯。 – hetepeperfan