תרחישי C++ Build נפוצים

כאן ניתן למצוא חלק ממקרי השימוש הנפוצים ביותר לבניית פרויקט C++ עם Bazel. אם עדיין לא עשיתם זאת, כדאי להתחיל לבנות פרויקטים עם C++ עם Bazel על ידי השלמת המדריך מבוא לבזל: בניית פרויקט C++.

אפשר למצוא מידע נוסף על קובצי כותרת מסוג cc_library ו-hdrs בכתובת cc_library.

הכללה של קבצים מרובים ביעד

ניתן לכלול כמה קבצים ביעד אחד באמצעות glob. למשל:

cc_library(
    name = "build-all-the-files",
    srcs = glob(["*.cc"]),
    hdrs = glob(["*.h"]),
)

עם היעד הזה, Bazel תבנה את כל הקבצים ש-.cc ו-.h היא מוצאת באותה ספרייה כמו קובץ BUILD שמכיל את היעד הזה (לא כולל ספריות משנה).

השימוש ברכיבים עקיפים כולל

אם קובץ כולל כותרת, הכלל הזה צריך לשמש כמקור (כלומר, עריכת הקובץ במאפיינים srcs, hdrs, או textual_hdrs) על כלל הספרייה של הכותרת הכלולה. לעומת זאת, יש לציין תלויות ישירות רק כיחסי תלות. לדוגמה, נניח ש-sandwich.h כולל את bread.h ואת bread.h כולל את flour.h. sandwich.h לא כולל את flour.h (מי רוצה קמח בסנדוויץ'?), כך שהקובץ BUILD ייראה כך:

cc_library(
    name = "sandwich",
    srcs = ["sandwich.cc"],
    hdrs = ["sandwich.h"],
    deps = [":bread"],
)

cc_library(
    name = "bread",
    srcs = ["bread.cc"],
    hdrs = ["bread.h"],
    deps = [":flour"],
)

cc_library(
    name = "flour",
    srcs = ["flour.cc"],
    hdrs = ["flour.h"],
)

כאן, ספריית sandwich תלויה בספריית bread, שתלויה בספרייה של flour.

מתבצעת הוספה של נתיבי הכללה

לפעמים אינך יכול (או לא רוצה) לכלול שורשים בסביבת העבודה הבסיסית. לספריות קיימות כבר יכולה להיות ספרייה הכללה שאינה תואמת לנתיב שלהן בסביבת העבודה שלכם. לדוגמה, נניח שיש לך את מבנה הספרייה הבא:

└── my-project
    ├── legacy
    │   └── some_lib
    │       ├── BUILD
    │       ├── include
    │       │   └── some_lib.h
    │       └── some_lib.cc
    └── WORKSPACE

Bazel תצפה ש-some_lib.h ייכלל כ-legacy/some_lib/include/some_lib.h, אבל נניח ש-some_lib.cc כולל את "some_lib.h". כדי שהנתיב יכלול נתיב חוקי, יהיה על legacy/some_lib/BUILD לציין שהספרייה some_lib/include היא ספריית הכללה:

cc_library(
    name = "some_lib",
    srcs = ["some_lib.cc"],
    hdrs = ["include/some_lib.h"],
    copts = ["-Ilegacy/some_lib/include"],
)

זוהי אפשרות שימושית במיוחד עבור יחסי תלות חיצוניים, מפני שקובצי הכותרת שלהם צריכים להיכלל בקידומת /.

כולל ספריות חיצוניות

נניח שאתם משתמשים ב-Google Test. תוכל להשתמש באחת מפונקציות המאגר בקובץ WORKSPACE כדי להוריד את בדיקת Google ולהפוך אותה לזמינה במאגר שלך:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "gtest",
    url = "https://github.com/google/googletest/archive/release-1.10.0.zip",
    sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
    build_file = "@//:gtest.BUILD",
)

