it's now safe (i hope) to include Jambase.configure multiple times
[k8jam.git] / src / hcache.c
blob528873237ab7ad6ad744ffbe37f6684a7f8a15cd
1 /*
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * This file has been donated to Jam.
19 int optShowHCacheStats = 0;
20 int optShowHCacheInfo = 0;
22 //#ifdef OPT_HEADER_CACHE_EXT
23 #include <ctype.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
30 #include "jam.h"
31 #include "lists.h"
32 #include "parse.h"
33 #include "rules.h"
34 #include "re9.h"
35 #include "headers.h"
36 #include "newstr.h"
37 #include "hash.h"
38 #include "hcache.h"
39 #include "variable.h"
40 #include "search.h"
41 #include "pathsys.h"
45 * Craig W. McPheeters, Alias|Wavefront.
47 * hcache.c hcache.h - handle cacheing of #includes in source files
49 * Create a cache of files scanned for headers. When starting jam,
50 * look for the cache file and load it if present. When finished the
51 * binding phase, create a new header cache. The cache contains
52 * files, their timestamps and the header files found in their scan.
53 * During the binding phase of jam, look in the header cache first for
54 * the headers contained in a file. If the cache is present and
55 * valid, use its contents. This results in dramatic speedups with
56 * large projects (eg. 3min -> 1min startup for one project.)
58 * External routines:
59 * hcache_init() - read and parse the local .jamdeps file.
60 * hcache_done() - write a new .jamdeps file
61 * hcache() - return list of headers on target. Use cache or do a scan.
63 typedef struct hcachedata_s {
64 const char *boundname;
65 time_t time;
66 LIST *includes;
67 LIST *hdrscan; /* the HDRSCAN value for this target */
68 int age; /* if too old, we'll remove it from cache */
69 struct hcachedata_s *next;
70 } hcachedata_t;
73 static struct hash *hcachehash = NULL;
74 static hcachedata_t *hcachelist = NULL;
76 static int queries = 0;
77 static int hits = 0;
78 static int hcache_changed = 0;
81 #define CACHE_FILE_VERSION "k8jam header cache!"
82 #define CACHE_RECORD_HEADER "hdr"
83 #define CACHE_RECORD_END "end"
87 * Return the name of the header cache file. May return NULL.
89 * The user sets this by setting the HCACHEFILE variable in a Jamfile.
90 * We cache the result so the user can't change the cache file during
91 * header scanning.
93 static const char *hcache_file_name (void) {
94 static const char *name = NULL;
95 if (name == NULL) {
96 LIST *hcachevar = var_get("HCACHEFILE");
97 if (hcachevar) {
98 TARGET *t = bindtarget(hcachevar->string);
99 pushsettings(t->settings);
100 t->boundname = search(t->name, &t->time);
101 popsettings(t->settings);
102 if (t->boundname) name = copystr(t->boundname);
105 return name;
110 * Return the maximum age a cache entry can have before it is purged from the cache.
112 static int hcache_maxage (void) {
113 int age = 100;
114 LIST *var = var_get("HCACHEMAXAGE");
115 if (var && var->string) {
116 age = atoi(var->string);
117 if (age < 0) age = 0;
119 return age;
124 * read string
125 * the returned value is as returned by newstr(), so it need not be freed
127 static const char *read_str (FILE *fl) {
128 uint16_t sz;
129 static char buf[33000];
130 if (fread(&sz, sizeof(sz), 1, fl) != 1) return NULL;
131 if (sz < 0 || sz > 32700) return NULL;
132 if (sz > 0) {
133 if (fread(buf, sz, 1, fl) != 1) return NULL;
135 buf[sz] = 0;
136 return newstr(buf);
141 * write string
143 static int write_str (FILE *fl, const char *s) {
144 int sz;
145 uint16_t sx;
146 if (s == NULL) s = "";
147 sz = strlen(s);
148 if (sz > 32700) return -1;
149 sx = sz;
150 if (fwrite(&sx, sizeof(sx), 1, fl) != 1) return -1;
151 if (sz > 0 && fwrite(s, sz, 1, fl) != 1) return -1;
152 return 0;
156 void hcache_init (void) {
157 hcachedata_t cachedata, *c;
158 FILE *fl;
159 const char *version, *hcachename;
160 int header_count = 0;
161 hcachehash = hashinit(sizeof(hcachedata_t), "hcache");
162 if (optShowHCacheInfo) printf("hcache_init: fn=[%s]\n", hcache_file_name());
163 if (!(hcachename = hcache_file_name())) return;
164 if (!(fl = fopen(hcachename, "rb"))) return;
165 version = read_str(fl);
166 if (!version || strcmp(version, CACHE_FILE_VERSION)) { fclose(fl); return; }
167 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: reading cache from '%s'\n", hcachename);
168 for (;;) {
169 const char *record_type;
170 int i, count;
171 LIST *l;
172 record_type = read_str(fl);
173 if (!record_type) goto bail;
174 if (!strcmp(record_type, CACHE_RECORD_END)) break;
175 if (strcmp(record_type, CACHE_RECORD_HEADER)) { printf("invalid %s with record separator <%s>\n", hcachename, record_type ? record_type : "<null>"); goto bail; }
176 c = &cachedata;
177 c->boundname = read_str(fl);
178 if (!c->boundname) goto bail;
179 if (fread(&c->time, sizeof(c->time), 1, fl) != 1) goto bail;
180 if (fread(&c->age, sizeof(c->age), 1, fl) != 1) goto bail;
181 if (fread(&count, sizeof(count), 1, fl) != 1) goto bail;
182 for (l = 0, i = 0; i < count; ++i) {
183 const char *s = read_str(fl);
184 if (!s) goto bail;
185 l = list_new(l, s, 1);
187 c->includes = l;
188 if (fread(&count, sizeof(count), 1, fl) != 1) { list_free(c->includes); goto bail; }
189 for (l = 0, i = 0; i < count; ++i) {
190 const char *s = read_str(fl);
191 if (!s) goto bail;
192 l = list_new(l, s, 1);
194 c->hdrscan = l;
195 if (!hashenter(hcachehash, (HASHDATA **)&c)) {
196 printf("can't insert header cache item, bailing on %s\n", hcachename);
197 goto bail;
199 c->next = hcachelist;
200 hcachelist = c;
201 ++header_count;
203 if (DEBUG_HEADER) printf("HCACHE: hcache read from file %s\n", hcachename);
204 fclose(fl);
205 return;
206 bail:
207 fclose(fl);
208 printf("HCACHE: invalid cache file: '%s'\n", hcachename);
212 void hcache_done (void) {
213 FILE *fl;
214 hcachedata_t *c;
215 int header_count = 0;
216 const char *hcachename;
217 int maxage;
218 if (optShowHCacheInfo) printf("hcache_done()\n");
219 if (!hcachehash) return;
220 maxage = hcache_maxage();
221 #if 0
222 /* this check is not necessary */
223 if (!hcache_changed && maxage > 0) {
224 /* check if we have some out-of-date items */
225 for (c = hcachelist; c != NULL; c = c->next) if (c->age > maxage) { hcache_changed = 1; break; }
227 #endif
228 if (!hcache_changed) return; /* nothing was changed, no need to save cache */
229 if (optShowHCacheInfo) printf("hcache_done: fn=[%s]\n", hcache_file_name());
230 if (!(hcachename = hcache_file_name())) return;
231 if (!(fl = fopen(hcachename, "wb"))) return;
232 if (optShowHCacheInfo) printf("hcache_done: saving cache\n");
233 /* print out the version */
234 if (write_str(fl, CACHE_FILE_VERSION)) goto bail;
235 //c = hcachelist;
236 for (c = hcachelist; c != NULL; c = c->next) {
237 LIST *l;
238 int count;
239 if (maxage == 0) c->age = 0;
240 else if (c->age > maxage) continue;
241 if (write_str(fl, CACHE_RECORD_HEADER)) goto bail;
242 if (write_str(fl, c->boundname)) goto bail;
243 if (fwrite(&c->time, sizeof(c->time), 1, fl) != 1) goto bail;
244 if (fwrite(&c->age, sizeof(c->age), 1, fl) != 1) goto bail;
245 count = list_length(c->includes);
246 if (fwrite(&count, sizeof(count), 1, fl) != 1) goto bail;
247 for (l = c->includes; l; l = list_next(l)) {
248 if (write_str(fl, l->string)) goto bail;
250 count = list_length(c->hdrscan);
251 if (fwrite(&count, sizeof(count), 1, fl) != 1) goto bail;
252 for (l = c->hdrscan; l; l = list_next(l)) {
253 if (write_str(fl, l->string)) goto bail;
255 ++header_count;
257 if (write_str(fl, CACHE_RECORD_END)) goto bail;
258 fclose(fl);
259 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);
260 return;
261 bail:
262 fclose(fl);
263 unlink(hcachename);
264 printf("HCACHE: can't write cache file: '%s'\n", hcachename);
268 LIST *hcache (TARGET *t, LIST *hdrscan) {
269 hcachedata_t cachedata, *c = &cachedata;
270 LIST *l = 0;
271 static char _normalizedPath[PATH_MAX]; /* hcache() can't be called recursive, so don't put this on stack */
272 char *normalizedPath = normalize_path(t->boundname, _normalizedPath, sizeof(_normalizedPath), NULL);
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);
289 printf("HDRSCAN out of date for %s\n", t->boundname);
290 printf(" real : ");
291 list_print_ex(stdout, hdrscan, LPFLAG_NO_TRSPACE);
292 printf("\n cached: ");
293 list_print_ex(stdout, c->hdrscan, LPFLAG_NO_TRSPACE);
294 printf("\n");
296 list_free(c->includes);
297 list_free(c->hdrscan);
298 c->includes = 0;
299 c->hdrscan = 0;
300 } else {
301 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: using header cache for %s\n", t->boundname);
302 c->age = 0;
303 ++hits;
304 l = list_copy(0, c->includes);
305 return l;
307 } else {
308 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: header cache out of date for %s\n", t->boundname);
309 list_free(c->includes);
310 list_free(c->hdrscan);
311 c->includes = 0;
312 c->hdrscan = 0;
314 } else {
315 if (hashenter(hcachehash, (HASHDATA **)&c)) {
316 c->boundname = newstr(c->boundname);
317 c->next = hcachelist;
318 hcachelist = c;
321 /* 'c' points at the cache entry; its out of date */
322 hcache_changed = 1;
323 l = headers1(t->boundname, hdrscan);
324 c->time = t->time;
325 c->age = 0;
326 c->includes = list_copy(0, l);
327 c->hdrscan = list_copy(0, hdrscan);
328 return l;
332 //#endif