쿼리 빠른 시작

이 튜토리얼에서는 Bazel을 사용하여 사전 제작된 Bazel 프로젝트를 사용하여 코드의 종속 항목을 추적하는 방법을 다룹니다.

언어 및 --output 플래그에 대한 자세한 내용은 Bazel 쿼리 참조Bazel cquery 참조 매뉴얼을 참조하세요. 명령줄에 bazel help query 또는 bazel help cquery를 입력하여 IDE에서 도움을 받으세요.

목표

이 가이드에서는 프로젝트의 파일 종속 항목에 대해 자세히 알아볼 수 있는 기본 쿼리를 살펴봅니다. 이 문서는 Bazel 및 BUILD 파일의 작동 방식에 대한 기본 지식을 갖춘 신규 Bazel 개발자를 대상으로 합니다.

기본 요건

아직 설치하지 않았다면 먼저 Bazel을 설치합니다. 이 튜토리얼에서는 소스 제어에 Git을 사용하므로 최상의 결과를 얻으려면 Git도 설치하세요.

종속 항목 그래프를 시각화하기 위해 Graphviz라는 도구를 사용하며 이 도구를 다운로드하여 따라할 수 있습니다.

샘플 프로젝트 가져오기

다음으로 원하는 명령줄 도구에서 다음을 실행하여 Bazel의 예시 저장소에서 샘플 앱을 검색합니다.

git clone https://github.com/bazelbuild/examples.git

이 튜토리얼의 샘플 프로젝트는 examples/query-quickstart 디렉터리에 있습니다.

시작하기

Bazel 쿼리란 무엇인가요?

쿼리는 BUILD 파일 간의 관계를 분석하고 결과 출력에서 유용한 정보를 검토하여 Bazel 코드베이스를 학습하는 데 도움이 됩니다. 이 가이드에서는 몇 가지 기본적인 쿼리 함수를 미리 보여주지만 더 많은 옵션을 보려면 쿼리 가이드를 참고하세요. 쿼리를 사용하면 BUILD 파일을 직접 탐색하지 않고도 대규모 프로젝트의 종속 항목을 알아볼 수 있습니다.

쿼리를 실행하려면 명령줄 터미널을 열고 다음을 입력합니다.

bazel query 'query_function'

시나리오

Cafe Bazel과 해당 요리사의 관계를 자세히 설명하는 시나리오를 생각해 보세요. 피자, 맥앤치즈를 판매하는 카페입니다. 아래에서 프로젝트의 구조를 확인하세요.

bazelqueryguide
├── BUILD
├── src
│   └── main
│       └── java
│           └── com
│               └── example
│                   ├── customers
│                   │   ├── Jenny.java
│                   │   ├── Amir.java
│                   │   └── BUILD
│                   ├── dishes
│                   │   ├── Pizza.java
│                   │   ├── MacAndCheese.java
│                   │   └── BUILD
│                   ├── ingredients
│                   │   ├── Cheese.java
│                   │   ├── Tomatoes.java
│                   │   ├── Dough.java
│                   │   ├── Macaroni.java
│                   │   └── BUILD
│                   ├── restaurant
│                   │   ├── Cafe.java
│                   │   ├── Chef.java
│                   │   └── BUILD
│                   ├── reviews
│                   │   ├── Review.java
│                   │   └── BUILD
│                   └── Runner.java
└── WORKSPACE

이 튜토리얼에서 달리 안내하지 않는 한 필요한 정보를 찾기 위해 BUILD 파일을 찾지 말고 쿼리 함수만 사용합니다.

프로젝트는 카페를 구성하는 다양한 패키지로 구성됩니다. restaurant, ingredients, dishes, customers, reviews로 구분됩니다. 이러한 패키지 내의 규칙은 다양한 태그와 종속 항목으로 Cafe의 다양한 구성요소를 정의합니다.

빌드 실행

이 프로젝트에는 카페 내부 메뉴를 출력하기 위해 실행할 수 있는 Runner.java 내부의 기본 메서드가 포함되어 있습니다. bazel build 명령어로 Bazel을 사용하여 프로젝트를 빌드하고 :를 사용하여 대상 이름이 runner임을 알립니다. 대상을 참조하는 방법은 대상 이름을 참조하세요.

