LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 798|回复: 4

apt-update-stat.pl 统计Debian软件库变更

[复制链接]
发表于 2006-5-25 10:45:32 | 显示全部楼层 |阅读模式
用个code标签包一下,保护一下文本中的空格和制表符。


  1. 实现的功能:
  2. update                下载Release和Packages.gz文件,这些文件应该可以
  3.                 拷贝到/var/lib/apt/lists下面(Packages.gz需要先解压缩),
  4.                 如果apt update不会在其它某个地方记录update时间的话。

  5. stat                统计Packages之间的关系,输出格式为:
  6.                 pkg_name  score  depends_score rdepends_score
  7.                 score是根据这个包的Essential, Priority, Section计算
  8.                 出来的,可能基准分数设置不合理,可以修改calculate_score()
  9.                 或者给它传第二个参数%SCORE;
  10.                 depends_score是递归统计Depends包的score得出的,反映一个包
  11.                 依赖其它包的程度;
  12.                 rdepends_score是递归统计Reverse Depends包的score得出的,
  13.                 反映一个包被其它包依赖的程度,在upgrade时可以参考这个分数
  14.                 估计升级的风险:-)
  15.                 有个问题没搞懂:Debian的包有循环依赖,我处理了直接依赖的
  16.                 情形,当作同一个包,但是非直接依赖的我就把分数全设置成了
  17.                 -100000000,在stat.err中可以看到是哪些包循环依赖了。

  18. compare                比较不同时间的Packages.gz文件的差异,输出跟stat类似,只是
  19.                 多了最后一列版本号。

  20. 用perl apt-update-stat.pl help可以看到帮助,或者看下面这个也可以,运行时
  21. 记得重定向输出到文件中,否则很耗时的。
  22. $ mkdir current
  23. $ perl apt-update-stat.pl update [url]http://archive.ubuntu.com/ubuntu[/url] \
  24.         dapper i386 current
  25. $ perl apt-update-stat.pl stat current/*Packages.gz 1>stat.log 2>stat.err

  26. $ perl apt-update-stat.pl compare old current 1>cmp.log 2>cmp.err

  27. update速度取决于网速,一般一分钟左右;
  28. stat刚按照上面步骤做了一下,P4 2.80GHz/512MB耗时四五十秒,分析18767个包,
  29. 如果关掉代码里面的Carp调用,可能会快个十来秒;
  30. compare昨晚在家拿了俩main_binary-i386_Packages测试,四千多个包吧,PM740
  31. 1.73GHz/512MB耗时六秒多;
  32. 感觉性能还可以忍:-D

  33. 写这个过程中发现有个libapt-pkg-perl的包,提供了操作/var/cache/apt/pkgcache.bin,
  34. 的接口,用这个应该会快很多,不过也有搞坏系统的可能,呵呵。

  35. 早上刚发现的问题,在update下载Packages.gz时会多打出一次文件名来,在
  36. "Successfully fetched to ..."之前, 看了一会,没瞧出来是哪里输出的,汗……

  37. 其它信息请见脚本里头的注释。

  38. 源文件用的制表符,set ts=8 sw=8 noet nowrap,请用这个设置查看以免缩进
  39. 混乱。
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
 楼主| 发表于 2006-5-25 10:47:21 | 显示全部楼层

  1. #! /usr/bin/perl -w
  2. #
  3. # analyse changes in Debian software repository.
  4. #
  5. # Usage:
  6. #        run `perl apt-update-stat.pl help` for help.
  7. #
  8. # Author:
  9. #         dieken at newsmth BBS <http://newsmth.net>
  10. #
  11. # Date:
  12. #         2006-05-21
  13. #
  14. #
  15. #################################################################
  16. # This program is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  19. #################################################################
  20. #
  21. #
  22. # Note:
  23. #
  24. # Generally, $pkgs is a hash reference, its elements are also hash
  25. # references named $pkg, $pkg records information about a package.
  26. #
  27. # $pkgs = {
  28. #         'PkgName'         => $pkg = {
  29. #                                 'Package'                 => 'PkgName',
  30. #                                 'Priority'                 => 'required',
  31. #                                 ...,
  32. #                                 '_score'                 => 300,
  33. #                                '_depends'                => [ [...], ...],
  34. #                                '_rdpends'                => [ .... ],
  35. #                                 '_depends_score'        => 1050,
  36. #                                 '_rdepends_score'        => 2070
  37. #                                 },
  38. #        'PkgName2'         => $pkg2 = {...},
  39. #         ...
  40. #         };
  41. #
  42. #
  43. # Layout of Debian Software Package Repository:
  44. #
  45. # TYPE         HOST                    VERSION     COMPONENTS
  46. # --- +-------------------------------+ ------ +---------------------------------+
  47. # deb [url]http://archive.ubuntu.com/ubuntu/[/url] dapper main restricted universe multiverse
  48. #
  49. # [url]http://archive.ubuntu.com/ubuntu/[/url]        (main url)
  50. #                                 |--dists/ (contains package indices of different versions)
  51. #                                  |       |
  52. #                                 |       |--dapper/
  53. #                                 |       |        |--Release
  54. #                                 |       |        |--Release.gpg
  55. #                                 |       |        |--main/   (one of components)
  56. #                                 |       |        |      |--binary-i386/Packages[.gz|.bz2]
  57. #                                 |       |        |      |--...
  58. #                                 |       |        |--restricted/
  59. #                                 |       |                     |--...
  60. #                                 |       |--breezy/
  61. #                                 |
  62. #                                 |--pool/        (real packages and sources are stored here)
  63. #
  64. # The `Release` file records what components and architectures a release version supports,
  65. # it also points out where to find binary package indices and source indices(this program
  66. # doesn't collect information about source packages).
  67. #
  68. # The `Packages` file records information of each package like name, priority, section and
  69. # where to find it.
  70. #
  71. # There is MD5 and SHA1 digest information in `Release` and `Packages` files, but this
  72. # little program doesn't make use of them.
  73. #
  74. # The `Release` files and `Packages` files are downloaded to /var/lib/apt/lists
  75. # by `apt-get update` or `aptitude update`.
  76. #
  77. #
  78. # Summary of each function and their dependence:
  79. #        see comment of each subroutine.
  80. #
  81. # XXX:
  82. #         don't know how to deal with indirect circular dependence;
  83. #         %option is not used;
  84. #        no MD5 or SHA1 check;
  85. #        don't take account of "Conflicts", "Recommends", "Suggests" and so on;
  86. #        some Perl tricks for performance ? (I think it's enough already)
  87. #        graphical report? (It seems unnecessary)
  88. #

  89. use strict;
  90. use Carp qw(carp cluck confess);
  91. use Cwd 'abs_path';
  92. use FileHandle;
  93. use File::Glob ':glob';
  94. use File::Path;
  95. use File::Spec;
  96. use LWP::Simple;
  97. use Data::Dumper;


  98. #-----------------------------  MAIN ENTRY -----------------------------------
  99. my %commands = (
  100.         "update"        => \&do_update,
  101.         "stat"                => \&do_stat,
  102.         "compare"        => \&do_compare
  103. );


  104. if (@ARGV < 1 || ! exists $commands{$ARGV[0]}) {
  105.         usage();
  106.         exit 0;
  107. }

  108. my $cmd = shift;

  109. $commands{$cmd}->(@ARGV);


  110. # -------------------------- COMMANDS  ---------------------------------------
  111. sub usage {
  112.         print<<EOF
  113. apt-update-stat.pl  <host>  <version>  <architecture>  <saveToDir>
  114. eg.
  115.         apt-update-stat.pl update [url]http://archive.ubuntu.com/ubuntu[/url] dapper i386 ./20060521

  116. apt-update-stat.pl stat  <Packages-file>...
  117. eg.
  118.         apt-update-stat.pl stat ./20060521/*Packages.gz  >stat.log 2>stat.err

  119. apt-update-stat.pl compare  <oldPackagesDir>  <newPackagesDir>
  120. eg.
  121.         apt-update-stat.pl compare ./20060521 ./20060525 >cmp.log 2>cmp.err
  122.         sort -r -n -k 4 cmp.log | less                # order by rdepends score
  123. EOF
  124. }


  125. sub do_update {
  126.         my ($host, $version, $arch, $dir) = @_;
  127.         if (! defined $dir) {
  128.                 print "please run `apt-update-stat.pl help` for help.\n";
  129.                 return;
  130.         }

  131.         my $release = fetch_Release($host, $version, $dir);
  132.         print "Successfully downloaded ", build_url($host, $version, "Release"), " to $release\n";

  133.         my %spec = parse_Release($release);

  134.         if (! grep /$arch/o, @{$spec{'Architectures'}}) {
  135.                 print "wrong arch name, available arch: @{$spec{'Architectures'}}\n";
  136.                 return;
  137.         }

  138.         my ($url, $packages_url, $path);
  139.         foreach (@{$spec{'Components'}}) {
  140.                 $packages_url = build_Packages_url($_, $arch, ".gz");
  141.                 $url=  build_url($host, $version, $packages_url);
  142.                 print "fetching $url...\n";
  143.                 $path = fetch_file($host, $version, $packages_url, $dir);
  144.                 print "Successfully fetched to $path\n";
  145.         }
  146. }


  147. sub do_stat {
  148.         if (@_ < 1) {
  149.                 print "please run `apt-update-stat.pl help` for help.\n";
  150.                 return;
  151.         }

  152.         my %pkgs = ();
  153.         my $Packages_file;
  154.         my $count = 0;
  155.         while ($Packages_file = shift) {
  156.                 $count += parse_Packages(\%pkgs, $Packages_file);
  157.         }

  158.         print "$count packages parsed.\n";

  159.         generate_depends_rdepends(\%pkgs);

  160.         # print STDERR Dumper(\%pkgs);

  161.         while (my ($name, $pkg) = each %pkgs) {
  162.                 printf "%-40s%5s%15s%15s\n", $name,
  163.                 calculate_score(\%pkgs, $name),
  164.                 calculate_depends_score(\%pkgs, $name),
  165.                 calculate_rdepends_score(\%pkgs, $name);
  166.         }
  167. }


  168. sub do_compare {
  169.         if (@_ < 2) {
  170.                 print "please run `apt-update-stat.pl help` for help.\n";
  171.                 return;
  172.         }

  173.         my ($olddir, $newdir) = @_;
  174.         my @oldPackages = <$olddir/*Packages.gz>;
  175.         my @newPackages = <$newdir/*Packages.gz>;
  176.         if (@oldPackages < 1 || @newPackages < 1) {
  177.                 print "you need some ...Packages.gz files in $olddir and $newdir\n";
  178.                 return;
  179.         }

  180.         my %oldpkgs = ();
  181.         my %newpkgs = ();
  182.         my ($oldcount, $newcount) = (0, 0);

  183.         foreach (@oldPackages) {
  184.                 $oldcount += parse_Packages(\%oldpkgs, $_);
  185.         }
  186.         foreach (@newPackages) {
  187.                 $newcount += parse_Packages(\%newpkgs, $_);
  188.         }

  189.         print "$olddir: \t$oldcount packages\n$newdir: \t$newcount packages.\n";

  190.         # print STDERR Dumper(\%oldpkgs, \%newpkgs);
  191.        
  192.         generate_depends_rdepends(\%oldpkgs);
  193.         generate_depends_rdepends(\%newpkgs);

  194.         while (my ($name, $pkg) = each %newpkgs) {
  195.                 if (! exists $oldpkgs{$name}) {
  196.                         printf "%-30s %5s %10s %10s %20s \n",
  197.                         "${name}[NEW]",
  198.                         calculate_score(\%newpkgs, $name),
  199.                         calculate_depends_score(\%newpkgs, $name),
  200.                         calculate_rdepends_score(\%newpkgs, $name),
  201.                         $pkg->{'Version'};
  202.                 } elsif ($oldpkgs{$name}->{'Version'} ne $pkg->{'Version'}) {
  203.                         printf "%-30s %5s %10s %10s %10s=>%10s\n",
  204.                         $name,
  205.                         calculate_score(\%newpkgs, $name),
  206.                         calculate_depends_score(\%newpkgs, $name),
  207.                         calculate_rdepends_score(\%newpkgs, $name),
  208.                         $oldpkgs{$name}->{'Version'},
  209.                         $pkg->{'Version'};
  210.                 }
  211.         }
  212. }



  213. # ----------------------------------SUBROUTINES----------------------------------

  214. # build_listname($host, $version, $file)
  215. #         build a file name as apt does, see /var/lib/apt/lists.
  216. #
  217. # eg: $name = build_listname("http://archive.ubuntu.com/ubuntu",
  218. #                         "dapper", "Release");
  219. #
  220. # dependence: none
  221. #
  222. sub build_listname {
  223.         my ($host, $version, $file) = @_;
  224.         $host =~ s|^.*?//||;                # trim protocol part, eg "http://"
  225.         $host =~ s|/$||;
  226.         $host =~ tr|/|_|;
  227.         $file =~ tr|/|_|;

  228.         # archive.ubuntu.com_ubuntu_dists_dapper_Release
  229.         return $host . "_dists_" . $version . "_" . $file;
  230. }


  231. # build_Packages_url($component, $architecture, $suffix)
  232. #         build relative url of a Packages file.
  233. #
  234. # eg. $partial_url = build_Packages_url("main", "i386", ".gz");
  235. #
  236. # dependence: none
  237. #
  238. sub build_Packages_url {
  239.         my ($component, $architecture, $suffix) = @_;

  240.         return $component . "/binary-" . $architecture . "/Packages" . $suffix;
  241. }

  242. # build_url($host, $version, $file)
  243. #         build a url like [url]http://archive.ubuntu.com/ubuntu/dists/dapper/Release[/url]
  244. #
  245. # eg. $full_url = build_url("http://archive.ubuntu.com/ubuntu",
  246. #                                 "dapper", "Release");
  247. # dependence: none
  248. #
  249. sub build_url {
  250.         my ($host, $version, $file) = @_;
  251.         $host =~ s|/$||;

  252.         return $host . "/dists/" . $version . "/" . $file;
  253. }


  254. # fetch_file($host, $version, $file, $dir)
  255. #         fetch "$host/dists/$version/$file", save it to $dir,
  256. #         and return its full path name.
  257. #
  258. # eg: $full_path = fetch_file("http://archive.ubuntu.com/ubuntu", "dapper",
  259. #                                 "Release", "20060521/");
  260. #
  261. # dependence: build_listname(), build_url
  262. #
  263. sub fetch_file {
  264.         my ($host, $version, $file, $dir) = @_;
  265.         my $path = abs_path($dir);
  266.         my $filename = build_listname($host, $version, $file);
  267.         my $url = build_url($host, $version, $file);

  268.         if (! -e $path) {
  269.                 mkpath $path || confess "Can't mkdir $path: $!\n";
  270.         }

  271.         $path = File::Spec->catfile($path, $filename);
  272.         if (is_success(mirror($url, $path))) {
  273.                 return $path;
  274.         } else {
  275.                 confess "Can't download $url: $!\n";
  276.         }
  277. }


  278. # fetch_Release($host, $version, $dir)
  279. #         fetch Release file of $version from $host and save to $dir.
  280. #         return full path name of Release file.
  281. #
  282. # eg. $full_path = fetch_Release("http://archive.ubuntu.com/ubuntu",
  283. #                                 "dapper","20060521/");
  284. #
  285. # dependence: fetch_file()
  286. #
  287. sub fetch_Release {
  288.         my ($host, $version, $dir) = @_;

  289.         return fetch_file($host, $version, "Release", $dir);
  290. }


  291. # parse_Release($release_file)
  292. #         parse Release file, return a hash representation:
  293. #                 (
  294. #                         "Architectures" => [....],
  295. #                         "Components" => [....]
  296. #                 )
  297. #
  298. # eg. %spec = parse_Release("20060521/archive.ubuntu.com_ubuntu_dists_dapper_Release");
  299. #
  300. # dependence: none
  301. #
  302. sub parse_Release {
  303.         my $release = shift;
  304.         my $fh = new FileHandle;
  305.         my %spec= ();
  306.         my $gotit = 0;

  307.         $fh->open("<$release") || confess "Can't open Release file $release: $!\n";
  308.         while (<$fh>) {
  309.                 if (/^Architectures: *(.*?) *$/) {
  310.                         $spec{"Architectures"} = \@{[split / +/, $1]};
  311.                         last if (++$gotit > 1);
  312.                 } elsif (/^Components: *(.*?) *$/) {
  313.                         $spec{"Components"} = \@{[split / +/, $1]};
  314.                         last if (++$gotit > 1);
  315.                 }
  316.         }

  317.         undef $fh;

  318.         confess "Wrong Release file: $release\n" if $gotit < 2;

  319.         return %spec;
  320. }


  321. # fetch_Packages($host, $version, $component, $architecture, $dir)
  322. #        fetch Packages file of $version from $host and save to $dir.
  323. #        return full path name of this Packages file.
  324. #
  325. # eg. $full_path = fetch_Packages("http://archive.ubuntu.com/ubuntu", "dapper",
  326. #                                 "main", "i386", "20060521/");
  327. #
  328. # dependence: fetch_file(), build_Package_url()
  329. #
  330. # XXX: let caller decide which format to download: Packages, Packages.gz
  331. # or Packages.bz2
  332. #
  333. sub fetch_Packages {
  334.         my ($host, $version, $comp, $arch, $dir) = @_;

  335.         return fetch_file($host, $version,
  336.                         build_Packages_url($comp, $arch, ".gz"),
  337.                         $dir);
  338. }


  339. # parse_Packages(\%packages, $filename, %option);
  340. #        parse Packages file $filename, add information to %packages
  341. #        according to %option.
  342. #
  343. # eg. $count_addition = parse_Packages(\%pkgs,
  344. #                 "20060521/archive.ubuntu.com_ubuntu_dists_dapper_main_binary-i386_Packages.gz");
  345. #
  346. # dependence: none
  347. #
  348. sub parse_Packages {
  349.         my ($pkgs, $filename, %option) = @_;
  350.         my $fh;
  351.         my $count;
  352.         my $pkg;
  353.         my $keyword;

  354.         if ($filename =~ /Packages\.gz$/) {
  355.                 $fh = new FileHandle "gzip -dc $filename |";
  356.         } elsif ($filename =~ /Packages\.bz2$/) {
  357.                 $fh = new FileHandle "bzip2 -dc $filename |";
  358.         } elsif ($filename =~ /Packages$/) {
  359.                 $fh = new FileHandle "< $filename";
  360.         } else {
  361.                 confess "Unknown Packages file format: $filename\n";
  362.         }

  363.         if (! defined($fh)) {
  364.                 carp "Can't read $filename: $!\n";
  365.                 return 0;
  366.         }

  367.         $count = 0;
  368.         $pkg = {};

  369.         while (<$fh>) {
  370.                 if (/^\s*$/) {
  371.                         # avoid multiple continuous white line.
  372.                         if (exists $pkg->{'Package'}) {
  373.                                 $pkgs->{$pkg->{'Package'}} = $pkg;
  374.                                 $pkg = {};
  375.                                 ++$count;
  376.                         }
  377.                         next;
  378.                 } elsif (/^ /) {        # skip description
  379.                         next;
  380.                 }

  381.                 foreach $keyword ('Package', 'Priority', 'Section', 'Essential',
  382.                                                         'Version', 'Depends', 'Conflicts',
  383.                                                         'Pre-Depends', 'Recommends', 'Suggests') {
  384.                         if (/^$keyword:\s*(.*)\s*$/) {
  385.                                 $pkg->{$keyword} = $1;
  386.                                 last;
  387.                         }
  388.                 }
  389.         }

  390.         undef $fh;

  391.         return $count;
  392. }


  393. # test_parse_Packages($filename)
  394. #         test case for parse_Packages() subroutine.
  395. #
  396. sub test_parse_Packages {
  397.         my %pkgs = ();
  398.        
  399.         confess "Usage: test_parse_Packages \$Packages_filename\n" if @_ < 1;

  400.         my $count = parse_Packages(\%pkgs, shift);
  401.         print "$count packages\n";

  402.         while (my ($name, $pkg) = each %pkgs) {
  403.                 print "=======================$name==============================\n";
  404.                 while (my ($key, $value) = each %$pkg) {
  405.                         print "$key => $value\n"
  406.                 }
  407.         }
  408. }

  409. # test_parse_Packages(shift);
  410. # exit 0;


  411. # calculate_score(\%pkgs, $name, %SCORE)
  412. #         calculate importance of a package according to its section,
  413. #         priority and so on.
  414. #
  415. # eg. $score = calculate_score(\%pkgs, $pkg_name);
  416. #
  417. # dependence: none
  418. #
  419. # note: must call parse_Packages() first  to get a %pkgs.
  420. #
  421. sub calculate_score {
  422.         my ($pkgs, $name, %SCORE) = @_;
  423.         my $pkg = $pkgs->{$name};
  424.        
  425.         if (! defined($pkg)) {
  426.                 cluck "WARN: Package \`$name\` is not found, take zero as its score.\n";
  427.                 return 0;
  428.         }

  429.         return $pkg->{'_score'} if exists $pkg->{'_score'};

  430.         $SCORE{'Essential'} = 20  if ! exists $SCORE{'Essential'};
  431.         # Priority:
  432.         $SCORE{'required'}         = 100 if ! exists $SCORE{'required'};
  433.         $SCORE{'standard'}         = 80  if ! exists $SCORE{'standard'};
  434.            $SCORE{'important'} = 60  if ! exists $SCORE{'important'};
  435.            $SCORE{'extra'}         = 40  if ! exists $SCORE{'extra'};
  436.            $SCORE{'optional'}         = 20  if ! exists $SCORE{'optional'};
  437.         # Section
  438.         $SCORE{'base'}         = 5 if ! exists $SCORE{'base'};
  439.            $SCORE{'admin'}        = 2 if ! exists $SCORE{'admin'};
  440.            $SCORE{'libs'}         = 2 if ! exists $SCORE{'libs'};
  441.         $SCORE{'gnome'}        = 1 if ! exists $SCORE{'gnome'};
  442.         $SCORE{'kde'}        = 1 if ! exists $SCORE{'kde'};
  443.         $SCORE{'x11'}        = 1 if ! exists $SCORE{'x11'};

  444.         $pkg->{'_score'} = 0;

  445.         $pkg->{'_score'} += $SCORE{'Essential'} if exists $pkg->{'Essential'};
  446.         $pkg->{'_score'} += $SCORE{$pkg->{'Section'}} if exists $SCORE{$pkg->{'Section'}};
  447.         $pkg->{'_score'} += $SCORE{$pkg->{'Priority'}} if exists $SCORE{$pkg->{'Priority'}};

  448.         return $pkg->{'_score'};
  449. }


  450. # generate_depends_rdepends(\%pkgs)
  451. #         generate depends and rdepends list for each package.
  452. #         $pkg = {
  453. #                 "Package" => "PkgName",
  454. #                 ...
  455. #                 "_depends" => [ [pkg_name, version_condition],
  456. #                                 [pkg2_name, version_condition,
  457. #                                  alternative_pkg2_name, version_condition]
  458. #                               ],
  459. #                 "_rdepends" => [pkgA_name, pkgB_name],
  460. #         };
  461. #
  462. # eg. generate_depends_rdepends(\%pkgs);
  463. #
  464. # dependence: none
  465. #
  466. # note: must call parse_Packages() first to get a %pkgs.
  467. #
  468. # XXX: There are some APIs to parse /var/cache/apt/pkgcache.bin, see
  469. # package `libapt-pkg-dev` and package `libapt-pkg-perl`.
  470. #
  471. sub generate_depends_rdepends {
  472.         my ($pkgs) = shift;
  473.         my ($name, $pkg, @Depends, @deps);
  474.         my ($p_name, $p_ver, $p);

  475.         while (($name, $pkg) = each %$pkgs) {
  476.                 next if ! exists $pkg->{'Depends'};

  477.                 $pkg->{'_depends'} = [];
  478.                 # eg. @Depends = ("a (>> 1.1) | b (>> 2.1)", "c (>> 2.1)", "d")
  479.                 @Depends = split /,\s*/, $pkg->{'Depends'};
  480.                 foreach (@Depends) {
  481.                         # eg. @deps = ("a (>> 1.1)", "b (>> 2.1)")
  482.                         # eg. $p = ["a", ">> 1.1",
  483.                         #             "b", ">> 2.1"];
  484.                         $p = [];
  485.                         @deps = split /\s*\|\s*/;
  486.                         foreach (@deps) {
  487.                                 # eg. ("a", ">> 1.1")
  488.                                 # $p_ver may be undef

  489.                                 ($p_name, $p_ver) = /([^\s]+)\s*(?:\((.*)\))?/;
  490.                                 push @$p, $p_name, $p_ver;

  491.                                 if (!defined($p_name)) {
  492.                                         print $_, "\n";
  493.                                         print "p_name is undef: $pkg->{'Depends'}\n";
  494.                                         next;
  495.                                 }
  496.                                 next if ! exists $pkgs->{$p_name};

  497.                                 if (exists $pkgs->{$p_name}->{'_rdepends'}) {
  498.                                         push @{$pkgs->{$p_name}->{'_rdepends'}}, $name;
  499.                                 } else {
  500.                                         $pkgs->{$p_name}->{'_rdepends'} = [$name];
  501.                                 }
  502.                         }
  503.                         push @{$pkg->{'_depends'}}, $p;
  504.                 }
  505.         }
  506. }


  507. # test_generate_depends_rdepends($oldPackages, $newPackages)
  508. #         test case for generate_depends_rdepends()
  509. #
  510. sub test_generate_depends_rdepends {
  511.         my %pkgs = ();
  512.         my $count;

  513.         confess "Usage: test_generate_depends_rdepends \$Packages_filename\n"
  514.                 if @_ < 1;

  515.         $count = parse_Packages(\%pkgs, shift);

  516.         print "count=$count\n";

  517.         generate_depends_rdepends(\%pkgs);

  518.         # $Data::Dumper::Maxdepth = 4;
  519.         print Dumper(\%pkgs);
  520. }

  521. # test_generate_depends_rdepends(@ARGV);
  522. # exit 0;


  523. # calculate_depends_score(\%pkgs, $name)
  524. #         calculate how much a package depends on other packages.
  525. #
  526. # eg. $depends_score = calculate_depends_score(\%pkgs, $pkg_name);
  527. #
  528. # dependence: calculate_score()
  529. #
  530. # note: must call generate_depends_rdepends() first.
  531. #
  532. sub calculate_depends_score {
  533.         my ($pkgs, $name) = @_;
  534.         my $pkg = $pkgs->{$name};

  535.         if (! defined($pkg)) {
  536.                 cluck "WARN: Package \`$name\` is not found, take zero as its depends score.\n";
  537.                 return 0;
  538.         }

  539.         return $pkg->{'_depends_score'} if exists $pkg->{'_depends_score'};

  540.         $pkg->{'_depends_score'} = 0;
  541.         return $pkg->{'_depends_score'} if ! exists $pkg->{'Depends'};

  542.         my ($alternative_depends, $maxscore, $score, $i, $p_name);

  543.         foreach $alternative_depends (@{$pkg->{'_depends'}}) {
  544.                 $maxscore = 0;
  545.                 #eg. $alternative_depends = [libc5, "1.2", "libc6", "2.3.2"];
  546.                 for ($i = 0; $i < @$alternative_depends; $i += 2) {
  547.                         $p_name = $alternative_depends->[$i];
  548.                         $score = calculate_depends_score($pkgs, $p_name);
  549.                         if ($score >= 0) {
  550.                                 $score += calculate_score($pkgs, $p_name);
  551.                                 $maxscore = $score if $maxscore < $score;
  552.                         } else {
  553.                                 my $found = 0;
  554.                                 foreach (@{$pkg->{'_rdepends'}}) {
  555.                                         if ($p_name eq $_) {
  556.                                                 $found = 1;
  557.                                                 last;
  558.                                         }
  559.                                 }

  560.                                 if ($found) {
  561.                                         # direct dependence is allowed, these
  562.                                         # two packages can be regarded as one
  563.                                         # package.
  564.                                 } else {
  565.                                         warn "ERROR! Circular dependence found: [$name] on [$p_name]\n";
  566.                                         # so we will get the wrong dependence path.
  567.                                         $pkg->{'_depends_score'} = - 100000000;
  568.                                         return $pkg->{'_depends_score'};
  569.                                 }
  570.                         }
  571.                 } # end for
  572.                 $pkg->{'_depends_score'} -= $maxscore;
  573.         }

  574.         # ok, there is no circular dependence.
  575.         $pkg->{'_depends_score'} = - $pkg->{'_depends_score'};

  576.         return $pkg->{'_depends_score'};
  577. }


  578. # calculate_rdepends_score(\%pkgs, $name)
  579. #         calculate how much a package is depended on by other packages.
  580. #
  581. # eg. $rdepends_score = calculate_rdepends_score(\%pkgs, $pkg_name);
  582. #
  583. # dependence: calculate_score()
  584. #
  585. # note: must call generate_depends_rdepends() first.
  586. #
  587. sub calculate_rdepends_score {
  588.         my ($pkgs, $name) = @_;
  589.         my $pkg = $pkgs->{$name};

  590.         if (! defined($pkg)) {
  591.                 cluck "WARN: Package \`$name\` is not found, take zero as its rdepends score.\n";
  592.                 return 0;
  593.         }

  594.         return $pkg->{'_rdepends_score'} if exists $pkg->{'_rdepends_score'};

  595.         $pkg->{'_rdepends_score'} = 0;
  596.         return $pkg->{'_rdepends_score'} if ! exists $pkg->{'_rdepends'};

  597.         my ($p_name, $score);

  598.         foreach $p_name (@{$pkg->{_rdepends}}) {
  599.                 $score = calculate_rdepends_score($pkgs, $p_name);
  600.                 if ($score >= 0) {
  601.                         $pkg->{'_rdepends_score'} -= $score + calculate_score($pkgs, $p_name);
  602.                 } else {
  603.                         my $found = 0;
  604.                         foreach (@{$pkgs->{$p_name}->{'_rdepends'}}) {
  605.                                 if ($name eq $_) {
  606.                                         $found = 1;
  607.                                         last;
  608.                                 }
  609.                         }

  610.                         if ($found) {
  611.                                 # direct dependence is allowed, these
  612.                                 # two packages can be regarded as one
  613.                                 # package.
  614.                         } else {
  615.                                 warn "ERROR! Circular dependence found: [$name] by [$p_name]\n";
  616.                                 # so we will get the wrong dependence path.
  617.                                 $pkg->{'_rdepends_score'} = - 100000000;
  618.                                 return $pkg->{'_rdepends_score'};
  619.                         }
  620.                 }
  621.         }

  622.         # ok, there is no circular dependence.
  623.         $pkg->{'_rdepends_score'} = - $pkg-> {'_rdepends_score'};

  624.         return $pkg->{'_rdepends_score'};
  625. }


  626. # test_calculate_depends_rdpends_score($Packages_file)
  627. #         test whether calculate_depends_score() and calculate_rdepends_score()
  628. # are implemented properly.
  629. #
  630. sub test_calculate_depends_rdpends_score {
  631.         my (%pkgs, $count);

  632.         confess "Usage: test_calculate_depends_rdpends_score \$Packages_filename\n"
  633.                 if @_ < 1;

  634.         $count = parse_Packages(\%pkgs, shift);

  635.         # print "count=$count\n";

  636.         generate_depends_rdepends(\%pkgs);

  637.         while (my ($name, $pkg) = each %pkgs) {
  638.                 print "$name\t\t", calculate_score(\%pkgs, $name),
  639.                         "\t", calculate_depends_score(\%pkgs, $name),
  640.                         "\t", calculate_rdepends_score(\%pkgs, $name),
  641.                         "\n";
  642.         }
  643. }

  644. # test_calculate_depends_rdpends_score(@ARGV);
  645. # exit(0);


  646. # compare_Packages(\%oldPackages, \%newPackages, %option)
  647. #         compare Packages of different version, for example, yesterday's and
  648. #         today's.
  649. #        return difference of two Packeges hash.
  650. #
  651. sub compare_Packages {
  652.         my ($oldpkgs, $newpkgs, %option) = @_;
  653.         my ($oldver, $newver);

  654.         foreach my $name (keys %$newpkgs) {
  655.                 next if (! exists($oldpkgs->{$name}));
  656.                 $oldver = $oldpkgs->{$name}->{"Version"};
  657.                 $newver = $newpkgs->{$name}->{"Version"};
  658.                 if ($oldver ne $newver) {
  659.                         print "$name: $oldver => $newver\n";
  660.                 }
  661.         }
  662. }


  663. # test_compare_Packages($oldPackages, $newPackages)
  664. #         test case for compare_Packages().
  665. #
  666. sub test_compare_Packages {
  667.         my (%oldpkgs, %newpkgs) = ();
  668.         my ($oldcount, $newcount);

  669.         confess "Usage: test_compare_Packages ",
  670.                         "\$old_Packages_filename \$new_Packages_filename\n"
  671.                 if @_ < 2;

  672.         $oldcount = parse_Packages(\%oldpkgs, shift);
  673.         $newcount = parse_Packages(\%newpkgs, shift);

  674.         print "old count=$oldcount, new count=$newcount\n";

  675.         compare_Packages(\%oldpkgs, \%newpkgs);
  676. }

  677. # test_compare_Packages(@ARGV);
  678. # exit 0;



  679. # vi:set ts=8 sw=8 noet nowrap ft=perl:
