این آموزش از یک سناریوی مثال برای توصیف نحوه پیکربندی زنجیرههای ابزار 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_directoriescc_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++ را ندارد و نمی تواند نمادهای آن را پیدا کند. راه های زیادی برای حل این مشکل وجود دارد، مانند استفاده از ویژگی
linkoptscc_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++ مراجعه کنید