關於OpenWrt metadata.pl腳本文件中的幾個函數說明_part1

-->>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/MakefileSource-Makefile: package/toolchain/Makefile後面都跟了多個Package:**


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章