Added C++ support & small cleanups.
[wine] / tools / makedep.c
1 /*
2  * Generate include file dependencies
3  *
4  * Copyright 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31
32 /* Max first-level includes per file */
33 #define MAX_INCLUDES 200
34
35 typedef struct _INCL_FILE
36 {
37     struct _INCL_FILE *next;
38     char              *name;
39     char              *filename;
40     struct _INCL_FILE *included_by;   /* file that included this one */
41     int                included_line; /* line where this file was included */
42     int                system;        /* is it a system include (#include <name>) */
43     struct _INCL_FILE *owner;
44     struct _INCL_FILE *files[MAX_INCLUDES];
45 } INCL_FILE;
46
47 static INCL_FILE *firstSrc;
48 static INCL_FILE *firstInclude;
49
50 typedef struct _INCL_PATH
51 {
52     struct _INCL_PATH *next;
53     const char        *name;
54 } INCL_PATH;
55
56 static INCL_PATH *firstPath;
57
58 static const char *SrcDir = NULL;
59 static const char *OutputFileName = "Makefile";
60 static const char *Separator = "### Dependencies";
61 static const char *ProgramName;
62
63 static const char Usage[] =
64     "Usage: %s [options] [files]\n"
65     "Options:\n"
66     "   -Idir   Search for include files in directory 'dir'\n"
67     "   -Cdir   Search for source files in directory 'dir'\n"
68     "   -fxxx   Store output in file 'xxx' (default: Makefile)\n"
69     "   -sxxx   Use 'xxx' as separator (default: \"### Dependencies\")\n";
70
71
72 /*******************************************************************
73  *         xmalloc
74  */
75 static void *xmalloc( int size )
76 {
77     void *res;
78     if (!(res = malloc (size ? size : 1)))
79     {
80         fprintf( stderr, "%s: Virtual memory exhausted.\n", ProgramName );
81         exit(1);
82     }
83     return res;
84 }
85
86
87 /*******************************************************************
88  *         xstrdup
89  */
90 static char *xstrdup( const char *str )
91 {
92     char *res = strdup( str );
93     if (!res)
94     {
95         fprintf( stderr, "%s: Virtual memory exhausted.\n", ProgramName );
96         exit(1);
97     }
98     return res;
99 }
100
101
102 /*******************************************************************
103  *         is_generated
104  *
105  * Test if a given file type is generated during the make process
106  */
107 static int is_generated( const char *name )
108 {
109     static const char * const extensions[] = { ".tab.h", ".mc.rc" };
110     size_t i, len = strlen(name);
111     for (i = 0; i < sizeof(extensions)/sizeof(extensions[0]); i++)
112     {
113         if (len <= strlen(extensions[i])) continue;
114         if (!strcmp( name + len - strlen(extensions[i]), extensions[i] )) return 1;
115     }
116     return 0;
117 }
118
119 /*******************************************************************
120  *         add_include_path
121  *
122  * Add a directory to the include path.
123  */
124 static void add_include_path( const char *name )
125 {
126     INCL_PATH *path = xmalloc( sizeof(*path) );
127     INCL_PATH **p = &firstPath;
128     while (*p) p = &(*p)->next;
129     *p = path;
130     path->next = NULL;
131     path->name = name;
132 }
133
134
135 /*******************************************************************
136  *         add_src_file
137  *
138  * Add a source file to the list.
139  */
140 static INCL_FILE *add_src_file( const char *name )
141 {
142     INCL_FILE **p = &firstSrc;
143     INCL_FILE *file = xmalloc( sizeof(*file) );
144     memset( file, 0, sizeof(*file) );
145     file->name = xstrdup(name);
146     while (*p) p = &(*p)->next;
147     *p = file;
148     return file;
149 }
150
151
152 /*******************************************************************
153  *         add_include
154  *
155  * Add an include file if it doesn't already exists.
156  */
157 static INCL_FILE *add_include( INCL_FILE *pFile, const char *name, int line, int system )
158 {
159     INCL_FILE **p = &firstInclude;
160     int pos;
161
162     for (pos = 0; pos < MAX_INCLUDES; pos++) if (!pFile->files[pos]) break;
163     if (pos >= MAX_INCLUDES)
164     {
165         fprintf( stderr, "%s: %s: too many included files, please fix MAX_INCLUDES\n",
166                  ProgramName, pFile->name );
167         exit(1);
168     }
169
170     while (*p && strcmp( name, (*p)->name )) p = &(*p)->next;
171     if (!*p)
172     {
173         *p = xmalloc( sizeof(INCL_FILE) );
174         memset( *p, 0, sizeof(INCL_FILE) );
175         (*p)->name = xstrdup(name);
176         (*p)->included_by = pFile;
177         (*p)->included_line = line;
178         (*p)->system = system || pFile->system;
179     }
180     pFile->files[pos] = *p;
181     return *p;
182 }
183
184
185 /*******************************************************************
186  *         open_src_file
187  */
188 static FILE *open_src_file( INCL_FILE *pFile )
189 {
190     FILE *file;
191
192     /* first try name as is */
193     if ((file = fopen( pFile->name, "r" )))
194     {
195         pFile->filename = xstrdup( pFile->name );
196         return file;
197     }
198     /* now try in source dir */
199     if (SrcDir)
200     {
201         pFile->filename = xmalloc( strlen(SrcDir) + strlen(pFile->name) + 2 );
202         strcpy( pFile->filename, SrcDir );
203         strcat( pFile->filename, "/" );
204         strcat( pFile->filename, pFile->name );
205         file = fopen( pFile->filename, "r" );
206     }
207     if (!file)
208     {
209         perror( pFile->name );
210         exit(1);
211     }
212     return file;
213 }
214
215
216 /*******************************************************************
217  *         open_include_file
218  */
219 static FILE *open_include_file( INCL_FILE *pFile )
220 {
221     FILE *file = NULL;
222     INCL_PATH *path;
223
224     for (path = firstPath; path; path = path->next)
225     {
226         char *filename = xmalloc(strlen(path->name) + strlen(pFile->name) + 2);
227         strcpy( filename, path->name );
228         strcat( filename, "/" );
229         strcat( filename, pFile->name );
230         if ((file = fopen( filename, "r" )))
231         {
232             pFile->filename = filename;
233             break;
234         }
235         free( filename );
236     }
237     if (!file && pFile->system) return NULL;  /* ignore system files we cannot find */
238
239     /* try in src file directory */
240     if (!file)
241     {
242         char *p = strrchr(pFile->included_by->filename, '/');
243         if (p)
244         {
245             int l = p - pFile->included_by->filename + 1;
246             char *filename = xmalloc(l + strlen(pFile->name) + 1);
247             memcpy( filename, pFile->included_by->filename, l );
248             strcpy( filename + l, pFile->name );
249             if ((file = fopen( filename, "r" ))) pFile->filename = filename;
250             else free( filename );
251         }
252     }
253
254     if (!file)
255     {
256         if (pFile->included_by->system) return NULL;  /* ignore if included by a system file */
257         if (firstPath) perror( pFile->name );
258         else fprintf( stderr, "%s: %s: File not found\n",
259                       ProgramName, pFile->name );
260         while (pFile->included_by)
261         {
262             fprintf( stderr, "  %s was first included from %s:%d\n",
263                      pFile->name, pFile->included_by->name, pFile->included_line );
264             pFile = pFile->included_by;
265         }
266         exit(1);
267     }
268     return file;
269 }
270
271
272 /*******************************************************************
273  *         parse_file
274  */
275 static void parse_file( INCL_FILE *pFile, int src )
276 {
277     char buffer[1024];
278     char *include;
279     int line = 0;
280     FILE *file;
281
282     if (is_generated( pFile->name ))
283     {
284         /* file is generated during make, don't try to open it */
285         pFile->filename = xstrdup( pFile->name );
286         return;
287     }
288
289     file = src ? open_src_file( pFile ) : open_include_file( pFile );
290     if (!file) return;
291
292     while (fgets( buffer, sizeof(buffer)-1, file ))
293     {
294         char quote;
295         char *p = buffer;
296         line++;
297         while (*p && isspace(*p)) p++;
298         if (*p++ != '#') continue;
299         while (*p && isspace(*p)) p++;
300         if (strncmp( p, "include", 7 )) continue;
301         p += 7;
302         while (*p && isspace(*p)) p++;
303         if (*p != '\"' && *p != '<' ) continue;
304         quote = *p++;
305         if (quote == '<') quote = '>';
306         include = p;
307         while (*p && (*p != quote)) p++;
308         if (!*p)
309         {
310             fprintf( stderr, "%s:%d: Malformed #include directive\n",
311                      pFile->filename, line );
312             exit(1);
313         }
314         *p = 0;
315         add_include( pFile, include, line, (quote == '>') );
316     }
317     fclose(file);
318 }
319
320
321 /*******************************************************************
322  *         output_include
323  */
324 static void output_include( FILE *file, INCL_FILE *pFile,
325                             INCL_FILE *owner, int *column )
326 {
327     int i;
328
329     if (pFile->owner == owner) return;
330     if (!pFile->filename) return;
331     pFile->owner = owner;
332     if (*column + strlen(pFile->filename) + 1 > 70)
333     {
334         fprintf( file, " \\\n" );
335         *column = 0;
336     }
337     fprintf( file, " %s", pFile->filename );
338     *column += strlen(pFile->filename) + 1;
339     for (i = 0; i < MAX_INCLUDES; i++)
340         if (pFile->files[i]) output_include( file, pFile->files[i],
341                                              owner, column );
342 }
343
344
345 /*******************************************************************
346  *         output_src
347  */
348 static void output_src( FILE *file, INCL_FILE *pFile, int *column )
349 {
350     char *obj = xstrdup( pFile->name );
351     char *ext = strrchr( obj, '.' );
352     if (ext && strchr( ext, '/' )) ext = NULL;
353     if (ext)
354     {
355         *ext++ = 0;
356         if (!strcmp( ext, "y" ))  /* yacc file */
357         {
358             *column += fprintf( file, "y.tab.o: y.tab.c" );
359         }
360         else if (!strcmp( ext, "l" ))  /* lex file */
361         {
362             *column += fprintf( file, "%s.o: %s.c", LEX_OUTPUT_ROOT, LEX_OUTPUT_ROOT );
363         }
364         else if (!strcmp( ext, "rc" ))  /* resource file */
365         {
366             *column += fprintf( file, "%s.res: %s", obj, pFile->filename );
367         }
368         else if (!strcmp( ext, "mc" ))  /* message file */
369         {
370             *column += fprintf( file, "%s.mc.rc: %s", obj, pFile->filename );
371         }
372         else
373         {
374             *column += fprintf( file, "%s.o: %s", obj, pFile->filename );
375         }
376     }
377     free( obj );
378 }
379
380
381 /*******************************************************************
382  *         output_dependencies
383  */
384 static void output_dependencies(void)
385 {
386     INCL_FILE *pFile;
387     int i, column;
388     FILE *file = NULL;
389     char buffer[1024];
390
391     if (Separator && ((file = fopen( OutputFileName, "r+" ))))
392     {
393         while (fgets( buffer, sizeof(buffer), file ))
394             if (!strncmp( buffer, Separator, strlen(Separator) )) break;
395         ftruncate( fileno(file), ftell(file) );
396         fseek( file, 0L, SEEK_END );
397     }
398     if (!file)
399     {
400         if (!(file = fopen( OutputFileName, Separator ? "a" : "w" )))
401         {
402             perror( OutputFileName );
403             exit(1);
404         }
405     }
406     for( pFile = firstSrc; pFile; pFile = pFile->next)
407     {
408         column = 0;
409         output_src( file, pFile, &column );
410         for (i = 0; i < MAX_INCLUDES; i++)
411             if (pFile->files[i]) output_include( file, pFile->files[i],
412                                                  pFile, &column );
413         fprintf( file, "\n" );
414     }
415     fclose(file);
416 }
417
418
419 /*******************************************************************
420  *         parse_option
421  */
422 static void parse_option( const char *opt )
423 {
424     switch(opt[1])
425     {
426     case 'I':
427         if (opt[2]) add_include_path( opt + 2 );
428         break;
429     case 'C':
430         if (opt[2]) SrcDir = opt + 2;
431         else SrcDir = NULL;
432         break;
433     case 'f':
434         if (opt[2]) OutputFileName = opt + 2;
435         break;
436     case 's':
437         if (opt[2]) Separator = opt + 2;
438         else Separator = NULL;
439         break;
440     default:
441         fprintf( stderr, "Unknown option '%s'\n", opt );
442         fprintf( stderr, Usage, ProgramName );
443         exit(1);
444     }
445 }
446
447
448 /*******************************************************************
449  *         main
450  */
451 int main( int argc, char *argv[] )
452 {
453     INCL_FILE *pFile;
454
455     ProgramName = argv[0];
456     while (argc > 1)
457     {
458         if (*argv[1] == '-') parse_option( argv[1] );
459         else
460         {
461             pFile = add_src_file( argv[1] );
462             parse_file( pFile, 1 );
463         }
464         argc--;
465         argv++;
466     }
467     for (pFile = firstInclude; pFile; pFile = pFile->next)
468         parse_file( pFile, 0 );
469     if( firstSrc ) output_dependencies();
470     return 0;
471 }