简体   繁体   English

使用 CMake 强制限制“需要一个替代库来链接可执行文件”

[英]Use CMake to enforce restriction "one of alternative libraries is needed to link executable"

I use static libraries on linux.我在 linux 上使用静态库。 I have two static libriaries ("implementers") that share interface which is packed into third library ("common").我有两个静态库(“实现者”),它们共享打包到第三个库(“公共”)中的接口。 Exactly one of them (implementers) is needed to build binary.构建二进制文件只需要其中之一(实现者)。 I need to build both versions of exec at the same time (separate targets).我需要同时构建两个版本的 exec(单独的目标)。 However I can create static libraries that use only common part.但是我可以创建仅使用公共部分的静态库。 There is so many of them that他们中有很多人

I want CMake to protect me from creating executables that are missing an implementer.我希望 CMake 保护我免于创建缺少实现者的可执行文件。 This is needed because finding out about this takes long time (long compilation, even longer linking).这是必需的,因为了解这一点需要很长时间(长时间编译,甚至更长的链接)。

Pseudo-c++ and cmake look like following (I simplified by ignoring includes, which are actually important part of "common" lib).伪 c++ 和 cmake 如下所示(我通过忽略包含来简化,它们实际上是“公共”库的重要部分)。

// 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)

I want to achieve cmake error on user_bad executable but not on userlib library.我想在user_bad可执行文件上实现 cmake 错误,而不是在userlib库上。 I imagine it to look like (very-pseudo cmake):我想象它看起来像(非常伪 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)

Is this possible?这可能吗?

Duplicating userlib to two versions (1 per implementer) is not scalable for my project.将 userlib 复制到两个版本(每个实现者 1 个)对于我的项目是不可扩展的。

This is problematic.这是有问题的。 You create a target with incomplete dependencies ( foobar ), which CMake very much does not expect you to do, and is in no way obliged to handle correctly.您创建了一个具有不完整依赖项 ( foobar ) 的目标,CMake 非常不希望您这样做,并且绝没有义务正确处理。

One way to get out of this without having to duplicate the libraries everywhere is through the use of object libraries :摆脱这种情况而不必到处复制库的一种方法是使用对象库

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)

Take care though to at no point in the build tree end up with an incomplete target.但请注意,构建树中的任何一点都不会以不完整的目标结束。

For instance, taking into account your example where foobar is being consumed by an intermediate userlib : At the point of declaration of userlib you will need to decide which implementation to use.例如,考虑到中间userlib正在使用foobar的示例:在声明userlib您需要决定使用哪个实现。 Otherwise you would end up with the same problematic situation as before, only now at the level of userlib .否则,您最终会遇到与以前相同的问题,只是现在出现在userlib级别。

If you absolutely 100% need to defer this decision to the point of declaration of the executable, you should push it all the way into runtime and use dynamic libraries instead.如果您绝对 100% 需要将此决定推迟到可执行文件的声明点,那么您应该将其一直推送到运行时并使用动态库。

Note that while technically you may be able to solve this differently for certain build generators, if you're looking for a clean and robust solution, those are the only viable options I see.请注意,虽然从技术上讲,对于某些构建生成器,您可能能够以不同的方式解决此问题,但如果您正在寻找一个干净而强大的解决方案,那么这些是我看到的唯一可行的选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM