يستخدِم هذا البرنامج التعليمي سيناريو نموذجيًا لوصف كيفية إعداد أدوات 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++