trick/bin/pm/ICG.pm
Alex Lin 14a75508a3 Cleaning up once include variables and copyright cleanup.
Changed all header file once include variables to follow the same naming
convention and not start with any underscores.  Also deleted old
incorrect copyright notices.  Also removed $Id: tags from all files.

Fixes #14.  Fixes #22.
2015-03-23 16:03:14 -05:00

455 lines
17 KiB
Perl

package ICG ;
use Exporter ();
@ISA = qw(Exporter);
use vars qw($arg_lang);
@EXPORT = qw(ICG);
use File::Basename ;
use Text::Balanced qw ( extract_bracketed );
use Cwd ;
use strict ;
use gte ;
use MIS ;
use html ;
use trick_print ;
use trick_version ;
use Data::Dumper ;
use Cwd 'abs_path';
##############################################################################
# Important Hashes
##############################################################################
#
# VALID-TYPES HASH
# Key Value
# <type-specifier> Ref to TYPE-INFO HASH
#
# ----------------------------------------------------------------------------
# TYPE-INFO HASH
# Key Value
# value Trick parameter type as defined in parameter_types.h
# Examples: "TRICK_CHARACTER", "TRICK_INTEGER", etc.
# name C/C++ type specifier.
# Examples: "char", "int", etc.
# intrinsic 0= User-defined type. 1= Intrinsic type.
# class Parent class.
# file File where this type was declared.
# typedef 0 or 1. Whether declaration starts with struct or union.
# inherit_class @ of strings that name the classes from which this class is inherits.
# virtual Whether this class inherits virtually.
# abstract Whether this class is abstract (and therefore cannot be instantiated). 0=no, 1=yes.
# def_construct 0 or 1. Indicates whether we should create an io_src_allocate_ routine.
##############################################################################
# Global Variables
##############################################################################
# Variable: $top_file
# Description:
my $top_file ;
# Variable: all_icg_depends_ref
# Description:
my ( $all_icg_depends_ref ) ;
my $verbose ;
my %header ;
my $indent = 3 ;
my %already_processed ;
my @file_levels ;
################################################################################
# SUBROUTINE: ICG
#
# DESCRIPTION: FIXME
#
# PARAMETERS:
# temp_h_files array of strings representing the names of header files to be processed.
# operation scalar string that indicates what ICG is supposed to do.
# Recognized values are "full", "CP", "sim_services", "single", "s_source", "tree".
# user_defs array of strings (e.g.: "-DFOO" representing user defines.
# sim_ref Reference to global SIM-HASH.
#
# RETURNS: FIXME
################################################################################
sub ICG(\@$$$) {
my ($temp_h_files , $operation, $user_defs, $sim_ref) = @_ ;
my @h_files ;
my $wd ;
my $path_dir ;
my @include_paths ;
my @defines ;
my $curr_file ;
my (@file_stack , %file_contents ) ;
my ($f) ;
my $file_path_dir ;
my @inc_paths ;
my @file_order ;
my %tree ;
my @icg_exclude_dirs ;
my ($version, $thread, $year) ;
my $cc ;
@h_files = @$temp_h_files ;
# used by make_makefile
$all_icg_depends_ref = \%{$$sim_ref{all_icg_depends}} ;
$wd = cwd();
$path_dir = dirname($wd) ;
if ($path_dir eq ".") {
$path_dir = cwd();
}
($version, $thread) = get_trick_version() ;
($year) = $version =~ /^(\d+)/ ;
($cc = gte("TRICK_CC")) =~ s/\n// ;
@include_paths = $ENV{"TRICK_CFLAGS"} =~ /(-I\s*\S+)/g ; # get include paths from TRICK_CFLAGS
push @include_paths , ("-I" . $ENV{"TRICK_HOME"} . "/trick_source" , "-I../include") ;
@defines = $ENV{"TRICK_CFLAGS"} =~ /(-D\S+)/g ; # get defines from TRICK_CFLAGS
if ( $ENV{"TRICK_CFLAGS"} !~ /DTRICK_VER=/ ) {
push @defines , "-DTRICK_VER=$year" ;
}
push @defines , "-DTRICK_ICG" ;
# Break up the $TRICK_ICG_EXCLUDE string based on some delimiter (colon).
# In an array context, split returns a list of the things that were found.
@icg_exclude_dirs = split /:/ , $ENV{"TRICK_ICG_EXCLUDE"};
# See if there are any elements in the icg_exclude_dirs array
if (scalar @icg_exclude_dirs) {
@icg_exclude_dirs = sort(@icg_exclude_dirs );
# Error check - delete any element that is null
# (note: sort forced all blank names to front of array
@icg_exclude_dirs = map { s/(^\s+|\s+$)//g ; $_ } @icg_exclude_dirs ;
while ( not length @icg_exclude_dirs[0] ) {
# Delete an element from the left side of an array (element zero)
shift @icg_exclude_dirs ;
}
@icg_exclude_dirs = map { (-e $_) ? abs_path($_) : $_ } @icg_exclude_dirs ;
}
@inc_paths = $ENV{"TRICK_CFLAGS"} =~ /-I\s*(\S+)/g ; # get include paths from TRICK_CFLAGS
# Get only the include paths that exist
my @valid_inc_paths ;
foreach (@inc_paths) {
push @valid_inc_paths , $_ if ( -e $_ ) ;
}
@inc_paths = @valid_inc_paths ;
# return if no header files are to be processed
if (scalar @h_files == 0) {
return -1 ;
}
open SLIB , ".icg_no_found" ;
while ( <SLIB> ) {
chomp ;
$$sim_ref{icg_no}{$_} = 1 ;
}
while (@h_files) {
my %temp_hash ;
my $cc_inc ;
my $curr_dir ;
#Clear out variables for new file
undef %file_contents ;
undef @file_stack ;
undef @file_levels ;
undef @file_order ;
$curr_file = shift @h_files ;
#get the absolute path to this header file... use this from now on.
$curr_file = abs_path(dirname($curr_file)) . "/" . basename($curr_file) ;
$top_file = $curr_file ;
$curr_dir = dirname($curr_file) ;
if ( $operation ne "treedep" and $operation ne "single" ) {
trick_print($$sim_ref{fh}, "ICG $curr_file\n\n" , "normal_cyan" , $$sim_ref{args}{v}) ;
}
open FILE, "$curr_file" or die "Couldn't find file: $curr_file\n";
# Use the C preprocessor to get all of the include dependencies for the current file
# This way we can properly take care of #defines with h files
# When you open a filehandle (FILE) that's already open, Perl
# implicitly closes it first.
if ( $curr_file =~ /\.h$/ ) {
open FILE, "cd $curr_dir ; $cc -E -C @include_paths @defines @$user_defs $curr_file |" ;
}
elsif ( $curr_file =~ /(\.hh|\.H|\.h\+\+|\.hxx)$/ ) {
open FILE, "cd $curr_dir ; $cc -E -C -x c++ @include_paths $cc_inc @defines @$user_defs $curr_file |" ;
}
else {
open FILE, "cd $curr_dir ; $cc -E -C -x c @include_paths $cc_inc @defines @$user_defs $curr_file |" ;
}
# read the result of the preprocessor and separate the output to individual files
while ( <FILE> ) {
# change file we are in to include dependencies
if ( /^#(\s*line)?\s*\d+\s*"(.*?)"/ ) {
$curr_file = $2 ;
$curr_file =~ s/\/(\/)+/\//g ;
# Replace directories that are symbolic links
# with real/absolute paths. Also substitute out
# /dir_a/dir_b/../file.c with /dir_a/file.c
$curr_file = abs_path(dirname($curr_file)) . "/" . basename($curr_file) ;
if ($curr_file !~ /^\/usr\/include/ and
$curr_file !~ /^\/usr\/lib/ and
($curr_file =~ /^\/usr\/local\/trick/ # Trick install PREFIX
or $curr_file !~ /^\/usr\/local/) and
$curr_file !~ /^\/usr\/X11/ and
$curr_file !~ /gcc-lib/ and
$curr_file !~ /^\/Developer/ and
$curr_file !~ /^\/Library\/Developer/ and
$curr_file !~ /^\/Applications\/Xcode.app/ and
$curr_file !~ /^\/usr\/llvm-gcc-4.2/ and
$curr_file !~ /opt\/sfw/ and
$curr_file !~ /<built-in>/ and
$curr_file !~ /^\/usr\/qnx.*?\/target/ and
$curr_file !~ /<command.line>/) {
# keep a record of the order the files were visited
push @file_order , $curr_file ;
}
}
# add line to file we are in
if ($curr_file !~ /^\/usr\/include/ and $curr_file !~ /^\/usr\/lib/ and
$curr_file !~ /^\/usr\/X11/ and $curr_file !~ /gcc-lib/ ) {
# string concatenate
$file_contents{$curr_file} .= "$_" ;
}
}
# Convert the flat file output from cc to a tree dependency hash
foreach $f ( @file_order ) {
# skip c++ .i files
next if ( $f =~ /\.i$/ ) ;
# Use absolute path
if ( $f =~ /^\// ) {
my $old_f = $f ;
$f = abs_path(dirname($f)) . "/" . basename($f) ;
if ( ! exists $file_contents{$f} ) {
$file_contents{$f} = $file_contents{$old_f} ;
}
}
else {
my $old_f = $f ;
$f = abs_path(dirname("$curr_dir/$f")) . "/" . basename($f) ;
if ( ! exists $file_contents{$f} ) {
$file_contents{$f} = $file_contents{$old_f} ;
}
}
if (! defined $file_stack[$#file_stack] or $f ne $file_stack[$#file_stack]) {
# if file name is equal to previous file on stack pop up to that file
if ($#file_stack ge 1 and $file_stack[$#file_stack - 1] eq $f) {
pop @file_stack ;
}
else {
# make sure this is not the top level file
if ( $#file_stack ge 0 ) {
# if file name does not exist in it's parent's list add it
my $temp_f = quotemeta $f ;
if ( !(grep /$temp_f/, @{$$all_icg_depends_ref{$top_file}{$file_stack[$#file_stack]}}) ) {
push @{$$all_icg_depends_ref{$top_file}{$file_stack[$#file_stack]}} , $f ;
}
# if there is no entry yet for this file make one
if (!exists $$all_icg_depends_ref{$top_file}{$f}) {
@{$$all_icg_depends_ref{$top_file}{$f}} = () ;
}
}
# push this file name onto the stack
push @file_stack, $f ;
$tree{$f} = 1 ;
if ($operation eq "tree" ) {
print $$sim_ref{fh}, " " x ($#file_stack * 3) . $f . "\n" ;
}
}
}
}
# if ICG was called with the -tree option exit here.
return %tree if ($operation eq "tree" or $operation eq "treedep" ) ;
# Get the realpath of all the files we read in from the C preprocessor
foreach $f ( keys %file_contents ) {
my $new_name = abs_path(dirname($f)) . "/" . basename($f) ;
if ( $new_name ne $f and !exists $file_contents{$new_name} ) {
$file_contents{$new_name} = $file_contents{$f};
}
}
my %all_file_names ;
@file_levels = traverse_tree_to_file_levels($top_file, \%all_file_names , \%{$$all_icg_depends_ref{$top_file}} ) ;
foreach $f (@file_levels) {
my @structs_info ;
my @enums_info ;
my $continue ;
# skip file if already processed this invocation of ICG
if ( exists $already_processed{$f} ) {
next ;
}
$already_processed{$f} = 1 ;
if ( $f =~ /\.C$|\.cc$|\.cxx$|\.cpp$|\.c\+\+$/ ) {
next ;
}
elsif ( $f =~ /\.c$/ ) {
# MIS
mis_c($f , $file_contents{$f} , $sim_ref) ;
next ;
}
elsif ( $f =~ /\.i$/ ) {
next ;
}
$continue = 1 ;
foreach my $ie ( @icg_exclude_dirs ) {
# if file location begins with $ie (an IGC exclude dir)
my $dum = $f ;
if ( $dum =~ s/^\Q$ie// ) {
if ( $dum =~ /^\// ) {
trick_print($$sim_ref{fh}, "ICG skipping $f (ICG exclude dir $ie)\n" , "normal_yellow" , $$sim_ref{args}{v}) ;
$continue = 0 ;
last ; # break out of loop
}
}
}
next if ( $continue == 0 ) ;
if ( exists $$sim_ref{icg_no}{$f} ) {
trick_print($$sim_ref{fh}, "ICG skipping $f (ICG No found)\n" , "normal_yellow" , $$sim_ref{args}{v}) ;
next ;
}
$file_path_dir = dirname($f) ;
if ($file_path_dir eq ".") {
$file_path_dir = cwd();
}
$file_path_dir =~ s/\/+$// ; # remove trailing slash
$file_path_dir =~ s/\/include$// ;
# extract the Trick header
%header = extract_trick_header( $f, $file_contents{$f}, $verbose , $indent );
$$sim_ref{python_module}{$f} = $header{python_module} ;
$$sim_ref{python_module}{$f} =~ s/\(|\)|\s+//g ;
if ( $operation eq "CP" ) {
trick_print($$sim_ref{fh}, "Getting dependencies for $f\n" , "normal_white" , $$sim_ref{args}{v});
delete $$sim_ref{head_deps}{$f} ;
# add any external dependencies from an .exd file
my ($exd_file_name) = $f ;
my @exd_file_list ;
undef @exd_file_list ;
$exd_file_name =~ s/\.[^\/]*$/\.exd/ ;
if ( open EXD , "$exd_file_name" ) {
while ( <EXD> ) {
push @exd_file_list , $_ if ( /\.o\s*$/ and !/^\s*#/ ) ;
}
chomp @exd_file_list ;
}
# we have library dependencies in an h file. ( possible for c++ )
if (exists $header{libdep} or (scalar @exd_file_list != 0) ) {
my @lib_list = split /\)[ \t\n\r\*]*\(/ , $header{libdep} ;
push @lib_list , @exd_file_list ;
foreach my $l (@lib_list) {
my $found = 0 ;
$l =~ s/\(|\)|\s+//g ;
$l =~ s/\${(.+?)}/$ENV{$1}/eg ;
next if ( $l eq "" ) ;
if ( $l !~ /\.o$/ ) {
foreach my $inc ( dirname($f) , @inc_paths) {
if ( -e "$inc/$l" ) {
#print "found $inc/$l$ext\n" ;
my $f = abs_path(dirname("$inc/$l")) . "/" . basename("$inc/$l") ;
push @{$$sim_ref{mis_entry_files}} , $f ;
push @{$$sim_ref{head_deps}{$f}{files}} , $f ;
$found = 1 ;
last ;
}
}
} else {
$l =~ s/o$// ;
foreach my $inc ( $file_path_dir , @inc_paths) {
foreach my $ext ( "cpp" , "cc" , "c" , "c++" , "cxx" , "C" ) {
if ( -e "$inc/$l$ext" ) {
#print "found $inc/$l$ext\n" ;
my $f = abs_path(dirname("$inc/$l$ext")) . "/" . basename("$inc/$l$ext") ;
push @{$$sim_ref{mis_entry_files}} , $f ;
push @{$$sim_ref{head_deps}{$f}{files}} , $f ;
$found = 1 ;
last ;
}
elsif ( -e "$inc/src/$l$ext" ) {
#print "found $inc/src/$l$ext\n" ;
my $f = abs_path(dirname("$inc/src/$l$ext")) . "/" . basename("$inc/src/$l$ext") ;
push @{$$sim_ref{mis_entry_files}} , $f ;
push @{$$sim_ref{head_deps}{$f}{files}} , $f ;
$found = 1 ;
last ;
}
}
last if ( $found == 1 ) ;
}
}
if ( $found == 0 ) {
trick_print($$sim_ref{fh}, "$f: Warning: Could not find dependency $l" . "o\n" ,
"normal_yellow" , $$sim_ref{args}{v}) ;
}
}
}
next ;
}
} # End of foreach on filelevel
} # End of gigantic while loop
return 0 ;
} # end of ICG
sub traverse_tree_to_file_levels($$$$$) {
my ($file, $all_file_names_ref , $curr_tree ) = @_ ;
my $a ;
my @lower_levels ;
my $io_src_file ;
my ($ret_stat , @ret_files) ;
my $ret ;
foreach $a ( @{$$curr_tree{$file}} ) {
if ( ($a ne $file) and !(exists $$all_file_names_ref{$a}) ) {
$$all_file_names_ref{$a} = 1 ;
( @ret_files ) = &traverse_tree_to_file_levels($a, $all_file_names_ref , $curr_tree ) ;
push @lower_levels , @ret_files ;
}
}
$$all_file_names_ref{$file} = 1 ;
return ( @lower_levels , $file ) ;
}
1;