2 * This file has been donated to Jam.
4 int optShowHCacheStats
= 0;
5 int optShowHCacheInfo
= 0;
7 //#ifdef OPT_HEADER_CACHE_EXT
30 * Craig W. McPheeters, Alias|Wavefront.
32 * hcache.c hcache.h - handle cacheing of #includes in source files
34 * Create a cache of files scanned for headers. When starting jam,
35 * look for the cache file and load it if present. When finished the
36 * binding phase, create a new header cache. The cache contains
37 * files, their timestamps and the header files found in their scan.
38 * During the binding phase of jam, look in the header cache first for
39 * the headers contained in a file. If the cache is present and
40 * valid, use its contents. This results in dramatic speedups with
41 * large projects (eg. 3min -> 1min startup for one project.)
44 * hcache_init() - read and parse the local .jamdeps file.
45 * hcache_done() - write a new .jamdeps file
46 * hcache() - return list of headers on target. Use cache or do a scan.
48 typedef struct hcachedata_s
{
49 const char *boundname
;
52 LIST
*hdrscan
; /* the HDRSCAN value for this target */
53 int age
; /* if too old, we'll remove it from cache */
54 struct hcachedata_s
*next
;
58 static struct hash
*hcachehash
= NULL
;
59 static hcachedata_t
*hcachelist
= NULL
;
61 static int queries
= 0;
63 static int hcache_changed
= 0;
66 #define CACHE_FILE_VERSION "k8jam header cache!"
67 #define CACHE_RECORD_HEADER "hdr"
68 #define CACHE_RECORD_END "end"
72 * Return the name of the header cache file. May return NULL.
74 * The user sets this by setting the HCACHEFILE variable in a Jamfile.
75 * We cache the result so the user can't change the cache file during
78 static const char *hcache_file_name (void) {
79 static const char *name
= NULL
;
81 LIST
*hcachevar
= var_get("HCACHEFILE");
83 TARGET
*t
= bindtarget(hcachevar
->string
);
84 pushsettings(t
->settings
);
85 t
->boundname
= search(t
->name
, &t
->time
);
86 popsettings(t
->settings
);
87 if (t
->boundname
) name
= copystr(t
->boundname
);
95 * Return the maximum age a cache entry can have before it is purged from the cache.
97 static int hcache_maxage (void) {
99 LIST
*var
= var_get("HCACHEMAXAGE");
100 if (var
&& var
->string
) {
101 age
= atoi(var
->string
);
102 if (age
< 0) age
= 0;
110 * the returned value is as returned by newstr(), so it need not be freed
112 static const char *read_str (FILE *fl
) {
114 static char buf
[33000];
115 if (fread(&sz
, sizeof(sz
), 1, fl
) != 1) return NULL
;
116 if (sz
< 0 || sz
> 32700) return NULL
;
118 if (fread(buf
, sz
, 1, fl
) != 1) return NULL
;
128 static int write_str (FILE *fl
, const char *s
) {
131 if (s
== NULL
) s
= "";
133 if (sz
> 32700) return -1;
135 if (fwrite(&sx
, sizeof(sx
), 1, fl
) != 1) return -1;
136 if (sz
> 0 && fwrite(s
, sz
, 1, fl
) != 1) return -1;
141 void hcache_init (void) {
142 hcachedata_t cachedata
, *c
;
144 const char *version
, *hcachename
;
145 int header_count
= 0;
146 hcachehash
= hashinit(sizeof(hcachedata_t
), "hcache");
147 if (optShowHCacheInfo
) printf("hcache_init: fn=[%s]\n", hcache_file_name());
148 if (!(hcachename
= hcache_file_name())) return;
149 if (!(fl
= fopen(hcachename
, "rb"))) return;
150 version
= read_str(fl
);
151 if (!version
|| strcmp(version
, CACHE_FILE_VERSION
)) { fclose(fl
); return; }
152 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: reading cache from '%s'\n", hcachename
);
154 const char *record_type
;
157 record_type
= read_str(fl
);
158 if (!record_type
) goto bail
;
159 if (!strcmp(record_type
, CACHE_RECORD_END
)) break;
160 if (strcmp(record_type
, CACHE_RECORD_HEADER
)) { printf("invalid %s with record separator <%s>\n", hcachename
, record_type
? record_type
: "<null>"); goto bail
; }
162 c
->boundname
= read_str(fl
);
163 if (!c
->boundname
) goto bail
;
164 if (fread(&c
->time
, sizeof(c
->time
), 1, fl
) != 1) goto bail
;
165 if (fread(&c
->age
, sizeof(c
->age
), 1, fl
) != 1) goto bail
;
166 if (fread(&count
, sizeof(count
), 1, fl
) != 1) goto bail
;
167 for (l
= 0, i
= 0; i
< count
; ++i
) {
168 const char *s
= read_str(fl
);
170 l
= list_new(l
, s
, 1);
173 if (fread(&count
, sizeof(count
), 1, fl
) != 1) { list_free(c
->includes
); goto bail
; }
174 for (l
= 0, i
= 0; i
< count
; ++i
) {
175 const char *s
= read_str(fl
);
177 l
= list_new(l
, s
, 1);
180 if (!hashenter(hcachehash
, (HASHDATA
**)&c
)) {
181 printf("can't insert header cache item, bailing on %s\n", hcachename
);
184 c
->next
= hcachelist
;
188 if (DEBUG_HEADER
) printf("HCACHE: hcache read from file %s\n", hcachename
);
193 printf("HCACHE: invalid cache file: '%s'\n", hcachename
);
197 void hcache_done (void) {
200 int header_count
= 0;
201 const char *hcachename
;
203 if (optShowHCacheInfo
) printf("hcache_done()\n");
204 if (!hcachehash
) return;
205 maxage
= hcache_maxage();
207 /* this check is not necessary */
208 if (!hcache_changed
&& maxage
> 0) {
209 /* check if we have some out-of-date items */
210 for (c
= hcachelist
; c
!= NULL
; c
= c
->next
) if (c
->age
> maxage
) { hcache_changed
= 1; break; }
213 if (!hcache_changed
) return; /* nothing was changed, no need to save cache */
214 if (optShowHCacheInfo
) printf("hcache_done: fn=[%s]\n", hcache_file_name());
215 if (!(hcachename
= hcache_file_name())) return;
216 if (!(fl
= fopen(hcachename
, "wb"))) return;
217 if (optShowHCacheInfo
) printf("hcache_done: saving cache\n");
218 /* print out the version */
219 if (write_str(fl
, CACHE_FILE_VERSION
)) goto bail
;
221 for (c
= hcachelist
; c
!= NULL
; c
= c
->next
) {
224 if (maxage
== 0) c
->age
= 0;
225 else if (c
->age
> maxage
) continue;
226 if (write_str(fl
, CACHE_RECORD_HEADER
)) goto bail
;
227 if (write_str(fl
, c
->boundname
)) goto bail
;
228 if (fwrite(&c
->time
, sizeof(c
->time
), 1, fl
) != 1) goto bail
;
229 if (fwrite(&c
->age
, sizeof(c
->age
), 1, fl
) != 1) goto bail
;
230 count
= list_length(c
->includes
);
231 if (fwrite(&count
, sizeof(count
), 1, fl
) != 1) goto bail
;
232 for (l
= c
->includes
; l
; l
= list_next(l
)) {
233 if (write_str(fl
, l
->string
)) goto bail
;
235 count
= list_length(c
->hdrscan
);
236 if (fwrite(&count
, sizeof(count
), 1, fl
) != 1) goto bail
;
237 for (l
= c
->hdrscan
; l
; l
= list_next(l
)) {
238 if (write_str(fl
, l
->string
)) goto bail
;
242 if (write_str(fl
, CACHE_RECORD_END
)) goto bail
;
244 if (DEBUG_HEADER
|| optShowHCacheStats
) printf("HCACHE: cache written to '%s'; %d dependencies, %.0f%% hit rate\n", hcachename
, header_count
, queries
?100.0*hits
/queries
:0);
249 printf("HCACHE: can't write cache file: '%s'\n", hcachename
);
253 LIST
*hcache (TARGET
*t
, LIST
*hdrscan
) {
254 hcachedata_t cachedata
, *c
= &cachedata
;
256 static char _normalizedPath
[PATH_MAX
]; /* hcache() can't be called recursive, so don't put this on stack */
257 char *normalizedPath
= normalize_path(t
->boundname
, _normalizedPath
, sizeof(_normalizedPath
));
259 c
->boundname
= (normalizedPath
!= NULL
? normalizedPath
: t
->boundname
);
260 if (hashcheck(hcachehash
, (HASHDATA
**)&c
)) {
261 if (c
->time
== t
->time
) {
262 LIST
*l1
= hdrscan
, *l2
= c
->hdrscan
;
264 if (l1
->string
!= l2
->string
) {
272 if (DEBUG_HEADER
) printf("HCACHE: HDRSCAN out of date in cache for %s\n", t
->boundname
);
274 printf("HDRSCAN out of date for %s\n", t->boundname);
277 printf("\n cached: ");
278 list_print(c->hdrscan);
281 list_free(c
->includes
);
282 list_free(c
->hdrscan
);
286 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: using header cache for %s\n", t
->boundname
);
289 l
= list_copy(0, c
->includes
);
293 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: header cache out of date for %s\n", t
->boundname
);
294 list_free(c
->includes
);
295 list_free(c
->hdrscan
);
300 if (hashenter(hcachehash
, (HASHDATA
**)&c
)) {
301 c
->boundname
= newstr(c
->boundname
);
302 c
->next
= hcachelist
;
306 /* 'c' points at the cache entry; its out of date */
308 l
= headers1(t
->boundname
, hdrscan
);
311 c
->includes
= list_copy(0, l
);
312 c
->hdrscan
= list_copy(0, hdrscan
);