blob: 10bf23fbc9c5e06e3b417b31171b689db7d43c59 [file] [log] [blame]
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +01001#!/usr/bin/perl
2# SPDX-License-Identifier: GPL-2.0
3
4use strict;
5use Pod::Usage;
6use Getopt::Long;
7use File::Find;
8use Fcntl ':mode';
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +01009use Cwd 'abs_path';
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +010010
11my $help;
12my $man;
13my $debug;
14my $arch;
15my $feat;
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +010016
17my $basename = abs_path($0);
18$basename =~ s,/[^/]+$,/,;
19
20my $prefix=$basename . "../Documentation/features";
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +010021
22GetOptions(
23 "debug|d+" => \$debug,
24 "dir=s" => \$prefix,
25 'help|?' => \$help,
26 'arch=s' => \$arch,
27 'feat=s' => \$feat,
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +010028 'feature=s' => \$feat,
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +010029 man => \$man
30) or pod2usage(2);
31
32pod2usage(1) if $help;
33pod2usage(-exitstatus => 0, -verbose => 2) if $man;
34
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +010035pod2usage(1) if (scalar @ARGV < 1 || @ARGV > 2);
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +010036
37my ($cmd, $arg) = @ARGV;
38
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +010039pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate"
40 && $cmd ne "ls" && $cmd ne "list");
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +010041
42require Data::Dumper if ($debug);
43
44my %data;
45my %archs;
46
47#
48# Displays an error message, printing file name and line
49#
50sub parse_error($$$$) {
51 my ($file, $ln, $msg, $data) = @_;
52
53 $data =~ s/\s+$/\n/;
54
55 print STDERR "Warning: file $file#$ln:\n\t$msg";
56
57 if ($data ne "") {
58 print STDERR ". Line\n\t\t$data";
59 } else {
60 print STDERR "\n";
61 }
62}
63
64#
65# Parse a features file, storing its contents at %data
66#
67
68my $h_name = "Feature";
69my $h_kconfig = "Kconfig";
70my $h_description = "Description";
71my $h_subsys = "Subsystem";
72my $h_status = "Status";
73my $h_arch = "Architecture";
74
75my $max_size_name = length($h_name);
76my $max_size_kconfig = length($h_kconfig);
77my $max_size_description = length($h_description);
78my $max_size_subsys = length($h_subsys);
79my $max_size_status = length($h_status);
80my $max_size_arch = length($h_arch);
81
82sub parse_feat {
83 my $file = $File::Find::name;
84
85 my $mode = (stat($file))[2];
86 return if ($mode & S_IFDIR);
87 return if ($file =~ m,($prefix)/arch-support.txt,);
88 return if (!($file =~ m,arch-support.txt$,));
89
90 my $subsys = "";
91 $subsys = $2 if ( m,.*($prefix)/([^/]+).*,);
92
93 if (length($subsys) > $max_size_subsys) {
94 $max_size_subsys = length($subsys);
95 }
96
97 my $name;
98 my $kconfig;
99 my $description;
100 my $comments = "";
101 my $last_status;
102 my $ln;
103 my %arch_table;
104
105 print STDERR "Opening $file\n" if ($debug > 1);
106 open IN, $file;
107
108 while(<IN>) {
109 $ln++;
110
111 if (m/^\#\s+Feature\s+name:\s*(.*\S)/) {
112 $name = $1;
113 if (length($name) > $max_size_name) {
114 $max_size_name = length($name);
115 }
116 next;
117 }
118 if (m/^\#\s+Kconfig:\s*(.*\S)/) {
119 $kconfig = $1;
120 if (length($kconfig) > $max_size_kconfig) {
121 $max_size_kconfig = length($kconfig);
122 }
123 next;
124 }
125 if (m/^\#\s+description:\s*(.*\S)/) {
126 $description = $1;
127 if (length($description) > $max_size_description) {
128 $max_size_description = length($description);
129 }
130 next;
131 }
132 next if (m/^\\s*$/);
133 next if (m/^\s*\-+\s*$/);
134 next if (m/^\s*\|\s*arch\s*\|\s*status\s*\|\s*$/);
135
136 if (m/^\#\s*(.*)/) {
137 $comments .= "$1\n";
138 next;
139 }
140 if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) {
141 my $a = $1;
142 my $status = $2;
143
144 if (length($status) > $max_size_status) {
145 $max_size_status = length($status);
146 }
147 if (length($a) > $max_size_arch) {
148 $max_size_arch = length($a);
149 }
150
151 $status = "---" if ($status =~ m/^\.\.$/);
152
153 $archs{$a} = 1;
154 $arch_table{$a} = $status;
155 next;
156 }
157
158 #Everything else is an error
159 parse_error($file, $ln, "line is invalid", $_);
160 }
161 close IN;
162
163 if (!$name) {
164 parse_error($file, $ln, "Feature name not found", "");
165 return;
166 }
167
168 parse_error($file, $ln, "Subsystem not found", "") if (!$subsys);
169 parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig);
170 parse_error($file, $ln, "Description not found", "") if (!$description);
171
172 if (!%arch_table) {
173 parse_error($file, $ln, "Architecture table not found", "");
174 return;
175 }
176
177 $data{$name}->{where} = $file;
178 $data{$name}->{subsys} = $subsys;
179 $data{$name}->{kconfig} = $kconfig;
180 $data{$name}->{description} = $description;
181 $data{$name}->{comments} = $comments;
182 $data{$name}->{table} = \%arch_table;
183}
184
185#
186# Output feature(s) for a given architecture
187#
188sub output_arch_table {
189 my $title = "Feature status on $arch architecture";
190
191 print "=" x length($title) . "\n";
192 print "$title\n";
193 print "=" x length($title) . "\n\n";
194
195 print "=" x $max_size_subsys;
196 print " ";
197 print "=" x $max_size_name;
198 print " ";
199 print "=" x $max_size_kconfig;
200 print " ";
201 print "=" x $max_size_status;
202 print " ";
203 print "=" x $max_size_description;
204 print "\n";
205 printf "%-${max_size_subsys}s ", $h_subsys;
206 printf "%-${max_size_name}s ", $h_name;
207 printf "%-${max_size_kconfig}s ", $h_kconfig;
208 printf "%-${max_size_status}s ", $h_status;
209 printf "%-${max_size_description}s\n", $h_description;
210 print "=" x $max_size_subsys;
211 print " ";
212 print "=" x $max_size_name;
213 print " ";
214 print "=" x $max_size_kconfig;
215 print " ";
216 print "=" x $max_size_status;
217 print " ";
218 print "=" x $max_size_description;
219 print "\n";
220
221 foreach my $name (sort {
222 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100223 ("\L$a" cmp "\L$b")
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100224 } keys %data) {
225 next if ($feat && $name ne $feat);
226
227 my %arch_table = %{$data{$name}->{table}};
228 printf "%-${max_size_subsys}s ", $data{$name}->{subsys};
229 printf "%-${max_size_name}s ", $name;
230 printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig};
231 printf "%-${max_size_status}s ", $arch_table{$arch};
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100232 printf "%-s\n", $data{$name}->{description};
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100233 }
234
235 print "=" x $max_size_subsys;
236 print " ";
237 print "=" x $max_size_name;
238 print " ";
239 print "=" x $max_size_kconfig;
240 print " ";
241 print "=" x $max_size_status;
242 print " ";
243 print "=" x $max_size_description;
244 print "\n";
245}
246
247#
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100248# list feature(s) for a given architecture
249#
250sub list_arch_features {
251 print "#\n# Kernel feature support matrix of the '$arch' architecture:\n#\n";
252
253 foreach my $name (sort {
254 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
255 ("\L$a" cmp "\L$b")
256 } keys %data) {
257 next if ($feat && $name ne $feat);
258
259 my %arch_table = %{$data{$name}->{table}};
260
261 my $status = $arch_table{$arch};
262 $status = " " x ((4 - length($status)) / 2) . $status;
263
264 printf " %${max_size_subsys}s/ ", $data{$name}->{subsys};
265 printf "%-${max_size_name}s: ", $name;
266 printf "%-5s| ", $status;
267 printf "%${max_size_kconfig}s # ", $data{$name}->{kconfig};
268 printf " %s\n", $data{$name}->{description};
269 }
270}
271
272#
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100273# Output a feature on all architectures
274#
275sub output_feature {
276 my $title = "Feature $feat";
277
278 print "=" x length($title) . "\n";
279 print "$title\n";
280 print "=" x length($title) . "\n\n";
281
282 print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys});
283 print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig});
284
285 my $desc = $data{$feat}->{description};
286 $desc =~ s/^([a-z])/\U$1/;
287 $desc =~ s/\.?\s*//;
288 print "\n$desc.\n\n";
289
290 my $com = $data{$feat}->{comments};
291 $com =~ s/^\s+//;
292 $com =~ s/\s+$//;
293 if ($com) {
294 print "Comments\n";
295 print "--------\n\n";
296 print "$com\n\n";
297 }
298
299 print "=" x $max_size_arch;
300 print " ";
301 print "=" x $max_size_status;
302 print "\n";
303
304 printf "%-${max_size_arch}s ", $h_arch;
305 printf "%-${max_size_status}s", $h_status . "\n";
306
307 print "=" x $max_size_arch;
308 print " ";
309 print "=" x $max_size_status;
310 print "\n";
311
312 my %arch_table = %{$data{$feat}->{table}};
313 foreach my $arch (sort keys %arch_table) {
314 printf "%-${max_size_arch}s ", $arch;
315 printf "%-${max_size_status}s\n", $arch_table{$arch};
316 }
317
318 print "=" x $max_size_arch;
319 print " ";
320 print "=" x $max_size_status;
321 print "\n";
322}
323
324#
325# Output all features for all architectures
326#
327
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100328sub matrix_lines($$$) {
329 my $desc_size = shift;
330 my $status_size = shift;
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100331 my $header = shift;
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100332 my $fill;
333 my $ln_marker;
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100334
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100335 if ($header) {
336 $ln_marker = "=";
337 } else {
338 $ln_marker = "-";
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100339 }
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100340
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100341 $fill = $ln_marker;
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100342
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100343 print "+";
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100344 print $fill x $max_size_name;
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100345 print "+";
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100346 print $fill x $desc_size;
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100347 print "+";
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100348 print $ln_marker x $status_size;
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100349 print "+\n";
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100350}
351
352sub output_matrix {
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100353 my $title = "Feature status on all architectures";
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100354
355 print "=" x length($title) . "\n";
356 print "$title\n";
357 print "=" x length($title) . "\n\n";
358
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100359 my $desc_title = "$h_kconfig / $h_description";
360
361 my $desc_size = $max_size_kconfig + 4;
362 $desc_size = $max_size_description if ($max_size_description > $desc_size);
363 $desc_size = length($desc_title) if (length($desc_title) > $desc_size);
364
365 my $status_size = 60;
366
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100367 my $cur_subsys = "";
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100368 foreach my $name (sort {
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100369 ($data{$a}->{subsys} cmp $data{$b}->{subsys}) or
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100370 ("\L$a" cmp "\L$b")
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100371 } keys %data) {
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100372
373 if ($cur_subsys ne $data{$name}->{subsys}) {
374 if ($cur_subsys ne "") {
375 printf "\n";
376 }
377
378 $cur_subsys = $data{$name}->{subsys};
379
380 my $title = "Subsystem: $cur_subsys";
381 print "$title\n";
382 print "=" x length($title) . "\n\n";
383
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100384
385 matrix_lines($desc_size, $status_size, 0);
386
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100387 printf "|%-${max_size_name}s", $h_name;
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100388 printf "|%-${desc_size}s", $desc_title;
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100389
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100390 printf "|%-${status_size}s|\n", "Status per architecture";
391 matrix_lines($desc_size, $status_size, 1);
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100392 }
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100393
394 my %arch_table = %{$data{$name}->{table}};
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100395 my $cur_status = "";
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100396
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100397 my @lines;
398 my $line = "";
399 foreach my $arch (sort {
400 ($arch_table{$a} cmp $arch_table{$b}) or
401 ("\L$a" cmp "\L$b")
402 } keys %arch_table) {
403
404 my $status = $arch_table{$arch};
405
406 if ($status eq "---") {
407 $status = "Not compatible";
Mauro Carvalho Chehabba813f72020-11-30 16:36:31 +0100408 }
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100409
410 if ($status ne $cur_status) {
411 if ($line ne "") {
412 push @lines, $line;
413 $line = "";
414 }
415 $line = "- **" . $status . "**: " . $arch;
416 } elsif (length($line) + length ($arch) + 2 < $status_size) {
417 $line .= ", " . $arch;
418 } else {
419 push @lines, $line;
420 $line = " " . $arch;
421 }
422 $cur_status = $status;
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100423 }
Mauro Carvalho Chehabdbb90902020-12-04 16:32:28 +0100424 push @lines, $line if ($line ne "");
425
426 # Ensure that description will be printed
427 push @lines, "" while (scalar(@lines) < 2);
428
429 my $ln = 0;
430 for my $line(@lines) {
431 if (!$ln) {
432 printf "|%-${max_size_name}s", $name;
433 printf "|%-${desc_size}s", "``" . $data{$name}->{kconfig} . "``";
434 } elsif ($ln == 2) {
435 printf "|%-${max_size_name}s", "";
436 printf "|%-${desc_size}s", $data{$name}->{description};
437 } else {
438 printf "|%-${max_size_name}s", "";
439 printf "|%-${desc_size}s", "";
440 }
441
442 printf "|%-${status_size}s|\n", $line;
443
444 $ln++;
445 }
446 matrix_lines($desc_size, $status_size, 0);
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100447 }
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100448}
449
450
451#
452# Parses all feature files located at $prefix dir
453#
454find({wanted =>\&parse_feat, no_chdir => 1}, $prefix);
455
456print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
457
458#
459# Handles the command
460#
461if ($cmd eq "current") {
462 $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
463 $arch =~s/\s+$//;
464}
465
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100466if ($cmd eq "ls" or $cmd eq "list") {
467 if (!$arch) {
468 $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
469 $arch =~s/\s+$//;
470 }
471
472 list_arch_features;
473
474 exit;
475}
476
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100477if ($cmd ne "validate") {
478 if ($arch) {
479 output_arch_table;
480 } elsif ($feat) {
481 output_feature;
482 } else {
483 output_matrix;
484 }
485}
486
487__END__
488
489=head1 NAME
490
491get_feat.pl - parse the Linux Feature files and produce a ReST book.
492
493=head1 SYNOPSIS
494
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100495B<get_feat.pl> [--debug] [--man] [--help] [--dir=<dir>] [--arch=<arch>]
496 [--feature=<feature>|--feat=<feature>] <COMAND> [<ARGUMENT>]
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100497
498Where <COMMAND> can be:
499
500=over 8
501
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100502B<current> - output table in ReST compatible ASCII format
503 with features for this machine's architecture
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100504
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100505B<rest> - output table(s) in ReST compatible ASCII format
506 with features in ReST markup language. The output
507 is affected by --arch or --feat/--feature flags.
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100508
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100509B<validate> - validate the contents of the files under
510 Documentation/features.
511
512B<ls> or B<list> - list features for this machine's architecture,
513 using an easier to parse format.
514 The output is affected by --arch flag.
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100515
516=back
517
518=head1 OPTIONS
519
520=over 8
521
522=item B<--arch>
523
524Output features for an specific architecture, optionally filtering for
525a single specific feature.
526
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100527=item B<--feat> or B<--feature>
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100528
Mauro Carvalho Chehabca908572020-11-30 16:36:32 +0100529Output features for a single specific feature.
Mauro Carvalho Chehab52a4be32020-11-30 16:36:30 +0100530
531=item B<--dir>
532
533Changes the location of the Feature files. By default, it uses
534the Documentation/features directory.
535
536=item B<--debug>
537
538Put the script in verbose mode, useful for debugging. Can be called multiple
539times, to increase verbosity.
540
541=item B<--help>
542
543Prints a brief help message and exits.
544
545=item B<--man>
546
547Prints the manual page and exits.
548
549=back
550
551=head1 DESCRIPTION
552
553Parse the Linux feature files from Documentation/features (by default),
554optionally producing results at ReST format.
555
556It supports output data per architecture, per feature or a
557feature x arch matrix.
558
559When used with B<rest> command, it will use either one of the tree formats:
560
561If neither B<--arch> or B<--feature> arguments are used, it will output a
562matrix with features per architecture.
563
564If B<--arch> argument is used, it will output the features availability for
565a given architecture.
566
567If B<--feat> argument is used, it will output the content of the feature
568file using ReStructured Text markup.
569
570=head1 BUGS
571
572Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
573
574=head1 COPYRIGHT
575
576Copyright (c) 2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
577
578License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
579
580This is free software: you are free to change and redistribute it.
581There is NO WARRANTY, to the extent permitted by law.
582
583=cut