复制代码
回复 支持 反对

使用道具 举报

发表于 2006-5-25 14:56:06 | 显示全部楼层
看起来很棒的东西,能给出一些输出范例么?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-5-25 15:23:55 | 显示全部楼层
Post by FireMeteor
看起来很棒的东西,能给出一些输出范例么?



stat.log (sort.exe -r -n -k 4 stat.log) (因为有制表符,所以加上code标签保护一下, 不然都挤一块去了)
======

  1. libxrender1                                22          10358       77736015
  2. libfreetype6                               22           1142       54685778
  3. libxfixes3                                 22          14735       34401220
  4. libxcursor1                                22          35495       31797406
  5. libfontenc1                                21           5519       28837607
  6. xcursorgen                                 21          57419       28809897
  7. xfonts-utils                               21           7846       28805163
  8. makedepend                                 21            520       28804515
  9. sessreg                                    21            520       28804494
  10. imake                                      21           1061       28804494
  11. xutils                                     21          66930       28804473
  12. libglib2.0-0                               22            520       26218863
  13. libexpat1                                  22            520       14510631
  14. ttf-bitstream-vera                         21           8609       14402688
  15. wget                                       80           4985       14402265
  16. ttf-freefont                               21           8609       14400823
  17. ttf-dejavu                                 21           8609       14399230
  18. gsfonts-x11                                21          75580       14397858
  19. cabextract                                 20            520       14397738
  20. msttcorefonts                              20          84551       14397718
  21. fontconfig                                 20         117502       14397698
  22. libfontconfig1                             22         120370       14083273
  23. libxml2                                    22           1142        6403433
  24. libcairo2                                  22         144080        5268249
  25. libdbus-1-2                                20            520        5136055
  26. libidl0                                    22           1602        5059538
  27. liborbit2                                  22           3271        4993344
  28. python2.4-minimal                         100           1142        3786344
  29. libxft2                                    22         142916        3085262
  30. libxinerama1                               22          34411        3018898
  31. libxrandr2                                 22          44791        3002039
  32. libpango1.0-0                              22         991594        2631011
  33. gconf2-common                              22           3281        2595071
  34. libatk1.0-0                                22           1062        2592025
  35. libbz2-1.0                                 62            520        2540732
  36. readline-common                            65              0        2350099
  37. libreadline5                               62           1210        2349237
  38. python2.4                                  60           9910        2038896
  39. libhal1                                    22           1060        2002538
  40. libgconf2-4                                22           9444        1826844
  41. libavahi-common-data                       22              0        1772631
  42. libavahi-common3                           22            542        1772549
  43. python-minimal                            120           1242        1734539
  44. python                                     60          11332        1734479
  45. libpam-foreground                         100            520        1440909
  46. libpam-runtime                            105            620        1439544
  47. libpam0g                                  105           1245        1430527
  48. libgtk2.0-0                                22        2930630        1332405
  49. gcj-4.1-base                               22              0        1081756
  50. libbonobo2-common                          20           6748        1044298
  51. libbonobo2-0                               22          11123        1044276
  52. libhal-storage1                            22           2142         995634
  53. libsepol1                                 102            520         981140
  54. libselinux1                               102           1142         978675
  55. libcap1                                   102            520         861476
  56. ...

  57. stat.err
  58. =====
  59. WARN: Package `perlapi-5.8.7` is not found, take zero as its depends score.
  60. at apt-update-stat.pl line 634
  61.         main::calculate_depends_score('HASH(0x1cd7450)', 'perlapi-5.8.7') called at apt-update-stat.pl line 650
  62.         main::calculate_depends_score('HASH(0x1cd7450)', 'libtext-iconv-perl') called at apt-update-stat.pl line 650
  63.         main::calculate_depends_score('HASH(0x1cd7450)', 'debconf-i18n') called at apt-update-stat.pl line 650
  64.         main::calculate_depends_score('HASH(0x1cd7450)', 'debconf') called at apt-update-stat.pl line 650
  65.         main::calculate_depends_score('HASH(0x1cd7450)', 'libssl0.9.8') called at apt-update-stat.pl line 650
  66.         main::calculate_depends_score('HASH(0x1cd7450)', 'libneon25') called at apt-update-stat.pl line 650
  67.         main::calculate_depends_score('HASH(0x1cd7450)', 'librpm4') called at apt-update-stat.pl line 650
  68.         main::calculate_depends_score('HASH(0x1cd7450)', 'libapt-rpm-pkg-libc6.3-6-0') called at apt-update-stat.pl line 186
  69.         main::do_stat('./current/*Packages.gz') called at apt-update-stat.pl line 116
  70. WARN: Package `perlapi-5.8.7` is not found, take zero as its score.
  71. at apt-update-stat.pl line 501
  72.         main::calculate_score('HASH(0x1cd7450)', 'perlapi-5.8.7') called at apt-update-stat.pl line 652
  73.         main::calculate_depends_score('HASH(0x1cd7450)', 'libtext-iconv-perl') called at apt-update-stat.pl line 650
  74.         main::calculate_depends_score('HASH(0x1cd7450)', 'debconf-i18n') called at apt-update-stat.pl line 650
  75.         main::calculate_depends_score('HASH(0x1cd7450)', 'debconf') called at apt-update-stat.pl line 650
  76.         main::calculate_depends_score('HASH(0x1cd7450)', 'libssl0.9.8') called at apt-update-stat.pl line 650
  77.         main::calculate_depends_score('HASH(0x1cd7450)', 'libneon25') called at apt-update-stat.pl line 650
  78.         main::calculate_depends_score('HASH(0x1cd7450)', 'librpm4') called at apt-update-stat.pl line 650
  79.         main::calculate_depends_score('HASH(0x1cd7450)', 'libapt-rpm-pkg-libc6.3-6-0') called at apt-update-stat.pl line 186
  80.         main::do_stat('./current/*Packages.gz') called at apt-update-stat.pl line 116
  81. WARN: Package `perlapi-5.8.7` is not found, take zero as its depends score.
  82. at apt-update-stat.pl line 634
  83.         main::calculate_depends_score('HASH(0x1cd7450)', 'perlapi-5.8.7') called at apt-update-stat.pl line 650
  84.         main::calculate_depends_score('HASH(0x1cd7450)', 'libtext-charwidth-perl') called at apt-update-stat.pl line 650
  85.         main::calculate_depends_score('HASH(0x1cd7450)', 'libtext-wrapi18n-perl') called at apt-update-stat.pl line 650
  86.         main::calculate_depends_score('HASH(0x1cd7450)', 'debconf-i18n') called at apt-update-stat.pl line 650
  87.         main::calculate_depends_score('HASH(0x1cd7450)', 'debconf') called at apt-update-stat.pl line 650
  88.         main::calculate_depends_score('HASH(0x1cd7450)', 'libssl0.9.8') called at apt-update-stat.pl line 650
  89.         main::calculate_depends_score('HASH(0x1cd7450)', 'libneon25') called at apt-update-stat.pl line 650
  90.         main::calculate_depends_score('HASH(0x1cd7450)', 'librpm4') called at apt-update-stat.pl line 650
  91.         main::calculate_depends_score('HASH(0x1cd7450)', 'libapt-rpm-pkg-libc6.3-6-0') called at apt-update-stat.pl line 186
  92.         main::do_stat('./current/*Packages.gz') called at apt-update-stat.pl line 116
  93. .....
