使用相同的需求作为 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 看起来像

./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 编译器和库

./CMakeLists.txt
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 头文件

./src/myaddresser.cpp
#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() 函数来检查一切是否正常工作

./test_package/src/example.cpp
#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 架构构建的。