2 # SPDX-License-Identifier: GPL-2.0
18 my $basename = abs_path($0);
19 $basename =~ s,/[^/]+$,/,;
21 my $prefix=$basename . "../Documentation/features";
23 # Used only at for full features output. The script will auto-adjust
24 # such values for the minimal possible values
26 my $description_size = 1;
29 "debug|d+" => \$debug,
34 'feature=s' => \$feat,
35 "enable-fname" => \$enable_fname,
39 pod2usage(1) if $help;
40 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
42 pod2usage(1) if (scalar @ARGV < 1 || @ARGV > 2);
44 my ($cmd, $arg) = @ARGV;
46 pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate"
47 && $cmd ne "ls" && $cmd ne "list");
49 require Data::Dumper if ($debug);
55 # Displays an error message, printing file name and line
57 sub parse_error($$$$) {
58 my ($file, $ln, $msg, $data) = @_;
62 print STDERR "Warning: file $file#$ln:\n\t$msg";
65 print STDERR ". Line\n\t\t$data";
72 # Parse a features file, storing its contents at %data
75 my $h_name = "Feature";
76 my $h_kconfig = "Kconfig";
77 my $h_description = "Description";
78 my $h_subsys = "Subsystem";
79 my $h_status = "Status";
80 my $h_arch = "Architecture";
82 my $max_size_name = length($h_name);
83 my $max_size_kconfig = length($h_kconfig);
84 my $max_size_description = length($h_description);
85 my $max_size_subsys = length($h_subsys);
86 my $max_size_status = length($h_status);
88 my $max_size_arch = 0;
89 my $max_size_arch_with_header;
90 my $max_description_word = 0;
93 my $file = $File::Find::name;
95 my $mode = (stat($file))[2];
96 return if ($mode & S_IFDIR);
97 return if ($file =~ m,($prefix)/arch-support.txt,);
98 return if (!($file =~ m,arch-support.txt$,));
101 printf ".. FILE %s\n", abs_path($file);
105 $subsys = $2 if ( m,.*($prefix)/([^/]+).*,);
107 if (length($subsys) > $max_size_subsys) {
108 $max_size_subsys = length($subsys);
119 print STDERR "Opening $file\n" if ($debug > 1);
125 if (m/^\#\s+Feature\s+name:\s*(.*\S)/) {
127 if (length($name) > $max_size_name) {
128 $max_size_name = length($name);
132 if (m/^\#\s+Kconfig:\s*(.*\S)/) {
134 if (length($kconfig) > $max_size_kconfig) {
135 $max_size_kconfig = length($kconfig);
139 if (m/^\#\s+description:\s*(.*\S)/) {
141 if (length($description) > $max_size_description) {
142 $max_size_description = length($description);
145 foreach my $word (split /\s+/, $description) {
146 if (length($word) > $max_description_word) {
147 $max_description_word = length($word);
154 next if (m/^\s*\-+\s*$/);
155 next if (m/^\s*\|\s*arch\s*\|\s*status\s*\|\s*$/);
161 if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) {
165 if (length($status) > $max_size_status) {
166 $max_size_status = length($status);
168 if (length($a) > $max_size_arch) {
169 $max_size_arch = length($a);
172 $status = "---" if ($status =~ m/^\.\.$/);
175 $arch_table{$a} = $status;
179 #Everything else is an error
180 parse_error($file, $ln, "line is invalid", $_);
185 parse_error($file, $ln, "Feature name not found", "");
189 parse_error($file, $ln, "Subsystem not found", "") if (!$subsys);
190 parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig);
191 parse_error($file, $ln, "Description not found", "") if (!$description);
194 parse_error($file, $ln, "Architecture table not found", "");
198 $data{$name}->{where} = $file;
199 $data{$name}->{subsys} = $subsys;
200 $data{$name}->{kconfig} = $kconfig;
201 $data{$name}->{description} = $description;
202 $data{$name}->{comments} = $comments;
203 $data{$name}->{table} = \%arch_table;
205 $max_size_arch_with_header = $max_size_arch + length($h_arch);
209 # Output feature(s) for a given architecture
211 sub output_arch_table {
212 my $title = "Feature status on $arch architecture";
214 print "=" x length($title) . "\n";
216 print "=" x length($title) . "\n\n";
218 print "=" x $max_size_subsys;
220 print "=" x $max_size_name;
222 print "=" x $max_size_kconfig;
224 print "=" x $max_size_status;
226 print "=" x $max_size_description;
228 printf "%-${max_size_subsys}s ", $h_subsys;
229 printf "%-${max_size_name}s ", $h_name;
230 printf "%-${max_size_kconfig}s ", $h_kconfig;
231 printf "%-${max_size_status}s ", $h_status;
232 printf "%-${max_size_description}s\n", $h_description;
233 print "=" x $max_size_subsys;
235 print "=" x $max_size_name;
237 print "=" x $max_size_kconfig;
239 print "=" x $max_size_status;
241 print "=" x $max_size_description;
244 foreach my $name (sort {
245 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
248 next if ($feat && $name ne $feat);
250 my %arch_table = %{$data{$name}->{table}};
251 printf "%-${max_size_subsys}s ", $data{$name}->{subsys};
252 printf "%-${max_size_name}s ", $name;
253 printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig};
254 printf "%-${max_size_status}s ", $arch_table{$arch};
255 printf "%-s\n", $data{$name}->{description};
258 print "=" x $max_size_subsys;
260 print "=" x $max_size_name;
262 print "=" x $max_size_kconfig;
264 print "=" x $max_size_status;
266 print "=" x $max_size_description;
271 # list feature(s) for a given architecture
273 sub list_arch_features {
274 print "#\n# Kernel feature support matrix of the '$arch' architecture:\n#\n";
276 foreach my $name (sort {
277 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
280 next if ($feat && $name ne $feat);
282 my %arch_table = %{$data{$name}->{table}};
284 my $status = $arch_table{$arch};
285 $status = " " x ((4 - length($status)) / 2) . $status;
287 printf " %${max_size_subsys}s/ ", $data{$name}->{subsys};
288 printf "%-${max_size_name}s: ", $name;
289 printf "%-5s| ", $status;
290 printf "%${max_size_kconfig}s # ", $data{$name}->{kconfig};
291 printf " %s\n", $data{$name}->{description};
296 # Output a feature on all architectures
299 my $title = "Feature $feat";
301 print "=" x length($title) . "\n";
303 print "=" x length($title) . "\n\n";
305 print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys});
306 print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig});
308 my $desc = $data{$feat}->{description};
309 $desc =~ s/^([a-z])/\U$1/;
311 print "\n$desc.\n\n";
313 my $com = $data{$feat}->{comments};
318 print "--------\n\n";
322 print "=" x $max_size_arch_with_header;
324 print "=" x $max_size_status;
327 printf "%-${max_size_arch}s ", $h_arch;
328 printf "%-${max_size_status}s", $h_status . "\n";
330 print "=" x $max_size_arch_with_header;
332 print "=" x $max_size_status;
335 my %arch_table = %{$data{$feat}->{table}};
336 foreach my $arch (sort keys %arch_table) {
337 printf "%-${max_size_arch}s ", $arch;
338 printf "%-${max_size_status}s\n", $arch_table{$arch};
341 print "=" x $max_size_arch_with_header;
343 print "=" x $max_size_status;
348 # Output all features for all architectures
351 sub matrix_lines($$$) {
352 my $desc_size = shift;
353 my $status_size = shift;
367 print $fill x $max_size_name;
369 print $fill x $desc_size;
371 print $ln_marker x $status_size;
376 my $title = "Feature status on all architectures";
377 my $notcompat = "Not compatible";
379 print "=" x length($title) . "\n";
381 print "=" x length($title) . "\n\n";
383 my $desc_title = "$h_kconfig / $h_description";
385 my $desc_size = $max_size_kconfig + 4;
386 if (!$description_size) {
387 $desc_size = $max_size_description if ($max_size_description > $desc_size);
389 $desc_size = $description_size if ($description_size > $desc_size);
391 $desc_size = $max_description_word if ($max_description_word > $desc_size);
393 $desc_size = length($desc_title) if (length($desc_title) > $desc_size);
395 $max_size_status = length($notcompat) if (length($notcompat) > $max_size_status);
397 # Ensure that the status will fit
398 my $min_status_size = $max_size_status + $max_size_arch + 6;
399 $status_size = $min_status_size if ($status_size < $min_status_size);
403 foreach my $name (sort {
404 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) or
408 if ($cur_subsys ne $data{$name}->{subsys}) {
409 if ($cur_subsys ne "") {
413 $cur_subsys = $data{$name}->{subsys};
415 my $title = "Subsystem: $cur_subsys";
417 print "=" x length($title) . "\n\n";
420 matrix_lines($desc_size, $status_size, 0);
422 printf "|%-${max_size_name}s", $h_name;
423 printf "|%-${desc_size}s", $desc_title;
425 printf "|%-${status_size}s|\n", "Status per architecture";
426 matrix_lines($desc_size, $status_size, 1);
429 my %arch_table = %{$data{$name}->{table}};
434 foreach my $arch (sort {
435 ($arch_table{$b} cmp $arch_table{$a}) or
437 } keys %arch_table) {
439 my $status = $arch_table{$arch};
441 if ($status eq "---") {
442 $status = $notcompat;
445 if ($status ne $cur_status) {
450 $line = "- **" . $status . "**: " . $arch;
451 } elsif (length($line) + length ($arch) + 2 < $status_size) {
452 $line .= ", " . $arch;
457 $cur_status = $status;
459 push @lines, $line if ($line ne "");
461 my $description = $data{$name}->{description};
462 while (length($description) > $desc_size) {
463 my $d = substr $description, 0, $desc_size;
465 # Ensure that it will end on a space
466 # if it can't, it means that the size is too small
467 # Instead of aborting it, let's print what we have
468 if (!($d =~ s/^(.*)\s+.*/$1/)) {
469 $d = substr $d, 0, -1;
471 $description =~ s/^\Q$d\E//;
474 $description =~ s/^\Q$d\E\s+//;
477 push @descs, $description;
479 # Ensure that the full description will be printed
480 push @lines, "" while (scalar(@lines) < 2 + scalar(@descs));
483 for my $line(@lines) {
485 printf "|%-${max_size_name}s", $name;
486 printf "|%-${desc_size}s", "``" . $data{$name}->{kconfig} . "``";
487 } elsif ($ln >= 2 && scalar(@descs)) {
488 printf "|%-${max_size_name}s", "";
489 printf "|%-${desc_size}s", shift @descs;
491 printf "|%-${max_size_name}s", "";
492 printf "|%-${desc_size}s", "";
495 printf "|%-${status_size}s|\n", $line;
499 matrix_lines($desc_size, $status_size, 0);
505 # Parses all feature files located at $prefix dir
507 find({wanted =>\&parse_feat, no_chdir => 1}, $prefix);
509 print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
512 # Handles the command
514 if ($cmd eq "current") {
515 $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
519 if ($cmd eq "ls" or $cmd eq "list") {
521 $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
530 if ($cmd ne "validate") {
544 get_feat.pl - parse the Linux Feature files and produce a ReST book.
548 B<get_feat.pl> [--debug] [--man] [--help] [--dir=<dir>] [--arch=<arch>]
549 [--feature=<feature>|--feat=<feature>] <COMAND> [<ARGUMENT>]
551 Where <COMMAND> can be:
555 B<current> - output table in ReST compatible ASCII format
556 with features for this machine's architecture
558 B<rest> - output table(s) in ReST compatible ASCII format
559 with features in ReST markup language. The output
560 is affected by --arch or --feat/--feature flags.
562 B<validate> - validate the contents of the files under
563 Documentation/features.
565 B<ls> or B<list> - list features for this machine's architecture,
566 using an easier to parse format.
567 The output is affected by --arch flag.
577 Output features for an specific architecture, optionally filtering for
578 a single specific feature.
580 =item B<--feat> or B<--feature>
582 Output features for a single specific feature.
586 Changes the location of the Feature files. By default, it uses
587 the Documentation/features directory.
589 =item B<--enable-fname>
591 Prints the file name of the feature files. This can be used in order to
592 track dependencies during documentation build.
596 Put the script in verbose mode, useful for debugging. Can be called multiple
597 times, to increase verbosity.
601 Prints a brief help message and exits.
605 Prints the manual page and exits.
611 Parse the Linux feature files from Documentation/features (by default),
612 optionally producing results at ReST format.
614 It supports output data per architecture, per feature or a
615 feature x arch matrix.
617 When used with B<rest> command, it will use either one of the tree formats:
619 If neither B<--arch> or B<--feature> arguments are used, it will output a
620 matrix with features per architecture.
622 If B<--arch> argument is used, it will output the features availability for
623 a given architecture.
625 If B<--feat> argument is used, it will output the content of the feature
626 file using ReStructured Text markup.
630 Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
634 Copyright (c) 2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
636 License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
638 This is free software: you are free to change and redistribute it.
639 There is NO WARRANTY, to the extent permitted by law.