Makefile学习

继Git之后,开始了makefile的学习,在此记录以供之后复习。

makefile描述了整个工程的编译和链接规则

规则

基本构成

  • 目标

    • 默认目标

      • 一个makefile可以有多个目标
      • 一般第一个目标是默认目标
    • 多目标

      • 一个规则中可以有多个目标
      • 多个目标具有相同的命令和依赖关系
    • 多规则目标

      • 多个规则可以是同一个目标
      • Make在解析时,会合并其依赖和命令
    • 伪目标

      • 并不是一个真正的文件名,可以看作是一个标签
      • 无依赖
      • 无条件执行
  • 目标依赖

    • 文件时间戳:根据其时间戳来判断目标依赖文件是否更新
    • 隐式规则:自动将.c文件编译成同名的.o文件等
    • 模式匹配:通配符等
  • 命令

    • 命令的组成:Shell命令组成,tab键开头
    • 命令的执行

      • 每条命令,make会开启一个进程执行
      • 每条命令执行完,make返回一个值,0成果,1失败,失败则退出
    • 并发执行:make -j4
    • 命令在同一进程中执行:用;进行连接
.PONHY:all clean
ifeq ($(DEBUG),"true")
CC = gcc -g
else
CC = gcc
endif
all:test1
all:test2
test1 :
    @echo "Just for test1:$@"
test2:
    @echo "Just for test2"

hello:lcd.o player.o
    @echo "start build hello"
    $(CC) -o hello lcd.o player.o
    @echo "build hello sucess"
#%.o:%.c
#    $(CC) -o $@ -c $^

clean:
    rm -f lcd.o hello player.o
#all:
#    cd /; \
#    pwd

变量

  1. 定义:CC= gcc
  2. 赋值

    • 追加赋值:+=
    • 条件赋值:?=
  3. 引用:$(CC)
  4. 立即展开变量

    • 使用:=进行赋值操作
    • 在预处理阶段进行替换
  5. 延迟展开变量

    • 使用=进行赋值操作
    • 在运行阶段,根据实际情况进行求值
  6. 注意事项:

    • 一般在依赖中使用:=
    • 在命令中使用=
  7. 一般变量:默认为从定义处到整个文件末尾的作用范围
  8. 目标变量:该目标所依赖的规则中都可使用
  9. 模式变量:变量可以定义在符合某种模式的目标上
  10. 自动变量:

    • 目标:$@
    • 所有依赖:$^
    • 第一个依赖:$<
  11. 系统环境变量

    • 对所有makefile有效
    • 若makefile中有同名变量,则系统环境变量背覆盖
    • 命令中传递的同名变量,系统环境变量背覆盖
  12. 变量的传递

    • makefile在多目录下递归执行

      • $(MAKE) -C subdir
      • cd subdir && $(MAKE)
    • 通过expert传递变量
    • 通过命令传递变量
.PHONY:clean

CC = gcc
BIN = hello
OBJS = player.o lcd.o
$(BIN): $(OBJS)
    $(CC) -o $(BIN) $(OBJS)

player.o:player.c
    $(CC) -o player.o -c player.c
lcd.o:lcd.c
    $(CC) -o lcd.o -c lcd.c
clean:
    rm -f player.o lcd.o hello
    
##############################################
STR = hello
STR2 = hello
STR2 += world!

test1 = a
test1 ?= b
test2 ?= b

all:
    @echo "STR = $(STR)"
    @echo "STR2 = $(STR2)"
    @echo "test1 = $(test1)"
    @echo "test2 = $(test2)"
    
##############################################
.PHONY:all

HELLO = Good
TIME = morning!
STRING = $(HELLO) $(TIME)
$(info $(STRING))
TIME = afternoon!
$(info $(STRING))

all:
    @echo "done"
#############################################    
.PHONY:clean

OBJS = player.o lcd.o
BIN  = mp3
N = 1
$(BIN): N = 2
$(BIN):$(OBJS)
    @echo "BIN: N = $(N)"
    gcc -o $(BIN) $(OBJS)
player.o:N= 3
player.o:player.c
    @echo "player.o: N = $(N)"
    gcc -o player.o -c player.c
lcd.o:lcd.c
    @echo "lcd.o: N = $(N)"
    gcc -o lcd.o -c lcd.c
clean:
    @echo "clean: N = $(N)"
    rm -f  $(BIN) $(OBJS)
