구성 가능한 쿼리 (cquery)

cqueryselect() 및 빌드 옵션이 빌드 그래프에 미치는 영향을 올바르게 처리하는 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](/versions/6.1.0/reference/be/general#test_suite) 규칙이 구성된 대상이 아니므로 이러한 규칙에 액세스할 수 없습니다. 전자의 경우 [aquery](/versions/6.1.0/docs/aquery)를 참조하세요.

기본 구문

간단한 cquery 호출은 다음과 같습니다.

bazel cquery "function(//target)"

"function(//target)" 쿼리 표현식은 다음으로 구성됩니다.

  • function(...)는 타겟에서 실행할 함수입니다. cqueryquery함수 대부분을 지원하며 몇 가지 새로운 함수도 있습니다.
  • //target은 함수에 제공되는 표현식입니다. 이 예시에서 표현식은 간단한 타겟입니다. 하지만 쿼리 언어를 통해 함수를 중첩할 수도 있습니다. 쿼리 방법에서 예를 확인하세요.

cquery에서는 로드 및 분석 단계를 통해 타겟을 실행해야 합니다. 달리 명시되지 않는 한 cquery는 쿼리 표현식에 나열된 대상을 파싱합니다. 최상위 빌드 타겟의 종속 항목을 쿼리하려면 --universe_scope를 참고하세요.

구성

다음 행은,

//tree:ash (9f87702)

이는 //tree:ash가 ID가 9f87702인 구성에서 빌드되었음을 의미합니다. 대부분의 대상에서 이는 구성을 정의하는 빌드 옵션 값의 불투명 해시입니다.

구성의 전체 콘텐츠를 보려면 다음을 실행합니다.

$ bazel config 9f87702

호스트 구성은 특수 ID (HOST)를 사용합니다. 생성되지 않는 소스 파일(예: srcs에 일반적으로 포함됨)은 특수 ID (null)을 사용합니다(구성이 필요하지 않음).

9f87702는 전체 ID의 프리픽스입니다. 그 이유는 전체 ID가 길고 따라하기 어려운 SHA-256 해시이기 때문입니다. cqueryGit 짧은 해시와 마찬가지로 전체 ID의 유효한 프리픽스를 이해합니다. 전체 ID를 보려면 $ bazel config를 실행하세요.

대상 패턴 평가

//foocqueryquery의 의미가 다릅니다. 이는 cquery구성된 대상을 평가하고 빌드 그래프에 구성된 여러 버전의 //foo가 있을 수 있기 때문입니다.

cquery의 경우 쿼리 표현식의 대상 패턴은 해당 패턴과 일치하는 라벨을 사용하여 구성된 모든 대상으로 평가됩니다. 출력은 확정적이지만 cquery핵심 쿼리 순서 지정 계약 이상의 순서를 보장하지 않습니다.

이렇게 하면 query를 사용하는 것보다 쿼리 표현식이 더 작아집니다. 예를 들어 다음은 여러 결과를 생성할 수 있습니다.

# Analyzes //foo in the target configuration, but also analyzes
# //genrule_with_foo_as_tool which depends on a host-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 (HOST)

쿼리할 인스턴스를 정확하게 선언하려면 config 함수를 사용합니다.

대상 패턴에 대한 자세한 내용은 query대상 패턴 문서를 참조하세요.

함수

query에서 지원하는 함수 집합cqueryvisible, siblings, buildfiles, tests를 제외한 모든 함수를 지원합니다.

cquery에는 다음과 같은 새 함수도 도입되었습니다.

config

expr ::= config(expr, word)

config 연산자는 첫 번째 인수로 표시되는 라벨 및 두 번째 인수로 지정된 구성에 대해 구성된 대상을 찾으려고 시도합니다.

두 번째 인수의 유효한 값은 target, host, null 또는 커스텀 구성 해시입니다. 해시는 $ bazel config 또는 이전 cquery의 출력에서 가져올 수 있습니다.

예:

$ bazel cquery "config(//bar, host)" --universe_scope=//foo
$ bazel cquery "deps(//foo)"
//bar (HOST)
//baz (3732cc8)

$ 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_library(
    name = "tool",
)

Genrule의 도구는 호스트 구성에서 구성되므로 다음 쿼리에서 다음과 같은 출력이 생성됩니다.

쿼리 타겟 빌드 출력
bazel cquery "//x:tool" //x:tool //x:tool(targetconfig)
bazel cquery "//x:tool" --universe_scopeitalicx:my_gen" //x:my_gen //x:tool(hostconfig)

이 플래그를 설정하면 내용이 빌드됩니다. 설정하지 않으면 쿼리 표현식에 언급된 모든 대상이 빌드됩니다. 빌드된 대상의 전이적 닫힘은 쿼리 세계관으로 사용됩니다. 어느 쪽이든 빌드할 타겟은 최상위 수준에서 빌드할 수 있어야 합니다 (즉, 최상위 옵션과 호환 가능). cquery는 이러한 최상위 타겟을 전이적으로 닫는 결과를 반환합니다.

최상위 수준에서 쿼리 표현식의 모든 대상을 빌드할 수 있더라도 이렇게 하지 않는 것이 좋습니다. 예를 들어 --universe_scope를 명시적으로 설정하면 중요하지 않은 구성에서 대상을 여러 번 빌드하지 못할 수 있습니다. 또한 찾고 있는 대상의 구성 버전을 지정하는 데도 도움이 될 수 있습니다 (현재 다른 방법으로는 완전히 지정할 수 없기 때문). 쿼리 표현식이 deps(//foo)보다 복잡한 경우 이 플래그를 설정해야 합니다.

--implicit_deps (불리언, 기본값=True)

이 플래그를 false로 설정하면 BUILD 파일에 명시적으로 설정되지 않은 모든 결과를 대신 필터링하고 대신 Bazel에서 다른 결과를 설정합니다. 여기에는 확인된 도구 모음 필터링이 포함됩니다.

--tool_deps (불리언, 기본값=True)

이 플래그를 false로 설정하면 쿼리된 대상에서 대상까지 경로가 대상 구성과 비대상 구성 간의 전환을 교차하는 모든 대상 설정이 필터링됩니다. 쿼리된 타겟이 타겟 구성에 있는 경우 --notool_deps를 설정하면 타겟 구성에도 있는 타겟만 반환됩니다. 쿼리된 대상이 대상이 아닌 구성인 경우 --notool_deps를 설정하면 대상이 아닌 구성에서도 대상이 반환됩니다. 이 설정은 일반적으로 확인된 도구 모음의 필터링에는 영향을 미치지 않습니다.

--include_aspects (불리언, 기본값=True)

평가 항목을 사용하면 빌드에 종속 항목을 추가할 수 있습니다. 기본적으로 cquery는 메모리를 더 많이 사용하는 쿼리 가능한 그래프를 더 크게 만들기 때문에 이 측면을 따르지 않습니다. 이를 따르면 더 정확한 결과를 얻을 수 있습니다.

대규모 쿼리의 메모리 영향이 우려되는 경우 bazelrc에서 기본적으로 이 플래그를 사용 설정합니다.

측면을 사용 중지한 상태에서 쿼리하면 대상 Y를 빌드하는 동안 대상 X가 실패하는 문제가 발생할 수 있지만, cquery somepath(Y, X)cquery deps(Y) | grep 'X'는 종속 항목을 통해 발생하므로 결과를 반환하지 않습니다.

출력 형식

기본적으로 cquery 출력은 종속 항목 순서의 라벨 및 구성 쌍 목록을 생성합니다. 결과를 노출하는 다른 옵션도 있습니다.

전환

--transitions=lite
--transitions=full

구성 전환은 최상위 타겟과 다른 구성의 최상위 타겟 아래에 타겟을 빌드하는 데 사용됩니다.

예를 들어 타겟은 tools 속성의 모든 종속 항목에 호스트 구성 전환을 적용할 수 있습니다. 이를 속성 전환이라고 합니다. 규칙은 자체 구성에 전환을 적용할 수도 있으며, 이를 규칙 클래스 전환이라고 합니다. 이 출력 형식은 유형 및 빌드 옵션에 미치는 영향 등 이러한 전환에 관한 정보를 출력합니다.

이 출력 형식은 기본적으로 NONE로 설정되는 --transitions 플래그에 의해 트리거됩니다. FULL 또는 LITE 모드로 설정할 수 있습니다. FULL 모드는 전환 전후의 옵션을 자세히 설명하는 규칙 클래스 전환 및 속성 전환에 관한 정보를 출력합니다. LITE 모드는 옵션 차이 없이 동일한 정보를 출력합니다.

프로토콜 메시지 출력

--output=proto

이 옵션을 사용하면 결과 타겟이 바이너리 프로토콜 버퍼 형식으로 출력됩니다. 프로토콜 버퍼의 정의는 src/main/protobuf/analysis.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 플래그에 따라 확인된 요청된 출력 그룹에 공지된 파일만 포함됩니다. 여기에는 소스 파일이 포함되지 않습니다.

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 값이 아닌 빌드 옵션은 이 지도에서 생략됩니다.

타겟이 입력 파일이면 build_options(target)는 None을 반환합니다. 입력 파일 대상에 null 구성이 있기 때문입니다.

제공업체(타겟)

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와 검색어

cqueryquery는 서로를 보완하며 다양한 틈새 시장에서 두드러집니다. 다음 중에서 무엇이 적합할지 결정하세요.

  • cquery는 특정 select() 브랜치를 따라 빌드한 정확한 그래프를 모델링합니다. query는 빌드가 어떤 브랜치를 선택하는지 알 수 없으므로 브랜치를 모두 포함하여 포함합니다.
  • cquery의 정밀도는 query보다 더 많은 그래프를 빌드해야 합니다. 특히 cquery구성된 대상을 평가하고 query대상만 평가합니다. 이 옵션은 시간이 더 많이 걸리고 메모리를 더 많이 사용합니다.
  • cquery쿼리 언어 해석에는 query에서 피하는 모호성이 발생합니다. 예를 들어 "//foo"가 2개의 구성에 있는 경우 cquery "deps(//foo)"는 무엇을 사용해야 하나요? [config](#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는 이전 명령어에서 빌드 그래프를 자동으로 완전 삭제하지 않으므로 이전 쿼리에서 결과를 선택하는 경향이 있습니다. 예를 들어 genquerytools 속성에 관해 호스트 전환을 실행합니다. 즉, 호스트 구성에서 도구를 구성합니다.

아래에서 이러한 전환의 영향을 확인할 수 있습니다.

$ 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 (host_config)
...

    $ bazel cquery "//foo:tool"
tool(host_config)

해결 방법: 시작 옵션을 변경하여 구성된 대상을 강제로 재분석합니다. 예를 들어 빌드 명령어에 --test_arg=&lt;whatever&gt;를 추가합니다.

문제 해결

재귀 대상 패턴 (/...)

다음에 해당하는 경우:

$ 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 "+" -))"