0

I am very inexperienced when it comes to C++ on Windows. For some odd reason add_library(<some-target-name> SHARED) generates both a static library (.lib) and a dynamic one (.dll). This wouldn't be an issue per say if it didn't break the following project during the linking stage.

CMakeLists.txt (library):

cmake_minimum_required (VERSION 3.8)

project(test
    LANGUAGES CXX
)

#set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

set(SRC
    ${CMAKE_CURRENT_SOURCE_DIR}/src/test.cpp
)
set(INCLUDE_DIR
    include
    #${PROJECT_BINARY_DIR}
)
include_directories(test ${INCLUDE_DIR})


add_library(test SHARED)
target_sources(test PUBLIC ${SRC})
include(GenerateExportHeader)
generate_export_header(test
    BASE_NAME test
    EXPORT_MACRO_NAME test_EXPORT
    STATIC_DEFINE test_BUILT_AS_STATIC
    #EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/deploy/test_export.h  
    EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/test_export.h
)

CMakeLists.txt (executable that uses library):

cmake_minimum_required (VERSION 3.8)
project(playground
    LANGUAGES CXX CUDA
)

set(SRC
    ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
set(INCLUDE_DIR
    ${CMAKE_SOURCE_DIR}/test/include
)

link_directories(${CMAKE_BINARY_DIR}/deploy)
add_executable(playground ${SRC})
target_include_directories(playground PUBLIC ${INCLUDE_DIR})
target_link_libraries(playground PRIVATE test)

CMakeLists.txt (top-level project):

cmake_minimum_required (VERSION 3.8)

project ("mainproject")

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/deploy)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/deploy)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/deploy)

add_subdirectory ("test")
add_subdirectory ("playground")

You can probably put anything in the source files above to reproduce the error. In my case I have the following simple content:

test.h (library)

#include "test_export.h"
#include <string>

namespace mainproject
{
    namespace library
    {
        class Test {
        private:
            std::string txt;
        public:
            test_EXPORT Test(std::string);
            virtual test_EXPORT void msg();
        };
    } // library
} // mainproject

test.cpp (library)

#include "test.h"
#include <iostream>

mainproject::library::Test::Test(std::string txt)
    : txt(txt)
{
}

void mainproject::library::Test::msg() {
    std::cout << "Message is " << this->txt << std::endl;
}

main.cpp (executable)

#include "test.h"
#include <iostream>


int main(int argc, char* argv[])
{
    std::cout << "PLAYGROUND" << std::endl;

    mainproject::library::Test t("hello");
    t.msg();

    return 0;
}

I use Visual Studio 2017 and CMake 3.21 on Windows 10.

The first thing I did after creating my top-level project was to create a sub-project for the library. Everything built without any issues. I didn't even look at the output directory of my build (normally I do that only when something breaks - more on that below :D).

But after linking against it all of a sudden I got:

fatal error LNK1169: one or more multiply defined symbols found

I spent a lot of time until I discovered the issue: my library was being built in two versions - static and dynamic. So simply using

target_link_libraries(playground PUBLIC test)

basically was throwing test.lib and test.dll at the linker hence the error. I fixed the issue by specifying that I want the DLL:

target_link_libraries(playground PUBLIC test.dll)

I would like to know why this is happening. On Linux I have never experienced such a behaviour. When I set it to SHARED I get a shared object file (.so; equivalent to the dynamic library on Windows). When I set it to STATIC I get an archive file (.a; equivalent to the static library on Windows). If the property is not set explicitly, the default is taken. The behaviour exhibited in the example above points at implicit overriding explicit.

It goes either weirder - even my executable has its own LIB file. If I remove all the LIB files, I can still run the executable without any issues.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
rbaleksandar
  • 8,713
  • 7
  • 76
  • 161
  • 1
    Does this answer your question? [DLL and LIB files - what and why?](https://stackoverflow.com/questions/913691/dll-and-lib-files-what-and-why) Or this? [Why do some DLL files need an additional .lib file for linking?](https://stackoverflow.com/questions/20216124/why-do-some-dll-files-need-an-additional-lib-file-for-linking) – DevSolar Aug 11 '21 at 14:29
  • Does this answer your question? [DLL and LIB files - what and why?](https://stackoverflow.com/questions/913691/dll-and-lib-files-what-and-why) – Alex Reinking Aug 11 '21 at 14:30
  • So the LIB file (for the library) is generated because I am importing it into my executable? This however still doesn't explain the last sentence of my post - a LIB file is generated even for my executable, which is not a library. Sometimes that LIB appears, sometimes it doesn't (I am completely deleting the build directory to ensure a clean configuration and build). Note the **sometimes** here. – rbaleksandar Aug 11 '21 at 14:37
  • *"If I remove all the LIB files, I can still run the executable without any issues."* you can always remove statically linked libs after the linking is done without affecting the compiled program. Not sure if this is an issue here, but your header file seems to be lacking a header guard (or `#pragma once`). Furthermore this command in the lib cmake file makes little sense `include_directories(test ${INCLUDE_DIR})`; This sets the include dirs for the current directory; the first argument is not interpreted as target name. – fabian Aug 11 '21 at 18:12
  • 2
    Furthermore since you're building the lib and the exe at the same time, get rid of the `link_directories` command; the only thing this can do here is screw things up; CMake knows where it outputs it's targets and automatically does the necessary magic, when passing a target to `target_link_libraries`. Also consider using `install()` commands for deploying; this makes restructuring the binary dir much simpler. – fabian Aug 11 '21 at 18:15

0 Answers0