您的位置:首页 > 运维架构

关于OpenWrt metadata.pl脚本文件中的几个函数说明_part1

2014-08-15 10:55 781 查看
-->>gen_kconfig_overrides()

metadata.pl ---->> gen_kconfig_overrides()

查看metadata.pl脚本,查看parse_command(),发现有以下几种用法:

/^target_config$/ and returngen_target_config();

/^package_mk$/ and returngen_package_mk();

/^package_config$/ and returngen_package_config();

/^kconfig/ and returngen_kconfig_overrides();

/^package_source$/ and returngen_package_source();

我这里主要关注的是以kconfig开头的参数数组;

通过执行make V=s,可以看到其中有这样一条语句:

scripts/metadata.plkconfig /tmp/.packageinfo .config > /build_dir/linux-ipq806x/linux-3.4.0/.config.override

上述语句以/tmp/.packageinfo 和 .config 两个文件作为输入源,最终生成目标是/build_dir/linux-ipq806x/linux-3.4.0/.config.override

现在我要追寻的答案是:根据.config中选取的CONFIG_PACKAGE_*=y的PACKAGE,其对应的depends中+kmod-*下面的内核配置选项是否在.config.override中全部进行了设置?

那就要看一下gen_kconfig_overrides()这个函数了。

subgen_kconfig_overrides() {

my %config;

my %kconfig;

my $package;

my $pkginfo = shift @ARGV;

my $cfgfile = shift @ARGV;

# parameter 2: build system config

open FILE, "<$cfgfile" orreturn;

while (<FILE>) {

/^(CONFIG_.+?)=(.+)$/ and$config{$1} = 1;

}

close FILE;

# parameter 1: package metadata

open FILE, "<$pkginfo" orreturn;

while (<FILE>) {

/^Package:\s*(.+?)\s*$/ and$package = $1;

/^Kernel-Config:\s*(.+?)\s*$/and do {

my @config = split/\s+/, $1;

foreach my $config(@config) {

my $val ='m';

my$override;

if ($config=~ /^(.+?)=(.+)$/) {

$config= $1;

$override= 1;

$val= $2;

}

if($config{"CONFIG_PACKAGE_$package"} and ($config ne 'n')) {

nextif $kconfig{$config} eq 'y';

$kconfig{$config}= $val;

} elsif(!$override) {

$kconfig{$config}or $kconfig{$config} = 'n';

}

}

};

};

close FILE;

foreach my $kconfig (sort keys%kconfig) {

if ($kconfig{$kconfig} eq'n') {

print "#$kconfig is not set\n";

} else {

print"$kconfig=$kconfig{$kconfig}\n";

}

}

}

该函数明显分为4个小部分;

第一个部分:

my %config; 应该是声明了hash数组,也可以说关联数组,更好理解,一个键紧跟一个值组成的列表,如%fred=(one, “zmd”, two, “cxm”), $fred{one}=”zmd”;

my$package; 声明了一个package变量;

my $pkginfo= shift @ARGV;

@ARGV是当前运行的命令行参数列表;

shift是把数组的第一个元素移出并返回它,然后把数组长度减1,并顺移剩下的元素。如果不再存在元素,返回undef。

所以,package为第一个参数,cfgfile为第一个参数,即$packge=/tmp/.packageinfo,$cfgfile=.config。

第二个部分:

open FILE, "<$cfgfile" orreturn;

while (<FILE>) {

/^(CONFIG_.+?)=(.+)$/ and$config{$1} = 1;

}

close FILE;

打开文件$cfgfile,即.config文件,且.config文件必须存在,否则直接返回。

读取.config文件的每一行,判断是否以CONFIG_开头,CONFIG_后的“.+”表示匹配1次或多次的任何字符,“(.+)$”表示结尾,即提取每行的“CONFIG_*=*”选项,后面的$1表示“CONFIG_.+?)”。

注意:Perl的正则表达式中如果出现(),则发生匹配或替换后,()内的模式被Perl解释器自动依次赋给系统$1,$2,……

然后对$config数组进行设置,$1即CONFIG_作为数组键,值为1

第三个部分:

openFILE, "<$pkginfo" or return;

