fixed pkg-config rule
[k8jam.git] / src / hcache.c
blobf22da66a8495200341d9d623210b0529eab03767
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 (t->boundname) 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 short sz;
112 static char buf[33000];
114 if (fread(&sz, sizeof(sz), 1, fl) != 1) return NULL;
115 if (sz < 0 || sz > 32700) 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;
129 short sx;
131 if (s == NULL) s = "";
132 sz = strlen(s);
133 if (sz > 32700) return -1;
134 sx = sz;
135 if (fwrite(&sx, sizeof(sx), 1, fl) != 1) return -1;
136 if (sz > 0) {
137 if (fwrite(s, sz, 1, fl) != 1) return -1;
139 return 0;
143 void hcache_init (void) {
144 HCACHEDATA cachedata, *c;
145 FILE *fl;
146 const char *version, *hcachename;
147 int header_count = 0;
149 hcachehash = hashinit(sizeof(HCACHEDATA), "hcache");
150 if (!(hcachename = hcacheFileName())) return;
151 if (!(fl = fopen(hcachename, "rb"))) return;
152 version = readStr(fl);
153 if (!version || strcmp(version, CACHE_FILE_VERSION)) { fclose(fl); return; }
154 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: reading cache from '%s'\n", hcachename);
155 for (;;) {
156 const char *record_type;
157 int i, count;
158 LIST *l;
160 record_type = readStr(fl);
161 if (!record_type) goto bail;
162 if (!strcmp(record_type, CACHE_RECORD_END)) break;
163 if (strcmp(record_type, CACHE_RECORD_HEADER)) { printf("invalid %s with record separator <%s>\n", hcachename, record_type ? record_type : "<null>"); goto bail; }
164 c = &cachedata;
165 c->boundname = readStr(fl);
166 if (!c->boundname) goto bail;
167 if (fread(&c->time, sizeof(c->time), 1, fl) != 1) goto bail;
168 if (fread(&c->age, sizeof(c->age), 1, fl) != 1) goto bail;
169 if (fread(&count, sizeof(count), 1, fl) != 1) goto bail;
171 for (l = 0, i = 0; i < count; ++i) {
172 const char *s = readStr(fl);
173 if (!s) goto bail;
174 l = list_new(l, s, 1);
176 c->includes = l;
177 if (fread(&count, sizeof(count), 1, fl) != 1) { list_free(c->includes); goto bail; }
179 for (l = 0, i = 0; i < count; ++i) {
180 const char *s = readStr(fl);
181 if (!s) goto bail;
182 l = list_new(l, s, 1);
184 c->hdrscan = l;
185 if (!hashenter(hcachehash, (HASHDATA **)&c)) {
186 printf("can't insert header cache item, bailing on %s\n", hcachename);
187 goto bail;
189 c->next = hcachelist;
190 hcachelist = c;
191 ++header_count;
193 if (DEBUG_HEADER) printf("HCACHE: hcache read from file %s\n", hcachename);
194 fclose(fl);
195 return;
196 bail:
197 fclose(fl);
198 printf("HCACHE: invalid cache file: '%s'\n", hcachename);
202 void hcache_done (void) {
203 FILE *fl;
204 HCACHEDATA *c;
205 int header_count = 0;
206 const char *hcachename;
207 int maxage;
209 if (!hcachehash) return;
210 if (!(hcachename = hcacheFileName())) return;
211 if (!(fl = fopen(hcachename, "wb"))) return;
212 maxage = cache_maxage();
213 /* print out the version */
214 if (writeStr(fl, CACHE_FILE_VERSION)) goto bail;
215 c = hcachelist;
216 for (c = hcachelist; c != NULL; c = c->next) {
217 LIST *l;
218 int count;
220 if (maxage == 0) c->age = 0;
221 else if (c->age > maxage) continue;
222 if (writeStr(fl, CACHE_RECORD_HEADER)) goto bail;
223 if (writeStr(fl, c->boundname)) goto bail;
224 if (fwrite(&c->time, sizeof(c->time), 1, fl) != 1) goto bail;
225 if (fwrite(&c->age, sizeof(c->age), 1, fl) != 1) goto bail;
226 count = list_length(c->includes);
227 if (fwrite(&count, sizeof(count), 1, fl) != 1) goto bail;
229 for (l = c->includes; l; l = list_next(l)) {
230 if (writeStr(fl, l->string)) goto bail;
233 count = list_length(c->hdrscan);
234 if (fwrite(&count, sizeof(count), 1, fl) != 1) goto bail;
236 for (l = c->hdrscan; l; l = list_next(l)) {
237 if (writeStr(fl, l->string)) goto bail;
239 ++header_count;
241 if (writeStr(fl, CACHE_RECORD_END)) goto bail;
242 fclose(fl);
243 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);
244 return;
245 bail:
246 fclose(fl);
247 unlink(hcachename);
248 printf("HCACHE: can't write cache file: '%s'\n", hcachename);
252 LIST *hcache (TARGET *t, LIST *hdrscan) {
253 HCACHEDATA cachedata, *c = &cachedata;
254 LIST *l = 0;
255 char _normalizedPath[PATH_MAX];
256 char *normalizedPath = normalize_path(t->boundname, _normalizedPath, sizeof(_normalizedPath));
258 ++queries;
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;
263 while (l1 && l2) {
264 if (l1->string != l2->string) {
265 l1 = NULL;
266 } else {
267 l1 = list_next(l1);
268 l2 = list_next(l2);
271 if (l1 || l2) {
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);
275 printf(" real : ");
276 list_print(hdrscan);
277 printf("\n cached: ");
278 list_print(c->hdrscan);
279 printf("\n");
281 list_free(c->includes);
282 list_free(c->hdrscan);
283 c->includes = 0;
284 c->hdrscan = 0;
285 } else {
286 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: using header cache for %s\n", t->boundname);
287 c->age = 0;
288 ++hits;
289 l = list_copy(0, c->includes);
290 return l;
292 } else {
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);
296 c->includes = 0;
297 c->hdrscan = 0;
299 } else {
300 if (hashenter(hcachehash, (HASHDATA **)&c)) {
301 c->boundname = newstr(c->boundname);
302 c->next = hcachelist;
303 hcachelist = c;
306 /* 'c' points at the cache entry; its out of date */
307 l = headers1(t->boundname, hdrscan);
308 c->time = t->time;
309 c->age = 0;
310 c->includes = list_copy(0, l);
311 c->hdrscan = list_copy(0, hdrscan);
312 return l;
316 //#endif