C ++(和C,虽然它更不重要)标准状态,程序中的所有翻译单元需要具有相同的定义;这包括编译器开关之类的东西。例如,在MSVC ++上,必须在所有翻译单元中链接到正确版本的C运行时库(/MT
与/MD
对比/MTd
与/MDd
)。
但是,我们想要使用几个第三方依赖项,并且有几件事情:
/MD
和/MDd
,而另一个图书馆强制/MT
和/MTd
)我们不确定处理这类事情的最佳方法是什么。我们已经讨论了以下选项:
我们不知道该怎么办;我们无法相信我们一个人就有这些问题。我们应该选择上述选项之一,还是我没想过的第三种选择?
答案 0 :(得分:6)
严格来说,您并不需要将所有库链接到同一个运行时。假设它们是DLL,如果它们通过DLL边界传递CRT数据结构,那只是一个问题。使用一个运行时在DLL中创建FILE*
,并从与另一个运行时链接的DLL中使用它是一种灾难。使用一个运行时从DLL调用malloc
或new
,从另一个运行时调用free
/ delete
将导致许多有趣的问题。
但只要所有与CRT相关的内容都保存在DLL内部,您就可以安全地链接到使用另一个CRT的DLL。这也意味着您的调试版本可以使用与发布CRT链接的库。同样,只要您不尝试跨库混合CRT数据结构。
另请注意,大多数编译器标志不会影响ABI,因此它们可以安全地在库(或文件)之间存在差异。改变ABI的那些通常是显而易见的,就像强行打包或堆叠对齐一样。
所以我们所做的基本上是这样的:
我们目前依赖于三种不同的VS运行时(2005年,2008年和2010年),这很难处理,但它有效。对于其中的一个或两个,我们总是使用发布版本,即使在我们自己的代码的调试版本中也是如此。
维护起来有点混乱,但工作。我无法真正看到更好的方法。
当然,您应该最小化您拥有的第三方依赖项,并且当您选择第三方库时,其构建系统绝对应该是一个需要考虑的因素。有些人很多比其他人更痛苦。
但是最后,你可能最终不得不使用一些只是没有表现良好的构建系统的库,或者构建那些不值得付出努力,或者您无法创建库的调试版本。然后只需拿走你能得到的东西。让它使用它喜欢使用的任何运行时。
答案 1 :(得分:2)
我假设您故意不提及任何特定的库?
无论如何,您应该问自己在 构建系统中是否真的需要此第三方代码。
我们使用的第三方库被编译一次(使用各自的构建脚本)并检查正确的VC开关,然后检查DLL或LIB文件到使用lib的应用程序的源代码控制中。
因此,第三方库的编译是我们每次第三方发布时只做一次的事情,我们不会为构建系统带来构建第三方库的错综复杂的负担。
我猜这两种方法都有正确的论据,也许你可以在问题中提供一些细节,为什么你需要/想要在你的构建系统中拥有第三方库。
答案 2 :(得分:2)
你是对的 - 你并不是唯一一个遇到这类问题的人!
根据我们的经验,依赖关系的松散耦合(我认为手动复制文件的一种奇特方式)和修改第三方构建系统是最有效的 - 尤其是在构建Windows时。
我们发现一些有用的东西: -
记录您必须应用的更改(我们使用维基页面)以获取特定版本并包含此处所需的任何步骤/依赖项(例如,构建OpenSSL所需的Perl解释器)并在使用构建之前运行任何/所有包含的测试
我们发现重命名输出库以便根据ABI标记它们一直非常有用,而不是使用第三方生成的名称。
所以第三方C ++依赖关系X最终在我们的目录结构中(提交给svn)
X / [version_number] / include(使用lib的头文件)
X / [version_number] / lib / Windows(手动构建,测试并重命名的库)
例如
X-vc100.lib X-VC100-gd.lib
等
(我们实际上从提升名http://www.boost.org/doc/libs/1_49_0/more/getting_started/windows.html#library-naming复制了我们的命名,因为它们似乎完全合情合理)
然后您的构建系统可以选择特定的第三方依赖项(对于VS,我们使用一个继承的属性表,其中所有依赖项都被命名为用户宏,因此可以将$(x_inc)添加到项目的include目录中, $(x_lib)添加到库中 - 这些宏然后选择该特定项目所需的版本和abi。
答案 3 :(得分:1)
没有确定的答案,这取决于第三方代码接口的设计方式。如果接口是紧密耦合的(例如,共享的非不透明数据类型),最好使用自己的构建和选项重建它们。您必须分析界面并确定如何集成它们。另一方面,如果界面简单且可以轻松解耦,则可以将它们构建为dll并按需调用。当然,您将在应用程序中加载不同版本的C库,它们都有不同的io缓冲区实例,内存管理等。
如果您有可用的源代码,那么更好的选择是投入更多时间将它们集成到您自己的构建中。