חשיפה

דף זה מכסה את שתי מערכות החשיפה של Bazel&#39: יעד החשיפה וחשיפה של טעינה.

שני סוגי החשיפה עוזרים למפתחים אחרים להבחין בין ה-API הציבורי של הספרייה שלך לבין פרטי ההטמעה שלה, ועוזרים לאכוף את המבנה כשסביבת העבודה גדלה. תוכלו גם לראות את הזמינות של ההוצאה משימוש של ממשק API ציבורי, כדי לאפשר למשתמשים הקיימים לדחות הרשאות של משתמשים חדשים.

החשיפה של היעד

חשיפה ליעד קובעת מי עשוי להיות תלוי ביעד שלך – כלומר, מי עשוי להשתמש בתווית של היעד בתוך מאפיין כמו deps.

יעד A גלוי ליעד B אם הוא נמצא באותה חבילה, או אם A מעניק חשיפה לחבילה B&33. לכן, חבילות הן היחידה של רמת הפירוט, שמאפשרת להחליט אם לאפשר גישה או לא. אם השדה B תלוי ב-A אבל A לא גלוי ל-B, כל ניסיון לבנות B ייכשל במהלך הניתוח.

שימו לב: הענקת חשיפה לחבילה לא מעניקה בעצמה חשיפה לחבילות המשנה. פרטים נוספים על חבילות וחבילות משנה זמינים במאמר מושגים ומונחים.

לצורך יצירת אב טיפוס, ניתן להשבית את האכיפה של הרשאות הגישה על ידי הגדרת הסימון --check_visibility=false. אין להשתמש באפשרות הזו לשימוש בסביבת ייצור בקוד שנשלח.

הדרך העיקרית לשלוט בחשיפה היא באמצעות המאפיין visibility ביעדים של כללים. קטע זה מתאר את הפורמט של המאפיין הזה, ואיך לקבוע את הרשאות הגישה של יעד.

מפרט הרשאות גישה

לכל יעדי הכללים יש מאפיין visibility שמקבל רשימת תוויות. לכל תווית יש אחד מהטפסים הבאים. למעט הטופס האחרון, אלה רק placeholders תחביריים שלא תואמים לאף יעד בפועל.

  • "//visibility:public": הענקת גישה לכל החבילות. (לא ניתן לשלב אותן עם כל מפרט אחר).

  • "//visibility:private": לא מעניק גישה נוספת. רק יעדים בחבילה הזו יכולים להשתמש ביעד הזה. (לא ניתן לשלב אותן עם כל מפרט אחר).

  • "//foo/bar:__pkg__": מעניק גישה ל-//foo/bar (אבל לא לחבילות המשנה שלו).

  • "//foo/bar:__subpackages__": אפשר לקבל גישה ל-//foo/bar ולכל חבילות המשנה הישירות והעקיפות שלו.

  • "//some_pkg:my_package_group": מעניק גישה לכל החבילות שכלולות בpackage_group הנתון.

    • לקבוצות חבילות יש תחביר שונה לציון חבילות ספציפיות. במסגרת חבילת חבילות, הטפסים "//foo/bar:__pkg__" ו-"//foo/bar:__subpackages__" יוחלפו בהתאמה ב-"//foo/bar" וב-"//foo/bar/...". באופן דומה, "//visibility:public" ו-"//visibility:private" הם רק "public" ו-"private".

לדוגמה, אם המאפיין visibility מוגדר ל-//some/package:mytarget, ניתן להשתמש בו לכל יעד שהוא חלק מעץ המקור של //some/package/..., וגם ליעדים שהוגדרו ב-//tests/BUILD, אבל לא ליעדים שהוגדרו ב-//tests/integration/BUILD.

שיטה מומלצת: כדי להפוך כמה יעדים לגלויים לאותה קבוצת חבילות, אפשר להשתמש ב-package_group במקום לחזור לרשימה בכל מאפיין visibility של יעד. כך ניתן לקרוא יותר ולמנוע את הסנכרון של הרשימות.

החשיפה של יעד הכלל

הנראות של כלל היעד היא:

  1. הערך של המאפיין visibility, אם הוא מוגדר, או אחרת

  2. הערך של ההצהרה default_visibility בהצהרת package בקובץ ה-BUILD של היעד, אם קיימת הצהרה כזו; או

  3. //visibility:private.

שיטה מומלצת: כדאי להגדיר את default_visibility כ'גלוי לכול'. זה עלול להיות נוח ליצירת אב-טיפוס או בבסיסי קוד קטנים, אבל הסיכון ליצור בטעות יעדים ציבוריים גדל ככל שבסיס הקוד גדל. עדיף לציין אילו יעדים הם חלק מהממשק הציבורי של חבילה.

דוגמה

קובץ //frobber/bin/BUILD:

# This target is visible to everyone
cc_binary(
    name = "executable",
    visibility = ["//visibility:public"],
    deps = [":library"],
)

# This target is visible only to targets declared in the same package
cc_library(
    name = "library",
    # No visibility -- defaults to private since no
    # package(default_visibility = ...) was used.
)

# This target is visible to targets in package //object and //noun
cc_library(
    name = "subject",
    visibility = [
        "//noun:__pkg__",
        "//object:__pkg__",
    ],
)

# See package group "//frobber:friends" (below) for who can
# access this target.
cc_library(
    name = "thingy",
    visibility = ["//frobber:friends"],
)

קובץ //frobber/BUILD:

# This is the package group declaration to which target
# //frobber/bin:thingy refers.
#
# Our friends are packages //frobber, //fribber and any
# subpackage of //fribber.
package_group(
    name = "friends",
    packages = [
        "//fribber/...",
        "//frobber",
    ],
)

הרשאות גישה ליעדי קבצים שנוצרו

ליעד קובץ שנוצר יש אותה חשיפה של יעד הכלל שיצר אותו.

הרשאות גישה לקובץ המקור

אפשר להגדיר באופן מפורש את החשיפה של יעד קובץ מקור על ידי התקשרות exports_files. כאשר לא מועברים ארגומנט visibility אל exports_files, הוא מוגדר כגלוי לכול. לא ניתן להשתמש ב-exports_files כדי לבטל את החשיפה של קובץ שנוצר.

ביעדים של קובצי מקור שלא מופיעים בקריאה אל exports_files, הרשאות הגישה תלויות בערך הדגל --incompatible_no_implicit_file_export:

  • אם הדגל מוגדר, החשיפה תהיה פרטית.

  • אחרת, ההתנהגות מהדור הקודם תחול: החשיפה זהה ל-default_visibility של הקובץ BUILD או כפרטית, אם לא צוינה ברירת מחדל לחשיפה.

לא כדאי להסתמך על ההתנהגות מהדור הקודם. יש לכתוב תמיד הצהרת exports_files בכל פעם שיעד של קובץ מקור דורש חשיפה שאינה פרטית.

שיטה מומלצת: כשהדבר אפשרי, עדיף לחשוף יעד של כלל ולא קובץ מקור. לדוגמה, במקום לקרוא ל-exports_files בקובץ .java, יש להקיף את הקובץ ביעד java_library לא פרטי. באופן כללי, טירגוטים לפי כללים צריכים להפנות ישירות לקובצי מקור ששייכים לאותה חבילה.

דוגמה

קובץ //frobber/data/BUILD:

exports_files(["readme.txt"])

קובץ //frobber/bin/BUILD:

cc_binary(
  name = "my-program",
  data = ["//frobber/data:readme.txt"],
)

הרשאות הגישה של הגדרת התצורה

בעבר, לא נאכפה ב-Bazel על יעדי config_setting שהוזכרו במפתחות של select(). יש שני דגלים להסרה של התנהגות זו מהדור הקודם:

  • --incompatible_enforce_config_setting_visibility מאפשרת בדיקת הרשאות גישה ליעדים האלה. כדי לבצע העברה, היא גם גורמת ל-config_setting שלא מציין את visibility להיחשב כ'גלוי לכול' (ללא קשר לרמת default_visibility).

  • --incompatible_config_setting_private_default_visibility גורם ל-config_setting שלא מציינים visibility לכבד את default_visibilityהחבילהdefault_visibility ולעזור בחשיפה פרטית, בדיוק כמו כל יעד אחר. אם המדיניות לא מוגדרת ב---incompatible_enforce_config_setting_visibility, זה לא אפשרי.

