构建软件包:build() 方法¶
我们已经使用了一个包含 build() 方法 的 Conan 配方,并学会了如何使用它来调用构建系统并构建我们的软件包。在本教程中,我们将修改该方法,并解释如何使用它来执行诸如
构建和运行测试
有条件地修补源代码
有条件地选择您想使用的构建系统
请先克隆源代码以重新创建此项目。您可以在 GitHub 上的examples2 仓库中找到它们。
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/creating_packages/build_method
构建和运行项目的测试¶
您会注意到与之前的配方相比,conanfile.py 文件中有一些更改。让我们看看相关的部分
配方中引入的更改¶
class helloRecipe(ConanFile):
name = "hello"
version = "1.0"
...
def source(self):
git = Git(self)
git.clone(url="https://github.com/conan-io/libhello.git", target=".")
# Please, be aware that using the head of the branch instead of an immutable tag
# or commit is not a good practice in general
git.checkout("with_tests")
...
def requirements(self):
if self.options.with_fmt:
self.requires("fmt/8.1.1")
def build_requirements(self):
self.test_requires("gtest/1.17.0")
...
def generate(self):
tc = CMakeToolchain(self)
if self.options.with_fmt:
tc.variables["WITH_FMT"] = True
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
if not self.conf.get("tools.build:skip_test", default=False):
test_folder = os.path.join("tests")
if self.settings.os == "Windows":
test_folder = os.path.join("tests", str(self.settings.build_type))
self.run(os.path.join(test_folder, "test_hello"))
...
我们将 gtest/1.17.0 的需求添加到了配方中,作为
test_requires()。这是一种用于测试库(如 Catch2 或 gtest)的需求类型,我们将其声明在build_requirements()方法中,这是声明测试依赖项的推荐位置。我们使用
tools.build:skip_test配置(默认为False),来告诉 CMake 是否构建和运行测试。有几点需要注意如果我们将来将
tools.build:skip_test配置设置为True,Conan 将自动将BUILD_TESTING变量注入到 CMake 中,并将其设置为OFF。您将在下一节中看到,我们在 CMakeLists.txt 中使用此变量来决定是否构建测试。我们在
build()方法中使用tools.build:skip_test配置,在构建软件包和测试之后,来决定是否要运行测试。在这种情况下,我们正在使用 gtest 进行测试,并且我们必须检查构建方法是否运行测试。此配置还会影响使用 CTest 时
CMake.test()的执行,以及使用 Meson 时Meson.test()的执行。
库源代码中引入的更改¶
首先,请注意我们正在使用 libhello 库的 另一个分支。该分支在库方面有两个新特性
我们在 库源代码 中添加了一个名为
compose_message()的新函数,以便我们可以为该函数添加一些单元测试。此函数只是根据传递的参数创建输出消息。正如我们在上一节中提到的,库的 CMakeLists.txt 使用
BUILD_TESTINGCMake 变量,该变量有条件地添加 tests 目录。
cmake_minimum_required(VERSION 3.15)
project(hello CXX)
...
if (NOT BUILD_TESTING STREQUAL OFF)
add_subdirectory(tests)
endif()
...
当 tools.build:skip_test 配置设置为 True 时,Conan 会声明并设置 BUILD_TESTING CMake 变量(如果尚未定义)。当您使用 CTest 时,CMake 通常会声明此变量,但通过使用 tools.build:skip_test 配置,您可以在 CMakeLists.txt 中使用它,即使您使用的是另一个测试框架。
我们在 tests 文件夹中有一个使用 googletest 进行测试的 CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(PackageTest CXX)
find_package(GTest REQUIRED CONFIG)
add_executable(test_hello test.cpp)
target_link_libraries(test_hello GTest::gtest GTest::gtest_main hello)
对 compose_message() 函数的功能进行基本测试
#include "../include/hello.h"
#include "gtest/gtest.h"
namespace {
TEST(HelloTest, ComposeMessages) {
EXPECT_EQ(std::string("hello/1.0: Hello World Release! (with color!)\n"), compose_message("Release", "with color!"));
...
}
}
现在我们已经回顾了代码中的所有更改,让我们来尝试一下
$ conan create . --build=missing -tf=""
...
[ 25%] Building CXX object CMakeFiles/hello.dir/src/hello.cpp.o
[ 50%] Linking CXX static library libhello.a
[ 50%] Built target hello
[ 75%] Building CXX object tests/CMakeFiles/test_hello.dir/test.cpp.o
[100%] Linking CXX executable test_hello
[100%] Built target test_hello
hello/1.0: RUN: ./tests/test_hello
Capturing current environment in /Users/user/.conan2/p/tmp/c51d80ef47661865/b/build/generators/deactivate_conanbuildenv-release-x86_64.sh
Configuring environment variables
Running main() from /Users/user/.conan2/p/tmp/3ad4c6873a47059c/b/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from HelloTest
[ RUN ] HelloTest.ComposeMessages
[ OK ] HelloTest.ComposeMessages (0 ms)
[----------] 1 test from HelloTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
hello/1.0: Package '82b6c0c858e739929f74f59c25c187b927d514f3' built
...
如您所见,测试已被构建和运行。现在让我们在命令行中使用 tools.build:skip_test 配置来跳过测试的构建和运行
$ conan create . -c tools.build:skip_test=True -tf=""
...
[ 50%] Building CXX object CMakeFiles/hello.dir/src/hello.cpp.o
[100%] Linking CXX static library libhello.a
[100%] Built target hello
hello/1.0: Package '82b6c0c858e739929f74f59c25c187b927d514f3' built
...
您现在可以看到,只有库目标被构建了,没有测试被构建或运行。
有条件地修补源代码¶
如果您需要修补源代码,推荐的方法是在 source() 方法中进行。有时,如果该补丁依赖于设置或选项,您必须在启动构建之前使用 build() 方法来应用补丁。Conan 中有 几种方法可以做到这一点。其中一种方法是使用 replace_in_file 工具
import os
from conan import ConanFile
from conan.tools.files import replace_in_file
class helloRecipe(ConanFile):
name = "hello"
version = "1.0"
# Binary configuration
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
def build(self):
replace_in_file(self, os.path.join(self.source_folder, "src", "hello.cpp"),
"Hello World",
"Hello {} Friends".format("Shared" if self.options.shared else "Static"))
请注意,如果可能,应避免在 build() 中进行修补,仅在非常特殊的情况下才这样做,因为它会增加本地开发软件包的难度(我们将在后面的 本地开发流程部分 中对此进行更详细的解释)。
有条件地选择您的构建系统¶
一些软件包根据我们正在构建的平台需要一个或另一个构建系统,这种情况并不少见。例如,hello 库可以在 Windows 上使用 CMake 构建,在 Linux 和 macOS 上使用 Autotools 构建。这可以在 build() 方法中轻松处理,如下所示
...
class helloRecipe(ConanFile):
name = "hello"
version = "1.0"
# Binary configuration
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
...
def generate(self):
if self.settings.os == "Windows":
tc = CMakeToolchain(self)
tc.generate()
deps = CMakeDeps(self)
deps.generate()
else:
tc = AutotoolsToolchain(self)
tc.generate()
deps = PkgConfigDeps(self)
deps.generate()
...
def build(self):
if self.settings.os == "Windows":
cmake = CMake(self)
cmake.configure()
cmake.build()
else:
autotools = Autotools(self)
autotools.autoreconf()
autotools.configure()
autotools.make()
...