Görünürlük

Sorun bildir Kaynağı görüntüle Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Bu sayfada Bazel'in iki görünürlük sistemi ele alınmaktadır: Hedef görünürlüğü ve yükleme görünürlüğü.

Her iki görünürlük türü de diğer geliştiricilerin kitaplığınızın herkese açık API'si ile uygulama ayrıntılarını ayırt etmesine ve çalışma alanınız büyüdükçe yapıyı zorunlu kılmaya yardımcı olur. Ayrıca, herkese açık bir API'nin desteğini sonlandırırken mevcut kullanıcılara izin verip yeni kullanıcıları engellemek için de görünürlüğü kullanabilirsiniz.

Hedef görünürlüğü

Hedef görünürlüğü, hedefinizden kimlerin yararlanabileceğini (ör. deps gibi bir özellik içinde hedefinizin etiketini kimlerin kullanabileceğini) kontrol etmenizi sağlar. Bir hedef, bağımlılıklarından birinin görünürlüğünü ihlal ederse analiz aşamasında oluşturulamaz.

Genel olarak, bir hedef A ile hedef B aynı konumdaysa veya A, B'nin konumuna görünürlük izni veriyorsa A, B'ye görünür olur. Sembolik makrolar olmadığında "konum" terimi yalnızca "paket" olarak basitleştirilebilir. Sembolik makrolar hakkında daha fazla bilgi için aşağıya bakın.

Görünürlük, izin verilen paketler listelenerek belirtilir. Bir pakete izin verilmesi, alt paketlerine de izin verileceği anlamına gelmez. Paketler ve alt paketler hakkında daha fazla bilgi için Kavramlar ve terminoloji başlıklı makaleyi inceleyin.

Prototip oluşturma için --check_visibility=false işaretini ayarlayarak hedef görünürlüğü zorunluluğunu devre dışı bırakabilirsiniz. Bu işlem, gönderilen kodda üretim kullanımı için yapılmamalıdır.

Görünürlüğü kontrol etmenin temel yolu, kuralın visibility özelliğini kullanmaktır. Aşağıdaki alt bölümlerde, özelliğin biçimi, çeşitli hedef türlerine nasıl uygulanacağı ve görünürlük sistemi ile sembolik makrolar arasındaki etkileşim açıklanmaktadır.

Görünürlük özellikleri

Tüm kural hedeflerinde, etiket listesi alan bir visibility özelliği bulunur. Her etiket aşağıdaki biçimlerden birine sahiptir. Son form hariç, bunlar herhangi bir gerçek hedefle eşleşmeyen yalnızca söz dizimsel yer tutuculardır.

  • "//visibility:public": Tüm paketlere erişim izni verir.

  • "//visibility:private": Ek erişim izni vermez. Yalnızca bu konum paketindeki hedefler bu hedefi kullanabilir.

  • "//foo/bar:__pkg__": //foo/bar'e (ancak alt paketlerine değil) erişim izni verir.

  • "//foo/bar:__subpackages__": //foo/bar ve doğrudan ve dolaylı olarak tüm alt paketlerine erişim izni verir.

  • "//some_pkg:my_package_group": Belirtilen package_group'ın parçası olan tüm paketlere erişim izni verir.

    • Paket grupları, paketleri belirtmek için farklı bir söz dizimi kullanır. Bir paket grubunda, "//foo/bar:__pkg__" ve "//foo/bar:__subpackages__" formları sırasıyla "//foo/bar" ve "//foo/bar/..." ile değiştirilir. Aynı şekilde, "//visibility:public" ve "//visibility:private" yalnızca "public" ve "private"'dür.

Örneğin, //some/package:mytarget öğesinin visibility değeri [":__subpackages__", "//tests:__pkg__"] olarak ayarlanmışsa bu öğe, //some/package/... kaynak ağacının parçası olan tüm hedefler ve //tests/BUILD içinde belirtilen hedefler tarafından kullanılabilir ancak //tests/integration/BUILD içinde tanımlanan hedefler tarafından kullanılamaz.

