-->>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 <[email protected]>
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 <[email protected]>
@@
/^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:**