VirtualBox

source: kBuild/trunk/src/kmk/incdep.c@ 3619

Last change on this file since 3619 was 3619, checked in by bird, 6 months ago

kmk/incdep.c: better accounting for debugging race/whatever

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette