[comp.sys.mac.programmer] Make question

wilson@csli.STANFORD.EDU (Nathan Wilson) (01/25/89)

This is a rather naive question but here goes.  What is the *real*
goal of make files.  Long ago in the dim recesses of my memory when I
was first learning to program in something other than basic, someone
introduced me to make files.  My understanding was that you create a
file that lists which files use things defined in other files.  The
make command then uses this file and the last modified dates to make a
list of commands to recompile all the files that have changed and all
the files that haven't changed but which depend on files that have
changed.  Now my question is why does one ever have to recompile a
file that hasn't changed?  Is it just to check that the functions
defined in the changed file need the same parameters and return the
same results, and that data structures and globals variables haven't
changed or is it more profound?  If not then if the only changes I
make are to the body of an existing function or procedure, or adding
new functions/data types, can I just just recompile the files that have
changed and then relink?  I've tried this and it seems to work, but
I'm still a bit leery of hidden gotchas.  The savings in turn around
time on large applications seems quite significant.
	A second question:  Is it ever necessary to compile a file
that only depends on files that haven't changed?  That is A depends
on B.  B depends on C.  I change C.  A does not depend on C.  Do I
need to recompile A?
	Thanks in advance,
		Nathan Wilson
		Teleos Research
		nathan%teleos.com@ai.sri.com

uucibg@sw1e.UUCP (3929] Brian Gilstrap) (01/25/89)

In article <7260@csli.STANFORD.EDU> nathan%teleos.com@ai.sri.com writes:
>This is a rather naive question but here goes.  What is the *real*
>goal of make files.
>
> [deleted to save bandwidth]
>
>........  Now my question is why does one ever have to recompile a
>file that hasn't changed?  Is it just to check that the functions
>defined in the changed file need the same parameters and return the
>same results, and that data structures and globals variables haven't
>changed or is it more profound?  If not then if the only changes I

Well, in languages like C, there are added subtleties such as macros,
which aren't really code but do affect the final code.  But in general,
the primary goal *is* to check these things you mention.

>changed or is it more profound?  If not then if the only changes I
>make are to the body of an existing function or procedure, or adding
>new functions/data types, can I just just recompile the files that have
>changed and then relink?  I've tried this and it seems to work, but

If you do it correctly, yes.  But this means knowing for *sure* that
none of the changes/additions that you made will affect the other files
that you don't recompile.

>changed and then relink?  I've tried this and it seems to work, but
>I'm still a bit leery of hidden gotchas.  The savings in turn around
>time on large applications seems quite significant.

It is a definite time savings.  However, the dangers of forgetting a
dependency and putting out a (more than usually :-) unstable program/system
is a serious risk.

>	A second question:  Is it ever necessary to compile a file
>that only depends on files that haven't changed?  That is A depends
>on B.  B depends on C.  I change C.  A does not depend on C.  Do I
>need to recompile A?

If A *truly* doesn't depend upon C, then you don't need to recompile A.
However, I would expect that all the routines in B ought to be related
(otherwise why would you put them in the same file?....I know, general
misc. routines all in one file is an exception but...)  So, if all the
routines in B are related, and they depend upon C, then the behavior of
any given routine in B is quite likely dependant upon the behavior of
things in C.  Since A's behavior may change if B's behavior changes, and B's
behavior may change if C's behavior changes, that means that A really does
depend upon C in many cases.

Note that languages like C and configuration managers like Make don't work well
together.  This is because Make doesn't allow you to specify the nature of
dependencies with any granularity.  It does the simple thing: modification time.
There are some other, more sophisiticated programs, such as 'cake' (see
comp.sources.unix) which allows you more control over dependencies.

Is it just me or does it seem that configuration management slides down the
slippery slope toward CASE kinds of tools that will help you figure out if the
semantics of your system is correct?
as well as the syntax.

>	Thanks in advance,
>		Nathan Wilson
>		Teleos Research
>		nathan%teleos.com@ai.sri.com

I'm sure I've forgotten something, so whoever catches it please follow-up.

Brian R. Gilstrap                          Southwestern Bell Telephone
One Bell Center Rm 17-G-4                  ...!ames!killer!texbell!sw1e!uucibg
St. Louis, MO 63101                        ...!bellcore!texbell!sw1e!uucibg
(314) 235-3929
#include <std_disclaimers.h>

suitti@haddock.ima.isc.com (Stephen Uitti) (01/26/89)

>In article <7260@csli.STANFORD.EDU> nathan%teleos.com@ai.sri.com writes:
>What is the *real* goal of make files?
>...dependencies are based on file dates

