关于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:**
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:**
相关文章推荐
- 关于Openwrt metadata.pl脚本的几个函数说明_part2
- php关于文件内容的几个操作函数总结
- 脚本文件的编写和关于文件的基本函数的用法
- 关于PHP-Zend framework2 框架 学习过程。 阅前须知: ZF2中的配置文件是可以静态文件配置来注册和通过相关函数动态注册。 1.EventManager(事件驱动),关于事件驱动,在ZF2相关资料没有详细说明,可以参考ANDROID的事件驱动,MFC的消息响应/事件驱动。
- 几个关于文件路径方面的函数,不断更新...
- R语言关于脚本文件的输入和输出 sink函数生动
- 关于文件和目录的几个函数
- 关于字符串和文件操作的几个函数
- 关于cocos2dx坐标及几个重要函数说明
- Spring MVC 教程,快速入门,深入分析――关于写几个配置文件的说明
- linux文件操作之open函数说明
- 关于文件和目录的几个函数
- 关于open函数的在不创建新文件时,打开不存在的文件的测试
- [QT]QFileDialog关于选择文件对话框中的几个信号的说明
- matlab中关于函数文件和脚本文件使用实例
- 关于C++中open打开文件函数的诡异的问题
- 关于java中servlet中的路径的几个函数的例子
- 关于文件系统设备堆栈的说明
- robot脚本__从文本中读入文件的函数input
- 关于Utf8编码的几个函数最近一段时间老弄Utf8编码,工作时写了几个函数,给大家指正一下(转载)