Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!att!ucbvax!lynx.northeastern.edu!cschmidt From: cschmidt@lynx.northeastern.edu Newsgroups: comp.lang.c Subject: Re: A question on C programming style Message-ID: Date: 20 Apr 91 23:00:35 GMT Sender: daemon@ucbvax.BERKELEY.EDU Lines: 68 > I'm profoundly lazy, and I refuse to keep even simple Makefile > dependencies up-to-date manually, let alone complicated ones which > require chasing through several levels of #inclusion. That's why I > use an automatic dependency generator. (For me, doing so has no > disadvantages, because I wrote my own that behaves just as I expect it > to.) It was heartening to read this. I have long believed that writing MAKE scripts by hand is both too time consuming and too unreliable. In one project with which I was associated, it was discovered AFTER the product shipped that about 14 dependencies were missing from the circa 500-line MAKE script. My current solution is similar to yours, Steve, but with an unusual twist: my dependency detector program never writes the dependency list to disk. Instead, it outputs a list of source files that need to be compiled right now. It then calls the compiler (if the list is not empty). The MAKE program is not used. (Naturally, when I am debugging a single module, I just call the compiler directly.) One obvious advantage to this approach is that the dependency list is always guaranteed to be up to date. When using a conventional dependency generator, there is a temptation to postpone using it. You might think that this approach would be slower than using a traditional MAKE program, but it turns out that it is actually faster in many cases. The MAKE program resolves dependencies one at a time. If ten modules need to be compiled, MAKE calls the C compiler ten times. Using my dependency detector, the C compiler is called just once; the names of the ten modules to be compiled are passed to the compiler as a single parameter in the form of a "response file". In any case, my dependency detector program employs these performance improvement techniques: o The source file is not scanned for include directives if the object file does not exist, or if the object file's time stamp is older than that of the source file. o To avoid reading the entire source file for include directives, the program does not read beyond a certain optional pragma directive, which happens to be the same pragma directive that my header file generator recognizes as marking the start of the implementation section. o By default, the program does not check the time stamps of standard header files (those whose names are enclosed in angle brackets), but there is an optional command line switch to override this. o Once the time stamp of a header file is determined, that header file name and its time stamp are stored in a global list, in case another source file includes the same header file. The time stamp assigned to a header file is either that header file's actual time stamp, or the time stamp of the newest header file specified in a nested include directive, whichever time stamp is newest. o Before scanning source files, the program notes the time stamp of the newest object file. When determining the time stamp of a header file, the scanning for nested include directives stops if the program finds a header file with a time stamp that is newer than that of the newest object file. I would be interested in any remarks you folks might have about this, especially if you have had any experience designing or using a dependency detector that uses this approach. Christopher Schmidt cschmidt@lynx.northeastern.edu