百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

大话C语言:大型项目构建基础Makefile

lipiwang 2024-11-27 17:15 11 浏览 0 评论

1 Makefile概述

Makefile是一种用于自动化构建和管理程序的工具,以文本文件的形式存在。它主要记录了程序的编译规则、依赖关系和操作指令,使得在开发过程中能够轻松地进行代码的编译、链接和部署。

Makefile文件中的命令有一定规范,一旦该文件编写好以后在Linux命令行中执行一条make命令即可自动编译整个工程。不同厂家的make可能会稍有不同,并且语法上也有区别,不过基本思想都差不多,主要还是落在目标依赖上,最广泛使用的是GNUmake。

Makefile的主要作用包括:

  • 自动化编译和链接:通过定义编译器、编译选项、源文件、目标文件等信息,Makefile可以自动完成代码的编译和链接过程,从而提高开发效率。
  • 管理依赖关系:Makefile中可以指定各个源文件之间的依赖关系,确保在修改某个文件后,只需要重新编译该文件及其相关的依赖文件。
  • 跳过不必要的编译:Makefile通过检查源文件和目标文件的时间戳,可以避免重新编译未发生变化的文件,从而加快构建速度。


2 make概述

make是一个命令行工具和构建自动化工具,主要用于管理和构建软件项目。它通过Makefile文件来定义构建任务和依赖关系,并根据这些规则自动执行任务以生成最终的目标文件。

Makefile中包含了目标、依赖关系和相应的构建命令。make工具会根据目标和依赖关系来确定构建顺序,并执行构建命令以生成新的目标文件。这使得make能够根据代码的依赖关系自动构建并更新目标文件,从而提高编译效率。

make具有依赖管理功能,可以确保任务的正确构建顺序。它支持任务并行执行,并可以在多种操作系统上运行。make是一个稳定且广泛使用的工具,已经存在多年,适用于多种编程语言和项目类型的构建任务。


3 为什么需要make和Makefile?

之所以需要make和Makefile,主要原因主要可以归结为以下几点:

  • 自动化编译:在软件开发过程中,尤其是大型项目,源文件众多,编译过程复杂。手动编译不仅效率低下,而且容易出错。make和Makefile可以自动化地处理编译过程,根据源文件的依赖关系自动确定编译顺序,并执行相应的编译命令,大大提高了编译效率。
  • 管理依赖关系:在软件项目中,源文件之间往往存在复杂的依赖关系。当一个源文件发生变化时,可能需要重新编译与其相关的其他源文件。make和Makefile可以方便地管理这些依赖关系,确保在需要时只重新编译必要的文件,避免了不必要的重复编译。
  • 抽象构建逻辑:Makefile将构建逻辑抽象化,使得开发者能够专注于代码编写,而不必关心繁琐的构建过程。Makefile中定义了目标、依赖和命令,使得构建过程更加清晰、易于理解和维护。
  • 跨平台兼容性:make工具在不同的操作系统上都可以使用,只需要根据不同的操作系统和编译器编写相应的Makefile即可。这使得软件项目可以在不同的平台上进行构建,提高了项目的可移植性。
  • 可定制性:Makefile具有很高的可定制性,可以根据项目的具体需求进行灵活的配置。开发者可以定义自己的变量、规则和函数,以满足特定的构建需求。
  • 提高开发效率:通过自动化编译和管理依赖关系,make和Makefile可以大大减少开发人员在构建过程中的重复劳动,让他们有更多的时间专注于代码的开发和调试,从而提高开发效率。


4 Makefile语法规则

4.1 模块代码

实现简单计算器功能,核心功能包括:

  • 两位数的加法,功能文件add.h和add.c
  • 两位数的加法,功能文件subtract.h和subtract.c
  • 两位数的除法,功能文件divide.h和divide.c
  • 两位数的乘法,功能文件multiply.h和multiply.c
  • 计算器功能主入口,功能文件calculator.c

核心功能模块:两位数的加法

// add.h文件实现
#ifndef __ADD_H__
#define __ADD_H__

/*
* 功能:实现两个整数相加
* @param num1 : 被加数
* @param num2 : 加数
* @return 两数相加之和
* @note 不考虑溢出问题
*/
int add(int num1, int num2); 

#endif
// add.c文件实现
#include "add.h"
  
int add(int num1, int num2) 
{  
    return num1 + num2;  
}

核心功能模块:两位数的减法

