Linux应用开发基础知识——Makefile 的使用(二)

news2024/9/30 9:25:34

前言:

在 Linux 中使用 make 命令来编译程序,特别是大程序;而 make 命令所执 行的动作依赖于 Makefile 文件。最简单的 Makefile 文件: hello: hello.c 、gcc -o hello hello.c 、clean:、 rm -f hello 将上述 4 行存为 Makefile 文件,放入 01_hello 目录下,然后直接执行 make 命令即可编译程序,执行 “make clean”即可清除编译出来的结果。 make 命令根据文件更新的时间戳来决定哪些文件需要重新编译,这使得可以避免编译已经编译过的、没有变化的程序,可以大大提高编译效率。目前我们只需要会使用Makefile即可,等后面学到驱动更底层的知识时候再详细认识Makefile。

一、Makefile 规则与示例 

规则

建立两个文件

(1)a.c

#include <stdio.h>

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

(2)b.c

#include <stdio.h>

void func_b()
{
        printf("This is B\n");
}

(3)Makefile

test:a.o b.o
        gcc -o test a.o b.o

a.o : a.c
        gcc -c -o a.o a.c

b.o : b.c
        gcc -c -o b.o b.c

book@100ask:~/source/04_2018_Makefile/001_test_app$ gcc -o test a.c b.c

        这样编译会全部编译步骤进行一下,如果每次这样编译会大大降低效率,应该改哪步进行编译哪步,这样才能更好的提高编译效率,所以我们就要学习makefile的规则,一个简单的 Makefile 文件包含一系列的“规则”,其样式如下:

目标(target)… : 依赖(prerequiries)…

命令(command)

如果“依赖文件”比“目标文件”更加新,那么执行“命令”来重新生成“目 标文件”。

命令被执行的 2 个条件:依赖文件比目标文件,或是目标文件还没生成。 

二、Makefile的语法

1.通配符: %.o

$@ 表示目标

$<  表示第1个依赖文件

$^   表示所有依赖文件

test: a.o b.o c.o
        gcc -o test $^

%.o : %.c
        gcc -c -o $@ $<

 2.假想目标:.PHONY

Makefile 

test: a.o b.o c.o
        gcc -o test $^

%.o : %.c
        gcc -c -o $@ $<

clean:
        rm *.o test

.PHONY: clean

3.即时变量、延时变量,export

简单变量(即时放量):

A := xxx        #    A的值即刻确定,在定义时即确定

B = xxx           #    B的值使用到时才确定

:=        #即时变量
=         #延时变量
?=       #延时变量,如果是第1次定义才起效,如果在前面该变量已定义则忽略这句

+=        #附加,它是即时变量还是延时变量取决于前面的定义

Makefile  

A := $(C)
B = $(C)
C = abc

#D = 100ask
D ?= weidongshan

all:
        @echo A = $(A)
        @echo B = $(B)
        @echo D = $(D)

C += 123

三、Makefile 的函数

$(foreach var,list,text)

简单地说,就是 for each var in list, change it to text。

对 list 中的每一个元素,取出来赋给 var,然后把 var 改为 text 所描述 的形式。

A = a b c
B = $(foreach f, $(A), $(f).o)

all:
        @echo B = $(B)

 $(wildcard pattern)

 pattern 所列出的文件是否存在,把存在的文件都列出来。

$(filter pattern. ..,text)                    #在text中取出符合patten格式的值

$(filter-out pattern.. . ,text)            #在text中取出不符合patten格式的值

C = a b c d/

D = $(filter %/, $(C))
E = $(filter-out %/, $(C))

all:
        @echo D = $(D)
        @echo E = $(E)

$(wildcard pattern)

pattern定义了文件名的格式,wildcard取出其中存在的文件

files = $(wildcard *.c)    #列出符合后缀是.c的文件都有哪些

all:
        @echo files = $(files)

files2 = a.c b.c c.c d.c e.c
files3 = $(wildcard $(files2))

all:
        @echo files = $(files)
        @echo files3 = $(files3)

 $(patsubst pattern,replacement,$(files2))

