/* * This file has been donated to Jam. */ # include "jam.h" # include "lists.h" # include "parse.h" # include "rules.h" # include "regexp.h" # include "headers.h" # include "newstr.h" # include "hash.h" # include "hcache.h" # include "variable.h" # include "search.h" #ifdef OPT_HEADER_CACHE_EXT /* * Craig W. McPheeters, Alias|Wavefront. Nov/2001. * Jan/2002. Extensions by Matt Armstrong. * See the file README.header_scan_cache for details on the extensions. * Jan/2002. Modification to extensions by Craig. * * hcache.c, hcache.h - handle cacheing of #includes in source files * * Create a cache of files scanned for headers. When starting jam, * look for the cache file and load it if present. When finished the * binding phase, create a new header cache. The cache contains * files, their timestamps and the header files found in their scan. * During the binding phase of jam, look in the header cache first for * the headers contained in a file. If the cache is present and * valid, use its contents. This can result in dramatic speedups on * large projects (eg. 3min -> 1min startup on one project.) * * External routines: * hcache_init() - read and parse the local .jamdeps file. * hcache_done() - write a new .jamdeps file * hcache() - return list of headers on target. Use cache or do a scan. * * The dependency file format is an ascii file with 1 line per target. * Each line has the following fields: * @boundname@ timestamp age num @file@ @file@ num @hdrscan@ @hdrscan@ ... \n * where the first number is the number of headers, and the second is the * number of elements in the hdrscan list. * * Filenames may contain any ascii or non-ascii characters. If they * contain the '@' or '#' characters, they are quoted on output and * their quoting is handled on input. Often the '\' character is used * for quoting, but as that is so common in NT pathnames, the '#' * character is used instead. Both '@' and '#' are characters * disallowed in Perforce filenames - and they should be rare in other * SCM systems hopefully. CWM. */ struct hcachedata { const char *boundname; time_t time; LIST *includes; LIST *hdrscan; /* the HDRSCAN value for this target */ int age; /* if too old, we'll remove it from cache */ struct hcachedata *next; } ; typedef struct hcachedata HCACHEDATA ; static struct hash *hcachehash = 0; static HCACHEDATA *hcachelist = 0; static int queries = 0; static int hits = 0; #define CACHE_FILE_VERSION "version 1" /* * Return the name of the header cache file. May return NULL. * * The user sets this by setting the HCACHEFILE variable in a Jamfile. * We cache the result so the user can't change the cache file during * header scanning. */ static const char* cache_name(void) { static const char *name = 0; if( !name ) { LIST *hcachevar = var_get( "HCACHEFILE" ); if( hcachevar ) { TARGET *t = bindtarget( hcachevar->string ); pushsettings( t->settings ); t->boundname = search( t->name, &t->time ); popsettings( t->settings ); name = copystr( t->boundname ); } } return name; } /* * Return the maximum age a cache entry can have before it is purged * from the cache. * * A maxage of 0 indicates that the cache entries should never be * purged, in effect disabling the aging of cache entries. */ static int cache_maxage(void) { int age = 100; LIST *var = var_get( "HCACHEMAXAGE" ); if( var ) { age = atoi( var->string ); if( age < 0 ) age = 0; } return age; } /* * Read any spaces we're on. Return the first non-space character */ static int skip_spaces( FILE *f ) { int ch = fgetc( f ); while( ch == ' ' ) ch = fgetc( f ); return ch; } /* * Read a string from the file. Handle quoted characters. The * returned value is as returned by newstr(), so it need not be freed. */ static const char * read_string( FILE *f ) { int ch, i = 0; char filename[ MAXJPATH ]; ch = skip_spaces( f ); if( ch != '@' ) return 0; ch = fgetc( f ); while( ch != '@' && ch != EOF && i < MAXJPATH ) { if( ch == '#' ) /* Quote */ filename[ i++ ] = fgetc( f ); else filename[ i++ ] = ch; ch = fgetc( f ); } if( ch != '@' ) return 0; filename[ i ] = 0; return newstr( filename ); } static int read_int( FILE *f ) { int ch, i = 0; char num[ 30 ]; ch = skip_spaces( f ); while( ch >= '0' && ch <= '9' ) { num[ i++ ] = ch; ch = fgetc( f ); } num[ i ] = 0; return atoi( num ); } static void write_string( FILE *f, const char *s ) { int i = 0; fputc( '@', f ); while( s[ i ] != 0 ) { if( s[ i ] == '@' || s[ i ] == '#' ) fputc( '#', f ); /* Quote */ fputc( s[ i++ ], f ); } fputs( "@ ", f ); } static void write_int( FILE *f, int i ) { fprintf( f, "%d ", i ); } void hcache_init(void) { HCACHEDATA cachedata, *c, *last = 0; FILE *f; int bad_cache = 1, ch; const char *version, *hcachename; hcachehash = hashinit( sizeof( HCACHEDATA ), "hcache" ); if( ! (hcachename = cache_name()) ) return; if( ! (f = fopen( hcachename, "rb" )) ) return; version = read_string( f ); ch = fgetc( f ); if (!version || strcmp( version, CACHE_FILE_VERSION ) || ch != '\n' ) { goto bail; } for(;;) { int i, count, ch; LIST *l; c = &cachedata; c->boundname = read_string( f ); if( !c->boundname ) /* Test for eof */ break; c->time = read_int( f ); c->age = read_int( f ) + 1; /* we're getting older... */ if( !c->boundname ) goto bail; /* headers */ count = read_int( f ); for( l = 0, i = 0; i < count; ++i ) { const char *s = read_string( f ); if( !s ) goto bail; l = list_new( l, s, 0 ); } c->includes = l; /* hdrscan */ count = read_int( f ); for( l = 0, i = 0; i < count; ++i ) { const char *s = read_string( f ); if( !s ) goto bail; l = list_new( l, s, 0 ); } c->hdrscan = l; /* Read the newline */ ch = skip_spaces( f ); if( ch != '\n' ) goto bail; if( !hashenter( hcachehash, (HASHDATA **)&c ) ) { printf( "jam: can't insert header cache item, bailing on %s\n", hcachename ); goto bail; } c->next = 0; if( last ) last->next = c; else hcachelist = c; last = c; } bad_cache = 0; if( DEBUG_HEADER ) printf( "hcache read from file %s\n", hcachename ); bail: /* If its bad, no worries, it'll be overwritten in hcache_done() */ if( bad_cache ) printf( "jam: warning: the cache was invalid: %s\n", hcachename ); fclose( f ); } void hcache_done(void) { FILE *f; HCACHEDATA *c; int header_count = 0; const char *hcachename; int maxage; if( !hcachehash ) return; if( ! (hcachename = cache_name()) ) return; if( ! (f = fopen( hcachename, "wb" ) ) ) return; maxage = cache_maxage(); /* print out the version */ fprintf( f, "@%s@\n", CACHE_FILE_VERSION ); c = hcachelist; for( c = hcachelist; c; c = c->next ) { LIST *l; if( maxage == 0 ) c->age = 0; else if( c->age > maxage ) continue; write_string( f, c->boundname ); write_int( f, c->time ); write_int( f, c->age ); write_int( f, list_length( c->includes ) ); for( l = c->includes; l; l = list_next( l ) ) { write_string( f, l->string ); } write_int( f, list_length( c->hdrscan ) ); for( l = c->hdrscan; l; l = list_next( l ) ) { write_string( f, l->string ); } fputc( '\n', f ); ++header_count; } if( DEBUG_HEADER ) printf( "hcache written to %s. %d dependencies, %.0f%% hit rate\n", hcachename, header_count, queries ? 100.0 * hits / queries : 0 ); fclose( f ); } LIST * hcache( TARGET *t, LIST *hdrscan ) { HCACHEDATA cachedata, *c = &cachedata; LIST *l = 0; int use_cache = 1; ++queries; c->boundname = t->boundname; if( hashcheck( hcachehash, (HASHDATA **) &c ) ) { if( c->time == t->time ) { LIST *l1 = hdrscan, *l2 = c->hdrscan; while( l1 && l2 ) { if( l1->string != l2->string ) { l1 = 0; } else { l1 = list_next( l1 ); l2 = list_next( l2 ); } } if( l1 || l2 ) use_cache = 0; } else use_cache = 0; if( use_cache ) { if( DEBUG_HEADER ) printf( "using header cache for %s\n", t->boundname ); c->age = 0; /* The entry has been used, its young again */ ++hits; l = list_copy( 0, c->includes ); return l; } else { if( DEBUG_HEADER ) printf( "header cache out of date for %s\n", t->boundname ); list_free( c->includes ); list_free( c->hdrscan ); c->includes = 0; c->hdrscan = 0; } } else { if( hashenter( hcachehash, (HASHDATA **)&c ) ) { c->boundname = newstr( c->boundname ); c->next = hcachelist; hcachelist = c; } } /* 'c' points at the cache entry. Its out of date. */ l = headers1( t->boundname, hdrscan ); c->time = t->time; c->age = 0; c->includes = list_copy( 0, l ); c->hdrscan = list_copy( 0, hdrscan ); return l; } #endif
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 4361 | Dick Dunbar |
Repair Jam build (rule name changes in Jamfile) Submit a starting Test directory |