เช่นเดียวกับไฟล์โค้ดที่มีโค้ดหนึ่งบรรทัดขึ้นไปเป็นเนื้อหาเพื่อให้คุ้มค่า makefile พื้นฐานจะถูกสร้างขึ้นโดยใช้ตัวแปร กฎ และเป้าหมาย นอกจากนั้น ยังมีปัจจัยอื่นๆ ที่จำเป็นในการสร้าง makefile ให้สมบูรณ์โดยไม่มีปัญหาใดๆ ในคู่มือนี้ เราจะพูดถึงไวยากรณ์ makefile พื้นฐานและปัญหาทั่วไปในขณะที่เขียน makefile และเสนอวิธีแก้ไขปัญหาเหล่านั้น
ทำความเข้าใจเกี่ยวกับไวยากรณ์พื้นฐานของ Makefile
ในการเริ่มต้นสร้าง makefile เราจะอธิบายคุณสมบัติพื้นฐานของ makefile ผ่านตัวอย่างโค้ด makefile จำเป็นต้องรวมคุณสมบัติไวยากรณ์ต่อไปนี้ในเนื้อหา makefile เพื่อรับไฟล์เรียกทำงาน:
ตัวแปร s: ข้อมูลพื้นฐานที่จัดเก็บอ็อบเจ็กต์ที่จำเป็นเพื่อใช้ใน makefile ตัวแปรเหล่านี้ใช้เพื่อระบุคอมไพลเลอร์ แฟล็ก ไฟล์ต้นฉบับ ไฟล์อ็อบเจ็กต์ และไฟล์เป้าหมาย ภายใน makefile ตัวอย่างต่อไปนี้ มีตัวแปรทั้งหมดห้าตัว ได้แก่ CXX (เพื่อตั้งค่าคอมไพลเลอร์ C++), CXXFLAGSc (แฟล็กคอมไพเลอร์), TARGET (เพื่อตั้งชื่อไฟล์ปฏิบัติการเป้าหมาย), SRCS (เพื่อตั้งค่าไฟล์ซอร์สโค้ด) , OBJS (เพื่อให้มีไฟล์อ็อบเจ็กต์ที่สร้างขึ้นผ่านไฟล์ซอร์สโค้ด)
เป้าหมาย: เอาต์พุตที่คาดว่าจะสร้างจากแหล่งที่มา อาจเป็นไฟล์เป้าหมายหรือชื่อสัญลักษณ์ใดก็ได้: “all” เป็นเป้าหมายเริ่มต้นที่ควรสร้างผ่านตัวแปร “TARGET” “$TARGET” ขึ้นอยู่กับตัวแปร “OBJS” และเป้าหมาย “clean” จะลบเป้าหมายและ ไฟล์อ็อบเจ็กต์จากไดเร็กทอรีการทำงาน
กฎและคำสั่งการสร้าง: ชุดคำสั่งพื้นฐานที่จะดำเนินการเพื่อสร้างเป้าหมายจากไฟล์ต้นฉบับหรือการอ้างอิง ตัวอย่างเช่น กฎ “%.o: %.cpp” แสดงว่าไฟล์ที่มีนามสกุล “cpp” ใช้เพื่อสร้างไฟล์อ็อบเจ็กต์ที่มีนามสกุล “o” ในขณะที่ทั้งสองไฟล์มีชื่อเดียวกัน ในทางกลับกันคำสั่ง build $(CXX) $(CXXFLAGS) -o $(เป้าหมาย) $(OBJS) ใช้เพื่อเชื่อมโยงไฟล์อ็อบเจ็กต์และไฟล์เป้าหมายใหม่เข้าด้วยกัน ในทำนองเดียวกันคำสั่ง build $(CXX) $(CXXFLAGS) -c $< -o $@ รวบรวมไฟล์ต้นฉบับเป็นไฟล์อ็อบเจ็กต์
การพึ่งพา: การขึ้นต่อกันจะอยู่ที่นั่นเสมอเมื่อคุณต้องการสร้าง makefile ตัวอย่างเช่น เป้าหมาย 'ทั้งหมด' ขึ้นอยู่กับตัวแปร 'TARGET' ในขณะที่ 'TARGET' ขึ้นอยู่กับตัวแปร 'OBJS' ในเวลาเดียวกัน ตัวแปร “OBJS” ขึ้นอยู่กับไฟล์ต้นฉบับผ่านตัวแปร “SRCS”
ความคิดเห็น: คำแนะนำที่มนุษย์เข้าใจได้มักจะใช้เพื่ออธิบายวัตถุประสงค์ของบรรทัดโค้ด ในกรณีที่คุณใช้ไฟล์หลังจากผ่านไปเป็นเวลานาน ใน makefile ต่อไปนี้ เราใช้ความคิดเห็นที่ขึ้นต้นด้วยเครื่องหมาย “#” เพื่ออธิบายแต่ละบรรทัด
ซีเอ็กซ์ = ก++
CXXFLAGS = -มาตรฐาน =ค++ สิบเอ็ด -กำแพง
เป้าหมาย = ใหม่
SRCS = main.cpp
OBJS = $ ( SRCS:.cpp=.o )
ทั้งหมด: $ ( เป้า )
$ ( เป้า ) : $ ( โอบีเจ )
$ ( ซีเอ็กซ์เอ็กซ์ ) $ ( CXXFLAGS ) -โอ $ ( เป้า ) $ ( โอบีเจ )
% .โอ: % .ซีพีพี
$ ( ซีเอ็กซ์เอ็กซ์ ) $ ( CXXFLAGS ) -ค $ < -โอ $ @
ทำความสะอาด:
RM -ฉ $ ( เป้า ) $ ( โอบีเจ )
ปัญหาทั่วไปและแนวทางแก้ไข
ในขณะที่เขียน makefile ใดๆ จำเป็นต้องพิจารณาทุกรายละเอียดเล็กๆ น้อยๆ เพื่อให้ได้ผลลัพธ์ที่ต้องการในตอนท้าย ผู้ใช้มักพบปัญหาทั่วไปบางประการขณะสร้าง makefile ภายในส่วนนี้ เราจะหารือเกี่ยวกับปัญหาเหล่านั้นและเสนอแนะแนวทางแก้ไขที่เป็นไปได้ดังต่อไปนี้:
1: ไม่ใช้ตัวแปร
การใช้ตัวแปรใน makefile เป็นสิ่งที่ต้องมี เนื่องจากจำเป็นต้องตั้งค่าคอมไพลเลอร์ เป้าหมาย ไฟล์ต้นฉบับ ฯลฯ ปัญหาที่พบบ่อยที่สุดที่สามารถพบได้คือการไม่ใช้ตัวแปรใดๆ ใน makefile ดังนั้น ตรวจสอบให้แน่ใจว่าใช้ตัวแปรที่จำเป็น เช่น CXX, CXXFLAGSc(แฟล็กคอมไพเลอร์), TARGET, SRCS และ OBJS ใน makefile ตัวอย่างก่อนหน้า
2: ปัญหาตัวคั่นหายไป
ในขณะที่เขียน makefile จำเป็นต้องพิจารณากฎการเยื้องอย่างระมัดระวัง เนื่องจากการใช้ช่องว่างแทนแท็บจะนำคุณไปสู่ปัญหา “ตัวคั่นที่ขาดหายไป” ในระหว่างการดำเนินการคำสั่ง “make” ตัวอย่างเช่น เราเพิ่มช่องว่างที่จุดเริ่มต้นของกฎที่บรรทัด 13 และลบแท็บออก
$ ( เป้า ) : $ ( โอบีเจ )$ ( ซีเอ็กซ์เอ็กซ์ ) $ ( CXXFLAGS ) -โอ $ ( เป้า ) $ ( โอบีเจ )
เมื่อดำเนินการค้นหา 'make' เราได้รับข้อผิดพลาด 'ตัวคั่นที่ขาดหายไป' ที่บรรทัด 13 และไฟล์หยุดทำงาน เพื่อหลีกเลี่ยงปัญหานี้ ตรวจสอบให้แน่ใจว่าใช้ 'แท็บ' แทนการเว้นวรรค
ทำ
เพื่อหลีกเลี่ยงปัญหานี้ ตรวจสอบให้แน่ใจว่าใช้ 'แท็บ' แทนการเว้นวรรคดังที่ปรากฎในภาพต่อไปนี้:
$ ( เป้า ) : $ ( โอบีเจ )$ ( ซีเอ็กซ์เอ็กซ์ ) $ ( CXXFLAGS ) -โอ $ ( เป้า ) $ ( โอบีเจ )
3: ปัญหา “ไม่พบจุดเข้าใช้งาน”
ข้อผิดพลาดนี้ส่วนใหญ่เกิดขึ้นเนื่องจากไฟล์ต้นฉบับ ไม่ใช่เพราะ makefile เช่นเมื่อคุณพลาดการใช้ฟังก์ชัน 'main()' ในไฟล์ซอร์สโค้ด ตัวอย่างเช่น เราแทนที่นิยามฟังก์ชัน main() ด้วยการประกาศฟังก์ชันที่ผู้ใช้กำหนดอย่างง่าย
#รวมอินท์โชว์ ( ) {
ถ่านวี;
มาตรฐาน::cout << 'ป้อนค่า:' ;
มาตรฐาน::cin >> ใน;
มาตรฐาน::cout << ใน << มาตรฐาน::endl;
กลับ 0 ;
}
เมื่อดำเนินการคำสั่ง 'make' บนพรอมต์คำสั่งของ Windows เราพบ 'การอ้างอิงที่ไม่ได้กำหนดไปยัง 'WinMain'' เนื่องจากคอมไพเลอร์ไม่พบจุดเริ่มต้นใด ๆ เพื่อเริ่มเรียกใช้งานไฟล์ C++ หากต้องการแก้ไขปัญหานี้ ให้แทนที่ 'show' ด้วย 'main'
4: การใช้ส่วนขยายที่ไม่ถูกต้อง
บางครั้งผู้ใช้อาจใช้นามสกุลที่ไม่ถูกต้องสำหรับไฟล์ต้นฉบับที่จะใช้ใน makefile โดยไม่ได้ตั้งใจ การใช้ส่วนขยายที่ไม่ถูกต้องจะทำให้เกิดข้อผิดพลาดรันไทม์ กล่าวคือ ไม่มีกฎในการสร้างเป้าหมาย เราสร้าง makefile เพื่อสร้างไฟล์ปฏิบัติการและไฟล์อ็อบเจ็กต์สำหรับไฟล์ C++ ในบรรทัดที่ 7 เราจัดเตรียมไฟล์ต้นฉบับที่มีนามสกุล “c”
CXX := ก++CXXFLAGS := -มาตรฐาน =ค++ สิบเอ็ด -กำแพง
เป้าหมาย = ใหม่
SRCS = main.c
OBJS = $ ( SRCS:.cpp=.o )
ทั้งหมด: $ ( เป้า )
$ ( เป้า ) : $ ( โอบีเจ )
การเรียกใช้คำสั่ง 'make' จะนำเราไปสู่ข้อผิดพลาด 'ไม่มีกฎในการสร้างเป้าหมาย 'main.c'' เพื่อหลีกเลี่ยงปัญหานี้ ตรวจสอบให้แน่ใจว่าใช้นามสกุลไฟล์ต้นฉบับที่ถูกต้อง
ทำ
5: ขาดการพึ่งพา
ขณะเขียน makefile คุณควรรวมการอ้างอิงทั้งหมดสำหรับไฟล์ต้นฉบับเพื่อรับเอาต์พุตที่ต้องการ ตัวอย่างเช่น ไฟล์โค้ด C++ ของเราใช้ไฟล์ 'myheader.h' เป็นตัวอ้างอิง ดังนั้นเราจึงกล่าวถึงมันในไฟล์โค้ด C++ ดังนี้:
#รวม#รวม “myheader.h”
อินท์โชว์ ( ) {
ถ่านวี;
มาตรฐาน::cout << 'ป้อนค่า:' ;
มาตรฐาน::cin >> ใน;
มาตรฐาน::cout << ใน << มาตรฐาน::endl;
กลับ 0 ;
}
ภายใน makefile เราจงใจเพิกเฉยต่อการใช้ไฟล์ 'myheader.h' ภายในกฎการสร้างที่เขียนไว้ที่บรรทัดที่ 9
% .โอ: % .ซีพีพี$ ( ซีเอ็กซ์เอ็กซ์ ) $ ( CXXFLAGS ) -ค $ < -โอ $ @
ขณะนี้ ขณะที่ใช้คำสั่ง 'make' เราพบข้อผิดพลาด 'ไม่มีอะไรที่ต้องทำสำหรับ 'ทั้งหมด'
ทำ% .โอ: % .cpp myheader.h
$ ( ซีเอ็กซ์เอ็กซ์ ) $ ( CXXFLAGS ) -ค $ < -โอ $ @
เพื่อหลีกเลี่ยงปัญหาดังกล่าวและเรียกใช้ซอร์สโค้ดได้สำเร็จ ให้ระบุชื่อไฟล์ “myheader.h” ที่บรรทัดที่เก้าของ makefile ดังที่แสดงไว้ต่อไปนี้:
บทสรุป
ภายในคู่มือนี้ เราได้อธิบายไวยากรณ์ของ makefile อย่างละเอียดโดยใช้เนื้อหาที่จำเป็น เช่น ตัวแปร คำสั่ง build กฎ ฯลฯ รวมตัวอย่างโค้ดไว้เพื่ออธิบายไวยากรณ์ให้ละเอียดยิ่งขึ้น ในตอนท้าย เราได้พูดคุยถึงปัญหาทั่วไปบางประการและแนวทางแก้ไขที่ผู้ใช้สามารถพบได้ขณะสร้าง makefile