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
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.)
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
;
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
;
72 static struct hash
*hcachehash
= NULL
;
73 static hcachedata_t
*hcachelist
= NULL
;
75 static int queries
= 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
92 static const char *hcache_file_name (void) {
93 static const char *name
= NULL
;
95 LIST
*hcachevar
= var_get("HCACHEFILE");
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
);
109 * Return the maximum age a cache entry can have before it is purged from the cache.
111 static int hcache_maxage (void) {
113 LIST
*var
= var_get("HCACHEMAXAGE");
114 if (var
&& var
->string
) {
115 age
= atoi(var
->string
);
116 if (age
< 0) age
= 0;
124 * the returned value is as returned by newstr(), so it need not be freed
126 static const char *read_str (FILE *fl
) {
128 static char buf
[33000];
129 if (fread(&sz
, sizeof(sz
), 1, fl
) != 1) return NULL
;
130 if (/*sz < 0 ||*/ sz
> 32700) return NULL
;
132 if (fread(buf
, sz
, 1, fl
) != 1) return NULL
;
142 static int write_str (FILE *fl
, const char *s
) {
145 if (s
== NULL
) s
= "";
147 if (sz
> 32700) return -1;
149 if (fwrite(&sx
, sizeof(sx
), 1, fl
) != 1) return -1;
150 if (sz
> 0 && fwrite(s
, sz
, 1, fl
) != 1) return -1;
155 void hcache_init (void) {
156 hcachedata_t cachedata
, *c
;
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
);
168 const char *record_type
;
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
; }
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
);
184 l
= list_new(l
, s
, 1);
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
);
191 l
= list_new(l
, s
, 1);
194 if (!hashenter(hcachehash
, (HASHDATA
**)&c
)) {
195 printf("can't insert header cache item, bailing on %s\n", hcachename
);
198 c
->next
= hcachelist
;
202 if (DEBUG_HEADER
) printf("HCACHE: hcache read from file %s\n", hcachename
);
207 printf("HCACHE: invalid cache file: '%s'\n", hcachename
);
211 void hcache_done (void) {
214 int header_count
= 0;
215 const char *hcachename
;
217 if (optShowHCacheInfo
) printf("hcache_done()\n");
218 if (!hcachehash
) return;
219 maxage
= hcache_maxage();
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; }
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
;
235 for (c
= hcachelist
; c
!= NULL
; c
= c
->next
) {
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
;
256 if (write_str(fl
, CACHE_RECORD_END
)) goto bail
;
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);
263 printf("HCACHE: can't write cache file: '%s'\n", hcachename
);
267 LIST
*hcache (TARGET
*t
, LIST
*hdrscan
) {
268 hcachedata_t cachedata
, *c
= &cachedata
;
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
);
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
;
278 if (l1
->string
!= l2
->string
) {
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);
290 list_print_ex(stdout, hdrscan, LPFLAG_NO_TRSPACE);
291 printf("\n cached: ");
292 list_print_ex(stdout, c->hdrscan, LPFLAG_NO_TRSPACE);
295 list_free(c
->includes
);
296 list_free(c
->hdrscan
);
300 if (DEBUG_HEADER
|| optShowHCacheInfo
) printf("HCACHE: using header cache for %s\n", t
->boundname
);
303 l
= list_copy(0, c
->includes
);
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
);
314 if (hashenter(hcachehash
, (HASHDATA
**)&c
)) {
315 c
->boundname
= newstr(c
->boundname
);
316 c
->next
= hcachelist
;
320 /* 'c' points at the cache entry; its out of date */
322 l
= headers1(t
->boundname
, hdrscan
);
325 c
->includes
= list_copy(0, l
);
326 c
->hdrscan
= list_copy(0, hdrscan
);