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

Last change on this file since 3642 was 3628, checked in by bird, 9 months ago

kmk/incdep.c: Fixed the list corruption when there are actually no dependency files to be added (they don't exist according to kFsCacheLookupWithLengthA).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.5 KB
Line 
1#ifdef CONFIG_WITH_INCLUDEDEP
2/* $Id: incdep.c 3628 2024-10-22 23:34:56Z bird $ */
3/** @file
4 * incdep - Simple dependency files.
5 */
6
7/*
8 * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@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 3 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, see <http://www.gnu.org/licenses/>
24 *
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#ifdef __OS2__
31# define INCL_BASE
32# define INCL_ERRORS
33#endif
34#ifdef KBUILD_OS_WINDOWS
35# ifdef KMK
36# define INCDEP_USE_KFSCACHE
37# endif
38#endif
39
40#include "makeint.h"
41
42#if !defined(WINDOWS32) && !defined(__OS2__)
43# define HAVE_PTHREAD
44#endif
45
46#include <assert.h>
47
48#include <glob.h>
49
50#include "filedef.h"
51#include "dep.h"
52#include "job.h"
53#include "commands.h"
54#include "variable.h"
55#include "rule.h"
56#include "debug.h"
57#include "strcache2.h"
58
59#ifdef HAVE_FCNTL_H
60# include <fcntl.h>
61#else
62# include <sys/file.h>
63#endif
64
65#ifdef WINDOWS32
66# include <io.h>
67# include <process.h>
68# include <Windows.h>
69# define PARSE_IN_WORKER
70#endif
71
72#ifdef INCDEP_USE_KFSCACHE
73# include "nt/kFsCache.h"
74extern PKFSCACHE g_pFsCache; /* dir-nt-bird.c for now */
75#endif
76
77#ifdef __OS2__
78# include <os2.h>
79# include <sys/fmutex.h>
80#endif
81
82#ifdef HAVE_PTHREAD
83# include <pthread.h>
84#endif
85
86#ifdef __APPLE__
87# include <malloc/malloc.h>
88# define PARSE_IN_WORKER
89#endif
90
91#if defined(__gnu_linux__) || defined(__linux__)
92# define PARSE_IN_WORKER
93#endif
94
95
96/*******************************************************************************
97* Defined Constants And Macros *
98********************************************************************************/
99/* Custom assertion macro */
100#if (!defined(NDEBUG) || 0) && defined(_MSC_VER)
101# define MY_ASSERT(expr) do { \
102 if (!!(expr)) { /* likely */ } \
103 else { fprintf (stderr, "assertion failed on line %u in incdep.c: %s\n", __LINE__, #expr); __debugbreak(); } \
104 } while (0)
105# undef assert
106# define assert(expr) MY_ASSERT(expr)
107#else
108# define MY_ASSERT(expr) ((void)0)
109#endif
110
111
112/*******************************************************************************
113* Structures and Typedefs *
114*******************************************************************************/
115struct incdep_worker_data
116{
117 int worker_tid; /* -1 for main, index for the others. */
118 unsigned int done_count; /* workers increment this */
119 unsigned int flushed_count; /* main thread: flushed done count. */
120 unsigned int todo_count; /* main thread: queued on todo */
121};
122
123struct incdep_variable_in_set
124{
125 struct incdep_variable_in_set *next;
126 /* the parameters */
127 struct strcache2_entry *name_entry; /* dep strcache - WRONG */
128 const char *value; /* xmalloc'ed */
129 unsigned int value_length;
130 int duplicate_value; /* 0 */
131 enum variable_origin origin;
132 int recursive;
133 struct variable_set *set;
134 const floc *flocp; /* NILF */
135};
136
137struct incdep_variable_def
138{
139 struct incdep_variable_def *next;
140 /* the parameters */
141 const floc *flocp; /* NILF */
142 struct strcache2_entry *name_entry; /* dep strcache - WRONG */
143 char *value; /* xmalloc'ed, free it */
144 unsigned int value_length;
145 enum variable_origin origin;
146 enum variable_flavor flavor;
147 int target_var;
148};
149
150struct incdep_recorded_file
151{
152 struct incdep_recorded_file *next;
153
154 /* the parameters */
155 struct strcache2_entry *filename_entry; /* dep strcache; converted to a nameseq record. */
156 struct dep *deps; /* All the names are dep strcache entries. */
157 const floc *flocp; /* NILF */
158};
159
160
161/* per dep file structure. */
162struct incdep
163{
164 struct incdep *next;
165 char *file_base;
166 char *file_end;
167
168 int worker_tid;
169#ifdef PARSE_IN_WORKER
170 unsigned int err_line_no;
171 const char *err_msg;
172
173 struct incdep_variable_in_set *recorded_variables_in_set_head;
174 struct incdep_variable_in_set *recorded_variables_in_set_tail;
175
176 struct incdep_variable_def *recorded_variable_defs_head;
177 struct incdep_variable_def *recorded_variable_defs_tail;
178
179 struct incdep_recorded_file *recorded_file_head;
180 struct incdep_recorded_file *recorded_file_tail;
181#endif
182#ifdef INCDEP_USE_KFSCACHE
183 /** Pointer to the fs cache object for this file (it exists and is a file). */
184 PKFSOBJ pFileObj;
185#else
186 char name[1];
187#endif
188};
189
190
191/*******************************************************************************
192* Global Variables *
193*******************************************************************************/
194
195/* mutex protecting the globals and an associated condition/event. */
196#ifdef HAVE_PTHREAD
197static pthread_mutex_t incdep_mtx;
198static pthread_cond_t incdep_cond_todo;
199static pthread_cond_t incdep_cond_done;
200
201#elif defined (WINDOWS32)
202static CRITICAL_SECTION incdep_mtx;
203static HANDLE incdep_hev_todo;
204static HANDLE incdep_hev_done;
205static int volatile incdep_hev_todo_waiters;
206static int volatile incdep_hev_done_waiters;
207
208#elif defined (__OS2__)
209static _fmutex incdep_mtx;
210static HEV incdep_hev_todo;
211static HEV incdep_hev_done;
212static int volatile incdep_hev_todo_waiters;
213static int volatile incdep_hev_done_waiters;
214#endif
215
216/* flag indicating whether the threads, lock and event/condvars has
217 been initialized or not. */
218static int incdep_initialized;
219
220/* the list of files that needs reading. */
221static struct incdep * volatile incdep_head_todo = NULL;
222static struct incdep * volatile incdep_tail_todo = NULL;
223static unsigned int volatile incdep_count_todo = 0;
224
225/* the number of files that are currently being read. */
226static int volatile incdep_num_reading = 0;
227
228/* the list of files that have been read. */
229static struct incdep * volatile incdep_head_done = NULL;
230static struct incdep * volatile incdep_tail_done = NULL;
231static unsigned int volatile incdep_count_done = 0;
232
233
234/* The handles to the worker threads. */
235#ifdef HAVE_PTHREAD
236# define INCDEP_MAX_THREADS 1
237static pthread_t incdep_threads[INCDEP_MAX_THREADS];
238
239#elif defined (WINDOWS32)
240# define INCDEP_MAX_THREADS 2
241static HANDLE incdep_threads[INCDEP_MAX_THREADS];
242
243#elif defined (__OS2__)
244# define INCDEP_MAX_THREADS 2
245static TID incdep_threads[INCDEP_MAX_THREADS];
246#endif
247
248static struct alloccache incdep_rec_caches[INCDEP_MAX_THREADS];
249static struct alloccache incdep_dep_caches[INCDEP_MAX_THREADS];
250static struct strcache2 incdep_dep_strcaches[INCDEP_MAX_THREADS];
251static struct strcache2 incdep_var_strcaches[INCDEP_MAX_THREADS];
252static unsigned incdep_num_threads;
253
254/* flag indicating whether the worker threads should terminate or not. */
255static int volatile incdep_terminate;
256
257#ifdef __APPLE__
258/* malloc zone for the incdep threads. */
259static malloc_zone_t *incdep_zone;
260#endif
261
262/* Worker specific data, the extra entry is for the main thread.
263 TODO: Move all parallel arrays in here to avoid unnecessary cacheline
264 sharing between worker threads. */
265static struct incdep_worker_data incdep_worker_data[INCDEP_MAX_THREADS + 1];
266
267
268/*******************************************************************************
269* Internal Functions *
270*******************************************************************************/
271static void incdep_flush_it (floc *);
272static void eval_include_dep_file (struct incdep *, floc *);
273static void incdep_commit_recorded_file (const char *filename, struct dep *deps,
274 const floc *flocp);
275
276
277/* xmalloc wrapper.
278 For working around multithreaded performance problems found on Darwin,
279 Linux (glibc), and possibly other systems. */
280static void *
281incdep_xmalloc (struct incdep *cur, size_t size)
282{
283 void *ptr;
284
285#ifdef __APPLE__
286 if (cur && cur->worker_tid != -1)
287 {
288 ptr = malloc_zone_malloc (incdep_zone, size);
289 if (!ptr)
290 O (fatal, NILF, _("virtual memory exhausted"));
291 }
292 else
293 ptr = xmalloc (size);
294#else
295 ptr = xmalloc (size);
296#endif
297
298 (void)cur;
299 return ptr;
300}
301
302#if 0
303/* cmalloc wrapper */
304static void *
305incdep_xcalloc (struct incdep *cur, size_t size)
306{
307 void *ptr;
308
309#ifdef __APPLE__
310 if (cur && cur->worker_tid != -1)
311 ptr = malloc_zone_calloc (incdep_zone, size, 1);
312 else
313 ptr = calloc (size, 1);
314#else
315 ptr = calloc (size, 1);
316#endif
317 if (!ptr)
318 fatal (NILF, _("virtual memory exhausted"));
319
320 (void)cur;
321 return ptr;
322}
323#endif /* unused */
324
325/* free wrapper */
326static void
327incdep_xfree (struct incdep *cur, void *ptr)
328{
329 /* free() *must* work for the allocation hacks above because
330 of free_dep_chain. */
331 free (ptr);
332 (void)cur;
333}
334
335/* alloc a dep structure. These are allocated in bunches to save time. */
336struct dep *
337incdep_alloc_dep (struct incdep *cur)
338{
339 struct alloccache *cache;
340 if (cur->worker_tid != -1)
341 cache = &incdep_dep_caches[cur->worker_tid];
342 else
343 cache = &dep_cache;
344 return alloccache_calloc (cache);
345}
346
347/* duplicates the dependency list pointed to by srcdep. */
348static struct dep *
349incdep_dup_dep_list (struct incdep *cur, struct dep const *srcdep)
350{
351 struct alloccache *cache;
352 struct dep *retdep;
353 struct dep *dstdep;
354
355 if (cur->worker_tid != -1)
356 cache = &incdep_dep_caches[cur->worker_tid];
357 else
358 cache = &dep_cache;
359
360 if (srcdep)
361 {
362 retdep = dstdep = alloccache_alloc (cache);
363 for (;;)
364 {
365 dstdep->name = srcdep->name; /* string cached */
366 dstdep->includedep = srcdep->includedep;
367 srcdep = srcdep->next;
368 if (!srcdep)
369 {
370 dstdep->next = NULL;
371 break;
372 }
373 dstdep->next = alloccache_alloc (cache);
374 dstdep = dstdep->next;
375 }
376 }
377 else
378 retdep = NULL;
379 return retdep;
380}
381
382
383/* allocate a record. */
384static void *
385incdep_alloc_rec (struct incdep *cur)
386{
387 return alloccache_alloc (&incdep_rec_caches[cur->worker_tid]);
388}
389
390/* free a record. */
391static void
392incdep_free_rec (struct incdep *cur, void *rec)
393{
394 /*alloccache_free (&incdep_rec_caches[cur->worker_tid], rec); - doesn't work of course. */
395}
396
397
398/* grow a cache. */
399static void *
400incdep_cache_allocator (void *thrd, unsigned int size)
401{
402 (void)thrd;
403#ifdef __APPLE__
404 return malloc_zone_malloc (incdep_zone, size);
405#else
406 return xmalloc (size);
407#endif
408}
409
410/* term a cache. */
411static void
412incdep_cache_deallocator (void *thrd, void *ptr, unsigned int size)
413{
414 (void)thrd;
415 (void)size;
416 free (ptr);
417}
418
419/* acquires the lock */
420void
421incdep_lock(void)
422{
423#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
424 pthread_mutex_lock (&incdep_mtx);
425#elif defined (WINDOWS32)
426 EnterCriticalSection (&incdep_mtx);
427#elif defined (__OS2__)
428 _fmutex_request (&incdep_mtx, 0);
429#elif !defined(CONFIG_WITHOUT_THREADS)
430# error Misconfig?
431#endif
432}
433
434/* releases the lock */
435void
436incdep_unlock(void)
437{
438#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
439 pthread_mutex_unlock (&incdep_mtx);
440#elif defined(WINDOWS32)
441 LeaveCriticalSection (&incdep_mtx);
442#elif defined(__OS2__)
443 _fmutex_release (&incdep_mtx);
444#endif
445}
446
447/* signals the main thread that there is stuff todo. caller owns the lock. */
448static void
449incdep_signal_done (void)
450{
451#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
452 pthread_cond_broadcast (&incdep_cond_done);
453#elif defined (WINDOWS32)
454 if (incdep_hev_done_waiters)
455 SetEvent (incdep_hev_done);
456#elif defined (__OS2__)
457 if (incdep_hev_done_waiters)
458 DosPostEventSem (incdep_hev_done);
459#endif
460}
461
462/* waits for a reader to finish reading. caller owns the lock. */
463static void
464incdep_wait_done (void)
465{
466#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
467 pthread_cond_wait (&incdep_cond_done, &incdep_mtx);
468
469#elif defined (WINDOWS32)
470 ResetEvent (incdep_hev_done);
471 incdep_hev_done_waiters++;
472 incdep_unlock ();
473 WaitForSingleObject (incdep_hev_done, INFINITE);
474 incdep_lock ();
475 incdep_hev_done_waiters--;
476
477#elif defined (__OS2__)
478 ULONG ulIgnore;
479 DosResetEventSem (incdep_hev_done, &ulIgnore);
480 incdep_hev_done_waiters++;
481 incdep_unlock ();
482 DosWaitEventSem (incdep_hev_done, SEM_INDEFINITE_WAIT);
483 incdep_lock ();
484 incdep_hev_done_waiters--;
485#endif
486}
487
488/* signals the worker threads. caller owns the lock. */
489static void
490incdep_signal_todo (void)
491{
492#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
493 pthread_cond_broadcast (&incdep_cond_todo);
494#elif defined(WINDOWS32)
495 if (incdep_hev_todo_waiters)
496 SetEvent (incdep_hev_todo);
497#elif defined(__OS2__)
498 if (incdep_hev_todo_waiters)
499 DosPostEventSem (incdep_hev_todo);
500#endif
501}
502
503/* waits for stuff to arrive in the todo list. caller owns the lock. */
504static void
505incdep_wait_todo (void)
506{
507#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
508 pthread_cond_wait (&incdep_cond_todo, &incdep_mtx);
509
510#elif defined (WINDOWS32)
511 ResetEvent (incdep_hev_todo);
512 incdep_hev_todo_waiters++;
513 incdep_unlock ();
514 WaitForSingleObject (incdep_hev_todo, INFINITE);
515 incdep_lock ();
516 incdep_hev_todo_waiters--;
517
518#elif defined (__OS2__)
519 ULONG ulIgnore;
520 DosResetEventSem (incdep_hev_todo, &ulIgnore);
521 incdep_hev_todo_waiters++;
522 incdep_unlock ();
523 DosWaitEventSem (incdep_hev_todo, SEM_INDEFINITE_WAIT);
524 incdep_lock ();
525 incdep_hev_todo_waiters--;
526#endif
527}
528
529/* Reads a dep file into memory. */
530static int
531incdep_read_file (struct incdep *cur, floc *f)
532{
533#ifdef INCDEP_USE_KFSCACHE
534 size_t const cbFile = (size_t)cur->pFileObj->Stats.st_size;
535
536 assert(cur->pFileObj->fHaveStats);
537 cur->file_base = incdep_xmalloc (cur, cbFile + 1);
538 if (cur->file_base)
539 {
540 if (kFsCacheFileSimpleOpenReadClose (g_pFsCache, cur->pFileObj, 0, cur->file_base, cbFile))
541 {
542 cur->file_end = cur->file_base + cbFile;
543 cur->file_base[cbFile] = '\0';
544 return 0;
545 }
546 incdep_xfree (cur, cur->file_base);
547 }
548 OSS (error, f, "%s/%s: error reading file", cur->pFileObj->pParent->Obj.pszName, cur->pFileObj->pszName);
549
550#else /* !INCDEP_USE_KFSCACHE */
551 int fd;
552 struct stat st;
553
554 errno = 0;
555# ifdef O_BINARY
556 fd = open (cur->name, O_RDONLY | O_BINARY, 0);
557# else
558 fd = open (cur->name, O_RDONLY, 0);
559# endif
560 if (fd < 0)
561 {
562 /* ignore non-existing dependency files. */
563 int err = errno;
564 if (err == ENOENT || stat (cur->name, &st) != 0)
565 return 1;
566 OSS (error, f, "%s: %s", cur->name, strerror (err));
567 return -1;
568 }
569# ifdef KBUILD_OS_WINDOWS /* fewer kernel calls */
570 if (!birdStatOnFdJustSize (fd, &st.st_size))
571# else
572 if (!fstat (fd, &st))
573# endif
574 {
575 cur->file_base = incdep_xmalloc (cur, st.st_size + 1);
576 if (read (fd, cur->file_base, st.st_size) == st.st_size)
577 {
578 close (fd);
579 cur->file_end = cur->file_base + st.st_size;
580 cur->file_base[st.st_size] = '\0';
581 return 0;
582 }
583
584 /* bail out */
585
586 OSS (error, f, "%s: read: %s", cur->name, strerror (errno));
587 incdep_xfree (cur, cur->file_base);
588 }
589 else
590 OSS (error, f, "%s: fstat: %s", cur->name, strerror (errno));
591
592 close (fd);
593#endif /* !INCDEP_USE_KFSCACHE */
594 cur->file_base = cur->file_end = NULL;
595 return -1;
596}
597
598/* Free the incdep structure. */
599static void
600incdep_freeit (struct incdep *cur)
601{
602#ifdef PARSE_IN_WORKER
603 assert (!cur->recorded_variables_in_set_head);
604 assert (!cur->recorded_variable_defs_head);
605 assert (!cur->recorded_file_head);
606#endif
607
608 incdep_xfree (cur, cur->file_base);
609#ifdef INCDEP_USE_KFSCACHE
610 /** @todo release object ref some day... */
611#endif
612 cur->next = NULL;
613 free (cur);
614}
615
616/* A worker thread. */
617void
618incdep_worker (int thrd)
619{
620 struct incdep_worker_data *thrd_data = &incdep_worker_data[thrd];
621 thrd_data->worker_tid = thrd;
622
623 incdep_lock ();
624
625 while (!incdep_terminate)
626 {
627 /* get job from the todo list. */
628
629 struct incdep *tmp;
630 struct incdep *cur = incdep_head_todo;
631 if (!cur)
632 {
633 incdep_wait_todo ();
634 continue;
635 }
636 if (cur->next)
637 {
638 assert (incdep_count_todo > 1);
639 assert (cur != incdep_tail_todo);
640 incdep_head_todo = cur->next;
641 }
642 else
643 {
644 assert (incdep_count_todo == 1);
645 assert (cur == incdep_tail_todo);
646 incdep_head_todo = incdep_tail_todo = NULL;
647 }
648 incdep_count_todo--;
649 incdep_num_reading++;
650
651 /* read the file. */
652
653 incdep_unlock ();
654 cur->worker_tid = thrd;
655
656 incdep_read_file (cur, NILF);
657#ifdef PARSE_IN_WORKER
658 eval_include_dep_file (cur, NILF);
659#endif
660
661 cur->worker_tid = -1;
662 incdep_lock ();
663
664 /* insert finished job into the done list. */
665
666 incdep_num_reading--;
667 cur->next = NULL;
668 tmp = incdep_tail_done;
669 if (tmp)
670 {
671 tmp->next = cur;
672 assert (incdep_count_done > 0);
673 }
674 else
675 {
676 assert (!incdep_head_done);
677 assert (incdep_count_done == 0);
678 incdep_head_done = cur;
679 }
680 incdep_tail_done = cur;
681 incdep_count_done++;
682
683 thrd_data->done_count++;
684
685 incdep_signal_done ();
686 }
687
688 incdep_unlock ();
689}
690
691/* Thread library specific thread functions wrapping incdep_wroker. */
692#ifdef HAVE_PTHREAD
693static void *
694incdep_worker_pthread (void *thrd)
695{
696 incdep_worker ((size_t)thrd);
697 return NULL;
698}
699
700#elif defined (WINDOWS32)
701static unsigned __stdcall
702incdep_worker_windows (void *thrd)
703{
704 incdep_worker ((size_t)thrd);
705 return 0;
706}
707
708#elif defined (__OS2__)
709static void
710incdep_worker_os2 (void *thrd)
711{
712 incdep_worker ((size_t)thrd);
713}
714#endif
715
716/* Checks if threads are enabled or not.
717
718 This is a special hack so that is possible to disable the threads when in a
719 debian fakeroot environment. Thus, in addition to the KMK_THREADS_DISABLED
720 and KMK_THREADS_ENABLED environment variable check we also check for signs
721 of fakeroot. */
722static int
723incdep_are_threads_enabled (void)
724{
725#if defined (CONFIG_WITHOUT_THREADS)
726 return 0;
727#endif
728
729 /* Generic overrides. */
730 if (getenv ("KMK_THREADS_DISABLED"))
731 {
732 O (message, 1, "Threads disabled (environment)");
733 return 0;
734 }
735 if (getenv ("KMK_THREADS_ENABLED"))
736 return 1;
737
738#if defined (__gnu_linux__) || defined (__linux__) || defined(__GLIBC__)
739 /* Try detect fakeroot. */
740 if (getenv ("FAKEROOTKEY")
741 || getenv ("FAKEROOTUID")
742 || getenv ("FAKEROOTGID")
743 || getenv ("FAKEROOTEUID")
744 || getenv ("FAKEROOTEGID")
745 || getenv ("FAKEROOTSUID")
746 || getenv ("FAKEROOTSGID")
747 || getenv ("FAKEROOTFUID")
748 || getenv ("FAKEROOTFGID")
749 || getenv ("FAKEROOTDONTTRYCHOWN")
750 || getenv ("FAKEROOT_FD_BASE")
751 || getenv ("FAKEROOT_DB_SEARCH_PATHS"))
752 {
753 O (message, 1, "Threads disabled (fakeroot)");
754 return 0;
755 }
756
757 /* LD_PRELOAD could indicate undetected debian fakeroot or some
758 other ingenius library which cannot deal correctly with threads. */
759 if (getenv ("LD_PRELOAD"))
760 {
761 O (message, 1, "Threads disabled (LD_PRELOAD)");
762 return 0;
763 }
764
765#elif defined(__APPLE__) \
766 || defined(__sun__) || defined(__SunOS__) || defined(__sun) || defined(__SunOS) \
767 || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) \
768 || defined(__HAIKU__)
769 /* No broken preload libraries known to be in common use on these platforms... */
770
771#elif defined(_MSC_VER) || defined(_WIN32) || defined(__OS2__)
772 /* No preload mess to care about. */
773
774#else
775# error "Add your self to the appropriate case above and send a patch to bird."
776#endif
777 return 1;
778}
779
780/* Creates the the worker threads. */
781static void
782incdep_init (floc *f)
783{
784 unsigned i;
785#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
786 int rc;
787 pthread_attr_t attr;
788
789#elif defined (WINDOWS32)
790 unsigned tid;
791 uintptr_t hThread;
792
793#elif defined (__OS2__)
794 int rc;
795 int tid;
796#endif
797 (void)f;
798
799 /* heap hacks */
800
801#ifdef __APPLE__
802 incdep_zone = malloc_create_zone (0, 0);
803 if (!incdep_zone)
804 incdep_zone = malloc_default_zone ();
805#endif
806
807
808 /* create the mutex and two condition variables / event objects. */
809
810#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
811 rc = pthread_mutex_init (&incdep_mtx, NULL);
812 if (rc)
813 ON (fatal, f, _("pthread_mutex_init failed: err=%d"), rc);
814 rc = pthread_cond_init (&incdep_cond_todo, NULL);
815 if (rc)
816 ON (fatal, f, _("pthread_cond_init failed: err=%d"), rc);
817 rc = pthread_cond_init (&incdep_cond_done, NULL);
818 if (rc)
819 ON (fatal, f, _("pthread_cond_init failed: err=%d"), rc);
820
821#elif defined (WINDOWS32)
822 InitializeCriticalSection (&incdep_mtx);
823 incdep_hev_todo = CreateEvent (NULL, TRUE /*bManualReset*/, FALSE /*bInitialState*/, NULL);
824 if (!incdep_hev_todo)
825 ON (fatal, f, _("CreateEvent failed: err=%d"), GetLastError());
826 incdep_hev_done = CreateEvent (NULL, TRUE /*bManualReset*/, FALSE /*bInitialState*/, NULL);
827 if (!incdep_hev_done)
828 ON (fatal, f, _("CreateEvent failed: err=%d"), GetLastError());
829 incdep_hev_todo_waiters = 0;
830 incdep_hev_done_waiters = 0;
831
832#elif defined (__OS2__)
833 _fmutex_create (&incdep_mtx, 0);
834 rc = DosCreateEventSem (NULL, &incdep_hev_todo, 0, FALSE);
835 if (rc)
836 ON (fatal, f, _("DosCreateEventSem failed: rc=%d"), rc);
837 rc = DosCreateEventSem (NULL, &incdep_hev_done, 0, FALSE);
838 if (rc)
839 ON (fatal, f, _("DosCreateEventSem failed: rc=%d"), rc);
840 incdep_hev_todo_waiters = 0;
841 incdep_hev_done_waiters = 0;
842#endif
843
844 /* create the worker threads and associated per thread data. */
845
846 incdep_terminate = 0;
847 if (incdep_are_threads_enabled())
848 {
849 incdep_num_threads = sizeof (incdep_threads) / sizeof (incdep_threads[0]);
850 if (incdep_num_threads + 1 > job_slots)
851 incdep_num_threads = job_slots <= 1 ? 1 : job_slots - 1;
852 for (i = 0; i < incdep_num_threads; i++)
853 {
854 /* init caches */
855 unsigned rec_size = sizeof (struct incdep_variable_in_set);
856 if (rec_size < sizeof (struct incdep_variable_def))
857 rec_size = sizeof (struct incdep_variable_def);
858 if (rec_size < sizeof (struct incdep_recorded_file))
859 rec_size = sizeof (struct incdep_recorded_file);
860 alloccache_init (&incdep_rec_caches[i], rec_size, "incdep rec",
861 incdep_cache_allocator, (void *)(size_t)i);
862 alloccache_init (&incdep_dep_caches[i], sizeof(struct dep), "incdep dep",
863 incdep_cache_allocator, (void *)(size_t)i);
864 strcache2_init (&incdep_dep_strcaches[i],
865 "incdep dep", /* name */
866 65536, /* hash size */
867 0, /* default segment size*/
868#ifdef HAVE_CASE_INSENSITIVE_FS
869 1, /* case insensitive */
870#else
871 0, /* case insensitive */
872#endif
873 0); /* thread safe */
874
875 strcache2_init (&incdep_var_strcaches[i],
876 "incdep var", /* name */
877 32768, /* hash size */
878 0, /* default segment size*/
879 0, /* case insensitive */
880 0); /* thread safe */
881
882 /* create the thread. */
883#if defined (HAVE_PTHREAD) && !defined (CONFIG_WITHOUT_THREADS)
884 rc = pthread_attr_init (&attr);
885 if (rc)
886 ON (fatal, f, _("pthread_attr_init failed: err=%d"), rc);
887 /*rc = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE); */
888 rc = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
889 if (rc)
890 ON (fatal, f, _("pthread_attr_setdetachstate failed: err=%d"), rc);
891 rc = pthread_create (&incdep_threads[i], &attr,
892 incdep_worker_pthread, (void *)(size_t)i);
893 if (rc)
894 ON (fatal, f, _("pthread_mutex_init failed: err=%d"), rc);
895 pthread_attr_destroy (&attr);
896
897#elif defined (WINDOWS32)
898 tid = 0;
899 hThread = _beginthreadex (NULL, 128*1024, incdep_worker_windows,
900 (void *)i, 0, &tid);
901 if (hThread == 0 || hThread == ~(uintptr_t)0)
902 ON (fatal, f, _("_beginthreadex failed: err=%d"), errno);
903 incdep_threads[i] = (HANDLE)hThread;
904
905#elif defined (__OS2__)
906 tid = _beginthread (incdep_worker_os2, NULL, 128*1024, (void *)i);
907 if (tid <= 0)
908 ON (fatal, f, _("_beginthread failed: err=%d"), errno);
909 incdep_threads[i] = tid;
910#endif
911 }
912 }
913 else
914 incdep_num_threads = 0;
915
916 incdep_initialized = 1;
917}
918
919/* Flushes outstanding work and terminates the worker threads.
920 This is called from snap_deps(). */
921void
922incdep_flush_and_term (void)
923{
924 unsigned i;
925 unsigned total_done = 0;
926
927 if (!incdep_initialized)
928 return;
929
930 /* flush any out standing work */
931
932 incdep_flush_it (NILF);
933
934 /* tell the threads to terminate */
935
936 incdep_lock ();
937 incdep_terminate = 1;
938 incdep_signal_todo ();
939 incdep_unlock ();
940
941 /* wait for the threads to quit */
942
943 for (i = 0; i < incdep_num_threads; i++)
944 {
945 /* more later? */
946
947 /* terminate or join up the allocation caches. */
948 alloccache_term (&incdep_rec_caches[i], incdep_cache_deallocator, (void *)(size_t)i);
949 alloccache_join (&dep_cache, &incdep_dep_caches[i]);
950 strcache2_term (&incdep_dep_strcaches[i]);
951 strcache2_term (&incdep_var_strcaches[i]);
952
953 /* accounting */
954 total_done += incdep_worker_data[i].done_count;
955 }
956 incdep_num_threads = 0;
957
958 /* sanity check */
959 if (total_done != incdep_worker_data[INCDEP_MAX_THREADS].flushed_count)
960 fprintf (stderr, "kmk/incdep: warning: total_done=%#x does not equal flushed_count=%#x!\n",
961 total_done, incdep_worker_data[INCDEP_MAX_THREADS].flushed_count);
962 if (total_done != incdep_worker_data[INCDEP_MAX_THREADS].todo_count)
963 fprintf (stderr, "kmk/incdep: warning: total_done=%#x does not equal todo_count=%#x!\n",
964 total_done, incdep_worker_data[INCDEP_MAX_THREADS].todo_count);
965
966 /* destroy the lock and condition variables / event objects. */
967
968 /* later */
969
970 incdep_initialized = 0;
971}
972
973#ifdef PARSE_IN_WORKER
974/* Flushes a strcache entry returning the actual string cache entry.
975 The input is freed! */
976static const char *
977incdep_flush_strcache_entry (struct strcache2_entry *entry)
978{
979 if (!entry->user)
980 entry->user = (void *) strcache2_add_hashed_file (&file_strcache,
981 (const char *)(entry + 1),
982 entry->length, entry->hash);
983 return (const char *)entry->user;
984}
985
986/* Flushes the recorded instructions. */
987static void
988incdep_flush_recorded_instructions (struct incdep *cur)
989{
990 struct incdep_variable_in_set *rec_vis;
991 struct incdep_variable_def *rec_vd;
992 struct incdep_recorded_file *rec_f;
993
994 /* Display saved error. */
995
996 if (cur->err_msg)
997#ifdef INCDEP_USE_KFSCACHE
998 OSSNS (error, NILF, "%s/%s(%d): %s", cur->pFileObj->pParent->Obj.pszName, cur->pFileObj->pszName,
999 cur->err_line_no, cur->err_msg);
1000#else
1001 OSNS (error,NILF, "%s(%d): %s", cur->name, cur->err_line_no, cur->err_msg);
1002#endif
1003
1004
1005 /* define_variable_in_set */
1006
1007 rec_vis = cur->recorded_variables_in_set_head;
1008 cur->recorded_variables_in_set_head = cur->recorded_variables_in_set_tail = NULL;
1009 if (rec_vis)
1010 do
1011 {
1012 void *free_me = rec_vis;
1013 unsigned int name_length = rec_vis->name_entry->length;
1014 define_variable_in_set (incdep_flush_strcache_entry (rec_vis->name_entry),
1015 name_length,
1016 rec_vis->value,
1017 rec_vis->value_length,
1018 rec_vis->duplicate_value,
1019 rec_vis->origin,
1020 rec_vis->recursive,
1021 rec_vis->set,
1022 rec_vis->flocp);
1023 rec_vis = rec_vis->next;
1024 incdep_free_rec (cur, free_me);
1025 }
1026 while (rec_vis);
1027
1028 /* do_variable_definition */
1029
1030 rec_vd = cur->recorded_variable_defs_head;
1031 cur->recorded_variable_defs_head = cur->recorded_variable_defs_tail = NULL;
1032 if (rec_vd)
1033 do
1034 {
1035 void *free_me = rec_vd;
1036 do_variable_definition_2 (rec_vd->flocp,
1037 incdep_flush_strcache_entry (rec_vd->name_entry),
1038 rec_vd->value,
1039 rec_vd->value_length,
1040 0,
1041 rec_vd->value,
1042 rec_vd->origin,
1043 rec_vd->flavor,
1044 rec_vd->target_var);
1045 rec_vd = rec_vd->next;
1046 incdep_free_rec (cur, free_me);
1047 }
1048 while (rec_vd);
1049
1050 /* record_files */
1051
1052 rec_f = cur->recorded_file_head;
1053 cur->recorded_file_head = cur->recorded_file_tail = NULL;
1054 if (rec_f)
1055 do
1056 {
1057 void *free_me = rec_f;
1058 struct dep *dep;
1059
1060 for (dep = rec_f->deps; dep; dep = dep->next)
1061 dep->name = incdep_flush_strcache_entry ((struct strcache2_entry *)dep->name);
1062
1063 incdep_commit_recorded_file (incdep_flush_strcache_entry (rec_f->filename_entry),
1064 rec_f->deps,
1065 rec_f->flocp);
1066
1067 rec_f = rec_f->next;
1068 incdep_free_rec (cur, free_me);
1069 }
1070 while (rec_f);
1071}
1072#endif /* PARSE_IN_WORKER */
1073
1074/* Record / issue a warning about a misformed dep file. */
1075static void
1076incdep_warn (struct incdep *cur, unsigned int line_no, const char *msg)
1077{
1078 if (cur->worker_tid == -1)
1079#ifdef INCDEP_USE_KFSCACHE
1080 OSSNS (error,NILF, "%s/%s(%d): %s", cur->pFileObj->pParent->Obj.pszName, cur->pFileObj->pszName, line_no, msg);
1081#else
1082 OSNS (error, NILF, "%s(%d): %s", cur->name, line_no, msg);
1083#endif
1084#ifdef PARSE_IN_WORKER
1085 else
1086 {
1087 cur->err_line_no = line_no;
1088 cur->err_msg = msg;
1089 }
1090#endif
1091}
1092
1093/* Dependency or file strcache allocation / recording. */
1094static const char *
1095incdep_dep_strcache (struct incdep *cur, const char *str, int len)
1096{
1097 const char *ret;
1098 if (cur->worker_tid == -1)
1099 {
1100 /* Make sure the string is terminated before we hand it to
1101 strcache_add_len so it does have to make a temporary copy
1102 of it on the stack. */
1103 char ch = str[len];
1104 ((char *)str)[len] = '\0';
1105 ret = strcache_add_len (str, len);
1106 ((char *)str)[len] = ch;
1107 }
1108 else
1109 {
1110 /* Add it out the strcache of the thread. */
1111 ret = strcache2_add (&incdep_dep_strcaches[cur->worker_tid], str, len);
1112 ret = (const char *)strcache2_get_entry(&incdep_dep_strcaches[cur->worker_tid], ret);
1113 }
1114 return ret;
1115}
1116
1117/* Variable name allocation / recording. */
1118static const char *
1119incdep_var_strcache (struct incdep *cur, const char *str, int len)
1120{
1121 const char *ret;
1122 if (cur->worker_tid == -1)
1123 {
1124 /* XXX: we're leaking this memory now! This will be fixed later. */
1125 ret = xmalloc (len + 1);
1126 memcpy ((char *)ret, str, len);
1127 ((char *)ret)[len] = '\0';
1128 }
1129 else
1130 {
1131 /* Add it out the strcache of the thread. */
1132 ret = strcache2_add (&incdep_var_strcaches[cur->worker_tid], str, len);
1133 ret = (const char *)strcache2_get_entry(&incdep_var_strcaches[cur->worker_tid], ret);
1134 }
1135 return ret;
1136}
1137
1138/* Record / perform a variable definition in a set.
1139 The NAME is in the string cache.
1140 The VALUE is on the heap.
1141 The DUPLICATE_VALUE is always 0. */
1142static void
1143incdep_record_variable_in_set (struct incdep *cur,
1144 const char *name, unsigned int name_length,
1145 const char *value,
1146 unsigned int value_length,
1147 int duplicate_value,
1148 enum variable_origin origin,
1149 int recursive,
1150 struct variable_set *set,
1151 const floc *flocp)
1152{
1153 assert (!duplicate_value);
1154 if (cur->worker_tid == -1)
1155 define_variable_in_set (name, name_length, value, value_length,
1156 duplicate_value, origin, recursive, set, flocp);
1157#ifdef PARSE_IN_WORKER
1158 else
1159 {
1160 struct incdep_variable_in_set *rec =
1161 (struct incdep_variable_in_set *)incdep_alloc_rec (cur);
1162 rec->name_entry = (struct strcache2_entry *)name;
1163 rec->value = value;
1164 rec->value_length = value_length;
1165 rec->duplicate_value = duplicate_value;
1166 rec->origin = origin;
1167 rec->recursive = recursive;
1168 rec->set = set;
1169 rec->flocp = flocp;
1170
1171 rec->next = NULL;
1172 if (cur->recorded_variables_in_set_tail)
1173 cur->recorded_variables_in_set_tail->next = rec;
1174 else
1175 cur->recorded_variables_in_set_head = rec;
1176 cur->recorded_variables_in_set_tail = rec;
1177 }
1178#endif
1179}
1180
1181/* Record / perform a variable definition. The VALUE should be disposed of. */
1182static void
1183incdep_record_variable_def (struct incdep *cur,
1184 const floc *flocp,
1185 const char *name,
1186 unsigned int name_length,
1187 char *value,
1188 unsigned int value_length,
1189 enum variable_origin origin,
1190 enum variable_flavor flavor,
1191 int target_var)
1192{
1193 if (cur->worker_tid == -1)
1194 do_variable_definition_2 (flocp, name, value, value_length, 0, value,
1195 origin, flavor, target_var);
1196#ifdef PARSE_IN_WORKER
1197 else
1198 {
1199 struct incdep_variable_def *rec =
1200 (struct incdep_variable_def *)incdep_alloc_rec (cur);
1201 rec->flocp = flocp;
1202 rec->name_entry = (struct strcache2_entry *)name;
1203 rec->value = value;
1204 rec->value_length = value_length;
1205 rec->origin = origin;
1206 rec->flavor = flavor;
1207 rec->target_var = target_var;
1208
1209 rec->next = NULL;
1210 if (cur->recorded_variable_defs_tail)
1211 cur->recorded_variable_defs_tail->next = rec;
1212 else
1213 cur->recorded_variable_defs_head = rec;
1214 cur->recorded_variable_defs_tail = rec;
1215 }
1216#else
1217 (void)name_length;
1218#endif
1219}
1220
1221/* Similar to record_files in read.c, only much much simpler. */
1222static void
1223incdep_commit_recorded_file (const char *filename, struct dep *deps,
1224 const floc *flocp)
1225{
1226 struct file *f;
1227
1228 /* Perform some validations. */
1229 if (filename[0] == '.'
1230 && ( streq(filename, ".POSIX")
1231 || streq(filename, ".EXPORT_ALL_VARIABLES")
1232 || streq(filename, ".INTERMEDIATE")
1233 || streq(filename, ".LOW_RESOLUTION_TIME")
1234 || streq(filename, ".NOTPARALLEL")
1235 || streq(filename, ".ONESHELL")
1236 || streq(filename, ".PHONY")
1237 || streq(filename, ".PRECIOUS")
1238 || streq(filename, ".SECONDARY")
1239 || streq(filename, ".SECONDTARGETEXPANSION")
1240 || streq(filename, ".SILENT")
1241 || streq(filename, ".SHELLFLAGS")
1242 || streq(filename, ".SUFFIXES")
1243 )
1244 )
1245 {
1246 OS (error, flocp, _("reserved filename '%s' used in dependency file, ignored"), filename);
1247 return;
1248 }
1249
1250 /* Lookup or create an entry in the database. */
1251 f = enter_file (filename);
1252 if (f->double_colon)
1253 {
1254 OS (error, flocp, _("dependency file '%s' has a double colon entry already, ignoring"), filename);
1255 return;
1256 }
1257 f->is_target = 1;
1258
1259 /* Append dependencies. */
1260 deps = enter_prereqs (deps, NULL);
1261 if (deps)
1262 {
1263 struct dep *last = f->deps;
1264 if (!last)
1265 f->deps = deps;
1266 else
1267 {
1268 while (last->next)
1269 last = last->next;
1270 last->next = deps;
1271 }
1272 }
1273}
1274
1275/* Record a file.*/
1276static void
1277incdep_record_file (struct incdep *cur,
1278 const char *filename,
1279 struct dep *deps,
1280 const floc *flocp)
1281{
1282 if (cur->worker_tid == -1)
1283 incdep_commit_recorded_file (filename, deps, flocp);
1284#ifdef PARSE_IN_WORKER
1285 else
1286 {
1287 struct incdep_recorded_file *rec =
1288 (struct incdep_recorded_file *) incdep_alloc_rec (cur);
1289
1290 rec->filename_entry = (struct strcache2_entry *)filename;
1291 rec->deps = deps;
1292 rec->flocp = flocp;
1293
1294 rec->next = NULL;
1295 if (cur->recorded_file_tail)
1296 cur->recorded_file_tail->next = rec;
1297 else
1298 cur->recorded_file_head = rec;
1299 cur->recorded_file_tail = rec;
1300 }
1301#endif
1302}
1303
1304/* Counts slashes backwards from SLASH, stopping at START. */
1305static size_t incdep_count_slashes_backwards(const char *slash, const char *start)
1306{
1307 size_t slashes = 1;
1308 assert (*slash == '\\');
1309 while ((uintptr_t)slash > (uintptr_t)start && slash[0 - slashes] == '\\')
1310 slashes++;
1311 return slashes;
1312}
1313
1314/* Whitespace cannot be escaped at the end of a line, there has to be
1315 some stuff following it other than a line continuation slash.
1316
1317 So, we look ahead and makes sure that there is something non-whitespaced
1318 following this allegedly escaped whitespace.
1319
1320 This code ASSUMES the file content is zero terminated! */
1321static int incdep_verify_escaped_whitespace(const char *ws)
1322{
1323 char ch;
1324
1325 assert(ws[-1] == '\\');
1326 assert(ISBLANK((unsigned int)ws[0]));
1327
1328 /* If the character following the '\ ' sequence is not a whitespace,
1329 another escape character or null terminator, we're good. */
1330 ws += 2;
1331 ch = *ws;
1332 if (ch != '\\' && !ISSPACE((unsigned int)ch) && ch != '\0')
1333 return 1;
1334
1335 /* Otherwise we'll have to parse forward till we hit the end of the
1336 line/file or something. */
1337 while ((ch = *ws++) != '\0')
1338 {
1339 if (ch == '\\')
1340 {
1341 /* escaped newline? */
1342 ch = *ws;
1343 if (ch == '\n')
1344 ws++;
1345 else if (ch == '\r' && ws[1] == '\n')
1346 ws += 2;
1347 else
1348 return 1;
1349 }
1350 else if (ISBLANK((unsigned int)ch))
1351 { /* contine */ }
1352 else if (!ISSPACE((unsigned int)ch))
1353 return 1;
1354 else
1355 return 0; /* newline; all trailing whitespace will be ignored. */
1356 }
1357
1358 return 0;
1359}
1360
1361/* Unescapes the next filename and returns cached copy.
1362
1363 Modifies the input string that START points to.
1364
1365 When NEXTP is not NULL, ASSUME target filename and that END isn't entirely
1366 accurate in case the filename ends with a trailing backslash. There can be
1367 more than one filename in a this case. NEXTP will be set to the first
1368 character after then filename.
1369
1370 When NEXTP is NULL, ASSUME exactly one dependency filename and that END is
1371 accurately deliminating the string.
1372 */
1373static const char *
1374incdep_unescape_and_cache_filename(struct incdep *curdep, char *start, const char *end,
1375 int const is_dep, const char **nextp, unsigned int *linenop)
1376{
1377 unsigned const esc_mask = MAP_BLANK /* ' ' + '\t' */
1378 | MAP_COLON /* ':' */
1379 | MAP_COMMENT /* '#' */
1380 | MAP_EQUALS /* '=' */
1381 | MAP_SEMI /* ';' */
1382 | ( is_dep
1383 ? MAP_PIPE /* '|' */
1384 : MAP_PERCENT); /* '%' */
1385 unsigned const all_esc_mask = esc_mask | MAP_BLANK | MAP_NEWLINE;
1386 unsigned const stop_mask = nextp ? MAP_BLANK | MAP_NEWLINE | (!is_dep ? MAP_COLON : 0) : 0;
1387 char volatile *src;
1388 char volatile *dst;
1389
1390 /*
1391 * Skip forward to the first escaped character so we can avoid unnecessary shifting.
1392 */
1393#if 1
1394 src = start;
1395 dst = start;
1396#elif 1
1397 static const char s_szStop[] = "\n\r\t ";
1398
1399 src = memchr(start, '$', end - start);
1400 dst = memchr(start, '\\', end - start);
1401 if (src && ((uintptr_t)src < (uintptr_t)dst || dst == NULL))
1402 dst = src;
1403 else if (dst && ((uintptr_t)dst < (uintptr_t)src || src == NULL))
1404 src = dst;
1405 else
1406 {
1407 assert(src == NULL && dst == NULL);
1408 if (nextp)
1409 {
1410 int i = sizeof(s_szStop);
1411 while (i-- > 0)
1412 {
1413 char *stop = memchr(start, s_szStop[i], end - start);
1414 if (stop)
1415 end = stop;
1416 }
1417 *nextp = end;
1418 }
1419 return incdep_dep_strcache (curdep, start, end - start);
1420 }
1421 if (nextp)
1422 {
1423 char *stop = src;
1424 int i = sizeof(s_szStop);
1425 while (i-- > 0)
1426 {
1427 char *stop2 = memchr(start, s_szStop[i], stop - start);
1428 if (stop2)
1429 stop = stop2;
1430 }
1431 if (stop != src)
1432 {
1433 *nextp = stop;
1434 return incdep_dep_strcache (curdep, start, stop - start);
1435 }
1436 }
1437#endif
1438
1439 /*
1440 * Copy char-by-char, undoing escaping as we go along.
1441 */
1442 while ((uintptr_t)src < (uintptr_t)end)
1443 {
1444 const char ch = *src++;
1445 if (ch != '\\' && ch != '$')
1446 {
1447 if (!STOP_SET (ch, stop_mask)
1448#ifdef HAVE_DOS_PATHS
1449 || ( ch == ':'
1450 && (uintptr_t)src < (uintptr_t)end
1451 && (*src == '/' || *src == '\\')
1452 && (dst - start) == 1
1453 && isalpha ((unsigned char)*start))
1454#endif
1455 )
1456 *dst++ = ch;
1457 else
1458 {
1459 src--;
1460 break;
1461 }
1462 }
1463 else
1464 {
1465 char ch2 = *src++; /* No bounds checking to handle "/dir/file\ : ..." when end points at " :". */
1466 if (ch == '$')
1467 {
1468 if (ch2 != '$') /* $$ -> $ - Ignores secondary expansion! */
1469 src--;
1470 *dst++ = ch;
1471 }
1472 else
1473 {
1474 unsigned int ch2_map;
1475
1476 /* Eat all the slashes and see what's at the end of them as that's all
1477 that's relevant. If there is an escapable char, we'll emit half of
1478 the slashes. */
1479 size_t const max_slashes = src - start - 1;
1480 size_t slashes = 1;
1481 while (ch2 == '\\')
1482 {
1483 slashes++;
1484 ch2 = *src++;
1485 }
1486
1487 /* Is it escapable? */
1488 ch2_map = stopchar_map[(unsigned char)ch2];
1489 if (ch2_map & all_esc_mask)
1490 {
1491 /* Non-whitespace is simple: Slash slashes, output or stop. */
1492 if (!(ch2_map & (MAP_BLANK | MAP_NEWLINE)))
1493 {
1494 assert(ch2_map & esc_mask);
1495 while (slashes >= 2)
1496 {
1497 *dst++ = '\\';
1498 slashes -= 2;
1499 }
1500 if (slashes || !(stop_mask & ch2_map))
1501 *dst++ = ch2;
1502 else
1503 {
1504 src--;
1505 break;
1506 }
1507 }
1508 /* Escaped blanks or newlines.
1509
1510 We have to pretent that we've already replaced any escaped newlines
1511 and associated whitespace with a single space here. We also have to
1512 pretend trailing whitespace doesn't exist when IS_DEP is non-zero.
1513 This makes for pretty interesting times... */
1514 else
1515 {
1516 char ch3;
1517
1518 /* An Escaped blank is interesting because it is striped unconditionally
1519 at the end of a line, regardless of how many escaped newlines may
1520 following it. We join the escaped newline handling if we fine one
1521 following us. */
1522 if (ch2_map & MAP_BLANK)
1523 {
1524 /* skip whitespace and check for escaped newline. */
1525 volatile char * const src_saved = src;
1526 while ((ch3 = *src) != '\0' && ISBLANK(ch3))
1527 src++;
1528 if (ch3 == '\\' && src[1] == '\n')
1529 src += 2; /* Escaped blank & newline joins into single space. */
1530 else if (ch3 == '\\' && src[1] == '\r' && src[2] == '\n')
1531 src += 3; /* -> Join the escaped newline code below on the next line. */
1532 else if (STOP_SET(ch3, stop_mask & MAP_NEWLINE))
1533 { /* last thing on the line, no blanks to escape. */
1534 while (slashes-- > 0)
1535 *dst++ = '\\';
1536 break;
1537 }
1538 else
1539 {
1540 src = src_saved;
1541 while (slashes >= 2)
1542 {
1543 *dst++ = '\\';
1544 slashes -= 2;
1545 }
1546 if (slashes)
1547 {
1548 *dst++ = ch2;
1549 continue;
1550 }
1551 assert (nextp || (uintptr_t)src >= (uintptr_t)end);
1552 break;
1553 }
1554 }
1555 /* Escaped newlines get special treatment as they an any adjacent whitespace
1556 gets reduced to a single space, including subsequent escaped newlines.
1557 In addition, if this is the final dependency/file and there is no
1558 significant new characters following this escaped newline, the replacement
1559 space will also be stripped and we won't have anything to escape, meaning
1560 that the slashes will remain as is. Finally, none of this space stuff can
1561 be stop characters, unless of course a newline isn't escaped. */
1562 else
1563 {
1564 assert (ch2_map & MAP_NEWLINE);
1565 if (ch2 == '\r' && *src == '\n')
1566 src++;
1567 }
1568
1569 /* common space/newline code */
1570 for (;;)
1571 {
1572 if (linenop)
1573 *linenop += 1;
1574 while ((uintptr_t)src < (uintptr_t)end && ISBLANK(*src))
1575 src++;
1576 if ((uintptr_t)src >= (uintptr_t)end)
1577 {
1578 ch3 = '\0';
1579 break;
1580 }
1581 ch3 = *src;
1582 if (ch3 != '\\')
1583 break;
1584 ch3 = src[1];
1585 if (ch3 == '\n')
1586 src += 2;
1587 else if (ch3 == '\r' && src[2] == '\n')
1588 src += 3;
1589 else
1590 break;
1591 }
1592
1593 if (is_dep && STOP_SET(ch3, stop_mask | MAP_NUL))
1594 { /* last thing on the line, no blanks to escape. */
1595 while (slashes-- > 0)
1596 *dst++ = '\\';
1597 break;
1598 }
1599 while (slashes >= 2)
1600 {
1601 *dst++ = '\\';
1602 slashes -= 2;
1603 }
1604 if (slashes)
1605 *dst++ = ' ';
1606 else
1607 {
1608 assert (nextp || (uintptr_t)src >= (uintptr_t)end);
1609 break;
1610 }
1611 }
1612 }
1613 /* Just output the slash if non-escapable character: */
1614 else
1615 {
1616 while (slashes-- > 0)
1617 *dst++ = '\\';
1618 src--;
1619 }
1620 }
1621 }
1622 }
1623
1624 if (nextp)
1625 *nextp = (const char *)src;
1626 return incdep_dep_strcache(curdep, start, dst - start);
1627}
1628
1629/* no nonsense dependency file including.
1630
1631 Because nobody wants bogus dependency files to break their incremental
1632 builds with hard to comprehend error messages, this function does not
1633 use the normal eval routine but does all the parsing itself. This isn't,
1634 as much work as it sounds, because the necessary feature set is very
1635 limited.
1636
1637 eval_include_dep_file groks:
1638
1639 define var
1640 endef
1641
1642 var [|:|?|>]= value [\]
1643
1644 [\]
1645 file: [deps] [\]
1646
1647 */
1648static void
1649eval_include_dep_file (struct incdep *curdep, floc *f)
1650{
1651 unsigned line_no = 1;
1652 const char *file_end = curdep->file_end;
1653 const char *cur = curdep->file_base;
1654 const char *endp;
1655
1656 /* if no file data, just return immediately. */
1657 if (!cur)
1658 return;
1659
1660 /* now parse the file. */
1661 while ((uintptr_t)cur < (uintptr_t)file_end)
1662 {
1663 /* skip empty lines */
1664 while ((uintptr_t)cur < (uintptr_t)file_end && ISSPACE (*cur) && *cur != '\n')
1665 ++cur;
1666 if ((uintptr_t)cur >= (uintptr_t)file_end)
1667 break;
1668 if (*cur == '#')
1669 {
1670 cur = memchr (cur, '\n', file_end - cur);
1671 if (!cur)
1672 break;
1673 }
1674 if (*cur == '\\')
1675 {
1676 unsigned eol_len = (file_end - cur > 1 && cur[1] == '\n') ? 2
1677 : (file_end - cur > 2 && cur[1] == '\r' && cur[2] == '\n') ? 3
1678 : (file_end - cur == 1) ? 1 : 0;
1679 if (eol_len)
1680 {
1681 cur += eol_len;
1682 line_no++;
1683 continue;
1684 }
1685 }
1686 if (*cur == '\n')
1687 {
1688 cur++;
1689 line_no++;
1690 continue;
1691 }
1692
1693 /* define var
1694 ...
1695 endef */
1696 if (strneq (cur, "define ", 7))
1697 {
1698 const char *var;
1699 unsigned var_len;
1700 const char *value_start;
1701 const char *value_end;
1702 char *value;
1703 unsigned value_len;
1704 int found_endef = 0;
1705
1706 /* extract the variable name. */
1707 cur += 7;
1708 while (ISBLANK (*cur))
1709 ++cur;
1710 value_start = endp = memchr (cur, '\n', file_end - cur);
1711 if (!endp)
1712 endp = cur;
1713 while (endp > cur && ISSPACE (endp[-1]))
1714 --endp;
1715 var_len = endp - cur;
1716 if (!var_len)
1717 {
1718 incdep_warn (curdep, line_no, "bogus define statement.");
1719 break;
1720 }
1721 var = incdep_var_strcache (curdep, cur, var_len);
1722
1723 /* find the end of the variable. */
1724 cur = value_end = value_start = value_start + 1;
1725 ++line_no;
1726 while ((uintptr_t)cur < (uintptr_t)file_end)
1727 {
1728 /* check for endef, don't bother with skipping leading spaces. */
1729 if ( file_end - cur >= 5
1730 && strneq (cur, "endef", 5))
1731 {
1732 endp = cur + 5;
1733 while ((uintptr_t)endp < (uintptr_t)file_end && ISSPACE (*endp) && *endp != '\n')
1734 endp++;
1735 if ((uintptr_t)endp >= (uintptr_t)file_end || *endp == '\n')
1736 {
1737 found_endef = 1;
1738 cur = (uintptr_t)endp >= (uintptr_t)file_end ? file_end : endp + 1;
1739 break;
1740 }
1741 }
1742
1743 /* skip a line ahead. */
1744 cur = value_end = memchr (cur, '\n', file_end - cur);
1745 if (cur != NULL)
1746 ++cur;
1747 else
1748 cur = value_end = file_end;
1749 ++line_no;
1750 }
1751
1752 if (!found_endef)
1753 {
1754 incdep_warn (curdep, line_no, "missing endef, dropping the rest of the file.");
1755 break;
1756 }
1757 value_len = value_end - value_start;
1758 if (memchr (value_start, '\0', value_len))
1759 {
1760 incdep_warn (curdep, line_no, "'\\0' in define, dropping the rest of the file.");
1761 break;
1762 }
1763
1764 /* make a copy of the value, converting \r\n to \n, and define it. */
1765 value = incdep_xmalloc (curdep, value_len + 1);
1766 endp = memchr (value_start, '\r', value_len);
1767 if (endp)
1768 {
1769 const char *src = value_start;
1770 char *dst = value;
1771 for (;;)
1772 {
1773 size_t len = endp - src;
1774 memcpy (dst, src, len);
1775 dst += len;
1776 src = endp;
1777 if ((uintptr_t)src + 1 < (uintptr_t)file_end && src[1] == '\n')
1778 src++; /* skip the '\r' */
1779 if (src >= value_end)
1780 break;
1781 endp = memchr (endp + 1, '\r', src - value_end);
1782 if (!endp)
1783 endp = value_end;
1784 }
1785 value_len = dst - value;
1786 }
1787 else
1788 memcpy (value, value_start, value_len);
1789 value [value_len] = '\0';
1790
1791 incdep_record_variable_in_set (curdep,
1792 var, var_len, value, value_len,
1793 0 /* don't duplicate */, o_file,
1794 0 /* defines are recursive but this is faster */,
1795 NULL /* global set */, f);
1796 }
1797
1798 /* file: deps
1799 OR
1800 variable [:]= value */
1801 else
1802 {
1803 const char *equalp;
1804 const char *eol;
1805
1806 /* Look for a colon or an equal sign. In the assignment case, we
1807 require it to be on the same line as the variable name to simplify
1808 the code. Because of clang, we cannot make the same assumptions
1809 with file dependencies. So, start with the equal. */
1810
1811 assert (*cur != '\n');
1812 eol = memchr (cur, '\n', file_end - cur);
1813 if (!eol)
1814 eol = file_end;
1815 equalp = memchr (cur, '=', eol - cur);
1816 if (equalp && equalp != cur && (ISSPACE(equalp[-1]) || equalp[-1] != '\\'))
1817 {
1818 /* An assignment of some sort. */
1819 const char *var;
1820 unsigned var_len;
1821 const char *value_start;
1822 const char *value_end;
1823 char *value;
1824 unsigned value_len;
1825 unsigned multi_line = 0;
1826 enum variable_flavor flavor;
1827
1828 /* figure the flavor first. */
1829 flavor = f_recursive;
1830 if (equalp > cur)
1831 {
1832 if (equalp[-1] == ':')
1833 flavor = f_simple;
1834 else if (equalp[-1] == '?')
1835 flavor = f_conditional;
1836 else if (equalp[-1] == '+')
1837 flavor = f_append;
1838 else if (equalp[-1] == '>')
1839 flavor = f_prepend;
1840 }
1841
1842 /* extract the variable name. */
1843 endp = flavor == f_recursive ? equalp : equalp - 1;
1844 while (endp > cur && ISBLANK (endp[-1]))
1845 --endp;
1846 var_len = endp - cur;
1847 if (!var_len)
1848 {
1849 incdep_warn (curdep, line_no, "empty variable. (includedep)");
1850 break;
1851 }
1852 if ( memchr (cur, '$', var_len)
1853 || memchr (cur, ' ', var_len)
1854 || memchr (cur, '\t', var_len))
1855 {
1856 incdep_warn (curdep, line_no, "fancy variable name. (includedep)");
1857 break;
1858 }
1859 var = incdep_var_strcache (curdep, cur, var_len);
1860
1861 /* find the start of the value. */
1862 cur = equalp + 1;
1863 while ((uintptr_t)cur < (uintptr_t)file_end && ISBLANK (*cur))
1864 cur++;
1865 value_start = cur;
1866
1867 /* find the end of the value / line (this isn't 101% correct). */
1868 value_end = cur;
1869 while ((uintptr_t)cur < (uintptr_t)file_end)
1870 {
1871 endp = value_end = memchr (cur, '\n', file_end - cur);
1872 if (!value_end)
1873 value_end = file_end;
1874 if (value_end - 1 >= cur && value_end[-1] == '\r')
1875 --value_end;
1876 if (value_end - 1 < cur || value_end[-1] != '\\')
1877 {
1878 cur = endp ? endp + 1 : file_end;
1879 break;
1880 }
1881 --value_end;
1882 if (value_end - 1 >= cur && value_end[-1] == '\\')
1883 {
1884 incdep_warn (curdep, line_no, "fancy escaping! (includedep)");
1885 cur = NULL;
1886 break;
1887 }
1888 if (!endp)
1889 {
1890 cur = file_end;
1891 break;
1892 }
1893
1894 cur = endp + 1;
1895 ++multi_line;
1896 ++line_no;
1897 }
1898 if (!cur)
1899 break;
1900 ++line_no;
1901
1902 /* make a copy of the value, converting \r\n to \n, and define it. */
1903 value_len = value_end - value_start;
1904 value = incdep_xmalloc (curdep, value_len + 1);
1905 if (!multi_line)
1906 memcpy (value, value_start, value_len);
1907 else
1908 {
1909 /* unescape it */
1910 const char *src = value_start;
1911 char *dst = value;
1912 while (src < value_end)
1913 {
1914 const char *nextp;
1915
1916 endp = memchr (src, '\n', value_end - src);
1917 if (!endp)
1918 nextp = endp = value_end;
1919 else
1920 nextp = endp + 1;
1921 if (endp > src && endp[-1] == '\r')
1922 --endp;
1923 if (endp > src && endp[-1] == '\\')
1924 --endp;
1925
1926 if (src != value_start)
1927 *dst++ = ' ';
1928 memcpy (dst, src, endp - src);
1929 dst += endp - src;
1930 src = nextp;
1931 }
1932 value_len = dst - value;
1933 }
1934 value [value_len] = '\0';
1935
1936 /* do the definition */
1937 if (flavor == f_recursive
1938 || ( flavor == f_simple
1939 && !memchr (value, '$', value_len)))
1940 incdep_record_variable_in_set (curdep,
1941 var, var_len, value, value_len,
1942 0 /* don't duplicate */, o_file,
1943 flavor == f_recursive /* recursive */,
1944 NULL /* global set */, f);
1945 else
1946 incdep_record_variable_def (curdep,
1947 f, var, var_len, value, value_len,
1948 o_file, flavor, 0 /* not target var */);
1949 }
1950 else
1951 {
1952 /* Expecting: file: dependencies */
1953
1954 int unescape_filename = 0;
1955 const char *filename;
1956 const char *fnnext;
1957 const char *fnend;
1958 const char *colonp;
1959 struct dep *deps = 0;
1960 struct dep **nextdep = &deps;
1961 struct dep *dep;
1962
1963
1964 /* Locate the next file colon. If it's not within the bounds of
1965 the current line, check that all new line chars are escaped. */
1966
1967 colonp = memchr (cur, ':', file_end - cur);
1968 while ( colonp
1969 && ( ( colonp != cur
1970 && colonp[-1] == '\\'
1971 && incdep_count_slashes_backwards (&colonp[-1], cur) & 1)
1972#ifdef HAVE_DOS_PATHS
1973 || ( colonp + 1 < file_end
1974 && (colonp[1] == '/' || colonp[1] == '\\')
1975 && colonp > cur
1976 && isalpha ((unsigned char)colonp[-1])
1977 && ( colonp == cur + 1
1978 || ISBLANK ((unsigned char)colonp[-2])))
1979#endif
1980 )
1981 )
1982 colonp = memchr (colonp + 1, ':', file_end - (colonp + 1));
1983 if (!colonp)
1984 {
1985 incdep_warn (curdep, line_no, "no colon.");
1986 break;
1987 }
1988
1989 if ((uintptr_t)colonp < (uintptr_t)eol)
1990 unescape_filename = memchr (cur, '\\', colonp - cur) != NULL
1991 || memchr (cur, '$', colonp - cur) != NULL;
1992 else if (memchr (eol, '=', colonp - eol))
1993 {
1994 incdep_warn (curdep, line_no, "multi line assignment / dependency confusion.");
1995 break;
1996 }
1997 else
1998 {
1999 const char *sol = cur;
2000 do
2001 {
2002 char *eol2 = (char *)eol - 1;
2003 if ((uintptr_t)eol2 >= (uintptr_t)sol && *eol2 == '\r') /* DOS line endings. */
2004 eol2--;
2005 if ((uintptr_t)eol2 < (uintptr_t)sol || *eol2 != '\\')
2006 incdep_warn (curdep, line_no, "no colon.");
2007 else if (eol2 != sol && eol2[-1] == '\\')
2008 incdep_warn (curdep, line_no, "fancy EOL escape. (includedep)");
2009 else
2010 {
2011 line_no++;
2012 sol = eol + 1;
2013 eol = memchr (sol, '\n', colonp - sol);
2014 continue;
2015 }
2016 sol = NULL;
2017 break;
2018 }
2019 while (eol != NULL);
2020 if (!sol)
2021 break;
2022 unescape_filename = 1;
2023 }
2024
2025 /* Extract the first filename after trimming and basic checks. */
2026 fnend = colonp;
2027 while ((uintptr_t)fnend > (uintptr_t)cur && ISBLANK (fnend[-1]))
2028 --fnend;
2029 if (cur == fnend)
2030 {
2031 incdep_warn (curdep, line_no, "empty filename.");
2032 break;
2033 }
2034 fnnext = cur;
2035 if (!unescape_filename)
2036 {
2037 while (fnnext != fnend && !ISBLANK (*fnnext))
2038 fnnext++;
2039 filename = incdep_dep_strcache (curdep, cur, fnnext - cur);
2040 }
2041 else
2042 filename = incdep_unescape_and_cache_filename (curdep, (char *)fnnext, fnend, 0, &fnnext, NULL);
2043
2044 /* parse any dependencies. */
2045 cur = colonp + 1;
2046 while ((uintptr_t)cur < (uintptr_t)file_end)
2047 {
2048 const char *dep_file;
2049
2050 /* skip blanks and count lines. */
2051 char ch = 0;
2052 while ((uintptr_t)cur < (uintptr_t)file_end && ISSPACE ((ch = *cur)) && ch != '\n')
2053 ++cur;
2054 if ((uintptr_t)cur >= (uintptr_t)file_end)
2055 break;
2056 if (ch == '\n')
2057 {
2058 cur++;
2059 line_no++;
2060 break;
2061 }
2062
2063 /* continuation + eol? */
2064 if (ch == '\\')
2065 {
2066 unsigned eol_len = (file_end - cur > 1 && cur[1] == '\n') ? 2
2067 : (file_end - cur > 2 && cur[1] == '\r' && cur[2] == '\n') ? 3
2068 : (file_end - cur == 1) ? 1 : 0;
2069 if (eol_len)
2070 {
2071 cur += eol_len;
2072 line_no++;
2073 continue;
2074 }
2075 }
2076
2077 /* find the end of the filename and cache it */
2078 dep_file = NULL;
2079 endp = cur;
2080 for (;;)
2081 if ((uintptr_t)endp < (uintptr_t)file_end)
2082 {
2083 ch = *endp;
2084 if (ch != '\\' && ch != '$' )
2085 {
2086 if (!ISSPACE (ch))
2087 endp++;
2088 else
2089 {
2090 dep_file = incdep_dep_strcache(curdep, cur, endp - cur);
2091 break;
2092 }
2093 }
2094 else
2095 {
2096 /* potential escape sequence, let the unescaper do the rest. */
2097 dep_file = incdep_unescape_and_cache_filename (curdep, (char *)cur, file_end, 1, &endp, &line_no);
2098 break;
2099 }
2100 }
2101 else
2102 {
2103 dep_file = incdep_dep_strcache(curdep, cur, endp - cur);
2104 break;
2105 }
2106
2107 /* add it to the list. */
2108 *nextdep = dep = incdep_alloc_dep (curdep);
2109 dep->includedep = 1;
2110 dep->name = dep_file;
2111 nextdep = &dep->next;
2112
2113 cur = endp;
2114 }
2115
2116 /* enter the file with its dependencies. */
2117 incdep_record_file (curdep, filename, deps, f);
2118
2119 /* More files? Record them with the same dependency list. */
2120 if ((uintptr_t)fnnext < (uintptr_t)fnend)
2121 for (;;)
2122 {
2123 const char *filename_prev = filename;
2124 while (fnnext != fnend && ISBLANK (*fnnext))
2125 fnnext++;
2126 if (fnnext == fnend)
2127 break;
2128 if (*fnnext == '\\')
2129 {
2130 if (fnnext[1] == '\n')
2131 {
2132 line_no++;
2133 fnnext += 2;
2134 continue;
2135 }
2136 if (fnnext[1] == '\r' && fnnext[2] == '\n')
2137 {
2138 line_no++;
2139 fnnext += 3;
2140 continue;
2141 }
2142 }
2143
2144 if (!unescape_filename)
2145 {
2146 const char *fnstart = fnnext;
2147 while (fnnext != fnend && !ISBLANK (*fnnext))
2148 fnnext++;
2149 filename = incdep_dep_strcache (curdep, fnstart, fnnext - fnstart);
2150 }
2151 else
2152 filename = incdep_unescape_and_cache_filename (curdep, (char *)fnnext, fnend, 0, &fnnext, NULL);
2153 if (filename != filename_prev) /* clang optimization, it emit two targets for each dependency list. */
2154 incdep_record_file (curdep, filename, incdep_dup_dep_list (curdep, deps), f);
2155 }
2156 }
2157 }
2158 }
2159
2160 /* free the file data */
2161 incdep_xfree (curdep, curdep->file_base);
2162 curdep->file_base = curdep->file_end = NULL;
2163}
2164
2165/* Flushes the incdep todo and done lists. */
2166static void
2167incdep_flush_it (floc *f)
2168{
2169 struct incdep_worker_data *thrd_data = &incdep_worker_data[INCDEP_MAX_THREADS];
2170
2171 incdep_lock ();
2172 for (;;)
2173 {
2174 struct incdep *cur = incdep_head_done;
2175 unsigned int count = incdep_count_done;
2176
2177 /* if the done list is empty, grab a todo list entry. */
2178 if (!cur)
2179 {
2180 cur = incdep_head_todo;
2181 if (cur)
2182 {
2183 if (cur->next)
2184 {
2185 assert (incdep_count_todo > 1);
2186 assert (cur != incdep_tail_todo);
2187 incdep_head_todo = cur->next;
2188 }
2189 else
2190 {
2191 assert (incdep_count_todo == 1);
2192 assert (cur == incdep_tail_todo);
2193 incdep_head_todo = incdep_tail_todo = NULL;
2194 }
2195 incdep_count_todo--;
2196 thrd_data->todo_count--;
2197 incdep_unlock ();
2198
2199 incdep_read_file (cur, f);
2200 eval_include_dep_file (cur, f);
2201 incdep_freeit (cur);
2202
2203 incdep_lock ();
2204 continue;
2205 }
2206 }
2207
2208 /* if the todo list and done list are empty we're either done
2209 or will have to wait for the thread(s) to finish. */
2210 if (!cur && !incdep_num_reading)
2211 break; /* done */
2212 if (!cur)
2213 {
2214 while (!incdep_head_done)
2215 incdep_wait_done ();
2216 cur = incdep_head_done;
2217 }
2218
2219 /* we grab the entire done list and work thru it. */
2220 incdep_head_done = incdep_tail_done = NULL;
2221 incdep_count_done = 0;
2222
2223 incdep_unlock ();
2224
2225 while (cur)
2226 {
2227 struct incdep *next = cur->next;
2228 assert (count > 0);
2229#ifdef PARSE_IN_WORKER
2230 incdep_flush_recorded_instructions (cur);
2231#else
2232 eval_include_dep_file (cur, f);
2233#endif
2234 incdep_freeit (cur);
2235 thrd_data->flushed_count++;
2236 count--;
2237 cur = next;
2238 }
2239 assert (count == 0);
2240
2241 incdep_lock ();
2242 } /* outer loop */
2243 incdep_unlock ();
2244}
2245
2246
2247/* splits up a list of file names and feeds it to eval_include_dep_file,
2248 employing threads to try speed up the file reading. */
2249void
2250eval_include_dep (const char *names, floc *f, enum incdep_op op)
2251{
2252 struct incdep_worker_data *thrd_data = &incdep_worker_data[INCDEP_MAX_THREADS];
2253 struct incdep *head = 0;
2254 struct incdep *tail = 0;
2255 struct incdep *cur;
2256 unsigned int count = 0;
2257 const char *names_iterator = names;
2258 const char *name;
2259 unsigned int name_len;
2260
2261#if 0 /* sanity / debug */
2262 {
2263 struct incdep *check, *checktail;
2264 unsigned items;
2265 for (items = 0, check = checktail = incdep_head_todo; check; check = check->next)
2266 checktail = check, items++;
2267 MY_ASSERT (items == incdep_count_todo);
2268 MY_ASSERT (checktail == incdep_tail_todo);
2269 }
2270#endif
2271
2272 /* loop through NAMES, creating a todo list out of them. */
2273
2274 while ((name = find_next_token (&names_iterator, &name_len)) != 0)
2275 {
2276#ifdef INCDEP_USE_KFSCACHE
2277 KFSLOOKUPERROR enmError;
2278 PKFSOBJ pFileObj = kFsCacheLookupWithLengthA (g_pFsCache, name, name_len, &enmError);
2279 if (!pFileObj)
2280 continue;
2281 if (pFileObj->bObjType != KFSOBJ_TYPE_FILE)
2282 {
2283 kFsCacheObjRelease (g_pFsCache, pFileObj);
2284 continue;
2285 }
2286
2287 cur = xmalloc (sizeof (*cur)); /* not incdep_xmalloc here */
2288 cur->pFileObj = pFileObj;
2289#else
2290 cur = xmalloc (sizeof (*cur) + name_len); /* not incdep_xmalloc here */
2291 memcpy (cur->name, name, name_len);
2292 cur->name[name_len] = '\0';
2293#endif
2294
2295 cur->file_base = cur->file_end = NULL;
2296 cur->worker_tid = -1;
2297#ifdef PARSE_IN_WORKER
2298 cur->err_line_no = 0;
2299 cur->err_msg = NULL;
2300 cur->recorded_variables_in_set_head = NULL;
2301 cur->recorded_variables_in_set_tail = NULL;
2302 cur->recorded_variable_defs_head = NULL;
2303 cur->recorded_variable_defs_tail = NULL;
2304 cur->recorded_file_head = NULL;
2305 cur->recorded_file_tail = NULL;
2306#endif
2307
2308 cur->next = NULL;
2309 if (tail)
2310 tail->next = cur;
2311 else
2312 head = cur;
2313 tail = cur;
2314 count++;
2315 }
2316
2317#ifdef ELECTRIC_HEAP
2318 if (1)
2319#else
2320 if (op == incdep_read_it)
2321#endif
2322 {
2323 /* work our way thru the files directly */
2324
2325 cur = head;
2326 while (cur)
2327 {
2328 struct incdep *next = cur->next;
2329 incdep_read_file (cur, f);
2330 eval_include_dep_file (cur, f);
2331 incdep_freeit (cur);
2332 cur = next;
2333 }
2334 }
2335 else
2336 {
2337 if (head)
2338 {
2339 struct incdep *tmp;
2340
2341 /* initialize the worker threads and related stuff the first time around. */
2342
2343 if (!incdep_initialized)
2344 incdep_init (f);
2345
2346 /* queue the files and notify the worker threads. */
2347
2348 incdep_lock ();
2349
2350 tmp = incdep_tail_todo;
2351 if (tmp)
2352 {
2353 assert (incdep_count_todo > 0);
2354 assert (incdep_head_todo != NULL);
2355 tmp->next = head;
2356 }
2357 else
2358 {
2359 assert (incdep_count_todo == 0);
2360 assert (incdep_head_todo == NULL);
2361 incdep_head_todo = head;
2362 }
2363 incdep_tail_todo = tail;
2364 incdep_count_todo += count;
2365 thrd_data->todo_count += count;
2366
2367#if 0 /* sanity / debug */
2368 {
2369 struct incdep *check, *checktail;
2370 unsigned items;
2371 for (items = 0, check = checktail = incdep_head_todo; check; check = check->next)
2372 checktail = check, items++;
2373 MY_ASSERT (items == incdep_count_todo);
2374 MY_ASSERT (checktail == incdep_tail_todo);
2375 }
2376#endif
2377
2378 incdep_signal_todo ();
2379 incdep_unlock ();
2380 }
2381
2382 /* flush the todo queue if we're requested to do so. */
2383
2384 if (op == incdep_flush)
2385 incdep_flush_it (f);
2386 }
2387}
2388
2389#endif /* CONFIG_WITH_INCLUDEDEP */
2390
Note: See TracBrowser for help on using the repository browser.