이 프로젝트를 빌드하려면 터미널에 다음 명령어를 붙여넣습니다.

bazel build :runner

빌드가 성공하면 다음과 같은 출력이 표시됩니다.

INFO: Analyzed target //:runner (49 packages loaded, 784 targets configured).
INFO: Found 1 target...
Target //:runner up-to-date:
  bazel-bin/runner.jar
  bazel-bin/runner
INFO: Elapsed time: 16.593s, Critical Path: 4.32s
INFO: 23 processes: 4 internal, 10 darwin-sandbox, 9 worker.
INFO: Build completed successfully, 23 total actions

성공적으로 빌드되면 다음 명령어를 붙여넣어 애플리케이션을 실행합니다.

bazel-bin/runner
--------------------- MENU -------------------------

Pizza - Cheesy Delicious Goodness
Macaroni & Cheese - Kid-approved Dinner

----------------------------------------------------

그러면 주어진 메뉴 항목과 간단한 설명이 표시됩니다.

타겟 탐색

프로젝트에는 재료와 요리가 포함된 패키지가 포함되어 있습니다. 쿼리를 사용하여 패키지의 규칙을 보려면 bazel query package/… 명령어를 실행하세요.

다음과 같은 방법으로 이 카페의 재료와 요리를 살펴볼 수 있습니다.

bazel query //src/main/java/com/example/dishes/...
bazel query //src/main/java/com/example/ingredients/...

재료 패키지의 타겟을 쿼리하면 다음과 같이 출력됩니다.

//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/ingredients:dough
//src/main/java/com/example/ingredients:macaroni
//src/main/java/com/example/ingredients:tomato

종속 항목 찾기

달리기를 하기 위해 달리기를 하는 주자는 무엇에 관한 것일까요?

파일 시스템에 집중하지 않고 프로젝트의 구조를 자세히 살펴보고 싶다고 가정해 보겠습니다 (대규모 프로젝트에서는 사용할 수 없음). Cafe Bazel은 어떤 규칙을 사용하나요?

이 예에서와 같이 실행기 대상이 runner이면 다음 명령어를 실행하여 대상의 기본 종속 항목을 검색합니다.

bazel query --noimplicit_deps "deps(target)"
bazel query --noimplicit_deps "deps(:runner)"
//:runner
//:src/main/java/com/example/Runner.java
//src/main/java/com/example/dishes:MacAndCheese.java
//src/main/java/com/example/dishes:Pizza.java
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:Cheese.java
//src/main/java/com/example/ingredients:Dough.java
//src/main/java/com/example/ingredients:Macaroni.java
//src/main/java/com/example/ingredients:Tomato.java
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/ingredients:dough
//src/main/java/com/example/ingredients:macaroni
//src/main/java/com/example/ingredients:tomato
//src/main/java/com/example/restaurant:Cafe.java
//src/main/java/com/example/restaurant:Chef.java
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef

대부분의 경우 deps() 함수를 사용하여 특정 타겟의 개별 출력 종속 항목을 확인합니다.

종속 항목 그래프 시각화 (선택사항)

이 섹션에서는 특정 쿼리의 종속 항목 경로를 시각화하는 방법을 설명합니다. Graphviz를 사용하면 평면화된 목록이 아닌 방향성 비순환 그래프 이미지로 경로를 확인할 수 있습니다. 다양한 --output 명령줄 옵션을 사용하여 Bazel 쿼리 그래프 표시를 변경할 수 있습니다. 옵션은 출력 형식을 참고하세요.

먼저 원하는 쿼리를 실행하고 --noimplicit_deps 플래그를 추가하여 과도한 도구 종속 항목을 삭제합니다. 그런 다음 출력 플래그와 함께 쿼리를 따르고 그래프를 graph.in 파일에 저장하여 그래프의 텍스트 표현을 만듭니다.

타겟 :runner의 모든 종속 항목을 검색하고 출력의 형식을 그래프로 지정하는 방법은 다음과 같습니다.

bazel query --noimplicit_deps 'deps(:runner)' --output graph > graph.in

