Makefile 教程(一)——基础

本文裁剪汇总自Makefile教程和示例指南。

Makefile 意义

Makefile 用于帮助决定大型程序的哪些部分需要重新编译。在绝大多数情况下,都会编译 C 或 C++ 文件。其他语言通常有自己的与 Make 功能类似的工具。Make 也可以在编译之外使用,即当需要根据已更改的文件运行一系列指令时。本教程将重点介绍 C/C++ 编译用例。

下面是可以使用 Make 构建的依赖关系图示例。如果任何文件的依赖项发生更改,则该文件将被重新编译:

one.cpp
one.h
main.cpp
two.cpp
two.h
libm
libc

Make 的替代

流行的 C/C++ 替代构建系统有 SCons、CMake、Bazel 和 Ninja。一些代码编辑器(例如 Microsoft Visual Studio)有自己的内置构建工具。对于 Java,有 Ant、Maven 和 Gradle。其他语言(例如 Go、Rust 和 TypeScript)都有自己的构建工具。

Python、Ruby 和 raw Javascript 等解释性语言不需要与 Makefile 类似的东西。 Makefile 的目标是根据已更改的文件来编译需要编译的任何文件。但是,当解释语言中的文件发生更改时,不需要重新编译任何内容。程序运行时,将使用该文件的最新版本。

Make 的版本和类型

Make 有多种实现,但本指南的大部分内容都适用于任一版本。然而,它是专门为 GNU Make 编写的,GNU Make 是 Linux 和 MacOS 上的标准实现。所有示例都适用于 Make 版本 3 和 4,除了一些深奥的差异之外,它们几乎相同。

Make 基本运行

运行首先需要一个终端,并安装 make。对每一个项目,需包含在一个名为Makefile的文件(无后缀)中,并在同级目录下运行命令make

最简单的 Makefile:

hello:echo "Hello, World"

Makefile 必须使用 TAB 键进行缩进,其中不能有空格。

运行如下:

$ make
echo "Hello, World"
Hello, World

Makefile 语法

Makefile 的主要部分由规则组成,可有多条规则。

一条规则的基本语法:

targets: prerequisitescommandcommand...
  • 目标(target),每条规则可有多个,以空格分隔,但一般只有一个,且为文件名(make将其视为文件名);
  • 命令(command),一系列步骤,一般用于创建目标文件,开始的缩进必须是tab而非空格;
  • 先决条件(prerequisite),又称依赖(dependency),为文件名,可有多个,以空格分隔,在运行命令前需要存在的文件。在执行规则时,prerequisites中的每个prerequisite均需满足以下至少一点:
    • 当前目录下存在同名文件;
    • Makefile中存在以此名为target的规则。

Makefile 精髓

  • 运行时,在终端输入make <target>,然后make会执行该target对应的规则(如果同一目标有多条规则,则会警告,并执行最后一条规则);若在终端输入make,则默认执行第一条规则;

  • 当在终端输入make [<target>]时,make对该target对应规则中的每一个prerequisite进行判断:Makefile中是否存在以其为名的target,若有,则先对对应规则进行递归执行;

    如:

    test1: test2echo "test1" > test1
    test2: echo "test2" > test2 
    

    在该例子中,test1的内容其实与test2无关,所以这个依赖关系只是对make而言的。

    当执行make test1make时,make实质上会先执行make test2

  • 执行规则时,make会先判断是否要执行命令,需满足以下条件的至少一点才会执行:

    1. target为名的文件不存在;
    2. 存在比target更新的prerequisite;(prerequisitetarget更新,即prerequisite文件内容的修改时间晚于target文件make会记录并更新该目录下每个文件被更新的时间戳)

    根据第一点,可以构造一种特殊规则,该规则不会创建以target为名的文件,因而一般情况下在输入make <target>时总是会被执行。常见的有cleanall等。(显然,若该目录下手动创建了对应名称的文件,则该构造就失效了,对应解决方案见后文)

    例如:若Makefile内容如下:

    test1: test2echo "test1"
    test2: test3echo "test2"
    
    1. 若开始时目录下只有该Makefile文件,然后手动依次创建test3test2test1文件(创建文件也是一种手动更新),再执行make,则会显示:

      $ make
      make: 'test1' is up to date.
      

      即没有规则被执行。

      分析如下:

      1. 执行make时,由于未指定target,故默认选择第一个规则,即执行make test1

      2. 执行make test1时,先递归执行其prerequisite,即先执行make test2

      3. make test2,进行判断,发现:

        1. 该目录下存在test2文件;
        2. 其先决条件test3的更新时间早于test2(这是因为创建时间test3 早于test2);

        故不执行对应规则;

      4. 递归回到make test1,进行判断,发现:

        1. 该目录下存在test1文件;
        2. 其先决条件test2的更新时间早于test1(这是因为创建时间test2 早于test1);

        故不执行对应规则;

    2. 换一种情况,若开始时目录下只有该Makefile文件,然后手动依次创建test2test3test1文件(即调换test2test3的创建顺序),再执行make,则会显示:

      $ make
      echo "test2"
      test2
      

      test2对应规则被执行了,test1对应规则未被执行。

