Domine facilmente o Makefile com a ajuda de exemplos - floresça e dê frutos



flor e fruta

insira a descrição da imagem aqui

Para praticar o aprofundamento da aplicação do Makefile através de um projeto relativamente complexo, vamos nomear o projeto como enorme (big guy);

O diagrama da estrutura do diretório do projeto é o seguinte:
insira a descrição da imagem aqui

Exemplo 1: Makefile em source/foo/src


editar source/foo/src/Makefile

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

CC = gcc

AR = ar
ARFLAGS = crs
DIR_OBJS = objs
DIR_EXES = ../../../build/exes/
DIR_DEPS = deps
DIR_LIBS = ../../../build/libs
DIRS = $(DIR_DEPS) $(DIR_OBJS) $(DIR_EXES) $(DIR_LIBS)
RMS = $(DIR_OBJS) $(DIR_DEPS)

EXE = zoo
ifneq ($(EXE), "")
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
RMS += $(EXE)
endif

LIB = libfoo.a


ifneq ($(LIB), "")
LIB := $(addprefix $(DIR_LIBS)/, $(LIB))
RMS += $(LIB)
endif

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

ifneq ($(EXE), "")
all: $(EXE)
endif

ifneq ($(LIB), "")
all: $(LIB)
endif

ifneq ($(MAKECMDGOALS), clean)
-include $(DEPS)
endif

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


Compilar e executar

$ make
$ ls
$ ls ../../../bulild/libs/
$ ls ../../../build/exes/
$ ls deps/
$ ls objs/
$ make clean


saída de resultado

insira a descrição da imagem aqui


Descrição gramatical

  • O propósito das duas variáveis ​​AR e ARFLAGS é gerar uma biblioteca estática;
  • IDS_EXES indica a localização real do diretório exes e agora usa um caminho relativo;
  • A variável DIRS aumenta o conteúdo da variável DIR_LIBS para que o diretório build/libs seja gerado antes que o arquivo de biblioteca seja gerado;
  • As variáveis ​​RMS são usadas para representar diretórios e arquivos a serem removidos;
  • O Makefile é apenas para construir a biblioteca libfoo.a, então make clean não pode deletar todos os diretórios exes e libs localizados no diretório de construção;
  • A instrução condicional ifneq é usada para determinar se a variável EXE está definida, pois posteriormente ao definir a dependência de todos os destinos, é necessário determinar se a variável EXE possui um valor. Se não houver valor, não precisamos fazer todos os alvos dependem de $(EXE) Adicione um prefixo para chamar a função addprefix, caso contrário, interromperá o método de julgar se a variável EXE tem um valor posteriormente, para decidir se deve fazer com que todos os diretórios dependam dela;
  • Se EXE tiver um valor, seu valor deve ser adicionado à variável RMS para que possamos limpá-lo quando chamarmos make clean;
  • A variável LIB é usada para armazenar o nome da biblioteca, por exemplo, o nome da biblioteca aqui é libfoo.a;
  • Adicione uma regra para construção que chame o comando ar com o argumento crs para gerar a biblioteca.
  • No comando clean target, o conteúdo da variável RMS é excluído em vez do conteúdo da variável DIRS. Porque não queremos excluir os diretórios libs e exes no diretório bulid ao fazer clean no módulo foo.


Exemplo 2: Usando make.rule para melhorar a reutilização


Divida o Makefile no módulo foo do Exemplo 1 em duas partes: make.rule no diretório build e Makefile no diretório source/foo/src, e expanda o Makefile no diretório large/src.


1. Edite build/make.rule

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

CC = gcc

AR = ar
ARFLAGS = crs
DIR_OBJS = objs
DIR_EXES = $(ROOT)/build/exes/
DIR_DEPS = deps
DIR_LIBS = $(ROOT)/build/libs
DIRS = $(DIR_DEPS) $(DIR_OBJS) $(DIR_EXES) $(DIR_LIBS)
RMS = $(DIR_OBJS) $(DIR_DEPS)

ifneq ($(EXE), "")
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
RMS += $(EXE)
endif


ifneq ($(LIB), "")
LIB := $(addprefix $(DIR_LIBS)/, $(LIB))
RMS += $(LIB)
endif

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

ifneq ($(EXE), "")
all: $(EXE)
endif

ifneq ($(LIB), "")
all: $(LIB)
endif

ifneq ($(MAKECMDGOALS), clean)
-include $(DEPS)
endif

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


2. Edite source/foo/src/Makefile

EXE = 
LIB = libfoo.a
include $(ROOT)/build/make.rule


Atribua um valor à variável ROOT no diretório huge/:

$ export ROOT=`pwd`


Digite enorme/fonte/foo/src para compilar e executar

$ make
$ ls
$ ls ../../../bulild/
$ ls ../../../build/libs/
$ ls deps/
$ ls objs/
$ make clean


saída de resultado

insira a descrição da imagem aqui


3. Crie main.c no diretório source/huge/src/

int main()
{
    
    
	return 0;
}


Editar source/huge/src/Makefile

EXE = huge.exe
LIB = 
include $(ROOT)/build/make.rule


