SRC, OBJ ve BIN alt dizinlerine sahip C projeleri için nasıl Makefile oluşturabilirim?


96

Birkaç ay önce, Makefileokul ödevleri için aşağıdaki jeneriği buldum :

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2010-11-05
#
# Changelog :
#   0.01 - first version
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc -std=c99 -c
# compiling flags here
CFLAGS   = -Wall -I.

LINKER   = gcc -o
# linking flags here
LFLAGS   = -Wall

SOURCES  := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS  := $(SOURCES:.c=*.o)
rm       = rm -f

$(TARGET): obj
    @$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

obj: $(SOURCES) $(INCLUDES)
    @$(CC) $(CFLAGS) $(SOURCES)
    @echo "Compilation complete!"

clean:
    @$(rm) $(TARGET) $(OBJECTS)
    @echo "Cleanup complete!"

Bu, temelde dosyaları ve yürütülebilir dosyaları aynı klasörde oluşturmak için her .cve .hdosyayı derler ..oprojectname

Şimdi, bunu biraz zorlamak istiyorum. Aşağıdaki dizin yapısıyla bir C projesini derlemek için nasıl Makefile yazabilirim?

 ./
 ./Makefile
 ./src/*.c;*.h
 ./obj/*.o
 ./bin/<executable>

Diğer bir deyişle, ben den derler C kaynakları bir Makefile olmasını isterdim ./src/içine ./obj/bağlantı herşeyden daha ve de yürütülebilir oluşturmak ./bin/.

Farklı Makefile'ları okumaya çalıştım, ancak bunları yukarıdaki proje yapısı için çalıştıramıyorum; bunun yerine proje her türlü hatayı derleyemez. Elbette, tam gelişmiş IDE (Monodevelop, Anjuta, vb.) Kullanabilirim, ancak dürüst olmak gerekirse gEdit ve iyi ol 'terminaline bağlı kalmayı tercih ederim.

Bana çalışan bir çözüm verebilecek veya bunun nasıl yapılabileceğine dair net bilgi verebilecek bir guru var mı? Teşekkür ederim!

** GÜNCELLEME (v4) **

Son çözüm :

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2011-08-10
#
# Changelog :
#   2010-11-05 - first version
#   2011-08-10 - added structure : sources, objects, binaries
#                thanks to http://stackoverflow.com/users/128940/beta
#   2017-04-24 - changed order of linker params
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc
# compiling flags here
CFLAGS   = -std=c99 -Wall -I.

LINKER   = gcc
# linking flags here
LFLAGS   = -Wall -I. -lm

# change these to proper directories where each file should be
SRCDIR   = src
OBJDIR   = obj
BINDIR   = bin

SOURCES  := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm       = rm -f


$(BINDIR)/$(TARGET): $(OBJECTS)
    @$(LINKER) $(OBJECTS) $(LFLAGS) -o $@
    @echo "Linking complete!"

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    @$(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

.PHONY: clean
clean:
    @$(rm) $(OBJECTS)
    @echo "Cleanup complete!"

.PHONY: remove
remove: clean
    @$(rm) $(BINDIR)/$(TARGET)
    @echo "Executable removed!"

Buradaki özel soru nedir?
Oliver Charlesworth

Ne yapmak istediğini anladığımdan emin değilim.
Tom

Güncellendi Makefile. Yaklaşıyorum, ancak otomatik değişkenlerle ilgili sorun yaşıyorum, bu yüzden yine de görünüyor
Yanick Rochon

Az önce bir çözüm buldum. Birisi daha iyi bir şey bulmayı umarsa, Makefile hala geliştirilebilir.
Yanick Rochon

2
@YanickRochon İngilizce becerilerinizi eleştirmek istemedim. Ama PHONY hedefleri, kesinlikle MUZ yazamazsınız herhangi bir anlam ifade etmek için;) gnu.org/software/make/manual/html_node/Phony-Targets.html
joni

Yanıtlar:


34

İlk olarak, $(OBJECTS)kuralınız sorunlu, çünkü:

  1. bu , her nesnenin tüm kaynakları ön koşullarını oluşturuyor,
  2. genellikle yanlış kaynağı kullanır ( file1.ove ile keşfettiğiniz gibi file2.o)
  3. nesnelerde durmak yerine yürütülebilir dosyalar oluşturmaya çalışır ve
  4. target ( foo.o) 'nin adı , kuralın gerçekte üreteceği şey değildir ( obj/foo.o).

Aşağıdakileri öneririm:

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

$(TARGET)Kuralı hedef adı aslında kural oluşturur açıklayın etmediğini aynı sorun var. Bu nedenle, makebirkaç kez yazarsanız , Make için bir neden olmasa da her seferinde hedefi yeniden oluşturur. Küçük bir değişiklik şunları düzeltir:

$(BINDIR)/$(TARGET): $(OBJECTS)
    $(LINKER) $@ $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

Hepsi bu kadar düzene girdiğinde, daha karmaşık bağımlılık işlemeyi düşünebilirsiniz; başlık dosyalarından birini değiştirirseniz, bu makefile hangi nesnelerin / yürütülebilir dosyaların yeniden oluşturulması gerektiğini bilmez. Ama bu başka bir günü bekleyebilir.

DÜZENLEME:
Üzgünüm, $(OBJECTS)yukarıdaki kuralın bir kısmını atladım ; Düzeltdim. (Keşke bir kod örneğinin içinde "strike" kullanabilseydim.)


Önerdiğiniz değişikliklerle şunu anlıyorum:obj/file1.o: In function 'main': \n main.c:(.text+0x0): multiple definition of 'main' \n obj/main.o:main.c:(.text+0x0): first defined here
Yanick Rochon

@Yanick Rochon: Birden fazla mainişleviniz var mı? Belki biri içeri file1.cve biri içeride main.c? Öyleyse, bu nesneleri bağlayamazsınız; mainbir yürütülebilir dosyada yalnızca bir tane olabilir .
Beta

Hayır. Soruda yayınladığım son sürümle her şey yolunda gidiyor. Makefile'ımı önerdiğiniz şeye değiştirdiğimde (ve söylediklerinizin faydalarını anlıyorum) elde ettiğim şey bu. Az önce yapıştırdım file1.cama projenin her bir dosyasına aynı mesajı veriyor. Ve main.colan sadece bir ana fonksiyonu olan ... ve main.cithalat file1.hve file2.h(arasında hiçbir ilişki yoktur file1.cve file2.c) ama sorun geliyor şüpheliyim.
Yanick Rochon

@Yanick Rochon: Kuralımın ilk satırını yapıştırırken bir hata yaptım $(OBJECTS); Ben düzenledim. Kötü çizgi ile bir hata aldım, ama senin olan değil ...
Beta

6

Ekleyebilir -Iderleyici kaynak dosyaları bakmak gerektiğini belirtmek için derleyici bayrakları (CFLAGS) bayrağı ve -o bayrağı ikili bırakılmalıdır yerlerini belirtmek için:

CFLAGS   = -Wall -I./src
TARGETPATH = ./bin

$(TARGET): obj
    @$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

Nesne dosyalarını objdizine bırakmak için , -oderleme sırasında seçeneğini kullanın . Ayrıca, $@ve $< otomatik değişkenlere bakın .

Örneğin, bu basit Makefile dosyasını düşünün.

CFLAGS= -g -Wall -O3                                                            
OBJDIR= ./obj

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)

%.o: %.c 
   $(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@

Güncelle>

Makefile dosyanıza bakarak -obayrağı kullandığınızı anlıyorum . İyi. Kullanmaya devam edin, ancak çıktı dosyasının nereye yazılacağını belirtmek için bir hedef dizin değişkeni ekleyin.


daha spesifik olabilir misin? Eklemekte ifade ediyor -l ...için CFLAGSve zaten var ... -olinker'a argüman ( LINKER)
Yanick Rochon

Evet, CFLAGS ve evet, -o kullanmaya devam edin, sadece TARGETPATH ​​değişkenini ekleyin.
Tom

Teşekkürler, değişiklikleri yaptım, ancak hala bazı şeyleri kaçırıyorum gibi görünüyor (soruyla ilgili güncellemeye bakın)
Yanick Rochon

sadece make, Makefile'ın bulunduğu yerden
Yanick Rochon

Yürütülmekte olan komutu okuyamıyor musunuz? örneğin gcc -c yadayada. Beklediğinizi içermeyen bir değişken olduğundan oldukça eminim
Tom

-1

Bu günlerde makefile yazmayı bıraktım, eğer niyetiniz devam etmeyi öğrenmekse, yoksa eclipse CDT ile birlikte gelen iyi bir makefile jeneratörünüz var. Yapı ağacınızda bazı sürdürülebilirlik / çoklu proje desteği istiyorsanız, aşağıdakilere bir göz atın -

https://github.com/dmoulding/boilermake Bunu oldukça iyi buldum ..!


3
Fikir temelli. OP'nin sorusuna cevap vermiyor. Eclipse ortamını varsayar.
Nathaniel Johnson
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.