Farklı dizinlerde kaynak dosyalara sahip Makefiles


135

Dizin yapısının şöyle olduğu bir projem var:

                         $projectroot
                              |
              +---------------+----------------+
              |               |                |
            part1/          part2/           part3/
              |               |                |
       +------+-----+     +---+----+       +---+-----+
       |      |     |     |        |       |         |
     data/   src/  inc/  src/     inc/   src/       inc/

Kısmen / src (veya gerçekten herhangi bir yerde) olacak ve c / c ++ kaynak dosyalarında kısmen tamamlayabilecek / bağlayabilecek bir makefile nasıl yazmalıyım? / Src?

-I $ projectroot / part1 / src -I $ projectroot / part1 / inc -I $ projectroot / part2 / src gibi bir şey yapabilir miyim ...

Bu işe yarayacaksa, yapmanın daha kolay bir yolu var mı? İlgili parçaların her birinde makefile bulunan projeler gördüm. klasörler. [bu yazıda bash sözdizimindeki gibi soru işaretini kullandım]



1
Orijinal gnu kılavuzunda ( gnu.org/software/make/manual/html_node/Phony-Targets.html ) Sahte Hedefler altında bir örnek var recursive invocation, bu coule oldukça zarif.
Frank Nocke

Bu metin grafiğini oluşturmak için hangi aracı kullandınız?
Halid Hüseyin

Yanıtlar:


114

Geleneksel yolu sahip olmaktır Makefilealt dizinleri (her birinde part1, part2bağımsız olarak oluşturmak için izin vb.) Ayrıca, Makefileprojenin kök dizininde her şeyi oluşturan bir var. "Kök" Makefileaşağıdaki gibi görünür:

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

Yapma hedefindeki her satır kendi kabuğunda çalıştırıldığı için, dizin ağacını veya diğer dizinleri geri döndürme konusunda endişelenmenize gerek yoktur.

GNU make manual bölüm 5.7'ye bir göz atmanızı öneririm ; çok faydalıdır.


26
Bu, +$(MAKE) -C part1vb. Olmalıdır. Bu, Make'in iş kontrolünün alt dizinlerde çalışmasına izin verir.
efemient

26
Bu klasik bir yaklaşımdır ve yaygın olarak kullanılmaktadır, ancak proje büyüdükçe kötüleşen çeşitli şekillerde yetersizdir. Dave Hinton'ın izleyeceği işaretçi var.
dmckee --- eski moderatör kedi yavrusu

89

Bir alt dizinde başka bir alt dizindeki koda bağlı kodunuz varsa, en üst düzeyde tek bir makefile ile muhtemelen daha iyi durumda olursunuz.

Tam gerekçe için Özyinelemeli Olarak Zararlı Olarak Kabul Edilmesi konusuna bakın , ancak temelde bir dosyanın yeniden oluşturulması gerekip gerekmediğine karar vermek için ihtiyaç duyduğu tüm bilgilere sahip olmak istiyorsunuz ve yalnızca üçte biri hakkında söylerseniz buna sahip olmayacak. senin projen.

Yukarıdaki bağlantıya ulaşılamıyor gibi görünüyor. Aynı belgeye buradan ulaşılabilir:


3
Teşekkür ederim, bunun farkında değildim. "Sadece işe yarayan" ya da standart olarak kabul edilen yollar yerine, işleri yapmanın "doğru yolunu" bilmek çok yararlıdır.
tjklemz

3
Yinelemeli yapım, gerçekten işe yaramadığında zararlı olarak kabul edildi. Bu günlerde zararlı sayılmıyor, aslında autotools / automake daha büyük projeleri nasıl yönetiyor.
Edwin Buck

36

VPATH seçeneği kullanışlı olabilir, bu da kaynak kodu için hangi dizinlere bakılacağını söyler. Yine de her dahil etme yolu için -I seçeneğine ihtiyacınız olacak. Bir örnek:

CXXFLAGS=-Ipart1/inc -Ipart2/inc -Ipart3/inc
VPATH=part1/src:part2/src:part3/src

OutputExecutable: part1api.o part2api.o part3api.o

Bu, belirtilen VPATH dizinlerinden herhangi birinde eşleşen partXapi.cpp dosyalarını otomatik olarak bulur ve derler. Ancak, src dizininiz alt dizinlere bölündüğünde bu daha kullanışlıdır. Tanımladığınız şey için, diğerlerinin de söylediği gibi, muhtemelen her parça için bir makefile ile daha iyi durumdasınız, özellikle de her bölüm tek başına durabiliyorsa.


2
Bu basit mükemmel cevabın daha fazla oy almadığına inanamıyorum. Benden +1.
Nicholas Hamilton

2
Alt klasörlerdeki birkaç farklı proje için daha yüksek bir dizinde bazı ortak kaynak dosyalarım vardı, VPATH=..benim için çalıştı!
EkriirkE

23

Gerekli cpp dosyalarını diğer dizinlerde derlemek için kök Makefile dosyanıza kurallar ekleyebilirsiniz. Aşağıdaki Makefile örneği, sizi olmak istediğiniz yere götürmek için iyi bir başlangıç ​​olmalıdır.

CC = g ++
HEDEF = cppTest
OTHERDIR = .. / .. / someotherpath / in / proje / src

KAYNAK = cppTest.cpp
KAYNAK = $ (DİĞER) /file.cpp

## Son kaynak tanımı
INCLUDE = -I./ $ (AN_INCLUDE_DIR)  
INCLUDE = -I. $ (DİĞER) /../ inc
## end more içerir

