Domine fácilmente Makefile con ejemplos -- Thrive



Prosperar

inserte la descripción de la imagen aquí

Para practicar más puntos de conocimiento sobre Makefile a través de un proyecto complejo, vamos a nombrar el proyecto playground (patio de recreo), los requisitos básicos de este proyecto para Makefile son:

Coloque todos los archivos de destino en el subdirectorio objs del directorio donde se encuentra el programa fuente;
coloque todos los programas ejecutables finales en el subdirectorio exes del directorio donde se encuentra el programa fuente;
introduzca archivos de encabezado de usuario para simular la situación de proyectos complejos .


inserte la descripción de la imagen aquí


Ejemplo 1: crear un directorio

Editar archivo MAKE

.PHONY: all
MKDIR = mkdir
DIRS = objs exes

all: $(DIRS)
$(DIRS):
	$(MKDIR) $@


Compilar y ejecutar

$ make 
$ ll 
$ ls 


salida de resultados

inserte la descripción de la imagen aquí


Gramática Descripción

  • Cabe señalar que la variable OBJS no es solo un destino dependiente, sino también un directorio, y su significado es diferente en diferentes ocasiones. Cuando el Makefile anterior se crea por primera vez, objs y exes no existen, por lo que todos los objetivos los consideran un requisito previo/objetivo de dependencia, y luego el Makefile primero crea los directorios objs y exes de acuerdo con las reglas de construcción de directorios. construyendo el directorio, el segundo se ejecutan los comandos de la regla, creando los directorios objs y exes.


Ejemplo 2: Borrar directorio

Editar archivo MAKE

.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
DIRS = objs exes

all: $(DIRS)
$(DIRS):
	$(MKDIR) $@
	
clean:
	$(RM) $(RMFLAGS) $(DIRS)


Compilar y ejecutar

$ make 
$ ls 
$ make clean
$ ls 


salida de resultados

inserte la descripción de la imagen aquí


Gramática Descripción

  • Cree un destino limpio para eliminar archivos de objetos y ejecutables generados.


Ejemplo 3: agregar archivos de encabezado

foo.h código fuente

#ifndef __FOO_H
#define __FOO_H

 void foo();

#endif

código fuente foo.c

#include <stdio.h>
#include "foo.h"
void foo()
{
    
    
	printf("This is foo()!\n");
}


código fuente main.c

#include "foo.h"

int main()
{
    
    
	foo();
	return 0;
}


Editar archivo MAKE

.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
EXE = playground
DIRS = objs exes
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)

all: $(DIRS)
$(DIRS):
	$(MKDIR) $@
$(EXE): $(OBJS)
	$(CC) -o $@ $^
%.o: %.c
	$(CC) -o $@ -c $^
	
clean:
	$(RM) $(RMFLAGS) $(DIRS) $(EXE) $(OBJS)


Compilar y ejecutar

$ make 
$ ls 
$ ./playground
$ make clean


salida de resultados

inserte la descripción de la imagen aquí


Gramática Descripción

  • Dependencia agregada en el destino EXE después de todo. Cuando aparecen varios requisitos previos en una regla (como la regla all aquí), make construirá los objetivos uno por uno en orden de izquierda a derecha.


Ejemplo 4: poner los archivos generados en el directorio


Editar archivo MAKE

.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
EXE = playground
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
DIRS = $(DIR_OBJS) $(DIR_EXES)
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))

all: $(DIRS) $(EXE)
$(DIRS):
	$(MKDIR) $@
$(EXE): $(OBJS)
	$(CC) -o $@ $^
$(DIR_OBJS)/%.o: %.c
	$(CC) -o $@ -c $^
	
clean:
	$(RM) $(RMFLAGS) $(DIRS) $(EXE)


Compilar y ejecutar

$ make 
$ ls 
$ cd exes
$ ls
$ cd ../objs/
$ ls


salida de resultados

inserte la descripción de la imagen aquí


Gramática Descripción

  • Aumente el uso de la función addprefix para agregar el prefijo "objs/" a cada archivo de destino;
  • También agregue el prefijo "objs/" antes del destino en la regla de patrón del archivo de destino de compilación;
  • La razón para agregar el prefijo: la opción -o en el comando de regla debe usarse como la ubicación de generación final del archivo de destino, y para poder usarse para la regla de creación de destino en el Makefile, también debe adoptar un formato similar.