There is more than one *real* goal to 'make' files.  Yes.  In UNIX
'make', dependencies are explicitly specified in the Makefile.  There
are shortcut rules, like *.o depends on *.c.  There are also a few
built in dependencies of this sort.

>...why does one ever have to recompile a file that hasn't changed?

A file is recompiled when it or something it depends on has changed.
In UNIX, the ".o" (relocatable object) depends on the ".c", which may
depend on other files, for example ".h" included files.  These may
then depend on other nested include files.  These are often omitted,
since people often don't have dependency generators, or want the
Makefile to work on other systems with different include structures
(SysV vs BSD UNIX or MSDOS).  Also, system include files are thought
not to change.  The final target generally depends on one or more ".o"
files, and one or more libraries.  Often, library dependencies are
omitted, since libraries seldom change, especially system libraries.
Sometimes, a change in the Makefile requires that the project be
recompiled.  Explicit Makefile dependencies are seldom provided.
People who edit the Makefile generally know what needs to be
recompiled.

>...is it just to check parameters, return results, data structures
>and globals variables changes?

Compilers don't do cross checking in this regard.  Either you have the
common things (structures, prototypes, etc.)  defined in commonly
included header files, in which case 'make' will do the right thing
(assuming the header file dependency is set up) or the compiler will
have no way to do any cross checking anyway.

>...can I just just recompile the files that have changed and then relink?

Usually.

>...The savings in turn around time on large applications is significant.

This is one of the points of 'make'.

>...if A depends on B.  B depends on C.  I change C.  A does not depend
>on C.  Do I need to recompile A?

If (for example), A includes B, which includes C, A should depend on
C.  UNIX 'make' does not check for this.

Things that 'make' is used for:
1. Documentation on how to build (& install...) the project.
2. Automation for project building.  Reduces (or magnifies)
   errors and improves speed.
3. Automated optimization (don't recompile everything if
   it can be quickly proven to not be required).
4. Automation for things often only slightly related to
   the project (general scripting).

Lightspeed C on the Mac has a builtin 'make' like system, which they
call the 'project'.  It provides 1, 2 & 3 above.  It is easier than
'make', since it is not a real language, and automatically determines
the dependencies (during compilation).  It is harder to make errors.
LSC has attempted to increase speed and reliability everywhere.
General scripting for other things is hard on the Mac, since unlike
UNIX, there aren't hundreds of utilities that can be run from a script
(though things like macromaker are attempting to change this).  On the
other hand, features of the Mac allow simpler project installation.
In particular, many more systems that need to have file storage can
have it without having more than one file (using resources), and an
application can find out where it was invoked, and where it lives,
allowing it to have other files there to reference, without the need
for imbedded path names, etc.

	Stephen.

jkjl@munnari.oz (John Lim) (01/26/89)

Dear Nathan,
	
	The answer to your first question as to why you need to recompile
dependant files also is simple. Let's say you have a file :

===========================================
main.c
------

#include "dumbo.h"

main()
{
	read_Dumbo_record_from_file(&dumbo,"elephant_record.file");
	printf("Dumbo's age is %d\n",dumbo.age);
}
===========================================

and dumbo.h contains :

===========================================
dumbo.h
-------

struct elephant {
	int size,age,height;
	} dumbo;


============================================

As you can see main.c depends on dumbo.h. (I hope you can read C).

Now if you change dumbo.h to :


============================================
dumbo.h
-------

struct elephant {
	char name[32];
	int size,age,height;
	} dumbo;
============================================

Now main.c, using the old dumbo.h assumes that dumbo.age is the second
field of struct elephant. But if main.c uses the new definition of dumbo,
then it's the third field as the offsets have changed. 

Now if you didnt recompile main.c, the actual program, when trying to   
read dumbo.age would be extracting the data from the dumbo.name field !

Hope this example helps clarify matters.

	john

jcl@hpausla.HP.COM (Jeff Laing) (01/27/89)

Nathan Wilson (wilson@csli.STANFORD.EDU) writes in comp.sys.mac.programmer:
> 	A second question:  Is it ever necessary to compile a file
> that only depends on files that haven't changed?  That is A depends
> on B.  B depends on C.  I change C.  A does not depend on C.  Do I
> need to recompile A?

Maybe.  Consider the following (nonsensical but not unreasonable, or unlikely)

/* a.c */
#include "b.h"
static myBuff[BUFSIZ];

/* b.h */
#include "c.h"
#define BUFSIZ SYSBUFSIZ-4

/* c.h */
#define SYSBUFIZ 200

Change the definition in C and the effect ripples right up to A.