##############################################
.PHONY:clean
N = 1
OBJS = player.o lcd.o
BIN  = mp3
$(BIN):$(OBJS)
    @echo "BIN:N=$(N)"
    gcc -o $(BIN) $(OBJS)
$(BIN): N = 2
%.o: N = 3
player.o:player.c
    @echo "player.o:N=$(N)"
    gcc -o player.o -c player.c
lcd.o:lcd.c
    @echo "lcd.o:N=$(N)"
    gcc -o lcd.o -c lcd.c
clean:
    @echo "clean: N = $(N)"
    rm  $(BIN) $(OBJS)
##############################################
.PHONY:clean
OBJS = player.o lcd.o
BIN  = mp3
$(BIN):$(OBJS)
    @echo "BIN------------$@:$^"
    gcc -o $@ $^ 
player.o:player.c
    @echo "------------$@:$^"
    gcc -o $@ -c $^
lcd.o:lcd.c
    @echo "---------$@:$^"
    gcc -o $@ -c $^
clean:
    rm -f $(BIN) $(OBJS)
##############################################
.PHONY:all
CFLAGS = -g
all:
    @echo "CFLAGS = $(CFLAGS)"
    @echo "SHELL = $(SHELL)"
    @echo "MAKE = $(MAKE)"
    @echo "HOSTNAME = $(HOSTNAME)"
#############################################
.PHONY:all

export N = 3
#N = 3
all:
    @echo "build...."
#    cd test && make N=$(N)
    cd test && make
    make -C lcd 

.PHONY:all
all:
    @echo "build lcd"
    @echo "N = $(N)"
    
.PHONY:all

all:
    @echo "build test"
    @echo "N = $(N)"

条件执行

关键字:

  • ifeq、else、endif

使用:括号与关键字隔开

ifeq ($(DEBUG),"true")
CC = gcc -g
else
CC = gcc
endif

文本、文件名处理函数

主要还是查看GUN-makefile手册,这里简单列举一些

.PHONY:all
SRCS = player.c lcd.c usb.c media.c hello.h main.txt
OBJS = $(subst .c,.o,$(strip $(SRCS)))
DEPS = $(patsubst %.c,%.d,$(SRCS))
DEPS2 = $(SRCS:.c=.d)
FIND = $(findstring usb,$(SRCS))
FILTER = $(filter %.c %.h, $(SRCS))
all:
    @echo "OBJS = $(OBJS)"    
    @echo "DEPS = $(DEPS)"
    @echo "DEPS2 = $(DEPS2)"
    @echo "FIND = $(FIND)"
    @echo "FILTER = $(FILTER)"
    
#############################################
.PHONY:all
LIB = /home/hello/libhello.a
LIB1 = $(dir $(LIB))
LIB2 = $(notdir $(LIB))
LIB3 = $(suffix $(LIB))
LIB4 = $(basename $(LIB))
LIB5 = $(addsuffix .c,$(LIB4))
LIB6 = $(addprefix /usr/lib/,$(LIB2))
SRCS = $(wildcard *.c)
all:
    @echo "LIB1 = $(LIB1)"
    @echo "LIB2 = $(LIB2)"
    @echo "LIB3 = $(LIB3)"
    @echo "LIB4 = $(LIB4)"
    @echo "LIB5 = $(LIB5)"
    @echo "LIB6 = $(LIB6)"    
    @echo "SRCS = $(SRCS)"
#############################################

A = 1 3 4 5 6 7 8 9
B = $(foreach i,$(A),$(addprefix 0.,$(i)))
C = $(foreach i,$(A),$(addsuffix .0,$(i)))


all:
    @echo "A = $(A)"
    @echo "B = $(B)"
    @echo "C = $(C)"
############################################
.PHONY:all clean
$(shell mkdir -p s1)
$(shell mkdir -p s2)
all:
    @echo "hello world"
clean:
    rm -r s1 s2

库的生成

ar命令生成静态库

.PHONY:clean

libmath.a:libmath.o
    ar rcs $@ $^
libmath.o:libmath.c libmath.h
clean:
    rm libmath.a libmath.o
#######################################
.PHONY: clean

hello:main.o
    gcc -o $@ $^ -L./ -lmath    #-L库的路径 -l库的名字
main.o:main.c
    gcc -o $@ -c $^
clean:
    rm hello main.o

动态库

.PHONY:clean
libdll.so:dll.o
    gcc -o $@ -shared $^
dll.o:dll.c
    gcc -o $@ -fPIC -c $^ 
