Generating Makefile dependencies - which tool does everyone use?

Generating Makefile dependencies - which tool does everyone use?

Post by Chris Ranki » Mon, 04 May 1998 04:00:00



Hi all,

I find that the single biggest pain when programming in C/C++ is
configuring the Makefile. I can manage the job by hand for a small
program, but for a large program or set of programs this quickly becomes
impossible. I CANNOT be the only person to have this problem, so I guess
that there must be a number of solutions out there already. I have
looked at sunsite's GNU Make tools, but saw nothing obvious. Can
somebody recommend a package and give me a website to download it from,
please?

Cheers,
Chris.

 
 
 

Generating Makefile dependencies - which tool does everyone use?

Post by Neil Schemenau » Tue, 05 May 1998 04:00:00



>Hi all,

>I find that the single biggest pain when programming in C/C++ is
>configuring the Makefile.

You could look at makedepend.  It comes in the xbase package with
Debian.  I believe many people just use an awk script too.

        Neil

--
#!/usr/bin/env python
import zlib, base64; print zlib.decompress(base64.decodestring("""
eJzT0yUF6HDVKPilZuYoBCdnpOam5iWWphYp2OQlFoO5DonJxXqlyYk56YlFlXrJiXYKNUD1zhC+
joKjkw6Qk5eYkqgAA6F5mRVAQR2FgMqSjPw8sHowyCgpKbDS1y8vL0c2UL8OZhVUfw1XAknuVwcA
ips+Jg=="""))

 
 
 

Generating Makefile dependencies - which tool does everyone use?

Post by Paul D. Smi » Tue, 05 May 1998 04:00:00



  cr> I find that the single biggest pain when programming in C/C++ is
  cr> configuring the Makefile. I can manage the job by hand for a small
  cr> program, but for a large program or set of programs this quickly
  cr> becomes impossible. I CANNOT be the only person to have this
  cr> problem, so I guess that there must be a number of solutions out
  cr> there already. I have looked at sunsite's GNU Make tools, but saw
  cr> nothing obvious. Can somebody recommend a package and give me a
  cr> website to download it from, please?

It's not appropriate for a make to provide these tools, since make can
and is used to build _anything_, not just programs from C source code.

However, the GNU make manual does contain a section that describes a way
to use GCC (or any other compiler which has an option to generate
dependency information) plus GNU make's "include" feature to create a
very nice, usable, and automatic dependency maintenance environment.
See the section "Generating Dependencies Automatically", under "Writing
Rules".

Also note you can substitute makedepend, as mentioned in another
response, for the compiler if your compiler or preprocessor doesn't
support the -M (or equivalent) option.

--
-------------------------------------------------------------------------------

 "Please remain calm...I may be mad, but I am a professional." --Mad Scientist
-------------------------------------------------------------------------------
     These are my opinions--Bay Networks takes no responsibility for them.

 
 
 

Generating Makefile dependencies - which tool does everyone use?

Post by Gary Hol » Fri, 08 May 1998 04:00:00


Chris Rankin <net.bellsouth@{no.spam}rankinc> writes:
> I find that the single biggest pain when programming in C/C++ is
> configuring the Makefile. I can manage the job by hand for a small
> program, but for a large program or set of programs this quickly becomes
> impossible. I CANNOT be the only person to have this problem, so I guess
> that there must be a number of solutions out there already. I have
> looked at sunsite's GNU Make tools, but saw nothing obvious. Can
> somebody recommend a package and give me a website to download it from,
> please?

I've been really frustrated with this too, and found several solutions.
The simplest is a perl script I wrote to determine makefile
dependencies.  It's much faster than makedepend or using gcc -M, because
it only rescans files that have changed.  However, it doesn't run the C
preprocessor, so if you do something like

   #if 0
   #include "junkfile.h"
   #endif

it will still think you included junkfile.h.  It ignores any includes
with angle brackets, e.g., "#include <stdio.h>", since those files don't
change.

Since it's fast, you can run it on every invocation of make; you never
again can forget to run "make depend" manually.  I've enclosed the
script (called "update_dependencies.pl") after my signature file.  To
use it, you simply append to your makefile lines that look like this:

#
# This line causes the dependencies list to be updated automatically, so make
# knows without being told what .h files each .cxx file depends on.
#
dependencies.mk: $(wildcard *.cxx *.h)
        @update_dependencies.pl dependencies.mk -obj_prefix . \
          $(ADDL_CXX_FLAGS) $^
# ADDL_CXX_FLAGS contains any -I flags you need, so the dependency
# scanner can find your include files.

-include dependencies.mk        # This forces make to include the
                                # generated dependency list, and to
                                # update it automatically if anything changed.

--
Gary Holt                       (213)-740-3397
h...@lnc.usc.edu             http://lnc.usc.edu/~holt/

