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

原创 thatway 那路谈OS与SoC嵌入式软件 2022-03-08 18:03

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

拿到一份完整的软件代码,面对成千上万的源码文件,都会觉得有点无从下手。这时候软件对你来说像一个黑盒子,里面有什么你不知道,想最快速的了解这个盒子里面有什么,就要去研究这个盒子
怎么造出
来的,反推第一步也就是编译
的过程。
分析代码
编译过程

我自己总结
有下面几点
好处

  1. 确定那些源码参与了编译,俗话说
    顺藤摸瓜
    ,编译就是藤顺着走,有几个瓜,啥样都会清楚。

  2. 要对代码进行修改移植调试
    必须要了解编译的过程,要具有再造能力。

  3. 很多
    中间代码**的生成**
    放在了编译过程,直接看代码是不完整的,像残缺的武功秘籍,缺失的部分需要在编译里面找。

1. Scons编译工具介绍

 首先我们回顾下AS代码编译的过程,参考:[AUTOSAR入门-AS开源代码运行环境搭建](http://mp.weixin.qq.com/s?__biz=MzUzMDMwNTg2Nw==&mid=2247483761&idx=1&sn=ce5ae0a3ef9282fdbeabe0b949eca3e2&chksm=fa528755cd250e43d8903eb3591f4f2d5315d2b2856cce2ab28dbeb5c9ae742c6ef6454a21f6&scene=21#wechat_redirect)  

中2.2节代码编译过程:

git clone https://github.com/thatway1989/as
cd as
scons
export BOARD=x86
export RELEASE=ascore
scons

注意
:如果你环境还没配置好,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来生成makefile,cmake脚本里面只关心要编译那些文件,编译出来的目标文件是什么,其他cmake工具去搞定;

第二种(scons
)你makefile麻烦,我不用你了,直接重新搞一套例如scons,直接调用编译器,同样达到cmake那些简单方便。另外scons的语法不是自己定义的,直接使用的python,对python程序员很友好。可以理解scons脚本实际就是python脚本

2.2 Scons使用基础

上面说到scons脚本里面都是用的python语言,scons基于python封装了一些库函数,下面挑一些主要的进行简单介绍

Program函数

src_files=Split('main.cpp file1.cpp file2.cpp')

Program('program', src_files)

Program函数规定编译的目标文件,这里为program,规定了要编译的源文件,这里为src_files 这个集合。

SConscript函数:

在配置文件SConstruct中可以使用函数SConscript()函数来定附属的配置文件。例如:
objs = SConscript('SConscr
ipt',variant_dir=BDIR,duplicate=0)

当然这里SConscript是配置文件,是用python语法写的,也可以随意命名。

Env环境:

构造Env环境,一个环境就是一些变量的集合。例如:

Env['CC']='arm-none-eabi-gcc -std=gnu99'

Env是一个变量集合,其中一个名为CC的变量值是gcc编译器。

参考scons官网文档:
https://scons.org/doc/production/PDF/scons-user.pdf

2. scons命令编译过程

**### 2.1 SConstruct和building.py

执行scons命令的时候,首先会执行根目录下as/SConstruct
,整体的执行流程为

PrepareEnv-》Conscript-》building

首先给sys添加了环境变量,方便直接访问这个目录

studio=os.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变量没赋值,先找到值

asenv=Environment(TOOLS=['ar', 'as','gcc','g++','gnulink'])
os.environ['ASROOT'] = ASROOT
asenv['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 asenv

PrepareBuilding函数,

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/SConstruct,PrepareEnv()执行完,

objs = SConscript('SConscript',variant_dir=BDIR, duplicate=0)

objs
就是要编译的文件的集合,读取objs完毕后,执行编译

Building(target,objs)

这个Building函数还是在com/as.tool/config.infrastructure.system/building.py中定义target
打印出来为:as/build/posix/x86/ascore/x86

Building函数中,首先对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))))

先判断cfgdone:
as/build/posix/x86/ascore/config/config.done是否存在,不然不存在,则
上面代码是对arxml的处理

新建文件夹cfgdir:as/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文件位置

filR:as/build/posix/x86/ascore/config/autosar.arxml

filC:as/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.h

GCC -E选项:对源程序做预处理操作,但是没有-o参数,就没有输出到目标文件。但是以字符串的形式存储到了txt变量里面

err, txt = RunSysCmd(cmd)

简单处理,去掉#注释后存入了filR:as/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.xml
as/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  ET
    if(os.path.exists(wfxml)):
        root =  ET.parse(wfxml).getroot();
        for arxml in root:
            ArGen(arxml,gendir)

