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
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.)
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
;
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
;
73 static struct hash
*hcachehash
= NULL
;
74 static hcachedata_t
*hcachelist
= NULL
;
76 static int queries
= 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
93 static const char *hcache_file_name (void) {
94 static const char *name
= NULL
;
96 LIST
*hcachevar
= var_get("HCACHEFILE");
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
);
110 * Return the maximum age a cache entry can have before it is purged from the cache.
112 static int hcache_maxage (void) {
114 LIST
*var
= var_get("HCACHEMAXAGE");
115 if (var
&& var
->string
) {
116 age
= atoi(var
->string
);
117 if (age
< 0) age
= 0;
125 * the returned value is as returned by newstr(), so it need not be freed
127 static const char *read_str (FILE *fl
) {
129 static char buf
[33000];
130 if (fread(&sz
, sizeof(sz
), 1, fl
) != 1) return NULL
;
131 if (sz
< 0 || sz
> 32700) return NULL
;
133 if (fread(buf
, sz
, 1, fl
) != 1) return NULL
;
143 static int write_str (FILE *fl
, const char *s
) {
146 if (s
== NULL
) s
= "";
148 if (sz
> 32700) return -1;
150 if (fwrite(&sx
, sizeof(sx
), 1, fl
) != 1) return -1;
151 if (sz
> 0 && fwrite(s
, sz
, 1, fl
) != 1) return -1;
156 void hcache_init (void) {
157 hcachedata_t cachedata
, *c
;
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
);
169 const char *record_type
;
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
; }
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
);
185 l
= list_new(l
, s
, 1);
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
);
192 l
= list_new(l
, s
, 1);
195 if (!hashenter(hcachehash
, (HASHDATA
**)&c
)) {
196 printf("can't insert header cache item, bailing on %s\n", hcachename
);
199 c
->next
= hcachelist
;
203 if (DEBUG_HEADER
) printf("HCACHE: hcache read from file %s\n", hcachename
);
208 printf("HCACHE: invalid cache file: '%s'\n", hcachename
);
212 void hcache_done (void) {
215 int header_count
= 0;
216 const char *hcachename
;
218 if (optShowHCacheInfo
) printf("hcache_done()\n");
219 if (!hcachehash
) return;
220 maxage
= hcache_maxage();
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; }
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
;
236 for (c
= hcachelist
; c
!= NULL
; c
= c
->next
) {
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
;
257 if (write_str(fl
, CACHE_RECORD_END
)) goto bail
;
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);
264 printf("HCACHE: can't write cache file: '%s'\n", hcachename
);
268 LIST
*hcache (TARGET
*t
, LIST
*hdrscan
) {
269 hcachedata_t cachedata
, *c
= &cachedata
;
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
);
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
;
279 if (l1
->string
!= l2
->string
) {
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);
291 list_print_ex(stdout, hdrscan, LPFLAG_NO_TRSPACE);
292 printf("\n cached: ");
293 list_print_ex(stdout, c->hdrscan, LPFLAG_NO_TRSPACE);
296 list_free(c
->includes
);
297 list_free(c
->hdrscan
);
301 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: using header cache for %s\n", t
->boundname
);
304 l
= list_copy(0, c
->includes
);
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
);
315 if (hashenter(hcachehash
, (HASHDATA
**)&c
)) {
316 c
->boundname
= newstr(c
->boundname
);
317 c
->next
= hcachelist
;
321 /* 'c' points at the cache entry; its out of date */
323 l
= headers1(t
->boundname
, hdrscan
);
326 c
->includes
= list_copy(0, l
);
327 c
->hdrscan
= list_copy(0, hdrscan
);