wine.inf: Use %CurrentVersionNT% wherever applicable.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #define NO_LIBWINE_PORT
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include "wine/list.h"
36
37 /* Max first-level includes per file */
38 #define MAX_INCLUDES 200
39
40 typedef struct _INCL_FILE
41 {
42     struct list        entry;
43     char              *name;
44     char              *filename;
45     struct _INCL_FILE *included_by;   /* file that included this one */
46     int                included_line; /* line where this file was included */
47     int                system;        /* is it a system include (#include <name>) */
48     struct _INCL_FILE *owner;
49     struct _INCL_FILE *files[MAX_INCLUDES];
50 } INCL_FILE;
51
52 static struct list sources = LIST_INIT(sources);
53 static struct list includes = LIST_INIT(includes);
54
55 typedef struct _INCL_PATH
56 {
57     struct list entry;
58     const char *name;
59 } INCL_PATH;
60
61 static struct list paths = LIST_INIT(paths);
62
63 static const char *src_dir;
64 static const char *top_src_dir;
65 static const char *top_obj_dir;
66 static const char *OutputFileName = "Makefile";
67 static const char *Separator = "### Dependencies";
68 static const char *ProgramName;
69 static int input_line;
70
71 static const char Usage[] =
72     "Usage: %s [options] [files]\n"
73     "Options:\n"
74     "   -Idir   Search for include files in directory 'dir'\n"
75     "   -Cdir   Search for source files in directory 'dir'\n"
76     "   -Sdir   Set the top source directory\n"
77     "   -Sdir   Set the top object directory\n"
78     "   -fxxx   Store output in file 'xxx' (default: Makefile)\n"
79     "   -sxxx   Use 'xxx' as separator (default: \"### Dependencies\")\n";
80
81
82 /*******************************************************************
83  *         fatal_error
84  */
85 static void fatal_error( const char *msg, ... )
86 {
87     va_list valist;
88     va_start( valist, msg );
89     vfprintf( stderr, msg, valist );
90     va_end( valist );
91     exit(1);
92 }
93
94
95 /*******************************************************************
96  *         xmalloc
97  */
98 static void *xmalloc( size_t size )
99 {
100     void *res;
101     if (!(res = malloc (size ? size : 1)))
102         fatal_error( "%s: Virtual memory exhausted.\n", ProgramName );
103     return res;
104 }
105
106
107 /*******************************************************************
108  *         xrealloc
109  */
110 void *xrealloc (void *ptr, size_t size)
111 {
112     void *res;
113     assert( size );
114     if (!(res = realloc( ptr, size )))
115         fatal_error( "%s: Virtual memory exhausted.\n", ProgramName );
116     return res;
117 }
118
119 /*******************************************************************
120  *         xstrdup
121  */
122 static char *xstrdup( const char *str )
123 {
124     char *res = strdup( str );
125     if (!res) fatal_error( "%s: Virtual memory exhausted.\n", ProgramName );
126     return res;
127 }
128
129
130 /*******************************************************************
131  *         strmake
132  */
133 char *strmake( const char* fmt, ... )
134 {
135     int n;
136     size_t size = 100;
137     va_list ap;
138
139     for (;;)
140     {
141         char *p = xmalloc (size);
142         va_start(ap, fmt);
143         n = vsnprintf (p, size, fmt, ap);
144         va_end(ap);
145         if (n == -1) size *= 2;
146         else if ((size_t)n >= size) size = n + 1;
147         else return p;
148         free(p);
149     }
150 }
151
152
153 /*******************************************************************
154  *         get_extension
155  */
156 static char *get_extension( char *filename )
157 {
158     char *ext = strrchr( filename, '.' );
159     if (ext && strchr( ext, '/' )) ext = NULL;
160     return ext;
161 }
162
163
164 /*******************************************************************
165  *         get_line
166  */
167 static char *get_line( FILE *file )
168 {
169     static char *buffer;
170     static unsigned int size;
171
172     if (!size)
173     {
174         size = 1024;
175         buffer = xmalloc( size );
176     }
177     if (!fgets( buffer, size, file )) return NULL;
178     input_line++;
179
180     for (;;)
181     {
182         char *p = buffer + strlen(buffer);
183         /* if line is larger than buffer, resize buffer */
184         while (p == buffer + size - 1 && p[-1] != '\n')
185         {
186             buffer = xrealloc( buffer, size * 2 );
187             fgets( buffer + size - 1, size + 1, file );
188             p = buffer + strlen(buffer);
189             size *= 2;
190         }
191         if (p > buffer && p[-1] == '\n')
192         {
193             *(--p) = 0;
194             if (p > buffer && p[-1] == '\r') *(--p) = 0;
195             if (p > buffer && p[-1] == '\\')
196             {
197                 *(--p) = 0;
198                 /* line ends in backslash, read continuation line */
199                 fgets( p, size - (p - buffer), file );
200                 input_line++;
201                 continue;
202             }
203         }
204         return buffer;
205     }
206 }
207
208 /*******************************************************************
209  *         is_generated
210  *
211  * Test if a given file type is generated during the make process
212  */
213 static int is_generated( const char *name )
214 {
215     static const char * const extensions[] = { ".tab.h", ".mc.rc" };
216     size_t i, len = strlen(name);
217     for (i = 0; i < sizeof(extensions)/sizeof(extensions[0]); i++)
218     {
219         if (len <= strlen(extensions[i])) continue;
220         if (!strcmp( name + len - strlen(extensions[i]), extensions[i] )) return 1;
221     }
222     return 0;
223 }
224
225 /*******************************************************************
226  *         add_include_path
227  *
228  * Add a directory to the include path.
229  */
230 static void add_include_path( const char *name )
231 {
232     INCL_PATH *path = xmalloc( sizeof(*path) );
233     list_add_tail( &paths, &path->entry );
234     path->name = name;
235 }
236
237
238 /*******************************************************************
239  *         add_src_file
240  *
241  * Add a source file to the list.
242  */
243 static INCL_FILE *add_src_file( const char *name )
244 {
245     INCL_FILE *file = xmalloc( sizeof(*file) );
246     memset( file, 0, sizeof(*file) );
247     file->name = xstrdup(name);
248     list_add_tail( &sources, &file->entry );
249     return file;
250 }
251
252
253 /*******************************************************************
254  *         add_include
255  *
256  * Add an include file if it doesn't already exists.
257  */
258 static INCL_FILE *add_include( INCL_FILE *pFile, const char *name, int line, int system )
259 {
260     INCL_FILE *include;
261     char *ext;
262     int pos;
263
264     for (pos = 0; pos < MAX_INCLUDES; pos++) if (!pFile->files[pos]) break;
265     if (pos >= MAX_INCLUDES)
266         fatal_error( "%s: %s: too many included files, please fix MAX_INCLUDES\n",
267                      ProgramName, pFile->name );
268
269     /* enforce some rules for the Wine tree */
270
271     if (!memcmp( name, "../", 3 ))
272         fatal_error( "%s:%d: #include directive with relative path not allowed\n",
273                      pFile->filename, line );
274
275     if (!strcmp( name, "config.h" ))
276     {
277         if ((ext = strrchr( pFile->filename, '.' )) && !strcmp( ext, ".h" ))
278             fatal_error( "%s:%d: config.h must not be included by a header file\n",
279                          pFile->filename, line );
280         if (pos)
281             fatal_error( "%s:%d: config.h must be included before anything else\n",
282                          pFile->filename, line );
283     }
284     else if (!strcmp( name, "wine/port.h" ))
285     {
286         if ((ext = strrchr( pFile->filename, '.' )) && !strcmp( ext, ".h" ))
287             fatal_error( "%s:%d: wine/port.h must not be included by a header file\n",
288                          pFile->filename, line );
289         if (!pos) fatal_error( "%s:%d: config.h must be included before wine/port.h\n",
290                                pFile->filename, line );
291         if (pos > 1)
292             fatal_error( "%s:%d: wine/port.h must be included before everything except config.h\n",
293                          pFile->filename, line );
294         if (strcmp( pFile->files[0]->name, "config.h" ))
295             fatal_error( "%s:%d: config.h must be included before wine/port.h\n",
296                          pFile->filename, line );
297     }
298
299     LIST_FOR_EACH_ENTRY( include, &includes, INCL_FILE, entry )
300         if (!strcmp( name, include->name )) goto found;
301
302     include = xmalloc( sizeof(INCL_FILE) );
303     memset( include, 0, sizeof(INCL_FILE) );
304     include->name = xstrdup(name);
305     include->included_by = pFile;
306     include->included_line = line;
307     include->system = system || pFile->system;
308     list_add_tail( &includes, &include->entry );
309 found:
310     pFile->files[pos] = include;
311     return include;
312 }
313
314
315 /*******************************************************************
316  *         open_src_file
317  */
318 static FILE *open_src_file( INCL_FILE *pFile )
319 {
320     FILE *file;
321
322     /* first try name as is */
323     if ((file = fopen( pFile->name, "r" )))
324     {
325         pFile->filename = xstrdup( pFile->name );
326         return file;
327     }
328     /* now try in source dir */
329     if (src_dir)
330     {
331         pFile->filename = strmake( "%s/%s", src_dir, pFile->name );
332         file = fopen( pFile->filename, "r" );
333     }
334     if (!file)
335     {
336         perror( pFile->name );
337         exit(1);
338     }
339     return file;
340 }
341
342
343 /*******************************************************************
344  *         open_include_file
345  */
346 static FILE *open_include_file( INCL_FILE *pFile )
347 {
348     FILE *file = NULL;
349     char *filename, *p;
350     INCL_PATH *path;
351
352     errno = ENOENT;
353
354     /* first try name as is */
355     if ((file = fopen( pFile->name, "r" )))
356     {
357         pFile->filename = xstrdup( pFile->name );
358         return file;
359     }
360
361     /* now try in source dir */
362     if (src_dir)
363     {
364         filename = strmake( "%s/%s", src_dir, pFile->name );
365         if ((file = fopen( filename, "r" ))) goto found;
366         free( filename );
367     }
368
369     /* now try in global includes */
370     if (top_obj_dir)
371     {
372         filename = strmake( "%s/include/%s", top_obj_dir, pFile->name );
373         if ((file = fopen( filename, "r" ))) goto found;
374         free( filename );
375     }
376     if (top_src_dir)
377     {
378         filename = strmake( "%s/include/%s", top_src_dir, pFile->name );
379         if ((file = fopen( filename, "r" ))) goto found;
380         free( filename );
381     }
382
383     /* now search in include paths */
384     LIST_FOR_EACH_ENTRY( path, &paths, INCL_PATH, entry )
385     {
386         filename = strmake( "%s/%s", path->name, pFile->name );
387         if ((file = fopen( filename, "r" ))) goto found;
388         free( filename );
389     }
390     if (pFile->system) return NULL;  /* ignore system files we cannot find */
391
392     /* try in src file directory */
393     if ((p = strrchr(pFile->included_by->filename, '/')))
394     {
395         int l = p - pFile->included_by->filename + 1;
396         filename = xmalloc(l + strlen(pFile->name) + 1);
397         memcpy( filename, pFile->included_by->filename, l );
398         strcpy( filename + l, pFile->name );
399         if ((file = fopen( filename, "r" ))) goto found;
400         free( filename );
401     }
402
403     if (pFile->included_by->system) return NULL;  /* ignore if included by a system file */
404
405     perror( pFile->name );
406     while (pFile->included_by)
407     {
408         fprintf( stderr, "  %s was first included from %s:%d\n",
409                  pFile->name, pFile->included_by->name, pFile->included_line );
410         pFile = pFile->included_by;
411     }
412     exit(1);
413
414 found:
415     pFile->filename = filename;
416     return file;
417 }
418
419
420 /*******************************************************************
421  *         parse_idl_file
422  */
423 static void parse_idl_file( INCL_FILE *pFile, FILE *file )
424 {
425     char *buffer, *include;
426
427     input_line = 0;
428     while ((buffer = get_line( file )))
429     {
430         char quote;
431         char *p = buffer;
432         while (*p && isspace(*p)) p++;
433
434         if (!strncmp( p, "import", 6 ))
435         {
436             p += 6;
437             while (*p && isspace(*p)) p++;
438             if (*p != '\"') continue;
439         }
440         else
441         {
442             if (*p++ != '#') continue;
443             while (*p && isspace(*p)) p++;
444             if (strncmp( p, "include", 7 )) continue;
445             p += 7;
446             while (*p && isspace(*p)) p++;
447             if (*p != '\"' && *p != '<' ) continue;
448         }
449
450         quote = *p++;
451         if (quote == '<') quote = '>';
452         include = p;
453         while (*p && (*p != quote)) p++;
454         if (!*p) fatal_error( "%s:%d: Malformed #include or import directive\n",
455                               pFile->filename, input_line );
456         *p = 0;
457         add_include( pFile, include, input_line, (quote == '>') );
458     }
459 }
460
461 /*******************************************************************
462  *         parse_c_file
463  */
464 static void parse_c_file( INCL_FILE *pFile, FILE *file )
465 {
466     char *buffer, *include;
467
468     input_line = 0;
469     while ((buffer = get_line( file )))
470     {
471         char quote;
472         char *p = buffer;
473         while (*p && isspace(*p)) p++;
474         if (*p++ != '#') continue;
475         while (*p && isspace(*p)) p++;
476         if (strncmp( p, "include", 7 )) continue;
477         p += 7;
478         while (*p && isspace(*p)) p++;
479         if (*p != '\"' && *p != '<' ) continue;
480         quote = *p++;
481         if (quote == '<') quote = '>';
482         include = p;
483         while (*p && (*p != quote)) p++;
484         if (!*p) fatal_error( "%s:%d: Malformed #include directive\n",
485                               pFile->filename, input_line );
486         *p = 0;
487         add_include( pFile, include, input_line, (quote == '>') );
488     }
489 }
490
491
492 /*******************************************************************
493  *         parse_file
494  */
495 static void parse_file( INCL_FILE *pFile, int src )
496 {
497     char *ext;
498     FILE *file;
499
500     if (is_generated( pFile->name ))
501     {
502         /* file is generated during make, don't try to open it */
503         pFile->filename = xstrdup( pFile->name );
504         return;
505     }
506
507     file = src ? open_src_file( pFile ) : open_include_file( pFile );
508     if (!file) return;
509     ext = get_extension( pFile->name );
510     if (ext && !strcmp( ext, ".idl" )) parse_idl_file( pFile, file );
511     else parse_c_file( pFile, file );
512     fclose(file);
513 }
514
515
516 /*******************************************************************
517  *         output_include
518  */
519 static void output_include( FILE *file, INCL_FILE *pFile,
520                             INCL_FILE *owner, int *column )
521 {
522     int i;
523
524     if (pFile->owner == owner) return;
525     if (!pFile->filename) return;
526     pFile->owner = owner;
527     if (*column + strlen(pFile->filename) + 1 > 70)
528     {
529         fprintf( file, " \\\n" );
530         *column = 0;
531     }
532     fprintf( file, " %s", pFile->filename );
533     *column += strlen(pFile->filename) + 1;
534     for (i = 0; i < MAX_INCLUDES; i++)
535         if (pFile->files[i]) output_include( file, pFile->files[i],
536                                              owner, column );
537 }
538
539
540 /*******************************************************************
541  *         output_src
542  */
543 static void output_src( FILE *file, INCL_FILE *pFile, int *column )
544 {
545     char *obj = xstrdup( pFile->name );
546     char *ext = get_extension( obj );
547     if (ext)
548     {
549         *ext++ = 0;
550         if (!strcmp( ext, "y" ))  /* yacc file */
551         {
552             *column += fprintf( file, "%s.tab.o: %s.tab.c", obj, obj );
553         }
554         else if (!strcmp( ext, "l" ))  /* lex file */
555         {
556             *column += fprintf( file, "%s.o: %s.c", LEX_OUTPUT_ROOT, LEX_OUTPUT_ROOT );
557         }
558         else if (!strcmp( ext, "rc" ))  /* resource file */
559         {
560             *column += fprintf( file, "%s.res: %s", obj, pFile->filename );
561         }
562         else if (!strcmp( ext, "mc" ))  /* message file */
563         {
564             *column += fprintf( file, "%s.mc.rc: %s", obj, pFile->filename );
565         }
566         else if (!strcmp( ext, "idl" ))  /* IDL file */
567         {
568             *column += fprintf( file, "%s.h: %s", obj, pFile->filename );
569         }
570         else
571         {
572             *column += fprintf( file, "%s.o: %s", obj, pFile->filename );
573         }
574     }
575     free( obj );
576 }
577
578
579 /*******************************************************************
580  *         output_dependencies
581  */
582 static void output_dependencies(void)
583 {
584     INCL_FILE *pFile;
585     int i, column;
586     FILE *file = NULL;
587     char *buffer;
588
589     if (Separator && ((file = fopen( OutputFileName, "r+" ))))
590     {
591         while ((buffer = get_line( file )))
592             if (!strncmp( buffer, Separator, strlen(Separator) )) break;
593         ftruncate( fileno(file), ftell(file) );
594         fseek( file, 0L, SEEK_END );
595     }
596     if (!file)
597     {
598         if (!(file = fopen( OutputFileName, Separator ? "a" : "w" )))
599         {
600             perror( OutputFileName );
601             exit(1);
602         }
603     }
604     LIST_FOR_EACH_ENTRY( pFile, &sources, INCL_FILE, entry )
605     {
606         column = 0;
607         output_src( file, pFile, &column );
608         for (i = 0; i < MAX_INCLUDES; i++)
609             if (pFile->files[i]) output_include( file, pFile->files[i],
610                                                  pFile, &column );
611         fprintf( file, "\n" );
612     }
613     fclose(file);
614 }
615
616
617 /*******************************************************************
618  *         parse_option
619  */
620 static void parse_option( const char *opt )
621 {
622     switch(opt[1])
623     {
624     case 'I':
625         if (opt[2]) add_include_path( opt + 2 );
626         break;
627     case 'C':
628         src_dir = opt + 2;
629         break;
630     case 'S':
631         top_src_dir = opt + 2;
632         break;
633     case 'T':
634         top_obj_dir = opt + 2;
635         break;
636     case 'f':
637         if (opt[2]) OutputFileName = opt + 2;
638         break;
639     case 's':
640         if (opt[2]) Separator = opt + 2;
641         else Separator = NULL;
642         break;
643     default:
644         fprintf( stderr, "Unknown option '%s'\n", opt );
645         fprintf( stderr, Usage, ProgramName );
646         exit(1);
647     }
648 }
649
650
651 /*******************************************************************
652  *         main
653  */
654 int main( int argc, char *argv[] )
655 {
656     INCL_FILE *pFile;
657     INCL_PATH *path, *next;
658     int i, j;
659
660     ProgramName = argv[0];
661
662     i = 1;
663     while (i < argc)
664     {
665         if (argv[i][0] == '-')
666         {
667             parse_option( argv[i] );
668             for (j = i; j < argc; j++) argv[j] = argv[j+1];
669             argc--;
670         }
671         else i++;
672     }
673
674     /* ignore redundant source paths */
675     if (src_dir && !strcmp( src_dir, "." )) src_dir = NULL;
676     if (top_src_dir && top_obj_dir && !strcmp( top_src_dir, top_obj_dir )) top_src_dir = NULL;
677
678     /* get rid of absolute paths that don't point into the source dir */
679     LIST_FOR_EACH_ENTRY_SAFE( path, next, &paths, INCL_PATH, entry )
680     {
681         if (path->name[0] != '/') continue;
682         if (top_src_dir)
683         {
684             if (!strncmp( path->name, top_src_dir, strlen(top_src_dir) )) continue;
685             if (path->name[strlen(top_src_dir)] == '/') continue;
686         }
687         list_remove( &path->entry );
688         free( path );
689     }
690
691     for (i = 1; i < argc; i++)
692     {
693         pFile = add_src_file( argv[i] );
694         parse_file( pFile, 1 );
695     }
696     LIST_FOR_EACH_ENTRY( pFile, &includes, INCL_FILE, entry ) parse_file( pFile, 0 );
697     if (!list_empty( &sources )) output_dependencies();
698     return 0;
699 }