while(<FILE>) {

/^Package:\s*(.+?)\s*$/and $package = $1;

/^Kernel-Config:\s*(.+?)\s*$/and do {

my@config = split /\s+/, $1;

foreachmy $config (@config) {

my$val = 'm';

my$override;

if($config =~ /^(.+?)=(.+)$/) {

$config= $1;

$override= 1;

$val= $2;

}

if($config{"CONFIG_PACKAGE_$package"} and ($config ne 'n')) {

nextif $kconfig{$config} eq 'y';

$kconfig{$config}= $val;

}elsif (!$override) {

$kconfig{$config}or $kconfig{$config} = 'n';

}

}

};

};

closeFILE;

首先检查$pkginfo即/tmp/.packageinfo文件是否存在,如果不存在直接返回;

然后一行一行读取.packageinfo的内容;

/^Package:\s*(.+?)\s*$/and $package = $1;

匹配以Package:开头,然后“\s”是空格,“\s*”匹配0次或多次空格,“(.+?)”匹配一个多个任意字符,“\s*$”以0个或多个空格结尾

即查找.packageinfo文件中形如“Package: kmod-ledtrig-netfilter”这样的行,即整合的每个package的第一行,并将形如“kmod-ledtrig-netfilter”赋给$package变量

/^Kernel-Config:\s*(.+?)\s*$/

查找形如“Kernel-Config: CONFIG_GPIOMMC CONFIG_CONFIGFS_FS=y”这样的行

接下来是一个do{}操作;

my @config= split /\s+/, $1;

split是把字符串进行分割并把分割后的结果存储在数组中。

注意:

PERL用@和$来区分数组整体和数组元素(其中数组元素其实就是一个普通变量,以$打头)

@_表示Perl的缺省数组;

$_表示Perl的缺省变量;

然后看一下foreach语句,

首先foreach my $config (@config)

注意:Perl的foreach语句,控制变量$config,如果在foreach结构主体中改变了控制变量的值,会改变控制变量在当前数组@config中对应的值。

if ($config =~ /^(.+?)=(.+)$/) {

$config= $1;

$override= 1;

$val = $2;

}

$config =~/^(.+?)=(.+)$/表示对$config进行正则匹配(“=~”符号),匹配以字符开头,以字符结尾,中间以“=”号相连的字符串,形如“CONFIG_CONFIGFS_FS=y”,接下来将第一个(.+?)赋值给$config,即如$config=CONFIG_CONFIGFS,并将$override=1,将第二个(.+)赋给$var,即如$var=y。

注意:在foreach结构体中第一句就指定了my $val = 'm';即当配置项没有对其赋值时,默认给定值=m;

if($config{"CONFIG_PACKAGE_$package"} and ($config ne 'n')) {

nextif $kconfig{$config} eq 'y';

$kconfig{$config}= $val;

}elsif (!$override) {

$kconfig{$config}or $kconfig{$config} = 'n';

}

首先判断$config{“CONFIG_PACKAGE_$package”}是否为1(实际上就是第一部分读取$cfgfile,.config文件中以CONFIG_*=*的形式的字符串,并将其键对应的值为1),且$config不等于‘n’(???$config选项,若匹配/^(.+?)=(.+)$/,则$config为$1;不匹配,则是分割的$config整体;有可能是单独的字符‘n’吗?);接下来

next if$kconfig{$config} eq 'y';如果$kconfig{$config}=y,则跳出该次循环;否则执行

$kconfig{$config}= $val; 默认为m,如果为y,则为覆盖掉m;是将$var赋给$kconfig{$config},

注意,实际上在这里就是对%kconfig数组进行初始化工作;

elsif (!$override) {

$kconfig{$config}or $kconfig{$config} = 'n';

}

当override不为1时,$kconfig{$config} or $kconfig{$config} ='n'; 即当$config不是写成形如“CONFIG_=*”的形式,如果$kconfig{$config}不存在,则$kconfig{$config}=’n’;

foreachmy $kconfig (sort keys %kconfig) {

if($kconfig{$kconfig} eq 'n') {

print"# $kconfig is not set\n";

}else {

print"$kconfig=$kconfig{$kconfig}\n";

}

}

遍历%kconfig数组,sort keys%kconfig,将%kconfig数组按照keys键顺序排列

if-else就是明显的判断输出。

