menuselect-dummy 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. #!/usr/bin/perl -w
  2. # menuselect - a simple drop-in replacement of the batch-mode menuselect
  3. # included with Asterisk.
  4. #
  5. # Copyright (C) 2008 by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  20. # USA
  21. # Installation: copy this script to menuselect/menuselect . Copy the
  22. # included Makefile as menuselect/Makefile and run:
  23. #
  24. # make -C makefile dummies
  25. #
  26. # It takes configuration from build_tools/conf . Sample config file:
  27. #
  28. # By default all modules will be built (except those marked not be
  29. # used by default)
  30. #
  31. # # exclude: Don't try to build the following modules.
  32. # #exclude app_test
  33. #
  34. # # You can have multiple items in each line, and multiple lines.
  35. # # Each item is a perl regular expression that must match the whole
  36. # # module name.
  37. # #exclude res_config_.*
  38. #
  39. # # include: syntax is the same as exclude. Overrides exclude and
  40. # # modules that are marked as disabled by defualt:
  41. # #include res_config_sqlite3 app_skel
  42. #
  43. # # If you want to make sure some modules will be conifgured to build,
  44. # # you can require them. If modules that match any of the 'require'
  45. # # pattern are not configured to build, menuselect will panic.
  46. # # Same pattern rules apply here. Why would you want that? I have no
  47. # # idea.
  48. # #require chan_h323 app_directory
  49. #
  50. # # random - the value for this keyword is a number between 1 and
  51. # # 100. The higher it is, more chances not to include each module.
  52. # # Writes the list of modules that got hit to
  53. # # build_tools/mods_removed_random .
  54. # # Note that unlike 'make randomconfig' and such the random
  55. # # configuration changes each time you run 'make', thus if a build
  56. # # failed you should first read build_tools/mods_removed_random
  57. # # before re-running make.
  58. # #random 10
  59. #
  60. # # Anything after a '#' is ignored, and likewise empty lines.
  61. # # Naturally.
  62. use strict;
  63. use Getopt::Long;
  64. # Holds global dependncy information. Keys are module names.
  65. my %ModInfo = ();
  66. # extract configuration from kernel modules:
  67. my $AutoconfDepsFile = "build_tools/menuselect-deps";
  68. my $AutoconfOptsFile = "makeopts";
  69. my %ConfigureOpts = (); #
  70. # configuration file to read for some directives:
  71. my $ConfFile = "build_tools/conf";
  72. my $DumpFile = 'build_tools/dump_deps';
  73. # Modules removed randomely:
  74. my $RandomeModsFile = "build_tools/mods_removed_random";
  75. my $MakedepsFile = "menuselect.makedeps";
  76. my $MakeoptsFile = "menuselect.makeopts";
  77. # If those modules are not present, the build will fail (PCRE patterns)
  78. my @RequiredModules = ();
  79. my @Subdirs = qw/addons apps bridges cdr cel channels codecs formats funcs main pbx res tests utils/;
  80. my @XmlCategories = 'cflags';
  81. # Modules should not bother building (PCRE patterns)
  82. my @ExcludedModules = ();
  83. # Do try building those. Overrides 'exclude' and 'defaultenable: no'
  84. my @IncludedModules = ();
  85. # A chance to rule-out a module randomely.
  86. my $RandomKnockoutFactor = 0;
  87. sub warning($) {
  88. my $msg = shift;
  89. print STDERR "$0: Warning: $msg\n";
  90. }
  91. # Convert XML syntax to mail-header-like syntax:
  92. # <var>value</var> --> Var: value
  93. sub extract_xml_key($) {
  94. my %attr = ();
  95. my $xml_line = shift;
  96. if ($xml_line !~ m{^\s*<([a-z_A-Z0-9]+)(\s+([^>]*))?>([^<]*)</\1>}) {
  97. warning "parsed empty value from XML line $xml_line";
  98. return ('', ''); # warn?
  99. }
  100. my ($var, $val) = ($1, $4);
  101. $var =~ s{^[a-z]}{\u$&};
  102. if (defined $3) {
  103. my $attr_text = $3;
  104. while ($attr_text =~ /^( *([^=]+)="([^"]+)")/) {
  105. my ($var, $val) = ($2, $3);
  106. $attr_text =~ s/^$1//;
  107. $attr{$var} = $val;
  108. }
  109. }
  110. return ($var, $val, %attr);
  111. }
  112. # Get information embedded in source files from a subdirectory.
  113. # First parameter is the subdirectory and further ones are the actual
  114. # source files.
  115. sub get_subdir_module_info {
  116. my $subdir = shift;
  117. my @files = @_;
  118. my $dir = uc($subdir);
  119. foreach my $src (@files) {
  120. open SRC,$src or die "Can't read from source file $src: $!\n";
  121. $src =~ m|.*/([^/]*)\.c|;
  122. my $mod_name = $1;
  123. my %data = (
  124. Type=>'module',
  125. Module=>$mod_name,
  126. Dir=> $dir,
  127. Avail=>1
  128. );
  129. while (<SRC>) {
  130. next unless (m|^/\*\*\* MODULEINFO| .. m|^ *[*]+/|);
  131. next unless (m|^[A-Z]| || m|^\s*<|);
  132. # At this point we can assume we're in the module
  133. # info section.
  134. chomp;
  135. my ($var, $val, %attr) = extract_xml_key($_);
  136. foreach (keys %attr) {
  137. push @{$data{$_}},($attr{$_});
  138. }
  139. if ($var =~ /^(Depend|Use)$/i) {
  140. # use uppercase for dependency names;
  141. $val = uc($val);
  142. }
  143. if ( ! exists $data{$var} ) {
  144. $data{$var} = [$val];
  145. } else {
  146. push @{$data{$var}},($val);
  147. }
  148. }
  149. close SRC;
  150. $ModInfo{uc($mod_name)} = \%data;
  151. }
  152. }
  153. # extract embedded information in all the source tree.
  154. sub extract_subdirs {
  155. for my $subdir(@_) {
  156. get_subdir_module_info($subdir, <$subdir/*.c> , <$subdir/*.cc>);
  157. }
  158. }
  159. # parse a partial XML document that is included as an input
  160. # for menuselect in a few places. Naturally a full-fledged XML parsing
  161. # will not be done here. A line-based parsing that happens to work will
  162. # have to do.
  163. sub parse_menuselect_xml_file($) {
  164. my $file_name = shift;
  165. open XML,$file_name or
  166. die "Failed opening XML file $file_name: $!.\n";
  167. my $header = <XML>;
  168. $header =~ /^\s*<category\s+name="MENUSELECT_([^"]+)"\s/;
  169. my $category = $1;
  170. my $member;
  171. while(<XML>){
  172. next unless (m{^\s*<(/?[a-z]+)[>\s]});
  173. my $tag = $1;
  174. if ($tag eq 'member') {
  175. if (! m{^\s*<member\s+name="([^"]+)" displayname="([^"]+)"\s*>}){
  176. warning "Bad XML member line: $_ ($file_name:$.)\n";
  177. next;
  178. }
  179. my ($name, $display_name) = ($1, $2);
  180. $member = {
  181. Type => 'XML',
  182. Dir => $category,
  183. Module => $1,
  184. DisplayName => $2,
  185. Defaultenabled => ['no'],
  186. Avail => 1,
  187. };
  188. } elsif ($tag eq '/member') {
  189. $ModInfo{$member->{Module}} = $member;
  190. } elsif ($tag eq '/category') {
  191. last;
  192. } else {
  193. if (! m/^\s*<([a-z]+)>([^<]+)</) {
  194. warning "(1) Unknown XML line $_ ($file_name:$.)\n";
  195. next
  196. }
  197. my ($key, $val) = extract_xml_key($_);
  198. if ($key eq '') {
  199. warning "Unknown XML line $_ ($file_name:$.)\n";
  200. next
  201. }
  202. if (! exists $member->{$key}) {
  203. $member->{$key} = [];
  204. }
  205. # Make sure dependencies are upper-case.
  206. # FIXME: this is not the proper place for such a fix
  207. $val = uc($val) if ($key =~ /Depend|Use/);
  208. # Using "unshift' rather than 'push'.
  209. # For a singleton value this makes the action an
  210. # override, as only the first value counts.
  211. # For a list value, however, it means a reversed
  212. # order.
  213. unshift @{$member->{$key}}, ($val);
  214. }
  215. }
  216. close XML;
  217. }
  218. # Dump our data structure to a file.
  219. sub dump_deps($) {
  220. my $file = shift;
  221. open OUTPUT,">$file" or
  222. die "cannot open category file $file for writing: $!\n";
  223. foreach my $mod_name (sort keys %ModInfo) {
  224. print OUTPUT "Key: $mod_name\n";
  225. my $data = $ModInfo{$mod_name};
  226. foreach my $var (sort keys %{$data} ) {
  227. my $val = $$data{$var};
  228. if (ref($val) eq 'ARRAY') {
  229. print OUTPUT $var.": ". (join ", ", @$val)."\n";
  230. } else {
  231. print OUTPUT "$var: $val\n";
  232. }
  233. }
  234. print OUTPUT "\n";
  235. }
  236. close OUTPUT;
  237. }
  238. # Get the available libraries that autoconf generated.
  239. sub get_autoconf_deps() {
  240. open DEPS, $AutoconfDepsFile or
  241. die "Failed to open $AutoconfDepsFile. Aborting: $!\n";
  242. my @deps_list = (<DEPS>);
  243. foreach (@deps_list){
  244. chomp;
  245. my ($lib, $avail_val) = split(/=/);
  246. my ($avail, $avail_old) = split(/:/, $avail_val);
  247. my $disabled = 0;
  248. if ($avail == -1) {
  249. $disabled = 1;
  250. $avail = 0;
  251. }
  252. $ModInfo{$lib} = {
  253. Type=>'lib', Avail=>$avail, Disabled => $disabled
  254. };
  255. if (defined $avail_old) {
  256. $ModInfo{$lib}{AvailOld} = $avail_old;
  257. }
  258. # FIXME:
  259. if (($avail ne "0") && ($avail ne "1")) {
  260. warning "Library $lib has invalid availability ".
  261. "value <$avail> (check $AutoconfDepsFile).\n";
  262. }
  263. }
  264. close DEPS;
  265. }
  266. # Get the available libraries that autoconf generated.
  267. sub get_autoconf_opts() {
  268. open OPTS, $AutoconfOptsFile or
  269. die "Failed to open $AutoconfOptsFile. Aborting: $!\n";
  270. while (<OPTS>) {
  271. chomp;
  272. next if /^(#|$)/;
  273. my ($var, $val) = split /\s*=\s*/, $_, 2;
  274. $ConfigureOpts{$var} = $val;
  275. }
  276. close OPTS;
  277. if (not exists $ConfigureOpts{AST_DEVMODE}) {
  278. $ConfigureOpts{AST_DEVMODE} = 'no';
  279. }
  280. }
  281. # Read our specific config file.
  282. #
  283. # Its format:
  284. #
  285. # keyword values
  286. #
  287. # values are always a spaces-separated list.
  288. sub read_conf() {
  289. open CONF,$ConfFile or return;
  290. while (<CONF>) {
  291. # remove comments and empty lines:
  292. chomp;
  293. s/#.*$//;
  294. next if /^\s*$/;
  295. my ($keyword, @value) = split;
  296. if ($keyword eq 'exclude') {
  297. push @ExcludedModules, @value;
  298. } elsif ($keyword eq 'include') {
  299. push @IncludedModules, @value;
  300. } elsif ($keyword eq 'require') {
  301. push @RequiredModules, @value;
  302. } elsif ($keyword eq 'random') {
  303. $RandomKnockoutFactor = $value[0] / 100;
  304. } else {
  305. warning "unknown keyword $keyword in line $. of $ConfFile.";
  306. }
  307. }
  308. }
  309. # generate menuselect.makedeps.
  310. # In this file menuselect writes dependecies of each module. CFLAGS will
  311. # then automatically include for each module the _INCLUDE and LDFLAGS
  312. # will include the _LIBS from all the depedencies of the module.
  313. sub gen_makedeps() {
  314. open MAKEDEPSS, ">$MakedepsFile" or
  315. die "Failed to open deps file $MakedepsFile for writing. Aborting: $!\n";
  316. for my $mod_name (sort keys %ModInfo) {
  317. next unless ($ModInfo{$mod_name}{Type} eq 'module');
  318. my $mod = $ModInfo{$mod_name};
  319. my @deps = ();
  320. # if we have Depend or Use, put their values into
  321. # @deps . If we have none, move on.
  322. push @deps, @{$mod->{Depend}} if (exists $mod->{Depend});
  323. push @deps, @{$mod->{Use}} if (exists $mod->{Use});
  324. next unless @deps;
  325. # TODO: don't print dependencies that are not external libs.
  326. # Not done yet until I figure out if this is safe.
  327. my $dep = join(' ', @deps);
  328. print MAKEDEPSS "MENUSELECT_DEPENDS_".$mod->{Module}."=$dep\n";
  329. }
  330. close MAKEDEPSS;
  331. }
  332. # Set modules from patterns specified by 'exclude' in the configuration file
  333. # to exclude modules from building (mark them as unavailable).
  334. sub apply_excluded_patterns() {
  335. foreach my $pattern (@ExcludedModules) {
  336. my @excluded = grep {/^$pattern$/i} (keys %ModInfo);
  337. foreach (@excluded) {
  338. $ModInfo{$_}{Avail} = 0;
  339. }
  340. }
  341. }
  342. # Set modules from patterns specified by 'include' in the configuration
  343. # file to exclude from building (mark them as available).
  344. sub apply_included_patterns() {
  345. foreach my $pattern (@IncludedModules) {
  346. my @included = grep {/^$pattern$/i} (keys %ModInfo);
  347. foreach (@included) {
  348. $ModInfo{$_}{Avail} = 1;
  349. }
  350. }
  351. }
  352. # If user set the "random" config to anything > 0, drop some random
  353. # modules. May help expose wrong dependencies.
  354. sub apply_random_drop() {
  355. return if ($RandomKnockoutFactor <= 0);
  356. open MODS_LIST, ">$RandomeModsFile" or
  357. die "Failed to open modules list file $RandomeModsFile for writing. Aborting: $!\n";
  358. for my $mod (keys %ModInfo) {
  359. next unless ($ModInfo{$mod}{Type} eq 'module');
  360. next unless (rand() < $RandomKnockoutFactor);
  361. $ModInfo{$mod}{Avail} = 0;
  362. $ModInfo{$mod}{RandomKill} = 1;
  363. print MODS_LIST $ModInfo{$mod}{Module}."\n";
  364. }
  365. close MODS_LIST;
  366. }
  367. sub check_required_patterns() {
  368. my @failed = ();
  369. foreach my $pattern (@RequiredModules) {
  370. my @required = grep {/^$pattern$/i} (keys %ModInfo);
  371. foreach my $mod (@required) {
  372. if ((! exists $ModInfo{$mod}{Checked}) ||
  373. (! $ModInfo{$mod}{Checked}) )
  374. {
  375. push @failed, $mod;
  376. }
  377. }
  378. }
  379. return unless (@failed);
  380. my $failed_str = join ' ',@failed;
  381. die("Missing dependencies for the following modules: $failed_str\n");
  382. }
  383. # Disable building for modules that were marked in the embedded module
  384. # information as disabled for building by default.
  385. sub apply_default_enabled() {
  386. foreach my $mod (keys %ModInfo) {
  387. if ((exists $ModInfo{$mod}{Defaultenabled}) &&
  388. $ModInfo{$mod}{Defaultenabled}[0] eq 'no')
  389. {
  390. $ModInfo{$mod}{Avail} = 0;
  391. }
  392. }
  393. }
  394. # We found a dependency we don't know about. Warn the user, and add
  395. # information about it:
  396. sub handle_unknown_dep($$) {
  397. my ($dep_mod, $mod) = @_;
  398. my $mod_info = {
  399. Type => 'Unknown',
  400. Avail => 0,
  401. Checked => 0,
  402. };
  403. $ModInfo{$dep_mod} = $mod_info;
  404. warning "Unknown dependency module $dep_mod (for e.g. $mod)\n";
  405. }
  406. # recursively check dependency for a module.
  407. #
  408. # We run a scan for modules. Modules marked as 'Checked' are ones we
  409. # have already fully verified to have proper dependencies.
  410. #
  411. # We can only use a module or library marked as Avail => 1 (library
  412. # available or module not excluded).
  413. sub check_module($);
  414. sub check_module($) {
  415. my $mod = shift;
  416. # we checked it:
  417. if (exists $ModInfo{$mod}{Checked}) {
  418. return $ModInfo{$mod}{Checked};
  419. }
  420. # A library has no dependencies of its own.
  421. if ($ModInfo{$mod}{Type} eq 'lib') {
  422. return ($ModInfo{$mod}{Avail} || 0);
  423. }
  424. # An excluded module.
  425. if ($ModInfo{$mod}{Avail} == 0) {
  426. return 0;
  427. }
  428. if (! exists $ModInfo{$mod}{Depend}) {
  429. $ModInfo{$mod}{Checked} = 1;
  430. return 1;
  431. }
  432. my $deps_checked = 1; # may be reset below on failures:
  433. if (exists $ModInfo{$mod}{Tested}) {
  434. # this probably means a circular dependency of some sort.
  435. warning "Got to module $mod that is already tested.";
  436. }
  437. $ModInfo{$mod}{Tested} = 1;
  438. foreach my $dep_mod (@{$ModInfo{$mod}{Depend}} ) {
  439. if (!exists ${ModInfo}{$dep_mod}) {
  440. handle_unknown_dep($dep_mod, $mod);
  441. return 0;
  442. }
  443. $deps_checked &= check_module($dep_mod);
  444. last if(!$deps_checked) # no point testing further if we failed.
  445. }
  446. $ModInfo{$mod}{Checked} = $deps_checked;
  447. return $deps_checked;
  448. }
  449. # The main dependency resolver function.
  450. sub resolve_deps() {
  451. apply_default_enabled();
  452. apply_excluded_patterns();
  453. apply_included_patterns();
  454. foreach my $mod (keys %ModInfo) {
  455. check_module($mod);
  456. }
  457. }
  458. # generate menuselect.makeopts.
  459. # The values in this file obey to different semantics:
  460. # 1. For modules, a module will be built unles listed here
  461. # 2. For XML values (sounds, CFLAGS) it will be enabled if listed here
  462. sub gen_makeopts() {
  463. open MAKEDEPS, ">$MakeoptsFile" or
  464. die "Failed to open opts file $MakeoptsFile for writing. Aborting: $!\n";
  465. my %Subdirs;
  466. foreach my $mod (sort keys %ModInfo) {
  467. next unless ($ModInfo{$mod}{Type} =~ /^(module|XML)$/);
  468. if ($ModInfo{$mod}{Type} eq 'XML') {
  469. next unless ($ModInfo{$mod}{Checked});
  470. } else {
  471. next if ($ModInfo{$mod}{Checked});
  472. }
  473. my $dir = $ModInfo{$mod}{Dir};
  474. if (! exists $Subdirs{$dir}) {
  475. $Subdirs{$dir} = [];
  476. }
  477. push @{$Subdirs{$dir}},( $ModInfo{$mod}{Module} );
  478. }
  479. foreach my $dir (sort keys %Subdirs) {
  480. my $deps = join(' ', @{$Subdirs{$dir}});
  481. print MAKEDEPS "MENUSELECT_$dir=$deps\n";
  482. }
  483. close MAKEDEPS;
  484. }
  485. # Main function for --check-deps
  486. sub check_dependencies() {
  487. read_conf();
  488. extract_subdirs(@Subdirs);
  489. get_autoconf_opts();
  490. parse_menuselect_xml_file('build_tools/cflags.xml');
  491. if ($ConfigureOpts{AST_DEVMODE} eq 'yes') {
  492. parse_menuselect_xml_file('build_tools/cflags-devmode.xml');
  493. }
  494. parse_menuselect_xml_file('sounds/sounds.xml');
  495. apply_random_drop();
  496. get_autoconf_deps();
  497. #dump_deps('build_tools/dump_deps_before_resolve');
  498. resolve_deps();
  499. # Handy debugging:
  500. dump_deps($DumpFile);
  501. check_required_patterns();
  502. gen_makedeps();
  503. gen_makeopts();
  504. }
  505. #
  506. # The main program start here
  507. #
  508. sub read_dump() {
  509. my %items = ();
  510. my $saved_rs = $/;
  511. $/ = "\n\n";
  512. open DUMP_FILE,$DumpFile or die "Can't read from dump file $DumpFile\n";
  513. while (<DUMP_FILE>) {
  514. my %item = ();
  515. my @item_lines = split /\n\r?/;
  516. foreach (@item_lines) {
  517. my ($var, $value) = split /: /, $_, 2;
  518. $item{$var} = $value;
  519. }
  520. # FIXME: dependencies are a list. This should not be a
  521. # special case.
  522. if (exists $item{Depend}) {
  523. $item{Depend} = [split /\s*,\s*/,$item{Depend}];
  524. }
  525. $items{$item{Key}} = \%item;
  526. }
  527. close DUMP_FILE;
  528. $/ = $saved_rs;
  529. return \%items;
  530. }
  531. # Explain why a module (read from the dump file) was not enabled.
  532. # (We assume here that $item->{Avail} is 0)
  533. sub fail_reason($) {
  534. my $item = shift;
  535. if ($item->{Type} eq 'lib') {
  536. return " Not found: system library";
  537. } elsif ($item->{Type} eq 'XML') {
  538. if ($item->{Defaultenabled} !~ /^y/) {
  539. return "Not enabled";
  540. } else {
  541. return "Missing dependencies";
  542. }
  543. } elsif ($item->{Type} eq 'module') {
  544. if (exists ($item->{Defaultenabled}) &&
  545. $item->{Defaultenabled} =~ /^n/) {
  546. return "Disabled";
  547. } else {
  548. return "Missing dependencies";
  549. }
  550. }
  551. }
  552. sub item_used($) {
  553. my $item = shift;
  554. my $type = $item->{Type};
  555. return $item->{Avail} if ($type eq 'lib');
  556. return $item->{Checked};
  557. }
  558. sub print_module_status {
  559. my $flag_verbose = shift;
  560. my $items = read_dump();
  561. my %items_matched = ();
  562. foreach my $pattern (@_) {
  563. foreach (keys %$items) {
  564. if (/$pattern/i) {
  565. $items_matched{$_} = 1;
  566. }
  567. }
  568. }
  569. my @items_list = sort keys %items_matched;
  570. foreach my $item_name (@items_list) {
  571. my $item = $items->{$item_name};
  572. if ($flag_verbose) {
  573. printf "%s %-8s %-30s\n",
  574. (item_used($item)? 'Y':'n'),
  575. $item->{Type},
  576. $item->{Key};
  577. if (!$item->{Avail}) {
  578. my $reason = fail_reason($item);
  579. print " $reason\n";
  580. }
  581. foreach (@{$item->{Depend}}) {
  582. my $depmod = $items->{$_};
  583. printf(" * %-12s ",$_);
  584. print (item_used($depmod)? '': "un");
  585. print "available\n";
  586. }
  587. } else {
  588. printf "%s %-8s %-30s",
  589. (item_used($item)? 'Y':'n'),
  590. $item->{Type},
  591. $item->{Key};
  592. foreach (@{$item->{Depend}}) {
  593. my $depmod = $items->{$_};
  594. if (item_used($depmod)) {
  595. print "$_ ";
  596. } else {
  597. printf "[%s] ", $_;
  598. }
  599. }
  600. print "\n";
  601. }
  602. }
  603. }
  604. sub usage() {
  605. print "$0: menuselect reimplementation\n";
  606. print "\n";
  607. print "Usage:\n";
  608. print "$0 # menuselect processing\n";
  609. print "$0 -m|--modinfo|--moduls-info PATTERN # Status of modules\n";
  610. print "$0 -v|--verbose # verbose (modinfo)\n";
  611. print "$0 -c|--check-deps # Check for dependencies\n";
  612. print "\n";
  613. print "PATTERN is a partial perl regex. Use '-m .' to list all.\n";
  614. }
  615. my @module_status = ();
  616. my $flag_verbose = 0;
  617. my $action = '';
  618. my $rc = GetOptions(
  619. 'modinfo|modules-info|m=s' => \@module_status,
  620. 'verbose|v' => \$flag_verbose,
  621. 'check-deps|c:s' => sub { $action = 'check_dependencies'},
  622. 'help|h' => sub { usage(); exit 0 },
  623. );
  624. if (!$rc) {
  625. usage();
  626. exit $rc;
  627. }
  628. if (@module_status) {
  629. $action = 'module_status';
  630. }
  631. if ($action eq 'module_status') {
  632. print_module_status($flag_verbose, @module_status);
  633. exit 0;
  634. } elsif ( $action eq 'check_dependencies') {
  635. check_dependencies();
  636. } else {
  637. usage(); exit(1);
  638. }