it-swarm.com.de

So generieren Sie ein Makefile mit Source in Unterverzeichnissen mit nur einem Makefile

Ich habe Quelle in einer Reihe von Unterverzeichnissen wie:

src/widgets/Apple.cpp
src/widgets/knob.cpp
src/tests/blend.cpp
src/ui/flash.cpp

Im Stammverzeichnis des Projekts möchte ich ein einzelnes Makefile mit einer Regel wie der folgenden generieren:

%.o: %.cpp
   $(CC) -c $<

build/test.exe: build/widgets/Apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o
   $(LD) build/widgets/Apple.o .... build/ui/flash.o -o build/test.exe

Wenn ich das versuche, findet es keine Regel für build/widgets/Apple.o. Kann ich etwas ändern, so dass% .o:% .cpp verwendet wird, wenn build/widgets/Apple.o erstellt werden muss?

54
Jeroen Dirks

Der Grund ist, dass Ihre Regel

%.o: %.cpp
       ...

erwartet, dass sich die CPP-Datei in demselben Verzeichnis befindet wie die CPP-Datei in Ihrem Gebäude. Da test.exe in Ihrem Fall von build/widgets/Apple.o (etc) abhängt, erwartet make, dass Apple.cpp build/widgets/Apple.cpp ist.

Sie können VPATH verwenden, um dies zu beheben:

VPATH = src/widgets

BUILDDIR = build/widgets

$(BUILDDIR)/%.o: %.cpp
      ...

Wenn Sie versuchen, "build/widgets/Apple.o" zu erstellen, sucht make nach Apple.cpp in VPATH. Beachten Sie, dass die Erstellungsregel spezielle Variablen verwenden muss, um auf die tatsächlichen make-Ergebnisse des Dateinamens zuzugreifen:

$(BUILDDIR)/%.o: %.cpp
        $(CC) $< -o [email protected]

Dabei wird "$ <" zu dem Pfad erweitert, in dem make die erste Abhängigkeit gefunden hat.

Beachten Sie auch, dass dadurch alle .o-Dateien in build/widgets erstellt werden. Wenn Sie die Binärdateien in verschiedenen Verzeichnissen erstellen möchten, können Sie so etwas tun

build/widgets/%.o: %.cpp
        ....

build/ui/%.o: %.cpp
        ....

build/tests/%.o: %.cpp
        ....

Ich würde empfehlen, " vorgefertigte Befehlssequenzen " zu verwenden, um zu vermeiden, dass die eigentliche Compiler-Erstellungsregel wiederholt wird:

define cc-command
$(CC) $(CFLAGS) $< -o [email protected]
endef

Sie können dann mehrere Regeln wie diese haben:

build1/foo.o build1/bar.o: %.o: %.cpp
    $(cc-command)

build2/frotz.o build2/fie.o: %.o: %.cpp
    $(cc-command)
67
JesperE

Das macht den Trick:

CC        := g++
LD        := g++

MODULES   := widgets test ui
SRC_DIR   := $(addprefix src/,$(MODULES))
BUILD_DIR := $(addprefix build/,$(MODULES))

SRC       := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp))
OBJ       := $(patsubst src/%.cpp,build/%.o,$(SRC))
INCLUDES  := $(addprefix -I,$(SRC_DIR))

vpath %.cpp $(SRC_DIR)

define make-goal
$1/%.o: %.cpp
    $(CC) $(INCLUDES) -c $$< -o [email protected]
endef

.PHONY: all checkdirs clean

all: checkdirs build/test.exe

build/test.exe: $(OBJ)
    $(LD) $^ -o [email protected]


checkdirs: $(BUILD_DIR)

$(BUILD_DIR):
    @mkdir -p [email protected]

clean:
    @rm -rf $(BUILD_DIR)

$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir))))

Dieses Makefile setzt voraus, dass Sie Ihre Include-Dateien in den Quellverzeichnissen haben. Außerdem wird geprüft, ob die Erstellungsverzeichnisse vorhanden sind, und sie werden erstellt, wenn sie nicht vorhanden sind.

