linux下的项目组成
linux下的项目组成
1认识Makefile
程序的自动编译
当程序中有多个源文件时,
先编译:
g++ ‐c main.cpp ‐o main.o
g++ ‐c other.cpp ‐o other.o
再链接
g++ main.o other.o ‐o helloworld
考虑:能不能写一个脚本,自动执行所有的步骤
?
对比:在VC下面,我们点一个“生成解决方案”就能 生成整个项目了。。。。
Makefile
Makefile是一个描述“如何生成整个项目”的脚 本文件
方法:
① 创建一个文件叫Makefile
② 输入命令,根据Makefile里面的指示,自动执 行所有的步骤
make ‐f Makefile
(或者直接make 不带参数)
演示1: helloworld: g++ main.cpp other.cpp ‐o helloworld
Makefile的写法Makefile就是一个普通的文本文件。。。
它由很多条“规则rule”组成,这些规则就是描 述了“先干什么、后干什么”。。。
每一条规则的格式为:
target: dependencies【TAB】
system command1
【TAB】system command2
【TAB】system command…
target: dependencies
【TAB】 system command1
【TAB】 system command2
【TAB】 system command…
target: 目标 ,
dependencies: 依赖
【TAB】: 每行命令前必须插一个TAB
system command: 系统命令
读作:要完成目标target,必须执行命令commands…
演示1
helloworld:
【TAB】g++ main.cpp other.cpp ‐o helloworld
演示2
helloworld:
【TAB】g++ ‐c main.cpp ‐o main.o
【TAB】g++ ‐c other.cpp ‐o other.o
【TAB】g++ main.o other.o ‐o helloworld
Makefile的写法当存在很多规则时,默认从第一条规则开始 执行。 也可以显式地执行某条规则。
演示2
helloworld:
【TAB】g++ ‐c main.cpp ‐o main.o
【TAB】g++ ‐c other.cpp ‐o other.o
【TAB】g++ main.o other.o ‐o helloworld
clean:
【TAB】rm ‐rf *.o helloworld
输入make命令时,同时显式指定要执行那一条rule:make clean make ‐f Makefile clean
注:make命令默认只执行一条规则,其他规则默认不执行
小结
(1)理解Makefile的作用
(2)学会make命令的用法
注意: 不要忘记插一个TAB符
2增量编译
增量编译
一个项目中有多个源文件,a.cpp b.cpp c.cpp … 编译:
a.cpp ‐> a.o
b.cpp ‐> b.o
c.cpp ‐> c.o
链接
(a.o , b.o , c.o ‐> helloworld )
当某个cpp更新后,只编译这个cpp文件,称为增量编译。 (增量编译,全量编译
)
在VC中演示。。。
Makefile: 增量编译
Makefile里使用“依赖dependency”来实现增量编译。
target: dependencies
【TAB】 system command1
【TAB】 system command…依赖:是一个文件列表
,当有文件更新时,执行这条 规则。
(注:根据文件的修改时间来判断是否更新) (如果某个依赖文件的时间比target的时间要新。。。)
示例1 演示dependency的写法
helloworld: main.cpp other.cpp other.h
g++ main.cpp other.cpp ‐o helloworld
表示:当main.cpp、other.cpp、other.h中有文件 比较helloworld更新时,则执行规则。。。 否则,不执行规则。查看文件时间 ls ‐l ‐‐full‐time
增量编译:不要一次性把所有文件都重新编 译一遍,而是只编译有变化的部分。。
示例2
helloworld: main.o other.o
【TAB】g++ main.o other.o ‐o helloworld
main.o: main.cpp other.h
【TAB】 g++ ‐c main.cpp ‐o main.o
other.o: other.cpp other.h
【TAB】g++ ‐c other.cpp ‐o other.o
clean:
【TAB】 rm *.o helloworld
演示:修改other.cpp。。。
特例
时间比较:
target (T1) : dependencies (T2)
①若target文件不存在,则T1为0
②若denpendencies为空,则T2为0
比较T2 与T1:
if ( T1==0) 执行;
else if (T2 > T1) 执行;
else "已是最新,不执行规则 "
(注:当依赖文件不存在,则提示编译错误…)
特例
举例:
helloworld:
g++ main.cpp other.cpp ‐o helloworld
(1)如果helloworld文件不存在,则T1为0,规 则被执行,生成helloworld
(2)如果helloworld存在,则T1>0, T2=0,规则 不被执行(已是最新)
小结
理解增量编译的含义
如何在Makefile中 实现增量编译
目标的时间>依赖的时间不执行
3注释、变量与函数
Makefile中的·注释·
以#开头的行,为注释行
#this is a test …
Makefile中支持变量
:
SUBDIR = src xml
SUBDIR+= osapi
test:
echo $(SUBDIR)
定义了一个变量SUBDIR①用 =号定义一个变量,并且赋值 (等号两侧可以加 空格) ②用 +=追加字符串 ③用$(SUBDIR)取得到变量的值(要加小括号)
Makefile中的变量
特殊的变量:[email protected]
指代target$^
指代dependencies依赖项列表$<
指代依赖项列表的第一项
注:这几种特殊变量经常用到
Makefile中的函数
Makefile中有一些预定义的函数
$(函数名 参数列表)
函数名:Makefile内部自带的函数
参数列表:以逗号分开 另,函数名和参数之间以空格分开
例如,
PWD=$(shell pwd)
CXX_SOURCE=$(wildcard ./*.cpp)
注意
:如果不想命令行显示命令,只显示结果,则makefile前面加一个@
小结:
1,注释
2,变量定义及应用
3,函数
4优化Makefile
优化Makefile
优化的方向:针对大量CPP文件,减少手工编 辑操作,争取最大程度的自动化。
原则:保持增量编译的功能。
①使用通配符
%.o: %.cpp
【TAB】g++ ‐c $< ‐o [email protected]
[email protected] 指target
$< 指第一个依赖项
② 自动罗列*.o 文件
CXX_SOURCES=$(wildcard
*.cpp) CXX_OBJECTS=$(patsubst
%.cpp, %.o, $(CXX_SOURCES))
patsubst函数:
源格式
目标格式
文件名列表
③ 设置EXE变量,表示输出程序的名称 EXE=helloworld
补充
把头文件作为依赖项,附加在后面
main.o: other.h
other.o: other.h
问题:如果自动生成头文件依赖项呢?
小结:
①使用通配符
② 自动罗列*.o 文件
③ 设置EXE变量,表示输出程序的名称
5头文件依赖的自动生成
头文件依赖
如果头文件被更新,则包含了它的cpp文件应 该被重新编译。
//////// main.cpp ///////// #include “object.h”
所以,增量编译必须考虑头文件的更新。。。 main.o: main.cpp object.h
头文件依赖的生成
main.o: other.h
other.o: other.h
这种头文件的依赖如何生成呢? (手写容易遗漏)
头文件依赖的生成
使用编译选项‐MMD
g++/gcc在编译xxx.cpp时,可以提取里面包含 的头文件(双引号包含的头文件)
#include “other.h”
然后,生成相应的.d 文件g++ ‐c ‐MMD main.cpp ‐o main.o (编译过程) 则在生成main.o的同时,生成一个main.d文件
优化Makefile
DEP_FILES = $(patsubst %.o, %.d,$(CXX_OBJECTS))
%.o: %.cpp
g++ ‐c ‐MMD $< ‐o [email protected]‐include $(DEP_FILES)
使用‐include指令,将所有的.d文件包含进来
注意
正常情况下,*.d文件被g++生成在和*.o相同的目录
例如,有一个子目录src
g++ ‐c ‐MMD src/main.cpp ‐o src/main.o
则生成的main.d也在src目录下
但有些Linux上的g++/gcc版本在生成.d文件的位置有点问题 可以使用(在Fedora Linux上存在这个问题)
%.o:%.cpp
g++ ‐c ‐MMD ‐MT [email protected] ‐MF $(patsubst %.cpp, %.d, $<)
$< ‐o [email protected]
小结:
6子目录的支持
Makefile Lv1.0
本章难度:在这一部教程里最难
当然,我们也可以使用笨办法:不使用自动化技 术,全部手工写Makefile。。。
缺点:工作量大,容易疏漏,无法扩展
我们把本节课创建的Makefile难度定为Lv1.0
① 支持子目录
②.d .o 文件与.cpp文件混在一起(缺点)
子目录
通常,一个项目下会有多个子目录,一个子目录通常 在逻辑上是一个模块(但并不是强制要求)
比如,一个项目文件的组织结构:
‐ K05_06/
‐ Makefile
‐ src/
‐main.cpp
‐ object/
‐object.cpp
‐object.h
子目录
包含别的子目录下的头文件。。。
///////// src/main.cpp //// #include “…/object/object.h”
在Makefile中支持子目录
在Makefile中,要自动罗列出子目录下的cpp 文件
使用foreach
小结
Makefile Lv1.0,我们即将使用此版本作为后续课 程的演示。
为什么要写一个自动化的Makefile:‐Makefile难度较大,但我们只需要写一次 ‐让我们的精力能够集中在代码上,而不是在 Makefile花太多时间
(此版本的待改进之处:生成.o和.d与源文件混在一起)