Statistics
| Branch: | Revision:

root / src / utils / opp_makedep @ cbd2c699

History | View | Annotate | Download (13.7 KB)

1 01873262 Georg Kunz
#!/usr/bin/env perl
2
#
3
# opp_makedep
4
#
5
# Update dependencies in a makefile.
6
#
7
# Based on the script at:
8
#  http://faqchest.dynhost.com/prgm/perlu-l/perl-98/perl-9802/perl-980200/perl98020418_25228.html
9
#
10
# Rewritten by Andras Varga, 2001
11
#
12
13
$Usage =
14
'Dependency Generator for OMNeT++/OMNEST, (c) 2001-2005 Andras Varga
15
Updates dependencies in a makefile for a C/C++ program.
16
17
Usage: opp_makedep [options] [--] <file_list>
18
19
where possible options are:
20
  --help
21
  -Y
22
  -I<include-dir>
23
  -I <include-dir>
24
  --stdout
25
  -f<makefile-name>
26
  --makefile <makefile-name>
27
  -P<object-file-prefix>, --objprefix <object-file-prefix>
28
  --objsuffix <object-file-suffix>
29
  --msgsuffix <msg-file-suffix>
30
  --msghsuffix <msg-header-file-suffix>
31
  --msgccsuffix <msg-cc-file-suffix>
32
  --srcdir <source-directory>
33
  --objdirtree
34
  -v
35
36
<file_list> is a list of C/C++ files for which dependencies must be generated.
37
38
 -- marks the end of the flags (optional).
39
 --help prints this message
40
 -Y causes opp_makedep to ignore (and also leave out from the dependencies)
41
   the include files that cannot be found.
42
 -I adds directories to the include search path
43
 -f,--makefile specifies the makefile in which to substitute. Defaults to
44
   "Makefile".
45
 --stdout causes dependencies to be written to the standard output,
46
   (instead of updating a makefile)
47
 -P, --objprefix prefixes each object file with the given string. When used,
48
   prefix is usually a directory prefix (for example "$(OBJDIR)/").
49
 --objsuffix sets the suffix of the object filename. It defaults to ".o".
50
 --msgsuffix, --msghsuffix, --msgccsuffix  specify the suffixes for .msg
51
   files and the files generated from them.
52
 --srcdir specifies the directory for the source files.
53
 --objdirtree the object files are assumed to be in a similar directory
54
   structure as the sources (by default, they are assumed to be all in a
55
   single directory)
56
 -v, --verbose  print progress information
