Vì đây là cho Unix, nên các tệp thực thi không có bất kỳ phần mở rộng nào.
Một điều cần lưu ý là root-config
một tiện ích cung cấp các cờ biên dịch và liên kết đúng; và các thư viện phù hợp để xây dựng các ứng dụng chống lại root. Đó chỉ là một chi tiết liên quan đến khán giả ban đầu cho tài liệu này.
Làm em yêu
hoặc bạn không bao giờ quên lần đầu tiên bạn thực hiện
Một cuộc thảo luận giới thiệu về make, và cách viết một makefile đơn giản
Làm là gì? Và tại sao tôi nên quan tâm?
Công cụ có tên Make là một trình quản lý phụ thuộc xây dựng. Đó là, cần quan tâm đến việc biết những lệnh nào cần được thực thi để lấy dự án phần mềm của bạn từ bộ sưu tập tệp nguồn, tệp đối tượng, thư viện, tiêu đề, v.v.-- một số trong đó có thể đã thay đổi gần đây --- và biến chúng thành một phiên bản cập nhật chính xác của chương trình.
Trên thực tế, bạn cũng có thể sử dụng Make cho những thứ khác, nhưng tôi sẽ không nói về điều đó.
Một Makefile tầm thường
Giả sử rằng bạn có một thư mục chứa: tool
tool.cc
tool.o
support.cc
support.hh
và support.o
phụ thuộc vào root
và được cho là sẽ được biên dịch thành một chương trình có tên tool
và giả sử rằng bạn đã hack các tệp nguồn (có nghĩa là hiện tool
tại đã hết hạn) và muốn biên dịch chương trình.
Để làm điều này bản thân bạn có thể
Kiểm tra xem một trong hai support.cc
hoặc support.hh
mới hơn support.o
và nếu chạy một lệnh như
g++ -g -c -pthread -I/sw/include/root support.cc
Kiểm tra xem một trong hai support.hh
hoặc tool.cc
mới hơn tool.o
và nếu chạy một lệnh như
g++ -g -c -pthread -I/sw/include/root tool.cc
Kiểm tra nếu tool.o
mới hơn tool
và nếu chạy một lệnh như
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
Phù! Thật là rắc rối! Có rất nhiều điều cần nhớ và một vài cơ hội để phạm sai lầm. (BTW-- các chi tiết của các dòng lệnh được trình bày ở đây phụ thuộc vào môi trường phần mềm của chúng tôi. Chúng hoạt động trên máy tính của tôi.)
Tất nhiên, bạn chỉ có thể chạy cả ba lệnh mỗi lần. Điều đó sẽ hoạt động, nhưng nó không mở rộng tốt cho một phần mềm đáng kể (như DOGS, mất hơn 15 phút để biên dịch từ nền tảng trên MacBook của tôi).
Thay vào đó bạn có thể viết một tập tin gọi là makefile
như thế này:
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
và chỉ cần gõ make
vào dòng lệnh. Mà sẽ thực hiện ba bước được hiển thị ở trên tự động.
Các dòng không xác định ở đây có dạng "đích: phụ thuộc" và nói với Make rằng các lệnh liên quan (dòng thụt) nên được chạy nếu bất kỳ phụ thuộc nào mới hơn mục tiêu. Đó là, các dòng phụ thuộc mô tả logic của những gì cần được xây dựng lại để phù hợp với những thay đổi trong các tệp khác nhau. Nếu support.cc
thay đổi có nghĩa là support.o
phải được xây dựng lại, nhưng tool.o
có thể để lại một mình. Khi support.o
thay đổi tool
phải được xây dựng lại.
Các lệnh được liên kết với mỗi dòng phụ thuộc được đặt bằng một tab (xem bên dưới) sẽ sửa đổi mục tiêu (hoặc ít nhất là chạm vào nó để cập nhật thời gian sửa đổi).
Biến, được xây dựng theo quy tắc và các tính năng khác
Tại thời điểm này, tệp thực hiện của chúng tôi chỉ đơn giản là ghi nhớ công việc cần thực hiện, nhưng chúng tôi vẫn phải tìm ra và nhập từng lệnh một cần thiết trong toàn bộ. Không cần phải như vậy: Make là một ngôn ngữ mạnh mẽ với các biến, các hàm thao tác văn bản và một loạt các quy tắc tích hợp có thể giúp chúng ta dễ dàng hơn nhiều.
Biến
Cú pháp để truy cập một biến tạo là $(VAR)
.
Cú pháp để gán cho biến Make là: VAR = A text value of some kind
(hoặc VAR := A different text value but ignore this for the moment
).
Bạn có thể sử dụng các biến trong các quy tắc như phiên bản cải tiến này của tệp thực hiện của chúng tôi:
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
dễ đọc hơn một chút, nhưng vẫn cần gõ nhiều
Tạo chức năng
GNU make hỗ trợ nhiều chức năng để truy cập thông tin từ hệ thống tập tin hoặc các lệnh khác trên hệ thống. Trong trường hợp này, chúng tôi quan tâm đến việc $(shell ...)
mở rộng ra đầu ra của (các) đối số và $(subst opat,npat,text)
thay thế tất cả các trường hợp opat
bằng npat
trong văn bản.
Lợi dụng điều này mang lại cho chúng ta:
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ái nào dễ gõ hơn và dễ đọc hơn nhiều.
Thông báo rằng
- Chúng tôi vẫn nêu rõ các phụ thuộc cho từng tệp đối tượng và tệp thực thi cuối cùng
- Chúng tôi đã phải gõ rõ ràng quy tắc biên dịch cho cả hai tệp nguồn
Quy tắc ngầm định và mẫu
Chúng tôi thường mong đợi rằng tất cả các tệp nguồn C ++ nên được xử lý theo cùng một cách và Make cung cấp ba cách để nêu điều này:
- quy tắc hậu tố (được coi là lỗi thời trong GNU make, nhưng được giữ cho tương thích ngược)
- quy tắc ngầm
- quy tắc mẫu
Các quy tắc ngầm được xây dựng và một vài quy tắc sẽ được thảo luận dưới đây. Quy tắc mẫu được chỉ định trong một hình thức như
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
có nghĩa là các tệp đối tượng được tạo từ các tệp nguồn C bằng cách chạy lệnh được hiển thị, trong đó biến "tự động" $<
mở rộng thành tên của phụ thuộc đầu tiên.
Quy tắc tích hợp
Make có một loạt các quy tắc dựng sẵn có nghĩa là rất thường xuyên, một dự án có thể được biên dịch bởi một tệp thực hiện rất đơn giản, thực sự.
GNU make được xây dựng theo quy tắc cho các tệp nguồn C là cái được trình bày ở trên. Tương tự, chúng tôi tạo các tệp đối tượng từ các tệp nguồn C ++ với quy tắc như $(CXX) -c $(CPPFLAGS) $(CFLAGS)
.
Các tệp đối tượng đơn được liên kết bằng cách sử dụng $(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
, nhưng điều này sẽ không hoạt động trong trường hợp của chúng tôi, vì chúng tôi muốn liên kết nhiều tệp đối tượng.
Các biến được sử dụng bởi các quy tắc tích hợp
Các quy tắc tích hợp sử dụng một tập hợp các biến tiêu chuẩn cho phép bạn chỉ định thông tin môi trường cục bộ (như nơi tìm ROOT bao gồm các tệp) mà không cần viết lại tất cả các quy tắc. Những thứ có khả năng thú vị nhất đối với chúng ta là:
CC
- trình biên dịch C để sử dụng
CXX
- trình biên dịch C ++ để sử dụng
LD
- trình liên kết để sử dụng
CFLAGS
- cờ biên dịch cho các tệp nguồn C
CXXFLAGS
- cờ biên dịch cho các tệp nguồn C ++
CPPFLAGS
- cờ cho bộ tiền xử lý c (thường bao gồm các đường dẫn tệp và ký hiệu được xác định trên dòng lệnh), được sử dụng bởi C và C ++
LDFLAGS
- cờ liên kết
LDLIBS
- thư viện để liên kết
Một Makefile cơ bản
Bằng cách tận dụng các quy tắc tích hợp, chúng ta có thể đơn giản hóa tệp thực hiện của mình để:
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
Chúng tôi cũng đã thêm một số mục tiêu tiêu chuẩn thực hiện các hành động đặc biệt (như dọn dẹp thư mục nguồn).
Lưu ý rằng khi make được gọi mà không có đối số, nó sử dụng đích đầu tiên được tìm thấy trong tệp (trong trường hợp này là tất cả), nhưng bạn cũng có thể đặt tên cho mục tiêu để lấy đó là thứ giúp make clean
loại bỏ các tệp đối tượng trong trường hợp này.
Chúng tôi vẫn có tất cả các phụ thuộc được mã hóa cứng.
Một số cải tiến bí ẩn
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
Thông báo rằng
- Không còn bất kỳ dòng phụ thuộc nào cho các tệp nguồn!?!
- Có một số phép thuật kỳ lạ liên quan đến .depend và phụ thuộc
- Nếu bạn làm như vậy
make
thì ls -A
bạn sẽ thấy một tệp có tên .depend
chứa những thứ trông giống như tạo các dòng phụ thuộc
Đọc khác
Biết lỗi và ghi chú lịch sử
Ngôn ngữ nhập cho Make là khoảng trắng nhạy cảm. Cụ thể, các dòng hành động sau phụ thuộc phải bắt đầu bằng một tab . Nhưng một loạt các khoảng trắng có thể trông giống nhau (và thực tế có những trình soạn thảo sẽ âm thầm chuyển đổi các tab thành khoảng trắng hoặc ngược lại), dẫn đến một tệp Make trông có vẻ đúng và vẫn không hoạt động. Điều này đã được xác định là một lỗi sớm, nhưng ( câu chuyện đã xảy ra ) nó không được sửa, vì đã có 10 người dùng.
(Điều này đã được sao chép từ một bài viết wiki tôi đã viết cho sinh viên tốt nghiệp vật lý.)