ฉันชอบ Makefile
(switowski.com)- ถึงแม้จะมีเครื่องมือ build ใหม่ ๆ ออกมาอย่างต่อเนื่อง Makefile ก็ยังคงมีประโยชน์ในฐานะ เครื่องมือพื้นฐานแบบเบา ๆ สำหรับรวมงานที่ต้องทำซ้ำในโปรเจ็กต์
- ด้วย คำสั่งตามธรรมเนียม อย่าง
make,make build,make install,make devจึงสามารถลอง build, ติดตั้ง และรันเซิร์ฟเวอร์พัฒนาของโปรเจ็กต์ที่ไม่คุ้นเคยได้อย่างรวดเร็ว - ไม่ว่าสแตกจะเป็น Jekyll, Hugo, 11ty, Python web framework, Docker หรือ gulp, Makefile ก็ทำหน้าที่เป็น อินเทอร์เฟซการรันร่วมกัน ได้
- ในโปรเจ็กต์ส่วนตัว หลายครั้งก็เพียงพอแล้วที่จะมี target ที่เรียบง่าย ไม่กี่ตัว เช่น
dev,build,deployแทนการใช้เงื่อนไขหรือแฟลก - GNU Make แพร่หลายอยู่แล้ว และถึงไม่มี ก็ยังสามารถรันคำสั่งเชลล์ใน Makefile ได้โดยตรง จึงมีภาระน้อยแม้ใน สภาพแวดล้อมที่มีข้อจำกัด
รวมคำสั่งที่ต่างกันในแต่ละโปรเจ็กต์ไว้ที่จุดเข้าใช้งานเดียว
- Makefile ถูกใช้เพื่อห่อโปรเจ็กต์ที่มีวิธี build, รัน, และ deploy ต่างกัน ให้ใช้งานผ่าน ชุดคำสั่งที่คุ้นเคย
- ถ้าโปรเจ็กต์ที่ไม่เคยเห็นมาก่อนมี
Makefileก็มักจะเริ่มลองจากคำสั่งต่อไปนี้ได้makemake buildmake install
- แม้จะกลับมาเปิดโปรเจ็กต์ส่วนตัวเก่าอีกครั้ง ก็สามารถตั้งค่าให้แค่รัน
make devแล้วจัดการทั้งขั้นตอน build ที่จำเป็นและการเปิด development server ได้ในครั้งเดียว - แม้เทคโนโลยีสแตกจะต่างกัน เช่น Jekyll, Hugo, 11ty หรือ Python web framework หลายแบบ ก็ยังคงใช้จุดเข้าใช้งานเดียวกันได้
- แม้ในกรณีที่ใช้ Docker หรือ gulp, Makefile ก็ไม่ได้มาแทนเครื่องมือเหล่านั้น แต่ทำงานเป็น ชั้น orchestration
- ตัวอย่าง:
make buildสามารถ build Docker image ที่จำเป็นพร้อมส่งอาร์กิวเมนต์เพิ่มเติมเฉพาะโปรเจ็กต์ได้
- ตัวอย่าง:
วิธีใช้งานแบบเรียบง่ายในโปรเจ็กต์ส่วนตัว
- การใช้ Makefile จะเน้นที่ การรวมคำสั่งเชลล์ มากกว่าฟีเจอร์ที่ซับซ้อน
- ไม่ใช้ฟีเจอร์อย่างเงื่อนไขหรือแฟลก
- หน่วยงานของ make ในทางเทคนิคคือ target แต่ในการใช้งานจริงจะมองเหมือน task
- แม้จะทำเป็นฟังก์ชันใน bash script ได้ แต่ Makefile ถูกมองว่าง่ายและเร็วกว่า
- งานที่ใช้บ่อยในโปรเจ็กต์ส่วนตัวโดยทั่วไปมีเพียง 2–3 อย่าง
dev: เริ่ม development serverbuild: build โปรเจ็กต์เมื่อจำเป็นdeploy: deploy หรือเผยแพร่โปรเจ็กต์watch: งานเสริมสำหรับรันงาน build ซ้ำอัตโนมัติเมื่อไฟล์ซอร์สมีการเปลี่ยนแปลง
- Makefile ของบล็อกปัจจุบันมีเพียง target
devตัวเดียว- ใต้
dev:จะรันnpm run dev
- ใต้
- ในโปรเจ็กต์ส่วนตัวที่ซับซ้อนกว่า Makefile จะรวมงานต่อไปนี้ไว้
dev: รัน development server ด้วยbundle exec jekyll servebuild: build asset ด้วยnpm run gulp buildwatch: เฝ้าดูโฟลเดอร์ที่กำหนดด้วยnpm run gulp watchdeploy: หลังจาก build Jekyll ในสภาพแวดล้อม production แล้ว รันmake encrypt,netlify deploy --prodencrypt: เข้ารหัสไฟล์ HTML ในโฟลเดอร์_siteด้วยnpx staticrypt
ข้อจำกัดและข้อดีที่ควรรู้
- ในตัวอย่างนี้ละ phony targets ไว้ แต่ถ้ามีไฟล์ชื่อ
dev,build,watch,deploy,encryptอยู่ Makefile อาจไม่ทำงานตามที่คาดไว้- ผู้อ่าน Hacker News แนะนำให้เพิ่ม phony target ในกรณีแบบนี้
- GNU Make คือซอฟต์แวร์ที่ใช้รัน makefile ซึ่งมีโอกาสติดตั้งอยู่แล้วบน Linux และก็แพร่หลายบน MacBook มากจนแทบจำไม่ได้ว่าต้องติดตั้งเพิ่มต่างหาก
- Make ไม่ต้องพึ่งพา dependency เพิ่มเติมมากนัก จึงอาจมีประโยชน์ใน สภาพแวดล้อมที่มีข้อจำกัด ซึ่งการติดตั้งแพ็กเกจอาจทำได้ยากหรือทำไม่ได้เลยด้วยเหตุผลด้านความปลอดภัย
- ถึงไม่มี Make ก็ยังสามารถคัดลอกคำสั่งใน Makefile ไปวางรันในเชลล์ได้โดยตรง
- ในทางกลับกัน ถ้า gulp ไม่มีอยู่บนเซิร์ฟเวอร์ ก็ยากที่จะคัดลอกโค้ด JavaScript ไปวางรันในเทอร์มินัลได้ตรง ๆ
- ต่อให้พบเครื่องมือ build รุ่นใหม่ที่ดีกว่าและเร็วกว่า Make ก็ยังสามารถถูกใช้ต่อไปในฐานะ ชั้นคำสั่งร่วมกัน สำหรับจัดการเครื่องมือและการตั้งค่าที่แตกต่างกัน
2 ความคิดเห็น
หากเป็น makefile ที่ไม่ได้กำหนดการพึ่งพาไว้ การแทนที่ด้วย justfile จะให้ประสบการณ์การใช้งานที่ดีกว่า
ความคิดเห็นจาก Hacker News
ไม่จำเป็นต้องท้อใจกับคำพูดในเธรดนี้ที่ว่า “คุณกำลังใช้ make ผิดวิธี” หนึ่งในเหตุผลที่ Make เป็นเครื่องมือที่ดีก็คือ มันเริ่มต้นได้ง่ายกว่าที่เห็น
ถ้าไม่ใช้
.PHONYก็อาจเกิดปัญหาได้ แต่สำหรับโปรเจกต์เล็ก ๆ มันอาจเป็นกับดักที่คุณจะเหยียบในอีก 1 ปีให้หลัง และถึงตอนนั้นก็น่าจะจบได้ด้วยการเกาหัวคิดสักชั่วโมง ใน 99% ของกรณี คุณเริ่มได้ทันทีโดยไม่ต้องสนใจ “วิธีที่ถูกต้องตามตำรา” และ Make ต้องการความซับซ้อนแค่พอไม่ให้พังเท่านั้นMake ทำหน้าที่รับไฟล์อินพุตและ dependency แล้วสร้าง ไฟล์เอาต์พุตเพียงไฟล์เดียวเท่านั้น กฎที่ไม่สร้างเอาต์พุตอย่าง
install,all,cleanหรือ target ทั้งหมดในบทความ ต้องพึ่ง target เวทมนตร์แบบพิเศษอย่าง.PHONYแต่.PHONYไม่มีอยู่ใน POSIX ปี 2017 (IEEE Std 1003.1-2017, https://pubs.opengroup.org/onlinepubs/9699919799/utilities/m...) และเพิ่งถูกใส่เข้ามาในมาตรฐานปัจจุบัน (IEEE Std 1003.1-2024, https://pubs.opengroup.org/onlinepubs/9799919799/utilities/m...) ถ้าต้องการสร้างหลายไฟล์ เช่น object file, module หรือ precompiled header ก็ต้องเขียนกลเม็ดที่เปราะบางเอง ไม่ใช่ Make ทุกตัวจะเป็น GNU Make และสาย nix อย่าง BSD, Solaris/Illumos ก็ยังมีอยู่ไม่ได้หมายความว่า Make ไร้ประโยชน์ มันมีประโยชน์กับโปรเจกต์ที่ซับซ้อนพอสมควร แต่ยังไม่ถึงขั้นต้องใช้ระบบ build ที่ “ดีกว่า” ปัญหาคือโปรเจกต์แบบนั้นจะซับซ้อนขึ้นเมื่อโค้ดเพิ่มขึ้น และสุดท้ายก็จะมีสคริปต์/โปรแกรมที่สร้าง Makefile หรือบางส่วนของมัน จนกลายเป็นระบบ meta build แบบเฉพาะกิจ
ปัญหาที่ใหญ่กว่านั้นไม่ใช่การใช้มัน แต่เป็นการเสนอให้เป็นคำตอบสำหรับ “ทุกคน” และทันทีที่มีไดเรกทอรีหรือไฟล์ชื่อ
buildหรือdevเกิดขึ้นที่ root ของโปรเจกต์ Makefile นั้นก็จะหยุดทำงานช่วงนี้ถึงจะเปลี่ยนไปใช้ pnpm แล้ว แต่ก็ยังไม่มองว่ามันซับซ้อนมากนัก แม้ต้องรู้ไวยากรณ์อยู่บ้าง แต่ก็ยังยากน้อยกว่าการทำให้เครื่องมืออื่น ๆ ทำงานได้ถูกต้องตั้งแต่แรก
.PHONYที่ผู้คนกังวลกันนักหนาเลยมันเรียบง่าย อ่านง่าย แก้ไขได้ ประกอบเข้ากับอย่างอื่นได้ และติดตั้งอยู่แล้วแทบทุกที่ มันทำสิ่งที่เขียนไว้บนผิวหน้า และแทบไม่ทำอะไรเกินกว่านั้น
อนึ่ง เวลาต้องรับมือกับระบบ build ตามกระแสหรือระบบ build ชวนฝันร้ายในโปรเจกต์อื่น ๆ ก็ใช้ make ครอบมันอีกที
ทุกคนต้องเริ่มจากที่ไหนสักแห่ง และเมื่อมันเริ่มทำงานได้แล้ว การปรับปรุงทีละขั้นก็เพิ่มเข้าไปได้ง่าย
$@ส่วน Makefile ทั้งไฟล์สามารถสร้างเอาต์พุตจำนวนเท่าใดก็ได้ เพราะกฎหนึ่งสามารถพึ่งพากฎอื่นได้จากตรงนี้จะได้หลักจำง่าย ๆ สำหรับ phony target ถ้า recipe ไม่แตะต้องแค่
$@ก็ควรทำเครื่องหมาย$@เป็น phony การจัดการ phony target เป็นรายการจะง่ายกว่ามากphonies :=phonies += somethingsomething:./do-somethingphonies += something-elsesomething-else: something./do-something-else# touches $@ and thus does not need to be phonycreate-file:./generate-some-output > $@.PHONY: $(phonies)Makefile เป็นเทคโนโลยีที่แย่มาก เพียงแต่มันแย่น้อยกว่าระบบ build อื่น ๆ ส่วนใหญ่ที่เราสร้างขึ้นมานิดหน่อย จึงกลายเป็นว่า มีประโยชน์ในเชิงทรมานตัวเอง
ระบบ build มักจะพื้นฐานเกินไปจนพอ build อะไรที่มากกว่าของเล่นก็กลายเป็นความวุ่นวาย หรือซับซ้อนเกินไปจนต้องมีความรู้ล่วงหน้า ขั้นตอนราชการ การซิงก์ และ boilerplate มากเกินไป หรือไม่มี standard library ทำให้ build pattern เดียวกันกระจัดกระจายเป็น implementation ที่เข้ากันไม่ได้เป็นหมื่นแบบ หรือมีข้อจำกัดมากเกินไปจนพอความต้องการเพิ่มขึ้นก็ต้องย้ายระบบ หรือมีเวทมนตร์มากเกินไป หรือ syntax เข้าใจยากและไม่สม่ำเสมอ
ซอฟต์แวร์มากกว่าครึ่งที่ผมใช้ใช้แค่ Makefile ธรรมดา ๆ และบางครั้งก็มีสคริปต์ configure เท่านั้น ไม่ใช้ autotools แค่รัน
./configureแบบเลือกได้ แล้วmake,make installก็ทำงานได้เลย นี่ไม่ใช่การตั้งค่าแบบของเล่นเลย และประกอบขึ้นจากโปรแกรมเล็ก ๆ ที่ทำงานเดียวได้ดีMake และวิธีที่มันทำงานนั้น อย่างน้อยสำหรับผม เข้าใจง่าย ผมมองว่าประสบการณ์แย่ ๆ ที่ผู้คนเจอมักเกิดจากการตั้งค่า build ที่แย่ของโปรเจกต์ซับซ้อน หรือการขาดความรู้ที่จำเป็น มากกว่าจะเป็นตัว Make เอง
การไม่มี standard library เป็นการตั้งใจให้ ไม่ขึ้นกับเครื่องมือ และเพราะอย่างนั้นมันจึงใช้ได้ทั่วไปกับเครื่องมือ ภาษา และวัตถุประสงค์หลากหลาย ผมมองว่าไม่ใช่ข้อบกพร่อง แต่เป็นฟีเจอร์ ชั้นที่เรียบง่ายคือ dependency tracking และ target กับ dependency ก็ชัดเจน จึงยากจะมองว่าเป็นเวทมนตร์ ส่วน syntax นั้นแน่นอนว่ายังปรับปรุงให้ดีขึ้นได้
ระบบ build อเนกประสงค์ที่ครอบคลุมหลายภาษาได้มีแค่ระบบที่ target ไปที่
makeอย่างmakeหรือ CMake เท่านั้น เหตุผลที่มันไม่ค่อยดีคือเพราะmakeไม่ค่อยดีmakeใช้ให้ถูกจริง ๆ ยากมาก มีปัญหาอย่าง recursive make กับ non-recursive make มี variant ที่เข้ากันไม่ได้มากมาย เช่น Unix/POSIX make, BSD make, GNU make, Windowsnmakeและก็ค่อนข้างน่าเกลียดถึงอย่างนั้น
makeที่ใช้ถูกต้องก็ค่อนข้างดี การที่มีmakeเป็น ตัวหารร่วมต่ำสุด นับว่าโชคดีจริง ๆมันค่อนข้างน่าเหลือเชื่อที่ Make จัดการแม้แต่ subdirectory ให้เรียบร้อยไม่ได้ด้วยซ้ำ สงสัยเหมือนกันว่ามีอะไรที่ดีกว่า Make ไหม Ninja ก็มีอยู่ แต่ไม่ได้ออกแบบมาให้คนเขียนใช้โดยตรง
คิดว่าอยู่ระดับไหน? สัก 20%, 40%, 70% ก็ใช่ไม่ใช่หรือ
ผมชอบ Make มากจริง ๆ ตลอด 20 ปีที่ผ่านมา งานหรือโปรเจกต์ทุกอย่างที่ได้แตะต้อง ผมเขียนไว้อย่างน้อยหนึ่งไฟล์เสมอ
ไม่มีอะไรฉลาดล้ำ เป็นแค่การรวบรวมชิ้นส่วนที่มีตัวแปรไม่กี่ตัวเท่านั้น เช่น
make run,make test,make lintmake recentจะ lint แล้วรันสคริปต์ที่แก้ไขล่าสุด จะทำด้วย Bash หรือ shell อื่นก็ได้ แต่ถ้าทำแบบนั้นก็จะถูกดูดเข้าไปในโลกของนักพัฒนา เพิ่มแต่ความซับซ้อนโดยไม่มีคุณค่าเพิ่ม Make เป็นเพียง ภาษาเฉพาะโดเมน ที่บอกว่า “ไฟล์แบบนี้สร้างจากไฟล์แบบนี้ และตอนนั้นให้รันคำสั่งหนึ่งหรือสองคำสั่งนี้” เท่านั้น เพราะอย่างนั้นมันจึงทรงพลังมากหลายสิบปีก่อนเคยสร้าง test framework ด้วย Java โดยให้ระบุ test และ class ที่พึ่งพาได้ด้วย syntax คล้าย make มีชุดของ test class ที่นิยาม “ชุดทดสอบมาตรฐาน” แล้วชั้นของ test class อื่น ๆ ที่พึ่งพาชุดนั้นจะรันได้ก็ต่อเมื่อส่วนหน้าสำเร็จก่อน
ทุกวันนี้ไม่เข้าใจว่าทำไมต้องทำทุกอย่างให้ซับซ้อนขนาดนั้น ผมมักแนะนำเสมอให้ยึดกับเครื่องมือ Unix มาตรฐานและวิธีของมัน นั่นคือวิธีที่ผ่านการพิสูจน์ด้วยเวลา เว้นแต่จะเป็นสถานการณ์ที่ทำแบบนั้นไม่ได้จริง ๆ เวลาเรามีจำกัด จึงอยากใช้ไปกับแก่นของการแก้ปัญหาอย่างการออกแบบระบบ/โปรแกรม การทำโมเดล โครงสร้าง pattern ฯลฯ ส่วนที่เหลือเป็นเรื่องรอง
การรู้ว่าไม่ว่า stack, ภาษา, หรือ repository จะเป็นอะไร แค่รัน
make testแล้วมันทำงานได้ เป็นเรื่องที่ช่วยชีวิตมากMakefile ในโปรเจกต์ก่อนมีคำสั่งหลักสี่อย่างคือ
build,test,frontend,deployโดย build กับ test เรียก maven, frontend เรียก npm, deploy เรียก docker กับ awsเครื่องมือเหล่านี้ล้วนมีการติดตามสถานะ caching และ incremental processing ของตัวเอง แต่ไม่รายงานว่าทำอะไรไปบ้าง ดังนั้นจึงเขียนกฎระดับโมเลกุลอย่าง “deploy เฉพาะเมื่อ build ถูกอัปเดตแล้ว” ไม่ได้ เพราะ maven/cargo/dotnet/npm/go ไม่เปิดเผยข้อมูลนั้น
ผู้เขียนไม่ได้ใช้แม้แต่ การติดตาม dependency ตาม mtime ด้วยซ้ำ target ต่าง ๆ ก็ควรเป็น
.PHONYแต่ไม่ได้ระบุไว้ สามารถแทนที่ด้วยเชลล์สคริปต์ที่อ่าน$1แล้ว match เพื่อกำหนดว่าจะทำอะไรได้เหมือนกันhttps://just.systems/
.PHONY: $(MAKECMDGOALS)./build.sh devอาจชัดเจนน้อยกว่าmake devก็จริง อีกเหตุผลหนึ่งที่ใช้ Make แม้จะไม่มีขั้นตอนที่ไม่ใช่ phony เลย คือภายหลังถ้าจำเป็นก็สามารถเพิ่มขั้นตอนแบบนั้นได้ แน่นอนว่าผมเห็นด้วยว่าผู้เขียนควรระบุ{dev,build,deploy}ให้เป็น phonyMakefile ของตัวเองอาจปรับปรุงในเชิงเทคนิคได้ แต่สิ่งที่สำคัญกว่าคือประสบการณ์ “เริ่มใช้งาน” เมื่อเข้าไปในโปรเจกต์นั้นมีความสม่ำเสมอ
ผู้คนอยากให้มันทำอย่างอื่นอีกมากมาย เช่นเป็นภาษา scripting, ตัวติดตาม dependency ฯลฯ แล้วก็ดึงเครื่องมืออ้วนเทอะทะเข้ามา ของใหม่ไม่ได้แปลว่าดีกว่าเสมอไป Autoconf กับ automake ไม่ใช่ make
ในเชิงเทคนิค target ของ make เหล่านี้ทั้งหมดจะมองหาไฟล์ที่มีชื่อเดียวกับ target แต่ละ target จึงควรถูกนิยามเป็น
.PHONYจริง ๆเมื่อก่อนผมก็ใช้ Makefile แบบนี้อยู่เสมอ แต่ช่วงไม่กี่ปีหลังย้ายไปใช้ just และ justfile แล้ว พฤติกรรมแบบนี้เป็นค่าเริ่มต้น และโดยรวมใช้ง่ายกว่า สิ่งอย่างพารามิเตอร์ก็ง่ายกว่าด้วย
https://github.com/casey/just
:อะไรทำนองนี้ เพื่อให้แยกได้จากชื่อเลยแน่นอนว่าตอนนี้สายไปแล้ว
ผมคิดว่าถึงเวลาแล้วที่จะมีดิสโทร coreutils แบบใหม่ที่รวมเครื่องมือใหม่ ๆ ที่มีประโยชน์อย่าง just, ripgrep ไว้ด้วยกัน แล้วติดตั้งได้ทุกที่ที่อยากใช้
น่าสนใจที่ make ก่อให้เกิดการถกเถียงรุนแรงได้ขนาดนี้ คล้ายสงครามกึ่งศาสนาในอดีตอย่าง vi ปะทะ emacs
ผมเห็นด้วยกับต้นฉบับเต็มที่ และโดยเฉพาะมองว่าวิธีห่อ build tool ที่ซับซ้อนกว่าด้วย Makefile ระดับบนสุดเป็นแนวทางที่ฉลาด ข้อดีคือ การทำให้เป็นมาตรฐาน ไม่ต้องจำอะไรเลย ถ้าเป็นโปรเจกต์ที่ผมเขียนเอง ก็รู้ได้ว่าแค่พิมพ์
makeแล้วมันจะทำงานเช่น คนที่ใช้ C เมื่อจะคอมไพล์โปรเจกต์ Rust ก็ไม่ต้องไปค้นวิธีใช้ cargo แค่พิมพ์
makeหรือgmakeก็พอ ผมพยายามไม่ใช้ฟีเจอร์เฉพาะ GNU และให้สอดคล้องกับ POSIX แต่ก็จริงเหมือนกันว่า make ที่ใช้กันจริงแทบ 100% คือ gmakeข้อเสนอให้ใช้
makeอันเหนือกาลเวลาเป็นตัวขับ build system ระดับบนสุดแบบหนึ่ง น่าจะยังใช้ได้แม้อีก 250 ปีข้างหน้าcargo buildกันอยู่แล้วตัว Make เองยอดเยี่ยม แต่โปรเจกต์ C ส่วนใหญ่ที่ผมเคยทำงานด้วยคือการผสม cmake/autotools กับการติดตั้งแพ็กเกจแบบ global และเรื่องพวกนั้นต้องค้นดูบ่อย ๆ
ผมเห็นด้วยกับความรู้สึกในนี้ แต่กำลังเขียนหลาย ๆ อย่างใหม่เป็น Justfile
https://github.com/casey/just
ช่วยหลีกเลี่ยงความแปลกเฉพาะตัวของ Makefile ได้เยอะ
ใช่ Make ยอดเยี่ยมมาก ผมใช้กับงานสารพัดจริง ๆ เป็นแนวทางที่ดีสำหรับการทำงานอัตโนมัติ
ตัวอย่างเช่นเว็บไซต์ส่วนตัวของผมใช้ Makefile เรียก bash script เพื่อสร้างหน้าเว็บที่อัปเดตใหม่ จากนั้น git push ไปยังเซิร์ฟเวอร์ แล้ว git hook บนเซิร์ฟเวอร์ก็เรียก Make เพื่อ deploy อย่างไรก็ตาม ผมไม่อยากเอาก้อน binary ที่เปลี่ยนบ่อยอย่าง PDF เอกสารประกอบการสอนไปใส่ใน Git repository ไม่เป็นไร ใน Makefile มี target
uploadsที่อัปโหลดเฉพาะ PDF ที่แก้ไขแล้วขึ้นเซิร์ฟเวอร์ และ target นี้เป็น dependency ของ targetdeployที่ทำ git push ดังนั้นไม่ต้องคอยสนใจอีกอย่าง PDF ที่อัปเดตสำหรับเอกสารประกอบการสอนก็ถูก Makefile อีกตัวใส่เข้าไปใน source tree ของเว็บไซต์ให้อัตโนมัติ ด้วย Makefile ตัวนั้น ผมสามารถสร้าง PDF ที่ใช้จากซอร์ส LaTeX หรือสร้างเวอร์ชันทางเลือกสำหรับนักศึกษาจากซอร์สเดียวกัน เช่น เวอร์ชันที่ไม่มีเฉลยแบบฝึกหัด แล้วเผยแพร่ลงเว็บไซต์เวอร์ชัน local อัตโนมัติ จากนั้นเมื่ออยากก็อัปโหลดไปยังเว็บไซต์ที่อัปเดตแล้วได้
เป็นโครงสร้างแบบ Makefile ต่อกันไม่รู้จบ นั่นแหละ ผมชอบ Makefile
ในกรณีแบบนี้ สิ่งที่ผู้เขียนต้องการจริง ๆ ไม่ใช่ make แต่เป็น just
https://just.systems/man/en/
ในฐานะ ตัวรันงาน make ก็ไม่ได้แย่ แต่สมัยนี้ก็มีทางเลือกที่ดีกว่าอย่าง just ตามที่คอมเมนต์อื่น ๆ พูดกัน
ในฐานะระบบบิลด์ make ก็ใช้ได้อยู่จนกว่าจะไปเจอข้อเสียของมัน make/Makefile ไม่ได้ถูกทำให้เป็นมาตรฐาน จึงมี automake ขึ้นมา ดังนั้นจึงไม่ได้เขียน Makefile โดยตรง แต่เขียนเทมเพลตแล้วให้สร้าง makefile จริงออกมา ถ้าคุณเป็นเจ้าของ toolchain ทั้งหมดเองก็ไม่เป็นไร แต่ส่วนใหญ่ไม่ใช่แบบนั้น จึงทำแบบนี้เพื่อรับประกัน Makefile ที่พกพาได้
make ไม่สามารถ resolve dependency ในรูปแบบใด ๆ ได้ และสมมติว่าสิ่งที่ต้องใช้มีอยู่ตรงนั้นเลย ดังนั้นจึงเกิดสคริปต์ configure ขึ้นมา และเพราะสคริปต์ configure ก็ไม่ใช่มาตรฐาน จึงต้องสร้างสคริปต์ configure ด้วย autoconf/autoreconf ก่อนรัน target ของ make
make และเครื่องมือข้างเคียงอย่าง automake/autoconf/autoreconf ใช้ mtime ในการตัดสินว่าอินพุตล้าสมัยหรือไม่ ต่อให้รัน autoconf/autoreconf/automake/configure แล้ว ก็อาจตกอยู่ในสภาพที่อินพุตยังคงล้าสมัยอย่างถาวรจนบิลด์อะไรไม่ได้เลย หมายเหตุว่า build system จำนวนมากใช้ mtime ได้ไม่มีปัญหา หากมีการติดตาม dependency ที่เหมาะสม
โดยรวมแล้ว ข้อบกพร่องเชิงการออกแบบพื้นฐานของ make คือมันยึดตามปรัชญา Unix คือ “ทำสิ่งเดียวให้ดี” สิ่งเดียวนั้นคือ “ถ้าอินพุตล้าสมัย ก็ rebuild target” แต่นี่เป็นเครื่องมือที่จำกัดมาก จน build system สมัยใหม่ต้องซ้อนงานจำนวนมากไว้บนมัน เพื่อให้มีประโยชน์มากกว่าตัวรันงานพื้นฐาน
makeresolve dependency ไม่ได้งั้นหรือ?dependency:...target: dependency...make configureคำสั่งนี้จะตั้งค่าสิ่งที่จำเป็นสำหรับmake [build]เลยต้องใช้ touchfile ซึ่งถึงจะรก แต่ก็ยังทำงานได้ดีกว่าอีกหลายอย่าง ผมพูดโดยเฉพาะเมื่อมองไปที่แคชของ docker build