Imported Upstream version 201104
[platform/upstream/boost-jam.git] / hcache.c
1 /*
2  * This file has been donated to Jam.
3  */
4
5 # include "jam.h"
6 # include "lists.h"
7 # include "parse.h"
8 # include "rules.h"
9 # include "regexp.h"
10 # include "headers.h"
11 # include "newstr.h"
12 # include "hash.h"
13 # include "hcache.h"
14 # include "variable.h"
15 # include "search.h"
16
17 #ifdef OPT_HEADER_CACHE_EXT
18
19 /*
20  * Craig W. McPheeters, Alias|Wavefront.
21  *
22  * hcache.c hcache.h - handle cacheing of #includes in source files.
23  *
24  * Create a cache of files scanned for headers. When starting jam, look for the
25  * cache file and load it if present. When finished the binding phase, create a
26  * new header cache. The cache contains files, their timestamps and the header
27  * files found in their scan. During the binding phase of jam, look in the
28  * header cache first for the headers contained in a file. If the cache is
29  * present and valid, use its contents. This results in dramatic speedups with
30  * large projects (eg. 3min -> 1min startup for one project.)
31  *
32  * External routines:
33  *    hcache_init() - read and parse the local .jamdeps file.
34  *    hcache_done() - write a new .jamdeps file.
35  *    hcache()      - return list of headers on target. Use cache or do a scan.
36  *
37  * The dependency file format is an ASCII file with 1 line per target. Each line
38  * has the following fields:
39  * @boundname@ timestamp @file@ @file@ @file@ ... \n
40  */
41
42 typedef struct hcachedata HCACHEDATA ;
43
44 struct hcachedata
45 {
46     char       * boundname;
47     time_t       time;
48     LIST       * includes;
49     LIST       * hdrscan;    /* the HDRSCAN value for this target */
50     int          age;        /* if too old, we'll remove it from cache */
51     HCACHEDATA * next;
52 };
53
54
55 static struct hash * hcachehash = 0;
56 static HCACHEDATA  * hcachelist = 0;
57
58 static int queries = 0;
59 static int hits = 0;
60
61 #define CACHE_FILE_VERSION "version 4"
62 #define CACHE_RECORD_HEADER "header"
63 #define CACHE_RECORD_END "end"
64
65
66 /*
67  * Return the name of the header cache file. May return NULL.
68  *
69  * The user sets this by setting the HCACHEFILE variable in a Jamfile. We cache
70  * the result so the user can not change the cache file during header scanning.
71  */
72
73 static char * cache_name( void )
74 {
75     static char * name = 0;
76     if ( !name )
77     {
78         LIST * hcachevar = var_get( "HCACHEFILE" );
79
80         if ( hcachevar )
81         {
82             TARGET * t = bindtarget( hcachevar->string );
83
84             pushsettings( t->settings );
85             /* Do not expect the cache file to be generated, so pass 0 as the
86              * third argument to search. Expect the location to be specified via
87              * LOCATE, so pass 0 as the fourth arugment.
88              */
89             t->boundname = search( t->name, &t->time, 0, 0 );
90             popsettings( t->settings );
91
92             if ( hcachevar )
93                 name = copystr( t->boundname );
94         }
95     }
96     return name;
97 }
98
99
100 /*
101  * Return the maximum age a cache entry can have before it is purged ftom the
102  * cache.
103  */
104
105 static int cache_maxage( void )
106 {
107     int age = 100;
108     LIST * var = var_get( "HCACHEMAXAGE" );
109     if ( var )
110     {
111         age = atoi( var->string );
112         if ( age < 0 )
113             age = 0;
114     }
115     return age;
116 }
117
118
119 /*
120  * Read a netstring. The caveat is that the string can not contain ASCII 0. The
121  * returned value is as returned by newstr(), so it need not be freed.
122  */
123
124 char * read_netstring( FILE * f )
125 {
126     unsigned long len;
127     static char * buf = NULL;
128     static unsigned long buf_len = 0;
129
130     if ( fscanf( f, " %9lu", &len ) != 1 )
131         return NULL;
132     if ( fgetc( f ) != (int)'\t' )
133         return NULL;
134
135     if ( len > 1024 * 64 )
136         return NULL;  /* sanity check */
137
138     if ( len > buf_len )
139     {
140         unsigned long new_len = buf_len * 2;
141         if ( new_len < len )
142             new_len = len;
143         buf = (char *)BJAM_REALLOC( buf, new_len + 1 );
144         if ( buf )
145             buf_len = new_len;
146     }
147
148     if ( !buf )
149         return NULL;
150
151     if ( fread( buf, 1, len, f ) != len )
152         return NULL;
153     if ( fgetc( f ) != (int)'\n' )
154         return NULL;
155
156     buf[ len ] = 0;
157     return newstr( buf );
158 }
159
160
161 /*
162  * Write a netstring.
163  */
164
165 void write_netstring( FILE * f, char const * s )
166 {
167     if ( !s )
168         s = "";
169     fprintf( f, "%lu\t%s\n", (long unsigned)strlen( s ), s );
170 }
171
172
173 void hcache_init()
174 {
175     HCACHEDATA   cachedata;
176     HCACHEDATA * c;
177     FILE       * f;
178     char       * version;
179     int          header_count = 0;
180     char       * hcachename;
181
182     hcachehash = hashinit( sizeof( HCACHEDATA ), "hcache" );
183
184     if ( !( hcachename = cache_name() ) )
185         return;
186
187     if ( !( f = fopen( hcachename, "rb" ) ) )
188         return;
189
190     version = read_netstring( f );
191     if ( !version || strcmp( version, CACHE_FILE_VERSION ) )
192     {
193         fclose( f );
194         return;
195     }
196
197     while ( 1 )
198     {
199         char * record_type;
200         char * time_str;
201         char * age_str;
202         char * includes_count_str;
203         char * hdrscan_count_str;
204         int    i;
205         int    count;
206         LIST * l;
207
208         record_type = read_netstring( f );
209         if ( !record_type )
210         {
211             fprintf( stderr, "invalid %s\n", hcachename );
212             goto bail;
213         }
214         if ( !strcmp( record_type, CACHE_RECORD_END ) )
215             break;
216         if ( strcmp( record_type, CACHE_RECORD_HEADER ) )
217         {
218             fprintf( stderr, "invalid %s with record separator <%s>\n",
219                 hcachename, record_type ? record_type : "<null>" );
220             goto bail;
221         }
222
223         c = &cachedata;
224
225         c->boundname       = read_netstring( f );
226         time_str           = read_netstring( f );
227         age_str            = read_netstring( f );
228         includes_count_str = read_netstring( f );
229
230         if ( !c->boundname || !time_str || !age_str || !includes_count_str )
231         {
232             fprintf( stderr, "invalid %s\n", hcachename );
233             goto bail;
234         }
235
236         c->time = atoi( time_str );
237         c->age = atoi( age_str ) + 1;
238
239         count = atoi( includes_count_str );
240         for ( l = 0, i = 0; i < count; ++i )
241         {
242             char * s = read_netstring( f );
243             if ( !s )
244             {
245                 fprintf( stderr, "invalid %s\n", hcachename );
246                 goto bail;
247             }
248             l = list_new( l, s );
249         }
250         c->includes = l;
251
252         hdrscan_count_str = read_netstring( f );
253         if ( !includes_count_str )
254         {
255             list_free( c->includes );
256             fprintf( stderr, "invalid %s\n", hcachename );
257             goto bail;
258         }
259
260         count = atoi( hdrscan_count_str );
261         for ( l = 0, i = 0; i < count; ++i )
262         {
263             char * s = read_netstring( f );
264             if ( !s )
265             {
266                 fprintf( stderr, "invalid %s\n", hcachename );
267                 goto bail;
268             }
269             l = list_new( l, s );
270         }
271         c->hdrscan = l;
272
273         if ( !hashenter( hcachehash, (HASHDATA * *)&c ) )
274         {
275             fprintf( stderr, "can't insert header cache item, bailing on %s\n",
276                 hcachename );
277             goto bail;
278         }
279
280         c->next = hcachelist;
281         hcachelist = c;
282
283         ++header_count;
284     }
285
286     if ( DEBUG_HEADER )
287         printf( "hcache read from file %s\n", hcachename );
288
289  bail:
290     fclose( f );
291 }
292
293
294 void hcache_done()
295 {
296     FILE       * f;
297     HCACHEDATA * c;
298     int          header_count = 0;
299     char       * hcachename;
300     int          maxage;
301
302     if ( !hcachehash )
303         return;
304
305     if ( !( hcachename = cache_name() ) )
306         return;
307
308     if ( !( f = fopen( hcachename, "wb" ) ) )
309         return;
310
311     maxage = cache_maxage();
312
313     /* Print out the version. */
314     write_netstring( f, CACHE_FILE_VERSION );
315
316     c = hcachelist;
317     for ( c = hcachelist; c; c = c->next )
318     {
319         LIST * l;
320         char   time_str[ 30 ];
321         char   age_str[ 30 ];
322         char   includes_count_str[ 30 ];
323         char   hdrscan_count_str[ 30 ];
324
325         if ( maxage == 0 )
326             c->age = 0;
327         else if ( c->age > maxage )
328             continue;
329
330         sprintf( includes_count_str, "%lu", (long unsigned) list_length( c->includes ) );
331         sprintf( hdrscan_count_str, "%lu", (long unsigned) list_length( c->hdrscan ) );
332         sprintf( time_str, "%lu", (long unsigned) c->time );
333         sprintf( age_str, "%lu", (long unsigned) c->age );
334
335         write_netstring( f, CACHE_RECORD_HEADER );
336         write_netstring( f, c->boundname );
337         write_netstring( f, time_str );
338         write_netstring( f, age_str );
339         write_netstring( f, includes_count_str );
340         for ( l = c->includes; l; l = list_next( l ) )
341             write_netstring( f, l->string );
342         write_netstring( f, hdrscan_count_str );
343         for ( l = c->hdrscan; l; l = list_next( l ) )
344             write_netstring( f, l->string );
345         fputs( "\n", f );
346         ++header_count;
347     }
348     write_netstring( f, CACHE_RECORD_END );
349
350     if ( DEBUG_HEADER )
351         printf( "hcache written to %s.   %d dependencies, %.0f%% hit rate\n",
352             hcachename, header_count, queries ? 100.0 * hits / queries : 0 );
353
354     fclose ( f );
355 }
356
357
358 LIST * hcache( TARGET * t, int rec, regexp * re[], LIST * hdrscan )
359 {
360     HCACHEDATA cachedata;
361     HCACHEDATA * c = &cachedata;
362
363     LIST * l = 0;
364
365     ++queries;
366
367     c->boundname = t->boundname;
368
369     if (hashcheck (hcachehash, (HASHDATA **) &c))
370     {
371     if (c->time == t->time)
372     {
373         LIST *l1 = hdrscan, *l2 = c->hdrscan;
374         while (l1 && l2) {
375         if (l1->string != l2->string) {
376             l1 = NULL;
377         } else {
378             l1 = list_next(l1);
379             l2 = list_next(l2);
380         }
381         }
382         if (l1 || l2) {
383         if (DEBUG_HEADER)
384             printf("HDRSCAN out of date in cache for %s\n",
385                t->boundname);
386
387         printf("HDRSCAN out of date for %s\n", t->boundname);
388         printf(" real  : ");
389         list_print(hdrscan);
390         printf("\n cached: ");
391         list_print(c->hdrscan);
392         printf("\n");
393
394         list_free(c->includes);
395         list_free(c->hdrscan);
396         c->includes = 0;
397         c->hdrscan = 0;
398         } else {
399         if (DEBUG_HEADER)
400             printf ("using header cache for %s\n", t->boundname);
401         c->age = 0;
402         ++hits;
403         l = list_copy (0, c->includes);
404         return l;
405         }
406     } else {
407         if (DEBUG_HEADER)
408             printf ("header cache out of date for %s\n", t->boundname);
409         list_free (c->includes);
410         list_free(c->hdrscan);
411         c->includes = 0;
412         c->hdrscan = 0;
413     }
414     } else {
415     if (hashenter (hcachehash, (HASHDATA **)&c)) {
416         c->boundname = newstr (c->boundname);
417         c->next = hcachelist;
418         hcachelist = c;
419     }
420     }
421
422     /* 'c' points at the cache entry. Its out of date. */
423
424     l = headers1 (0, t->boundname, rec, re);
425
426     c->time = t->time;
427     c->age = 0;
428     c->includes = list_copy (0, l);
429     c->hdrscan = list_copy(0, hdrscan);
430
431     return l;
432 }
433
434 #endif