bump product version to 6.4.0.3
[LibreOffice.git] / solenv / bin / concat-deps.c
blob946270998c08998006b173156f8c1081d9dd68a2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2011 Norbert Thiebaud
4 * License: GPLv3
5 */
7 /* define to activate stats reporting on hash usage*/
8 /* #define HASH_STAT */
10 /* ===============================================
11 * Set-up: defines to identify the system and system related properties
12 * ===============================================
15 #ifdef __APPLE__
16 #ifdef __x86_64__
17 #undef CORE_BIG_ENDIAN
18 #define CORE_LITTLE_ENDIAN
19 #else
20 #define CORE_BIG_ENDIAN
21 #undef CORE_LITTLE_ENDIAN
22 #endif
24 #endif
25 #ifdef _AIX
26 #define CORE_BIG_ENDIAN
27 #undef CORE_LITTLE_ENDIAN
28 #endif /* Def _AIX */
30 #ifdef _MSC_VER
31 #undef CORE_BIG_ENDIAN
32 #define CORE_LITTLE_ENDIAN
33 #endif /* Def _MSC_VER */
35 #if defined(__linux) || defined(__FreeBSD_kernel__)
36 #include <sys/param.h>
37 #if __BYTE_ORDER == __LITTLE_ENDIAN
38 #undef CORE_BIG_ENDIAN
39 #define CORE_LITTLE_ENDIAN
40 #else /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
41 #if __BYTE_ORDER == __BIG_ENDIAN
42 #define CORE_BIG_ENDIAN
43 #undef CORE_LITTLE_ENDIAN
44 #endif /* __BYTE_ORDER == __BIG_ENDIAN */
45 #endif /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
46 #endif /* Def __linux */
48 #if defined(__OpenBSD__) || defined(__FreeBSD__) || \
49 defined(__NetBSD__) || defined(__DragonFly__)
50 #include <machine/endian.h>
51 #if _BYTE_ORDER == _LITTLE_ENDIAN
52 #undef CORE_BIG_ENDIAN
53 #define CORE_LITTLE_ENDIAN
54 #else /* !(_BYTE_ORDER == _LITTLE_ENDIAN) */
55 #if _BYTE_ORDER == _BIG_ENDIAN
56 #define CORE_BIG_ENDIAN
57 #undef CORE_LITTLE_ENDIAN
58 #endif /* _BYTE_ORDER == _BIG_ENDIAN */
59 #endif /* !(_BYTE_ORDER == _LITTLE_ENDIAN) */
60 #endif /* Def *BSD */
62 #if defined(__HAIKU__)
63 #include <endian.h>
64 #if __BYTE_ORDER == __LITTLE_ENDIAN
65 #undef CORE_BIG_ENDIAN
66 #define CORE_LITTLE_ENDIAN
67 #else /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
68 #if __BYTE_ORDER == __BIG_ENDIAN
69 #define CORE_BIG_ENDIAN
70 #undef CORE_LITTLE_ENDIAN
71 #endif /* __BYTE_ORDER == __BIG_ENDIAN */
72 #endif /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
73 #endif /* Def __HAIKU__ */
75 #ifdef __sun
76 #ifdef __sparc
77 #define CORE_BIG_ENDIAN
78 #undef CORE_LITTLE_ENDIAN
79 #else /* Ndef __sparc */
80 #undef CORE_BIG_ENDIAN
81 #define CORE_LITTLE_ENDIAN
82 #endif /* Ndef __sparc */
83 #endif /* Def __sun */
85 #include <assert.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <sys/types.h>
89 #include <sys/stat.h>
90 #include <errno.h>
91 #include <fcntl.h>
92 #include <string.h>
93 #include <ctype.h>
95 #ifdef _MSC_VER
96 #include <io.h>
97 #else
98 #include <unistd.h>
99 #endif
101 #include <config_options.h>
103 /* modes */
104 #ifdef _MSC_VER
105 #define FILE_O_RDONLY _O_RDONLY
106 #define FILE_O_BINARY _O_BINARY
107 #define PATHNCMP _strnicmp /* MSVC converts paths to lower-case sometimes? */
108 #define ssize_t long
109 #define S_ISREG(mode) (((mode) & _S_IFMT) == (_S_IFREG)) /* MSVC does not have this macro */
110 #else /* not windaube */
111 #define FILE_O_RDONLY O_RDONLY
112 #define FILE_O_BINARY 0
113 #define PATHNCMP strncmp
114 #endif /* not windaube */
116 #ifndef TRUE
117 #define TRUE 1
118 #endif
119 #ifndef FALSE
120 #define FALSE 0
121 #endif
123 static int internal_boost = 0;
124 static char* base_dir;
125 static char* work_dir;
126 static size_t work_dir_len;
128 #ifdef __GNUC__
129 #define clz __builtin_clz
130 #else
131 static int clz(unsigned int value)
133 int result = 32;
135 while(value)
137 value >>= 1;
138 result -= 1;
140 return result;
142 #endif
144 static unsigned int get_unaligned_uint(const unsigned char* cursor)
146 unsigned int result;
148 memcpy(&result, cursor, sizeof(unsigned int));
149 return result;
152 /* ===============================================
153 * memory pool for fast fix-size allocation (non-thread-safe)
154 * ===============================================
156 struct pool
158 void* head_free; /**< head of a linked list of freed element */
159 char* fresh; /**< top of a memory block to dig new element */
160 char* tail; /**< to detect end of extent... when fresh pass tail */
161 void* extent; /**< pointer to the primary extent block */
162 int size_elem; /**< size of an element. */
163 int primary; /**< primary allocation in bytes */
164 int secondary; /**< secondary allocation in bytes */
166 #define POOL_ALIGN_INCREMENT 8 /**< alignment, must be a power of 2 and of size > to sizeof(void*) */
169 static void* pool_take_extent(struct pool* pool, int allocate)
171 unsigned int size = 0;
172 void* extent;
173 void* data = NULL;
175 if(pool->extent)
177 /* we already have an extent, so this is a secondary */
178 if(pool->secondary)
180 size = pool->secondary;
183 else
185 assert(pool->primary);
186 size = pool->primary;
188 if(size)
190 extent = malloc(size);
191 if(extent)
193 *(void**)extent = pool->extent;
194 pool->extent = extent;
195 if(allocate)
197 data = ((char*)extent) + POOL_ALIGN_INCREMENT;
198 pool->fresh = ((char*)data) + pool->size_elem;
199 pool->tail = pool->fresh + (size - pool->size_elem);
201 else
203 pool->fresh = ((char*)extent) + POOL_ALIGN_INCREMENT;
204 pool->tail = pool->fresh + (size - pool->size_elem);
208 return data;
211 /* Create a memory pool for fix size objects
212 * this is a simplified implementation that
213 * is _not_ thread safe.
215 static struct pool* pool_create(int size_elem, int primary, int secondary)
217 struct pool* pool;
219 assert(primary > 0);
220 assert(secondary >= 0);
221 assert(size_elem > 0);
223 pool = (struct pool*)calloc(1, sizeof(struct pool));
224 if(!pool) return NULL;
225 /* Adjust the element size so that it be aligned, and so that an element could
226 * at least contain a void*
228 pool->size_elem = size_elem = (size_elem + POOL_ALIGN_INCREMENT - 1) & ~(POOL_ALIGN_INCREMENT - 1);
230 pool->primary = (size_elem * primary) + POOL_ALIGN_INCREMENT;
231 pool->secondary = secondary > 0 ? (size_elem * secondary) + POOL_ALIGN_INCREMENT : 0;
232 pool_take_extent(pool, FALSE);
234 return pool;
238 static void pool_destroy(struct pool* pool)
240 void* extent;
241 void* next;
243 if(pool != NULL)
245 extent = pool->extent;
246 while(extent)
248 next = *(void**)extent;
249 free(extent);
250 extent = next;
252 free(pool);
256 static void* pool_alloc(struct pool* pool)
258 void* data;
260 data = pool->head_free;
261 if(data == NULL)
263 /* we have no old-freed elem */
264 if(pool->fresh <= pool->tail)
266 /* pick a slice of the current extent */
267 data = (void*)pool->fresh;
268 pool->fresh += pool->size_elem;
270 else
272 /* allocate a new extent */
273 data = pool_take_extent(pool, TRUE);
276 else
278 /* re-used old freed element by chopping the head of the free list */
279 pool->head_free = *(void**)data;
282 return data;
286 /* ===============================================
287 * Hash implementation customized to be just tracking
288 * a unique list of string (i.e no data associated
289 * with the key, no need for retrieval, etc...
291 * This is tuned for the particular use-case we have here
292 * measures in tail_build showed that
293 * we can get north of 4000 distinct values stored in a hash
294 * the collision rate is at worse around 2%
295 * the collision needing an expensive memcmp to resolve
296 * have a rate typically at 1 per 1000
297 * for tail_build we register 37229 unique key
298 * with a total of 377 extra memcmp needed
299 * which is completely negligible compared to the
300 * number of memcmp required to eliminate duplicate
301 * entry (north of 2.5 millions for tail_build)
302 * ===============================================
305 struct hash_elem
307 struct hash_elem* next;
308 const char* key;
309 int key_len;
312 struct hash
314 struct hash_elem** array;
315 struct pool* elems_pool;
316 unsigned int used;
317 unsigned int size;
318 unsigned int load_limit;
319 #ifdef HASH_STAT
320 int stored;
321 int collisions;
322 int cost;
323 int memcmp;
324 #endif
327 /* The following hash_compute function was adapted from :
328 * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
330 * The changes from the original are mostly cosmetic
332 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
335 #if defined CORE_BIG_ENDIAN
336 #define MASK_C1 0xFFFFFF00
337 #define MASK_C2 0xFFFF0000
338 #define MASK_C3 0xFF000000
339 #elif defined CORE_LITTLE_ENDIAN
340 #define MASK_C1 0xFFFFFF
341 #define MASK_C2 0xFFFF
342 #define MASK_C3 0xFF
343 #else
344 #error "Missing Endianness definition"
345 #endif
348 #define mix(a,b,c) \
350 a -= c; a ^= rot(c, 4); c += b; \
351 b -= a; b ^= rot(a, 6); a += c; \
352 c -= b; c ^= rot(b, 8); b += a; \
353 a -= c; a ^= rot(c,16); c += b; \
354 b -= a; b ^= rot(a,19); a += c; \
355 c -= b; c ^= rot(b, 4); b += a; \
357 #define final(a,b,c) \
359 c ^= b; c -= rot(b,14); \
360 a ^= c; a -= rot(c,11); \
361 b ^= a; b -= rot(a,25); \
362 c ^= b; c -= rot(b,16); \
363 a ^= c; a -= rot(c,4); \
364 b ^= a; b -= rot(a,14); \
365 c ^= b; c -= rot(b,24); \
368 static unsigned int hash_compute( struct hash const * hash, const char* key, int length)
370 unsigned int a;
371 unsigned int b;
372 unsigned int c; /* internal state */
373 const unsigned char* uk = (const unsigned char*)key;
375 /* Set up the internal state */
376 a = b = c = 0xdeadbeef + (length << 2);
378 /* we use this to 'hash' full path with mostly a common root
379 * let's now waste too much cycles hashing mostly constant stuff
381 if(length > 36)
383 uk += length - 36;
384 length = 36;
386 /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
387 while (length > 12)
389 a += get_unaligned_uint(uk);
390 b += get_unaligned_uint(uk+4);
391 c += get_unaligned_uint(uk+8);
392 mix(a,b,c);
393 length -= 12;
394 uk += 12;
397 /*----------------------------- handle the last (probably partial) block */
398 /* Note: we possibly over-read, which would trigger complaint from VALGRIND
399 * but we mask the undefined stuff if any, so we are still good, thanks
400 * to alignment of memory allocation and tail-memory management overhead
401 * we always can read 3 bytes past the official end without triggering
402 * a segfault -- if you find a platform/compiler couple for which that postulate
403 * is false, then you just need to over-allocate by 2 more bytes in file_load()
404 * file_load already over-allocate by 1 to stick a \0 at the end of the buffer.
406 switch(length)
408 case 12: c+=get_unaligned_uint(uk+8); b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
409 case 11: c+=get_unaligned_uint(uk+8) & MASK_C1; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
410 case 10: c+=get_unaligned_uint(uk+8) & MASK_C2; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
411 case 9 : c+=get_unaligned_uint(uk+8) & MASK_C3; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
412 case 8 : b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
413 case 7 : b+=get_unaligned_uint(uk+4) & MASK_C1; a+=get_unaligned_uint(uk); break;
414 case 6 : b+=get_unaligned_uint(uk+4) & MASK_C2; a+=get_unaligned_uint(uk); break;
415 case 5 : b+=get_unaligned_uint(uk+4) & MASK_C3; a+=get_unaligned_uint(uk); break;
416 case 4 : a+=get_unaligned_uint(uk); break;
417 case 3 : a+=get_unaligned_uint(uk) & MASK_C1; break;
418 case 2 : a+=get_unaligned_uint(uk) & MASK_C2; break;
419 case 1 : a+=get_unaligned_uint(uk) & MASK_C3; break;
420 case 0 : return c & hash->size; /* zero length strings require no mixing */
423 final(a,b,c);
424 return c & hash->size;
427 static void hash_destroy(struct hash* hash)
429 if(hash)
431 if(hash->array)
433 free(hash->array);
435 if(hash->elems_pool)
437 pool_destroy(hash->elems_pool);
439 free(hash);
443 static struct hash* hash_create(unsigned int size)
445 struct hash* hash;
447 assert(size > 0);
448 hash = calloc(1, sizeof(struct hash));
449 if(hash)
451 size += (size >> 2) + 1; /* ~ 75% load factor */
452 if(size >= 15)
454 hash->size = (((unsigned int)0xFFFFFFFF) >> clz((unsigned int)size));
456 else
458 hash->size = size = 15;
460 hash->load_limit = hash->size - (hash->size >> 2);
461 hash->used = 0;
462 hash->array = (struct hash_elem**)calloc(hash->size + 1, sizeof(struct hash_elem*));
463 if(hash->array == NULL)
465 hash_destroy(hash);
466 hash = NULL;
469 if(hash)
471 hash->elems_pool = pool_create(sizeof(struct hash_elem),
472 size, size << 1);
473 if(!hash->elems_pool)
475 hash_destroy(hash);
476 hash = NULL;
479 return hash;
482 static void hash_resize(struct hash* hash)
484 unsigned int old_size = hash->size;
485 unsigned int hashed;
486 struct hash_elem* hash_elem;
487 struct hash_elem* next;
488 struct hash_elem** array;
489 unsigned int i;
491 hash->size = (old_size << 1) + 1;
492 /* we really should avoid to get there... so print a message to alert of the condition */
493 fprintf(stderr, "resize hash %u -> %u\n", old_size, hash->size);
494 if(hash->size == old_size)
496 return;
498 array = calloc(hash->size + 1, sizeof(struct hash_elem*));
499 if(array)
501 hash->load_limit = hash->size - (hash->size >> 2);
502 for(i=0; i <= old_size; i++)
504 hash_elem = (struct hash_elem*)hash->array[i];
505 while(hash_elem)
507 next = hash_elem->next;
509 hashed = hash_compute(hash, hash_elem->key, hash_elem->key_len);
510 hash_elem->next = array[hashed];
511 array[hashed] = hash_elem;
512 hash_elem = next;
515 free(hash->array);
516 hash->array = (struct hash_elem**)array;
518 else
520 hash->size = old_size;
524 static int compare_key(struct hash const * hash, const char* a, const char* b, int len, int const * cost)
526 #ifdef HASH_STAT
527 *cost += 1;
528 hash->memcmp += 1;
529 #else
530 (void) hash;
531 (void) cost;
532 #endif
533 return memcmp(a,b, len);
536 /* a customized hash_store function that just store the key and return
537 * TRUE if the key was effectively stored, or FALSE if the key was already there
539 static int hash_store(struct hash* hash, const char* key, int key_len)
541 unsigned int hashed;
542 struct hash_elem* hash_elem;
543 int cost = 0;
545 (void) cost;
546 hashed = hash_compute(hash, key, key_len);
547 #ifdef HASH_STAT
548 hash->stored += 1;
549 #endif
550 hash_elem = (struct hash_elem*)hash->array[hashed];
551 while(hash_elem && (hash_elem->key_len != key_len || compare_key(hash, hash_elem->key, key, key_len, &cost)))
553 hash_elem = hash_elem->next;
556 if(!hash_elem)
558 hash_elem = pool_alloc(hash->elems_pool);
559 if(hash_elem)
561 hash_elem->key = key;
562 hash_elem->key_len = key_len;
563 hash_elem->next = hash->array[hashed];
565 #ifdef HASH_STAT
566 if(hash_elem->next)
568 hash->collisions += 1;
569 hash->cost += cost;
571 #endif
572 hash->array[hashed] = hash_elem;
573 hash->used += 1;
574 if(hash->used > hash->load_limit)
576 hash_resize(hash);
579 return TRUE;
581 return FALSE;
584 static int file_stat(const char* name, struct stat* buffer_stat, int* rc)
586 int rc_local = stat(name, buffer_stat);
587 if (rc_local < 0)
589 *rc = errno;
591 return rc_local;
594 static off_t file_get_size(const char* name, int* rc)
596 struct stat buffer_stat;
597 off_t size = -1;
599 if (!file_stat(name, &buffer_stat, rc))
601 if(S_ISREG(buffer_stat.st_mode))
603 size = buffer_stat.st_size;
605 else
607 *rc = EINVAL;
610 return size;
613 #if !ENABLE_RUNTIME_OPTIMIZATIONS
614 static void * file_load_buffers[100000];
615 static size_t file_load_buffer_count = 0;
616 #endif
618 static char* file_load(const char* name, off_t* size, int* return_rc)
620 off_t local_size = 0;
621 int rc = 0;
622 char* buffer = NULL;
623 int fd;
625 assert(name != NULL);
627 if(!size)
629 size = &local_size;
631 *size = file_get_size(name, &rc);
632 if (!rc && *size >= 0)
634 fd = open(name, FILE_O_RDONLY | FILE_O_BINARY);
635 if (!(fd == -1))
637 buffer = malloc((size_t)(*size + 1));
638 #if !ENABLE_RUNTIME_OPTIMIZATIONS
639 if (buffer != NULL)
641 if (file_load_buffer_count == 100000)
643 free(buffer);
644 buffer = NULL;
646 else
648 file_load_buffers[file_load_buffer_count++] = buffer;
651 #endif
652 if (buffer == NULL)
654 rc = ENOMEM;
656 else
658 ssize_t i;
660 REDO:
661 i = read(fd, buffer, (size_t)(*size));
662 if(i == -1)
664 if(errno == EINTR)
666 goto REDO;
668 else
670 rc = errno;
673 else
675 if (i != *size)
677 rc = EIO;
680 buffer[*size] = 0;
682 close(fd);
686 if(rc && buffer)
688 free(buffer);
689 buffer = NULL;
691 if(return_rc)
693 *return_rc = rc;
695 return buffer;
698 static void cancel_relative(char const * base, char** ref_cursor, char** ref_cursor_out, char const * end)
700 char* cursor = *ref_cursor;
701 char* cursor_out = *ref_cursor_out;
705 cursor += 3;
706 while(cursor_out > base && cursor_out[-1] == '/')
707 cursor_out--;
708 while(cursor_out > base && *--cursor_out != '/');
710 while(cursor + 3 < end && !memcmp(cursor, "/../", 4));
711 *ref_cursor = cursor;
712 *ref_cursor_out = cursor_out;
715 static void eat_space(char ** token)
717 while ((' ' == **token) || ('\t' == **token)) {
718 ++(*token);
723 * Prune LibreOffice specific duplicate dependencies to improve
724 * gnumake startup time, and shrink the disk-space footprint.
726 static int
727 elide_dependency(const char* key, int key_len, const char **unpacked_end)
729 #if 0
731 int i;
732 fprintf (stderr, "elide?%d!: '", internal_boost);
733 for (i = 0; i < key_len; i++) {
734 fprintf (stderr, "%c", key[i]);
736 fprintf (stderr, "'\n");
738 #endif
740 /* boost brings a plague of header files */
741 int i;
742 int unpacked = 0;
743 /* walk down path elements */
744 for (i = 0; i < key_len - 1; i++)
746 if (key[i] == '/')
748 if (0 == unpacked)
750 if (!PATHNCMP(key + i + 1, "workdir/", 8))
752 unpacked = 1;
753 continue;
756 else
758 if (!PATHNCMP(key + i + 1, "UnpackedTarball/", 16))
760 if (unpacked_end)
761 *unpacked_end = strchr(key + i + 17, '/');
762 return 1;
768 return 0;
772 * We collapse tens of internal boost headers to the unpacked target, such
773 * that you can re-compile / install boost and all is well.
775 static void emit_single_boost_header(void)
777 #define BOOST_TARGET "/UnpackedTarball/boost.done"
778 fprintf(stdout, "%s" BOOST_TARGET " ", work_dir);
781 static void emit_unpacked_target(const char* token, const char* end)
783 fwrite(token, 1, end-token, stdout);
784 fputs(".done ", stdout);
787 /* prefix paths to absolute */
788 static void print_fullpaths(char* line)
790 char* token;
791 char* end;
792 int boost_count = 0;
793 int token_len;
794 const char * unpacked_end = NULL; /* end of UnpackedTarget match (if any) */
795 /* for UnpackedTarget the target is GenC{,xx}Object, don't mangle! */
796 int target_seen = 0;
798 token = line;
799 eat_space(&token);
800 while (*token)
802 end = token;
803 /* hard to believe that in this day and age drive letters still exist */
804 if (*end && (':' == *(end+1)) &&
805 (('\\' == *(end+2)) || ('/' == *(end+2))) &&
806 isalpha((unsigned char)*end))
808 end = end + 3; /* only one cross, err drive letter per filename */
810 while (*end && (' ' != *end) && ('\t' != *end) && (':' != *end)) {
811 ++end;
813 token_len = end - token;
814 if (target_seen &&
815 elide_dependency(token, token_len, &unpacked_end))
817 if (unpacked_end)
819 if (internal_boost && !PATHNCMP(unpacked_end - 5, "boost", 5))
821 ++boost_count;
822 if (boost_count == 1)
823 emit_single_boost_header();
824 else
826 /* don't output, and swallow trailing \\\n if any */
827 token = end;
828 eat_space(&token);
829 if (token[0] == '\\' && token[1] == '\n')
830 end = token + 2;
833 else
835 emit_unpacked_target(token, unpacked_end);
837 unpacked_end = NULL;
840 else
842 if (fwrite(token, token_len, 1, stdout) != 1)
843 abort();
844 fputc(' ', stdout);
846 token = end;
847 eat_space(&token);
848 if (!target_seen && ':' == *token)
850 target_seen = 1;
851 fputc(':', stdout);
852 ++token;
853 eat_space(&token);
858 static char * eat_space_at_end(char * end)
860 char * real_end;
861 assert('\0' == *end);
862 real_end = end - 1;
863 while (' ' == *real_end || '\t' == *real_end || '\n' == *real_end
864 || ':' == *real_end)
865 { /* eat colon and whitespace at end */
866 --real_end;
868 return real_end;
871 static char* phony_content_buffer;
872 static char* generate_phony_line(char const * phony_target, char const * extension)
874 char const * src;
875 char* dest;
876 char* last_dot = NULL;
877 //fprintf(stderr, "generate_phony_line called with phony_target %s and extension %s\n", phony_target, extension);
878 for(dest = phony_content_buffer+work_dir_len+1, src = phony_target; *src != 0; ++src, ++dest)
880 *dest = *src;
881 if(*dest == '.')
883 last_dot = dest;
886 //fprintf(stderr, "generate_phony_line after phony_target copy: %s\n", phony_content_buffer);
887 for(dest = last_dot+1, src = extension; *src != 0; ++src, ++dest)
889 *dest = *src;
891 //fprintf(stderr, "generate_phony_line after extension add: %s\n", phony_content_buffer);
892 strcpy(dest, ": $(gb_Helper_PHONY)\n");
893 //fprintf(stderr, "generate_phony_line after phony add: %s\n", phony_content_buffer);
894 return phony_content_buffer;
897 static int generate_phony_file(char* fn, char const * content)
899 FILE* depfile;
900 depfile = fopen(fn, "w");
901 if(!depfile)
903 fprintf(stderr, "Could not open '%s' for writing: %s\n", fn, strerror(errno));
905 else
907 fputs(content, depfile);
908 fclose(depfile);
910 return !depfile;
913 static int process(struct hash* dep_hash, char* fn)
915 int rc;
916 char* buffer;
917 char* end;
918 char* cursor;
919 char* cursor_out;
920 char* base;
921 char* created_line = NULL;
922 char* src_relative;
923 int continuation = 0;
924 char last_ns = 0;
925 off_t size;
927 buffer = file_load(fn, &size, &rc);
928 if(!rc)
930 base = cursor_out = cursor = end = buffer;
931 end += size;
933 /* first eat unneeded space at the beginning of file
935 while(cursor < end && (*cursor == ' ' || *cursor == '\\'))
936 ++cursor;
938 while(cursor < end)
940 if(*cursor == '\\')
942 continuation = 1;
943 *cursor_out++ = *cursor++;
945 else if(*cursor == '/')
947 if(cursor + 3 < end)
949 if(!memcmp(cursor, "/../", 4))
951 cancel_relative(base, &cursor, &cursor_out, end);
954 *cursor_out++ = *cursor++;
956 else if(*cursor == '\n')
958 if(!continuation)
960 *cursor_out = 0;
961 if(base < cursor)
963 /* here we have a complete rule */
964 if(last_ns == ':')
966 /* if the rule ended in ':' that is a no-dep rule
967 * these are the one for which we want to filter
968 * duplicate out
970 int key_len = eat_space_at_end(cursor_out) - base;
971 if (!elide_dependency(base,key_len + 1, NULL)
972 && hash_store(dep_hash, base, key_len))
974 /* DO NOT modify base after it has been added
975 as key by hash_store */
976 print_fullpaths(base);
977 putc('\n', stdout);
980 else
982 /* rule with dep, just write it */
983 print_fullpaths(base);
984 putc('\n', stdout);
986 last_ns = ' '; // cannot hurt to reset it
988 cursor += 1;
989 base = cursor_out = cursor;
991 else
993 /* here we have a '\' followed by \n this is a continuation
994 * i.e not a complete rule yet
996 *cursor_out++ = *cursor++;
997 continuation = 0; // cancel current one (empty lines!)
1000 else
1002 continuation = 0;
1003 /* not using isspace() here save 25% of I refs and 75% of D refs based on cachegrind */
1004 if(*cursor != ' ' && *cursor != '\n' && *cursor != '\t' )
1006 last_ns = *cursor;
1008 *cursor_out++ = *cursor++;
1011 /* just in case the file did not end with a \n, there may be a pending rule */
1012 if(base < cursor_out)
1014 if(last_ns == ':')
1016 int key_len = eat_space_at_end(cursor_out) - base;
1017 if (!elide_dependency(base,key_len + 1, NULL) &&
1018 hash_store(dep_hash, base, key_len))
1020 puts(base);
1021 putc('\n', stdout);
1024 else
1026 puts(base);
1027 putc('\n', stdout);
1031 else
1033 if(strncmp(fn, work_dir, work_dir_len) == 0)
1035 if(strncmp(fn+work_dir_len, "/Dep/", 5) == 0)
1037 src_relative = fn+work_dir_len+5;
1038 // cases ordered by frequency
1039 if(strncmp(src_relative, "CxxObject/", 10) == 0)
1041 created_line = generate_phony_line(src_relative, "o");
1042 rc = generate_phony_file(fn, created_line);
1044 else if(strncmp(src_relative, "GenCxxObject/", 13) == 0)
1046 created_line = generate_phony_line(src_relative, "o");
1047 rc = generate_phony_file(fn, created_line);
1049 else if(strncmp(src_relative, "CObject/", 8) == 0)
1051 created_line = generate_phony_line(src_relative, "o");
1052 rc = generate_phony_file(fn, created_line);
1054 else if(strncmp(src_relative, "GenCObject/", 11) == 0)
1056 created_line = generate_phony_line(src_relative, "o");
1057 rc = generate_phony_file(fn, created_line);
1059 else if(strncmp(src_relative, "SdiObject/", 10) == 0)
1061 created_line = generate_phony_line(src_relative, "o");
1062 rc = generate_phony_file(fn, created_line);
1064 else if(strncmp(src_relative, "AsmObject/", 10) == 0)
1066 created_line = generate_phony_line(src_relative, "o");
1067 rc = generate_phony_file(fn, created_line);
1069 else if(strncmp(src_relative, "ObjCxxObject/", 13) == 0)
1071 created_line = generate_phony_line(src_relative, "o");
1072 rc = generate_phony_file(fn, created_line);
1074 else if(strncmp(src_relative, "ObjCObject/", 11) == 0)
1076 created_line = generate_phony_line(src_relative, "o");
1077 rc = generate_phony_file(fn, created_line);
1079 else if(strncmp(src_relative, "CxxClrObject/", 13) == 0)
1081 created_line = generate_phony_line(src_relative, "o");
1082 rc = generate_phony_file(fn, created_line);
1084 else if(strncmp(src_relative, "GenCxxClrObject/", 16) == 0)
1086 created_line = generate_phony_line(src_relative, "o");
1087 rc = generate_phony_file(fn, created_line);
1089 else
1091 fprintf(stderr, "no magic for %s(%s) in %s\n", fn, src_relative, work_dir);
1094 if(!rc)
1096 puts(created_line);
1100 /* Note: yes we are going to leak 'buffer'
1101 * this is on purpose, to avoid cloning the 'key' out of it and our special
1102 * 'hash' just store the pointer to the key inside of buffer, hence it need
1103 * to remain allocated
1105 // coverity[leaked_storage] - this is on purpose
1106 return rc;
1109 static void usage(void)
1111 fputs("Usage: concat-deps <file that contains dep_files>\n", stderr);
1114 #define kDEFAULT_HASH_SIZE 4096
1115 #define PHONY_TARGET_BUFFER 4096
1117 static int get_var(char **var, const char *name)
1119 *var = (char *)getenv(name);
1120 if(!*var)
1122 fprintf(stderr,"Error: %s is missing in the environment\n", name);
1123 return 1;
1125 return 0;
1128 int main(int argc, char** argv)
1130 int rc = 0;
1131 off_t in_list_size = 0;
1132 char* in_list;
1133 char* in_list_cursor;
1134 char* in_list_base;
1135 struct hash* dep_hash = NULL;
1136 const char *env_str;
1138 if(argc < 2)
1140 usage();
1141 return 1;
1143 if(get_var(&base_dir, "SRCDIR") || get_var(&work_dir, "WORKDIR"))
1144 return 1;
1145 work_dir_len = strlen(work_dir);
1146 phony_content_buffer = malloc(PHONY_TARGET_BUFFER);
1147 assert(phony_content_buffer); // Don't handle OOM conditions
1148 strcpy(phony_content_buffer, work_dir);
1149 phony_content_buffer[work_dir_len] = '/';
1151 env_str = getenv("SYSTEM_BOOST");
1152 internal_boost = !env_str || strcmp(env_str,"TRUE");
1154 in_list = file_load(argv[1], &in_list_size, &rc);
1155 if(!rc)
1157 dep_hash = hash_create( kDEFAULT_HASH_SIZE);
1158 in_list_base = in_list_cursor = in_list;
1160 /* extract filename of dep file from a 'space' separated list */
1161 while(*in_list_cursor)
1163 /* the input here may contain Win32 \r\n EOL */
1164 if(*in_list_cursor == ' '
1165 || *in_list_cursor == '\n' || *in_list_cursor == '\r')
1167 *in_list_cursor = 0;
1168 if(in_list_base < in_list_cursor)
1170 rc = process(dep_hash, in_list_base);
1171 if(rc)
1173 break;
1176 in_list_cursor += 1;
1177 in_list_base = in_list_cursor;
1179 else
1181 in_list_cursor += 1;
1184 if(!rc)
1186 /* catch the last entry in case the input did not terminate with a 'space' */
1187 if(in_list_base < in_list_cursor)
1189 rc = process(dep_hash, in_list_base);
1192 #ifdef HASH_STAT
1193 fprintf(stderr, "stats: u:%d s:%d l:%d t:%d c:%d m:%d $:%d\n",
1194 dep_hash->used, dep_hash->size, dep_hash->load_limit, dep_hash->stored,
1195 dep_hash->collisions, dep_hash->memcmp, dep_hash->cost);
1196 #endif
1198 #if !ENABLE_RUNTIME_OPTIMIZATIONS
1200 size_t i;
1201 hash_destroy(dep_hash);
1202 for (i = 0; i != file_load_buffer_count; ++i)
1204 free(file_load_buffers[i]);
1207 #endif
1208 return rc;
1211 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */