Как настроить Bazel/Google Test для устранения ошибки компоновщика `bazel test`

Я начал новый проект C++ и пытаюсь настроить Bazel и Google Test.

Bazel настроен на сборку основного проекта в виде DLL. DLL собирается нормально, и я также могу собрать exe-файл, который зависит от DLL. Примеры тестов Google работают нормально, поскольку они не ссылаются на библиотеку, но когда тест связывает заголовок библиотеки, я получаю такие ошибки компоновщика:

error LNK2019: unresolved external symbol "public: void __cdecl CallbackLogger::registerLogInfo(void (__cdecl*)(char const *,int))" (?registerLogInfo@CallbackLogger@@QEAAXP6AXPEBDH@Z@Z) referenced in function "private: virtual void __cdecl CallbackLoggerTest_RegisterLogInfoCallback_Test::TestBody(void)" (?TestBody@CallbackLoggerTest_RegisterLogInfoCallback_Test@@EEAAXXZ)

Мой проект имеет следующую структуру:

src
  WORKSPACE
  MODULE.bazel
  core
    ... some other files/folders
    logger
      callback_logger.h
      callback_logger.cpp
    windows_dll_library.bzl
    BUILD
  test
    BUILD
    callback_logger_test.cpp

Тестовый код:

/test/callback_logger_test.cpp

#include "../core/logger/callback_logger.h"
#include <gtest/gtest.h>
#include <iostream>

using namespace std;

void test_callback(const char *message, int size)
{
    string str(message, size);
    cout << "Message received: " + str << endl;
}

// Tests registering loginfo callback
TEST(CallbackLoggerTest, RegisterLogInfoCallback)
{
    auto logger = new CallbackLogger();
    logger->registerLogInfo(&test_callback);
}

А это мои файлы BUILD:

/core/BUILD:

load(":windows_dll_library.bzl", "windows_dll_library")

# Define the shared library
windows_dll_library(
    name = "core",
    srcs = glob(["**/*.cpp"]),
    hdrs = glob(["**/*.h"]),
    # Define COMPILING_DLL to export symbols during compiling the DLL.
    copts = ["/DCOMPILING_DLL"],
    visibility = ["//test:__pkg__", "//core_runner:__pkg__"],
)

/test/BUILD:

cc_test(
    name = "test",
    size = "small",
    srcs = glob(["*.cpp"]),
    deps = [
        "@googletest//:gtest",
        "@googletest//:gtest_main",
        "//core:core",
    ],
)

Я запускаю тесты с помощью этой команды Bazel: bazel test --test_output=all //test:test

Как настроить Bazel для подключения библиотеки?

Дополнительная информация

Вот конфигурация bazel для библиотеки Windows DLL: windows_dll_library.bzl

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_import", "cc_library")

def windows_dll_library(
        name,
        srcs = [],
        deps = [],
        hdrs = [],
        visibility = None,
        **kwargs):
    """A simple windows_dll_library rule for builing a DLL Windows."""
    dll_name = name + ".dll"
    import_lib_name = name + "_import_lib"
    import_target_name = name + "_dll_import"

    # Build the shared library
    cc_binary(
        name = dll_name,
        srcs = srcs + hdrs,
        deps = deps,
        linkshared = 1,
        **kwargs
    )

    # Get the import library for the dll
    native.filegroup(
        name = import_lib_name,
        srcs = [":" + dll_name],
        output_group = "interface_library",
    )

    # Because we cannot directly depend on cc_binary from other cc rules in deps attribute,
    # we use cc_import as a bridge to depend on the dll.
    cc_import(
        name = import_target_name,
        interface_library = ":" + import_lib_name,
        shared_library = ":" + dll_name,
    )

    # Create a new cc_library to also include the headers needed for the shared library
    cc_library(
        name = name,
        hdrs = hdrs,
        visibility = visibility,
        deps = deps + [
            ":" + import_target_name,
        ],
    )

Это стандартный файл, предоставленный в образце Bazel.

Если я добавлю пакет DLL (//core:core) в deps при сборке exe, это все, что мне нужно сделать:

load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
    name = "my_exe",
    srcs = ["my_exe.cpp"],
    deps = [
        "//core:core",
    ],
)

my_exe теперь может вызывать экспортированные функции в DLL.

Но для своих модульных тестов я хочу использовать классы библиотеки, а не только внешний интерфейс DLL.

Как мне настроить Bazel для этого? Нужно ли мне предоставить другую конфигурацию библиотеки в BUILD, чтобы пакет DLL также был доступен как статическая библиотека?

glob(["*.cpp"]) - не делай этого. Явно перечислите необходимые файлы. Нам нужен минимально воспроизводимый пример . Я могу предположить, что вы неправильно импортируете dll CallbackLogger.
3CxEZiVlQ 04.08.2024 03:47

@3CxEZiVlQ Вы правы. Я хочу протестировать внутренний код, который обычно не экспортируется в dll, поэтому я извлек эту функциональность в статическую библиотеку, которую могут связать пакет dll и тестовый пакет.

Kyohei Kaneko 05.08.2024 23:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема вызвана тем, что мои тесты пытаются получить доступ к функциям, которые не экспортируются в dll.

Экспортированные функции dll имеют префикс __declspec(dllexport), и только эти функции можно вызывать в dll. Поскольку я хочу выполнить модульное тестирование внутренних классов библиотеки, но не хочу экспортировать эту внутреннюю функциональность в dll, мне нужно разделить основную функциональность на статическую библиотеку и сделать dll оболочкой для этой статической библиотеки. Тестовый пакет может напрямую связать статическую библиотеку, а dll нужно только экспортировать необходимые функции.

Это дает такую ​​структуру проекта:

src
  WORKSPACE
  MODULE.bazel
  api
    api.h
    api.cpp
    windows_dll_library.bzl
    BUILD
  core
    ... other files/folders
    logger
      callback_logger.h
      callback_logger.cpp
    BUILD
  test
    BUILD
    callback_logger_test.cpp

Предоставляем 3 пакета, которые можно собрать:

  • //core:core. Это статическая библиотека, содержащая большую часть библиотечного кода.
  • //api:api. При этом создается DLL и экспортируются только функции, отмеченные __declspec(dllexport). Зависит от //core:core.
  • //test:test. Это тестовый пакет, который реализует основные функции. Зависит от //core:core.

Вот новые файлы BUILD:

/core/BUILD

cc_library(
    name = "core",
    srcs = glob(["**/*.cpp"]),
    hdrs = glob(["**/*.h"]),
    visibility = [
        "//api:__pkg__",
        "//test:__pkg__",
    ],
)

/api/BUILD

load(":windows_dll_library.bzl", "windows_dll_library")

# Define the shared library
windows_dll_library(
    name = "api",
    srcs = ["api.cpp"],
    hdrs = ["api.h"],
    deps = ["//core:core"],
    # Define COMPILING_DLL to export symbols during compiling the DLL.
    # See api.h
    copts = ["/DCOMPILING_DLL"],
)

test/BUILD

cc_test(
    name = "test",
    size = "small",
    srcs = glob(["*.cpp"]),
    deps = [
        "@googletest//:gtest",
        "@googletest//:gtest_main",
        "//core:core",
    ],
)

Другие вопросы по теме