ฉันจะให้ Makefile สร้างซอร์สไฟล์ใหม่โดยอัตโนมัติที่มีไฟล์ส่วนหัวที่แก้ไขได้อย่างไร (ในภาษา C / C ++)


92

ฉันมี makefile ต่อไปนี้ที่ใช้สร้างโปรแกรม (จริงๆแล้วเคอร์เนล) ที่ฉันกำลังทำงาน ตั้งแต่เริ่มต้นและฉันกำลังเรียนรู้เกี่ยวกับกระบวนการดังนั้นมันจึงไม่สมบูรณ์แบบ แต่ฉันคิดว่ามันมีประสิทธิภาพเพียงพอสำหรับระดับประสบการณ์การเขียน makefiles

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

ปัญหาหลักของฉันกับ makefile นี้คือเมื่อฉันแก้ไขไฟล์ส่วนหัวที่มีไฟล์ C ตั้งแต่หนึ่งไฟล์ขึ้นไปไฟล์ C จะไม่ถูกสร้างขึ้นมาใหม่ ฉันสามารถแก้ไขสิ่งนี้ได้อย่างง่ายดายโดยให้ไฟล์ส่วนหัวทั้งหมดของฉันเป็นที่อ้างอิงสำหรับไฟล์ C ทั้งหมดของฉัน แต่นั่นจะทำให้เกิดการสร้างโครงการใหม่อย่างสมบูรณ์ทุกครั้งที่ฉันเปลี่ยน / เพิ่มไฟล์ส่วนหัวซึ่งจะไม่เป็นประโยชน์

สิ่งที่ฉันต้องการคือเฉพาะไฟล์ C ที่มีไฟล์ส่วนหัวที่ฉันเปลี่ยนเพื่อสร้างใหม่และสำหรับโครงการทั้งหมดที่จะเชื่อมโยงอีกครั้ง ฉันสามารถทำการเชื่อมโยงได้โดยทำให้ไฟล์ส่วนหัวทั้งหมดขึ้นต่อกันของเป้าหมาย แต่ฉันไม่สามารถหาวิธีทำให้ไฟล์ C ไม่ถูกต้องได้เมื่อไฟล์ส่วนหัวที่รวมไว้นั้นใหม่กว่า

ฉันได้ยินมาว่า GCC มีคำสั่งบางอย่างที่จะทำให้สิ่งนี้เป็นไปได้ (ดังนั้น makefile จึงสามารถระบุได้ว่าไฟล์ใดต้องสร้างใหม่) แต่ฉันไม่สามารถหาตัวอย่างการใช้งานจริงให้ดูได้ตลอดชีวิต ใครสามารถโพสต์วิธีแก้ปัญหาที่จะเปิดใช้งานพฤติกรรมนี้ใน makefile ได้หรือไม่

แก้ไข: ฉันควรชี้แจงฉันคุ้นเคยกับแนวคิดในการใส่เป้าหมายแต่ละรายการและมีแต่ละเป้าหมายต้องการไฟล์ส่วนหัว นั่นทำให้ฉันต้องแก้ไข makefile ทุกครั้งที่รวมไฟล์ส่วนหัวไว้ที่ไหนสักแห่งซึ่งเป็นเรื่องที่น่าปวดหัว ฉันกำลังมองหาวิธีแก้ปัญหาที่สามารถได้รับการอ้างอิงไฟล์ส่วนหัวด้วยตัวมันเองซึ่งฉันค่อนข้างมั่นใจว่าฉันเคยเห็นในโครงการอื่น ๆ

คำตอบ:


30

ตามที่ได้ระบุไว้แล้วในที่อื่น ๆ ในไซต์นี้โปรดดูหน้านี้: การสร้างการพึ่งพาอัตโนมัติ

กล่าวโดยย่อ gcc สามารถสร้างไฟล์อ้างอิง. d ให้คุณโดยอัตโนมัติซึ่งเป็นแฟรกเมนต์ makefile ขนาดเล็กที่มีการอ้างอิงของไฟล์. c ที่คุณคอมไพล์ ทุกครั้งที่คุณเปลี่ยนไฟล์. c และคอมไพล์ไฟล์. d จะได้รับการอัพเดต

