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