1 #!/usr/bin/perl
2 ############################################################################
3 #
4 # Creates make.dep - a list of all dependencies.
5 # Uses make.files as input; mkfiles.pl should be run before mkdep.pl.
6 #
7 ############################################################################
9 $obj_ext = "o";
10 $linebreak = "\\";
11 $dir_sep = "/";
12 $nodepend = 0;
13 $tolower = 1; # lowercase all filenames
14 $include_path = "";
16 #
17 # main - top level code
18 #
20 if ( @ARGV ) { # parse command line args
21 foreach $a ( @ARGV ) {
22 if ( $a eq "-w" ) {
23 $warnon = 1;
24 }
25 elsif ( ( $a eq "-?" ) || ( $a eq "-h" ) ) {
26 print "usage: perl mkdep.pl includedirs...\"\n\n" .
27 "\"includedirs\" is any number of include directories that are located\n".
28 "outside of your project directory tree.\n\n".
29 "example:\n perl mkdep.pl s:\\ w:\\include\n";
30 exit 1;
31 }
32 else {
33 push @extradirs, $a;
34 $include_path = $include_path . $a . ";";
35 }
36 }
37 print "Includepath used: ".join(" ",@extradirs)."\n";
38 }
40 &make_files;
41 &make_dep;
43 exit 0;
46 #
47 # make_files: read "make.files", create "make.ofiles"
48 #
50 sub make_files {
52 # create output file (now, to check for permissions)
53 open OFILES, ">make.ofiles" or
54 die("Can't create make.ofiles");
56 # read file list
57 #open FILES,"<make.files" or
58 # die "Can't open make.files";
59 #@lines = <FILES>;
60 #close FILES;
62 open FILES, "<make.files" or
63 die "Can't open make.files: $!";
64 while (<FILES>)
65 {
66 $line = $_;
67 #Trim whitespace from ends
68 $line =~ s/^\s|\t|\n//;
69 #Ignore if no text in line
70 next if ( length($line) < 1 );
71 #Ignore if starts with '#'
72 next if ( $line =~ /^#/ );
73 # print $line, "\n";
74 push @lines, $line;
75 }
76 close FILES;
78 # sort out header and source files
79 @hdr = sort grep(/\.(h|hpp|icc|ia|xpm)$/i,@lines);
80 @src = sort grep(/\.(c|cpp|cc|s)$/i,@lines);
82 print STDERR scalar @lines ." files found (".scalar @src." sources and ".scalar @hdr." headers) in \"make.files\".\n";
84 # scan specified include dirs
85 for ( @extradirs ) {
86 push @extrafiles, &find_files($_,"\.*",1);
87 }
88 # add external includefiles to file list
89 push @lines, @extrafiles;
91 print STDERR scalar @lines . " files found everywhere.\n";
93 # create @inc and %full_path
94 $last = "";
95 for ( @lines ) {
96 m/^(.*\/)([^\/]*)$/ || m/^()(.*)$/;
97 $path = $1;
98 $filepart = $2;
99 if ( $path ne $last ) {
100 # store dir path
101 if (length($path) > 0) {
102 push (@inc, $path );
103 }
104 $last = $path;
105 }
107 # remove any newlines at end of line
108 $filepart =~ s/(.*)\n+$/$1/g;
110 # store full path for all include files
111 if ( !defined $full_path{ lc $filepart } ) {
112 $full_path{lc $filepart} = $path.$filepart;
113 }
114 }
116 $includes = join (" \\\n\t-I", @inc);
117 $includes =~ s/\/ \\/ \\/g; # remove trailing slashes
119 # save source list for use by make_depend
120 $sourcelist = join(" ",@src);
122 # create object list from source list
123 $objects = &Objects( $sourcelist );
125 # save object list for use by make_depend
126 $objectlist = $objects;
128 # make a "lints" list for linting
129 $lints = $objects;
130 $lints =~ s/\.${obj_ext}/\.lint/g;
131 $lints =~ s/ / \\\n\t/g;
133 # put newline+tab on all files (to make nice lists)
134 $objects =~ s/ / \\\n\t/g;
136 # print file
137 $datestr = gmtime();
138 print OFILES "########################################################\n";
139 print OFILES "## File: make.ofiles\n";
140 print OFILES "## Purpose: Object file listing for use by Makefiles\n";
141 print OFILES "## Generated by mkdep.pl at :$datestr\n";
142 print OFILES "## Do not edit this file! Changes will be lost.\n";
143 print OFILES "########################################################\n\n";
144 print OFILES "OBJECTS = \\\n\t" . $objects . "\n\n";
145 print OFILES "LINTS = \\\n\t" . $lints . "\n\n";
146 if ( $includes ne "" ) {
147 print OFILES "INCLUDEPATH = \\\n\t-I" . $includes . "\n";
148 }
150 close OFILES;
151 }
155 #
156 # Finds files.
157 #
158 # Examples:
159 # find_files("/usr","\.cpp$",1) - finds .cpp files in /usr and below
160 # find_files("/tmp","^#",0) - finds #* files in /tmp
161 #
163 sub find_files {
164 my($dir,$match,$descend) = @_;
165 my($file,$p,@files);
166 local(*D);
167 $dir =~ s=\\=/=g;
168 ($dir eq "") && ($dir = ".");
170 if ( opendir(D,$dir) ) {
171 if ( $dir eq "." ) {
172 $dir = "";
173 } else {
174 ($dir =~ /\/$/) || ($dir .= "/");
175 }
176 foreach $file ( readdir(D) ) {
178 next if ( $file =~ /^\.\.?$/ );
179 $p = $dir . $file;
180 # ($file =~ /$match/i) && (push @files, ($tolower==0 ? $p : lc($p)));
181 ($file =~ /$match/i) && (push @files, $p );
182 if ( $descend && -d $p && ! -l $p ) {
183 push @files, &find_files($p,$match,$descend);
184 }
185 }
186 closedir(D);
187 }
188 return @files;
189 }
192 #
193 # strip_project_val(tag)
194 #
195 # Strips white space from project value strings.
196 #
198 sub strip_project_val {
199 my($v) = @_;
200 $v =~ s/^\s+//; # trim white space
201 $v =~ s/\s+$//;
202 return $v;
203 }
205 #
206 # Objects(files)
207 #
208 # Replaces any extension with .o ($obj_ext).
209 #
211 sub Objects {
212 local($_) = @_;
213 my(@a);
214 @a = split(/\s+/,$_);
215 foreach ( @a ) {
216 s-\.\w+$-.${obj_ext}-;
217 }
218 return join(" ",@a);
219 }
222 sub make_dep {
224 $outfile = "make.dep";
226 print STDERR "Parsing source files...\n";
228 open(DEP,">" . fix_path($outfile)) or
229 die ("Can't create \"$outfile\"");
231 &BuildObj( $objectlist, $sourcelist );
233 $datestr = gmtime();
234 print DEP "########################################################\n";
235 print DEP "## File: make.dep\n";
236 print DEP "## Purpose: Dependency listing for use by Makefiles\n";
237 print DEP "## Generated by mkdep.pl at :$datestr\n";
238 print DEP "## Do not edit this file! Changes will be lost.\n";
239 print DEP "########################################################\n\n";
240 print DEP $text;
241 close DEP;
242 print STDERR "\n";
245 if ( $warnon && (keys %missing) ) {
246 # print out missing files
247 if ( !open MISSING, ">makemiss.txt" ) {
248 print "Couldn't create \"makemiss.txt\": $!\n";
249 print "Printing on screen.\n";
250 *MISSING = *STDOUT;
251 }
253 print MISSING "Missing files: (Note: only the first miss for each file is logged)\n\n";
254 printf MISSING "%-32s %s\n","<file>","<included from>";
255 printf MISSING "%-32s %s\n","------","---------------";
256 @missingfiles = sort @missingfiles;
257 foreach ( @missingfiles ) {
258 @a = split( ",", $_ );
259 printf MISSING "%-32s %s\n",$a[0],$a[1];
260 }
261 close MISSING;
263 print STDOUT "(missing files written to \"makemiss.txt\")\n";
264 }
265 }
268 #
269 # BuildObj(objects,sources)
270 #
271 # Builds the object files.
272 #
274 sub uniq (@) {
275 if ($#_ <= 0) {
276 return @_;
277 }
278 my $prev = shift(@_);
279 my @ret = ($prev);
280 while(@_) {
281 my $curr = shift(@_);
282 if ($curr ne $prev) {
283 push @ret, $curr;
284 $prev = $curr;
285 }
286 }
287 return @ret;
288 }
290 sub BuildObj {
291 my($obj,$src) = @_;
292 my(@objv,$srcv,$i,$s,$o,$d,$c,$comp,$cimp);
293 $text = "";
294 @objv = split(/\s+/,$obj);
295 @srcv = split(/\s+/,$src);
296 $tot = $#objv;
298 # fix dependpath
299 if ( ! $depend_path_fixed ) {
300 $depend_path_fixed = 1;
301 $depend_path = $include_path;
302 $count = 0;
304 while ( $count < 100 ) {
305 if ( $depend_path =~ s/(\$[\{\(]?\w+[\}\)]?)/035/ ) {
306 $_ = $1;
307 s/[\$\{\}\(\)]//g;
308 $depend_path =~ s/035/$ENV{$_}/g;
309 } else {
310 $count = 100;
311 }
312 }
313 @dep_path = &split_path($depend_path);
314 unshift @dep_path, ""; # current dir first
315 }
317 %missing = ();
318 # go through file list
319 for $i ( 0..$#objv ) {
320 $s = $srcv[$i];
321 $o = $objv[$i];
322 next if $s eq "";
324 if ( $warnon ) {
325 print STDERR "\r" . ($i+1) . " missing files: ". scalar keys %missing;
326 }
327 else {
328 print STDERR "\r" . ($i+1);
329 }
331 @incfiles = ();
332 $d = &make_depend(lc $s);
334 for ( @incfiles ) {
335 push @ifiles, $full_path{$n};
336 }
338 $text .= $o . ": ${linebreak}\n\t" . $s;
340 @incpath = ();
341 for ( @incfiles ) {
342 if ( defined $full_path{$_} ) {
343 push @incpath, $full_path{$_};
344 }
345 }
346 @incpath = uniq sort @incpath;
347 for ( @incpath ) {
348 $text .= " ${linebreak}\n\t" . $_;
349 }
350 $text .= "\n\n";
352 # -----------------------------
353 # print "\n". $text;
354 # exit;
355 }
356 chop $text;
357 }
360 #
361 # build_dep() - Internal for make_depend()
362 #
364 sub build_dep {
365 my($file) = @_;
366 my(@i,$a,$n);
367 $a = "";
369 if ( !defined $depend_dict{$file} ) {
370 return $a;
371 }
373 @i = split(/ /,$depend_dict{$file});
374 # print "INC2: ($file) ".$depend_dict{$file}."\n";
376 for $n ( @i ) {
377 # if ( (!defined $dep_dict{$n}) && defined($full_path{$n}) ) {
378 if ( !defined $dep_dict{$n} ) {
379 $dep_dict{$n} = 1;
380 push @incfiles, $n;
381 # $a .= $full_path{$n} . " " . &build_dep($n);
382 &build_dep($n);
383 }
384 }
385 # print "INCFILES ($file): ".@incfiles."\n";
386 return $a;
387 }
390 #
391 # make_depend(file)
392 #
393 # Returns a list of included files.
394 # Uses the global $depend_path variable.
395 #
397 sub make_depend {
398 my($file) = @_;
399 my($i,$count);
400 if ( $nodepend ) {
401 return "";
402 }
404 @cur_dep_path = @dep_path;
405 if ( $file =~ /(.*[\/\\])/ ) {
406 $dep_curdir = $1;
407 splice( @cur_dep_path, 0, 0, $dep_curdir );
408 } else {
409 $dep_curdir = "";
410 }
411 $dep_file = $file;
412 &canonical_dep($file);
413 %dep_dict = ();
416 $i = &build_dep($file);
417 # chop $i;
419 $i =~ s=/=$dir_sep=g unless $is_unix;
420 $i =~ s=([a-zA-Z]):/=//$1/=g if (defined($gnuwin32) && $gnuwin32);
421 # @l = sort split(/ /,$i);
422 @l = split(/ /,$i);
423 return join(" ${linebreak}\n\t", @l );
424 # return $i; # all on one line!
425 }
427 #
428 # canonical_dep(file) - Internal for make_depend()
429 #
430 # Reads the file and all included files recursively.
431 # %depend_dict associates a file name to a list of included files.
432 #
434 sub canonical_dep {
435 my($file) = @_;
436 my(@inc,$i);
437 push @sfile, $file;
438 # -----------------------------
439 # print "FILE: $file\n";
440 @inc = &scan_dep($file);
442 if ( @inc ) {
443 $depend_dict{$file} = join(" ",@inc);
445 # recursively scan all files not already scanned
446 for $i ( @inc ) {
447 if ( ! defined( $depend_dict{$i} ) ) {
448 &canonical_dep($i);
450 # still nothing defined?
451 if ( ! defined( $depend_dict{$i} ) ) {
452 # insert dummy string, so we don't parse the file again
453 $depend_dict{$i} = "";
454 }
455 # print "CACHE: $i: $depend_dict{$i}\n";
456 }
457 }
458 }
459 pop @sfile, $file;
460 }
462 #
463 # scan_dep(file) - Internal for make_depend()
464 #
465 # Returns an array of included files.
466 #
468 sub scan_dep {
469 my($file) = @_;
470 my($dir,$path,$found,@allincs,@includes,%incs);
471 $path = ($file eq $dep_file) ? $file : $dep_curdir . $file;
472 @includes = ();
474 # print STDERR "SCAN_DEP $file\n";
476 # replace backslash with regular slash
477 $file =~ s-\\-/-g;
479 # look in the file list
480 if ( !defined $full_path{$file} ) {
481 # file not in list - some special case
483 # handle explicit path'ed includes (such as #include "common/bsp821.h")
484 if ( $file =~ /[\/\\]([^\\\/]+)$/ ) {
485 $full_path{$file} = $full_path{$1};
486 }
487 }
489 $open = open TMP,fix_path( $full_path{$file} );
491 if ( $open ) {
492 @source = <TMP>;
494 # find all lines with include as first text in line (no comments allowed)
495 @allincs = grep(/^\s*[\#\.]\s*include/,@source);
497 # print "SCAN_ALLINC: $#allincs include lines found\n";
499 # iterate all include lines
500 foreach ( @allincs ) {
501 # parse out filename
502 if ( !(/\s*[\#\.]\s*include\s+[<\"]([^>\"]*)[>\"]/) || defined($incs{$1}) ) {
503 next;
504 }
505 push(@includes, ($tolower==0 ? $1 : lc($1)));
507 # print "SCAN_INC: $1\n";
509 $incs{$1} = "1";
510 }
512 # -----------------------------
513 # print "INC: ".join( ",", @includes)."\n";
514 close(TMP);
515 }
516 else {
517 &add_missing( $file );
518 }
519 $/ = "\n";
520 return @includes;
521 }
524 sub add_missing {
525 my($file) = @_;
526 @s = @sfile;
527 pop @s;
528 push @missingfiles, $file.",".join(" -> ",@s);
529 $missing{$file} = 1;
530 }
532 #
533 # split_path(path)
534 #
535 # Splits a path containing : (Unix) or ; (MSDOS, NT etc.) separators.
536 # Returns an array.
537 #
539 sub split_path {
540 my($p) = @_;
541 return "" if !defined($p);
542 $p =~ s=:=;=g if $is_unix;
543 $p =~ s=[/\\]+=/=g;
544 $p =~ s=([^/:]);=$1/;=g;
545 $p =~ s=([^:;/])$=$1/=;
546 $p =~ s=/=$dir_sep=g unless $is_unix;
547 return split(/;/,$p);
548 }
550 #
551 # fix_path(path)
552 #
553 # Converts all '\' to '/' if this really seems to be a Unix box.
554 #
556 sub fix_path {
557 my($p) = @_;
558 # if ( $really_unix ) {
559 # $p =~ s-\\-/-g;
560 # } else {
561 # $p =~ s-/-\\-g;
562 $p =~ s-\\-/-g;
563 # }
564 return $p;
565 }