首页 > 其他分享 >Makefile快速入门教程

Makefile快速入门教程

时间:2025-01-18 21:32:08浏览次数:3  
标签:CC make 入门教程 Makefile cpp main 快速 hello

Makefile 快速入门教程

本教程旨在帮助读者全面深入地掌握 Makefile 的编写与使用。Makefile 是一种用于自动化编译和构建程序的文件,通过定义一系列规则和命令,能够高效地管理项目的编译过程,尤其在大型项目中,其优势尤为明显。本教程从基础概念入手,逐步深入到高级特性,涵盖了 Makefile 的各个方面,包括宏的定义与使用、特殊宏、目标规则、隐式规则、自定义后缀、指令、递归使用等,旨在使读者能够灵活运用 Makefile,提高编程效率和项目管理能力。

一、Makefile 基础

宏的定义和使用

宏是 Makefile 中的变量,使用 = 号赋值。

CC = gcc
CFLAGS = -std=c99 -fexec-charset=GBK -finput-charset=UTF-8

定义了 CCgccCFLAGS-std=c99 -fexec-charset=GBK -finput-charset=UTF-8。使用宏时,在宏名称前后加 $()${},例如:

all: main
main: main.o hello.o
    $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
main.o: main.c
    $(CC) $(CFLAGS) -c $<
hello.o: hello.c
    $(CC) $(CFLAGS) -c $<
clean:
    rm -f *.o

这里 $(CC)$(CFLAGS) 会被替换为相应的变量值。

特殊宏

  • $@:目标文件的名称。
  • $<:第一个依赖文件的名称。
  • $^:所有依赖文件的名称。

示例:

main: main.o hello.o
    $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

此例中 $@main$^main.o hello.o

二、完整 Makefile 示例

CC = gcc
CFLAGS = -std=c99 -fexec-charset=GBK -finput-charset=UTF-8
LDFLAGS = -lm
all: main
main: main.o hello.o
    $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
main.o: main.c
    $(CC) $(CFLAGS) -c $<
hello.o: hello.c
    $(CC) $(CFLAGS) -c $<
clean:
    rm -f *.o

该 Makefile 定义了编译器、编译标志和链接标志,并利用宏来编译和链接程序。在当前目录下使用 makemake all 编译项目,使用 make clean 清理项目。make 默认寻找当前目录下的 makefile 文件并执行 all 部分,执行 make cleanmake main.o 等会按相应规则执行。

三、常规宏

有各种默认宏,可通过 make -p 查看默认值。预定义变量(隐式规则中使用的宏)分为两类:

  • 作为程序名称的宏(例如 CC)。
  • 包含程序参数的宏(例如 CFLAGS)。
序号变量和描述
1AR 档案维护计划;默认为“ar”。
2AS 编译汇编文件的程序;默认为“as”。
3CC 编译 C 程序的程序;默认为“cc”。
4CO 从 RCS 签出文件的程序;默认为“co”。
5CXX 编译 C++ 程序的程序;默认为“g++”。
6CPP 运行 C 预处理器的程序,并将结果输出到标准输出;默认是 $(CC) -E
7FC 编译或预处理 Fortran 和 Ratfor 程序的程序;默认为“f77”。
8GET 从 SCCS 中提取文件的程序;默认为“获取”。
9LEX 用于将 Lex 语法转换为源代码的程序;默认为“lex”。
10YACC 用于将 Yacc 语法转换为源代码的程序;默认为“yacc”。
11LINT 用于在源代码上运行 lint 的程序;默认为“lint”。
12M2C 用于编译 Modula-2 源代码的程序;默认为“m2c”。
13PC 用于编译 Pascal 程序的程序;默认为“电脑”。
14MAKEINFO 将 Texinfo 源文件转换为 Info 文件的程序;默认为“makeinfo”。
15TEX 从 TeX 源代码制作 TeX dvi 文件的程序;默认为“tex”。
16TEXI2DVI 从 Texinfo 源制作 TeX dvi 文件的程序;默认为“texi2dvi”。
17WEAVE 将 Web 翻译成 TeX 的程序;默认为“编织”。
18CWEAVE 将 C Web 翻译成 TeX 的程序;默认为“cweave”。
19TANGLE 将 Web 翻译成 Pascal 的程序;默认为“缠结”。
20CTANGLE 将 C Web 翻译成 C 的程序;默认为“矩形”。
21RM 删除文件的命令;默认为“rm -f”。

这是程序名称的变量表,还有一个包含程序附加参数的变量表:

序号变量和描述
1ARFLAGS 提供存档维护程序的标志;默认为“rv”。
2ASFLAGS.s.S 文件上显式调用时提供给汇编器的额外标志。
3CFLAGS 提供给 C 编译器的额外标志。
4CXXFLAGS 提供给 C 编译器的额外标志。
5COFLAGS 提供给 RCS co 程序的额外标志。
6CPPFLAGS 提供给 C 预处理器和使用它的程序(例如 C 和 Fortran 编译器)的额外标志。
7FFLAGS 提供给 Fortran 编译器的额外标志。
8GFLAGS 提供给 SCCS 获取程序的额外标志。
9LDFLAGS 当编译器应该调用链接器时提供额外的标志,‘ld’。
10LFLAGS 给 Lex 的额外标志。
11YFLAGS 给 Yacc 的额外标志。
12PFLAGS 提供给 Pascal 编译器的额外标志。
13RFLAGS 为 Ratfor 程序提供给 Fortran 编译器的额外标志。
14LINTFLAGS 给予 lint 的额外标志。

可以在命令行定义宏,例如:make CPP = /home/courses/cop4530/spring02

四、Makefile - 依赖

简述

最终的二进制文件依赖于各种源代码和源头文件。例如:

hello: main.o factorial.o hello.o
    $(CC) main.o factorial.o hello.o -o hello

这里 hello 依赖于 main.ofactorial.ohello.o,当这些文件有变化时,make 会采取行动。同时需要定义如何准备 .o 文件:

main.o: main.cpp functions.h
    $(CC) -c main.cpp
factorial.o: factorial.cpp functions.h
    $(CC) -c factorial.cpp
hello.o: hello.cpp functions.h
    $(CC) -c hello.cpp

五、Makefile - 规则

简述

Makefile 目标规则的一般语法是:

target [target...] : [dependent....]
    [ command...]

每个命令前面的制表符是必需的。示例:

hello: main.o factorial.o hello.o
    $(CC) main.o factorial.o hello.o -o hello

当执行 make target 时,make 找到适用的目标规则,如果依赖项比目标新,make 会执行命令(宏替换后),如果依赖项需要建立,会先建立依赖项(递归)。make 会终止如果命令返回失败状态。以破折号开头的命令行上的状态会被 make 忽略,例如:

clean:
    -rm *.o *~ core paper

make 在宏替换后会回显命令,可使用 @ 关闭回显,例如:

install:
    @echo You must be root to install

常见目标有 all(或 make)、installclean

  • make all:编译所有内容,方便本地测试。
  • make install:将应用程序安装到正确位置。
  • make clean:清理应用程序,删除可执行文件、临时文件、目标文件等。

Makefile 隐式规则

从源代码 x.cpp 构建可执行 x 的隐含规则:

.cpp:
    $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@

此规则说明如何从 x.cpp 生成 x,是隐含规则,可在所有情况下使用。从 .cpp 构建 .o 文件的常见隐含规则:

.cpp.o:
    $(CC) $(CFLAGS) -c

.cpp.o:
    $(CC) $(CFLAGS) -c $*.cpp

六、Makefile - 自定义后缀

简述

make 可自动创建 .o 文件,利用此优势可缩短 Makefile。例如:

OBJECTS = main.o hello.o factorial.o
hello: $(OBJECTS)
    cc $(OBJECTS) -o hello
hello.o: functions.h
main.o: functions.h
factorial.o: functions.h

make 使用 .SUFFIXES 特殊目标来定义自己的后缀,例如:

.SUFFIXES:.foo.bar

可定义自己的规则,例如:

.foo.bar:
    tr '[A-Z][a-z]' '[N-Z][A-M][n-z][a-m]' <
< $@
.c.o:
    $(CC) $(CFLAGS) -c

第一条规则允许从 .foo 文件创建 .bar 文件,第二条是默认的从 .c 文件创建 .o 文件的规则。

七、Makefile - 指令

简述

make 程序上的指令有多种形式,不同 make 程序支持的指令可能不同,这里主要介绍 GNU make 支持的指令。

条件指令

  • ifeq:开始条件,包含两个参数,用逗号分隔并用括号括起来,对参数进行变量替换并比较,若匹配则遵循后续行,否则忽略。
  • ifneq:开始条件,与 ifeq 相反。
  • ifdef:开始条件,包含单个参数,若参数为真则条件为真。
  • ifndef:开始条件,包含单个参数,若参数为假则条件为真。
  • else:若前一个条件失败,遵循后续行,在条件中可选。
  • endif:结束条件,每个条件必须以 endif 结尾。

语法:

conditional-directive
    text-if-true
endif

conditional-directive
    text-if-true
else
    text-if-false
endif

示例:

libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
    $(CC) -o foo $(objects) $(libs_for_gcc)
else
    $(CC) -o foo $(objects) $(normal_libs)
endif

包含指令

include directive 允许 make 暂停读取当前 makefile 并读取其他 makefile,是 makefile 中的一行:

include filenames...

文件名可包含 shell 文件名模式,行首可含空格但不能有制表符。例如:

include foo *.mk $(bar)

等同于:

include foo a.mk b.mk c.mk bish bash

make 处理该指令时暂停读取当前 makefile,依次读取列出的文件,读完后继续。

覆盖指令

若使用命令参数设置了变量,会忽略 makefile 中的普通赋值,若想在 makefile 中设置变量,即使它被命令参数设置过,可使用覆盖指令:

override variable = value
or
override variable := value

八、Makefile - 重新编译

简述

make 是智能程序,根据源文件更改工作。例如有 main.cpphello.cppfactorial.cppfunctions.hfunctions.h 被其他文件依赖,main.cpp 依赖 hello.cppfactorial.cpp,对 functions.h 更改会重新编译所有源文件,对 main.cpp 更改只会重新编译 main.cppmake 编译文件时检查目标文件时间戳,若源文件时间戳比目标文件新,会生成新目标文件。

避免重新编译

对于大型项目,可能不想因头文件更改重新编译依赖文件。可使用 -t 标志,告诉 make 不运行规则中的命令,而是通过更改最后修改日期将目标标记为最新:

  • 使用 make 重新编译需重新编译的源文件。
  • 在头文件中更改。
  • 使用 make -t 将目标文件标记为最新。

若头文件已改且部分文件需重新编译,可使用 -o file 标志将指定文件标记为“旧”:

  • 使用 make -o header file 重新编译需要编译的源文件(多个头文件时对每个使用 -o 选项)。
  • 使用 make -t 更新目标文件。

九、Makefile - 其他功能

Make 的递归使用

递归使用 make 是在 makefile 中使用 make 作为命令,适用于为子系统生成文件。例如:

subsystem:
    cd subdir && $(MAKE)
or, equivalently:
subsystem:
    $(MAKE) -C subdir

了解其工作原理和子 make 与顶级 make 的关系。

将变量传递给子制作

顶层 make 的变量值可通过环境传递给子 make,在子 make 中作为默认值。使用 -e 开关可覆盖子 makefile 中指定的内容。make 将变量及其值添加到运行命令的环境中,子 make 用环境初始化变量值表。特殊变量 SHELL 和 MAKEFLAGS 始终被导出,除非取消导出,MAKEFILES 被设置时会被导出。导出变量使用:

export variable...

防止变量导出使用:

unexport variable...

变量 MAKEFILES

若定义了环境变量 MAKEFILES,make 将其值视为要在其他文件前读取的附加 makefile 名称列表,类似 include 指令,在不同目录搜索。主要用于递归调用 make 之间的通信。

包括来自不同目录的头文件

若头文件在不同目录且在不同目录运行 make,使用 -I 选项提供头文件路径。例如:

INCLUDES = -I "/home/jc2182/header"
CC = gcc
LIBS =  -lm
CFLAGS = -g -Wall
OBJ =  main.o factorial.o hello.o
hello: ${OBJ}
    ${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}
.cpp.o:
    ${CC} ${CFLAGS} ${INCLUDES} -c

将更多文本附加到变量

使用 += 可向已定义变量添加更多文本,例如:

objects += another.o

等同于:

objects = main.o hello.o factorial.o
objects := $(objects) another.o

Makefile 中的续行

可使用反斜杠 \ 换行,例如:

OBJ =  main.o factorial.o \
    hello.o

等同于:

OBJ =  main.o factorial.o hello.o

从命令提示符运行 Makefile

若有 Makefile,直接使用 make 运行,若 Makefile 是其他名称,使用:

make -f your-makefile-name

十、Makefile - 示例

简述

一个编译 hello 程序的 Makefile 示例,程序由 main.cppfactorial.cpphello.cpp 组成。

# Define required macros here
SHELL = /bin/sh
OBJS =  main.o factorial.o hello.o
CFLAG = -Wall -g
CC = gcc
INCLUDE =
LIBS = -lm
hello:${OBJ}
    ${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}
clean:
    -rm -f *.o core *.core
.cpp.o:
    ${CC} ${CFLAGS} ${INCLUDES} -c

可使用 make 构建 hello 程序,使用 make clean 删除目标文件和核心文件。

标签:CC,make,入门教程,Makefile,cpp,main,快速,hello
From: https://blog.csdn.net/weixin_74256690/article/details/145227979