复制代码

compare的没得比, 我现在在公司Windows机器上, 拿最新的Packages测试的, 没
旧的可比, 输出跟上面类似, 就是cmp.log多一列  1.2.1=>1.2.2这样的字样.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-5-25 16:06:27 | 显示全部楼层
dump 出的main的$pkgs, 这个比较好玩. 全部导出太慢了, 极为消耗内存.
main: 4332 pakcages

  1. $VAR1 = {
  2.           'groff-base' => {
  3.                             '_rdepends' => [
  4.                                              'groff',
  5.                                              'man-db'
  6.                                            ],
  7.                             '_depends' => [
  8.                                             [
  9.                                               'libc6',
  10.                                               '>= 2.3.4-1'
  11.                                             ],
  12.                                             [
  13.                                               'libgcc1',
  14.                                               '>= 1:4.0.2'
  15.                                             ],
  16.                                             [
  17.                                               'libstdc++6',
  18.                                               '>= 4.0.2-4'
  19.                                             ]
  20.                                           ],
  21.                             'Section' => 'text',
  22.                             'Suggests' => 'groff',
  23.                             'Version' => '1.18.1.1-11',
  24.                             'Depends' => 'libc6 (>= 2.3.4-1), libgcc1 (>= 1:4.0.2), libstdc++6 (>= 4.0.2-4)',
  25.                             'Priority' => 'standard',
  26.                             'Package' => 'groff-base',
  27.                             'Conflicts' => 'groff (<< 1.17-1), jgroff (<< 1.17-1), pmake (<< 1.45-7), troffcvt (<< 1.04-14)'
  28.                           },
  29.           'libtest-pod-perl' => {
  30.                                   '_depends' => [
  31.                                                   [
  32.                                                     'perl',
  33.                                                     '>= 5.6.0-16'
  34.                                                   ],
  35.                                                   [
  36.                                                     'libio-stringy-perl',
  37.                                                     undef
  38.                                                   ],
  39.                                                   [
  40.                                                     'libpod-simple-perl',
  41.                                                     undef
  42.                                                   ]
  43.                                                 ],
  44.                                   'Version' => '1.20-2',
  45.                                   'Section' => 'perl',
  46.                                   'Depends' => 'perl (>= 5.6.0-16), libio-stringy-perl, libpod-simple-perl',
  47.                                   'Priority' => 'optional',
  48.                                   'Package' => 'libtest-pod-perl'
  49.                                 },
  50.           'language-pack-gnome-bg-base' => {
  51.                                              '_rdepends' => [
  52.                                                               'language-pack-gnome-bg'
  53.                                                             ],
  54.                                              '_depends' => [
  55.                                                              [
  56.                                                                'locales',
  57.                                                                '>= 2.3.6'
  58.                                                              ],
  59.                                                              [
  60.                                                                'language-pack-gnome-bg',
  61.                                                                '>= 1:6.06+20060522'
  62.                                                              ]
  63.                                                            ],
  64.                                              'Section' => 'translations',
  65.                                              'Recommends' => 'language-support-bg',
  66.                                              'Pre-Depends' => 'dpkg (>= 1.10.27ubuntu1)',
  67.                                              'Version' => '1:6.06+20060522',
  68.                                              'Depends' => 'locales (>= 2.3.6), language-pack-gnome-bg (>= 1:6.06+20060522)',
  69.                                              'Priority' => 'optional',
  70.                                              'Package' => 'language-pack-gnome-bg-base',
  71.                                              'Conflicts' => 'language-pack-gnome-bg (<< 1:6.06+20060522)'
  72.                                            },
  73.           'gcompris-sound-ru' => {
  74.                                    '_rdepends' => [
  75.                                                     'edubuntu-desktop'
  76.                                                   ],
  77.                                    '_depends' => [
  78.                                                    [
  79.                                                      'gcompris-data',
  80.                                                      '= 7.2-1ubuntu6'
  81.                                                    ]
  82.                                                  ],
  83.                                    'Section' => 'games',
  84.                                    'Recommends' => 'gcompris',
  85.                                    'Version' => '7.2-1ubuntu6',
  86.                                    'Depends' => 'gcompris-data (= 7.2-1ubuntu6)',
  87.                                    'Priority' => 'optional',
  88.                                    'Package' => 'gcompris-sound-ru'
  89.                                  },
  90.           'python-egenix-mxproxy' => {
  91.                                        '_rdepends' => [
  92.                                                         'kubuntu-desktop',
  93.                                                         'edubuntu-desktop',
  94.                                                         'ubuntu-desktop'
  95.                                                       ],
  96.                                        '_depends' => [
  97.                                                        [
  98.                                                          'python',
  99.                                                          '<< 2.5'
  100.                                                        ],
  101.                                                        [
  102.                                                          'python2.4-egenix-mxproxy',
  103.                                                          undef
  104.                                                        ],
  105.                                                        [
  106.                                                          'python',
  107.                                                          '>= 2.4'
  108.                                                        ],
  109.                                                        [
  110.                                                          'python2.4-egenix-mxproxy',
  111.                                                          undef
  112.                                                        ]
  113.                                                      ],
  114.                                        'Version' => '2.0.6ubuntu1-1ubuntu4',
  115.                                        'Section' => 'python',
  116.                                        'Depends' => 'python (<< 2.5), python2.4-egenix-mxproxy, python (>= 2.4), python2.4-egenix-mxproxy',
  117.                                        'Priority' => 'optional',
  118.                                        'Package' => 'python-egenix-mxproxy'
  119.                                      },
  120.           'libxevie1-dbg' => {
  121.                                '_depends' => [
  122.                                                [
  123.                                                  'x11-common',
  124.                                                  '>= 7.0.0-0ubuntu3'
  125.                                                ],
  126.                                                [
  127.                                                  'libxevie1',
  128.                                                  '= 1:1.0.0-0ubuntu4'
  129.                                                ]
  130.                                              ],
  131.                                'Version' => '1:1.0.0-0ubuntu4',
  132.                                'Section' => 'libdevel',
  133.                                'Depends' => 'x11-common (>= 7.0.0-0ubuntu3), libxevie1 (= 1:1.0.0-0ubuntu4)',
  134.                                'Priority' => 'extra',
  135.                                'Package' => 'libxevie1-dbg'
  136.                              },
  137.           'libwnck-dev' => {
  138.                              '_depends' => [
  139.                                              [
  140.                                                'libwnck18',
  141.                                                '= 2.14.1-0ubuntu2'
  142.                                              ],
  143.                                              [
  144.                                                'libgtk2.0-dev',
  145.                                                '>= 2.5.4'
  146.                                              ],
  147.                                              [
  148.                                                'libstartup-notification0-dev',
  149.                                                '>= 0.5'
  150.                                              ],
  151.                                              [
  152.                                                'libxres-dev',
  153.                                                undef
  154.                                              ]
  155.                                            ],
  156.                              'Version' => '2.14.1-0ubuntu2',
  157.                              'Section' => 'libdevel',
  158.                              'Depends' => 'libwnck18 (= 2.14.1-0ubuntu2), libgtk2.0-dev (>= 2.5.4), libstartup-notification0-dev (>= 0.5), libxres-dev',
  159.                              'Priority' => 'optional',
  160.                              'Package' => 'libwnck-dev'
  161.                            },
  162. ....
复制代码
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表