Makefile基础学习

Makefile基础学习 

https://my.oschina.net/lvhuizhenblog/blog/677231

书写命令
    用"@"字符在命令前,那么这个命令就不被make显示出来
    
    如果要让上一条命令的结果应用在下一条命令时,应该使用分号分隔这两条命令
    例如:
        exec:
            cd /home/;pwd
            
    忽略命令的出错,可以在Makefile的命令行前加一个减号"-"(在Tab键之后),标记为不管命令出不出错都认为是成功的。如:
    clean:
        -rm -f *.o
    
    嵌套执行make:
    有一个子目录叫subdir,这个目录下有个makefile文件,来指明了这个目录下文件的编译则,
    那么总控的Makefile可以这样书写:
        subsystem:
            cd subdir && $(MAKE)
    其等价于:
        subsystem:
            $(MAKE) -C subdir
        
    定义命令包:
        如果Makefile中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。
        定义这个命令序列的语法以"define"开始,以"endif"结束。


使用变量
    变量的基础:
    变量在声明时需要给予初值,而在使用时,需要给在变量名前加上"$"符号,但最好用小括号"()"或是大括号"{}"把变量给包括起来。如果你要使用真实的"$"字符,那么你需要用"$$"表示。
    
    变量中的变量:
    第一种:简单的使用"="号,在"="左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的值。这个功能有好的地方,也有不好的地方,好的地方是,可以把变量的真实值推到后面来定义。
    不好的地方:就是递归定义。
    如:
        A = $(B)
        B = $(A)
    为了避免上面的这种方法,可以使用make中的另一种用变量来定义变量的方法。这种方法使用的是 ":="操作符。这种方法不能使用后面的变量,只能使用前面已定义好了的变量。
    还有一个比较有用的操作符是"?=",如:
    FOO ?= bar
    其含义是,如果FOO没有被定义过,那么变量FOO的值就是"bar"
    
    变量高级用法:
    变量值的替换
    替换变量中的共有部分,其格式是"$(var:a=b)"或是"${var:a=b}",其意思是,把变量"var"中所有以 "a"字串"结尾"的"a"替换成"b"字串。这里的"结尾"意思是"空格"或者"结束符"。
    另一种变量替换的技术是"静态模式",如:
    foo := a.o b.o c.o
    bar := $(foo:%.o=%.c)
    这依赖于被替换字串中的有相同的模式,模式中必须包含一个"%"字符

    
    追加变量值:+=
    
    override指示符
    如果有变量是通常make的命令行参数设置的,那么makefile中对这个变量的赋值会被忽略。
    
    环境变量:
        make运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是如果Makefile中已经定义了这个变量,或者这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。 (如果make指定了"-e"参数,那么,系统环境变量将覆盖Makefile中定义的变量)
        因此,如果我们在环境变量中定义了"CFLAGS"环境变量,那么我们就可以在所有的Makefile中使用这个变量了。如果Makefile中定义了CFLAGS,那么则会使用Makefile中的这个变量, 如果没有定义则使用系统变量的值。


条件判断
    条件表达式的语法为:
    <conditional-directive>
    <text-if-true>
    endif
    以及:
    <conditional-directive>
    <text-if-true>
    else
    <text-if-false>
    endif


    如:判断$(CC)变量是否"gcc"
    ifeq($(CC), gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
    else
        $(CC) -o foo $(objects) $(normal_libs)
    endif
    
    ifdef<variable-name>
    如果变量<variable-name>的值非空,那么表达式为真。否则,表达式为假。


函数的调用语法
    $(<function><arguments>) 或 ${<function><arguments>}
    这里,<function>就是函数名,make支持的函数不多。<arguments>是函数的参数,参数之间以逗号","分隔,而函数名和参数之间以"空格"分隔。函数调用以"$"开头,以圆括号或花括号把函数名和参数括起。
    
    字符串处理函数
    $(subst<from>,<to>,<text>)
    名称:字符串替换函数: subst.
    功能:把字串<text>中的<from>字符串替换成<to>
    返回: 函数返回被替换过后的字符串
    示例:
        $(subst ee,EE,feet on the street)
        把"feet on the street"中"ee"替换成"EE",返回结果是"fEEt on the strEEt"
    
    $(patsubst <pattern>,<replacement>,<text>)
    名称:模式字符串替换函数——patsubst.
    功能:查找<text>中的单词(单词以"空格","Tab"或者"回车","换行"分隔)是否符合<pattern>,
    如果匹配的话,则以<replacement>替换。这里,<pattern>可以包含通配符"%",表示任意长度的字串。  如果<replacement>中也包含"%",那么,<replacement>中的这个"%"将是<pattern>中的那个"%"所代表 的字串。(可以用"\"来转义,以"\%"来表示真实含义的"%"字符)
    返回值:函数返回被替换过后的字符串
    示例:
        $(patsubst %.c,%.o,x.c.c bar.c)
        把字符串"x.c.c bar.c"符合模式[%.c]的单词替换成[%.o],返回结果是"x.c.o bar.o"
        
    $(wordlist <s>,<e>,<text>)
    名称:去单词串函数——wordlist
    功能:返回字符串<text>中从<s>到<e>的单词字串。如果<s>比<text>中的单词数要大,那么返回空字符串。如果<e>大于<text>的单词数,那么返回<s>开始,到<text>结束的单词串。
    示例:
        $(wordlist 2,3,foo bar baz)
        返回值是"bar baz"


MAKE的运行
    makefile的目标:
    "all"
    这个伪目标是所有目标的目标,其功能一般是编译所有的目标。

    "clean"
    这个伪目标功能是删除所有的被make创建的文件。
    "install"
    这个伪目标功能是安装已编译好的程序,其实就是把目标自行文件拷贝到指定的目标中去。
    "print"
    这个伪目标的功能是例出改变过的源文件。
    "tar"
    这个伪目标功能是把源程序打包备份。也就是一个tar文件。
    "dist"
    这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或者gz文件。
    "check"和"test"
    这两个伪目标一般用来测试makefile的流程


在文件名中使用通配符:

Makefile的通配符为*,?,[],与shell使用的是一样的通配符。Makefile的通配符只有在targets 和prerequisites中展开,在定义变量时是不会展开的,

如果想在定义变量时展开通配符,需要使用wildcard函数。

如果文件名包含*,号,需要用\号转义,如foo*bar可以这样表示:foo\*bar.

如在/opt/src文件夹下包含有mcu.c test.c,

SRCS=$(wildcard *.c)

SRCS = mcu.c test.c

objects := $(patsubst %.c,%.o,$(wildcard *.c))

objects=mcu.o test.o


Redis src中的makefile文件

# Redis Makefile
# Copyright (C) 2009 Salvatore Sanfilippo <antirez at gmail dot com>
# This file is released under the BSD license, see the COPYING file
#
# The Makefile composes the final FINAL_CFLAGS and FINAL_LDFLAGS using
# what is needed for Redis plus the standard CFLAGS and LDFLAGS passed.
# However when building the dependencies (Jemalloc, Lua, Hiredis, ...)
# CFLAGS and LDFLAGS are propagated to the dependencies, so to pass
# flags only to be used when compiling / linking Redis itself REDIS_CFLAGS
# and REDIS_LDFLAGS are used instead (this is the case of 'make gcov').
#
# Dependencies are stored in the Makefile.dep file. To rebuild this file
# Just use 'make dep', but this is only needed by developers.

#  =  前面的变量可以使用后面的变量,可能会导致递归定义
# :=  前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
# ?=  如果OPTIMIZATION没有定义过,那么OPTIMIZATION的值就是-O2
# $(shell ) shell函数

release_hdr := $(shell sh -c './mkreleasehdr.sh')
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
OPTIMIZATION?=-O2
DEPENDENCY_TARGETS=hiredis linenoise lua

# Default settings
STD=-std=c99 -pedantic
# -Wall 允许发出GCC提供的所有有用的报警信息
# -w 关闭所有告警
WARN=-Wall -W 
OPT=$(OPTIMIZATION)

# ?=  如果PREFIX没有被定义过则PREFIX的值为/usr/local
PREFIX?=/usr/local
# INSTALL_BIN=/usr/local/bin
INSTALL_BIN=$(PREFIX)/bin
INSTALL=install

# ifeql  uname_s和"Linux"相等
# Default allocator
ifeq ($(uname_S),Linux)
	MALLOC=jemalloc
else
	MALLOC=libc
endif

# Backwards compatibility for selecting an allocator
ifeq ($(USE_TCMALLOC),yes)
	MALLOC=tcmalloc
endif

ifeq ($(USE_TCMALLOC_MINIMAL),yes)
	MALLOC=tcmalloc_minimal
endif

ifeq ($(USE_JEMALLOC),yes)
	MALLOC=jemalloc
endif

# 使用include关键字可以把别的Makefile包含进来
# 如果想让make不理那些无法读取的文件,而继续执行,可以在include前加一个减号"-"
# Override default settings if possible
-include .make-settings

# CFLAGS  C编译器参数
# LDFLAGS 链接器参数
FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)
FINAL_LIBS=-lm
DEBUG=-g -ggdb

ifeq ($(uname_S),SunOS)
	# SunOS
	INSTALL=cp -pf
	FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6
	FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt
else
ifeq ($(uname_S),Darwin)
	# Darwin (nothing to do)
else
ifeq ($(uname_S),AIX)
        # AIX
        FINAL_LDFLAGS+= -Wl,-bexpall
        FINAL_LIBS+= -pthread -lcrypt -lbsd

else
	# All the other OSes (notably Linux)
	FINAL_LDFLAGS+= -rdynamic
	FINAL_LIBS+= -pthread
endif
endif
endif
# Include paths to dependencies
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src

ifeq ($(MALLOC),tcmalloc)
	FINAL_CFLAGS+= -DUSE_TCMALLOC
	FINAL_LIBS+= -ltcmalloc
endif

ifeq ($(MALLOC),tcmalloc_minimal)
	FINAL_CFLAGS+= -DUSE_TCMALLOC
	FINAL_LIBS+= -ltcmalloc_minimal
endif

ifeq ($(MALLOC),jemalloc)
	DEPENDENCY_TARGETS+= jemalloc
	FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include
	FINAL_LIBS+= ../deps/jemalloc/lib/libjemalloc.a -ldl
endif

REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)

# ??颜色的控制?
CCCOLOR="\033[34m"
LINKCOLOR="\033[34;1m"
SRCCOLOR="\033[33m"
BINCOLOR="\033[37;1m"
MAKECOLOR="\033[32;1m"
ENDCOLOR="\033[0m"

# ifndef<variable-name> 如果变量<variable-name>的值为空,那么表达式为真,否则为假。
ifndef V
QUIET_CC = @printf '    %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2;
QUIET_LINK = @printf '    %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
QUIET_INSTALL = @printf '    %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
endif

REDIS_SERVER_NAME=redis-server
REDIS_SENTINEL_NAME=redis-sentinel
REDIS_SERVER_OBJ=adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o
REDIS_CLI_NAME=redis-cli
REDIS_CLI_OBJ=anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o
REDIS_BENCHMARK_NAME=redis-benchmark
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o redis-benchmark.o
REDIS_CHECK_DUMP_NAME=redis-check-dump
REDIS_CHECK_DUMP_OBJ=redis-check-dump.o lzf_c.o lzf_d.o crc64.o
REDIS_CHECK_AOF_NAME=redis-check-aof
REDIS_CHECK_AOF_OBJ=redis-check-aof.o

all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME)
	@echo ""
	@echo "Hint: It's a good idea to run 'make test' ;)"
	@echo ""

# .PHONY 伪目标
# all 这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
.PHONY: all

# Deps (use make dep to generate this)
include Makefile.dep

dep:
	$(REDIS_CC) -MM *.c > Makefile.dep

.PHONY: dep