นอกจากการเพิ่มแฟล็ก -M ใน gcc แล้วคุณจะต้องรวมไฟล์. d ไว้ใน makefile (เหมือนที่ Chris เขียนไว้ด้านบน) มีปัญหาที่ซับซ้อนกว่านี้ในหน้าซึ่งได้รับการแก้ไขโดยใช้ sed แต่คุณสามารถเพิกเฉยและทำ "ทำความสะอาด" เพื่อล้างไฟล์. d เมื่อใดก็ตามที่มีการบ่นว่าไม่สามารถสร้างไฟล์ส่วนหัวที่ไม่มีอยู่แล้ว .


2
ฉันอาจจะเข้าใจผิด แต่ฉันคิดว่า GCC ได้เพิ่มคุณลักษณะเพื่อพยายามแก้ไขปัญหานี้แล้ว ตรวจสอบgcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.htmlโดยเฉพาะ -MP
Eugene Marcotte

ใช่ -MP มีอยู่ตั้งแต่ GCC 3 มีอยู่ในเสียงดังและ icc และทำให้ความจำเป็นในการ sed เป็นโมฆะ bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail ไว้อาลัยลาออก

เวอร์ชันล่าสุดของลิงก์ในคำตอบประกอบด้วยตัวอย่างที่ใช้แฟล็ก GCC
MadScientist

20

คุณสามารถเพิ่มคำสั่ง 'make depend' ตามที่คนอื่นระบุไว้ แต่ทำไมไม่รับ gcc เพื่อสร้างการอ้างอิงและคอมไพล์ในเวลาเดียวกัน:

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

-include $(DEPS)

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

พารามิเตอร์ '-MF' ระบุไฟล์เพื่อเก็บการอ้างอิงใน.

เส้นประที่จุดเริ่มต้นของ '-include' จะบอกให้ Make ดำเนินการต่อเมื่อไม่มีไฟล์. d (เช่นในการคอมไพล์ครั้งแรก)

โปรดทราบว่ามีข้อบกพร่องใน gcc เกี่ยวกับตัวเลือก -o หากคุณตั้งค่าชื่อไฟล์ออบเจ็กต์ให้พูดobj/_file__c.oสิ่งที่สร้างขึ้น_file_.dจะยังคงมี_file_.oobj/_file_c.oไม่


18
สิ่งนี้ไม่ได้ผลสำหรับฉัน เช่น makefile ที่สร้างขึ้นและรันสิ่งนี้: g++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp ฉันมีไฟล์ main.d แต่ main.o คือ 0 ไบต์ อย่างไรก็ตามแฟล็ก -MMD ดูเหมือนจะทำในสิ่งที่ต้องการ ดังนั้นกฎการสร้างไฟล์ที่ใช้งานได้ของฉันจึงกลายเป็น:$(CC) -c $(CFLAGS) -MMD -o $@ $<
Darren Cook

17