לא כדאי להסתמך על ההתנהגות מהדור הקודם. כל config_setting שמיועד לשימוש מחוץ לחבילה הנוכחית צריך לכלול visibility מפורש, אם החבילה לא מציינת כבר default_visibility מתאים.

החשיפה של יעד קבוצת החבילות

ליעדים package_group אין מאפיין visibility. הם תמיד גלויים לכולם.

חשיפה של יחסי תלות מרומזים

לחלק מהכללים יש תלויות משתמעות — תלויות שלא מאויתות בקובץ BUILD, אבל מטבען הן מופעות של כל כלל. לדוגמה, כלל cc_library יכול ליצור תלות מרומזת מכל אחד מהיעדים שלו ליעד הפעלה שמציג את המהדר C++.

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

אפשר לשנות את ההתנהגות הזו באמצעות ההגדרה --incompatible_visibility_private_attributes_at_definition. כשמפעילים את היעד, הוא צריך להיות גלוי רק לכלל שמצהיר עליו כי הוא תלוי באופן מרומז. כלומר, הוא צריך להיות גלוי לחבילה שמכילה את הקובץ .bzl שבו הכלל מוגדר. בדוגמה שלנו, המהדר C++ יכול להיות פרטי, כל עוד הוא חי באותה חבילה כמו ההגדרה של הכלל cc_library.

טעינת החשיפה

הרשאות טעינה קובעת אם ניתן לטעון קובץ .bzl מקבצים אחרים מ-BUILD או .bzl.

באופן דומה, חשיפת היעד מגנה על קוד המקור שמורחב על ידי יעדים, וגם חשיפת הטעינה מגנה על לוגיקת build הכלולה בקובצי .bzl. למשל, למחבר של קובץ BUILD מומלץ לשייך כמה הגדרות יעד חוזרות למאקרו בקובץ .bzl. בלי ההגנה על חשיפת העומס, הם יוכלו למצוא שימוש חוזר במאקרו של שותפי עריכה אחרים באותו סביבת עבודה, כך ששינוי המאקרו יקטע צוותים אחרים.

לתשומת ליבך, קובץ .bzl יכול להיות בלי יעד של קובץ מקור תואם. במקרה כזה, אין ערובה לכך שחשיפה של הטעינה וחשיפה של היעד אכן חופפים. כלומר, יכול להיות שאותו קובץ (BUILD) יוכל לטעון את הקובץ .bzl, אבל לא לציין אותו ב-srcs של filegroup. לפעמים זה יכול לגרום לבעיות בכללים שרוצים לצרוך .bzl קבצים כקוד המקור, כמו יצירת מסמכים או בדיקה.

לצורך יצירת אב טיפוס, ניתן להשבית את האכיפה לחשיפת החשיפה באמצעות ההגדרה --check_bzl_visibility=false. כמו במקרה של --check_visibility=false, אין לעשות זאת עבור קוד שנשלח.

ניראות של עומסים זמינה החל מ-Bazel 6.0.

הצהרה על חשיפה לטעינה

כדי להגדיר את עומס הטעינה של קובץ .bzl, אפשר לקרוא לפונקציה visibility() מתוך הקובץ. הארגומנט ל-visibility() הוא רשימה של מפרטי חבילות, בדיוק כמו המאפיין packages של package_group. עם זאת, המוצר visibility() לא מקבל מפרטים שליליים לחבילה.

הקריאה ל-visibility() חייבת להתבצע רק פעם אחת בכל קובץ, ברמה העליונה (לא בתוך פונקציה), ומומלץ להוסיף אותה מיד אחרי ההצהרות של load().

בשונה מחשיפה ליעד, החשיפה לעומס שמוגדרת כברירת מחדל היא תמיד ציבורית. תמיד אפשר לטעון קבצים שלא קוראים ל-visibility() מכל מקום בסביבת העבודה. כדאי להוסיף את visibility("private") בחלק העליון של כל קובץ .bzl חדש שלא מיועד ספציפית לשימוש מחוץ לחבילה.

דוגמה

# //mylib/internal_defs.bzl

# Available to subpackages and to mylib's tests.
visibility(["//mylib/...", "//tests/mylib/..."])

def helper(...):
    ...
# //mylib/rules.bzl

load(":internal_defs.bzl", "helper")
# Set visibility explicitly, even though public is the default.
# Note the [] can be omitted when there's only one entry.
visibility("public")

