一个大工程往往是由许多模块组成的,模块下又细分很多子模块,比如Autosar工程,分为bsw、mcal、rte等,而bsw模块下又有Com、PduR、CanIf、CanNm等等几十个子模块,而且完成一个大工程的开发往往由许多工程师一起参与,然后各自提交代码到主工程,这就引起一个潜在问题,在众多的子目录之间是否有同名的文件呢?因为许多工程师可能需要使用同一个头文件,而在他们的子工程中就可能放了多个这个头文件,这个在编译的时候可能出现不同的c文件使用了不同目录下的同名头文件,如果这些同名头文件内容不一致,这会带来很多潜在的bug。那么怎么知道这个大工程中存不存在同名而分布在不同目录下的文件呢(有的编译器对于这个问题是不会给任何提示的,比如Greenhills)。本文就来讲讲如何使用shell脚本来解决此问题,并输出这些同名文件所在的路径。
先介绍一下本文所使用的工程,第一级目录如下图所示:
各个目录下又细分许多模块,模块中可能还细分成多个子模块,不同层级目录下有各种类型的格式文件,比如: .h, .c, .hex, .dpa, .inc, .asm, .pro文件等等。
Grennhills头文件查找规则:Greenhills是一个总的gpj工程文件下,包含多个子gpj工程构成,如下图:
最小子工程中有此子工程所有c文件编译时的头文件查找目录,例如bsw.gpj:
-I..\..\Mcal\Gpt\include
-I..\..\Mcal\Spi\include
-I..\..\Mcal\Adc\include
-I..\..\Davinci\Appl\Include
-I..\..\Davinci\Appl\IncludeHw
…
..\..\Mcal\Crypto_TS \src\Crypto.c
..\..\Mcal\Crypto_TS \src\Crypto_ASRExtension.c
..\..\Mcal\Crypto_TS \src\Crypto_Hse.c
当编译Crypto.c时,头文件将按照”-I”指定的目录从第一个开始往下查找,直到找到所需的头文件。
那么这种按顺序查找头文件的方法有个问题,就是如果不同的子工程所指定的头文件目录顺序不同甚至文件夹不同,而这些文件夹中很可能存在多个同名但是内容却不相同的头文件,这样会给工程带来逻辑错误。
那我们如何确保所有文件夹中的头文件都是唯一的呢?或者说假设存在这些同名而不同目录的头文件,怎么找到它们并且将其目录列出来呢?本文将使用shell脚本寻找这些同名头文件,并将它们的目录打印到一个txt文件中。So, let’s go.
STEP 1
首先在编译工程根目录下找出所有的gpj文件并将其目录打印到gpjdir.txt
find . -type f -name "*.gpj"> gpjdir.txt
得到:
STEP 2
利用每个子工程的头文件目录前面都有”-I”的特点,我们可以逐个gpj文件将这些”-I”的行提取出来,对于没有”-I”的gpj则忽略。比如bsw.gpj:
sed -n '/.*-I/p' ./src/bsw.gpj > bsw.txt
得到:
…共76个目录。
为了便于使用,我们需要将”-I”及其左边的空格全部删除,且将左斜杠替换成右斜杠(linux的目录使用的是右斜杠):
删除”-I”及其左边的空格:
sed -i 's/.*-I//' bsw.txt
左斜杠替换成右斜杠:
sed -i 's/\\/\//g' bsw.txt
得到:
因为这些gpj文件都在工程的3级目录下,而我们是在编译工程GHS文件夹下工作即二级目录,因此需要删除每行的一个”../”:
sed -i 's/\.\.\///' bsw.txt
得到:
接下来我们就可以将每个目录下的头文件及其路径全部打印到bswhfilesdir.txt中,这里将使用一个while循环:
while read linedofind $line -type f -name "*.h" >> bswhfilesdir.txtdone < bsw.txt
将得到bsw.gpj子工程的全部头文件及其目录:
…共1957个。
STEP 3
接下来就是本文的关键部分:这1957个头文件是不是有同名而不同目录的文件。
方法很多,楼主打算这么做:先去掉所有行的目录,只保留文件名。
while read linedoecho ${line##*/} >>bswhNoDirFiles.txtdone < bswhfilesdir.txt
得到(1957行,示意仅展示头几行):
然后排序,再删除掉单个的文件而保留重复的行(只保留一行)。将使用sort+uniq组合拳一步到位:
sort -n bswhNoDirFiles.txt |uniq -d > bswrepeat.txt
…
哇塞,共计147个同名而不同目录的头文件!!可以随机抽几个到bswhfilesdir.txt查找验证一下的。
STEP 4
最后将根据这些文件名,在bswhfilesdir.txt中找到它们对应的主人:
while read linedosed -n "/\/$line$/p" bswhfilesdir.txt >> bswrepeatDir.txtdone < bswrepeat.txt
对于像最后示意的这一对,是同名同目录,这是因为有重复包含的原因,所以最后要删除掉这些行:
Uniq -n bswrepeatDir.txt > bswrepeatDirUniq.txt
最后用Beyond Compare对比一下:
以上仅仅是对bsw.gpj工程的示例,读者可使用while循环等将所有子工程集中在一个脚本中进行处理。
编辑:Zhang Jinwei
往期文章:
C++面向对象编程的三个特性
嵌入式Linux学哪些东西
关于软件刷新的Utility File
c语言中的存储
DNS 报文格式简介