บทแนะนำ Bazel: สร้างโปรเจ็กต์ C++

รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

บทนำ

หากเพิ่งเริ่มใช้ Bazel คุณมาถูกที่แล้ว ทำตามบทแนะนำการสร้างครั้งแรกนี้สำหรับ ข้อมูลเบื้องต้นเกี่ยวกับการใช้ Bazel บทแนะนำนี้จะให้นิยามคำศัพท์สำคัญดังต่อไปนี้ ในบริบทของ Bazel และแนะนำพื้นฐานต่างๆ ของ Bazel เริ่มจากเครื่องมือที่จำเป็น คุณจะต้องสร้างและเรียกใช้ โดยจะมีความซับซ้อนมากขึ้นเรื่อยๆ รวมถึงเรียนรู้วิธีและสาเหตุที่ทำให้ซับซ้อนมากขึ้น

แม้ว่า Bazel จะเป็นระบบการสร้างที่ สนับสนุนบิลด์หลายภาษา บทแนะนำนี้ใช้โปรเจ็กต์ C++ เป็นตัวอย่าง และแสดงหลักเกณฑ์และขั้นตอนทั่วไปที่ใช้กับภาษาส่วนใหญ่

เวลาที่ใช้ดำเนินการจนเสร็จสิ้นโดยประมาณ: 30 นาที

ข้อกำหนดเบื้องต้น

เริ่มต้นด้วยการติดตั้ง Bazel หากยังไม่ได้ทำ เรียบร้อยแล้ว บทแนะนำนี้ใช้ Git สำหรับการควบคุมแหล่งที่มา ดังนั้นเพื่อผลลัพธ์ที่ดีที่สุด โปรดติดตั้ง Git ด้วยเช่นกัน

ถัดไป ให้เรียกโปรเจ็กต์ตัวอย่างจากที่เก็บ GitHub ของ Bazel โดยการเรียกใช้ ต่อไปนี้ในเครื่องมือบรรทัดคำสั่งที่คุณเลือก

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

โปรเจ็กต์ตัวอย่างสำหรับบทแนะนำนี้อยู่ใน examples/cpp-tutorial ไดเรกทอรี

ลองดูโครงสร้างต่อไปนี้

examples
└── cpp-tutorial
    ├──stage1
    │  ├── main
    │  │   ├── BUILD
    │  │   └── hello-world.cc
    │  └── MODULE.bazel
    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── MODULE.bazel
    └──stage3
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── lib
       │   ├── BUILD
       │   ├── hello-time.cc
       │   └── hello-time.h
       └── MODULE.bazel

ไฟล์มีอยู่ 3 ชุด โดยแต่ละชุดจะแสดงขั้นตอนในบทแนะนำนี้ ในระยะแรก คุณจะต้องสร้างเป้าหมายเดียวที่อยู่ในแพ็กเกจเดียว ในขั้นตอนที่ 2 คุณจะ สร้างทั้งไบนารีและไลบรารีจากแพ็กเกจเดียว ในอินนิงที่ 3 และสุดท้าย คุณจะสร้างโปรเจ็กต์ที่มีแพ็กเกจหลายรายการ และสร้างด้วย หลายเป้าหมาย

สรุป: บทนำ

การติดตั้ง Bazel (และ Git) และโคลนที่เก็บสำหรับบทแนะนำนี้ทำให้คุณสามารถ ได้วางรากฐานในการสร้างสิ่งแรกกับ Bazel ไปยังรายการถัดไป เพื่อกำหนดคำศัพท์และตั้งค่า workspace

เริ่มต้นใช้งาน

