diff options
| author | Patrick Simianer <simianer@cl.uni-heidelberg.de> | 2012-05-13 03:35:30 +0200 | 
|---|---|---|
| committer | Patrick Simianer <simianer@cl.uni-heidelberg.de> | 2012-05-13 03:35:30 +0200 | 
| commit | 670a8f984fc6d8342180c59ae9e96b0b76f34d3d (patch) | |
| tree | 9f2ce7eec1a77e56b3bb1ad0ad40f212d7a996b0 /jam-files/engine/hcache.c | |
| parent | eb3ee28dc0eb1d3e5ed01ba0df843be329ae450d (diff) | |
| parent | 2f64af3e06a518b93f7ca2c30a9d0aeb2c947031 (diff) | |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'jam-files/engine/hcache.c')
| -rw-r--r-- | jam-files/engine/hcache.c | 434 | 
1 files changed, 434 insertions, 0 deletions
| diff --git a/jam-files/engine/hcache.c b/jam-files/engine/hcache.c new file mode 100644 index 00000000..70bb798c --- /dev/null +++ b/jam-files/engine/hcache.c @@ -0,0 +1,434 @@ +/* + * 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. + * + * 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 results in dramatic speedups with + * large projects (eg. 3min -> 1min startup for 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 @file@ @file@ @file@ ... \n + */ + +typedef struct hcachedata HCACHEDATA ; + +struct hcachedata +{ +    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 */ +    HCACHEDATA * next; +}; + + +static struct hash * hcachehash = 0; +static HCACHEDATA  * hcachelist = 0; + +static int queries = 0; +static int hits = 0; + +#define CACHE_FILE_VERSION "version 4" +#define CACHE_RECORD_HEADER "header" +#define CACHE_RECORD_END "end" + + +/* + * 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 not change the cache file during header scanning. + */ + +static char * cache_name( void ) +{ +    static char * name = 0; +    if ( !name ) +    { +        LIST * hcachevar = var_get( "HCACHEFILE" ); + +        if ( hcachevar ) +        { +            TARGET * t = bindtarget( hcachevar->string ); + +            pushsettings( t->settings ); +            /* Do not expect the cache file to be generated, so pass 0 as the +             * third argument to search. Expect the location to be specified via +             * LOCATE, so pass 0 as the fourth arugment. +             */ +            t->boundname = search( t->name, &t->time, 0, 0 ); +            popsettings( t->settings ); + +            if ( hcachevar ) +                name = copystr( t->boundname ); +        } +    } +    return name; +} + + +/* + * Return the maximum age a cache entry can have before it is purged ftom the + * cache. + */ + +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 a netstring. The caveat is that the string can not contain ASCII 0. The + * returned value is as returned by newstr(), so it need not be freed. + */ + +char * read_netstring( FILE * f ) +{ +    unsigned long len; +    static char * buf = NULL; +    static unsigned long buf_len = 0; + +    if ( fscanf( f, " %9lu", &len ) != 1 ) +        return NULL; +    if ( fgetc( f ) != (int)'\t' ) +        return NULL; + +    if ( len > 1024 * 64 ) +        return NULL;  /* sanity check */ + +    if ( len > buf_len ) +    { +        unsigned long new_len = buf_len * 2; +        if ( new_len < len ) +            new_len = len; +        buf = (char *)BJAM_REALLOC( buf, new_len + 1 ); +        if ( buf ) +            buf_len = new_len; +    } + +    if ( !buf ) +        return NULL; + +    if ( fread( buf, 1, len, f ) != len ) +        return NULL; +    if ( fgetc( f ) != (int)'\n' ) +        return NULL; + +    buf[ len ] = 0; +    return newstr( buf ); +} + + +/* + * Write a netstring. + */ + +void write_netstring( FILE * f, char const * s ) +{ +    if ( !s ) +        s = ""; +    fprintf( f, "%lu\t%s\n", (long unsigned)strlen( s ), s ); +} + + +void hcache_init() +{ +    HCACHEDATA   cachedata; +    HCACHEDATA * c; +    FILE       * f; +    char       * version; +    int          header_count = 0; +    char       * hcachename; + +    hcachehash = hashinit( sizeof( HCACHEDATA ), "hcache" ); + +    if ( !( hcachename = cache_name() ) ) +        return; + +    if ( !( f = fopen( hcachename, "rb" ) ) ) +        return; + +    version = read_netstring( f ); +    if ( !version || strcmp( version, CACHE_FILE_VERSION ) ) +    { +        fclose( f ); +        return; +    } + +    while ( 1 ) +    { +        char * record_type; +        char * time_str; +        char * age_str; +        char * includes_count_str; +        char * hdrscan_count_str; +        int    i; +        int    count; +        LIST * l; + +        record_type = read_netstring( f ); +        if ( !record_type ) +        { +            fprintf( stderr, "invalid %s\n", hcachename ); +            goto bail; +        } +        if ( !strcmp( record_type, CACHE_RECORD_END ) ) +            break; +        if ( strcmp( record_type, CACHE_RECORD_HEADER ) ) +        { +            fprintf( stderr, "invalid %s with record separator <%s>\n", +                hcachename, record_type ? record_type : "<null>" ); +            goto bail; +        } + +        c = &cachedata; + +        c->boundname       = read_netstring( f ); +        time_str           = read_netstring( f ); +        age_str            = read_netstring( f ); +        includes_count_str = read_netstring( f ); + +        if ( !c->boundname || !time_str || !age_str || !includes_count_str ) +        { +            fprintf( stderr, "invalid %s\n", hcachename ); +            goto bail; +        } + +        c->time = atoi( time_str ); +        c->age = atoi( age_str ) + 1; + +        count = atoi( includes_count_str ); +        for ( l = 0, i = 0; i < count; ++i ) +        { +            char * s = read_netstring( f ); +            if ( !s ) +            { +                fprintf( stderr, "invalid %s\n", hcachename ); +                goto bail; +            } +            l = list_new( l, s ); +        } +        c->includes = l; + +        hdrscan_count_str = read_netstring( f ); +        if ( !includes_count_str ) +        { +            list_free( c->includes ); +            fprintf( stderr, "invalid %s\n", hcachename ); +            goto bail; +        } + +        count = atoi( hdrscan_count_str ); +        for ( l = 0, i = 0; i < count; ++i ) +        { +            char * s = read_netstring( f ); +            if ( !s ) +            { +                fprintf( stderr, "invalid %s\n", hcachename ); +                goto bail; +            } +            l = list_new( l, s ); +        } +        c->hdrscan = l; + +        if ( !hashenter( hcachehash, (HASHDATA * *)&c ) ) +        { +            fprintf( stderr, "can't insert header cache item, bailing on %s\n", +                hcachename ); +            goto bail; +        } + +        c->next = hcachelist; +        hcachelist = c; + +        ++header_count; +    } + +    if ( DEBUG_HEADER ) +        printf( "hcache read from file %s\n", hcachename ); + + bail: +    fclose( f ); +} + + +void hcache_done() +{ +    FILE       * f; +    HCACHEDATA * c; +    int          header_count = 0; +    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. */ +    write_netstring( f, CACHE_FILE_VERSION ); + +    c = hcachelist; +    for ( c = hcachelist; c; c = c->next ) +    { +        LIST * l; +        char   time_str[ 30 ]; +        char   age_str[ 30 ]; +        char   includes_count_str[ 30 ]; +        char   hdrscan_count_str[ 30 ]; + +        if ( maxage == 0 ) +            c->age = 0; +        else if ( c->age > maxage ) +            continue; + +        sprintf( includes_count_str, "%lu", (long unsigned) list_length( c->includes ) ); +        sprintf( hdrscan_count_str, "%lu", (long unsigned) list_length( c->hdrscan ) ); +        sprintf( time_str, "%lu", (long unsigned) c->time ); +        sprintf( age_str, "%lu", (long unsigned) c->age ); + +        write_netstring( f, CACHE_RECORD_HEADER ); +        write_netstring( f, c->boundname ); +        write_netstring( f, time_str ); +        write_netstring( f, age_str ); +        write_netstring( f, includes_count_str ); +        for ( l = c->includes; l; l = list_next( l ) ) +            write_netstring( f, l->string ); +        write_netstring( f, hdrscan_count_str ); +        for ( l = c->hdrscan; l; l = list_next( l ) ) +            write_netstring( f, l->string ); +        fputs( "\n", f ); +        ++header_count; +    } +    write_netstring( f, CACHE_RECORD_END ); + +    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, int rec, regexp * re[], LIST * hdrscan ) +{ +    HCACHEDATA cachedata; +    HCACHEDATA * c = &cachedata; + +    LIST * l = 0; + +    ++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 = NULL; +        } else { +            l1 = list_next(l1); +            l2 = list_next(l2); +        } +        } +        if (l1 || l2) { +        if (DEBUG_HEADER) +            printf("HDRSCAN out of date in cache for %s\n", +               t->boundname); + +        printf("HDRSCAN out of date for %s\n", t->boundname); +        printf(" real  : "); +        list_print(hdrscan); +        printf("\n cached: "); +        list_print(c->hdrscan); +        printf("\n"); + +        list_free(c->includes); +        list_free(c->hdrscan); +        c->includes = 0; +        c->hdrscan = 0; +        } else { +        if (DEBUG_HEADER) +            printf ("using header cache for %s\n", t->boundname); +        c->age = 0; +        ++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 (0, t->boundname, rec, re); + +    c->time = t->time; +    c->age = 0; +    c->includes = list_copy (0, l); +    c->hdrscan = list_copy(0, hdrscan); + +    return l; +} + +#endif | 