นี้จะเทียบเท่ากับคำตอบที่คริสด็อดแต่ใช้การประชุมตั้งชื่อที่แตกต่างกัน (และบังเอิญไม่จำเป็นต้องใช้sedเวทมนตร์. คัดลอกจากที่ซ้ำกันในภายหลัง


หากคุณใช้คอมไพเลอร์ GNU คอมไพลเลอร์สามารถรวบรวมรายการการอ้างอิงสำหรับคุณ Makefile ส่วนย่อย:

depend: .depend

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

include .depend

นอกจากนี้ยังมีเครื่องมือmakedependแต่ฉันไม่เคยชอบมันมากเท่าgcc -MM


4
ทำไมคุณไม่สะกดคำว่า SOURCES? ไม่จำเป็นต้องใช้อักขระเพิ่มเติมโดยเฉพาะอย่างยิ่งและไม่ซับซ้อนเท่ากับ "SRCS" ซึ่งอาจดูเหมือนคำย่อ
HelloGoodbye

@HelloGoodbye สไตล์หน่อย. ผมเคยใช้อยู่เสมอและมักจะเห็นและSRCS OBJSฉันเห็นด้วยเกือบตลอดเวลา แต่ทุกคนควรรู้ว่าสิ่งเหล่านี้คืออะไร
sherrellbc

@sherrellbc สิ่งที่คน "ควร" รู้และสิ่งที่คนรู้จริงมักมีสองอย่างที่แตกต่างกัน P
HelloGoodbye

7

คุณจะต้องสร้างเป้าหมายแต่ละรายการสำหรับไฟล์ C แต่ละไฟล์จากนั้นแสดงรายการไฟล์ส่วนหัวเป็นการอ้างอิง คุณยังคงสามารถใช้เป้าหมายทั่วไปของคุณได้และเพียงแค่วางการ.hอ้างอิงในภายหลังดังนี้:

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

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

4

โดยทั่วไปคุณต้องสร้างกฎ makefile แบบไดนามิกเพื่อสร้างไฟล์ออบเจ็กต์ใหม่เมื่อไฟล์ส่วนหัวเปลี่ยนไป หากคุณใช้ gcc และ gnumake สิ่งนี้ค่อนข้างง่าย เพียงแค่ใส่สิ่งที่ต้องการ:

$(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

ใน makefile ของคุณ


2
ฉันเข้าใจสิ่งนี้ยกเว้นว่า (นอกเหนือจากแฟล็ก he -MM และ -MG เป็นของใหม่) ฉันไม่เข้าใจว่าบรรทัดข้อความที่คลุมเครือของ regex มีไว้เพื่ออะไร นั่นจะไม่ทำให้เพื่อนร่วมทีมมีความสุข ... ^ _ ^ ฉันจะลองดูว่าฉันได้ผลลัพธ์หรือไม่
Nicholas Flynt

sed ย่อมาจาก "stream editor" ซึ่งสามารถแก้ไขสตรีมข้อความได้โดยไม่จำเป็นต้องใช้ไฟล์ เป็นเครื่องมือ Unix มาตรฐานและมีขนาดเล็กและเร็วกว่าจึงใช้บ่อยกว่า awk หรือ perl
Zan Lynx

อามีปัญหา: ฉันกำลังทำสิ่งนี้ใน Windows
Nicholas Flynt

3

เหนือสิ่งที่ @mipadi กล่าวคุณยังสามารถสำรวจการใช้-Mตัวเลือก '' เพื่อสร้างบันทึกการอ้างอิง คุณอาจสร้างไฟล์เหล่านั้นเป็นไฟล์แยกต่างหาก (อาจจะเป็น "depend.mk") ซึ่งคุณรวมไว้ใน makefile หรือคุณสามารถค้นหาmake dependกฎ '' ซึ่งแก้ไข makefile ด้วยการอ้างอิงที่ถูกต้อง (ข้อกำหนดของ Google: "อย่าลบบรรทัดนี้" และขึ้นอยู่กับ)


1

ไม่มีคำตอบใดที่เหมาะกับฉัน เช่นคำตอบของ Martin Fido แสดงให้เห็นว่า gcc สามารถสร้างไฟล์อ้างอิงได้ แต่เมื่อฉันพยายามที่จะสร้างไฟล์อ็อบเจ็กต์ว่าง (ศูนย์ไบต์) ให้ฉันโดยไม่มีคำเตือนหรือข้อผิดพลาดใด ๆ อาจเป็นข้อบกพร่องของ gcc ฉันอยู่

$ gcc - เวอร์ชัน gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)

นี่คือ Makefile ฉบับสมบูรณ์ของฉันที่เหมาะกับฉัน เป็นการรวมกันของโซลูชัน + สิ่งที่ไม่มีใครกล่าวถึง (เช่น "กฎการเปลี่ยนคำต่อท้าย" ที่ระบุเป็น. 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

แจ้งให้ทราบว่าฉันใช้. cc .. Makefile ข้างต้นนั้นง่ายต่อการปรับสำหรับไฟล์. c

สังเกตความสำคัญของสองบรรทัดนี้ด้วย:

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

ดังนั้น gcc จึงถูกเรียกหนึ่งครั้งเพื่อสร้างไฟล์อ้างอิงก่อนจากนั้นจึงรวบรวมไฟล์. cc และอื่น ๆ สำหรับไฟล์ซอร์สแต่ละไฟล์


0

วิธีแก้ปัญหาที่ง่ายกว่า: เพียงใช้ Makefile เพื่อให้กฎการคอมไพล์. c ถึง. o ขึ้นอยู่กับไฟล์ส่วนหัวและสิ่งอื่นใดที่เกี่ยวข้องในโปรเจ็กต์ของคุณเป็นการพึ่งพา

เช่นใน Makefile บางแห่ง:

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

ฉันเชื่อว่าmkdepคำสั่งคือสิ่งที่คุณต้องการ จริงๆแล้วมันจะสแกนไฟล์. c สำหรับ#includeบรรทัดและสร้างแผนผังการอ้างอิงสำหรับพวกเขา ฉันเชื่อว่าโครงการ Automake / Autoconf ใช้สิ่งนี้เป็นค่าเริ่มต้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.