使用 CMake 链接到 windows 上的静态库时遇到问题
Troubles linking to a static library on windows with CMake
LS,
我正在尝试构建一个可用于 link 动态和静态的静态和动态库。无论使用何种编译器,我都希望该库在尽可能多的平台上 运行。为了构建库和一些测试程序,我使用 CMake 分别使用 g++ 和 MSVC++ 在 Linux 和 windows 上构建库 eeg。
在 Linux 上,动态库和静态库似乎都像我怀疑的那样工作,在 windows 上,.dll 似乎 link 完美,我的测试程序 运行 .但是,使用静态库的程序会抱怨 linking 错误。我真的很想念我做错的地方,它可能在我的 CMakeLists.txt 中,也可能在我的库的设置中。
下面我制作了一个最小程序,它使用我的库来演示我遇到的问题。该库包含两个 C++ 文件,一个导出 C++ 文件中代码的 C API,一个使用 C API 的 C++ 程序,最后一个 CMakeList.txt 可以构建所有程序,但使用静态库的程序。这一切都产生了一个美妙的 "Hello, World!".
我知道我展示了很多代码,但至少它是一个最小的项目,它演示了我在 linking 到静态库时遇到的问题。我希望有人好心地看看这个项目并向我解释我做错了什么。
亲切的问候,
hetepeperfan
C++ 文件PriCpp.cpp
#include "PriCpp.h"
using namespace std;
string PriMessageBuilder::message() const {
return "Hello, World!";
}
header PriCpp.h
#ifndef PRICPP_H
#define PRICPP_H
#include <string>
class PriMessageBuilder{
public:
std::string message() const;
};
#endif
CAPI是:
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)
如果我构建解决方案,我会得到这些 linker 错误:
1>program.obj : error LNK2019: unresolved external symbol __imp__message_builder_new referenced in function "public: __thiscall MessageBuilder::MessageBuilder(void)" (??0MessageBuilder@@QAE@XZ)
1>program.obj : error LNK2019: unresolved external symbol __imp__message_builder_destroy referenced in function "public: __thiscall MessageBuilder::~MessageBuilder(void)" (??1MessageBuilder@@QAE@XZ)
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)" (?message@MessageBuilder@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)
1>K:\duijn119\programming\cmake\staticsharedlib\build\Debug\program-static.exe : fatal error LNK1120: 3 unresolved externals
生成的 CMake 包含 header 是:
#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
您需要在 add_executable(program-static program.cpp)
之后添加 set_target_properties(program-static PROPERTIES COMPILE_FLAGS -DBUILDER_STATIC_DEFINE)
。目前,您的静态库构建正确,但应用程序获取的库 header 错误 declspec
(好吧,根本不应该有 declspec
而您有一个)
另外,使用下面的概念似乎更合适:
set_target_properties(program-static PROPERTIES COMPILE_DEFINITIONS BUILDER_STATIC_DEFINE)
COMPILE_DEFINITIONS
而不是 COMPILE_FLAGS
LS,
我正在尝试构建一个可用于 link 动态和静态的静态和动态库。无论使用何种编译器,我都希望该库在尽可能多的平台上 运行。为了构建库和一些测试程序,我使用 CMake 分别使用 g++ 和 MSVC++ 在 Linux 和 windows 上构建库 eeg。
在 Linux 上,动态库和静态库似乎都像我怀疑的那样工作,在 windows 上,.dll 似乎 link 完美,我的测试程序 运行 .但是,使用静态库的程序会抱怨 linking 错误。我真的很想念我做错的地方,它可能在我的 CMakeLists.txt 中,也可能在我的库的设置中。 下面我制作了一个最小程序,它使用我的库来演示我遇到的问题。该库包含两个 C++ 文件,一个导出 C++ 文件中代码的 C API,一个使用 C API 的 C++ 程序,最后一个 CMakeList.txt 可以构建所有程序,但使用静态库的程序。这一切都产生了一个美妙的 "Hello, World!".
我知道我展示了很多代码,但至少它是一个最小的项目,它演示了我在 linking 到静态库时遇到的问题。我希望有人好心地看看这个项目并向我解释我做错了什么。
亲切的问候,
hetepeperfan
C++ 文件PriCpp.cpp
#include "PriCpp.h"
using namespace std;
string PriMessageBuilder::message() const {
return "Hello, World!";
}
header PriCpp.h
#ifndef PRICPP_H
#define PRICPP_H
#include <string>
class PriMessageBuilder{
public:
std::string message() const;
};
#endif
CAPI是: 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)
如果我构建解决方案,我会得到这些 linker 错误:
1>program.obj : error LNK2019: unresolved external symbol __imp__message_builder_new referenced in function "public: __thiscall MessageBuilder::MessageBuilder(void)" (??0MessageBuilder@@QAE@XZ)
1>program.obj : error LNK2019: unresolved external symbol __imp__message_builder_destroy referenced in function "public: __thiscall MessageBuilder::~MessageBuilder(void)" (??1MessageBuilder@@QAE@XZ)
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)" (?message@MessageBuilder@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)
1>K:\duijn119\programming\cmake\staticsharedlib\build\Debug\program-static.exe : fatal error LNK1120: 3 unresolved externals
生成的 CMake 包含 header 是:
#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
您需要在 add_executable(program-static program.cpp)
之后添加 set_target_properties(program-static PROPERTIES COMPILE_FLAGS -DBUILDER_STATIC_DEFINE)
。目前,您的静态库构建正确,但应用程序获取的库 header 错误 declspec
(好吧,根本不应该有 declspec
而您有一个)
另外,使用下面的概念似乎更合适:
set_target_properties(program-static PROPERTIES COMPILE_DEFINITIONS BUILDER_STATIC_DEFINE)
COMPILE_DEFINITIONS
而不是 COMPILE_FLAGS