c++cmakezliblibzip

CMake: Can't link simple project that uses shared zlib wrapper library


In my situation I have two CMake projects (built using MSYS Makefiles if that matters):

  1. One is a shared library that uses Zlib and Libzip to provide zipping functionality to the library user, let's call it mylib.
  2. Another is an app that uses mylib to do some zipping stuff. It doesn't have to worry about Zlib, Libzip or anything. Let's call it app.

I have created a bare-bones example below that demonstrates the bizarre issue I'm having. Simply put, app refuses to link when running make, but only if some Libzip code is left uncommented in mylib (which compiles and links fine, no matter what).

Zlib and Libzip source code is located in 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;
}

Output from building 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

Output from building 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

If I comment src = zip_source_buffer_create(0, 1, 0, &error); in lib/src/lib.cpp, rebuild and then rebuild app:

$ 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

Which yields the expected result.

The issue must lie in the CMakeLists of app, since mylib builds fine without any linking errors. Or am I adding zlib/libzip incorrectly in mylib? I need to link to them statically and I couldn't get it to work with find_package.

Thanks!


Solution

  • Got it working after copying over the .dll.a file as well:

    configure_file("${mylib_DIR}/libmylib.dll.a" "libmylib.dll.a" COPYONLY)
    

    and prefixing all my methods in lib with the following:

    __declspec(dllexport)