Ejemplo 5: Dependencias más complejas


Editar archivo MAKE

.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps
EXE = playground
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

all: $(DIRS) $(DEPS) $(EXE)
$(DIRS):
	$(MKDIR) $@
$(EXE): $(OBJS)
	$(CC) -o $@ $^
$(DIR_OBJS)/%.o: %.c
	$(CC) -o $@ -c $^
$(DIR_DEPS)/%.dep: %.c
	@echo "Making $@ ..."
	@set -e; \
	$(RM) $(RMFLAGS) $@.tmp ; \
	$(CC) -E -MM $^ > $@.tmp ; \
	sed 's,\(.*\)\.o[ :]*,objs/\1.o: ,g' < $@.tmp > $@ ; \
	$(RM) $(RMFLAGS) $@.tmp
	
clean:
	$(RM) $(RMFLAGS) $(DIRS)


Compilar y ejecutar

$ make 
$ ls exes/
$ ls objs/
$ ls deps/


salida de resultados

inserte la descripción de la imagen aquí


Gramática Descripción

  • En comparación con el Ejemplo 4, este ejemplo mejora una relación de dependencia más completa;
  • Problema existente en el Ejemplo 4: En el contexto de que el programa se compiló con éxito una vez, el archivo foo.h se modifica (de void foo(); a void foo(int)), y luego se vuelve a compilar, se encuentra que el programa no reporta un error, la razón es que el Makefile de la instancia 23 no depende de foo, lo que lleva a la imposibilidad de identificar con precisión los problemas existentes.

    Tecnología clave:
  • La opción -M y la opción -MM en gcc , la diferencia entre las dos es: la opción -MM no enumera las dependencias en los archivos de encabezado del sistema, como stdio.h;
  • sed : puede buscar y reemplazar cadenas;
  • set -e : La función es decirle a BASH Shell que salga directamente cuando ocurra algún error durante el proceso de generación de archivos dependientes. La actuación más definitiva es que make nos dirá que algo salió mal, deteniendo así el trabajo posterior de make. Si no se realiza esta configuración, cuando se produzca un error en el archivo de dependencia de compilación, make seguirá funcionando más tarde, que no es lo que queremos.


Ejemplo 6: declaración condicional de archivo MAKE


Editar archivo MAKE

.PHONY: all
sharp = square
desk = square
table = circle
foo =  defined

ifeq ($(sharp), $(desk))
	result1 = "desk == sharp"
endif

ifneq "$(table)" 'square'
	result2 = "table != square"
endif

ifdef foo
	result3 = "foo is defined"
endif

ifdef zoo
	resunlt4 = "zoo is not defined"
endif

all:
	@echo $(result1)
	@echo $(result2)
	@echo $(result3)
	@echo $(result4)


Compilar y ejecutar

$ make 


salida de resultados

inserte la descripción de la imagen aquí


Gramática Descripción

  • make analizará la sintaxis condicional tan pronto como la vea, que incluye las cuatro formas de declaración de ifdef, ifeq, ifndef e ifneq.


Resumen: Makefile para este proyecto

.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIR_DEPS = deps
DIRS = $(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
EXE = complicated.exe
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

all: $(EXE)
ifneq ($(MAKECMDGOALS), clean)
-include $(DEPS)
endif
$(DIRS):
	$(MKDIR) $@
$(EXE): $(DIR_EXES) $(OBJS)
	$(CC) -o $@ $(filter %.o, $^)
$(DIR_OBJS)/%.o: $(DIR_OBJS) %.c
	$(CC) -o $@ -c $(filter %.c, $^)
$(DIR_DEPS)/%.dep: $(DIR_DEPS) %.c
	@echo "Making $@ ..."
	@set -e; \
	$(RM) $(RMFLAGS) $@.tmp ; \
	$(CC) -E -MM $(filter %.c, $^) > $@.tmp ; \
	sed 's,\(.*\)\.o[ :]*,objs/\1.o $@: ,g' < $@.tmp > $@ ; \
	$(RM) $(RMFLAGS) $@.tmp
clean:
	$(RM) $(RMFLAGS) $(DIRS)

Supongo que te gusta

Origin blog.csdn.net/locahuang/article/details/126994333
Recomendado
Clasificación