Các ký hiệu makefile $ @ và $ <có nghĩa là gì?


416
CC=g++
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=main.cpp hello.cpp factorial.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=hello

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(LDFLAGS) $(OBJECTS) -o $@

.cpp.o:
    $(CC) $(CFLAGS) $< -o $@

Làm gì $@$<làm chính xác?


5
Liên kết ở trên bị hỏng, đây là một liên kết khác: gnu.org/software/make/manual/html_node/Automatic-Variables.html
asciz

1
Xin chào, ".cpp.o:" này có nghĩa là gì? (dòng cuối cùng?).
bút danh_127

3
".Cpp.o:" có nghĩa là xây dựng ".o" (tệp đối tượng) từ ".cpp" (tệp nguồn)
jaguzu

1
Tôi cảm thấy cần lưu ý rằng có một hướng dẫn thực hiện tại liên kết sau mà từ đó tôi tin rằng Mohit đã thu được tệp Makefile trong bài đăng của mình. mrbook.org/blog/tutorials/make
DeepDeadpool

Microsoft gọi nó là Tên tệp Macros (đối với NMAKE) rõ ràng hơn Biến tự động (đối với MAKE). Thật hữu ích khi thấy cả hai mặt cho mục đích giáo dục.
Ivanzinho

Câu trả lời:


502

$@là tên của tệp được tạo và $<là điều kiện tiên quyết đầu tiên (thường là tệp nguồn). Bạn có thể tìm thấy một danh sách tất cả các biến đặc biệt này trong hướng dẫn sử dụng GNU Make .

Ví dụ, hãy xem xét khai báo sau:

all: library.cpp main.cpp

Trong trường hợp này:

  • $@ đánh giá all
  • $< đánh giá library.cpp
  • $^ đánh giá library.cpp main.cpp

16
Điều đáng chú ý là $@không nhất thiết phải là một tệp, nó cũng có thể là tên của một .PHONYmục tiêu.
Ephemera

Tôi có thể thêm vào các tùy chọn dòng lệnh này không: $@sđể tạo đầu ra lắp ráp, chẳng hạn như name.os?
huseyin tugrul buyukisik 30/07/17

4
Coi chừng khi phụ thuộc đầu tiên là một biến đại diện cho một danh sách, $ <được ước tính sau khi nó được mở rộng. Vì vậy, khi LIST = lib1.cpp lib2.cpp và tất cả: $ {LIST} main.cpp, $ <được ước tính chỉ là lib1.cpp. Vài năm trước, tôi đã dành một chút thời gian để tìm hiểu những gì xảy ra trong kết quả do hành vi này gây ra.
Chân Kim

Nói chung $ @ đề cập đến tên mục tiêu nằm ở bên trái của:
Deepak Kiran

78

Các $@$<được gọi là biến tự động . Biến $@đại diện cho tên của tệp được tạo (tức là đích) và $<đại diện cho điều kiện tiên quyết đầu tiên cần có để tạo tệp đầu ra.
Ví dụ:

hello.o: hello.c hello.h
         gcc -c $< -o $@

Đây hello.olà tập tin đầu ra. Đây là những gì $@mở rộng đến. Sự phụ thuộc đầu tiên là hello.c. Đó là những gì $<mở rộng ra.

Các -ccờ tạo ra các .otập tin; xem man gccđể được giải thích chi tiết hơn Chỉ -ođịnh tệp đầu ra để tạo.

Để biết thêm chi tiết, bạn có thể đọc bài viết này về Linux Makefiles .

Ngoài ra, bạn có thể kiểm tra hướng dẫn sử dụng GNU make . Nó sẽ làm cho Makefiles dễ dàng hơn và gỡ lỗi chúng.

Nếu bạn chạy lệnh này, nó sẽ xuất cơ sở dữ liệu makefile:

make -p 

1
Câu trả lời của bạn nghe có vẻ như $<sẽ mở rộng sang hello.c hello.h(cả hai). Vui lòng làm rõ.
Bác sĩ Beco

Vâng, nó sẽ bao gồm cả hello.c và hello.h
khéo léo

19
$<chỉ là mục đầu tiên. Để bao gồm tất cả, sử dụng $^.
Bác sĩ Beco

1
Bác sĩ Beco đã đúng. Tác giả nên sửa đổi câu trả lời của mình.
PT Huỳnh

67

Từ việc quản lý dự án với GNU Make, Ấn bản thứ 3, tr. 16 (theo Giấy phép Tài liệu Miễn phí GNU ):

Biến tự động được đặt makesau khi quy tắc được khớp. Chúng cung cấp quyền truy cập vào các phần tử từ danh sách mục tiêu và điều kiện tiên quyết để bạn không phải chỉ định rõ ràng bất kỳ tên tệp nào. Chúng rất hữu ích để tránh trùng lặp mã, nhưng rất quan trọng khi xác định các quy tắc mẫu chung hơn.

