removed some OS/2 remnants
[k8jam.git] / src / hcache.c
blobc071505e797852cb53ec08e640c69766534a402f
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 <ctype.h>
9 #include <limits.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
15 #include "jam.h"
16 #include "lists.h"
17 #include "parse.h"
18 #include "rules.h"
19 #include "re9.h"
20 #include "headers.h"
21 #include "newstr.h"
22 #include "hash.h"
23 #include "hcache.h"
24 #include "variable.h"
25 #include "search.h"
26 #include "pathsys.h"
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.)
43 * External routines:
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;
50 time_t time;
51 LIST *includes;
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;
55 } hcachedata_t;
58 static struct hash *hcachehash = NULL;
59 static hcachedata_t *hcachelist = NULL;
61 static int queries = 0;
62 static int hits = 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
76 * header scanning.
78 static const char *hcache_file_name (void) {
79 static const char *name = NULL;
80 if (name == NULL) {
81 LIST *hcachevar = var_get("HCACHEFILE");
82 if (hcachevar) {
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);
90 return name;
95 * Return the maximum age a cache entry can have before it is purged from the cache.
97 static int hcache_maxage (void) {
98 int age = 100;
99 LIST *var = var_get("HCACHEMAXAGE");
100 if (var && var->string) {
101 age = atoi(var->string);
102 if (age < 0) age = 0;
104 return age;
109 * read string
110 * the returned value is as returned by newstr(), so it need not be freed
112 static const char *read_str (FILE *fl) {
113 uint16_t sz;
114 static char buf[33000];
115 if (fread(&sz, sizeof(sz), 1, fl) != 1) return NULL;
116 if (sz < 0 || sz > 32700) return NULL;
117 if (sz > 0) {
118 if (fread(buf, sz, 1, fl) != 1) return NULL;
120 buf[sz] = 0;
121 return newstr(buf);
126 * write string
128 static int write_str (FILE *fl, const char *s) {
129 int sz;
130 uint16_t 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 && fwrite(s, sz, 1, fl) != 1) return -1;
137 return 0;
141 void hcache_init (void) {
142 hcachedata_t cachedata, *c;
143 FILE *fl;
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);
153 for (;;) {
154 const char *record_type;
155 int i, count;
156 LIST *l;
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; }
161 c = &cachedata;
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);
169 if (!s) goto bail;
170 l = list_new(l, s, 1);
172 c->includes = l;
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);
176 if (!s) goto bail;
177 l = list_new(l, s, 1);
179 c->hdrscan = l;
180 if (!hashenter(hcachehash, (HASHDATA **)&c)) {
181 printf("can't insert header cache item, bailing on %s\n", hcachename);
182 goto bail;
184 c->next = hcachelist;
185 hcachelist = c;
186 ++header_count;
188 if (DEBUG_HEADER) printf("HCACHE: hcache read from file %s\n", hcachename);
189 fclose(fl);
190 return;
191 bail:
192 fclose(fl);
193 printf("HCACHE: invalid cache file: '%s'\n", hcachename);
197 void hcache_done (void) {
198 FILE *fl;
199 hcachedata_t *c;
200 int header_count = 0;
201 const char *hcachename;
202 int maxage;
203 if (optShowHCacheInfo) printf("hcache_done()\n");
204 if (!hcachehash) return;
205 maxage = hcache_maxage();
206 #if 0
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; }
212 #endif
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;
220 //c = hcachelist;
221 for (c = hcachelist; c != NULL; c = c->next) {
222 LIST *l;
223 int count;
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;
240 ++header_count;
242 if (write_str(fl, CACHE_RECORD_END)) goto bail;
243 fclose(fl);
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);
245 return;
246 bail:
247 fclose(fl);
248 unlink(hcachename);
249 printf("HCACHE: can't write cache file: '%s'\n", hcachename);
253 LIST *hcache (TARGET *t, LIST *hdrscan) {
254 hcachedata_t cachedata, *c = &cachedata;
255 LIST *l = 0;
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));
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 hcache_changed = 1;
308 l = headers1(t->boundname, hdrscan);
309 c->time = t->time;
310 c->age = 0;
311 c->includes = list_copy(0, l);
312 c->hdrscan = list_copy(0, hdrscan);
313 return l;
317 //#endif