#!/usr/bin/perl ############################################################################ # # Creates make.dep - a list of all dependencies. # Uses make.files as input; mkfiles.pl should be run before mkdep.pl. # ############################################################################ $obj_ext = "o"; $linebreak = "\\"; $dir_sep = "/"; $nodepend = 0; $tolower = 1; # lowercase all filenames $include_path = ""; # # main - top level code # if ( @ARGV ) { # parse command line args foreach $a ( @ARGV ) { if ( $a eq "-w" ) { $warnon = 1; } elsif ( ( $a eq "-?" ) || ( $a eq "-h" ) ) { print "usage: perl mkdep.pl includedirs...\"\n\n" . "\"includedirs\" is any number of include directories that are located\n". "outside of your project directory tree.\n\n". "example:\n perl mkdep.pl s:\\ w:\\include\n"; exit 1; } else { push @extradirs, $a; $include_path = $include_path . $a . ";"; } } print "Includepath used: ".join(" ",@extradirs)."\n"; } &make_files; &make_dep; exit 0; # # make_files: read "make.files", create "make.ofiles" # sub make_files { # create output file (now, to check for permissions) open OFILES, ">make.ofiles" or die("Can't create make.ofiles"); # read file list #open FILES,"; #close FILES; open FILES, ") { $line = $_; #Trim whitespace from ends $line =~ s/^\s|\t|\n//; #Ignore if no text in line next if ( length($line) < 1 ); #Ignore if starts with '#' next if ( $line =~ /^#/ ); # print $line, "\n"; push @lines, $line; } close FILES; # sort out header and source files @hdr = sort grep(/\.(h|hpp|icc|ia|xpm)$/i,@lines); @src = sort grep(/\.(c|cpp|cc|s)$/i,@lines); print STDERR scalar @lines ." files found (".scalar @src." sources and ".scalar @hdr." headers) in \"make.files\".\n"; # scan specified include dirs for ( @extradirs ) { push @extrafiles, &find_files($_,"\.*",1); } # add external includefiles to file list push @lines, @extrafiles; print STDERR scalar @lines . " files found everywhere.\n"; # create @inc and %full_path $last = ""; for ( @lines ) { m/^(.*\/)([^\/]*)$/ || m/^()(.*)$/; $path = $1; $filepart = $2; if ( $path ne $last ) { # store dir path if (length($path) > 0) { push (@inc, $path ); } $last = $path; } # remove any newlines at end of line $filepart =~ s/(.*)\n+$/$1/g; # store full path for all include files if ( !defined $full_path{ lc $filepart } ) { $full_path{lc $filepart} = $path.$filepart; } } $includes = join (" \\\n\t-I", @inc); $includes =~ s/\/ \\/ \\/g; # remove trailing slashes # save source list for use by make_depend $sourcelist = join(" ",@src); # create object list from source list $objects = &Objects( $sourcelist ); # save object list for use by make_depend $objectlist = $objects; # make a "lints" list for linting $lints = $objects; $lints =~ s/\.${obj_ext}/\.lint/g; $lints =~ s/ / \\\n\t/g; # put newline+tab on all files (to make nice lists) $objects =~ s/ / \\\n\t/g; # print file $datestr = gmtime(); print OFILES "########################################################\n"; print OFILES "## File: make.ofiles\n"; print OFILES "## Purpose: Object file listing for use by Makefiles\n"; print OFILES "## Generated by mkdep.pl at :$datestr\n"; print OFILES "## Do not edit this file! Changes will be lost.\n"; print OFILES "########################################################\n\n"; print OFILES "OBJECTS = \\\n\t" . $objects . "\n\n"; print OFILES "LINTS = \\\n\t" . $lints . "\n\n"; if ( $includes ne "" ) { print OFILES "INCLUDEPATH = \\\n\t-I" . $includes . "\n"; } close OFILES; } # # Finds files. # # Examples: # find_files("/usr","\.cpp$",1) - finds .cpp files in /usr and below # find_files("/tmp","^#",0) - finds #* files in /tmp # sub find_files { my($dir,$match,$descend) = @_; my($file,$p,@files); local(*D); $dir =~ s=\\=/=g; ($dir eq "") && ($dir = "."); if ( opendir(D,$dir) ) { if ( $dir eq "." ) { $dir = ""; } else { ($dir =~ /\/$/) || ($dir .= "/"); } foreach $file ( readdir(D) ) { next if ( $file =~ /^\.\.?$/ ); $p = $dir . $file; # ($file =~ /$match/i) && (push @files, ($tolower==0 ? $p : lc($p))); ($file =~ /$match/i) && (push @files, $p ); if ( $descend && -d $p && ! -l $p ) { push @files, &find_files($p,$match,$descend); } } closedir(D); } return @files; } # # strip_project_val(tag) # # Strips white space from project value strings. # sub strip_project_val { my($v) = @_; $v =~ s/^\s+//; # trim white space $v =~ s/\s+$//; return $v; } # # Objects(files) # # Replaces any extension with .o ($obj_ext). # sub Objects { local($_) = @_; my(@a); @a = split(/\s+/,$_); foreach ( @a ) { s-\.\w+$-.${obj_ext}-; } return join(" ",@a); } sub make_dep { $outfile = "make.dep"; print STDERR "Parsing source files...\n"; open(DEP,">" . fix_path($outfile)) or die ("Can't create \"$outfile\""); &BuildObj( $objectlist, $sourcelist ); $datestr = gmtime(); print DEP "########################################################\n"; print DEP "## File: make.dep\n"; print DEP "## Purpose: Dependency listing for use by Makefiles\n"; print DEP "## Generated by mkdep.pl at :$datestr\n"; print DEP "## Do not edit this file! Changes will be lost.\n"; print DEP "########################################################\n\n"; print DEP $text; close DEP; print STDERR "\n"; if ( $warnon && (keys %missing) ) { # print out missing files if ( !open MISSING, ">makemiss.txt" ) { print "Couldn't create \"makemiss.txt\": $!\n"; print "Printing on screen.\n"; *MISSING = *STDOUT; } print MISSING "Missing files: (Note: only the first miss for each file is logged)\n\n"; printf MISSING "%-32s %s\n","",""; printf MISSING "%-32s %s\n","------","---------------"; @missingfiles = sort @missingfiles; foreach ( @missingfiles ) { @a = split( ",", $_ ); printf MISSING "%-32s %s\n",$a[0],$a[1]; } close MISSING; print STDOUT "(missing files written to \"makemiss.txt\")\n"; } } # # BuildObj(objects,sources) # # Builds the object files. # sub uniq (@) { if ($#_ <= 0) { return @_; } my $prev = shift(@_); my @ret = ($prev); while(@_) { my $curr = shift(@_); if ($curr ne $prev) { push @ret, $curr; $prev = $curr; } } return @ret; } sub BuildObj { my($obj,$src) = @_; my(@objv,$srcv,$i,$s,$o,$d,$c,$comp,$cimp); $text = ""; @objv = split(/\s+/,$obj); @srcv = split(/\s+/,$src); $tot = $#objv; # fix dependpath if ( ! $depend_path_fixed ) { $depend_path_fixed = 1; $depend_path = $include_path; $count = 0; while ( $count < 100 ) { if ( $depend_path =~ s/(\$[\{\(]?\w+[\}\)]?)/035/ ) { $_ = $1; s/[\$\{\}\(\)]//g; $depend_path =~ s/035/$ENV{$_}/g; } else { $count = 100; } } @dep_path = &split_path($depend_path); unshift @dep_path, ""; # current dir first } %missing = (); # go through file list for $i ( 0..$#objv ) { $s = $srcv[$i]; $o = $objv[$i]; next if $s eq ""; if ( $warnon ) { print STDERR "\r" . ($i+1) . " missing files: ". scalar keys %missing; } else { print STDERR "\r" . ($i+1); } @incfiles = (); $d = &make_depend(lc $s); for ( @incfiles ) { push @ifiles, $full_path{$n}; } $text .= $o . ": ${linebreak}\n\t" . $s; @incpath = (); for ( @incfiles ) { if ( defined $full_path{$_} ) { push @incpath, $full_path{$_}; } } @incpath = uniq sort @incpath; for ( @incpath ) { $text .= " ${linebreak}\n\t" . $_; } $text .= "\n\n"; # ----------------------------- # print "\n". $text; # exit; } chop $text; } # # build_dep() - Internal for make_depend() # sub build_dep { my($file) = @_; my(@i,$a,$n); $a = ""; if ( !defined $depend_dict{$file} ) { return $a; } @i = split(/ /,$depend_dict{$file}); # print "INC2: ($file) ".$depend_dict{$file}."\n"; for $n ( @i ) { # if ( (!defined $dep_dict{$n}) && defined($full_path{$n}) ) { if ( !defined $dep_dict{$n} ) { $dep_dict{$n} = 1; push @incfiles, $n; # $a .= $full_path{$n} . " " . &build_dep($n); &build_dep($n); } } # print "INCFILES ($file): ".@incfiles."\n"; return $a; } # # make_depend(file) # # Returns a list of included files. # Uses the global $depend_path variable. # sub make_depend { my($file) = @_; my($i,$count); if ( $nodepend ) { return ""; } @cur_dep_path = @dep_path; if ( $file =~ /(.*[\/\\])/ ) { $dep_curdir = $1; splice( @cur_dep_path, 0, 0, $dep_curdir ); } else { $dep_curdir = ""; } $dep_file = $file; &canonical_dep($file); %dep_dict = (); $i = &build_dep($file); # chop $i; $i =~ s=/=$dir_sep=g unless $is_unix; $i =~ s=([a-zA-Z]):/=//$1/=g if (defined($gnuwin32) && $gnuwin32); # @l = sort split(/ /,$i); @l = split(/ /,$i); return join(" ${linebreak}\n\t", @l ); # return $i; # all on one line! } # # canonical_dep(file) - Internal for make_depend() # # Reads the file and all included files recursively. # %depend_dict associates a file name to a list of included files. # sub canonical_dep { my($file) = @_; my(@inc,$i); push @sfile, $file; # ----------------------------- # print "FILE: $file\n"; @inc = &scan_dep($file); if ( @inc ) { $depend_dict{$file} = join(" ",@inc); # recursively scan all files not already scanned for $i ( @inc ) { if ( ! defined( $depend_dict{$i} ) ) { &canonical_dep($i); # still nothing defined? if ( ! defined( $depend_dict{$i} ) ) { # insert dummy string, so we don't parse the file again $depend_dict{$i} = ""; } # print "CACHE: $i: $depend_dict{$i}\n"; } } } pop @sfile, $file; } # # scan_dep(file) - Internal for make_depend() # # Returns an array of included files. # sub scan_dep { my($file) = @_; my($dir,$path,$found,@allincs,@includes,%incs); $path = ($file eq $dep_file) ? $file : $dep_curdir . $file; @includes = (); # print STDERR "SCAN_DEP $file\n"; # replace backslash with regular slash $file =~ s-\\-/-g; # look in the file list if ( !defined $full_path{$file} ) { # file not in list - some special case # handle explicit path'ed includes (such as #include "common/bsp821.h") if ( $file =~ /[\/\\]([^\\\/]+)$/ ) { $full_path{$file} = $full_path{$1}; } } $open = open TMP,fix_path( $full_path{$file} ); if ( $open ) { @source = ; # find all lines with include as first text in line (no comments allowed) @allincs = grep(/^\s*[\#\.]\s*include/,@source); # print "SCAN_ALLINC: $#allincs include lines found\n"; # iterate all include lines foreach ( @allincs ) { # parse out filename if ( !(/\s*[\#\.]\s*include\s+[<\"]([^>\"]*)[>\"]/) || defined($incs{$1}) ) { next; } push(@includes, ($tolower==0 ? $1 : lc($1))); # print "SCAN_INC: $1\n"; $incs{$1} = "1"; } # ----------------------------- # print "INC: ".join( ",", @includes)."\n"; close(TMP); } else { &add_missing( $file ); } $/ = "\n"; return @includes; } sub add_missing { my($file) = @_; @s = @sfile; pop @s; push @missingfiles, $file.",".join(" -> ",@s); $missing{$file} = 1; } # # split_path(path) # # Splits a path containing : (Unix) or ; (MSDOS, NT etc.) separators. # Returns an array. # sub split_path { my($p) = @_; return "" if !defined($p); $p =~ s=:=;=g if $is_unix; $p =~ s=[/\\]+=/=g; $p =~ s=([^/:]);=$1/;=g; $p =~ s=([^:;/])$=$1/=; $p =~ s=/=$dir_sep=g unless $is_unix; return split(/;/,$p); } # # fix_path(path) # # Converts all '\' to '/' if this really seems to be a Unix box. # sub fix_path { my($p) = @_; # if ( $really_unix ) { # $p =~ s-\\-/-g; # } else { # $p =~ s-/-\\-g; $p =~ s-\\-/-g; # } return $p; }