CMake:如何尽可能地生成“静态”的二进制文件

51
我希望能够控制在CMake中找到/链接二进制文件所使用的库类型。最终目标是生成尽可能静态的二进制文件,即与每个具有静态版本的库进行静态链接。这很重要,因为可以在测试期间在不同系统之间实现二进制文件的可移植性。
目前似乎很难实现此目标,因为FindXXX.cmake包,或更确切地说是find_library命令,总是会选择动态库,无论静态库和动态库是否都可用。
如何以优雅的方式实现此功能的提示将非常受欢迎!

这并不完全是一个重复的问题:http://stackoverflow.com/questions/2113231/making-cmake-choose-static-linkage-when-possible,该问题是针对GCC特定的。 - Jack Kelly
实际上,不仅仅是GCC特定的,而且这也是一个不方便的解决方案。请参阅我在另一个问题中的评论。 - pszilard
@pszilard,你最终找到解决方案了吗?我正在尝试使用g++做同样的事情。 - augustin
1
在没有得到适当答案的情况下,如何“顶起”一个有趣的问题是什么? - augustin
@augustin - 不确定,但这绝对是一个有趣的问题 :) - warren
3
通过设置赏金来提高帖子的曝光度。 - Bradley Kreider
2个回答

29

我做了一些调查,虽然我找不到一个令人满意的解决方案,但我找到了一个半解决方案。

静态构建的问题归结为以下3点:

  1. 构建和链接项目的内部库。

    相当简单,只需要将 BUILD_SHARED_LIBS 开关翻转为 OFF

  2. 寻找外部库的静态版本。

    唯一的方法似乎是设置 CMAKE_FIND_LIBRARY_SUFFIXES 以包含所需文件的后缀名(它是一个优先级列表)。

    这个解决方案相当“肮脏”,非常违背 CMake 跨平台的愿望。在我看来,这应该在 CMake 的后台处理,但据我所知,由于在 Windows 上的“.lib”混淆,CMake 的开发人员似乎更喜欢当前的实现。

  3. 静态链接系统库。

CMake 提供了一个选项 LINK_SEARCH_END_STATIC,根据文档:“结束链接行,以使用静态系统库。” 人们会认为,这就是解决问题的方法。然而,目前的实现似乎无法胜任。如果打开此选项,CMake 会生成一个带有参数列表的隐式链接器调用,其中包含传递给链接器的选项,包括 -Wl,-Bstatic。然而,这还不够。仅仅告诉链接器进行静态链接会导致错误,在我的情况下是: /usr/bin/ld: cannot find -lgcc_s。缺少的是通过 -static 参数告诉 gcc 需要进行静态链接,这个参数并没有被 CMake 生成到链接器调用中。我认为这是个 bug,但我还没有从开发人员那里得到确认。

最后,我认为所有这些都应该由CMake在幕后完成,毕竟它并不是很复杂,除非在Windows上执行时会变得复杂……


2
你不应该将 BUILD_SHARED_LIBS 设置为关闭(这样它就不会构建共享库)吗? - Nooble
哈哈,近五年来你是第一个注意到那个错误的人。谢谢。 - pszilard

16
一个良好的FindXXX.cmake文件会包含这方面的内容。如果你查看FindBoost.cmake,你可以设置Boost_USE_STATIC_LIBS变量来控制它是否找到静态或共享库。不幸的是,大多数软件包没有实现这个功能。
如果模块使用了find_library命令(大多数都是),那么你可以通过CMAKE_FIND_LIBRARY_SUFFIXES变量改变CMake的行为。下面是从FindBoost.cmake中相关的CMake代码:
IF(WIN32)
    SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
ELSE(WIN32)
    SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
ENDIF(WIN32)

你可以在调用find_package之前放置这个,或者更好的方法是修改.cmake文件本身并向社区做出贡献。
对于我在项目中使用的.cmake文件,我将它们全部保存在源代码控制的自己的文件夹中。我这样做是因为我发现一些库的正确.cmake文件不一致,而保留自己的副本则允许我进行修改并确保检出代码的每个人都拥有相同的构建系统文件。

感谢您的评论,与此同时,我已经得出结论,上述方法是唯一可行的解决方案,以便检测到外部库的静态版本。然而,这是一种相当不好的方式,我宁愿不必指定后缀。不幸的是,根据在CMake邮件列表上的讨论,由于Windows具有相当混乱的库命名,似乎没有计划正确实现它。虽然这应该适用于大多数*NIX系统。 - pszilard
请注意,如果您只想检测静态库(因此可能会导致配置失败),则上述命令不应将静态库文件名添加到CMAKE_FIND_LIBRARY_SUFFIXES之前,而是将该变量设置为这些文件名。这会阻止find_*()函数从通常的后缀中获取共享库。 - mabraham
1
请注意,如果库在隐式链接目录(例如系统路径)中找到,则FindXXXX.cmake包的制作质量并不重要。在这种情况下,CMake有意回到-lXXXX,以允许链接器使用其正常解析方式-请参见http://public.kitware.com/pipermail/cmake/2015-January/059702.html。 - mabraham
这应该是:IF(MSVC),对吗? - frankelot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接