makefile - make - Cómo colocar los archivos de objetos en subdirectorio separado
makefile linux c (6)
Como está utilizando GNUmake, use una regla de patrón para compilar archivos de objeto:
$(OBJDIR)/%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
Tengo problemas para tratar de usar make para colocar archivos de objetos en un subdirectorio separado, probablemente una técnica muy básica. He tratado de usar la información en esta página: http://www.gnu.org/software/hello/manual/make/Prerequisite-Types.html#Prerequisite-Types
Obtengo el siguiente resultado de make: make: *** No hay regla para hacer objetivo ku.h'', needed by
obj / kumain.o''. Detener.
Sin embargo, ku.h es una dependencia, no un objetivo (aunque es obviamente #incluido dentro de los c archivos de origen). Cuando no trato de usar un subdirectorio para archivos de objeto (es decir, me pierdo las partes OBJDIR), funciona bien. ¿Por qué hacer pensar que ku.h es un objetivo?
mi archivo MAKE es este: (el estilo es después de leer varias fuentes de información)
.SUFFIXES:
.SUFFIXES: .c .o
CC=gcc
CPPFLAGS=-Wall
LDLIBS=-lhpdf
VPATH=%.c src
VPATH=%.h src
VPATH=%.o obj
OBJDIR=obj
objects= $(addprefix $(OBJDIR)/, kumain.o kudlx.o kusolvesk.o kugetpuz.o kuutils.o /
kurand.o kuASCboard.o kuPDFs.o kupuzstrings.o kugensud.o /
kushapes.o )
ku : $(objects)
$(CC) $(CPPFLAGS) -o ku $(objects) $(LDLIBS)
$(objects) : ku.h kudefines.h kuglobals.h kufns.h | $(OBJDIR)
$(OBJDIR):
mkdir $(OBJDIR)
.PHONY: clean
clean :
rm $(objects)
Editar: apliqué el cambio para usar la directiva vpath. Mi versión era una mala mezcla de VPATH = xxx y vpath% .c xxx. Sin embargo ahora tengo otro problema (que era el problema original antes de agregar el vpath incorrecto). Este es ahora el resultado:
gcc -o ku -lhpdf obj/kumain.o obj/kudlx.o obj/kusolvesk.o ..etc
gcc: obj/kumain.o: No such file or directory
gcc: obj/kudlx.o: No such file or directory
gcc: obj/kusolvesk.o: No such file or directory
gcc: obj/kugetpuz.o: No such file or directory
gcc: obj/kuutils.o: No such file or directory
gcc: obj/kurand.o: No such file or directory
gcc: obj/kuASCboard.o: No such file or directory
gcc: obj/kuPDFs.o: No such file or directory
gcc: obj/kupuzstrings.o: No such file or directory
gcc: obj/kugensud.o: No such file or directory
gcc: obj/kushapes.o: No such file or directory
make: *** [ku] Error 1
Parece que make no está aplicando la regla implícita para un archivo de objeto aunque el manual dice "Las reglas implícitas indican cómo usar las técnicas habituales para que no tenga que especificarlas en detalle cuando desee usarlas. Por ejemplo, es una regla implícita para la compilación C. Los nombres de archivo determinan qué reglas implícitas se ejecutan. Por ejemplo, la compilación C normalmente toma un archivo .c y crea un archivo .o. Así que make aplica la regla implícita para la compilación C cuando ve esta combinación de nombre de archivo finales. " y también "La búsqueda a través de los directorios especificados en VPATH o con vpath también ocurre durante la consideración de reglas implícitas (ver Uso de reglas implícitas)".
De nuevo aquí "Por ejemplo, cuando un archivo foo.o no tiene una regla explícita, make considera reglas implícitas, como la regla incorporada para compilar foo.c si ese archivo existe. Si falta ese archivo en el directorio actual, haga se buscan los directorios apropiados. Si foo.c existe (o se menciona en el archivo MAKE) en cualquiera de los directorios, se aplica la regla implícita para la compilación C. "
Cualquier ayuda para lograr que las reglas implícitas funcionen para mi archivo MAKE sería muy apreciada.
Edit no 2: Gracias a Jack Kelly he hecho una regla explícita para compilar los archivos .c ya que no pude llegar a ninguna parte tratando de usar reglas implícitas. También gracias a al_miro por la información vpath.
Aquí está el makfile de trabajo:
.SUFFIXES:
.SUFFIXES: .c .o
CC=gcc
CPPFLAGS=-Wall
LDLIBS=-lhpdf
OBJDIR=obj
vpath %.c src
vpath %.h src
objects = $(addprefix $(OBJDIR)/, kumain.o kudlx.o kusolvesk.o kugetpuz.o kuutils.o /
kurand.o kuASCboard.o kuPDFs.o kupuzstrings.o kugensud.o /
kushapes.o )
ku : $(objects)
$(CC) $(CPPFLAGS) -o ku $(objects) $(LDLIBS)
$(OBJDIR) obj/%.o : %.c ku.h kudefines.h kuglobals.h kufns.h
$(CC) -c $(CPPFLAGS) $< -o $@
.PHONY : clean
clean :
rm $(objects)
En general, debe especificar $(OBJDIR)
en el lado izquierdo de todas las reglas que colocan archivos en $(OBJDIR)
, o puede ejecutar make desde $(OBJDIR)
. VPATH
es para fuentes, no para objetos.
Eche un vistazo a estos dos enlaces para obtener más explicaciones y una solución "inteligente".
Este es el archivo MAKE que uso para la mayoría de mis proyectos,
Permite colocar archivos de origen, encabezados y archivos en línea en subcarpetas y subcarpetas de subcarpetas y demás, y generará automáticamente un archivo de dependencia para cada objeto. Esto significa que la modificación de encabezados y archivos en línea desencadenará la recompilación de archivos que son dependientes.
Los archivos de origen se detectan a través del comando shell find, por lo que no es necesario especificarlo explícitamente, solo debe seguir codificando según el contenido de su corazón.
También copiará todos los archivos de una carpeta de ''recursos'', en la carpeta bin cuando se compile el proyecto, que me resulta útil la mayor parte del tiempo.
Para proporcionar crédito donde corresponde, la función de autodependencias se basó principalmente en la página de Scott McPeak que se puede encontrar HERE , con algunas modificaciones / ajustes adicionales para mis necesidades.
Ejemplo Makefile
#Compiler and Linker
CC := g++-mp-4.7
#The Target Binary Program
TARGET := program
#The Directories, Source, Includes, Objects, Binary and Resources
SRCDIR := src
INCDIR := inc
BUILDDIR := obj
TARGETDIR := bin
RESDIR := res
SRCEXT := cpp
DEPEXT := d
OBJEXT := o
#Flags, Libraries and Includes
CFLAGS := -fopenmp -Wall -O3 -g
LIB := -fopenmp -lm -larmadillo
INC := -I$(INCDIR) -I/usr/local/include
INCDEP := -I$(INCDIR)
#---------------------------------------------------------------------------------
#DO NOT EDIT BELOW THIS LINE
#---------------------------------------------------------------------------------
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
#Defauilt Make
all: resources $(TARGET)
#Remake
remake: cleaner all
#Copy Resources from Resources Directory to Target Directory
resources: directories
@cp $(RESDIR)/* $(TARGETDIR)/
#Make the Directories
directories:
@mkdir -p $(TARGETDIR)
@mkdir -p $(BUILDDIR)
#Clean only Objecst
clean:
@$(RM) -rf $(BUILDDIR)
#Full Clean, Objects and Binaries
cleaner: clean
@$(RM) -rf $(TARGETDIR)
#Pull in dependency info for *existing* .o files
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
#Link
$(TARGET): $(OBJECTS)
$(CC) -o $(TARGETDIR)/$(TARGET) $^ $(LIB)
#Compile
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(INC) -c -o $@ $<
@$(CC) $(CFLAGS) $(INCDEP) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
@cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
@sed -e ''s|.*:|$(BUILDDIR)/$*.$(OBJEXT):|'' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
@sed -e ''s/.*://'' -e ''s///$$//'' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e ''s/^ *//'' -e ''s/$$/:/'' >> $(BUILDDIR)/$*.$(DEPEXT)
@rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp
#Non-File Targets
.PHONY: all remake clean cleaner resources
La siguiente solución no es agradable en mi opinión, ya que realmente amo las reglas integradas. Sin embargo, GNU make no es compatible con algo como vpath
para los directorios de salida. Y las reglas incorporadas no pueden coincidir, ya que el %
en %.o
coincidiría con obj/foo
de obj/foo.o
, dejando make
con una búsqueda en vpath %.c src/
para cosas como src/obj/foo.c
, pero no src/foo.c
Pero esto es lo más cercano a las reglas integradas que puede obtener, y por lo tanto, para mi mejor conocimiento, es la mejor solución disponible.
$(OBJDIR)/%.o: %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
Explicación: $(COMPILE.c) $(OUTPUT_OPTION) $<
realidad es cómo se implementa .co
, vea http://git.savannah.gnu.org/cgit/make.git/tree/default.c (y es incluso mencionado en el manual)
Además, si $(OBJDIR)
solo contendría archivos autogererated, podría crearlo sobre la marcha con un requisito previo solo de pedido, simplificando la regla de limpieza:
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(COMPILE.c) $(OUTPUT_OPTION) $<
.PHONY: clean
clean:
$(RM) -r $(OBJDIR)
Esto requiere que solo esté disponible la función de orden , que puede verificar usando $(filter order-only, $(.FETAURES))
. Revisé Kubuntu 14.04 GNU make 3.81 y OpenSUSE 13.1 GNU make 3.82. Ambos fueron creados con solo el pedido habilitado, y ahora me deja perplejo por qué Kubuntu 14.04 viene con una versión anterior de GNU make que OpenSUSE 13.1. De todos modos, voy a descargar hacer 4.1 ahora :)
Las líneas de VPATH son incorrectas, deberían ser
vpath %.c src
vpath %.h src
es decir, no capital y sin el =. Tal como está ahora, no encuentra el archivo .h y piensa que es un objetivo que se debe crear.
Para todos aquellos que trabajan con reglas implícitas (y GNU MAKE). Aquí hay un archivo make simple que admite diferentes directorios:
#Start of the makefile
VPATH = ./src:./header:./objects
OUTPUT_OPTION = -o objects/$@
CXXFLAGS += -Wall -g -I./header
Target = $(notdir $(CURDIR)).exe
Objects := $(notdir $(patsubst %.cpp,%.o,$(wildcard src/*.cpp)))
all: $(Target)
$(Target): $(Objects)
$(CXX) $(CXXFLAGS) -o $(Target) $(addprefix objects/,$(Objects))
#Beware of -f. It skips any confirmation/errors (e.g. file does not exist)
.PHONY: clean
clean:
rm -f $(addprefix objects/,$(Objects)) $(Target)
Veamos más de cerca (me referiré al Directorio actual con curdir):
Esta línea se usa para obtener una lista de los archivos .o utilizados que están en curdir / src.
Objects := $(notdir $(patsubst %.cpp,%.o,$(wildcard src/*.cpp)))
#expands to "foo.o myfoo.o otherfoo.o"
A través de la variable, la salida se establece en un directorio diferente (curdir / objects).
OUTPUT_OPTION = -o objects/$@
#OUTPUT_OPTION will insert the -o flag into the implicit rules
Para asegurarse de que el compilador encuentra los objetos en la nueva carpeta de objetos, la ruta se agrega al nombre del archivo.
$(Target): $(Objects)
$(CXX) $(CXXFLAGS) -o $(Target) $(addprefix objects/,$(Objects))
# ^^^^^^^^^^^^^^^^^^^^
Esto se entiende como un ejemplo y definitivamente hay margen de mejora.
Para información adicional consultar: Hacer documentación. Ver el capítulo 10.2