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