PDA

View Full Version : [Makefile] Compilazione modulare su directory diverse


Emalele1688
19-03-2012, 22:19
Salve a tutti:)
Premetto che conosco bene come si usa qmake, ma vorrei capire anche i makefile poichè vorrei metterci le mani sopra.

Il problema è il seguente:
Devo compilare un programma su diversi moduli (in questo esempio sono main.c e util.c) i quali sorgenti sono ubicati in una cartella src.
La situazione della directory è questa:

Programma
|__src
| |_main.c
| |_util.c
| |_makefile2
|
|__include
| |_util.h
|
|__bin
|
|_makefile

Il makefile è sulla prima directory, e chiama il makefile2 in src per compilare util.c e main.c.
E qui viè il problema del secolo: util.o e main.o mi vengono generati nella direcotry principale (sopra l'ho chiamata programma) dove è presente il primo makefile.
Anche se dovessi usare un solo makefile, come faccio a fargli capire che i file oggetto (util.o e main.o) devono andare in src e non nella directory dove parte il makefile?
Infati l'errore generato è:
gcc: error: src/main.o: File o directory non esistente
gcc: error: src/util.o: File o directory non esistente

Voi come scrivereste un makefile in una situazione del genere? (non che una normale situazione dove ogni cosa trova il suo posto)

Posto i due Makefile
Makefile

CC = gcc
SOURCE_PATH = src/
INCLUDE_PATH = include/
INCLUDE = -I$(INCLUDE_PATH)
OBJS = $(SOURCE_PATH)main.o $(SOURCE_PATH)util.o
BIN_PATH = bin/
BIN_NAME = somma
CC_BIN = -o $(BIN_PATH)$(BIN_NAME)
DEBUG = -g
CFLAGS = -Wall -c


$(BIN_PATH)$(BIN_NAME): $(OBJS)
$(CC) $(INCLUDE) $(DEBUG) $(OBJS) $(CC_BIN)

#Genera util.o
$(SOURCE_PATH)util.o: $(INCLUDE_PATH)util.h $(SOURCE_PATH)util.c
$(MAKE) -f src/Makefile2 util.o


#genera main.o
$(SOURCE_PATH)main.o: $(SOURCE_PATH)main.c
$(MAKE) -f src/Makefile2 main.o



CC = gcc
INCLUDE_PATH = include
INCLUDE = -I$(INCLUDE_PATH)
SOURCE_PATH = src/
DEBUG = -g
CFLAGS = -Wall -c


#Genera util.o
util.o: include/util.h src/util.c
$(CC) $(INCLUDE) $(DEBUG) $(CFLAGS) $(SOURCE_PATH)util.c

#genera main.o
main.o: src/main.c
$(CC) $(INCLUDE) $(DEBUG) $(CFLAGS) $(SOURCE_PATH)main.c

Emalele1688
22-03-2012, 23:28
up

marco.r
23-03-2012, 22:06
vado a memoria:
il make lo esegui dalla cartella root per cui l'output finisce in quella cartella. specifica piuttosto il nome di file di output:


#Genera util.o
src/util.o: include/util.h src/util.c
$(CC) $(INCLUDE) $(DEBUG) $(CFLAGS) $(SOURCE_PATH)util.c -o src/util.o
[code]
Meglio ancora, sposta makefile2 in src/ e rinominalo semplicemente in Makefile, quindi chiamalo con un "make -C src" invece che make -f.
La prassi in questi casi e' di non fare una regola per i singoli file nel makefile principale, ma semplicemente chiamare il make nelle sottocartelle. in altre parole nel makefile principale avresti solo una regola del tipo
[code]
src:
make -C src


Se poi vuoi essere piu' pulito, spara tutti i file che generi in una cartella a parte (tipo build/), cosi' che se vuoi fare pulizia totale basta che cancelli quella cartella.

Emalele1688
30-03-2012, 10:11
Ok, lo sto ricreando da capo perchè mi sono accorto di alcuni errori.
Devo ben capire quand'è che c'è bisogno di creare una macro e quand'è che se ne può fare a meno. Ad esempio stavo creando macro per ogni file sorgente .c ed ogni file oggetto .o, ma mi sono ben presto accorto dell'inutilità di ciò, e che il cmake quando crea dei makefile non usa flags per path e nomi di file.
Una cosa: Il binario finale, per correttezza, andrebbe creato nel makefile di root o nel makefile in src/ ?

marco.r
30-03-2012, 14:19
Ok, lo sto ricreando da capo perchè mi sono accorto di alcuni errori.
Devo ben capire quand'è che c'è bisogno di creare una macro e quand'è che se ne può fare a meno. Ad esempio stavo creando macro per ogni file sorgente .c ed ogni file oggetto .o, ma mi sono ben presto accorto dell'inutilità di ciò, e che il cmake quando crea dei makefile non usa flags per path e nomi di file.
Una cosa: Il binario finale, per correttezza, andrebbe creato nel makefile di root o nel makefile in src/ ?

Non occorre generare un target per ogni file.
Ci sono le cosidette regole implicite (http://www.gnu.org/software/make/manual/make.html#Implicit-Rules) che funzionano in base ad un pattern, ad esempio come generare un file .o partendo da un file .cpp.
Queste sono solitamente predefinite ma le puoi customizzare.
Ad esempio per il file C++ esiste una regola implicita (predefinita) come la seguente:

%.o : %.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c

che vuol dire "per generare un file .o partendo da un file con lo stesso nome ma estensione .cpp la regola e'..."

Per customizzarla puoi procedere in due modi: ridefinisci le variabili, oppure la riscrivi. Generalmente e' piu' pratica la prima
Questo vuol dire che puoi avere un makefile del tipo

CXXFLAGS=-O3 -march=native
...
my-program: a.o b.o c.o d.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(CPPFLAGS) -o $@

senza dover preoccuparti di dover creare le regole per i vari file .o

marco.r
30-03-2012, 14:21
Una cosa: Il binario finale, per correttezza, andrebbe creato nel makefile di root o nel makefile in src/ ?
dipende. Se il makefile in src/ ha tutti gli elementi per creare il binario, crealo la (diventa piu' comodo spostare/rinominare la cartella)

Emalele1688
30-03-2012, 17:03
Le regole implicite devo ben capirle ancora.
Per quanto riguarda il mio esempio, ora so risolverlo così:

root_folder/makefile:

CC = gcc
DEBUG = -g
OBJ_FLAG = -c
WARNING = -Wall
LIBS =
CFLAGS = -O1
BINARY_NAME = bin/somma
BINARY_BUILD = -o $(BINARY_NAME)
SOURCE_PATH = src/
INCLUDE_PATH = include/

$(BINARY_NAME): $(SOURCE_PATH)util.o $(SOURCE_PATH)main.o
$(CC) $(DEBUG) $(WARNING) $(CFLAGS) $(SOURCE_PATH)util.o $(SOURCE_PATH)main.o $(BINARY_BUILD)

$(SOURCE_PATH)util.o: $(SOURCE_PATH)util.c $(INCLUDE_PATH)util.h
make -C $(SOURCE_PATH) util.o

$(SOURCE_PATH)main.o: $(SOURCE_PATH)main.c
make -C $(SOURCE_PATH) main.o



/root_folder/src/makefile:

include ../Makefile

INCLUDE = ../include/
INCLUDE_FLAGS = -I$(INCLUDE)


util.o: util.c $(INCLUDE)util.h
$(CC) $(DEBUG) $(WARNING) $(CFLAGS) $(INCLUDE_FLAGS) $(OBJ_FLAG) util.c

main.o: main.c
$(CC) $(DEBUG) $(WARNING) $(CFLAGS) $(INCLUDE_FLAGS) $(OBJ_FLAG) main.c


Devo sbatterci ancora un po la testa su, lo so, però adesso funziona...
Le regole implicite mi sembra di aver capito che ti semplificano nel scrivere tutti i sorgenti uno ad uno. Ma se io ho un solo file .c che dipende da un file .h come "glielo spiego"??
Esempio:

%.o : %.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c

e util.h??