C-Scan-Constants

 view release on metacpan or  search on metacpan

lib/C/Scan/Constants.pm  view on Meta::CPAN

package C::Scan::Constants;

use 5.008003;
use strict;
use warnings;
use Carp;

use ExtUtils::Constant;
use ModPerl::CScan;
use File::Temp qw( tempdir );
use File::Copy;
use File::Spec;
use File::Path;
use Data::Dumper;
use IO::File;
use Config;

require Exporter;

our @ISA = qw(Exporter);

# Our functions are pretty uniquely named, and intended for
# calling from Makefile.PL, so we simply export them be default.
our @EXPORT      = qw( extract_constants_from
                       write_constants_module );

our %EXPORT_TAGS = ( 'all' => [ @EXPORT ] );
our @EXPORT_OK   = ( @{ $EXPORT_TAGS{'all'} } );

our $VERSION = "1.020";
$VERSION = eval $VERSION;

# This module was originally written to support a custom pure-Perl
# build system named Blueprint.  If you know of or use Blueprint,
# this section will mean something to you.  If not, ignore it.
my $g_use_blueprint_sections;
BEGIN {
    # Initialize global variable(s)
    $g_use_blueprint_sections = 0;

    eval 'require Blueprint';

    unless ($@) {
        $g_use_blueprint_sections = 1;
    }

    # Now blueprint comment block protection is quietly enabled.
    # This will almost never be turned on.
}

# _get_constant_data_blobs_from()
#
# Internal function.
#
# Returns a two blobs of data from the supplied file:
#   ($defines,   <-- #define macros with no args
#    $typedefs)  <-- #typedef enum constants
sub _get_constant_data_blobs_from {
    my $file_to_relocate = shift;

    if ( ! -f $file_to_relocate ) {
        croak "$file_to_relocate does not appear to be accessible";
    }

    # Create a temp directory here.
    my $temp_scan_dir = tempdir( 'c_scan_const_XXXXX',
                                 DIR     => File::Spec->tmpdir(),
                                 CLEANUP => 1 )
        or die "Internal error: failed to create temp dir";

    # copy the file into it
    my $scan_file_basename = ( File::Spec->splitpath($file_to_relocate) )[2];
    my $relocated_file = File::Spec->catpath( '',
                                              $temp_scan_dir,
                                              $scan_file_basename );
    copy($file_to_relocate, $relocated_file)
        or croak "Could not copy $file_to_relocate to $relocated_file";

    # scan the file
    my $c_header_file = ModPerl::CScan->new( filename => $relocated_file );
    
    if ( !defined( $c_header_file ) ) {
        croak "Could not create ModPerl::CScan obj for $relocated_file";
    }

    # Ugly hack to fix ActivePerl config bomb, i.e. expectation that "cppstdin"
    # is the cpp we'll be using.  This assumes MinGW is installed, which we
    # attempted to enforce in the Makefile.PL.  It probably assumes more than
    # should be safely assumed about the return data structure from Data::Flow,
    # but it seems to work.
    if ( $^O =~ /MSWin/i ) {
        my $cur_cppstdin = $c_header_file->get('Cpp')->{cppstdin};
	my $cur_cc = $Config{cc};
	unless (     $cur_cppstdin =~ /$cur_cc/
	         and $cur_cppstdin =~ /\-E/ ) {
            $c_header_file->get('Cpp')->{cppstdin} = "$cur_cc -E";
	}
    }
    
    # Swallow STDERR temporarily
    open my $OLDERR, ">&", STDERR;
    close(STDERR);

	# Redirect temporarily to the bit bucket, but keep it open
	# to avoid conflicting in a -w environment such as under test.
    # TBD: Make this friendlier for non-*n[u|i]x systems.
    open *STDERR, ">", "/dev/null";

    # We only care about unadorned macros, i.e. "defines"
    my $defs     = $c_header_file->get("defines_no_args");
### These next lines represent possible future functionality ####
#    my $defs2    = $c_header_file->get("defines_maybe");
#    my $defs3    = $c_header_file->get("defines_full");
#    my $defs4    = $c_header_file->get("defines_args");
#    my $defs5    = $c_header_file->get("defines_no_args_full");
#    my $defs6    = $c_header_file->get("Defines");
##################################################################
    my $typedefs = $c_header_file->get("typedef_texts");


### For debugging only ######################################################
### NOTE: need to send STDERR somewhere other than /dev/null for these to
###       work as intended.
###
#    warn sprintf("[$file_to_relocate] defines_no_args = %s", Dumper($defs));
#    warn sprintf("[$file_to_relocate] defines_maybe = %s", Dumper($defs2));
#    warn sprintf("[$file_to_relocate] defines_full = %s", Dumper($defs3));
#    warn sprintf("[$file_to_relocate] defines_args = %s", Dumper($defs4));
#    warn sprintf("[$file_to_relocate] defines_no_args_full = %s", Dumper($defs5));
#    warn sprintf("[$file_to_relocate] Defines = %s", Dumper($defs6));
#    warn sprintf("[$file_to_relocate] enums = %s", Dumper($typedefs));
#############################################################################

    # Restore STDERR and close the temp filehandle for neatness.
    close STDERR;
    open STDERR, ">&", $OLDERR;
	close $OLDERR;

    # Return the file object returned from ModPerl::CScan->new()
    # Note: these may be empty (hashref, arrayref)
    return ($defs, $typedefs);
}




# extract_constants_from()
#
# Exported function.
#
# This function takes a list of C header (.h) files and returns a list
# of constants information suitable for supplying as the NAME parameter
# to ExtUtils::Constant.
sub extract_constants_from {
    my @c_header_paths = @_;         # full paths to each .h file to scan

    my @all_constants;

    C_HEADER_FILE:
    foreach my $c_header_file ( @c_header_paths ) {
        my ($defs,
            $typedefs) = _get_constant_data_blobs_from( $c_header_file );

        if ( ( !defined $defs ||
               (defined $defs && scalar( keys %$defs ) == 0) ) and
             ( !defined $typedefs ||
               (defined $typedefs && scalar @$typedefs == 0) ) ) {
            warn "WARNING: Found no constants in $c_header_file.";
            next C_HEADER_FILE;
        }

        # Do the messy enum extraction
        my @enums = _extract_enum_constants_from( $typedefs );

        # We convert the base filename into something we can use
        # to avoid the error of throwing away the "filename constant"
	# e.g.  #ifndef FOO_H_
	#       #define FOO_H_
	my $all_caps_basename = uc ( ( File::Spec->splitpath($c_header_file) )[2] );
	$all_caps_basename =~ s/[.]/_/g;

        # Consolidate all names found into a single list.
        # Note that we discard string constants.
        my @constant_names = ( @enums,
                               grep {
                                   my $defn = $_;

                                   # Toss header file identifiers, but only
				   # when they are *really* header file identifiers.
                                   ( $defn !~ /_H[_]?$/
				     or ($defn =~ /_H[_]?$/



( run in 0.481 second using v1.01-cache-2.11-cpan-71847e10f99 )