compilar - gcc linux
¿Cómo puedo crear un Makefile para proyectos C con subdirectorios SRC, OBJ y BIN? (3)
Hace unos meses, se me ocurrió el siguiente Makefile
genérico para tareas escolares:
# ------------------------------------------------
# Generic Makefile
#
# Author: [email protected]
# 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!"
Esto básicamente compilará todos los .c
y .h
para generar archivos .o
y el nombre del proyecto ejecutable, todo en la misma carpeta.
Ahora, me gustaría presionar esto un poco. ¿Cómo puedo escribir un Makefile para compilar un proyecto C con la siguiente estructura de directorio?
./
./Makefile
./src/*.c;*.h
./obj/*.o
./bin/<executable>
En otras palabras, me gustaría tener un Makefile que compila fuentes C de ./src/
en ./obj/
y luego vincular todo para crear el ejecutable en ./bin/
.
Intenté leer diferentes Makefiles, pero simplemente no puedo hacer que funcionen para la estructura del proyecto anterior; en cambio, el proyecto no compila con todo tipo de errores. Claro, podría usar IDE completo (Monodesarrollo, Anjuta, etc.), pero honestamente prefiero quedarme con gEdit y la buena terminal.
¿Hay un gurú que pueda darme una solución funcional o información clara sobre cómo se puede hacer esto? ¡Gracias!
** ACTUALIZACIÓN (v4) **
La solución definitiva :
# ------------------------------------------------
# Generic Makefile
#
# Author: [email protected]
# 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!"
He dejado de escribir makefiles en estos días, si tu intención es aprender a seguir adelante, de lo contrario, tienes un buen generador de archivos que viene con eclipse CDT. Si desea compatibilidad con mantenimiento / proyectos múltiples en su árbol de compilación, eche un vistazo a lo siguiente:
https://github.com/dmoulding/boilermake Lo encontré bastante bueno ...!
Primero, su regla de $(OBJECTS)
es problemática porque:
- es un tanto indiscriminado, lo que hace que todas las fuentes sean requisitos previos para cada objeto,
- a menudo usa la fuente incorrecta (como descubrió con
file1.o
yfile2.o
) - intenta construir ejecutables en lugar de detenerse en objetos, y
- el nombre del objetivo (
foo.o
) no es lo que realmente producirá la regla (obj/foo.o
).
Sugiero lo siguiente:
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
@echo "Compiled "$<" successfully!"
La regla $(TARGET)
tiene el mismo problema que el nombre del objetivo en realidad no describe lo que construye la regla. Por esa razón, si escribe make
varias veces, Make reconstruirá el objetivo cada vez, aunque no haya ninguna razón para hacerlo. Un pequeño cambio corrige que:
$(BINDIR)/$(TARGET): $(OBJECTS)
$(LINKER) $@ $(LFLAGS) $(OBJECTS)
@echo "Linking complete!"
Una vez que todo esté en orden, podría considerar un manejo de dependencias más sofisticado; si modifica uno de los archivos de encabezado, este archivo MAKE no sabrá qué objetos / ejecutables deben reconstruirse. Pero eso puede esperar otro día.
EDITAR:
Lo siento, omití parte de la regla de $(OBJECTS)
anterior. Lo he corregido (Desearía poder usar "strike" dentro de una muestra de código).
Puede agregar el distintivo -I
indicadores del compilador (CFLAGS) para indicar dónde debe buscar el compilador los archivos fuente y el indicador -o para indicar dónde debe quedar el archivo binario:
CFLAGS = -Wall -I./src
TARGETPATH = ./bin
$(TARGET): obj
@$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
@echo "Linking complete!"
Para colocar los archivos de objetos en el directorio obj
, use la opción -o
al compilar. Además, mire las variables automáticas $@
y $<
.
Por ejemplo, considere este simple Makefile
CFLAGS= -g -Wall -O3
OBJDIR= ./obj
SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@
Actualización>
Al mirar tu archivo MAKE, me doy cuenta de que estás usando la bandera -o
. Bueno. Continúe usándolo, pero agregue una variable de directorio de destino para indicar dónde debe escribirse el archivo de salida.