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