En iyi uygulama: Aynı paket grubunun birden fazla hedefi görmesini sağlamak için listeyi her hedefin visibility özelliğinde tekrarlamak yerine package_group kullanın. Bu sayede okunabilirlik artar ve listelerin senkronizasyonunun bozulması önlenir.

En iyi uygulama: Başka bir ekibin projesine görünürlük izni verirken, bu proje geliştikçe ve yeni alt paketler eklendikçe gereksiz görünürlük değişikliklerinden kaçınmak için __pkg__ yerine __subpackages__ tercih edin.

Kural hedefi görünürlüğü

Bir kural hedefinin görünürlüğü, visibility özelliği (verilmediyse uygun bir varsayılan değer) alınarak ve hedefin bildirildiği konum eklenerek belirlenir. Sembolik bir makroda belirtilmeyen hedefler için paket bir default_visibility belirtiyorsa bu varsayılan değer kullanılır. Diğer tüm paketler ve sembolik bir makroda belirtilen hedefler için varsayılan değer yalnızca ["//visibility:private"]'dir.

# //mypkg/BUILD

package(default_visibility = ["//friend:__pkg__"])

cc_library(
    name = "t1",
    ...
    # No visibility explicitly specified.
    # Effective visibility is ["//friend:__pkg__", "//mypkg:__pkg__"].
    # If no default_visibility were given in package(...), the visibility would
    # instead default to ["//visibility:private"], and the effective visibility
    # would be ["//mypkg:__pkg__"].
)

cc_library(
    name = "t2",
    ...
    visibility = [":clients"],
    # Effective visibility is ["//mypkg:clients, "//mypkg:__pkg__"], which will
    # expand to ["//another_friend:__subpackages__", "//mypkg:__pkg__"].
)

cc_library(
    name = "t3",
    ...
    visibility = ["//visibility:private"],
    # Effective visibility is ["//mypkg:__pkg__"]
)

package_group(
    name = "clients",
    packages = ["//another_friend/..."],
)

En iyi uygulama: default_visibility değerini herkese açık olarak ayarlamaktan kaçının. Prototip oluşturma veya küçük kod tabanlarında kullanışlı olabilir ancak kod tabanı büyüdükçe yanlışlıkla herkese açık hedefler oluşturma riski artar. Hangi hedeflerin bir paketin herkese açık arayüzünün parçası olduğu konusunda açık olmak daha iyidir.

Oluşturulan dosyanın hedef görünürlüğü

Oluşturulan dosya hedefi, onu oluşturan kural hedefiyle aynı görünürlüğe sahiptir.

# //mypkg/BUILD

java_binary(
    name = "foo",
    ...
    visibility = ["//friend:__pkg__"],
)
# //friend/BUILD

some_rule(
    name = "bar",
    deps = [
        # Allowed directly by visibility of foo.
        "//mypkg:foo",
        # Also allowed. The java_binary's "_deploy.jar" implicit output file
        # target the same visibility as the rule target itself.
        "//mypkg:foo_deploy.jar",
    ]
    ...
)

Kaynak dosya hedef görünürlüğü

Kaynak dosya hedefleri, exports_files kullanılarak açıkça tanımlanabilir veya bir kuralın etiket özelliğinde (sembolik makro dışında) dosya adına referans verilerek örtülü olarak oluşturulabilir. Kural hedeflerinde olduğu gibi, exports_files çağrısının konumu veya giriş dosyasına başvuran BUILD dosyası her zaman dosyanın görünürlüğüne otomatik olarak eklenir.

exports_files tarafından bildirilen dosyaların görünürlüğü, bu işlevin visibility parametresiyle ayarlanabilir. Bu parametre verilmezse görünürlük herkese açık olur.

exports_files çağrısında görünmeyen dosyaların görünürlüğü, --incompatible_no_implicit_file_export işaretinin değerine bağlıdır:

  • İşaret doğruysa görünürlük gizlidir.

  • Aksi takdirde eski davranış geçerli olur: Görünürlük, BUILD dosyasının default_visibility görünürlüğüyle aynıdır veya varsayılan görünürlük belirtilmemişse gizlidir.

Eski davranışa güvenmekten kaçının. Bir kaynak dosya hedefinin gizli olmayan görünürlüğe ihtiyacı olduğunda her zaman exports_files bildirim yazın.

En iyi uygulama: Mümkün olduğunda kaynak dosya yerine kural hedefi göstermeyi tercih edin. Örneğin, .java dosyasında exports_files işlevini çağırmak yerine dosyayı gizli olmayan bir java_library hedefiyle sarmalayın. Genel olarak, kural hedefleri yalnızca aynı pakette bulunan kaynak dosyalara doğrudan referans vermelidir.

Örnek

Dosya //frobber/data/BUILD:

exports_files(["readme.txt"])

Dosya //frobber/bin/BUILD:

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

Yapılandırma ayarı görünürlüğü

Geçmişte Bazel, select() anahtarlarında referans verilen config_setting hedefler için görünürlüğü zorunlu kılmıyordu. Bu eski davranışı kaldırmak için iki işaret vardır:

  • --incompatible_enforce_config_setting_visibility bu hedefler için görünürlük kontrolünü etkinleştirir. Taşımaya yardımcı olmak için visibility belirtilmeyen tüm config_setting öğelerinin herkese açık olarak kabul edilmesine de neden olur (paket düzeyindeki default_visibility'den bağımsız olarak).

  • --incompatible_config_setting_private_default_visibility visibility belirtmeyen config_setting'lerin, paketin default_visibility'ına uymasına ve diğer tüm kural hedefleri gibi özel görünürlüğe geri dönmesine neden olur. --incompatible_enforce_config_setting_visibility ayarlanmamışsa işlem yapılmaz.

Eski davranışa güvenmekten kaçının. Mevcut paketin dışında kullanılmak üzere tasarlanan tüm config_setting, paket uygun bir default_visibility belirtmiyorsa açık bir visibility içermelidir.

Paket grubu hedef görünürlüğü

package_group hedeflerinin visibility özelliği yok. Bu yorumlar her zaman herkese açık olarak görünür.

Örtülü bağımlılıkların görünürlüğü

Bazı kuralların örtülü bağımlılıkları vardır. Bu bağımlılıklar, BUILD dosyasında açıkça belirtilmeyen ancak kuralın her örneğinde bulunan bağımlılıklardır. Örneğin, bir cc_library kuralı, kural hedeflerinin her birinden C++ derleyicisini temsil eden yürütülebilir bir hedefe örtülü bağımlılık oluşturabilir.

Bu tür bir örtülü bağımlılığın görünürlüğü, kuralın (veya yönün) tanımlandığı .bzl dosyasını içeren paketle ilgili olarak kontrol edilir. Örneğimizde, C++ derleyicisi cc_library kuralının tanımıyla aynı pakette bulunduğu sürece özel olabilir. Yedek olarak, tanımda örtülü bağımlılık görünmüyorsa cc_library hedefiyle ilgili olarak kontrol edilir.

Bir kuralın kullanımını belirli paketlerle sınırlamak istiyorsanız bunun yerine load visibility'yi kullanın.

Görünürlük ve sembolik makrolar

Bu bölümde, görünürlük sisteminin sembolik makrolarla nasıl etkileşime girdiği açıklanmaktadır.

Sembolik makrolardaki konumlar

Görünürlük sisteminin önemli bir ayrıntısı, bir bildirimin konumunu nasıl belirlediğimizdir. Sembolik bir makroda beyan edilmeyen hedefler için konum, yalnızca hedefin bulunduğu pakettir (BUILD dosyasının paketi). Ancak sembolik bir makroda oluşturulan hedefler için konum, makronun tanımının (my_macro = macro(...) ifadesi) göründüğü .bzl dosyasını içeren pakettir. Bir hedef, iç içe yerleştirilmiş birden fazla hedef içinde oluşturulduğunda her zaman en içteki sembolik makronun tanımı kullanılır.

Belirli bir bağımlılığın görünürlüğünü kontrol etmek için hangi konumun kullanılacağını belirlemek üzere aynı sistem kullanılır. Tüketen hedef bir makro içinde oluşturulduysa tüketen hedefin bulunduğu pakete değil, en içteki makronun tanımına bakarız.

Bu, kodu aynı pakette tanımlanan tüm makroların otomatik olarak birbirinin "arkadaşı" olduğu anlamına gelir. //lib:defs.bzl içinde tanımlanan bir makro tarafından doğrudan oluşturulan tüm hedefler, makroların gerçekte hangi paketlerde örneklendiğinden bağımsız olarak //lib içinde tanımlanan diğer tüm makrolardan görülebilir. Aynı şekilde, //lib/BUILD ve eski makrolarında doğrudan belirtilen hedefleri görebilir ve bu hedefler tarafından görülebilirler. Aynı pakette bulunan hedeflerden en az biri sembolik bir makro tarafından oluşturulmuşsa bu hedefler birbirini göremeyebilir.

Sembolik bir makronun uygulama işlevinde, visibility parametresi, makronun çağrıldığı konum eklendikten sonra makronun visibility özelliğinin etkin değerine sahiptir. Bir makronun hedeflerinden birini arayanına aktarmasının standart yolu, bu değeri hedef beyanına iletmektir (some_rule(..., visibility = visibility) örneğinde olduğu gibi). Bu özelliği atlayan hedefler, makro tanımıyla aynı pakette olmadıkları sürece makroyu çağıran kişiye görünmez. Bu davranış, alt makrolara yapılan iç içe yerleştirilmiş çağrılar zincirinin her biri visibility = visibility'yı geçirebileceği anlamında birleştirir. Bu durumda, iç makronun dışa aktarılan hedefleri, makroların uygulama ayrıntılarından herhangi birini göstermeden her düzeyde arayana yeniden dışa aktarılır.

Ayrıcalıkları bir alt makroya devretme

Görünürlük modelinde, bir makronun izinlerini alt makroya devretmesine olanak tanıyan özel bir özellik bulunur. Bu, makroların faktöriyel olarak hesaplanması ve oluşturulması için önemlidir.

Başka bir paketteki some_library kuralını kullanarak bağımlılık kenarı oluşturan bir makronuz my_macro olduğunu varsayalım:

# //macro/defs.bzl
load("//lib:defs.bzl", "some_library")

def _impl(name, visibility, ...):
    ...
    native.genrule(
        name = name + "_dependency"
        ...
    )
    some_library(
        name = name + "_consumer",
        deps = [name + "_dependency"],
        ...
    )

my_macro = macro(implementation = _impl, ...)
# //pkg/BUILD

load("//macro:defs.bzl", "my_macro")

my_macro(name = "foo", ...)

//pkg:foo_dependency hedefinde visibility belirtilmediğinden yalnızca //macro içinde görünür. Bu durum, tüketen hedef için sorun teşkil etmez. Peki //lib yazarı, some_library işlevini makro kullanarak uygulanacak şekilde yeniden düzenlerse ne olur?

# //lib:defs.bzl

def _impl(name, visibility, deps, ...):
    some_rule(
        # Main target, exported.
        name = name,
        visibility = visibility,
        deps = deps,
        ...)

some_library = macro(implementation = _impl, ...)

Bu değişiklikle birlikte //pkg:foo_consumer'nın konumu artık //macro yerine //lib olduğundan //pkg:foo_dependency kullanımı, bağımlılığın görünürlüğünü ihlal ediyor. my_macro yazarı, bu uygulama ayrıntısını çözmek için visibility = ["//lib"] bağımlılık bildirimine iletmek zorunda kalmamalıdır.

Bu nedenle, bir hedefin bağımlılığı aynı zamanda hedefi bildiren makronun bir özellik değeri olduğunda, bağımlılığın görünürlüğünü tüketen hedefin konumu yerine makronun konumuna göre kontrol ederiz.

Bu örnekte, //pkg:foo_consumer öğesinin //pkg:foo_dependency öğesini görebildiğini doğrulamak için //pkg:foo_dependency öğesinin my_macro içindeki some_library çağrısına giriş olarak da iletildiğini görüyoruz ve bunun yerine bağımlılığın görünürlüğünü bu çağrının konumu olan //macro'e göre kontrol ediyoruz.

Bu işlem, bir hedef veya makro bildirimi, bağımlılığın etiketini etiket türünde özelliklerinden birinde alan başka bir sembolik makronun içinde olduğu sürece yinelemeli olarak tekrarlanabilir.

Sonlandırıcılar

Bir kural sonlandırıcıda (finalizer = True ile sembolik bir makro) belirtilen hedefler, normal sembolik makro görünürlük kurallarına uyan hedefleri görmenin yanı sıra, sonlandırıcı hedefinin paketine görünür olan tüm hedefleri de görebilir.

Diğer bir deyişle, native.existing_rules() tabanlı eski bir makroyu sonlandırıcıya taşırsanız sonlandırıcı tarafından bildirilen hedefler eski bağımlılıklarını görmeye devam edebilir.

Sonlandırıcıların native.existing_rules() kullanarak inceleyebileceği ancak görünürlük sistemi kapsamında bağımlılık olarak kullanamayacağı hedefler tanımlamak mümkündür. Örneğin, makro tanımlı bir hedef kendi paketine veya sonlandırıcı makronun tanımına görünmüyorsa ve sonlandırıcıya devredilmemişse sonlandırıcı bu tür bir hedefi göremez. Ancak native.existing_rules() tabanlı eski bir makronun da böyle bir hedefi göremeyeceğini unutmayın.

Yük görünürlüğü

Yükleme görünürlüğü, geçerli paketin dışındaki diğer BUILD veya .bzl dosyalarından bir .bzl dosyasının yüklenip yüklenemeyeceğini kontrol eder.

Hedef görünürlüğü, hedefler tarafından kapsanan kaynak kodu koruduğu gibi yük görünürlüğü de .bzl dosyaları tarafından kapsanan derleme mantığını korur. Örneğin, bir BUILD dosyasının yazarı, bazı tekrarlayan hedef bildirimlerini .bzl dosyasındaki bir makroya dahil etmek isteyebilir. Yük görünürlüğü koruması olmadan, makrolarının aynı çalışma alanındaki diğer katılımcılar tarafından yeniden kullanıldığını görebilirler. Bu durumda, makroda yapılan değişiklikler diğer ekiplerin derlemelerini bozabilir.

.bzl dosyasının karşılık gelen bir kaynak dosyası hedefi olabilir veya olmayabilir. Bu durumda, yük görünürlüğü ile hedef görünürlüğün aynı olacağı garanti edilmez. Yani, aynı BUILD dosyası .bzl dosyasını yükleyebilir ancak filegroup srcs bölümünde listelemeyebilir veya bunun tam tersi olabilir. Bu durum, bazen doküman oluşturma veya test etme gibi kaynak kodu olarak .bzl dosyalarını kullanmak isteyen kurallar için sorunlara neden olabilir.

Prototip oluşturma için --check_bzl_visibility=false ayarını yaparak yükleme görünürlüğü zorunluluğunu devre dışı bırakabilirsiniz. --check_visibility=false ile aynı şekilde, bu işlem gönderilen kod için yapılmamalıdır.

Yük görünürlüğü, Bazel 6.0'dan itibaren kullanılabilir.

Yük görünürlüğünü bildirme

.bzl dosyasının yükleme görünürlüğünü ayarlamak için dosyanın içinden visibility() işlevini çağırın. visibility() işlevinin bağımsız değişkeni, package_group öğesinin packages özelliği gibi bir paket spesifikasyonları listesidir. Ancak visibility(), negatif paket spesifikasyonlarını kabul etmez.

visibility() çağrısı dosya başına yalnızca bir kez, en üst düzeyde (bir işlevin içinde değil) ve ideal olarak load() ifadelerinden hemen sonra yapılmalıdır.

Hedef görünürlüğün aksine, varsayılan yükleme görünürlüğü her zaman herkese açıktır. visibility() işlevi çağrılmayan dosyalar, çalışma alanının herhangi bir yerinden her zaman yüklenebilir. Paket dışında kullanılmak üzere tasarlanmamış yeni .bzl dosyaların en üstüne visibility("private") eklemek iyi bir fikirdir.

Örnek

# //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

...

Yük görünürlüğü uygulamaları

Bu bölümde, yük görünürlüğü beyanlarını yönetmeyle ilgili ipuçları açıklanmaktadır.

Görünürlükleri faktörize etme

Birden fazla .bzl dosyanın aynı görünürlüğe sahip olması gerektiğinde, paket özelliklerini ortak bir listede birleştirmek faydalı olabilir. Örneğin:

# //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)

...

Bu, çeşitli .bzl dosyalarının görünürlükleri arasında yanlışlıkla eğrilik oluşmasını önlemeye yardımcı olur. Ayrıca, clients listesi büyük olduğunda daha kolay okunur.

Görünürlükleri oluşturma

Bazen bir .bzl dosyasının, birden fazla küçük izin verilenler listesinden oluşan bir izin verilenler listesi tarafından görünür olması gerekebilir. Bu, bir package_group öğesinin includes özelliği aracılığıyla diğer package_group öğelerini dahil etmesine benzer.

Yaygın olarak kullanılan bir makroyu kullanımdan kaldırdığınızı varsayalım. Yalnızca mevcut kullanıcılar ve kendi ekibinizin sahip olduğu paketler tarafından görülebilmesini istiyorsanız. Şuna benzer bir mesaj yazabilirsiniz:

# //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)

Paket gruplarıyla yinelenen öğeleri kaldırma

Hedef görünürlüğün aksine, yük görünürlüğünü bir package_group cinsinden tanımlayamazsınız. Hem hedef görünürlüğü hem de yük görünürlüğü için aynı izin verilenler listesini yeniden kullanmak istiyorsanız paket spesifikasyonları listesini, her iki tür bildirimin de başvurabileceği bir .bzl dosyasına taşımanız en iyisidir. Yukarıdaki Görünürlükleri hesaba katma bölümündeki örnekten yola çıkarak şunları yazabilirsiniz:

# //mylib/BUILD

load(":internal_defs", "clients")

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

Bu yalnızca listede negatif paket belirtimi yoksa çalışır.

Tek tek simgeleri koruma

Adı alt çizgiyle başlayan Starlark sembolleri başka bir dosyadan yüklenemez. Bu sayede özel semboller oluşturmak kolaylaşır ancak bu sembolleri sınırlı sayıda güvenilir dosyayla paylaşamazsınız. Diğer taraftan, yük görünürlüğü, diğer paketlerin .bzl file öğenizi görme şekli üzerinde kontrol sahibi olmanızı sağlar ancak alt çizgi içermeyen sembollerin yüklenmesini engellemenize izin vermez.

Neyse ki ayrıntılı kontrol için bu iki özelliği birleştirebilirsiniz.

# //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

bzl-visibility Buildifier lint'i

Kullanıcılar, dosyaları internal veya private adlı bir dizinden yüklediğinde ve dosyaları bu dizinin üst dizininin altında olmadığında uyarı veren bir Buildifier lint vardır. Bu lint, yükleme görünürlüğü özelliğinden önce oluşturulmuştur ve .bzl dosyalarının görünürlükleri bildirdiği çalışma alanlarında gereksizdir.