Bazel 모듈은 여러 버전이 있을 수 있는 Bazel 프로젝트로, 각 버전은 종속되는 다른 모듈에 관한 메타데이터를 게시합니다. 이는 Maven 아티팩트, npm 패키지, Go 모듈 또는 Cargo 크레이트와 같은 다른 종속 항목 관리 시스템의 익숙한 개념과 유사합니다.
모듈의 저장소 루트에 MODULE.bazel
파일이 있어야 합니다. 이 파일은 모듈의 매니페스트로, 모듈의 이름, 버전, 직접 종속 항목 목록, 기타 정보를 선언합니다. 기본 예를 들면 다음과 같습니다.
module(name = "my-module", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")
MODULE.bazel
파일에서 사용할 수 있는 지시어의 전체 목록을 참조하세요.
모듈 확인을 실행하기 위해 Bazel은 루트 모듈의 MODULE.bazel
파일을 읽는 것으로 시작한 후 전체 종속 항목 그래프를 검색할 때까지 Bazel 레지스트리에 종속 항목의 MODULE.bazel
파일을 반복적으로 요청합니다.
그러면 기본적으로 Bazel은 사용할 각 모듈의 한 버전을 선택합니다. Bazel은 각 모듈을 저장소로 나타내고 레지스트리를 다시 참조하여 각 저장소를 정의하는 방법을 알아봅니다.
버전 형식
Bazel에는 다양한 생태계가 있으며 프로젝트는 다양한 버전 관리 스키마를 사용합니다. 가장 인기 있는 것은 SemVer이지만 Abseil과 같이 다른 스키마를 사용하는 유명한 프로젝트도 있습니다. 이 프로젝트의 버전은 날짜 기반(예: 20210324.2
)입니다.
따라서 Bzlmod는 보다 완화된 버전의 SemVer 사양을 채택합니다. 차이점은 다음과 같습니다.
- SemVer에 따르면 버전의 '출시' 부분은
MAJOR.MINOR.PATCH
세그먼트로 구성되어야 합니다. Bazel에서는 여러 세그먼트가 허용되도록 이 요구사항이 완화됩니다. - SemVer에서 'release' 부분의 각 세그먼트는 숫자여야 합니다. Bazel에서는 문자도 허용하도록 완화되었으며 비교 시맨틱은 '사전 출시' 부분의 '식별자'와 일치합니다.
- 또한 주 버전, 부 버전, 패치 버전 증가의 시맨틱스가 적용되지 않습니다. 그러나 이전 버전과의 호환성을 표시하는 방법에 관한 자세한 내용은 호환성 수준을 참고하세요.
유효한 SemVer 버전은 모두 유효한 Bazel 모듈 버전입니다. 또한 두 개의 SemVer 버전 a
및 b
는 Bazel 모듈 버전과 비교될 때 동일한 유지 권한이 있는 경우에만 a < b
를 비교합니다.
버전 선택
버전이 지정된 종속 항목 관리 공간의 핵심인 다이아몬드 종속 항목 문제를 생각해 보세요. 다음과 같은 종속 항목 그래프가 있다고 가정해 보겠습니다.
A 1.0
/ \
B 1.0 C 1.1
| |
D 1.0 D 1.1
어떤 버전의 D
를 사용해야 하나요? 이 문제를 해결하기 위해 Bzlmod는 Go 모듈 시스템에 도입된 최소 버전 선택(MVS) 알고리즘을 사용합니다. MVS는 모듈의 모든 새 버전이 하위 호환된다고 가정하고 종속 항목에서 지정한 최신 버전(이 예에서는 D 1.1
)을 선택합니다. D 1.1
가 요구사항을 충족할 수 있는 가장 오래된 버전이므로 '최소'라고 합니다. D 1.2
이상 버전이 있더라도 선택하지 않습니다. MVS를 사용하면 고화질이고 재현 가능한 버전 선택 프로세스가 생성됩니다.
취소된 버전
레지스트리는 특정 버전을 피해야 하는 경우(예: 보안 취약점) 양크됨으로 선언할 수 있습니다. Bazel에서 취소된 모듈 버전을 선택할 때 오류가 발생합니다. 이 오류를 해결하려면 최신 버전이 아닌 최신 버전으로 업그레이드하거나 --allow_yanked_versions
플래그를 사용하여 약화된 버전을 명시적으로 허용하세요.
호환성 수준
Go에서는 이전 버전과 호환되지 않는 버전의 모듈을 별도의 모듈로 취급하므로 MVS의 이전 버전과의 호환성 가정이 효과가 있습니다. 즉, SemVer의 관점에서 A 1.x
와 A 2.x
는 별개의 모듈로 간주되며 해결된 종속 항목 그래프에 공존할 수 있습니다. 이는 Go의 패키지 경로에서 주 버전을 인코딩하여 가능해지므로 컴파일 시간 또는 연결 시간 충돌이 없습니다.
그러나 Bazel은 이러한 보장을 제공할 수 없으므로 이전 버전과 호환되지 않는 버전을 감지하려면 '주 버전' 번호가 필요합니다. 이 숫자를 호환성 수준이라고 하며 module()
지시문에 각 모듈 버전별로 지정됩니다. 이 정보를 통해 Bazel은 확인된 종속 항목 그래프에 호환성 수준이 다른 동일한 모듈의 버전이 존재함을 감지할 때 오류가 발생할 수 있습니다.
재정의
MODULE.bazel
파일에서 재정의를 지정하여 Bazel 모듈 확인의 동작을 변경합니다. 루트 모듈의 재정의만 적용됩니다. 모듈이 종속 항목으로 사용되는 경우 재정의는 무시됩니다.
각 재정의는 특정 모듈 이름에 대해 지정되며 종속 항목 그래프의 모든 버전에 영향을 미칩니다. 루트 모듈의 재정의만 적용되지만 루트 모듈이 직접 종속되지 않는 전이 종속 항목이 적용될 수 있습니다.
단일 버전 재정의
single_version_override
는 다음과 같은 여러 용도로 사용됩니다.
version
속성을 사용하면 종속 항목 그래프에서 요청된 종속 항목 버전과 관계없이 종속 항목을 특정 버전에 고정할 수 있습니다.registry
속성을 사용하면 일반적인 레지스트리 선택 프로세스를 따르는 대신 특정 레지스트리에서 이 종속 항목을 가져오도록 강제할 수 있습니다.patch*
속성을 사용하여 다운로드된 모듈에 적용할 패치 집합을 지정할 수 있습니다.
이러한 속성은 모두 선택사항이며 서로 조합할 수 있습니다.
다중 버전 재정의
동일한 모듈의 여러 버전이 확인된 종속 항목 그래프에 공존하도록 multiple_version_override
를 지정할 수 있습니다.
모듈에 허용되는 버전의 명시적 목록을 지정할 수 있으며, 이 목록은 모두 확인 전에 종속 항목 그래프에 있어야 합니다. 허용되는 각 버전에 따라 일부 전이 종속 항목이 있어야 합니다. 해결 후에는 허용되는 버전의 모듈만 남게 되고 Bazel은 모듈의 다른 버전을 동일한 호환성 수준에서 허용되는 가장 높은 버전으로 업그레이드합니다. 동일한 호환성 수준에서 허용되는 상위 버전이 없으면 Bazel에서 오류가 발생합니다.
예를 들어 확인 전에 종속 항목 그래프에 버전 1.1
, 1.3
, 1.5
, 1.7
, 2.0
가 존재하고 주 버전이 호환성 수준인 경우:
1.3
,1.7
,2.0
를 허용하는 여러 버전 재정의를 사용하면1.1
이1.3
로 업그레이드되고1.5
이1.7
로 업그레이드되며 다른 버전은 동일하게 유지됩니다.1.5
및2.0
를 허용하는 다중 버전 재정의는 오류가 발생합니다.1.7
에는 업그레이드할 수 있는 동일한 호환성 수준의 더 높은 버전이 없기 때문입니다.1.9
및2.0
를 허용하는 다중 버전 재정의는 오류를 발생시킵니다. 확인 전에1.9
가 종속 항목 그래프에 없기 때문입니다.
또한 단일 버전 재정의와 마찬가지로 registry
속성을 사용하여 레지스트리를 재정의할 수도 있습니다.
레지스트리 외 재정의
비 레지스트리 재정의는 버전 확인에서 모듈을 완전히 삭제합니다. Bazel은 이러한 MODULE.bazel
파일을 레지스트리에서 요청하지 않고 대신 저장소 자체에서 요청합니다.
Bazel은 다음과 같은 비레지스트리 재정의를 지원합니다.
Bazel 모듈을 나타내지 않는 저장소 정의
bazel_dep
를 사용하면 다른 Bazel 모듈을 나타내는 저장소를 정의할 수 있습니다.
Bazel 모듈을 나타내지 않는 저장소를 정의해야 하는 경우가 있습니다. 예를 들어 데이터로 읽을 일반 JSON 파일이 포함된 저장소가 여기에 해당합니다.
이 경우 use_repo_rule
디렉티브를 사용하여 저장소 규칙을 호출하여 저장소를 직접 정의할 수 있습니다. 이 저장소는 정의된 모듈에만 표시됩니다.
내부적으로는 모듈 확장과 동일한 메커니즘을 사용하여 구현되므로 더 유연하게 저장소를 정의할 수 있습니다.
저장소 이름 및 엄격한 deps
bazel_dep
지시어의 repo_name
속성에 달리 명시되지 않는 한 모듈을 직접 종속 항목에 지원하는 저장소의 명확한 이름은 기본적으로 모듈 이름으로 설정됩니다. 즉, 모듈은 직접 종속 항목만 찾을 수 있습니다. 이렇게 하면 전이 종속 항목의 변경으로 인한 우발적인 중단을 방지할 수 있습니다.
모듈을 지원하는 저장소의 표준 이름은 전체 종속 항목 그래프에 모듈의 여러 버전이 있는지에 따라 module_name~version
(예: bazel_skylib~1.0.3
) 또는 module_name~
(예: bazel_features~
)입니다(multiple_version_override
참고). 표준 이름 형식은 종속해야 하는 API가 아니며 언제든지 변경될 수 있습니다. 정규 이름을 하드코딩하는 대신 지원되는 방법을 사용하여 Bazel에서 직접 가져옵니다.
* BUILD 및 .bzl
파일에서 저장소의 표시 이름으로 지정된 라벨 문자열로 구성된 Label
인스턴스에 Label.repo_name
를 사용합니다(예:
Label("@bazel_skylib").repo_name
.
* 실행 파일을 조회할 때는 @bazel_tools//tools/{bash,cpp,java}/runfiles
에 있는 $(rlocationpath ...)
또는 실행 파일 라이브러리 중 하나를 사용하거나 규칙 집합 rules_foo
의 경우 @rules_foo//foo/runfiles
에 사용합니다.
* IDE 또는 언어 서버와 같은 외부 도구에서 Bazel과 상호작용할 때 bazel mod dump_repo_mapping
명령어를 사용하여 명확한 이름을 특정 저장소 집합의 표준 이름으로 매핑합니다.
모듈 확장은 모듈의 공개 범위에 추가 저장소를 도입할 수도 있습니다.