이렇게 하면 빌드 그래프의 텍스트 표현인 graph.in이라는 파일이 생성됩니다. Graphviz는 텍스트를 시각화로 처리하는 도구인 dot 를 사용하여 png를 만듭니다.

dot -Tpng < graph.in > graph.png

graph.png를 열면 다음과 같이 표시됩니다. 아래의 그래프는 이 가이드에 필수 경로 세부정보를 명확히 하기 위해 단순화되었습니다.

카페, 요리사, 요리, 치즈, 토마토, 반죽, 마카로니의 각기 다른 재료로 구분되는 치즈의 관계를 보여주는 다이어그램입니다.

이 가이드를 통해 다양한 쿼리 함수의 출력을 확인하고 싶을 때 유용합니다.

역 종속 항목 찾기

다른 타겟을 사용하고자 하는 대상을 분석하려는 경우 쿼리를 사용하면 특정 규칙에 종속된 대상을 검사할 수 있습니다. 이를 '역 종속 항목'이라고 합니다. 익숙하지 않은 코드베이스에서 파일을 수정할 때 rdeps()를 사용하면 유용할 수 있으며 종속된 다른 파일이 자신도 모르게 깨지는 것을 방지할 수 있습니다.

예를 들어 cheese 재료를 수정하는 것이 좋습니다. Cafe Bazel에 문제를 일으키지 않으려면 cheese에 어떤 요리가 사용되는지 확인해야 합니다.

특정 타겟/패키지에 종속되는 타겟을 확인하려면 rdeps(universe_scope, target)를 사용하면 됩니다. rdeps() 쿼리 함수는 universe_scope(관련 디렉터리) 및 target라는 2개 이상의 인수를 사용합니다. Bazel은 제공된 universe_scope 내에서 대상의 역방향 종속 항목을 검색합니다. rdeps() 연산자는 선택사항인 세 번째 인수(검색 심도의 상한값을 지정하는 정수 리터럴)를 허용합니다.

전체 프로젝트 ‘//...’의 범위 내에서 타겟 cheese의 역방향 종속 항목을 찾으려면 다음 명령어를 실행합니다.

bazel query "rdeps(universe_scope, target)"
ex) bazel query "rdeps(//... , //src/main/java/com/example/ingredients:cheese)"
//:runner
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef

쿼리 반환에는 피자와 macAndCheese가 모두 사용되는 치즈가 표시됩니다. 놀라운 소식입니다.

태그를 기반으로 대상 찾기

두 명의 고객, 아미르와 제니라는 Bazel Cafe가 들어서 있습니다. 이름 이외에는 알려진 바가 없습니다. 다행히 '고객' BUILD 파일에 태그가 지정되어 있습니다. 이 태그에 액세스하려면 어떻게 해야 하나요?

개발자는 테스트를 위해 다양한 식별자로 Bazel 대상에 태그를 지정할 수 있습니다. 예를 들어 테스트의 태그는 특히 런타임 주석 기능이 없는 C++ 및 Python 테스트에서 디버그 및 출시 프로세스에서 테스트의 역할에 주석을 달 수 있습니다. 태그 및 크기 요소를 사용하면 코드베이스의 체크인 정책을 기반으로 테스트 모음을 유연하게 구성할 수 있습니다.

이 예에서 태그는 메뉴 항목을 나타내는 pizza 또는 macAndCheese 중 하나입니다. 이 명령어는 특정 패키지 내에서 식별자와 일치하는 태그가 있는 타겟을 쿼리합니다.

bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)'

이 쿼리는 '고객' 패키지의 '피자' 태그가 있는 모든 타겟을 반환합니다.

테스트

이 쿼리를 사용해 제니가 주문하려는 것을 알아보세요.

답변

맥앤치즈

새 종속 항목 추가

Cafe Bazel이 메뉴를 확대하여 이제 고객이 스무디를 주문할 수 있습니다. 이 특정 스무디는 StrawberryBanana 재료로 구성됩니다.

먼저 스무디에 사용되는 재료인 Strawberry.javaBanana.java을 추가합니다. 빈 자바 클래스를 추가합니다.

src/main/java/com/example/ingredients/Strawberry.java

package com.example.ingredients;

public class Strawberry {

}

