Come posso creare un Makefile per progetti C con sottodirectory SRC, OBJ e BIN?


95

Qualche mese fa, mi è venuto in mente il seguente generico Makefileper i compiti scolastici:

# ------------------------------------------------
# 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!"

Questo fondamentalmente compilerà ogni file .ce .hper generare .ofile e l'eseguibile projectnametutti nella stessa cartella.

Ora, vorrei spingere un po 'questo. Come posso scrivere un Makefile per compilare un progetto C con la seguente struttura di directory?

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

In altre parole, mi piacerebbe avere un Makefile che compili i sorgenti C da ./src/in ./obj/e poi colleghi tutto per creare l'eseguibile in ./bin/.

Ho provato a leggere diversi Makefile, ma semplicemente non riesco a farli funzionare per la struttura del progetto sopra; invece, il progetto non viene compilato con tutti i tipi di errori. Certo, potrei usare l'IDE completo (Monodevelop, Anjuta, ecc.), Ma onestamente preferisco restare con gEdit e il buon vecchio terminale.

C'è un guru che può fornirmi una soluzione funzionante o informazioni chiare su come farlo? Grazie!

** AGGIORNAMENTO (v4) **

La soluzione finale:

# ------------------------------------------------
# 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!"

Qual è la domanda specifica qui?
Oliver Charlesworth

Non sono sicuro di aver capito cosa vuoi fare.
Tom

Aggiornato il Makefile. Mi sto avvicinando, ma ho problemi con le variabili automatiche, quindi sembra comunque
Yanick Rochon

Ho appena trovato una soluzione. Se a qualcuno interessa trovare qualcosa di meglio, il Makefile può ancora essere migliorato.
Yanick Rochon

2
@YanickRochon Non volevo criticare le tue abilità in inglese. Ma affinché gli obiettivi PHONY abbiano un senso, non puoi assolutamente scrivere BANANA;) gnu.org/software/make/manual/html_node/Phony-Targets.html
joni

Risposte:


34

Innanzitutto, la tua $(OBJECTS)regola è problematica, perché:

  1. è un po 'indiscriminato, rendendo tutte le fonti prerequisiti di ogni oggetto,
  2. spesso usa la fonte sbagliata (come hai scoperto con file1.oe file2.o)
  3. cerca di creare eseguibili invece di fermarsi sugli oggetti, e
  4. il nome di target ( foo.o) non è ciò che la regola effettivamente produrrà ( obj/foo.o).

Suggerisco quanto segue:

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

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

La $(TARGET)regola ha lo stesso problema che il nome di destinazione non descrive effettivamente ciò che la regola crea. Per questo motivo, se digiti makepiù volte, Make ricostruirà l'obiettivo ogni volta, anche se non c'è motivo per farlo. Una piccola modifica risolve che:

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

Una volta che è tutto in ordine, potresti prendere in considerazione una gestione delle dipendenze più sofisticata; se modifichi uno dei file di intestazione, questo makefile non saprà quali oggetti / eseguibili devono essere ricostruiti. Ma questo può aspettare un altro giorno.

EDIT:
Scusa, ho omesso parte della $(OBJECTS)regola sopra; L'ho corretto. (Vorrei poter usare "strike" all'interno di un esempio di codice.)


con le modifiche suggerite, ottengo: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: hai più mainfunzioni? Forse uno dentro file1.ce uno dentro main.c? In tal caso non sarai in grado di collegare questi oggetti; ce ne può essere solo uno mainin un eseguibile.
Beta

No, io non lo faccio. Funziona tutto bene con l'ultima versione che ho pubblicato nella domanda. Quando cambio il mio Makefile con quello che suggerisci (e capisco i vantaggi di quello che dici) è quello che ottengo. Ho appena incollato file1.cma dà lo stesso messaggio a ogni singolo file del progetto. Ed main.cè l' unico con una funzione principale ... e main.cimporta file1.he file2.h(non c'è relazione tra file1.ce file2.c), ma dubito che il problema derivi da lì.
Yanick Rochon

@Yanick Rochon: ho sbagliato a incollare la prima riga della mia $(OBJECTS)regola; L'ho modificato. Con la linea sbagliata ho ricevuto un errore, ma non quello che hai ottenuto ...
Beta

6

È possibile aggiungere il -Iflag ai flag del compilatore (CFLAGS) per indicare dove il compilatore deve cercare i file sorgente e il flag -o per indicare dove deve essere lasciato il binario:

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

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

Per rilasciare i file oggetto nella objdirectory, utilizzare l' -oopzione durante la compilazione. Inoltre, guarda le variabili automatiche$@ e .$<

Ad esempio, considera questo semplice Makefile

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

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

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

Aggiorna>

Guardando il tuo makefile, mi rendo conto che stai usando la -obandiera. Buona. Continua a usarlo, ma aggiungi una variabile di directory di destinazione per indicare dove deve essere scritto il file di output.


Potresti essere più specifico? Intendi aggiungere -l ...a CFLAGSe ... c'è già l' -oargomento al linker ( LINKER)
Yanick Rochon

Sì, le CFLAGS e sì, continua a utilizzare -o, aggiungi semplicemente la variabile TARGETPATH.
Tom

Grazie, ho apportato le modifiche, ma sembra che mi manchi ancora qualcosa (vedi l'aggiornamento sulla domanda)
Yanick Rochon

solo make, da dove si trova il Makefile
Yanick Rochon il

Non riesci a leggere il comando in esecuzione? ad esempio gcc -c yadayada. Abbastanza sicuro che ci sia una variabile che non contiene ciò che ti aspetti
Tom

-1

Ho smesso di scrivere makefile in questi giorni, se la tua intenzione è di imparare vai avanti, altrimenti hai un buon generatore di makefile fornito con eclipse CDT. Se vuoi un po 'di manutenibilità / supporto di più progetti nel tuo albero di build, dai un'occhiata a quanto segue:

https://github.com/dmoulding/boilermake L' ho trovato abbastanza buono ..!


3
Basato su opinioni. Non risponde alla domanda di OP. Presuppone l'ambiente Eclipse.
Nathaniel Johnson
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.