Làm cách nào để Makefile tự động xây dựng lại các tệp nguồn bao gồm tệp tiêu đề đã sửa đổi? (Trong C / C ++)


92

Tôi có makefile sau mà tôi sử dụng để xây dựng một chương trình (thực tế là một hạt nhân) mà tôi đang làm việc. Nó từ đầu và tôi đang tìm hiểu về quy trình, vì vậy nó không hoàn hảo, nhưng tôi nghĩ rằng nó đủ mạnh vào thời điểm này đối với mức độ kinh nghiệm viết trang điểm của tôi.

AS  =   nasm
CC  =   gcc
LD  =   ld

TARGET      =   core
BUILD       =   build
SOURCES     =   source
INCLUDE     =   include
ASM         =   assembly

VPATH = $(SOURCES)

CFLAGS  =   -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
            -nostdinc -fno-builtin -I $(INCLUDE)
ASFLAGS =   -f elf

#CFILES     =   core.c consoleio.c system.c
CFILES      =   $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
SFILES      =   assembly/start.asm

SOBJS   =   $(SFILES:.asm=.o)
COBJS   =   $(CFILES:.c=.o)
OBJS    =   $(SOBJS) $(COBJS)

build : $(TARGET).img

$(TARGET).img : $(TARGET).elf
    c:/python26/python.exe concat.py stage1 stage2 pad.bin core.elf floppy.img

$(TARGET).elf : $(OBJS)
    $(LD) -T link.ld -o $@ $^

$(SOBJS) : $(SFILES)
    $(AS) $(ASFLAGS) $< -o $@

%.o: %.c
    @echo Compiling $<...
    $(CC) $(CFLAGS) -c -o $@ $<

#Clean Script - Should clear out all .o files everywhere and all that.
clean:
    -del *.img
    -del *.o
    -del assembly\*.o
    -del core.elf

Vấn đề chính của tôi với makefile này là khi tôi sửa đổi tệp tiêu đề mà một hoặc nhiều tệp C bao gồm, các tệp C không được tạo lại. Tôi có thể khắc phục điều này khá dễ dàng bằng cách đặt tất cả các tệp tiêu đề của tôi là tệp phụ thuộc cho tất cả các tệp C của tôi, nhưng điều đó có hiệu quả sẽ gây ra việc xây dựng lại hoàn toàn dự án bất kỳ khi nào tôi thay đổi / thêm tệp tiêu đề, điều này sẽ không được tốt cho lắm.

Điều tôi muốn là chỉ những tệp C bao gồm tệp tiêu đề mà tôi thay đổi được xây dựng lại và toàn bộ dự án được liên kết lại. Tôi có thể thực hiện liên kết bằng cách làm cho tất cả các tệp tiêu đề là phụ thuộc của đích, nhưng tôi không thể tìm ra cách làm cho tệp C bị vô hiệu khi tệp tiêu đề được bao gồm của chúng mới hơn.

Tôi đã nghe nói rằng GCC có một số lệnh để thực hiện điều này (vì vậy makefile bằng cách nào đó có thể tìm ra tệp nào cần được xây dựng lại) nhưng tôi không thể tìm thấy một ví dụ triển khai thực tế để xem xét. Ai đó có thể đăng một giải pháp sẽ kích hoạt hành vi này trong makefile không?

CHỈNH SỬA: Tôi nên làm rõ, tôi đã quen với khái niệm đưa các mục tiêu riêng lẻ vào và có mỗi mục tiêu. Để yêu cầu các tệp tiêu đề. Điều đó yêu cầu tôi phải chỉnh sửa tệp makefile mỗi khi tôi đưa tệp tiêu đề vào đâu đó, điều này hơi khó khăn. Tôi đang tìm kiếm một giải pháp có thể tự tạo ra các phụ thuộc tệp tiêu đề, điều mà tôi khá chắc chắn là tôi đã thấy trong các dự án khác.

Câu trả lời:


30

Như đã chỉ ra ở những nơi khác trên trang web này, hãy xem trang này: Tạo phụ thuộc tự động

Tóm lại, gcc có thể tự động tạo các tệp phụ thuộc .d cho bạn, là các đoạn tệp nhỏ tạo tệp chứa các tệp phụ thuộc của tệp .c mà bạn đã biên dịch. Mỗi khi bạn thay đổi tệp .c và biên dịch nó, tệp .d sẽ được cập nhật.

