[project @ 1997-01-21 10:45:35 by sof]
[ghc-hetmet.git] / ghc / utils / mkdependHS / mkdependHS.prl
1 # *** MSUB does some substitutions here ***
2 # *** grep for $( ***
3 #
4 # tries to work like mkdependC
5 #
6 # ToDo: strip out all the .h junk
7 #
8 ($Pgm = $0) =~ s/.*\/([^\/]+)$/\1/;
9 $Usage  = <<EOUSAGE;
10 Usage: $Pgm [mkdependHS options] [-- GHC options --] srcfile1 [srcfile2 ...]
11
12 Options recognised wherever they occur (mkdependHS or GHC):
13
14     -D<blah>    A cpp #define; usual meaning
15     -i<dirs>    Add <dirs> (colon-separated) to list of directories
16                 to search for "import"ed modules
17     -I<dir>     Add <dir> to list of directories to search for
18                 .h files (i.e., usual meaning)
19     -syslib <blah> This program uses this GHC system library; take
20                 appropriate action (e.g., recognise when they are
21                 "import"ing a module from that library).
22     -fhaskell1.[2-9] Deal with the oddities associated with a
23                 particular version of Haskell 1.
24
25 mkdependHS-specific options (not between --'s):
26
27     -v          Be verbose.
28     -v -v       Be very verbose.
29     -f blah     Use "blah" as the makefile, rather than "makefile"
30                 or "Makefile".
31     -o <osuf>   Use <osuf> as the "object file" suffix ( default: .o)
32     -s <suf>    Make extra dependencies for files with
33                 suffix <suf><osuf>; thus, "-o .hc -s _a" will
34                 make dependencies both for .hc files and for _a.hc
35                 files.  (Useful in conjunction with NoFib "ways".)
36     --exclude-module=<file> 
37                 Regard <file> as "stable"; i.e., eXclude it from having
38                 dependencies on it.
39     -x          same as --exclude-module
40     --exclude-directory=<dirs> 
41                 Regard : separated list of directories as containing stable,
42                 don't generate any dependencies on modules therein.
43     -Xdirs      same as --exclude-directory
44     --include-module=<file> 
45                 Regard <file> as not "stable"; i.e., generate dependencies
46                 on it (if any). This option is normally used in conjunction 
47                 with the --exclude-directory option.
48
49 EOUSAGE
50
51 $Status  = 0; # just used for exit() status
52 $Verbose = 0; # 1 => verbose, 2 => very verbose
53 $Dashdashes_seen = 0;
54
55 # Try to guess how to run gcc's CPP directly -------------
56
57 $OrigCpp = '$(RAWCPP)';
58 if ( $OrigCpp !~ /(\S+)\s+(.*)/ ) {
59     $Cpp = $OrigCpp;
60 } else {
61     $cmd  = $1;
62     $rest = $2;
63     if ( -x $cmd ) { # cool
64         $Cpp = $OrigCpp;
65     } else { # oops; try to guess
66         $GccV = `gcc -v 2>&1`;
67         if ( $GccV =~ /Reading specs from (.*)\/specs/ ) {
68             $Cpp = "$1/cpp $rest";
69         } else {
70             die "hscpp: don't know how to run cpp: $OrigCpp\n";
71         }
72     }
73 }
74
75 if ( $ENV{'TMPDIR'} ) { # where to make tmp file names
76     $Tmp_prefix = $ENV{'TMPDIR'} . "/mkdependHS$$";
77 } else {
78     $Tmp_prefix ="$(TMPDIR)/mkdependHS$$";
79     $ENV{'TMPDIR'} = '$(TMPDIR)'; # set the env var as well
80 }
81
82 #------------------------------------------------------------------------
83 # If you are adjusting paths by hand for a binary GHC distribution,
84 # de-commenting the line to set GLASGOW_HASKELL_ROOT should do.
85 # Or you can leave it as is, and set the environment variable externally.
86 #------------------------------------------------------------------------
87 # $ENV{'GLASGOW_HASKELL_ROOT'} = '/some/absolute/path/name';
88
89 if (! $ENV{'GLASGOW_HASKELL_ROOT'}) { # good -- death to environment variables
90     $TopPwd         = '$(TOP_PWD)';
91     $InstLibDirGhc  = '$(INSTLIBDIR_GHC)';
92     $InstDataDirGhc = '$(INSTDATADIR_GHC)';
93 } else {
94     $TopPwd = $ENV{'GLASGOW_HASKELL_ROOT'};
95
96     if ('$(INSTLIBDIR_GHC)' =~ /.*(\/lib\/ghc\/\d\.\d\d\/[^-]-[^-]-[^-]\/.*)/) {
97         $InstLibDirGhc  = $ENV{'GLASGOW_HASKELL_ROOT'} . $1;
98     } else {
99         print STDERR "GLASGOW_HASKELL_ROOT environment variable is set;\nBut can't untangle $(INSTLIBDIR_GHC).\n(Installation error)\n";
100         exit(1);
101     }
102
103     if ('$(INSTDATADIR_GHC)' =~ /.*(\/lib\/ghc\/\d\.\d\d\/.*)/) {
104         $InstDataDirGhc = $ENV{'GLASGOW_HASKELL_ROOT'} . $2;
105     } else {
106         print STDERR "GLASGOW_HASKELL_ROOT environment variable is set;\nBut can't untangle $(INSTDATADIR_GHC).\n(Installation error)\n";
107         exit(1);
108     }
109 }
110
111 $Unlit = ( $(INSTALLING) ) ? "$InstLibDirGhc/unlit"
112                            : "$TopPwd/ghc/utils/unlit/unlit";
113
114 $Begin_magic_str = "# DO NOT DELETE: Beginning of Haskell dependencies\n";
115 $End_magic_str = "# DO NOT DELETE: End of Haskell dependencies\n";
116 $Obj_suffix = 'o';
117 $ghc_version_info = int ( $(PROJECTVERSION) * 100 );
118
119 $Import_dirs = '.';
120 %Syslibs = ();
121 %LibIfaces  = ();  # known prelude/syslib ifaces; read from a file
122 %Ignore_dirs = (); # directories to considered stable.
123 %IgnoreMe = ();
124
125 $Haskell_1 = 3; # assume Haskell 1.3. Changed by -fhaskell-1.?
126 $Include_dirs = '-I.';
127 $Makefile = '';
128 @Src_files = ();
129
130 &mangle_command_line_args();
131
132 # load up LibIfaces tables:
133 &read_MODULES('prelude', 'prelude');
134 foreach $lib ( @Syslibs ) {
135     &read_MODULES('syslib', $lib);
136 }
137 #print STDERR "libs provide:",(keys %LibIfaces),"\n";
138
139 if ( $Status ) {
140     print stderr $Usage;
141     exit(1);
142 }
143
144 push(@Defines,
145      ("-D__HASKELL1__=$Haskell_1",
146       "-D__GLASGOW_HASKELL__=$ghc_version_info"));
147
148 @Import_dirs  = split(/:/,$Import_dirs);
149 @Include_dirs = split(/\s+/,$Include_dirs); # still has -I's in it
150 # NB: We keep the scalar-variable equivalents to use in error messages
151
152 if ( ! $Makefile && -f 'makefile' ) {
153     $Makefile = 'makefile';
154 } elsif ( ! $Makefile && -f 'Makefile') {
155     $Makefile = 'Makefile';
156 } elsif ( ! $Makefile) {
157     die "$Pgm: no makefile or Makefile found\n";
158 }
159
160 print STDERR "CPP defines=@Defines\n" if $Verbose;
161 print STDERR "Import_dirs=@Import_dirs\n" if $Verbose;
162 print STDERR "Include_dirs=@Include_dirs\n" if $Verbose;
163
164 &preprocess_import_dirs();
165
166 @Depend_lines = ();
167
168 foreach $sf (@Src_files) {
169     # just like lit-inputter
170     # except it puts each file through CPP and
171     # a de-commenter (not implemented);
172     # builds up @Depend_lines
173     print STDERR "Here we go for source file: $sf\n" if $Verbose;
174     ($bf = $sf) =~ s/\.l?hs$//;
175     #push(@Depend_lines, "$bf.$Obj_suffix $bf.hi : $sf\n");
176     push(@Depend_lines, "$bf.$Obj_suffix : $sf\n");
177     foreach $suff (@File_suffix) {
178         push(@Depend_lines, "$bf.${suff}_$Obj_suffix : $sf\n");
179     }
180
181     # if it's a literate file, .lhs, then we de-literatize it:
182     if ( $sf !~ /\.lhs$/ ) {
183         $file_to_read = $sf;
184     } else {
185         $file_to_read = "$Tmp_prefix.hs";
186         local($to_do) = "$Unlit $sf $file_to_read";
187         &run_something($to_do, 'unlit');
188     }
189     &slurp_file_for_imports($file_to_read, $sf);
190
191     if ( $sf =~ /\.lhs$/ ) {
192         unlink "$Tmp_prefix.hs";
193     }
194 }
195
196 # OK, mangle the Makefile
197 unlink("$Makefile.bak");
198 rename($Makefile,"$Makefile.bak");
199 # now copy Makefile.bak into Makefile, rm'ing old dependencies
200 # and adding the new
201 open(OMKF,"< $Makefile.bak") || die "$Pgm: can't open $Makefile.bak: $!\n";
202 open(NMKF,"> $Makefile") || die "$Pgm: can't open $Makefile: $!\n";
203 select(NMKF);
204 $_ = <OMKF>;
205 while ($_ && $_ ne $Begin_magic_str) { # copy through, 'til Begin_magic_str
206     print $_;
207     $_ = <OMKF>;
208 }
209 while ($_ && $_ ne $End_magic_str) { # delete 'til End_magic_str
210     $_ = <OMKF>;
211 }
212 # insert dependencies
213 print $Begin_magic_str;
214 print @Depend_lines;
215 print $End_magic_str;
216 while (<OMKF>) { # copy the rest through
217     print $_;
218 }
219 close(NMKF) || exit(1);
220 close(OMKF) || exit(1);
221 exit 0;
222
223 sub mangle_command_line_args {
224     while($_ = $ARGV[0]) {
225         shift(@ARGV);
226
227         if ( /^--$/ ) {
228             $Dashdashes_seen++;
229
230         } elsif ( /^-D(.*)/ ) { # recognized wherever they occur
231             push(@Defines, $_);
232         } elsif ( /^-i(.*)/ ) {
233             $Import_dirs .= ":$1";
234         } elsif ( /^-I/ ) {
235             $Include_dirs .= " $_";
236         } elsif ( /^-syslib$/ ) {
237             push(@Syslibs, &grab_arg_arg($_,''));
238         } elsif ( /^-fhaskell-1\.([2-9])/ ) {
239             $Haskell_1 = $1;
240         } elsif ($Dashdashes_seen != 1) { # not between -- ... --
241             if ( /^-v$/ ) {
242                 $Verbose++;
243             } elsif ( /^-f(.*)/ ) {
244                 $Makefile       = &grab_arg_arg('-f',$1);
245             } elsif ( /^-o(.*)/ ) {
246                 $Obj_suffix     = &grab_arg_arg('-o',$1);
247             #
248             # --exclude-module=mod => it's stable, trust me!
249             
250             } elsif ( /^-(x|-exclude-module=)(.*)/ ) { 
251                 local($thing) = &grab_arg_arg($1,$2);
252                 $IgnoreMe{$thing} = 'y';
253             } elsif ( /^-(X|-exclude-directory=)(.*)/ ) { 
254                 foreach $d ( split(/:/,&grab_arg_arg($1, $2)) ) {
255                    $Ignore_dirs{$d} = "$d";
256                 }
257             } elsif ( /^--include-module=(.*)/ ) { 
258                 local($thing) = &grab_arg_arg($1,$2);
259                 $IgnoreMe{$thing} = 'n';
260             } elsif ( /^-s(.*)/ ) {
261                 local($suff)    =  &grab_arg_arg('-s',$1);
262                 push(@File_suffix, $suff);
263             } elsif ( /^-/ ) {
264                 print STDERR "$Pgm: unknown option ignored: $_\n";
265                 $Status++;
266             } else {
267                 push(@Src_files, $_);
268             }
269
270         } elsif ($Dashdashes_seen == 1) { # where we ignore unknown options
271             push(@Src_files, $_) if ! /^-/;
272         }
273     }
274     @File_suffix = sort (@File_suffix);
275 }
276
277 sub read_MODULES {
278     local($flavor,$lib) = @_;
279
280     local($m_dir) = '';
281     if ($flavor eq 'prelude') {    
282         $m_dir = ( $(INSTALLING) ) ? "$InstDataDirGhc/imports" : "$TopPwd/ghc/lib";
283     } else {
284         $m_dir = ( $(INSTALLING) ) ? "$InstSysLibDir/$lib"     : "$TopPwd/hslibs/$lib";
285     }
286     local($m_file) = "$m_dir/MODULES";
287
288     open(MFILE, "< $m_file") || die "$Pgm: can't open $m_file to read\n";
289     while (<MFILE>) {
290         chop;
291         # strip comments and leading/trailing whitespace
292         s/#.*//;
293         s/^\s+//;
294         s/\s+$//;
295         next if /^$/; # nothing left!
296         
297         $LibIfaces{"$lib:$_"} = 1; # record that this library provides this iface
298     }
299     close(MFILE);
300 }
301
302 sub grab_arg_arg {
303     local($option, $rest_of_arg) = @_;
304     
305     if ($rest_of_arg) {
306         return($rest_of_arg);
307     } elsif ($#ARGV >= 0) {
308         local($temp) = $ARGV[0]; shift(@ARGV); 
309         return($temp);
310     } else {
311         print STDERR "$Pgm: no argument following $option option\n";
312         $Status++;
313     }
314 }
315
316 sub preprocess_import_dirs {
317     # it's probably cheaper to find out what's in all
318     # the @Import_dirs before we start processing.
319     local($d, $thing);
320     local($_);
321     %ModuleIn = ();
322
323     foreach $d ( @Import_dirs ) {
324         # Check to see if it can be ignored
325         print STDERR "Ignore imports from $d\n" if $Verbose && $Ignore_dirs{$d};
326         next if $Ignore_dirs{$d};
327
328         opendir(DIR, $d) || die "$Pgm: can't open directory $d\n";
329
330         for ($_ = readdir(DIR); $_; $_ = readdir(DIR)) {
331             next unless /(.*)\.hi$/;
332             $thing = $1;
333             if ($ModuleIn{$thing} && $ModuleIn{$thing} ne $d) {
334                 print STDERR "$Pgm: warning: $thing.hi appears in both $ModuleIn{$thing} and $d!\n";
335             } else {
336                 $ModuleIn{$thing} = $d;
337             }
338         }
339         closedir(DIR); # No, don't check the error code
340     }
341
342     # Add all the modules found in the ignorable directories
343     # to the IgnoreMe array before we start scanning for imports.
344     foreach $d (keys %Ignore_dirs) {
345
346         opendir(DIR, $d) || die "$Pgm: can't open directory $d\n";
347
348         for ($_ = readdir(DIR); $_; $_ = readdir(DIR)) {
349             next unless /(.*)\.(hi|l?hs)$/;
350             #don't tag it twice or overwrite it with a diff. value
351             next if $IgnoreMe{$1};
352             print STDERR "Module $d/$1.$2 will be ignored\n" if $Verbose;
353
354             $IgnoreMe{$1} = 'y';
355         }
356         closedir(DIR); # No, don't check the error code
357     }
358 }
359
360 sub slurp_file_for_imports {
361     local($file_to_read, $orig_src_file) = @_;
362     local($follow_file);
363
364     local($last_seen_dir) = $orig_src_file;
365     $last_seen_dir =~ s/\/[^\/]+$//; # strip to dir name
366     $last_seen_dir = '.' if ($last_seen_dir eq $orig_src_file);
367
368     # we mangle #include's so they will also leave something
369     # behind to indicate the dependency on _them_
370     
371     # Worth our while to relativise the path or 
372     # assume it is there in the first place? -- SOF
373     #
374     print STDERR "/usr/bin/sed -e '/^# *include/{p;s/^# *include/!include/;};s/'\\''//g;s/\"//g' $file_to_read | $Cpp $Include_dirs -I$last_seen_dir @Defines |\n" if $Verbose;
375
376     open(SRCFILE, "/usr/bin/sed -e '/^# *include/{p;s/^# *include/!include/;};s/'\\''//g;s/\"//g' $file_to_read | $Cpp $Include_dirs -I$last_seen_dir @Defines |")
377         || die "$Pgm: Can't open $file_to_read: $!\n";
378
379     while (<SRCFILE>) {
380         #
381         # inport {-# SOURCE #-} Foo (bar) generates dependencies on the source file only,
382         # the compiler will deal with the absence of Foo.hi by consulting the
383         # source for Foo directly.
384         #
385         # >? import qualified ModuleName | !include "foo"
386         #
387         #
388         next unless (/^>?\s*(import)(\s+{-#\s*SOURCE\s*#-})?(\s+qualified)?\s+([A-Z][A-Za-z0-9_']*)/ || /^!(include)(\s+)"(\S+)"/);
389         $todo    = $1;
390         $source  = ( $2 ne '') ? 1 : 0;
391         $modname = $4;
392
393         if ($todo eq 'import') {
394             if ( $IgnoreMe{$modname} eq 'y' ) {
395                 $follow_file = '__ignore__';
396             } elsif ( $ModuleIn{$modname} ) {
397                    $follow_file = "$ModuleIn{$modname}/$modname.hi";
398             } else { # hard way
399                 $follow_file
400                   = &find_in_Import_dirs($orig_src_file, $modname, $last_seen_dir );
401             }
402         } else {
403             if ( $IgnoreMe{$modname} eq 'y' ) {
404                 $follow_file = '__ignore__';
405             } else {
406                 $follow_file
407                   = &find_in_Include_dirs($orig_src_file, $modname, $last_seen_dir);
408             }
409         }
410
411         if (! $follow_file) { # it didnae find anything
412             die "$orig_src_file: Couldn't handle: $_\n";
413
414         } else { # it found something
415             if ($follow_file ne '__ignore__') {
416                 local($int_file);
417                 $int_file = $follow_file;
418
419                 if ( $int_file !~ /\.(l?hs|hi)$/ ) {
420                     push(@Depend_lines, "$bf.$Obj_suffix : $int_file\n");
421                     foreach $suff (@File_suffix) {
422                         push(@Depend_lines, "$bf.${suff}_$Obj_suffix : $int_file\n");
423                     }
424
425                 } else {
426                     $int_file =~ s/\.l?hs$//;
427                     $int_file =~ s/\.hi$//;
428                     local($source_dep);
429
430                     if ( $source && -f "$int_file.hs" ) {
431                         $source_dep = "$int_file.hs";
432                         push(@Depend_lines, "$bf.$Obj_suffix : $source_dep\n");
433                     } elsif ( $source && -f "$int_file.lhs" ) {
434                         $source_dep = "$int_file.lhs";
435                         push(@Depend_lines, "$bf.$Obj_suffix : $source_dep\n");
436                     } else {
437                         if ( $source ) {
438                            print STDERR "Warning: could not find source file dependency $int_file.(hs|lhs)\n";
439                         }
440                         push(@Depend_lines, "$bf.$Obj_suffix : $int_file.hi\n");
441                     }
442
443                     if ( ! $source ) {
444                        foreach $suff (@File_suffix) {
445                           push(@Depend_lines, "$bf.${suff}_$Obj_suffix : $int_file.${suff}_hi\n");
446                        }
447                     } else {
448                        foreach $suff (@File_suffix) {
449                           push(@Depend_lines, "$bf.${suff}_$Obj_suffix : $source_dep\n");
450                        }
451                    }
452                 }
453             }
454         }
455     }
456     close(SRCFILE) || exit(1);
457 }
458
459 # when we see something, we cache that fact ('y').
460 # also, when we get a miss, we cache that (so we don't try later); ('n')
461 %FileExists = ();
462
463 sub find_in_Import_dirs {
464     local($orig_src_file, $modname, $last_seen_dir, $source) = @_;
465     local($import_dir);
466     local($do_magical_check) = 0;
467     local($name_to_check);
468
469     # do it the old hard way: hop along Import_dir list
470     foreach $import_dir (@Import_dirs) {
471         # handle . magically
472         if ($import_dir eq '.') {
473             # record that we should do a SPECIAL try for a file in last_seen_dir (LAST)
474             $do_magical_check = 1;
475         }
476
477         $name_to_check = "$import_dir/$modname.hi";
478         if ( $FileExists{$name_to_check} ne 'n' ) { # either 'y' or nothing
479             print STDERR "trying $name_to_check...\n" if $Verbose >= 2; # very verbose
480             return($name_to_check) if $FileExists{$name_to_check} eq 'y';
481             if (-f $name_to_check) {
482                 $FileExists{$name_to_check} = 'y';
483                 return($name_to_check) ;
484             } else {
485                 $FileExists{$name_to_check} = 'n';
486             }
487         }
488
489         $name_to_check = "$import_dir/$modname.hs";
490         print STDERR "trying... $name_to_check\n" if $Verbose >= 2; # very verbose
491         return($name_to_check) if -f $name_to_check;
492
493         $name_to_check = "$import_dir/$modname.lhs";
494         print STDERR "trying... $name_to_check\n" if $Verbose >= 2; # very verbose
495         return($name_to_check) if -f $name_to_check;
496     }
497     if ($do_magical_check == 1) {
498         $name_to_check = "$last_seen_dir/$modname.hi";
499
500         if ( $FileExists{$name_to_check} ne 'n' ) { # either 'y' or nothing
501             print STDERR "trying $name_to_check...\n" if $Verbose >= 2; # very verbose
502             return($name_to_check) if $FileExists{$name_to_check} eq 'y';
503             if (-f $name_to_check) {
504                 $FileExists{$name_to_check} = 'y';
505                 return($name_to_check) ;
506             } else {
507                 $FileExists{$name_to_check} = 'n';
508             }
509         }
510
511         $name_to_check = "$last_seen_dir/$modname.lhs";
512         print STDERR "trying... $name_to_check\n" if $Verbose >= 2; # very verbose
513         return($name_to_check) if -f $name_to_check;
514
515         $name_to_check = "$last_seen_dir/$modname.hs";
516         print STDERR "trying... $name_to_check\n" if $Verbose >= 2; # very verbose
517         return($name_to_check) if -f $name_to_check;
518     }
519
520     # OK, maybe it's referring to something in a system library
521     foreach $lib ( @Syslibs ) {
522         return('__ignore__') if $LibIfaces{"$lib:$modname"};
523     }
524
525     # Last hope: referring to a Prelude interface
526     return('__ignore__') if $LibIfaces{"prelude:$modname"};
527
528     die "No file `$modname.hi', `$modname.lhs' or `$modname.hs' (reqd from file `$orig_src_file')\namong import directories:\n\t$Import_dirs\n";
529 }
530
531 sub find_in_Include_dirs {
532     local($orig_src_file, $name, $last_seen_dir) = @_;
533     local($include_dir);
534     local($do_magical_check) = 0;
535
536     # no funny name guessing here
537
538     # hop along Include_dir list
539     foreach $include_dir (@Include_dirs) {
540         $include_dir =~ s/^-I//;
541
542         # handle . magically
543         if ($include_dir eq '.') {
544             # record that we should do a SPECIAL try for a file in last_seen_dir (LAST)
545             $do_magical_check = 1;
546         }
547         print STDERR "trying $include_dir/$name...\n" if $Verbose >= 2; # very verbose
548         if (-f "$include_dir/$name") {
549             return("$include_dir/$name");
550         }
551     }
552     if ($do_magical_check == 1) {
553         print STDERR "trying $last_seen_dir/$name...\n" if $Verbose >= 2; # very verbose
554         if (-f "$last_seen_dir/$name") {
555             return("$last_seen_dir/$name");
556         }
557     }
558     die "No file `$name' (reqd from file `$orig_src_file') among include directories: $Include_dirs\n";
559 }
560
561 # out of the driver, actually
562 sub run_something {
563     local($str_to_do, $tidy_name) = @_;
564
565     print STDERR "\n$tidy_name:\n\t" if $Verbose;
566     print STDERR "$str_to_do\n" if $Verbose;
567
568     local($return_val) = system($str_to_do) >> 8;
569
570     if ($return_val != 0) {
571         local($die_msg) = "$Pgm: execution of the $tidy_name had trouble";
572         $die_msg .= " (program not found)" if $return_val == 255;
573         $die_msg .= " ($!)" if $Verbose && $! != 0;
574         $die_msg .= "\n";
575         print STDERR $die_msg;
576         exit $return_val;
577     }
578 }