Получение нескольких ошибок повторного определения при попытке сборки с использованием CMake и MinGW64

Я столкнулся с проблемой, которую не могу решить. Я пытаюсь создать проект, в котором используется библиотека журналов spdlog, но она не собирается, когда я использую ее в своем проекте. MinGW64 сообщает, что для некоторых функций существует несколько определений:

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 

Теперь я сделал отдельный проект с минимальным количеством необходимых для воспроизведения этой проблемы в надежде найти решение, но я получаю те же результаты. Я также пробовал создавать через визуальную студию, и это работает, хотя способ создания библиотек, конечно, отличается. Также пробовал разные версии MinGW64, но, к сожалению, это тоже не решает.

Чтобы продемонстрировать проблему, вот содержание этого «минимального» проекта.

Мой проект разделен на 3 части: приложение, третья сторона и инструменты. Third_party в этом случае содержит подмодуль git для репозитория spdlog

Приложение содержит файл main.cpp со следующим содержимым

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

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

Инструменты включают следующее:

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);
} 

Для его создания я создал 3 файла cmake.

Корневой 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") 

Приложение 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) 

Инструменты 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) 

Наконец, я использую следующий набор команд, чтобы все было готово:

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

Итак, в итоге структура проекта следующая:

Проект

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

Проблема возникает только тогда, когда я начинаю включать Logger.h в несколько файлов. Надеюсь, я предоставил достаточно информации, чтобы прояснить проблему. Любая помощь будет принята с благодарностью!


person Steven    schedule 04.01.2020    source источник
comment
Из вывода компоновщика кажется, что вы используете стороннюю библиотеку spdlog в качестве статической (из-за суффикса .a), и каким-то образом эта библиотека включена в вашу Tools библиотеку и ваш Application исполняемый файл, что приводит к дублированию символов. Я бы исследовал зависимость Application от spdlog, начиная с проверки того, что я сказал, глядя на вывод make VERBOSE=1.   -  person albestro    schedule 04.01.2020
comment
Я нашел решение. Я должен был убедиться, что файл CMakeLists из Tools связывает spdlog как PRIVATE с Tools. Затем мне также пришлось включить spdlog в приложение. Однако я не уверен, зачем это нужно. Намерение состоит в том, чтобы иметь инструменты, включающие spdlog, затем может быть открыт класс-оболочка, который обертывает функциональность spdlog. Затем я бы добавил библиотеку Tools во все мои другие проекты и везде использовал этот класс-оболочку. Однако теперь мне нужно снова отдельно включить spdlog во все другие проекты, хотя я уже везде включаю инструменты.   -  person Steven    schedule 04.01.2020
comment
Если вы нашли решение, опубликуйте объяснение и исправленный код в качестве ответа на свой вопрос. Таким образом, будущие посетители этой страницы смогут понять, что решило проблему.   -  person squareskittles    schedule 06.01.2020


Ответы (1)


Я нашел решение, которое работает, но имеет побочный эффект - нужно отдельно включать spdlog в проекты, которые включают инструменты. В Tools CMakeLists.txt мне пришлось заменить последнюю строку «target_link_libraries (Tools spdlog)» на target_link_libraries (Tools PRIVATE spdlog). После этого мне пришлось добавить «$ {CMAKE_SOURCE_DIR} / third_party / spdlog / include» в путь включения моего приложения. По какой-то причине spdlog - единственный внешний проект, который требует от меня этого. Не совсем то, что я хотел, так как теперь я должен включать spdlog вместе с инструментами во все будущие проекты, которые в нем нуждаются, но сейчас он работает.

Обновленные инструменты 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) 

Обновленное приложение 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) 
person Steven    schedule 09.01.2020