Linux下Makefile的使用

Linux下Makefile的使用

  • 前言
    • 一、文件组织形式
    • 二、make的工作流程
    • 三、Makefile的手动编写
    • 四、Makefile的变量使用
      • 1.用户自定义变量
      • 2.预定义变量
      • 3.自动变量
    • 五、Makefile的包含和嵌套
    • 六、Makefile的自动生成
      • 1.Autotools工具
      • 2.configure文件的生成
    • 七、使用shell脚本辅助编译
  • 总结
  • 参考


前言

多个文件的组织即使由Make完成的,即组织文件,确定他们之间的依赖关系。层层的文件依赖关系。本文只记录最简单(依赖处于同一个文件夹下,未涉及库的使用)的Makefile文件创建方法,因为其实还有很多情况,这篇文章并不能完全讲解清楚。

一、文件组织形式

比如对于多程序文件的组织

gcc 源程序文件.c 程序文件1.c 程序文件2.c -o 可执行文件名

实现多个文件编译为一个可执行文件

二、make的工作流程

当你在一个文件夹下运行make命令时
1.make首先会在当前目录下寻找Makefile或者makefile文件
2.找到Makefile文件后,会在其中找到第一个目标文件,将其作为最终的输出文件
3.根据时间戳生成目标文件,比如说如果你的依赖生成时间都小于目标文件时间,那么,make不会生成新的目标文件,只会利用旧的目标文件
4.从第一个目标文件开始,递归寻找依赖文件

整个一个make过程就是编译c程序,链接依赖,生成可执行文件的过程

三、Makefile的手动编写

首先注意Makefile文件可以写为Makefile或者makefile的形式
工程文件组织工具,也即是安装工具,卸载工具
首先创建一个空的文件

touch Makefile#创建一个空的Makefile文件

最简单的Makefile的编写,内容如下

#create first Makefile 这里仅仅表示
main:main.c func1.c func2.c#准备执行下面指令需要准备的程序文件,书写方式即目标文件:依赖文件1 依赖文件2 依赖文件3
	gcc main.cxx func1.cxx func2.cxx -o main#执行的指令

Makefile的目录文件夹下,运行命令

make

即可按照Makefile里面的命令生成可执行文件
Makefile文件的复杂化编写,其内容可以编写为如下

#create Makefile
#这里采用多行的方式
main:main.o func1.o func2.o
        gcc main.o func1.o func2.o -o main
        
main.o:main.cxx#目标文件的编译不需要链接,执行文件的编译才需要
        gcc -c main.cxx -o main.o

func1.o:func1.cxx
        gcc -c func1.cxx -o func1.o

func2.o:func2.cxx
        gcc -c func2.cxx -o func2.o 

编写完Makefile文件之后直接进行make即可

make#按照Makefile进行操作

如果要编写make clean命令
即在Makefile文件目录下运行

make clean

达到清理中间产生的文件的作用,需要在Makefile文件的最后一行加上

clean:
	rm func1.o func2.o main.o main#这里删除的文件可以灵活指定,我删除了所有生成的文件

对于Makefile中目标文件后没有跟上依赖文件的称为伪目标
clean后面并没有指明依赖项
因此可以加上的伪目标指令有installuninstall
伪目标的标准书写方式为

.PHONY: clean#作用是防止在当前文件夹下有一个与伪目标相同的文件
clean:
	rm func1.o func2.o main.o main

要注意,Makefile里面的内容主要形式为

目标文件:依赖文件1 依赖文件2 依赖文件3

四、Makefile的变量使用

1.用户自定义变量

上一节讲述最简单的Makefile手动编写
这一节讲述变量的定义以及使用
Makefile下变量的命名使用全大写开头的形式

变量名 = 值1 值2 值3

变量在具体命令中的使用,加上$号,告诉系统这是变量,使用值进行替换

$(变量名)

可能是考虑到系统解析命令的时候,到底将你的变量名当作一个命令,还是一个变量来解析,加上$符号,直接告诉系统作为一个变量来解析
即然大彻大悟了,直接开始使用即可!!!!
使用变量之后的Makefile如下

#create Makefile
#这里采用多行的方式
MObj = main.o func1.o func2.o
AllObj = main.o func1.o func2.o main
Name = main#直接猛用,除了赋值之外,系统都能解析
$(Name): $(MObj)
        gcc $(MObj) -o main

main.o:main.cxx#目标文件的编译不需要链接,执行文件的编译才需要
        gcc -c main.cxx -o main.o

func1.o:func1.cxx
        gcc -c func1.cxx -o func1.o

func2.o:func2.cxx
        gcc -c func2.cxx -o func2.o

.PHONY: clean
clean:
        rm $(AllObj)

2.预定义变量

以上说明了用户自定义变量的一些内容,当然,在make中存在有一些预定义的变量。变量的使用,是为了方便更改工程配置

系统变量名 变量值
AR 库文件维护程序名称,默认值为ar
AS 汇编程序名称,默认值为as
CC c编译器名称,默认值为cc
CXX c++编译器名称,默认值为g++

什么时候需要用到这些系统变量呢?
比如Makefile中的语句

gcc -c main.cxx -o main.o

可以改写为

g$(CC) -c main.cxx -o main.o

可能就有人要??????了,这么写不是丑死了?
因为CC的默认值为cc
因此,我们赋值改变其为gcc

CC = gcc

然后那一串命令就可以改写为

$(CC) -c main.cxx -o main.o

其实这么做的目的是为了工程中当我们不想使用某一编译器而改成其他编译器之后,仅仅只需要更改CC的值即可,不用一条命令一条命令地更改

3.自动变量

什么是自动变量?其值不是固定的,在具体使用的情况下代表某一具体的值,其值是自动赋予的,主要包括以下几种变量

