前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Makefile入门

Makefile入门

作者头像
章工运维
发布2024-02-21 12:04:47
930
发布2024-02-21 12:04:47
举报
文章被收录于专栏:章工运维章工运维

# 一、Makefile简介

# 1、Makefile是什么

Makefile是一种用于自动化构建程序的工具,它提供了一系列规则来指定源代码文件之间的依赖关系,以及如何生成目标文件。通过使用Makefile,程序员可以有效地管理和组织软件项目的编译过程,从而提高开发效率。

Makefile文件是一个文本文件,其中包含一系列规则和指令,用于编译源代码并生成可执行文件或库。每个规则由一个目标文件、一个或多个依赖文件和一组命令组成,这些命令描述了如何从依赖文件生成目标文件。

Makefile的主要作用是简化或组织编译代码的过程,它可以帮助程序员自动化编译、链接和打包程序。通过将整个项目分解为多个模块,并定义每个模块之间的依赖关系,当某个模块发生变化时,只需要重新编译该模块及其依赖的其他模块即可。这有助于减少手动操作和错误,并提高代码质量。

此外,Makefile还支持变量定义和隐晦规则等特性,这些特性可以帮助程序员更加灵活地编写Makefile,并使构建过程更加易于维护和扩展。

# 2、make 和 Makefile的关系

Make是一个命令工具,用于解释和执行Makefile中的指令,完成项目的自动化构建。Makefile是一个文件,其中定义了一系列的规则来指定哪些文件需要先编译、哪些文件需要后编译、哪些文件需要重新编译等。

当我们在命令行中输入make命令时,Make会查找当前目录下是否存在名为Makefile或makefile的文件。如果找到,Make会按照Makefile文件中的规则和指令,自动执行相应的命令来编译和链接源代码文件,生成可执行文件或库。

# 二、Makefile 三要素

Makefile的三个要素是目标、依赖和命令。它们在Makefile中的格式如下:

目标(Target):目标是指需要生成的文件或目标体,可以是Object File(一般称为中间文件)、可执行文件或标签。目标定义了生成的目标体,并指明生成目标体需要哪些依赖文件。 依赖(Dependency):依赖是指生成目标体所需的文件或另一个目标。它可以是一个或多个文件,也可以没有。依赖项描述了目标文件与源文件之间的依赖关系,告诉Make如何从源文件生成目标文件。 命令(Command):命令是Make需要执行的命令行指令,可以是任意的shell命令。这些命令描述了如何从依赖文件生成目标文件。在Makefile中,命令部分需要有一定的缩进,可以是一行或多行,它们会依次执行。 以下是Makefile三要素的格式示例:

代码语言:javascript
复制
目标: 依赖  
    命令

其中,目标和依赖之间用冒号(:)分隔,命令部分需要缩进。

示例:

代码语言:javascript
复制
# vim Makefile
targeta:targetb targetc
        echo "targeta"

targetb:
        echo "targetb"

targetc:
        echo "targetc"

执行make命令

代码语言:javascript
复制
# make
echo "targetb"
targetb
echo "targetc"
targetc
echo "targeta"
targeta

指定执行命令

代码语言:javascript
复制
# make targetb
echo "targetb"
targetb
# touch targetb
# make targetb			# 当前目录下targetb是最新文件不需要改变
make: `targetb' is up to date.

修改Makefile文件

代码语言:javascript
复制
# vim Makefile
.PHONY:targetb
targeta:targetb targetc
        echo "targeta"

targetb:
        echo "targetb"

targetc:
        echo "targetc"
# make targetb
echo "targetb"
targetb        

.PHONY:targetb的意思是声明``targetb是一个伪目标,即使存在名为targetb的文件。这意味着,每次当你运行make targetb时,make不会尝试查找一个叫做targetb的文件并尝试运行它的命令,而是会执行与targetb` 关联的命令。

在这个例子中,如果你运行 make targetb,它会输出 "targetb"。如果你运行 make targeta,它会首先运行 targetb 和 targetc 的命令,然后执行与 targeta 关联的命令,输出 "targeta"。

