Câu hỏi Makefile, phụ thuộc tiêu đề


Giả sử tôi có một makefile với quy tắc

%.o: %.c
 gcc -Wall -Iinclude ...

Tôi muốn * .o được xây dựng lại bất cứ khi nào một tập tin tiêu đề thay đổi. Thay vì làm việc ra một danh sách các phụ thuộc, bất cứ khi nào bất kỳ tập tin tiêu đề trong /include thay đổi, sau đó tất cả các đối tượng trong dir phải được xây dựng lại.

Tôi không thể nghĩ ra một cách tốt đẹp để thay đổi quy tắc để đáp ứng điều này, tôi mở để gợi ý. Điểm thưởng nếu danh sách tiêu đề không phải được mã hóa cứng


76
2018-03-07 00:09


gốc


Có viết câu trả lời của tôi dưới đây tôi nhìn vào danh sách liên quan và tìm thấy: stackoverflow.com/questions/297514/… dường như trùng lặp. Câu trả lời của Chris Dodd tương đương với câu trả lời của tôi, mặc dù nó sử dụng một quy ước đặt tên khác. - dmckee


Các câu trả lời:


Nếu bạn đang sử dụng trình biên dịch GNU, trình biên dịch có thể tập hợp danh sách các phụ thuộc cho bạn. Phân đoạn Makefile:

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ -MF  ./.depend;

include .depend

hoặc là

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ > ./.depend;

include .depend

Ở đâu SRCS là một biến trỏ đến toàn bộ danh sách các tệp nguồn của bạn.

Ngoài ra còn có công cụ makedepend, nhưng tôi chưa bao giờ thích nó nhiều như gcc -MM


106
2018-05-09 16:04



Tôi thích mẹo này, nhưng làm sao tôi có thể depend chỉ chạy khi các tệp nguồn đã thay đổi? Dường như nó chạy mỗi lần bất kể ... - chase
@chase: Vâng, tôi đã sai lầm làm cho sự phụ thuộc vào các tập tin đối tượng, khi nó rõ ràng nên được trên các nguồn và có thứ tự phụ thuộc sai cho hai mục tiêu, quá. Đó là những gì tôi nhận được để gõ từ bộ nhớ. Thử ngay bây giờ. - dmckee
Có cách nào để thêm trước mỗi tệp một số tiền tố để hiển thị rằng nó nằm trong một thư mục khác, ví dụ: build/file.o ? - RiaD
Tôi đã thay đổi SRCS thành OBJECTS, trong đó OBJECTS là một danh sách các tệp * .o của tôi. Điều đó dường như ngăn chặn phụ thuộc từ chạy mọi thời gian và cũng bắt gặp những thay đổi đối với các tập tin tiêu đề mà thôi. Điều này dường như chống lại các ý kiến ​​trước đây .. tôi thiếu một cái gì đó? - BigBrownBear00
câu trả lời tốt hơn kỹ thuật, nhưng dài hơn và ít toàn diện hơn ... - m-ric


Hầu hết các câu trả lời là đáng ngạc nhiên phức tạp hoặc sai lầm. Tuy nhiên các ví dụ đơn giản và mạnh mẽ đã được đăng ở nơi khác [codereview]. Phải thừa nhận rằng các tùy chọn được cung cấp bởi bộ tiền xử lý gnu là một chút khó hiểu. Tuy nhiên, việc xóa tất cả các thư mục khỏi mục tiêu xây dựng bằng -MM được ghi lại và không phải là lỗi [gpp]:

Theo mặc định CPP lấy tên của tệp đầu vào chính, xóa bất kỳ thành phần thư mục và bất kỳ hậu tố tệp nào như ‘.c’, và gắn thêm   hậu tố đối tượng thông thường của nền tảng.

(Hơi mới hơn) -MMD tùy chọn có lẽ là những gì bạn muốn. Để hoàn thành một ví dụ về một makefile hỗ trợ nhiều thư mục src và xây dựng các thư mục với một số chú thích. Đối với một phiên bản đơn giản mà không cần xây dựng dirs xem [codereview].

CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow

# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build

# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)

# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)

# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)

# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
    # Create build directories - same structure as sources.
    mkdir -p $(@D)
    # Just link all the object files.
    $(CXX) $(CXX_FLAGS) $^ -o $@

# Include all .d files
-include $(DEP)

# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
    mkdir -p $(@D)
    # The -MMD flags additionaly creates a .d file with
    # the same name as the .o file.
    $(CXX) $(CXX_FLAGS) -MMD -c $< -o $@

.PHONY : clean
clean :
    # This should remove all generated files.
    -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)

Phương thức này hoạt động bởi vì nếu có nhiều dòng phụ thuộc cho một mục tiêu duy nhất, các phụ thuộc sẽ được nối một cách đơn giản, ví dụ:

a.o: a.h
a.o: a.c
    ./cmd

tương đương với:

a.o: a.c a.h
    ./cmd

như đã đề cập tại: Tạo nhiều dòng phụ thuộc cho một mục tiêu duy nhất? 


38
2018-03-23 16:27



Tôi thích giải pháp này. Tôi không muốn gõ lệnh phụ thuộc. Hữu ích !! - Robert
Có lỗi chính tả trong giá trị biến OBJ: CPP nên đọc CPPS - ctrucza
Đây là câu trả lời ưa thích của tôi; 1 cho bạn. Đây là trang duy nhất trên trang này có ý nghĩa và bao quát (cho những gì tôi có thể thấy) tất cả các tình huống cần biên dịch lại (tránh biên dịch không cần thiết, nhưng vẫn chưa đủ) - Joost
Cảm ơn @ctrucza, nên được khắc phục ngay bây giờ. - Sophie
Ra khỏi hộp, điều này không thể xác định vị trí các tiêu đề cho tôi mặc dù hpp và cpp đều trên cùng một thư mục. - villasv