make clean

clean通常用作删除其他目标的输出的目标,但它在make中并不是一个专有的词。

注:

  • 一般而言,clean不是第一个目标 (默认目标),也不是先决条件。 这意味着除非显式调用make clean,否则它永远不会运行;
  • 如“Makefile 精髓”中所述,clean不是一个文件名。 如果该目录下有一个名为 “clean” 的文件,则此目标将无法运行。 解决方案参阅后续教程。

例:

some_file: touch some_fileclean:rm -f some_file

变量

变量只能是字符串,一般通过:=赋值(使用= 也可以,具体见后续部分)。

使用$()${}引用变量(单字母名称变量也可以不用括号,但不推荐)。

例:

files := file1 file2
some_file: $(files)echo "Look at this variable: " $(files)touch some_filefile1:touch file1
file2:touch file2clean:rm -f file1 file2 some_file

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

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

相关文章

绘制决策树尝试3

目录 代码解读AI 随机状态 种子 定义决策树回归模型 tree的decision regressor fit 还可用来预测 export 效果图 我的X只有一个特征 为何这么多分支 &#xff1f;&#xff1f;&#xff1f; 这是CART回归 CART回归 为什么说代码是CART回归&#xff1f; 不是所有的决…

为大模型提供webui界面的利器:Open WebUI 完全本地离线部署deepseek r1

为大模型提供webui界面的利器&#xff1a;Open WebUI Open WebUI的官网&#xff1a;&#x1f3e1; Home | Open WebUI 开源代码&#xff1a;WeTab 新标签页 Open WebUI是一个可扩展、功能丰富、用户友好的自托管AI平台&#xff0c;旨在完全离线运行。它支持各种LLM运行程序&am…

langchain 实现多智能体多轮对话

这里写目录标题 工具定义模型选择graph节点函数定义graph 运行 工具定义 import random from typing import Annotated, Literalfrom langchain_core.tools import tool from langchain_core.tools.base import InjectedToolCallId from langgraph.prebuilt import InjectedSt…

【Block总结】CPCA,通道优先卷积注意力|即插即用

论文信息 标题: Channel Prior Convolutional Attention for Medical Image Segmentation 论文链接: arxiv.org 代码链接: GitHub 创新点 本文提出了一种新的通道优先卷积注意力&#xff08;CPCA&#xff09;机制&#xff0c;旨在解决医学图像分割中存在的低对比度和显著…

Python从零构建macOS状态栏应用(仿ollama)并集成AI同款流式聊天 API 服务(含打包为独立应用)

在本教程中,我们将一步步构建一个 macOS 状态栏应用程序,并集成一个 Flask 服务器,提供流式响应的 API 服务。 如果你手中正好持有一台 MacBook Pro,又怀揣着搭建 AI 聊天服务的想法,却不知从何处迈出第一步,那么这篇文章绝对是你的及时雨。 最终,我们将实现以下功能: …

强化学习、深度学习、深度强化学习的区别是什么?

前言 深度强化学习就是 深度学习 和 强化学习 的结合体。它让计算机程序&#xff08;也就是智能体&#xff09;在特定环境中不断尝试&#xff0c;从错误中学习&#xff0c;最终找到最优的行动策略。 深度学习是AlphaGo从棋谱里学习&#xff0c;强化学些Alphazero 学习规则&am…

string类(详解)

为什么学习string类&#xff1f; 1.1 C语言中的字符串 C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP的思想&#xf…

【Redis】set 和 zset 类型的介绍和常用命令

1. set 1.1 介绍 set 类型和 list 不同的是&#xff0c;存储的元素是无序的&#xff0c;并且元素不允许重复&#xff0c;Redis 除了支持集合内的增删查改操作&#xff0c;还支持多个集合取交集&#xff0c;并集&#xff0c;差集 1.2 常用命令 命令 介绍 时间复杂度 sadd …

一些计算机零碎知识随写(25年2月)