persist-settings: distclean
	echo STD=$(STD) >> .make-settings
	echo WARN=$(WARN) >> .make-settings
	echo OPT=$(OPT) >> .make-settings
	echo MALLOC=$(MALLOC) >> .make-settings
	echo CFLAGS=$(CFLAGS) >> .make-settings
	echo LDFLAGS=$(LDFLAGS) >> .make-settings
	echo REDIS_CFLAGS=$(REDIS_CFLAGS) >> .make-settings
	echo REDIS_LDFLAGS=$(REDIS_LDFLAGS) >> .make-settings
	echo PREV_FINAL_CFLAGS=$(FINAL_CFLAGS) >> .make-settings
	echo PREV_FINAL_LDFLAGS=$(FINAL_LDFLAGS) >> .make-settings
	-(cd ../deps && $(MAKE) $(DEPENDENCY_TARGETS))

.PHONY: persist-settings

# Prerequisites target
.make-prerequisites:
	@touch $@

# Clean everything, persist settings and build dependencies if anything changed
ifneq ($(strip $(PREV_FINAL_CFLAGS)), $(strip $(FINAL_CFLAGS)))
.make-prerequisites: persist-settings
endif

ifneq ($(strip $(PREV_FINAL_LDFLAGS)), $(strip $(FINAL_LDFLAGS)))
.make-prerequisites: persist-settings
endif

# $@表示规则中的目标文件集。在模式规则中,
# 如果有多个目标,那么"S@"就是匹配于目标中模式定义的集合

# $^ 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,
# 那么这个变量会去除重复的依赖目标,只保留一份。

# redis-server
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)
	$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)

# redis-sentinel
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)
	$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME)

# redis-cli
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
	$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)

# redis-benchmark
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
	$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)

# redis-check-dump
$(REDIS_CHECK_DUMP_NAME): $(REDIS_CHECK_DUMP_OBJ)
	$(REDIS_LD) -o $@ $^ $(FINAL_LIBS)

# redis-check-aof
$(REDIS_CHECK_AOF_NAME): $(REDIS_CHECK_AOF_OBJ)
	$(REDIS_LD) -o $@ $^ $(FINAL_LIBS)

# $< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,
# 那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。

# Because the jemalloc.h header is generated as a part of the jemalloc build,
# building it should complete before building any other object. Instead of
# depending on a single artifact, build all dependencies first.
%.o: %.c .make-prerequisites
	$(REDIS_CC) -c $<

# clean 这个伪目标功能是删除所有的被make创建的文件
clean:
	rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html

.PHONY: clean

# - 在Makefile的命令行前加一个减号"-"(在Tab键之后),标记为不管命令出不出错都认为是成功的。
distclean: clean
	-(cd ../deps && $(MAKE) distclean)
	-(rm -f .make-*)

.PHONY: distclean

# @ 用"@"字符在命令前,那么这个命令就不被make显示出来
# test check 这两个伪目标一般用来测试makefile的流程
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)
	@(cd ..; ./runtest)

test-sentinel: $(REDIS_SENTINEL_NAME)
	@(cd ..; ./runtest-sentinel)

check: test

lcov:
	$(MAKE) gcov
	@(set -e; cd ..; ./runtest --clients 1)
	@geninfo -o redis.info .
	@genhtml --legend -o lcov-html redis.info

.PHONY: lcov

bench: $(REDIS_BENCHMARK_NAME)
	./$(REDIS_BENCHMARK_NAME)

32bit:
	@echo ""
	@echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
	@echo ""
	$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"

gcov:
	$(MAKE) REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage"

noopt:
	$(MAKE) OPTIMIZATION="-O0"

valgrind:
	$(MAKE) OPTIMIZATION="-O0" MALLOC="libc"

src/help.h:
	@../utils/generate-command-help.rb > help.h

# install 这个伪目标功能是安装已编译好的程序,其实就是把目标自行文件拷贝到指定的目标中去
install: all
	@mkdir -p $(INSTALL_BIN)
	$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN)
	$(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN)
	$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)
	$(REDIS_INSTALL) $(REDIS_CHECK_DUMP_NAME) $(INSTALL_BIN)
	$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)
	@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)

相关资料

http://my.oschina.net/hevakelcj/blog/414295
   

猜你喜欢

转载自blog.csdn.net/chenxiuli0810/article/details/93752907