I still consider myself a fairly novice C++ programmer and with CMake I have even less experience.
In an Eclipse CMake project I have written a shared library for accessing an XML database (BaseX). In the IDE the code compiles without errors to a shared object. In another project I wrote a test program that links to the library. This program compiles with the public API and works as expected.
According to this article ‘Modern CMake tutorial for C++ library developers’, it should be able to package a library. This article provides a template that should make packaging easy (GitHub - pananton/cpp-lib-template: Template for C++ library built with CMake).
- In this template I first replaced all occurrences of Myliband toMYLIBtoBasexCpprespectivelyBASEXCPP.
- The commands cmake -S . -B Build/Debug --fresh --preset=dev-linux -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Debug -G Ninjaandninja(after cd to Build/Debug) result in a shared library with only 1 function (Add) and an executable (libTest). This program uses theAddfunction that is provided by the library.
I then added the dependency with Curl in CMakeLists.txt and copied the source code of my library to the directory tree. The new CMakeLists.txt, the public API BasexCpp.h and the testfile main.cpp are added below.
Even after adding my sources, the test program works. However, when I uncomment the code in main.cpp, ninja returns this error message:
[11/11] Linking CXX executable examples/add/libTest
FAILED: examples/add/libTest 
: && /usr/lib64/ccache/c++ -std=c++20 -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wsign-conversion -Wcast-align -Wcast-qual -Wnull-dereference -Woverloaded-virtual -Wformat=2 -Werror -g  examples/add/CMakeFiles/libTest.dir/main.cpp.o -o examples/add/libTest  -Wl,-rpath,/home/bengbers/Thuis/CPP_LIB4/Build/Debug  libBasexCpp.so.1.0.0  -Llib64  -lcurlpp  -L/usr/lib64  -lcurl && :
/usr/bin/ld: examples/add/CMakeFiles/libTest.dir/main.cpp.o: in function `main':
/home/bengbers/Thuis/CPP_LIB4/examples/add/main.cpp:19:(.text+0xf6): undefined reference to `BasexCpp::BasexClient::BasexClient(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
/usr/bin/ld: /home/bengbers/Thuis/CPP_LIB4/examples/add/main.cpp:21:(.text+0x154): undefined reference to `BasexCpp::BasexClient::Create(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
/usr/bin/ld: /home/bengbers/Thuis/CPP_LIB4/examples/add/main.cpp:24:(.text+0x185): undefined reference to `BasexCpp::BasexClient::~BasexClient()'
/usr/bin/ld: /home/bengbers/Thuis/CPP_LIB4/examples/add/main.cpp:24:(.text+0x1f8): undefined reference to `BasexCpp::BasexClient::~BasexClient()'
collect2: fout: ld gaf exit-status 1 terug
ninja: build stopped: subcommand failed.
I don’t know how to interpret the results from the command readelf -s Build/Debug/libBasexCpp.so > ReadElf.out but after some googling I learned that readelf can be used to inspect the contents from the library.
This is part of ReadElf.out
    9: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS Add.cpp
    10: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS Base.cpp
    11: 00000000000451c7     1 OBJECT  LOCAL  DEFAULT   14 _ZNSt8__detail30[...]
    12: 00000000000451c8     1 OBJECT  LOCAL  DEFAULT   14 _ZNSt8__detail30[...]
    13: 00000000000451c9     1 OBJECT  LOCAL  DEFAULT   14 _ZNSt8__detail30[...]
    14: 00000000000451ca     1 OBJECT  LOCAL  DEFAULT   14 _ZNSt8__detail14[...]
    15: 00000000000451cb     1 OBJECT  LOCAL  DEFAULT   14 _ZNSt8__detail19[...]
    16: 00000000000451cc     1 OBJECT  LOCAL  DEFAULT   14 _ZNSt8__detail14[...]
    17: 00000000000451cd     1 OBJECT  LOCAL  DEFAULT   14 _ZNSt8__detail19[...]
I should expect that the lines 11-17 would export the Base-constructor.
Cane someone help me and tell me what I am doing wrong?
Ben
CmakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(BasexCpp
    VERSION 1.0.0
    DESCRIPTION "Template for C++ library built with CMake"
    LANGUAGES CXX)
#----------------------------------------------------------------------------------------------------------------------
# general settings and options
#----------------------------------------------------------------------------------------------------------------------
include(cmake/utils.cmake)
include(GNUInstallDirs)
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" is_top_level)
# BASEXCPP_SHARED_LIBS option (undefined by default) can be used to force shared/static build
set(BASEXCPP_SHARED_LIBS ON)
option(BASEXCPP_BUILD_TESTS "Build BasexCpp tests" OFF)
# option(BASEXCPP_BUILD_EXAMPLES "Build BasexCpp examples" ON)
option(BASEXCPP_BUILD_DOCS "Build BasexCpp documentation" OFF)
option(BASEXCPP_INSTALL "Generate target for installing BasexCpp" ${is_top_level})
set_if_undefined(BASEXCPP_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/BasexCpp" CACHE STRING
    "Install path for BasexCpp package-related CMake files")
if(DEFINED BASEXCPP_SHARED_LIBS)
    set(BUILD_SHARED_LIBS ${BASEXCPP_SHARED_LIBS})
endif()
if(NOT DEFINED CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
set_if_undefined(CMAKE_CXX_VISIBILITY_PRESET hidden)
set_if_undefined(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
add_library(BasexCpp) # initialized below
add_library(BasexCpp::BasexCpp ALIAS BasexCpp)
#----------------------------------------------------------------------------------------------------------------------
# BasexCpp dependencies
#----------------------------------------------------------------------------------------------------------------------
# Search for your dependencies here
include(FindPkgConfig)
pkg_check_modules(CURLPP REQUIRED curlpp)
include(FindCURL)
find_package(CURL REQUIRED)
if(NOT CURL_FOUND)
  message(FATAL_ERROR "Could not find CURL")
else()
  target_link_libraries(${PROJECT_NAME} ${CURLPP_LDFLAGS})
endif()
#----------------------------------------------------------------------------------------------------------------------
# BasexCpp sources
#----------------------------------------------------------------------------------------------------------------------
include(GenerateExportHeader)
set(export_file_name "export_shared.h")
if(NOT BUILD_SHARED_LIBS)
    set(export_file_name "export_static.h")
endif()
generate_export_header(BasexCpp EXPORT_FILE_NAME include/BasexCpp/${export_file_name})
target_include_directories(BasexCpp PRIVATE src)
set(public_headers
    include/BasexCpp/export.h
#    include/BasexCpp/Add.h
    include/BasexCpp/BasexCpp.h
)
set(sources
    ${public_headers}
    src/Add.cpp
    src/Base.cpp
    src/BasexClient.cpp
    src/BasexSocket.cpp
    src/ClientUtils.cpp
    src/QueryObject.cpp
    src/ResponseObj.cpp
    src/BasexClient.h
    src/BasexSocket.h
    src/ClientUtils.h
    src/QueryObject.h
    src/ResponseObj.h
    )
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources})
#----------------------------------------------------------------------------------------------------------------------
# BasexCpp target
#----------------------------------------------------------------------------------------------------------------------
include(CMakePackageConfigHelpers)
target_sources(BasexCpp PRIVATE ${sources})
target_compile_definitions(BasexCpp PUBLIC "$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:BASEXCPP_STATIC_DEFINE>")
target_include_directories(BasexCpp
    PUBLIC
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
set_target_properties(BasexCpp PROPERTIES
    SOVERSION ${PROJECT_VERSION_MAJOR}
    VERSION ${PROJECT_VERSION})
if(BASEXCPP_INSTALL AND NOT CMAKE_SKIP_INSTALL_RULES)
    configure_package_config_file(cmake/BasexCpp-config.cmake.in BasexCpp-config.cmake
        INSTALL_DESTINATION "${BASEXCPP_INSTALL_CMAKEDIR}")
    write_basic_package_version_file(BasexCpp-config-version.cmake
        COMPATIBILITY SameMajorVersion)
    install(TARGETS BasexCpp EXPORT BasexCpp_export
        RUNTIME COMPONENT BasexCpp
        LIBRARY COMPONENT BasexCpp NAMELINK_COMPONENT BasexCpp-dev
        ARCHIVE COMPONENT BasexCpp-dev
        INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
    install(DIRECTORY include/
        TYPE INCLUDE
        COMPONENT BasexCpp-dev)
    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/BasexCpp/${export_file_name}"
        COMPONENT BasexCpp-dev
        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/BasexCpp")
    set(targets_file "BasexCpp-shared-targets.cmake")
    if(NOT BUILD_SHARED_LIBS)
        set(targets_file "BasexCpp-static-targets.cmake")
    endif()
    install(EXPORT BasexCpp_export
        COMPONENT BasexCpp-dev
        FILE "${targets_file}"
        DESTINATION "${BASEXCPP_INSTALL_CMAKEDIR}"
        NAMESPACE BasexCpp::)
    install(FILES
        "${CMAKE_CURRENT_BINARY_DIR}/BasexCpp-config.cmake"
        "${CMAKE_CURRENT_BINARY_DIR}/BasexCpp-config-version.cmake"
        COMPONENT BasexCpp-dev
        DESTINATION "${BASEXCPP_INSTALL_CMAKEDIR}")
    if(MSVC)
        set(pdb_file "")
        set(pdb_file_destination "")
        if(BUILD_SHARED_LIBS)
            set(pdb_file "$<TARGET_PDB_FILE:BasexCpp>")
            set(pdb_file_destination "${CMAKE_INSTALL_BINDIR}")
        else()
            # TARGET_PDB_FILE does not work for pdb file generated for static library build, determining it manually
            set(pdb_file "$<TARGET_FILE_DIR:BasexCpp>/$<TARGET_FILE_PREFIX:BasexCpp>$<TARGET_FILE_BASE_NAME:BasexCpp>.pdb")
            set(pdb_file_destination "${CMAKE_INSTALL_LIBDIR}")
        endif()
        install(FILES "${pdb_file}"
            COMPONENT BasexCpp-dev
            CONFIGURATIONS Debug RelWithDebInfo
            DESTINATION "${pdb_file_destination}"
            OPTIONAL)
    endif()
endif()
#----------------------------------------------------------------------------------------------------------------------
# other targets
#----------------------------------------------------------------------------------------------------------------------
if(BASEXCPP_BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()
if(BASEXCPP_BUILD_EXAMPLES)
    add_subdirectory(examples)
endif()
if(BASEXCPP_BUILD_DOCS)
    find_package(Doxygen REQUIRED)
    doxygen_add_docs(docs include)
endif()
BasexCpp.h
Edit
I have added BASEXCPP_EXPORT to all the class methods that should be exported.
Also I added another testfunction (minus'). minusandadd` will be removed in the final library.
#pragma once
#ifndef BASEXCPP_H_
#define BASEXCPP_H_
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <cstddef>
#include <iomanip>
#include <wordexp.h>
#include <iostream>
#include <list>
#include <sys/types.h>
#include <stdlib.h>
typedef std::vector < std::byte > ByteVect;
typedef std::vector < ByteVect> VectOfByteVect;
#include <memory>
class BasexSocket;
typedef std::shared_ptr < BasexSocket > BasexSock_Sptr;
#include <utility>
#include <cassert>
#include <stdexcept>
#include <fstream>
#include <sstream>
#include <array>
#include <regex>
#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <curlpp/Exception.hpp>
#include <cstdio>
#ifdef __WIN32__
#include <winsock2.h>
#include <Windows.h>
#else
#include <sys/socket.h>
#include <unistd.h>
#endif
#include <BasexCpp/export.h>
using namespace std;
namespace BasexCpp {
BASEXCPP_EXPORT  int add(int a, int b);
BASEXCPP_EXPORT  int minus(int a, int b);
class QueryObject;
typedef std::unique_ptr < QueryObject > QueryObj_Uptr;
// 1 ### #include "ClientUtils.h"
BASEXCPP_EXPORT  ByteVect getBytes (string const &s);
BASEXCPP_EXPORT  string getChar (ByteVect const &b);
BASEXCPP_EXPORT  string pathExpander (const string & s, int flags = 0);
BASEXCPP_EXPORT  std::ostream & debug_dump (const ByteVect & str, std::ostream & stm = std::cout);
BASEXCPP_EXPORT  std::ostream & debug_dump (const string & str, std::ostream & stm = std::cout);
// 2 ### #include "Base.h"
class BASEXCPP_EXPORT Base {
public:
BASEXCPP_EXPORT 	explicit Base (const string &, const int &, const string &, const string &);
BASEXCPP_EXPORT 	explicit Base (const string &, const string &, const string &, const string &);
BASEXCPP_EXPORT 	explicit Base (BasexSock_Sptr socket);
protected:
	BasexSock_Sptr Socket;
};
// 3 ### #include "ResponseObj.h"
class BASEXCPP_EXPORT ResponseObj {
public:
	ResponseObj ();
BASEXCPP_EXPORT 	explicit ResponseObj (const string &, ByteVect &);
};
// 4 ### #include "QueryObject.h"
class BASEXCPP_EXPORT QueryObject: virtual public Base {
public:
BASEXCPP_EXPORT 	void Close ();
BASEXCPP_EXPORT 	void Bind (const string & name, const string & value, const string & type = "");
BASEXCPP_EXPORT 	void Bind (const string & name, const std::list < string > &valueList, const string & type = "");
BASEXCPP_EXPORT 	void Bind (const string & name, const std::list < string > &valueList, const std::list < string > &typeList);
BASEXCPP_EXPORT 	ByteVect Next ();
BASEXCPP_EXPORT 	bool More ();
BASEXCPP_EXPORT 	void Exec ();
BASEXCPP_EXPORT 	void Info ();
BASEXCPP_EXPORT 	void Options ();
BASEXCPP_EXPORT 	void Context (const string & value, const string & type = "");
BASEXCPP_EXPORT 	void Updating ();
BASEXCPP_EXPORT 	void Full ();
BASEXCPP_EXPORT 	string getQueryString ();
BASEXCPP_EXPORT 	string getStatus ();
BASEXCPP_EXPORT 	ByteVect getResultBytes ();
BASEXCPP_EXPORT 	ByteVect getResult ();
BASEXCPP_EXPORT 	string getResultString ();
BASEXCPP_EXPORT 	string asString (const ByteVect & vect);
	ResponseObj Response;
};
// 5 ### #include "BasexClient.h"
class BASEXCPP_EXPORT BasexClient: virtual public Base {
public:
BASEXCPP_EXPORT 	explicit BasexClient (const string &, const int &, const string &, const string &);
BASEXCPP_EXPORT 	explicit BasexClient (const string &, const string &, const string &, const string &);
BASEXCPP_EXPORT 	virtual ~ BasexClient ();
BASEXCPP_EXPORT 	void Execute (const string & command);
BASEXCPP_EXPORT 	void Create (const string & dbName, const string & content = "");
BASEXCPP_EXPORT 	void Add (const string & path, const string & input);
BASEXCPP_EXPORT 	void Put (const string & path, const string & input);
BASEXCPP_EXPORT 	void PutBinary (const string & path, const ByteVect & input);
BASEXCPP_EXPORT 	QueryObj_Uptr Query (const string & query);
BASEXCPP_EXPORT 	string getCaller ();
BASEXCPP_EXPORT 	string getInfo ();
BASEXCPP_EXPORT 	string getStatus ();
BASEXCPP_EXPORT 	ByteVect getResultBytes ();
BASEXCPP_EXPORT 	ByteVect getResult ();
};
}
#endif // BASEXCPP_H_
Main.cpp
#include <BasexCpp/BasexCpp.h>
#include <iostream>
using namespace std;
using namespace BasexCpp;
int main(int, char*[])
{
    // auto sum = BasexCpp::add(3, 1);
    auto sum = add(3, 1);
    std::cout << sum << std::endl;
/*
	string DBHOST("localhost");
	int DBPORT(1984);
	string DBUSERNAME("Test");
	string DBPASSWORD("testBaseX");
	BasexClient Session (DBHOST, DBPORT, DBUSERNAME, DBPASSWORD);
	Session.Create("TestCpp");
*/
    return 0;
}
 )
)
