VxWorks操作系统MakeFile
- 格式:doc
- 大小:54.50 KB
- 文档页数:7
vxWorks开发环境构建步骤及开发资源技创科技(Technique Innovator Inc.) 一、Tornado集成开发环境构成Tornado是集成开发环境的名称,主要由帮助及参考文档、操作系统vxWorks、开发工具(编译器、调试器、编辑器、target server等,据统计,挂接在Tornado下的可选工具和第三方有数百个)等三部分构成,分别对应三个目录:i.docs/ 所有文档都集中在该目录中, books.html是根索引,入门级开发请务必查看以下文档:●如果要熟悉使用界面及Tornado使用、创建工程,请参考:Tornado用户手册及Tornado集成开发环境的HELP->content调出来的联机帮助文档;●vxWorks的编译是使用标准GNU Makefile来编译连接的,要了解Makefile语法请参考”GNU Make”;具体的Make rules存放在target/h/make/目录下。
●如何调试?请参考“Debugging with GDB”●vxWorks构成、特点及kernel,编程等:请参考:VxWorks Programmer's Guide,系统调用、库函数接口标准等,请参考:VxWorks Reference Manual●有关BSP(board support package)的构成、初始化、底层驱动等,请参考“BSPReference”●网络应用编程指南(socket编程): “VxWorks Network Programmer's Guide”ii.host/ 该部分存放主机端(开发机)的工具,如Tornado.exe,make.exe,编译器、调试器及TCL命令解释器等等,开发中要用到的工具都存放在目录host\x86-win32\bin 下,部分命令是.exe文件,部分是.bat的,其中有torvars.bat文件,是设置Tornado集成开发环境的环境变量用的,在使用其他工具前,要运行它设置正确的环境变量及可执行文件搜索路径。
VxWork介绍及编程一.嵌入式操作系统VxWorks简介VxWorks操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统(RTOS),是嵌入式开发环境的关键组成部分。
良好的持续发展能力、高性能的内核以及友好的用户开发环境,在嵌入式实时操作系统领域占据一席之地。
它以其良好的可靠性和卓越的实时性被广泛地应用在通信、军事、航空、航天等高精尖技术及实时性要求极高的领域中,如卫星通讯、军事演习、弹道制导、飞机导航等。
在美国的 F-16、FA-18 战斗机、B-2 隐形轰炸机和爱国者导弹上,甚至连1997年4月在火星表面登陆的火星探测器上也使用到了VxWorks。
WindRiver公司网址实时操作系统和分时操作系统的区别从操作系统能否满足实时性要求来区分,可把操作系统分成分时操作系统和实时操作系统。
分时操作系统按照相等的时间片调度进程轮流运行,分时操作系统由调度程序自动计算进程的优先级,而不是由用户控制进程的优先级。
这样的系统无法实时响应外部异步事件。
实时操作系统能够在限定的时间内执行完所规定的功能,并能在限定的时间内对外部的异步事件作出响应。
分时系统主要应用于科学计算和一般实时性要求不高的场合。
实时性系统主要应用于过程控制、数据采集、通信、多媒体信息处理等对时间敏感的场合。
VxWorks的特点∙可靠性操作系统的用户希望在一个工作稳定,可以信赖的环境中工作,所以操作系统的可靠性是用户首先要考虑的问题。
而稳定、可靠一直是VxWorks的一个突出优点。
自从对中国的销售解禁以来,VxWorks以其良好的可靠性在中国赢得了越来越多的用户。
∙实时性实时性是指能够在限定时间内执行完规定的功能并对外部的异步事件作出响应的能力。
实时性的强弱是以完成规定功能和作出响应时间的长短来衡量的。
VxWorks 的实时性做得非常好,其系统本身的开销很小,进程调度、进程间通信、中断处理等系统公用程序精练而有效,它们造成的延迟很短。
VxWorks操作系统简介VxWorks操作系统简介VxWorks操作系统是一种广泛应用于嵌入式系统开发的实时操作系统。
本文将详细介绍VxWorks操作系统的架构、特性、应用领域以及相关的法律名词注释。
一、VxWorks操作系统架构VxWorks操作系统采用分层架构,包括内核层、中间层以及外围应用层。
具体架构如下:⒈内核层:提供底层的操作系统服务,包括任务管理、内存管理、中断处理、设备驱动等。
内核层使用高度模块化的设计,可以根据需求选择性地加载不同的内核服务。
⒉中间层:提供更高层次的功能,如文件系统、网络协议栈、图形用户界面等。
中间层通过对内核接口的封装,提供更便捷的应用开发接口。
⒊外围应用层:包括用户应用程序和系统管理工具。
用户应用程序可以基于VxWorks操作系统的开发环境进行开发,并且通过内核和中间层提供的接口与系统进行交互。
二、VxWorks操作系统特性VxWorks操作系统有以下特点:⒈实时性:VxWorks操作系统被广泛应用于实时系统开发,具有快速响应、低延迟和可预测性等特性。
它使用了优化的调度算法和中断处理机制,确保系统对实时任务的及时响应。
⒉可靠性:VxWorks操作系统采用了可靠性设计和故障恢复机制,例如内存保护、任务隔离和异常处理等。
这些机制可最大限度地减少系统崩溃和错误的影响。
⒊可扩展性:VxWorks操作系统具有高度可扩展性,可以根据应用的需要进行灵活配置。
开发人员可以根据系统需求选择性地加载所需的内核服务和中间层模块,以达到最佳性能和资源利用。
⒋安全性:VxWorks操作系统提供了多层次的安全机制,包括权限控制、数据加密和访问控制等。
这些机制可以在网络和物理环境中保护系统不受未经授权的访问和攻击。
三、VxWorks操作系统的应用领域VxWorks操作系统在各行各业都有广泛的应用,特别适用于对实时性和可靠性要求较高的领域。
以下是一些典型的应用领域:⒈工业自动化:VxWorks操作系统被广泛用于控制系统和工业领域,如汽车制造、航空航天和机械制造等。
Vxworks虚拟机环境搭建一、tornado的安装大部分地方默认,部分要注意的地方如下:需要的安装包:[风河VxWorks].WINDRIVER.TORNADO.V2.2.FOR.PENTIUM.CD1.ISO[风河VxWorks].WINDRIVER.TORNADO.V2.2.FOR.PENTIUM.CD2.ISO 虚拟光驱工具:任意DTLite4356-0091.zip (必须通过虚拟光驱来安装)注册序列号:sn_all.txt 见第四条四、WINDRIVER.TORNADO.V2.2.FOR.PENTIUM软件大小:328M安装方法:1)首先安装虚拟光驱软件DAEMON3.22)分别MOUNT 文件夹CD1和CD2下后缀名为BIN的文件3)自动安装后,输入序列号如下:CD1: cAQ8i-qP2I&-g$vT6-3&gjw-bEk9gCD2: A7KbB-Rd37p-hNFBE-kzgaT-b9gpC4)拷贝CRACK目录下的license.dat 到c:\flexlm 目录,如果没有这个目录,自己建一个。
设置如下环境变量:LM_LICENSE_FILE = c:\flexlm\license.dat按装完tornado后,首先要解压vxworks_end.exe文件三次出现target文件夹,将target\h\drv\end\ln97xEnd.h和target\src\drv\end\ln97xEnd.c、Makefile 覆盖对应的问件。
不要全部覆盖。
(其他的config\pc486.972、man文件夹都不要,太旧了。
注意做好整个tornado2.2\target的备份)二安装vxworks:总的过程分为以下几步:1、编译网卡驱动单击windows系统的开始菜单,在“运行”里输入“cmd”打开控制台程序。
进入:D:\tornado2.2\host\x86-win32\bin\运行:torVars.bat;进入:cd D:\tornado2.2\target\src\drv\end目录运行:make CPU=PENTIUM tool=gnu ln97xend.o (这一步会出现重复提示,不要管它,它自己会结束。
版权说明本文件中出现的任何文字叙述、文件格式、插图、照片、方法、过程等内容,除另有特别注明,版权均属本人所有。
----WangYiwei由于我正在学习“嵌入式系统原理与应用——基于ARM微处理器和Linux操作系统”这门课程,网络上找不到课后习题答案。
因此本人通过认真地做习题,不懂百度,查阅相关书籍等途径,整理了一份答案,可供各位参考。
注意:答案并不一定完全正确,有异议的答案欢迎提出来大家一起探讨。
练习题P141.选择题(1)A说明:嵌入式系统的发展趋势表现在以下几方面:1.产品种类不断丰富,应用范围不断普及2.产品性能不断提高3.产品功耗不断降低,体积不断缩小4.网络化、智能化程度不断提高5.软件成为影响价格的主要因素(2)D说明:常见的嵌入式操作系统: VxWorks,Windows CE、uC/OS-II和嵌入式Linux。
(3)A说明:VxWorks是美国WindRiver公司于1983年开发的一种32位嵌入式实时操作系统。
2.填空题(1)嵌入式计算机(2)微处理器外围电路外部设备(3)板级支持包实时操作系统应用编程接口应用程序(4)嵌入式处理器微控制器数字信号处理器3.简答题(1)简述嵌入式系统的定义和特点答:定义:以应用为中心,以计算机技术为基础,软硬件可裁剪,应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。
特点:专用性强、实时性好、可裁剪性好、可靠性高和功耗低等。
(2)简述计算机系统的发展历程第一阶段大致在20世纪70年代前后,可以看成是嵌入式系统的萌芽阶段;第二阶段是以嵌入式微处理器为基础,以简单操作系统为核心的嵌入式系统;第三阶段是以嵌入式操作系统为标志的嵌入式系统,也是嵌入式应用开始普及的阶段;第四阶段是以基于Internet为标志的嵌入式系统,这是一个正在迅速发展的阶段。
(3)简述MCU和DSP的区别MCU是微控制器,DSP是数字信号处理器。
MCU相当于小型的电脑,内部集成的CPU、ROM、RAM、I/O总线,所以集成度高是它的特点。
vmware下vxworks6.6安装⽂档VmWare下跑VxWorks 6.6 教程1、先期准备a)开发环境VxWorks 6.6 for pentium 安装包VmWare WorkStation 7.1.2b)⼯具虚拟软驱RamDiskNT 1.52、虚拟机设置2.1安装虚拟软驱(供虚拟硬盘做DOS,和初期拷贝⽂件使⽤)保证红圈配置正确后,点击START运⾏,随后重启电脑,虚拟软驱A应该就出现了2.2 新建虚拟机File->New->Virtual Machine->Custom->New-Workstation 5->other(other)->指定虚拟机名和保存位置->选单处理器->内存128MB对应VxWorks⾜以->使⽤桥接⽹络(注意:⼀定要使⽤⽹桥连接)->IO适配器选BusLogic->创建虚拟硬盘->类型选择IDE(Recommended) ->磁盘空间设置0.25GB(256MB)⾜以,并勾上⽴刻分配磁盘空间->最终完成虚拟机配置3、FTP引导的bootrom制作修改BSP⾸先,我们复制⼀份纯净未做修改的D:\WindRiver-GPPVE-3.6-IA-Eval\vxworks-6.6\target\config\下的pcPentium3⽂件夹,改名为Pentium3(名称随便你起),再将该改名后⽂件夹放到config⽬录下,最好不要带空格或什么特殊符号。
我们要修改编译bootrom、VxWorks的配置头⽂件Config.h中定义的⼀些参数,使编译出来的系统引导程序bootrom和VxWorks 的映象符合我们的要求;3.1修改config.h1)定位到⽬录D:\WindRiver-GPPVE-3.6-IA-Eval\vxworks-6.6\target\config\Pentium3并打开该⽬录下Config.h⽂件;2)定位到”INCLUDE_CPU_PROBE”,更改成如下:3)查找到定义DEFAULT_BOOT_LINE宏的地⽅,修改预处理条件CPU = = PENTIUM3分⽀下的定义如下:"lnPci(0,0)host: vxWorks h=172.18.101.121 e=172.18.101.124:ffffff00 u=target pw=target tn=target"这⾥的host地址得修改成你的主机地址,target地址只需要在同⼀个⽹段内就可以了4)定位到INCLUDE_ATA,在前⾯⼀句添加#undef INCLUDE_FD,如下所⽰:5)定位到/* Network driver options: VxBus drivers */作如下修改:6)定位到INCLUDE_LN_97X_END,将其定义上,修改如下:7)定位到INCLUDE_PC_CONSOLE,将其设成定义的,如下所⽰8)定位到#if (SYS_WARM_TYPE == SYS_WARM_BIOS),修改如下:3.2修改 configNet.h1)定位到/* max number of END ipAttachments we can have */ 在上⾯添加如下内容:/* Am79C97x (lnPci) driver defines */#ifdef INCLUDE_LN_97X_END#define LN_97X_LOAD_FUNC sysLn97xEndLoad#define LN_97X_BUFF_LOAN TRUE#define LN_97X_LOAD_STR ""IMPORT END_OBJ * LN_97X_LOAD_FUNC (char *, void *);#endif /* INCLUDE_LN_97X_END */截图如下:2)定位到/* Atheros AR521X WLAN Support */在上⾯添加如下内容:#ifdef INCLUDE_LN_97X_END{0, LN_97X_LOAD_FUNC, LN_97X_LOAD_STR, LN_97X_BUFF_LOAN,NULL, FALSE},#endif /* INCLUDE_LN_97X_END */截图如下:3.3 将D:\WindRiver-GPPVE-3.6-IA-Eval\vxworks-6.6\target\src\drv\end⽬录下的ln97xEnd.c拷贝进你的Pentium3⽬录下,并作如下修改:定位到 do {; /* poll for suspend mode entry */}while ((csrLockedRead (pDrvCtrl, CSR(5)) & CSR5_SPND) == 0);添加宏如下所⽰:3.4将5.5的Sysln97xEnd.c移植到你的Pentium3⽬录下,并作如下修改:1)定位到/* map a 4Kb 32-bit non-prefetchable memory address decoder */ 添加如下宏2)定位到ln97xPciResources[ln97XUnits].bar[0] = ioBase;添加如下宏:3.5修改makefile⽂件,作如下修改:1)定位到TOOL = diab将diab修改为gnu2) 定位到EXTRA_DEFINE,作如下修改:3.6修改sysnet.c1)定位到# include "sysUltraEnd.c"添加内容如下:2)定位到LOCAL VEND_ID_DESC vendorIdEnet [] = 修改内容如下:3.7 将安装⽬录下的wrenv.exe拷贝进Pentium3⽬录下,删除如下⽂件:bootrom;bin;bootrom.pxe;vxWorks;vxWorks.st;vxWorks.sym并添加两个⽂件如下:1)torVars.bat内容如下:wrenv -p vxworks-6.62)vxcopy.bat内容如下:objcopypentium -O binary --gap-fill=0 %1 %23.8 编译BootRom往PATH环境变量添加路径,如下所⽰D:\WindRiver-GPPVE-3.6-IA-Eval\vxworks-6.6\host\x86-win32\bin;D:\WindRiver-GPPVE-3.6-IA-Eval\workbench-3.0\x86-win32\bin打开Wind River Workbench 3.0,File->New ->VxWorks BootLloader / BSP Project,填写⼯程名,并选择我们的Pentium3⽂件夹,及⼯具gnu,然后编译:打开CMD,进去⽬录Pentium3⽂件夹,运⾏如下命令mkboot a: bootrom 注意:之间的空格,不然会出错3.9基于BSP创建VxWorks镜像打开Wind River Workbench 3.0,File->New->VxWorks Image Project,还是选择Pentium跟⼯具gnu,然后配置组件FTP Server组件,编译⼯程⽂件3.5开始配置FTP Server1)打开FTP Server->Security -> users/rights -> New User -> ⽤户名跟密码都是vxworks –>在Home Directory 填写你编译好的vxworks image所放置路径,然后选择Done 3.6关闭防⽕墙,设置Vmware从软盘启动,最后运⾏VmWare,然后就能够加载成功了。
第15卷 第2期2005年6月天 津 工 程 师 范 学 院 学 报JO URNAL O F T I ANJ I N UN I VERS I TY O F TECHNOLO GY AND ED UCAT I O N Vol .15 No .2Jun .2005嵌入式实时操作系统Vx W orks 下BSP 分析及Vx W orks 裁减3褚 哲,孟小锁(西安微电子技术研究所,陕西西安710075)摘 要:以Vx Works 操作系统为例,阐述了BSP 的概念、原理和系统启动流程,并在此基础上以某目标机为原型,着重叙述了Vx Works 的裁减方法。
关键词:Vx Works;板级支持包;映像中图分类号:TP316.2 文献标识码:A 文章编号:167321018(2005)022*******BSP ana lysis under em bedded rea l 2ti m e opera ti n gsystem Vx W orks and Vx W orks ′t a ilorCHU Zhe,ME NG Xiao 2suo(Xi ′an M icr oelectr onics Technol ogy I nstitute,Xi ′an 710054,China )Abstract :This article takes the Vx Works operating system as an examp le to illustrate the concep t,the p rincirle and the start 2up p r ocedure of BSP .Under this foundation it uses certain target machine as original for m to ex 2p lain the tail or method of Vx Works .Key words :Vx Works;board suppot package;i mage Vx Works 操作系统是美国W indR iver 公司推出的一种嵌入式强实时操作系统,自20世纪80年代问世以来,以其不断推出的升级版本、高性能内核以及友好的用户开发环境,在嵌入式实时操作系统领域逐渐占据一席之地,尤其以成功应用于火星探测车和爱国者导弹等高科技产品而声名鹊起,拥有较多的用户。
Makefile经典教程0 Makefile概述什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makefile还是要懂。
这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专业人士,你还是要了解HTML的标识的含义。
特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
因为,makefile关系到了整个工程的编译规则。
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。
可见,makefile都成为了一种在工程方面的编译方法。
现在讲述如何写makefile的文章比较少,这是我想写这篇文章的原因。
当然,不同产商的make各不相同,也有不同的语法,但其本质都是在“文件依赖性”上做文章,这里,我仅对GNU的make进行讲述,我的环境是RedHat Linux 8.0,make的版本是3.80。
必竟,这个make是应用最为广泛的,也是用得最多的。
而且其还是最遵循于IEEE 1003.2-1992 标准的(POSIX.2)。
在这篇文档中,将以C/C++的源码作为我们基础,所以必然涉及一些关于C/C++的编译的知识,相关于这方面的内容,还请各位查看相关的编译器的文档。
Makefile使⽤总结1. Makefile 简介Makefile 是和 make 命令⼀起配合使⽤的.很多⼤型项⽬的编译都是通过 Makefile 来组织的, 如果没有 Makefile, 那很多项⽬中各种库和代码之间的依赖关系不知会多复杂. Makefile的组织流程的能⼒如此之强, 不仅可以⽤来编译项⽬, 还可以⽤来组织我们平时的⼀些⽇常操作. 这个需要⼤家发挥⾃⼰的想象⼒.本篇博客是基于⽽整理的, 有些删减, 追加了⼀些⽰例.⾮常感谢 gunguymadman_cu 提供如此详尽的Makefile介绍, 这正是我⼀直寻找的Makefile中⽂⽂档.1.1 Makefile 主要的 5个部分 (显⽰规则, 隐晦规则, 变量定义, ⽂件指⽰, 注释)Makefile基本格式如下:target ... : prerequisites ...command......其中,target - ⽬标⽂件, 可以是 Object File, 也可以是可执⾏⽂件prerequisites - ⽣成 target 所需要的⽂件或者⽬标command - make需要执⾏的命令 (任意的shell命令), Makefile中的命令必须以 [tab] 开头1. 显⽰规则 :: 说明如何⽣成⼀个或多个⽬标⽂件(包括⽣成的⽂件, ⽂件的依赖⽂件, ⽣成的命令)2. 隐晦规则 :: make的⾃动推导功能所执⾏的规则3. 变量定义 :: Makefile中定义的变量4. ⽂件指⽰ :: Makefile中引⽤其他Makefile; 指定Makefile中有效部分; 定义⼀个多⾏命令5. 注释 :: Makefile只有⾏注释 "#", 如果要使⽤或者输出"#"字符, 需要进⾏转义, "\#"1.2 GNU make 的⼯作⽅式1. 读⼊主Makefile (主Makefile中可以引⽤其他Makefile)2. 读⼊被include的其他Makefile3. 初始化⽂件中的变量4. 推导隐晦规则, 并分析所有规则5. 为所有的⽬标⽂件创建依赖关系链6. 根据依赖关系, 决定哪些⽬标要重新⽣成7. 执⾏⽣成命令2. Makefile 初级语法2.1 Makefile 规则2.1.1 规则语法规则主要有2部分: 依赖关系和⽣成⽬标的⽅法.语法有以下2种:target ... : prerequisites ...command...或者target ... : prerequisites ; commandcommand...*注* command太长, 可以⽤ "\" 作为换⾏符2.1.2 规则中的通配符* :: 表⽰任意⼀个或多个字符:: 表⽰任意⼀个字符[...] :: ex. [abcd] 表⽰a,b,c,d中任意⼀个字符, [^abcd]表⽰除a,b,c,d以外的字符, [0-9]表⽰ 0~9中任意⼀个数字~ :: 表⽰⽤户的home⽬录2.1.3 路径搜索当⼀个Makefile中涉及到⼤量源⽂件时(这些源⽂件和Makefile极有可能不在同⼀个⽬录中),这时, 最好将源⽂件的路径明确在Makefile中, 便于编译时查找. Makefile中有个特殊的变量VPATH就是完成这个功能的.指定了VPATH之后, 如果当前⽬录中没有找到相应⽂件或依赖的⽂件, Makefile 回到VPATH指定的路径中再去查找.. VPATH使⽤⽅法:vpath <directories> :: 当前⽬录中找不到⽂件时, 就从<directories>中搜索vpath <pattern> <directories> :: 符合<pattern>格式的⽂件, 就从<directories>中搜索vpath <pattern> :: 清除符合<pattern>格式的⽂件搜索路径vpath :: 清除所有已经设置好的⽂件路径# ⽰例1 - 当前⽬录中找不到⽂件时, 按顺序从 src⽬录 ../parent-dir⽬录中查找⽂件VPATH src:../parent-dir# ⽰例2 - .h结尾的⽂件都从 ./header ⽬录中查找VPATH %.h ./header# ⽰例3 - 清除⽰例2中设置的规则VPATH %.h# ⽰例4 - 清除所有VPATH的设置VPATH2.2 Makefile 中的变量2.2.1 变量定义 ( = or := )OBJS = programA.o programB.oOBJS-ADD = $(OBJS) programC.o# 或者OBJS := programA.o programB.oOBJS-ADD := $(OBJS) programC.o其中 = 和 := 的区别在于, := 只能使⽤前⾯定义好的变量, = 可以使⽤后⾯定义的变量测试 =# Makefile内容OBJS2 = $(OBJS1) programC.oOBJS1 = programA.o programB.oall:@echo $(OBJS2)# bash中执⾏make, 可以看出虽然 OBJS1 是在 OBJS2 之后定义的, 但在 OBJS2中可以提前使⽤$ makeprogramA.o programB.o programC.o测试 :=# Makefile内容OBJS2 := $(OBJS1) programC.oOBJS1 := programA.o programB.oall:@echo $(OBJS2)# bash中执⾏make, 可以看出 OBJS2 中的 $(OBJS1) 为空$ makeprogramC.o2.2.2 变量替换# Makefile内容SRCS := programA.c programB.c programC.cOBJS := $(SRCS:%.c=%.o)all:@echo "SRCS: " $(SRCS)@echo "OBJS: " $(OBJS)# bash中运⾏make$ makeSRCS: programA.c programB.c programC.cOBJS: programA.o programB.o programC.o2.2.3 变量追加值 +=# Makefile内容SRCS := programA.c programB.c programC.cSRCS += programD.call:@echo "SRCS: " $(SRCS)# bash中运⾏make$ makeSRCS: programA.c programB.c programC.c programD.c2.2.4 变量覆盖 override作⽤是使 Makefile中定义的变量能够覆盖 make 命令参数中指定的变量语法:override <variable> = <value>override <variable> := <value>override <variable> += <value>下⾯通过⼀个例⼦体会 override 的作⽤:# Makefile内容 (没有⽤override)SRCS := programA.c programB.c programC.call:@echo "SRCS: " $(SRCS)# bash中运⾏make$ make SRCS=nothingSRCS: nothing################################################## Makefile内容 (⽤override)override SRCS := programA.c programB.c programC.call:@echo "SRCS: " $(SRCS)# bash中运⾏make$ make SRCS=nothingSRCS: programA.c programB.c programC.c2.2.5 ⽬标变量作⽤是使变量的作⽤域仅限于这个⽬标(target), ⽽不像之前例⼦中定义的变量, 对整个Makefile都有效.语法:<target ...> :: <variable-assignment><target ...> :: override <variable-assignment> (override作⽤参见变量覆盖的介绍)⽰例:# Makefile 内容SRCS := programA.c programB.c programC.ctarget1: TARGET1-SRCS := programD.ctarget1:@echo "SRCS: " $(SRCS)@echo "SRCS: " $(TARGET1-SRCS)target2:@echo "SRCS: " $(SRCS)@echo "SRCS: " $(TARGET1-SRCS)# bash中执⾏make$ make target1SRCS: programA.c programB.c programC.cSRCS: programD.c$ make target2 <-- target2中显⽰不了 $(TARGET1-SRCS)SRCS: programA.c programB.c programC.cSRCS:2.3 Makefile 命令前缀Makefile 中书写shell命令时可以加2种前缀 @ 和 -, 或者不⽤前缀.3种格式的shell命令区别如下:不⽤前缀 :: 输出执⾏的命令以及命令执⾏的结果, 出错的话停⽌执⾏前缀 @ :: 只输出命令执⾏的结果, 出错的话停⽌执⾏前缀 - :: 命令执⾏有错的话, 忽略错误, 继续执⾏⽰例:# Makefile 内容 (不⽤前缀)all:echo"没有前缀"cat this_file_not_existecho"错误之后的命令" <-- 这条命令不会被执⾏# bash中执⾏make$ makeecho"没有前缀" <-- 命令本⾝显⽰出来没有前缀 <-- 命令执⾏结果显⽰出来cat this_file_not_existcat: this_file_not_exist: No such file or directorymake: *** [all] Error 1############################################################ Makefile 内容 (前缀 @)all:@echo "没有前缀"@cat this_file_not_exist@echo "错误之后的命令" <-- 这条命令不会被执⾏# bash中执⾏make$ make没有前缀 <-- 只有命令执⾏的结果, 不显⽰命令本⾝cat: this_file_not_exist: No such file or directorymake: *** [all] Error 1############################################################ Makefile 内容 (前缀 -)all:-echo"没有前缀"-cat this_file_not_exist-echo"错误之后的命令" <-- 这条命令会被执⾏# bash中执⾏make$ makeecho"没有前缀" <-- 命令本⾝显⽰出来没有前缀 <-- 命令执⾏结果显⽰出来cat this_file_not_existcat: this_file_not_exist: No such file or directorymake: [all] Error 1 (ignored)echo"错误之后的命令" <-- 出错之后的命令也会显⽰错误之后的命令 <-- 出错之后的命令也会执⾏2.4 伪⽬标伪⽬标并不是⼀个"⽬标(target)", 不像真正的⽬标那样会⽣成⼀个⽬标⽂件.典型的伪⽬标是 Makefile 中⽤来清理编译过程中中间⽂件的 clean 伪⽬标, ⼀般格式如下: .PHONY: clean <-- 这句没有也⾏, 但是最好加上clean:-rm -f *.o2.5 引⽤其他的 Makefile语法: include <filename> (filename 可以包含通配符和路径)⽰例:# Makefile 内容all:@echo "主 Makefile begin"@make other-all@echo "主 Makefile end"include ./other/Makefile# ./other/Makefile 内容other-all:@echo "other makefile begin"@echo "other makefile end"# bash中执⾏make$ lltotal 20K-rw-r--r-- 1 wangyubin wangyubin 125 Sep 2316:13 Makefile-rw-r--r-- 1 wangyubin wangyubin 11K Sep 2316:15 <-- 这个⽂件不⽤管drwxr-xr-x 2 wangyubin wangyubin 4.0K Sep 2316:11 other$ ll other/total 4.0K-rw-r--r-- 1 wangyubin wangyubin 71 Sep 2316:11 Makefile$ make主 Makefile beginmake[1]: Entering directory `/path/to/test/makefile'other makefile beginother makefile endmake[1]: Leaving directory `/path/to/test/makefile'主 Makefile end2.6 查看C⽂件的依赖关系写 Makefile 的时候, 需要确定每个⽬标的依赖关系.GNU提供⼀个机制可以查看C代码⽂件依赖那些⽂件, 这样我们在写 Makefile ⽬标的时候就不⽤打开C源码来看其依赖那些⽂件了.⽐如, 下⾯命令显⽰内核源码中 virt/kvm/kvm_main.c 中的依赖关系$ cd virt/kvm/$ gcc -MM kvm_main.ckvm_main.o: kvm_main.c iodev.h coalesced_mmio.h async_pf.h <-- 这句就可以加到 Makefile 中作为编译 kvm_main.o 的依赖关系2.7 make 退出码Makefile的退出码有以下3种:0 :: 表⽰成功执⾏1 :: 表⽰make命令出现了错误2 :: 使⽤了 "-q" 选项, 并且make使得⼀些⽬标不需要更新2.8 指定 Makefile,指定特定⽬标默认执⾏ make 命令时, GNU make在当前⽬录下依次搜索下⾯3个⽂件 "GNUmakefile", "makefile", "Makefile",找到对应⽂件之后, 就开始执⾏此⽂件中的第⼀个⽬标(target). 如果找不到这3个⽂件就报错.⾮默认情况下, 可以在 make 命令中指定特定的 Makefile 和特定的⽬标.⽰例:# Makefile⽂件名改为 MyMake, 内容target1:@echo "target [1] begin"@echo "target [1] end"target2:@echo "target [2] begin"@echo "target [2] end"# bash 中执⾏make$ lsMakefile$ mv Makefile MyMake$ lsMyMake$ make <-- 找不到默认的 Makefilemake: *** No targets specified and no makefile found. Stop.$ make -f MyMake <-- 指定特定的Makefiletarget [1] begintarget [1] end$ make -f MyMake target2 <-- 指定特定的⽬标(target)target [2] begintarget [2] end2.9 make 参数介绍make 的参数有很多, 可以通过 make -h 去查看, 下⾯只介绍⼏个我认为⽐较有⽤的.参数含义--debug[=<options>]输出make的调试信息, options 可以是 a, b, v-j --jobs同时运⾏的命令的个数, 也就是多线程执⾏ Makefile-r --no-builtin-rules禁⽌使⽤任何隐含规则-R --no-builtin-variabes禁⽌使⽤任何作⽤于变量上的隐含规则-B --always-make假设所有⽬标都有更新, 即强制重编译2.10 Makefile 隐含规则这⾥只列⼀个和编译C相关的.编译C时,<n>.o 的⽬标会⾃动推导为 <n>.c# Makefile 中main : main.ogcc -o main main.o#会⾃动变为:main : main.ogcc -o main main.omain.o: main.c <-- main.o 这个⽬标是隐含⽣成的gcc -c main.c2.11 隐含规则中的命令变量和命令参数变量2.11.1 命令变量, 书写Makefile可以直接写 shell时⽤这些变量.下⾯只列出⼀些C相关的变量名含义RM rm -fAR arCC ccCXX g++⽰例:# Makefile 内容all:@echo $(RM)@echo $(AR)@echo $(CC)@echo $(CXX)# bash 中执⾏make, 显⽰各个变量的值$ makerm -farccg++2.11.2 命令参数变量变量名含义ARFLAGS AR命令的参数CFLAGS C语⾔编译器的参数CXXFLAGS C++语⾔编译器的参数⽰例: 下⾯以 CFLAGS 为例演⽰# test.c 内容#include <stdio.h>int main(int argc, char *argv[]){printf ("Hello Makefile\n");return 0;}# Makefile 内容test: test.o$(CC) -o test test.o# bash 中⽤make来测试$ lltotal 24K-rw-r--r-- 1 wangyubin wangyubin 69 Sep 2317:31 Makefile-rw-r--r-- 1 wangyubin wangyubin 14K Sep 2319:51 <-- 请忽略这个⽂件-rw-r--r-- 1 wangyubin wangyubin 392 Sep 2317:31 test.c$ makecc -c -o test.o test.ccc -o test test.o <-- 这个是⾃动推导的$ rm -f test test.o$ make CFLAGS=-Wall <-- 命令中加的编译器参数⾃动追加⼊下⾯的编译中了cc -Wall -c -o test.o test.ccc -o test test.o2.12 ⾃动变量Makefile 中很多时候通过⾃动变量来简化书写, 各个⾃动变量的含义如下:⾃动变量含义$@⽬标集合$%当⽬标是函数库⽂件时, 表⽰其中的⽬标⽂件名$<第⼀个依赖⽬标. 如果依赖⽬标是多个, 逐个表⽰依赖⽬标$?⽐⽬标新的依赖⽬标的集合$^所有依赖⽬标的集合, 会去除重复的依赖⽬标$+所有依赖⽬标的集合, 不会去除重复的依赖⽬标$*这个是GNU make特有的, 其它的make不⼀定⽀持3. Makefile ⾼级语法3.1 嵌套Makefile在 Makefile 初级语法中已经提到过引⽤其它 Makefile的⽅法. 这⾥有另⼀种写法, 并且可以向引⽤的其它 Makefile 传递参数.⽰例: (不传递参数, 只是调⽤⼦⽂件夹 other 中的Makefile)# Makefile 内容all:@echo "主 Makefile begin"@cd ./other && make@echo "主 Makefile end"# ./other/Makefile 内容other-all:@echo "other makefile begin"@echo "other makefile end"# bash中执⾏make$ lltotal 28K-rw-r--r-- 1 wangyubin wangyubin 104 Sep 2320:43 Makefile-rw-r--r-- 1 wangyubin wangyubin 17K Sep 2320:44 <-- 这个⽂件不⽤管drwxr-xr-x 2 wangyubin wangyubin 4.0K Sep 2320:42 other$ ll other/total 4.0K-rw-r--r-- 1 wangyubin wangyubin 71 Sep 2316:11 Makefile$ make主 Makefile beginmake[1]: Entering directory `/path/to/test/makefile/other'other makefile beginother makefile endmake[1]: Leaving directory `/path/to/test/makefile/other'主 Makefile end⽰例: (⽤export传递参数)# Makefile 内容export VALUE1 := export.c <-- ⽤了 export, 此变量能够传递到 ./other/Makefile 中VALUE2 := no-export.c <-- 此变量不能传递到 ./other/Makefile 中all:@echo "主 Makefile begin"@cd ./other && make@echo "主 Makefile end"# ./other/Makefile 内容other-all:@echo "other makefile begin"@echo "VALUE1: " $(VALUE1)@echo "VALUE2: " $(VALUE2)@echo "other makefile end"# bash中执⾏make$ make主 Makefile beginmake[1]: Entering directory `/path/to/test/makefile/other'other makefile beginVALUE1: export.c <-- VALUE1 传递成功VALUE2: <-- VALUE2 传递失败other makefile endmake[1]: Leaving directory `/path/to/test/makefile/other'主 Makefile end*补充* export 语法格式如下:export variable = valueexport variable := valueexport variable += value3.2 定义命令包命令包有点像是个函数, 将连续的相同的命令合成⼀条, 减少 Makefile 中的代码量, 便于以后维护.语法:define <command-name>command...endef⽰例:# Makefile 内容define run-hello-makefile@echo -n "Hello"@echo " Makefile!"@echo "这⾥可以执⾏多条 Shell 命令!"endefall:$(run-hello-makefile)# bash 中运⾏make$ makeHello Makefile!这⾥可以执⾏多条 Shell 命令!3.3 条件判断条件判断的关键字主要有 ifeq ifneq ifdef ifndef语法:<conditional-directive><text-if-true>endif# 或者<conditional-directive><text-if-true>else<text-if-false>endif⽰例: ifeq的例⼦, ifneq和ifeq的使⽤⽅法类似, 就是取反# Makefile 内容all:ifeq ("aa", "bb")@echo "equal"else@echo "not equal"endif# bash 中执⾏make$ makenot equal⽰例: ifdef的例⼦, ifndef和ifdef的使⽤⽅法类似, 就是取反# Makefile 内容SRCS := program.call:ifdef SRCS@echo $(SRCS)else@echo "no SRCS"# bash 中执⾏make$ makeprogram.c3.4 Makefile 中的函数Makefile 中⾃带了⼀些函数, 利⽤这些函数可以简化 Makefile 的编写.函数调⽤语法如下:$(<function> <arguments>)# 或者${<function> <arguments>}<function> 是函数名<arguments> 是函数参数3.4.1 字符串函数字符串替换函数: $(subst <from>,<to>,<text>)功能: 把字符串<text> 中的 <from> 替换为 <to>返回: 替换过的字符串# Makefile 内容all:@echo $(subst t,e,maktfilt) <-- 将t替换为e# bash 中执⾏make$ makemakefile模式字符串替换函数: $(patsubst <pattern>,<replacement>,<text>)功能: 查找<text>中的单词(单词以"空格", "tab", "换⾏"来分割) 是否符合 <pattern>, 符合的话, ⽤ <replacement> 替代.返回: 替换过的字符串# Makefile 内容all:@echo $(patsubst %.c,%.o,programA.c programB.c)# bash 中执⾏make$ makeprogramA.o programB.o去空格函数: $(strip <string>)功能: 去掉 <string> 字符串中开头和结尾的空字符返回: 被去掉空格的字符串值# Makefile 内容VAL := " aa bb cc "all:@echo "去除空格前: " $(VAL)@echo "去除空格后: " $(strip $(VAL))# bash 中执⾏make去除空格前: aa bb cc去除空格后: aa bb cc查找字符串函数: $(findstring <find>,<in>)功能: 在字符串 <in> 中查找 <find> 字符串返回: 如果找到, 返回 <find> 字符串, 否则返回空字符串# Makefile 内容VAL := " aa bb cc "all:@echo $(findstring aa,$(VAL))@echo $(findstring ab,$(VAL))# bash 中执⾏make$ makeaa过滤函数: $(filter <pattern...>,<text>)功能: 以 <pattern> 模式过滤字符串 <text>, *保留* 符合模式 <pattern> 的单词, 可以有多个模式返回: 符合模式 <pattern> 的字符串# Makefile 内容all:@echo $(filter %.o %.a,program.c program.o program.a)# bash 中执⾏make$ makeprogram.o program.a反过滤函数: $(filter-out <pattern...>,<text>)功能: 以 <pattern> 模式过滤字符串 <text>, *去除* 符合模式 <pattern> 的单词, 可以有多个模式返回: 不符合模式 <pattern> 的字符串# Makefile 内容all:@echo $(filter-out %.o %.a,program.c program.o program.a)# bash 中执⾏make$ makeprogram.c排序函数: $(sort <list>)功能: 给字符串 <list> 中的单词排序 (升序)返回: 排序后的字符串# Makefile 内容all:@echo $(sort bac abc acb cab)# bash 中执⾏make$ makeabc acb bac cab取单词函数: $(word <n>,<text>)功能: 取字符串 <text> 中的第<n>个单词 (n从1开始)返回: <text> 中的第<n>个单词, 如果<n> ⽐ <text> 中单词个数要⼤, 则返回空字符串# Makefile 内容all:@echo $(word 1,aa bb cc dd)@echo $(word 5,aa bb cc dd)@echo $(word 4,aa bb cc dd)# bash 中执⾏make$ makeaadd取单词串函数: $(wordlist <s>,<e>,<text>)功能: 从字符串<text>中取从<s>开始到<e>的单词串. <s>和<e>是⼀个数字.返回: 从<s>到<e>的字符串# Makefile 内容all:@echo $(wordlist 1,3,aa bb cc dd)@echo $(word 5,6,aa bb cc dd)@echo $(word 2,5,aa bb cc dd)# bash 中执⾏make$ makeaa bb ccbb单词个数统计函数: $(words <text>)功能: 统计字符串 <text> 中单词的个数返回: 单词个数# Makefile 内容all:@echo $(words aa bb cc dd)@echo $(words aabbccdd)@echo $(words )# bash 中执⾏make$ make41⾸单词函数: $(firstword <text>)功能: 取字符串 <text> 中的第⼀个单词返回: 字符串 <text> 中的第⼀个单词# Makefile 内容all:@echo $(firstword aa bb cc dd)@echo $(firstword aabbccdd)@echo $(firstword )# bash 中执⾏make$ makeaaaabbccdd3.4.2 ⽂件名函数取⽬录函数: $(dir <names...>)功能: 从⽂件名序列 <names> 中取出⽬录部分返回: ⽂件名序列 <names> 中的⽬录部分# Makefile 内容all:@echo $(dir /home/a.c ./bb.c ../c.c d.c)# bash 中执⾏make$ make/home/ ./ ../ ./取⽂件函数: $(notdir <names...>)功能: 从⽂件名序列 <names> 中取出⾮⽬录部分返回: ⽂件名序列 <names> 中的⾮⽬录部分# Makefile 内容all:@echo $(notdir /home/a.c ./bb.c ../c.c d.c)# bash 中执⾏make$ makea.c bb.cc.cd.c取后缀函数: $(suffix <names...>)功能: 从⽂件名序列 <names> 中取出各个⽂件名的后缀返回: ⽂件名序列 <names> 中各个⽂件名的后缀, 没有后缀则返回空字符串# Makefile 内容all:@echo $(suffix /home/a.c ./b.o ../c.a d)# bash 中执⾏make$ make.c .o .a取前缀函数: $(basename <names...>)功能: 从⽂件名序列 <names> 中取出各个⽂件名的前缀返回: ⽂件名序列 <names> 中各个⽂件名的前缀, 没有前缀则返回空字符串# Makefile 内容all:@echo $(basename /home/a.c ./b.o ../c.a /home/.d .e)# bash 中执⾏make$ make/home/a ./b ../c /home/加后缀函数: $(addsuffix <suffix>,<names...>)功能: 把后缀 <suffix> 加到 <names> 中的每个单词后⾯返回: 加过后缀的⽂件名序列# Makefile 内容all:@echo $(addsuffix .c,/home/a b ./c.o ../d.c)# bash 中执⾏make$ make/home/a.c b.c ./c.o.c ../d.c.c加前缀函数: $(addprefix <prefix>,<names...>)功能: 把前缀 <prefix> 加到 <names> 中的每个单词前⾯返回: 加过前缀的⽂件名序列# Makefile 内容all:@echo $(addprefix test_,/home/a.c b.c ./d.c)# bash 中执⾏make$ maketest_/home/a.c test_b.c test_./d.c连接函数: $(join <list1>,<list2>)功能: <list2> 中对应的单词加到 <list1> 后⾯返回: 连接后的字符串# Makefile 内容all:@echo $(join a b c d,1234)@echo $(join a b c d,12345)@echo $(join a b c d e,1234)# bash 中执⾏make$ makea1 b2 c3 d4a1 b2 c3 d4 5a1 b2 c3 d4 e3.4.3 foreach语法:$(foreach <var>,<list>,<text>)⽰例:# Makefile 内容targets := a b c dobjects := $(foreach i,$(targets),$(i).o)all:@echo $(targets)@echo $(objects)# bash 中执⾏make$ makea b c da.ob.oc.od.o3.4.4 if这⾥的if是个函数, 和前⾯的条件判断不⼀样, 前⾯的条件判断属于Makefile的关键字语法:$(if <condition>,<then-part>)$(if <condition>,<then-part>,<else-part>)⽰例:# Makefile 内容val := aobjects := $(if $(val),$(val).o,nothing)no-objects := $(if $(no-val),$(val).o,nothing)all:@echo $(objects)@echo $(no-objects)# bash 中执⾏make$ makea.onothing3.4.5 call - 创建新的参数化函数语法:$(call <expression>,<parm1>,<parm2>,<parm3>...)⽰例:# Makefile 内容log = "====debug====" $(1) "====end===="all:@echo $(call log,"正在 Make")# bash 中执⾏make$ make====debug==== 正在 Make ====end====3.4.6 origin - 判断变量的来源语法:$(origin <variable>)返回值有如下类型:类型含义undefined<variable> 没有定义过default<variable> 是个默认的定义, ⽐如 CC 变量environment<variable> 是个环境变量, 并且 make时没有使⽤ -e 参数file<variable> 定义在Makefile中command line<variable> 定义在命令⾏中override<variable> 被 override 重新定义过automatic<variable> 是⾃动化变量⽰例:# Makefile 内容val-in-file := test-fileoverride val-override := test-overrideall:@echo $(origin not-define) # not-define 没有定义@echo $(origin CC) # CC 是Makefile默认定义的变量@echo $(origin PATH) # PATH 是 bash 环境变量@echo $(origin val-in-file) # 此Makefile中定义的变量@echo $(origin val-in-cmd) # 这个变量会加在make的参数中@echo $(origin val-override) # 此Makefile中定义的override变量@echo $(origin @) # ⾃动变量, 具体前⾯的介绍# bash 中执⾏make$ make val-in-cmd=val-cmdundefineddefaultenvironmentfilecommand lineoverrideautomatic3.4.7 shell语法:$(shell <shell command>)它的作⽤就是执⾏⼀个shell命令, 并将shell命令的结果作为函数的返回.作⽤和 `<shell command>` ⼀样, ` 是反引号3.4.8 make 控制函数产⽣⼀个致命错误: $(error <text ...>)功能: 输出错误信息, 停⽌Makefile的运⾏# Makefile 内容all:$(error there is an error!)@echo "这⾥不会执⾏!"# bash 中执⾏make$ makeMakefile:2: *** there is an error!. Stop.输出警告: $(warning <text ...>)功能: 输出警告信息, Makefile继续运⾏# Makefile 内容all:$(warning there is an warning!)@echo "这⾥会执⾏!"# bash 中执⾏make$ makeMakefile:2: there is an warning!这⾥会执⾏!3.5 Makefile中⼀些GNU约定俗成的伪⽬标如果有过在Linux上, 从源码安装软件的经历的话, 就会对 make clean, make install ⽐较熟悉.像 clean, install 这些伪⽬标, ⼴为⼈知, 不⽤解释就⼤家知道是什么意思了.下⾯列举⼀些常⽤的伪⽬标, 如果在⾃⼰项⽬的Makefile合理使⽤这些伪⽬标的话, 可以让我们⾃⼰的Makefile看起来更专业, 呵呵 :)伪⽬标含义all所有⽬标的⽬标,其功能⼀般是编译所有的⽬标clean删除所有被make创建的⽂件install安装已编译好的程序,其实就是把⽬标可执⾏⽂件拷贝到指定的⽬录中去print列出改变过的源⽂件tar把源程序打包备份. 也就是⼀个tar⽂件dist创建⼀个压缩⽂件, ⼀般是把tar⽂件压成Z⽂件. 或是gz⽂件TAGS更新所有的⽬标, 以备完整地重编译使⽤check 或 test⼀般⽤来测试makefile的流程。
VxWorks操作系统MakeFile(一)时间:2008-8-24 夜版权申明:本文为水煮鱼为水煮鱼@博客园撰写,不得用于商业用途,如需摘用,请与水煮鱼联系。
1、介绍本文将介绍为什么要将你的C源代码分离成几个合理的独立文档,什么时候需要拆分,那又怎么拆分呢?然后再介绍如何使用GUN Make使你的编译和链接步骤自动化。
可能你使用的是其他的make工具,但是其实道理都差不多。
当然如果你对自己的编程工具有怀疑的话,可以不妨实际的试试。
2、多文件项目介绍a. why?为什么使用多文件项目?他们有什么好处呢?从表面上看,多文件项目是够复杂的了,又要头文件,又需要extern申明,并且如果你要查找一个文件的话,还需要在更多的文件里搜索。
但是如果把其考虑成一个项目,那一个项目根据功能划分为小的模块,那就不难理解了。
想想如果是一个一万行代码,如果你把其放到一个文件里,则在编译的时候,则需要对一万行代码进行重新编译。
不过如果你如果把其放到不同的文件里,那修改一行,则只需要编译一个文件就可以了。
可能你会说,一万行代码,就算全部编译,那点时间也基本可以忽略不计,但是实际情况是,在一个大的系统里,可能代码达到几十万甚至上百万,千万行代码的规模。
以我们的项目为例,目前代码规模已经达到了上千万行的级别,如果全部重新编译,则将耗费几个小时甚至半天的时间。
如果将其划分多多个文件,则修改一行所引入的编译代码,将不会随着你代码规模的增大而增大。
所以多个文件的优点不言自明了。
不过对于不便于搜索的问题,其实只要文件划分得当,也并不会造成多大的困难。
其实,从多个目标文件生成一个程序包比从一个单一文件生成程序包要好的多。
当然,实际上这是不是一个优势还与你所使用的系统有关。
但是当使用gcc/ld(一个GUN C编译器/连接器)把一个程序包连接到一个程序时,在连接的过程中,它会尝试不去连接没有使用到的部分,但它每次只能从程序包中把一个完整的目标文件排除在外。
因此,如果你修改了一个程序包中某一个目标文档中任何一个符号的话,那么这个目标文件整个都会被连接进来。
要是一个程序包被非常充分的分解的话,那么经过链接后,得到的可执行文件会比从一个大目标文件组成的程序包连接得到的文件小的多。
并且常常我们的程序是模块化的,高内聚,低耦合,使得文件之间共享部分被减少到了最少,因此采用多文件的方式,可以比较容易的找到代码中的bug。
b.when?那什么时候分解你的项目?如果你开发的是一个大项目,在开始前,应该好好考虑一下你将如何实现,并且将生成几个文件来存放你的代码。
当然,在项目的开发过程中,你可以建立新的我文件,但是这将打乱你的整体布局,可能造成你整体结构的调整。
因此特别建议在做之前,需要想想细细的考虑清楚,开发过程中允许有小范围的调节,但是涉及到整体结构的修改,将为你的项目引入过多的风险。
如果你开发的是一个中型的项目,你可以向上面介绍的那样,想清楚了再开始动手,但是你也可以想当然的写到哪就到哪,当你发现你的代码已经多到难以管理的时候在进行分解,但是希望你尽量先有一个好的谋局后,再动手,你将花费的代码最小。
c.how?如何分解呢?一下仅仅是个人的建议,如果你有自己的风格的话,如果你认为更好,那请保持你原来的做法:i. 不用使用一个header指向多个源代码。
使用一个头文件对应一个源代码文件,不仅更容易查询,方便代码的阅读,而且,无需修改一个头文件,而引起更多的文件的重新编译。
ii.如果可以的话,推荐采用多个头文件指向一个源代码文件的方式。
有时将不可公开调用的函数原型,类型定义等,从它们的C源嗲吗文件中分离出来是非常有用的。
可以使用一个库文件装共用的变量,而使用另外一个库文件装私用的变量,这样不会导致你修改源代码文件的内部结构时,你只需要重新编译这个源代码文件,而无需重新编译调用了公开的库文件的其他源代码。
iii.不要再多个库文件中重复定义。
这个不说,大家应该都知道为什么了。
iv. 在每一个源代码文件中,#include那些申明了源代码符号的所有头文件。
这样一来,你在源文件和头文件对某些函数做出的矛盾声明就可以比较容易的被编译器发现。
编译中常见的一些错误:1、定义符在源码文件中的矛盾。
在C语言里,变量和函数的缺省状态是公用的。
因此任何C源码的文件都可以引用存在于其他源码文件中的全局函数和全局变量,即使在这个源码文件中,并没有该全局函数或者全局变量的声明或者原型。
因此你必须保证在你的全局函数或者全局变量没有重复的定义,否则在连接的时候会出现错误,可能在编译的时候也会提示告警。
解决方法:一种有效的解决方法,就是在你申明的全局函数或者全局变量名前,加入一个可以区分文件的前缀名:比如如果在gfx.c里的函数完全就可以加上前缀名gfx_。
如果要防止一个符号在它被定义的源文件以外看到,可以在它的定义前加上关键字“static", 则该关键字定义了该符号的局部属性,仅能在定义的文件内使用。
(请注意,改关键字”static“的使用与局部变量定义时的区别)。
2、多次定义的符号。
当你用#include包含一个库文件的时候,实际上在编译的时候仅仅是在该处使用你包含的库文件逐字替换。
如果头文件被#include到多个文档中时,该头文件中的所有定义都会在你引用的源代码中重新定义一次。
重复定义,会导致链接出现错误。
解决方法:不要在头文件中定义变量。
定义变量一般放到使用他们的源码问文件中,而在其他文件中声明一下就可以了。
对于初学者来说,定义和声明,总是容易混淆。
定义的时候,编译器会给该变量分配实际的内存空间,而声明,仅仅是通知编译器该变量存在,并且告知该变量的类型。
声明一个变量的时候,需要在它的前面加上extern的关键字。
由于函数的原型中已经有了隐式的extern,所以不要考虑该问题。
3、重复定义、重复声明或者类型矛盾考虑如果你的源码文件中#include了头文件a.h和b.h,但是你的头文件中a.h又包含了b.h,则编译的时候会出现什么结果呢?很明显,b.h中定义的宏或者声明都会被执行两次。
从理论上讲,这样的重复都是完全一样的拷贝,不会出现问题,但是在实际的编译中,这是不符合C的基本语法,可鞥编译的时候会出现错误,至少也是一个告警。
解决办法:确定每一个头文件中在任何一个源码文件中仅仅包含了一次。
但是随着代码规模的扩大,通过认为控制的手段实在有点低效。
聪明的方法是使用预处理器。
常用的用法是:#ifndef MACRO_XXXX#define MACRO_XXXX XXXX#endif但是如果每一个宏都这么放一条,是不是显得也太低效了呢?(还不说,我还真的看到了这么做的,呵呵!!)在实际的应用中,我们只需在头文件的开头处定义:#ifndef FILENAME_H#define FILENAME_H在库文件的最后处定义:#endif使用库文件的文档名代替上面的FILENAME就可以了Makefile的结构:一般编译的步骤为:1、将每一个单独的源代码文件首先编译成为目标文件。
2、通过链接器,将目标文件连接成为可执行文件。
由于本文只讨论vxworks中的makefile,因此本文主要以常用的gcc为例。
通过-c开关,可以使用gcc将源文件编译成为期望的目标文件。
生成文件以.o为后缀名。
然后通过命令gcc -o exec_filename *.o,将生成的目标文件连接成为可执行文件。
在gcc中,生成的可执行文件以.out为后缀名。
对于一个多文件的项目,这些非常繁琐。
但是GNU Make工具让一切都可以变得很简单。
GUN Make的输入是一个文本文件:makefile。
在这个文件中,主要描述了目的文件是从那些依靠文件中产生的关联关系。
根据文本中描述的关系,make通过检查磁盘上的文件,如果目的文件的时间戳比至少它一个依靠文件的时间戳旧的话,make工具将执行相应的命令,以更新目的文件。
一个makefile主要含有以下的规则::....(tab)<command>(tab)<command>...例:====make file 开始====myprog:foo.o bar.ogcc foo.o bar.o -o myprogfoo.o: foo.c foo.h bar.hgcc -c foo.c -o foo.obar.o: bar.c bar.hgcc -c bar.c -o bar.o====make file 结束====这是一个非常典型的makefile。
make从最上面开始,把上面第一个目的, "myprog",作为它的主要目标。
给出跪着说明只要文件"myprog"比文件"foo.o"或者"bar.o"中的任何一个旧,则下一行命令将会被执行。
但是在检查文件foo.o和bar.o的文件戳之前,它会往下查找那些把foo.o或者bar.o 作为目标文件的规则,以此递归,直到找到最新的目标文件,并执行该目标文件下的命令。
从此可以看出,make工具通过这种递归查找最新时间戳的方式,保证当你修改任何代码的时候(可能是只有修改了一个源码文件,或者一个头文件),保证与之关联的文件都可以被编译。
当然,你必须保证你在makefile中写的编译规则,都是正确的,只列出了那些在源码文件中被包括的头文件。
编写Make的规则:通过上节的介绍,知道了怎么去生成一个makefile文件。
当文件较少的时候,采用上述的方法是可行的。
但是随着文件数目的增加,已经各个文件相互依赖关系变复杂以后,以上生成makefile的方式已经不再具有可行性了。
幸好很多编译器都具有自动生成makefile的功能。
以gcc为例:打开-m开关,则gcc 可以将你输入的C文件,根据其中包含的头文件自动生成依赖文件。
注意,如果采用-mm的方式,采用“<”,“>”方式包含的头文件将不生成依赖文件。
通过gcc生成的makefile,不会包含命令部分,该部分你可以自己补充,或者直接使用它的隐藏规则。
为了便于编写makefile文件,下面将具体介绍生成的makefile文件中的一些基本概念:1、makefile变量makefile变量类似于环境变量,起对大小写敏感,一般使用大写字母表示,它可以在任何地方引用,主要具有如下的功能:a. 用于存储文件列表名。
生成可执行文件的makefile,需要指定一些目标文件作为依赖文件;或者在命令行部分,需要会执行那些文件作为gcc命令的输入文件,通过变量的方式来存储这些文件名,更便于makefile的维护;b.用于存储可执行文件名。
如果你使用的是一个非gcc的编译器,则可能你需要修改所有命令行的使用的gcc的编译名部分,你可以定义一个变量保存编译器名称,则当编译器不同时,简单的修改该变量名称从而实现对不同编译器的命令的整体替代修改。