يستخدِم هذا البرنامج التعليمي سيناريو نموذجيًا لوصف كيفية إعداد أدوات C++
لمشروع. ويستند ذلك إلى
مثال على مشروع C++
والذي يخلو من الأخطاء باستخدام clang
.
ما ستتعرَّف عليه
ستتعرّف في هذا البرنامج التعليمي على كيفية:
- إعداد بيئة الإصدار
- ضبط سلسلة أدوات C++
- أنشِئ قاعدة Starlark التي تقدِّم
ضبطًا إضافيًا لجهاز
cc_toolchain
حتى يتمكّن Bazel من إنشاء التطبيق باستخدامclang
- يمكنك تأكيد النتيجة المتوقعة من خلال تشغيل
bazel build --config=clang_config //main:hello-world
على جهاز يعمل بنظام التشغيل Linux. - إنشاء تطبيق C++
قبل البدء
إعداد بيئة الإصدار
يفترض هذا البرنامج التعليمي أنك تستخدم نظام التشغيل Linux وقد أنشأت تطبيقات 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
، يستخدم بازيل
سلسلة الأدوات المخصّصة الخاصة بك من
cc_toolchain_suite
//toolchain:clang_suite
. يمكن أن تدرج الحزمة
سلاسل أدوات مختلفة لمختلف وحدات المعالجة المركزية،
ولهذا السبب يتم تمييزها مع العلامة --cpu=k8
.
لأن Bazel تستخدم العديد من الأدوات الداخلية المكتوبة باستخدام C++ أثناء عملية الإنشاء، مثل برنامج تضمين المعالجة، يتم تحديد سلسلة أدوات 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'
اكتشف بازيل أن علامة
--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 شكوى بشأن ذلك قريبًا.شغِّل الإصدار مرة أخرى. يعرض 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++ الخاصة بك. لإصلاح هذا الخطأ، أنشئ قاعدة Starlark توفرCcToolchainConfigInfo
لـ Bazel من خلال إنشاء ملفtoolchain/cc_toolchain_config.bzl
يتضمن المحتوى التالي: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`
في هذه المرحلة، تتوفّر لدى "بازيل" معلومات كافية لمحاولة إنشاء الرمز، لكنها لا تزال لا تعرف الأدوات التي يمكن استخدامها لإكمال إجراءات الإصدار المطلوبة. عليك تعديل تنفيذ قاعدة Starlark لإعلام "بازيل" بالأدوات التي يمكن استخدامها. لتنفيذ هذا الإجراء، يجب استخدام أداة إنشاء bundle_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
، سيكون مسار التضمين مختلفًا. وقد تختلف هذه المسارات أيضًا حسب التوزيع.عدِّل قيمة الإرجاع في
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
للتمييز بينها.
- يتعيّن عليك السماح لسلسلة الأدوات بمعرفة أماكن الأدوات. في هذا البرنامج التعليمي، تتوفر نسخة مبسّطة يمكنك من خلالها الوصول إلى الأدوات التي يوفّرها النظام. إذا كنت
مهتمًا باتّباع نهج مستقل أكثر، يمكنك الاطّلاع على
مساحات العمل هنا. ويمكن أن تأتي أدواتك من
مساحة عمل مختلفة، ما يجعل عليك إتاحة ملفاتها
لـ cc_toolchain
مع اعتمادية الارتباطات على السمات، مثل
compiler_files
. يجب أيضًا تغيير tool_paths
.
- يمكنك إنشاء ميزات لتخصيص العلامات التي يجب تمريرها إلى
الإجراءات المختلفة، سواء كانت رابطًا أو أي نوع آخر من الإجراءات.
قراءة إضافية
لمزيد من التفاصيل، يمكنك الاطّلاع على ضبط سلسلة أدوات C++