多个文件的组织即使由Make完成的,即组织文件,确定他们之间的依赖关系。层层的文件依赖关系。本文只记录最简单(依赖处于同一个文件夹下,未涉及库的使用)的Makefile
文件创建方法,因为其实还有很多情况,这篇文章并不能完全讲解清楚。
比如对于多程序文件的组织
gcc 源程序文件.c 程序文件1.c 程序文件2.c -o 可执行文件名
实现多个文件编译为一个可执行文件
当你在一个文件夹下运行make
命令时
1.make
首先会在当前目录下寻找Makefile
或者makefile
文件
2.找到Makefile
文件后,会在其中找到第一个目标文件,将其作为最终的输出文件
3.根据时间戳生成目标文件,比如说如果你的依赖生成时间都小于目标文件时间,那么,make
不会生成新的目标文件,只会利用旧的目标文件
4.从第一个目标文件开始,递归寻找依赖文件
整个一个make
过程就是编译c
程序,链接依赖,生成可执行文件的过程
首先注意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
后面并没有指明依赖项
因此可以加上的伪目标指令有install
和uninstall
伪目标的标准书写方式为
.PHONY: clean#作用是防止在当前文件夹下有一个与伪目标相同的文件
clean:
rm func1.o func2.o main.o main
要注意,Makefile
里面的内容主要形式为
目标文件:依赖文件1 依赖文件2 依赖文件3
上一节讲述最简单的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)
以上说明了用户自定义变量的一些内容,当然,在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
的值即可,不用一条命令一条命令地更改
什么是自动变量?其值不是固定的,在具体使用的情况下代表某一具体的值,其值是自动赋予的,主要包括以下几种变量
自动变量 | 值 |
---|---|
$* | 表示不包含扩展名的目标文件名称 |
$< | 第一个依赖文件名称 |
$? | 所有事件戳比目标文件晚的依赖文件 |
$@ | 目标文件完整名称 |
$^ | 所有不重复的依赖文件 |
比如我们用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)
变量代换是有助于提高开发时候的更改效率的。
例如可以创建一个.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
文件是十分困难的,因此借助脚本和工具可以大大提高我们的开发效率,借助Autotools
工具我们可以自动生成Makefile
而不需要手动编写。
Autotools
工具由以下工具组成:autoscan、aclocal、autoconf、autoheader、automake
,这些工具在生成configure
文件时需要用上
在生成configure
文件的具体流程如GNU
官网所示
首先,在源程序目录下运行命令
autoscan
于是在源文件目录下会生成两个文件autoscan.log
和configure.scan
我们需要修改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
整个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
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