Download presentation
Presentation is loading. Please wait.
1
大连理工大学软件学院 嵌入式系统工程系 赖晓晨
嵌入式系统程序设计 大连理工大学软件学院 嵌入式系统工程系 赖晓晨
2
交叉开发环境 嵌入式系统软件开发流程 高级语言编写 的源代码文件 主机系统 目标处理器的 汇编语言编写 交叉编译器 交叉汇编器 目标文件
连接器/ 加载器 目 标 系 统 以下取自白金手册
3
嵌入式系统开发工具链 ★ ★ ★ ★ 嵌入式系统开发工具链概述 交叉开发环境的建立
4
开发环境分类 开发环境分类: 运行平台:本地开发环境、交叉开发环境 商业角度:收费工具、免费工具
5
GNU工具链 GNU工具链: GNU既支持本地程序开发,又支持交叉编译
GNU Tools:全称(GNU Development Toolchains) GNU Tools交叉开发环境:全称(GNU Cross-Plateform Development Toolchains) GNU既支持本地程序开发,又支持交叉编译
6
GNU开发工具简介 GNU开发工具 自由软件
完备的工具链: GCC、binutils、gdb、 GNU make、patch、CVS 、开发库 命令行方式:使用稍复杂、功能强大 下载:
8
ARM平台GNU开发工具(续) 基于ARM9平台的GNU开发工具(linux) arm-linux-as arm-linux-gcc
arm-linux-ld arm-linux-objcopy 下载:
10
GNU Tools开发工具组成 GNU Tools是linux环境下最主要的开发工具集,主要有以下几个部分:
编译开发工具:把源程序编译为可执行文件,如gcc。 调试工具:对执行程序进行源码或汇编级调试的软件,如gdb。 软件工程工具:用于协助多人开发或大型软件项目的管理的软件,如make、cvs等。 gnu的编译工具不但可以编译程序,甚至可以编译linux内核
11
一、Linux开发工具链简介 1. GCC(GNU Compiler Collection)
支持C、C++、ADA、Object C、Java、Fortran、PASCAL等语言。 主要包括: cpp:GNU C编译器的预处理器。 gcc:符合ISO标准的C编译器。 g++:基本符合ISO标准的C++编译器。 gcj:GCC的java前端。 gnat:GCC的GNU ADA 95的前端。
12
一、Linux开发工具链简介 2. binutils 是一组二进制工具程序集合,是辅助GCC的主要软件。 主要包括: as:GNU汇编器
ld:GNU链接器 ar:创建归档文件,向库中添加/提取obj文件 nm:列出obj文件中的符号 objcopy:复制和转化obj文件
13
一、Linux开发工具链简介 2. binutils 主要包括: objdump:显示对象文件的信息
ranlib:根据归档文件中内容建立索引 readelf:显示elf格式执行文件中的各种信息 size:显示object文件和执行文件各段的总大小 strings:显示文件中可以打印的字符 strip:去掉执行文件中多余的信息(如调试信息) gprof:用来显示图表档案数据
14
一、Linux开发工具链简介 gdb:GNU调试器。可以用来调试C、C++和其他语言编写的程序。如加一些图形前端(如DDD),可以在图形环境下调试程序。 GNU make:是一个用来控制可执行程序生成过程、从其他源码文件中生成可执行程序的程序。它允许用户生成和安装软件包,而无需了解生成、安装软件包的过程。
15
一、Linux开发工具链简介 diff/diff3/sidff:比较文本差异的工具,也可以用来生成补丁。
patch:补丁安装程序,可根据diff生成的补丁来更新程序 版本控制系统:可以帮助管理程序的历史版本。 RCS(Revision Control System) CVS(Concurrent Version System) SVN(Subversion)
16
ARM平台GNU开发工具 基于ARM7平台的GNU开发工具(uclinux) arm-elf-as arm-elf-gcc
arm-elf-ld arm-elf-objcopy arm7的工具链
17
二、GNU交叉开发环境的建立 编译源码配置安装GNU交叉开发环境。 源码包下载 解压缩 编译 建立配置目录 安装 配置 测试
直接安装二进制形式的工具链 编译 安装 测试 binutils、GCC、glibc库有依赖关系,只下载最新版本不一定能兼容 先取最新版,如不成功就降低版本号
18
二、GNU交叉开发环境的建立(续) 1. 从GCC的网(http://gcc.gnu.org/)下载资源 gcc-4.1.1.tar.gz
供下载的文件有两种形式(格式不同,内容一致): gcc tar.gz gcc tar.bz2 arm7的工具链
19
二、GNU交叉开发环境的建立(续) 2. 解压缩 在当前目录下生成名为gcc-4.1.1的源码目录
文件gcc-4.1.1/INSTALL/index.html包含了安装gcc4.1.1的详细说明。 arm7的工具链
20
二、GNU交叉开发环境的建立(续) 3. 建立配置目录
建立一个和目录gcc-4.1.1同级的目录,命名为gcc-conf,作为保存配置文件的目录 arm7的工具链
21
二、GNU交叉开发环境的建立(续) 4. 配置 配置通过执行如下脚本 cd gcc-conf
../gcc-4.1.1/configure --prefix=/usr/local/gcc enable-threads --disable-checking --enable--long-long --host=i386-redhat-linux --with-system-zlib --enable-language=c,c++ arm7的工具链
22
二、GNU交叉开发环境的建立(续) 5. 编译 6. 安装 在配置目录下执行make命令
在配置目录下执行make install指令,即可将编译好的可执行文件复制到安装目录下 arm7的工具链
23
二、GNU交叉开发环境的建立(续) 7. 测试 切换到安装目录下,执行./gcc,显示"no input file",表示安装成功
对于编译好的工具链来说 图见下页 #mkdir -p /usr/local/arm #cd /usr/local/arm #tar -xjvf cross tar.bz2 #./arm-linux-gcc -v arm7的工具链
24
二、GNU交叉开发环境的建立(续) arm7的工具链
25
编辑器 ★ ★ 操作模式 进入和退出vi 光标移动 删除文本 查找和替换 复制和粘贴 保存文本块 与shell交互
26
vi简介 linux编辑器:ex、edit、ed、vi、emacs
vi(visual interface):是一个功能强大的,快速命令驱动的全屏幕编辑器。类比DOS的edit vi和vim rh9中,vi是vim的一个符号链接
27
一、操作模式 vi具有两种基本的操作模式 命令模式(指令模式):此模式下的按键当作指令来处理。 输入模式:可进行文本的录入。 命令模式负责
28
一、操作模式(续) 切换到输入模式的命令(输入文本位置不同) 从输入模式按esc进入命令模式
新增:‘a’,从从当前光标之后输入文本。‘A’,从光标所在行末尾输入新的文本。 插入:‘i’,从光标前开始插入文本。‘I’,从光标所在行的第一个非空格字符前开始插入文本。 开始:‘o’,从光标所在行下新增一行并进入输入模式。‘O’,从光标所在行上新增一行并进入输入模式。 从输入模式按esc进入命令模式 命令模式负责
29
二、进入和退出vi 进入vi,在提示符下: 退出vi,在指令模式下: 以冒号开始的命令要回车后才能执行 vi vi 文件名 :q,退出
:wq,存盘后退出 :q!,不存盘强制退出 :x,强制存盘退出 以冒号开始的命令要回车后才能执行 命令模式负责
30
三、光标移动 基本移动: nh,nj,nk,nl表示按某方向移动n行(列),任何模式下,方向箭头可以控制方向 h:向左移动一列
命令模式负责
31
三、光标移动(续) 移动多个位置: :n回车:将光标移动到第n行 :$回车或L:将光标移动到最后一行 M:将光标移动到当前屏幕中央行
H:将光标移动到当前屏幕第一行 命令模式负责
32
三、光标移动(续) 按单词移动: 组合:nw、nb、ne w:将光标移动到下一个单词头 b:将光标移动到前一个单词头
命令模式负责
33
三、光标移动(续) 按字符移动: 光标返回原处 $:将光标移动到当前末尾 ^或0:将光标移动到当前行首 n|:将光标移动到当前行的第n个字符
fm:将光标移动到当前行的下一个字符m处 光标返回原处 两个单引号‘’
34
三、光标移动(续) 翻页: 显示和取消行号 Ctrl+d:下翻半屏 Ctrl+u:上翻半屏 Ctrl+f:下翻一屏 Ctrl+b:上翻一屏
:set number:显示行号 :set nonumber:取消显示行号
35
四、删除文本 指令模式下: x:删除光标处的一个字符 dd:删除光标所在行 r:修改光标所在字符 R:进入改写状态
s:删除光标所在字符,并进入输入状态 S:删除光标所在行,并进入输入状态
36
四、删除文本(续) 指令模式下: dw:删除单词 D:删除从光标到行末的所有字符 dfm:删除从光标到第一个字符m间的文本
:nd:删除第n行 :n,$d:删除从第n行到最后一行,n为数字
37
五、查找和替换 指令模式下: 继续查找: /string:从当前位置向前查找字符串string
38
五、查找和替换(续) 继续查找: rm:替换当前字符为m,替换后仍为命令模式 R:替换当前字符后的一系列字符,替换后变为输入模式
s:多个字符替换单个字符 cw:单词替换 cc:行替换 C:替换当前行剩余部分 cfm:低缓当前字符到指定的字符m
39
六、复制和粘贴 指令模式下: yy:复制当前行 dd:剪切当前行(删除) p、P:粘贴剪切板上的内容到当前行位置 操作对象 删除 复制 修改
cc 行的剩余部分 d$或D y$ c$或C 从光标到第一个字符m dfm yfm cfm 单词 dw yw cw 字符 x yl s
40
六、复制和粘贴(续) 指令模式下: 把某(几)行复制并粘贴到某行后 :m copy n 把m行复制并粘贴到第n行后
:m,n copy $ 把m-n行复制并粘贴到末行后 :.,$ copy 0 把当前行到末行复制并粘贴到文件头 把copy改为move即为移动文本块
41
七、保存文件块 指令模式下: 保存文本块 :m,n write file:把第m到第n行另存为文件file
保存文本块,并直接覆盖掉已有(如果有)文件 :n write! file 把第n行强行另存为文件file 向文件追加文本 :n write >> file 把第n行追加到文件file末尾
42
八、与shell交互 指令模式下: 读某文件的内容到当前打开文件 :n read a 把文件a中的内容读到当前打开文件的第n行后
在vi中执行shell命令 :!pwd 在vi中执行shell命令,并把结果添加到vi中 :n read! pwd 把pwd执行结果插入到第n行后
43
gcc编译器 ★ ★ ★ GCC简介 gcc的基本用法 警告提示功能 代码优化 常用选项
44
一、GCC简介 GCC:GNU Compiler Collection
能够编译c、c++、Object C, 通过前端模块扩展还可支持java、fortran、pascal、ada、modula-3等。 提供了30多条警告信息和3个警告级别 gcc与g++ gcc和g++的区别在于 当你的程序中出现using namespace std这样的东西的时候 如果用gcc编译 必须显式指明这个程序要用C++标准库编译 而g++可以直接编译
45
A. GCC简介 GCC:GNU Compiler Collection 能够编译c、c++、Object C, 通过前端模块扩展还可支持java、fortran、pascal、ada、modula-3等。 提供了30多条警告信息和3个警告级别 gcc与g++ gcc和g++的区别在于: 当你的程序中出现using namespace std等带有C++特性的语句时,如果用gcc编译,必须显式指明这个程序要用C++标准库编译。而g++可以直接编译 gcc和g++的区别在于 当你的程序中出现using namespace std这样的东西的时候 如果用gcc编译 必须显式指明这个程序要用C++标准库编译 而g++可以直接编译
46
B. gcc支持的文件 后缀名 所支持的文件 .c C源程序 .C C++源程序 .cc .cxx .m Object C源程序 .i
.ii 经过预处理的C++源程序 .s 汇编语言源程序 .S .h 头文件 .o 目标文件 .a 存档文件
47
C. gcc是高效的编译器 g++编译后 4.6k tc++3编译后 7.8k bc45编译后 53.8k vc6编译后 184k
#include <stdio.h> main() { printf("hello world\n"); } g++编译后 k tc++3编译后 7.8k bc45编译后 k vc6编译后 k
48
D. 编译过程 用gcc编译程序时,分为四个阶段: 程序员可以根据需要在任何一阶段停止 预处理(pre-processing)
编译(compiling) 汇编(assembling) 链接(linking) 程序员可以根据需要在任何一阶段停止 用途:分析c源代码的执行原理
49
二、gcc的基本用法 语法格式: 举例(单文件) gcc [option] [filenames]
gcc -o hello hello.c 把hello.c文件编译为hello 用途:分析c源代码的执行原理
50
二、gcc的基本用法(续) 举例(多文件) gcc m1.c m2.c -o hello
把源文件m1.c和m2.c编译链接为可执行文件hello 用途:分析c源代码的执行原理
51
A. gcc常用选项 -o output_filename: 输出文件名称为output_filename
注意:这个名称不能和源文件同名,否则就会替换掉源文件。 如果不给出这个选项,gcc默认输出文件为a.out 用途:分析c源代码的执行原理
52
A. gcc常用选项(续) -g: -Idirname: 在可执行文件中包含符号调试工具所必须得调试信息
53
A. gcc常用选项(续) -Ldirname: -llibname: 指定目标文件链接时需要的库文件名 -v:
54
B. gcc工作过程 分四个阶段 gcc调用预处理程序cpp进行预处理,负责展开宏、插入头文件等;
gcc调用ccl,把预处理后的文件翻译成汇编; gcc调用as,把汇编程序汇编为目标文件; gcc调用链接程序ld,把目标代码链接为可执行文件。 用途:分析c源代码的执行原理
55
B. gcc工作过程(续) 分步编译源程序 gcc –E hello.c –o hello.i
gcc –S hello.c –o hello.s gcc –c hello.c –o hello.o gcc hello.c –o hello 用途:分析c源代码的执行原理 ~/exp/gcc/hello.c
56
三、警告提示功能 gcc包含完整的出错检查和警告提示功能,可以帮助程序员尽快找到错误的代码。 gcc包含30多个警告和3个警告级别。
57
三、警告提示功能(续) -Wall: -Werror -w 可以显示尽可能多的警告 可以警告当前错误,并停止编译 禁止所有的警告
用途:分析c源代码的执行原理
58
【例4-1】 /* ch4_1.c */ #include <stdio.h> void main(void) {
long long a = 1; printf("there are three warnings!\n"); } 用gcc -Wall编译时 用途:分析c源代码的执行原理 ch4_1.c:4:warning: return type of 'main' is not int ch4_1.c:in function 'main': ch4_1.c:5: warning: unused varibale 'a' ch4_1.c:7:2: warning: no newline at end of file
59
三、警告提示功能(续) 选 项 名 作 用 -Wcomment 如果出现注释嵌套则警告(/*后又出现/*) -Wformat
选 项 名 作 用 -Wcomment 如果出现注释嵌套则警告(/*后又出现/*) -Wformat 如果传递给printf的参数与制定格式不匹配则警告 -Wmain 如果main的返回类型不是int或者调用main时参数不正确则警告 -Wparentheses 根据上下文推断,如果把(n==10)写作(n=10)则警告 -Wswitch 如果switch中少了一个或多个case分支(仅对enum适用)则警告 -Wunused 变量声明了但未使用,或static类型函数未被调用则警告 -Wuninitialized 使用的自动变量没有初始化则警告 -Wundef 如果在#if宏中使用了未定义的变量做判断则警告 -Winline 函数不能被内联则警告 -Wmissing-declarations 如果定义了全局函数但却没有在任何头文件中声明则警告 -Wlong-long 使用了long long类型则警告 -Werror 把所有警告转变为错误 优化会改变代码结构
60
四、gcc代码优化 代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执行性能。
gcc代码优化采用“-On”选项 -00:对程序的编译、链接不进行优化 -O/ -O1:同时减小代码的长度和执行时间 -O2:包括-O的功能,以及指令调度等调整工作 -O3:包括-O2的功能,以及循环展开等工作 减少跳转操作的次数 通过在嵌套函数调用时推迟退栈的时间而优化运行效率,未优化的情况下每次函数调用完成后返回时都需要弹出栈中的函数参数,而优化后在栈中保留了参数,直到完成所有递归调用后才同时弹出栈中积累的函数参数。
61
【例4-2】 /* ch4_2.c */ #include <stdio.h> int main() { double i;
double t; double m; for(i=0;i<5000.1*5000.1*5000.1/ ;i+=( )/3.9) { m=i/2000.1; t=i+2.3; } return 0; linux下c程序的开发通常都要使用第三方函数库 ~/exp/gcc/optimeze.c
62
【例4-2】(续) 分别使用以下选项编译程序。 运行程序,找到运行时间 gcc ch4_2.c -o a1
gcc –O ch4_2.c -o a2 time ./a //1分19秒 time ./a //11秒 分别是7秒和1秒
63
代码优化的缺点(续) 代码优化的缺点 优化等级越高,编译的时间越长 例如优化可能会增加程序体积
64
代码优化的缺点(续) 代码优化的缺点 优化等级越高,编译的时间越长 资源受限时,优化可能会导致程序不可用,如嵌入式设备中
例如优化可能会增加程序体积
65
代码优化的缺点(续) 代码优化的缺点 优化等级越高,编译的时间越长 资源受限时,优化可能会导致程序不可用,如嵌入式设备中
优化使跟踪调试变得困难 优化会改变代码结构
66
五、gcc常用选项 选 项 名 作 用 -Dmacro 定义指定的宏,使它能够通过源码中的#ifdef 进行检验 -static
选 项 名 作 用 -Dmacro 定义指定的宏,使它能够通过源码中的#ifdef 进行检验 -static 对库文件进行静态链接 -ANSI 支持ANSI/ISO语法标准,取消gnu与所有与其冲突的语法 -pedantic 可以找到不符合ANSI/ISO标准的语句(并非全部) --pedantic-error 尽可能显示ANSI/ISO C标准列出的所有错误 -pipe 采用管道技术,加快程序编译 -save-temps 保留可执行文件编译过程中的临时文件 -Q 显示编译各阶段工作需要的详细的时间 优化会改变代码结构
67
ld链接器 ★ ★ ★ ld链接器概述 链接描述文件简介
68
一、ld链接器概述 ld链接器:把各种目标文件和库文件链接在一起,形成可执行文件。 输出文件 输入文件
gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。有关elf文件格式,读者可自行参考相关资料。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。 输出文件 输入文件
69
A. 可执行文件结构示意图 目标文件由段组成 data段:保存有初值的全局变量。 text段:保存代码 bss段:保存无初值的全局变量。
70
B. ld链接器的主要工作内容 Two pass设计流程 重点是重新计算各段、标志符号的位置。
扫描所有输入文件,包括目标文件和函数库,统计各段需要的总空间大小,并安排各段在可执行文件中的先后顺序,建立临时的符号表 重新扫描,建立最终符号表,组合各段,链接生成可执行程序 重点是重新计算各段、标志符号的位置。 gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。有关elf文件格式,读者可自行参考相关资料。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。
71
C. ld工作方式 工作方式: 链接描述文件:描述了各个输入文件的各段如何映射到输出文件的各段中,并控制输出文件各段布局。
gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。 ld程序识别链接描述文件(Linker Script)来显式的控制链接的过程。 链接描述文件:描述了各个输入文件的各段如何映射到输出文件的各段中,并控制输出文件各段布局。 链接命令语言(Linker Command Language):用来编写链接描述文件。 gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。有关elf文件格式,读者可自行参考相关资料。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。
72
【例】 下面的写法错误 下面写法正确 可见自己执行ld命令的过程是很繁琐的 ld hello.o -o myhello
gcc –c hello.c –o hello.o ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/c rtn.o hello.o -lc -o myhello crt*.o是C语言的run time运行库,是为进程运行作初始化和运行结束后的收尾工作的 Ld-linux.so.2是装载器 可见自己执行ld命令的过程是很繁琐的 ~/exp/vmlinux/hello.c
73
D. ld链接器选项 -EB:定义大端模式 -EL:定义小端模式 -l LIBNAME:要链接的库文件
-L DIRECTORY:搜索库文件时的路径 -o FILE:输出文件名 -O LEVEL:优化级别 -T FILE:读链接描述文件名 -e:设置执行程序入口点 ?-gdwarf2是指定ELF调试信息格式的标签
74
二、链接描述文件介绍 链接脚本文件支持命令操作,支持简单的数学运算,支持对目标机存储系统的定义,以及负责实际内存在目标机上的映射。
75
A. 链接描述文件举例1 输出文件段描述的格式如下: SETCTION { .=0x2000000; .text:{*(.text)}
.data:{*(.data)} .bss:{*(.bss)} } //符号赋值命令 //输出文件段描述
76
A. 链接描述文件举例1(续) .代表的含义是当前的位置。这段代码表示在地址0x 处放置程序的代码段,此代码段是由各模块的代码段合并而成,在地址为0x500000处放置数据段,接下来放置未初始化的数据段。
77
B. 链接描述文件的命令 简单赋值语句(符合赋值语句): symbol = expression;
78
C. 链接描述文件举例2 SECTIONS { ROM_BASE = 0x00000000; . = 0x0C000000;
Image_RO_Base = .; .text : { *(.text) } Image_RO_Limit = .; . = (. + 3) & ~ 3; Image_RW_Base = .; .data : { *(.data) } .rodata : { *(.rodata) } Image_RW_Limit = .; Image_ZI_Base = .; .bss : { *(.bss) } Image_ZI_Limit = .; }
79
gdb调试器 ★ ★ ★ ★ gdb简介 使用gdb
80
一、gdb简介 ★ ★ ★ ★ gdb简介 使用gdb 开始使用gdb gdb常用命令 gdb的停止点 查看和修改变量值
81
1. gdb简介 gdb是GNU发布的linux下的字符界面调试工具。功能强大。 gdb的功能 按照用户的要求启动程序
让被调试的程序在任意断点处停止(断点可以是条件表达式) 程序暂停时可检查运行环境 程序暂停时可动态改变运行环境
82
1. gdb简介(续) 要使用gdb调试程序,在用 gcc编译源文件时要指定-g选项, 以使程序中包含必要的信息
gdb是GNU发布的linux下的字符界面调试工具。功能强大。 gdb的功能 按照用户的要求启动程序 让被调试的程序在任意断点处停止(断点可以是条件表达式) 程序暂停时可检查运行环境 程序暂停时可动态改变运行环境 要使用gdb调试程序,在用 gcc编译源文件时要指定-g选项, 以使程序中包含必要的信息 DEBUG/RELEASE版本
83
【例4-3】 /* ch4_3.c */ #include <stdio.h> int func(int m) { int a;
float b; a=m; b=0.5; a=b*a; return a; } int main() { int i; int fact=1; for(i=6;i>=1;i--) fact*=i; } printf("the factorial of 6 is %d\n",fact); printf("the half of the number is: %d\n" ,func(fact)); return 0;
84
【例4-3】(续) 执行下列命令得到包含调试信息的可执行文件 执行下面程序启动gdb调试程序 gcc -g ch4_3.c -o ch4_3
gdb ch4_3
85
【例4-3】(续) 演示命令: 执行list查看源代码 用break在func()函数及18行设置断点 list 1 list
86
【例4-3】(续 演示命令) 执行list查看源代码 用break在func()函数及18行设置断点 list 1 list
info break run
87
【例4-3】(续 演示命令) 观察变量的值 print i print fact next 删除断点 delete 2 info break
88
【例4-3】(续 演示命令) 观继续执行 continue print a print b 运行结束
89
二、使用gdb ★ ★ ★ ★ gdb简介 使用gdb 开始使用gdb gdb常用命令 gdb的停止点 查看和修改变量值
90
1. 开始使用gdb 启动gdb: gdb的帮助信息: gdb program gdb (gdb) file program
(gdb)help 查看命令用法 (gdb)help classname --查看类中的命令 help status 显示status类的所有命令
91
2. gdb的常用命令 file:装入想调试的可执行文件 run:在gdb中运行程序 continue:继续运行被中止的程序
kil:终止正在调试的程序 quit:退出gdb list:列出源代码 info:查看断点信息 disassemble:查看函数反汇编代码 x addr:显示内存单元内容
92
2. gdb的常用命令(续) print:显示变量的值,只显示一次 display:显示变量的值,每次单步运行都会显示
undisplay:取消显示变量的值 backtrace:查看栈信息 where:得到函数调用链信息 next:单步执行程序,相当于step over step:相当于step into finish:执行完函数剩余部分,相当于step out return:不执行函数剩余部分,直接返回 call:强制调用函数
93
2. gdb的常用命令(续) print:显示变量的值,只显示一次 display:显示变量的值,每次单步运行都会显示
undisplay:取消显示变量的值 backtrace:查看栈信息 where:得到函数调用链信息 next:单步执行程序,相当于step over step:相当于step into finish:执行完函数剩余部分,相当于step out return:不执行函数剩余部分,直接返回 call:强制调用函数
94
2. gdb的常用命令(续) up:向栈上方移动 down:向栈下方移动 watch:监视变量的值 break:设置断点
jump:程序跳转到某处继续执行 show:查看环境变量等信息 delete:清除停止点 disable:禁止自动显示、断点等 enable:启动自动显示、断点等 set var:设置变量的值
95
2. gdb的常用命令(续) search:查找字符串 whatis:得到变量的类型 make:不退出gdb就可以重新产生可执行文件
shell:在gdb中执行shell命令。例如 shell ls 注意: 在gdb下直接回车,可以重复执行上一条命令, tab可以补齐命令 命令在不冲突的情况下都有缩写形式
96
3. gdb的停止点 gdb中程序的暂停有以下几种: 断点(breakpoint) 观察点(watchpoint)
捕捉点(catchpoint) 信号(signals) 线程停止(thread stops)
97
A. 设置断点 用break命令设置断点,有以下几种方法: break function //执行到某函数中止
break linenum //执行到某行中止 break +offset //在当前行后offset行停止 break –offset //在当前行前offset行停止 break filename:linenum break filename:function break *address tbreak //仅中断一次,中断后断点即自动删除 break … if condition //条件断点
98
B. 条件断点 设置条件断点: 用condition命令可以修改条件: 修改1号断点的条件 将条件断点变为一般断点
condition bnum break 10 if i==20 condition 1 i==30 condition 1 //清除1号断点
99
B. 条件断点(续) ignore命令可以指定程序运行时忽略停止条件几次。 执行时忽略1号断点的前10次中断 info命令查询停止点
info breakpoint :查看所有断点 info breakpoint n:查看第n个断点 ignore 1 10
100
C. 设置观察点 观察点一般用来观察某个表达式是否变化了,如果变化了,则暂停程序。 watch expr //表达式expr变化则停止
rwatch expr //表达式expr被读时停止 awatch expr //表达式expr被读写时停止 程序运行起来才能设置观察点,否则提示找不到观察点
101
C. 设置观察点(续) 观察点一般用来观察某个表达式是否变化了,设置观察点如下: 查看观察点:
watch expr //表达式expr变化则停止 rwatch expr //表达式expr被读时停止 awatch expr //表达式expr被读写时停止 查看观察点: info watchpoints info watchpoints n 程序运行起来才能设置观察点,否则提示找不到观察点
102
D. 维护停止点 维护停止点的命令 delete //删除停止点 clear //删除停止点 disable //禁用停止点
enable //启用停止点 tcatch //仅中断一次,中断后捕捉点即自动删除
103
【例】 clear //清除所有停止点 clear function //清除函数的停止点 clear filename:function
clear linenum //清除某行的停止点 clear filename:linenum delete breakpoints range //清除号码在某个范围的断点,如:d b 1-3 delete //清除所有断点 tcatch //仅中断一次,中断后捕捉点即自动删除
104
【例】(续) disable breakpoints range //禁用号码在某个范围的断点,如:disalbe b 1-3
enable breakpoints range //启用号码在某个范围的断点,如:ena b 1 2 3 enable breakpoints once range //仅启用某些断点一次,停止后立刻disable enable breakpoints delete range //仅启用某些断点一次,停止后立刻删除 tcatch //仅中断一次,中断后捕捉点即自动删除
105
E. 为停止点设定运行命令 可以利用command命令为停止点设定一系列运行命令。 程序指定到1号断点处会中断,并执行设置好的shell命令
shell pwd shell ls –l end tcatch //仅中断一次,中断后捕捉点即自动删除
106
4. 查看和修改变量值 print命令:查看程序中变量、数组的值 print /<formation> n //显示变量n的值
print /<formation> array //显示数组各元素值 print /<formation> //显示动态分配内存的数组各元素值 until不大好使 /<formation>表示显示格式
107
4. 查看和修改变量值(续) 显示格式 x,a:十六进制格式显示变量 d:十进制格式显示变量 u:十六进制格式显示无符号整形
o:八进制格式显示变量 t:二进制格式显示变量 c:按字符格式显示变量 f:按浮点数格式显示变量 until不大好使
108
【例4-4】 1 /* ch4_4.c */ 2 #include <stdlib.h> 3 int globle=120;
4 int main() 5 { 6 int a[]={1,2,3,4,5}; 7 int i; 8 int *p; 9 p=(int*)malloc(20); 10 for(i=0;i<5;i++) 11 { 12 p[i]=globle/a[i]; 13 } 14 free(p); 15 return 0; 16 } until不大好使
109
【例4-4】(续) 首先设置14行的断点,break 14,执行run (gdb) print i $1 = 5
(gdb) print globle $2 = 120 (gdb) print :: globle $3 = 120 (gdb) print a $4 = {1, 2, 3, 4, 5} (gdb) print $5 = {120, 60, 40, 30, 24} until不大好使
110
4. 查看和修改变量值(续) show命令和set命令 作用: show path:查看程序运行路径 show env:查看所有的环境变量
查看和修改系统的运行的环境变量 show path:查看程序运行路径 show env:查看所有的环境变量 show env HOME:查看某环境变量 set env LINES=25:设置某环境变量 until不大好使
111
自动化编译器配置 自动化编译器配置文件简介 MakeFile规则 MakeFile的变量 MakeFile的执行 make内嵌函数
112
一、自动化编译器配置文件简介 Makefile文件描述了整个工程的编译、链接规则,其中包括:工程中的哪些源文件需要编译以及如何编译,需要创建哪些库文件以及如何创建这些库文件,如何产生期望得到的最终可执行文件。 Makefile文件描述了工程中所有文件的编译顺序、编译规则,make根据这些规则来编译工程。
113
【例 4-5】 /* ch4_5.c */ #include <stdio.h> int main() {
printf(“hello everybody. \n”); return 0; } 方式1:命令行模式直接使用gcc命令编译程序,格式如下: #gcc hello.c –o hello 方式2:使用GNU make工具编译程序,需要编写Makefile,内容如下: hello:hello.c gcc hello.c –o hello
114
【例 4-6】 /* ch4_6.c */ #include <stdio.h> main() {
printf("hello\n"); foo(); } /* foo.c */ #include <stdio.h> void foo() { printf("you are in foo"); }
115
【例 4-6】(续) 方式1:命令行模式直接使用gcc命令编译程序,格式如下: #gcc main.c foo.c –o hello
方式2:使用GNU make工具编译程序,需要编写Makefile,内容如下: hello:main.o foo.o gcc main.o foo.o –o hello main.o:main.c gcc main.c –o main.o foo.o:foo.c gcc foo.c –o foo.o 在命令行运行make命令,同样可以编译出可执行程序hello。如下图所示:
116
【例 4-6】(续)
117
【例 4-6】(续)
118
【例 4-6】(续) 比对两种方式(产生文件的时间),我们发现:如果修改源文件foo.c,采用方式1重新编译程序,在编译foo的同时,未被修改的main.c也被重新编译,效率低下。采用方式2重新编译程序,make命令会分析各源文件、目标文件和可执行文件的生成时间,决定哪些文件需要重新编译和链接,因此main.c不需要重新编译,效率提高。
119
自动化编译器配置文件简介(续) 自动化编译器配置文件可以起名为: make命令按照以上顺序在当前目录搜索
GNUmakefile Makefile makefile make命令按照以上顺序在当前目录搜索 也可使用别的文件名来书写makefile,此时make命令要加选择:-f 例如: make –f mymake
120
自动化编译器配置文件简介(续) GNU make的执行过程分为:
在Linux命令行提示符输入make,它会在当前目录下按顺序寻找GNUmakefile、Makefile和makefile,如未找到则报错,找到则把Makefile文件中的第一个目标作为最终目标。 按照“堆栈”顺序,依次找到每一个目标文件,判断新旧关系,必要时生成新的目标文件,直到生成最终目标。
121
二、MakeFile规则 伪目标 MakeFile显示规则 标准目标 指定变量 特殊目标 隐含规则 命令错误 清空目标文件的规则 通配符
模式规则 伪目标 标准目标 特殊目标 命令错误 通配符
122
1. MakeFile显示规则 Makefile的基本结构由描述规则组成,规则负责描述在何种情况下如何重建目标文件,通常规则中包括了目标的依赖关系和重建目标的命令。一个简单的Makefile描述规则如下: target...:prerequisites... command... ...
123
MakeFile显示规则(续) target称为规则的目标,通常是最后需要生成的文件名,或是为了实现这个目的而必须的中间过程文件名。另外,目标也可以是一个make需要执行的动作的名称,如目标“clean”,称这样的目标是“伪目标”。伪目标一般不会被自动执行,而必须通过命令行指定伪目标名才能执行,例如“make clean”.
124
MakeFile显示规则(续) prerequistes称为规则的依赖文件,是生成规则目标所需要的文件名列表。通常是一个目标依赖于一个或者多个文件。 command称为规则命令行,这是规则所要执行的动作,可以是任意的shell命令或者可以在shell下执行的程序,它限定make执行这条规则时执行的操作。
125
例 4-7:makefile实例 edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o gcc -o edit main.o kbd.o command.o display.o \ main.o : main.c defs.h gcc -c main.c kbd.o : kbd.c defs.h command.h gcc -c kbd.c command.o : command.c defs.h command.h gcc -c command.c display.o : display.c defs.h buffer.h gcc -c display.c insert.o : insert.c defs.h buffer.h gcc -c insert.c search.o : search.c defs.h buffer.h gcc -c search.c files.o : files.c defs.h buffer.h command.h gcc -c files.c utils.o : utils.c defs.h gcc -c utils.c clean : rm edit main.o kbd.o command.o display.o \
126
例:makefile实例 连接符 目标文件 依赖文件 生成规则 makefile结构分析
edit : main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o gcc -o edit main.o kbd.o command.o display.o \ main.o : main.c defs.h gcc -c main.c kbd.o : kbd.c defs.h command.h gcc -c kbd.c command.o : command.c defs.h command.h gcc -c command.c display.o : display.c defs.h buffer.h gcc -c display.c insert.o : insert.c defs.h buffer.h gcc -c insert.c search.o : search.c defs.h buffer.h gcc -c search.c files.o : files.c defs.h buffer.h command.h gcc -c files.c utils.o : utils.c defs.h gcc -c utils.c clean : rm edit main.o kbd.o command.o display.o \ 连接符 例:makefile实例 目标文件 依赖文件 生成规则 makefile结构分析
127
总结 对于一个Makefile文件,make首先解析默认目标所在的规则,根据依赖文件(例子中第一个规则的8个.o文件)依次寻找创建这些依赖文件的规则。 创建或者更新每一个规则依赖文件的过程都是这样的一个过程。 更新默认目标的过程中,如果任何一个规则执行出现错误make就立即报错并退出。
128
2. 指定变量 edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o gcc -o edit main.o kbd.o command.o display.o \ objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
129
2。在makefile中使用变量 objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o edit : $(objects) gcc -o edit $(objects) main.o : main.c defs.h gcc -c main.c kbd.o : kbd.c defs.h command.h gcc -c kbd.c command.o : command.c defs.h command.h gcc -c command.c display.o : display.c defs.h buffer.h gcc -c display.c insert.o : insert.c defs.h buffer.h gcc -c insert.c search.o : search.c defs.h buffer.h gcc -c search.c files.o : files.c defs.h buffer.h command.h gcc -c files.c utils.o : utils.c defs.h gcc -c utils.c clean : rm edit $(objects) 2。在makefile中使用变量 edit : main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o gcc -o edit main.o kbd.o command.o display.o \ objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o
130
3. 隐含规则 什么是隐含规则 一些经常使用而且使用频率很高的,事先已经约定好,不需显式书写出的规则。隐含规则是一种惯例,make会按照这种“惯例”心照不宣的运行。
131
隐含规则(续) 隐含规则 objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o edit : $(objects) gcc -o edit $(objects) main.o : defs.h kbd.o : defs.h command.h command.o : defs.h command.h display.o : defs.h buffer.h insert.o : defs.h buffer.h search.o : defs.h buffer.h files.o : defs.h buffer.h command.h utils.o : defs.h .PHONY : clean clean : rm edit $(objects) 隐含规则
132
隐含规则(续) 隐含规则 显式指明clean是个伪目标文件
objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o edit : $(objects) gcc -o edit $(objects) main.o : defs.h kbd.o : defs.h command.h command.o : defs.h command.h display.o : defs.h buffer.h insert.o : defs.h buffer.h search.o : defs.h buffer.h files.o : defs.h buffer.h command.h utils.o : defs.h .PHONY : clean clean : rm edit $(objects) 隐含规则 显式指明clean是个伪目标文件
133
隐含规则(续) objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o edit : $(objects) gcc -o edit $(objects) $(objects) : defs.h kbd.o command.o files.o : command.h display.o insert.o search.o files.o : buffer.h .PHONY : clean clean : rm edit $(objects)
134
4. 清空目标文件的规则 每个makefile文件都应有一个“清空目标文件”的规则: .PHONY表示clean是一个伪目标
减号“-”的作用是一旦出现问题,可以继续执行后面的操作 clean不能放在文件头,习惯上放在文件尾 clean: rm edit $(objects) .PHONY : clean clean : -rm edit $(objects)
135
5. 模式规则 在模式规则中,目标名中需要包含有一个模式字符“%”,包含有模式字符的目标被用来匹配一个文件名。一个模式规则的格式为:
%.o:%.c:指定了如何由文件“N.c”来创建文件“N.o”,文件“N.c”必须是已经存在或者可被创建的;
136
6. 伪目标 伪目标一般来说: 也可为伪目标指定依赖关系,用于同时生成多个可执行文件。 没有依赖关系,不能直接执行 伪目标不能与文件名重名
显式指明伪目标:.PHONY 也可为伪目标指定依赖关系,用于同时生成多个可执行文件。
137
同时生成多个可执行文件 .PHONY: all all: p1 p2 p3 p1:p1.c gcc p1.c -o p1 p2:p2.c
#include <stdio.h> //p1.c main() { printf("this is p1.\n"); }
138
7. 标准目标 Makefile标准目标是一些约定俗成的目标,它们代表一个确定的含义,包含: all:编译所有的伪目标;
clean:删除所有被make创建的文件; install:安装已编译好的程序; print:列出改变过的源文件; tar:打包备份源程序; dist:创建一个压缩文件; TAGS:更新所有的目标;
139
8. 特殊目标 GNU make所支持的特殊目标有: .PHONY:它的所有依赖被视为伪目标。伪目标所在规则定义的命令会被无条件执行。
.PRECIOUS:它的所有依赖文件在make过程中会被特殊处理。 .DELETE_ON_ERROR:规则的命令执行错误,它将删除已经被修改的目标文件。 .SILENT:指定命令行没有意义,这是旧版中的用法。当前用法是使用make命令行参数“-s”或者“--silent” ……
140
9. 命令错误 make运行时,make会检测每个命令的执行的返回码,如果命令返回成功,make会执行下一条命令,否则make终止。
-:在makefile的命令行前加一个减号,此时不管命令是否出错,都认为是成功的。
141
10. 通配符 make支持2种通配符 通配符可在规则命令中使用,它是在命令被执行时由shell进行处理。例如: * ? clean:
rm -f *.o
142
三、MakeFile的变量 MakeFile的变量 MakeFile的变量高级用法 变量取值 override指示符 多行定义 系统环境变量
自动化变量 预定义变量
143
1. MakeFile的变量 Makefile中可以使用变量 变量类似于C语言的宏,但值可修改 变量名大小写敏感
变量名不应该包含:#=或空格 变量使用时用$(var)形式
144
变量定义两种方式 递归展开变量:使用“=”表示,可先使用,后定义。弊病:有可能递归定义,导致无限展开。
直接展开变量:使用“:=”表示,须先定义再使用。 foo = $(bar) bar = $(ugh) ugh = Huh? all: echo $(foo) a = $(b) b = $(a) all: echo $(a) x:=foo y:=$(x) bar x:=later all: @echo $(y) y:=$(x) bar x:=foo all: @echo $(y)
145
2. MakeFile的变量高级用法 变量值的替换:
可以替换变量的共有部分,格式为“$(var:a=b)”,可以把变量var中所有以a字串结尾的a替换为b字串 静态模式替换,模式中必须有一个“%” x:=a.o b.o c.o y:=$(x:%.o=%.c) all: @echo $(y) x:=a.o b.o c.o y:=$(x:.o=.c) all: @echo $(y)
146
MakeFile的变量高级用法(续) 2. 变量嵌套: x=y y=z a:=$($(x)) all: @echo $(a) x=$(y)
2. 变量嵌套: x=y y=z a:=$($(x)) all: @echo $(a) x=$(y) y=z z=hello a:=$($(x)) all: @echo $(a) x=y y=z z=hello a:=$($(x)) all: @echo $(a)
147
MakeFile的变量高级用法(续) 2. 变量嵌套: 函数subst:把x中的所有1字串替换成2字串 x=v1 v2:=hello
2. 变量嵌套: x=v1 v2:=hello y=$(subst 1,2,$(x)) z=y a:=$($($(z))) all: @echo $(a) 函数subst:把x中的所有1字串替换成2字串
148
3. 变量取值 变量的定义值在长度上没有限制; 当引用一个没有定义的变量时,make默认它的值为空;
一些特殊的变量在make中内嵌有固定的值,不过这些变量允许在Makefile中显式的重新赋值。 自动化变量不能在Makefile中显式修改; 如果希望实现这样一个操作,仅对一个之前没有定义过的变量赋值,可以使用“?=”代替“=”或者“:=”来实现。
149
变量取值(续) 可以使用+=给变量追加值 a=x.o y.o a+=z.o all: @echo $(a) a=x.o y.o
a:=$(a) z.o all: @echo $(a)
150
4. override指示符 如果有变量是make的命令行参数设置的,makefile中对这个变量的赋值就会被忽略。如果想使用makefile中设置的这个参数的值,可以使用override指示符,其语法是: override <variable>=<value> override <variable>:=<value>
151
override指示符(续) 使用override a=asdf override a=asdf all: all: @echo $(a)
$ make –f m13 a=123 $ make –f m14 a=123
152
5. 多行定义 定义变量的另外一种方式是使用“define”指示符。它定义一个包含多行字符串的变量。语法格式:以指示符“define”开始,以“endef”结束,之间的所有内容就是所定义的变量值。 define two-lines echo foo echo $(bar) endef two-lines=echo foo;echo $(bar) =
153
6. 系统环境变量 make运行时的系统环境变量可以在make开始运行时被载入到makefile中。
但是如果makefile定义了这个变量,则环境变量的值被覆盖(除非make运行时指定了-e参数)。 all: @echo $(PATH) PATH := " home" all: @echo $(PATH)
154
7. 自动化变量 $@:表示规则中的目标文件名。
$<:表示依赖目标中的第一个目标名字。如果依赖目标以模式(即%)表示,则$<将其一个一个取出来。 hello : a.c b.c c.c @echo hello : a.c b.c c.c @echo $<
155
自动化变量(续) 其他自动化变量: $%、$?、$^、$*、$+
156
8. 预定义变量 AR:归档维护程序,默认为ar; AS:汇编程序,默认为as; CC:C编译程序,默认为cc;
CXX:C++编译程序。默认是“g++”; CO:从RCS中提取文件的程序。默认是“co”; CPP:C预处理程序, 默认为cpp; FC:编译器和预处理Fortran和Ratfor源文件的编译器。默认是“f77”;
157
预定义变量(续) GET:从SCCS中提取文件程序。默认是“get”; LEX:将Lex语言转变为C或Ratfo的程序。默认是“lex”;
PC:Pascal语言编译器。默认是“pc”; YACC:Yacc文法分析器(针对于C程序)。默认命令是“yacc”; MAKEINFO:转换Textinfo源文件(.texi)到Info文件程序。默认是“makeinfo”; TEX:从Tex源文件创建Tex DVI文件的程序。默认是“tex”;
158
预定义变量(续) TEXI2DVI:从Texinfo源文件创建Tex DVI文件的程序。默认是“texi2dvi”;
WEAVE:转换Web到Tex的程序。默认是“weave”; CWEAVE:转换C Web到Tex的程序。默认是“cweave”; TANGLE:转换Web到Pascal语言的程序。默认是“tangle”; CTANGLE:转换C Web到C语言的程序。默认是“ctangle”; RM:文件删除程序,默认为:rm –f.
159
预定义变量(续) ARFLAGS:传给归档维护程序的参数,默认rv ASFLAGS:传给汇编程序的参数,无默认值
CFLAGS:传给C编译器的参数,无默认值 CPPFLAGS:传给C预处理器的参数,无默认值 LDFLAGS:传给链接器的参数,无默认值 ……
160
预定义变量举例: hello : a.c b.c $(CC) -o hello a.c b.c all : $(RM) hello.exe
161
四、MakeFile的执行 目录搜寻 make的嵌套执行 Makefile包含 条件执行 Makefile的命令行选项
162
1. 目录搜寻 文件根据类别存储于不同目录下,此时使用makefile要指定搜索文件的路径,有两种方法: 一般性搜索:特殊变量VPATH
VPATH = dir1:dir2:… 选择性搜索:关键字vpath vpath pattern directories:某类文件在某路径 vpath pattern:取消某类文件的搜索路径 vpath:取消所有文件的搜索路径
163
目录搜寻(续) 在makefile中使用VPATH指定路径 在makefile中使用vpath指定路径 特殊变量 关键字
VPATH = ./path1 dir:dir.c gcc ./path1/dir.c -o dir 特殊变量 vpath %.h ./head dir:dir.c gcc -I ./head dir.c -o dir 关键字
164
2. make的嵌套执行 make嵌套的含义 总控makefile 总控 makefile Makefile 源文件1 源文件2 源文件3
在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。
165
make嵌套执行举例 目录结构: 总控makefile: embedmake: makefile subdir1:makefile
.PHONY: both both: a b c a: gcc a.c -o a b: cd subdir1;make c: cd subdir2;make 定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。
166
make嵌套执行举例(续) subdir1/makefile: subdir2/makefile b:b.c gcc b.c -o b
c:c.c gcc c.c -o c
167
3. Makefile包含 使用关键字“include”可以再一个Makefile中包含其他的Makefile文件,这和C语言对头文件的包含方式一致。格式如下: include FILENAMES 通常指示符“include”用在以下场合: 有多个不同的程序,由不同目录下的几个独立的Makefile来描述其建立规则; 当根据源文件自动产生依赖文件时,可以将自动产生的依赖关系保存在另一个文件中,主Makefile使用指示符“include”包含这些文件。
168
4. 条件执行 使用条件判断,可以让make根据运行时的不同情况执行不同的分支。 CC=g++ objects=test.c
libs_for_gcc=-lgnu normal_libs= foo:$(objects) ifeq ($(CC),gcc) $(CC) -o foo $(objects) $(libs_for_gcc) else $(CC) -o foo $(objects) $(normal_libs) endif ifeq后面要有个空格
169
条件执行的语法 语法: 四种用于测试不通条件的关键字:ifeq、ifneq、ifdef、ifndef 可选
conditional-directive text-if-true else text-if false endif 四种用于测试不通条件的关键字:ifeq、ifneq、ifdef、ifndef 可选 条件指示
170
5. Makefile的命令行选项 只检查流程,不执行:-n 只更新目标文件访问时间,不重新编译:-t 认为所有目标都需要重新编译:-B
指定makefile文件的目录:-C 环境变量中的值覆盖文件中的值:-e
171
Makefile的命令行选项(续) 输出makefile中的所有信息:-p 禁止make使用任何隐含规则:-r
命令运行时不显示任何输出:-s 输出make的版本信息:-v 输出运行make前后的信息:-w
172
五、make内嵌函数 函数调用语法 文本处理函数 文件名处理函数 foreach函数 if函数 origin函数 shell函数
173
1. 函数调用语法 GNU make支持的函数语法如下 $(FUNCTION ARGUMENTS)
174
2. 文本处理函数 字符串替换函数:subst 模式字符串替换函数:patsubst
$(subst ee,EE,feet on the street) 返回:fEEt on the strEEt $(patsubst %.c,%.o, x.c.c bar.o) 返回:a.o b.o c.o d.o
175
文本处理函数(续) 去空格函数:strip 查找字符串函数:findstring STR = a b c $(strip $(STR))
$(findstring a, a b c) 返回:a $(findstring a, b c) 返回空 Strip,去掉字串开头和结尾的空格
176
文本处理函数(续) 过滤函数:filter 反过滤函数:filter-out
$(filter %.c %.s, a.c b.c b.s u.h) 返回:a.c b.c b.s $(filter-out %.c %.s, a.c b.c b.s u.h) 返回:u.h
177
文本处理函数(续) 排序函数:sort (升序) 取单词函数:word $(sort foo bar lose)
返回:bar foo lose $(word 2, foo bar baz) 返回:bar
178
文本处理函数(续) 取单词串函数:wordlist 单词个数统计函数:words
$(wordlist 2, 4, bar foo lose hi) 返回:foo lose hi $(words foo bar baz) 返回:3
179
文本处理函数(续) 取首单词串函数:firstword $(firstword foo bar) 返回:foo
180
综合举例: 综合举例: patsubst:模式字符串替换 subst:字符串替换 override CFLAGS +=
$(patsubst %, -I%,$(subst :, ,$(VPATH))) 如VPATH为src:../headers,则函数返回值为: ?? patsubst:模式字符串替换 subst:字符串替换
181
综合举例:(续) 综合举例: 1。 src ../headers 2。-Isrc –I../headers
override CFLAGS += $(patsubst %, -I%,$(subst :, ,$(VPATH))) 如VPATH为src:../headers,则函数返回值为: -Isrc –I../headers,恰为gcc搜索头文件的路径。 1。 src ../headers 2。-Isrc –I../headers
182
3. 文件名处理函数 取目录函数:dir 取文件名函数:notdir $(dir src/foo.c hacks) 返回:src/ ./
$(notdir src/foo.c hacks) 返回:foo.c hacks
183
文件名处理函数(续) 取后缀函数:suffix 取前缀函数:basename
$(suffix src/foo.c usr/bar.s hacks) 返回:.c .s $(basename src/foo.c hacks.s) 返回:src/foo hacks
184
文件名处理函数(续) 加后缀函数:addsuffix 加前缀函数:addprefix $(addsuffix .c, foo bar)
返回:foo.c bar.c $(addprefix src/, foo hacks) 返回:src/foo src/hacks
185
文件名处理函数(续) 单词连接函数:join $(join a b c, .c .o) 返回:a.c b.o c
Join list1,list2 把list2中的单词对应加到list1后面,如果list1的单词个数多,list1多出来的单词保持原样,如果list2的多,多出来的被复制到list2里面
186
4. foreach函数 格式: $(foreach VAR,LIST,TEST)
功能:把LIST中的单词逐一取出,送入VAR指定的变量中,然后再执行TEST表达式。每执行一次TEST返回一个字串,所有字串用空格连接起来就是函数的返回值。 names:=a b c d $(foreach n, $(names), $(n).o) 返回:a.o b.o c.o d.o
187
5. if函数 格式: $(if <condition>,<then>,<else>)
SUBDIR += $(if $(SRC_DIR), $(SRC_DIR), /home/src)
188
6. origin函数 功能:说明变量的来源,包括: undefined:未定义; default:默认定义,如CC;
environment:系统环境变量,make没有使用命令行选项"-e"; environment override:系统环境变量,make使用命令行选项"-e"; file:文件中定义; command line:命令行定义; override:用override指示符重新定义; automatic:自动化变量;
189
origin函数(续) 功能:说明变量的来源,包括: undefined:未定义 default:默认定义,如CC
environment:环境变量 file:文件中定义 command line:命令行定义 override:用override指示符重新定义 automatic:自动化变量 bletch=asdf ifdef bletch ifeq "$(origin bletch)" "environment" bletch=cut endif all: @echo $(bletch)
190
7. shell函数 功能:运行shell命令。参数是操作系统shell的命令,返回shell命令的输出。
contents := $(shell cat foo)
191
六、make的常见错误信息 make执行过程中所产生的错误并都是致命的,特别是命令行之前存在“-”、或者make使用“-k”选项执行时。make执行过程的致命错误都带前缀字符串“***”。 错误信息都有前缀,一种是执行程序名作为错误前缀,通常是“make”;另外一种是当Makefile本身存在语法错误无法被make解析并执行时,前缀包含Makefile文件名和出现错误的行号。
192
make的常见错误信息举例 不可识别行,make在读取Makefile过程中不能解析其中内容。make不能发现一个合法分隔符。
missing seperator. Stop 不可识别行,make在读取Makefile过程中不能解析其中内容。make不能发现一个合法分隔符。 以[Tab]字符开始,但不是一个合法的命令行 command commence before first target. Stop missing rule before commands. Stop
193
make的常见错误信息举例(续) 无法为重建目标“XXX”找到合适的规则,报考明确规则和隐含规则。
No rule to make target 'XXX' 无法为重建目标“XXX”找到合适的规则,报考明确规则和隐含规则。 对同一个目标“XXX”存在一个以上的重建命令。 warning: overriding commands for target 'XXX' warning: ignoring old commands for target 'XXX'
194
make的常见错误信息举例(续) 变量或者函数引用语法不正确,没有使用完整的括号。 函数“XXX”引用时参数数目不正确,函数缺少参数。 ……
Unterminated variable reference. Stop. 变量或者函数引用语法不正确,没有使用完整的括号。 函数“XXX”引用时参数数目不正确,函数缺少参数。 …… insufficient arguments to function 'XXX'. Stop
195
源码包配置工具 ★ ★ ★ GNU autoconf简介 自动生成makefile文件实例
196
一、GNU autoconf简介 Autoconf是一个shell脚本工具,用于生成可以自动地配置软件源代码包以适应多种Unix类系统的。由Autoconf生成的配置脚本在运行的时候与Autoconf是无关的,就是说配置脚本的用户并不需要拥有Autoconf 由Autoconf生成的配置脚本在运行的时候不需要用户的手工干预;通常它们甚至不需要通过给出参数以确定系统的类型。
197
autoconf功能 autoconf所生成的makefile除了可以做到程序的编译、链接外,还把如何产生程序文件(手册、info文件等)的动作和把源程序打包以供发布的动作都考虑进去,生成的makefile符合标准格式 有make clean、make install等
198
使用环境 使用autoconf前,系统必须安装了如下软件 GNU automake GNU autoconf GNU m4 perl
GNU libtool 安装红帽子linux时,上述工具都可以选则安装
199
二、自动生成makefile文件实例 建立目录: 新建hello.c文件 mkdir /devel/hello
cd /devel/hello 新建hello.c文件 vi hello.c 有make clean、make install等 #include <stdio.h> main() { printf(“hello world.\n”); }
200
自动生成makefile文件(续) 运行命令 修改文件configure.scan后缀为in autoscan
编辑configure.in文件如下 AC_PREREQ宏声明本文件要求的autoconf版本,如本例使用的版本2.59。 · AC_INIT宏用来定义软件的名称和版本等信息,在本例中省略了BUG-REPORT-ADDRESS,一般为作者的 。 · AM_INIT_AUTOMAKE是笔者另加的,它是automake所必备的宏,也同前面一样,PACKAGE是所要产生软件套件的名称,VERSION是版本编号。 · AC_CONFIG_SRCDIR宏用来侦测所指定的源码文件是否存在,来确定源码目录的有 效性。在此处为当前目录下的hello.c。 · AC_CONFIG_HEADER宏用于生成config.h文件,以便autoheader使用。 · AC_CONFIG_FILES宏用于生成相应的Makefile文件。 AC_PROG_CC:检查c编译器 AC_INIT(hello.c) AM_INIT_AUTOMAKE(hello, 1.0) AC_PROG_CC AC_OUTPUT(makefile)
201
自动生成makefile文件(续) 运行命令 编辑文件:makefile.am,内容如下: aclocal autoconf
生成了configure文件 编辑文件:makefile.am,内容如下: foreign:软件版权级别检查、foreign、gnu、gnits Makefile.am中 AUTOMAKE_OPTIONS=xxx xxx为gnu,foreign,和gnits。指用什么风格的工程,如果是gnu 则必须要自己写AUTHOR,NEWS等文件;一般用foreign,在automake时会给你照搬一套默 认的AUTHOR,NEWS等文件。 AUTOMAKE_OPTIONS=foreign bin_PROGRAMS=hello hello_SOURCES=hello.c
202
自动生成makefile文件(续) 运行命令 automake --add-missing 生成了makefile.in文件
./configure 生成makefile文件 make 生成了可执行文件hello ./hello --add-missing:告诉automake顺便加入包装一个软件包所必要的其他文件
203
自动生成makefile文件(续) automake生成标准makefile文件 运行命令 make all:编译可执行文件
make clean:清除可执行文件和.o文件 make distclean:清除可执行文件、.o文件和makefile文件 make install:安装可执行文件到系统标准目录 make dist:打包源文件及相关文件 有make clean、make install等 运用autoconf和automake的最大好处是,你的程序以源程序方式发布后,其它所有人只需要依次输入 ./configure make make install 命令就可以把你的程序安装在自己的电脑上运行了。所有符合GNU标准的UNIX/Linux都不需要再修改Makefile里的任何字符。
204
makefile生成流程 编辑hello.c configure.scan configure.in autoscan aclocal
autoconf configure 编辑makefile.am makefile.in automake ./configure makefile 修改 hello make 宏 hello-1.0.tar.gz make dist 有make clean、make install等
205
版本控制工具 版本控制工具是指记录、跟踪和管理代码文件变动的自动过程。典型的版本控制系统有: 修订控制系统 并发版本系统 工具链总结
★ ★ ★ 版本控制工具是指记录、跟踪和管理代码文件变动的自动过程。典型的版本控制系统有: RCS:Revision Control System 修订控制系统 CVS:Concurrent Version System 并发版本系统 SVN:Subversion 工具链总结
206
一、修订控制系统RCS 版本控制是指跟踪和管理代码文件变化的自动过程。 版本控制的意义 保持完整的版本历史记录信息 便于跟踪对比不同版本
防止代码损失或者被他人误修改 ~/exp/rcs
207
1. RCS 术语 RCS File:在RCS目录下的源码库文件,由RCS管理,通过RCS命令创建。它包含了某个文件对象的所有版本。文件名后缀通常为.v。 Working file:从RCS源码库中检索到的一个或多个文件。被放到当前目录下并可以被编辑。 Lock:锁定检出文件,以编辑为目的取出的源码文件为可写状态,同时使其他人无法再取出编辑此文件。 Revision:源文件的版本号,用x.y标识。
208
2. 基本RCS操作 ci命令:检入源代码(检入时输入此版本描述信息) co命令:检出源码 不带参数检入后立刻删除源码
-l 检入后立刻检出锁定的源码 -u 检入后立刻检出不锁定的源码 -r 把当前源码指定版本号检入 co命令:检出源码 -l 表示锁定源码,可以编辑 -r 检出某个特定版本的源码,如无此参数则检出最新版源码 -f 强制检出,覆盖掉当前目录现存的源码文件
209
基本RCS操作(续) ci命令:检入源代码(检入时输入此版本描述信息) co命令:检出源码 不带参数检入后立刻删除源码
-l 检入后立刻检出锁定的源码 -u 检入后立刻检出不锁定的源码 -r 把当前源码指定版本号检入 co命令:检出源码 -l 表示锁定源码,可以编辑 -r 检出某个特定版本的源码,如无此参数则检出最新版源码 -f 强制检出,覆盖掉当前目录现存的源码文件 创建rcs文件: ci a.c 检出: co –l a.c 检入: ci a.c ci –l a.c ci –u a.c ci –r2 a.c 检出: co –r1.1 a.c
210
3. RCS关键字 RCS关键字是一些类似于宏的记号,可以用来在源码、目标文件、二进制可执行文件中插入和维护标识信息。
RCS关键字的形式为:$keyword$,当一个包含了关键字的文件被检出时扩展为: $keyword:value$ 常用关键字: $Id$:扩展为“文件名、修订号、日期、时间、作者、状态、锁定文件者的登录名” $Log$:日志
211
RCS关键字(续) 常用关键字: $Id$:扩展为“文件名、修订号、日期、时间、作者、状态、锁定文件者的登录名” $Log$:日志
/* a.c * $Id$ */ 先检入,再显示,则a.c中的$Id$已经被扩展
212
RCS关键字(续) $Author$:检入该版本的用户登录名 $Date$:检入时间日期 $Header$:检入RCS文件的全路径名
$Locker$:锁定该版本的使用者的登录名 $Name$:检入该版本的符号名 $RCSfile$:检入不包含路径的RCS文件名 $Revision$:检入版本号 $Source$:检入RCS全路径名 $State$:检入该版本的状态
213
4. RCS命令 ident命令:能够在所有类型的文件中定位并显示RCS关键字,使开发人员在程序中嵌入附加信息
在程序中添加静态全局数组,然后保存退出 static char a[]=“$Id$\n”; ci –l a.c cat a.c gcc a.c ident a.out
214
找出RCS文件间的不同 rcsdiff命令:此命令调用diff命令找出不同版本源码的不同。 检出并修改当前文件
rcsdiff a.c //比较工作文件与RCS中最新文件的区别 rcsdiff –r1.1 a.c //比较1.1版与工作文件的区别 rcsdiff –r1.1 –r1.2 a.c //比较1.1版与1.2版的区别
215
查看RCS日志消息 rlog命令:此命令可以打印文件存储在RCS源代码库中的日志消息和其他消息。
216
并发版本系统CVS CVS是最流行的开放源代码版本控制系统。它既可以用于单独一台计算机、一个局域网,又可以利用其客户机/服务器的体系结构在internet上使用。 可以处理分布式项目。 多个程序员可以同时在一个文件上工作,CVS试图把几个开发人员的工作合在一起。
217
开发工具链关系图 有make clean、make install等
Similar presentations