Makefile, başlık bağımlılıkları


98

Diyelim ki kuralı olan bir makefile'm var

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

Bir başlık dosyası her değiştiğinde * .o'nun yeniden oluşturulmasını istiyorum. Bir bağımlılıklar listesi oluşturmak yerine, herhangi bir başlık dosyası /includedeğiştiğinde, dizindeki tüm nesnelerin yeniden oluşturulması gerekir.

Bunu yerine getirmek için kuralı değiştirmenin güzel bir yolunu düşünemiyorum, önerilere açığım. Başlık listesinin sabit kodlanması gerekmiyorsa bonus puanları


Cevabımı aşağıya yazdıktan sonra ilgili listeye baktım ve buldum: stackoverflow.com/questions/297514/… ki bu bir kopya gibi görünüyor. Chris Dodd'un cevabı benimkine eşdeğer, ancak farklı bir adlandırma kuralı kullanıyor.
dmckee --- eski moderatör kedicik

Yanıtlar:


117

Bir GNU derleyicisi kullanıyorsanız, derleyici sizin için bir bağımlılıklar listesi oluşturabilir. Makefile parçası:

depend: .depend

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

include .depend

veya

depend: .depend

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

include .depend

nerede SRCSkaynak dosyalarının tüm listeye işaret eden bir değişkendir.

Araç da var makedepend, ama onu hiç bu kadar sevmedimgcc -MM


2
Bu hileyi sevdim, ancak dependyalnızca kaynak dosyalar değiştiğinde nasıl çalıştırabilirim? ... ne olursa olsun her zaman çalıştırmak gibi görünüyor
kovalamak

2
@chase: Kaynaklara bağlı olması gerektiği ve iki hedef için de bağımlılık sırasının yanlış olduğu durumlarda yanlışlıkla nesne dosyalarına bağımlılık yaptım. Hafızadan yazdığım şey bu. Şimdi dene.
dmckee --- eski moderatör kedi

4
Her dosyadan önce başka bir dizinde olduğunu göstermek için bir önek eklemenin bir yolu build/file.omu?
RiaD

SRCS'yi OBJECTS olarak değiştirdim, burada OBJECTS * .o dosyalarımın bir listesi. Bu, bağımlılığın her seferinde çalışmasını engelledi ve yalnızca başlık dosyalarındaki değişiklikleri yakaladı. Bu, önceki yorumlara aykırı görünüyor .. Bir şey mi kaçırıyorum?
BigBrownBear 00

2
Noktalı virgül neden gereklidir? Bunu onsuz veya -MF ./.d ile denersem, son bağımsız değişken olmamasına bağlı olarak, yalnızca son dosyanın bağımlılıklarını $ (SRCS) olarak kaydeder.
humodz

75

Cevapların çoğu şaşırtıcı derecede karmaşık veya hatalıdır. Ancak basit ve sağlam örnekler başka bir yerde yayınlanmıştır [ codereview ]. Kuşkusuz, gnu ön işlemcisi tarafından sağlanan seçenekler biraz kafa karıştırıcıdır. Ancak, tüm dizinlerin derleme hedefinden kaldırılması -MMbelgelenmiştir ve bir hata [ gpp ] değildir:

Varsayılan olarak CPP, ana girdi dosyasının adını alır, herhangi bir dizin bileşenini ve '.c' gibi herhangi bir dosya sonekini siler ve platformun olağan nesne son ekini ekler.

(Biraz daha yeni) -MMDseçeneği muhtemelen istediğiniz şeydir. Tamlık için, birden çok src dizinini destekleyen ve bazı yorumlarla dizinler oluşturan bir makefile örneği. Derleme dizinleri olmayan basit bir sürüm için bkz. [ 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)

Bu yöntem işe yarar çünkü tek bir hedef için birden fazla bağımlılık satırı varsa, bağımlılıklar basitçe birleştirilir, örneğin:

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

eşdeğerdir:

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

belirtildiği gibi: Tek bir hedef için birden fazla bağımlılık satırı Makefile?


1
Bu çözümü beğendim. Make bağımlı komutu yazmak istemiyorum. İşe yarar !!
Robert

1
OBJ değişken değerinde bir yazım hatası var: CPPokunmalıCPPS
ctrucza

1
Bu benim tercih ettiğim cevap; +1 sizin için. Bu, bu sayfadaki mantıklı ve yeniden derlemenin gerekli olduğu (gereksiz derlemeden kaçınarak, ancak yeterli) tüm durumları (görebildiğim kadarıyla) kapsayan tek sayfadır
Joost

1
Kutunun dışında, bu, hpp ve cpp'nin her ikisi de aynı dizinde olmasına rağmen benim için başlıkları bulamadı.
villasv

1
içinde kaynak dosyalarınız ( a.cpp, b.cpp) varsa ./src/, bu değişiklik olmaz $(OBJ)=./build/src/a.o ./build/src/b.omı?
galois

26

Ben yayınlanmıştır gibi burada gcc bağımlılıkları oluşturmak ve aynı anda derlemek için:

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

-include $(DEPS)

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

'-MF' parametresi, bağımlılıkların saklanacağı bir dosyayı belirtir.

'-İnclude' başındaki tire, Make'e .d dosyası yokken devam etmesini söyler (örn. İlk derlemede).

Gcc'de -o seçeneğiyle ilgili bir hata olduğunu unutmayın. Nesne dosya adını obj / _file__c.o diyecek şekilde ayarlarsanız, oluşturulan dosya .d obj / _file__c.o dosyasını değil .o dosyasını içerecektir.