คุณต้องตั้งค่าพื้นที่ทำงานของโปรเจ็กต์ก่อนจึงจะสร้างโปรเจ็กต์ได้ พื้นที่ทำงาน เป็นไดเรกทอรีที่ใช้เก็บไฟล์ต้นฉบับของโครงการและเอาต์พุตบิลด์ของ Bazel นอกจากนี้ยังมีไฟล์สำคัญเหล่านี้ด้วย

  • ไฟล์ MODULE.bazel ซึ่งระบุไดเรกทอรีและเนื้อหาในไดเรกทอรีเป็น พื้นที่ทำงานของ Bazel และอยู่ที่รูทของไดเรกทอรีของโปรเจ็กต์ ใหม่ นอกจากนี้ยังเป็นที่ที่คุณระบุทรัพยากร Dependency ภายนอกด้วย
  • BUILD อย่างน้อย 1 รายการ ซึ่งจะทำให้ Bazel ทราบ วิธีสร้างส่วนอื่นๆ ของโครงการ ไดเรกทอรีภายในแท็ก พื้นที่ทำงานที่มีไฟล์ BUILD คือ package (ข้อมูลเพิ่มเติมเกี่ยวกับแพ็กเกจ ต่อไปในบทแนะนำนี้)

ในโปรเจ็กต์ในอนาคต หากต้องการกำหนดไดเรกทอรีเป็นพื้นที่ทำงานของ Bazel ให้สร้าง ไฟล์ว่างชื่อ MODULE.bazel ในไดเรกทอรีนั้น เพื่อจุดประสงค์ของกรณีนี้ บทแนะนำ ไฟล์ MODULE.bazel มีอยู่แล้วในแต่ละขั้นตอน

ทำความเข้าใจไฟล์ BUILD

ไฟล์ BUILD มีวิธีการหลายประเภทสำหรับ Bazel ชิ้น BUILD ไฟล์ต้องมีอย่างน้อย 1 ไฟล์ กฎเป็นชุดคำสั่ง ซึ่งบอก Bazel ถึงวิธีสร้างเอาต์พุตที่คุณต้องการ เช่น ไบนารีที่ดำเนินการได้ หรือห้องสมุด อินสแตนซ์แต่ละรายการของกฎบิลด์ในไฟล์ BUILD จะเรียกว่า กำหนดเป้าหมายและชี้ไปยัง ชุดของไฟล์ต้นฉบับและ การพึ่งพากัน เป้าหมายสามารถ ชี้ไปที่เป้าหมายอื่นๆ ด้วย

ดูไฟล์ BUILD ในไดเรกทอรี cpp-tutorial/stage1/main:

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

ในตัวอย่าง เป้าหมาย hello-world จะสร้างอินสแตนซ์ในตัวของ Bazel cc_binary กฎ กฎ บอก Bazel ให้สร้างไบนารีที่ดำเนินการได้ในตัวเองจาก hello-world.cc> ไฟล์ต้นฉบับที่ไม่มีทรัพยากร Dependency

สรุป: การเริ่มต้นใช้งาน

ตอนนี้คุณก็คุ้นเคยกับคำศัพท์สำคัญ และความหมายของคำเหล่านั้นในบริบทของ โครงการนี้ และ Bazel ทั่วไป ในส่วนถัดไป คุณจะได้สร้างและทดสอบ ขั้นที่ 1 ของโครงการ

ขั้นที่ 1: เป้าหมายเดียว แพ็กเกจเดียว

ถึงเวลาสร้างส่วนแรกของโปรเจ็กต์แล้ว เพื่อเป็นข้อมูลอ้างอิง ของส่วนขั้นที่ 1 ของโครงการคือ

examples
└── cpp-tutorial
    └──stage1
       ├── main
       │   ├── BUILD
       │   └── hello-world.cc
       └── MODULE.bazel

เรียกใช้คำสั่งต่อไปนี้เพื่อย้ายไปยังไดเรกทอรี cpp-tutorial/stage1

cd cpp-tutorial/stage1

ต่อไป ให้เรียกใช้

bazel build //main:hello-world

ในป้ายกำกับเป้าหมาย ส่วน //main: คือตำแหน่งของไฟล์ BUILD สัมพัทธ์กับรูทของพื้นที่ทำงาน และ hello-world คือชื่อเป้าหมายใน ไฟล์ BUILD

Bazel ผลิตเนื้อหาที่มีลักษณะเช่นนี้

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.267s, Critical Path: 0.25s

คุณเพิ่งสร้างเป้าหมาย Bazel แรก Bazel วางเอาต์พุตของบิลด์ไว้ใน bazel-bin ที่รูทของพื้นที่ทำงาน

ตอนนี้ให้ทดสอบไบนารีที่สร้างใหม่ ซึ่งก็คือ

bazel-bin/main/hello-world

วิธีนี้ให้ผลลัพธ์เป็น "Hello world" ที่พิมพ์

กราฟการขึ้นต่อกันของระยะที่ 1 มีดังนี้

กราฟการขึ้นต่อกันสำหรับ Hello-world แสดงเป้าหมายเดียวที่มีแหล่งที่มาเดียว

สรุป: ขั้นที่ 1

ตอนนี้คุณก็ได้สร้างเสร็จแล้ว พอมีไอเดียพื้นฐานว่า การสร้างโครงสร้าง ในขั้นตอนถัดไป คุณจะต้องเพิ่มความซับซ้อนโดยการเพิ่ม เป้าหมายอื่น

ขั้นที่ 2: เป้าหมายบิลด์หลายรายการ

แม้ว่าเป้าหมายเดียวจะเพียงพอแล้วสำหรับโปรเจ็กต์ขนาดเล็ก แต่คุณอาจต้องการแยกเป้าหมาย โปรเจ็กต์ขนาดใหญ่ให้กลายเป็นหลายเป้าหมายและแพ็กเกจ วิธีนี้ทำให้ได้เร็ว สิ่งก่อสร้างที่เพิ่มขึ้นเรื่อยๆ กล่าวคือ Bazel สร้างเฉพาะสิ่งที่เปลี่ยนแปลงไปขึ้นมาใหม่เท่านั้น และเร็วขึ้น ด้วยการสร้างส่วนต่างๆ ของโปรเจ็กต์พร้อมกัน ขั้นตอนนี้ บทแนะนำจะเพิ่มเป้าหมาย และรายการถัดไปจะเพิ่มแพ็กเกจ

นี่คือไดเรกทอรีที่คุณกำลังใช้งานสำหรับระยะที่ 2:

    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── MODULE.bazel

ดูไฟล์ BUILD ในไดเรกทอรี cpp-tutorial/stage2/main:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)

ด้วยไฟล์ BUILD นี้ Bazel จะสร้างไลบรารี hello-greet ก่อน (โดยใช้ cc_library ในตัวของ Bazel ) จากนั้น ไบนารี hello-world แอตทริบิวต์ deps ในเป้าหมาย hello-world บอก Bazel ว่าต้องมีไลบรารี hello-greet เพื่อสร้าง hello-world 2.

ก่อนที่คุณจะสร้างโปรเจ็กต์เวอร์ชันใหม่นี้ได้ คุณต้องเปลี่ยน ไดเรกทอรี โดยเปลี่ยนเป็นไดเรกทอรี cpp-tutorial/stage2 ด้วยการเรียกใช้:

cd ../stage2

ตอนนี้คุณสร้างไบนารีใหม่ได้โดยใช้คำสั่งที่คุ้นเคยต่อไปนี้

bazel build //main:hello-world

Bazel ผลิตเนื้อหาที่มีลักษณะเช่นนี้อีกแล้ว

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s

ตอนนี้คุณทดสอบไบนารีที่สร้างใหม่ได้แล้ว ซึ่งจะแสดงผล "Hello world" อีกรายการหนึ่ง:

bazel-bin/main/hello-world

หากตอนนี้คุณแก้ไข hello-greet.cc และสร้างโปรเจ็กต์ใหม่ Bazel เท่านั้น คอมไพล์ไฟล์นั้นซ้ำ

เมื่อดูกราฟการขึ้นต่อกัน คุณจะเห็นว่า hello-world อ้างอิง อินพุตเพิ่มเติมชื่อ hello-greet:

กราฟการขึ้นต่อกันสำหรับ "hello-world" แสดงการเปลี่ยนแปลงทรัพยากร Dependency หลังจาก
แก้ไขไฟล์ได้

สรุป: ขั้นที่ 2

ตอนนี้คุณได้สร้างโปรเจ็กต์ที่มีเป้าหมาย 2 รายการแล้ว บิลด์ hello-world เป้าหมาย ไฟล์ต้นฉบับ 1 ไฟล์และขึ้นอยู่กับเป้าหมายอื่นอีก 1 รายการ (//main:hello-greet) ซึ่ง สร้างไฟล์ต้นฉบับเพิ่มเติมอีก 2 ไฟล์ ในส่วนถัดไป ให้ก้าวไปอีกขั้น และเพิ่มแพ็กเกจอื่น

ขั้นที่ 3: หลายแพ็กเกจ

ขั้นตอนถัดไปนี้จะเพิ่มข้อมูลแทรกอีกชั้นหนึ่งและสร้างโปรเจ็กต์ที่มี หลายแพ็กเกจ ลองดูโครงสร้างและเนื้อหาของ ไดเรกทอรี cpp-tutorial/stage3:

└──stage3
   ├── main
   │   ├── BUILD
   │   ├── hello-world.cc
   │   ├── hello-greet.cc
   │   └── hello-greet.h
   ├── lib
   │   ├── BUILD
   │   ├── hello-time.cc
   │   └── hello-time.h
   └── MODULE.bazel

คุณจะเห็นได้ว่าตอนนี้จะมีไดเรกทอรีย่อย 2 รายการ และแต่ละไดเรกทอรีย่อยมี BUILD ดังนั้น สำหรับ Bazel แล้ว พื้นที่ทำงานจะมี 2 แพ็กเกจ ได้แก่ lib และ main

ดูไฟล์ lib/BUILD:

cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["//main:__pkg__"],
)

และในไฟล์ main/BUILD

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
        "//lib:hello-time",
    ],
)

เป้าหมาย hello-world ในแพ็กเกจหลักขึ้นอยู่กับเป้าหมาย hello-time ในแพ็กเกจ lib (ดังนั้นป้ายกำกับเป้าหมาย //lib:hello-time) - Bazel ทราบว่า ผ่านแอตทริบิวต์ deps คุณจะเห็นข้อมูลนี้ในทรัพยากร Dependency กราฟ

กราฟการขึ้นต่อกันของ "hello-world" แสดงวิธีที่เป้าหมายในแพ็กเกจหลัก
ขึ้นอยู่กับเป้าหมายใน "lib"
ใหม่

คุณกำหนดเป้าหมาย //lib:hello-time ใน lib/BUILD เพื่อให้บิลด์ประสบความสำเร็จ เป้าหมายใน main/BUILD มองเห็นได้อย่างชัดเจนโดยใช้แอตทริบิวต์การเปิดเผย เนื่องจากโดยค่าเริ่มต้น เป้าหมายเดียวกันจะปรากฏต่อเป้าหมายอื่นๆ ในกลุ่มเดียวกันเท่านั้น BUILD ไฟล์ Bazel ใช้ระดับการเข้าถึงเป้าหมายเพื่อป้องกันปัญหาต่างๆ เช่น ไลบรารี มีรายละเอียดการใช้งานที่รั่วไหลใน API สาธารณะ

จากนั้นสร้างโปรเจ็กต์เวอร์ชันสุดท้ายนี้ เปลี่ยนไปใช้ cpp-tutorial/stage3 ไดเรกทอรีโดยการเรียกใช้:

cd  ../stage3

เรียกใช้คำสั่งต่อไปนี้อีกครั้ง

bazel build //main:hello-world

Bazel ผลิตเนื้อหาที่มีลักษณะเช่นนี้

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s

ตอนนี้ให้ทดสอบไบนารีสุดท้ายของบทแนะนำนี้สำหรับข้อความ Hello world สุดท้าย:

bazel-bin/main/hello-world

สรุป: ระยะที่ 3

ตอนนี้คุณได้สร้างโปรเจ็กต์ที่มี 2 แพ็กเกจที่มี 3 เป้าหมายแล้ว และทำความเข้าใจ ทรัพยากร Dependency ต่างๆ ซึ่งจะช่วยให้คุณออกเดินทางและสร้างอนาคตได้ กับ Bazel ในส่วนถัดไป โปรดดูวิธีต่อ การเดินทางของ Bazel

ขั้นตอนถัดไป

ตอนนี้คุณได้สร้างงานพื้นฐานแรกกับ Bazel เสร็จแล้ว แต่นี่เป็น เริ่ม แหล่งข้อมูลเพิ่มเติมเพื่อการเรียนรู้กับ Bazel ต่อไปมีดังนี้

ขอให้สนุกกับการสร้าง