VirtualBox

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

Last change on this file since 3387 was 3318, checked in by bird, 4 years ago

kmk/incdep.c: Unescaping.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use