将同一需求用作 requires 和 tool_requires

有些库可以同时作为库和工具需求使用,例如,protobuf 这些库通常包含库本身的头文件/源文件,以及一些额外的工具(编译器、shell 脚本等)。这两部分在不同的上下文中使用,让我们以使用 protobuf 为例来考虑这种情况

  • 我想创建一个包含编译后的 protobuf 消息的库。protobuf 编译器(构建上下文)需要在构建时调用,并且包含编译后的 .pb.cc 文件的库需要链接到 protobuf 库(主机上下文)。

鉴于此,我们应该能够在同一个 Conan 配方中在构建/主机上下文中使用 protobuf。基本上,你的包配方应该如下所示

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 系统进行交叉编译,但你当然可以使用自己的配置文件根据你的需求。

警告

运行此示例的这部分需要 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 架构构建。