Bên cạnh việc thêm cờ -M vào gcc, bạn sẽ cần đưa các tệp .d vào makefile (như Chris đã viết ở trên). Có một số vấn đề phức tạp hơn trong trang được giải quyết bằng cách sử dụng sed, nhưng bạn có thể bỏ qua chúng và thực hiện "làm sạch" để xóa các tệp .d bất cứ khi nào phàn nàn về việc không thể tạo tệp tiêu đề không còn tồn tại .


2
Tôi có thể nhầm, nhưng tôi nghĩ GCC thực sự đã thêm một tính năng để thử và khắc phục vấn đề sed đó. Hãy xem gcc.gnu.org/onlineocs/gcc-4.3.1/gcc/Preprocessor-Options.html cụ thể là -MP.
Eugene Marcotte

Có, -MP tồn tại kể từ GCC 3, tồn tại trong clang và icc, và làm mất đi nhu cầu về sed. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/...
thương tiếc hmijail resignees

Phiên bản mới nhất của liên kết trong câu trả lời chứa các ví dụ sử dụng cờ GCC.
MadScientist

20

Bạn có thể thêm lệnh 'tạo phụ thuộc' như những người khác đã nêu nhưng tại sao không lấy gcc để tạo phụ thuộc và biên dịch cùng một lúc:

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

-include $(DEPS)

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

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

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

Lưu ý rằng dường như có một 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.othì tệp được tạo _file_.dsẽ vẫn chứa _file_.o, không obj/_file_c.o.


18
Điều này đã không làm việc cho tôi. Ví dụ: makefile được tạo và chạy như sau: g++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp Tôi nhận được tệp main.d, nhưng main.o là 0 byte. Tuy nhiên, cờ -MMD dường như làm chính xác những gì được yêu cầu. Vì vậy, quy tắc makefile làm việc của tôi đã trở thành:$(CC) -c $(CFLAGS) -MMD -o $@ $<
Darren Nấu

17

Điều này tương đương với câu trả lời của Chris Dodd , nhưng sử dụng một quy ước đặt tên khác (và tình cờ là không yêu cầu sedphép thuật. Được sao chép từ một bản sao sau đó .


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ần phụ thuộc cho bạn. Đoạn Makefile:

depend: .depend

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

include .depend

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


4
Tại sao bạn không đánh vần SOURCES? Đặc biệt không yêu cầu nhiều ký tự hơn và không bị xáo trộn như "SRCS" mà có thể trông giống như một từ viết tắt.
HelloGoodbye 14/09/15

@HelloGoodbye Một chút phong cách. Tôi đã luôn sử dụng, và luôn thấy, SRCSOBJS. Tôi đồng ý hầu hết thời gian, nhưng mọi người nên biết đó là gì.
sherrellbc

@sherrellbc Những gì mọi người "nên" biết và những gì mọi người thực sự biết thường là hai điều khác nhau: P
HelloGoodbye

7

Bạn sẽ phải tạo các mục tiêu riêng lẻ cho từng tệp C, và sau đó liệt kê tệp tiêu đề như một phần phụ thuộc. Bạn vẫn có thể sử dụng các mục tiêu chung của mình và chỉ cần đặt các .hphụ thuộc sau đó, như sau:

%.o: %.c
        @echo Compiling $<...
        $(CC) $(CFLAGS) -c -o $@ $<

foo.c: bar.h
# And so on...

4

Về cơ bản, bạn cần tạo động các quy tắc makefile để xây dựng lại các tệp đối tượng khi tệp tiêu đề thay đổi. Nếu bạn sử dụng gcc và gnumake, điều này khá dễ dàng; chỉ cần đặt một cái gì đó như:

$(OBJDIR)/%.d: %.c
        $(CC) -MM -MG $(CPPFLAGS) $< | sed -e 's,^\([^:]*\)\.o[ ]*:,$(@D)/\1.o $(@D)/\1.d:,' >$@

ifneq ($(MAKECMDGOALS),clean)
include $(SRCS:%.c=$(OBJDIR)/%.d)
endif

trong makefile của bạn.


2
Tôi hiểu điều này, ngoại trừ điều đó (ngoại trừ cờ -MM và -MG là mới) Tôi không hiểu dòng văn bản khó hiểu của regex lookin 'là để làm gì. Điều đó sẽ không làm cho các đồng đội của tôi hài lòng ... ^ _ ^ Tôi sẽ thử nó và xem liệu tôi có kết quả không.
Nicholas Flynt

sed là viết tắt của "trình chỉnh sửa luồng" có thể sửa đổi luồng văn bản mà không cần sử dụng tệp. Nó là một công cụ Unix tiêu chuẩn, nhỏ hơn và nhanh hơn nên nó được sử dụng thường xuyên hơn awk hoặc perl.
Zan Lynx

À, có một vấn đề: Tôi đang làm việc này trong Windows.
Nicholas Flynt

3

Hơn và trên những gì @mipadi đã nói, bạn cũng có thể khám phá việc sử dụng -Mtùy chọn '' để tạo bản ghi các phần phụ thuộc. Bạn thậm chí có thể tạo chúng thành một tệp riêng biệt (có thể là 'depend.mk') mà sau đó bạn đưa vào makefile. Hoặc bạn có thể tìm thấy make dependquy tắc '' chỉnh sửa makefile với các phần phụ thuộc chính xác (điều khoản của Google: "không xóa dòng này" và phụ thuộc).


1

Không có câu trả lời nào phù hợp với tôi. Ví dụ: câu trả lời của Martin Fido gợi ý rằng gcc có thể tạo tệp phụ thuộc, nhưng khi tôi thử nó đang tạo tệp đối tượng trống (không byte) cho tôi mà không có bất kỳ cảnh báo hoặc lỗi nào. Nó có thể là một lỗi gcc. Tôi đang trên

$ gcc --version gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)

