[英]“Hiding” transitive external dependencies / combining libraries with CMake
This question may be partly duplicate, eg this question , but is more about what if any are better solutions.这个问题可能部分重复,例如这个问题,但更多的是关于如果有更好的解决方案的话。 Since this question ended up rather long, I marked specific questions with " +Q+ " in bold italic.
由于这个问题结束得相当长,我用粗斜体“ +Q+ ”标记了具体问题。
I have the situation that I wrote a small library B
that depends on some other huge project A
split into many libraries A1, A2, ..., An
, some of which my library depends on and some on which it doesn't.我的情况是,我编写了一个小型库
B
,它依赖于其他一些大型项目A
拆分为许多库A1, A2, ..., An
,其中一些是我的库所依赖的,而另一些则不是。 It was a bit of a pain to do the proper linking.进行正确的链接有点痛苦。 The library is starting to be used by others and I would like to avoid everyone having to go through this awful linking process, ie I want to compile all the external libraries of
A
into my B
.该库开始被其他人使用,我想避免每个人都不得不通过这个糟糕的链接过程来 go,即我想将
A
所有外部库编译到我的B
中。 Assume A
is completely external, ie that I have no way to recompile A
(in this case I do, but it is complicated and I would like to know options for the case that I don't).假设
A
完全是外部的,即我无法重新编译A
(在这种情况下我可以,但它很复杂,我想知道我没有的情况下的选项)。
I imagine this must be a very standard thing to do, I have used other popular libraries and never did I have to link all the other libraries that they transitively depend on.. ?我想这一定是一件非常标准的事情,我使用过其他流行的库,我从来不需要链接它们传递依赖的所有其他库..? So I started looking for solutions, and while I found working ones, most solutions seem like a big mess and I wonder if this is really done in practice or if there is some idiomatic way for this.
所以我开始寻找解决方案,虽然我找到了可行的解决方案,但大多数解决方案似乎都是一团糟,我想知道这是否真的在实践中完成,或者是否有一些惯用的方法。
To avoid more eventual headaches if I ever need a different case, I want to consider all combinations of static/shared libraries, ie如果我需要不同的情况,为了避免更多的最终头痛,我想考虑静态/共享库的所有组合,即
To give some MWE of the code setup (the variables LIB1_ROOT
and LIB2_ROOT
in the CMakeLists.txt files are A/ and B/ respectively):给出代码设置的一些 MWE( CMakeLists.txt文件中的变量
LIB1_ROOT
和LIB2_ROOT
分别是A/和B/ ):
A/include/lib1.hh A/include/lib1.hh
struct Lib1 { void run() const; };
A/src/lib1.cc一个/src/lib1.cc
#include <iostream>
#include <lib1.hh>
void Lib1::run() const { std::cout << "Hello from lib1\n"; }
A/CMakeLists.txt A/CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(A)
include_directories(include)
add_library(lib1 src/lib1.cc)
install(TARGETS lib1 DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/lib")
B/include/lib2.hh B/include/lib2.hh
class Lib2 {
class Implementation;
Implementation* impl;
public:
Lib2();
~Lib2();
void run() const;
};
B/src/lib2.cc B/src/lib2.cc
#include <iostream>
#include <lib1.hh>
#include <lib2.hh>
class Lib2::Implementation {
const Lib1 m_lib1{};
public:
void run() const { std::cout << "using lib1 from lib2: "; m_lib1.run(); }
};
Lib2::Lib2() : impl{new Implementation} {}
Lib2::~Lib2() { delete impl; };
void Lib2::run() const { impl->run(); }
App/src/app.cc应用程序/src/app.cc
#include <lib2.hh>
int main() { Lib2 l; l.run(); }
App/CMakeLists.cc应用程序/CMakeLists.cc
cmake_minimum_required(VERSION 3.14)
project(App)
include_directories(include "${LIB2_ROOT}/include")
find_library(LIB2 lib2 "${LIB2_ROOT}/lib")
add_executable(app src/main.cc)
target_link_libraries(app "${LIB2}")
install(TARGETS app DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/bin")
I used the pImpl pattern for B
since what is the point of hiding link dependencies when I then make the user of my library dig out all the headers anyway.我对
B
使用了 pImpl 模式,因为当我让我的库的用户无论如何都挖掘出所有标题时,隐藏链接依赖项的意义何在。
Finally B/CMakeLists.txt (for my library) depends on the cases I mentioned above:最后B/CMakeLists.txt (对于我的库)取决于我上面提到的情况:
B/CMakeLists.txt B/CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(B)
include_directories(include "${LIB1_ROOT}/include")
find_library(LIB1 lib1 "${LIB1_ROOT}/lib")
add_library(lib2_dependent src/lib2.cc)
add_custom_target(lib2 ALL
COMMAND ar -x "${LIB1}"
COMMAND ar -x "$<TARGET_FILE:lib2_dependent>"
COMMAND ar -qcs "${CMAKE_STATIC_LIBRARY_PREFIX}lib2${CMAKE_STATIC_LIBRARY_SUFFIX}" *.o
COMMAND rm *.o
DEPENDS lib2_dependent
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib"
)
This solution is from the question I linked at the start.这个解决方案来自我一开始链接的问题。 There was also an in my opinion much better version in this answer using
在我看来, 这个答案中还有一个更好的版本,使用
ar -M <<EOM
CREATE lib2.a
ADDLIB lib1.a
ADDLIB lib2_dependent.a
SAVE
END
EOM
but I could not get to work the here-document inside CMakeLists.txt... ?但我无法在 CMakeLists.txt 中处理此处的文档...? There was an additional answer providing what I think was a CMake function to do this, but it was a huge block of code which I found a bit ridiculous for something that should be simple / standard practice / integrated into CMake?
还有一个额外的答案提供了我认为是 CMake function 来执行此操作,但这是一个巨大的代码块,我发现对于应该简单/标准实践/集成到 ZDF49ADAB93B9E0C10C614F724 中的东西有点荒谬? The custom_target solution I wrote here also works, but as mentioned in other answers as well, it unpacks object files which lie around and have to be removed again, for each library I want to compile this way.
我在此处编写的 custom_target 解决方案也有效,但正如其他答案中提到的那样,它解压缩了 object 文件,这些文件存在并且必须再次删除,对于我想以这种方式编译的每个库。 And still in both cases, I can only wonder what is the point of using CMake then if I have to use ar manually anyhow.
仍然在这两种情况下,我只能想知道使用 CMake 有什么意义,那么无论如何我必须手动使用 ar。 +Q+ Is there no better / CMake-integrated way to "compile-in" transitive dependencies / combine static libraries?
+Q+ 没有更好的/CMake 集成方式来“编译”传递依赖/组合 static 库吗?
As far as I found in this case, I'm out of luck if I cannot recomple A
and it is not compiled as position independent code.据我在这种情况下发现,如果我不能重新编译
A
并且它没有被编译为 position 独立代码,我就不走运了。 The next best thing to make it work was to do just that by adding set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
to A/CMakeLists.txt .让它工作的下一个最好的事情就是通过将
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
添加到A/CMakeLists.txt来做到这一点。 Again, there seemed to be better alternatives: set(CMAKE_POSITION_INDEPENDENT_CODE ON)
or set_property(TARGET lib1 PROPERTY POSITION_INDEPENDENT_CODE ON)
from here , which were complete ignored in my case.. (compiling with VERBOSE=1
, there was no -fPIC
flag to be seen anywhere and B
did not compile)同样,似乎有更好的选择:
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
或set_property(TARGET lib1 PROPERTY POSITION_INDEPENDENT_CODE ON)
from here ,在我的情况下完全忽略了..(使用VERBOSE=1
编译,没有-fPIC
标志在任何地方都可以看到并且B
没有编译)
The B/CMakeLists.txt was easy in this case在这种情况下, B/CMakeLists.txt很简单
cmake_minimum_required(VERSION 3.14)
project(B)
include_directories(include "${LIB1_ROOT}/include")
find_library(LIB1 lib1 "${LIB1_ROOT}/lib")
add_library(lib2 SHARED src/lib2.cc)
target_link_libraries(lib2 "${LIB1}")
install(TARGETS lib2 DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/lib")
and while I don't find anything wrong with this solution, according to answers I found I should need to set additional flags like set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols")
in order for the symbols in the static library to be found.虽然我没有发现此解决方案有任何问题,但根据我发现的答案,我应该设置额外的标志,如
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols")
以便 static 中的符号要找到的图书馆。 However the above works just fine, compiles, and App
runs without issues?然而,上面的工作正常,编译,
App
运行没有问题? +Q+ Am I doing anything wrong here? +Q+ 我在这里做错了吗? Or is it maybe due to some update to CMake since these older answers?
或者可能是由于这些较旧的答案对 CMake 进行了一些更新?
According to what I found here, this is basically impossible, because shared libraries are "final" in some sense.根据我在这里的发现,这基本上是不可能的,因为共享库在某种意义上是“最终的”。 I find this very strange, surely there are many libraries that do not require a project that uses them to link every single dependency of that library that happens to be a shared library?
我觉得这很奇怪,肯定有很多库不需要使用它们来链接恰好是共享库的库的每个依赖项的项目? +Q+ Are there really no options in these cases?
+Q+ 在这些情况下真的没有选择吗?
Yep, you're doing wrong:)是的,你做错了:)
The CMake-way is to use packages . CMake 方式是使用packages 。 Once you've made the
LibA
package, you just do find_package(LibA)
in your B/CMakeLists.txt
and also the generated LibBConfig.cmake
(the package config file, so your clients would need just find_package(libB)
in their App/CMakeLists.txt
) ought to find (that is questionable IMHO, but currently its CMake way) its dependency the same way (using or not the CMake's helper find_dependency
). Once you've made the
LibA
package, you just do find_package(LibA)
in your B/CMakeLists.txt
and also the generated LibBConfig.cmake
(the package config file, so your clients would need just find_package(libB)
in their App/CMakeLists.txt
)应该以相同的方式(使用或不使用 CMake 的助手find_dependency
)找到(恕我直言,但目前它的 CMake 方式)。
The whole process gets much simpler that way:这样整个过程就变得简单多了:
libA
on your developer's host and customer's machine (so dependent projects could find it)libA
的位置(以便相关项目可以找到它)libB
and App
libB
和App
相同CMakeLists.txt
of all involved projectsCMakeLists.txt
中变得更加简单
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.