Mnemosyne is a abstract distribution originally designed for ROCKLinux, but currently only support the trunk of OpenSDE.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

675 lines
16 KiB

  1. #!/usr/bin/perl
  2. # --- SDE-COPYRIGHT-NOTE-BEGIN ---
  3. # This copyright note is auto-generated by ./scripts/Create-CopyPatch.
  4. #
  5. # Filename: target/mnemosyne/mnemosyne.pl
  6. # Copyright (C) 2002 - 2006 Alejandro Mery
  7. #
  8. # More information can be found in the files COPYING and README.
  9. #
  10. # This program is free software; you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License as published by
  12. # the Free Software Foundation; version 2 of the License. A copy of the
  13. # GNU General Public License can be found in the file COPYING.
  14. # --- SDE-COPYRIGHT-NOTE-END ---
  15. use warnings;
  16. use strict;
  17. use IPC::Open2;
  18. use constant {ALL => 0, ASK => 1, CHOICE => 2 };
  19. %::FOLDER=();
  20. %::MODULE=();
  21. sub scandir {
  22. my ($pkgseldir,$prefix) = @_;
  23. my %current=('location', $pkgseldir, 'var', "CFGTEMP_$prefix");
  24. # $current{desc,var} for sub-pkgsel dirs
  25. if ($pkgseldir ne $::ROOT) {
  26. my ($relative,$dirvar,$dirname);
  27. $_ = $pkgseldir;
  28. $relative = (m/^$::ROOT\/(.*)/i)[0];
  29. $dirvar = "CFGTEMP_$prefix\_$relative";
  30. $dirvar =~ tr,a-z\/ ,A-Z__,;
  31. $dirname=$relative;
  32. $dirname=~ s/.*\///g;
  33. ($current{desc} = $dirname) =~ s/_/ /g;
  34. $current{var} = $dirvar;
  35. }
  36. # make this folder global
  37. $::FOLDER{$current{var}} = \%current;
  38. {
  39. # make scandir recursive
  40. my @children;
  41. opendir(my $DIR, $pkgseldir);
  42. foreach( grep { ! /^\./ } sort readdir($DIR) ) {
  43. $_ = "$pkgseldir/$_";
  44. if ( -d $_ ) {
  45. my $subdir = scandir($_,$prefix);
  46. push @children,$subdir;
  47. } else {
  48. my $module=scanmodule($_,$prefix,$current{var});
  49. if ($module) {
  50. push @children,$module unless grep(/^$module$/,@children);
  51. }
  52. }
  53. }
  54. closedir $DIR;
  55. $current{children} = \@children;
  56. return $current{var};
  57. }
  58. }
  59. sub scanmodule {
  60. my ($file,$prefix,$folder)=@_;
  61. my (%current,$FILE);
  62. # this defines dir,key,option and kind acording to the following format.
  63. # $dir/[$prio-]$var[$option].$kind
  64. do {
  65. my ($dir,$key,$option,$kind);
  66. m/^(.*)\/(\d+-)?([^\.]*).?([^\.]*)?\.([^\/\.]*)/i;
  67. ($dir,$key,$option,$kind) = ($1,$3,$4,$5);
  68. if ($kind eq 'choice') { $current{kind} = CHOICE; $current{option} = $option; }
  69. elsif ($kind eq 'all') { $current{kind} = ALL; }
  70. elsif ($kind eq 'ask') { $current{kind} = ASK; }
  71. else { return; }
  72. $current{location} = $dir;
  73. $current{key} = $key;
  74. $current{file} = $file;
  75. } for $file;
  76. open($FILE,'<',$file);
  77. while(<$FILE>) {
  78. if (/^#[^#: ]+: /) {
  79. my ($field,$value) = m/^#([^#: ]+): (.*)$/i;
  80. if ($field eq 'Description') {
  81. $current{desc} = $value;
  82. } elsif ($field eq 'Variable') {
  83. $current{var} = $value;
  84. } elsif ($field eq 'Default') {
  85. $current{default} = $value;
  86. } elsif ($field eq 'Forced') {
  87. $current{forced} = $value;
  88. } elsif ($field eq 'Imply') {
  89. $current{imply} = $value;
  90. } elsif ($field eq 'Dependencies') {
  91. $current{deps} = $value;
  92. # } else {
  93. # print "$file:$field:$value.\n";
  94. }
  95. }
  96. }
  97. close($FILE);
  98. # var name
  99. $current{var} = uc $current{key}
  100. unless exists $current{var};
  101. $current{var} = "SDECFG_$prefix\_" . $current{var}
  102. unless $current{var} =~ /^SDECFG_$prefix\_/;
  103. # for choices, we use $option instead of $key as description
  104. ($current{desc} = $current{option}) =~ s/_/ /g
  105. if exists $current{option} && ! exists $current{desc};
  106. ($current{desc} = $current{key}) =~ s/_/ /g
  107. unless exists $current{desc};
  108. # dependencies
  109. # NOTE: don't use spaces on the pkgsel file, only to delimite different dependencies
  110. if (exists $current{deps}) {
  111. my @deps;
  112. for ( split (/\s+/,$current{deps}) ) {
  113. $_="SDECFG_$prefix\_$_" unless /^(SDECFG|CFGTEMP)/;
  114. if (/=/) {
  115. m/(.*?)(==|!=|=)(.*)/i;
  116. $_="\"\$$1\" $2 $3";
  117. } else {
  118. $_="\"\$$_\" == 1";
  119. }
  120. push @deps,$_;
  121. }
  122. $current{deps} = \@deps;
  123. }
  124. # forced modules
  125. if (exists $current{forced}) {
  126. my @forced;
  127. for ( split (/\s+/,$current{forced}) ) {
  128. $_="SDECFG_$prefix\_$_" unless /^SDECFG/;
  129. $_="$_=1" unless /=/;
  130. push @forced,$_;
  131. }
  132. $current{forced} = \@forced;
  133. }
  134. # implied options
  135. if (exists $current{imply}) {
  136. my @imply = split (/\s+/,$current{imply});
  137. $current{imply} = \@imply;
  138. }
  139. # make this module global
  140. if ( $current{kind} == CHOICE ) {
  141. # prepare the option for this choice
  142. my %option;
  143. for ('desc','forced','imply','deps','option','file') {
  144. $option{$_}=$current{$_} if exists $current{$_};
  145. }
  146. if ( exists $::MODULE{$current{var}} ) {
  147. push @{ $::MODULE{$current{var}}{options} },\%option;
  148. } else {
  149. # prepare and add this choice module
  150. my @options = (\%option);
  151. $::MODULE{$current{var}} = {
  152. 'kind', CHOICE,
  153. 'options', \@options,
  154. };
  155. for ('key','location','var') {
  156. $::MODULE{$current{var}}{$_}=$current{$_}
  157. if exists $current{$_};
  158. }
  159. }
  160. } else {
  161. $::MODULE{$current{var}} = {};
  162. for ('key','location','var','desc','forced','deps','file','kind') {
  163. $::MODULE{$current{var}}{$_}=$current{$_}
  164. if exists $current{$_};
  165. }
  166. }
  167. # default value
  168. $::MODULE{$current{var}}{folder} = $folder;
  169. $::MODULE{$current{var}}{default} = $current{default}
  170. if exists $current{default};
  171. return $current{var};
  172. }
  173. sub process_modules {
  174. my ($READ,$WRITE,$pid);
  175. my $i=0;
  176. $pid = open2($READ, $WRITE, 'tsort');
  177. # prepare topographic modules map
  178. for my $module (values %::MODULE) {
  179. my $related;
  180. if ($module->{kind} == CHOICE) {
  181. for (@{ $module->{options} }) {
  182. my $option = $_;
  183. for (@{exists $option->{deps} ? $option->{deps} : []} ) {
  184. my $dep = (m/"\$([^"]+)"/i)[0];
  185. print $WRITE "$dep $module->{var}\n";
  186. $related=1;
  187. }
  188. for (@{exists $option->{forced} ? $option->{forced} : []} ) {
  189. my $forced = (m/([^"]+)=/i)[0];
  190. print $WRITE "$module->{var} $forced\n";
  191. $related=1;
  192. }
  193. }
  194. } else {
  195. for (@{exists $module->{deps} ? $module->{deps} : []} ) {
  196. my $dep = (m/"\$([^"]+)"/i)[0];
  197. print $WRITE "$dep $module->{var}\n";
  198. $related=1;
  199. }
  200. for (@{exists $module->{forced} ? $module->{forced} : []} ) {
  201. my $forced = (m/([^"]+)=/i)[0];
  202. print $WRITE "$module->{var} $forced\n";
  203. $related=1;
  204. }
  205. }
  206. if (! $related) {
  207. print $WRITE "$module->{var} $module->{var}\n";
  208. }
  209. }
  210. close($WRITE);
  211. # and populate the sorted list
  212. my @sorted;
  213. while(<$READ>) {
  214. if (/(.*)\n/) { push @sorted, $1; }
  215. }
  216. waitpid $pid,0;
  217. die if $?;
  218. # and remember the sorted list
  219. $::MODULES=\@sorted;
  220. }
  221. sub process_folders {
  222. my ($READ,$WRITE,$pid);
  223. $pid = open2($READ, $WRITE, 'tsort | tac');
  224. # prepare topographic modules map
  225. for my $folder (values %::FOLDER) {
  226. for ( exists $folder->{children} ? grep(!/^SDECFG/, @{$folder->{children}}) : [] ) {
  227. print $WRITE "$folder->{var} $_\n";
  228. }
  229. }
  230. close($WRITE);
  231. # and populate the sorted list
  232. my @sorted;
  233. while(<$READ>) {
  234. if (/(.*)\n/) { push @sorted, $1; }
  235. }
  236. waitpid $pid,0;
  237. die if $?;
  238. # and remember the sorted list
  239. $::FOLDERS=\@sorted;
  240. }
  241. sub render_widgets_folder {
  242. my ($folder,$offset) = @_;
  243. for (@{$folder->{children}}) {
  244. if (/^CFGTEMP/) {
  245. my $subfolder=$::FOLDER{$_};
  246. print "\n${offset}# $_\n${offset}#\n";
  247. # opening
  248. print "${offset}if [ \"\$$subfolder->{var}\" == 1 ]; then\n";
  249. print "${offset}\tmenu_begin $subfolder->{var} '$subfolder->{desc}'\n";
  250. print "${offset}fi\n";
  251. render_widgets_folder($::FOLDER{$_},"$offset\t");
  252. # closing
  253. print "${offset}if [ \"\$$subfolder->{var}\" == 1 ]; then\n";
  254. print "${offset}\tmenu_end\n";
  255. print "${offset}fi\n";
  256. } else {
  257. my $module=$::MODULE{$_};
  258. my $var=$module->{var};
  259. my $conffile="$module->{location}/$module->{key}.conf"
  260. if -f "$module->{location}/$module->{key}.conf";
  261. my $noconffile="$module->{location}/$module->{key}-no.conf"
  262. if -f "$module->{location}/$module->{key}-no.conf";
  263. print "${offset}# $var\n";
  264. if ($module->{kind} == CHOICE) {
  265. # CHOICE
  266. my $tmpvar = "CFGTEMP_$1" if $var =~ m/^SDECFG_(.*)/i;
  267. my $listvar = "$tmpvar\_LIST";
  268. my $defaultvar = "$tmpvar\_DEFAULT";
  269. print "${offset}if \[ -n \"\$$var\" \]; then\n";
  270. print "${offset}\tchoice $var \$$defaultvar \$$listvar\n";
  271. print "${offset}\t. $conffile\n" if $conffile;
  272. print "${offset}\telse\n" if $noconffile;
  273. print "${offset}\t. $noconffile\n" if $noconffile;
  274. print "${offset}fi\n";
  275. } elsif ($module->{kind} == ASK) {
  276. # ASK
  277. my $default=0;
  278. $default = $module->{default} if exists $module->{default};
  279. print "${offset}if \[ -n \"\$$var\" \]; then\n";
  280. print "${offset}\tbool '$module->{desc}' $module->{var} $default\n";
  281. print "${offset}\t\[ \"\$$var\" == 1 \] && . $conffile\n" if $conffile;
  282. print "${offset}\t\[ \"\$$var\" != 1 \] && . $noconffile\n" if $noconffile;
  283. print "${offset}fi\n";
  284. } elsif ($conffile) {
  285. # ALL, only if $conffile
  286. print "${offset}if \[ -n \"\$$var\" \]; then\n";
  287. print "${offset}\t. $conffile\n" if $conffile;
  288. print "${offset}\telse\n" if $noconffile;
  289. print "${offset}\t. $noconffile\n" if $noconffile;
  290. print "${offset}fi\n";
  291. }
  292. }
  293. }
  294. }
  295. sub render_widgets {
  296. open(my $FILE,'>',$_[0]);
  297. my $root="CFGTEMP_$_[1]";
  298. select $FILE;
  299. render_widgets_folder($::FOLDER{$root},'');
  300. select STDOUT;
  301. close($FILE);
  302. }
  303. sub pkgsel_parse {
  304. my ($action,$patternlist) = @_;
  305. if ($action eq 'X' or $action eq 'x' ) {
  306. $action = '$1="X"';
  307. } elsif ($action eq 'O' or $action eq 'o') {
  308. $action = '$1="O"';
  309. } elsif ($action eq '-') {
  310. $action = 'next';
  311. } else {
  312. $action = '{ exit; }';
  313. }
  314. my ($address,$first,$others)= ('','( ','&& ');
  315. for (split(/\s+/,$patternlist)) {
  316. if (! $address and $_ eq '!') {
  317. $address = '! ';
  318. $others = '|| $4"/"$5 ~';
  319. } else {
  320. $_="\*/$_" unless /\//;
  321. s,[^a-zA-Z0-9_/\*+\.-],,g;
  322. s,([/\.\+]),\\$1,g;
  323. s,\*,[^/]*,g;
  324. next unless $_;
  325. $address = "$address$first";
  326. $address = "$address / $_ /";
  327. $first = "$others";
  328. }
  329. =for nobody
  330. [ "$pattern" ] || continue
  331. address="$address$first"
  332. address="$address / $pattern /"
  333. first=" $others"
  334. =cut
  335. }
  336. print "\techo '$address ) { $action; }'\n";
  337. return 1;
  338. }
  339. sub render_awkgen {
  340. open(my $OUTPUT,'>',$_[0]);
  341. my $root="CFGTEMP_$_[1]";
  342. select $OUTPUT;
  343. # initially change packages $4 and $5 to be able to correctly match repo based.
  344. print "echo '{'\n";
  345. print "echo '\trepo=\$4 ;'\n";
  346. print "echo '\tpkg=\$5 ;'\n";
  347. print "echo '\t\$5 = \$4 \"/\" \$5 ;'\n";
  348. print "echo '\t\$4 = \"placeholder\" ;'\n";
  349. print "echo '}'\n";
  350. render_awkgen_folder($::FOLDER{$root});
  351. # ... restore $4 and $5, and print the resulting line
  352. print "echo '\n{'\n";
  353. print "echo '\t\$4=repo ;'\n";
  354. print "echo '\t\$5=pkg ;'\n";
  355. print "echo '\tprint ;'\n";
  356. print "echo '}'\n";
  357. select STDOUT;
  358. close($OUTPUT);
  359. }
  360. sub render_awkgen_folder {
  361. my ($folder) = @_;
  362. for (@{$folder->{children}}) {
  363. if (/^CFGTEMP/) {
  364. render_awkgen_folder($::FOLDER{$_});
  365. } else {
  366. my $module=$::MODULE{$_};
  367. if ($module->{kind} == CHOICE) {
  368. my %options;
  369. # the list of options
  370. for (@{ $module->{options} }) {
  371. my $option = $_;
  372. my @array=("\"\$$module->{var}\" == $_->{option}");
  373. $options{$_->{option}} = \@array;
  374. }
  375. # and their implyed options
  376. for (@{ $module->{options} }) {
  377. my $option = $_;
  378. for (@{exists $option->{imply}? $option->{imply} : [] }) {
  379. push @{$options{$_}},
  380. "\"\$$module->{var}\" == $option->{option}";
  381. }
  382. }
  383. print "\n";
  384. # and finally, render.
  385. for (@{ $module->{options} }) {
  386. print "if [ " . join(' -o ',@{ $options{ $_->{option} }}). " ]; then\n";
  387. open(my $FILE,'<',$_->{file});
  388. my $hasrules=0;
  389. while(<$FILE>) {
  390. next if /^#/;
  391. next if /^\s*$/;
  392. pkgsel_parse($1,$2) if m/^([^\s]+)\s+(.*)\s*\n?$/i;
  393. $hasrules=1;
  394. }
  395. close($FILE);
  396. print "\ttrue\n" unless $hasrules;
  397. print "fi\n";
  398. }
  399. } else {
  400. print "\nif [ \"\$$module->{var}\" == 1 ]; then\n";
  401. open(my $FILE,'<',$module->{file});
  402. my $hasrules=0;
  403. while(<$FILE>) {
  404. next if /^#/;
  405. next if /^\s*$/;
  406. pkgsel_parse($1,$2) if m/^([^\s]+)\s+(.*)\s*\n?$/i;
  407. $hasrules=1;
  408. }
  409. close($FILE);
  410. print "\ttrue\n" unless $hasrules;
  411. print "fi\n";
  412. }
  413. }
  414. }
  415. }
  416. sub render_rules_module {
  417. my ($module,$offset) = @_;
  418. my $var = $module->{var};
  419. if ($module->{kind} == CHOICE) {
  420. my $tmpvar = "CFGTEMP_$1" if $var =~ m/^SDECFG_(.*)/i;
  421. my $listvar = "$tmpvar\_LIST";
  422. my $defaultvar = "$tmpvar\_DEFAULT";
  423. my $default = "undefined";
  424. my $forcer;
  425. $default = $module->{default} if exists $module->{default};
  426. # initialize the list
  427. print "${offset}$listvar=\n";
  428. print "${offset}$defaultvar=$default\n";
  429. print "${offset}\[ -n \"\$$var\" \] || $var=$default\n\n";
  430. for ( @{ $module->{options} } ) {
  431. my $option = $_;
  432. (my $desc = $option->{desc}) =~ s/ /_/g;
  433. # has something to force?
  434. if (exists $option->{forced}) { $forcer = 1; }
  435. if (exists $option->{deps}) {
  436. print "${offset}if [ " .
  437. join(' -a ', @{ $option->{deps} } ) .
  438. " ]; then\n";
  439. print "${offset}\t$listvar=\"\$$listvar $option->{option} $desc\"\n";
  440. print "${offset}fi\n";
  441. } else {
  442. print "${offset}$listvar=\"\$$listvar $option->{option} $desc\"\n";
  443. }
  444. }
  445. # enable the folder display
  446. print "${offset}if \[ -n \"\$$listvar\" \]; then\n";
  447. print "${offset}\t$module->{folder}=1\n";
  448. print "${offset}else\n";
  449. print "${offset}\tunset $module->{var}\n";
  450. print "${offset}fi\n";
  451. # has something to force?
  452. if ($forcer) {
  453. print "\n${offset}case \"\$$var\" in\n";
  454. for ( @{ $module->{options} } ) {
  455. my $option = $_;
  456. if (exists $option->{forced}) {
  457. print "${offset}\t$option->{option})\n";
  458. for ( @{ $option->{forced} } ) {
  459. print "$offset\t\t$_\n";
  460. print "$offset\t\tSDECFGSET_$1\n" if $_ =~ m/^SDECFG_(.*)/i;
  461. }
  462. print "${offset}\t\t;;\n";
  463. }
  464. }
  465. print "${offset}esac\n";
  466. }
  467. # printref($var,$module,$offset);
  468. } elsif ($module->{kind} == ASK) {
  469. my $default=0;
  470. $default = $module->{default} if exists $module->{default};
  471. #enable the folder display
  472. print "$offset$module->{folder}=1\n";
  473. # and set the default value if none is set.
  474. print "$offset\[ -n \"\$$var\" \] || $var=$default\n";
  475. # if enabled, append pkgsel and force the forced
  476. if (exists $module->{forced}) {
  477. print "\n${offset}if [ \"\$$var\" == 1 ]; then\n";
  478. for ( @{ $module->{forced} } ) {
  479. print "$offset\t$_\n";
  480. print "$offset\tSDECFGSET_$1\n" if $_ =~ m/^SDECFG_(.*)/i;
  481. }
  482. print $offset."fi\n";
  483. }
  484. } else {
  485. # just enable the feature
  486. print "$offset$var=1\n";
  487. # forced list doesn't make sense for {kind} == ALL
  488. }
  489. }
  490. sub render_rules_nomodule {
  491. my ($module,$offset) = @_;
  492. my $var = $module->{var};
  493. # unset the choice list, and the var
  494. if ($module->{kind} == CHOICE) {
  495. my $listvar = "CFGTEMP_$1_LIST" if $var =~ m/^SDECFG_(.*)/i;
  496. print "${offset}unset $listvar\n";
  497. }
  498. print "${offset}unset SDECFGSET_$1\n" if $var =~ m/^SDECFG_(.*)/i;
  499. print "${offset}unset $var\n";
  500. }
  501. sub render_rules {
  502. open(my $FILE,'>',$_[0]);
  503. my $root="CFGTEMP_$_[1]";
  504. select $FILE;
  505. # clean folder enablers
  506. print "#\n# folder enablers\n#\n\n";
  507. for (@$::FOLDERS) { print "$_=\n" unless /^$root$/; }
  508. # pkgsel list
  509. for (@$::MODULES) {
  510. if (exists $::MODULE{$_}) {
  511. my $module = $::MODULE{$_};
  512. print "\n#\n# $module->{var} ("
  513. . ($module->{kind} == ALL ? "ALL" : ($module->{kind} == ASK ? "ASK" : "CHOICE" ) )
  514. . ")\n#\n";
  515. if (exists $module->{deps}) {
  516. print "if [ " . join(' -a ', @{ $module->{deps} } ) . " ]; then\n";
  517. render_rules_module($module,"\t");
  518. print "else\n";
  519. render_rules_nomodule($module,"\t");
  520. print "fi\n";
  521. } else {
  522. render_rules_module($module,"");
  523. }
  524. }
  525. }
  526. print "\n#\n# enable folder with enabled subfolders\n#\n";
  527. for (@$::FOLDERS) {
  528. my $folder = $::FOLDER{$_};
  529. my @subdirs = grep(/^CFGTEMP/,@{$folder->{children}});
  530. if ( @subdirs ) {
  531. print "if [ -n \"\$".join('$', @subdirs )."\" ]; then\n";
  532. print "\t$folder->{var}=1\n";
  533. print "fi\n";
  534. }
  535. }
  536. select STDOUT;
  537. close($FILE);
  538. }
  539. # print the content of a hash
  540. sub printref {
  541. my ($name,$ref,$offset) = @_;
  542. my $typeof = ref($ref);
  543. print "$offset$name:";
  544. if ($typeof eq '') {
  545. print " '$ref'\n";
  546. } elsif ($typeof eq 'HASH') {
  547. print "\n";
  548. for (sort keys %{ $ref }) {
  549. printref($_,$ref->{$_},"$offset\t");
  550. }
  551. } elsif ($typeof eq 'ARRAY') {
  552. my $i=0;
  553. print "\n";
  554. for (@{ $ref }) {
  555. printref("[$i]",$_,"$offset\t");
  556. $i++;
  557. }
  558. } else {
  559. print " -> $typeof\n";
  560. }
  561. }
  562. if ($#ARGV != 4) {
  563. print "Usage mnemosyne.pl: <pkgseldir> <prefix> <configfile> <rulesfile> <awkgenerator>\n";
  564. exit (1);
  565. }
  566. $| = 1;
  567. $::ROOT=$ARGV[0];
  568. scandir($ARGV[0],$ARGV[1]);
  569. process_modules();
  570. process_folders();
  571. render_rules($ARGV[3],$ARGV[1]);
  572. render_widgets($ARGV[2],$ARGV[1]);
  573. render_awkgen($ARGV[4],$ARGV[1]);