综上所述,gen_kconfig_overrides()的作用是读取.config文件中CONFIG_*=*的选项,然后在tmp/.packageinfo文件中匹配对应的Package:*下的Kernel-Config: *行,提取出对应的的内核配置选项,默认置为m,

1) 如果内核配选项满足形如“CONFIG_*=*”格式,即后面有对相应内核配置选项进行赋值,如果该配置选项对应的“CONFIG_PACKAGE_*”package键在%config数组中存在;若该内核配置选项已经写进%kconfig数组,而且赋值为“y”,则跳过此次循环,继续匹配下个配置选项(即=y选项可以完全覆盖掉=n或者=m或者没有进行赋值的选项),否则完全copy该配置选项以及其值;

2) 如果仅仅写成一个单独的配置选项,即形如“CONFIG_*”,没有对其赋值,且之前如果没有对其进行配置,即%kconfig数组中不存在该配置选项,或者说该配置选项第一次出现,但是其对应的“CONFIG_PACKAGE_*”已经写进%kconfig数组,则默认置为m;否则将其赋值为“n”。

-->>gen_package_config()

metadata.pl ---->> gen_package_config()

具体调用是在make menuconfig的依赖目标prepare-tmpinfo中,语句如下:

./scripts/metadata.plpackage_config tmp/packageinfo > tmp/config-package.in

现在让我们来看一下具体的函数实现:

sub gen_package_config() {

parse_package_metadata($ARGV[0])or exit 1;

print"menuconfig IMAGEOPT\n\tbool \"Image configuration\"\n\tdefaultn\n";

foreachmy $preconfig (keys %preconfig) {

foreachmy $cfg (keys %{$preconfig{$preconfig}}) {

my$conf = $preconfig{$preconfig}->{$cfg}->{id};

$conf=~ tr/\.-/__/;

print<<EOF

configUCI_PRECONFIG_$conf

string"$preconfig{$preconfig}->{$cfg}->{label}" if IMAGEOPT

dependsPACKAGE_$preconfig

default"$preconfig{$preconfig}->{$cfg}->{default}"

EOF

}

}

print"source \"package/*/image-config.in\"\n";

if(scalar glob "package/feeds/*/*/image-config.in") {

print "source\"package/feeds/*/*/image-config.in\"\n";

}

print_package_features();

print_package_config_category'Base system';

foreachmy $cat (keys %category) {

print_package_config_category$cat;

}

}

-->>parse_package_metadata()

函数的第一句话parse_package_metadata($ARGV[0]) or exit 1;执行parse_package_metadata($ARGV[0])函数,若返回false则exit.。$ARGV[0]实际上就是tmp/.packageinfo。让我们来看以下parse_package_metadata($ARGV[0]) 函数:

可以在scripts/metadata.pm文件中该函数定义。

openFILE, "<$file" or do {

warn"Cannot open '$file': $!\n";

returnundef;

};

读取文件tmp/.packageinfo。

while (<FILE>) {……}对文件进行处理;

chomp; 去掉换行符;

/^Source-Makefile: \s*((.+\/)([^\/]+)\/Makefile)\s*$/and do {

$makefile= $1;

$subdir= $2;

$src= $3;

$subdir=~ s/^package\///;

$subdir{$src}= $subdir;

$srcpackage{$src}= [];

undef$pkg;

};

匹配以$Source-Makefile: 开头的行,\s*匹配0个或多个空格,(.+\/)匹配任意多个字符,紧跟一个/,([^\/]+)多个不是\的字符,\/Makefile完全匹配/Makefile,即匹配形如“Source-Makefile: package/px5g/Makefile”的字符串,下面以这个为例解析;

$makefile = $1; 即((.+\/)([^\/]+)\/Makefile),package/px5g/Makefile

$subdir = $2; 即$makefile=(.+\/),package/

$src = $3; 即([^\/]+),px5g

$subdir =~ s/^package\///; 将package/替换为空,即$subdir为空

$subdir{$src} = $subdir;

$srcpackage{$src} = [];

不用说,是对%subdir的hash数组设值,以及%srcpackage 的hash数组设值

undef $pkg;

next unless $src; 当$src为假时跳出当前循环;

注意:在perl中,unless是条件为假时执行;if是条件为真时执行;last会立即结束循环;

对%package、%srcpackage的hash 数组进行赋值;

注意%srcpackage的每个值也是hash数组;

