使用cmake构建Windows DLL的简单示例

17 投票
1 回答
24921 浏览
提问于 2025-04-18 14:08

几年前,我被迫去构建和修改一个来自东北某合作工程机构的大型软件包。这个软件包是用一种叫“cmake”的工具构建的,目标是Linux。说实话,cmake是我见过的最让人抓狂、文档不全、结构奇怪的构建系统,考虑到我在*NIX系统上有28年的专业经验和构建大量开源代码的经历,这真是让我感到不快。

后来,我又需要用“cmake”构建一个针对MSVS(微软视觉工作室)的项目。哦,太开心了!终于有了一个可以可靠生成那些讨厌的“项目”和“解决方案”文件的工具。而且那些CMakeLists.txt文件也可以重新针对Linux!哇,我终于看到了希望。

不过,我现在的情况还是挺糟糕的。除非我有一个现成的CMakeLists.txt文件,否则我总是无法从零开始创建一个,哪怕是处理最简单的问题,花上一整天也搞不定。

我有一个任务,要用MSVS构建一个DLL(动态链接库),让Python脚本可以通过ctypes访问。简单来说,就是需要一个带有符号的DLL。由于我遇到了一个老问题,我的VS 2008和VS 2010都无法创建新的C++项目,所以我决定尝试用cmake来生成一个DLL解决方案。

我一直找不到一个现代的(也就是cmake 2.8.5之后的)完整示例来构建DLL,而这个版本的cmake在这方面应该比以前好多了。

我翻阅了这个教程 http://www.cmake.org/cmake/help/cmake_tutorial.html,结果发现这个教程糟糕透顶,因为他们希望你在学习cmake的同时写C++代码。(嘿,老兄!我连cmake都搞不定,更别提写能编译的代码了!)这个教程讲了如何构建一个简单的二进制文件,然后是使用库的二进制文件,但没有生成DLL。

我在阅读 http://www.cmake.org/Wiki/BuildingWinDLL 的时候,天真地在lib目录的CMakeLists.txt文件中添加了一些代码:

之前:

add_library(MathFunctions mysqrt.cxx)

install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

之后:

add_library(MathFunctions SHARED mysqrt.cxx)
GENERATE_EXPORT_HEADER( MathFunctions
             BASE_NAME MathFunctions
             EXPORT_MACRO_NAME MathFunctions_EXPORT
             EXPORT_FILE_NAME MathFunctions_Export.h
             STATIC_DEFINE MathFunctions_BUILT_AS_STATIC
)
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

cmake 3.0.0和cmake 2.8.12.2都对这个文件发出了警告:

CMake Error at MathFunctions/CMakeLists.txt:2 (GENERATE_EXPORT_HEADER):
  Unknown CMake command "GENERATE_EXPORT_HEADER".

这个函数似乎在cmake安装中叫做GenerateExportHeader.cmake,但无论我怎么调试都没能找出这个错误的原因。而且在网上也找不到这个错误的信息。

这就是我一天的前六个小时。

我最终决定删除那个有问题的命令,尝试这样做:

add_library(MathFunctions mysqrt.cxx)

install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

哇!cmake配置成功,生成了,MSVS也成功构建了,DLL出现在库目录的Debug子目录中。太酷了。

不过,这个DLL并没有包含允许python/ctypes访问所需函数的符号。在BuildingWinDLL页面上再找了一下,我终于找到了符号。Python非常高兴,现在我有了一个未来工作的模型,尽管这只是一个粗糙、简单的解决方案!

所以,在这段冗长的讨论之后:

  • BuildingWinDLL提到的cmake 2.8.5及以上版本,是否完全错误?
  • 正确的做法是什么?
  • 有没有人能提供一个简单的“cmake 101”示例,教我如何创建一个带有导出符号的MSVS DLL,而不是一个临时解决方案,这样我就可以扔掉我的临时方案?

附言:在stackoverflow上写文章的系统真不错。我会很享受这个过程,希望我能继续回来...

更新:在steveire的回答后:

原问题得到了解答,我发现我在BuildingWinDLL页面上错过了一个提示。我还发现我没有为自己的代码更改示例中的一个字段。

所以现在我们进入了下一个层次。使用参考示例,VS2010解决方案构建时出现了警告:

LINK : fatal error LNK1104: cannot open file 'MathFunctions\Debug\MathFunctions.lib'

我从BuildingWinDLL中了解到,GENERATE_EXPORT_HEADER()在构建DLL方面非常强大。可是.lib文件没有生成,而生成的.dll也不包含符号...

BuildingWinDLL页面讨论的是cmake 2.8.5之前的过程。页面顶部提到的2.8.5过程是如何使用GENERATE_EXPORT_HEADER()自动生成页面底部的文件。不过,仍然需要将各个部分结合起来,这一点在文本中并不清楚。

因此,MathFunctions_Export.h是由GENERATE_EXPORT_HEADER() cmake命令生成的,特定参数会创建一个C头文件,里面有一个宏用于导出符号。这个文件显然需要被明确引用,并且导出的符号需要正确限定:

#include <math.h>
#include <Mathfunctions/MathFunctions_Export.h>

MathFunctions_EXPORT double mysqrt(double v) {
    return sqrt(v);
}

添加了#include和*EXPORT限定符后,符号现在可以被导出,VS也知道生成.lib并填充.dll中的符号。

成功了!感谢所有在这个过程中帮助我的人,和我一起经历了痛苦。

1 个回答

11
include(GenerateExportHeader)

在使用它之前。

http://www.cmake.org/cmake/help/v3.0/module/GenerateExportHeader.html

CMake 默认会构建静态库,所以如果你想构建共享库,记得选择 SHARED。

撰写回答