Die letzte Zeile ist die wichtigste. Es erstellt die impliziten Regeln für jeden Build mit der Funktion make-goal, und es ist nicht notwendig, sie einzeln zu schreiben

Sie können auch die automatische Generierung von Abhängigkeiten mithilfe von nach Tromeys Methode hinzufügen

65
Manzill0

Die Angabe [email protected] Enthält den gesamten (relativen) Pfad zur Quelldatei, der wiederum zur Erstellung des Objektnamens (und damit des relativen Pfads) verwendet wird.

Wir gebrauchen:

#####################
# rules to build the object files
$(OBJDIR_1)/%.o: %.c
    @$(ECHO) "$< -> [email protected]"
    @test -d $(OBJDIR_1) || mkdir -pm 775 $(OBJDIR_1)
    @test -d $(@D) || mkdir -pm 775 $(@D)
    @-$(RM) [email protected]
    $(CC) $(CFLAGS) $(CFLAGS_1) $(ALL_FLAGS) $(ALL_DEFINES) $(ALL_INCLUDEDIRS:%=-I%) -c $< -o [email protected]

Dadurch wird ein Objektverzeichnis mit dem in $(OBJDIR_1) angegebenen Namen und Unterverzeichnissen entsprechend den Unterverzeichnissen in der Quelle erstellt.

Zum Beispiel (nimm objs als oberstes Objektverzeichnis an), in Makefile:

widget/Apple.cpp
tests/blend.cpp

ergibt folgendes Objektverzeichnis:

objs/widget/Apple.o
objs/tests/blend.o
4
Tim Ruijs

Dies geschieht ohne schmerzhafte Manipulationen oder mehrere Befehlssequenzen:

 Build /%. o: Quelle /%. cpp 
 Quelle /%. o: Quelle /%. cpp 
%. o: 
 $ (CC) -c $ <-o $ @ 
 
 build/test.exe: build/widgets/Apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash .o 
 $ (LD) $ ^ -o $ @ 

JasperE hat erklärt, warum "% .o:% .cpp" nicht funktioniert. Diese Version hat eine Musterregel (% .o :) mit Befehlen und keinen Befehlen und zwei Musterregeln (build /% .o: und src /% .o :) mit Befehlen und keinen Befehlen. (Beachten Sie, dass ich die Regel src /%. O eingegeben habe, um mit src/ui/flash.o umzugehen, vorausgesetzt, das war kein Tippfehler für build/ui/flash.o. Wenn Sie es also nicht benötigen, können Sie es Lass es aus.)

build/test.exe benötigt build/widgets/Apple.o,
build/widgets/Apple.o sieht aus wie build /% .o, benötigt also src /% .cpp (in diesem Fall src/widgets/Apple.cpp),
build/widgets/Apple.o sieht ebenfalls wie% .o aus, führt also den CC-Befehl aus und verwendet die soeben gefundenen Voraussetzungen (src/widgets/Apple.cpp), um das Ziel zu erstellen (build/widgets) /Apple.o)

3
Beta

Dies ist ein weiterer Trick.

In main 'Makefile' definieren Sie SRCDIR für jedes Quellverzeichnis und fügen 'makef.mk' für jeden Wert von SRCDIR hinzu. In jedes Quellverzeichnis legen Sie die Datei 'files.mk' mit einer Liste der Quelldateien und Kompilierungsoptionen für einige von ihnen. In main 'Makefile' kann man Kompilieroptionen definieren und Dateien für jeden Wert von SRCDIR ausschließen.

Makefile:

PRG             := prog-name

OPTIMIZE        := -O2 -fomit-frame-pointer

CFLAGS += -finline-functions-called-once
LDFLAGS += -Wl,--gc-section,--reduce-memory-overheads,--relax


.DEFAULT_GOAL   := hex