自动变量
$* 表示不包含扩展名的目标文件名称
$< 第一个依赖文件名称
$? 所有事件戳比目标文件晚的依赖文件
$@ 目标文件完整名称
$^ 所有不重复的依赖文件

比如我们用Makefile下的内容来解析,对于命令

func1.o:func1.cxx
        gcc -c func1.cxx -o func1.o

$* 就代表func1
$<就代表func1.cxx
$?就代表(。。。额,这里需要去看文件的创建事件,比如如果创建过func1.o之后func1.cxx文件我进行过修改的话,那这里就表示func1.cxx,否则并没有晚于func1.o的依赖文件)
$@就代表func1.o
$^代表func1.cxx
自动变量只在具体的命令下有对应的值

于是,使用变量进行替代的Makefile文件就可以表示为

#create Makefile
#这里采用多行的方式
MObj = main.o func1.o func2.o
AllObj = main.o func1.o func2.o main
Name = main
CC = gcc
$(Name): $(MObj)
        $(CC) $^ -o $@

main.o:main.cxx#目标文件的编译不需要链接,执行文件的编译才需要
        $(CC) -c $^ -o $@

func1.o:func1.cxx
        $(CC) -c $^ -o $@

func2.o:func2.cxx
        $(CC) -c $^ -o $@

.PHONY: clean
clean:
        rm $(AllObj)

变量代换是有助于提高开发时候的更改效率的。

五、Makefile的包含和嵌套

例如可以创建一个.mk文件
Makefile中加入代码

include 包含文件.mk

嵌套即是进入不同的子文件夹下对程序文件进行编译处理
Makefile中写下以下代码

subsystem:
	cd 子文件夹 $$ $(MAKE)
	或者
	cd 子文件夹 $$ gcc -c 程序文件.c -o 目标文件

包含和嵌套常用的指令

命令 意义
-C 路径 读入指定路径下的Makefile
-f 文件名 读入当前目录下的指定文件为Makefile,文件名可以比较任意
-I 路径 指定被包含的Makefile所在的目录

使用方式

make -C 路径
make -f 文件名
make -I 路径#这里执行的Makefile应该是在当前文件夹下,指定的路径说的是其所包含的Makefile目录

六、Makefile的自动生成

1.Autotools工具

其实手动编写Makefile文件是十分困难的,因此借助脚本和工具可以大大提高我们的开发效率,借助Autotools工具我们可以自动生成Makefile而不需要手动编写。
Autotools工具由以下工具组成:autoscan、aclocal、autoconf、autoheader、automake,这些工具在生成configure文件时需要用上
在生成configure文件的具体流程如GNU官网所示
Linux下Makefile的使用_第1张图片

2.configure文件的生成

首先,在源程序目录下运行命令

autoscan

于是在源文件目录下会生成两个文件autoscan.logconfigure.scan
Linux下Makefile的使用_第2张图片
我们需要修改configure.scan文件下的内容
通过命令

vim configure.scan

AC_INIT处改为

AC_INIT([你的目标文件名], [你的版本], [你的bug收集邮箱])

并在文件中新建一行加入

AM_INIT_AUTOMAKE#不加入这一行后面不会生成一个.m4文件

退出保存,修改其后缀为.ac

mv configure.scan configure.ac

之后就可以运行命令

aclocal

生成.m4文件
依次执行

autoconf
autoheader

生成configure和.h.in文件
创建Makefile.ac文件

vim Makefile.ac

并且输入以下内容

AUTOMAKE_OPTIONS = foreign	#软件包检查标准
bin_PROGRAMS = 执行文件名称		#软件名称
执行文件名称_SOURCES = 源文件.c 依赖文件1.c 依赖文件2.c	#所有依赖都要有,空格隔开

退出保存,然后运行命令

automake --add-missing

最后,运行

./configure
make

即可将程序文件编译为可执行文件
所有文件编写完毕之后,可以运行打包命令进行打包

make dist

七、使用shell脚本辅助编译

整个Makefile文件的生成其实并不容易,可以创建两个shell脚本辅助自己执行命令。例如创建一个make.sh脚本进行Makefile文件的编译,但是记住,configure.ac文件和Makefile.am文件你必须事先准备好
configure.ac通过运行一次autoscan命令生成的configure.scan修改即可。Makefile.am自己创建编辑
make.sh脚本的内容如下:

#! /usr/bin/bash

autoscan
aclocal
autoheader
autoconf
automake --add-missing

相当于上一节中提到指令的集成,运行该脚本直接一步完成到configure文件的生成
在这里插入图片描述
获得configure文件后运行

./configure

即可生成Makefile文件
在这里插入图片描述
然后运行

make

即可生成可执行文件
创建一个rm.sh脚本帮助自己删除创建出来的文件,其内容如下

#! /usr/bin/bash

find . ! -name 'subdir' ! -name 'configure.ac' ! -name 'func1.h' ! -name 'func1.cxx' ! -name 'func2.h' ! -name 'func2.cxx' ! -name 'main.cxx' ! -name 'make.sh' ! -name 'Makefile.am' ! -name 'rm.sh' -delete

注意:name后面的字符串表示自己不想删除的文件和文件夹,根据你编写的代码文件填写,不要直接复制粘贴我的使用
脚本的执行

chmod 777 make.sh#给脚本执行权限
chmod 777 rm.sh#给脚本执行权限
./make.sh#执行configure文件的创建
./rm.sh#删除除了源代码文件之外的文件

总结

本文只介绍了最简单的Makefile文件的生成,并没有全面的介绍Makefile各个场景的配置与生成,后面可能再深入学习一下相关的知识。如有纰漏,还望指教!

参考

【1】Making configure Scripts

你可能感兴趣的:(Linux编程,linux,运维,服务器)