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.