OBJDIR          := obj

MK_DIRS         := $(OBJDIR)


SRCDIR          := .
include         makef.mk

SRCDIR := crc
CFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE
ASFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE
include makef.mk

################################################################

CC              := avr-gcc -mmcu=$(MCU_TARGET) -I.
OBJCOPY         := avr-objcopy
OBJDUMP         := avr-objdump

C_FLAGS         := $(CFLAGS) $(REGS) $(OPTIMIZE)
CPP_FLAGS       := $(CPPFLAGS) $(REGS) $(OPTIMIZE)
AS_FLAGS        := $(ASFLAGS)
LD_FLAGS        := $(LDFLAGS) -Wl,-Map,$(OBJDIR)/$(PRG).map


C_OBJS          := $(C_SRC:%.c=$(OBJDIR)/%.o)
CPP_OBJS        := $(CPP_SRC:%.cpp=$(OBJDIR)/%.o)
AS_OBJS         := $(AS_SRC:%.S=$(OBJDIR)/%.o)

C_DEPS          := $(C_OBJS:%=%.d)
CPP_DEPS        := $(CPP_OBJS:%=%.d)
AS_DEPS         := $(AS_OBJS:%=%.d)

OBJS            := $(C_OBJS) $(CPP_OBJS) $(AS_OBJS)
DEPS            := $(C_DEPS) $(CPP_DEPS) $(AS_DEPS)


hex:  $(PRG).hex
lst:  $(PRG).lst


$(OBJDIR)/$(PRG).elf : $(OBJS)
    $(CC) $(C_FLAGS) $(LD_FLAGS) $^ -o [email protected]

%.lst: $(OBJDIR)/%.elf
    [email protected] [email protected] 2> /dev/nul
    $(OBJDUMP) -h -s -S $< > [email protected]

%.hex: $(OBJDIR)/%.elf
    [email protected] [email protected] 2> /dev/nul
    $(OBJCOPY) -j .text -j .data -O ihex $< [email protected]


$(C_OBJS) : $(OBJDIR)/%.o : %.c Makefile
    $(CC) -MMD -MF [email protected] -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o [email protected]
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected]
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected]
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected]
    [email protected] -f [email protected]

$(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile
    $(CC) -MMD -MF [email protected] -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o [email protected]
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected]
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected]
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected]
    [email protected] -f [email protected]

$(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile
    $(CC) -MMD -MF [email protected] -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o [email protected]
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected]
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected]
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected]
    [email protected] -f [email protected]


