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 Cwd 'abs_path'; ############################################################################## # Important Hashes ############################################################################## # # VALID-TYPES HASH # Key Value # 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 ( ) { 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 ( ) { # 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 !~ // and $curr_file !~ /^\/usr\/qnx.*?\/target/ and $curr_file !~ //) { # 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 ( ) { 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;