2 * This file has been donated to Jam.
4 int optShowHCacheStats
= 0;
5 int optShowHCacheInfo
= 0;
7 //#ifdef OPT_HEADER_CACHE_EXT
29 * Craig W. McPheeters, Alias|Wavefront.
31 * hcache.c hcache.h - handle cacheing of #includes in source files
33 * Create a cache of files scanned for headers. When starting jam,
34 * look for the cache file and load it if present. When finished the
35 * binding phase, create a new header cache. The cache contains
36 * files, their timestamps and the header files found in their scan.
37 * During the binding phase of jam, look in the header cache first for
38 * the headers contained in a file. If the cache is present and
39 * valid, use its contents. This results in dramatic speedups with
40 * large projects (eg. 3min -> 1min startup for one project.)
43 * hcache_init() - read and parse the local .jamdeps file.
44 * hcache_done() - write a new .jamdeps file
45 * hcache() - return list of headers on target. Use cache or do a scan.
47 typedef struct hcachedata
{
48 const char *boundname
;
51 LIST
*hdrscan
; /* the HDRSCAN value for this target */
52 int age
; /* if too old, we'll remove it from cache */
53 struct hcachedata
*next
;
57 static struct hash
*hcachehash
= NULL
;
58 static HCACHEDATA
*hcachelist
= NULL
;
60 static int queries
= 0;
64 #define CACHE_FILE_VERSION "k8jam header cache!"
65 #define CACHE_RECORD_HEADER "hdr"
66 #define CACHE_RECORD_END "end"
70 * Return the name of the header cache file. May return NULL.
72 * The user sets this by setting the HCACHEFILE variable in a Jamfile.
73 * We cache the result so the user can't change the cache file during
76 static const char *hcacheFileName (void) {
77 static const char *name
= NULL
;
79 LIST
*hcachevar
= var_get("HCACHEFILE");
81 TARGET
*t
= bindtarget(hcachevar
->string
);
82 pushsettings(t
->settings
);
83 t
->boundname
= search(t
->name
, &t
->time
);
84 popsettings(t
->settings
);
85 if (hcachevar
) name
= copystr(t
->boundname
);
93 * Return the maximum age a cache entry can have before it is purged from the cache.
95 static int cache_maxage (void) {
97 LIST
*var
= var_get("HCACHEMAXAGE");
99 age
= atoi(var
->string
);
100 if (age
< 0) age
= 0;
108 * the returned value is as returned by newstr(), so it need not be freed
110 static const char *readStr (FILE *fl
) {
112 static char buf
[66000];
114 if (fread(&sz
, sizeof(sz
), 1, fl
) != 1) return NULL
;
115 if (sz
< 0 || sz
> 64*1024) return NULL
;
117 if (fread(buf
, sz
, 1, fl
) != 1) return NULL
;
127 static int writeStr (FILE *fl
, const char *s
) {
130 if (s
== NULL
) s
= "";
132 if (fwrite(&sz
, sizeof(sz
), 1, fl
) != 1) return -1;
134 if (fwrite(s
, sz
, 1, fl
) != 1) return -1;
140 void hcache_init (void) {
141 HCACHEDATA cachedata
, *c
;
143 const char *version
, *hcachename
;
144 int header_count
= 0;
146 hcachehash
= hashinit(sizeof(HCACHEDATA
), "hcache");
147 if (!(hcachename
= hcacheFileName())) return;
148 if (!(fl
= fopen(hcachename
, "rb"))) return;
149 version
= readStr(fl
);
150 if (!version
|| strcmp(version
, CACHE_FILE_VERSION
)) { fclose(fl
); return; }
151 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: reading cache from '%s'\n", hcachename
);
153 const char *record_type
;
157 record_type
= readStr(fl
);
158 if (!record_type
) goto bail
;
159 if (!strcmp(record_type
, CACHE_RECORD_END
)) break;
160 if (strcmp(record_type
, CACHE_RECORD_HEADER
)) { fprintf(stderr
, "invalid %s with record separator <%s>\n", hcachename
, record_type
? record_type
: "<null>"); goto bail
; }
162 c
->boundname
= readStr(fl
);
163 if (!c
->boundname
) goto bail
;
164 //time_str = readStr(fl);
165 //age_str = readStr(fl);
166 //includes_count_str = readStr(fl);
167 //c->time = atoi(time_str);
168 //c->age = atoi(age_str)+1;
169 //count = atoi(includes_count_str);
170 if (fread(&c
->time
, sizeof(c
->time
), 1, fl
) != 1) goto bail
;
171 if (fread(&c
->age
, sizeof(c
->age
), 1, fl
) != 1) goto bail
;
172 if (fread(&count
, sizeof(count
), 1, fl
) != 1) goto bail
;
174 for (l
= 0, i
= 0; i
< count
; ++i
) {
175 const char *s
= readStr(fl
);
177 l
= list_new(l
, s
, 1);
180 //hdrscan_count_str = readStr(fl);
181 //if (!includes_count_str) { list_free(c->includes); goto bail; }
182 //count = atoi(hdrscan_count_str);
183 if (fread(&count
, sizeof(count
), 1, fl
) != 1) { list_free(c
->includes
); goto bail
; }
185 for (l
= 0, i
= 0; i
< count
; ++i
) {
186 const char *s
= readStr(fl
);
188 l
= list_new(l
, s
, 1);
191 if (!hashenter(hcachehash
, (HASHDATA
**)&c
)) {
192 fprintf(stderr
, "can't insert header cache item, bailing on %s\n", hcachename
);
195 c
->next
= hcachelist
;
199 if (DEBUG_HEADER
) printf("HCACHE: hcache read from file %s\n", hcachename
);
204 fprintf(stderr
, "HCACHE: invalid cache file: '%s'\n", hcachename
);
208 void hcache_done (void) {
211 int header_count
= 0;
212 const char *hcachename
;
215 if (!hcachehash
) return;
216 if (!(hcachename
= hcacheFileName())) return;
217 if (!(fl
= fopen(hcachename
, "wb"))) return;
218 maxage
= cache_maxage();
219 /* print out the version */
220 if (writeStr(fl
, CACHE_FILE_VERSION
)) goto bail
;
222 for (c
= hcachelist
; c
!= NULL
; c
= c
->next
) {
226 if (maxage
== 0) c
->age
= 0;
227 else if (c
->age
> maxage
) continue;
228 //sprintf(includes_count_str, "%u", (unsigned int)list_length(c->includes));
229 //sprintf(hdrscan_count_str, "%u", (unsigned int)list_length(c->hdrscan));
230 //sprintf(time_str, "%u", (unsigned int)c->time);
231 //sprintf(age_str, "%u", (unsigned int)c->age);
232 if (writeStr(fl
, CACHE_RECORD_HEADER
)) goto bail
;
233 if (writeStr(fl
, c
->boundname
)) goto bail
;
234 //writeStr(fl, time_str);
235 //writeStr(fl, age_str);
236 //writeStr(fl, includes_count_str);
237 if (fwrite(&c
->time
, sizeof(c
->time
), 1, fl
) != 1) goto bail
;
238 if (fwrite(&c
->age
, sizeof(c
->age
), 1, fl
) != 1) goto bail
;
239 count
= list_length(c
->includes
);
240 if (fwrite(&count
, sizeof(count
), 1, fl
) != 1) goto bail
;
242 for (l
= c
->includes
; l
; l
= list_next(l
)) {
243 if (writeStr(fl
, l
->string
)) goto bail
;
246 //writeStr(fl, hdrscan_count_str);
247 count
= list_length(c
->hdrscan
);
248 if (fwrite(&count
, sizeof(count
), 1, fl
) != 1) goto bail
;
250 for (l
= c
->hdrscan
; l
; l
= list_next(l
)) {
251 if (writeStr(fl
, l
->string
)) goto bail
;
256 if (writeStr(fl
, CACHE_RECORD_END
)) goto bail
;
258 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);
263 fprintf(stderr
, "HCACHE: can't write cache file: '%s'\n", hcachename
);
267 LIST
*hcache (TARGET
*t
, LIST
*hdrscan
) {
268 HCACHEDATA cachedata
, *c
= &cachedata
;
270 char _normalizedPath
[PATH_MAX
];
271 char *normalizedPath
= normalize_path(t
->boundname
, _normalizedPath
, sizeof(_normalizedPath
));
274 c
->boundname
= normalizedPath
!=NULL
?normalizedPath
:t
->boundname
;
275 if (hashcheck(hcachehash
, (HASHDATA
**)&c
)) {
276 if (c
->time
== t
->time
) {
277 LIST
*l1
= hdrscan
, *l2
= c
->hdrscan
;
279 if (l1
->string
!= l2
->string
) {
287 if (DEBUG_HEADER
) printf("HCACHE: HDRSCAN out of date in cache for %s\n", t
->boundname
);
288 printf("HDRSCAN out of date for %s\n", t
->boundname
);
291 printf("\n cached: ");
292 list_print(c
->hdrscan
);
294 list_free(c
->includes
);
295 list_free(c
->hdrscan
);
299 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: using header cache for %s\n", t
->boundname
);
302 l
= list_copy(0, c
->includes
);
306 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: header cache out of date for %s\n", t
->boundname
);
307 list_free(c
->includes
);
308 list_free(c
->hdrscan
);
313 if (hashenter(hcachehash
, (HASHDATA
**)&c
)) {
314 c
->boundname
= newstr(c
->boundname
);
315 c
->next
= hcachelist
;
319 /* 'c' points at the cache entry; its out of date */
320 l
= headers1(t
->boundname
, hdrscan
);
323 c
->includes
= list_copy(0, l
);
324 c
->hdrscan
= list_copy(0, hdrscan
);