从列表中取出每——个值,如果符合pattern,则替换为replacement
 

files2 = a.c b.c c.c d.c e.c
files3 = $(wildcard $(files2))

dep_files = $(patsubst %.c,%.d,$(files2))    #将files2中.c后缀文件替换为.d后缀文件

all:
        @echo dep_files = $(dep_files)

 所有Makefile 函数代码

A = a b c
B = $(foreach f, $(A), $(f).o)


C = a b c d/

D = $(filter %/, $(C))
E = $(filter-out %/, $(C))

files = $(wildcard *.c)


files2 = a.c b.c c.c d.c e.c  abc
files3 = $(wildcard $(files2))

dep_files = $(patsubst %.c,%.d,$(files2))

all:
        @echo B = $(B)
        @echo D = $(D)
        @echo E = $(E)
        @echo files = $(files)
        @echo files3 = $(files3)
        @echo dep_files = $(dep_files)

四、Makefile实例

1.支持头文件依赖

a.c


#include <stdio.h>

void func_b();
void func_c();


int main()
{
        func_b();
        func_c();
        return 0;
}

 b.c


#include <stdio.h>

void func_b()
{
        printf("This is B\n");
}

c.c


#include <stdio.h>
#include "c.h"

void func_c()
{
        printf("This is C = %d\n", C);
}

 c.h


#define C 4

gcc -M c.c                                       //打印出依赖
gcc -M -MF c.d c.c                         //把依赖写入文件c.d
gcc -c -o c.o c.c -MD -MF c.d        //编译c.o,把依赖写入文件c.d

Makefile 

objs = a.o b.o c.o

dep_files := $(patsubst %,.%.d, $(objs))
dep_files := $(wildcard $(dep_files))


test: $(objs)
	gcc -o test $^

ifneq ($(dep_files),)                              #将依赖文件添加进去
include $(dep_files)
endif

%.o : %.c
	gcc -c -o $@ $< -MD -MF .$@.d        #自动生成依赖文件

clean:
	rm *.o test

distclean:
	rm $(dep_files)
	
.PHONY: clean	

 2.添加CFLAGS

c.c


#include <stdio.h>
#include <c.h>

void func_c()
{
        printf("This is C = %d\n", C);
}

CFLAGS = -Werror -I.include         #-Werror将所有的警告变为错误
                                                       #-I.include执行当前目录下的include文件

%.o : %.c
        gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d       #自动生成依赖文件
 

Makefile  

objs = a.o b.o c.o

dep_files := $(patsubst %,.%.d, $(objs))
dep_files := $(wildcard $(dep_files))

CFLAGS = -Werror -I.include                           #-Werror将所有的警告变为错误
                                                      #-I.include执行当前目录下的include文件

test: $(objs)
        gcc -o test $^

ifneq ($(dep_files),)                                 #将依赖文件添加进去
include $(dep_files)
endif

%.o : %.c
        gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d       #自动生成依赖文件

clean:
        rm *.o test

distclean:
        rm $(dep_files)

.PHONY: clean


五、Makefile的使用

参考 Linux 内核的 Makefile 编写了一个通用的 Makefile,它可以用来 编译应用程序:

1.支持多个目录、多层目录、多个文件;

2.支持给所有文件设置编译选项;

3.支持给某个目录设置编译选项;

4.支持给某个文件单独设置编译选项;

5.简单、好用。

说明:

本程序的Makefile分为3类:
1. 顶层目录的Makefile
2. 顶层目录的Makefile.build
3. 各级子目录的Makefile

一、各级子目录的Makefile:
   它最简单,形式如下:

EXTRA_CFLAGS  := 
CFLAGS_file.o := 

obj-y += file.o
obj-y += subdir/
   
   "obj-y += file.o"  表示把当前目录下的file.c编进程序里,
   "obj-y += subdir/" 表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。
   "EXTRA_CFLAGS",    它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置
   "CFLAGS_xxx.o",    它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置

注意: 
1. "subdir/"中的斜杠"/"不可省略
2. 顶层Makefile中的CFLAGS在编译任意一个.c文件时都会使用
3. CFLAGS  EXTRA_CFLAGS  CFLAGS_xxx.o 三者组成xxx.c的编译选项