# 三、引入Makefile管理项目

生成两个文件

代码语言:javascript
复制
# vim mp3.c
#include<stdio.h>

void play()
{
        printf("paly music!\r\n");
}

void stop()
{
        printf("stop music!\r\n");
}
# vim main.c
#include<stdio.h>

int main()
{
        play();
        stop();
        return 0;
}

编写Makefile

代码语言:javascript
复制
# vim Makefile
mq3:main.c mp3.c
        gcc main.c mp3.c -o mp3

.PHONE:clean

clean:
        rm mp3
# make
# ls
main.c  Makefile  mp3  mp3.c
# ./mp3
paly music!
stop music!

改造Makefile,目的是将mp3.c和main.c解耦,当修改mp3.c或者main.c时,不需要重新编译另一个文件

代码语言:javascript
复制
# vim Makefile
mq3:main.o mp3.o
        gcc main.o mp3.o -o mp3

main.o:
        gcc -c main.c -o main.o

mp3.o:
        gcc -c mp3.c -o mp3.o

.PHONE:clean

clean:
        rm mp3
# make clean
rm mp3
# make
# ./mp3
paly music!
stop music!

# 四、Makefile的变量和模式匹配

# 1、系统变量

代码语言:javascript
复制
# vim Makefile
.PHONY:all

all:
        echo "${CC}"
        echo "${AS}"
        echo "${MAKE}"
        echo "${PATH}"
# make
echo "cc"
cc
echo "as"
as
echo "make"
make
echo "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

# 2、自定义变量

1、延迟赋值

在Makefile中,变量的默认赋值方式是“延迟”的。这意味着变量只有在被引用时才会被计算,并且只计算一次。如果一个变量在多个地方被引用,那么只有第一次引用时会被计算,后续的引用会使用第一次计算的结果。

代码语言:javascript
复制
# vim Makefile
A=123
B=$(A)
A=456

.PHONY:all

all:
        echo "${B}"
# make
echo "456"
456

2、立即赋值

为了改变默认的延迟赋值行为,我们可以使用:=操作符进行“立即”赋值。这意味着变量会在声明时立即被计算,并且在之后的所有引用中都会使用这个计算结果。即使变量的值在后续发生了变化,之前的引用也不会受到影响。

代码语言:javascript
复制
# vim Makefile
A=123
B:=$(A)
A=456

.PHONY:all

all:
        echo "${B}"
# make
echo "123"
123        

3、空赋值

使用=操作符可以将变量设置为空字符串。这在需要临时清除变量的内容时很有用。

代码语言:javascript
复制
# vim Makefile
A?=123
#B:=$(A)
A?=456

.PHONY:all

all:
        echo "${A}"
# make
echo "123"
123        

4、追加赋值

如果想要将一个值追加到变量的末尾,可以使用+=操作符。这不会覆盖变量的现有内容,而是将新值添加到变量的末尾。

代码语言:javascript
复制
# vim Makefile
A?=123
#B:=$(A)
A+=456

.PHONY:all

all:
        echo "${A}"
# make
echo "123 456"
123 456

# 3、自动化变量

1、$<

代表第一个依赖项。在规则的命令部分,$<将被替换为第一个依赖项的文件名。

代码语言:javascript
复制
# vim Makefile
all:targeta targetb
        echo "$<"

targeta:

targetb:
# make
echo "targeta"
targeta

2、$^

代表所有的依赖项。在规则的命令部分,$^将被替换为一个空格分隔的列表,包含了所有依赖项的文件名

代码语言:javascript
复制
# vim Makefile
all:targeta targetb
        echo "$^"

targeta:

targetb:
# make
echo "targeta targetb"
targeta targetb

3、$@

代表目标文件。在规则的命令部分,$@将被替换为目标文件的名字。

代码语言:javascript
复制
# vim Makefile
all:targeta targetb
        echo "$@"

