ตั้งแต่นี้เป็น Unix, executables ไม่มีส่วนขยายใด ๆ
สิ่งหนึ่งที่ควรทราบก็คือ root-config
คือยูทิลิตี้ที่ให้การรวบรวมที่ถูกต้องและการเชื่อมโยงธง; และไลบรารีที่เหมาะสมสำหรับการสร้างแอปพลิเคชันกับรูท นั่นเป็นเพียงรายละเอียดที่เกี่ยวข้องกับผู้ชมดั้งเดิมสำหรับเอกสารนี้
ทำให้ฉันเป็นเด็ก
หรือคุณไม่เคยลืมครั้งแรกที่คุณทำ
การสนทนาเบื้องต้นเกี่ยวกับ make และวิธีเขียน makefile อย่างง่าย
ทำคืออะไร แล้วทำไมฉันถึงต้องแคร์?
เครื่องมือที่เรียกว่าMakeเป็นตัวจัดการการพึ่งพาสร้าง นั่นคือมันจะต้องระมัดระวังในการรู้ว่าคำสั่งใดที่จะต้องดำเนินการในสิ่งที่จะนำโครงการซอฟต์แวร์ของคุณจากการรวบรวมไฟล์ต้นฉบับ, วัตถุไฟล์, ห้องสมุด, ส่วนหัว, ฯลฯ - บางอันอาจมีการเปลี่ยนแปลง เมื่อเร็ว ๆ นี้ --- และเปลี่ยนให้เป็นเวอร์ชั่นที่ถูกต้องของโปรแกรม
จริงๆแล้วคุณสามารถใช้ Make เพื่อสิ่งอื่นได้เช่นกัน แต่ฉันจะไม่พูดเกี่ยวกับเรื่องนี้
Makefile เล็กน้อย
สมมติว่าคุณมีไดเรกทอรีที่มี: tool
tool.cc
tool.o
support.cc
support.hh
และ support.o
ขึ้นอยู่กับroot
และควรจะรวบรวมเป็นโปรแกรมที่เรียกว่าtool
และสมมติว่าคุณได้แฮ็คไฟล์ต้นฉบับ (ซึ่งหมายความว่าที่มีอยู่tool
ล้าสมัยแล้ว) และต้องการ รวบรวมโปรแกรม
คุณสามารถทำได้
ตรวจสอบว่าอย่างใดอย่างหนึ่งsupport.cc
หรือsupport.hh
ใหม่กว่าsupport.o
และถ้าเป็นเช่นนั้นเรียกใช้คำสั่งเช่น
g++ -g -c -pthread -I/sw/include/root support.cc
ตรวจสอบว่าอย่างใดอย่างหนึ่งsupport.hh
หรือtool.cc
ใหม่กว่าtool.o
และถ้าเป็นเช่นนั้นเรียกใช้คำสั่งเช่น
g++ -g -c -pthread -I/sw/include/root tool.cc
ตรวจสอบว่าtool.o
ใหม่กว่าtool
และถ้าเป็นเช่นนั้นเรียกใช้คำสั่งเช่น
g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
วุ้ย ช่างเป็นเรื่องยุ่งยาก! มีหลายสิ่งที่ต้องจดจำและหลายโอกาสที่จะทำผิดพลาด (BTW - รายละเอียดของบรรทัดคำสั่งที่แสดงที่นี่ขึ้นอยู่กับสภาพแวดล้อมซอฟต์แวร์ของเรารายการเหล่านี้ใช้ได้กับคอมพิวเตอร์ของฉัน)
แน่นอนคุณสามารถรันทั้งสามคำสั่งได้ทุกครั้ง อาจใช้งานได้ แต่ไม่สามารถปรับขนาดให้เหมาะสมกับซอฟต์แวร์ชิ้นใหญ่ (เช่น DOGS ซึ่งใช้เวลามากกว่า 15 นาทีในการรวบรวมจากพื้นดินบน MacBook ของฉัน)
แต่คุณสามารถเขียนไฟล์ที่เรียกว่าmakefile
ดังนี้:
tool: tool.o support.o
g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl
tool.o: tool.cc support.hh
g++ -g -c -pthread -I/sw/include/root tool.cc
support.o: support.hh support.cc
g++ -g -c -pthread -I/sw/include/root support.cc
และเพียงพิมพ์ make
ที่บรรทัดคำสั่ง ซึ่งจะดำเนินการตามสามขั้นตอนที่แสดงด้านบนโดยอัตโนมัติ
บรรทัดที่ไม่มีการย่อหน้าที่นี่มีรูปแบบ"target: dependencies"และแจ้งให้ Make ว่าคำสั่งที่เกี่ยวข้อง (บรรทัดที่เยื้อง) ควรรันหากการพึ่งพาใด ๆ นั้นใหม่กว่าเป้าหมาย นั่นคือบรรทัดการพึ่งพาอธิบายถึงตรรกะของสิ่งที่ต้องสร้างใหม่เพื่อรองรับการเปลี่ยนแปลงในไฟล์ต่างๆ หากsupport.cc
การเปลี่ยนแปลงนั้นหมายความว่าsupport.o
จะต้องสร้างใหม่ แต่tool.o
สามารถปล่อยไว้ตามลำพัง เมื่อsupport.o
การเปลี่ยนแปลงtool
ต้องถูกสร้างใหม่
คำสั่งที่เกี่ยวข้องกับแต่ละบรรทัดอ้างอิงถูกตั้งค่าด้วยแท็บ (ดูด้านล่าง) ควรแก้ไขเป้าหมาย (หรืออย่างน้อยก็แตะเพื่อปรับปรุงเวลาแก้ไข)
ตัวแปรกฎในตัวและสารพัดอื่น ๆ
ณ จุดนี้ makefile ของเราเพียงจำงานที่ต้องทำ แต่เรายังต้องคิดและพิมพ์คำสั่งที่จำเป็นทุกอย่างครบถ้วน มันไม่จำเป็นต้องเป็นแบบนั้น: Make เป็นภาษาที่ทรงพลังด้วยตัวแปรฟังก์ชั่นการจัดการข้อความและกฎระเบียบในตัวทั้งหมดซึ่งทำให้เราง่ายขึ้นมาก
ทำตัวแปร
$(VAR)
ไวยากรณ์สำหรับการเข้าถึงตัวแปรให้เป็น
ไวยากรณ์สำหรับการกำหนดให้กับตัวแปร Make คือ: VAR = A text value of some kind
(หรือVAR := A different text value but ignore this for the moment
)
คุณสามารถใช้ตัวแปรในกฎเช่น makefile รุ่นปรับปรุงของเรา:
CPPFLAGS=-g -pthread -I/sw/include/root
LDFLAGS=-g
LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
-Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
-lm -ldl
tool: tool.o support.o
g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)
tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc
support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc
ซึ่งอ่านได้มากกว่านี้เล็กน้อย แต่ยังคงต้องการการพิมพ์จำนวนมาก
ทำหน้าที่
GNU ทำหน้าที่สนับสนุนฟังก์ชั่นที่หลากหลายสำหรับการเข้าถึงข้อมูลจากระบบไฟล์หรือคำสั่งอื่น ๆ ในระบบ ในกรณีนี้เราสนใจ$(shell ...)
ที่จะขยายออกไปยังเอาท์พุทของอาร์กิวเมนต์และ$(subst opat,npat,text)
แทนที่อินสแตนซ์ทั้งหมดopat
ด้วยnpat
ในข้อความ
การใช้ประโยชน์จากสิ่งนี้ทำให้เรา:
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
tool: $(OBJS)
g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc
support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc
ซึ่งง่ายต่อการพิมพ์และอ่านได้มากขึ้น
สังเกตว่า
- เรายังคงระบุการอ้างอิงอย่างชัดเจนสำหรับแต่ละอ็อบเจ็กต์ไฟล์และไฟล์สั่งการสุดท้าย
- เราต้องพิมพ์กฎการคอมไพล์สำหรับไฟล์ต้นฉบับอย่างชัดเจน
กฎโดยนัยและรูปแบบ
โดยทั่วไปเราคาดหวังว่าไฟล์ต้นฉบับ C ++ ทั้งหมดควรได้รับการปฏิบัติเช่นเดียวกันและ Make จัดเตรียมสามวิธีในการระบุสิ่งนี้:
- กฎต่อท้าย (ถือว่าล้าสมัยใน GNU make แต่เก็บไว้เพื่อความเข้ากันได้ย้อนหลัง)
- กฎเกณฑ์โดยปริยาย
- กฎรูปแบบ
มีการสร้างกฎโดยปริยายและจะมีการพูดคุยกันเล็กน้อยด้านล่าง กฎรูปแบบที่ระบุไว้ในรูปแบบเช่น
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
ซึ่งหมายความว่าอ็อบเจ็กต์ไฟล์ถูกสร้างจากไฟล์ต้นฉบับ C โดยการรันคำสั่งที่แสดงซึ่งตัวแปร "อัตโนมัติ" $<
ขยายเป็นชื่อของการขึ้นต่อกันครั้งแรก
กฎในตัว
Make มีโฮสต์ทั้งหมดของกฎในตัวซึ่งหมายความว่าบ่อยครั้งมากโครงการสามารถคอมไพล์โดย makefile ที่เรียบง่ายมากแน่นอน
GNU ที่สร้างขึ้นในกฎสำหรับไฟล์ต้นฉบับ C คือไฟล์ที่จัดแสดงด้านบน ในทำนองเดียวกันเราสร้างไฟล์อ็อบเจ็กต์จากไฟล์ต้นฉบับ C ++ ด้วยกฎ$(CXX) -c $(CPPFLAGS) $(CFLAGS)
ดังนี้
ไฟล์วัตถุเดียวมีการเชื่อมโยงโดยใช้$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
แต่สิ่งนี้จะไม่ทำงานในกรณีของเราเพราะเราต้องการเชื่อมโยงหลายวัตถุไฟล์
ตัวแปรที่ใช้โดยกฎในตัว
กฎในตัวใช้ชุดของตัวแปรมาตรฐานที่อนุญาตให้คุณระบุข้อมูลสภาพแวดล้อมท้องถิ่น (เช่นตำแหน่งที่จะค้นหาไฟล์ ROOT include) โดยไม่ต้องเขียนกฎทั้งหมดอีกครั้ง สิ่งที่น่าสนใจสำหรับเรามากที่สุดคือ:
CC
- คอมไพเลอร์ C ที่จะใช้
CXX
- คอมไพเลอร์ C ++ ที่ใช้
LD
- ตัวเชื่อมโยงที่จะใช้
CFLAGS
- การรวบรวมธงสำหรับไฟล์ต้นฉบับ C
CXXFLAGS
- ธงการรวบรวมสำหรับไฟล์ต้นฉบับ C ++
CPPFLAGS
- แฟล็กสำหรับ c-preprocessor (โดยทั่วไปจะมีพา ธ ไฟล์และสัญลักษณ์ที่กำหนดไว้ในบรรทัดคำสั่ง) ซึ่งใช้โดย C และ C ++
LDFLAGS
- ธงลิงเกอร์
LDLIBS
- ห้องสมุดเชื่อมโยง
Makefile ขั้นพื้นฐาน
ด้วยการใช้ประโยชน์จากกฎในตัวเราสามารถทำให้ makefile ของเราง่ายขึ้นไปที่:
CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
all: tool
tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
tool.o: tool.cc support.hh
support.o: support.hh support.cc
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) tool
เราได้เพิ่มเป้าหมายมาตรฐานหลายอย่างที่ดำเนินการพิเศษ (เช่นการล้างไดเรกทอรีแหล่งที่มา)
โปรดทราบว่าเมื่อทำการเรียกใช้โดยไม่มีอาร์กิวเมนต์จะใช้เป้าหมายแรกที่พบในไฟล์ (ในกรณีนี้ทั้งหมด) แต่คุณยังสามารถตั้งชื่อเป้าหมายเพื่อรับสิ่งที่ทำให้ make clean
ลบไฟล์วัตถุในกรณีนี้
เรายังคงมีการอ้างอิงฮาร์ดโค้ดทั้งหมด
การปรับปรุงที่ลึกลับบางอย่าง
CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))
all: tool
tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)
depend: .depend
.depend: $(SRCS)
$(RM) ./.depend
$(CXX) $(CPPFLAGS) -MM $^>>./.depend;
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) *~ .depend
include .depend
สังเกตว่า
- ไม่มีเส้นอ้างอิงใด ๆ สำหรับไฟล์ต้นฉบับอีกต่อไป!?!
- มีเวทย์มนตร์แปลก ๆ ที่เกี่ยวข้องกับ. ขึ้นอยู่กับ
- ถ้าคุณทำ
make
แล้วls -A
คุณจะเห็นไฟล์ชื่อ.depend
ซึ่งมีสิ่งที่ดูเหมือนว่าทำให้สายการพึ่งพา
การอ่านอื่น ๆ
รู้ข้อบกพร่องและบันทึกทางประวัติศาสตร์
ภาษาที่ป้อนสำหรับการสร้างนั้นไวต่อช่องว่าง โดยเฉพาะอย่างยิ่งสายการดำเนินการอ้างอิงต่อไปนี้ต้องเริ่มต้นด้วยแท็บ แต่ชุดของช่องว่างสามารถมีลักษณะเหมือนกัน (และแน่นอนว่ามีตัวแก้ไขที่จะแปลงแท็บให้เป็นช่องว่างหรือในทางกลับกันอย่างเงียบ ๆ ) ซึ่งส่งผลให้ไฟล์สร้างที่ดูถูกต้องและยังใช้งานไม่ได้ สิ่งนี้ถูกระบุว่าเป็นข้อผิดพลาด แต่เนิ่น ๆ ( เรื่องดำเนินไป ) มันไม่ได้รับการแก้ไขเนื่องจากมีผู้ใช้ 10 รายแล้ว
(นี่ถูกคัดลอกมาจาก wiki post ที่ฉันเขียนสำหรับนักศึกษาระดับบัณฑิตศึกษาสาขาฟิสิกส์)