File Coverage

blib/lib/LCFG/Build/Tool/CheckMacros.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package LCFG::Build::Tool::CheckMacros; # -*-cperl-*-
2 1     1   1048 use strict;
  1         2  
  1         30  
3 1     1   4 use warnings;
  1         2  
  1         38  
4              
5             # $Id: CheckMacros.pm.in 23323 2013-05-14 08:12:39Z squinney@INF.ED.AC.UK $
6             # $Source: /var/cvs/dice/LCFG-Build-Tools/lib/LCFG/Build/Tool/CheckMacros.pm.in,v $
7             # $Revision: 23323 $
8             # $HeadURL: https://svn.lcfg.org/svn/source/tags/LCFG-Build-Tools/LCFG_Build_Tools_0_4_4/lib/LCFG/Build/Tool/CheckMacros.pm.in $
9             # $Date: 2013-05-14 09:12:39 +0100 (Tue, 14 May 2013) $
10              
11             our $VERSION = '0.4.4';
12              
13 1     1   4 use File::Spec ();
  1         1  
  1         11  
14 1     1   4 use File::Temp ();
  1         1  
  1         8  
15 1     1   3 use IO::File ();
  1         1  
  1         16  
16 1     1   3 use LCFG::Build::Utils;
  1         1  
  1         16  
17              
18 1     1   208 use Moose;
  0            
  0            