// subtract.h文件实现
#ifndef __SUBTRACT_H__
#define __SUBTRACT_H__

/*
* 功能:实现两个整数相减
* @param num1 : 被减数
* @param num2 : 减数
* @return 两数相减之差
* @note 不考虑溢出问题
*/
int subtract(int num1, int num2);  

#endif
// subtract.c文件实现
#include "subtract.h"  
  
int subtract(int num1, int num2) 
{  
    return num1 - num2;  
}

核心功能模块:两位数的除法

// divide.h文件实现
#ifndef __DIVIDE_H__
#define __DIVIDE_H__

/*
* 功能:实现两个整数相除
* @param num1 : 被除数
* @param num2 : 除数,不能为0
* @return 两数相除
* @note 除数不能为0
*/
double divide(int num1, int num2); 

#endif
// divide.C文件实现
#include "divide.h" 
  
double divide(int num1, int num2) 
{  
    if (b != 0) 
    {  
        return (double)num1 / num2;  
    } 
    else 
    {  
        return 0.0;  
    }  
}

核心功能模块:两位数的乘法

// multiply.h文件实现
#ifndef __MULTIPLY_H__
#define __MULTIPLY_H__

/*
* 功能:实现两个整数相乘
* @param num1 : 被乘数
* @param num2 : 乘数
* @return 两数相乘
* @note 不考虑溢出问题
*/
int multiply(int num1, int num2);  

#endif
// multiply.c文件实现
#include "multiply.h"  
  
int multiply(int num1, int num2) 
{  
    return num1 * num2;  
}

核心功能模块:计算器功能主入口法

// calculator.c文件实现
#include <stdio.h>  
#include "add.h"
#include "subtract.h"  
#include "divide.h" 
#include "multiply.h" 

int main() 
{  
    int num1 = 20
    int num2 = 5;  
    
    printf("两数相加之和: %d\n", add(num1, num2));  
    printf("两数相减之差: %d\n", subtract(num1, num2));  
    printf("两数相乘之积: %d\n", multiply(num1, num2));  
    printf("两数相除之商: %f\n", divide(num1, num2));  
    
    return 0;  
}

4.2 Makefile基础语法规则

Makefile遵循基础规则如下:

target: dependencies  
    command  
    ...

其中,

  • target:表示要生成的目标文件或执行的动作,它通常是文件名或特殊的伪目标(如clean)。
  • dependencies:表示生成目标所依赖的文件列表,多个文件之间使用空格分隔。
  • command:表示生成目标时执行的命令,每个命令占一行,且前面必须有一个Tab字符作为缩进。

4.3 make命令格式

make命令格式如下:

make [选项] [目标]

其中,

  • 选项:是 make 命令的可选参数,用于控制 make 的行为。
  • 目标:是要构建的目标文件或标签名。如果没有指定目标,make 会使用 Makefile 中的第一个目标(通常是 all)

make 命令选项包括:

  • -f FILE 或 --file=FILE:使用指定的 Makefile,而不是默认的 Makefile 或 makefile。
  • -n 或 --just-print:显示将要执行的命令,但并不真正执行它们。
  • -s 或 --silent 或 --quiet:不显示执行的命令。
  • -k 或 --keep-going:在出现错误时继续执行其他命令。
  • -j [N] 或 --jobs[=N]:同时运行命令的个数。如果没有指定 N,则 make 会尝试并行执行尽可能多的任务。
  • --load-average[=LOAD]:当系统负载高于某个值时,不运行新的任务。
  • -C DIR 或 --directory=DIR:在读取 Makefile 之前改变到指定的目录 DIR。

目标 通常是 Makefile 中定义的一个标签或文件名,它可能代表一个要构建的特定目标文件,或者是一组任务的集合。例如,Makefile 中可能定义了 all、clean、install 等目标。

一般的时候,直接使用make就可以。

4.4 多文件Makefile设计

以简单计算器为例,设计一个多文件编译的Makefile,具体内容如下:

calculator:calculator.o add.o subtract.o divide.o multiply.o
	gcc calculator.o add.o subtract.o divide.o multiply.o ‐o calculator

calculator.o:calculator.c
	gcc ‐c calculator.c ‐o calculator.o

add.o:add.c
	gcc ‐c add.c ‐o add.o

subtract.o:subtract.c
	gcc ‐c subtract.c ‐o subtract.o

divide.o:divide.c
	gcc ‐c divide.c ‐o divide.o