src/main/java/com/example/ingredients/Banana.java

package com.example.ingredients;

public class Banana {

}

그런 다음 적절한 디렉터리(dishes)에 Smoothie.java를 추가합니다.

src/main/java/com/example/dishes/Smoothie.java

package com.example.dishes;

public class Smoothie {
    public static final String DISH_NAME = "Smoothie";
    public static final String DESCRIPTION = "Yummy and Refreshing";
}

마지막으로 이러한 파일을 적절한 BUILD 파일에 규칙으로 추가합니다. 이름, 공개 상태, 새로 만든 'src' 파일을 포함하여 새로운 각 요소마다 새로운 자바 라이브러리를 만듭니다. 업데이트된 BUILD 파일로 마무리해야 합니다.

src/main/java/com/example/ingredients/BUILD

java_library(
    name = "cheese",
    visibility = ["//visibility:public"],
    srcs = ["Cheese.java"],
)

java_library(
    name = "dough",
    visibility = ["//visibility:public"],
    srcs = ["Dough.java"],
)

java_library(
    name = "macaroni",
    visibility = ["//visibility:public"],
    srcs = ["Macaroni.java"],
)

java_library(
    name = "tomato",
    visibility = ["//visibility:public"],
    srcs = ["Tomato.java"],
)

java_library(
    name = "strawberry",
    visibility = ["//visibility:public"],
    srcs = ["Strawberry.java"],
)

java_library(
    name = "banana",
    visibility = ["//visibility:public"],
    srcs = ["Banana.java"],
)

요리의 BUILD 파일에서 Smoothie의 새 규칙을 추가하려고 합니다. 이렇게 하면 Smoothie용으로 생성된 자바 파일이 'src' 파일로 생성되며, 스무디의 각 재료와 관련해 새로 만든 규칙이 포함됩니다.

src/main/java/com/example/dishes/BUILD

java_library(
    name = "macAndCheese",
    visibility = ["//visibility:public"],
    srcs = ["MacAndCheese.java"],
    deps = [
        "//src/main/java/com/example/ingredients:cheese",
        "//src/main/java/com/example/ingredients:macaroni",
    ],
)

java_library(
    name = "pizza",
    visibility = ["//visibility:public"],
    srcs = ["Pizza.java"],
    deps = [
        "//src/main/java/com/example/ingredients:cheese",
        "//src/main/java/com/example/ingredients:dough",
        "//src/main/java/com/example/ingredients:tomato",
    ],
)

java_library(
    name = "smoothie",
    visibility = ["//visibility:public"],
    srcs = ["Smoothie.java"],
    deps = [
        "//src/main/java/com/example/ingredients:strawberry",
        "//src/main/java/com/example/ingredients:banana",
    ],
)

마지막으로 스무디를 Chef의 BUILD 파일에 종속 항목으로 포함합니다.

src/main/java/com/example/restaurant/BUILD

java\_library(
    name = "chef",
    visibility = ["//visibility:public"],
    srcs = [
        "Chef.java",
    ],

    deps = [
        "//src/main/java/com/example/dishes:macAndCheese",
        "//src/main/java/com/example/dishes:pizza",
        "//src/main/java/com/example/dishes:smoothie",
    ],
)

java\_library(
    name = "cafe",
    visibility = ["//visibility:public"],
    srcs = [
        "Cafe.java",
    ],
    deps = [
        ":chef",
    ],
)

cafe를 다시 빌드하여 오류가 없는지 확인합니다. 빌드가 성공적으로 완료되면 축하합니다! 'Cafe'에 대한 종속 항목을 새로 추가했습니다. 그렇지 않은 경우 철자 오류 및 패키지 이름을 확인하세요. BUILD 파일 작성에 관한 자세한 내용은 빌드 스타일 가이드를 참고하세요.

이제 이전 종속 항목과 비교할 수 있도록 Smoothie가 추가된 새 종속 항목 그래프를 시각화합니다. 명확하게 하려면 그래프 입력의 이름을 graph2.ingraph2.png로 지정합니다.

bazel query --noimplicit_deps 'deps(:runner)' --output graph > graph2.in
dot -Tpng < graph2.in > graph2.png

