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.