通过Makefile编译源码

Compile Source Code By Makefile

Posted by ChenJian on April 29, 2018

编译和链接

  • 编译(compile): 将源码(Source Code)编译成中间代码文件(Object File,Windows下的obj文件,Unix下的o文件)

  • 打包(pack): 将中间代码文件打包, Windows下的lib文件(库文件, Library File),Unix下的a文件(Archive)

  • 链接(link): 将中间代码文件合成执行文件

参考Makefile文件

libnvidia-container之Makefile

规则

Makefile的规则如下:

<target> : <prerequisites> 
[tab]  <commands>
  • target: 目标,*必需项。一个目标构成一条规则;
  • prerequisites: 前置条件。指定“目标”是否要重建构建的判断标准,只要一个前置文件不存在或者有过更新(前置文件的last-modification时间戳比目标的时间戳新),“目标”就需重新构建;
  • [tab]: TAB键,第二行必须以TAB键开头;
  • commands:命令行,运行结果通常是目标文件。

伪目标

例如:

.PHONY: all tools shared static deps install uninstall dist depsclean mostlyclean clean distclean

在使用伪目标时,当文件路径下有个all文件,如果使用命令make all,则会执行对应目标命令。

关键字Include

例如:

include $(MAKE_DIR)/common.mk

在文件common.mk中包含全局变量定义函数定义

在Makefile使用include关键字可以把别的 Makefile 包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。

内置变量

Make命令提供一系列内置变量,比如,$(CC) 指向当前使用的编译器,$(MAKE) 指向当前使用的Make工具。这主要是为了跨平台的兼容性,详细的内置变量清单见手册

$(LIB_OBJS): %.lo: %.c | deps
	$(CC) $(LIB_CFLAGS) $(LIB_CPPFLAGS) -MMD -MF $*.d -c $(OUTPUT_OPTION) $<

deps: $(LIB_RPC_SRCS) $(BUILD_DEFS)
	$(MKDIR) -p $(DEPS_DIR)
	$(MAKE) -f $(MAKE_DIR)/nvidia-modprobe.mk install

函数

Makefile提供了许多内置函数,可供调用。

  • shell函数
UID      := $(shell id -u)
GID      := $(shell id -g)
  • call函数

call函数是唯一一个可以用来创建新的参数化的函数

在makefile中:

ARCH    ?= $(call getarch)

其对应的getarch函数在common.mk

getarch = $(shell [ -f /etc/debian_version ] && echo "amd64" || echo "x86_64")

忽略错误

ifeq ($(WITH_LIBELF), no)
	-$(MAKE) -f $(MAKE_DIR)/elftoolchain.mk clean

$(MAKE)前面加-,表示出现错误时不停止,继续执行下去。

赋值运算符

# 在执行时扩展,允许递归扩展。
VARIABLE = value

# 在定义时扩展。
VARIABLE := value

# 只有在该变量为空时才设置值。
VARIABLE ?= value

# 将值追加到变量的尾端。
VARIABLE += value

区别可查看What is the difference between the GNU Makefile variable assignments =, ?=, := and +=?

循环

Makefile使用 Bash 语法,完成判断和循环。

ifeq ($(WITH_LIBELF), no)
	-$(MAKE) -f $(MAKE_DIR)/elftoolchain.mk clean
endif
ifeq ($(WITH_TIRPC), yes)
	-$(MAKE) -f $(MAKE_DIR)/libtirpc.mk clean
endif

使用docker编译

命令:make docker-centos:7 TAG=beta.1

docker-%: SHELL:=/bin/bash
docker-%:
	image=$* ;\
	$(MKDIR) -p $(DIST_DIR)/$${image/:} ;\
	$(DOCKER) build --network=host \
                    --build-arg IMAGESPEC=$* \
                    --build-arg USERSPEC=$(UID):$(GID) \
                    --build-arg WITH_LIBELF=$(WITH_LIBELF) \
                    --build-arg WITH_TIRPC=$(WITH_TIRPC) \
                    --build-arg WITH_SECCOMP=$(WITH_SECCOMP) \
                    -f $(MAKE_DIR)/Dockerfile.$${image%%:*} -t $(LIB_NAME):$${image/:} . ;\
	$(DOCKER) run --rm -v $(DIST_DIR)/$${image/:}:/mnt:Z -e TAG -e DISTRIB -e SECTION $(LIB_NAME):$${image/:}
  • docker-%: 目标(target)。可以使用make docker-centos:7

  • %: 匹配符。上述%为centos:7;

  • SHELL:=/bin/bash: 前置条件(prerequisites)。意思为当符合前置条件的情况下,执行目标。或者先执行前置条件,再执行目标,该句意思为,在当前SHELL为/bin/bash时,会执行目标docker-%

  • 第二个docker-%,则为目标的主体。
    • 第二行必须由一个TAB键起首,后续跟着命令(commands)
    • 命令中每行在不同进程执行。若要保持连续性,命令之间需要添加;。为编写方便,可添加反斜杠转义\
  • $*: 指代匹配符%匹配的部分, 比如%匹配docker-centos:7中的centos:7 ,$*就表示centos:7;

  • $${image/:}: 在makefile中要引用shell命令中的变量,要使用$${VAR}格式

参考博文

  1. Makefile中使用$$的使用
  2. Make命令教程

知识共享许可协议本作品由陈健采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。