57
58
Bug (feature): ignores #ifdefs altogether.
59
60
';
61
62
63
# the #include syntax we are looking for
64
$Syntax = '^\s*#\s*include\s+["<]([^">]*)([">])' ;
65
66
# cache. key: filename (project relative), value: array of include files (with project relative path)
67
$IncludeCache = ();
68
69
# marker line in the makefile
70
$Separator = '# DO NOT DELETE THIS LINE -- make depend depends on it.';
71
72
# IncludePath is a list of the directories to search for includes
73
@IncludePath = ();
74
75
# CFileList is a list of files that need to be scanned for #include
76
@CFileList = ();
77
78
# Below is the default suffix for the object file printed in the
79
# dependency list.  This default is overriden with --objsuffix
80
$ObjSuffix = ".o";
81
82
# Message file suffix, and suffix of the files generated from them
83
$MsgSuffix = ".msg";
84
$MsgHSuffix = "_m.h";
85
$MsgCCSuffix = "_m.cc";
86
87
# Source directory
88
$SourceDirectory = "";
89
90
# Prefix for object files in the generated dependencies (usually "$(OBJDIR)/"
91
# or something like that)
92
$ObjPrefix = "";
93
94
# If set, the object files are assumed to be in the same directory structure
95
# as the sources. Otherwise, they're assumed to be all in the same directory.
96
$ObjDirTree = 0;
97
98
# If this flag is set, include files that are not found in the search path are
99
# ignored. (Otherwise they cause an error.) The -Y option sets the flag to 1.
100
$IgnoreStdHeaders = 0;
101
102
# If set, writes dependencies to standard output instead up updating a makefile
103
$UseStdout = 0;
104
105
# Name of the makefile in which the dependencies should be substituted
106
$Makefile = "Makefile";
107
108
# Whether to be verbose
109
$Verbose = 0;
110
111
#
112
# If no args, print usage
113
#
114
if ($#ARGV == -1)
115
{
116
    print $Usage;
117
    exit(0);
118
}
119
120
#
121
#  This first thing this script does is parse the command line
122
#  for include path specifications, options, and files.
123
#
124
$accept_flags = 1;
125
while (@ARGV)
126
{
127
    $arg = shift @ARGV;
128
129
    if (!$accept_flags)
130
    {
131
        # after "--", everything is treated as input file name
132
        # call glob for Windows' sake
133
        push(@CFileList, glob($arg));
134
    }
135
    elsif ($arg eq "--")
136
    {
137
        $accept_flags = 0;
138
    }
139
    elsif ($arg eq "--help")
140
    {
141
        print $Usage;
142
        exit(1);
143
    }
144
    elsif ($arg eq "-I")
145
    {
146
        $dir_name = shift @ARGV;
147
        push(@IncludePath, $dir_name);
148
    }
149
    elsif ($arg =~ /^-I/)
150
    {
151
        $arg =~ s/^-I//;
152
        push(@IncludePath, $arg);
153
    }
154
    elsif ($arg eq "-Y")
155
    {
156
        $IgnoreStdHeaders = 1;
157
    }
158
    elsif ($arg eq "--stdout")
159
    {
160
        $UseStdout = 1;
161
    }
162
    elsif ($arg eq "--makefile")
163
    {
164
        $Makefile = shift @ARGV;
165
    }
166
    elsif ($arg eq "-f")
167
    {
168
        $Makefile = shift @ARGV;
169
    }
170
    elsif ($arg =~ /^-f/)
171
    {
172
        $arg =~ s/^-f//;
173
        $Makefile = $arg;
174
    }
175
    elsif ($arg eq "--objsuffix")
176
    {
177
        $ObjSuffix = shift @ARGV;
178
    }
179
    elsif ($arg =~ /^-P/)
180
    {
181
        $arg =~ s/^-P//;
182
        $ObjPrefix = $arg;
183
    }
184
    elsif ($arg eq "--objprefix")
185
    {
186
        $ObjPrefix = shift @ARGV;
187
    }
188
    elsif ($arg eq "--msgsuffix")
189
    {
190
        $MsgSuffix = shift @ARGV;
191
    }
192
    elsif ($arg eq "--msghsuffix")
193
    {
194
        $MsgHSuffix = shift @ARGV;
195
    }
196
    elsif ($arg eq "--msgccsuffix")
197
    {
198
        $MsgCCSuffix = shift @ARGV;
199
    }
200
    elsif ($arg eq "--objdirtree")
201
    {
202
        $ObjDirTree = 1;
203
    }
204
    elsif ($arg eq "--srcdir")
205
    {
206
        $SourceDirectory = shift @ARGV;
207
    }
208
    elsif ($arg eq "-v" || $arg eq "--verbose")
209
    {
210
        $Verbose = 1;
211
    }
212
    elsif ($arg =~ /^-/)
213
    {
214
        print STDERR "opp_makedep: warning: ignoring unknown option $arg (--help prints valid options)\n";
215
    }
216
    else
217
    {
218
        # otherwise we have a file name that needs to be added to the file list
219
        # call glob for Windows' sake
220
        push(@CFileList, glob($arg));
221
    }
222
}
223
224
# Remove "^" from the beginning of the prefix. This is needed to get
225
# "nmake -f Makefile.vc depend" working with both vc71 and vc80.
226
# In makemake-generated makefiles, objprefix is supposed to be "$O",
227
# and to prevent "$O" from being resolved prematurely by nmake, it needs
228
# to be quoted with "^"; however, vc80's nmake (unlike vc71 nmake) doesn't
229
# remove "^" from the command-line that actually invokes opp_makedep.
230
# Hence the workaround. Greetings to Microsoft.
231
$ObjPrefix =~ s/^\^//;
232
233
#
234
# If no files given, print warning
235
#
236
if ($#CFileList == -1)
237
{
238
    print STDERR "opp_makedep: warning: no input files\n";
239
    exit(0);
240
}
241
242
#
243
# for each file in the file list
244
#    scan it for #includes
245
#    print out the list of #includes
246
#
247
$Deps = "";
248
for (@CFileList)
249
{
250
    print "Processing $_...\n" if $Verbose;
251
    my @Includes = @ {&scan( $SourceDirectory.$_ ) };
252
    $Deps .= &print_dep( $_, @Includes );
253
}
254
if ($UseStdout) {
255
    print $Deps;
256
} else {
257
    update_makefile( $Deps );
258
}
259
exit(0);
260
261
262
sub scan {
263
   local($filename) = @_ ;
264
   %recursedFiles = ();
265
   $result = &do_scan($filename);
266
   die if %recursedFiles;  # must be empty again
267
   return $result;
268
}
269
270
#
271
# do_scan
272
#
273
#       This function is the guts of the dependency generation.
274
#  It is a recursive function that searches for #include's in
275
#  the file list.  If it runs into an include file, it then opens that file to
276
#  search for more #include's.
277
#
278
sub do_scan {
279
   local( $filename ) = @_ ;
280
   local( $included, $found, $_ );
281
   local( @list ) = ();
282
   local($IncludeDir);
283
   local( @result);
284
285
   if (! $IncludeCache{$filename})
286
   {
287
       my $txt = &read_file($filename);
288
       if ($txt eq '-')
289
       {
290
          # be more tolerant if filename contains wildcard (which means the shell couldn't expand it)
291
          if ($filename =~ /[\*\?]/) {
292
             print STDERR "opp_makedep: warning: unable to open $filename: $!\n";
293
          } else {
294
             print STDERR "opp_makedep: unable to open $filename: $!\n" ;
295
             exit(2);
296
          }
297
       }
298
299
       while ($txt =~ s/^\s*#\s*include\s+["<]([^">]*)([">])//m)
300
       {
301
           $included = $1;
302
           $delim = $2;  # " or >
303
304
           # try to find included file
305
           undef $found;
306
           if ( $included =~ /^\// )
307
           {
308
              # absolute path -- no search needed
309
              if (&exists($included))
310
              {
311
                 $found = 1;
312
              }
313
           }
314
           else
315
           {
316
              # filename only or relative path
317
              # if quote delimited, try relative path from location of current file
318
              if ($delim eq '"')
319
              {
320
                 $filename =~ /^(.*?)[^\/]*$/;
321
                 $dir = $1;
322
                 if (&exists($dir.$included))
323
                 {
324
                    $found = 1;
325
                    $included = $dir.$included;
326
                 }
327
              }
328
329
              # look for file in the search path
330
              if (!defined($found))
331
              {
332
                 foreach $IncludeDir (@IncludePath)
333
                 {
334
                    if (&exists("$IncludeDir/$included"))
335
                    {
336
                       $found = 1;
337
                       $included = "$IncludeDir/$included";
338
                       last;
339
                    }
340
                 }
341
              }
342
           }
343
344
           # if not found, either skip this file or raise error
345
           if (!defined($found))
346
           {
347
              if ($IgnoreStdHeaders)
348
              {
349
                 next;
350
              }
351
              else
352
              {
353
                 print STDERR "opp_makedep: file not in include path: $included (use -Y to turn off this error)\n";
354
                 exit(1);
355
              }
356
           }
357
358
           # if found, see if it's already on the list
359
           undef $found ;
360
           for ($[ .. $#Includes)
361
           {
362
               $found = $_, last if ( $Includes[$_] eq $included );
363
           }
364
365
           # nope! put it on
366
           if ( ! defined( $found ) )
367
           {
368
              push( @result, $included );
369
              push( @list, $included );
370
           }
371
       }
372
373
       # scan through each of the files that turned up
374
       foreach my $i (@list) {
375
           if (!$recursedFiles{$i}) { # guard against include cycles
376
               $recursedFiles{$i} = 1;
377
               push(@result, @{ &do_scan($i) } );
378
               delete $recursedFiles{$i};
379
           }
380
       }
381
382
       # throw out duplicates
383
       my %tmp;
384
       foreach my $i (@result) {
385
           $tmp{$i} = 1;
386
       }
387
       @result = keys(%tmp);
388
389
       $IncludeCache{$filename} = [ @result ];
390
   }
391
392
   return $IncludeCache{$filename};
393
}
394
395
#
396
# Returns true if the given file exists
397
#
398
sub exists {
399
   local($filename) = @_;
400
   return 1 if (-f $filename);
401
402
   # for *_m.h files, pretend that the file exists if the corresponding .msg file exists
403
   if ($filename =~ /${MsgHSuffix}$/) {
404
       my $msgfilename = $filename;
405
       $msgfilename =~ s/${MsgHSuffix}$/${MsgSuffix}/;
406
       return 1 if (-f $msgfilename);
407
   }
408
   elsif ($filename =~ /${MsgCCSuffix}$/) {
409
       my $msgfilename = $filename;
410
       $msgfilename =~ s/${MsgCCSuffix}$/${MsgSuffix}/;
411
       return 1 if (-f $msgfilename);
412
   }
413
   return 0;
414
}
415
416
#
417
# Returns the file's contents if exists, otherwise returns "-" (not "" because
418
# that might be the actual file contents, but "-" is not valid as C/C++ code).
419
#
420
sub read_file {
421
   local($filename) = @_;
422
   local($txt);
423
424
   print "    Reading file: $filename \n" if $Verbose;
425
   if (!open(INFILE, $filename)) {
426
       if ($filename =~ /${MsgHSuffix}$/) {
427
           # for *_m.h files, return the corresponding .msg file contents (which will be parsed for includes)
428
           my $msgfilename = $filename;
429
           $msgfilename =~ s/${MsgHSuffix}$/${MsgSuffix}/;
430
           open(INFILE, $msgfilename) || return "-";
431
           $filename = $msgfilename;
432
       }
433
       elsif ($filename =~ /${MsgCCSuffix}$/) {
434
           # for *_m.cc files, pretend it exists and includes its header file
435
           my $msgfilename = $filename;
436
           $msgfilename =~ s/${MsgCCSuffix}$/${MsgSuffix}/;
437
           if (-e $msgfilename) {
438
               my $msgheaderfile = $filename;
439
               $msgheaderfile =~ s/${MsgCCSuffix}$/${MsgHSuffix}/;
440
               return "#include <$msgheaderfile>\n";
441
           }
442
           else {
443
               return "-";
444
           }
445
       }
446
       else {
447
           return "-";
448
       }
449
   }
450
   read(INFILE, $txt, 100000) || die "cannot read $filename";
451
   close INFILE;
452
   return $txt;
453
}
454
455
#
456
# Return dependencies as a string ready to be inserted into a makefile
457
#
458
sub print_dep {
459
   local( $filename, @includes ) = @_ ;
460
   local( $_ );
461
   local( $dep );
462
463
   $filename =~ s/^\.\///;
464
465
   if ($ObjDirTree) {
466
       $target = $filename;
467
   } else {
468
       $filename =~ /([^\/]*)$/;
469
       $target = $1;
470
   }
471
   $target =~ s/\.[^.]*$/$ObjSuffix/;
472
   $target = $ObjPrefix . $target;
473
474
   $dep = "$target: ";
475
   $dep .= join( " \\\n  ", ("$SourceDirectory$filename", @includes) );
476
   $dep .= "\n";
477
   return $dep;
478
}
479
480
#
481
# Update makefile with the dependencies.
482
#
483
sub update_makefile {
484
    local($deps) = @_;
485
486
    # read makefile
487
    $makefiletext = "";
488
    open(INPUT, $Makefile) || die "opp_makedep: cannot open $Makefile: $!";
489
    while (<INPUT>) {
490
        chomp;
491
        s/\r$//;  # cygwin/mingw perl does not do CR/LF translation
492
        $makefiletext .= $_."\n";
493
    }
494
    close(INPUT);
495
496
    # hack in dependencies
497
    $makefiletext =~ s/\n${Separator}.*//s;
498
    $makefiletext .= "\n".$Separator."\n".$deps;
499
500
    # write it back into a temp file (overwriting directly the original
501
    # file is not safe -- what if we're aborted during write?)
502
    $tmpmakefile = $Makefile.".tmp";
503
    open(OUT, ">$tmpmakefile") || die "opp_makedep: cannot write $tmpmakefile: $!";
504
    print OUT $makefiletext;
505
    close(OUT);
506
507
    # replace makefile with temp file.
508
    # but under windows, rename chokes if target file already exists.
509
    # thus, try unix way first, and if it fails, do in the windows way
510
    if (!rename($tmpmakefile, $Makefile)) {
511
        unlink $Makefile || die "opp_makedep: cannot replace makefile $Makefile: $!";
512
        rename($tmpmakefile, $Makefile) || die "opp_makedep: cannot replace makefile $Makefile: $!";
513
    }
514
}
515