linux学习记录:makefile(代码片段)

河边小咸鱼 河边小咸鱼     2022-12-18     423

关键词:

Makefile

  • 这是本人在学习makefile时的记录,方便日后查询。
  • 所有我遇到的makefile相关的内容都会被记录在这篇笔记中,所以在之后接触到makefile相关的新内容后,会对这篇笔记的内容进行更新。

零、Makefile简介

  1. 描述了整个工程的编译、链接规则
    · 工程中的哪些源文件需要编译以及如何编译
    · 需要创建哪些库文件以及如何创建这些库文件
    · 如何产生期望得到的最终可执行文件
    · 可以快速的构建和管理工程

  2. make的执行机制
    · makefile文件的命名: Makefilemakefile
    · 在make执行时,会依次寻找GNUmakefile、Makefile、makefile,如未找到则报错,找到则执行此makefile文件。
    · 在执行makefile文件时,make会检测每一条命令的返回值,如果失败的话会报错并终止make,否则会继续进行。
    · 可以使用 make -f 来指定make命令读取的脚本名

  3. makefile的执行流程
    · 从makefile的第一个目标开始执行(从上往下数第一个)
    · 首先看该目标的依赖项,看依赖项是否存在
    · 如果不存在依赖项,则执行命令后结束。
    · 如果存在就先执行依赖项相关目标(然后看依赖项目标是否有依赖项…以此不断寻找最内层,有点dfs的感觉)

PS: 这里简单提一嘴编译和链接的过程…

步骤号执行前要干嘛过程执行后
1源文件(.c, .cpp, .h)预处理器(Preprocessor)进行预处理引入头文件、进行宏替换等预编译文件(.i, .ii)
2预编译文件(.i, .ii)编译器(Compiler)进行编译处理比如使用gcc或者g++进行编译汇编码(.s)
3汇编码(.s)汇编程序(Assembler)进行汇编把汇编码转为机器码机器码(.o, .obj)
4机器码(.o, .obj)链接器(Linker)进行链接对静态库(.lib, .a)进行连接得到可执行文件

一、基础语法

基础语法如下:

target...: prerequisttes...
	command...
	...

其中:
1.target为目标文件,可以是obj文件也可以是可执行文件
2.prerequisttes为依赖性,即生成目标文件所关联到的文件
3.command为指令,即make所需要执行的指令

1. 单源文件例子

下面是一个简单的单源文件makefile例子:

  如上图所示,在当前文件夹内有两个文件 main.c 和 makefile,而文件中的内容分别如下:

/*
* main.c
* 为简单的打印HelloWorld
*/
#include<stdio.h>

int main()

        printf("HelloWorld\\n");
        return 0;


/*
* makefile
* 设定目标文件为main,依赖项为main.c,命令语句为gcc main.c -o main
*/
main: main.c
        gcc main.c -o main

  如下图,执行make命令后,可以看到它执行了预设的gcc命令,随后正常生成了main可执行文件,并可以正常执行。由此,一个最简单的基础makefile示例就完成了。

2. 多源文件例子

下面是一个简单的多源文件makefile例子:
  如下,是一个文件夹里的四个文件:func.hfunc.cmain.cmakefile,具体内容都列在下方。其中func为一个简单的加法函数,而makefile中涉及了变量和伪目标的概念,具体内容在下面都会记录。

/*func.h*/
#ifndef _FUNC_H_
#define _FUNC_H_

int func(int a, int b);

#endif

/*func.c*/
#include"func.h"

int func(int a, int b)

        return a + b;


/*main.c*/
#include<stdio.h>
#include"func.h"

int main()

        printf("HelloWorld\\n1 + 2 = %d\\n", func(1, 2));
        return 0;


/*makefile 涉及到变量和伪目标,具体概念都在下文*/
TARGET = main
cc = gcc
FILE = main.c func.o

TARGET: $(FILE)
        $(CC) $(FILE) -w -o $(TARGET)
func.o: func.c
        $(CC) -c func.c

.PHONY: clean
clean:
        rm $(TARGET) *.o

  如下图,执行make命令,可以看到gcc命令由下向上执行(因为从最内层依赖项开始生成),生成了最终目标文件main,执行main发现func函数正常被调用,说明make成功。

  如下图,执行make clean命令,可以看到rm命令被正常执行,删除了main文件和所有.o后缀的文件。说明伪目标clean建立成功。自此该多源文件例子已经完成。

二、变量相关

变量简介:
  makefile脚本中可以引入变量来使得编写更加简便以及清晰。变量的声明非常简单,格式为 变量名 = 值 ;而调用变量的格式则为 $(变量名), 由此即可使用变量。

一个简单的例子:
  对上文单文件例子中的makefile进行简单修改,引入一些变量。修改如下:

/*修改前*/
main: main.c
        gcc main.c -o main

/*修改后*/
TARGET = main
cc = gcc
FILE = main.c
$(TARGET): $(FILE)
        $(CC) $(FILE) -o $(TARGET)

  如下图,在修改完makefile后,make仍可正常进行。通过变量,可以方便日后的修改,比如说想把gcc换成g++,就可以把编译器设为变量,想更换编译器时直接修改变量值即可,可以大大减少修改量。

三、伪目标

  通常makefile中第一个目标为最终目标,后续目标和最终目标有依赖关系。但是有时候想要执行清空生成的文件等一些单独执行的命令时,很明显这些命令并不会生成目标文件,由此和生成最终目标也没有必要关系,需要与普通的command进行区分,这时就出现了伪目标的概念。
  伪目标不是一个输出文件,而是一个标签。在执行make指令时,并不会主动执行伪目标的命令(因为伪目标没有依赖项),想要执行伪目标就必须使用命令 make 伪目标名 或者把伪目标放到makefile最上面。而显式声明伪目标的语法为 .PHONY: 伪目标名,随后设定调用此伪目标时执行的命令即可。

一个简单的例子:
  对上文单文件例子中的makefile进行简单修改(多文件例子中已经存在clean),在末尾引入伪目标,使其可以删除掉生成的main可执行文件。修改如下:

/*修改后*/
TARGET = main
cc = gcc
FILE = main.c
$(TARGET): $(FILE)
        $(CC) $(FILE) -o $(TARGET)
.PHONY: clean
clean:
        rm $(TARGET)

  如下图,在执行伪目标clean后,删除掉了设定好的最终目标main。在make后,再次生成最终目标main文件。

四、make嵌套执行

  在大的工程会把源文件分为很多个目录,为了逻辑上的简单,会为每个子目录写一个makefile文件,而最上层的makefile文件被称为总控makefile。通过执行总控makefile,即可自动执行下层的makefile文件,从而使得项目总体进行make操作。

下面是一个例子:
  我在当前文件夹内创建了三个新文件夹:main存放main.c源码、func存放func.h/c源码、build中存放obj文件和最终可执行文件。并且三个文件夹内都有自己的makefile文件,而当前文件夹下有总控makefile文件。源码如下:

  • ./func:
/* func.h */
#ifndef _FUNC_H_
#define _FUNC_H_

int func(int a, int b);

#endif

/* func.c */
#include"func.h"

int func(int a, int b)

        return a + b;


/* makefile */
CC = gcc

func.o: func.c
        $(CC) -c func.c -o func.o
        cp func.o ../build/func.o

.PHONY: clean
clean:
        rm *.o
  • ./main:
/* main.c */
#include<stdio.h>
#include"../func/func.h"

int main()

        printf("HelloWorld\\n1 + 2 = %d\\n", func(1, 2));
        return 0;


/* makefile */
CC = gcc

main.o: main.c
        $(CC) -c main.c -o main.o
        cp main.o ../build/main.o

.PHONY: clean
clean:
        rm *.o
  • ./build:
CC = gcc

test: func.o main.o
        $(CC) func.o main.o -I ../func -o test

.PHONY: clean
clean:
        rm test *.o
  • ./:
.PHONY: all
all:
        cd func;make
        cd main;make
        cd build;make

.PHONY: clean
clean:
        cd build;make clean
        cd main;make clean
        cd func;make clean

  可以看到上面四部分文件存在一个总控makefile和三个子makefile,通过make最终在build文件夹内生成可执行文件test,而test输出HelloWorld已经一个简单的加法式子。

  由上图可以看到,在执行make命令后,其按顺序依次进入各个文件夹并执行make命令(蓝框内),在make完成后,挨个查看各个文件夹,发现文件均正常生成。最后执行可执行文件test,发现正常执行,说明本次make成功。

  由上图可以看到,在执行make clean命令后,其按顺序进入各个文件夹并执行make clean命令,随后相关文件均被清除。自此该例子完成,其已经实现了make嵌套的功能。

linux开发基础知识makefile语法(代码片段)

文章目录Makefile学习笔记gccmake的使用Makefile的底层规则Makefile变量的引入以及使用信息显示函数的使用预定义变量定义命令包语法规则Makefile常用函数字符处理文件名操作其他函数foreach函数if函数call函数origin函数shell函数wildcard函... 查看详情

makefile的理论和实践的学习记录(代码片段)

1:Makefile的变量的四种基本赋值方式:简单赋值(:=)编程语言中常规理解的赋值方式,只对当前语句的变量有效。递归赋值(=)赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。条件赋值(?&#... 查看详情

makefile学习记录简单的两个c文件编译(代码片段)

实现一个简单的两个c文件的编译#makefile文件方法一CC=gcc#编译器的型号.PHONY:all#伪所址不管有没有all都会执行all:hello.ctool.o# $(CC)hello.ctool.o-oall#生成all.o文件tool.o:tool.c $(CC)-ctool.cclean:#清除文件 rm*.oallCC=gcc#编译器的型号RM=rm-... 查看详情

gdbmake/makefile学习心得(代码片段)

gdb、make/makefile学习心得文章目录gdb、make/makefile学习心得gcc/g++文件名问题gdb进入和退出gdb基本调试操作make/makefile依赖关系:如何写一个makefile多文件的makefile多个文件在一个makefile分别编译gcc/g++预处理的博客在学... 查看详情

gdbmake/makefile学习心得(代码片段)

gdb、make/makefile学习心得文章目录gdb、make/makefile学习心得gcc/g++文件名问题gdb进入和退出gdb基本调试操作make/makefile依赖关系:如何写一个makefile多文件的makefilegcc/g++预处理的博客在学习C语言的时候曾经讲过一个代... 查看详情

linux开发基础知识makefile语法(代码片段)

文章目录Makefile学习笔记gccmake的使用Makefile的底层规则Makefile变量的引入以及使用信息显示函数的使用预定义变量定义命令包语法规则Makefile常用函数字符处理文件名操作其他函数foreach函数if函数call函数origin函数shell函数wildcard函... 查看详情

linux学习记录:shell脚本(代码片段)

shell脚本这是本人在学习shell脚本时的记录,方便日后查询。里面会记录一些自己写的shell脚本,都是在实习中用到的。由此这篇笔记的内容也会不断扩充,也算是记录一下心路历程。默认使用解释器/bin/bash目录零、she... 查看详情

linux学习记录:shell脚本(代码片段)

shell脚本这是本人在学习shell脚本时的记录,方便日后查询。里面会记录一些自己写的shell脚本,都是在实习中用到的。由此这篇笔记的内容也会不断扩充,也算是记录一下心路历程。默认使用解释器/bin/bash目录零、she... 查看详情

makefile学习笔记系列4:makefile模板化(代码片段)

...12436/article/details/52461906中已经实现具有子目录层次结构的makefile写法,即主目录Makefile调用到每个子目录中的Makefile编译相应子目录的代码。但是每增加一个子目录又得为这个子目录添加一个为该子目录编译的Makefile文件,... 查看详情

linux学习记录:sudo相关(代码片段)

Linux学习记录:sudo相关在实习中接触到了系统安全相关的内容,其中一个重点就是对sudo权限的控制。正好我对这块东西不太熟悉,于是对相关的内容做了笔记汇总。这篇笔记中重点放在sudo的配置文件/etc/sudoers上,... 查看详情

linux驱动学习记录-新字符设备(代码片段)

...加载需要mknod命令创建节点。这些都是老版本,现在学习新字符设备驱动开发。第一节提了一点点,申请设备号是用自动分配的方式就是新设备开发。 1.新字符设备注册        Linux中cdev结 查看详情

makefile学习(代码片段)

1、多个if判断DEMO:=2all:ifeq($(DEMO),1)@echo"DEMO1"elseifeq($(DEMO),2)@echo"DEMO2"elseifeq($(DEMO),3)@echo"DEMO3"else@echo"DEMOOther"endif 2、打印变量DEMO:=1all:@echo$$DEMO=$DEMO@echo$$(DEMO)=$(D 查看详情

makefile编写学习--1(代码片段)

makefile是在编译中大型程序中使用的自动化编译工具make依赖的指令文件。这样可以使得程序的编译更加便捷快速。makefile的一般规则如下:target...:prerequisites...commandtarget即是一个目标文件,它可以是可执行程序、目标中间文件、... 查看详情

makefile学习(代码片段)

...,也就是.lib文件,在UNIX下,是ArchiveFile,也就是.a文件。MakefileMakefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。1、显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile... 查看详情

深度学习记录(代码片段)

下载安装anaconda官网:ananconda.comAnaconda3-2021.05-Linux-x86_64.sh安装bashAnaconda3-2021.05-Linux-x86_64.sh重启终端验证是否安装成功,安装成功后会有(bash)前缀4.输入condalist可以查看安装了哪些包5.安装detectron2的环境condacreate--named 查看详情

makefile的学习(代码片段)

目录MakeMakefile了解规则符号小demo通用Makefile更新中MakeMakefile了解一个最简单的小demohello:hello.cgcc-ohellohello.cclean:rm-fhello//强制删除(f)make命令根据文件更新的时间戳来决定哪些文件需要重新编译,这使得可以避免编译... 查看详情

[linux高并发服务器]makefile(代码片段)

[Linux高并发服务器]Makefile[Linux高并发服务器]Makefile[Linux高并发服务器]Makefile什么是Makefile文件命名和规则Makefile工作原理一、命令在执行之前,需要先检查依赖是否存在二、检测更新在执行规则中的命令时,会比较目标和... 查看详情

makefile学习笔记(代码片段)

Makefile学习笔记1.gcc编译过程预处理gcc-Ehello.c-ohello.i编译gcc-Shello.i-ohello.s汇编gcc-chello.s-ohello.o链接gcchello.o-ohello2.Makefile基本介绍我们把编译分为两个步骤。先编译gcc-ca.c-oa.ogcc-cb.c-ob.ogcc-cc.c-oc.o...再链接gcca.ob.oc.o-ohe 查看详情