/^Package:\s*(.+?)\s*$/ and do {

undef$feature;

$pkg= {};

$pkg->{src}= $src;

$pkg->{makefile}= $makefile;

$pkg->{name}= $1;

$pkg->{title}= "";

$pkg->{default}= "m if ALL";

$pkg->{depends}= [];

$pkg->{mdepends}= [];

$pkg->{vdepends}= $package{$1}->{vdepends};

$pkg->{builddepends}= [];

$pkg->{buildtypes}= [];

$pkg->{subdir}= $subdir;

$pkg->{tristate}= 1;

$package{$1}= $pkg;

push@{$srcpackage{$src}}, $pkg;

};

/^Package:\s*(.+?)\s*$/ 匹配形如“Package:px5g”的字符串

push @{$srcpackage{$src}}, $pkg; 将$pkg 加入到@{$srcpackage{$src}}的末尾

注意:perl中->{}表示散列引用,->[]表示数组引用,->()表示子程序引用

在Perl中,如果使用use strict和my,所有的变量在声明前必须定义;my,代表只在当前作用域起作用,例如:如果在文件头定义,那么其作用域直到文件尾;如果定义在函数体内,那么只在该函数体内起作用。

在Perl中,our是定义全局变量,全局变量可以在任何地方引用。如果该perl文件定义了package名字,如

package bug;

our $eg; 则可以通过$bug:eg来访问这个变量。默认package名字为main。

对%features 的hash 数组进行赋值

/^Feature:\s*(.+?)\s*$/ and do {

undef$pkg;

$feature= {};

$feature->{name}= $1;

$feature->{priority}= 0;

};

$feature and do {

/^Target-Name:\s*(.+?)\s*$/and do {

$features{$1}or $features{$1} = [];

push@{$features{$1}}, $feature;

};

/^Target-Title:\s*(.+?)\s*$/and $feature->{target_title} = $1;

/^Feature-Priority:\s*(\d+)\s*$/and $feature->{priority} = $1;

/^Feature-Name:\s*(.+?)\s*$/and $feature->{title} = $1;

/^Feature-Description:/and $feature->{description} = get_multiline(\*FILE, "\t\t\t");

next;

};

nextunless $pkg;

next unless $pkg; 这句话的意思是说如果$pkg为空,则跳出循环;可以看到$pkg是在存在feature时会undef $pkg;这里应该就是说当检测到有feature存在,对feature赋值以后跳出此次循环;

/^Provides: \s*(.+)\s*$/ and do {

my@vpkg = split /\s+/, $1;

foreachmy $vpkg (@vpkg) {

$package{$vpkg}or $package{$vpkg} = {

name=> $vpkg,

vdepends => [],

src=> $src,

subdir=> $subdir,

makefile=> $makefile,

virtual=> 1

};

push@{$package{$vpkg}->{vdepends}}, $pkg->{name};

}

};

综上,parse_package_metadata函数实际上就是对tmp/.packaginfo下面的每个package的描述进行依次遍历,然后将每个package的信息保存在对应的$packge{$packagename}元素中,这里的%package实际上是一个散列数组,而$package{$packagename}也是一个散列数组。

-->>metadata.pm

现在回过头来看一下整个metadata.pm文件,

在文件头部定义了

package metadata;定义package名称,为以后调用该文件中的全局变量提供了自定义的package入口,即“metadata:”

our @EXPORT = qw(%package %srcpackage %category%subdir %preconfig %features clear_packages parse_package_metadataget_multiline);在Perl中,qw意思为用空格分隔字符串;@EXPORT,默认导出符号,@EXPORT数组可以包含变量和函数名称;

可以看到在文件metadata.pl文件头部使用了语句“use metadata;”,这时就会导出@EXPORT数组;

-->>举例说明parse_package_metadata()

现在让我们回顾一下parse_package_metadata函数,%package的各个元素依赖于哪些条件,即在哪些条件下才会被赋值;

举例说明吧,选取tmp/.packageinfo文件中的一个片段,如下:

Source-Makefile: package/libpcap/Makefile

/^Source-Makefile:\s*((.+\/)([^\/]+)\/Makefile)\s*$/

$makefile=$1;即package/libpcap/Makefile

$subdir=$2;即package

$src=$3;即libpcap

