AUTOSAR入门-AS开源代码编译过程详解

📅 发布时间:2026/7/5 10:23:09 👁️ 浏览次数:
AUTOSAR入门-AS开源代码编译过程详解
AUTOSAR入门-AS开源代码编译过程详解拿到一份完整的软件代码面对成千上万的源码文件都会觉得有点无从下手。这时候软件对你来说像一个黑盒子里面有什么你不知道想最快速的了解这个盒子里面有什么就要去研究这个盒子怎么造出来的反推第一步也就是编译的过程。分析代码编译过程我自己总结有下面几点好处确定那些源码参与了编译俗话说顺藤摸瓜编译就是藤顺着走有几个瓜啥样都会清楚。要对代码进行修改移植调试必须要了解编译的过程要具有再造能力。很多中间代码的生成放在了编译过程直接看代码是不完整的像残缺的武功秘籍缺失的部分需要在编译里面找。1. Scons编译工具介绍首先我们回顾下AS代码编译的过程参考AUTOSAR入门-AS开源代码运行环境搭建中2.2节代码编译过程git clone https://github.com/thatway1989/ascd assconsexport BOARDx86export RELEASEascorescons注意如果你环境还没配置好AS代码还没编译通过可能你是伪程序员建议不用往下看了本系列文章强调动手调试直接研究代码搞代码才能有无穷乐趣,All is in the code. Code can tell you everything。可以看到编译的过程一直在用scons命令那么scons是什么下面进行介绍。1.1 Scons介绍文章封面中编译器可以把c语言文件编程二进制例如gcc hello.c -o hello但是对于大型工程项目有很多c文件要一块编译成一个二进制文件就需要制定一个编译规则规定那些文件参与编译怎么去编译这时有人会想到makefile的确makefile是干这个事的但是时过境迁很多懒人觉得makefile太麻烦了makefile中的代码需要写的太多能不能智能化一点不关注过程直接关注结果过程让机器去搞这样就可以少写一点编译脚本然后就进化出来两个方向第一种 (cmake) 你makefile麻烦我造一个工具例如cmake来生成makefilecmake脚本里面只关心要编译那些文件编译出来的目标文件是什么其他cmake工具去搞定第二种scons你makefile麻烦我不用你了直接重新搞一套例如scons直接调用编译器同样达到cmake那些简单方便。另外scons的语法不是自己定义的直接使用的python对python程序员很友好。可以理解scons脚本实际就是python脚本。2.2 Scons使用基础上面说到scons脚本里面都是用的python语言scons基于python封装了一些库函数下面挑一些主要的进行简单介绍Program函数src_filesSplit(main.cpp file1.cpp file2.cpp)Program(program, src_files)Program函数规定编译的目标文件这里为program规定了要编译的源文件这里为src_files 这个集合。SConscript函数在配置文件SConstruct中可以使用函数SConscript()函数来定附属的配置文件。例如objs SConscript(SConscript,variant_dirBDIR,duplicate0)当然这里SConscript是配置文件是用python语法写的也可以随意命名。Env环境构造Env环境一个环境就是一些变量的集合。例如Env[CC]arm-none-eabi-gcc -stdgnu99Env是一个变量集合其中一个名为CC的变量值是gcc编译器。参考scons官网文档https://scons.org/doc/production/PDF/scons-user.pdf2. scons命令编译过程2.1 SConstruct和building.py执行scons命令的时候首先会执行根目录下as/SConstruct整体的执行流程为PrepareEnv-》Conscript-》building首先给sys添加了环境变量方便直接访问这个目录studioos.path.abspath(./com/as.tool/config.infrastructure.system/)sys.path.append(studio)from building import *asenv PrepareEnv()from building是python语法从building这个包里面加载类这个包的位置在./com/as.tool/config.infrastructure.system/building.py所以PrepareEnv函数就是在这个building.py里面声明的。进入这个函数ASROOT变量没赋值先找到值asenvEnvironment(TOOLS[ar, as,gcc,g,gnulink])os.environ[ASROOT] ASROOTasenv[ASROOT] ASROOT构造环境asenv然后把ASROOT变量加进去同时把ASROOT也加到了os.environ里面BOARD os.getenv(BOARD)if(BOARD not in board_list):print(Error: no BOARD specified!)help()exit(-1)检查系统环境变量里面有BOARD设置没没设置就会提醒在com/as.tool/config.infrastructure.system/building.py中PrepareEnv函数的最后PrepareBuilding(asenv)return asenvPrepareBuilding函数GetConfig(%s/.config%(env[BDIR]),env)之后添加了一些变量和一些选项打印出来第一个参数为as/build/posix/x86/ascore/.config没这个文件​​​​​​​AddOption(--menuconfig,dest menuconfig,action store_true,default False,help make menuconfig for Automotive Software AS)如果使用scons --menuconfig命令则下面这个if就会进去执行menuconfig函数进行系统配置详细说明见2.5使用AddOption函数定义自己的命令行选项。​​​​​​​if(GetOption(menuconfig)):menuconfig(env)获取环境变量后就开始执行SConscript函数后面详细介绍最后还回到as/SConstructPrepareEnv()执行完objs SConscript(SConscript,variant_dirBDIR, duplicate0)objs就是要编译的文件的集合读取objs完毕后执行编译Building(target,objs)这个Building函数还是在com/as.tool/config.infrastructure.system/building.py中定义target打印出来为as/build/posix/x86/ascore/x86Building函数中首先对sobjs中要编译的文件进行了分类arxml、xml、py、dts、其他。2.2 SConscript生成objs根目录下SConscript文件如下​​​​​​​from building import *objs SConscript(com/SConscript)objs SConscript(release/SConscript)Return(objs)可以知道这个脚本是一个嵌套格式就是把要编译的文件加入到objs里面如果想加入一个功能需要修改这个脚本让源码文件能编译进去。2.3 arxml生成LCfg文件这个流程也是xml生成c源码的过程Building函数中如下对arxml进行了处理​​​​​​​if( ( (not os.path.exists(cfgdone)) and (not GetOption(clean)) )or forceGen ):MKDir(cfgdir)RMFile(cfgdone)xcc.XCC(cfgdir, env, True)if(arxml ! None):arxmlR PreProcess(cfgdir, str(arxml))for xml in xmls:MKSymlink(str(xml),%s/%s%(cfgdir,os.path.basename(str(xml))))xcc.XCC(cfgdir)argen.ArGen.ArGenMain(arxmlR,cfgdir)MKFile(cfgdone, SHA256(glob.glob(%s/*xml%(cfgdir))))先判断cfgdoneas/build/posix/x86/ascore/config/config.done是否存在不然不存在则上面代码是对arxml的处理新建文件夹cfgdiras/build/posix/x86/ascore/config--生成代码位置xcc.XCC(cfgdir, env, True)函数在xcc.py中定义fp open(%s/asmconfig.h%(gendir),w)fp.write(#ifndef _AS_MCONF_H_\n\n)if(MODULES in env and env[MODULES] is not None):for m in env[MODULES]:fp.write(#ifndef USE_%s\n#define USE_%s 1\n#endif\n\n%(m,m))if(CONFIGS in env and env[CONFIGS] is not None):根据env里面MOUDLES生成宏例如#defineUSE_ARCH_X86 1根据env里面CONFIGS生成宏例如#define ARCHx86写入asmconfig.h文件中。最后XCC函数里面还执行了两个生成函数from argen.KsmGen import *from argen.OsGen import *__gen__ [KsmGen,OsGen]for g in __gen__:print( %s ...%(g.__name__))g(gendir)OsGen在argen/OsGen.py中定义这里因为没有xml文件没有生成os的cfg文件KsmGen在argen/KsmGen.py中定义生成ksm_cfg.h上面的过程可以理解为python代码生成.h代码的过程。PreProcess(cfgdir, str(arxml))函数中arxdml打印出来为com/as.application/common/autosar.arxml--xml文件位置filRas/build/posix/x86/ascore/config/autosar.arxmlfilCas/build/posix/x86/ascore/config/autosar.arxml.h为一个链接文件autosar.arxml.h -/home/XXX/autosar/as/com/as.application/common/autosar.arxml执行命令gcc -E --include/home/XXX/autosar/as/build/posix/x86/ascore/config/asmconfig.h/home/XXX/autosar/as/build/posix/x86/ascore/config/autosar.arxml.hGCC -E选项对源程序做预处理操作但是没有-o参数就没有输出到目标文件。但是以字符串的形式存储到了txt变量里面err, txt RunSysCmd(cmd)简单处理去掉#注释后存入了filRas/build/posix/x86/ascore/config/autosar.arxml文件for xml in xmls:MKSymlink(str(xml),%s/%s%(cfgdir,os.path.basename(str(xml))))lwip.xml - /home/XXX/autosar/as/com/as.application/common/config/lwip.xmlas/build/posix/x86/ascore/config/目录下的xml文件都建立了软连接到对应的xml文件argen.ArGen.ArGenMain(arxmlR,cfgdir)#生成config.done文件MKFile(cfgdone, SHA256(glob.glob(%s/*xml%(cfgdir))))argen.ArGen.ArGenMain函数在argen/ArGen.py中定义def ArGenMain(wfxml,gendir):import xml.etree.ElementTree as ETif(os.path.exists(wfxml)):root ET.parse(wfxml).getroot();for arxml in root:ArGen(arxml,gendir)找一个简单的模块例如Eawfxml是autosar.arxml对这个文件进行xml解析找到tag就子目录的标识Ea General Comment* DevelopmentErrorDetectionON NvmJobEndNotificationNULL NvmJobErrorNotificationNULL SetModeApiON VersionInfoApiOFF VirtualPageSize8 /BlockList MaxTBDBlock ArraySize2 BlockSize32 Comment* ImmediateDataFalse IsArrayFalse NameEaTest1 NumberOfWriteCycles0xFFFFFFFF /Block ArraySize2 BlockSize32 Comment* ImmediateDataFalse IsArrayFalse NameEaTest2 NumberOfWriteCycles0xFFFFFFFF //BlockList/Ea--》engine(arxml,dir)ArGen函数调用--》‘Ea:GenEa在argen/GenEa.py中def GenEa(root,dir):global __dirGLInit(root)__dir %s%(dir)if(len(GLGet(BlockList)) 0):returnGenH()GenC()print( Gen Ea DONE )GLInit函数在argen/GCF.py中定义def GLInit(root): global __root__rootroot如果没有BlockList则推出。我们查看arxml文件里面Ea下有这个的。GLGet也在argen/GCF.py中定义作用是找到xml里面的某项先看GenC()生成Ea_Cfg.c文件fp open(%s/Ea_Cfg.h%(__dir),w)fp.write(GHeader(Ea))整个过程就是根据arxml里面的生成c文件的这里业务部分不具体分析了。GHeader在argen/GCF.py中定义用于生成版权文件头部分。生成代码的位置为./build/posix/x86/ascore/config/Ea_Cfg.cGenH()也是同样的过程。在Building函数中如下定义了studio命令的处理方法后续文章再详细分析if((studio in COMMAND_LINE_TARGETS) and (env Env)):studioos.path.abspath(%s/com/as.tool/config.infrastructure.system/%(env[ASROOT]))assert(arxml)pd os.path.abspath(cfgdir)RunCommand(cd %s %s studio.py %s%(studio,env[python3],pd))exit(0)2.4 DTS、OFS、SWCS编译BuildDTS(dts,BDIR)print(!!!Building ofs,)BuildOFS(ofs)print(!!!Building swcs,)BuildingSWCS(swcs)从打印上看没有dts of和py文件文件所以上面上个函数都没文件要处理。在building函数的结尾会定义默认编译为可以执行文件if(BUILD_TYPE exe):env.Program(target, objs)objs是SConscript生成的分析见2.2中objs里面还添加了生成的cfg文件。target打印出来为as/build/posix/x86/ascore/x862.5 生成TINIX.IMGscons读完脚本后就会去编译目标文件x86这个过程是编译器gcc完成的scons: done reading SConscript files.scons: Building targets ...scons: building associated VariantDir targets: build/posix/x86/ascore会用gcc编译器对objs里面出现的c文件进行编译成o文件最后进行链接为x86目标文件。编译出来x86目标文件后还执行了几个操作在AddPostAction中定义。AddPostActionscons中的库函数安排在构建指定目标之后执行的指定操作命令。如下if(POSTACTION in env):for action in env[POSTACTION]:env.AddPostAction(target, action)target 为as/build/posix/x86/ascore/x86action打印出来为dd if/dev/zero ofTINIX.IMG bs512 count2880/home/XXX/autosar/as/com/as.infrastructure/system/fs/fatfs/fatfs.exe mkfs TINIX.IMGdd convnotrunc if/home/XXX/autosar/as/com/as.infrastructure/arch/x86/boot/boot.bin ofTINIX.IMG bs512 count1/home/XXX/autosar/as/com/as.infrastructure/system/fs/fatfs/fatfs.exe cp /home/XXX/autosar/as/com/as.infrastructure/arch/x86/boot/loader.bin /loader.bin TINIX.IMGobjcopy -S build/posix/x86/ascore/x86 kernel.bin/home/XXX/autosar/as/com/as.infrastructure/system/fs/fatfs/fatfs.execp kernel.bin /kernel.bin TINIX.IMGdd 可从标准输入或文件中读取数据根据指定的格式来转换数据再输出到文件、设备或标准输出。进行制作文件系统最后形成IMG文件。• if文件名输入文件名默认为标准输入。即指定源文件。• of文件名输出文件名默认为标准输出。即指定目的文件。• bsbytes同时设置读入/输出的块大小为bytes个字节。• countblocks仅拷贝blocks个块块大小等于ibs指定的字节数。if/dev/zero就是创建一个新的文件块大小为512个字节有2880个块。文件大小计算2880*512/1024/10241.4MFatFs是一个用于小型嵌入式系统的通用FAT/exFAT文件系统模块。下载下来在download/ff13文件夹下。convnotrunc不截断输出文件。把boot.bin放到TINIX.IMG结尾objcopy -S 不从源文件拷贝符号信息和relocation信息。fatfs.exe cp命令把二进制文件复制进IMG文件生成的TINIX.IMG有三部分boot.bin、loader.bin、x86-》kernel.binTINIX.IMG 镜像文件怎么在qemu里面加载运行涉及那部分代码这里篇幅有限下次更新了再详细说明。后记首先感谢各位的关注这里也不搞什么圈粉变现什么的就是开源软件分享。说点感悟用微信公众号来写文章主要还是得用口语化的语言图文并茂方便轻松的去阅读。所以基本都是先不看其他的资料先凭自己的理解口语化的表述出来其实挺费时间但这感觉可以锻炼表达能力写作风格还是口语化表述code讲解。公众号“那路谈OS与SoC嵌入式软件”欢迎关注个人文章汇总https://thatway1989.github.io