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
는 visible
, siblings
, buildfiles
, tests
을 제외한 모든 함수를 지원합니다.
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
모드는 옵션 diff 없이 동일한 정보를 출력합니다.
프로토콜 메시지 출력
--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로 설정하세요.
proto 출력 관련 옵션은 쿼리의 proto 출력 문서를 참조하세요.
그래프 출력
--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(target)
는 키가 제공업체의 이름(예: "DefaultInfo"
)이고 값이 Starlark 값인 맵을 반환합니다. 값이 유효한 Starlark 값이 아닌 제공업체는 이 맵에서 생략됩니다.
예
//foo
에서 생성된 모든 파일의 기본 이름을 공백으로 구분하여 출력합니다.
bazel cquery //foo --output=starlark \ --starlark:expr="' '.join([f.basename for f in target.files.to_list()])"
//bar
및 하위 패키지의 규칙 타겟에 의해 생성된 모든 파일의 경로를 공백으로 구분된 목록으로 출력합니다.
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
는 이전 명령어에서 빌드 그래프를 자동으로 삭제하지 않으므로 이전 쿼리의 결과를 가져올 수 있습니다. 예를 들어 genquery
는 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 "+" -))"