二、顶层目录的Makefile:
   它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,
   主要是定义工具链前缀CROSS_COMPILE,
   定义编译参数CFLAGS,
   定义链接参数LDFLAGS,
   这些参数就是文件中用export导出的各变量。

三、顶层目录的Makefile.build:
   这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o

四、怎么使用这套Makefile:
1.把顶层Makefile, Makefile.build放入程序的顶层目录
   在各自子目录创建一个空白的Makefile

2.确定编译哪些源文件
   修改顶层目录和各自子目录Makefile的obj-y
    obj-y += xxx.o
    obj-y += yyy/

    这表示要编译当前目录下的xxx.c, 要编译当前目录下的yyy子目录    

3. 确定编译选项、链接选项
   修改顶层目录Makefile的CFLAGS,这是编译所有.c文件时都要用的编译选项;
   修改顶层目录Makefile的LDFLAGS,这是链接最后的应用程序时的链接选项;
   
   修改各自子目录下的Makefile:
   "EXTRA_CFLAGS",    它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置
   "CFLAGS_xxx.o",    它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置
   
4. 使用哪个编译器?
   修改顶层目录Makefile的CROSS_COMPILE, 用来指定工具链的前缀(比如arm-linux-)
   
5. 确定应用程序的名字:
   修改顶层目录Makefile的TARGET, 这是用来指定编译出来的程序的名字

6. 执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除

1.顶层目录

 Makefile


CROSS_COMPILE = 
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nm

STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump

export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP

CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include

LDFLAGS := 

export CFLAGS LDFLAGS

TOPDIR := $(shell pwd)
export TOPDIR

TARGET := test


obj-y += main.o
obj-y += sub.o
obj-y += a/


all : start_recursive_build $(TARGET)
	@echo $(TARGET) has been built!

start_recursive_build:
	make -C ./ -f $(TOPDIR)/Makefile.build

$(TARGET) : start_recursive_build
	$(CC) -o $(TARGET) built-in.o $(LDFLAGS)

clean:
	rm -f $(shell find -name "*.o")
	rm -f $(TARGET)

distclean:
	rm -f $(shell find -name "*.o")
	rm -f $(shell find -name "*.d")
	rm -f $(TARGET)
	

Makefile.build

PHONY := __build
__build:


obj-y :=
subdir-y :=
EXTRA_CFLAGS :=

include Makefile

# obj-y := a.o b.o c/ d/
# $(filter %/, $(obj-y))   : c/ d/
# __subdir-y  : c d
# subdir-y    : c d
__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y	+= $(__subdir-y)

# c/built-in.o d/built-in.o
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)

# a.o b.o
cur_objs := $(filter-out %/, $(obj-y))
dep_files := $(foreach f,$(cur_objs),.$(f).d)
dep_files := $(wildcard $(dep_files))

ifneq ($(dep_files),)
  include $(dep_files)
endif


PHONY += $(subdir-y)


__build : $(subdir-y) built-in.o

$(subdir-y):
	make -C $@ -f $(TOPDIR)/Makefile.build

built-in.o : $(subdir-y) $(cur_objs)
	$(LD) -r -o $@ $(cur_objs) $(subdir_objs)

dep_file = .$@.d

%.o : %.c
	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
	
.PHONY : $(PHONY)

2.子目录 


EXTRA_CFLAGS := -D DEBUG
CFLAGS_sub3.o := -D DEBUG_SUB3

obj-y += sub2.o 
obj-y += sub3.o 

 六、通用 Makefile 的解析

通用 Makefile 的设计思想

在 Makefile 文件中确定要编译的文件、目录,比如:

obj-y += main.o
obj-y += a/

“Makefile”文件总是被“Makefile.build”包含的。

在 Makefile.build 中设置编译规则,有 3 条编译规则:

怎么编译子目录? 进入子目录编译:

$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build

怎么编译当前目录中的文件?

%.o : %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<

当前目录下的.o 和子目录下的 built-in.o 要打包起来:

built-in.o : $(cur_objs) $(subdir_objs)
$(LD) -r -o $@ $^

顶层 Makefile 中把顶层目录的 built-in.o 链接成 APP:

$(TARGET) : built-in.o
    $(CC) $(LDFLAGS) -o $(TARGET) built-in.o

情景演绎

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1181002.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通过关键词合并2张excel表格——Python代码实现

问题描述 2个excel表格中都包含了一个共同的关键字【单品编码】&#xff0c;如何将2个表通过该关键字合并为一张表格。使用Python代码完成该任务。 1.数据说明 附件1中的数据excel格式 销售日期,扫码销售时间,单品编码,销量(千克),销售单价(元/千克),销售类型,是否打折销售…

访问控制列表

目录 ACL ACL原理 ACL包过滤方式 ACL通用命令 查看ACL表命令 删除整张表命令 接口配置ACL ACL分类 标准ACL 标准ACL的动作与条件 通配符掩码 扩展ACL 扩展ACL的动作与条件 命名ACL 前言 书写方式 ACL 含义&#xff1a;访问控制列表&#xff0c;其是一种包过滤…

element 弹窗浏览器后退-遮照层还存在问题 以及跟vue keep-alive冲突

问题&#xff1a;element 弹窗浏览器后退-遮照层还存在问题 查询官网可以设置 modal-append-to-body“false” 可以全局设置 ElementUI.Dialog.props.modalAppendToBody.default false 后续 基本到这能解决问题&#xff0c;不过本项目比较特殊&#xff0c;使用了 keep-alive…

你真的了解static吗?

目录 一.static1.概念2.存在形式3.访问形式注意4.static成员变量的初始化4.1就地初始化4.2使用代码块初始化 5.关于main 二.代码块概念1.普通代码块2.构造代码块3.静态代码块注意事项①加载形式②加载顺序 一.static 1.概念 在Java中&#xff0c;被static修饰的成员&#xff0…

Visual Studio 2017附加依赖项

在读韩国人尹圣雨的《TCP/IP网络编程》,在书中教我如何在Visual Studio 2008中设置附加依赖项&#xff0c;但是我使用的是Visual Studio 2017&#xff0c;所以我写下这篇文章学习如何在Visual Studio 2017附加依赖项。 在项目这里选择属性。 选择输入这一项&#xff0c;然后点…

如何通过货架电子标签PTL灯光指引拣选优化仓库管理

物流仓储行业的不断发展和智能化技术的应用&#xff0c;仓库管理的效率和精度要求也日益提高。在这样的背景下&#xff0c;PTL亮灯拣选系统作为一种智能化设备&#xff0c;采用分布式管理结构&#xff0c;为仓库管理带来了新的可能性。 PTL亮灯拣选系统采用智能计算机作为主控设…

低代码工具的常见用例与受众市场

目录 一、低代码工具的常见用例是什么&#xff1f; 1.业务流程管理&#xff08;BPM&#xff09; 2.自定义应用程序开发 3.数据管理和分析 4.移动应用程序开发 二、低代码受众和市场 1.制造商 2.个人开发者/自由职业者 3.代理商 4.小型企业和初创企业 5.中型企业 6.营销团队 7.软…

Voice Control for ChatGPT简单高效的与ChatGPT进行交流学习。

快捷又不失灵活性 日常生活中&#xff0c;我们与亲人朋友沟通交流一般都是喜欢语音的形式来完成的&#xff0c;毕竟相对于文字来说语音就不会显的那么的苍白无力&#xff0c;同时最大的好处就是能解放我们的双手吧&#xff0c;能更快实现两者间的对话&#xff0c;沟通便更高效…

基于Java Web的在线教学质量评价系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

【数据结构】树与二叉树(五):二叉树的顺序存储(初始化,插入结点,获取父节点、左右子节点等)

文章目录 5.1 树的基本概念5.1.1 树的定义5.1.2 森林的定义5.1.3 树的术语5.1.4 树的表示 5.2 二叉树5.2.1 二叉树1. 定义2. 特点3. 性质引理5.1&#xff1a;二叉树中层数为i的结点至多有 2 i 2^i 2i个&#xff0c;其中 i ≥ 0 i \geq 0 i≥0。引理5.2&#xff1a;高度为k的二叉…

免费好用的网页采集工具软件推荐

在众多各具特色的采集器软件中&#xff0c;真正好用的采集器软件有哪些&#xff1f; 自己一个个去查找和尝试无疑会耗费大量的时间和精力。 因此&#xff0c;在深入体验大多数采集器后&#xff0c;给大家推荐几款优秀且好用的免费网页采集器软件。 本文将对这几款采集器进行…

【机器学习】梯度下降预测波士顿房价

文章目录 前言一、数据集介绍二、预测房价代码1.引入库2.数据3.梯度下降 总结 前言 梯度下降算法学习。 一、数据集介绍 波士顿房价数据集&#xff1a;波士顿房价数据集&#xff0c;用于线性回归预测 二、预测房价代码 1.引入库 from sklearn.linear_model import Linear…

如何处理 java.lang.NoClassDefFoundError

1. 问题背景 系统异常提示java.lang.NoClassDefFoundError: ch/qos/logback/classic/spi/ThrowableProxy 2.问题分析 了解NoClassDefFoundError含义 在开始解决这个问题之前&#xff0c;我们需要先了解一下java.lang.NoClassDefFoundError错误的含义。这个错误通常表示在运行…

借助 DevChat AI 之力,成就我之全栈梦想

何为 DevChat &#xff1f; DevChat 是集好多种 AI 大模型的智能编程工具,可以大大增加我们上班摸鱼的时间。 整合了如 ChatGPT、Codex等热门 AI 模型支持自然语言编程、代码生成与编写、代码补全等功能因其集成热门 AI 智能&#xff0c;相当于站在了巨人的肩膀上&#xff0c…

琛蓝健康明星产品ClamBP™蛤蜊肽,调节血压的针对性方案

琛蓝健康明星产品ClamBP™蛤蜊肽&#xff0c;调节血压的针对性方案 高血压是现今社会最紧迫的公共卫生挑战之一&#xff0c;影响着全球约20%的成人人口。据《中国心血管健康与疾病报告2019》显示&#xff0c;中国高血压患者达2.45亿。长期高血压可引起心脏病、中风、肾功能减退…

Kibana Dashboard饼图展示keyword子字符串去重统计

日志内容 log.info("请求开始 uri: {} header RequestId:{}", request.getRequestURI(), reqId, request.getHeader("request_id"));操作步骤 进入Dashboard菜单 点击Create Dashboard按钮 点击Create Panel按钮 选择Aggregation based 然后选择Pie饼图 …

C#在.NET Windows窗体应用中使用LINQtoSQL

目录 一、新建Windows窗体应用并添加LINQtoSQL类 二、错误信息CS0234 三、添加扩展包让Windows窗体应用支持LINQtoSQL类 默认安装的背景下&#xff0c;新建的Windows窗体应用是不支持LINQtoSQL类的。现象是资源管理器里的依赖项中默认的安装不能自动生成支持system.data.lin…

触摸屏通过modbus转profinet网关连接PLC与变频器485modbus通讯案例

通过兴达易控modbus转profinet网关&#xff08;XD-MDPN100&#xff09;的桥接&#xff0c;数据可以以高速、可靠的方式从触摸屏传递到PLC&#xff0c;同时能够实现PLC对变频器的监控和控制。这四台变频器通过485modbus协议与PLC通讯&#xff0c;使得系统能够实现对变频器的高效…

AI系统源码ChatGPT网站源码+ai绘画系统/支持GPT4.0/支持Midjourney局部编辑重绘

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

信号的阻塞与递达

目录 阻塞信号 信号和相关概念 sigset_t 信号集 信号的保存与阻塞 第一个问题 第二个问题 第三个问题 信号的递达 信号递达 内核态与用户态 为什么可以从用户态到内核态 我们怎么知道现在是内核态还是用户态&#xff1f; 信号的处理 sigaction 可重入函数 vola…