#!/usr/local/bin/perl
#
# This perl script updates the list of dependencies for a given set of C
# sources.  It is intended to be sufficiently fast that it can be run every
# time you run make.
#
# It simply scans files for '#include "file.h"', and scans those files, etc.
# It maintains a database of the last time that it scanned each file, so
# it only scans files which have changed.
#
# Usage:
#    update_dependencies.pl [-Iinclude-directory] Makefile *.cxx *.c
#
# This updates the dependency information in the Makefile.  Alternatively,
# you can store the dependency information in a separate file and have the
# Makefile include it.
#

@include_path = ('./');         # The default list of directories to search.
                                # Each directory must be followed by a
                                # trailing slash.
$obj_prefix = '';               # No prefix to add to object files.
#
# Parse the argument list:
#
@files = ();                    # No files to parse yet.

while ($_ = shift(@ARGV)) {     # Get the next argument.
  if (/^-I(.*)$/) {             # Specify include path?
    unshift(@include_path, "$1/"); # Remember the path.
  } elsif (/^-obj_prefix$/) {   # Object file directory specified?
    $obj_prefix = shift(@ARGV) . "/"; # Remember it.
  } elsif (/^-/) {              # Ignore other compiler options.
    next;
  } else {                      # Unrecognized name, must be a file name.
    push(@files, $_);
  }

}

$dependency_file = shift(@files); # Take the first file specified as the
                                # file we're going to update.

$update_time = time;            # Remember when we started to update this file.

%includes = ();                 # Array containing a list of the files that
                                # each file directly includes.
%file_scanned = ();             # The time when the file was last scanned.

%not_found = ();                # Array used to make sure we only print error
                                # messages about files not found once.

