Bagaimana saya bisa membuat Makefile secara otomatis membangun kembali file sumber yang menyertakan file header yang dimodifikasi? (Dalam C / C ++)


92

Saya memiliki makefile berikut yang saya gunakan untuk membangun program (sebenarnya kernel) yang sedang saya kerjakan. Ini dari awal dan saya belajar tentang prosesnya, jadi ini tidak sempurna, tapi saya pikir ini cukup kuat pada titik ini untuk tingkat pengalaman saya menulis 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

Masalah utama saya dengan makefile ini adalah bahwa ketika saya memodifikasi file header yang disertakan satu atau lebih file C, file C tidak dibangun kembali. Saya dapat memperbaikinya dengan cukup mudah dengan membuat semua file header saya menjadi dependensi untuk semua file C saya, tetapi itu secara efektif akan menyebabkan pembangunan kembali proyek secara lengkap setiap kali saya mengubah / menambahkan file header, yang tidak akan terlalu anggun.

Yang saya inginkan hanya untuk file C itu menyertakan file header yang saya ubah untuk dibangun kembali, dan untuk seluruh proyek untuk ditautkan lagi. Saya dapat melakukan penautan dengan membuat semua file header menjadi dependensi dari target, tetapi saya tidak dapat menemukan cara membuat file C menjadi tidak valid ketika file header yang disertakan lebih baru.

Saya pernah mendengar bahwa GCC memiliki beberapa perintah untuk memungkinkan hal ini (sehingga makefile entah bagaimana dapat mengetahui file mana yang perlu dibangun kembali) tetapi saya tidak dapat seumur hidup saya menemukan contoh implementasi yang sebenarnya untuk dilihat. Dapatkah seseorang memposting solusi yang akan mengaktifkan perilaku ini di makefile?

EDIT: Saya harus mengklarifikasi, saya akrab dengan konsep menempatkan target individu dan memiliki setiap target.o memerlukan file header. Itu mengharuskan saya untuk mengedit makefile setiap kali saya menyertakan file header di suatu tempat, yang agak merepotkan. Saya mencari solusi yang dapat memperoleh dependensi file header sendiri, yang saya cukup yakin pernah saya lihat di proyek lain.

Jawaban:


30

Seperti yang telah ditunjukkan di tempat lain di situs ini, lihat halaman ini: Pembuatan Ketergantungan Otomatis

Singkatnya, gcc dapat secara otomatis membuat file dependensi .d untuk Anda, yang merupakan fragmen makefile mini yang berisi dependensi dari file .c yang Anda kompilasi. Setiap kali Anda mengubah file .c dan mengompilasinya, file .d akan diperbarui.

Selain menambahkan -M flag ke gcc, Anda harus menyertakan file .d di makefile (seperti yang ditulis Chris di atas). Ada beberapa masalah yang lebih rumit di halaman yang diselesaikan menggunakan sed, tetapi Anda dapat mengabaikannya dan melakukan "make clean" untuk membersihkan file .d setiap kali membuat keluhan tentang tidak dapat membangun file header yang sudah tidak ada lagi .


2
Saya mungkin salah, tetapi saya pikir GCC sebenarnya telah menambahkan fitur untuk mencoba dan mengatasi masalah sed itu. Lihat gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html khususnya -MP .
Eugene Marcotte

Ya, -MP ada sejak GCC 3, ada di clang dan icc, dan tidak diperlukan sed. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail berduka atas pengunduran diri

Versi terbaru dari tautan di jawaban berisi contoh penggunaan tanda GCC.
MadScientist

20

Anda dapat menambahkan perintah 'make depend' seperti yang dinyatakan orang lain tetapi mengapa tidak mendapatkan gcc untuk membuat dependensi dan mengompilasi pada saat yang sama:

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

-include $(DEPS)

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

Parameter '-MF' menentukan file untuk menyimpan dependensi.

Tanda hubung di awal '-include' memberitahu Make untuk melanjutkan ketika file .d tidak ada (misalnya pada kompilasi pertama).

Perhatikan bahwa tampaknya ada bug di gcc terkait opsi -o. Jika Anda menyetel nama file objek menjadi mengatakan obj/_file__c.omaka yang dihasilkan _file_.dakan tetap berisi _file_.o, bukan obj/_file_c.o.


18
Ini tidak berhasil untuk saya. Misalnya makefile menghasilkan dan menjalankan ini: g++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp Saya mendapat file main.d, tetapi main.o adalah 0 byte. Namun flag -MMD sepertinya melakukan apa yang diperlukan. Jadi aturan makefile kerja saya menjadi:$(CC) -c $(CFLAGS) -MMD -o $@ $<
Darren Cook