targeta:

targetb:
# make
echo "all"
all

# 4、变量使用

代码语言:javascript
复制
# vim Makefile
CC=gcc
TARGET=mp3
OBJS=main.o mp3.o

$(TARGET):$(OBJS)
        $(CC) $^ -o $@

main.o:main.c
        $(CC) -c main.c -o main.o

mp3.o:mp3.c
        $(CC) -c mp3.c -o mp3.o

.PHONY:clean

clean:
        rm mp3
# make
gcc main.o mp3.o -o mp3
# ./mp3
paly music!
stop music!

# 5、模式匹配

%:匹配任意多个非空字符

代码语言:javascript
复制
# vim Makefile
%:
        echo "$@"
# make test			# make后跟任意字符
echo "test"
test

优化Makefile

代码语言:javascript
复制
# vim Makefile
CC=gcc
TARGET=mp3
OBJS=main.o mp3.o

$(TARGET):$(OBJS)
        $(CC) $^ -o $@
# 因为.o文件默认使用.c文件来进行编译,所以可以注释以下两行
#%.o:%.c
#        ${CC} -c $< -o $@

.PHONY:clean

clean:
        rm mp3
# make
gcc main.o mp3.o -o mp3
# ./mp3
paly music!
stop music!

# 五、Makefile的条件分支

代码语言:javascript
复制
# vim Makefile
ARCH ?= x86

ifeq ($(ARCH),x86)
        CC=gcc
else
        CC=arm-linux-gnueabihf-gcc
endif

TARGET=mp3
OBJS=main.o mp3.o

$(TARGET):$(OBJS)
        $(CC) $^ -o $@

.PHONY:clean

clean:
        rm mp3 *.o
# make
gcc    -c -o mp3.o mp3.c
gcc main.o mp3.o -o mp3
# make ARCH=arm
arm-linux-gnueabihf-gcc    -c -o mp3.o mp3.c
arm-linux-gnueabihf-gcc main.o mp3.o -o mp3

# 六、Makefile管理Docker

# 1、创建一个Dockerfile

代码语言:javascript
复制
FROM nginx
RUN echo '<h1>Hello World!</h1>' > /usr/share/nginx/html/index.html

# 2、创建Makefile文件

代码语言:javascript
复制
# 定义 Docker 镜像名称和标签  
REGISTRY := your-private-registry  
USERNAME := your-username  
PASSWORD := your-password  
TAG := latest  
IMAGE := my-web  
# 这是一个条件赋值。如果变量 CONTAINER_NAME 还没有被赋值,那么它会被赋值为 my-container
CONTAINER_NAME ?= my-container

# 构建镜像 
build: docker-build  
    docker build -t $(IMAGE):$(TAG) .  
    
# 上传镜像到私有仓库  
upload: docker-push  
    docker login -u $(USERNAME) -p $(PASSWORD) $(REGISTRY)  
    docker push $(IMAGE):$(TAG)  
  
# 运行镜像
run: docker-run  
    docker run -p 80:80 $(CONTAINER_NAME) $(IMAGE):$(TAG)
    
# 停止并删除容器
clean: docker-stop docker-rm  
    docker stop $(CONTAINER_NAME)
    docker rm $(CONTAINER_NAME)  

# 3、执行命令

代码语言:javascript
复制
# make build
# make upload
# make run
# make clean
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • # 一、Makefile简介
    • # 1、Makefile是什么
      • # 2、make 和 Makefile的关系
      • # 二、Makefile 三要素
      • # 三、引入Makefile管理项目
      • # 四、Makefile的变量和模式匹配
        • # 1、系统变量
          • # 2、自定义变量
            • # 3、自动化变量
              • # 4、变量使用
                • # 5、模式匹配
                • # 五、Makefile的条件分支
                • # 六、Makefile管理Docker
                  • # 1、创建一个Dockerfile
                    • # 2、创建Makefile文件
                      • # 3、执行命令
                      相关产品与服务
                      容器服务
                      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档