[comp.unix.programmer] Makefiles -- .c and .h

ellis@ultra.dec.com (David Ellis 15-Nov-1990 0915) (11/15/90)

Consider a Makefile for an executable built from a large number of .o files, 
each separately compiled from a .c file.

If we use a single .c.o rule for compiling all the source files, then it 
seems that a change in a .h file that is #include'd in a .c file will
not be picked up by Make to automatically force recompilation of the .c file.

One workaround is to replace the single .c.o rule with a collection of rules, 
one for each .o file, listing the dependencies on the .h files #include'd in
the corresponding .c file.  But this is a lot of writing, and if we change
the "#include" lines in any .c file, we have to update the Makefile with the
corresponding change.

Is there a simpler way?

----
David  J  Ellis
Digital Equipment Corporation, Secure Systems Group 
Mailstop LTN1-1/D07
295 Foster Street, Littleton MA 01460
(508) 486-6157
Usenet:   {ucbvax,allegra,decvax}!decwrl!ultra.enet!ellis
Internet: ellis@ultra.enet.dec.com

jik@athena.mit.edu (Jonathan I. Kamens) (11/16/90)

  The somewhat standard solution to this problem is to create a program or
sequence of shell commands to parse the source files and determine the
dependencies on .h files, and to append those dependencies to the end of the
Makefile (or, in some versions of make, to put them in a file that is included
by the Makefile).

  There are various "makedepend" programs and scripts floating all over the
place.  The standard X11 distribution includes a makedepend, for example. 
Also, I believe there are a few Makefiles in the standard 4.3BSD distribution
that have shell dependency generators based on "cc -E" on the source files
built in.  There may even be some stuff in the comp.sources.unix or
comp.sources.misc archives to do this.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

s887212@minyos.xx.rmit.oz.au (Stephen Riehm [Romulis]) (11/16/90)

jik@athena.mit.edu (Jonathan I. Kamens) writes:


>  The somewhat standard solution to this problem is to create a program or
>sequence of shell commands to parse the source files and determine the
>dependencies on .h files, and to append those dependencies to the end of the
>Makefile (or, in some versions of make, to put them in a file that is included
>by the Makefile).

[stuff not saved]

>-- 
>Jonathan Kamens			              USnail:
>MIT Project Athena				11 Ashford Terrace
>jik@Athena.MIT.EDU				Allston, MA  02134
>Office: 617-253-8085			      Home: 617-782-0710

This might be the current "standard method" of getting around this
problem.. but wouldn't it be "Better" <totally subjective term> to "modify"
make / pmake to accept lines similar to..
.o : .c header.h
or maybe some form of wildcard system?
Is this concept TOTALLY unrealistic. I have found that most of the programs
that I write have only one or two .h files so this sort of structure would
be most reasonable for most of my applications.
Does this also apply to others or am I on my own on this.

Comments welcome by email or news.

thanx in advance
============================================================================
Romulis [Stephen Riehm]	            Royal Melbourne Institute of Technology,
					       (124 Latrobe St., Melbourne.)
s887212@minyos.xx.rmit.oz.au					   Australia.

