Используйте CMake для применения ограничения «одна из альтернативных библиотек необходима для связи исполняемого файла» - PullRequest
0 голосов
/ 23 марта 2020

Я использую stati c библиотеки на linux. У меня есть две stati c библиотеки («реализации»), которые совместно используют интерфейс, который упакован в третью библиотеку («общую»). Именно один из них (реализаторы) необходим для сборки двоичного файла. Мне нужно собрать обе версии exe c одновременно (отдельные цели). Однако я могу создавать stati c библиотеки, которые используют только общую часть. Их так много, что

Я хочу, чтобы CMake защитил меня от создания исполняемых файлов, в которых отсутствует реализатор. Это необходимо, потому что на выяснение этого уходит много времени (долгая компиляция, даже более продолжительное связывание).

Псевдо-c ++ и cmake выглядят следующим образом (я упростил, игнорируя включения, которые на самом деле являются важной частью «общего») lib).

// libFooBar - "common"
void foo(); // only declaration for includers. Is implemented by implementers
void bar() {
    foo();
}

// libFooBarPrintf - "implementer1"
void foo() {
    printf("Foo");
}

// libFooBarCout - "implementer2"
void foo() {
    std::cout<<"Foo";
}

// userlib - uses only common interface

void useBar() {
    bar();
}

// foobar_user.exe 
// needs "common" for compilation and one of "implementers" for linking
int main () {
    useBar();
    return 0;
}
add_library(foobar        STATIC foobar.cpp)
add_library(foobar_printf STATIC foobar_printf.cpp)
add_library(foobar_cout   STATIC foobar_cout.cpp)

add_library(          userlib STATIC userlib.cpp)
target_link_libraries(userlib PUBLIC foobar)

# this passes cmake, but fails to link with undefined ref to foo()
add_executable(       user_bad main.cpp)
target_link_libraries(user_bad userlib)

#those two are working as intended
add_executable(       user_printf main.cpp)
target_link_libraries(user_printf userlib foobar_printf)
add_executable(       user_cout   main.cpp)
target_link_libraries(user_cout   userlib foobar_cout)

Я хочу добиться ошибки cmake в user_bad исполняемом файле, но не в библиотеке userlib. Я представляю, как это выглядит (очень псевдо-cmake):

set_target_properties(foobar_printf foobar_cout 
    PROPERTIES 
        provides_implementation_for_foobar    1)
set_target_properties(foobar 
    PROPERTIES 
    INTERFACE_FOR_EXECUTABLES_needs_property    provides_implementation_for_foobar)

Возможно ли это?

Дублирование userlib на две версии (по одной на разработчика) не масштабируется для моего проекта.

1 Ответ

3 голосов
/ 23 марта 2020

Это проблематично c. Вы создаете цель с неполными зависимостями (foobar), которую CMake очень не ожидает от вас, и никоим образом не обязана обрабатывать ее правильно.

Один из способов выйти из этого без необходимости Повсеместное дублирование библиотек происходит благодаря использованию библиотек объектов :

add_library(foobar OBJECT ...)

add_library(foobar_printf STATIC foobar_printf.cpp $<TARGET_OBJECTS:foobar>)

add_executable(user_printf main.cpp)
target_link_libraries(user_printf userlib foobar_printf)

Будьте осторожны, чтобы ни в одной точке дерева сборки не оказалась неполная цель.

Например, принимая во внимание ваш пример, где foobar используется промежуточным userlib: в точке объявления userlib вам необходимо решить, какую реализацию использовать. В противном случае вы столкнулись бы с той же проблемной ситуацией c, что и раньше, только сейчас на уровне userlib.

Если вам абсолютно на 100% нужно отложить это решение до точки объявления исполняемого файла , вы должны выполнить sh все это во время выполнения и использовать вместо него динамические c библиотеки.

Обратите внимание, что, хотя технически вы можете решить эту проблему по-разному для определенных генераторов сборки, если вы ищете для чистого и надежного решения это единственные жизнеспособные варианты, которые я вижу.

...