History

scripts: get_feat.pl: add a script to handle Documentation/features

The Documentation/features contains a set of parseable files. It is not worth converting them to ReST format, as they're useful the way it is. It is, however, interesting to parse them and produce output on different formats: 1) Output the contents of a feature in ReST format; 2) Output what features a given architecture supports; 3) Output a matrix with features x architectures.

Mauro Carvalho Chehab committed 5 years ago

Commit 52a4be31 parent 868770c1 file · +474 −0Browse files

Showing the changes to scripts (1 of 1 file). Show all

Unified · Split

scripts/get_feat.pl+474−0
@@ -0,0 +1,474 @@
1+#!/usr/bin/perl
2+# SPDX-License-Identifier: GPL-2.0
3+
4+use strict;
5+use Pod::Usage;
6+use Getopt::Long;
7+use File::Find;
8+use Fcntl ':mode';
9+
10+my $help;
11+my $man;
12+my $debug;
13+my $arch;
14+my $feat;
15+my $prefix="Documentation/features";
16+
17+GetOptions(
18+ "debug|d+" => \$debug,
19+ "dir=s" => \$prefix,
20+ 'help|?' => \$help,
21+ 'arch=s' => \$arch,
22+ 'feat=s' => \$feat,
23+ man => \$man
24+) or pod2usage(2);
25+
26+pod2usage(1) if $help;
27+pod2usage(-exitstatus => 0, -verbose => 2) if $man;
28+
29+pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2);
30+
31+my ($cmd, $arg) = @ARGV;
32+
33+pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate");
34+
35+require Data::Dumper if ($debug);
36+
37+my %data;
38+my %archs;
39+
40+#
41+# Displays an error message, printing file name and line
42+#
43+sub parse_error($$$$) {
44+ my ($file, $ln, $msg, $data) = @_;
45+
46+ $data =~ s/\s+$/\n/;
47+
48+ print STDERR "Warning: file $file#$ln:\n\t$msg";
49+
50+ if ($data ne "") {
51+ print STDERR ". Line\n\t\t$data";
52+ } else {
53+ print STDERR "\n";
54+ }
55+}
56+
57+#
58+# Parse a features file, storing its contents at %data
59+#
60+
61+my $h_name = "Feature";
62+my $h_kconfig = "Kconfig";
63+my $h_description = "Description";
64+my $h_subsys = "Subsystem";
65+my $h_status = "Status";
66+my $h_arch = "Architecture";
67+
68+my $max_size_name = length($h_name);
69+my $max_size_kconfig = length($h_kconfig);
70+my $max_size_description = length($h_description);
71+my $max_size_subsys = length($h_subsys);
72+my $max_size_status = length($h_status);
73+my $max_size_arch = length($h_arch);
74+
75+sub parse_feat {
76+ my $file = $File::Find::name;
77+
78+ my $mode = (stat($file))[2];
79+ return if ($mode & S_IFDIR);
80+ return if ($file =~ m,($prefix)/arch-support.txt,);
81+ return if (!($file =~ m,arch-support.txt$,));
82+
83+ my $subsys = "";
84+ $subsys = $2 if ( m,.*($prefix)/([^/]+).*,);
85+
86+ if (length($subsys) > $max_size_subsys) {
87+ $max_size_subsys = length($subsys);
88+ }
89+
90+ my $name;
91+ my $kconfig;
92+ my $description;
93+ my $comments = "";
94+ my $last_status;
95+ my $ln;
96+ my %arch_table;
97+
98+ print STDERR "Opening $file\n" if ($debug > 1);
99+ open IN, $file;
100+
101+ while(<IN>) {
102+ $ln++;
103+
104+ if (m/^\#\s+Feature\s+name:\s*(.*\S)/) {
105+ $name = $1;
106+ if (length($name) > $max_size_name) {
107+ $max_size_name = length($name);
108+ }
109+ next;
110+ }
111+ if (m/^\#\s+Kconfig:\s*(.*\S)/) {
112+ $kconfig = $1;
113+ if (length($kconfig) > $max_size_kconfig) {
114+ $max_size_kconfig = length($kconfig);
115+ }
116+ next;
117+ }
118+ if (m/^\#\s+description:\s*(.*\S)/) {
119+ $description = $1;
120+ if (length($description) > $max_size_description) {
121+ $max_size_description = length($description);
122+ }
123+ next;
124+ }
125+ next if (m/^\\s*$/);
126+ next if (m/^\s*\-+\s*$/);
127+ next if (m/^\s*\|\s*arch\s*\|\s*status\s*\|\s*$/);
128+
129+ if (m/^\#\s*(.*)/) {
130+ $comments .= "$1\n";
131+ next;
132+ }
133+ if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) {
134+ my $a = $1;
135+ my $status = $2;
136+
137+ if (length($status) > $max_size_status) {
138+ $max_size_status = length($status);
139+ }
140+ if (length($a) > $max_size_arch) {
141+ $max_size_arch = length($a);
142+ }
143+
144+ $status = "---" if ($status =~ m/^\.\.$/);
145+
146+ $archs{$a} = 1;
147+ $arch_table{$a} = $status;
148+ next;
149+ }
150+
151+ #Everything else is an error
152+ parse_error($file, $ln, "line is invalid", $_);
153+ }
154+ close IN;
155+
156+ if (!$name) {
157+ parse_error($file, $ln, "Feature name not found", "");
158+ return;
159+ }
160+
161+ parse_error($file, $ln, "Subsystem not found", "") if (!$subsys);
162+ parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig);
163+ parse_error($file, $ln, "Description not found", "") if (!$description);
164+
165+ if (!%arch_table) {
166+ parse_error($file, $ln, "Architecture table not found", "");
167+ return;
168+ }
169+
170+ $data{$name}->{where} = $file;
171+ $data{$name}->{subsys} = $subsys;
172+ $data{$name}->{kconfig} = $kconfig;
173+ $data{$name}->{description} = $description;
174+ $data{$name}->{comments} = $comments;
175+ $data{$name}->{table} = \%arch_table;
176+}
177+
178+#
179+# Output feature(s) for a given architecture
180+#
181+sub output_arch_table {
182+ my $title = "Feature status on $arch architecture";
183+
184+ print "=" x length($title) . "\n";
185+ print "$title\n";
186+ print "=" x length($title) . "\n\n";
187+
188+ print "=" x $max_size_subsys;
189+ print " ";
190+ print "=" x $max_size_name;
191+ print " ";
192+ print "=" x $max_size_kconfig;
193+ print " ";
194+ print "=" x $max_size_status;
195+ print " ";
196+ print "=" x $max_size_description;
197+ print "\n";
198+ printf "%-${max_size_subsys}s ", $h_subsys;
199+ printf "%-${max_size_name}s ", $h_name;
200+ printf "%-${max_size_kconfig}s ", $h_kconfig;
201+ printf "%-${max_size_status}s ", $h_status;
202+ printf "%-${max_size_description}s\n", $h_description;
203+ print "=" x $max_size_subsys;
204+ print " ";
205+ print "=" x $max_size_name;
206+ print " ";
207+ print "=" x $max_size_kconfig;
208+ print " ";
209+ print "=" x $max_size_status;
210+ print " ";
211+ print "=" x $max_size_description;
212+ print "\n";
213+
214+ foreach my $name (sort {
215+ ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
216+ ($data{$a}->{name} cmp $data{$b}->{name})
217+ } keys %data) {
218+ next if ($feat && $name ne $feat);
219+
220+ my %arch_table = %{$data{$name}->{table}};
221+ printf "%-${max_size_subsys}s ", $data{$name}->{subsys};
222+ printf "%-${max_size_name}s ", $name;
223+ printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig};
224+ printf "%-${max_size_status}s ", $arch_table{$arch};
225+ printf "%-${max_size_description}s\n", $data{$name}->{description};
226+ }
227+
228+ print "=" x $max_size_subsys;
229+ print " ";
230+ print "=" x $max_size_name;
231+ print " ";
232+ print "=" x $max_size_kconfig;
233+ print " ";
234+ print "=" x $max_size_status;
235+ print " ";
236+ print "=" x $max_size_description;
237+ print "\n";
238+}
239+
240+#
241+# Output a feature on all architectures
242+#
243+sub output_feature {
244+ my $title = "Feature $feat";
245+
246+ print "=" x length($title) . "\n";
247+ print "$title\n";
248+ print "=" x length($title) . "\n\n";
249+
250+ print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys});
251+ print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig});
252+
253+ my $desc = $data{$feat}->{description};
254+ $desc =~ s/^([a-z])/\U$1/;
255+ $desc =~ s/\.?\s*//;
256+ print "\n$desc.\n\n";
257+
258+ my $com = $data{$feat}->{comments};
259+ $com =~ s/^\s+//;
260+ $com =~ s/\s+$//;
261+ if ($com) {
262+ print "Comments\n";
263+ print "--------\n\n";
264+ print "$com\n\n";
265+ }
266+
267+ print "=" x $max_size_arch;
268+ print " ";
269+ print "=" x $max_size_status;
270+ print "\n";
271+
272+ printf "%-${max_size_arch}s ", $h_arch;
273+ printf "%-${max_size_status}s", $h_status . "\n";
274+
275+ print "=" x $max_size_arch;
276+ print " ";
277+ print "=" x $max_size_status;
278+ print "\n";
279+
280+ my %arch_table = %{$data{$feat}->{table}};
281+ foreach my $arch (sort keys %arch_table) {
282+ printf "%-${max_size_arch}s ", $arch;
283+ printf "%-${max_size_status}s\n", $arch_table{$arch};
284+ }
285+
286+ print "=" x $max_size_arch;
287+ print " ";
288+ print "=" x $max_size_status;
289+ print "\n";
290+}
291+
292+#
293+# Output all features for all architectures
294+#
295+
296+sub matrix_lines {
297+ print "=" x $max_size_subsys;
298+ print " ";
299+ print "=" x $max_size_name;
300+ print " ";
301+
302+ foreach my $arch (sort keys %archs) {
303+ my $len = $max_size_status;
304+
305+ $len = length($arch) if ($len < length($arch));
306+
307+ print "=" x $len;
308+ print " ";
309+ }
310+ print "=" x $max_size_kconfig;
311+ print " ";
312+ print "=" x $max_size_description;
313+ print "\n";
314+}
315+
316+sub output_matrix {
317+
318+ my $title = "Feature List (feature x architecture)";
319+
320+ print "=" x length($title) . "\n";
321+ print "$title\n";
322+ print "=" x length($title) . "\n\n";
323+
324+ matrix_lines;
325+
326+ printf "%-${max_size_subsys}s ", $h_subsys;
327+ printf "%-${max_size_name}s ", $h_name;
328+
329+ foreach my $arch (sort keys %archs) {
330+ printf "%-${max_size_status}s ", $arch;
331+ }
332+ printf "%-${max_size_kconfig}s ", $h_kconfig;
333+ printf "%-${max_size_description}s\n", $h_description;
334+
335+ matrix_lines;
336+
337+ foreach my $name (sort {
338+ ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
339+ ($data{$a}->{name} cmp $data{$b}->{name})
340+ } keys %data) {
341+ printf "%-${max_size_subsys}s ", $data{$name}->{subsys};
342+ printf "%-${max_size_name}s ", $name;
343+
344+ my %arch_table = %{$data{$name}->{table}};
345+
346+ foreach my $arch (sort keys %arch_table) {
347+ my $len = $max_size_status;
348+
349+ $len = length($arch) if ($len < length($arch));
350+
351+ printf "%-${len}s ", $arch_table{$arch};
352+ }
353+ printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig};
354+ printf "%-${max_size_description}s\n", $data{$name}->{description};
355+ }
356+
357+ matrix_lines;
358+}
359+
360+
361+#
362+# Parses all feature files located at $prefix dir
363+#
364+find({wanted =>\&parse_feat, no_chdir => 1}, $prefix);
365+
366+print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
367+
368+#
369+# Handles the command
370+#
371+if ($cmd eq "current") {
372+ $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/');
373+ $arch =~s/\s+$//;
374+}
375+
376+if ($cmd ne "validate") {
377+ if ($arch) {
378+ output_arch_table;
379+ } elsif ($feat) {
380+ output_feature;
381+ } else {
382+ output_matrix;
383+ }
384+}
385+
386+__END__
387+
388+=head1 NAME
389+
390+get_feat.pl - parse the Linux Feature files and produce a ReST book.
391+
392+=head1 SYNOPSIS
393+
394+B<get_feat.pl> [--debug] [--man] [--help] [--dir=<dir>]
395+ [--arch=<arch>] [--feat=<feature>] <COMAND> [<ARGUMENT>]
396+
397+Where <COMMAND> can be:
398+
399+=over 8
400+
401+B<current> - output features for this machine's architecture
402+
403+B<rest> - output features in ReST markup language
404+
405+B<validate> - validate the feature contents
406+
407+=back
408+
409+=head1 OPTIONS
410+
411+=over 8
412+
413+=item B<--arch>
414+
415+Output features for an specific architecture, optionally filtering for
416+a single specific feature.
417+
418+=item B<--feat>
419+
420+Output features for a single specific architecture.
421+
422+=item B<--dir>
423+
424+Changes the location of the Feature files. By default, it uses
425+the Documentation/features directory.
426+
427+=item B<--debug>
428+
429+Put the script in verbose mode, useful for debugging. Can be called multiple
430+times, to increase verbosity.
431+
432+=item B<--help>
433+
434+Prints a brief help message and exits.
435+
436+=item B<--man>
437+
438+Prints the manual page and exits.
439+
440+=back
441+
442+=head1 DESCRIPTION
443+
444+Parse the Linux feature files from Documentation/features (by default),
445+optionally producing results at ReST format.
446+
447+It supports output data per architecture, per feature or a
448+feature x arch matrix.
449+
450+When used with B<rest> command, it will use either one of the tree formats:
451+
452+If neither B<--arch> or B<--feature> arguments are used, it will output a
453+matrix with features per architecture.
454+
455+If B<--arch> argument is used, it will output the features availability for
456+a given architecture.
457+
458+If B<--feat> argument is used, it will output the content of the feature
459+file using ReStructured Text markup.
460+
461+=head1 BUGS
462+
463+Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
464+
465+=head1 COPYRIGHT
466+
467+Copyright (c) 2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
468+
469+License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
470+
471+This is free software: you are free to change and redistribute it.
472+There is NO WARRANTY, to the extent permitted by law.
473+
474+=cut