@>---`--,--( Still Stuck on the wrong side of the deep pink sea )--.--'---<@
======================< Insert Usual Disclaimer >===========================

pmoore@hemel.bull.co.uk (Paul Moore) (11/16/90)

ellis@ultra.dec.com (David Ellis 15-Nov-1990 0915) writes:

>Consider a Makefile for an executable built from a large number of .o files, 
>each separately compiled from a .c file.

>If we use a single .c.o rule for compiling all the source files, then it 
>seems that a change in a .h file that is #include'd in a .c file will
>not be picked up by Make to automatically force recompilation of the .c file.

>One workaround is to replace the single .c.o rule with a collection of rules, 
>one for each .o file, listing the dependencies on the .h files #include'd in
>the corresponding .c file.  But this is a lot of writing, and if we change
>the "#include" lines in any .c file, we have to update the Makefile with the
>corresponding change.

>Is there a simpler way?
Yes - the simplest think to do is leave the single rules line in there
and then add the dependancy lines after:-

.c.o
	cc ....... 

foo.o: foo.c inc.h
bar.o: bar.c inc.h

This says that to make a .o from and .c do a cc on it AND that foo.o
depends both on foo.c and inc.h.

This works fine. However you must update the make to reflect the changes
in dependancy if you add another .h. There are dependancy makers around
- ie progs/scripts that scan the sources and build the rules but I have
never used them as it seems to me you can keep them up to date by hand.

>----
>David  J  Ellis
>Digital Equipment Corporation, Secure Systems Group 
>Mailstop LTN1-1/D07
>295 Foster Street, Littleton MA 01460
>(508) 486-6157
>Usenet:   {ucbvax,allegra,decvax}!decwrl!ultra.enet!ellis
>Internet: ellis@ultra.enet.dec.com

mark@hsi86.hsi.com (Mark Sicignano) (11/16/90)

Our cc program has a -M option that prints dependencies on stdout.
This output is suitable to be included in a makefile.

If I do a 

cc -M chmod.c 

This is the output I get:

chmod.o: chmod.c
chmod.o: /usr/include/stdio.h
chmod.o: /usr/include/sys/types.h
chmod.o: /usr/include/sys/stat.h
chmod.o: /usr/include/sys/dir.h

This should get you going in the right direction perhaps.
-- 
Mark Sicignano                                  ...!uunet!hsi!mark
3M Health Information Systems                   mark@hsi.com

lbr@holos0.uucp (Len Reed) (11/17/90)

In article <9011151442.AA02010@decpa.pa.dec.com> ellis@ultra.dec.com (David Ellis 15-Nov-1990 0915) writes:
=Consider a Makefile for an executable built from a large number of .o files, 
=each separately compiled from a .c file.
=
=If we use a single .c.o rule for compiling all the source files, then it 
=seems that a change in a .h file that is #include'd in a .c file will
=not be picked up by Make to automatically force recompilation of the .c file.
=
=One workaround is to replace the single .c.o rule with a collection of rules, 
=one for each .o file, listing the dependencies on the .h files #include'd in
=the corresponding .c file.  But this is a lot of writing, and if we change
=the "#include" lines in any .c file, we have to update the Makefile with the
=corresponding change.
=
=Is there a simpler way?

You don't want a separate rule for each C file, just a separate dependency
line.  Like this:

MOST = this.h that.h more.h another.h etc.h
one.o : $(MOST) header_a.h header_b.h
two.o : $(MOST) header_c.h
three.o : $(MOST)

.c.o :
	$(CC) $(CFLAGS) $<

The MOST macro is used to group headers that most C files include.  You only
have one rule.  Note that x.o depends implicitly upon x.c for all x.  In
this example the rule is superfluous, since it's just the default.

Now, what about those dependency lines?  There are dependency
checkers out there, and for complicated projects using one is far better
than trying to maintain these by hand.
-- 
Len Reed
Holos Software, Inc.
Voice: (404) 496-1358
UUCP: ...!gatech!holos0!lbr

weimer@ssd.kodak.com (Gary Weimer) (11/17/90)

In article <6268@minyos.xx.rmit.oz.au> s887212@minyos.xx.rmit.oz.au (Stephen Riehm [Romulis]) writes:
>jik@athena.mit.edu (Jonathan I. Kamens) writes:
>
>
>>  The somewhat standard solution to this problem is to create a program or
>>sequence of shell commands to parse the source files and determine the
>>dependencies on .h files, and to append those dependencies to the end of the
>>Makefile (or, in some versions of make, to put them in a file that is included
>>by the Makefile).
>
>This might be the current "standard method" of getting around this
>problem.. but wouldn't it be "Better" <totally subjective term> to "modify"
>make / pmake to accept lines similar to..
>.o : .c header.h
>or maybe some form of wildcard system?
>Is this concept TOTALLY unrealistic. I have found that most of the programs
>that I write have only one or two .h files so this sort of structure would
>be most reasonable for most of my applications.
>Does this also apply to others or am I on my own on this.

Does this method imply that all .c files built with this makefile include
header.h? This would hardly be practical for large projects.

As long as we are modifying make, why not do something a little more
robust and automated? SunOS's make has the "special-function target":

.KEEP_STATE:

which causes make to keep a record (in the file .make.state?) of which
include files each .c file uses (amoung other information). When make
is invoked after changing only one of these include files, make will
catch this and recompile the appropriate .c files. If a .c file is
changed, it needs recompiled anyway and a new list of include files
is generated.

Notice that jnk.c does not get recompiled because header.h was changed
if jnk.c does not include it. No tinkering with the makefile is needed
when new include files are created, or old ones reorganized. Very clean,
very sweet.

rwhite@nusdecs.uucp (0257014-Robert White(140)) (11/17/90)

In article <9011151442.AA02010@decpa.pa.dec.com> ellis@ultra.dec.com (David Ellis 15-Nov-1990 0915) writes:
>If we use a single .c.o rule for compiling all the source files, then it 
>seems that a change in a .h file that is #include'd in a .c file will

The simplest thing to do is to take the line

someobj.o:	someobj.c

and turn it into:

someobj.o:	someobj.c someheader.h

When the header is older than the .o then everything is as it should be
but when the header is changed the object is out of date wrt the source(s)
and it is recompiled.

An automatic way to generate this sort of dependancy list is to get make
cc output the included file list instead of compiling an object.  Take
that output and massage it into a dependancy list.

Rob.

hsandhu@white.toronto.edu (Harjinder S Sandhu) (11/17/90)

ellis@ultra.dec.com (David Ellis 15-Nov-1990 0915) writes:
>If we use a single .c.o rule for compiling all the source files, then it 
>seems that a change in a .h file that is #include'd in a .c file will
>not be picked up by Make to automatically force recompilation of the .c file.

>One workaround is to replace the single .c.o rule with a collection of rules, 
>one for each .o file, listing the dependencies on the .h files #include'd in
>the corresponding .c file.  But this is a lot of writing, and if we change
>the "#include" lines in any .c file, we have to update the Makefile with the
>corresponding change.

>Is there a simpler way?

 I do the following to generate my entire makefile

note: I have all my .c files in c/ and all .h files in h/ and I put all
 objects in o/
note 2: I use awk excessively due to the lack of a better output
formatter

----------------------------------------
# Find the Source
echo SRC | awk '{printf("\n%s=    ",$1)}'
foreach i (c/*)
 echo $i | awk '{printf("\\\n\t\t\t%s  ", $1) }'
end

# The objects to compile,
echo OBJ | awk '{printf("\n\n%s=    ",$1)}'
foreach i (c/*)
 echo $i | awk '{printf("\\\n\t\t\to/%s.o  ", substr($1,3,length($1)-4))}'
end

# The make command, assuming flags and everything are defined already
echo " "
echo $all': $(OBJ)'
echo '$(CC) $(CFLAGS) $(OBJ) -o ' $all '$(LIB) -lm' | \
awk '{printf("\t%s\n",$0)}'
echo " "
echo '$(OBJ):'
echo '$(CC) $(CFLAGS) $(IDIR) -c -o o/$*.o c/$*.c' | \
awk '{printf("\t%s\n",$0)}'
echo " "


# Generate the dependencies, basically searches .c files for 
#  include statements. If the included files is in quotes,
#  it is a dependency, otherwise not.
egrep include c/* | sed -e "s/#/\ /g" -e s/\"/\ /g  |   \
awk 'BEGIN {src=" "} \
        {sn=substr($1,3,length($1)-5) } \
        {if (src!=$1)printf("\n\no/%s.o:\t\tc/%s.c", sn, sn) } \
        {src=$1} \
        {if (substr($3,1,1)!="<") printf("\t\\\n\t\t\th/%s\t\t",$3) }'

--------------------------
 
    Harjinder Sandhu
    hsandhu@white.toronto.edu
    
--
 

jik@athena.mit.edu (Jonathan I. Kamens) (11/19/90)

In article <6268@minyos.xx.rmit.oz.au>, s887212@minyos.xx.rmit.oz.au (Stephen Riehm [Romulis]) writes:
|> This might be the current "standard method" of getting around this
|> problem.. but wouldn't it be "Better" <totally subjective term> to "modify"
|> make / pmake to accept lines similar to..
|> .o : .c header.h
|> or maybe some form of wildcard system?

  That's just fine if every one of your source files includes the same header
files, but in a large software project, that is almost definitely not the case.

  Recompiling is a slow process; I want it to take place *only* when
necessary, which means that I only want dependencies for each .o file to exist
for the actual include files upon which that .o file depends, not for every
include file in the software project.

  Another reason automatically generated dependencies are a good thing is that
they do recursive dependency generation.  That means that if I include
<X11/Xlib.h>, and that file includes <X11/X.h>, then both of those include
files will be added as dependencies.

  Some people see this as a bad thing; they say, "Why should I have to
recompile everything if my administrator changes <stdio.h>?"  My answer is
that if any standard system include file was changed, there's a *reason* for
it, and you should be recompiling if for no other reason than to verify that
your source code still works with the changed header files.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

jik@athena.mit.edu (Jonathan I. Kamens) (11/19/90)

In article <1990Nov16.171816.7173@ssd.kodak.com>, weimer@ssd.kodak.com (Gary Weimer) writes:
|> SunOS's make has the "special-function target":
|> 
|> .KEEP_STATE:
|> 
|> which causes make to keep a record (in the file .make.state?) of which
|> include files each .c file uses (amoung other information). When make
|> is invoked after changing only one of these include files, make will
|> catch this and recompile the appropriate .c files. If a .c file is
|> changed, it needs recompiled anyway and a new list of include files
|> is generated.

  How does the SunOs make figure out which files were included by each source
file?  Does it do a "cc -E" on the source file, or something similar?  If so,
isn't that (a) slow since it has to do it again each time the .c file changes,
and (b) a problem with source files on which "cc -E" (or whatever command make
uses) won't do the right thing?  It seems to me that you're introducing a lot
of complexity into make, and it isn't clear to me that it can do the job
correctly and reliably.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

ericco@ssl.berkeley.edu (Eric C. Olson) (11/20/90)

Why not compile the object files directly into a compilation dependent
library.  Like:

lib1.a	: $(SRC)
	cc $(CFLAGS1) $? | exit
	ar a $@ $(?:.c=.o)
	/bin/rm $(?:.c=.o)

lib2.a	: $(SRC)
	cc $(CFLAGS2) $? | exit
	ar a $@ $(?:.c=.o)
	/bin/rm $(?:.c=.o)

exe1	: main.c lib1.a
	cc $(LFLAGS) -o $@ main.c lib1.a

exe2	: main.c lib2.a
	cc $(LFLAGS) -o $@ main.c lib2.a

If the compilation fails, you may get into trouble, i think the exit
will take care of this.  In general, in Sun make, this could be:

all	:	exeDEF1 exeDEF2 exeDEF3

lib%.a	: $(SRC)
	cc -c -D% $? | exit
	ar a $@ $(?:.c=.o)
	/bin/rm $(?:.c=.o)

exe%	: main.c lib%.a
	cc -o $@ main.c lib%.a
--
Eric
ericco@ssl.berkeley.edu

guy@auspex.auspex.com (Guy Harris) (11/20/90)

>  How does the SunOs make figure out which files were included by each source
>file?  Does it do a "cc -E" on the source file, or something similar?

"Something similar".  Basically, it provokes the compiler into producing
a dependency list as it compiles.

>If so, isn't that (a) slow since it has to do it again each time the .c
>file changes,

Well, if the ".c" file changes, it has to compile it in any case.  There
is some extra overhead in getting the compiler (in particular, the "C
preprocessor" phase of the compiler) to produce the dependency list as
it compiles.

jik@athena.mit.edu (Jonathan I. Kamens) (11/20/90)

In article <ERICCO.90Nov19112308@soc1.ssl.berkeley.edu>, ericco@ssl.berkeley.edu (Eric C. Olson) writes:
|> Why not compile the object files directly into a compilation dependent
|> library.  Like:

Maybe I'm missing something, but I don't see how this resolves the problem of
dependencies on include files.  Your answer doesn't seem to have anything at
all to do with include files.

|> 	ar a $@ $(?:.c=.o)
|> 
|> lib%.a	: $(SRC)

What do the "(?:.c=.o)" and "%" do?  I can guess given the context, but these
are *not* portable Make constructs...

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (11/20/90)

In article <1990Nov18.204706.7044@athena.mit.edu> jik@athena.mit.edu (Jonathan I. Kamens) writes:
>   How does the SunOs make figure out which files were included by each source
> file?  Does it do a "cc -E" on the source file, or something similar?

I don't know what it actually uses, but cc -M would be one solution.

> If so,
> isn't that (a) slow since it has to do it again each time the .c file changes,

It has to recompile every time the file changes. The extra time is
negligible.

> and (b) a problem with source files on which "cc -E" (or whatever command make
> uses) won't do the right thing?

If every transformation command accepted -M, it wouldn't be a problem.

One strategy to handle all cases would be to trace the executable and
keep track of what files were opened. Shared libraries could be
special-cased here. Under some tracing mechanisms this would even allow
the recursive make that some people want.

---Dan

sralston@srwic.UUCP (Steve Ralston) (11/20/90)

Of the many replies to this subject, I have not seen mention of the method
which I am currently using, namely:

	gcc -M file.c

For those without gcc, press the 'n' key:
According to the man page on gcc:

	  -M   Tell the	preprocessor to	output a rule suitable for
	       make(1) describing the dependencies of each source
	       file.  For each source file, the	preprocessor outputs
	       one make-rule whose target is the object	file name for
	       that source file	and whose dependencies are all the
	       files #included in it.  This rule may be	a single line
	       or may be continued with	\-newline if it	is long.

	       -M implies -E.

	  -E   Run only	the C preprocessor.  Preprocess	all the	C
	       source files specified and output the results to
	       standard	output.

It's not real difficult to incorporate this command right in your Makefile
(piping to sed, etc, or whatever) so that when you type something like:

	make depend

the dependency lists are recreated from scratch.
-- 
Steve Ralston						sralston@srwic.UUCP
235 N Zelta						voice: 316-686-2019
Wichita, KS 67206			..!uunet!ncrlnk!ncrwic!srwic!sralston

meissner@osf.org (Michael Meissner) (11/21/90)

In article <4455@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:

| >  How does the SunOs make figure out which files were included by each source
| >file?  Does it do a "cc -E" on the source file, or something similar?
| 
| "Something similar".  Basically, it provokes the compiler into producing
| a dependency list as it compiles.

I added this type of functionality to GCC's cpp.  I added a new switch
-MD, which runs the compile as normal, but places the dependency list
for foo.c into foo.d in the current directory (CMU had done a similar
thing previously).  Part of the OSF/1 build process uses this and
automatically updates the Makefiles.  It was fairly simple to do
providing you have source to the compiler (or a tame compiler wizard
on hand)....
--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142

Considering the flames and intolerance, shouldn't USENET be spelled ABUSENET?

tif@doorstop.austin.ibm.com (Paul Chamberlain) (11/22/90)

In article <9011151442.AA02010@decpa.pa.dec.com> ellis@ultra.dec.com (David Ellis 15-Nov-1990 0915) writes:
>a change in a .h file that is #include'd in a .c file will not be picked
>up by Make to automatically force recompilation of the .c file.
>
>One workaround is to [use] a collection of rules, ...

It has already been pointed out that if one .c file does an include you
can simply add this to the bottom of the makefile with no other changes:

	file1.o: include1.h

But I thought it would be useful to point out these syntaxes as well:

	file1.o file2.o: common.h
	$(ALL_OBJS): common1.h common2.h

Paul Chamberlain | I do NOT represent IBM.     tif@doorstop, sc30661 at ausvm6
512/838-7008     | ...!cs.utexas.edu!ibmchs!auschs!doorstop.austin.ibm.com!tif