이 페이지에서는 Bazel의 두 가지 공개 상태 시스템인 타겟 공개 상태 및 로드 공개 상태를 설명합니다.
두 유형의 공개 범위 모두 다른 개발자가 라이브러리의 공개 API와 구현 세부정보를 구분하고 워크스페이스가 커짐에 따라 구조를 적용하는 데 도움이 됩니다. 공개 API를 지원 중단할 때 공개 범위를 사용하여 기존 사용자는 허용하고 신규 사용자는 거부할 수도 있습니다.
타겟 공개 상태
타겟 공개 상태는 타겟에 종속될 수 있는 사용자(즉, deps
와 같은 속성 내에서 타겟 라벨을 사용할 수 있는 사용자)를 제어합니다. 종속 항목 중 하나의 공개 상태를 위반하는 경우 분석 단계에서 타겟이 빌드되지 않습니다.
일반적으로 타겟 A
는 동일한 위치에 있거나 A
가 B
의 위치에 대한 공개 범위를 부여하는 경우 타겟 B
에 표시됩니다. 기호 매크로가 없는 경우 '위치'라는 용어를 '패키지'로 단순화할 수 있습니다. 기호 매크로에 관한 자세한 내용은 아래를 참고하세요.
공개 상태는 허용된 패키지를 나열하여 지정됩니다. 패키지를 허용한다고 해서 하위 패키지도 허용되는 것은 아닙니다. 패키지 및 하위 패키지에 관한 자세한 내용은 개념 및 용어를 참고하세요.
프로토타입 제작의 경우 --check_visibility=false
플래그를 설정하여 타겟 공개 상태 적용을 사용 중지할 수 있습니다. 제출된 코드의 프로덕션 사용을 위해 이렇게 해서는 안 됩니다.
공개 상태를 제어하는 기본 방법은 규칙의 visibility
속성을 사용하는 것입니다.
다음 하위 섹션에서는 속성의 형식, 다양한 유형의 타겟에 속성을 적용하는 방법, 공개 상태 시스템과 기호 매크로 간의 상호작용을 설명합니다.
공개 상태 사양
모든 규칙 타겟에는 라벨 목록을 사용하는 visibility
속성이 있습니다. 각 라벨은 다음 형식 중 하나를 취합니다. 마지막 형식을 제외하고 이는 실제 타겟과 일치하지 않는 문법적 자리표시자일 뿐입니다.
"//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"
입니다.
- 패키지 그룹은 패키지를 지정할 때 다른 문법을 사용합니다. 패키지 그룹 내에서
예를 들어 //some/package:mytarget
의 visibility
가 [":__subpackages__", "//tests:__pkg__"]
로 설정된 경우 //some/package/...
소스 트리의 일부인 모든 대상과 //tests/BUILD
에 선언된 대상에서 사용할 수 있지만 //tests/integration/BUILD
에 정의된 대상에서는 사용할 수 없습니다.
권장사항: 여러 대상을 동일한 패키지 세트에 표시하려면 각 대상의 visibility
속성에서 목록을 반복하는 대신 package_group
를 사용하세요. 이렇게 하면 가독성이 향상되고 목록이 동기화되지 않도록 방지할 수 있습니다.
권장사항: 다른 팀의 프로젝트에 공개 상태를 부여할 때는 프로젝트가 발전하고 새 하위 패키지를 추가할 때 불필요한 공개 상태 변경을 방지하기 위해 __pkg__
보다 __subpackages__
를 사용하는 것이 좋습니다.
규칙 타겟 공개 상태
규칙 타겟의 공개 상태는 visibility
속성(지정되지 않은 경우 적절한 기본값)을 가져와 타겟이 선언된 위치를 추가하여 결정됩니다. 기호 매크로에 선언되지 않은 타겟의 경우 패키지가 default_visibility
를 지정하면 이 기본값이 사용됩니다. 다른 모든 패키지와 기호 매크로에 선언된 타겟의 경우 기본값은 ["//visibility:private"]
입니다.
# //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/..."],
)
권장사항: default_visibility
를 공개로 설정하지 마세요. 프로토타입 제작이나 소규모 코드베이스에서는 편리할 수 있지만 코드베이스가 커짐에 따라 실수로 공개 타겟을 만들 위험이 커집니다. 패키지의 공개 인터페이스에 속하는 타겟을 명시하는 것이 좋습니다.
생성된 파일 타겟 공개 상태
생성된 파일 타겟은 이를 생성하는 규칙 타겟과 동일한 공개 상태를 갖습니다.
# //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",
]
...
)
소스 파일 타겟 공개 상태
소스 파일 타겟은 exports_files
를 사용하여 명시적으로 선언하거나 규칙의 라벨 속성 (기호 매크로 외부)에서 파일 이름을 참조하여 암시적으로 만들 수 있습니다. 규칙 타겟과 마찬가지로 exports_files
호출 위치 또는 입력 파일을 참조한 BUILD 파일이 항상 파일의 공개 상태에 자동으로 추가됩니다.
exports_files
로 선언된 파일의 공개 상태는 해당 함수의 visibility
매개변수에 의해 설정될 수 있습니다. 이 매개변수를 지정하지 않으면 공개 상태가 됩니다.
exports_files
호출에 표시되지 않는 파일의 경우 공개 상태는 플래그 --incompatible_no_implicit_file_export
의 값에 따라 다릅니다.
플래그가 true이면 공개 상태가 비공개입니다.
그 외의 경우에는 기존 동작이 적용됩니다. 공개 상태는
BUILD
파일의default_visibility
와 동일하거나 기본 공개 상태가 지정되지 않은 경우에는 비공개입니다.
기존 동작에 의존하지 마세요. 소스 파일 타겟에 비공개가 아닌 공개 상태가 필요한 경우 항상 exports_files
선언을 작성합니다.
권장사항: 가능하면 소스 파일 대신 규칙 타겟을 노출하는 것이 좋습니다. 예를 들어 .java
파일에서 exports_files
를 호출하는 대신 비공개가 아닌 java_library
타겟으로 파일을 래핑합니다. 일반적으로 규칙 타겟은 동일한 패키지에 있는 소스 파일만 직접 참조해야 합니다.
예
//frobber/data/BUILD
파일:
exports_files(["readme.txt"])
//frobber/bin/BUILD
파일:
cc_binary(
name = "my-program",
data = ["//frobber/data:readme.txt"],
)
구성 설정 공개 상태
이전에는 Bazel에서 select()
의 키에 참조된 config_setting
타겟의 공개 상태를 적용하지 않았습니다. 이 기존 동작을 삭제하는 플래그는 두 가지가 있습니다.
--incompatible_enforce_config_setting_visibility
: 이러한 타겟의 공개 상태를 확인할 수 있습니다. 또한 이전을 돕기 위해visibility
를 지정하지 않은 모든config_setting
가 패키지 수준default_visibility
와 관계없이 공개로 간주됩니다.--incompatible_config_setting_private_default_visibility
:visibility
를 지정하지 않은config_setting
가 다른 모든 규칙 타겟과 마찬가지로 패키지의default_visibility
를 따르고 비공개 공개 상태로 대체합니다.--incompatible_enforce_config_setting_visibility
가 설정되지 않은 경우에는 아무 일도 하지 않습니다.
기존 동작에 의존하지 마세요. 패키지가 아직 적절한 default_visibility
를 지정하지 않은 경우 현재 패키지 외부에서 사용하려는 모든 config_setting
에는 명시적 visibility
가 있어야 합니다.
패키지 그룹 타겟 공개 상태
package_group
타겟에는 visibility
속성이 없습니다. 항상 공개적으로 표시됩니다.
암시적 종속 항목의 공개 상태
일부 규칙에는 암시적 종속 항목이 있습니다. BUILD
파일에 명시되지는 않지만 해당 규칙의 모든 인스턴스에 내재된 종속 항목입니다. 예를 들어 cc_library
규칙은 각 규칙 타겟에서 C++ 컴파일러를 나타내는 실행 파일 타겟으로의 암시적 종속 항목을 만들 수 있습니다.
이러한 암시적 종속 항목의 공개 상태는 규칙 (또는 측면)이 정의된 .bzl
파일을 포함하는 패키지에 관해 확인됩니다. 이 예에서 C++ 컴파일러는 cc_library
규칙의 정의와 동일한 패키지에 있는 한 비공개일 수 있습니다. 대체로 암시적 종속 항목이 정의에서 표시되지 않으면 cc_library
타겟과 관련하여 확인됩니다.
규칙의 사용을 특정 패키지로 제한하려면 로드 공개 상태를 대신 사용하세요.
공개 상태 및 기호 매크로
이 섹션에서는 공개 상태 시스템이 기호 매크로와 상호작용하는 방식을 설명합니다.
기호 매크로 내 위치
공개 상태 시스템의 핵심 세부정보는 선언의 위치를 결정하는 방법입니다. 기호 매크로에 선언되지 않은 타겟의 경우 위치는 타겟이 있는 패키지, 즉 BUILD
파일의 패키지입니다.
그러나 기호 매크로에서 생성된 타겟의 경우 위치는 매크로 정의 (my_macro = macro(...)
문)가 표시되는 .bzl
파일이 포함된 패키지입니다. 중첩된 여러 타겟 내에서 타겟이 생성되면 항상 가장 안쪽에 있는 기호 매크로의 정의가 사용됩니다.
동일한 시스템은 지정된 종속 항목의 공개 상태를 확인할 위치를 결정하는 데도 사용됩니다. 소비 타겟이 매크로 내에서 생성된 경우 소비 타겟이 있는 패키지가 아닌 가장 안쪽의 매크로 정의를 확인합니다.
즉, 코드가 동일한 패키지에 정의된 모든 매크로는 서로 자동으로 '친구'가 됩니다. //lib:defs.bzl
에 정의된 매크로로 직접 생성된 모든 타겟은 매크로가 실제로 인스턴스화된 패키지에 관계없이 //lib
에 정의된 다른 매크로에서 볼 수 있습니다. 마찬가지로 //lib/BUILD
및 기존 매크로에서 직접 선언된 타겟을 볼 수 있고 타겟에서 볼 수 있습니다. 반대로 동일한 패키지에 있는 타겟 중 하나 이상이 기호 매크로로 생성된 경우 반드시 서로를 볼 수 있는 것은 아닙니다.
기호 매크로의 구현 함수 내에서 visibility
매개변수는 매크로가 호출된 위치를 추가한 후 매크로의 visibility
속성의 유효 값을 갖습니다. 매크로가 호출자에게 타겟 중 하나를 내보내는 표준 방법은 some_rule(..., visibility = visibility)
와 같이 이 값을 타겟 선언으로 전달하는 것입니다. 이 속성을 생략하는 타겟은 호출자가 매크로 정의와 동일한 패키지에 있지 않는 한 매크로 호출자에게 표시되지 않습니다. 이 동작은 하위 매크로에 대한 중첩 호출 체인이 각각 visibility = visibility
를 전달할 수 있다는 의미에서 컴포지션되며, 매크로의 구현 세부정보를 노출하지 않고 각 수준의 호출자에게 내부 매크로의 내보낸 타겟을 다시 내보냅니다.
하위 매크로에 권한 위임
공개 상태 모델에는 매크로가 권한을 하위 매크로에 위임할 수 있는 특수 기능이 있습니다. 이는 매크로 분해 및 구성에 중요합니다.
다른 패키지의 규칙 some_library
를 사용하여 종속 항목 가장자리를 만드는 매크로 my_macro
가 있다고 가정해 보겠습니다.
# //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
타겟에는 지정된 visibility
가 없으므로 //macro
내에만 표시되며 이는 소비 타겟에 적합합니다. 이제 //lib
의 작성자가 some_library
를 리팩터링하여 매크로를 사용하여 구현하도록 하면 어떻게 되나요?
# //lib:defs.bzl
def _impl(name, visibility, deps, ...):
some_rule(
# Main target, exported.
name = name,
visibility = visibility,
deps = deps,
...)
some_library = macro(implementation = _impl, ...)
이 변경으로 인해 //pkg:foo_consumer
의 위치가 //macro
이 아닌 //lib
이므로 //pkg:foo_dependency
의 사용이 종속 항목의 공개 상태를 위반합니다. my_macro
의 작성자는 이 구현 세부정보를 해결하기 위해 visibility = ["//lib"]
를 종속 항목 선언에 전달할 수 없습니다.
따라서 타겟의 종속 항목이 타겟을 선언한 매크로의 속성 값이기도 한 경우 사용하는 타겟의 위치 대신 매크로의 위치를 기준으로 종속 항목의 공개 상태를 확인합니다.
이 예에서 //pkg:foo_consumer
가 //pkg:foo_dependency
를 볼 수 있는지 확인하기 위해 //pkg:foo_dependency
가 my_macro
내부의 some_library
호출에 입력으로 전달되었음을 확인하고 대신 이 호출의 위치인 //macro
를 기준으로 종속 항목의 공개 상태를 확인합니다.
대상 또는 매크로 선언이 라벨 유형 속성 중 하나에서 종속 항목의 라벨을 사용하는 다른 기호 매크로 내에 있는 한 이 프로세스는 재귀적으로 반복될 수 있습니다.
로드 공개 상태
로드 공개 상태는 .bzl
파일이 현재 패키지 외부의 다른 BUILD
또는 .bzl
파일에서 로드될 수 있는지 여부를 제어합니다.
타겟 공개 상태가 타겟에 의해 캡슐화된 소스 코드를 보호하는 것과 마찬가지로, 로드 공개 상태는 .bzl
파일에 의해 캡슐화된 빌드 로직을 보호합니다. 예를 들어 BUILD
파일 작성자는 반복되는 일부 타겟 선언을 .bzl
파일의 매크로로 분해할 수 있습니다. 로드 공개 상태 보호가 없으면 동일한 워크스페이스의 다른 공동작업자가 매크로를 재사용하는 것을 발견할 수 있으므로 매크로를 수정하면 다른 팀의 빌드가 중단됩니다.
.bzl
파일에는 상응하는 소스 파일 타겟이 있을 수도 있고 없을 수도 있습니다.
로드 가시성과 대상 가시성이 일치하지 않을 수 있습니다. 즉, 동일한 BUILD
파일은 .bzl
파일을 로드할 수 있지만 filegroup
의 srcs
에 나열하지 못할 수도 있고 그 반대의 경우도 가능합니다. 이로 인해 문서 생성 또는 테스트와 같이 .bzl
파일을 소스 코드로 사용하려는 규칙에 문제가 발생할 수 있습니다.
프로토타입 제작의 경우 --check_bzl_visibility=false
를 설정하여 로드 공개 상태 적용을 사용 중지할 수 있습니다. --check_visibility=false
와 마찬가지로 제출된 코드에는 이 작업을 수행해서는 안 됩니다.
로드 공개 상태는 Bazel 6.0부터 사용할 수 있습니다.
로드 공개 상태 선언
.bzl
파일의 로드 공개 상태를 설정하려면 파일 내에서 visibility()
함수를 호출합니다.
visibility()
의 인수는 package_group
의 packages
속성과 마찬가지로 패키지 사양 목록입니다. 그러나 visibility()
는 음수 패키지 사양을 허용하지 않습니다.
visibility()
호출은 파일당 한 번만, 최상위 수준 (함수 내부가 아님)에서, load()
문이 바로 뒤에 오는 것이 가장 좋습니다.
대상 공개 상태와 달리 기본 로드 공개 상태는 항상 공개입니다. visibility()
를 호출하지 않는 파일은 항상 워크스페이스의 어느 위치에서나 로드할 수 있습니다. 패키지 외부에서 사용하도록 특별히 의도되지 않은 새 .bzl
파일의 상단에 visibility("private")
를 추가하는 것이 좋습니다.
예
# //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
가 includes
속성을 통해 다른 package_group
를 통합하는 방식과 유사합니다.
널리 사용되는 매크로를 지원 중단한다고 가정해 보겠습니다. 기존 사용자와 자체 팀에서 소유한 패키지에만 표시되도록 하려면 다음과 같이 작성할 수 있습니다.
# //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
bzl-visibility Buildifier 린트
사용자가 internal
또는 private
라는 디렉터리에서 파일을 로드하는 경우, 사용자의 파일이 해당 디렉터리의 상위 디렉터리 아래에 있지 않으면 경고를 제공하는 Buildifier 린트가 있습니다. 이 린트는 가시성 로드 기능보다 먼저 도입되었으며 .bzl
파일이 가시성을 선언하는 워크스페이스에서는 필요하지 않습니다.