นักพัฒนาโต้เถียงระหว่าง Make กับระบบ Build สมัยใหม่ หลังคู่มือการคอมไพล์ C จุดประกายการอภิปราย

ทีมบรรณาธิการ BigGo
นักพัฒนาโต้เถียงระหว่าง Make กับระบบ Build สมัยใหม่ หลังคู่มือการคอมไพล์ C จุดประกายการอภิปราย

คู่มือเกี่ยวกับการคอมไพล์โปรแกรม 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 ที่วางไว้ก่อนไฟล์ object
  • LDLIBS: แฟล็กของไลบรารี (เช่น -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 container
  • make docker_run: รัน Docker container
  • make 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)