Я начал новый проект 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 также был доступен как статическая библиотека?
@3CxEZiVlQ Вы правы. Я хочу протестировать внутренний код, который обычно не экспортируется в dll, поэтому я извлек эту функциональность в статическую библиотеку, которую могут связать пакет dll и тестовый пакет.
Проблема вызвана тем, что мои тесты пытаются получить доступ к функциям, которые не экспортируются в 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",
],
)
glob(["*.cpp"])
- не делай этого. Явно перечислите необходимые файлы. Нам нужен минимально воспроизводимый пример . Я могу предположить, что вы неправильно импортируете dll CallbackLogger.