Vpath = $ (OTHERDIR)
OBJ = $ ($ (addsuffix ../obj/, $ (dir $ (SOURCE))), $ (notdir $ (KAYNAK: .cpp = .o)) 

## Bağımlılık hedefini src dizinine göre ../.dep olacak şekilde düzeltin
DEPENDS = $ ($ (addsuffix ../.dep/, $ (dir $ (KAYNAK))), $ (notdir $ (KAYNAK: .cpp = .d))

## Varsayılan kural yürütülür
tümü: $ (TARGET)
        @doğru

## Temiz Kural
temiz:
        @ -rm -f $ (HEDEF) $ (OBJ) $ (DEPENDS)


## Gerçek hedefi yapmak için kural
$ (HEDEF): $ (OBJ)
        @echo "============="
        @echo "$ @ hedefini bağlama"
        @echo "============="
        @ $ (CC) $ (CFLAGS) -o $ @ $ ^ $ (LIBS)
        @echo - Bağlantı bitti -

## Genel derleme kuralı
% .o:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo "$ <Derleniyor"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @


## cpp dosyalarından nesne dosyaları için kurallar
## Her dosya için nesne dosyası obj dizinine yerleştirilir
## gerçek kaynak dizinden bir seviye yukarı.
../obj/%.o:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo "$ <Derleniyor"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @

# "Diğer dizin" kuralı "Diğer" dizin başına birine ihtiyacınız olacak
$ (DİĞER) /../ obj /%. O:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo "$ <Derleniyor"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @

## Bağımlılık kuralları yapın
../.dep/%.d:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo $ * için bağımlılıklar dosyası oluşturma o
        @ $ (SHELL) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *. o ^ .. / obj / $ *. o ^"> $ @ '

## "Diğer" dizin için bağımlılık kuralı
$ (DİĞER) /../. Dep /%. D:% .cpp
        @mkdir -p $ (dir $ @)
        @echo "============="
        @echo $ * için bağımlılıklar dosyası oluşturma o
        @ $ (SHELL) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *. o ^ $ (DİĞERDİR) /../ obj / $ *. o ^"> $ @ '

## Bağımlılık dosyalarını dahil edin
- $ (DEPENDS) dahil


7
Bunun şimdiye kadar oldukça eski olduğunu biliyorum, ancak hem SOURCE hem de INCLUDE bir satır sonra başka bir atama tarafından değiştirilmiyor mu?
skelliam

@skelliam evet, öyle.
nabroyan

1
Bu yaklaşım KURU ilkesini ihlal etmektedir. Her bir "diğer dizinin" kodunu çoğaltmak kötü bir fikir
İsa H

20

Kaynaklar birçok klasöre yayılmışsa ve tek tek Makefile'lere sahip olmak mantıklıysa, daha önce önerildiği gibi yinelemeli yapmak iyi bir yaklaşımdır, ancak daha küçük projeler için Makefile'daki tüm kaynak dosyalarını göreceli yollarıyla listelemeyi daha kolay buluyorum için Makefile böyle:

# common sources
COMMON_SRC := ./main.cpp \
              ../src1/somefile.cpp \
              ../src1/somefile2.cpp \
              ../src2/somefile3.cpp \

Daha sonra VPATHşu şekilde ayarlayabilirim :

VPATH := ../src1:../src2

Sonra nesneleri oluşturuyorum:

COMMON_OBJS := $(patsubst %.cpp, $(ObjDir)/%$(ARCH)$(DEBUG).o, $(notdir $(COMMON_SRC)))

Şimdi kural basit:

# the "common" object files
$(ObjDir)/%$(ARCH)$(DEBUG).o : %.cpp Makefile
    @echo creating $@ ...
    $(CXX) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<

Ve çıktı oluşturmak daha da kolay:

# This will make the cbsdk shared library
$(BinDir)/$(OUTPUTBIN): $(COMMON_OBJS)
    @echo building output ...
    $(CXX) -o $(BinDir)/$(OUTPUTBIN) $(COMMON_OBJS) $(LFLAGS)

Aşağıdakilerle VPATHnesil otomatik hale getirilebilir:

VPATH := $(dir $(COMMON_SRC))

Veya sortkopyaları kaldıran gerçeğini kullanarak (önemli olmamasına rağmen):

VPATH := $(sort  $(dir $(COMMON_SRC)))

bu, bazı özel kitaplıklar eklemek ve bunları ayrı bir klasörde bulundurmak istediğiniz küçük projeler için harika çalışır. Çok teşekkür ederim
zitroneneis

6

Make kullanmanın (yinelemeli veya değil) genellikle kaçınmak isteyebileceğiniz bir şey olduğunu belirtmenin daha iyi olacağını düşünüyorum, çünkü günümüz araçlarına kıyasla öğrenmesi, sürdürmesi ve ölçeklemesi zor.

Bu harika bir araçtır, ancak doğrudan kullanımının 2010 + 'da eski olduğu düşünülmelidir.

Tabii özel bir ortamda, yani eski bir proje ile vb. Çalışmıyorsanız.

Bir IDE, CMake veya sert çekirdek iseniz, Autotools kullanın .

(olumsuz oylar nedeniyle düzenlendi, ty Honza işaret ettiği için)


1
Olumsuz oyların açıklanması güzel olurdu. Ben de Makefiles'ımı kendim yapardım.
IlDan

2
"Bunu yapma" önerisi için oy veriyorum - KDevelop, Make ve diğer derleme sistemlerini yapılandırmak için oldukça grafik bir arayüze sahip ve Makefiles'ı kendim yazarken, KDevelop iş arkadaşlarıma verdiğim şey - ama ben Autotools bağlantısının yardımcı olduğunu düşünmeyin. Autotools'u anlamak için m4, libtool, automake, autoconf, shell, make ve temelde tüm yığını anlamanız gerekir.
ephemient

7
Olumsuz oylar muhtemelen kendi sorunuzu yanıtladığınız içindir. Soru Makefiles kullanıp kullanmamayla ilgili değildi. Nasıl yazılacağı ile ilgiliydi.
Honza

8
Birkaç cümleyle modern araçların hayatı Makefile yazmaktan nasıl daha kolay hale getirdiğini açıklasanız iyi olurdu. Daha yüksek düzeyde bir soyutlama sağlıyorlar mı? ya da ne?
Abhishek Anand

1
xterm, vi, bash, makefile == dünyayı yönetin, cmake kodu kıramayan insanlar içindir.
μολὼν.λαβέ

2

RC'nin gönderisi SÜPER faydalı oldu. $ (Dir $ @) işlevini kullanmayı hiç düşünmedim, ama tam olarak yapmam gerekeni yaptı.

ParentDir'de, içinde kaynak dosyalar bulunan bir dizi dizine sahip olun: dirA, dirB, dirC. Çeşitli dosyalar diğer dizinlerdeki nesne dosyalarına bağlıdır, bu yüzden bir dizinden bir dosya oluşturabilmek ve bu bağımlılıkla ilişkili makefile'ı çağırarak bu bağımlılığı yapmasını istedim.

Esasen, parentDir'de RC'lere benzer genel bir kurala sahip (diğer birçok şeyin yanı sıra) bir Makefile yaptım:

%.o : %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@

Her alt dizin, bu genel kuralı miras almak için bu üst düzey makefile dosyasını içeriyordu. Her bir alt dizinin Makefile dosyasında, her bir dosyanın bağlı olduğu her şeyi takip edebilmem için her dosya için özel bir kural yazdım.

Ne zaman bir dosya oluşturmaya ihtiyacım olsa, bu kuralı (esasen) özyinelemeli olarak herhangi bir / tüm bağımlılıkları oluşturmak için kullandım. Mükemmel!

NOT: Bu görevi daha da sezgisel olarak yapan "makepp" adında bir yardımcı program var, ancak taşınabilirlik uğruna ve başka bir araca bağlı kalmadan, bunu bu şekilde yapmayı seçtim.

Bu yardımcı olur umarım!


2

Make'in Yinelemeli Kullanımı

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

Bu, makeişlere bölünmeye ve birden çok çekirdek kullanılmasına izin verir


2
Bu nasıl bir şey yapmaktan daha iyi make -j4?
devin

1
@devin gördüğüm kadarıyla, daha iyi değil , sadece iş kontrolünü kullanmayı sağlıyor make . Ayrı bir yapım süreci çalıştırıldığında, iş kontrolüne tabi değildir.
Michael Pankov 13

1

Bunun gibi bir şey arıyordum ve bazı denemeler ve düşmelerden sonra kendi makefile'ımı oluşturdum, bunun "deyimsel yol" olmadığını biliyorum ama anlamaya başladım ve bu benim için işe yarıyor, belki projenizde deneyebilirsiniz.

PROJ_NAME=mono

CPP_FILES=$(shell find . -name "*.cpp")

S_OBJ=$(patsubst %.cpp, %.o, $(CPP_FILES))

CXXFLAGS=-c \
         -g \
        -Wall

all: $(PROJ_NAME)
    @echo Running application
    @echo
    @./$(PROJ_NAME)

$(PROJ_NAME): $(S_OBJ)
    @echo Linking objects...
    @g++ -o $@ $^

%.o: %.cpp %.h
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS) -o $@

main.o: main.cpp
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS)

clean:
    @echo Removing secondary things
    @rm -r -f objects $(S_OBJ) $(PROJ_NAME)
    @echo Done!

Bunun basit olduğunu ve bazı insanlar için bayraklarımın yanlış olduğunu biliyorum, ancak söylediğim gibi bu, projemi birden çok dizinde derleyen ve ardından kutumu oluşturmak için hepsini birbirine bağlayan ilk Makefile'ım.

Önerileri kabul ediyorum: D


-1

Kullanmayı öneririm autotools:

//## Özyinelemesiz yapım kullanıldığında çarpışmaları önlemek için, oluşturulan nesne dosyalarını (.o) kaynak dosyalarıyla aynı dizine yerleştirin.

AUTOMAKE_OPTIONS = subdir-objects

sadece Makefile.amdiğer oldukça basit şeylere dahil.

İşte eğitim .

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.