Vì vậy, đây là Makefile hoàn chỉnh của tôi phù hợp với tôi; đó là sự kết hợp của các giải pháp + thứ gì đó chưa được ai khác đề cập đến (ví dụ: "quy tắc thay thế hậu tố" được chỉ định là .cc.o :):

CC = g++
CFLAGS = -Wall -g -std=c++0x
INCLUDES = -I./includes/

# LFLAGS = -L../lib
# LIBS = -lmylib -lm

# List of all source files
SRCS = main.cc cache.cc

# Object files defined from source files
OBJS = $(SRCS:.cc=.o)

# # define the executable file 
MAIN = cache_test

#List of non-file based targets:
.PHONY: depend clean all

##  .DEFAULT_GOAL := all

# List of dependencies defined from list of object files
DEPS := $(OBJS:.o=.d)

all: $(MAIN)

-include $(DEPS)

$(MAIN): $(OBJS)
    $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS)

#suffix replacement rule for building .o's from .cc's
#build dependency files first, second line actually compiles into .o
.cc.o:
    $(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<

clean:
    $(RM) *.o *~ $(MAIN) *.d

Lưu ý rằng tôi đã sử dụng .cc .. Makefile ở trên rất dễ điều chỉnh cho các tệp .c.

Cũng lưu ý tầm quan trọng của hai dòng này:

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

vì vậy gcc được gọi một lần để tạo tệp phụ thuộc trước tiên và sau đó thực sự biên dịch tệp .cc. Và tiếp tục như vậy đối với mỗi tệp nguồn.


0

Giải pháp đơn giản hơn: Chỉ cần sử dụng Makefile để quy tắc biên dịch .c thành .o phụ thuộc vào (các) tệp tiêu đề và bất kỳ thứ gì khác có liên quan trong dự án của bạn làm phụ thuộc.

Ví dụ, trong Makefile ở đâu đó:

DEPENDENCIES=mydefs.h yourdefs.h Makefile GameOfThrones.S07E01.mkv

::: (your other Makefile statements like rules 
:::  for constructing executables or libraries)

# Compile any .c to the corresponding .o file:
%.o: %.c $(DEPENDENCIES)
        $(CC) $(CFLAGS) -c -o $@ $<

-1

Tôi tin rằng mkdeplệnh là những gì bạn muốn. Nó thực sự quét các tệp .c cho #includecác dòng và tạo một cây phụ thuộc cho chúng. Tôi tin rằng các dự án Automake / Autoconf sử dụng điều này theo mặc định.

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.