17

Ini sama dengan jawaban Chris Dodd , tetapi menggunakan konvensi penamaan yang berbeda (dan secara kebetulan tidak memerlukan sedkeajaiban. Disalin dari duplikat selanjutnya .


Jika Anda menggunakan kompiler GNU, kompilator dapat menyusun daftar dependensi untuk Anda. Fragmen makefile:

depend: .depend

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

include .depend

Ada juga alatnya makedepend, tapi saya tidak pernah menyukainyagcc -MM


4
Mengapa Anda tidak mengeja SOURCES? Tidak memerlukan lebih banyak karakter dan tidak dikaburkan seperti "SRCS" yang mungkin terlihat seperti akronim.
HelloGoodbye

@HelloGoodbye Sedikit gaya. Saya selalu menggunakan, dan selalu melihat, SRCSdan OBJS. Saya setuju sebagian besar waktu, tetapi setiap orang harus tahu apa itu.
sherrellbc

@sherrellbc Apa yang orang "harus" ketahui dan apa yang sebenarnya diketahui orang sering kali merupakan dua hal yang berbeda: P
HelloGoodbye

7

Anda harus membuat target individual untuk setiap file C, lalu mencantumkan file header sebagai dependensi. Anda masih dapat menggunakan target umum Anda, dan hanya menempatkan .hdependensi setelahnya, seperti:

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

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

4

Pada dasarnya, Anda perlu membuat aturan makefile secara dinamis untuk membangun kembali file objek saat file header berubah. Jika Anda menggunakan gcc dan gnumake, ini cukup mudah; taruh saja sesuatu seperti:

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

di makefile Anda.


2
Saya agak memahami ini, kecuali bahwa (selain dari flag -MM dan -MG yang baru) saya tidak mengerti untuk apa baris teks samar regex lookin 'itu. Itu tidak akan membuat saya rekan satu tim senang ... ^ _ ^ Saya akan mencobanya dan melihat apakah saya mendapatkan hasil.
Nicholas Flynt

sed adalah kependekan dari "editor aliran" yang dapat memodifikasi aliran teks tanpa perlu menggunakan file. Ini adalah alat Unix standar dan lebih kecil dan lebih cepat sehingga digunakan lebih sering daripada awk atau perl.
Zan Lynx

Ah, ada masalahnya: Saya melakukan ini di bawah Windows.
Nicholas Flynt

3

Selain dan di atas apa yang dikatakan @mipadi, Anda juga dapat menjelajahi penggunaan opsi ' -M' untuk menghasilkan catatan dependensi. Anda bahkan dapat membuatnya menjadi file terpisah (mungkin 'depend.mk') yang kemudian Anda masukkan ke dalam makefile. Atau Anda dapat menemukan make dependaturan ' ' yang mengedit makefile dengan dependensi yang benar (istilah Google: "jangan hapus baris ini" dan bergantung).


1

Tidak ada jawaban yang berhasil untuk saya. Misalnya jawaban Martin Fido menyarankan gcc dapat membuat file dependensi, tetapi ketika saya mencoba itu menghasilkan file objek kosong (nol byte) untuk saya tanpa peringatan atau kesalahan apa pun. Ini mungkin bug gcc. aku berada

$ gcc --version gcc (GCC) 4.4.7 20120313 (Topi Merah 4.4.7-16)

Jadi, inilah Makefile lengkap saya yang cocok untuk saya; ini adalah kombinasi dari solusi + sesuatu yang tidak disebutkan oleh orang lain (misalnya, "aturan penggantian sufiks" ditetapkan sebagai .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

Perhatikan saya menggunakan .cc .. Makefile di atas mudah disesuaikan untuk file .c.

Perhatikan juga pentingnya dua baris ini:

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

jadi gcc dipanggil sekali untuk membuat file dependensi terlebih dahulu, lalu mengompilasi file .cc. Begitu seterusnya untuk setiap file sumber.


0

Solusi yang lebih sederhana: Cukup gunakan Makefile agar aturan kompilasi .c ke .o bergantung pada file header dan apa pun yang relevan dalam proyek Anda sebagai dependensi.

Misalnya, di Makefile di suatu tempat:

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

Saya percaya mkdepperintah itu adalah apa yang Anda inginkan. Ini sebenarnya memindai file .c untuk #includebaris dan membuat pohon ketergantungan untuk mereka. Saya yakin proyek Automake / Autoconf menggunakan ini secara default.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.