找一个简单的模块,例如Ea
wfxml是autosar.arxml,对这个文件进行xml解析,找到tag,就子目录的标识

<Ea>                                                                                     <General Comment="*"  DevelopmentErrorDetection="ON"  NvmJobEndNotification="NULL" NvmJobErrorNotification="NULL"  SetModeApi="ON" VersionInfoApi="OFF"  VirtualPageSize="8" />
<BlockList Max="TBD">  
<Block ArraySize="2" BlockSize="32"  Comment="*" ImmediateData="False"  IsArray="False" Name="EaTest1"  NumberOfWriteCycles="0xFFFFFFFF" />
<Block ArraySize="2" BlockSize="32"  Comment="*" ImmediateData="False" IsArray="False"  Name="EaTest2" NumberOfWriteCycles="0xFFFFFFFF" />
</BlockList>
</Ea>

--》engine(arxml,dir)
ArGen函数调用

--》‘Ea':GenEa

在argen/GenEa.py中,

def GenEa(root,dir):
    global __dir
    GLInit(root)
    __dir = '%s'%(dir)
    if(len(GLGet('BlockList')) ==  0):return
    GenH()
    GenC()
    print('    >>> Gen Ea DONE <<<')

GLInit函数在argen/GCF.py中定义

def GLInit(root):    global __root
    __root=root

如果没有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.c

GenH()也是同样的过程。

在Building函数中,如下定义了studio命令的处理方法,后续文章再详细分析

if(('studio' in COMMAND_LINE_TARGETS) and (env == Env)):
     studio=os.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/x86

2.5 生成TINIX.IMG

scons读完脚本后,就会去编译目标文件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中定义。

AddPostAction
:scons中的库函数,安排在构建指定目标之后执行的指定操作命令。如下:

if('POSTACTION' in env):
    for action in env['POSTACTION']:
        env.AddPostAction(target, action)

target 为:as/build/posix/x86/ascore/
x86

action
打印出来为:

dd if=/dev/zero of=TINIX.IMG bs=512 count=2880
/home/XXX/autosar/as/com/as.infrastructure/system/fs/fatfs/fatfs.exe  mkfs TINIX.IMG
dd conv=notrunc if=/home/XXX/autosar/as/com/as.infrastructure/arch/x86/boot/boot.bin  of=TINIX.IMG bs=512 count=1
/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.IMG
objcopy -S build/posix/x86/ascore/x86  kernel.bin
/home/XXX/autosar/as/com/as.infrastructure/system/fs/fatfs/fatfs.exe  
cp kernel.bin /kernel.bin TINIX.IMG

dd 可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。
进行制作文件系统,最后形成IMG文件。

• if=文件名:输入文件名,默认为标准输入。即指定源文件。

• of=文件名:输出文件名,默认为标准输出。即指定目的文件。

• bs=bytes:同时设置读入/输出的块大小为bytes个字节。

• count=blocks:仅拷贝blocks个块,块大小等于ibs指定的字节数。

if=/dev/zero,就是创建一个新的文件,块大小为512个字节,有2880个块。

文件大小计算:2880*512/1024/1024=1.4M

FatFs是一个用于小型嵌入式系统的通用FAT/exFAT文件系统模块。下载下来在download/ff13文件夹下。

conv=notrunc:不截断输出文件。把boot.bin放到TINIX.IMG结尾

objcopy -S :不从源文件拷贝符号信息和relocation信息。

fatfs.exe cp命令,把二进制文件复制进IMG文件

生成的
TINIX.IMG
有三部分:
boot.bin

loader.bin

x86
-》kernel.bin

TINIX.IMG 镜像文件,怎么在qemu里面加载运行,涉及那部分代码,这里篇幅有限,下次更新了再详细说明。

后记:

 首先感谢各位的关注,这里也不搞什么圈粉变现什么的,就是  

开源软件分享
。说点感悟,用微信公众号来写文章,主要还是得用口语化的语言,图文并茂,方便轻松的去阅读。所以基本都是先不看其他的资料,先
凭自己的理解
口语化的表述出来,其实挺费时间,但这感觉可以锻炼表达能力,写作风格还是:
口语化表述

  • code讲解

Talk is cheap
,show methe code
,持续更新,纯干货分享,无广告,不打赏,欢迎
转载
,欢迎
评论交流

往期回顾:

AUTOSAR入门-江湖

AUTOSAR入门-AS开源代码运行环境搭建

results matching ""

    No results matching ""