Digite source/huge/src para compilar e executar

$ make
$ ls
$ ls deps/
$ ls objs/
$ ls ../../../build/exes/
$ make clean


saída de resultado

insira a descrição da imagem aqui


Descrição gramatical

  • Por meio do arquivo make.rule no diretório build, todos os Makefiles localizados no diretório src de cada módulo de software podem ser usados ​​para melhorar a capacidade de reutilização;
  • Colocando parte do Makefile do módulo foo em make.rule;
  • As definições das variáveis ​​EXE e LIB são diferentes para cada módulo de software, por exemplo, neste projeto, é necessário definir o valor da variável LIB para libfoo.a no Makefile no diretório source/foo.src, e a variável EXE está vazio; e No Makefile no diretório source/huge/src, defina apenas o valor da variável EXE como large.exe;
  • Para tornar a variável DIR_EXES e a variável DIR_LIBS iguais para todos os módulos, pode-se realizar usando o caminho absoluto; este projeto é realizado definindo a variável de ambiente ROOT (deve-se notar que ao exportar a variável ROOT necessária, em além de entrar primeiro fora do diretório raiz do projeto enorme, os caracteres antes e depois do comando pwd são "`" em vez de "'", este caractere é o que está à esquerda da tecla "!" no teclado)


Exemplo 3: adicionar arquivos de programa de origem


1. Edite build/make.rule

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

CC = gcc

AR = ar
ARFLAGS = crs
DIR_OBJS = objs
DIR_EXES = $(ROOT)/build/exes/
DIR_DEPS = deps
DIR_LIBS = $(ROOT)/build/libs
DIRS = $(DIR_DEPS) $(DIR_OBJS) $(DIR_EXES) $(DIR_LIBS)
RMS = $(DIR_OBJS) $(DIR_DEPS)


ifneq ($(EXE), "")
EXE := $(addprefix $(DIR_EXES)/, $(EXE))
RMS += $(EXE)
endif



ifneq ($(LIB), "")
LIB := $(addprefix $(DIR_LIBS)/, $(LIB))
RMS += $(LIB)
endif

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS = $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

ifneq ($(EXE), "")
all: $(EXE)
endif

ifneq ($(LIB), "")
all: $(LIB)
endif

ifneq ($(MAKECMDGOALS), clean)
include $(DEPS)
endif

ifneq ($(INC_DIRS), "")
INC_DIRS:=$(strip $(INC_DIRS))
INC_DIRS:=$(addprefix -I, $(INC_DIRS))
endif

ifneq ($(LINK_LIBS),"")
LINK_LIBS:=$(strip $(LINK_LIBS))
LINK_LIBS := $(addprefix -l, $(LINK_LIBS))
endif

$(DIRS):
        $(MKDIR) -p $@
$(EXE): $(DIR_EXES) $(OBJS)
        $(CC)-L$(DIR_LIBS) -o $@ $(filter %.o, $^) $(LINK_LIBS)
$(LIB): $(DIR_LIBS) $(OBJS)
        $(AR) $(ARFLAGS) $@ $(filter %.o, $^)
$(DIR_OBJS)/%.o: $(DIR_OBJS) %.c
        $(CC) $(INC_DIRS) -o $@ -c $(filter %.c, $^)
$(DIR_DEPS)/%.dep: $(DIR_DEPS) %.c
        set -e ; \
        echo "Making $@ ..." ; \
        $(RM) $(RMFLAGS) $@.tmp ; \
        $(CC) $(INC_DIRS) -E -MM $(filter %.c, $^) > $@.tmp ; \
sed 's,\(.*\)\.o[ :]*,objs/\1.o $@: ,g' < $@.tmp > $@ ; \
        $(RM) $(RMFLAGS) $@.tmp
clean:
        $(RM) $(RMFLAGS) $(RMS)



2. Edite source/foo/src/Makefile

EXE =
LIB = libfoo.a
INC_DIRS = $(ROOT)/source/foo/inc
LINK_LIBS =
include $(ROOT)/build/make.rule


3. Edite source/huge/src/Makefile

EXE = huge
LIB =
INC_DIRS = $(ROOT)/source/foo/inc
LINK_LIBS = foo
include $(ROOT)/build/make.rule


4. Código fonte do arquivo fonte

// huge/source/foo/inc目录 foo.h
#ifndef __FOO_H
#define __FOO_H
void foo();
#endif

// huge/source/foo/src 目录 foo.c
#include <stdio.h>
#include "foo.h"
void foo()
{
    
    
        printf("This is foo()!\n");
}

// huge/source/huge/src 目录 main.c
#include <stdio.h>
#include "foo.h"

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


5. Compile o Makefile de huge/source/foo/src

insira a descrição da imagem aqui

6. Compile o Makefile de huge/source/huge/src

insira a descrição da imagem aqui

7. Execute o enorme de huge/build/exes

insira a descrição da imagem aqui


