2 * This file has been donated to Jam.
4 int optShowHCacheStats
= 0;
5 int optShowHCacheInfo
= 0;
7 //#ifdef OPT_HEADER_CACHE_EXT
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.)
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
;
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
;
57 static struct hash
*hcachehash
= NULL
;
58 static HCACHEDATA
*hcachelist
= NULL
;
60 static int queries
= 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
76 static const char *hcacheFileName (void) {
77 static const char *name
= NULL
;
79 LIST
*hcachevar
= var_get("HCACHEFILE");
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
);
93 * Return the maximum age a cache entry can have before it is purged from the cache.
95 static int cache_maxage (void) {
97 LIST
*var
= var_get("HCACHEMAXAGE");
99 age
= atoi(var
->string
);
100 if (age
< 0) age
= 0;
108 * the returned value is as returned by newstr(), so it need not be freed
110 static const char *readStr (FILE *fl
) {
112 static char buf
[33000];
114 if (fread(&sz
, sizeof(sz
), 1, fl
) != 1) return NULL
;
115 if (sz
< 0 || sz
> 32700) return NULL
;
117 if (fread(buf
, sz
, 1, fl
) != 1) return NULL
;
127 static int writeStr (FILE *fl
, const char *s
) {
131 if (s
== NULL
) s
= "";
133 if (sz
> 32700) return -1;
135 if (fwrite(&sx
, sizeof(sx
), 1, fl
) != 1) return -1;
137 if (fwrite(s
, sz
, 1, fl
) != 1) return -1;
143 void hcache_init (void) {
144 HCACHEDATA cachedata
, *c
;
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
);
156 const char *record_type
;
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
)) { fprintf(stderr
, "invalid %s with record separator <%s>\n", hcachename
, record_type
? record_type
: "<null>"); goto bail
; }
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
);
174 l
= list_new(l
, s
, 1);
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
);
182 l
= list_new(l
, s
, 1);
185 if (!hashenter(hcachehash
, (HASHDATA
**)&c
)) {
186 fprintf(stderr
, "can't insert header cache item, bailing on %s\n", hcachename
);
189 c
->next
= hcachelist
;
193 if (DEBUG_HEADER
) printf("HCACHE: hcache read from file %s\n", hcachename
);
198 fprintf(stderr
, "HCACHE: invalid cache file: '%s'\n", hcachename
);
202 void hcache_done (void) {
205 int header_count
= 0;
206 const char *hcachename
;
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
;
216 for (c
= hcachelist
; c
!= NULL
; c
= c
->next
) {
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
;
241 if (writeStr(fl
, CACHE_RECORD_END
)) goto bail
;
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);
248 fprintf(stderr
, "HCACHE: can't write cache file: '%s'\n", hcachename
);
252 LIST
*hcache (TARGET
*t
, LIST
*hdrscan
) {
253 HCACHEDATA cachedata
, *c
= &cachedata
;
255 char _normalizedPath
[PATH_MAX
];
256 char *normalizedPath
= normalize_path(t
->boundname
, _normalizedPath
, sizeof(_normalizedPath
));
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
;
264 if (l1
->string
!= l2
->string
) {
272 if (DEBUG_HEADER
) printf("HCACHE: HDRSCAN out of date in cache for %s\n", t
->boundname
);
273 printf("HDRSCAN out of date for %s\n", t
->boundname
);
276 printf("\n cached: ");
277 list_print(c
->hdrscan
);
279 list_free(c
->includes
);
280 list_free(c
->hdrscan
);
284 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: using header cache for %s\n", t
->boundname
);
287 l
= list_copy(0, c
->includes
);
291 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: header cache out of date for %s\n", t
->boundname
);
292 list_free(c
->includes
);
293 list_free(c
->hdrscan
);
298 if (hashenter(hcachehash
, (HASHDATA
**)&c
)) {
299 c
->boundname
= newstr(c
->boundname
);
300 c
->next
= hcachelist
;
304 /* 'c' points at the cache entry; its out of date */
305 l
= headers1(t
->boundname
, hdrscan
);
308 c
->includes
= list_copy(0, l
);
309 c
->hdrscan
= list_copy(0, hdrscan
);