option.c: fixed warnings
[k8jam.git] / src / hcache.c
blob248164916b26952bc0b78916f65403c56827c5c8
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, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * This file has been donated to Jam.
18 int optShowHCacheStats = 0;
19 int optShowHCacheInfo = 0;
21 //#ifdef OPT_HEADER_CACHE_EXT
22 #include <ctype.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
29 #include "jam.h"
30 #include "lists.h"
31 #include "parse.h"
32 #include "rules.h"
33 #include "re9.h"
34 #include "headers.h"
35 #include "newstr.h"
36 #include "hash.h"
37 #include "hcache.h"
38 #include "variable.h"
39 #include "search.h"
40 #include "pathsys.h"
44 * Craig W. McPheeters, Alias|Wavefront.
46 * hcache.c hcache.h - handle cacheing of #includes in source files
48 * Create a cache of files scanned for headers. When starting jam,
49 * look for the cache file and load it if present. When finished the
50 * binding phase, create a new header cache. The cache contains
51 * files, their timestamps and the header files found in their scan.
52 * During the binding phase of jam, look in the header cache first for
53 * the headers contained in a file. If the cache is present and
54 * valid, use its contents. This results in dramatic speedups with
55 * large projects (eg. 3min -> 1min startup for one project.)
57 * External routines:
58 * hcache_init() - read and parse the local .jamdeps file.
59 * hcache_done() - write a new .jamdeps file
60 * hcache() - return list of headers on target. Use cache or do a scan.
62 typedef struct hcachedata_s {
63 const char *boundname;
64 time_t time;
65 LIST *includes;
66 LIST *hdrscan; /* the HDRSCAN value for this target */
67 int age; /* if too old, we'll remove it from cache */
68 struct hcachedata_s *next;
69 } hcachedata_t;
72 static struct hash *hcachehash = NULL;
73 static hcachedata_t *hcachelist = NULL;
75 static int queries = 0;
76 static int hits = 0;
77 static int hcache_changed = 0;
80 #define CACHE_FILE_VERSION "k8jam header cache!"
81 #define CACHE_RECORD_HEADER "hdr"
82 #define CACHE_RECORD_END "end"
86 * Return the name of the header cache file. May return NULL.
88 * The user sets this by setting the HCACHEFILE variable in a Jamfile.
89 * We cache the result so the user can't change the cache file during
90 * header scanning.
92 static const char *hcache_file_name (void) {
93 static const char *name = NULL;
94 if (name == NULL) {
95 LIST *hcachevar = var_get("HCACHEFILE");
96 if (hcachevar) {
97 TARGET *t = bindtarget(hcachevar->string);
98 pushsettings(t->settings);
99 t->boundname = search(t->name, &t->time);
100 popsettings(t->settings);
101 if (t->boundname) name = copystr(t->boundname);
104 return name;
109 * Return the maximum age a cache entry can have before it is purged from the cache.
111 static int hcache_maxage (void) {
112 int age = 100;
113 LIST *var = var_get("HCACHEMAXAGE");
114 if (var && var->string) {
115 age = atoi(var->string);
116 if (age < 0) age = 0;
118 return age;
123 * read string
124 * the returned value is as returned by newstr(), so it need not be freed
126 static const char *read_str (FILE *fl) {
127 uint16_t sz;
128 static char buf[33000];
129 if (fread(&sz, sizeof(sz), 1, fl) != 1) return NULL;
130 if (/*sz < 0 ||*/ sz > 32700) return NULL;
131 if (sz > 0) {
132 if (fread(buf, sz, 1, fl) != 1) return NULL;
134 buf[sz] = 0;
135 return newstr(buf);
140 * write string
142 static int write_str (FILE *fl, const char *s) {
143 size_t sz;
144 uint16_t sx;
145 if (s == NULL) s = "";
146 sz = strlen(s);
147 if (sz > 32700) return -1;
148 sx = (uint16_t)sz;
149 if (fwrite(&sx, sizeof(sx), 1, fl) != 1) return -1;
150 if (sz > 0 && fwrite(s, sz, 1, fl) != 1) return -1;
151 return 0;
155 void hcache_init (void) {
156 hcachedata_t cachedata, *c;
157 FILE *fl;
158 const char *version, *hcachename;
159 int header_count = 0;
160 hcachehash = hashinit(sizeof(hcachedata_t), "hcache");
161 if (optShowHCacheInfo) printf("hcache_init: fn=[%s]\n", hcache_file_name());
162 if (!(hcachename = hcache_file_name())) return;
163 if (!(fl = fopen(hcachename, "rb"))) return;
164 version = read_str(fl);
165 if (!version || strcmp(version, CACHE_FILE_VERSION)) { fclose(fl); return; }
166 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: reading cache from '%s'\n", hcachename);
167 for (;;) {
168 const char *record_type;
169 int i, count;
170 LIST *l;
171 record_type = read_str(fl);
172 if (!record_type) goto bail;
173 if (!strcmp(record_type, CACHE_RECORD_END)) break;
174 if (strcmp(record_type, CACHE_RECORD_HEADER)) { printf("invalid %s with record separator <%s>\n", hcachename, record_type ? record_type : "<null>"); goto bail; }
175 c = &cachedata;
176 c->boundname = read_str(fl);
177 if (!c->boundname) goto bail;
178 if (fread(&c->time, sizeof(c->time), 1, fl) != 1) goto bail;
179 if (fread(&c->age, sizeof(c->age), 1, fl) != 1) goto bail;
180 if (fread(&count, sizeof(count), 1, fl) != 1) goto bail;
181 for (l = 0, i = 0; i < count; ++i) {
182 const char *s = read_str(fl);
183 if (!s) goto bail;
184 l = list_new(l, s, 1);
186 c->includes = l;
187 if (fread(&count, sizeof(count), 1, fl) != 1) { list_free(c->includes); goto bail; }
188 for (l = 0, i = 0; i < count; ++i) {
189 const char *s = read_str(fl);
190 if (!s) goto bail;
191 l = list_new(l, s, 1);
193 c->hdrscan = l;
194 if (!hashenter(hcachehash, (HASHDATA **)&c)) {
195 printf("can't insert header cache item, bailing on %s\n", hcachename);
196 goto bail;
198 c->next = hcachelist;
199 hcachelist = c;
200 ++header_count;
202 if (DEBUG_HEADER) printf("HCACHE: hcache read from file %s\n", hcachename);
203 fclose(fl);
204 return;
205 bail:
206 fclose(fl);
207 printf("HCACHE: invalid cache file: '%s'\n", hcachename);
211 void hcache_done (void) {
212 FILE *fl;
213 hcachedata_t *c;
214 int header_count = 0;
215 const char *hcachename;
216 int maxage;
217 if (optShowHCacheInfo) printf("hcache_done()\n");
218 if (!hcachehash) return;
219 maxage = hcache_maxage();
220 #if 0
221 /* this check is not necessary */
222 if (!hcache_changed && maxage > 0) {
223 /* check if we have some out-of-date items */
224 for (c = hcachelist; c != NULL; c = c->next) if (c->age > maxage) { hcache_changed = 1; break; }
226 #endif
227 if (!hcache_changed) return; /* nothing was changed, no need to save cache */
228 if (optShowHCacheInfo) printf("hcache_done: fn=[%s]\n", hcache_file_name());
229 if (!(hcachename = hcache_file_name())) return;
230 if (!(fl = fopen(hcachename, "wb"))) return;
231 if (optShowHCacheInfo) printf("hcache_done: saving cache\n");
232 /* print out the version */
233 if (write_str(fl, CACHE_FILE_VERSION)) goto bail;
234 //c = hcachelist;
235 for (c = hcachelist; c != NULL; c = c->next) {
236 LIST *l;
237 int count;
238 if (maxage == 0) c->age = 0;
239 else if (c->age > maxage) continue;
240 if (write_str(fl, CACHE_RECORD_HEADER)) goto bail;
241 if (write_str(fl, c->boundname)) goto bail;
242 if (fwrite(&c->time, sizeof(c->time), 1, fl) != 1) goto bail;
243 if (fwrite(&c->age, sizeof(c->age), 1, fl) != 1) goto bail;
244 count = list_length(c->includes);
245 if (fwrite(&count, sizeof(count), 1, fl) != 1) goto bail;
246 for (l = c->includes; l; l = list_next(l)) {
247 if (write_str(fl, l->string)) goto bail;
249 count = list_length(c->hdrscan);
250 if (fwrite(&count, sizeof(count), 1, fl) != 1) goto bail;
251 for (l = c->hdrscan; l; l = list_next(l)) {
252 if (write_str(fl, l->string)) goto bail;
254 ++header_count;
256 if (write_str(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 printf("HCACHE: can't write cache file: '%s'\n", hcachename);
267 LIST *hcache (TARGET *t, LIST *hdrscan) {
268 hcachedata_t cachedata, *c = &cachedata;
269 LIST *l = 0;
270 static char _normalizedPath[PATH_MAX]; /* hcache() can't be called recursive, so don't put this on stack */
271 char *normalizedPath = normalize_path(t->boundname, _normalizedPath, sizeof(_normalizedPath), NULL);
272 ++queries;
273 c->boundname = (normalizedPath != NULL ? normalizedPath : t->boundname);
274 if (hashcheck(hcachehash, (HASHDATA **)&c)) {
275 if (c->time == t->time) {
276 LIST *l1 = hdrscan, *l2 = c->hdrscan;
277 while (l1 && l2) {
278 if (l1->string != l2->string) {
279 l1 = NULL;
280 } else {
281 l1 = list_next(l1);
282 l2 = list_next(l2);
285 if (l1 || l2) {
286 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_ex(stdout, hdrscan, LPFLAG_NO_TRSPACE);
291 printf("\n cached: ");
292 list_print_ex(stdout, c->hdrscan, LPFLAG_NO_TRSPACE);
293 printf("\n");
295 list_free(c->includes);
296 list_free(c->hdrscan);
297 c->includes = 0;
298 c->hdrscan = 0;
299 } else {
300 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: using header cache for %s\n", t->boundname);
301 c->age = 0;
302 ++hits;
303 l = list_copy(0, c->includes);
304 return l;
306 } else {
307 if (DEBUG_HEADER || optShowHCacheInfo) printf("HCACHE: header cache out of date for %s\n", t->boundname);
308 list_free(c->includes);
309 list_free(c->hdrscan);
310 c->includes = 0;
311 c->hdrscan = 0;
313 } else {
314 if (hashenter(hcachehash, (HASHDATA **)&c)) {
315 c->boundname = newstr(c->boundname);
316 c->next = hcachelist;
317 hcachelist = c;
320 /* 'c' points at the cache entry; its out of date */
321 hcache_changed = 1;
322 l = headers1(t->boundname, hdrscan);
323 c->time = t->time;
324 c->age = 0;
325 c->includes = list_copy(0, l);
326 c->hdrscan = list_copy(0, hdrscan);
327 return l;
331 //#endif