Windows下Cmake生成动态库install失败、导入库概念
摘要:
- Windows下cmake install命令安装动态库文件失败
- .dll.a和.dll文件的区别
- 动态库、静态库和导入库
1、Windows下cmake install命令安装动态库文件失败
1.1 问题
cmake(V3.10.2) 从源文件生成动态库,在Windows下,以MinGW Makefiles
(其他未测试)为生成类型,最终会生成libXXX.dll, libXXX.dll.a, libXXX.a
类型的库文件。
CMakeLists中install代码如下:
add_library(XXX SHARED ${SRCS})
add_library(XXXS STATIC ${SRCS})
...
install(TARGETS XXX XXXS
# install(TARGETS myExe XXX XXX
# RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/install/bin
LIBRARY DESTINATION ${InstallDir}/lib
ARCHIVE DESTINATION ${InstallDir}/lib/static)
在Linux下会分别在${InstallDir}/lib
和${InstallDir}/lib/static
下生成libXXX.so,libXXXS.a
。
但是Windows下,lib目录下无动态库libXXX.dll
(build文件夹下存在),在lib/static
目录下有libXXX.dll.a, libXXXS.a
,即说明动态库安装失败。
于是修改CMakeLists.txt如下:
if(UNIX)
install(TARGETS XXX XXXS
# install(TARGETS myExe XXX XXX
# RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/install/bin
LIBRARY DESTINATION ${InstallDir}/lib
ARCHIVE DESTINATION ${InstallDir}/lib/static)
else(UNIX)
install(TARGETS XXX DESTINATION ${InstallDir}/lib)
install(TARGETS XXXS DESTINATION ${InstallDir}/lib/static)
endif(UNIX)
修改后编译、安装,可以看到在Windows对应版本的${InstallDir}/lib
下出现了libXXX.dll.a, libXXX.dll
,并且在${InstallDir}/lib/static
下有libXXXS.a
。
1.2 深入分析
查阅cmake文档后知道:
install(TARGETS targets... [EXPORT <export-name>]
[[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]
] [...]
[INCLUDES DESTINATION [<dir> ...]]
)
- The
TARGETS
form specifies rules for installing targets from a project. There are six kinds of target files that may be installed: ARCHIVE, LIBRARY, RUNTIME, OBJECTS, FRAMEWORK, and BUNDLE.- Executables are treated as
RUNTIME
targets, except that those marked with theMACOSX_BUNDLE
property are treated as BUNDLE targets on OS X.- Static libraries are treated as
ARCHIVE
targets, except that those marked with theFRAMEWORK
property are treated asFRAMEWORK
targets on OS X. Module libraries are always treated asLIBRARY
targets.- For non-DLL platforms shared libraries are treated as
LIBRARY
targets, except that those marked with the FRAMEWORK property are treated as FRAMEWORK targets on OS X.- For DLL platforms the DLL part of a shared library is treated as a RUNTIME target and the corresponding import library is treated as an ARCHIVE target.
- All Windows-based systems including Cygwin are DLL platforms.
- Object libraries are always treated as
OBJECTS
targets.- The
ARCHIVE, LIBRARY, RUNTIME, OBJECTS, and FRAMEWORK
arguments change the type of target to which the subsequent properties apply. If none is given the installation properties apply to all target types. If only one is given then only targets of that type will be installed (which can be used to install just a DLL or just an import library).
要点翻译如下:
- 可执行文件被当做
RUNTIME
; - 静态库一般被看做
ARCHIVE
,模块库(Module libraries)当做LIBRARY
; - 非DLL平台,共享库被当做
LIBRARY
; - DLL平台,共享库的
DLL
部分被当做RUNTIME
,导入库视为ARCHIVE
;
索引基于Windows的系统包括(Cygwin)是DLL平台。 - 目标库视为
OBJECTS
; ARCHIVE, LIBRARY, RUNTIME, OBJECTS, and FRAMEWORK
改变后续属性所作用的目标的类型。如果不指定,则安装属性适用所有目标类型。 如果仅给出一个,则将仅安装该类型的目标(可用于仅安装DLL或导入库)。
通过开始的CMakeLists代码看到,由于注释了RUNTIME
行,故在Windows下不会安装动态库。(Windows下会安装到RUNTIME
属性对应的目录)。
1.3 官方解释
One or more groups of properties may be specified in a single call to the TARGETS form of this command. A target may be installed more than once to different locations. Consider hypothetical targets myExe, mySharedLib, and myStaticLib. The code:
install(TARGETS myExe mySharedLib myStaticLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
install(TARGETS mySharedLib DESTINATION /some/full/path)
will install
myExe
to<prefix>/bin
andmyStaticLib
to<prefix>/lib/static
.
On non-DLL platformsmySharedLib
will be installed to<prefix>/lib
and/some/full/path
.
On DLL platforms themySharedLib
DLL will be installed to<prefix>/bin
and/some/full/path
and its import library will be installed to<prefix>/lib/static
and/some/full/path
.
安装情况:
myExe
--><prefix>/bin
myStaticLib
--><prefix>/lib/static
Non-DLL
platforms:
mySharedLib
--><prefix>/lib
和/some/full/path
DLL
platforms:
mySharedLib
--><prefix>/bin
和/some/full/path
mySharedLib-import-library
--><prefix>/lib/static
和/some/full/path
2、关于.dll.a和.dll的区别
*.dll.a不是静态库,而是导入库。libXXXS.a是静态库。
在1
中的安装结果里,仅ARCHIVE
属性起作用,并同时安装了导入库(libXXX.dll.a)和静态库(libXXXS.a)。从而可知.dll.a是导入库。
其他参考:
Naming the output file libjvm.dll.a will allow gcc to recognize it as a library named jvm. The .dll.a suffix indicates (by convention) that it is an import library, rather than a static library (which would simply be named libjvm.a, again by convention).
https://stackoverflow.com/questions/185326/whats-a-dll-a-file
3、动态库、静态库、导入库
静态库(也称为归档)由直接编译并链接到程序中的例程组成。编译使用静态库的程序时,程序所使用的静态库的所有功能都将成为可执行文件的一部分。在Windows上,静态库通常具有.lib扩展名,而在linux上,静态库通常具有.a(archive)扩展名。
静态库的一个优点是,用户只需发布可执行文件即可。由于库成为程序的一部分,这将确保程序始终使用正确版本的库。另外,因为静态库成为程序的一部分,所以可以像为自己的程序编写的功能一样使用它们。
缺点是,由于库的副本成为使用它的每个可执行文件的一部分,这可能会造成大量的空间浪费。静态库也不容易升级——要更新库,需要替换整个可执行文件。
动态库(也称为共享库)由运行时加载到应用程序中的子程序组成。当编译使用动态库的程序时,库不会成为可执行文件的一部分,而是作为单独的单元保留。在Windows上,动态库通常具有.dll(动态链接库)扩展名,而在Linux上,动态库通常具有.so(共享对象)扩展名。
动态库的一个优点是许多程序可以共享一个副本,这节省了空间。
动态库可以升级到一个新版本,而不必替换使用它的所有可执行文件。
由于动态库未链接到程序中,因此使用动态库的程序必须显式加载并与动态库交互。这种机制可能会让人困惑,并使与动态库的交互变得不易处理。为了使动态库更易于使用,可以使用导入库。
导入库是让加载和使用动态库的过程变得自动化的库。在Windows上,这通常是通过与动态库(.dll)同名的小型静态库(.lib)来完成的。小型静态库在编译时链接到程序中,然后动态库的功能可以像静态库一样有效地使用。在Linux上,共享对象(.so)文件兼作动态库和导入库。大多数链接器可以在创建动态库时为动态库构建导入库。
4、cmake 3.13开始支持安装其他目录所创建的目标
According to this bugreport, install(TARGETS) command flow accepts only targets created within the same directory.
Since CMake 3.13 install(TARGETS) can work even with targets created in other directories.
install(TARGETS) can install targets that were created in other directories. When using such cross-directory install rules, running make install (or similar) from a subdirectory will not guarantee that targets from other directories are up-to-date.