첫 번째 그래프와 동일한 그래프. 지금은 스무디가 있는 스포크에서 스포크가 나오고 있어 바나나와 딸기로 이어집니다.

graph2.png를 보면 Smoothie가 다른 요리와 공유된 종속 항목이 없지만 Chef가 사용하는 또 다른 타겟임을 알 수 있습니다.

somepath() 및 allpaths()

한 패키지가 다른 패키지에 종속되는 이유를 쿼리하려면 어떻게 해야 할까요? 두 항목 간의 종속 항목 경로를 표시하면 답변이 가능합니다.

두 가지 함수(somepath()allpaths())가 종속 항목 경로를 찾는 데 도움이 될 수 있습니다. 시작 대상 S와 끝 E가 주어지면 somepath(S,E)를 사용하여 S와 E 사이의 경로를 찾습니다.

'요리' 타겟과 '치즈' 타겟 간의 관계를 확인하여 이 두 기능의 차이를 알아보세요. 한 대상에서 다른 대상으로 이동할 수 있는 다양한 경로가 있습니다.

  • 요리사 → MacAndCheese → 치즈
  • 요리사 → 피자 → 치즈

somepath()는 두 옵션 중 하나의 경로를 제공하는 반면, 'allpaths()'는 가능한 모든 경로를 출력합니다.

예를 들어 Cafe Bazel을 사용하여 다음을 실행하세요.

bazel query "somepath(//src/main/java/com/example/restaurant/..., //src/main/java/com/example/ingredients:cheese)"
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/ingredients:cheese

출력은 Cafe → Chef → MacAndCheese → Cheese의 첫 번째 경로를 따릅니다. 대신 allpaths()를 사용하면 다음과 같은 이점이 있습니다.

bazel query "allpaths(//src/main/java/com/example/restaurant/..., //src/main/java/com/example/ingredients:cheese)"
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef

카페에서 셰프로, 피자에서 맥으로, 치즈에서 치즈로 이어지는 출력 경로

allpaths()는 종속 항목의 평면화된 목록이므로 읽기가 조금 더 어렵습니다. Graphviz를 사용하여 이 그래프를 시각화하면 관계를 더 명확하게 이해할 수 있습니다.

테스트

Cafe Bazel의 고객 중 한 명이 레스토랑에 대한 첫 리뷰를 남겼습니다. 리뷰에서 신고자의 신원 및 참조하는 요리와 같은 일부 세부정보가 누락되었습니다. 다행히 Bazel을 사용하면 이 정보에 액세스할 수 있습니다. reviews 패키지에는 미스터리 고객의 리뷰를 출력하는 프로그램이 포함되어 있습니다. 다음을 사용하여 빌드하고 실행합니다.

bazel build //src/main/java/com/example/reviews:review
bazel-bin/src/main/java/com/example/reviews/review

Bazel 쿼리만 사용하고 리뷰를 작성한 사람과 리뷰에서 어떤 요리를 제공했는지 알아보세요.

힌트

태그와 종속 항목을 확인하여 유용한 정보를 확인하세요.

답변

이 리뷰에서는 Pizza에 대해 설명하고 Amir는 리뷰 작성자입니다. bazel query --noimplicit\_deps 'deps(//src/main/java/com/example/reviews:review)'를 사용하여 이 규칙에 적용된 종속 항목을 살펴보면 Amir가 리뷰어임을 알 수 있습니다. 다음으로 리뷰 작성자가 Amir임을 알고 있으므로 쿼리 함수를 사용하여 `mir` 파일에 어떤 요리가 있는지 살펴보고 어떤 요리가 있는지 확인합니다. 명령어 bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)'은 아미르가 피자를 주문한 유일한 사용자이며 답변을 제공하는 리뷰어라는 것을 출력합니다.

요약

축하합니다. 이제 몇 가지 기본 쿼리를 실행하여 자체 프로젝트에서 사용해 볼 수 있습니다. 쿼리 언어 구문에 대한 자세한 내용은 쿼리 참조 페이지를 참조하세요. 고급 쿼리를 사용해 보고 싶으신가요? 쿼리 가이드에는 이 가이드에서 다룬 것보다 더 많은 사용 사례가 자세히 나와 있습니다.