clean:
    rm libdll.so dll.o
#################################
.PHONY:clean
hello:main.o
    gcc -o hello main.o -L./ -ldll
main.o:main.c
    gcc -o $@ -c -fPIC $^

clean:
    rm -f  main.o hello

基本结构了解后,写了一个小小的例子

#第一个文件:makefile########################
.PHONY:all clean
export BUILD_ROOT = $(shell pwd)
export HEAD_PATH = $(BUILD_ROOT)/inc
all:
    make -C lcd
    make -C usb
    make -C media
    make -C math
    make -C app

clean:
    rm -rf app/link_obj app/dep app/lib_obj mp3
    rm -rf lib/*.a
#第二个文件:common.mk########################
.PHONY:all clean

SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
DEPS = $(SRCS:.c=.d)
BIN := $(addprefix $(BUILD_ROOT)/,$(BIN))

LINK_OBJ_DIR = $(BUILD_ROOT)/app/link_obj
$(shell mkdir -p $(LINK_OBJ_DIR))
DEP_DIR = $(BUILD_ROOT)/app/dep
$(shell mkdir -p $(DEP_DIR))
LIB_OBJ_DIR = $(BUILD_ROOT)/app/lib_obj
$(shell mkdir -p $(LIB_OBJ_DIR))
LIB_DIR = $(BUILD_ROOT)/lib
$(shell mkdir -p $(LIB_DIR))

OBJ_DIR = $(LINK_OBJ_DIR)
ifneq ("$(LIB)","")
OBJ_DIR = $(LIB_OBJ_DIR)
endif
ifneq ("$(DLL)","")
OBJ_DIR = $(LIB_OBJ_DIR)
PIC = -fPIC
endif


OBJS := $(addprefix $(OBJ_DIR)/,$(OBJS))
DEPS := $(addprefix $(DEP_DIR)/,$(DEPS))
LIB  := $(addprefix $(LIB_DIR)/,$(LIB))
DLL  := $(addprefix $(LIB_DIR)/,$(DLL))

LINK_OBJ = $(wildcard $(LINK_OBJ_DIR)/*.o)
LINK_OBJ += $(OBJS)

LIB_DEP = $(wildcard $(LIB_DIR)/*.a) $(wildcard $(LIB_DIR)/*.so)
LINK_LIB_NAME = $(patsubst lib%,-l%,$(basename $(notdir $(LIB_DEP))))    

all: $(DEPS) $(OBJS) $(LIB) $(DLL) $(BIN)
ifneq ("$(wildcard $(DEPS))","")    
include $(DEPS)
endif
$(BIN):$(LINK_OBJ)
    gcc -o $@ $^ -L$(LIB_DIR) $(LINK_LIB_NAME)
$(LIB):$(OBJS)
    ar rcs $@ $^
$(DLL):$(OBJS)
    gcc -shared -o $@ $^
$(OBJ_DIR)/%.o:%.c
    gcc -I$(HEAD_PATH) -o $@ -c $(PIC) $(filter %.c,$^)
$(DEP_DIR)/%.d:%.c
    gcc -I$(HEAD_PATH) -MM $(filter %.c,$^) | sed 's,\(.*\)\.o[ :]*,$(OBJ_DIR)/\1.o $@:,g' > $@
clean:
    rm -f  $(BIN) $(OBJS) $(DEPS)

#第三个文件:驱动之一######################

BIN = 
LIB = libmedia.a
include ../common.mk

安装 Autotools、Automake、Autoconf和Libtool

在安装上述软件时,还是遇到一些了一些问题:

我的环境时Centos7,具体参考了如下内容

[](https://www.codenong.com/cs10...)

但是在最后一个问题的地方,遇到了vim删除“none”无法保存的情况,解决办法是在vim保存时使用如下命令:

  • :w ! sudo tee % > /dev/null

[](http://blog.jacklau2018.com/l...)

最后测试成功,自动生成了Makefile和可执行文件

在在安装libtool要注意其安装路径,可能会安装到/usr/share/aclocal,要将其文件拷贝到/usr/local/share/aclocal下,不然AC_PROG_LIBTOOL宏会找不到。

Makefile学习_第1张图片

注意事项

  • 在引用第三方库时,要考虑在自己环境下的兼容性,但是在发布时这个问题怎么解决?
  • 在此,我只是做了简单的学习,在对参数的设置上还有诸多不熟悉的地方,准备在之后的实战中继续学习。

你可能感兴趣的