$subdir=~ s/^package\///;即为空

$subdir{$src}= $subdir;

$srcpackage{$src}= [];

Package: libpcap

/^Package:\s*(.+?)\s*$/

$pkg->{src}=$src,即libpcap

$pkg->{makefile}=$makefile,即package/libpcap/Makefile

$pkg->{name}=$1,即libpcap

$pkg->{title}=””

$pkg->{default}=”mif ALL”

$pkg->{depends}=[]

$pkg->{mdepends}=[]

$pkg->{vdepends}=$package{$1}->{vdepends},即$package{libpcap}->{vdepends}

$pkg->{builddepends}= [];

$pkg->{buildtypes}= [];

$pkg->{subdir}= $subdir;即空

$pkg->{tristate}= 1;

$package{$1}= $pkg;即$package{libpcap}=$pkg

push@{$srcpackage{$src}}, $pkg;即将$pkg压入@{$srcpackage{libpcap}}数组

Menu: 1

/^Menu:\s*(.+)\s*$/ and $pkg->{menu} = $1;

$pkg->{menu}=1

Version:1.1.1-2

/^Version:\s*(.+)\s*$/ and $pkg->{version} = $1;

$pkg->{version}=1.1.1-2

Depends:+libc +USE_EGLIBC:librt +USE_EGLIBC:libpthread

/^Depends:\s*(.+)\s*$/ and $pkg->{depends} = [ split /\s+/, $1 ];

$pkg->{depends}=[+libc+USE_EGLIBC:librt +USE_EGLIBC:libpthread]

Menu-Depends:

/^Menu-Depends:\s*(.+)\s*$/ and $pkg->{mdepends} = [ split /\s+/, $1 ];

此处为空,故不会对$pkg->{mdepends}进行赋值

Provides:

/^Provides:\s*(.+)\s*$/ and do {…..}

此处为空,故不会进行do操作

Section:libs

Category:Libraries

/^Category:\s*(.+)\s*$/ and do {……}

$pkg->{category}= $1;即$pkg->{category}=Libraries

defined$category{$1} or $category{$1} = {};即$category{Libraries}={}

defined$category{$1}->{$src} or $category{$1}->{$src} = [];即$category{Libraries}->{libpcap}={}

push@{$category{$1}->{$src}}, $pkg;即将$pkg压入@{$category{Libraries}->{libpcap}}数组

Title:Low-level packet capture library

/^Title:\s*(.+)\s*$/ and $pkg->{title} = $1;

$pkg->{title}=Low-levelpacket capture library

Maintainer:OpenWrt Developers Team <openwrt-devel@openwrt.org>

Source:libpcap-1.1.1.tar.gz

/^Source:\s*(.+)\s*$/ and $pkg->{source} = $1;

$pkg->{source}=libpcap-1.1.1.tar.gz

Type: ipkg

/^Type:\s*(.+)\s*$/ and do {……}

$pkg->{type} = [ split/\s+/, $1 ];

undef $pkg->{tristate};

foreach my $type(@{$pkg->{type}}) {

$type =~ /ipkg/ and$pkg->{tristate} = 1;

}

$pkg->{type}= ipkg

$pkg->{tristate}=1

Description:This package contains a system-independent library for user-level networkpacket

capture.

http://www.tcpdump.org/

OpenWrtDevelopers Team <openwrt-devel@openwrt.org>

@@

/^Description:\s*(.*)\s*$/ and $pkg->{description} = "\t\t $1\n".get_multiline(*FILE, "\t\t ");

$pkg->{description}=”\t\tThis package contains a system-independent library for user-level networkpacket\n”.get_multiline(*FILE,”\t\t”)

函数get_multiline()可以看出是读取当前指针下面的行,直到遇到@@字符才停止

Config:

source "package/libpcap/Config.in"

@@

/^Config:\s*(.*)\s*$/and $pkg->{config} = "$1\n".get_multiline(*FILE, "\t");

$pkg->{config}=”$1\n”.get_multiline(*FILE,”\t”)

另外,也请注意,一个Source-Makefile:字段后面可能跟了多个Package: **,这多个Package: **的{src},{subdir},{makefile}都是一样的,例如Source-Makefile:package/kernel/Makefile、Source-Makefile:
package/toolchain/Makefile后面都跟了多个Package:**
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: