source: trunk/src/kmk/incdep.c@ 1872

Last change on this file since 1872 was 1872, checked in by bird, 17 years ago

incdep: use an allocation cache for the records to reduce xmalloc and free calls.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 45.3 KB
Line 
1#ifdef CONFIG_WITH_INCLUDEDEP
2/* $Id: incdep.c 1872 2008-10-17 00:05:27Z bird $ */
3/** @file
4 * incdep - Simple dependency files.
5 */
6
7/*
8 * Copyright (c) 2006-2008 knut st. osmundsen <bird-src-spam@anduin.net>
9 *
10 * This file is part of kBuild.
11 *
12 * kBuild is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * kBuild is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with kBuild; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 *
26 */
27
28
29/*******************************************************************************
30* Header Files *
31*******************************************************************************/
32#ifdef __OS2__
33# define INCL_BASE
34# define INCL_ERRORS
35#endif
36
37#include "make.h"
38
39#if !defined(WINDOWS32) && !defined(__OS2__)
40# define HAVE_PTHREAD
41#endif
42
43#include <assert.h>
44
45#include <glob.h>
46
47#include "dep.h"
48#include "filedef.h"
49#include "job.h"
50#include "commands.h"
51#include "variable.h"
52#include "rule.h"
53#include "debug.h"
54#include "strcache2.h"
55
56#ifdef HAVE_FCNTL_H
57# include <fcntl.h>
58#else
59# include <sys/file.h>
60#endif
61
62#ifdef WINDOWS32
63# include <io.h>
64# include <process.h>
65# include <Windows.h>
66# define PARSE_IN_WORKER
67#endif
68
69#ifdef __OS2__
70# include <os2.h>
71# include <sys/fmutex.h>
72#endif
73
74#ifdef HAVE_PTHREAD
75# include <pthread.h>
76#endif
77
78#ifdef __APPLE__
79# include <malloc/malloc.h>
80# define PARSE_IN_WORKER
81#endif
82
83#if defined(__gnu_linux__) || defined(__linux__)
84# define PARSE_IN_WORKER
85#endif
86
87
88/*******************************************************************************
89* Structures and Typedefs *
90*******************************************************************************/
91struct incdep_variable_in_set
92{
93 struct incdep_variable_in_set *next;
94 /* the parameters */
95 struct strcache2_entry *name_entry; /* dep strcache - WRONG */
96 const char *value; /* xmalloc'ed */
97 unsigned int value_length;
98 int duplicate_value; /* 0 */
99 enum variable_origin origin;
100 int recursive;
101 struct variable_set *set;
102 const struct floc *flocp; /* NILF */
103};
104
105struct incdep_variable_def
106{
107 struct incdep_variable_def *next;
108 /* the parameters */
109 const struct floc *flocp; /* NILF */
110 struct strcache2_entry *name_entry; /* dep strcache - WRONG */
111 char *value; /* xmalloc'ed, free it */
112 unsigned int value_length;
113 enum variable_origin origin;
114 enum variable_flavor flavor;
115 int target_var;
116};
117
118struct incdep_recorded_files
119{
120 struct incdep_recorded_files *next;
121
122 /* the parameters */
123 struct strcache2_entry *filename_entry; /* dep strcache; converted to a nameseq record. */
124 const char *pattern; /* NULL */
125 const char *pattern_percent; /* NULL */
126 struct dep *deps; /* All the names are dep strcache entries. */
127 unsigned int cmds_started; /* 0 */
128 char *commands; /* NULL */
129 unsigned int commands_idx; /* 0 */
130 int two_colon; /* 0 */
131 const struct floc *flocp; /* NILF */
132};
133
134
135/* per dep file structure. */
136struct incdep
137{
138 struct incdep *next;
139 char *file_base;
140 char *file_end;
141
142 int worker_tid;
143#ifdef PARSE_IN_WORKER
144 unsigned int err_line_no;
145 const char *err_msg;
146
147 struct incdep_variable_in_set *recorded_variables_in_set_head;
148 struct incdep_variable_in_set *recorded_variables_in_set_tail;
149
150 struct incdep_variable_def *recorded_variable_defs_head;
151 struct incdep_variable_def *recorded_variable_defs_tail;
152
153 struct incdep_recorded_files *recorded_files_head;
154 struct incdep_recorded_files *recorded_files_tail;
155#endif
156
157 char name[1];
158};
159
160
161/*******************************************************************************
162* Global Variables *
163*******************************************************************************/
164
165/* mutex protecting the globals and an associated condition/event. */
166#ifdef HAVE_PTHREAD
167static pthread_mutex_t incdep_mtx;
168static pthread_cond_t incdep_cond_todo;
169static pthread_cond_t incdep_cond_done;
170
171#elif defined (WINDOWS32)
172static CRITICAL_SECTION incdep_mtx;
173static HANDLE incdep_hev_todo;
174static HANDLE incdep_hev_done;
175static int volatile incdep_hev_todo_waiters;
176static int volatile incdep_hev_done_waiters;
177
178#elif defined (__OS2__)
179static fmutex incdep_mtx;
180static HEV incdep_hev_todo;
181static HEV incdep_hev_done;
182static int volatile incdep_hev_todo_waiters;
183static int volatile incdep_hev_done_waiters;
184#endif
185
186/* flag indicating whether the threads, lock and event/condvars has
187 been initialized or not. */
188static int incdep_initialized;
189
190/* the list of files that needs reading. */
191static struct incdep * volatile incdep_head_todo;
192static struct incdep * volatile incdep_tail_todo;
193
194/* the number of files that are currently being read. */
195static int volatile incdep_num_reading;
196
197/* the list of files that have been read. */
198static struct incdep * volatile incdep_head_done;
199static struct incdep * volatile incdep_tail_done;
200
201
202/* The handles to the worker threads. */
203#ifdef HAVE_PTHREAD
204# define INCDEP_MAX_THREADS 1
205static pthread_t incdep_threads[INCDEP_MAX_THREADS];
206
207#elif defined (WINDOWS32)
208# define INCDEP_MAX_THREADS 2
209static HANDLE incdep_threads[INCDEP_MAX_THREADS];
210
211#elif defined (__OS2__)
212# define INCDEP_MAX_THREADS 2
213static TID incdep_threads[INCDEP_MAX_THREADS];
214#endif
215
216static struct alloccache incdep_rec_caches[INCDEP_MAX_THREADS];
217static struct alloccache incdep_dep_caches[INCDEP_MAX_THREADS];
218static struct strcache2 incdep_dep_strcaches[INCDEP_MAX_THREADS];
219static unsigned incdep_num_threads;
220
221/* flag indicating whether the worker threads should terminate or not. */
222static int volatile incdep_terminate;
223
224#ifdef __APPLE__
225/* malloc zone for the incdep threads. */
226static malloc_zone_t *incdep_zone;
227#endif
228
229
230/*******************************************************************************
231* Internal Functions *
232*******************************************************************************/
233static void incdep_flush_it (struct floc *);
234static void eval_include_dep_file (struct incdep *, struct floc *);
235
236
237/* xmalloc wrapper.
238 For working around multithreaded performance problems found on Darwin,
239 Linux (glibc), and possibly other systems. */
240static void *
241incdep_xmalloc (struct incdep *cur, size_t size)
242{
243 void *ptr;
244
245#ifdef __APPLE__
246 if (cur && cur->worker_tid != -1)
247 {
248 ptr = malloc_zone_malloc (incdep_zone, size);
249 if (!ptr)
250 fatal (NILF, _("virtual memory exhausted"));
251 }
252 else
253 ptr = xmalloc (size);
254#else
255 ptr = xmalloc (size);
256#endif
257
258 (void)cur;
259 return ptr;
260}
261
262#if 0
263/* memset(malloc(sz),'\0',sz) wrapper. */
264static void *
265incdep_xcalloc (struct incdep *cur, size_t size)
266{
267 void *ptr;
268
269#ifdef __APPLE__
270 if (cur && cur->worker_tid != -1)
271 ptr = malloc_zone_calloc (incdep_zone, size, 1);
272 else
273 ptr = calloc (size, 1);
274#else
275 ptr = calloc (size, 1);
276#endif
277 if (!ptr)
278 fatal (NILF, _("virtual memory exhausted"));
279
280 (void)cur;
281 return ptr;
282}
283#endif /* unused */
284
285/* free wrapper */
286static void
287incdep_xfree (struct incdep *cur, void *ptr)
288{
289 /* free() *must* work for the allocation hacks above because
290 of free_dep_chain. */
291 free (ptr);
292 (void)cur;
293}
294
295/* alloc a dep structure. These are allocated in bunches to save time. */
296struct dep *
297incdep_alloc_dep (struct incdep *cur)
298{
299 struct alloccache *cache;
300 if (cur->worker_tid != -1)
301 cache = &incdep_dep_caches[cur->worker_tid];
302 else
303 cache = &dep_cache;
304 return alloccache_calloc (cache);
305}
306
307/* allocate a record. */
308static void *
309incdep_alloc_rec (struct incdep *cur)
310{
311 return alloccache_alloc (&incdep_rec_caches[cur->worker_tid]);
312}
313
314
315/* grow a cache. */
316static void *
317incdep_cache_allocator (void *thrd, unsigned int size)
318{
319 (void)thrd;
320#ifdef __APPLE__
321 return malloc_zone_malloc (incdep_zone, size);
322#else
323 return xmalloc (size);
324#endif
325}
326
327/* term a cache. */
328static void
329incdep_cache_deallocator (void *thrd, void *ptr, unsigned int size)
330{
331 (void)thrd;
332 (void)size;
333 free (ptr);
334}
335
336/* acquires the lock */
337void
338incdep_lock(void)
339{
340#ifdef HAVE_PTHREAD
341 pthread_mutex_lock (&incdep_mtx);
342#elif defined (WINDOWS32)
343 EnterCriticalSection (&incdep_mtx);
344#elif defined (__OS2__)
345 _fmutex_request (&incdep_mtx, 0)
346#endif
347}
348
349/* releases the lock */
350void
351incdep_unlock(void)
352{
353#ifdef HAVE_PTHREAD
354 pthread_mutex_unlock (&incdep_mtx);
355#elif defined(WINDOWS32)
356 LeaveCriticalSection (&incdep_mtx);
357#elif defined(__OS2__)
358 _fmutex_release (&incdep_mtx)
359#endif
360}
361
362/* signals the main thread that there is stuff todo. caller owns the lock. */
363static void
364incdep_signal_done (void)
365{
366#ifdef HAVE_PTHREAD
367 pthread_cond_broadcast (&incdep_cond_done);
368#elif defined (WINDOWS32)
369 if (incdep_hev_done_waiters)
370 SetEvent (incdep_hev_done);
371#elif defined (__OS2__)
372 if (incdep_hev_done_waiters)
373 DosPostEventSem (incdep_hev_done);
374#endif
375}
376
377/* waits for a reader to finish reading. caller owns the lock. */
378static void
379incdep_wait_done (void)
380{
381#ifdef HAVE_PTHREAD
382 pthread_cond_wait (&incdep_cond_done, &incdep_mtx);
383
384#elif defined (WINDOWS32)
385 ResetEvent (incdep_hev_done);
386 incdep_hev_done_waiters++;
387 incdep_unlock ();
388 WaitForSingleObject (incdep_hev_done, INFINITE);
389 incdep_lock ();
390 incdep_hev_done_waiters--;
391
392#elif defined (__OS2__)
393 ULONG ulIgnore;
394 DosResetEventSem (incdep_hev_done, &ulIgnore);
395 incdep_hev_done_waiters++;
396 incdep_unlock ();
397 DosWaitEventSem (incdep_hev_done, SEM_INDEFINITE_WAIT);
398 incdep_lock ();
399 incdep_hev_done_waiters--;
400#endif
401}
402
403/* signals the worker threads. caller owns the lock. */
404static void
405incdep_signal_todo (void)
406{
407#ifdef HAVE_PTHREAD
408 pthread_cond_broadcast (&incdep_cond_todo);
409#elif defined(WINDOWS32)
410 if (incdep_hev_todo_waiters)
411 SetEvent (incdep_hev_todo);
412#elif defined(__OS2__)
413 if (incdep_hev_todo_waiters)
414 DosPostEventSem (incdep_hev_todo);
415#endif
416}
417
418/* waits for stuff to arrive in the todo list. caller owns the lock. */
419static void
420incdep_wait_todo (void)
421{
422#ifdef HAVE_PTHREAD
423 pthread_cond_wait (&incdep_cond_todo, &incdep_mtx);
424
425#elif defined (WINDOWS32)
426 ResetEvent (incdep_hev_todo);
427 incdep_hev_todo_waiters++;
428 incdep_unlock ();
429 WaitForSingleObject (incdep_hev_todo, INFINITE);
430 incdep_lock ();
431 incdep_hev_todo_waiters--;
432
433#elif defined (__OS2__)
434 ULONG ulIgnore;
435 DosResetEventSem (incdep_hev_todo, &ulIgnore);
436 incdep_hev_todo_waiters++;
437 incdep_unlock ();
438 DosWaitEventSem (incdep_hev_todo, SEM_INDEFINITE_WAIT);
439 incdep_lock ();
440 incdep_hev_todo_waiters--;
441#endif
442}
443
444/* Reads a dep file into memory. */
445static int
446incdep_read_file (struct incdep *cur, struct floc *f)
447{
448 int fd;
449 struct stat st;
450
451 errno = 0;
452#ifdef O_BINARY
453 fd = open (cur->name, O_RDONLY | O_BINARY, 0);
454#else
455 fd = open (cur->name, O_RDONLY, 0);
456#endif
457 if (fd < 0)
458 {
459 /* ignore non-existing dependency files. */
460 int err = errno;
461 if (err == ENOENT || stat (cur->name, &st) != 0)
462 return 1;
463 error (f, "%s: %s", cur->name, strerror (err));
464 return -1;
465 }
466 if (!fstat (fd, &st))
467 {
468 cur->file_base = incdep_xmalloc (cur, st.st_size + 1);
469 if (read (fd, cur->file_base, st.st_size) == st.st_size)
470 {
471 close (fd);
472 cur->file_end = cur->file_base + st.st_size;
473 cur->file_base[st.st_size] = '\0';
474 return 0;
475 }
476
477 /* bail out */
478
479 error (f, "%s: read: %s", cur->name, strerror (errno));
480 incdep_xfree (cur, cur->file_base);
481 }
482 else
483 error (f, "%s: fstat: %s", cur->name, strerror (errno));
484
485 close (fd);
486 cur->file_base = cur->file_end = NULL;
487 return -1;
488}
489
490/* Free the incdep structure. */
491static void
492incdep_freeit (struct incdep *cur)
493{
494#ifdef PARSE_IN_WORKER
495 assert (!cur->recorded_variables_in_set_head);
496 assert (!cur->recorded_variable_defs_head);
497 assert (!cur->recorded_files_head);
498#endif
499
500 incdep_xfree (cur, cur->file_base);
501 free (cur);
502}
503
504/* A worker thread. */
505void
506incdep_worker (int thrd)
507{
508 incdep_lock ();
509
510 while (!incdep_terminate)
511 {
512 /* get job from the todo list. */
513
514 struct incdep *cur = incdep_head_todo;
515 if (!cur)
516 {
517 incdep_wait_todo ();
518 continue;
519 }
520 if (cur->next)
521 incdep_head_todo = cur->next;
522 else
523 incdep_head_todo = incdep_tail_todo = NULL;
524 incdep_num_reading++;
525
526 /* read the file. */
527
528 incdep_unlock ();
529 cur->worker_tid = thrd;
530
531 incdep_read_file (cur, NILF);
532#ifdef PARSE_IN_WORKER
533 eval_include_dep_file (cur, NILF);
534#endif
535
536 cur->worker_tid = -1;
537 incdep_lock ();
538
539 /* insert finished job into the done list. */
540
541 incdep_num_reading--;
542 cur->next = NULL;
543 if (incdep_tail_done)
544 incdep_tail_done->next = cur;
545 else
546 incdep_head_done = cur;
547 incdep_tail_done = cur;
548
549 incdep_signal_done ();
550 }
551
552 incdep_unlock ();
553}
554
555/* Thread library specific thread functions wrapping incdep_wroker. */
556#ifdef HAVE_PTHREAD
557static void *
558incdep_worker_pthread (void *thrd)
559{
560 incdep_worker ((size_t)thrd);
561 return NULL;
562}
563
564#elif defined (WINDOWS32)
565static unsigned __stdcall
566incdep_worker_windows (void *thrd)
567{
568 incdep_worker ((size_t)thrd);
569 return 0;
570}
571
572#elif defined (__OS2__)
573static void
574incdep_worker_os2 (void *thrd)
575{
576 incdep_worker ((size_t)thrd);
577}
578#endif
579
580/* Creates the the worker threads. */
581static void
582incdep_init (struct floc *f)
583{
584 unsigned i;
585#ifdef HAVE_PTHREAD
586 int rc;
587 pthread_attr_t attr;
588
589#elif defined (WINDOWS32)
590 unsigned tid;
591 uintptr_t hThread;
592
593#elif defined (__OS2__)
594 int rc;
595 int tid;
596#endif
597
598 /* heap hacks */
599
600#ifdef __APPLE__
601 incdep_zone = malloc_create_zone (0, 0);
602 if (!incdep_zone)
603 incdep_zone = malloc_default_zone ();
604#endif
605
606
607 /* create the mutex and two condition variables / event objects. */
608
609#ifdef HAVE_PTHREAD
610 rc = pthread_mutex_init (&incdep_mtx, NULL);
611 if (rc)
612 fatal (f, _("pthread_mutex_init failed: err=%d"), rc);
613 rc = pthread_cond_init (&incdep_cond_todo, NULL);
614 if (rc)
615 fatal (f, _("pthread_cond_init failed: err=%d"), rc);
616 rc = pthread_cond_init (&incdep_cond_done, NULL);
617 if (rc)
618 fatal (f, _("pthread_cond_init failed: err=%d"), rc);
619
620#elif defined (WINDOWS32)
621 InitializeCriticalSection (&incdep_mtx);
622 incdep_hev_todo = CreateEvent (NULL, TRUE /*bManualReset*/, FALSE /*bInitialState*/, NULL);
623 if (!incdep_hev_todo)
624 fatal (f, _("CreateEvent failed: err=%d"), GetLastError());
625 incdep_hev_done = CreateEvent (NULL, TRUE /*bManualReset*/, FALSE /*bInitialState*/, NULL);
626 if (!incdep_hev_done)
627 fatal (f, _("CreateEvent failed: err=%d"), GetLastError());
628 incdep_hev_todo_waiters = 0;
629 incdep_hev_done_waiters = 0;
630
631#elif defined (__OS2__)
632 _fmutex_create (&incdep_mtx, 0)
633 rc = DosCreateEventSem (NULL, &incdep_hev_todo, 0, FALSE);
634 if (rc)
635 fatal (f, _("DosCreateEventSem failed: rc=%d"), rc);
636 rc = DosCreateEventSem (NULL, &incdep_hev_done, 0, FALSE);
637 if (rc)
638 fatal (f, _("DosCreateEventSem failed: rc=%d"), rc);
639 incdep_hev_todo_waiters = 0;
640 incdep_hev_done_waiters = 0;
641#endif
642
643 /* create the worker threads and associated per thread data. */
644
645 incdep_terminate = 0;
646 incdep_num_threads = sizeof (incdep_threads) / sizeof (incdep_threads[0]);
647 if (incdep_num_threads + 1 > job_slots)
648 incdep_num_threads = job_slots <= 1 ? 1 : job_slots - 1;
649 for (i = 0; i < incdep_num_threads; i++)
650 {
651 /* init caches */
652 unsigned rec_size = sizeof (struct incdep_variable_in_set);
653 if (rec_size < sizeof (struct incdep_variable_def))
654 rec_size = sizeof (struct incdep_variable_def);
655 if (rec_size < sizeof (struct incdep_recorded_files))
656 rec_size = sizeof (struct incdep_recorded_files);
657 alloccache_init (&incdep_rec_caches[i], rec_size, "incdep rec",
658 incdep_cache_allocator, (void *)(size_t)i);
659 alloccache_init (&incdep_dep_caches[i], sizeof(struct dep), "incdep dep",
660 incdep_cache_allocator, (void *)(size_t)i);
661 strcache2_init(&incdep_dep_strcaches[i],
662 "incdep dep", /* name */
663 65536, /* hash size */
664 0, /* default segment size*/
665#ifdef HAVE_CASE_INSENSITIVE_FS
666 1, /* case insensitive */
667#else
668 0, /* case insensitive */
669#endif
670 0); /* thread safe */
671
672 /* create the thread. */
673#ifdef HAVE_PTHREAD
674 rc = pthread_attr_init (&attr);
675 if (rc)
676 fatal (f, _("pthread_attr_init failed: err=%d"), rc);
677 /*rc = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE); */
678 rc = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
679 if (rc)
680 fatal (f, _("pthread_attr_setdetachstate failed: err=%d"), rc);
681 rc = pthread_create(&incdep_threads[i], &attr,
682 incdep_worker_pthread, (void *)(size_t)i);
683 if (rc)
684 fatal (f, _("pthread_mutex_init failed: err=%d"), rc);
685 pthread_attr_destroy (&attr);
686
687#elif defined (WINDOWS32)
688 tid = 0;
689 hThread = _beginthreadex (NULL, 128*1024, incdep_worker_windows,
690 (void *)i, 0, &tid);
691 if (hThread == 0 || hThread == ~(uintptr_t)0)
692 fatal (f, _("_beginthreadex failed: err=%d"), errno);
693 incdep_threads[i] = (HANDLE)hThread;
694
695#elif defined (__OS2__)
696 tid = _beginthread (incdep_worker_os2, NULL, 128*1024, (void *)i);
697 if (tid <= 0)
698 fatal (f, _("_beginthread failed: err=%d"), errno);
699 incdep_threads[i] = tid;
700#endif
701 }
702
703 incdep_initialized = 1;
704}
705
706/* Flushes outstanding work and terminates the worker threads.
707 This is called from snap_deps(). */
708void
709incdep_flush_and_term (void)
710{
711 unsigned i;
712
713 if (!incdep_initialized)
714 return;
715
716 /* flush any out standing work */
717
718 incdep_flush_it (NILF);
719
720 /* tell the threads to terminate */
721
722 incdep_lock ();
723 incdep_terminate = 1;
724 incdep_signal_todo ();
725 incdep_unlock ();
726
727 /* wait for the threads to quit */
728
729 for (i = 0; i < incdep_num_threads; i++)
730 {
731 /* more later? */
732
733 /* terminate or join up the allocation caches. */
734 alloccache_term (&incdep_rec_caches[i], incdep_cache_deallocator, (void *)(size_t)i);
735 alloccache_join (&dep_cache, &incdep_dep_caches[i]);
736 strcache2_term (&incdep_dep_strcaches[i]);
737 }
738 incdep_num_threads = 0;
739
740 /* destroy the lock and condition variables / event objects. */
741
742 /* later */
743
744 incdep_initialized = 0;
745}
746
747#ifdef PARSE_IN_WORKER
748/* Flushes a strcache entry returning the actual string cache entry.
749 The input is freed! */
750static const char *
751incdep_flush_strcache_entry (struct strcache2_entry *entry)
752{
753 if (!entry->user)
754 entry->user = (void *)strcache_add_prehashed ((const char *)(entry + 1),
755 entry->length,
756 entry->hash1,
757 entry->hash2);
758 return (const char *)entry->user;
759}
760
761/* Flushes the recorded instructions. */
762static void
763incdep_flush_recorded_instructions (struct incdep *cur)
764{
765 struct incdep_variable_in_set *rec_vis;
766 struct incdep_variable_def *rec_vd;
767 struct incdep_recorded_files *rec_f;
768
769 /* define_variable_in_set */
770
771 rec_vis = cur->recorded_variables_in_set_head;
772 cur->recorded_variables_in_set_head = cur->recorded_variables_in_set_tail = NULL;
773 if (rec_vis)
774 do
775 {
776 unsigned int name_length = rec_vis->name_entry->length;
777 define_variable_in_set (incdep_flush_strcache_entry (rec_vis->name_entry),
778 name_length,
779 rec_vis->value,
780 rec_vis->value_length,
781 rec_vis->duplicate_value,
782 rec_vis->origin,
783 rec_vis->recursive,
784 rec_vis->set,
785 rec_vis->flocp);
786 rec_vis = rec_vis->next;
787 }
788 while (rec_vis);
789
790 /* do_variable_definition */
791
792 rec_vd = cur->recorded_variable_defs_head;
793 cur->recorded_variable_defs_head = cur->recorded_variable_defs_tail = NULL;
794 if (rec_vd)
795 do
796 {
797 do_variable_definition_2 (rec_vd->flocp,
798 incdep_flush_strcache_entry (rec_vd->name_entry),
799 rec_vd->value,
800 rec_vd->value_length,
801 0,
802 rec_vd->value,
803 rec_vd->origin,
804 rec_vd->flavor,
805 rec_vd->target_var);
806 rec_vd = rec_vd->next;
807 }
808 while (rec_vd);
809
810 /* record_files */
811
812 rec_f = cur->recorded_files_head;
813 cur->recorded_files_head = cur->recorded_files_tail = NULL;
814 if (rec_f)
815 do
816 {
817 struct dep *dep;
818 struct nameseq *filenames;
819
820 for (dep = rec_f->deps; dep; dep = dep->next)
821 dep->name = incdep_flush_strcache_entry ((struct strcache2_entry *)dep->name);
822
823 filenames = (struct nameseq *) alloccache_alloc (&nameseq_cache);
824 filenames->next = 0;
825 filenames->name = incdep_flush_strcache_entry (rec_f->filename_entry);
826
827 record_files (filenames,
828 rec_f->pattern,
829 rec_f->pattern_percent,
830 rec_f->deps,
831 rec_f->cmds_started,
832 rec_f->commands,
833 rec_f->commands_idx,
834 rec_f->two_colon,
835 rec_f->flocp);
836
837 rec_f = rec_f->next;
838 }
839 while (rec_f);
840}
841#endif /* PARSE_IN_WORKER */
842
843/* Record / issue a warning about a misformed dep file. */
844static void
845incdep_warn (struct incdep *cur, unsigned int line_no, const char *msg)
846{
847 if (cur->worker_tid == -1)
848 error (NILF, "%s(%d): %s", cur->name, line_no, msg);
849#ifdef PARSE_IN_WORKER
850 else
851 {
852 cur->err_line_no = line_no;
853 cur->err_msg = msg;
854 }
855#endif
856}
857
858/* Record / execute a strcache add. */
859static const char *
860incdep_record_strcache (struct incdep *cur, const char *str, int len)
861{
862 const char *ret;
863 if (cur->worker_tid == -1)
864 {
865 /* Make sure the string is terminated before we hand it to
866 strcache_add_len so it does have to make a temporary copy
867 of it on the stack. */
868 char ch = str[len];
869 ((char *)str)[len] = '\0';
870 ret = strcache_add_len (str, len);
871 ((char *)str)[len] = ch;
872 }
873 else
874 {
875 /* Add it out the strcache of the thread. */
876 ret = strcache2_add (&incdep_dep_strcaches[cur->worker_tid], str, len);
877 ret = (const char *)strcache2_get_entry(&incdep_dep_strcaches[cur->worker_tid], ret);
878 }
879 return ret;
880}
881
882/* Record / perform a variable definition in a set.
883 The NAME is in the string cache.
884 The VALUE is on the heap.
885 The DUPLICATE_VALUE is always 0. */
886static void
887incdep_record_variable_in_set (struct incdep *cur,
888 const char *name, unsigned int name_length,
889 const char *value,
890 unsigned int value_length,
891 int duplicate_value,
892 enum variable_origin origin,
893 int recursive,
894 struct variable_set *set,
895 const struct floc *flocp)
896{
897 assert (!duplicate_value);
898 if (cur->worker_tid == -1)
899 define_variable_in_set (name, name_length, value, value_length,
900 duplicate_value, origin, recursive, set, flocp);
901#ifdef PARSE_IN_WORKER
902 else
903 {
904 struct incdep_variable_in_set *rec =
905 (struct incdep_variable_in_set *)incdep_alloc_rec (cur);
906 rec->name_entry = (struct strcache2_entry *)name;
907 rec->value = value;
908 rec->value_length = value_length;
909 rec->duplicate_value = duplicate_value;
910 rec->origin = origin;
911 rec->recursive = recursive;
912 rec->set = set;
913 rec->flocp = flocp;
914
915 rec->next = NULL;
916 if (cur->recorded_variables_in_set_tail)
917 cur->recorded_variables_in_set_tail->next = rec;
918 else
919 cur->recorded_variables_in_set_head = rec;
920 cur->recorded_variables_in_set_tail = rec;
921 }
922#endif
923}
924
925/* Record / perform a variable definition. The VALUE should be disposed of. */
926static void
927incdep_record_variable_def (struct incdep *cur,
928 const struct floc *flocp,
929 const char *name,
930 unsigned int name_length,
931 char *value,
932 unsigned int value_length,
933 enum variable_origin origin,
934 enum variable_flavor flavor,
935 int target_var)
936{
937 if (cur->worker_tid == -1)
938 do_variable_definition_2 (flocp, name, value, value_length, 0, value,
939 origin, flavor, target_var);
940#ifdef PARSE_IN_WORKER
941 else
942 {
943 struct incdep_variable_def *rec =
944 (struct incdep_variable_def *)incdep_alloc_rec (cur);
945 rec->flocp = flocp;
946 rec->name_entry = (struct strcache2_entry *)name;
947 rec->value = value;
948 rec->value_length = value_length;
949 rec->origin = origin;
950 rec->flavor = flavor;
951 rec->target_var = target_var;
952
953 rec->next = NULL;
954 if (cur->recorded_variable_defs_tail)
955 cur->recorded_variable_defs_tail->next = rec;
956 else
957 cur->recorded_variable_defs_head = rec;
958 cur->recorded_variable_defs_tail = rec;
959 }
960#else
961 (void)name_length;
962#endif
963}
964
965/* Record files.*/
966static void
967incdep_record_files (struct incdep *cur,
968 const char *filename, const char *pattern,
969 const char *pattern_percent, struct dep *deps,
970 unsigned int cmds_started, char *commands,
971 unsigned int commands_idx, int two_colon,
972 const struct floc *flocp)
973{
974 if (cur->worker_tid == -1)
975 {
976 struct nameseq *filenames = (struct nameseq *) alloccache_alloc (&nameseq_cache);
977 filenames->next = 0;
978 filenames->name = filename;
979 record_files (filenames, pattern, pattern_percent, deps, cmds_started,
980 commands, commands_idx, two_colon, flocp);
981 }
982#ifdef PARSE_IN_WORKER
983 else
984 {
985 struct incdep_recorded_files *rec =
986 (struct incdep_recorded_files *) incdep_alloc_rec (cur);
987
988 rec->filename_entry = (struct strcache2_entry *)filename;
989 rec->pattern = pattern;
990 rec->pattern_percent = pattern_percent;
991 rec->deps = deps;
992 rec->cmds_started = cmds_started;
993 rec->commands = commands;
994 rec->commands_idx = commands_idx;
995 rec->two_colon = two_colon;
996 rec->flocp = flocp;
997
998 rec->next = NULL;
999 if (cur->recorded_files_tail)
1000 cur->recorded_files_tail->next = rec;
1001 else
1002 cur->recorded_files_head = rec;
1003 cur->recorded_files_tail = rec;
1004 }
1005#endif
1006}
1007
1008
1009/* no nonsense dependency file including.
1010
1011 Because nobody wants bogus dependency files to break their incremental
1012 builds with hard to comprehend error messages, this function does not
1013 use the normal eval routine but does all the parsing itself. This isn't,
1014 as much work as it sounds, because the necessary feature set is very
1015 limited.
1016
1017 eval_include_dep_file groks:
1018
1019 define var
1020 endef
1021
1022 var [|:|?|>]= value [\]
1023
1024 [\]
1025 file: [deps] [\]
1026
1027 */
1028static void
1029eval_include_dep_file (struct incdep *curdep, struct floc *f)
1030{
1031 unsigned line_no = 1;
1032 const char *file_end = curdep->file_end;
1033 const char *cur = curdep->file_base;
1034 const char *endp;
1035
1036 /* if no file data, just return immediately. */
1037 if (!cur)
1038 return;
1039
1040 /* now parse the file. */
1041 while (cur < file_end)
1042 {
1043 /* skip empty lines */
1044 while (cur < file_end && isspace ((unsigned char)*cur) && *cur != '\n')
1045 ++cur;
1046 if (cur >= file_end)
1047 break;
1048 if (*cur == '#')
1049 {
1050 cur = memchr (cur, '\n', file_end - cur);
1051 if (!cur)
1052 break;
1053 }
1054 if (*cur == '\\')
1055 {
1056 unsigned eol_len = (file_end - cur > 1 && cur[1] == '\n') ? 2
1057 : (file_end - cur > 2 && cur[1] == '\r' && cur[2] == '\n') ? 3
1058 : (file_end - cur == 1) ? 1 : 0;
1059 if (eol_len)
1060 {
1061 cur += eol_len;
1062 line_no++;
1063 continue;
1064 }
1065 }
1066 if (*cur == '\n')
1067 {
1068 cur++;
1069 line_no++;
1070 continue;
1071 }
1072
1073 /* define var
1074 ...
1075 endef */
1076 if (strneq (cur, "define ", 7))
1077 {
1078 const char *var;
1079 unsigned var_len;
1080 const char *value_start;
1081 const char *value_end;
1082 char *value;
1083 unsigned value_len;
1084 int found_endef = 0;
1085
1086 /* extract the variable name. */
1087 cur += 7;
1088 while (isblank ((unsigned char)*cur))
1089 ++cur;
1090 value_start = endp = memchr (cur, '\n', file_end - cur);
1091 if (!endp)
1092 endp = cur;
1093 while (endp > cur && isspace ((unsigned char)endp[-1]))
1094 --endp;
1095 var_len = endp - cur;
1096 if (!var_len)
1097 {
1098 incdep_warn (curdep, line_no, "bogus define statement.");
1099 break;
1100 }
1101 var = incdep_record_strcache (curdep, cur, var_len);
1102
1103 /* find the end of the variable. */
1104 cur = value_end = value_start = value_start + 1;
1105 ++line_no;
1106 while (cur < file_end)
1107 {
1108 /* check for endef, don't bother with skipping leading spaces. */
1109 if ( file_end - cur >= 5
1110 && strneq (cur, "endef", 5))
1111 {
1112 endp = cur + 5;
1113 while (endp < file_end && isspace ((unsigned char)*endp) && *endp != '\n')
1114 endp++;
1115 if (endp >= file_end || *endp == '\n')
1116 {
1117 found_endef = 1;
1118 cur = endp >= file_end ? file_end : endp + 1;
1119 break;
1120 }
1121 }
1122
1123 /* skip a line ahead. */
1124 cur = value_end = memchr (cur, '\n', file_end - cur);
1125 if (cur != NULL)
1126 ++cur;
1127 else
1128 cur = value_end = file_end;
1129 ++line_no;
1130 }
1131
1132 if (!found_endef)
1133 {
1134 incdep_warn (curdep, line_no, "missing endef, dropping the rest of the file.");
1135 break;
1136 }
1137 value_len = value_end - value_start;
1138 if (memchr (value_start, '\0', value_len))
1139 {
1140 incdep_warn (curdep, line_no, "'\\0' in define, dropping the rest of the file.");
1141 break;
1142 }
1143
1144 /* make a copy of the value, converting \r\n to \n, and define it. */
1145 value = incdep_xmalloc (curdep, value_len + 1);
1146 endp = memchr (value_start, '\r', value_len);
1147 if (endp)
1148 {
1149 const char *src = value_start;
1150 char *dst = value;
1151 for (;;)
1152 {
1153 size_t len = endp - src;
1154 memcpy (dst, src, len);
1155 dst += len;
1156 src = endp;
1157 if (src + 1 < file_end && src[1] == '\n')
1158 src++; /* skip the '\r' */
1159 if (src >= value_end)
1160 break;
1161 endp = memchr (endp + 1, '\r', src - value_end);
1162 if (!endp)
1163 endp = value_end;
1164 }
1165 value_len = dst - value;
1166 }
1167 else
1168 memcpy (value, value_start, value_len);
1169 value [value_len] = '\0';
1170
1171 incdep_record_variable_in_set (curdep,
1172 var, var_len, value, value_len,
1173 0 /* don't duplicate */, o_file,
1174 0 /* defines are recursive but this is faster */,
1175 NULL /* global set */, f);
1176 }
1177
1178 /* file: deps
1179 OR
1180 variable [:]= value */
1181 else
1182 {
1183 const char *colonp;
1184 const char *equalp;
1185
1186 /* Look for a colon and an equal sign, optimize for colon.
1187 Only one file is support and the colon / equal must be on
1188 the same line. */
1189 colonp = memchr (cur, ':', file_end - cur);
1190#ifdef HAVE_DOS_PATHS
1191 while ( colonp
1192 && colonp + 1 < file_end
1193 && (colonp[1] == '/' || colonp[1] == '\\')
1194 && colonp > cur
1195 && isalpha ((unsigned char)colonp[-1])
1196 && ( colonp == cur + 1
1197 || strchr (" \t(", colonp[-2]) != 0))
1198 colonp = memchr (colonp + 1, ':', file_end - (colonp + 1));
1199#endif
1200 endp = NULL;
1201 if ( !colonp
1202 || (endp = memchr (cur, '\n', colonp - cur)))
1203 {
1204 colonp = NULL;
1205 equalp = memchr (cur, '=', (endp ? endp : file_end) - cur);
1206 if ( !equalp
1207 || (!endp && memchr (cur, '\n', equalp - cur)))
1208 {
1209 incdep_warn (curdep, line_no, "no colon.");
1210 break;
1211 }
1212 }
1213 else
1214 equalp = memchr (cur, '=', (colonp + 2 <= file_end
1215 ? colonp + 2 : file_end) - cur);
1216 if (equalp)
1217 {
1218 /* An assignment of some sort. */
1219 const char *var;
1220 unsigned var_len;
1221 const char *value_start;
1222 const char *value_end;
1223 char *value;
1224 unsigned value_len;
1225 unsigned multi_line = 0;
1226 enum variable_flavor flavor;
1227
1228 /* figure the flavor first. */
1229 flavor = f_recursive;
1230 if (equalp > cur)
1231 {
1232 if (equalp[-1] == ':')
1233 flavor = f_simple;
1234 else if (equalp[-1] == '?')
1235 flavor = f_conditional;
1236 else if (equalp[-1] == '+')
1237 flavor = f_append;
1238 else if (equalp[-1] == '>')
1239 flavor = f_prepend;
1240 }
1241
1242 /* extract the variable name. */
1243 endp = flavor == f_recursive ? equalp : equalp - 1;
1244 while (endp > cur && isblank ((unsigned char)endp[-1]))
1245 --endp;
1246 var_len = endp - cur;
1247 if (!var_len)
1248 {
1249 incdep_warn (curdep, line_no, "empty variable. (includedep)");
1250 break;
1251 }
1252 if ( memchr (cur, '$', var_len)
1253 || memchr (cur, ' ', var_len)
1254 || memchr (cur, '\t', var_len))
1255 {
1256 incdep_warn (curdep, line_no, "fancy variable name. (includedep)");
1257 break;
1258 }
1259 var = incdep_record_strcache (curdep, cur, var_len);
1260
1261 /* find the start of the value. */
1262 cur = equalp + 1;
1263 while (cur < file_end && isblank ((unsigned char)*cur))
1264 cur++;
1265 value_start = cur;
1266
1267 /* find the end of the value / line (this isn't 101% correct). */
1268 value_end = cur;
1269 while (cur < file_end)
1270 {
1271 endp = value_end = memchr (cur, '\n', file_end - cur);
1272 if (!value_end)
1273 value_end = file_end;
1274 if (value_end - 1 >= cur && value_end[-1] == '\r')
1275 --value_end;
1276 if (value_end - 1 < cur || value_end[-1] != '\\')
1277 {
1278 cur = endp ? endp + 1 : file_end;
1279 break;
1280 }
1281 --value_end;
1282 if (value_end - 1 >= cur && value_end[-1] == '\\')
1283 {
1284 incdep_warn (curdep, line_no, "fancy escaping! (includedep)");
1285 cur = NULL;
1286 break;
1287 }
1288 if (!endp)
1289 {
1290 cur = file_end;
1291 break;
1292 }
1293
1294 cur = endp + 1;
1295 ++multi_line;
1296 ++line_no;
1297 }
1298 if (!cur)
1299 break;
1300 ++line_no;
1301
1302 /* make a copy of the value, converting \r\n to \n, and define it. */
1303 value_len = value_end - value_start;
1304 value = incdep_xmalloc (curdep, value_len + 1);
1305 if (!multi_line)
1306 memcpy (value, value_start, value_len);
1307 else
1308 {
1309 /* unescape it */
1310 const char *src = value_start;
1311 char *dst = value;
1312 while (src < value_end)
1313 {
1314 const char *nextp;
1315
1316 endp = memchr (src, '\n', value_end - src);
1317 if (!endp)
1318 nextp = endp = value_end;
1319 else
1320 nextp = endp + 1;
1321 if (endp > src && endp[-1] == '\r')
1322 --endp;
1323 if (endp > src && endp[-1] == '\\')
1324 --endp;
1325
1326 if (src != value_start)
1327 *dst++ = ' ';
1328 memcpy (dst, src, endp - src);
1329 dst += endp - src;
1330 src = nextp;
1331 }
1332 value_len = dst - value;
1333 }
1334 value [value_len] = '\0';
1335
1336 /* do the definition */
1337 if (flavor == f_recursive
1338 || ( flavor == f_simple
1339 && !memchr (value, '$', value_len)))
1340 incdep_record_variable_in_set (curdep,
1341 var, var_len, value, value_len,
1342 0 /* don't duplicate */, o_file,
1343 flavor == f_recursive /* recursive */,
1344 NULL /* global set */, f);
1345 else
1346 incdep_record_variable_def (curdep,
1347 f, var, var_len, value, value_len,
1348 o_file, flavor, 0 /* not target var */);
1349 }
1350 else
1351 {
1352 /* file: dependencies */
1353
1354 const char *filename;
1355 struct dep *deps = 0;
1356 struct dep **nextdep = &deps;
1357 struct dep *dep;
1358
1359 /* extract the filename, ASSUME a single one. */
1360 endp = colonp;
1361 while (endp > cur && isblank ((unsigned char)endp[-1]))
1362 --endp;
1363 if (cur == endp)
1364 {
1365 incdep_warn (curdep, line_no, "empty filename.");
1366 break;
1367 }
1368 if ( memchr (cur, '$', endp - cur)
1369 || memchr (cur, ' ', endp - cur)
1370 || memchr (cur, '\t', endp - cur))
1371 {
1372 incdep_warn (curdep, line_no, "multiple / fancy file name. (includedep)");
1373 break;
1374 }
1375 filename = incdep_record_strcache (curdep, cur, endp - cur);
1376
1377 /* parse any dependencies. */
1378 cur = colonp + 1;
1379 while (cur < file_end)
1380 {
1381 /* skip blanks and count lines. */
1382 while (cur < file_end && isspace ((unsigned char)*cur) && *cur != '\n')
1383 ++cur;
1384 if (cur >= file_end)
1385 break;
1386 if (*cur == '\n')
1387 {
1388 cur++;
1389 line_no++;
1390 break;
1391 }
1392
1393 /* continuation + eol? */
1394 if (*cur == '\\')
1395 {
1396 unsigned eol_len = (file_end - cur > 1 && cur[1] == '\n') ? 2
1397 : (file_end - cur > 2 && cur[1] == '\r' && cur[2] == '\n') ? 3
1398 : (file_end - cur == 1) ? 1 : 0;
1399 if (eol_len)
1400 {
1401 cur += eol_len;
1402 line_no++;
1403 continue;
1404 }
1405 }
1406
1407 /* find the end of the filename */
1408 endp = cur;
1409 while (endp < file_end && !isspace ((unsigned char)*endp))
1410 ++endp;
1411
1412 /* add it to the list. */
1413 *nextdep = dep = incdep_alloc_dep (curdep);
1414 dep->name = incdep_record_strcache (curdep, cur, endp - cur);
1415 dep->includedep = 1;
1416 nextdep = &dep->next;
1417
1418 cur = endp;
1419 }
1420
1421 /* enter the file with its dependencies. */
1422 incdep_record_files (curdep,
1423 filename, NULL, NULL, deps, 0, NULL, 0, 0, f);
1424 }
1425 }
1426 }
1427
1428 /* free the file data */
1429 incdep_xfree (curdep, curdep->file_base);
1430 curdep->file_base = curdep->file_end = NULL;
1431}
1432
1433/* Flushes the incdep todo and done lists. */
1434static void
1435incdep_flush_it (struct floc *f)
1436{
1437 incdep_lock ();
1438 for (;;)
1439 {
1440 struct incdep *cur = incdep_head_done;
1441
1442 /* if the done list is empty, grab a todo list entry. */
1443 if (!cur && incdep_head_todo)
1444 {
1445 cur = incdep_head_todo;
1446 if (cur->next)
1447 incdep_head_todo = cur->next;
1448 else
1449 incdep_head_todo = incdep_tail_todo = NULL;
1450 incdep_unlock ();
1451
1452 incdep_read_file (cur, f);
1453 eval_include_dep_file (cur, f);
1454 incdep_freeit (cur);
1455
1456 incdep_lock ();
1457 continue;
1458 }
1459
1460 /* if the todo list and done list are empty we're either done
1461 or will have to wait for the thread(s) to finish. */
1462 if (!cur && !incdep_num_reading)
1463 break; /* done */
1464 if (!cur)
1465 {
1466 while (!incdep_head_done)
1467 incdep_wait_done ();
1468 cur = incdep_head_done;
1469 }
1470
1471 /* we grab the entire done list and work thru it. */
1472 incdep_head_done = incdep_tail_done = NULL;
1473 incdep_unlock ();
1474
1475 while (cur)
1476 {
1477 struct incdep *next = cur->next;
1478#ifdef PARSE_IN_WORKER
1479 incdep_flush_recorded_instructions (cur);
1480#else
1481 eval_include_dep_file (cur, f);
1482#endif
1483 incdep_freeit (cur);
1484 cur = next;
1485 }
1486
1487 incdep_lock ();
1488 } /* outer loop */
1489 incdep_unlock ();
1490}
1491
1492
1493/* splits up a list of file names and feeds it to eval_include_dep_file,
1494 employing threads to try speed up the file reading. */
1495void
1496eval_include_dep (const char *names, struct floc *f, enum incdep_op op)
1497{
1498 struct incdep *head = 0;
1499 struct incdep *tail = 0;
1500 struct incdep *cur;
1501 const char *names_iterator = names;
1502 const char *name;
1503 unsigned int name_len;
1504
1505 /* loop through NAMES, creating a todo list out of them. */
1506
1507 while ((name = find_next_token (&names_iterator, &name_len)) != 0)
1508 {
1509 cur = xmalloc (sizeof (*cur) + name_len); /* not incdep_xmalloc here */
1510 cur->file_base = cur->file_end = NULL;
1511 memcpy (cur->name, name, name_len);
1512 cur->name[name_len] = '\0';
1513 cur->worker_tid = -1;
1514#ifdef PARSE_IN_WORKER
1515 cur->err_line_no = 0;
1516 cur->err_msg = NULL;
1517 cur->recorded_variables_in_set_head = NULL;
1518 cur->recorded_variables_in_set_tail = NULL;
1519 cur->recorded_variable_defs_head = NULL;
1520 cur->recorded_variable_defs_tail = NULL;
1521 cur->recorded_files_head = NULL;
1522 cur->recorded_files_tail = NULL;
1523#endif
1524
1525 cur->next = NULL;
1526 if (tail)
1527 tail->next = cur;
1528 else
1529 head = cur;
1530 tail = cur;
1531 }
1532
1533#ifdef ELECTRIC_HEAP
1534 if (1)
1535#else
1536 if (op == incdep_read_it)
1537#endif
1538 {
1539 /* work our way thru the files directly */
1540
1541 cur = head;
1542 while (cur)
1543 {
1544 struct incdep *next = cur->next;
1545 incdep_read_file (cur, f);
1546 eval_include_dep_file (cur, f);
1547 incdep_freeit (cur);
1548 cur = next;
1549 }
1550 }
1551 else
1552 {
1553 /* initialize the worker threads and related stuff the first time around. */
1554
1555 if (!incdep_initialized)
1556 incdep_init (f);
1557
1558 /* queue the files and notify the worker threads. */
1559
1560 incdep_lock ();
1561
1562 if (incdep_tail_todo)
1563 incdep_tail_todo->next = head;
1564 else
1565 incdep_head_todo = head;
1566 incdep_tail_todo = tail;
1567
1568 incdep_signal_todo ();
1569 incdep_unlock ();
1570
1571 /* flush the todo queue if we're requested to do so. */
1572
1573 if (op == incdep_flush)
1574 incdep_flush_it (f);
1575 }
1576}
1577
1578#endif /* CONFIG_WITH_INCLUDEDEP */
1579
Note: See TracBrowser for help on using the repository browser.