19              
20             extends 'LCFG::Build::Tool';
21              
22             # We do not want this option for these commands so use an override.
23              
24             has '+resultsdir' => ( traits => ['NoGetopt'] );
25              
26             has 'fix_deprecated' => (
27             is => 'rw',
28             isa => 'Bool',
29             default => 0,
30             documentation => 'Replace deprecated macros with new-style names',
31             );
32              
33             __PACKAGE__->meta->make_immutable;
34              
35             sub abstract {
36             return q{Check for correct macro usage};
37             }
38              
39             my %messages = (
40             unknown => 'Use of unknown macro',
41             deprecated => 'Use of deprecated macro',
42             linux => 'Use of linux-only macro',
43             macosx => 'Use of MacOSX-only macro',
44             buildtime => 'Use of build-time-only macro',
45             );
46              
47             my %basic = map { $_ => 'basic' } qw(
48             LCFG_ABSTRACT
49             LCFG_NAME
50             LCFG_FULLNAME
51             LCFG_VERSION
52             LCFG_VERSION
53             LCFG_RELEASE
54             LCFG_SCHEMA
55             LCFG_VENDOR
56             LCFG_GROUP
57             LCFG_AUTHOR
58             LCFG_PLATFORMS
59             LCFG_DATE
60             LCFG_LICENSE
61             LCFG_TARNAME
62             LCFG_CHANGELOG
63             BOOTSTAMP
64             INITDIR
65             LCFGBIB
66             LCFGBIN
67             LCFGCLIENTDEF
68             LCFGCOMP
69             LCFGCONF
70             LCFGCONFIGMSG
71             LCFGDATA
72             LCFGDOC
73             LCFGHTML
74             LCFGLIB
75             LCFGLOCK
76             LCFGLOG
77             LCFGMAN
78             LCFGOM
79             LCFGPDF
80             LCFGPOD
81             LCFGROTATED
82             LCFGSBIN
83             LCFGSERVERDEF
84             LCFGSTATUS
85             LCFGRUN
86             LCFGTMP
87             LCFGVAR
88             LIBMANDIR
89             LIBMANSECT
90             MANDIR
91             MANSECT
92             RELEASEFILE
93             );
94              
95             my %deprecated = (
96             COMP => 'LCFG_NAME',
97             NAME => 'LCFG_FULLNAME',
98             DESCR => 'LCFG_ABSTRACT',
99             V => 'LCFG_VERSION',
100             VERSION => 'LCFG_VERSION',
101             R => 'LCFG_RELEASE',
102             RELEASE => 'LCFG_RELEASE',
103             SCHEMA => 'LCFG_SCHEMA',
104             VENDOR => 'LCFG_VENDOR',
105             ORGANIZATION => 'LCFG_VENDOR',
106             GROUP => 'LCFG_GROUP',
107             AUTHOR => 'LCFG_AUTHOR',
108             PLATFORMS => 'LCFG_PLATFORMS',
109             DATE => 'LCFG_DATE',
110             TARFILE => 'LCFG_TARNAME',
111             );
112              
113             my %buildtime = map { $_ => 'buildtime' } qw(
114             HAS_PROC
115             BOOTCOMP
116             PERL
117             PERL_EXECUTABLE
118             PERL_INSTALLDIRS
119             PERL_ARCHDIR
120             PERL_LIBDIR
121             SHELL
122             EGREP
123             SED
124             SORT
125             LCFGOS
126             LCFGARCH
127             MSG
128             CONFIGDIR
129             ICONDIR
130             SCRIPTDIR
131             LCFG_TMPLDIR
132             );
133              
134             my %linux = map { $_ => 'linux' } qw(
135             LSB_VERSION
136             DISTRIB_ID
137             DISTRIB_DESCRIPTION
138             DISTRIB_RELEASE
139             DISTRIB_CODENAME
140             OS_VERSION
141             OS_RELEASE
142             LIBDIR
143             LIBSECURITYDIR
144             );
145              
146             my %macosx = map { $_ => 'macosx' } qw(
147             OSX_VERSION
148             );
149              
150             sub complain {
151             my ( $self, $msg, $macro, @where ) = @_;
152              
153             print "$msg, $macro, at: \n";
154             for my $where (@where) {
155             print "\t$where\n";
156             }
157              
158             return;
159             }
160              
161             sub execute {
162             my ($self) = @_;
163              
164             my $dir = $self->dir;
165             my @translate = $self->spec->translate;
166              
167             my %files = LCFG::Build::Utils::find_trans_files( $dir, @translate,
168             'specfile' );
169              
170             my %macros_found;
171             for my $file ( keys %files ) {
172             my $path = File::Spec->catfile( $dir, $file );
173             my $fh = IO::File->new( $path, 'r' )
174             or $self->fail("Could not open $path: $!");
175              
176             my $count = 0;
177             while ( defined( my $line = $fh->getline ) ) {
178             $count++;
179              
180             my @macros = (
181             $line =~ m{\@
182             (\w+) # The macro name
183             \@}gx
184             );
185              
186             # unique-ify
187             my %macros = map { $_ => 1 } @macros;
188             @macros = keys %macros;
189              
190             for my $macro (@macros) {
191             if ( exists $macros_found{$macro} ) {
192             push @{ $macros_found{$macro} }, "$file:$count";
193             }
194             else {
195             $macros_found{$macro} = ["$file:$count"];
196             }
197             }
198             }
199             }
200              
201             # Collate the results
202              
203             my %comments;
204             for my $macro ( keys %macros_found ) {
205              
206             if ( $basic{$macro} ) {
207              
208             # ok
209             }
210             elsif ( $deprecated{$macro} ) {
211             $comments{deprecated}{$macro} = $macros_found{$macro};
212             }
213             elsif ( $buildtime{$macro} ) {
214             $comments{buildtime}{$macro} = $macros_found{$macro};
215             }
216             elsif ( $linux{$macro} ) {
217             $comments{linux}{$macro} = $macros_found{$macro};
218             }
219             elsif ( $macosx{$macro} ) {
220             $comments{macosx}{$macro} = $macros_found{$macro};
221             }
222             else {
223             $comments{unknown}{$macro} = $macros_found{$macro};
224             }
225             }
226              
227             # Make the report
228              
229             for my $key (qw/unknown deprecated linux macosx buildtime/) {
230             if ( exists $comments{$key} && ref $comments{$key} eq 'HASH' ) {
231             for my $macro ( sort keys %{ $comments{$key} } ) {
232             $self->complain( $messages{$key}, $macro,
233             @{ $comments{$key}{$macro} } );
234             }
235             }
236             }
237              
238             if ( $self->fix_deprecated ) {
239             my %files_using_deprecated;
240             for my $macro ( keys %{$comments{deprecated}} ) {
241             my @found = @{$comments{deprecated}{$macro}};
242             for my $entry (@found) {
243             if ( $entry =~ m/^(.*?):\d+$/ ) {
244             $files_using_deprecated{$1} = 1;
245             }
246             }
247             }
248              
249             if ( 0 == scalar keys %files_using_deprecated ) {
250             $self->log("No deprecated macro usage found.");
251             }
252              
253             for my $file ( sort keys %files_using_deprecated ) {
254             $self->log("Fixing deprecated macros in $file");
255              
256             my $path = File::Spec->catfile( $dir, $file );
257             my $in = IO::File->new( $path, 'r' )
258             or $self->fail("Could not open $path: $!");
259              
260             my $tmp = File::Temp->new( UNLINK => 0,
261             DIR => $dir );
262              
263             while ( defined( my $line = <$in> ) ) {
264              
265             # Find a unique list of macros in this line
266              
267             my @macros = ( $line =~ m/\@(\w+)\@/g );
268             my %macros = map { $_ => 1 } @macros;
269             @macros = keys %macros;
270              
271             for my $macro (@macros) {
272             if ( exists $deprecated{$macro} ) {
273             $line =~ s/\@\Q$macro\E\@/\@$deprecated{$macro}\@/g;
274             }
275             }
276              
277             print {$tmp} $line;
278             }
279              
280             my $out = $tmp->filename;
281             $tmp->close or $self->fail("Could not close $out: $!");
282              
283             if ( !$self->dryrun ) {
284             rename $out, $path
285             or $self->fail("Could not move $out to $path: $!");
286             }
287             else {
288             unlink $out; # Just tidying
289             }
290             }
291             }
292              
293             return;
294             }
295              
296             no Moose;
297             1;
298             __END__
299              
300             =head1 NAME
301              
302             LCFG::Build::Tool::CheckMacros - LCFG software packaging tool
303              
304             =head1 VERSION
305              
306             This documentation refers to LCFG::Build::Tool::CheckMacros version 0.4.4
307              
308             =head1 SYNOPSIS
309              
310             my $tool = LCFG::Build::Tool::CheckMacros->new( dir => '.' );
311              
312             $tool->execute;
313              
314             my $tool2 = LCFG::Build::Tool::CheckMacros->new_with_options();
315              
316             $tool2->execute;
317              
318             =head1 DESCRIPTION
319              
320             This module provides software release tools for the LCFG build
321             suite.
322              
323             The LCFG build tools have support for autoconf-style (e.g. @FOO@)
324             macro substitution when building packages. There is a set of macros
325             which are built-in and the list can be extended by the user. This is a
326             tool for checking substitution variable usage to help spot potential
327             problems. It prints out a list of warnings, ordered by importance,
328             along with the file names and line numbers of where the macros are
329             used.
330              
331             More information on the LCFG build tools is available from the website
332             http://www.lcfg.org/doc/buildtools/
333              
334             =head1 ATTRIBUTES
335              
336             The following attributes are modifiable via the command-line (i.e. via
337             @ARGV) as well as the normal way when the Tool object is
338             created. Unless stated the options take strings as arguments and can
339             be used like C<--foo=bar>. Boolean options can be expressed as either
340             C<--foo> or C<--no-foo> to signify true and false values.
341              
342             =over 4
343              
344             =item fix_deprecated
345              
346             A boolean value which indicates whether any deprecated macros that are
347             found in the files scanned should be automatically replaced with their
348             modern equivalents.
349              
350             =item dryrun
351              
352             A boolean value which indicates whether actions which permanently
353             alter the contents of files should be carried out. The default value
354             is false (0). When running in dry-run mode various you will typically
355             get extra output to the screen showing what would have been done.
356              
357             =item quiet
358              
359             A boolean value which indicates whether the actions should attempt to
360             be quieter. The default value is false (0).
361              
362             =item dir
363              
364             The path of the project directory which contains the software for
365             which you want to create a release. If this is not specified then a
366             default value of the current directory (.) will be used. This
367             directory must already contain the LCFG build metadata file (lcfg.yml)
368             for the software.
369              
370             =back
371              
372             The following methods are not modifiable by the command-line, they are
373             however directly modifiable via the Tool object if
374             necessary. Typically you will only need to query these attributes,
375             they are automatically created when you need them using values for
376             some of the other command-line attributes.
377              
378             =over 4
379              
380             =item spec
381              
382             This is a reference to the current project metadata object, see
383             L<LCFG::Build::PkgSpec> for full details.
384              
385             =item vcs
386              
387             This is a reference to the current version-control object, see
388             L<LCFG::Build::VCS> for full details.
389              
390             =back
391              
392             =head1 SUBROUTINES/METHODS
393              
394             =over 4
395              
396             =item execute
397              
398             This method should be called to check macro usage within a project. It
399             will check every file which matches the specifications in the
400             C<translate> list (specified in the metadata file). Also, if present,
401             it will check the template for the project specfile. See the
402             "EXPLANATION OF OUTPUT" section below for an outline of the messages
403             which might be generated.
404              
405             =item fail($message)
406              
407             Immediately fails (i.e. dies) and displays the message.
408              
409             =item log($message)
410              
411             Logs the message to the screen if the C<quiet> attribute has not been
412             specified. A message string is prefixed with 'LCFG: ' to help visually
413             separate it from other output.
414              
415             =back
416              
417             =head1 EXPLANATION OF OUTPUT
418              
419             The possible warnings are listed in order of importance are:
420              
421             =over 4
422              
423             =item Use of unknown macro
424              
425             =item Use of deprecated macro
426              
427             =item Use of linux-only macro
428              
429             =item Use of macosx-only macro
430              
431             =item Use of compile-time-only macro
432              
433             =back
434              
435             Note that none of the warnings can be truly considered an error. Even
436             a message about an unknown macro is fine B<if> you add the
437             specification for that variable to a local C<CMakeLists.txt> file for
438             that component. In general it has to be left up to the software author
439             to interpret the true importance of a particular warning
440              
441             The special case in which all of these warnings (except that for
442             "deprecated macro") should be considered a fatal error is with the RPM
443             specfile. By design, locally defined macros and those which are
444             platform specific or compile-time only cannot be used in the specfile.
445              
446              
447             =head1 DEPENDENCIES
448              
449             This module is L<Moose> powered and uses L<MooseX::App::Cmd> to handle
450             command-line options.
451              
452             The following modules from the LCFG build tools suite are also
453             required: L<LCFG::Build::Tool>, L<LCFG::Build::PkgSpec>,
454             L<LCFG::Build::VCS> and VCS helper module for your preferred
455             version-control system.
456              
457             =head1 SEE ALSO
458              
459             L<LCFG::Build::Tools>, L<LCFG::Build::Skeleton>, lcfg-reltool(1)
460              
461             =head1 PLATFORMS
462              
463             This is the list of platforms on which we have tested this
464             software. We expect this software to work on any Unix-like platform
465             which is supported by Perl.
466              
467             Fedora12, Fedora13, ScientificLinux5, ScientificLinux6, MacOSX7
468              
469             =head1 BUGS AND LIMITATIONS
470              
471             There are no known bugs in this application. Please report any
472             problems to bugs@lcfg.org, feedback and patches are also always very
473             welcome.
474              
475             =head1 AUTHOR
476              
477             Stephen Quinney <squinney@inf.ed.ac.uk>
478              
479             =head1 LICENSE AND COPYRIGHT
480              
481             Copyright (C) 2008 University of Edinburgh. All rights reserved.
482              
483             This library is free software; you can redistribute it and/or modify
484             it under the terms of the GPL, version 2 or later.
485              
486             =cut