今天复习 MySQL 的时候&#xff0c;我突然冒出一个想法&#xff1a;能不能远程连接 MySQL 呢&#xff1f;虽说心里清楚理论上可行&#xff0c;但一直没实际操作过。 于是&#xff0c;起床后我立马打开服务器&#xff0c;准备启动 MySQL。结果&#xff0c;这一启动就发现问题了&…

ESP32-c3实现获取土壤湿度(ADC模拟量)

1硬件实物图 2引脚定义 3使用说明 4实例代码 // 定义土壤湿度传感器连接的模拟输入引脚 const int soilMoisturePin 2; // 假设连接到GPIO2void setup() {// 初始化串口通信Serial.begin(115200); }void loop() {// 读取土壤湿度传感器的模拟值int sensorValue analogRead…

Java 大视界 -- Java 大数据在量子通信安全中的应用探索(69)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

86.(2)攻防世界 WEB PHP2

之前做过&#xff0c;回顾一遍&#xff0c;详解见下面这篇博客 29.攻防世界PHP2-CSDN博客 既然是代码审计题目&#xff0c;打开后又不显示代码&#xff0c;肯定在文件里 <?php // 首先检查通过 GET 请求传递的名为 "id" 的参数值是否严格等于字符串 "admi…

从理论到实践:Linux 进程替换与 exec 系列函数

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; 在Linux中&#xff0c;进程替换&#xff08;Process Substitution&#xff09;是一个非常强大的特性&#xff0c;它允许将一个进程的输出直接当作一个文件来处理。这种技术通常用于Shell脚本和命令行操作中…

【数据结构】初识链表

顺序表的优缺点 缺点&#xff1a; 中间/头部的插入删除&#xff0c;时间复杂度效率较低&#xff0c;为O(N) 空间不够的时候需要扩容。 如果是异地扩容&#xff0c;增容需要申请新空间&#xff0c;拷贝数据&#xff0c;释放旧空间&#xff0c;会有不小的消耗。 扩容可能会存在…

增删改查(CRUD)操作

文章目录 MySQL系列&#xff1a;1.CRUD简介2.Create(创建)2.1单行数据全列插入2.2 单行数据指定插入2.3 多⾏数据指定列插⼊ 3.Retrieve(读取)3.1 Select查询3.1.1 全列查询3.1.2 指定列查询3.1.3 查询字段为表达式&#xff08;都是临时表不会对原有表数据产生影响&#xff09;…

使用Pygame制作“贪吃蛇”游戏

贪吃蛇 是一款经典的休闲小游戏&#xff1a;玩家通过操控一条会不断变长的“蛇”在屏幕中移动&#xff0c;去吃随机出现的食物&#xff0c;同时要避免撞到墙壁或自己身体的其他部分。由于其逻辑相对简单&#xff0c;但可玩性和扩展性都不错&#xff0c;非常适合作为新手练习游戏…

JavaScript闭包深入剖析:性能剖析与优化技巧

一、引言 在 JavaScript 的奇妙世界里&#xff0c;闭包无疑是一个既强大又迷人的特性。它就像是一把万能钥匙&#xff0c;为开发者打开了实现各种高级功能的大门。从数据封装与保护&#xff0c;到函数的记忆化&#xff0c;再到模块化开发&#xff0c;闭包都发挥着举足轻重的作…

蓝桥杯嵌入式赛道备考1 —— 基础GPIO实战

1. 点亮一个LED 蓝桥杯的板子资料的URL&#xff0c;笔者是从GitHub - JoyRiderJie/LanQiaoBei-QianRuShi拉去下来的。这个是Github仓库地址。 从应用层去玩一个开发板子&#xff0c;首先需要的是去尝试是点亮一个LED。让我们切换到手册《CT117E——产品手册》的第11页&#x…

浅析DNS污染及防范

DNS污染&#xff08;DNS Cache Poisoning&#xff09;是一种网络攻击手段&#xff0c;通过篡改DNS服务器的缓存数据&#xff0c;将域名解析结果指向错误的IP地址&#xff0c;从而误导用户访问恶意网站或无法访问目标网站。这种攻击利用了DNS协议的特性&#xff0c;例如“只认第…

AI编程:如何编写提示词

这是小卷对AI编程工具学习的第2篇文章&#xff0c;今天讲讲如何编写AI编程的提示词&#xff0c;并结合实际功能需求案例来进行开发 1.编写提示词的技巧 好的提示词应该是&#xff1a;目标清晰明确&#xff0c;具有针对性&#xff0c;能引导模型理解问题 下面是两条提示词的对…