multiply.o:multiply.c
	gcc ‐c multiply.c ‐o multiply.o

clean:
	rm *.o calculator ‐rf

注意,运行时使用make clean 就会执行clean后面的命令


5 Makefile变量

Makefile支持变量的使用,类似于C语言中的宏定义。变量可以用于存储文件名、编译选项等,使得Makefile更易于维护。

Makefile中的变量可以根据其来源和用途分为以下几类:

  • 环境变量:执行Makefile时从外部传入Make命令的环境变量。例如,`PWD 表示当前目录的绝对路径。环境变量通常是全局的,并且可以在整个Makefile中引用。
  • 内置变量或默认变量:Makefile中有一些内置的或默认的变量,这些变量在Makefile中有特定的含义和用途。例如,CC 用于指定编译器的类型,CFLAGS 用于指定编译器选项(如调试信息、优化等)。这些变量在Makefile中预定义,并且可以在需要时进行修改。
  • 自动变量:自动变量是Makefile中根据当前的目标和依赖关系自动生成的变量。例如,$@ 表示目标文件的名称(包含扩展名),lt; 表示第一个依赖文件的名称。这些变量在规则中特别有用,因为它们可以根据上下文自动变化。
  • 用户自定义变量:除了上述几类变量外,用户还可以在Makefile中定义自己的变量。这些变量可以根据需要命名和赋值,用于存储文件名、目录路径、编译器选项等常用的定义,并可以动态地用于自动构建步骤的指令中。

5.1 用户自定义变量

用户自定义变量规则:

# 定义变量:
变量名=变量值

# 变量引用或者使用
$(变量名)或${变量名}

注意:

  • 变量是大小写敏感的
  • 变量一般都在makefile的头部定义
  • 变量几乎可在makefile的任何地方使用

5.2 内置变量

makefile中有许多预定义变量,这些变量具有特殊的含义,可在makefile中直接使用。

变量名

含义

$@

当前规则中的目标文件名

lt;

当前规则中的第一个依赖文件名

$^

当前规则中的所有依赖文件名列表

AR

归档维护程序的程序名,默认值为ar

ARFLAGS

归档维护程序的选项

AS

汇编程序的名称,默认值为as

ASFLAGS

汇编程序的选项

CC

C编译器的名称,默认值为cc

CFLAGS

C编译器的选项

CPP

C预编译器的名称,默认值为$(CC) -E

CPPFLAGS

C预编译的选项

CXX

C++编译器的名称,默认值为g++

CXXFLAGS

C++编译器的选项

5.3 优化多文件Makefile

基于Makefile变量规则,进一步优化设计多文件Makefile,优化后的Makefile如下:

# 编译器选项
CC=gcc
CFLAGS=‐Wall ‐g

#目标文件选项
obj=calculator
obj1=add
obj2=subtract
obj3=divide
obj4=multiply
OBJ=calculator.o add.o subtract.o divide.o multiply.o

# 构建目标程序
$(obj):$(OBJ)
	$(CC) $^ ‐o $@

# 编译目标文件
$(obj).o:$(obj).c
	$(CC) $(CFLAGS) ‐c lt; ‐o $@

$(obj1).o:$(obj1).c
	$(CC) $(CFLAGS) ‐c lt; ‐o $@

$(obj2).o:$(obj2).c
	$(CC) $(CFLAGS) ‐c lt; ‐o $@

$(obj3).o:$(obj3).c
	$(CC) $(CFLAGS) ‐c lt; ‐o $@
	
$(obj4).o:$(obj4).c
	$(CC) $(CFLAGS) ‐c lt; ‐o $@	

# 中间文件清理
clean:
	rm $(OBJ) $(obj) ‐rf

Makefile支持使用通配符来匹配文件名,常用的通配符有:%:代表任意数量的任意字符,可以用于匹配文件名中的一部分。例如,%.o 表示所有以 .o 结尾的文件。

进一步优化Makefile文件:

# 编译器选项
CC=gcc
CFLAGS=‐Wall ‐g

#目标文件选项
obj=calculator
OBJ=calculator.o add.o subtract.o divide.o multiply.o


$(obj):$(OBJ)
	$(CC) $^ ‐o $@

%.o:%.c
	$(CC) $(CFLAGS) ‐c lt; ‐o $@

# 中间文件清理
clean:
rm $(OBJ) $(obj) ‐rf



---E N D---

喜欢的记得关注哦!

您的支持是我们前进的动力!

职创未来|专注IT与新能源领域中高端人才培养

相关推荐

Nat. Synthesis: 重大突破,电化学形成C-S键

第一作者:JunnanLi,HasanAl-Mahayni通讯作者:AliSeifitokaldani,NikolayKornienko通讯单位:蒙特利尔大学,麦吉尔大学【研究亮点】形成C-...

网络安全与应用(二)(网络安全理论与应用)

1、应用层安全协议SHTTP和HTTPS:SHTTP:SecHTTP,安全超文本传输协议,是HTTP扩展,使用TCP的80端口。HTTPS:HTTP+SSL,使用TCP的443端口。大部分web应用...

TN-C、TN-S、TT、IT供电系统详解及对比

TN-C、TN-S、TT、IT供电系统是低压配电系统中常见的四种接地方式,它们各自有不同的特点和适用场景。一、系统介绍TN-C供电系统①定义:整个系统中,工作零线(N线)与保护零线(PE线)是合一的,...

网络应用服务器(三)(网络应用程序服务器)

#头条创作挑战赛#1、DNS协议:域名解析协议,用于把主机域名解析为对应的IP地址。是一个分布式数据库,C/S工作方式。主要基于UDP协议,少数使用TCP,端口号都是53。常用域名如下2、DNS协议...

腾讯发布混元Turbo S:业界首次无损应用Mamba架构

21世纪经济报道记者白杨北京报道2月27日,腾讯正式发布新一代基座模型——混元TurboS。据腾讯混元团队介绍,混元TurboS在架构方面创新性地采用了Hybrid-Mamba-Transfor...

【收藏】低压配电系统中TT IT TN-S/TN-C/TN-C-S 的区别?

低压配电系统的接地型式选择是电气安全设计的核心环节,TT、IT、TN-S、TN-C、TN-C-S这五种主要接地型式因其结构、保护原理和故障特性的显著差异,在工程应用中有不同的适用范围和限制条件。如若发...

金万维公有云平台如何实现C/S架构软件快速SaaS化

金万维作为国内领先的企业信息化垂直B2B平台运营商,拥有超过5000家管理软件合作伙伴,掌握管理软件一线的发展动态,因此深知传统管理软件近年来面对的困境和问题。而SaaS却在软件行业内发展迅猛势如燎原...

随时随地做翻译:B/S架构的传奇时代到来

随着新兴技术的发展和大数据时代的到来,翻译作为连接各国语言和文化的工具,更是具有前所未有的拓展空间。传统的在计算机辅助翻译软件(CAT)上进行翻译的模式,受到时间和空间的限制,导致翻译过程中面临层层障...

BS和CS 架构的介绍(一篇就够了)(cs和bs架构的含义)

简介C/S又称Client/Server或客户/服务器模式。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如Oracle、Sybase、Informix或SQLServer。...

物管王(包租婆)软件架构与B/S和C/S架构的优点和缺点比较

一、B/S系统架构的优点和缺点优点:1)客户端无需安装,有Web浏览器即可。2)BS架构可以直接放在广域网上,通过一定的权限控制实现多客户访问的目的,交互性较强。3)BS架构无需升级多个客户端,升级服...

监听器入门看这篇就够了(怎么检查车上有没有被别人安装监听器)

什么是监听器监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。。为什么我们要使用监听器?...

购物车【JavaWeb项目、简单版】(java购物车的实现原理)

①构建开发环境免费学习资料获取方式导入需要用到的开发包建立程序开发包②设计实体书籍实体publicclassBook{privateStringid;privat...

基础篇-SpringBoot监听器Listener的使用

1.监听器Listener简介1.1监听器Listener介绍Listener是JavaWeb的三大组件(Servlet、Filter、Listener)之一,JavaWeb中的监听器主要用...

你在 Spring Boot3 整合 JWT 实现 RESTful 接口鉴权时是否遇到难题?

各位后端开发小伙伴们!在日常使用SpringBoot3搭建项目时,RESTful接口的鉴权至关重要。而JWT技术,作为一种简洁且高效的鉴权方式,被广泛应用。但大家是不是在整合过程中遇到过各...

javaWeb RSA加密使用(rsa加密java代码)

加密算法在各个网站运用很平常,今天整理代码的时候看到了我们项目中运用了RSA加密,就了解了一下。先简单说一下RSA加密算法原理,RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要...

取消回复欢迎 发表评论: