makefile 编写

makefile的作用是用来编译管理项目代码,节省编译项目所用的时间,并且一次编写终身受益。

并且由于目标比依赖生成的要晚,更新依赖之后会检测目标或者是依赖的生成的时间,所以即使当 .c 程序被修改之后再次使用 make 命令来运行 makefile 文件的时候,(运行makefile文件使用的是 make 命令)所显示的结果却可以是更改程序的结果(makefile的工作机制)。

创建 makefile 的基本要素有三个,分别是目标,依赖和命令。其中目标指的是 makefile 中要生成的目标文件。

依赖指的是用来生成目标文件的文件(即,目标文件是由依赖来生成的)。命令指的是由依赖生成目标文件的命令(即,通过这个命令可由依赖来得到目标文件,确切的说从依赖到目标是由 gcc 命令来生成的)

在 makefile 中主要三个内容,这三个内容可以描述为一个规则,两个函数,三个自动变量。

三原则

一个规则

这个规则叫做模式规则,模式规则指:

在makefile中的目标和依赖中,在目标中可以包含一个或者多个%,同样在依赖中也可以包含%,并且在依赖条件中的%的取值取决于目标中的%,换句话说,依赖和目标中的%所代表的值必须是相等的。

两个函数

src=$(wildcard *.c)函数

它的功能是找到目标文件下的.c文件并将它们全都赋给src (即,src中存放的是.c文件,并且这个函数中的src是个任意命名的变量,但是$(wildcard .c)是这个实现找出.c文件的makefile函数),要注意的是在这个函数中wildcard和.c之间是没有逗号将它们两个隔开的,它们两个之间只有空格)

obj=$(patsubst %.c , %.o , $(src))函数

它的功能是把后缀中的.c 文件替换为 .o文件并赋给obj,把第三个参数的集合当中取出来 里面所有包含第一个参数的部分 替换成第二个参数的部分 (即,obj中存放的是.o的目标文件),在这里obj是个任意命名的变量,但是$(patsubst %.c , %.o , $(src))是实现这个将.c 文件转换为.o文件的makefile函数。要注意的是在这个函数中patsubst 和%.c 之间是没有逗号将它们两个隔开的,它们两个之间只有空格,%.c , %.o , $(src)之间使用逗号隔开的。

三个自动变量

在makefile中有三个自动变量,分别为

$@(在命令中表示规则中的目标) 
$^(目标:依赖格式中的所有的依赖) 
$< (目标:依赖中的第一个依赖,并且该依赖可以依次向后读取)

在makefile中除了这一个规则,两个函数,三个规则外,还有一个clean和在makefile中变量的定义和对变量的取值也是一块重要的内容。

这个clean在格式上只有目标没有依赖,在clean的命令中可以编写上对目标文件和最终文件进行删除的命令。(在makefile中规定,当只有目标文件,而没有依赖的时候,则直接执行这个目标下所对应的命令)当编写完这个makefile文件后,当要去执行这里的clean的时候,所执行的命令是 make clean。

而命令make clear -n(-n表示模拟执行以下makefile中的clear命令,只为了把makefile中的这个命令显示一下,并没有真正的执行)。

在clean目标中需要配合使用伪目标声明,这个伪目标声明是指当当前路径下含有以clean命令的文件或者是目录的时候,则在使用命令make clean的时候,就会导致结果无法执行,并会提示“clean是最新的”。

解决这种问题,只需要在makefile中加入一个伪目标声明即可,格式为 .PHONY.clean

img

解决方法:

img

在makefile中除了自带的三个自动变量外还可以手动的去自定义变量。对makefile中所定义的变量进行取址的方式为:$(变量名) ,对这里的变量所赋的值可以是.c文件和.o文件

makefile的具体制作

(假设有三个用来实现加减乘功能的函数分别为在 add.c mul.c sub.c 中,以及一个 main 函数)

1.创建 makefile

首先创建的makefile文件的文件名可以是makefile,也可以是Makefile ,创建该文件的命令是vi makefile或者是vi Makefile

vi Makefile

2.编写依赖规则

目标:依赖
(一个Tab的距离)命令

# app 为最后需要编译成的目标,后面为依赖
app:main.c add.c mul.c sub.c
        # 使用 gcc 编译命令,编译成 app
        gcc main.c add.c mul.c sub.c -o app

第二行 gcc 则是实现从依赖到目标的命令

或者是

# 依赖 .o 文件生成可执行的文件app
app:main.o add.o mul.o sub.o
        gcc main.o add.o mul.o sub.o -o app
main.o:main.c
        gcc -c main.c -o main.o
# 依赖 .c 文件生成 .o 文件
# 但是本身只有 .c 文件,所以还需要命令来生成 .o 文件
add.o:add.c
        gcc -c add.c -o add.o
mul.o:mul.c
        gcc -c mul.c -o mul.o
sub.o:sub.c
        gcc -c sub.c -o sub.o
# clean 目标用于删除 .o 文件和可执行文件的
clean:
        rm -rf main.o add.o sub.o mul.o

在这里要注意,由于makefile文件是从上往下读,并且把文件的第一行作为终极目标。所以要把最终生成可执行文件的目标和依赖放在makefile文件的第一行。

使用自定义变量来进行编译

# 此处设置自定义变量 src
src=main.o add.o mul.o sub.o
# 使用 $(src) 去除自定义变量中的值
app:$(src)
        gcc main.o add.o mul.o sub.o -o app
main.o:main.c
        gcc -c main.c -o main.o
add.o:add.c
        gcc -c add.c -o add.o
mul.o:mul.c
        gcc -c mul.c -o mul.o
sub.o:sub.c
        gcc -c sub.c -o sub.o
clean:
        rm -rf main.o add.o sub.o mul.o

使用 makefile 三个自定义变量进行编译

obj = add.o sub.o mul.o main.o
target=app
$(target):$(obj)
        gcc $(obj) -o $(target)
# 当查找顶层的 main.o 时没找到,则执行下面的语句,将 %.o:%.c 替换为 main.o:main.c ,如此类推 
%.o:%.c
    #  $@ (在命令中表示规则中的目标) 
    #  $^ (目标:依赖格式中的所有的依赖) 
    #  $< (目标:依赖中的第一个依赖,并且该依赖可以依次向后读取)
    #  上面三个命令只能在规则中使用
        gcc -c $< -o $@

使用makefile 中维护的变量进行编译

obj = add.o sub.o mul.o main.o
target=app
# makefile 中维护的变量, 默认为 cc
CC = gcc
# 预编译时的选项,如 头文件 -I
CPPFLAGS =
# 编译的时候使用的参数 -Wall -g -c
CFLAGS =
# 链接库使用的选项 -L -l
LDFLAGS =
$(target):$(obj)
        $(CC) $(obj) -o $(target)
%.o:%.c
        $(CC) -c $< -o $@

使用makefile提供的函数进行编译