相关文章

  • 快速数论变换总结
    前置根据快速傅里叶变换,可以在\(\Theta(n\logn)\)的时间计算卷积。但是由于用到了复数及三角函数,具有精度误差,且不方便取模。于是考虑快速傅里叶变换在数论上的实现,避免了精度误差,支持了取模运算。引入概念原根:阶定义由欧拉定理可知,对\(a\in\mathbf{Z},m\in\mathbf{N}^......
  • 快速部署WSL(Windows Subsystem for Linux)
    概述WindowsSubsystemforLinux(WSL)是微软为Windows10及更高版本推出的一项功能,允许用户在Windows上运行Linux二进制可执行文件。WSL提供了一个完全兼容的Linux内核接口,使用户能够在不使用虚拟机或双启动的情况下运行Linux环境。本文将详细介绍如何快速部署WSL,包括安装、配置和......
  • PowerShell 脚本调整鼠标指针的速度,虽然这不会直接影响鼠标的 DPI(硬件设置)。这个方法
    在PowerShell中,直接通过系统设置控制鼠标DPI或鼠标速度并不是一个简单的操作,因为这些设置通常依赖于硬件和驱动程序。大部分操作系统(包括Windows)本身并不提供简单的接口来直接控制DPI设置。通常,这些设置通过鼠标驱动程序或专门的鼠标软件来管理(例如:Logitech、Razer、Corsai......
  • SpringCloudAlibaba:从0搭建一套快速开发框架-06 告别重复代码,使用Freemarker轻松生成
    序言:上篇主要优化完善公共模块,本篇主要创建一个生成代码的独立模块,提升开发效率,避免繁琐的重复的crud操作。由于内容较多,我就分两节写了。本节我们主要以创建项目并简单的生成数据库实体类即可,下节我们会直接搞完。Freemarker是什么Freemarker是一个基于Java的模板引擎......
  • 集成AI离线免费,全平台毫秒级快速处理!
    随着PS技术的发展,大家对图像的要求和处理更加的多样化,其中,抠图作为一种常见的图像处理操作,并不是每个小伙伴都完全掌握PS技能,对于那些复杂的抠图操作往往会显得捉襟见肘,近两年随着AI技术的进步,各类软件都和AI集合,希望通过AI快速、高效的实现某些操作;分享一款免费、离线并且......
  • 数学知识(一)--质数、约数、欧拉函数、快速幂、扩展欧几里得、高斯消元
    目录一、质数1.试除法判断质数2.试除法分解质因数3.筛选法找质数(1)朴素筛法--埃氏筛(2)线性筛法二、约数 1.试除法求约数2.约数个数问题 3.约数之和问题4.欧几里得算法(辗转相除法)三、欧拉函数1.公式法求欧拉函数2.线性筛法求欧拉函数  3.欧拉定理和费马......
  • [BZOJ2194] 快速傅立叶之二 题解
    看名字,然后准备转化为多项式乘法。\[c_k=\sum_{i=0}^{n-k-1}a_{i+k}b_i\]将\(a\)反转,得:\[c_k=\sum_{i=0}^{n-k-1}a_{n-i-k-1}b_i\]这已经是多项式乘法的格式了,直接多项式乘法,最后对函数\(c\)的\(0\)到\(n-1\)次项倒序输出即可。时间复杂度\(O(n\logn)\)。#include......
  • Python_CUDA入门教程学习记录
    这是本人21年读书时学习CUDA基础知识保留的一些笔记,学习时的内容出处和图片来源不记得了,仅作为个人记录!CUDA编程关键术语:host:cpudevice:GPUhostmemory:cpu内存devicememory:gpuonboard显存kernels:调用CPU上的在GPU执行的函数devicefunction:只能在GP......
  • 快速傅里叶变换总结
    基本概念对于求和式\(\suma_ix^i\),如果是有限项相加,称为多项式,记作\[f(x)=\sum_{i=0}^na_ix^i。\]其中最高次项的次数为\(n\),为\(n\)次多项式。用\(n+1\)个点可以唯一地确定一个\(n\)次多项式,这一过程可以参考拉格朗日插值。引入给定多项式\(f(x),g(x)\),求\(f(......
  • Agent系列(一)——利用OpenAI快速搭建简易Agent
    目录1、Agent 简介1.1Agents的核心组件1.1.1模型(Model):1.1.2工具(Tools):1.1.3编排层(OrchestrationLayer):1.2Agents的运作机制:从输入到输出 2、搭建简易的Agent 2.1模型准备2.1.1获取 api_key2.1.2获取base_url和chat_model2.2搭建Agent2.2.......