if (open(INFILE, "$dependency_file")) {       # Does the input file exist?
  rename($dependency_file, "$dependency_file~") ||
    die("Rename of $dependency_file to $dependency_file~ failed--$!\n");
                                # Make a backup copy of the file.
  open(OUTFILE, ">$dependency_file") ||
    die("Can't create new $dependency_file--$!\n");

#
# Copy everything above our special header to the output file verbatim:
#
  while (defined($_ = <INFILE>)) {
    last if (/^\#\# Output of update_dependencies.pl below this line \#\#$/);
                                # Did we find our special header line?
    print OUTFILE $_;           # Just copy the line verbatim.
  }

#
# Now we've gotten to the point in our file where we stored the dependency
# list.  Read it in:
#

  while (defined($_ = <INFILE>)) {
    next unless s/^\#\#//;      # If it doesn't begin with two number signs,
                                # we're not interested in it.
#
# Lines with this format contain the file name in the first column, the
# time it was last scanned in the second, and the list of files that it
# explicitly includes (not the ones included by those include files).
#
    my ($file, $scan_time, @includes) = split(' ', $_);
                                # Extract all the information.
    $includes{$file} = \@includes; # Remember this list of files.
    $file_scanned{$file} = $scan_time; # Remember when it was scanned.
  }

  close(INFILE);                        # Done with the input.

}

else                            # No input file:
{
  warn("No input file $dependency_file--creating it\n");
  open(OUTFILE, ">$dependency_file") ||    # Make the output file.
    die("Can't create new $dependency_file--$!\n");

}

#
# Now we've read the input file.  Scan each of the files we're supposed to.
# Note that we don't scan any files that we aren't told to.
#
print OUTFILE "## Output of update_dependencies.pl below this line ##
# Do not not modify these two comment lines or anything below them.
";
                                # Make a header to separate our output from
                                # the rest of the file.

foreach $file (@files) {
  my $obj_fname;
  ($obj_fname .= $file) =~ s/\.[^.]+$/.o/; # Form the name of the .o file.
  $obj_fname =~ s@.*/@@         # Strip out any path
    if $obj_prefix;             # if an object directory was specified.

  $obj_fname = canonicalize_filename($obj_prefix . $obj_fname);
                                # Mark the location of the output file.

  print OUTFILE "$obj_fname : ", join(' ', &all_includes($file)), "\n\n";
                                # Output the result.

}

#
# Now output our internal information back into the file so we know what
# we have scanned in the past:
#
foreach $file (keys %includes) {
  print OUTFILE "##$file $file_scanned{$file} ",
    join(' ', sort @{$includes{$file}}), "\n";

}

close(OUTFILE);

#
# The following subroutine returns all the files that a given file includes.
# Arguments:
# 1) The file name.
# 2) (Optional) whether this is being called recursively or not.
#
# Returns:
#       An array of all the files that will be referenced when the C
#       preprocessor is run on this file.
#
# Side effects:
#       Updates the %includes and %file_scanned arrays as necessary.
#
sub all_includes {
  my ($file, $recurse_flag) = @_; # Name the arguments.

  %already_returned_includes = () if (!$recurse_flag);

  $file = canonicalize_filename($file); # Make sure the file name is
                                # of the canonical form.

  return () if $already_returned_includes{$file}; # Don't get stuck in a loop
                                # if the file is included recursively.
                                # (Presumably preprocessor directives that
                                # we don't see eliminate the recursion.)
  $already_returned_includes{$file} = 1; # Remember not to do anything for this
                                # file again.

  my %this_include_list;        # Keys of this array are the files that it
                                # includes.  It's an associative array so we
                                # can remove duplicate names.

  my $ftime = (stat($file))[9]; # Get the file's time.
  unless (defined($ftime)) {
    warn "Can't stat $file--$!\n";
    return ();
  }
  if ($ftime > ($file_scanned{$file} || 0)) { # Do we need to rescan this file?
    $includes{$file} = &scan_file($file); # Remember which files it includes.
    $file_scanned{$file} = $update_time; # Remember when we scanned it.
  }

  foreach (@{$includes{$file}}) { # Look at each one of these files.
    my $canon_name = find_file_in_path($_, \@include_path);
                                # Get the full name for the file.
    if (!$canon_name) { # File not found?
      warn("File $_ not found in include path\n") unless $not_found{$_}++;
                                # Warn once about the file.
      next;                     # Just skip it.
    }
    $this_include_list{$canon_name} = 1; # Remember that this file is included.
    my @addl_includes = &all_includes($canon_name, 1); # Get all the files that
                                # this includes as well.
    @this_include_list{@addl_includes} = (1) x @addl_includes;
                                # Remember those files as well.
  }

  keys %this_include_list;      # Return the list of files.

}

#
# The following subroutine performs a quick scan on a file, looking only for
# "#include",  and returns a list of files that it includes.  It does not
# search the path for these files.
#
# Arguments:
# 1) The file name.
#
# Returns a reference to an array of the file names specified by
# '#include "file"'.  It does not return the file names specified by
# '#include <file>' since those are system files.
# This subroutine does not attempt to locate the files in the path.
#
# Side effects:
#       None.
#
sub scan_file {
  my ($file) = @_;              # Name the arguments.
  local (*INFILE);              # Make a local file handle.
  local ($_);

  my @directly_included_files;
  if (open(INFILE, $file)) {    # Open the file.
    print STDERR "Scanning file $file\n";
    while (defined($_ = <INFILE>)) {
      next unless /^\s*\#\s*include\s+\"([^\" ]+)\"/; # Ignore if this line
                                # isn't a #include.
      push(@directly_included_files, $1); # Remember the name.
    }

    close(INFILE);              # Done with this file.
  } else {                      # File not found:
    warn("Can't scan file $file--$!\n");
  }

  \@directly_included_files;

}

#
# The following subroutine scans a path for a given file.
# Arguments:
# 1) The file to scan for.
# 2) The path.  Each element of the path should have the trailing slashes
#    (on unix), or trailing colons/directory specification on VMS.
#
# Returns the canonicalized file name if the file was found, or undef if not.
#
sub find_file_in_path {
  my ($file, $path) = @_;       # Name the arguments.

  foreach
...

read more »

 
 
 

Generating Makefile dependencies - which tool does everyone use?

Post by Ross Osbo » Sat, 09 May 1998 04:00:00





>> I find that the single biggest pain when programming in C/C++ is
>> configuring the Makefile. I can manage the job by hand for a small
>> program, but for a large program or set of programs this quickly becomes
>> impossible. I CANNOT be the only person to have this problem, so I guess
>> that there must be a number of solutions out there already. I have
>> looked at sunsite's GNU Make tools, but saw nothing obvious. Can
>> somebody recommend a package and give me a website to download it from,
>> please?

> I've been really frustrated with this too, and found several solutions.
> The simplest is a perl script I wrote to determine makefile
> dependencies.  It's much faster than makedepend or using gcc -M, because
> it only rescans files that have changed.  However, it doesn't run the C
> preprocessor, so if you do something like

There is no reason to do the "gcc -M" if the source or its dependencies
haven't changed.  This is very easy and very well documented in the
gnu make documentation.

If you're not an emacs person you probably hate the info program as
much as I do but there is a very easy to use alternative called tkinfo.

At the top level menu in the make documentation, follow the menu entry
named "Rules".

At this node there will be a menu entry named "Automatic Dependencies".

Ross

 
 
 

Generating Makefile dependencies - which tool does everyone use?

Post by Jochen K"upp » Sat, 09 May 1998 04:00:00






> >> I find that the single biggest pain when programming in C/C++ is
> >> configuring the Makefile. I can manage the job by hand for a small
> >> program, but for a large program or set of programs this quickly becomes
> >> impossible. I CANNOT be the only person to have this problem, so I guess
> >> that there must be a number of solutions out there already. I have
> >> looked at sunsite's GNU Make tools, but saw nothing obvious. Can
> >> somebody recommend a package and give me a website to download it from,
> >> please?

Probably use automake/autoconf ?
It does dependency checkingautomatically, and "on the side" is much better
for a large program anyway.

Jochen

--
-----------------------------------------------------------------------
  Jochen K"upper


  Institut f"ur Physikalische Chemie I
  Universit"atsstr. 1, Geb 26.43 Raum 02.29    phone ++49-211-8113681
  40225 D"usseldorf                            fax   ++49-211-8115195
  Germany             http://www-public.rz.uni-duesseldorf.de/~jochen
-----------------------------------------------------------------------