cquery
는 빌드 그래프에 미치는 select()
및 빌드 옵션의 효과를 올바르게 처리하는 query
의 변형입니다.
이를 위해 이러한 효과를 통합하는 Bazel의 분석 단계 결과를 실행합니다. 반면 query
는 옵션이 평가되기 전에 Bazel의 로드 단계 결과를 실행합니다.
예를 들면 다음과 같습니다.
$ cat > tree/BUILD <<EOF sh_library( name = "ash", deps = select({ ":excelsior": [":manna-ash"], ":americana": [":white-ash"], "//conditions:default": [":common-ash"], }), ) sh_library(name = "manna-ash") sh_library(name = "white-ash") sh_library(name = "common-ash") config_setting( name = "excelsior", values = {"define": "species=excelsior"}, ) config_setting( name = "americana", values = {"define": "species=americana"}, ) EOF
# Traditional query: query doesn't know which select() branch you will choose, # so it conservatively lists all of possible choices, including all used config_settings. $ bazel query "deps(//tree:ash)" --noimplicit_deps //tree:americana //tree:ash //tree:common-ash //tree:excelsior //tree:manna-ash //tree:white-ash # cquery: cquery lets you set build options at the command line and chooses # the exact dependencies that implies (and also the config_setting targets). $ bazel cquery "deps(//tree:ash)" --define species=excelsior --noimplicit_deps //tree:ash (9f87702) //tree:manna-ash (9f87702) //tree:americana (9f87702) //tree:excelsior (9f87702)
각 결과에는 타겟을 빌드하는 구성의 고유 식별자 (9f87702)
가 포함됩니다.
cquery
는 구성된 타겟 그래프를 통해 실행되므로 빌드 작업과 같은 아티팩트에 대한 통계가 없으며 구성된 타겟이 아니므로 test_suite
규칙에 액세스할 수 없습니다. 전자의 경우 aquery
를 참고하세요.
기본 구문
간단한 cquery
호출은 다음과 같습니다.
bazel cquery "function(//target)"
쿼리 표현식 "function(//target)"
는 다음으로 구성됩니다.
function(...)
은 타겟에서 실행할 함수입니다.cquery
는query
의 대부분의 함수와 몇 가지 새로운 함수를 지원합니다.//target
는 함수에 제공되는 표현식입니다. 이 예에서 표현식은 간단한 타겟입니다. 하지만 쿼리 언어에서는 함수 중첩도 허용합니다. 예시는 쿼리 가이드를 참고하세요.
cquery
는 타겟이 로드 및 분석 단계를 실행해야 합니다. 달리 지정되지 않으면 cquery
은 쿼리 표현식에 나열된 타겟을 파싱합니다. 최상위 빌드 타겟의 종속 항목을 쿼리하는 방법은 --universe_scope
를 참고하세요.
구성
다음 행은,
//tree:ash (9f87702)
//tree:ash
는 ID가 9f87702
인 구성에서 빌드되었음을 의미합니다. 대부분의 타겟에서 구성을 정의하는 빌드 옵션 값의 불투명 해시입니다.
구성의 전체 콘텐츠를 보려면 다음을 실행합니다.
$ bazel config 9f87702
9f87702
는 전체 ID의 접두사입니다. 전체 ID는 길고 추적하기 어려운 SHA-256 해시이기 때문입니다. cquery
는 Git 짧은 해시와 마찬가지로 전체 ID의 유효한 접두사를 이해합니다.
전체 ID를 보려면 $ bazel config
를 실행합니다.
타겟 패턴 평가
//foo
는 cquery
와 query
의 의미가 다릅니다. 이는 cquery
가 구성된 타겟을 평가하고 빌드 그래프에 구성된 여러 버전의 //foo
가 있을 수 있기 때문입니다.
cquery
의 경우 쿼리 표현식의 타겟 패턴은 해당 패턴과 일치하는 라벨이 있는 구성된 모든 타겟으로 평가됩니다. 출력은 결정론적이지만 cquery
는 핵심 쿼리 순서 지정 계약 이외의 순서를 보장하지 않습니다.
이렇게 하면 query
을 사용할 때보다 쿼리 표현식에 대해 미묘한 결과가 생성됩니다.
예를 들어 다음은 여러 결과를 생성할 수 있습니다.
# Analyzes //foo in the target configuration, but also analyzes # //genrule_with_foo_as_tool which depends on an exec-configured # //foo. So there are two configured target instances of //foo in # the build graph. $ bazel cquery //foo --universe_scope=//foo,//genrule_with_foo_as_tool //foo (9f87702) //foo (exec)
쿼리할 인스턴스를 정확하게 선언하려면 config
함수를 사용합니다.
대상 패턴에 대한 자세한 내용은 query
의 대상 패턴 문서를 참조하세요.
함수
query
에서 지원하는 함수 집합 중에서 cquery
는 allrdeps
, buildfiles
, rbuildfiles
, siblings
, tests
, visible
를 제외한 모든 함수를 지원합니다.
cquery
에는 다음과 같은 새로운 함수도 도입되었습니다.
config
expr ::= config(expr, word)
config
연산자는 첫 번째 인수로 지정된 라벨과 두 번째 인수로 지정된 구성에 대해 구성된 대상을 찾으려고 시도합니다.
두 번째 인수에 유효한 값은 null
또는 커스텀 구성 해시입니다. 해시는 $
bazel config
또는 이전 cquery
의 출력에서 가져올 수 있습니다.
예:
$ bazel cquery "config(//bar, 3732cc8)" --universe_scope=//foo
$ bazel cquery "deps(//foo)" //bar (exec) //baz (exec) $ bazel cquery "config(//baz, 3732cc8)"
지정된 구성에서 첫 번째 인수의 결과 중 일부를 찾을 수 없는 경우, 찾을 수 있는 결과만 반환됩니다. 지정된 구성에서 결과를 찾을 수 없는 경우 쿼리가 실패합니다.
옵션
빌드 옵션
cquery
는 일반 Bazel 빌드에서 실행되므로 빌드 중에 사용할 수 있는 옵션 집합을 상속합니다.
cquery 옵션 사용
--universe_scope
(쉼표로 구분된 목록)
구성된 타겟의 종속 항목은 전환을 거치는 경우가 많으며, 이로 인해 구성이 종속 항목과 달라집니다. 이 플래그를 사용하면 마치 대상이 다른 대상의 종속 항목으로 또는 전이 종속 항목으로 빌드된 것처럼 대상을 쿼리할 수 있습니다. 예를 들면 다음과 같습니다.
# x/BUILD genrule( name = "my_gen", srcs = ["x.in"], outs = ["x.cc"], cmd = "$(locations :tool) $< >$@", tools = [":tool"], ) cc_binary( name = "tool", srcs = ["tool.cpp"], )
Genrule은 다음 쿼리가 다음과 같은 출력을 생성하도록 exec 구성에서 도구를 구성합니다.
쿼리 | 타겟 빌드됨 | 출력 |
---|---|---|
bazel cquery '//x:도구' | //x:tool | //x:tool(targetconfig) |
bazel cquery "//x:tool" --universe_scope="//x:my_gen" | //x:my_gen | //x:tool(execconfig) |
이 플래그가 설정되면 콘텐츠가 빌드됩니다. 설정하지 않으면 쿼리 표현식에 언급된 모든 타겟이 빌드됩니다. 빌드된 타겟의 전이 폐쇄가 쿼리의 유니버스로 사용됩니다. 어느 쪽이든 빌드할 타겟은 최상위 수준에서 빌드 가능해야 합니다 (즉, 최상위 옵션과 호환됨). cquery
는 이러한 최상위 타겟의 전이 폐쇄에서 결과를 반환합니다.
최상위 수준의 쿼리 표현식에서 모든 대상을 빌드할 수 있지만 그렇게 하지 않는 것이 좋을 수 있습니다. 예를 들어 --universe_scope
를 명시적으로 설정하면 중요하지 않은 구성에서 대상을 여러 번 빌드하지 않을 수 있습니다. 또한 현재 다른 방법으로 이를 완전히 지정할 수 없으므로 찾고 있는 타겟의 구성 버전을 지정하는 데도 도움이 될 수 있습니다. 쿼리 표현식이 deps(//foo)
보다 복잡한 경우 이 플래그를 설정해야 합니다.
--implicit_deps
(불리언, 기본값=True)
이 플래그를 false로 설정하면 BUILD 파일에 명시적으로 설정되지 않고 대신 Bazel에서 다른 위치에 설정된 모든 결과가 필터링됩니다. 여기에는 해결된 도구 모음을 필터링하는 것도 포함됩니다.
--tool_deps
(불리언, 기본값=True)
이 플래그를 false로 설정하면 쿼리된 대상에서 해당 대상까지의 경로가 대상 구성과 타겟 외 구성 간의 전환을 지나는 구성된 모든 대상이 필터링됩니다.
쿼리된 타겟이 타겟 구성에 있는 경우 --notool_deps
를 설정하면 타겟 구성에 있는 타겟만 반환됩니다. 쿼리된 대상이 타겟이 아닌 구성에 있는 경우 --notool_deps
를 설정하면 타겟이 아닌 구성에 있는 대상만 반환됩니다. 일반적으로 이 설정은 해결된 도구 모음의 필터링에 영향을 미치지 않습니다.
--include_aspects
(불리언, 기본값=True)
요소에 의해 추가된 종속 항목을 포함합니다.
이 플래그가 사용 중지된 경우 X가 Y를 측면을 통해서만 종속하는 경우 cquery somepath(X, Y)
및 cquery deps(X) | grep 'Y'
는 Y를 생략합니다.
출력 형식
기본적으로 cquery는 종속 항목 순으로 정렬된 라벨 및 구성 쌍 목록으로 결과를 출력합니다. 결과를 노출하는 다른 옵션도 있습니다.
화면전환
--transitions=lite --transitions=full
구성 전환은 최상위 타겟과 다른 구성으로 최상위 타겟 아래의 타겟을 빌드하는 데 사용됩니다.
예를 들어 타겟은 tools
속성의 모든 종속 항목에 exec 구성으로의 전환을 적용할 수 있습니다. 이를 속성 전환이라고 합니다. 규칙은 자체 구성에 전환을 적용할 수도 있습니다. 이를 규칙 클래스 전환이라고 합니다. 이 출력 형식은 이러한 전환에 관한 정보(예: 유형, 빌드 옵션에 미치는 영향)를 출력합니다.
이 출력 형식은 기본적으로 NONE
로 설정된 --transitions
플래그에 의해 트리거됩니다. FULL
또는 LITE
모드로 설정할 수 있습니다. FULL
모드는 전환 전후에 옵션의 세부적인 차이를 포함하여 규칙 클래스 전환 및 속성 전환에 관한 정보를 출력합니다. LITE
모드는 옵션 차이 없이 동일한 정보를 출력합니다.
프로토콜 메시지 출력
--output=proto
이 옵션을 사용하면 결과 타겟이 바이너리 프로토콜 버퍼 형식으로 출력됩니다. 프로토콜 버퍼의 정의는 src/main/protobuf/analysis_v2.proto에서 확인할 수 있습니다.
CqueryResult
는 cquery의 결과를 포함하는 최상위 메시지입니다. 여기에는 ConfiguredTarget
메시지 목록과 Configuration
메시지 목록이 있습니다. 각 ConfiguredTarget
에는 상응하는 Configuration
메시지의 id
필드 값과 값이 동일한 configuration_id
가 있습니다.
--[no]proto:include_configurations
기본적으로 cquery 결과는 구성된 각 타겟의 일부로 구성 정보를 반환합니다. 이 정보를 생략하고 쿼리의 proto 출력과 정확히 동일한 형식이 지정된 proto 출력을 가져오려면 이 플래그를 false로 설정하세요.
프로토 출력 관련 옵션에 관한 자세한 내용은 쿼리의 프로토 출력 문서를 참고하세요.
그래프 출력
--output=graph
이 옵션은 Graphviz 호환 .dot 파일로 출력을 생성합니다. 자세한 내용은 query
의 그래프 출력 문서를 참고하세요. cquery
는 --graph:node_limit
및 --graph:factored
도 지원합니다.
파일 출력
--output=files
이 옵션은 bazel build
호출 끝에 출력된 목록과 유사하게 쿼리와 일치하는 각 타겟에서 생성된 출력 파일의 목록을 출력합니다. 출력에는 --output_groups
플래그에 의해 결정된 대로 요청된 출력 그룹에서 공지된 파일만 포함됩니다.
소스 파일은 포함됩니다.
이 출력 형식에서 내보내는 모든 경로는 bazel info execution_root
를 통해 가져올 수 있는 execroot를 기준으로 합니다. bazel-out
편의용 심볼릭 링크가 있는 경우 기본 저장소의 파일 경로도 작업공간 디렉터리에 상대적으로 확인됩니다.
Starlark를 사용하여 출력 형식 정의
--output=starlark
이 출력 형식은 쿼리 결과에서 구성된 각 타겟에 대해 Starlark 함수를 호출하고 호출에서 반환된 값을 출력합니다. --starlark:file
플래그는 단일 매개변수 target
를 사용하여 format
라는 함수를 정의하는 Starlark 파일의 위치를 지정합니다. 이 함수는 쿼리 결과의 각 타겟에 대해 호출됩니다. 또는 편의를 위해 --starlark:expr
플래그를 사용하여 def format(target): return expr
로 선언된 함수의 본문만 지정할 수 있습니다.
'cquery' Starlark 방언
cquery Starlark 환경은 BUILD 또는 .bzl 파일과 다릅니다. 여기에는 모든 핵심 Starlark 기본 제공 상수 및 함수와 아래에 설명된 몇 가지 cquery 관련 함수가 포함되지만 glob
, native
, rule
는 포함되지 않으며 로드 문이 지원되지 않습니다.
build_options(target)
build_options(target)
는 키가 빌드 옵션 식별자 (구성 참고)이고 값이 Starlark 값인 맵을 반환합니다. 유효한 Starlark 값이 아닌 값이 이 맵에서 생략됩니다.
타겟이 입력 파일인 경우 입력 파일 타겟의 구성이 null이므로 build_options(target)
는 None을 반환합니다.
providers(타겟)
providers(target)
는 키가 제공업체의 이름(예: "DefaultInfo"
)이고 값이 Starlark 값인 맵을 반환합니다. 값이 유효한 Starlark 값이 아닌 제공업체는 이 맵에서 생략됩니다.
예
//foo
에서 생성된 모든 파일의 기본 이름을 공백으로 구분된 목록으로 출력합니다.
bazel cquery //foo --output=starlark \ --starlark:expr="' '.join([f.basename for f in target.files.to_list()])"
//bar
및 그 하위 패키지의 rule 타겟에서 생성된 모든 파일의 경로를 공백으로 구분하여 출력합니다.
bazel cquery 'kind(rule, //bar/...)' --output=starlark \ --starlark:expr="' '.join([f.path for f in target.files.to_list()])"
//foo
에 의해 등록된 모든 작업의 니모닉 목록을 출력합니다.
bazel cquery //foo --output=starlark \ --starlark:expr="[a.mnemonic for a in target.actions]"
cc_library
//baz
에 의해 등록된 컴파일 출력 목록을 출력합니다.
bazel cquery //baz --output=starlark \ --starlark:expr="[f.path for f in target.output_groups.compilation_outputs.to_list()]"
//foo
를 빌드할 때 명령줄 옵션 --javacopt
의 값을 출력합니다.
bazel cquery //foo --output=starlark \ --starlark:expr="build_options(target)['//command_line_option:javacopt']"
정확히 한 개의 출력으로 각 대상의 라벨을 출력합니다. 이 예에서는 파일에 정의된 Starlark 함수를 사용합니다.
$ cat example.cquery def has_one_output(target): return len(target.files.to_list()) == 1 def format(target): if has_one_output(target): return target.label else: return "" $ bazel cquery //baz --output=starlark --starlark:file=example.cquery
엄격하게 Python 3인 각 타겟의 라벨을 출력합니다. 이 예에서는 파일에 정의된 Starlark 함수를 사용합니다.
$ cat example.cquery def format(target): p = providers(target) py_info = p.get("PyInfo") if py_info and py_info.has_py3_only_sources: return target.label else: return "" $ bazel cquery //baz --output=starlark --starlark:file=example.cquery
사용자 정의 제공업체에서 값을 추출합니다.
$ cat some_package/my_rule.bzl MyRuleInfo = provider(fields={"color": "the name of a color"}) def _my_rule_impl(ctx): ... return [MyRuleInfo(color="red")] my_rule = rule( implementation = _my_rule_impl, attrs = {...}, ) $ cat example.cquery def format(target): p = providers(target) my_rule_info = p.get("//some_package:my_rule.bzl%MyRuleInfo'") if my_rule_info: return my_rule_info.color return "" $ bazel cquery //baz --output=starlark --starlark:file=example.cquery
cquery와 쿼리 비교
cquery
와 query
는 서로를 보완하며 서로 다른 틈새 시장에서 우수한 성능을 발휘합니다. 다음을 고려하여 적합한 방법을 결정하세요.
cquery
는 특정select()
브랜치를 따라 빌드하는 정확한 그래프를 모델링합니다.query
는 빌드에서 선택하는 브랜치를 알 수 없으므로 모든 브랜치를 포함하여 과도하게 근사합니다.cquery
의 정밀도를 얻으려면query
보다 더 많은 그래프를 빌드해야 합니다. 구체적으로cquery
는 구성된 타겟을 평가하는 반면query
는 타겟만 평가합니다. 이렇게 하면 시간이 더 많이 걸리고 더 많은 메모리를 사용합니다.cquery
의 쿼리 언어 해석은query
에서 피하는 모호성을 도입합니다. 예를 들어"//foo"
가 두 구성에 있는 경우cquery "deps(//foo)"
는 어떤 구성을 사용해야 하나요? 이때config
함수가 도움이 될 수 있습니다.- 최신 도구인
cquery
는 특정 사용 사례를 지원하지 않습니다. 자세한 내용은 알려진 문제를 참고하세요.
알려진 문제
cquery
'빌드'가 있는 모든 대상은 구성이 동일해야 합니다.
쿼리를 평가하기 전에 cquery
는 빌드 작업이 실행되는 시점 직전의 빌드를 트리거합니다. '빌드'하는 대상은 기본적으로 쿼리 표현식에 나타나는 모든 라벨에서 선택됩니다 (--universe_scope
로 재정의될 수 있음). 구성이 동일해야 합니다.
일반적으로 최상위 '대상' 구성을 공유하지만 규칙은 수신 에지 전환을 통해 자체 구성을 변경할 수 있습니다.
cquery
는 이 점에서 부족합니다.
해결 방법: 가능하면 --universe_scope
를 더 엄격한 범위로 설정합니다. 예를 들면 다음과 같습니다.
# This command attempts to build the transitive closures of both //foo and # //bar. //bar uses an incoming edge transition to change its --cpu flag. $ bazel cquery 'somepath(//foo, //bar)' ERROR: Error doing post analysis query: Top-level targets //foo and //bar have different configurations (top-level targets with different configurations is not supported) # This command only builds the transitive closure of //foo, under which # //bar should exist in the correct configuration. $ bazel cquery 'somepath(//foo, //bar)' --universe_scope=//foo
--output=xml
는 지원되지 않습니다.
비확정 출력.
cquery
는 이전 명령어에서 빌드 그래프를 자동으로 완전 삭제하지 않으므로 이전 쿼리에서 결과를 선택할 가능성이 높습니다. 예를 들어 genrule
는 tools
속성에 exec 전환을 적용합니다. 즉, exec 구성에서 도구를 구성합니다.
아래에서 이 전환의 여파가 지속되는 것을 확인할 수 있습니다.
$ cat > foo/BUILD <<<EOF genrule( name = "my_gen", srcs = ["x.in"], outs = ["x.cc"], cmd = "$(locations :tool) $< >$@", tools = [":tool"], ) cc_library( name = "tool", ) EOF $ bazel cquery "//foo:tool" tool(target_config) $ bazel cquery "deps(//foo:my_gen)" my_gen (target_config) tool (exec_config) ... $ bazel cquery "//foo:tool" tool(exec_config)
해결 방법: 구성된 타겟을 강제로 재분석하도록 시작 옵션을 변경합니다.
예를 들어 빌드 명령어에 --test_arg=<whatever>
를 추가합니다.
문제 해결
재귀 타겟 패턴 (/...
)
다음과 같은 문제가 발생할 경우:
$ bazel cquery --universe_scope=//foo:app "somepath(//foo:app, //foo/...)" ERROR: Error doing post analysis query: Evaluation failed: Unable to load package '[foo]' because package is not in scope. Check that all target patterns in query expression are within the --universe_scope of this query.
--universe_scope=//foo:app
에 포함되어 있음에도 불구하고 //foo
패키지가 범위에 포함되지 않는다고 잘못 제안합니다. 이는 cquery
의 설계 제한 때문입니다. 해결 방법으로 유니버스 범위에 //foo/...
를 명시적으로 포함합니다.
$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"
이렇게 해도 작동하지 않으면(예: //foo/...
의 일부 타겟을 선택한 빌드 플래그로 빌드할 수 없기 때문에) 전처리 쿼리를 사용하여 패턴을 구성 패키지로 수동으로 래핑합니다.
# Replace "//foo/..." with a subshell query call (not cquery!) outputting each package, piped into # a sed call converting "<pkg>" to "//<pkg>:*", piped into a "+"-delimited line merge. # Output looks like "//foo:*+//foo/bar:*+//foo/baz". # $ bazel cquery --universe_scope=//foo:app "somepath(//foo:app, $(bazel query //foo/... --output=package | sed -e 's/^/\/\//' -e 's/$/:*/' | paste -sd "+" -))"