myrule = rule(
    ...
)
# //someclient/BUILD

load("//mylib:rules.bzl", "myrule")          # ok
load("//mylib:internal_defs.bzl", "helper")  # error

...

טעינת שיטות חשיפה

בקטע הזה מתוארים טיפים לניהול הצהרות לגבי ניראות של עומסים.

התחשבות בחשיפות

כאשר למספר קובצי .bzl צריכה להיות אותה חשיפה, כדאי לשבץ את מפרטי החבילות שלהם ברשימה משותפת. למשל:

# //mylib/internal_defs.bzl

visibility("private")

clients = [
    "//foo",
    "//bar/baz/...",
    ...
]
# //mylib/feature_A.bzl

load(":internal_defs.bzl", "clients")
visibility(clients)

...
# //mylib/feature_B.bzl

load(":internal_defs.bzl", "clients")
visibility(clients)

...

כך אפשר למנוע הטיה מקרית בין .bzl הקבצים השונים. אפשר גם לקרוא אותה אם הרשימה clients תהיה גדולה.

חשיפות של חשיפות

לפעמים קובץ .bzl אולי צריך להיות גלוי לרשימת היתרים שמורכבת מכמה רשימות היתרים קטנות יותר. קטע זה דומה לאופן שבו package_group יכול לשלב package_group אחרים באמצעות המאפיין includes.

נניח שמוציאים משימוש מאקרו נפוץ. אתם רוצים שהוא יהיה גלוי רק למשתמשים קיימים ולחבילות שבבעלות הצוות שלכם. אפשר לכתוב:

# //mylib/macros.bzl

load(":internal_defs.bzl", "our_packages")
load("//some_big_client:defs.bzl", "their_remaining_uses)

# List concatenation. Duplicates are fine.
visibility(our_packages + their_remaining_uses)

ביטול כפילויות עם קבוצות חבילות

בניגוד להגדרת הרשאות הגישה של יעד, לא ניתן להגדיר חשיפה של המרות במונחים של package_group. אם אתם רוצים להוסיף את אותה רשימת היתרים גם לחשיפה של יעד וגם לחשיפת עומס, עדיף להעביר את רשימת המפרט של החבילה לקובץ .bzl שבו שתי ההצהרות יכולות להתייחס. אם תוסיפו את הדוגמה בחיזוק הניראות למעלה, תוכלו לכתוב:

# //mylib/BUILD

load(":internal_defs", "clients")

package_group(
    name = "my_pkg_grp",
    packages = clients,
)

אפשרות זו פועלת רק אם הרשימה לא מכילה מפרטים שליליים לחבילה.

הגנה על סמלים ספציפיים

לא ניתן לטעון כל קובץ Starlark שהשם שלו מתחיל בקו תחתון. כך קל ליצור סמלים פרטיים, אבל אי אפשר לשתף את הסמלים האלה עם קבוצה מוגבלת של קבצים מהימנים. מצד שני, הרשאות גישה יאפשרו לך לקבוע אילו חבילות אחרות יוצגו ב-.bzl file, אבל לא תמנע טעינה של סמלים שאינם מסומנים.

למזלך, אפשר לשלב את שתי התכונות האלה כדי לקבל שליטה מדוקדקת.

# //mylib/internal_defs.bzl

# Can't be public, because internal_helper shouldn't be exposed to the world.
visibility("private")

# Can't be underscore-prefixed, because this is
# needed by other .bzl files in mylib.
def internal_helper(...):
    ...

def public_util(...):
    ...
# //mylib/defs.bzl

load(":internal_defs", "internal_helper", _public_util="public_util")
visibility("public")

# internal_helper, as a loaded symbol, is available for use in this file but
# can't be imported by clients who load this file.
...

# Re-export public_util from this file by assigning it to a global variable.
# We needed to import it under a different name ("_public_util") in order for
# this assignment to be legal.
public_util = _public_util

מוך build של Bzl

יש קונטיינר של build שמספק אזהרה אם משתמשים טוענים קובץ מהספרייה בשם internal או private, כאשר הקובץ של המשתמש לא נמצא מתחת להורה של אותה ספרייה. המוך הזה מוקדם יותר מתכונת החשיפה של הטעינה, והוא מיותר בסביבות עבודה שבהן קובצי .bzl מצהירים על חשיפות.