คู่มือเกี่ยวกับการคอมไพล์โปรแกรม C โดยใช้ Make ได้จุดประกายการอภิปรายอย่างร้อนแรงในหมู่นักพัฒนาเกี่ยวกับระบบ build การจัดการ dependency และความเกี่ยวข้องที่ยังคงมีอยู่ของเครื่องมือที่มีอายุหลายทศวรรษในการพัฒนาซอฟต์แวร์สมัยใหม่
Environment Variables และ Compiler Flags สร้างความสับสน
ชุมชนได้เน้นย้ำถึงความสับสนอย่างมากเกี่ยวกับ environment variables ของ Make โดยเฉพาะ CPPFLAGS และ CXXFLAGS นักพัฒนาหลายคนเข้าใจผิดว่าตัวแปรเหล่านี้มีวัตถุประสงค์คล้ายกัน แต่จริงๆ แล้วแตกต่างกันมาก CPPFLAGS จัดการกับตัวเลือกของ C preprocessor โดยไม่คำนึงถึงภาษา ในขณะที่ CXXFLAGS มุ่งเป้าไปที่การคอมไพล์ C++ โดยเฉพาะ ความแตกต่างนี้มีความสำคัญมากเมื่อแก้ไขปัญหาการคอมไพล์ โดยเฉพาะบน macOS ที่เส้นทาง dependency มักต้องการการระบุด้วยตนเอง
การอภิปรายยังเผยให้เห็นว่าลำดับของ linker flag มีความสำคัญอย่างมากในสถานการณ์ static linking แม้ว่าข้อกำหนดนี้จะหายไปเมื่อใช้ dynamic linking ความซับซ้อนทางเทคนิคนี้ทำให้นักพัฒนาหลายคนประหลาดใจ นำไปสู่ความล้มเหลวในการ build ที่ลึกลับซึ่งดูเหมือนจะทำงานได้ในระบบบางระบบแต่ไม่ได้ผลในระบบอื่น
หมายเหตุ: C preprocessor (cpp) เป็นเครื่องมือที่ประมวลผลซอร์สโค้ดก่อนการคอมไพล์ จัดการงานต่างๆ เช่น การรวมไฟล์ header และการขยาย macro
ตัวแปรสภาพแวดล้อมสำคัญของ Make:
CPPFLAGS
: แฟล็กของ C preprocessor (สำหรับตำแหน่งของไฟล์ header)CXXFLAGS
: แฟล็กของคอมไพเลอร์ C++LDFLAGS
: แฟล็กของ Linker ที่วางไว้ก่อนไฟล์ objectLDLIBS
: แฟล็กของไลบรารี (เช่น -lssl -ldl) ที่วางไว้หลังไฟล์ object
Make ในฐานะ Interface สากลได้รับความนิยม
แนวโน้มที่น่าสนใจได้เกิดขึ้นโดยนักพัฒนาใช้ Make เป็น interface มาตรฐานข้ามภาษาโปรแกรมมิ่งและโปรเจกต์ต่างๆ แทนที่จะจำคำสั่ง build เฉพาะภาษาต่างๆ ทีมงานหลายทีมตอนนี้ใช้ Make targets ที่สอดคล้องกัน เช่น make format
, make lint
และ make build
ไม่ว่าจะทำงานกับ Go, Rust, Python หรือภาษาอื่นๆ
แนวทางนี้แก้ไขความหงุดหงิดทั่วไปที่โปรเจกต์ย้ายไปมาระหว่างระบบ build ที่แตกต่างกันตลอดเวลา ในขณะที่เครื่องมือพื้นฐานอาจเปลี่ยนจาง package manager หนึ่งไปยังอีกตัวหนึ่ง คำสั่ง Make ระดับสูงยังคงเหมือนเดิม ลดภาระทางปัญญาสำหรับนักพัฒนาที่สลับไปมาระหว่างโปรเจกต์
เป้าหมาย Make ทั่วไปในภาษาต่างๆ:
make format
: การจัดรูปแบบโค้ดmake lint
: การตรวจสอบ/วิเคราะห์โค้ดmake build
: สร้างโปรเจกต์make docker_build
: สร้าง Docker containermake docker_run
: รัน Docker containermake install_deps
: ติดตั้ง dependencies
การจัดการ Dependency ยังคงเป็นจุดอ่อนของ C
การอภิปรายของชุมชนได้เสริมความเข้าใจว่าการขาด dependency manager ในตัวของ C ยังคงเป็นจุดเจ็บปวดหลัก ในขณะที่บางคนโต้แย้งว่า system package managers เช่น apt หรือ homebrew ทำหน้าที่นี้ คนอื่นๆ ชี้ไปที่โซลูชันใหม่ๆ เช่น Conan และ vcpkg เป็นทางเลือกที่กำลังเกิดขึ้น
อย่างไรก็ตาม นักพัฒนา C ที่มีประสบการณ์หลายคนมองว่านี่เป็นฟีเจอร์มากกว่าข้อบกพร่อง โปรเจกต์ที่ซับซ้อนมักเกี่ยวข้องกับภาษาโปรแกรมมิ่งหลายภาษา ทำให้ dependency managers เฉพาะภาษาอาจเป็นอันตรายต่อกระบวนการ build โดยรวม แนวทางการจัดการ dependency ด้วยตนเอง แม้ว่าจะต้องใช้งานมากขึ้นในตอนแรก แต่ให้การควบคุมและความโปร่งใสมากกว่า
โซลูชันการจัดการ Dependency ของ C:
- System Package Managers: apt, homebrew, dnf, pacman
- เครื่องมือเฉพาะสำหรับ C: Conan, vcpkg
- การจัดการแบบ Manual: วิธีการแบบดั้งเดิมด้วยการติดตั้ง library ด้วยตนเอง
- Containerized Builds: การรวม dependency แบบ Docker-based
ระบบ Build ทางเลือกท้าทายการครอบงำของ Make
การอภิปรายได้เผยให้เห็นความสนใจที่เพิ่มขึ้นในทางเลือกอื่นนอกเหนือจาก Make แบบดั้งเดิม นักพัฒนาบางคนสนับสนุนระบบ mk ของ Plan 9 ชื่นชมไวยากรณ์ที่สะอาดกว่าและตัวแปรอัตโนมัติที่บรรยายได้ดีกว่า คนอื่นๆ แนะนำว่าเครื่องมือสมัยใหม่เช่น CMake แม้จะมีความซับซ้อนของตัวเอง แต่ก็ให้การสนับสนุน cross-platform และการจัดการ dependency ที่ดีกว่า
ไม่มีใครรู้ว่ามันทำงานอย่างไร นั่นเป็นเหตุผลที่ทุก configure script ตรวจสอบหา fortran compiler ที่ทำงานได้
ความรู้สึกนี้เกี่ยวกับ autotools สะท้อนความหงุดหงิดในวงกว้างต่อระบบ build แบบเก่าที่สะสมความซับซ้อนและกรณีพิเศษมาหลายทศวรรษ
Docker เกิดขึ้นเป็นโซลูชันสำหรับ Build Environment
โซลูชันเชิงปฏิบัติที่ได้รับความนิยมเพิ่มขึ้นเกี่ยวข้องกับการใช้ Docker containers เพื่อให้ build environments ที่สอดคล้องกัน แนวทางนี้รวม compilers และ dependencies เข้าไปใน containers ที่ทำซ้ำได้ ขจัดปัญหา works on my machine ที่รบกวนการคอมไพล์ C ข้ามระบบต่างๆ
อย่างไรก็ตาม กลยุทธ์นี้เผชิญกับข้อจำกัดเกี่ยวกับ static linking โดยเฉพาะในระบบที่ใช้ glibc ซึ่งไม่สนับสนุน static compilation อย่างเต็มที่ ข้อจำกัดทางเทคนิคนี้หมายความว่าการ build แบบ containerized ไม่ได้ผลิต binaries ที่พกพาได้อย่างแท้จริงเสมอไป
การโต้เถียงที่กำลังดำเนินอยู่สะท้อนความตึงเครียดในวงกว้างในการพัฒนาซอฟต์แวร์ระหว่างการรักษาความเข้ากันได้กับเครื่องมือที่จัดตั้งขึ้นแล้วและการยอมรับทางเลือกสมัยใหม่ที่สัญญาว่าจะให้ประสบการณ์นักพัฒนาที่ดีกว่า ในขณะที่ Make ใกล้จะครบรอบ 50 ปี ชุมชนยังคงแบ่งแยกว่าอายุยืนของมันแสดงถึงความน่าเชื่อถือที่พิสูจน์แล้วหรือหนี้ทางเทคนิคที่สะสมมา
อ้างอิง: Using make
to compile C programs (for non-C-programmers)