Như tôi đã đăng đây gcc có thể tạo phụ thuộc và biên dịch cùng một lúc:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<

Tham số '-MF' chỉ định tệp để lưu trữ các phụ thuộc trong.

Dấu gạch ngang khi bắt đầu '-include' cho phép Tiếp tục khi tệp .d không tồn tại (ví dụ: khi biên dịch đầu tiên).

Lưu ý rằng dường như có lỗi trong gcc liên quan đến tùy chọn -o. Nếu bạn đặt tên tệp đối tượng để nói obj / _file__c.o thì tệp được tạo tập tin.d sẽ vẫn chứa tập tin.o, không phải obj / _file__c.o.


24
2018-03-07 00:15



Khi tôi thử điều này, nó sẽ dẫn đến tất cả các tệp .o của tôi được tạo dưới dạng tệp trống. Tôi có các đối tượng của tôi trong một thư mục con xây dựng (vì vậy $ OBJECTS chứa build / main.o build / smbus.o build / etc ...) và chắc chắn tạo ra các tệp .d như bạn đã mô tả với lỗi rõ ràng, nhưng chắc chắn nó không phải là xây dựng các tập tin .o ở tất cả, trong khi nó không nếu tôi loại bỏ các -MM và -MF. - bobpaul
Sử dụng-MT sẽ giải quyết ghi chú trong các dòng cuối cùng của câu trả lời của bạn, cập nhật mục tiêu của mỗi danh sách phụ thuộc. - Godric Seer
Tôi đã thử điều này trong các kết hợp khác nhau và nó dường như không hoạt động ... - g24l
@bobpaul bởi vì man gcc nói -MM ngụ ý -E, "dừng lại sau khi xử lý trước". Bạn cần -MMD thay thế: stackoverflow.com/a/30142139/895245 - Ciro Santilli 新疆改造中心 六四事件 法轮功


Làm thế nào về một cái gì đó như:

includes = $(wildcard include/*.h)

%.o: %.c ${includes}
    gcc -Wall -Iinclude ...

Bạn cũng có thể sử dụng các ký tự đại diện trực tiếp, nhưng tôi có xu hướng tìm thấy tôi cần chúng ở nhiều nơi.

Lưu ý rằng điều này chỉ hoạt động tốt trên các dự án nhỏ, vì nó giả định rằng mọi tệp đối tượng phụ thuộc vào mọi tệp tiêu đề.


21
2018-04-01 00:05



cảm ơn, tôi đã làm việc này phức tạp hơn rất nhiều - Mike
Tuy nhiên, vấn đề này là mọi tệp đối tượng sẽ được biên dịch lại, mỗi khi một thay đổi nhỏ được thực hiện, nghĩa là nếu bạn có 100 tệp nguồn / tiêu đề và bạn thực hiện một thay đổi nhỏ cho một tệp, tất cả 100 sẽ được biên dịch lại . - Nicholas Hamilton
Bạn thực sự nên cập nhật câu trả lời của bạn để nói rằng đây là một cách rất không hiệu quả để làm điều đó bởi vì nó xây dựng lại TẤT CẢ các tập tin mỗi khi bất kỳ tập tin tiêu đề được thay đổi. Các câu trả lời khác tốt hơn nhiều. - xaxxon
Đây là một giải pháp rất xấu. Chắc chắn nó sẽ làm việc trên một dự án nhỏ, nhưng đối với bất kỳ nhóm sản xuất kích thước và xây dựng, điều này sẽ dẫn đến thời gian biên dịch khủng khiếp và trở thành tương đương với chạy make clean all mỗi lần. - Julien Guertault


Giải pháp của Martin ở trên hoạt động rất tốt, nhưng không xử lý các tệp .o nằm trong các thư mục con. Godric chỉ ra rằng cờ -MT chịu trách nhiệm về vấn đề đó, nhưng nó đồng thời ngăn không cho tệp .o được viết chính xác. Sau đây sẽ chăm sóc cả hai vấn đề sau:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<

4
2017-12-15 12:36





Điều này sẽ làm công việc tốt và thậm chí xử lý các subdir được chỉ định:

    $(CC) $(CFLAGS) -MD -o $@ $<

thử nghiệm nó với gcc 4.8.3


3
2018-06-16 12:44





Tôi thích giải pháp này, qua câu trả lời được chấp nhận bởi Michael Williamson, nó bắt những thay đổi đối với nguồn + tệp nội tuyến, sau đó là nguồn + tiêu đề và cuối cùng chỉ là nguồn. Lợi thế ở đây là toàn bộ thư viện không được biên dịch lại nếu chỉ có một vài thay đổi được thực hiện. Không phải là một xem xét rất lớn cho một dự án với một vài tập tin, bur nếu bạn có 10 hoặc 100 nguồn, bạn sẽ nhận thấy sự khác biệt.

COMMAND= gcc -Wall -Iinclude ...

%.o: %.cpp %.inl
    $(COMMAND)

%.o: %.cpp %.hpp
    $(COMMAND)

%.o: %.cpp
    $(COMMAND)

0
2017-12-15 19:34



Điều này chỉ hoạt động nếu bạn không có bất cứ điều gì trong các tập tin tiêu đề của bạn mà sẽ yêu cầu biên dịch lại của bất kỳ tập tin cpp khác sau đó các tập tin thực hiện tương ứng. - matec