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

รายงานปัญหา ดูแหล่งที่มา รุ่น Nightly · 7.4 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
    │  └── WORKSPACE
    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── WORKSPACE
    └──stage3
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── lib
       │   ├── BUILD
       │   ├── hello-time.cc
       │   └── hello-time.h
       └── WORKSPACE

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

สรุป: บทนำ

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

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

ตั้งค่าพื้นที่ทำงาน

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

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

ในโปรเจ็กต์ในอนาคต หากต้องการกำหนดไดเรกทอรีเป็นเวิร์กสเปซ Bazel ให้สร้างไฟล์ว่างชื่อ WORKSPACE ในไดเรกทอรีนั้น บทแนะนํานี้จะถือว่ามีไฟล์ WORKSPACE อยู่ในแต่ละระยะอยู่แล้ว

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

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

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

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

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

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

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

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

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

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

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

เรียกใช้คำสั่งต่อไปนี้เพื่อย้ายไปยังไดเรกทอรี 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 ที่รูทของ Google Workspace ได้อย่างเต็มประสิทธิภาพ

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

bazel-bin/main/hello-world

การดำเนินการนี้ส่งผลให้ข้อความ "Hello world" แสดงในการพิมพ์

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

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

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

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

ระยะที่ 2: เป้าหมายการสร้างหลายรายการ

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

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

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

โปรดดูไฟล์ 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 rule ในตัวของ Bazel) แล้วตามด้วยไบนารี hello-world แอตทริบิวต์ deps ใน เป้าหมาย hello-world บอก Bazel ว่า hello-greet ต้องมีไลบรารีเพื่อสร้างไบนารี hello-world

คุณต้องเปลี่ยนไดเรกทอรีโดยเปลี่ยนไปใช้ไดเรกทอรี 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 จะทำคอมไพล์ซ้ำเท่านั้น ไฟล์นั้น

เมื่อดูกราฟทรัพยากร Dependency จะเห็นได้ว่า Hello-world อ้างอิง ป้อนข้อมูลเหมือนเดิม แต่โครงสร้างของบิลด์จะแตกต่างออกไป

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

สรุป: ขั้นที่ 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
   └── WORKSPACE

คุณจะเห็นได้ว่าตอนนี้จะมีไดเรกทอรีย่อย 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 มีดังนี้

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