# 取出制定目录下的所有 .c 文件
src = $(wildcard ./*.c)
# 在$(patsubst %.c,%.o,$(dir) )中,patsubst把$(dir)中的变量符合后缀是.c的全部替换成
.o
obj = $(patsubst ./%.c, ./%.o, $(src))
target=app
ALL:$(target)
CC = gcc
CPPFLAGS =
CFLAGS =
LDFLAGS =

$(obj):%.o:%.c
        $(CC) -c $< -o $@

$(target):$(obj)
        $(CC) $(obj) -o $(target)

clean:
        # 若 rm 改为 -rm 则即使clean出错也会继续向下执行
        -rm -rf $(target) $(obj)
# 声明 clean 为伪目标,避免目录中存在 clean 文件
.PHONY: clean ALL

使用 makefile 编译不同目录中的文件

假设现在将 .c 文件放置与 src 目录中,.h 文件放置于 include 目录中,并将要将编译的 .o 文件放置于 obj 中,目录图如下:

.
├── include
│   └── head.h
├── obj
└── src
    ├── add.c
    ├── main.c
    ├── mul.c
    └── sub.c

此时的 makefile 应该这样编写:

src = $(wildcard ./src/*.c)
obj = $(patsubst ./src/%.c, ./obj/%.o, $(src))

inc_path = ./include/
CC = gcc
target=app	
CPPFLAGS =
CFLAGS = -I
LDFLAGS = 

ALL:$(target)



$(obj):./obj/%.o:./src/%.c	
	$(CC) -c $< -o $@ $(CFLAGS) $(inc_path)
	
$(target):$(obj) 
	$(CC) $(obj) -o $(target)
	
clean:
	-rm -rf $(target) $(obj)

.PHONY: clean ALL

扩展知识

Makefile 选项说明 CFLAGS 、LDFLAGS 、LIBS

  • CFLAGS 表示用于C编译器的选项
    CXXFLAGS 表示用于C++编译器的选项
    这两个变量实际上涵盖了编译和汇编的两个步骤

  • CFLAGS:指定头文件(.h)的路径,如:CFLAGS=-I/usr/include -I/path/include 。

相同地,安装一个包时会在安装路径下建立一个include文件夹,当安装过程中出现故障时,试着把曾经安装的包的include文件夹增加到该变量中来。

  • LDFLAGS:gcc 等编译器会用到的一些优化參数,也能够在里面指定库文件的位置。

使用方法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安装一个包都差点儿一定的会在安装文件夹里建立一个lib文件夹。假设明明安装了某个包,而安装还有一个包时,它愣是说找不到,能够抒那个包的lib路径增加的LDFALGS中试一下。

  • LIBS:告诉链接器要链接哪些库文件。如LIBS = -lpthread -liconv

简单地说,LDFLAGS是告诉链接器从哪里寻找库文件,而LIBS是告诉链接器要链接哪些库文件。

有时候LDFLAGS指定-L尽管能让链接器找到库进行链接。可是运行时链接器却找不到这个库。假设要让软件运行时库文件的路径也得到扩展,那么我们须要增加这两个库给”-Wl,R”:

LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib

假设在运行./configure曾经环境变量设置export LDFLAGS=”-L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib” ,注意环境变量设置等号两边不能够有空格,并且要加上引號(shell的使用方法)。那么运行configure以后。Makefile将会设置这个选项。链接时会有这个參数,编译出来的可运行程序的库文件搜索路径就得到扩展了。

linux 中使用动态库

前面介绍了如何使用 Makefile 编译,但是编译完成后引用动态库也是一件麻烦事,例如程序集成了 x264 的动态库,但是调用的时候却找不到动态库,这就是一件麻烦事,有以下三种解决方法。

如 x264 动态库位于 /home/username/x264build/lib 目录下

  • 在用户环境变量中添加 LD__LIBRARY_PATH (不常用)

在用户环境变量中添加动态库位置可以永久生效(只要该动态库还在此位置),如 ubuntu 中用户环境变量在 /home/username/.profile 中

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/username/x264build/lib

配置完后,要重启终端

  • 将动态库路径添加到临时路径中 (测试使用)

此方法直接在终端中输入

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/username/x264build/lib

此时会将动态库临时导入环境变量,缺点是关闭此终端则无法使用。

  • 将动态库路径写到配置文件中

配置文件路径为 /etc/ld.so.conf ,将路径添加进去,然后执行更新配置命令,方法如下

sudo vi /etc/ld.so.conf

然后将路径写入:

# 原本就有的路径
include /etc/ld.so.conf.d/*.conf
# 自己添加的路径
/home/username/x264build/lib

最后执行更新配置文件命令

sudo ldconfig -v
  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值