MSVC动态链接库、静态库和导入库的命名规范是什么?

12

MSVC库构建的标准或“最流行”的命名约定是什么?

例如,对于以下平台库,foo 有以下约定:

Linux/gcc:

shared: libfoo.so
import: ---
static: libfoo.a

Cygwin/gcc:

shared: cygfoo.dll
import: libfoo.dll.a
static: libfoo.a

Windows/MinGW:

shared: libfoo.dll
import: libfoo.dll.a
static: libfoo.a

我应该使用什么来进行MSVC构建?据我所知,通常的名称为foo.dllfoo.lib,但是你通常如何区分导入库和静态库?

注意:我之所以问这个问题,是因为CMake会在它们两者之间创建非常不好的冲突,将导入库和静态库都命名为foo.lib。请参见bug report。答案将帮助我说服开发人员修复此错误。


当使用_env.SharedLibrary_和_env.StaticLibrary_具有相同目标名称时,SCons也会“创建(这个)非常不愉快的冲突”。 - Wilbur Whateley
6个回答

6
正如其他人所提到的,没有标准,但有流行的惯例。我不确定如何明确判断什么是最流行的惯例。此外,在静态库与导入库的命名方面,您所问的问题,还存在类似于发布库与调试库之间的区别,特别是在Windows上。
两种情况(即静态库与导入库,以及调试库与发布库)可以通过以下两种方式之一处理:不同的名称或不同的目录位置。我通常选择使用不同的名称,因为我觉得这样可以最大程度地减少稍后误认库类型的机会,特别是在安装或其他文件移动活动之后。
当我希望同时拥有共享和静态版本时,我通常在Windows上使用foo.dllfoo.lib作为共享库,而将foo_static.lib用作静态库。我已经看到其他人使用这个约定,所以它可能是“最流行的”。
因此,我建议将以下内容添加到您的表中:

Windows/MSVC:

shared: foo.dll
import: foo.lib
static: foo_static.lib

然后在cmake中,你可以选择:
add_library(foo_static STATIC foo.cpp)

或者

add_library(FooStatic STATIC foo.cpp)
set_target_properties(FooStatic PROPERTIES OUTPUT_NAME "foo_static") 

如果由于某些原因您不希望使用"foo_static"作为符号库名称。


6
你可以通过文件扩展名来区分库和.dll文件。但是,你需要通过文件名而不是扩展名来区分导入库和静态库。
对于构建为静态库的一组代码,不会存在导入库,也不会存在DLL的静态库。它们是两个不同的东西。
没有单一的MSVC标准文件名约定。作为一条规则,以“D”结尾的库名称通常是库代码的调试版本,例如msvcrtd.dll与msvcrt.dll,但除此之外,没有其他标准。

2
据我所知,至少没有大多数软件都符合的真正的“标准”。
我的惯例是给我的动态库和静态库相同的名称,但如果一个项目支持静态和动态链接,那就将它们放在不同的目录中。例如:
foo-static
   foo.lib

foo
   foo.lib
   foo.dll

需要链接的库取决于库目录的选择,因此它几乎完全与构建过程分离(如果使用MSVC的#pragma comment(lib,"foo.lib")功能,则不会出现在源代码中,也不会出现在链接器的导入库列表中)。

我已经看到这种情况很多次了。此外,我认为基于MSVC / Windows的项目倾向于更经常地使用单个官方链接类型 - 静态或动态。但这只是我的个人观察。

简而言之:Windows/MSVC

shared: foo.dll
import: foo.lib
static: foo.lib

你应该能够使用基于目录的模式与CMAKE(我从未使用过)一起使用。此外,我不认为这是一个“错误”。这仅仅是缺乏标准化。在每个人都喜欢不同的情况下,CMAKE做了(我认为)正确的事情,即不建立伪标准。


2

正如其他人所说,Windows上没有单一的文件命名标准。

对于我们涵盖数百个exe、dll和静态库的完整产品库,我们多年来成功地使用了以下方法,并且它已经节省了很多混乱。基本上,这是我多年来看到的几种方法的混合。

简而言之,我们所有的文件都有前缀和后缀(不包括扩展名本身)。它们都以“om”开头(基于我们公司名称),然后有一个大约识别代码区域的1或2个字符组合。

后缀解释了它们是什么类型的构建文件,并包括最多三个字母的组合,具体取决于构建方式,包括Unicode、Static、Debug(Dll构建是默认的,没有明确的后缀标识符)。当我们开始使用这个系统时,Unicode并不是那么普遍,我们必须支持Unicode和非Unicode构建(在Windows 2000操作系统之前),现在一切都是专门构建Unicode,但我们仍然使用相同的命名法。

因此,一个典型的.lib“集合”文件可能看起来像:

omfThreadud.lib (Unicode/Debug/Dll)
omfThreadusd.lib (Unicode/Static/Debug)
omfThreadu.lib (Unicode/Release/Dll)
omfThreadus.lib (Unicode/static)

所有文件都内置到一个共同的bin文件夹中,这消除了开发人员很多dll-hell问题,并使调整编译器/链接器设置更加简单-它们都使用相对路径指向同一位置,从不需要手动(或自动)复制项目所需的库。 这些后缀也消除了任何有关文件类型的混淆,并保证您不会在发布工具包上放置调试dll或反之亦然。 所有exe文件也使用类似的后缀(Unicode/Debug)并构建到同一个bin文件夹中。

同样有一个单独的“include”文件夹,每个库在include文件夹中都有一个与库/dll名称匹配的头文件(例如omfthread.h)。该文件本身#include所有由该库公开的其他项。 如果您想要foo.dll中的功能,则仅需#include“foo.h”即可使其更简单;我们的库根据功能区域高度分段-实际上我们没有“瑞士军刀”dll,因此包含库的所有功能是有意义的。(每个这些标题还包括其他必要的标题,无论是我们的内部库还是其他供应商SDK)

每个这些包含文件都在内部使用宏,这些宏使用#pramga将适当的库名称添加到链接器行中,因此单个项目不需要关心它。我们的大多数库都可以静态构建或作为DLL构建,#define OM_LINK_STATIC(如果已定义)用于确定单个项目想要哪个(我们通常使用DLL,但在某些情况下,将静态库内置到.exe中更有意义,以便部署或其他原因)。

#if defined(OM_LINK_STATIC)
 #pragma comment (lib, OMLIBNAMESTATIC("OMFTHREAD"))
#else
 #pragma comment (lib, OMLIBNAME("OMFTHREAD"))
#endif

这些宏(OMLIBNAMESTATIC和OMLIBNAME)使用_DEBUG来确定构建类型,并生成正确的库名称以添加到链接器行中。
我们在静态和dll版本的库中使用一个常见的定义,以控制在dll构建中正确导出类/函数。从库中导出的每个类或函数都使用此宏进行修饰(其名称与库的基本名称匹配,但这在很大程度上并不重要)。
class OMUTHREAD_DECLARE CThread : public CThreadBase

在项目设置的DLL版本中,我们定义OMFTHREAD_DECLARE=__declspec(dllexport),在静态库版本中,我们将OMFTHREAD_DECLARE定义为空。
在库的头文件中,我们根据客户端尝试链接的方式进行定义。
#if defined(OM_LINK_STATIC)
 #define OMFTHREAD_DECLARE
#else
 #define OMFTHREAD_DECLARE __declspec(dllimport)
#endif

一个想要使用我们内部库之一的典型项目只需将适当的包含文件添加到他们的stdafx.h中(通常是这样),然后它就可以正常工作。如果他们需要链接静态版本,只需将OM_LINK_STATIC添加到编译器设置中(或在stdafx.h中定义),然后它也可以正常工作。


2

对于库的命名规范没有标准化。传统的库名称前缀是 lib。许多链接器在命令行上有选项来将 lib 添加到库名称前面。

静态和动态库通常通过其文件扩展名进行标识,尽管这不是必需的。所以,libmath.a 将是一个静态库,而 libmath.solibmath.dll 将是一个动态库。

一种常见的命名约定是将库的类别附加到名称后面。例如,调试静态数学库将是 'libmathd.a' 或在 Windows 中是 'lib_math_debug'。一些商店还会将 Unicode 作为文件名属性添加。

如果你想,可以将 _msvc 附加到库名称中,以表示该库需要或由 MSVC 创建(以区分于 GCC 和其他工具)。当使用多个平台时,流行的约定是将对象和库放置在特定于平台的文件夹中。例如,./linux/ 文件夹将包含 Linux 的对象和库,类似地,./msw/ 是 Microsoft Windows 平台。

这是一个风格问题。风格问题通常像宗教问题一样处理:它们都没有错,没有通用风格,而且它们是个人偏好。无论你选择什么系统,都要保持一致。


2
据我所知,目前还没有关于这个的任何公约。以下是我的做法示例:
{Project}{SubModule}{Platform}{Architecture}{CompilerRuntime}_{BuildType}.lib/dll
完整的文件名应该只包含字母数字和预定下划线,并且全部小写。子模块字段及其前导下划线是可选的。
项目:保存项目名称/标识符。最好尽可能短。例如“dna”。
子模块:可选。保存模块名称。最好尽可能短。例如“dna_audio”。
平台:标识编译二进制文件的平台。例如“win32”(Windows)、“winrt”、“xbox”、“android”。
架构:描述编译二进制文件的架构。例如“x86”、“x64”、“arm”。对于各种位数相同的架构名称,请使用其名称后跟位数。例如“name16”、“name32”、“name64”。
编译器运行时:可选。并非所有二进制文件都链接到编译器运行时,但如果它们链接了,则在此处包括。例如“vc90”(Visual Studio 2008)、“gcc”。适用时可以包含公寓,例如“vc90mt”。
构建类型:可选。这可以包含字母(以任何所需顺序),每个字母都说明构建特定信息。d=debug(如果是release则省略)t=static(如果是dynamic则省略)a=ansi(如果是unicode则省略)
示例(假设项目名称为“DNA”): dna_win32_x86_vc90.lib/dll dna_win32_x64_vc90_d.lib/dll dna_win32_x86_vc90_sd.lib dna_audio_win32_x64_vc90.lib/dll dna_audio_winrt_x64_vc110.lib/dll

编译器运行时类似于vc141,但在项目文件中只定义了PlatfromToolset即v141。这有什么区别? - Fantastory
1
@Fantastory,你的问题本身就是一个高级问题(或者说是 Stack Overflow 如何称呼的)。PlatformToolset 比 CompilerRuntime 更广泛,CompilerRuntime 是 PlatformToolset 的一部分。PlatformToolset 还包括链接器和系统库等内容。随着 Windows 的新版本推出,会增加新的功能,例如 Toasts 或者 Direct3D 的新版本。这些新的或更新的库导出了这些函数,也是 PlatformToolset 的一部分。同样的,语言支持的增强,例如从 C11 到 C20 也是如此。 - Coriiander Drewitt
抱歉,这是因为一些库将它们命名为“-v140-”而不是“-vc140-”。总之,“vc140”才是正确的。 - Fantastory

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