Come generare un Makefile con origine sub-directory, utilizzando i makefile

Mi hanno origine in un gruppo di sottodirectory come:

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

Nella root del progetto voglio generare un unico file Makefile utilizzando una regola simile:

%.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

Quando cerco di non trovare una regola per costruire/widget/apple.o. Posso modificare qualcosa in modo che l’ %.o: %.il cpp è utilizzato quando è necessario fare costruire/widget/apple.o ?

 

7 Replies
  1. 67

    Il motivo è che la regola

    %.o: %.cpp
           ...
    

    si aspetta che il .file cpp risiedere nella stessa directory .o il vostro edificio. Dal test.exe nel tuo caso dipende dalla build/widget/apple.o (ecc), è in attesa di fare apple.cpp per essere build/widgets/apple.cpp.

    È possibile utilizzare VPATH per risolvere questo problema:

    VPATH = src/widgets
    
    BUILDDIR = build/widgets
    
    $(BUILDDIR)/%.o: %.cpp
          ...
    

    Quando si tenta di costruire “costruire/widget/apple.o”, fare ricerca apple.cpp in VPATH. Si noti che la regola di generazione è l’utilizzo di particolari variabili, in modo da accedere alla vera filename fare trova:

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

    Dove “$<” espande il percorso in cui fare trova la prima dipendenza.

    Anche notare che questo crea tutti i .o file build/widget. Se si vuole costruire i binari in directory diverse, si può fare qualcosa di simile

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

    Mi consiglia di utilizzare “scatola comando sequenze” per evitare il ripetersi del compilatore effettivo regola di compilazione:

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

    Si può quindi avere più regole come questo:

    build1/foo.o build1/bar.o: %.o: %.cpp
        $(cc-command)
    
    build2/frotz.o build2/fie.o: %.o: %.cpp
        $(cc-command)
    
    • VPATH non consente di avere diversi file di origine con lo stesso nome in diverse directory, che sconfigge lo scopo di directory, in primo luogo.
    • Eh, no, io non credo di sì. Organizzare il vostro codice sorgente in directory diverse, è più vantaggioso che permette a più file di origine con lo stesso nome.
    • Ho provato a seguire la tua soluzione e creata obiettivi del modulo .build/cwrapper/%.o: %.c %.cpp. Tuttavia, quando si utilizzano questi come dipendenza, ad esempio,cwrapper: .build/cwrapper/%.o, make si lamenta: “Nessuna regola per rendere target `.costruire/cwrapper/%.o'”. Che cosa sto facendo di sbagliato?
    • Nel caso In cui qualcuno bisogno di: gnu.org/software/make/manual/html_node/File-Name-Functions.html
  2. 65

    Questo fa il trucco:

    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))))
    

    Questo Makefile si presuppone il tuo includere i file nella directory di origine. Inoltre controlla se il processo di costruzione directory esiste, e li crea se non esiste.

    L’ultima riga è la più importante. Crea le regole implicite per ogni generazione, utilizzando la funzione make-goal, e non è necessario scrivere uno per uno

    È inoltre possibile aggiungere automatico dipendenza generazione, utilizzando Tromey modo

    • Ottimo, proprio quello che stavo cercando 😉 Grazie Manzill0
    • Il tuo uso del foreach mi ha davvero aiutato. Ho creato una lunga lista di file C che genera un singolo oggetto file. Grazie!
    • la seguente domanda è la stessa serie di problemi quando si tratta con più destinazioni e le loro dipendenze? stackoverflow.com/questions/30043480/…
    • Questo è veramente utile, purtroppo chiamando mkdir ogni volta che davvero rallenta il processo di generazione. Non ho trovato una soluzione per questo.
    • Ho Makefile:34: *** missing separator. Stop. E 34 ‘la maggior parte importante funzione. Inoltre non ottenere l’evidenziazione della sintassi. Solo quando ho messo il nome di una variabile e un = davanti
    • Di solito sostituendo gli spazi con schede risolve il missing separator errore.

  3. 4

    Cosa è [email protected] comprenderà tutta la (relativa) percorso per il file di origine che è a sua volta utilizzato per costruire il nome dell’oggetto (e quindi il suo percorso relativo)

    Usiamo:

    #####################
    # 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]
    

    Questo crea un oggetto di directory con il nome specificato nel $(OBJDIR_1)
    e sottodirectory secondo le sottodirectory in origine.

    Per esempio (si supponga objs come principale oggetto di directory), nel Makefile:

    widget/apple.cpp
    tests/blend.cpp
    

    risultati nella seguente directory di oggetto:

    objs/widget/apple.o
    objs/tests/blend.o
    
  4. 3

    Questo farà senza doloroso manipolazione o più sequenze di comandi:

    costruire/%.o: src/%.cpp 
    src/%.o: src/%.cpp 
    %.o: 
    $(CC) -c $< -o [email protected] 
    
    build/test.exe: costruire/widget/apple.o costruire/widget/manopola.o costruire/test/miscela.o .. /src /ui/flash.o 
    $(LD) $^ -o [email protected] 
    

    JasperE ha spiegato perché “%.o: %.cpp” non funziona; questa versione ha una regola (%.o:) con i comandi e prereqs, e due regole di pattern (build/%.o: e .. /src/%.o:) con prereqs e non i comandi. (Nota che ho messo nella cartella src/%.o la regola per affrontare src/ui/flash.o, supponendo che non era un errore di battitura per costruire/ui/flash.o, quindi se non hai bisogno di esso si può lasciare fuori.)

    build/test.exe necessità di costruire/widget/apple.o,

    costruire/widget/apple.o aspetto di costruire/%.o, quindi ha bisogno di src/%.cpp (in questo caso src/widgets/apple.cpp),

    costruire/widget/apple.o sembra anche %.o, in modo che esegua il comando CC e utilizza il prereqs appena trovato (cioè src/widgets/apple.cpp costruire il target (build/widget/apple.o)

    • Questo si rompe, perché il tuo %.o: regola non ha prerequisiti, ma si riferisce a loro tramite $< (che è quindi vuoto). “No input file”, mi dispiace.
  5. 3

    Questo è un altro trucco.

    In main ‘Makefile’ definire SRCDIR per ogni sorgente dir e includono ‘makef.mk’ per ogni valore di SRCDIR. In ogni origine dir mettere il file ‘file.mk’ con la lista dei file di origine e le opzioni di compilazione per alcuni di loro. In main ‘Makefile’ possibile definire le opzioni di compilazione e di escludere i file per ogni valore di SRCDIR.

    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)
    

    ./i file.mk

    C_SRC   := main.c
    CPP_SRC :=
    AS_SRC  := timer.S
    
    main.c += -DDEBUG
    

    ./crc/file.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
    
  6. 3

    Ecco la mia soluzione, ispirata dalla Beta risposta. E ‘ più semplice rispetto alle altre soluzioni proposte

    Ho un progetto con diversi C i file, memorizzati in molte sottodirectory.
    Per esempio:

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

    Qui è il mio Makefile (in src/ directory):

    # 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
    

    Questo esempio funziona bene per una libreria condivisa, e dovrebbe essere molto facile da adattare a qualsiasi processo di compilazione.

  7. 0

    Di solito, è possibile creare un Makefile in ogni sottodirectory, e scrivi in alto a livello di Makefile per chiamare fare nelle sottodirectory.

    Questa pagina può aiutare: http://www.gnu.org/software/make/

    • Questo è comunemente fatto, ma è pieno di problemi. La principale è che nessuno sa fare il processo su tutte le dipendenze, quindi le cose come -j2 su sistemi multicore non funziona. Vedere aegis.sourceforge.net/auug97.pdf
    • Keith riferimento è a un ottimo documento chiamato ‘Ricorsiva Rendere Considerati Nocivi’. Questo è un contributo per la serie di articoli a partire Dijkstra s ‘Va Considerato Dannoso per una lettera, che si conclude con “‘Considerato Nocivo’ Considerato Nocivo”.
    • È fatto, di solito perché la gente non riesce a capire come scrivere un Makefile. Un Makefile per directory schifo.

Lascia un commento