Có bảy biến số tự động lõi cốt lõi:

  • $@: Tên tệp đại diện cho mục tiêu.

  • $%: Phần tử tên tệp của một đặc tả thành viên lưu trữ.

  • $<: Tên tệp của điều kiện tiên quyết đầu tiên.

  • $?: Tên của tất cả các điều kiện tiên quyết mới hơn mục tiêu, được phân tách bằng dấu cách.

  • $^: Tên tệp của tất cả các điều kiện tiên quyết, cách nhau bởi khoảng trắng. Danh sách này đã loại bỏ tên tệp trùng lặp vì hầu hết các mục đích sử dụng, chẳng hạn như biên dịch, sao chép, v.v., không muốn sao chép.

  • $+: Tương tự $^, đây là tên của tất cả các điều kiện tiên quyết được phân tách bằng dấu cách, ngoại trừ $+bao gồm các bản sao. Biến này được tạo cho các tình huống cụ thể như đối số cho các trình liên kết trong đó các giá trị trùng lặp có ý nghĩa.

  • $*: Thân của tên tệp đích. Một thân cây thường là một tên tệp không có hậu tố của nó. Việc sử dụng nó bên ngoài các quy tắc mẫu là không được khuyến khích.

Ngoài ra, mỗi biến trên có hai biến thể để tương thích với các biến khác. Một biến thể chỉ trả về phần thư mục của giá trị. Đây được chỉ định bằng cách thêm một “D” để biểu tượng, $(@D), $(<D)vv Các báo biến thể khác chỉ có phần tập tin giá trị. Đây được chỉ định bằng cách thêm một “F” để biểu tượng, $(@F), $(<F)vv Lưu ý rằng các tên biến thể có nhiều hơn một ký tự dài và do đó phải được đặt trong dấu ngoặc đơn. GNU make cung cấp một sự thay thế dễ đọc hơn với các hàm dir và notdir.


37

Các $@$<là các macro đặc biệt.

Ở đâu:

$@ là tên tập tin của mục tiêu.

$< là tên của sự phụ thuộc đầu tiên.


19

Các Makefile xây dựng các hellothực thi nếu có một trong những main.cpp, hello.cpp, factorial.cppthay đổi. Makefile nhỏ nhất có thể để đạt được đặc tả đó có thể là:

hello: main.cpp hello.cpp factorial.cpp
    g++ -o hello main.cpp hello.cpp factorial.cpp
  • pro: rất dễ đọc
  • con: cơn ác mộng duy trì, sự trùng lặp của các phụ thuộc C ++
  • con: vấn đề hiệu quả, chúng tôi biên dịch lại tất cả C ++ ngay cả khi chỉ có một thay đổi

Để cải thiện vấn đề trên, chúng tôi chỉ biên dịch các tệp C ++ đã được chỉnh sửa. Sau đó, chúng tôi chỉ liên kết các tệp đối tượng kết quả với nhau.

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

main.o: main.cpp
    g++ -c main.cpp

hello.o: hello.cpp
    g++ -c hello.cpp

factorial.o: factorial.cpp
    g++ -c factorial.cpp
  • pro: khắc phục vấn đề hiệu quả
  • con: cơn ác mộng bảo trì mới, lỗi đánh máy tiềm năng trên các quy tắc tệp đối tượng

Để cải thiện điều này, chúng ta có thể thay thế tất cả các quy tắc tệp đối tượng bằng một .cpp.oquy tắc duy nhất :

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

.cpp.o:
    g++ -c $< -o $@
  • pro: trở lại với một makefile ngắn, hơi dễ đọc

Ở đây .cpp.oquy tắc xác định làm thế nào để xây dựng anyfile.otừ anyfile.cpp.

  • $< phù hợp với sự phụ thuộc đầu tiên, trong trường hợp này, anyfile.cpp
  • $@phù hợp với mục tiêu, trong trường hợp này , anyfile.o.

Những thay đổi khác có trong Makefile là:

  • Giúp dễ dàng thay đổi trình biên dịch từ g ++ sang bất kỳ trình biên dịch C ++ nào.
  • Làm cho nó dễ dàng hơn để thay đổi các tùy chọn trình biên dịch.
  • Làm cho nó dễ dàng hơn để thay đổi các tùy chọn liên kết.
  • Làm cho nó dễ dàng hơn để thay đổi các tệp nguồn và đầu ra C ++.
  • Đã thêm quy tắc mặc định 'tất cả' hoạt động như một kiểm tra nhanh để đảm bảo tất cả các tệp nguồn của bạn đều có mặt trước khi nỗ lực xây dựng ứng dụng của bạn được thực hiện.

1

trong ví dụ nếu bạn muốn biên dịch các nguồn nhưng có các đối tượng trong một thư mục khác:

Bạn cần phải làm :

gcc -c -o <obj/1.o> <srcs/1.c> <obj/2.o> <srcs/2.c> ...

nhưng với hầu hết các macro, kết quả sẽ là tất cả các đối tượng được theo sau bởi tất cả các nguồn, như:

gcc -c -o <all OBJ path> <all SRC path>

vì vậy, điều này sẽ không biên dịch bất cứ thứ gì ^^ và bạn sẽ không thể đặt các tệp đối tượng của mình vào một thư mục khác :(

giải pháp là sử dụng các macro đặc biệt này

$@ $<

điều này sẽ tạo ra một tệp .o (obj / file.o) cho mỗi tệp .c trong SRC (src / file.c)

$(OBJ):$(SRC)
   gcc -c -o $@ $< $(HEADERS) $(FLAGS)

nó có nghĩa là:

    $@ = $(OBJ)
    $< = $(SRC)

nhưng các dòng theo dòng INSTEAD của tất cả các dòng OBJ theo sau là tất cả các dòng SRC


Tôi tự hỏi, bạn sẽ làm gì với tất cả khoảng thời gian bạn tiết kiệm bằng cách gõ "u" thay vì "bạn"?
Ivanzinho
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.