Descrição gramatical

  • A variável INC_DIRS é adicionada no arquivo make.rule para armazenar todos os arquivos de cabeçalho usados ​​por um módulo, e um bloco de declaração condicional é adicionado no make.rule, ou seja, quando o valor em INC_DIRS não estiver vazio, a função strip é usado primeiro Remova os espaços extras e, em seguida, use a função addprefix para prefixar todos os diretórios com "-I" (i maiúsculo). A alteração final é adicionar uma referência à variável INC_DIRS nas regras de geração de arquivo de objeto e nas regras de geração de arquivo dependente para informar ao gcc onde localizar o arquivo de cabeçalho;
  • A variável LINK_LIBS é adicionada ao arquivo make.rule para armazenar todas as bibliotecas que precisam ser usadas ao vincular;
  • No arquivo make.rule, adicione a variável DIR_LIBS ao diretório de pesquisa do vinculador usando a opção -L do gcc. Como colocamos todos os arquivos de biblioteca no diretório $(DIR_LIBS), esse método pode simplificar o Makefile. porque não preciso especificar vários diretórios;
  • O Makefile no diretório src de cada módulo adiciona a variável LINK_LIBS e, ao mesmo tempo, no source/huge/src/Makefile, o valor negativo de LINK_LIBS é foo (o formato de um nome de biblioteca no linux é libxxxx.a ou .so, onde xxxx É o nome que precisamos dar quando usamos a opção -l (L minúsculo) do gcc)


Exemplo 4: Adicionando módulos para verificar a compatibilidade do projeto de compilação

insira a descrição da imagem aqui

Adicione um módulo zoo ao projeto large e use-o como uma biblioteca libzoo.a no projeto.O módulo zoo e o módulo foo estão no mesmo caminho, conforme mostrado na figura acima.


1. Código fonte do arquivo fonte

// huge/source/zoo/inc目录 zoo.h
#ifndef __ZOO_H
#define __ZOO_H
void zoo();
#endif

// huge/source/zoo/src 目录 zoo.c
#include <stdio.h>
#include "zoo.h"
void zoo()
{
    
    
        printf("This is zoo()!\n");
}

// huge/source/huge/src 目录 main.c
#include <stdio.h>
#include "foo.h"
#include "zoo.h"
int main()
{
    
    
        foo();
        zoo();
        return 0;
}


2. Edite source/zoo/src/Makefile

EXE =
LIB = libzoo.a
INC_DIRS = $(ROOT)/source/zoo/inc
LINK_LIBS =
include $(ROOT)/build/make.rule


3. Edite source/huge/src/Makefile

EXE = huge
LIB =
INC_DIRS = $(ROOT)/source/foo/inc \
$(ROOT)/source/zoo/inc
LINK_LIBS = foo zoo
include $(ROOT)/build/make.rule


4. Compilar source/foo/src/Makefile

insira a descrição da imagem aqui


5. Compile source/zoo/src/Makefile

insira a descrição da imagem aqui


6. Compile source/huge/src/Makefile

insira a descrição da imagem aqui


7. Execute build/exes/huge

insira a descrição da imagem aqui


Descrição gramatical

  • O Makefile sob o módulo zoo é basicamente o mesmo que o Makefile do módulo foo, exceto por algumas pequenas alterações adicionais;
  • O Makefile sob o módulo enorme adiciona uma referência à biblioteca do módulo zoo.

Exemplo 5: Simplifique as operações


A partir do exemplo 4 acima, podemos ver que do arquivo de biblioteca para o arquivo executável, o processo de compilação precisa passar por: "Entre no diretório source/foo/src/ para executar a compilação - insira o source/zoo/src/ diretório para executar a compilação - entre em source/huge/ src/diretório execute a compilação", um total de 3 compilações manuais; se o número de módulos continuar aumentando, as etapas de compilação serão mais complicadas, dificultando o desenvolvimento e a manutenção . Portanto, introduzimos o arquivo Makefile no diretório build/ para simplificar a compilação do projeto.


Edite o Makefile no diretório build/

.PHONY: all clean
DIRS = $(ROOT)/source/foo/src \
$(ROOT)/source/bar/src \
$(ROOT)/source/huge/src

RM = rm
RMFLAGS = -fr
RMS = $(ROOT)/build/exes $(ROOT)/build/libs

all:
	@set -e; \
	for dir in $(DIRS); \
		do \
		cd $$dir && $(MAKE) ; \
	done
	@echo ""
	@echo ":-) Completed"
	@echo ""
clean:
	@set -e; \
	for dir in $(DIRS); \
		do \
		cd $$dir && $(MAKE) clean;\
	done
	$(RM) $(RMFLAGS) $(RMS)
	@echo ""
	@echo ":-) Completed"
	@echo ""


Compile o Makefile no diretório build/

$ ganhar
insira a descrição da imagem aqui


$ make
insira a descrição da imagem aqui

descrição de sintaxe limpa

  • Use a instrução for do shell para percorrer cada diretório na variável DIRS e insira o diretório para executar o make;
  • Use variáveis ​​especiais MAKE em vez de usar make diretamente para melhor portabilidade;
  • O arquivo da biblioteca precisa ser construído antes do programa executável.Na variável DIRS, o diretório da biblioteca precisa ser colocado antes do diretório do programa executável, pois o Makefile é construído de acordo com a ordem dos diretórios.

Acho que você gosta

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