0001
0002
0003
0004 use strict;
0005 use Pod::Usage;
0006 use Getopt::Long;
0007 use File::Find;
0008 use Fcntl ':mode';
0009 use Cwd 'abs_path';
0010
0011 my $help;
0012 my $man;
0013 my $debug;
0014 my $arch;
0015 my $feat;
0016 my $enable_fname;
0017
0018 my $basename = abs_path($0);
0019 $basename =~ s,/[^/]+$,/,;
0020
0021 my $prefix=$basename . "../Documentation/features";
0022
0023
0024
0025 my $status_size = 1;
0026 my $description_size = 1;
0027
0028 GetOptions(
0029 "debug|d+" => \$debug,
0030 "dir=s" => \$prefix,
0031 'help|?' => \$help,
0032 'arch=s' => \$arch,
0033 'feat=s' => \$feat,
0034 'feature=s' => \$feat,
0035 "enable-fname" => \$enable_fname,
0036 man => \$man
0037 ) or pod2usage(2);
0038
0039 pod2usage(1) if $help;
0040 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
0041
0042 pod2usage(1) if (scalar @ARGV < 1 || @ARGV > 2);
0043
0044 my ($cmd, $arg) = @ARGV;
0045
0046 pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate"
0047 && $cmd ne "ls" && $cmd ne "list");
0048
0049 require Data::Dumper if ($debug);
0050
0051 my %data;
0052 my %archs;
0053
0054
0055
0056
0057 sub parse_error($$$$) {
0058 my ($file, $ln, $msg, $data) = @_;
0059
0060 $data =~ s/\s+$/\n/;
0061
0062 print STDERR "Warning: file $file#$ln:\n\t$msg";
0063
0064 if ($data ne "") {
0065 print STDERR ". Line\n\t\t$data";
0066 } else {
0067 print STDERR "\n";
0068 }
0069 }
0070
0071
0072
0073
0074
0075 my $h_name = "Feature";
0076 my $h_kconfig = "Kconfig";
0077 my $h_description = "Description";
0078 my $h_subsys = "Subsystem";
0079 my $h_status = "Status";
0080 my $h_arch = "Architecture";
0081
0082 my $max_size_name = length($h_name);
0083 my $max_size_kconfig = length($h_kconfig);
0084 my $max_size_description = length($h_description);
0085 my $max_size_subsys = length($h_subsys);
0086 my $max_size_status = length($h_status);
0087
0088 my $max_size_arch = 0;
0089 my $max_size_arch_with_header;
0090 my $max_description_word = 0;
0091
0092 sub parse_feat {
0093 my $file = $File::Find::name;
0094
0095 my $mode = (stat($file))[2];
0096 return if ($mode & S_IFDIR);
0097 return if ($file =~ );
0098 return if (!($file =~ ));
0099
0100 if ($enable_fname) {
0101 printf ".. FILE %s\n", abs_path($file);
0102 }
0103
0104 my $subsys = "";
0105 $subsys = $2 if ( );
0106
0107 if (length($subsys) > $max_size_subsys) {
0108 $max_size_subsys = length($subsys);
0109 }
0110
0111 my $name;
0112 my $kconfig;
0113 my $description;
0114 my $comments = "";
0115 my $last_status;
0116 my $ln;
0117 my %arch_table;
0118
0119 print STDERR "Opening $file\n" if ($debug > 1);
0120 open IN, $file;
0121
0122 while(<IN>) {
0123 $ln++;
0124
0125 if () {
0126 $name = $1;
0127 if (length($name) > $max_size_name) {
0128 $max_size_name = length($name);
0129 }
0130 next;
0131 }
0132 if () {
0133 $kconfig = $1;
0134 if (length($kconfig) > $max_size_kconfig) {
0135 $max_size_kconfig = length($kconfig);
0136 }
0137 next;
0138 }
0139 if () {
0140 $description = $1;
0141 if (length($description) > $max_size_description) {
0142 $max_size_description = length($description);
0143 }
0144
0145 foreach my $word (split /\s+/, $description) {
0146 if (length($word) > $max_description_word) {
0147 $max_description_word = length($word);
0148 }
0149 }
0150
0151 next;
0152 }
0153 next if ();
0154 next if ();
0155 next if ();
0156
0157 if () {
0158 $comments .= "$1\n";
0159 next;
0160 }
0161 if () {
0162 my $a = $1;
0163 my $status = $2;
0164
0165 if (length($status) > $max_size_status) {
0166 $max_size_status = length($status);
0167 }
0168 if (length($a) > $max_size_arch) {
0169 $max_size_arch = length($a);
0170 }
0171
0172 $status = "---" if ($status =~ );
0173
0174 $archs{$a} = 1;
0175 $arch_table{$a} = $status;
0176 next;
0177 }
0178
0179
0180 parse_error($file, $ln, "line is invalid", $_);
0181 }
0182 close IN;
0183
0184 if (!$name) {
0185 parse_error($file, $ln, "Feature name not found", "");
0186 return;
0187 }
0188
0189 parse_error($file, $ln, "Subsystem not found", "") if (!$subsys);
0190 parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig);
0191 parse_error($file, $ln, "Description not found", "") if (!$description);
0192
0193 if (!%arch_table) {
0194 parse_error($file, $ln, "Architecture table not found", "");
0195 return;
0196 }
0197
0198 $data{$name}->{where} = $file;
0199 $data{$name}->{subsys} = $subsys;
0200 $data{$name}->{kconfig} = $kconfig;
0201 $data{$name}->{description} = $description;
0202 $data{$name}->{comments} = $comments;
0203 $data{$name}->{table} = \%arch_table;
0204
0205 $max_size_arch_with_header = $max_size_arch + length($h_arch);
0206 }
0207
0208
0209
0210
0211 sub output_arch_table {
0212 my $title = "Feature status on $arch architecture";
0213
0214 print "=" x length($title) . "\n";
0215 print "$title\n";
0216 print "=" x length($title) . "\n\n";
0217
0218 print "=" x $max_size_subsys;
0219 print " ";
0220 print "=" x $max_size_name;
0221 print " ";
0222 print "=" x $max_size_kconfig;
0223 print " ";
0224 print "=" x $max_size_status;
0225 print " ";
0226 print "=" x $max_size_description;
0227 print "\n";
0228 printf "%-${max_size_subsys}s ", $h_subsys;
0229 printf "%-${max_size_name}s ", $h_name;
0230 printf "%-${max_size_kconfig}s ", $h_kconfig;
0231 printf "%-${max_size_status}s ", $h_status;
0232 printf "%-${max_size_description}s\n", $h_description;
0233 print "=" x $max_size_subsys;
0234 print " ";
0235 print "=" x $max_size_name;
0236 print " ";
0237 print "=" x $max_size_kconfig;
0238 print " ";
0239 print "=" x $max_size_status;
0240 print " ";
0241 print "=" x $max_size_description;
0242 print "\n";
0243
0244 foreach my $name (sort {
0245 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
0246 ("\L$a" cmp "\L$b")
0247 } keys %data) {
0248 next if ($feat && $name ne $feat);
0249
0250 my %arch_table = %{$data{$name}->{table}};
0251 printf "%-${max_size_subsys}s ", $data{$name}->{subsys};
0252 printf "%-${max_size_name}s ", $name;
0253 printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig};
0254 printf "%-${max_size_status}s ", $arch_table{$arch};
0255 printf "%-s\n", $data{$name}->{description};
0256 }
0257
0258 print "=" x $max_size_subsys;
0259 print " ";
0260 print "=" x $max_size_name;
0261 print " ";
0262 print "=" x $max_size_kconfig;
0263 print " ";
0264 print "=" x $max_size_status;
0265 print " ";
0266 print "=" x $max_size_description;
0267 print "\n";
0268 }
0269
0270
0271
0272
0273 sub list_arch_features {
0274 print "#\n# Kernel feature support matrix of the '$arch' architecture:\n#\n";
0275
0276 foreach my $name (sort {
0277 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
0278 ("\L$a" cmp "\L$b")
0279 } keys %data) {
0280 next if ($feat && $name ne $feat);
0281
0282 my %arch_table = %{$data{$name}->{table}};
0283
0284 my $status = $arch_table{$arch};
0285 $status = " " x ((4 - length($status)) / 2) . $status;
0286
0287 printf " %${max_size_subsys}s/ ", $data{$name}->{subsys};
0288 printf "%-${max_size_name}s: ", $name;
0289 printf "%-5s| ", $status;
0290 printf "%${max_size_kconfig}s # ", $data{$name}->{kconfig};
0291 printf " %s\n", $data{$name}->{description};
0292 }
0293 }
0294
0295
0296
0297
0298 sub output_feature {
0299 my $title = "Feature $feat";
0300
0301 print "=" x length($title) . "\n";
0302 print "$title\n";
0303 print "=" x length($title) . "\n\n";
0304
0305 print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys});
0306 print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig});
0307
0308 my $desc = $data{$feat}->{description};
0309 $desc =~ s/^([a-z])/\U$1/;
0310 $desc =~ s/\.?\s*//;
0311 print "\n$desc.\n\n";
0312
0313 my $com = $data{$feat}->{comments};
0314 $com =~ s/^\s+//;
0315 $com =~ s/\s+$//;
0316 if ($com) {
0317 print "Comments\n";
0318 print "--------\n\n";
0319 print "$com\n\n";
0320 }
0321
0322 print "=" x $max_size_arch_with_header;
0323 print " ";
0324 print "=" x $max_size_status;
0325 print "\n";
0326
0327 printf "%-${max_size_arch}s ", $h_arch;
0328 printf "%-${max_size_status}s", $h_status . "\n";
0329
0330 print "=" x $max_size_arch_with_header;
0331 print " ";
0332 print "=" x $max_size_status;
0333 print "\n";
0334
0335 my %arch_table = %{$data{$feat}->{table}};
0336 foreach my $arch (sort keys %arch_table) {
0337 printf "%-${max_size_arch}s ", $arch;
0338 printf "%-${max_size_status}s\n", $arch_table{$arch};
0339 }
0340
0341 print "=" x $max_size_arch_with_header;
0342 print " ";
0343 print "=" x $max_size_status;
0344 print "\n";
0345 }
0346
0347
0348
0349
0350
0351 sub matrix_lines($$$) {
0352 my $desc_size = shift;
0353 my $status_size = shift;
0354 my $header = shift;
0355 my $fill;
0356 my $ln_marker;
0357
0358 if ($header) {
0359 $ln_marker = "=";
0360 } else {
0361 $ln_marker = "-";
0362 }
0363
0364 $fill = $ln_marker;
0365
0366 print "+";
0367 print $fill x $max_size_name;
0368 print "+";
0369 print $fill x $desc_size;
0370 print "+";
0371 print $ln_marker x $status_size;
0372 print "+\n";
0373 }
0374
0375 sub output_matrix {
0376 my $title = "Feature status on all architectures";
0377 my $notcompat = "Not compatible";
0378
0379 print "=" x length($title) . "\n";
0380 print "$title\n";
0381 print "=" x length($title) . "\n\n";
0382
0383 my $desc_title = "$h_kconfig / $h_description";
0384
0385 my $desc_size = $max_size_kconfig + 4;
0386 if (!$description_size) {
0387 $desc_size = $max_size_description if ($max_size_description > $desc_size);
0388 } else {
0389 $desc_size = $description_size if ($description_size > $desc_size);
0390 }
0391 $desc_size = $max_description_word if ($max_description_word > $desc_size);
0392
0393 $desc_size = length($desc_title) if (length($desc_title) > $desc_size);
0394
0395 $max_size_status = length($notcompat) if (length($notcompat) > $max_size_status);
0396
0397
0398 my $min_status_size = $max_size_status + $max_size_arch + 6;
0399 $status_size = $min_status_size if ($status_size < $min_status_size);
0400
0401
0402 my $cur_subsys = "";
0403 foreach my $name (sort {
0404 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) or
0405 ("\L$a" cmp "\L$b")
0406 } keys %data) {
0407
0408 if ($cur_subsys ne $data{$name}->{subsys}) {
0409 if ($cur_subsys ne "") {
0410 printf "\n";
0411 }
0412
0413 $cur_subsys = $data{$name}->{subsys};
0414
0415 my $title = "Subsystem: $cur_subsys";
0416 print "$title\n";
0417 print "=" x length($title) . "\n\n";
0418
0419
0420 matrix_lines($desc_size, $status_size, 0);
0421
0422 printf "|%-${max_size_name}s", $h_name;
0423 printf "|%-${desc_size}s", $desc_title;
0424
0425 printf "|%-${status_size}s|\n", "Status per architecture";
0426 matrix_lines($desc_size, $status_size, 1);
0427 }
0428
0429 my %arch_table = %{$data{$name}->{table}};
0430 my $cur_status = "";
0431
0432 my (@lines, @descs);
0433 my $line = "";
0434 foreach my $arch (sort {
0435 ($arch_table{$b} cmp $arch_table{$a}) or
0436 ("\L$a" cmp "\L$b")
0437 } keys %arch_table) {
0438
0439 my $status = $arch_table{$arch};
0440
0441 if ($status eq "---") {
0442 $status = $notcompat;
0443 }
0444
0445 if ($status ne $cur_status) {
0446 if ($line ne "") {
0447 push @lines, $line;
0448 $line = "";
0449 }
0450 $line = "- **" . $status . "**: " . $arch;
0451 } elsif (length($line) + length ($arch) + 2 < $status_size) {
0452 $line .= ", " . $arch;
0453 } else {
0454 push @lines, $line;
0455 $line = " " . $arch;
0456 }
0457 $cur_status = $status;
0458 }
0459 push @lines, $line if ($line ne "");
0460
0461 my $description = $data{$name}->{description};
0462 while (length($description) > $desc_size) {
0463 my $d = substr $description, 0, $desc_size;
0464
0465
0466
0467
0468 if (!($d =~ s/^(.*)\s+.*/$1/)) {
0469 $d = substr $d, 0, -1;
0470 push @descs, "$d\\";
0471 $description =~ s/^\Q$d\E//;
0472 } else {
0473 push @descs, $d;
0474 $description =~ s/^\Q$d\E\s+//;
0475 }
0476 }
0477 push @descs, $description;
0478
0479
0480 push @lines, "" while (scalar(@lines) < 2 + scalar(@descs));
0481
0482 my $ln = 0;
0483 for my $line(@lines) {
0484 if (!$ln) {
0485 printf "|%-${max_size_name}s", $name;
0486 printf "|%-${desc_size}s", "``" . $data{$name}->{kconfig} . "``";
0487 } elsif ($ln >= 2 && scalar(@descs)) {
0488 printf "|%-${max_size_name}s", "";
0489 printf "|%-${desc_size}s", shift @descs;
0490 } else {
0491 printf "|%-${max_size_name}s", "";
0492 printf "|%-${desc_size}s", "";
0493 }
0494
0495 printf "|%-${status_size}s|\n", $line;
0496
0497 $ln++;
0498 }
0499 matrix_lines($desc_size, $status_size, 0);
0500 }
0501 }
0502
0503
0504
0505
0506
0507 find({wanted =>\&parse_feat, no_chdir => 1}, $prefix);
0508
0509 print STDERR Data::Dumper->Dump([\%data], []) if ($debug);
0510
0511
0512
0513
0514 if ($cmd eq "current") {
0515 $arch = ;
0516 $arch =~s/\s+$//;
0517 }
0518
0519 if ($cmd eq "ls" or $cmd eq "list") {
0520 if (!$arch) {
0521 $arch = ;
0522 $arch =~s/\s+$//;
0523 }
0524
0525 list_arch_features;
0526
0527 exit;
0528 }
0529
0530 if ($cmd ne "validate") {
0531 if ($arch) {
0532 output_arch_table;
0533 } elsif ($feat) {
0534 output_feature;
0535 } else {
0536 output_matrix;
0537 }
0538 }
0539
0540 __END__
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584
0585
0586
0587
0588
0589
0590
0591
0592
0593
0594
0595
0596
0597
0598
0599
0600
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621
0622
0623
0624
0625
0626
0627
0628
0629
0630
0631
0632
0633
0634
0635
0636
0637
0638
0639
0640
0641