이 페이지에서는 Bazel의 외부 종속 항목에 관해 자주 묻는 질문에 대한 답변을 제공합니다.
MODULE.bazel
Bazel 모듈의 버전은 어떻게 지정해야 하나요?
소스 보관 파일 MODULE.bazel
에서 module
지시어를 사용하여 version
를 설정하면 신중하게 관리하지 않는 경우 여러 단점과 의도하지 않은 부작용이 발생할 수 있습니다.
중복: 모듈의 새 버전을 출시하려면 일반적으로
MODULE.bazel
의 버전을 증분하고 출시를 태그해야 합니다. 이 두 단계는 동기화되지 않을 수 있습니다. 자동화로 이 위험을 줄일 수 있지만, 위험을 완전히 피하는 것이 더 간단하고 안전합니다.불일치: 비등록기관 재정의를 사용하여 특정 커밋으로 모듈을 재정의하는 사용자에게 잘못된 버전이 표시됩니다. 예를 들어 소스 보관 파일의
MODULE.bazel
이version = "0.3.0"
을 설정했지만 해당 출시 이후 추가 커밋이 이루어진 경우 이러한 커밋 중 하나로 재정의하는 사용자에게는 여전히0.3.0
이 표시됩니다. 실제로 버전은 출시보다 앞서 있음을 반영해야 합니다(예:0.3.1-rc1
).비등록 재정의 문제: 자리표시자 값을 사용하면 사용자가 비등록 재정의로 모듈을 재정의할 때 문제가 발생할 수 있습니다. 예를 들어
0.0.0
는 가장 높은 버전으로 정렬되지 않습니다. 이는 일반적으로 사용자가 레지스트리 외 재정의를 실행할 때 원하는 동작입니다.
따라서 소스 보관 파일 MODULE.bazel
에 버전을 설정하지 않는 것이 좋습니다. 대신 레지스트리(예: Bazel 중앙 레지스트리)에 저장된 MODULE.bazel
에 설정합니다. 이는 Bazel의 외부 종속 항목 해결 중에 모듈 버전의 실제 소스입니다 (Bazel 레지스트리 참고).
이는 일반적으로 자동화됩니다. 예를 들어 rules-template
예시 규칙 저장소는 bazel-contrib/publish-to-bcr publish.yaml GitHub 작업을 사용하여 출시를 BCR에 게시합니다. 이 작업은 출시 버전으로 소스 보관 파일 MODULE.bazel
의 패치를 생성합니다. 이 패치는 레지스트리에 저장되며 Bazel의 외부 종속 항목 해결 중에 모듈이 가져올 때 적용됩니다.
이렇게 하면 레지스트리의 출시 버전이 출시된 버전으로 올바르게 설정되므로 bazel_dep
, single_version_override
, multiple_version_override
가 예상대로 작동하며, 소스 보관 파일의 버전이 기본값 (''
)이므로 레지스트리 외 재정의를 실행할 때 발생할 수 있는 문제를 방지할 수 있습니다. 기본값은 항상 올바르게 처리되고 (기본 버전 값이므로) 정렬 시 예상대로 작동합니다 (빈 문자열은 가장 높은 버전으로 처리됨).
호환성 수준은 언제 증가시켜야 하나요?
Bazel 모듈의 compatibility_level
은 하위 호환되지 않는 ('브레이킹') 변경사항을 도입하는 동일한 커밋에서 증가해야 합니다.
하지만 Bazel은 확인된 종속 항목 그래프에 호환성 수준이 다른 동일한 모듈 버전이 있는 것으로 감지되면 오류를 발생시킬 수 있습니다. 예를 들어 두 모듈이 호환성 수준이 다른 세 번째 모듈 버전에 종속된 경우에 발생할 수 있습니다.
따라서 compatibility_level
를 너무 자주 증가시키면 매우 방해가 될 수 있으므로 권장되지 않습니다. 이러한 상황을 방지하려면 호환성이 깨지는 변경사항이 대부분의 사용 사례에 영향을 미치고 마이그레이션하거나 해결하기 쉽지 않은 경우에만 compatibility_level
를 증가해야 합니다.
MODULE.bazel이 load
를 지원하지 않는 이유는 무엇인가요?
종속 항목 해결 중에 참조된 모든 외부 종속 항목의 MODULE.bazel 파일이 레지스트리에서 가져옵니다. 이 단계에서는 종속 항목의 소스 보관 파일이 아직 가져와지지 않았습니다. 따라서 MODULE.bazel 파일이 다른 파일을 load
하는 경우 Bazel이 전체 소스 보관 파일을 가져오지 않고는 해당 파일을 실제로 가져올 방법이 없습니다. MODULE.bazel 파일 자체는 레지스트리에서 직접 호스팅되므로 특별합니다.
MODULE.bazel에서 load
를 요청하는 사용자가 일반적으로 관심을 갖는 몇 가지 사용 사례가 있으며, 이러한 사용 사례는 load
없이 해결할 수 있습니다.
- MODULE.bazel에 나열된 버전이 다른 곳에 저장된 빌드 메타데이터(예: .bzl 파일)와 일치하는지 확인: 이는 BUILD 파일에서 로드된 .bzl 파일에서
native.module_version
메서드를 사용하여 달성할 수 있습니다. - 매우 큰 MODULE.bazel 파일을 관리 가능한 섹션으로 분할(특히 모노레포의 경우): 루트 모듈은
include
지시어를 사용하여 MODULE.bazel 파일을 여러 세그먼트로 분할할 수 있습니다. MODULE.bazel 파일에서load
를 허용하지 않는 것과 같은 이유로include
는 루트가 아닌 모듈에서 사용할 수 없습니다. - 이전 WORKSPACE 시스템 사용자는 저장소를 선언한 후 복잡한 로직을 실행하기 위해 해당 저장소에서 즉시
load
하는 것을 기억할 수 있습니다. 이 기능은 모듈 확장 프로그램으로 대체되었습니다.
bazel_dep
의 SemVer 범위를 지정할 수 있나요?
아니요. npm 및 Cargo와 같은 다른 패키지 관리자는 버전 범위를 지원하며(암시적 또는 명시적) 이는 종종 제약 조건 솔버를 필요로 하여 사용자가 출력을 예측하기 어려워지고 lockfile이 없으면 버전 확인을 재현할 수 없게 됩니다.
대신 Bazel은 Go와 같은 최소 버전 선택을 사용하므로 출력을 쉽게 예측할 수 있고 재현성을 보장합니다. 이는 Bazel의 설계 목표와 일치하는 절충안입니다.
또한 Bazel 모듈 버전은 SemVer의 상위 집합이므로 엄격한 SemVer 환경에서 적합한 것이 항상 Bazel 모듈 버전으로 이어지지는 않습니다.
bazel_dep
의 최신 버전을 자동으로 받을 수 있나요?
일부 사용자는 종속 항목의 최신 버전을 자동으로 가져오기 위해 bazel_dep(name = "foo",
version = "latest")
를 지정하는 기능을 요청하기도 합니다. 이는 SemVer 범위에 관한 질문과 유사하며 대답은 아니요입니다.
여기에서 권장되는 해결 방법은 자동화로 이를 처리하는 것입니다. 예를 들어 Renovate는 Bazel 모듈을 지원합니다.
이 질문을 하는 사용자는 로컬 개발 중에 빠르게 반복하는 방법을 찾고 있는 경우가 있습니다. 이는 local_path_override
를 사용하여 달성할 수 있습니다.
use_repo
가 표시되는 이유는 무엇인가요?
MODULE.bazel 파일의 모듈 확장 프로그램 사용에는 큰 use_repo
지시문이 포함되는 경우가 있습니다. 예를 들어 gazelle
에서 go_deps
확장 프로그램을 일반적으로 사용하는 방법은 다음과 같습니다.
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(
go_deps,
"com_github_gogo_protobuf",
"com_github_golang_mock",
"com_github_golang_protobuf",
"org_golang_x_net",
... # potentially dozens of lines...
)
정보가 참조된 go.mod
파일에 이미 있으므로 긴 use_repo
지시어가 중복되는 것처럼 보일 수 있습니다.
Bazel에 이 use_repo
지시어가 필요한 이유는 모듈 확장 프로그램을 지연 로딩하기 때문입니다. 즉, 모듈 확장 프로그램은 결과가 관찰된 경우에만 실행됩니다. 모듈 확장 프로그램의 '출력'은 저장소 정의이므로 이는 정의된 저장소가 요청된 경우에만 모듈 확장 프로그램을 실행한다는 의미입니다 (예: 위의 예에서 타겟 @org_golang_x_net//:foo
가 빌드된 경우). 하지만 모듈 확장 프로그램이 정의하는 저장소는 실행하기 전에는 알 수 없습니다. 이때 use_repo
지시어가 사용됩니다. 사용자는 확장 프로그램이 생성할 리포지토리를 Bazel에 알릴 수 있으며, Bazel은 이러한 특정 리포지토리가 사용될 때만 확장 프로그램을 실행합니다.
이 use_repo
지시어를 유지관리할 수 있도록 모듈 확장 프로그램은 구현 함수에서 extension_metadata
객체를 반환할 수 있습니다. 사용자는 bazel mod tidy
명령어를 실행하여 이러한 모듈 확장 프로그램의 use_repo
지시어를 업데이트할 수 있습니다.
Bzlmod 이전
MODULE.bazel과 WORKSPACE 중 어느 것이 먼저 평가되나요?
--enable_bzlmod
와 --enable_workspace
가 모두 설정된 경우 어떤 시스템이 먼저 참조되는지 궁금할 수 있습니다. 간단히 말해 MODULE.bazel(Bzlmod)이 먼저 평가됩니다.
긴 대답은 '어떤 것이 먼저 평가되는가'가 올바른 질문이 아니라는 것입니다. 오히려 올바른 질문은 표준 이름 @@foo
이 있는 저장소의 컨텍스트에서 명백한 저장소 이름 @bar
이 무엇으로 확인되는가입니다. 또는 @@base
의 저장소 매핑은 무엇인가요?
명백한 저장소 이름 (선행 @
하나)이 있는 라벨은 해결되는 컨텍스트에 따라 다른 것을 참조할 수 있습니다. @bar//:baz
라벨을 보고 실제로 무엇을 가리키는지 궁금한 경우 먼저 컨텍스트 저장소를 알아야 합니다. 예를 들어 라벨이 @@foo
저장소에 있는 BUILD 파일에 있는 경우 컨텍스트 저장소는 @@foo
입니다.
그런 다음 컨텍스트 저장소에 따라 마이그레이션 가이드의 '저장소 공개 상태' 표를 사용하여 표시된 이름이 실제로 확인되는 저장소를 확인할 수 있습니다.
- 컨텍스트 저장소가 기본 저장소 (
@@
)인 경우:bar
이 루트 모듈의 MODULE.bazel 파일 (bazel_dep
,use_repo
,module
,use_repo_rule
중 하나를 통해)에 의해 도입된 명백한 저장소 이름인 경우@bar
은 해당 MODULE.bazel 파일에서 주장하는 내용으로 확인됩니다.- 그렇지 않고
bar
이 WORKSPACE에 정의된 저장소 (즉, 정식 이름이@@bar
)인 경우@bar
은@@bar
로 확인됩니다. - 그렇지 않으면
@bar
가@@[unknown repo 'bar' requested from @@]
와 같은 값으로 확인되며, 이는 결국 오류를 발생시킵니다.
- 컨텍스트 저장소가 Bzlmod 세계 저장소인 경우 (즉, 루트가 아닌 Bazel 모듈에 해당하거나 모듈 확장 프로그램에 의해 생성됨) 다른 Bzlmod 세계 저장소만 표시되고 WORKSPACE 세계 저장소는 표시되지 않습니다.
- 특히 여기에는 루트 모듈의
non_module_deps
유사 모듈 확장 프로그램에 도입된 리포지토리나 루트 모듈의use_repo_rule
인스턴스화가 포함됩니다.
- 특히 여기에는 루트 모듈의
- 컨텍스트 저장소가 WORKSPACE에 정의된 경우:
- 먼저 컨텍스트 저장소 정의에 마법 같은
repo_mapping
속성이 있는지 확인합니다. 그렇다면 먼저 매핑을 진행합니다 (repo_mapping = {"@bar": "@baz"}
로 정의된 저장소의 경우 아래의@baz
을 확인). bar
이 루트 모듈의 MODULE.bazel 파일에 의해 도입된 명백한 저장소 이름인 경우@bar
은 해당 MODULE.bazel 파일에서 주장하는 내용으로 확인됩니다. (이는 기본 저장소 케이스의 항목 1과 동일합니다.)- 그렇지 않으면
@bar
이@@bar
로 확인됩니다. 이는 WORKSPACE에 정의된 저장소bar
를 가리킬 가능성이 가장 큽니다. 이러한 저장소가 정의되지 않은 경우 Bazel에서 오류가 발생합니다.
- 먼저 컨텍스트 저장소 정의에 마법 같은
더 간결한 버전의 경우:
- bzlmod-world 저장소 (기본 저장소 제외)에는 bzlmod-world 저장소만 표시됩니다.
- WORKSPACE-world 저장소 (기본 저장소 포함)는 먼저 Bzlmod 세계의 루트 모듈이 정의한 내용을 확인한 다음 WORKSPACE-world 저장소를 확인합니다.
Bazel 명령줄의 라벨 (Starlark 플래그, 라벨 유형 플래그 값, 빌드/테스트 타겟 패턴 포함)은 기본 저장소를 컨텍스트 저장소로 사용하는 것으로 처리됩니다.
기타
오프라인 빌드를 준비하고 실행하려면 어떻게 해야 하나요?
bazel fetch
명령어를 사용하여 저장소를 미리 가져옵니다. --repo
플래그(예: bazel fetch --repo @foo
)를 사용하여 저장소 @foo
만 가져오거나 (기본 저장소 컨텍스트에서 해결됨, 위의 질문 참고) 타겟 패턴 (예: bazel fetch @foo//:bar
)을 사용하여 @foo//:bar
의 모든 전이 종속 항목을 가져올 수 있습니다 (bazel build --nobuild @foo//:bar
와 동일).
빌드 중에 가져오기가 발생하지 않도록 하려면 --nofetch
를 사용하세요. 더 정확히 말하면 이렇게 하면 로컬이 아닌 저장소 규칙을 실행하려는 시도가 실패합니다.
리포를 가져오고 로컬에서 테스트하도록 수정하려면 bazel vendor
명령어를 사용하는 것이 좋습니다.
HTTP 프록시는 어떻게 사용하나요?
Bazel은 curl과 같은 다른 프로그램에서 일반적으로 허용되는 http_proxy
및 HTTPS_PROXY
환경 변수를 따릅니다.
이중 스택 IPv4/IPv6 설정에서 Bazel이 IPv6를 선호하도록 하려면 어떻게 해야 하나요?
IPv6 전용 머신에서 Bazel은 변경 없이 종속 항목을 다운로드할 수 있습니다. 하지만 이중 스택 IPv4/IPv6 머신에서 Bazel은 Java와 동일한 규칙을 따르며, 사용 설정된 경우 IPv4를 선호합니다. 예를 들어 IPv4 네트워크가 외부 주소를 확인할 수 없거나 외부 주소에 연결할 수 없는 경우 Network
unreachable
예외와 빌드 실패가 발생할 수 있습니다. 이 경우 java.net.preferIPv6Addresses=true
시스템 속성을 사용하여 Bazel의 동작을 재정의하여 IPv6를 선호할 수 있습니다.
구체적인 내용은 다음과 같습니다.
--host_jvm_args=-Djava.net.preferIPv6Addresses=true
시작 옵션을 사용합니다(예:.bazelrc
파일에 다음 줄을 추가).startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true
인터넷에 연결해야 하는 Java 빌드 타겟 (예: 통합 테스트)을 실행할 때는
--jvmopt=-Djava.net.preferIPv6Addresses=true
도구 플래그를 사용하세요. 예를 들어.bazelrc
파일에 다음을 포함합니다.build --jvmopt=-Djava.net.preferIPv6Addresses
종속 항목 버전 확인에
rules_jvm_external
를 사용하는 경우COURSIER_OPTS
환경 변수에-Djava.net.preferIPv6Addresses=true
도 추가하여 Coursier의 JVM 옵션을 제공하세요.
원격 실행을 사용하여 저장소 규칙을 원격으로 실행할 수 있나요?
아니요. 아직은 아닙니다. 빌드 속도를 높이기 위해 원격 실행 서비스를 사용하는 사용자는 저장소 규칙이 여전히 로컬에서 실행된다는 것을 알 수 있습니다. 예를 들어 http_archive
는 먼저 로컬 머신에 다운로드되고 (해당하는 경우 로컬 다운로드 캐시 사용) 추출된 다음 각 소스 파일이 원격 실행 서비스에 입력 파일로 업로드됩니다. 원격 실행 서비스가 해당 보관 파일을 다운로드하고 추출하여 쓸모없는 왕복을 방지하지 않는 이유를 묻는 것은 당연합니다.
그 이유 중 하나는 저장소 규칙 (및 모듈 확장 프로그램)이 Bazel 자체에서 실행되는 '스크립트'와 유사하기 때문입니다. 원격 실행기에는 Bazel이 설치되어 있지 않을 수도 있습니다.
또 다른 이유는 Bazel이 다운로드되고 추출된 보관 파일의 BUILD 파일을 사용하여 로드 및 분석을 실행해야 하는 경우가 많기 때문입니다. 로드 및 분석은 로컬에서 실행됩니다.
리포 규칙을 빌드 규칙으로 다시 구상하여 이 문제를 해결하는 예비 아이디어가 있습니다. 이렇게 하면 자연스럽게 원격으로 실행할 수 있지만 반대로 새로운 아키텍처 문제가 발생합니다 (예: query
명령어가 작업을 실행해야 할 수 있으므로 설계가 복잡해짐).
이 주제에 관한 이전 논의는 가져오기 위해 Bazel이 필요한 저장소를 지원하는 방법을 참고하세요.