source: trunk/MAKE/mkmf @ 4

Last change on this file since 4 was 4, checked in by vancop, 8 years ago

initial import /Users/ioulianikolskaia/Boulot/CODES/LIM1D/ARCHIVE/TMP/LIM1D_v3.20/

File size: 19.6 KB
Line 
1#!/usr/bin/perl
2#-----------------------------------------------------------------------
3#              mkmf: Perl script for makefile construction
4#
5# AUTHOR: V. Balaji (v.balaji@noaa.gov)
6#         Princeton University/GFDL
7#
8# Full web documentation for mkmf:
9#     http://www.gfdl.noaa.gov/~vb/mkmf.html
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19# GNU General Public License for more details.
20#
21# For the full text of the GNU General Public License,
22# write to: Free Software Foundation, Inc.,
23#           675 Mass Ave, Cambridge, MA 02139, USA.
24#-----------------------------------------------------------------------
25
26$ENV{'LANG'} = 'C';
27require 5;
28use strict;
29use File::Basename;
30use Getopt::Std;
31use Config;                     # use to put in platform-specific stuff
32use vars qw( $opt_a $opt_c $opt_d $opt_f $opt_l $opt_m $opt_o $opt_p $opt_t $opt_v $opt_x ); # declare these global to be shared with Getopt:Std
33
34#subroutines
35sub ensureTrailingSlash {
36#ensure trailing slash on directory names
37   local $/ = '/'; chomp @_[0]; @_[0] .= '/';
38}
39
40my $version = '$Id: mkmf,v 14.0 2007/03/20 22:13:27 fms Exp $ ';
41
42# initialize variables: use getopts for these
43getopts( 'a:c:dfm:o:p:t:vx' ) || die "\aSyntax: $0 [-a abspath] [-c cppdefs] [-d] [-f] [-m makefile] [-o otherflags] ][-p program] [-t template] [-v] [-x] [targets]\n";
44$opt_v = 1 if $opt_d;   # debug flag turns on verbose flag also
45print "$0 $version\n" if $opt_v;
46
47my $mkfile = $opt_m || 'Makefile';
48print "Making makefile $mkfile ...\n" if $opt_v;
49
50$opt_p = 'a.out' unless $opt_p; # set default program name
51my @targets = '.';              # current working directory is always included in targets
52push @targets, @ARGV;           # then add remaining arguments on command line
53
54ensureTrailingSlash($opt_a) if $opt_a;
55
56#some generic declarations
57my( $file, $include, $line, $module, $name, $object, $path, $source, $suffix, $target, $word );
58my @list;
59#some constants
60my $endline = $/;
61my @src_suffixes = ( q/\.F/, q/\.F90/, q/\.c/, q/\.f/, q/\.f90/ );
62my @inc_suffixes = ( q/\.H/, q/\.fh/, q/\.h/, q/\.inc/, q/\.h90/ );
63# push @inc_suffixes, @src_suffixes; # sourcefiles can be includefiles too: DISALLOW, 6 May 2004
64# suffixes for the target (mkmf -p): if isn't on the list below it's a program
65my @tgt_suffixes = ( q/\.a/ );
66
67my %compile_cmd = (                     # command to create .o file from a given source file suffix
68      q/.F/   => q/$(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(OTHERFLAGS) -c/,
69      q/.F90/ => q/$(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(OTHERFLAGS) -c/,
70      q/.c/   => q/$(CC) $(CPPDEFS) $(CPPFLAGS) $(CFLAGS) $(OTHERFLAGS) -c/,
71      q/.f/   => q/$(FC) $(FFLAGS) $(OTHERFLAGS) -c/,
72      q/.f90/ => q/$(FC) $(FFLAGS) $(OTHERFLAGS) -c/              );
73my %delim_match = ( q/'/ => q/'/,       # hash to find includefile delimiter pair
74                    q/"/ => q/"/,
75                    q/</ => q/>/ );
76
77#formatting command for MAKEFILE, keeps very long lines to 256 characters
78format MAKEFILE =
79^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \~
80$line
81.
82
83sub print_formatted_list{
84#this routine, in conjunction with the format line above, can be used to break up long lines
85# it is currently used to break up the potentially long defs of SRC, OBJ, CPPDEFS, etc.
86# not used for the dependency lists
87   $line = "@_";
88   local $: = " \t\n";          # the default formatting word boundary includes the hyphen, but not here
89   while ( $opt_f && length $line > 254 ) {
90      write MAKEFILE, $line;
91   }
92   print MAKEFILE $line unless $line eq '';
93   print MAKEFILE "\n";
94}
95
96#begin writing makefile
97open MAKEFILE, ">$mkfile" or die "\aERROR opening file $mkfile for writing: $!\n";
98printf MAKEFILE "# Makefile created by %s $version\n\n", basename($0);
99print  MAKEFILE "include $opt_t\n\n" if $opt_t; #include template if supplied
100print  MAKEFILE "SRCROOT = $opt_a\n\n" if $opt_a; # make abspath a variable
101if ( $opt_c ) {
102   if ( $Config{osname} eq 'aix' ) {
103      $opt_c .= ' -D__aix';
104#AIX fortran (xlf) requires -WF, in front, comma delimiter, no spaces
105      my $cppdefs_xlf = '-WF "' . $opt_c . '"';
106      $cppdefs_xlf =~ s/,/\\,/g; # escape any commas already there
107      $cppdefs_xlf =~ s/\s+/,/g; # replace whitespace with commas
108      &print_formatted_list("CPPDEFS_XLF = $cppdefs_xlf");
109      $compile_cmd{'.F'}   = q/$(FC) $(CPPDEFS_XLF) $(FFLAGS) $(OTHERFLAGS) -c/;
110      $compile_cmd{'.F90'} = q/$(FC) $(CPPDEFS_XLF) $(FFLAGS) $(OTHERFLAGS) -c/;
111   }
112   &print_formatted_list("CPPDEFS = $opt_c") if $opt_c;
113}
114print MAKEFILE "\nOTHERFLAGS = $opt_o" if $opt_o;
115print MAKEFILE "\n.DEFAULT:\n\t-touch \$@\n";
116print MAKEFILE "all: $opt_p\n"; # first target should be program, so you can type just 'make'
117
118#if cppdefs flag is present, look for changes in cppdefs
119my %chgdefs;
120if ( $opt_c ) {
121#split argument of -c into newdefs
122   my %newdefs;
123   foreach ( split /\s*-D/, $opt_c ) {
124      $newdefs{$_} = 1;
125   }
126#get olddefs from file .cppdefs
127   my %olddefs;
128   my $cppdefsfile = '.cppdefs';
129   if ( -f $cppdefsfile ) {
130      open CPPFILE, $cppdefsfile or die "\aERROR opening cppdefsfile $cppdefsfile: $!\n";
131      while ( <CPPFILE> ) {
132         foreach $word ( split ) {
133            $olddefs{$word} = 1;
134         }
135      }
136      close CPPFILE;
137#get words that are not in both newdefs and olddefs
138#if you move this foreach{} outside the enclosing if{} then
139#   all cppdefs will be considered changed if there is no .cppdefs file.
140      foreach ( keys %newdefs, keys %olddefs ) {
141         $chgdefs{$_} = 1 unless( $newdefs{$_} && $olddefs{$_} );
142      }
143   }
144#write current cppdefs list to file .cppdefs
145   open  CPPFILE, ">$cppdefsfile";
146   my @newdefs = keys %newdefs;
147   print CPPFILE " @newdefs\n";
148   close CPPFILE;
149   if( $opt_d ) {
150      @list = keys %newdefs; print "newdefs= @list\n";
151      @list = keys %olddefs; print "olddefs= @list\n";
152      @list = keys %chgdefs; print "chgdefs= @list\n";
153   }
154}
155delete $chgdefs{''};
156
157# get a list of sourcefiles to be treated from targets
158# (a sourcefile is any regular file with a suffix matching src_suffixes)
159# if target is a sourcefile, add to list
160# if target is a directory, get all sourcefiles there
161# if target is a regular file that is not a sourcefile, look for a
162#    sourcefile on last work of each line, rest of line (if present) is the
163#    compile command to apply to this file.
164#@sources will contain a unique list of sourcefiles in targets
165#@objects will contain corresponding objects
166
167#separate targets into directories and files
168my %scanned;                    # list of directories/files already scanned
169my %actual_source_of;           # hash returning sourcefile from object
170my %source_of; # sourcefile from object, using SRCROOT variable if present
171my @includepaths;
172my $scanOrder = 0;              # used to remember order of directory scan
173foreach $target ( @targets ) {
174   print STDERR '.' unless $opt_v; # show progress on screen (STDERR is used because it is unbuffered)
175   if ( $opt_a and substr($target,0,1) ne '/' ) {
176      # if an abs_path exists, attach it to all relative paths
177      $target = $opt_a . $target;
178   }
179   ensureTrailingSlash($target) if( -d $target );
180   print "target=$target\n" if $opt_v;
181#directory
182   if ( -d $target && !$scanned{$target} ) {
183      print "Processing directory $target\n" if $opt_v;
184      opendir DIR, $target;
185      my @files = readdir DIR;
186#find all sourcefiles in directory DIR
187      foreach ( @files ) {
188         ( $name, $path, $suffix ) = fileparse( "$target$_", @inc_suffixes );
189         push @includepaths, $target if $suffix; # is this line doing anything? looks like includepaths='' later...
190         ( $name, $path, $suffix ) = fileparse( "$target$_", @src_suffixes );
191         $object = "$name.o";
192         if( $suffix && !$actual_source_of{$object} ) {
193            if ( $opt_a and substr($path,0,1) ne '/' ) { # if an abs_path exists, attach it to all relative paths
194               ensureTrailingSlash($path);
195               $path = '' if $path eq './';
196               $source_of{$object} = '$(SRCROOT)' . "$path$name$suffix";
197               $path = $opt_a . $path;
198            }
199            $actual_source_of{$object} = "$path$name$suffix";
200            $source_of{$object} = $actual_source_of{$object} unless $source_of{$object};
201         }
202      }
203      closedir DIR;
204      $scanned{$target} = $scanOrder++;
205   } elsif ( -f $target ) {
206#file: check if it is a sourcefile
207      ( $name, $path, $suffix ) = fileparse( $target, @src_suffixes );
208      $object = "$name.o";
209      if ( !$actual_source_of{$object} ) {
210         if ( $suffix ) {
211            $path = '' if $path eq './';
212            if ( $opt_a and substr($path,0,1) ne '/' ) { # if an abs_path exists, attach it to all relative paths
213               ensureTrailingSlash($path);
214               $source_of{$object} = '$(SRCROOT)' . "$path$name$suffix";
215               $path = $opt_a . $path;
216            }
217            $actual_source_of{$object} = "$path$name$suffix";
218            $source_of{$object} = $actual_source_of{$object} unless $source_of{$object};
219         } else {
220            ( $name, $path, $suffix ) = fileparse( $target, @inc_suffixes );
221            if ( ! $suffix ) {
222#not a sourcefile: assume it contains list of sourcefiles
223#specify files requiring special commands (e.g special compiler flags) thus:
224#   f90 -Oaggress a.f90
225#if last word on line is not a valid sourcefile, line is ignored
226               open CMDFILE, $target;
227               print "Reading commands from $target...\n" if $opt_v;
228               while ( <CMDFILE> ) {
229                  next if ( $_ eq "\n");
230                  $line = $_;
231                  my @wordlist = split;
232                  $file = @wordlist[$#wordlist]; # last word on line
233                  ( $name, $path, $suffix ) = fileparse( $file, @src_suffixes );
234                  print "file=$file suffix=$suffix in $target\n" if $opt_d;
235                  $object = "$name.o";
236                  if ( $suffix && !$actual_source_of{$object} ) {
237                     $path = '' if $path eq './';
238                     if ( $opt_a and ( substr($path,0,1) ne '/' ) ) { # if an abs_path exists, attach it to all relative paths
239                        ensureTrailingSlash($path);
240                        $source_of{$object} = '$(SRCROOT)' . "$path$name$suffix";
241                        $path = $opt_a . $path;
242                     }
243                     $actual_source_of{$object} = "$path$name$suffix";
244                     $source_of{$object} = $actual_source_of{$object} unless $source_of{$object};
245                     $scanned{$path} = $scanOrder++ unless $scanned{$path};
246#command for this file is all of line except the filename
247                     $line =~ /\s+$file/; $line=$`;
248                     if ( $line ) {
249                        $compile_cmd{"$name$suffix"} = $line;
250                        print "Special command for file $name$suffix: ($line)\n" if $opt_v;
251                     }
252                  }
253                  if ( ! $suffix ) { # look for include files
254                     ( $name, $path, $suffix ) = fileparse( $file, @inc_suffixes );
255                     if ( $opt_a and ( substr($path,0,1) ne '/' ) ) { # if an abs_path exists, attach it to all relative paths
256                        ensureTrailingSlash($path);
257                        $path = $opt_a . $path;
258                     }
259                     print "file=$file path=$path suffix=$suffix order=$scanOrder in $target\n" if $opt_d;
260# anything that's found here is an includefile but not a sourcefile...
261# just include directory in scan
262                     $scanned{$path} = $scanOrder++
263                       if ( $suffix && !$scanned{$path} );
264                  }
265               }
266               close CMDFILE;
267            }
268         }
269      }
270   }
271}
272delete $actual_source_of{''};
273# sort scanned directories by scan order
274sub ascendingScanOrder { $scanned{$a} <=> $scanned{$b}; }
275my @dirs = sort ascendingScanOrder keys %scanned;
276my @sources = values %source_of;
277my @objects = keys   %source_of;
278if( $opt_d ) {
279   print "DEBUG: dirs= @dirs\n";
280   print "DEBUG: sources= @sources\n";
281   print "DEBUG: objects= @objects\n";
282}
283
284my %obj_of_module;              # hash returning name of object file containing module
285my %modules_used_by;            # hash of modules used by a given source file (hashed against the corresponding object)
286my %includes_in;                # hash of includes in a given source file (hashed against the corresponding object)
287my %has_chgdefs;                # hash of files contains cppdefs that have been changed
288#subroutine to scan file for use and module statements, and include files
289# first argument is $object, second is $file
290sub scanfile_for_keywords {
291   my $object = shift;
292   my $file = shift;
293   local $/ = $endline;
294#if file has already been scanned, return: but first check if any .o needs to be removed
295   if( $scanned{$file} ) {
296       if( $has_chgdefs{$file} and -f $object ) {
297           unlink $object or die "\aERROR unlinking $object: $!\n";
298           print "   Object $object is out-of-date because of change to cppdefs, removed.\n" if $opt_v;
299       }
300       return;
301   }
302   print "Scanning file $file of object $object ...\n" if $opt_v;
303   open FILE, $file or die "\aERROR opening file $file of object $object: $!\n";
304   foreach $line ( <FILE> ) {
305      if ( $line =~ /^\s*module\s+(\w*)/ix ) {
306         if ( $1 ) {
307            my $module = lc $1;
308            if ( $obj_of_module{$module} && $module ne "procedure" ) {
309               die "\a\nAMBIGUOUS: Module $module is associated with $file as well as $actual_source_of{$obj_of_module{$module}}.\n";
310            }
311            $obj_of_module{$module} = $object;
312         }
313      }
314      if ( $line =~ /^\s*use\s*(\w*)/ix ) {
315         $modules_used_by{$object} .= ' ' . lc $1 if $1;
316      }
317      if ( $line =~ /^[\#\s]*include\s*(['""'<])([\w\.\/]*)$delim_match{\1}/ix ) {
318         $includes_in{$file} .= ' ' . $2 if $2;
319      }
320      foreach ( keys %chgdefs ) {
321         $_ .= '='; /\s*=/; $word=$`; #cut string at =sign, else whole string
322         if ( $line =~ /\b$word\b/ ) {
323            $has_chgdefs{$file} = 1;
324            if ( -f $object ) {
325               unlink $object or die "\aERROR unlinking $object: $!\n";
326               print "   Object $object is out-of-date because of change to cppdef $word, removed.\n" if $opt_v;
327            }
328         }
329      }
330   }
331   close FILE;
332   $scanned{$file} = 1;
333   print "   uses modules=$modules_used_by{$object}, and includes=$includes_in{$file}.\n" if $opt_d;
334}
335
336foreach $object ( @objects ) {
337   &scanfile_for_keywords( $object, $actual_source_of{$object} );
338}
339
340my %off_sources;                # list of source files not in current directory
341my %includes;                   # global list of includes
342my %used;                       # list of object files that are used by others
343my @cmdline;
344# for each file in sources, write down dependencies on includes and modules
345foreach $object ( sort @objects ) {
346   print STDERR '.' unless $opt_v; # show progress on screen (STDERR is used because it is unbuffered)
347   my %is_used;                 # hash of objects containing modules used by current object
348   my %obj_of_include;          # hash of includes for current object
349   $is_used{$object} = 1;       # initialize with current object so as to avoid recursion
350   print "Collecting dependencies for $object ...\n" if $opt_v;
351   @cmdline = "$object: $source_of{$object}";
352   ( $name, $path, $suffix ) = fileparse( $actual_source_of{$object}, @src_suffixes );
353   $off_sources{$source_of{$object}} = 1 unless( $path eq './' or $path eq '' );
354#includes: done in subroutine since it must be recursively called to look for embedded includes
355   @includepaths = '';
356   &get_include_list( $object, $actual_source_of{$object} );
357#modules
358   foreach $module ( split /\s+/, $modules_used_by{$object} ) {
359      $target = $obj_of_module{$module};
360#we need to check target ne '' also below, since it is not mkmf's privilege
361#to complain about modules not found. That should be left to the compiler.
362      if( $target and !$is_used{$target} ) {
363         $is_used{$target} = 1;
364         push @cmdline, $target;
365         $used{$target} = 1;
366         print "   found module $module in object $target ...\n" if $opt_v;
367      }
368   }
369#write the command line: if no file-specific command, use generic command for this suffix
370   &print_formatted_list(@cmdline);
371   $file = $actual_source_of{$object};
372   if ( $compile_cmd{$name.$suffix} ) {
373      print MAKEFILE "\t$compile_cmd{$name.$suffix}";
374   } else {
375      print MAKEFILE "\t$compile_cmd{$suffix}";
376   }
377   foreach ( @includepaths ) { # include files may be anywhere in directory array
378      print MAKEFILE " -I$_" if $_;
379   }
380   print MAKEFILE "\t$source_of{$object}\n";
381
382# subroutine to seek out includes recursively
383   sub get_include_list {
384      my( $incfile, $incname, $incpath, $incsuffix );
385      my @paths;
386      my $object = shift;
387      my $file = shift;
388      foreach ( split /\s+/, $includes_in{$file} ) {
389         print "object=$object, file=$file, include=$_.\n" if $opt_d;
390         ( $incname, $incpath, $incsuffix ) = fileparse( $_, @inc_suffixes );
391         if( $incsuffix ) {     # only check for files with proper suffix
392            undef $incpath if $incpath eq './';
393            if( $incpath =~ /^\// ) {
394               @paths = $incpath; # exact incpath specified, use it
395            } else {
396               @paths = @dirs;
397            }
398            foreach ( @paths ) {
399               local $/ = '/'; chomp; # remove trailing / if present
400               my $newincpath = "$_/$incpath" if $_;
401               undef $newincpath if $newincpath eq './';
402               $incfile = "$newincpath$incname$incsuffix";
403               if ( $opt_a and ( substr($newincpath,0,1) ne '/' ) ) {
404                  $newincpath = '$(SRCROOT)' . $newincpath;
405               }
406               print "DEBUG: checking for $incfile in $_ ...\n" if $opt_d;
407               if ( -f $incfile and $obj_of_include{$incfile} ne $object ) {
408                  print "   found $incfile ...\n" if $opt_v;
409                  push @cmdline, "$newincpath$incname$incsuffix";
410                  $includes{$incfile} = 1;
411                  chomp( $newincpath, $path );
412                  $off_sources{$incfile} = 1 if $newincpath;
413                  $newincpath = '.' if $newincpath eq '';
414                  push @includepaths, $newincpath unless( grep $_ eq $newincpath, @includepaths );
415                  &scanfile_for_keywords($object,$incfile);
416                  $obj_of_include{$incfile} = $object;
417                  &get_include_list($object,$incfile); # recursively look for includes
418                  last;
419               }
420            }
421         }
422      }
423   }
424}
425
426#lines to facilitate creation of local copies of source from other directories
427#commented out because it makes make default rules kick in
428foreach ( keys %off_sources ) {
429   my $file = basename($_);
430   $file =~ s/\$\(SRCROOT\)//;
431   print MAKEFILE "./$file: $_\n\tcp $_ .\n";
432}
433
434#objects not used by other objects
435#if every object is a module, then only the unused objects
436#need to be passed to the linker (see commented OBJ = line below).
437#if any f77 or C routines are present, we need complete list
438my @unused_objects;
439foreach $object ( @objects ) {
440   push @unused_objects, $object unless $used{$object};
441}
442
443&print_formatted_list( "SRC =", @sources, keys %includes );
444&print_formatted_list( "OBJ =", @objects );
445# &print_formatted_list( "OBJ =", @unused_objects );
446my $noff = scalar keys %off_sources;
447&print_formatted_list( "OFF =", keys %off_sources ) if $noff > 0;
448
449#write targets
450print MAKEFILE "clean: neat\n\t-rm -f .cppdefs *.com *.mod \$(OBJ) $opt_p\n";
451print MAKEFILE "neat:\n\t-rm -f \$(TMPFILES)\n";
452print MAKEFILE "localize: \$(OFF)\n\tcp \$(OFF) .\n" if $noff > 0;
453print MAKEFILE "TAGS: \$(SRC)\n\tetags \$(SRC)\n";
454print MAKEFILE "tags: \$(SRC)\n\tctags \$(SRC)\n";
455( $name, $path, $suffix ) = fileparse( $opt_p, @tgt_suffixes );
456if( $suffix eq '.a' ) {
457   print MAKEFILE "$opt_p: \$(OBJ)\n\t\$(AR) \$(ARFLAGS) $opt_p \$(OBJ)\n";
458} else {
459# opt_l is a new flag added to take care of libraries
460   print MAKEFILE "$opt_p: \$(OBJ) $opt_l\n\t\$(LD) \$(OBJ) -o $opt_p $opt_l \$(LDFLAGS)\n";
461}
462close MAKEFILE;
463print " $mkfile is ready.\n";
464
465exec 'make', '-f', $mkfile if $opt_x;
Note: See TracBrowser for help on using the repository browser.