clean:
    [email protected] -rf $(OBJDIR)/$(PRG).elf
    [email protected] -rf $(PRG).lst $(OBJDIR)/$(PRG).map
    [email protected] -rf $(PRG).hex $(PRG).bin $(PRG).srec
    [email protected] -rf $(PRG)_eeprom.hex $(PRG)_eeprom.bin $(PRG)_eeprom.srec
    [email protected] -rf $(MK_DIRS:%=%/*.o) $(MK_DIRS:%=%/*.o.d)
    [email protected] -f tags cscope.out

#   -rm -rf $(OBJDIR)/*
#   -rm -rf $(OBJDIR)
#   -rm $(PRG)


tag: tags
tags: $(SRC_FILES)
    if [ -e tags ] ; then ctags -u $? ; else ctags $^ ; fi
    cscope -U -b $^


# include dep. files
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEPS)
endif


# Create directory
$(Shell mkdir $(MK_DIRS) 2>/dev/null)

makef.mk

SAVE_C_SRC := $(C_SRC)
SAVE_CPP_SRC := $(CPP_SRC)
SAVE_AS_SRC := $(AS_SRC)

C_SRC :=
CPP_SRC :=
AS_SRC :=


include $(SRCDIR)/files.mk
MK_DIRS += $(OBJDIR)/$(SRCDIR)


clear_name = $(subst /,_,$(1))


define rename_var
$(2)_$(call clear_name,$(SRCDIR))_$(call clear_name,$(1)) := \
    $($(subst _,,$(2))_$(call clear_name,$(SRCDIR))) $($(call clear_name,$(1)))
$(call clear_name,$(1)) :=
endef


define proc_lang

Origin_SRC_FILES := $($(1)_SRC)

ifneq ($(strip $($(1)_ONLY_FILES)),)
$(1)_SRC := $(filter $($(1)_ONLY_FILES),$($(1)_SRC))
else

ifneq ($(strip $(ONLY_FILES)),)
$(1)_SRC := $(filter $(ONLY_FILES),$($(1)_SRC))
else
$(1)_SRC := $(filter-out $(EXCLUDE_FILES),$($(1)_SRC))
endif

endif

$(1)_ONLY_FILES :=
$(foreach name,$($(1)_SRC),$(eval $(call rename_var,$(name),$(1)_FLAGS)))
$(foreach name,$(Origin_SRC_FILES),$(eval $(call clear_name,$(name)) :=))

endef


$(foreach lang,C CPP AS, $(eval $(call proc_lang,$(lang))))


EXCLUDE_FILES :=
ONLY_FILES :=


SAVE_C_SRC += $(C_SRC:%=$(SRCDIR)/%)
SAVE_CPP_SRC += $(CPP_SRC:%=$(SRCDIR)/%)
SAVE_AS_SRC += $(AS_SRC:%=$(SRCDIR)/%)

C_SRC := $(SAVE_C_SRC)
CPP_SRC := $(SAVE_CPP_SRC)
AS_SRC := $(SAVE_AS_SRC)

./files.mk

C_SRC   := main.c
CPP_SRC :=
AS_SRC  := timer.S

main.c += -DDEBUG

./crc/files.mk

C_SRC    := byte-modbus-crc.c byte-crc8.c
AS_SRC   := modbus-crc.S crc8.S modbus-crc-table.S crc8-table.S

byte-modbus-crc.c += --std=gnu99
byte-crc8.c       += --std=gnu99
3
iLya

Hier ist meine Lösung, inspiriert von Betas Antwort. Es ist einfacher als die anderen vorgeschlagenen Lösungen

Ich habe ein Projekt mit mehreren C-Dateien, die in vielen Unterverzeichnissen gespeichert sind. Beispielsweise:

src/lib.c
src/aa/a1.c
src/aa/a2.c
src/bb/b1.c
src/cc/c1.c

Hier ist mein Makefile (im src/ Verzeichnis):

# make       -> compile the shared library "libfoo.so"
# make clean -> remove the library file and all object files (.o)
# make all   -> clean and compile
SONAME  = libfoo.so
SRC     = lib.c   \
          aa/a1.c \
          aa/a2.c \
          bb/b1.c \
          cc/c1.c
# compilation options
CFLAGS  = -O2 -g -W -Wall -Wno-unused-parameter -Wbad-function-cast -fPIC
# linking options
LDFLAGS = -shared -Wl,-soname,$(SONAME)

# how to compile individual object files
OBJS    = $(SRC:.c=.o)
.c.o:
    $(CC) $(CFLAGS) -c $< -o [email protected]

.PHONY: all clean

# library compilation
$(SONAME): $(OBJS) $(SRC)
    $(CC) $(OBJS) $(LDFLAGS) -o $(SONAME)

# cleaning rule
clean:
    rm -f $(OBJS) $(SONAME) *~

# additional rule
all: clean lib

Dieses Beispiel eignet sich gut für eine gemeinsam genutzte Bibliothek und sollte für jeden Kompilierungsprozess sehr einfach anzupassen sein.

3
Amaury Bouchard

Normalerweise erstellen Sie in jedem Unterverzeichnis ein Makefile und schreiben in das Makefile der obersten Ebene, um make in den Unterverzeichnissen aufzurufen.

Diese Seite kann helfen: http://www.gnu.org/software/make/

0
stephanea