CMake:不能 link 使用共享 zlib 包装器库的简单项目
CMake: Can't link simple project that uses shared zlib wrapper library
在我的情况下,我有两个 CMake 项目(如果重要的话,使用 MSYS Makefiles 构建):
- 一个是共享库,使用 Zlib 和 Libzip 为库用户提供压缩功能,我们称它为 mylib.
- 另一个应用程序使用 mylib 来执行一些压缩操作。它不必担心 Zlib、Libzip 或任何东西。我们称它为 app.
我在下面创建了一个简单的示例来演示我遇到的奇怪问题。简单地说,app 在 运行 make 时拒绝 link,但前提是某些 Libzip 代码在 mylib(编译并且link没问题,无论如何)。
Zlib 和 Libzip 源代码位于 C:/Dev/Libs.
lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
# Set project
project(mylib)
set(LIBS_DIR "C:/Dev/Libs")
# Add mylib sources
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src")
file(GLOB_RECURSE SRCS "src/*.cpp")
add_library(mylib SHARED ${SRCS})
# Add Zlib
set(ZLIB_ROOT "${LIBS_DIR}/zlib-1.2.11")
include_directories(${ZLIB_ROOT})
target_include_directories(mylib PUBLIC ${ZLIB_ROOT})
file(GLOB ZLIB_SRCS "${ZLIB_ROOT}/*.c")
add_library(zlib STATIC ${ZLIB_SRCS})
# Add Libzip
set(LIBZIP_ROOT "${LIBS_DIR}/libzip-1.1.3")
include_directories("${LIBZIP_ROOT}/lib")
target_include_directories(mylib PUBLIC "${LIBZIP_ROOT}/lib")
file(GLOB LIBZIP_SRCS "${LIBZIP_ROOT}/lib/*.c")
add_library(libzip STATIC ${LIBZIP_SRCS})
set(LIBS_LINK zlib libzip)
set(LIBS_INSTALL zlib libzip)
set(LIBS_EXPORT zlib libzip)
target_link_libraries(mylib PUBLIC ${LIBS_LINK})
# https://rix0r.nl/blog/2015/08/13/cmake-guide/
# Define headers for this library. PUBLIC headers are used for
# compiling the library, and will be added to consumers' build
# paths.
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include>
PRIVATE src)
# 'make install' to the correct locations (provided by GNUInstallDirs).
include(GNUInstallDirs)
install(TARGETS mylib ${LIBS_INSTALL} EXPORT mylibConfig
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) # This is for Windows
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# This makes the project importable from the install directory
# Put config file in per-project dir (name MUST match), can also
# just go into 'cmake'.
install(EXPORT mylibConfig DESTINATION share/mylib/cmake)
# This makes the project importable from the build directory
export(TARGETS mylib ${LIBS_EXPORT} FILE mylibConfig.cmake)
lib/src/lib.hpp
#pragma once
namespace lib
{
int func();
}
lib/src/lib.cpp
#include "lib.hpp"
#define ZIP_STATIC
#include "zip.h"
int lib::func()
{
zip_source_t *src;
zip_error_t error;
src = zip_source_buffer_create(0, 1, 0, &error);
return 10;
}
app/CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(app)
# Add app sources
file(GLOB_RECURSE SRCS "src/*.cpp")
add_executable(app ${SRCS})
# Add mylib
set(mylib_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lib")
find_package(mylib REQUIRED)
configure_file("${mylib_DIR}/libmylib.dll" "libmylib.dll" COPYONLY)
target_link_libraries(app mylib)
app/src/app.hpp
#pragma once
#include <lib.hpp>
app/src/app.cpp
#include <iostream>
#include "app.hpp"
int main()
{
std::cout << lib::func() << std::endl;
return 0;
}
构建 mylib
的输出
$ make
[ 13%] Built target zlib
[ 98%] Built target libzip
Scanning dependencies of target mylib
[ 99%] Building CXX object CMakeFiles/mylib.dir/src/lib.cpp.obj
[100%] Linking CXX shared library libmylib.dll
[100%] Built target mylib
构建 app
的输出
$ make
Scanning dependencies of target app
[ 50%] Building CXX object CMakeFiles/app.dir/src/app.cpp.obj
[100%] Linking CXX executable app.exe
CMakeFiles/app.dir/objects.a(app.cpp.obj):app.cpp:(.text+0x17): undefined reference to `lib::func()'
collect2.exe: error: ld returned 1 exit status
make[2]: *** [app.exe] Error 1
make[1]: *** [CMakeFiles/app.dir/all] Error 2
make: *** [all] Error 2
如果我在lib/src/lib.cpp中注释src = zip_source_buffer_create(0, 1, 0, &error);
,重建然后重建应用:
$ make
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Dev/Builds/cmaketest/app
[ 50%] Linking CXX executable app.exe
[100%] Built target app
$ ./app
10
产生了预期的结果。
问题一定出在 app 的 CMakeLists 中,因为 mylib 构建良好,没有任何 linking 错误。还是我在 mylib 中错误地添加了 zlib/libzip?我需要静态地 link 给他们,但我无法让它与 find_package
一起工作。
谢谢!
复制 .dll.a 文件后也能正常工作了:
configure_file("${mylib_DIR}/libmylib.dll.a" "libmylib.dll.a" COPYONLY)
并在 lib 中为我的所有方法添加以下前缀:
__declspec(dllexport)
在我的情况下,我有两个 CMake 项目(如果重要的话,使用 MSYS Makefiles 构建):
- 一个是共享库,使用 Zlib 和 Libzip 为库用户提供压缩功能,我们称它为 mylib.
- 另一个应用程序使用 mylib 来执行一些压缩操作。它不必担心 Zlib、Libzip 或任何东西。我们称它为 app.
我在下面创建了一个简单的示例来演示我遇到的奇怪问题。简单地说,app 在 运行 make 时拒绝 link,但前提是某些 Libzip 代码在 mylib(编译并且link没问题,无论如何)。
Zlib 和 Libzip 源代码位于 C:/Dev/Libs.
lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
# Set project
project(mylib)
set(LIBS_DIR "C:/Dev/Libs")
# Add mylib sources
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src")
file(GLOB_RECURSE SRCS "src/*.cpp")
add_library(mylib SHARED ${SRCS})
# Add Zlib
set(ZLIB_ROOT "${LIBS_DIR}/zlib-1.2.11")
include_directories(${ZLIB_ROOT})
target_include_directories(mylib PUBLIC ${ZLIB_ROOT})
file(GLOB ZLIB_SRCS "${ZLIB_ROOT}/*.c")
add_library(zlib STATIC ${ZLIB_SRCS})
# Add Libzip
set(LIBZIP_ROOT "${LIBS_DIR}/libzip-1.1.3")
include_directories("${LIBZIP_ROOT}/lib")
target_include_directories(mylib PUBLIC "${LIBZIP_ROOT}/lib")
file(GLOB LIBZIP_SRCS "${LIBZIP_ROOT}/lib/*.c")
add_library(libzip STATIC ${LIBZIP_SRCS})
set(LIBS_LINK zlib libzip)
set(LIBS_INSTALL zlib libzip)
set(LIBS_EXPORT zlib libzip)
target_link_libraries(mylib PUBLIC ${LIBS_LINK})
# https://rix0r.nl/blog/2015/08/13/cmake-guide/
# Define headers for this library. PUBLIC headers are used for
# compiling the library, and will be added to consumers' build
# paths.
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include>
PRIVATE src)
# 'make install' to the correct locations (provided by GNUInstallDirs).
include(GNUInstallDirs)
install(TARGETS mylib ${LIBS_INSTALL} EXPORT mylibConfig
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) # This is for Windows
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# This makes the project importable from the install directory
# Put config file in per-project dir (name MUST match), can also
# just go into 'cmake'.
install(EXPORT mylibConfig DESTINATION share/mylib/cmake)
# This makes the project importable from the build directory
export(TARGETS mylib ${LIBS_EXPORT} FILE mylibConfig.cmake)
lib/src/lib.hpp
#pragma once
namespace lib
{
int func();
}
lib/src/lib.cpp
#include "lib.hpp"
#define ZIP_STATIC
#include "zip.h"
int lib::func()
{
zip_source_t *src;
zip_error_t error;
src = zip_source_buffer_create(0, 1, 0, &error);
return 10;
}
app/CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(app)
# Add app sources
file(GLOB_RECURSE SRCS "src/*.cpp")
add_executable(app ${SRCS})
# Add mylib
set(mylib_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lib")
find_package(mylib REQUIRED)
configure_file("${mylib_DIR}/libmylib.dll" "libmylib.dll" COPYONLY)
target_link_libraries(app mylib)
app/src/app.hpp
#pragma once
#include <lib.hpp>
app/src/app.cpp
#include <iostream>
#include "app.hpp"
int main()
{
std::cout << lib::func() << std::endl;
return 0;
}
构建 mylib
的输出$ make
[ 13%] Built target zlib
[ 98%] Built target libzip
Scanning dependencies of target mylib
[ 99%] Building CXX object CMakeFiles/mylib.dir/src/lib.cpp.obj
[100%] Linking CXX shared library libmylib.dll
[100%] Built target mylib
构建 app
的输出$ make
Scanning dependencies of target app
[ 50%] Building CXX object CMakeFiles/app.dir/src/app.cpp.obj
[100%] Linking CXX executable app.exe
CMakeFiles/app.dir/objects.a(app.cpp.obj):app.cpp:(.text+0x17): undefined reference to `lib::func()'
collect2.exe: error: ld returned 1 exit status
make[2]: *** [app.exe] Error 1
make[1]: *** [CMakeFiles/app.dir/all] Error 2
make: *** [all] Error 2
如果我在lib/src/lib.cpp中注释src = zip_source_buffer_create(0, 1, 0, &error);
,重建然后重建应用:
$ make
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Dev/Builds/cmaketest/app
[ 50%] Linking CXX executable app.exe
[100%] Built target app
$ ./app
10
产生了预期的结果。
问题一定出在 app 的 CMakeLists 中,因为 mylib 构建良好,没有任何 linking 错误。还是我在 mylib 中错误地添加了 zlib/libzip?我需要静态地 link 给他们,但我无法让它与 find_package
一起工作。
谢谢!
复制 .dll.a 文件后也能正常工作了:
configure_file("${mylib_DIR}/libmylib.dll.a" "libmylib.dll.a" COPYONLY)
并在 lib 中为我的所有方法添加以下前缀:
__declspec(dllexport)