Search code examples
c++cmakemingw-w64spdlog

Getting multiple redifinition errors when trying to build using CMake and MinGW64


I've come across a problem that I can't seem to fix. I'm trying to build a project which makes use of a logging library called spdlog but it won't build when I use it in my project. MinGW64 reports that there's multiple definitions for some functions:

Scanning dependencies of target Application
[ 91%] Building CXX object Application/CMakeFiles/Application.dir/src/main.cpp.obj
[100%] Linking CXX executable ..\Debug\bin\Application.exe
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `std::is_same<long double, float> fmt::v6::internal::const_check<std::is_same<long double, float> >(std::is_same<long double, float>)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:58: multiple definition of `fmt::v6::internal::assert_fail(char const*, int, char const*)'
../Debug/lib/libTools.dll.a(d000329.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `fmt::v6::format_error::~format_error()':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format.h:691: multiple definition of `fmt::v6::format_error::~format_error()'
../Debug/lib/libTools.dll.a(d000167.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `fmt::v6::internal::error_handler::on_error(char const*)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:1363: multiple definition of `fmt::v6::internal::error_handler::on_error(char const*)'
../Debug/lib/libTools.dll.a(d000735.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `std::string fmt::v6::internal::grouping_impl<char>(fmt::v6::internal::locale_ref)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:203: multiple definition of `std::string fmt::v6::internal::grouping_impl<char>(fmt::v6::internal::locale_ref)'
../Debug/lib/libTools.dll.a(d000741.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `char fmt::v6::internal::thousands_sep_impl<char>(fmt::v6::internal::locale_ref)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:206: multiple definition of `char fmt::v6::internal::thousands_sep_impl<char>(fmt::v6::internal::locale_ref)'
../Debug/lib/libTools.dll.a(d000999.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `char fmt::v6::internal::decimal_point_impl<char>(fmt::v6::internal::locale_ref)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:210: multiple definition of `char fmt::v6::internal::decimal_point_impl<char>(fmt::v6::internal::locale_ref)'
../Debug/lib/libTools.dll.a(d000994.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `int fmt::v6::internal::snprintf_float<double>(double, int, fmt::v6::internal::float_specs, fmt::v6::internal::buffer<char>&)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:1117: multiple definition of `int fmt::v6::internal::snprintf_float<double>(double, int, fmt::v6::internal::float_specs, fmt::v6::internal::buffer<char>&)'
../Debug/lib/libTools.dll.a(d000876.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `int fmt::v6::internal::snprintf_float<long double>(long double, int, fmt::v6::internal::float_specs, fmt::v6::internal::buffer<char>&)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:1117: multiple definition of `int fmt::v6::internal::snprintf_float<long double>(long double, int, fmt::v6::internal::float_specs, fmt::v6::internal::buffer<char>&)'
../Debug/lib/libTools.dll.a(d000877.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `int fmt::v6::internal::format_float<double>(double, int, fmt::v6::internal::float_specs, fmt::v6::internal::buffer<char>&)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:1045: multiple definition of `int fmt::v6::internal::format_float<double>(double, int, fmt::v6::internal::float_specs, fmt::v6::internal::buffer<char>&)'
../Debug/lib/libTools.dll.a(d000693.o):(.text+0x0): first defined here
../Debug/lib/libspdlogd.a(fmt.cpp.obj): In function `int fmt::v6::internal::format_float<long double>(long double, int, fmt::v6::internal::float_specs, fmt::v6::internal::buffer<char>&)':
D:/Development/git/cmaketest/third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h:1045: multiple definition of `int fmt::v6::internal::format_float<long double>(long double, int, fmt::v6::internal::float_specs, fmt::v6::internal::buffer<char>&)'
../Debug/lib/libTools.dll.a(d000695.o):(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
mingw32-make[2]: *** [Application\CMakeFiles\Application.dir\build.make:88: Debug/bin/Application.exe] Error 1
mingw32-make[1]: *** [CMakeFiles\Makefile2:190: Application/CMakeFiles/Application.dir/all] Error 2
mingw32-make: *** [Makefile:83: all] Error 2 

Now, I've made a separate project with the bare minimum needed to reproduce this problem in the hopes of finding the solution but I'm getting the same results. I've also tried building through visual studio and that works, although the way libs are created is of course different. Also tried different versions of MinGW64 but that unfortunately doesn't solve it either.

To demonstrate the problem, here's the contents of this "bare minimum" project.

My project is seperated in 3 parts: Application, third_party and Tools. Third_party in this case contains a git submodule to the spdlog repository

Application contains a file main.cpp with the following contents

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

int main()
{
    DEBUGERROR("error in main {}", 1);
    getchar();
    return 0;
}

Tools includes the following:

Logger.cpp

#pragma once
#include <string>
#include <iostream>
#include <spdlog/spdlog.h>

#define DEBUGERROR(userFormat,...) spdlog::error("[{}] [{}:{}] " userFormat, __COMPONENT__, __func__, __LINE__, ##__VA_ARGS__);
#define DEBUGINFO(userFormat,...) spdlog::info("[{}] [{}:{}] " userFormat, __COMPONENT__, __func__, __LINE__, ##__VA_ARGS__);;
#define DEBUGWARNING(userFormat,...) spdlog::warn("[{}] [{}:{}] " userFormat, __COMPONENT__, __func__, __LINE__, ##__VA_ARGS__);
#define DEBUGCRITICAL(userFormat,...) spdlog::critical("[{}] [{}:{}] " userFormat, __COMPONENT__, __func__, __LINE__, ##__VA_ARGS__);

namespace Tools
{
    class Logger
    {
    public:
        void Logger::init()
        {
            spdlog::set_pattern("%^[thread %t] [%H:%M:%S] [%l] [%s:%#] %v%$");
        }
    };
} 

Printer.h

#pragma once

class Printer
{
public:
    Printer();
    virtual ~Printer();
    static void printError();
private:
}; 

Printer.cpp

#include "printer.h"
#include "Logger.h"

Printer::Printer()
{

}
Printer::~Printer()
{

}

void Printer::printError()
{
    DEBUGERROR("printing an error {}", 1);
} 

To build this, I've created 3 cmake files.

Root CMakeLists.txt

cmake_minimum_required(VERSION 3.15.3)
project(AllProjects LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/bin)

add_subdirectory("third_party/spdlog")
add_subdirectory("Tools")
add_subdirectory("Application") 

Application CMakeLists.txt

cmake_minimum_required(VERSION 3.15.3)
project(Application LANGUAGES CXX C)
add_definitions( /D__COMPONENT__="app" )

file(GLOB_RECURSE SOURCES src/*.cpp)

set(INCLUDEDIR ${PROJECT_SOURCE_DIR}/include
               ${CMAKE_SOURCE_DIR}/Tools/include
)

include_directories(${INCLUDEDIR})
add_executable(Application ${SOURCES})
target_link_libraries (Application Tools) 

Tools CMakeLists.txt

cmake_minimum_required(VERSION 3.15.3)
project(Tools LANGUAGES CXX C)

add_definitions( /D__COMPONENT__="tools" )

file(GLOB_RECURSE SOURCES src/*.cpp
)

set(INCLUDEDIR ${PROJECT_SOURCE_DIR}/include
    ${CMAKE_SOURCE_DIR}/third_party/spdlog/include
)

include_directories(${INCLUDEDIR})

add_library(Tools SHARED ${SOURCES})
target_link_libraries (Tools spdlog) 

Finally, I'm using the following set of commands to get everything to build:

cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE -DBUILD_SHARED_LIBS=TRUE -G "MinGW Makefiles" ..
mingw32-make
cd .. 

So in the end, the structure of the project is as follows:

Project

  • Application
    • include
    • src
    • CMakeLists.txt
  • Tools
    • include
    • src
    • CMakeLists.txt
  • third_party
    • spdlog repository as git submodule
  • CMakeLists.txt

The problem only starts arising when I start including Logger.h in multiple files. I hope I've provided enough information to make the problem clear. Any help would be greatly appreciated!


Solution

  • I have found a solution that works, but has the side effect of having to include spdlog separately in projects that includ tools. In Tools CMakeLists.txt, I had to change the last line "target_link_libraries (Tools spdlog)" into target_link_libraries (Tools PRIVATE spdlog). After doing that I did have to add "${CMAKE_SOURCE_DIR}/third_party/spdlog/include" to my Application's include path. For some reason spdlog is the only external project that requires me to do it this way. Not realy what I wanted since I now have to include spdlog alongside tools in all future projects that need it, but it does work now.

    Updated Tools CMakeLists.txt:

    cmake_minimum_required(VERSION 3.15.3)
    project(Tools LANGUAGES CXX C)
    
    add_definitions( /D__COMPONENT__="tools" )
    
    file(GLOB_RECURSE SOURCES src/*.cpp
    )
    
    set(INCLUDEDIR ${PROJECT_SOURCE_DIR}/include
        ${CMAKE_SOURCE_DIR}/third_party/spdlog/include
    )
    
    include_directories(${INCLUDEDIR})
    
    add_library(Tools SHARED ${SOURCES})
    target_link_libraries (Tools PRIVATE spdlog) 
    

    Updated Application CMakeLists.txt

    cmake_minimum_required(VERSION 3.15.3)
    project(Application LANGUAGES CXX C)
    add_definitions( /D__COMPONENT__="app" )
    
    file(GLOB_RECURSE SOURCES src/*.cpp)
    
    set(INCLUDEDIR ${PROJECT_SOURCE_DIR}/include
                   ${CMAKE_SOURCE_DIR}/Tools/include
                   ${CMAKE_SOURCE_DIR}/third_party/spdlog/include
    )
    
    include_directories(${INCLUDEDIR})
    add_executable(Application ${SOURCES})
    target_link_libraries (Application Tools)