header cache now uses binary file format
[k8jam.git] / src / hcache.c
blob6fd2807f4950b70752f6d4887e96be6fc4ef5327
1 /*
2 * This file has been donated to Jam.
3 */
4 int optShowHCacheStats = 0;
5 int optShowHCacheInfo = 0;
7 //#ifdef OPT_HEADER_CACHE_EXT
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
14 #include "jam.h"
15 #include "lists.h"
16 #include "parse.h"
17 #include "rules.h"
18 #include "hsregexp.h"
19 #include "headers.h"
20 #include "newstr.h"
21 #include "hash.h"
22 #include "hcache.h"
23 #include "variable.h"
24 #include "search.h"
25 #include "pathsys.h"
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.)
42 * External routines:
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;
49 time_t time;
50 LIST *includes;
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;
54 } HCACHEDATA;
57 static struct hash *hcachehash = NULL;
58 static HCACHEDATA *hcachelist = NULL;
60 static int queries = 0;
61 static int hits = 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
74 * header scanning.
76 static const char *hcacheFileName (void) {
77 static const char *name = NULL;
78 if (name == NULL) {
79 LIST *hcachevar = var_get("HCACHEFILE");
80 if (hcachevar) {
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);
88 return name;
93 * Return the maximum age a cache entry can have before it is purged from the cache.
95 static int cache_maxage (void) {
96 int age = 100;
97 LIST *var = var_get("HCACHEMAXAGE");
98 if (var) {
99 age = atoi(var->string);
100 if (age < 0) age = 0;
102 return age;
107 * read string
108 * the returned value is as returned by newstr(), so it need not be freed
110 static const char *readStr (FILE *fl) {
111 int sz;
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;
116 if (sz > 0) {
117 if (fread(buf, sz, 1, fl) != 1) return NULL;
119 buf[sz] = 0;
120 return newstr(buf);
125 * write string
127 static int writeStr (FILE *fl, const char *s) {
128 int sz;
130 if (s == NULL) s = "";
131 sz = strlen(s);
132 if (fwrite(&sz, sizeof(sz), 1, fl) != 1) return -1;
133 if (sz > 0) {
134 if (fwrite(s, sz, 1, fl) != 1) return -1;
136 return 0;
140 void hcache_init (void) {
141 HCACHEDATA cachedata, *c;
142 FILE *fl;
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);
152 for (;;) {
153 const char *record_type;
154 int i, count;
155 LIST *l;
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; }
161 c = &cachedata;
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);
176 if (!s) goto bail;
177 l = list_new(l, s, 1);
179 c->includes = l;
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);
187 if (!s) goto bail;
188 l = list_new(l, s, 1);
190 c->hdrscan = l;
191 if (!hashenter(hcachehash, (HASHDATA **)&c)) {
192 fprintf(stderr, "can't insert header cache item, bailing on %s\n", hcachename);
193 goto bail;
195 c->next = hcachelist;
196 hcachelist = c;
197 ++header_count;
199 if (DEBUG_HEADER) printf("HCACHE: hcache read from file %s\n", hcachename);
200 fclose(fl);
201 return;
202 bail:
203 fclose(fl);
204 fprintf(stderr, "HCACHE: invalid cache file: '%s'\n", hcachename);
208 void hcache_done (void) {
209 FILE *fl;
210 HCACHEDATA *c;
211 int header_count = 0;
212 const char *hcachename;
213 int maxage;
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;
221 c = hcachelist;
222 for (c = hcachelist; c != NULL; c = c->next) {
223 LIST *l;
224 int count;
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;
253 //fputs("\n", fl);
254 ++header_count;
256 if (writeStr(fl, CACHE_RECORD_END)) goto bail;
257 fclose(fl);
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);
259 return;
260 bail:
261 fclose(fl);
262 unlink(hcachename);
263 fprintf(stderr, "HCACHE: can't write cache file: '%s'\n", hcachename);
267 LIST *hcache (TARGET *t, LIST *hdrscan) {
268 HCACHEDATA cachedata, *c = &cachedata;
269 LIST *l = 0;
270 char _normalizedPath[PATH_MAX];
271 char *normalizedPath = normalize_path(t->boundname, _normalizedPath, sizeof(_normalizedPath));
273 ++queries;
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;
278 while (l1 && l2) {
279 if (l1->string != l2->string) {
280 l1 = NULL;
281 } else {
282 l1 = list_next(l1);
283 l2 = list_next(l2);
286 if (l1 || l2) {
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);
289 printf(" real : ");
290 list_print(hdrscan);
291 printf("\n cached: ");
292 list_print(c->hdrscan);
293 printf("\n");
294 list_free(c->includes);
295 list_free(c->hdrscan);
296 c->includes = 0;
297 c->hdrscan = 0;
298 } else {
299 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: using header cache for %s\n", t->boundname);
300 c->age = 0;
301 ++hits;
302 l = list_copy(0, c->includes);
303 return l;
305 } else {
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);
309 c->includes = 0;
310 c->hdrscan = 0;
312 } else {
313 if (hashenter(hcachehash, (HASHDATA **)&c)) {
314 c->boundname = newstr(c->boundname);
315 c->next = hcachelist;
316 hcachelist = c;
319 /* 'c' points at the cache entry; its out of date */
320 l = headers1(t->boundname, hdrscan);
321 c->time = t->time;
322 c->age = 0;
323 c->includes = list_copy(0, l);
324 c->hdrscan = list_copy(0, hdrscan);
325 return l;
329 //#endif