4
Bunu denediğimde, tüm .o dosyalarımın boş dosyalar olarak oluşturulmasıyla sonuçlanıyor. Nesnelerim bir build alt klasöründe var (bu nedenle $ OBJECTS, build / main.o build / smbus.o build / etc ... içeriyor) ve bu kesinlikle görünen hatayla tanımladığınız gibi .d dosyalarını oluşturuyor, ancak kesinlikle .o dosyalarını oluşturmuyor, oysa -MM ve -MF'yi kaldırırsam yapar.
bobpaul

1
-MT kullanmak, her bağımlılık listesinin hedefini güncelleyen cevabınızın son satırlarındaki notu çözecektir.
Godric Seer

3
Çünkü @bobpaul man gccdiyor -MMima -E"ön işleme sonra durur", hangi. Bunun -MMDyerine ihtiyacınız var : stackoverflow.com/a/30142139/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

23

Şunun gibi bir şeye ne dersiniz:

includes = $(wildcard include/*.h)

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

Joker karakterleri doğrudan da kullanabilirsiniz, ancak bunlara birden fazla yerde ihtiyacım olduğunu bulma eğilimindeyim.

Her nesne dosyasının her başlık dosyasına bağlı olduğunu varsaydığından, bunun yalnızca küçük projelerde işe yaradığını unutmayın.


teşekkürler, bunu gerekenden çok daha karmaşık hale getiriyordum
Mike

15
Bu işe yarıyor, ancak bununla ilgili sorun, her nesne dosyasının yeniden derlenmesidir, her küçük değişiklik yapıldığında, yani, 100 kaynak / başlık dosyanız varsa ve yalnızca birinde küçük bir değişiklik yaparsanız, 100'ün tümü yeniden derlenir. .
Nicholas Hamilton

1
HİÇBİR başlık dosyası her değiştirildiğinde TÜM dosyaları yeniden oluşturduğu için bunun çok verimsiz bir yol olduğunu söylemek için cevabınızı gerçekten güncellemelisiniz. Diğer cevaplar çok daha iyi.
xaxxon

2
Bu çok kötü bir çözüm. Elbette küçük bir proje üzerinde çalışacaktır, ancak herhangi bir üretim boyutu ekibi ve yapısı için bu, korkunç derleme sürelerine yol açacak ve make clean allher seferinde koşmaya eşdeğer hale gelecektir .
Julien Guertault

Benim testimde bu hiç çalışmıyor. gccHat tüm yapılmaması, ancak yerleşik kural ( %o: %.ckural) yerine yürütülür.
Penghe Geng

4

Martin'in yukarıdaki çözümü harika çalışıyor, ancak alt dizinlerde bulunan .o dosyalarını işlemez. Godric, -MT bayrağının bu sorunu çözdüğünü, ancak aynı zamanda .o dosyasının doğru yazılmasını engellediğini belirtir. Aşağıdakiler bu sorunların her ikisini de halledecektir:

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

-include $(DEPS)

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

3

Bu, işi gayet iyi yapacak ve hatta belirtilen alt dizinleri bile işleyecektir:

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

gcc 4.8.3 ile test etti


3

İşte iki astar:

CPPFLAGS = -MMD
-include $(OBJS:.c=.d)

Bu, içindeki tüm nesne dosyalarınızın bir listesine sahip olduğunuz sürece varsayılan yapım tarifi ile çalışır OBJS.


1

Bu çözümü Michael Williamson tarafından kabul edilen cevaba tercih ediyorum, kaynaklarda + satır içi dosyalarda, sonra kaynaklarda + başlıklarda ve son olarak yalnızca kaynaklarda yapılan değişiklikleri yakalar. Buradaki avantaj, yalnızca birkaç değişiklik yapılırsa tüm kitaplığın yeniden derlenmemesidir. Birkaç dosya içeren bir proje için çok fazla dikkate alınmaz, eğer 10 veya 100 kaynağınız varsa, farkı göreceksiniz.

COMMAND= gcc -Wall -Iinclude ...

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

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

%.o: %.cpp
    $(COMMAND)

2
Bu, yalnızca başlık dosyalarınızda karşılık gelen uygulama dosyası dışında herhangi bir cpp dosyasının yeniden derlenmesini gerektirecek hiçbir şey yoksa çalışır.
matec

0

Aşağıdakiler benim için çalışıyor:

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

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<

0

Sophie'nin yanıtının , * .d dosyalarının farklı bir klasöre çıktısını almasına izin veren biraz değiştirilmiş bir versiyonu ( sadece bağımlılık dosyalarını oluşturan ilginç kısmı yapıştıracağım):

$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@

Parametrenin

-MT $@

oluşturulan * .d dosyalarındaki hedeflerin (yani nesne dosya adlarının) sadece dosya adını değil, * .o dosyalarının tam yolunu içermesini sağlamak için kullanılır.

-C ile kombinasyon halinde -MMD kullanıldığında (Sophie'nin sürümünde olduğu gibi ) bu parametreye neden ihtiyaç duyulmadığını bilmiyorum . Bu kombinasyonda, * .o dosyalarının tam yolunu * .d dosyalarına yazıyor gibi görünüyor. Bu kombinasyon olmadan, -MMD ayrıca herhangi bir dizin bileşeni olmadan yalnızca saf dosya adlarını * .d dosyalarına yazar. Belki birisi -MMD'nin -c ile birleştirildiğinde tam yolu yazdığını biliyordur. G ++ man sayfasında herhangi bir ipucu bulamadım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.