לאחר מכן יש ליצור gtest.BUILD, קובץ BUILD המשמש להידור של בדיקת Google. לבדיקה של Google יש כמה דרישות "מיוחדות" שהופכות את הכלל cc_library למורכבות יותר:

  • googletest-release-1.10.0/src/gtest-all.cc #includeכל שאר הקבצים ב-googletest-release-1.10.0/src/: יש להחריג את הקובץ מההידור כדי למנוע שגיאות בקישור לסמלים כפולים.

  • היא משתמשת בקובצי כותרת שהם יחסיים לספרייה googletest-release-1.10.0/include/ ("gtest/gtest.h"), לכן עליך להוסיף את הספרייה לנתיבי ההכללה.

  • הקישור חייב להתבצע ב-pthread, לכן יש להוסיף אותו כ-linkopt.

לכן הכלל האחרון נראה כך:

cc_library(
    name = "main",
    srcs = glob(
        ["googletest-release-1.10.0/src/*.cc"],
        exclude = ["googletest-release-1.10.0/src/gtest-all.cc"]
    ),
    hdrs = glob([
        "googletest-release-1.10.0/include/**/*.h",
        "googletest-release-1.10.0/src/*.h"
    ]),
    copts = [
        "-Iexternal/gtest/googletest-release-1.10.0/include",
        "-Iexternal/gtest/googletest-release-1.10.0"
    ],
    linkopts = ["-pthread"],
    visibility = ["//visibility:public"],
)

משהו קצת מבולגן: התחילית של googletest-release-1.10.0 היא תוצר נוסף של מבנה הארכיון. ניתן להוסיף http_archive לקידומת הזו על ידי הוספת המאפיין strip_prefix:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "gtest",
    url = "https://github.com/google/googletest/archive/release-1.10.0.zip",
    sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
    build_file = "@//:gtest.BUILD",
    strip_prefix = "googletest-release-1.10.0",
)

אז gtest.BUILD ייראה כך:

cc_library(
    name = "main",
    srcs = glob(
        ["src/*.cc"],
        exclude = ["src/gtest-all.cc"]
    ),
    hdrs = glob([
        "include/**/*.h",
        "src/*.h"
    ]),
    copts = ["-Iexternal/gtest/include"],
    linkopts = ["-pthread"],
    visibility = ["//visibility:public"],
)

עכשיו כללי cc_ תלויים ב-@gtest//:main.

כתיבה והפעלה של בדיקות C++

לדוגמה, אפשר ליצור בדיקה של ./test/hello-test.cc, למשל:

#include "gtest/gtest.h"
#include "main/hello-greet.h"

TEST(HelloTest, GetGreet) {
  EXPECT_EQ(get_greet("Bazel"), "Hello Bazel");
}

לאחר מכן, יוצרים קובץ ./test/BUILD לבדיקות:

cc_test(
    name = "hello-test",
    srcs = ["hello-test.cc"],
    copts = ["-Iexternal/gtest/include"],
    deps = [
        "@gtest//:main",
        "//main:hello-greet",
    ],
)

כדי שhello-greet יהיה גלוי ל-hello-test, עליך להוסיף את "//test:__pkg__", למאפיין visibility ב-./main/BUILD.

עכשיו אפשר להשתמש ב-bazel test כדי להריץ את הבדיקה.

bazel test test:hello-test

זה מפיק את הפלט הבא:

INFO: Found 1 test target...
Target //test:hello-test up-to-date:
  bazel-bin/test/hello-test
INFO: Elapsed time: 4.497s, Critical Path: 2.53s
//test:hello-test PASSED in 0.3s

Executed 1 out of 1 tests: 1 test passes.

הוספת יחסי תלות בספריות שנוצרו מראש

אם ברצונך להשתמש בספרייה שיש לך רק גרסה מגובשת (לדוגמה, כותרות וקובץ .so), יש לעטוף אותה בכלל של cc_library:

cc_library(
    name = "mylib",
    srcs = ["mylib.so"],
    hdrs = ["mylib.h"],
)

באופן כזה, יעדי C++ אחרים בסביבת העבודה שלך עשויים להיות תלויים בכלל הזה.