使用相同的需求作为 requires 和 tool_requires¶
有些库可以同时作为库和工具需求,例如 protobuf。这些库通常包含库本身的头文件/源文件,并且可能包含一些额外的工具(编译器、shell 脚本等)。这两个部分在不同的上下文中使用,让我们以protobuf为例来考虑这种情况。
我想创建一个库,其中包含编译后的 protobuf 消息。protobuf 编译器(构建上下文)需要在构建时调用,并且包含编译后的 .pb.cc 文件的库需要链接到 protobuf 库(宿主上下文)。
鉴于此,我们应该能够在同一个 Conan recipe 中在构建/宿主上下文中使用 protobuf。基本上,你的包 recipe 应该看起来像这样
def requirements(self):
self.requires("protobuf/3.18.1")
def build_requirements(self):
self.tool_requires("protobuf/<host_version>")
注意
protobuf/<host_version>
表达式确保在两个上下文中使用相同版本的库。你可以 在这里 阅读更多相关信息。
这是处理在两个上下文中使用的任何其他库的方法。尽管如此,让我们看一个详细的例子,看看这个例子是什么样的。
请先克隆源代码以重新创建此项目。你可以在 GitHub 上的 examples2 仓库 中找到它们
git clone https://github.com/conan-io/examples2.git
cd examples2/examples/graph/tool_requires/using_protobuf/myaddresser
项目的结构如下
./
├── conanfile.py
├── CMakeLists.txt
├── addressbook.proto
├── apple-arch-armv8
├── apple-arch-x86_64
└── src
└── myaddresser.cpp
└── include
└── myaddresser.h
└── test_package
├── conanfile.py
├── CMakeLists.txt
└── src
└── example.cpp
conanfile.py
看起来像
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout
class myaddresserRecipe(ConanFile):
name = "myaddresser"
version = "1.0"
package_type = "library"
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
generators = "CMakeDeps", "CMakeToolchain"
# Sources are located in the same place as this recipe, copy them to the recipe
exports_sources = "CMakeLists.txt", "src/*", "include/*", "addressbook.proto"
def config_options(self):
if self.settings.os == "Windows":
self.options.rm_safe("fPIC")
def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
def requirements(self):
self.requires("protobuf/3.18.1")
def build_requirements(self):
self.tool_requires("protobuf/<host_version>")
def layout(self):
cmake_layout(self)
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
self.cpp_info.libs = ["myaddresser"]
self.cpp_info.requires = ["protobuf::libprotobuf"]
正如你所看到的,我们同时在不同的上下文中使用 protobuf。
CMakeLists.txt
展示了这个例子如何使用 protobuf 编译器和库
cmake_minimum_required(VERSION 3.15)
project(myaddresser LANGUAGES CXX)
find_package(protobuf CONFIG REQUIRED)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS addressbook.proto)
add_library(myaddresser src/myaddresser.cpp ${PROTO_SRCS})
target_include_directories(myaddresser PUBLIC include)
target_include_directories(myaddresser PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(myaddresser PUBLIC protobuf::libprotobuf)
set_target_properties(myaddresser PROPERTIES PUBLIC_HEADER "include/myaddresser.h;${PROTO_HDRS}")
install(TARGETS myaddresser)
其中库本身定义了一个简单的 myaddresser.cpp,它使用了生成的 addressbook.pb.h 头文件
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
#include "myaddresser.h"
void myaddresser(){
// Testing header generated by protobuf
GOOGLE_PROTOBUF_VERIFY_VERSION;
tutorial::AddressBook address_book;
auto * person = address_book.add_people();
person->set_id(1337);
std::cout << "myaddresser(): created a person with id 1337\n";
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
}
最后,test_package 示例简单地调用 myaddresser()
函数来检查一切是否正常工作
#include <iostream>
#include <fstream>
#include <string>
#include "myaddresser.h"
int main(int argc, char* argv[]) {
myaddresser();
return 0;
}
那么,让我们看看它是否运行良好
$ conan create . --build missing
...
Requirements
myaddresser/1.0#71305099cc4dc0b08bb532d4f9196ac1:c4e35584cc696eb5dd8370a2a6d920fb2a156438 - Build
protobuf/3.18.1#ac69396cd9fbb796b5b1fc16473ca354:e60fa1e7fc3000cc7be2a50a507800815e3f45e0#0af7d905b0df3225a3a56243841e041b - Cache
zlib/1.2.13#13c96f538b52e1600c40b88994de240f:d0599452a426a161e02a297c6e0c5070f99b4909#69b9ece1cce8bc302b69159b4d437acd - Cache
Build requirements
protobuf/3.18.1#ac69396cd9fbb796b5b1fc16473ca354:e60fa1e7fc3000cc7be2a50a507800815e3f45e0#0af7d905b0df3225a3a56243841e041b - Cache
...
-- Install configuration: "Release"
-- Installing: /Users/myuser/.conan2/p/b/myser03f790a5a5533/p/lib/libmyaddresser.a
-- Installing: /Users/myuser/.conan2/p/b/myser03f790a5a5533/p/include/myaddresser.h
-- Installing: /Users/myuser/.conan2/p/b/myser03f790a5a5533/p/include/addressbook.pb.h
myaddresser/1.0: package(): Packaged 2 '.h' files: myaddresser.h, addressbook.pb.h
myaddresser/1.0: package(): Packaged 1 '.a' file: libmyaddresser.a
....
======== Testing the package: Executing test ========
myaddresser/1.0 (test package): Running test()
myaddresser/1.0 (test package): RUN: ./example
myaddresser(): created a person with id 1337
在看到它运行良好后,让我们尝试使用交叉构建。请注意,这部分是基于 MacOS Intel 系统,并为 MacOS ARM 系统进行交叉编译,但你当然可以根据自己的需求使用自己的 profile。
警告
运行此示例的这部分需要 MacOS 系统。
$ conan create . --build missing -pr:b apple-arch-x86_64 -pr:h apple-arch-armv8
...
-- Install configuration: "Release"
-- Installing: /Users/myuser/.conan2/p/b/myser03f790a5a5533/p/lib/libmyaddresser.a
-- Installing: /Users/myuser/.conan2/p/b/myser03f790a5a5533/p/include/myaddresser.h
-- Installing: /Users/myuser/.conan2/p/b/myser03f790a5a5533/p/include/addressbook.pb.h
myaddresser/1.0: package(): Packaged 2 '.h' files: myaddresser.h, addressbook.pb.h
myaddresser/1.0: package(): Packaged 1 '.a' file: libmyaddresser.a
....
======== Testing the package: Executing test ========
myaddresser/1.0 (test package): Running test()
现在,我们看不到示例运行,因为宿主架构的原因。如果我们想检查 example 可执行文件是否为正确的架构构建
$ file test_package/build/apple-clang-13.0-armv8-gnu17-release/example
test_package/build/apple-clang-13.0-armv8-gnu17-release/example: Mach-O 64-bit executable arm64
一切都按预期工作,并且可执行文件是为 64 位可执行 arm64 架构构建的。