این آموزش از یک سناریوی مثال برای توصیف نحوه پیکربندی زنجیرههای ابزار C++ برای یک پروژه استفاده میکند. این بر اساس یک پروژه مثال C ++ است که بدون خطا با استفاده از clang
ایجاد می کند.
چیزی که یاد خواهید گرفت
در این آموزش شما یاد می گیرید که چگونه:
- محیط ساخت را تنظیم کنید
- زنجیره ابزار C++ را پیکربندی کنید
- یک قانون Starlark ایجاد کنید که پیکربندی اضافی برای
cc_toolchain
فراهم می کند تا Bazel بتواند برنامه را باclang
بسازد. - با اجرای
bazel build --config=clang_config //main:hello-world
در یک ماشین لینوکس، نتیجه مورد انتظار را تأیید کنید. - برنامه C++ را بسازید
قبل از اینکه شروع کنی
محیط ساخت را تنظیم کنید
این آموزش فرض میکند که شما روی لینوکس هستید و برنامههای C++ را با موفقیت ساختهاید و ابزارها و کتابخانههای مناسب را نصب کردهاید. در این آموزش از clang version 9.0.1
استفاده شده است که می توانید آن را روی سیستم خود نصب کنید.
محیط ساخت خود را به صورت زیر تنظیم کنید:
اگر قبلا این کار را نکرده اید، Bazel 0.23 یا بالاتر را دانلود و نصب کنید .
نمونه پروژه ++C را از GitHub دانلود کنید و آن را در یک پوشه خالی در ماشین محلی خود قرار دهید.
هدف
cc_binary
زیر را به فایلmain/BUILD
اضافه کنید:cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
یک فایل
.bazelrc
. در ریشه دایرکتوری فضای کاری با محتویات زیر ایجاد کنید تا استفاده از پرچم--config
را فعال کنید:# Use our custom-configured c++ toolchain. build:clang_config --crosstool_top=//toolchain:clang_suite # Use --cpu as a differentiator. build:clang_config --cpu=k8 # Use the default Bazel C++ toolchain to build the tools used during the # build. build:clang_config --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
برای build:{config_name} --flag=value
، پرچم خط فرمان --config={config_name}
با آن پرچم خاص مرتبط است. به مستندات پرچمهای مورد استفاده مراجعه کنید: crosstool_top
، cpu
و host_crosstool_top
.
وقتی هدف خود را با bazel build --config=clang_config //main:hello-world
می سازید، Bazel از زنجیره ابزار سفارشی شما از cc_toolchain_suite //toolchain:clang_suite
استفاده می کند. مجموعه ممکن است زنجیره های ابزار مختلفی را برای CPU های مختلف فهرست کند، و به همین دلیل است که با پرچم --cpu=k8
می شود.
از آنجایی که Bazel از بسیاری از ابزارهای داخلی نوشته شده در C++ در حین ساخت استفاده می کند، مانند process-wrapper، زنجیره ابزار پیش فرض C++ موجود برای پلتفرم میزبان مشخص شده است، به طوری که این ابزارها به جای ابزار ایجاد شده در این آموزش، با استفاده از آن زنجیره ابزار ساخته می شوند. .
پیکربندی زنجیره ابزار C++
برای پیکربندی زنجیره ابزار C++، به طور مکرر برنامه را بسازید و هر خطا را یکی یکی حذف کنید، همانطور که در زیر توضیح داده شده است.
بیلد را با دستور زیر اجرا کنید:
bazel build --config=clang_config //main:hello-world
از آنجایی که شما
--crosstool_top=//toolchain:clang_suite
را در فایل.bazelrc
. مشخص کرده اید، Bazel خطای زیر را می دهد:No such package `toolchain`: BUILD file not found on package path.
در دایرکتوری فضای کاری، دایرکتوری
toolchain
برای بسته و یک فایلBUILD
خالی در دایرکتوریtoolchain
ایجاد کنید.بیلد را دوباره اجرا کنید. از آنجایی که بسته
toolchain
هنوز هدفclang_suite
را تعریف نکرده است، Bazel خطای زیر را ایجاد می کند:No such target '//toolchain:clang_suite': target 'clang_suite' not declared in package 'toolchain' defined by .../toolchain/BUILD
در فایل
toolchain/BUILD
یک گروه فایل خالی را به صورت زیر تعریف کنید:package(default_visibility = ["//visibility:public"]) filegroup(name = "clang_suite")
بیلد را دوباره اجرا کنید. Bazel خطای زیر را می دهد:
'//toolchain:clang_suite' does not have mandatory providers: 'ToolchainInfo'
Bazel کشف کرد که پرچم
--crosstool_top
به قانونی اشاره می کند که ارائه دهندهToolchainInfo
لازم را ارائه نمی دهد. بنابراین باید--crosstool_top
را به قانونی اشاره کنید کهToolchainInfo
را ارائه می کند - این قانونcc_toolchain_suite
است. در فایلtoolchain/BUILD
، گروه فایل خالی را با موارد زیر جایگزین کنید:cc_toolchain_suite( name = "clang_suite", toolchains = { "k8": ":k8_toolchain", }, )
ویژگی
toolchains
به طور خودکار--cpu
(و همچنین--compiler
در صورت مشخص شدن) را بهcc_toolchain
می کند. شما هنوز هیچ هدفcc_toolchain
به زودی در مورد آن شکایت خواهد کرد.بیلد را دوباره اجرا کنید. Bazel خطای زیر را می دهد:
Rule '//toolchain:k8_toolchain' does not exist
اکنون باید اهداف
cc_toolchain
را برای هر مقدار در ویژگیcc_toolchain_suite.toolchains
کنید. موارد زیر را به فایلtoolchain/BUILD
اضافه کنید:filegroup(name = "empty") cc_toolchain( name = "k8_toolchain", toolchain_identifier = "k8-toolchain", toolchain_config = ":k8_toolchain_config", all_files = ":empty", compiler_files = ":empty", dwp_files = ":empty", linker_files = ":empty", objcopy_files = ":empty", strip_files = ":empty", supports_param_files = 0, )
بیلد را دوباره اجرا کنید. Bazel خطای زیر را می دهد:
Rule '//toolchain:k8_toolchain_config' does not exist
سپس یک هدف ":k8_toolchain_config" را به فایل
toolchain/BUILD
اضافه کنید:filegroup(name = "k8_toolchain_config")
بیلد را دوباره اجرا کنید. Bazel خطای زیر را می دهد:
'//toolchain:k8_toolchain_config' does not have mandatory providers: 'CcToolchainConfigInfo'
CcToolchainConfigInfo
ارائه دهنده ای است که شما از آن برای پیکربندی زنجیره های ابزار C++ خود استفاده می کنید. برای رفع این خطا، یک قانونCcToolchainConfigInfo
ایجاد کنید که با ایجاد یک فایلtoolchain/cc_toolchain_config.bzl
با محتوای زیر، CcToolchainConfigInfo را در اختیار Bazel قرار می دهد:def _impl(ctx): return cc_common.create_cc_toolchain_config_info( ctx = ctx, toolchain_identifier = "k8-toolchain", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", ) cc_toolchain_config = rule( implementation = _impl, attrs = {}, provides = [CcToolchainConfigInfo], )
cc_common.create_cc_toolchain_config_info()
ارائه دهنده مورد نیازCcToolchainConfigInfo
را ایجاد می کند. برای استفاده از قانونcc_toolchain_config
، یک دستور بار را بهtoolchains/BUILD
اضافه کنید:load(":cc_toolchain_config.bzl", "cc_toolchain_config")
و گروه فایل "k8_toolchain_config" را با یک قانون
cc_toolchain_config
:cc_toolchain_config(name = "k8_toolchain_config")
بیلد را دوباره اجرا کنید. Bazel خطای زیر را می دهد:
.../BUILD:1:1: C++ compilation of rule '//:hello-world' failed (Exit 1) src/main/tools/linux-sandbox-pid1.cc:421: "execvp(toolchain/DUMMY_GCC_TOOL, 0x11f20e0)": No such file or directory Target //:hello-world failed to build`
در این مرحله، Bazel اطلاعات کافی برای تلاش برای ساخت کد دارد، اما هنوز نمی داند از چه ابزارهایی برای تکمیل اقدامات ساخت مورد نیاز استفاده کند. شما اجرای قانون Starlark را تغییر خواهید داد تا به Bazel بگویید از چه ابزارهایی استفاده کند. برای آن، به سازنده tool_path() از
@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl
:# toolchain/cc_toolchain_config.bzl: # NEW load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "tool_path") def _impl(ctx): tool_paths = [ # NEW tool_path( name = "gcc", path = "/usr/bin/clang", ), tool_path( name = "ld", path = "/usr/bin/ld", ), tool_path( name = "ar", path = "/usr/bin/ar", ), tool_path( name = "cpp", path = "/bin/false", ), tool_path( name = "gcov", path = "/bin/false", ), tool_path( name = "nm", path = "/bin/false", ), tool_path( name = "objdump", path = "/bin/false", ), tool_path( name = "strip", path = "/bin/false", ), ] return cc_common.create_cc_toolchain_config_info( ctx = ctx, toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, # NEW )
مطمئن شوید که
/usr/bin/clang
و/usr/bin/ld
مسیرهای صحیح سیستم شما هستند.بیلد را دوباره اجرا کنید. Bazel خطای زیر را می دهد:
..../BUILD:3:1: undeclared inclusion(s) in rule '//main:hello-world': this rule is missing dependency declarations for the following files included by 'main/hello-world.cc': '/usr/include/c++/9/ctime' '/usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h' '/usr/include/x86_64-linux-gnu/c++/9/bits/os_defines.h' ....
Bazel باید بداند که کجا سرصفحه های موجود را جستجو کند. راههای مختلفی برای حل این مشکل وجود دارد، مانند استفاده
includes
ویژگیcc_binary
، اما در اینجا این مشکل در سطح زنجیره ابزار با پارامترcxx_builtin_include_directories
cc_common.create_cc_toolchain_config_info
. مراقب باشید که اگر از نسخه دیگری ازclang
استفاده می کنید، مسیر include متفاوت خواهد بود. این مسیرها نیز ممکن است بسته به توزیع متفاوت باشند.مقدار بازگشتی در
toolchain/cc_toolchain_config.bzl
را به شکل زیر تغییر دهید:return cc_common.create_cc_toolchain_config_info( ctx = ctx, cxx_builtin_include_directories = [ # NEW "/usr/lib/llvm-9/lib/clang/9.0.1/include", "/usr/include", ], toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, )
دوباره دستور build را اجرا کنید، با خطایی مانند:
/usr/bin/ld: bazel-out/k8-fastbuild/bin/main/_objs/hello-world/hello-world.o: in function `print_localtime()': hello-world.cc:(.text+0x68): undefined reference to `std::cout'
دلیل این امر این است که پیوند دهنده کتابخانه استاندارد C++ را ندارد و نمی تواند نمادهای آن را پیدا کند. راه های زیادی برای حل این مشکل وجود دارد، مانند استفاده از ویژگی
linkopts
cc_binary
. در اینجا با اطمینان از اینکه هیچ هدفی که از زنجیره ابزار استفاده می کند نیازی به تعیین این پرچم ندارد، حل می شود.کد زیر را در
cc_toolchain_config.bzl
کپی کنید:# NEW load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") # NEW load( "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "feature", "flag_group", "flag_set", "tool_path", ) all_link_actions = [ # NEW ACTION_NAMES.cpp_link_executable, ACTION_NAMES.cpp_link_dynamic_library, ACTION_NAMES.cpp_link_nodeps_dynamic_library, ] def _impl(ctx): tool_paths = [ tool_path( name = "gcc", path = "/usr/bin/clang", ), tool_path( name = "ld", path = "/usr/bin/ld", ), tool_path( name = "ar", path = "/bin/false", ), tool_path( name = "cpp", path = "/bin/false", ), tool_path( name = "gcov", path = "/bin/false", ), tool_path( name = "nm", path = "/bin/false", ), tool_path( name = "objdump", path = "/bin/false", ), tool_path( name = "strip", path = "/bin/false", ), ] features = [ # NEW feature( name = "default_linker_flags", enabled = True, flag_sets = [ flag_set( actions = all_link_actions, flag_groups = ([ flag_group( flags = [ "-lstdc++", ], ), ]), ), ], ), ] return cc_common.create_cc_toolchain_config_info( ctx = ctx, features = features, # NEW cxx_builtin_include_directories = [ "/usr/lib/llvm-9/lib/clang/9.0.1/include", "/usr/include", ], toolchain_identifier = "local", host_system_name = "local", target_system_name = "local", target_cpu = "k8", target_libc = "unknown", compiler = "clang", abi_version = "unknown", abi_libc_version = "unknown", tool_paths = tool_paths, ) cc_toolchain_config = rule( implementation = _impl, attrs = {}, provides = [CcToolchainConfigInfo], )
اگر
bazel build --config=clang_config //main:hello-world
را اجرا کنید، در نهایت باید ساخته شود.
کار خود را مرور کنید
در این آموزش شما یاد گرفتید که چگونه یک زنجیره ابزار پایه C++ را پیکربندی کنید، اما زنجیره های ابزار قدرتمندتر از این مثال ساده هستند.
نکات کلیدی عبارتند از: - شما باید یک پرچم --crosstool_top
در خط فرمان مشخص کنید که باید به یک cc_toolchain_suite
- می توانید یک میانبر برای یک پیکربندی خاص با استفاده از فایل .bazelrc
. ایجاد کنید - cc_toolchain_suite ممکن است cc_toolchains
را برای موارد مختلف فهرست کند. پردازنده ها و کامپایلرها برای تمایز می توانید از پرچم های خط فرمان مانند --cpu
استفاده کنید. - باید به زنجیره ابزار اطلاع دهید که ابزارها در کجا زندگی می کنند. در این آموزش یک نسخه ساده شده وجود دارد که در آن به ابزارهای سیستم دسترسی دارید. اگر به یک رویکرد مستقلتر علاقه دارید، میتوانید درباره فضاهای کاری اینجا بخوانید . ابزارهای شما میتوانند از فضای کاری متفاوتی باشند و باید فایلهای آنها را با وابستگیهای هدف به ویژگیهایی مانند compiler_files
در دسترس cc_toolchain
قرار دهید. tool_paths
نیز باید تغییر کند. - میتوانید ویژگیهایی را برای سفارشی کردن پرچمها ایجاد کنید که باید به اقدامات مختلف منتقل شوند، خواه پیوند یا هر نوع عمل دیگری.
بیشتر خواندن
برای جزئیات بیشتر، به پیکربندی زنجیره ابزار C++ مراجعه کنید