package derobert::InputCapsFromHeader; use 5.028; use Moose; with 'Dist::Zilla::Role::FileGatherer'; use namespace::autoclean; use Dist::Zilla::File::InMemory; use IPC::Run3 qw(run3); use Text::Wrap qw(wrap); sub gather_files { my ($self) = @_; my $code = <<~'HEADER'; package Linux::Input::Capabilities::Constants; #ABSTRACT: Constants defining various input device capabilities. use strict; use Exporter qw(import); our @EXPORT_OK = ('%_REVERSE_TABLE'); HEADER my $macros = _get_relevant_defines(); $code .= "\n# \@EXPORT_OK setup:\n"; $code .= _macros_to_export_ok($macros); $code .= "\n# Sub declarations (prototypes):\n"; $code .= _macros_to_prototypes($macros); $code .= "\n# Sub definitions:\n"; $code .= _macros_to_subs($macros); $code .= "\n# Reverse lookup table:\n"; $code .= _macros_to_reverse($macros); $code .= <<~'FOOTER'; 1; =head1 DESCRIPTION This provides constants to use with L. =head1 SEE ALSO =over 4 =item L This is where to find the documentation. =back FOOTER $self->add_file( Dist::Zilla::File::InMemory->new( name => 'lib/Linux/Input/Capabilities/Constants.pm', content => $code, ) ); return; } use Data::Dump qw(pp); sub _get_relevant_defines { my %macros; run3( [qw(gcc -dM -E -)], \"#include \n", sub { my ($name, $type, $value) = $_[0] =~ m{^ \#define \s+ ((ABS|EV|FF|KEY|BTN|LED|MSC|REL|SND|SW)_\S+) \s+ (\S+) }ax or return; # BTN and KEY use the same capabilities file (key); unify # them. $type = 'KEY' if $type eq 'BTN'; # oct handles various prefixes, not just octal. Basically # just like C. $value = oct($value) if $value =~ /^0/; $macros{$type}{$name} = $value; }, undef); die "gcc preproccessor failed" if $?; return \%macros; } sub _macros_to_export_ok { my ($macros) = @_; local $_; my $res = qq|push \@EXPORT_OK, (\n|; local $Text::Wrap::huge = 'overflow'; $res .= wrap("\t", "\t", join(q{, }, map(qq{"$_"}, sort map(keys %$_, values %$macros)))); $res .= "\n);\n"; return $res; } sub _macros_to_prototypes { my ($macros) = @_; local $_; join("", map(qq{sub $_();\n}, sort map(keys %$_, values %$macros))); } sub _macros_to_subs { my ($macros) = @_; my $res; foreach my $type (values %$macros) { while (my ($name, $val) = each %$type) { $res .= qq{sub $name() { $val }\n}; } } return $res; } sub _macros_to_reverse { my ($macros) = @_; my $res = "our %_REVERSE_TABLE = (\n"; while (my ($type, $members) = each %$macros) { $res .= "\t'$type' => {\n"; while (my ($name, $val) = each %$members) { $res .= "\t\t$name() => '$name',\n"; } $res .= "\t},\n"; } $res .= ");\n"; return $res; } __END__ =head1 NAME derobert:::InputCapsFromHeader - grabs macros from F =head1 DESCRIPTION This Dist::Zilla plugin automatically grabs a bunch of macros from F and converts them to a form L can use. =head1 GENERATED CODE Code is generated as F. It consists of three parts: =over 4 =item * A C statement to make it possible to export all the generated constants. =item * A long list of constant subroutines. These are the converted macros; all of them are exportable. =item * A data structure, C<%_REVERSE_TABLE>, which is used to look up the name given a value. =back =head1 SEE ALSO =over 4 =item L The main interface to this mass of generated code. =back