Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / solenv / bin / concat-deps.c
blobfec98ee2931fc450e6dd5edcfeab8551b6c27f34
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 #define __windows
32 #undef CORE_BIG_ENDIAN
33 #define CORE_LITTLE_ENDIAN
34 #endif /* Def _MSC_VER */
36 #if defined(__linux) || defined(__OpenBSD__) || \
37 defined(__FreeBSD__) || defined(__NetBSD__) || \
38 defined(__DragonFly__) || defined(__FreeBSD_kernel__)
39 #include <sys/param.h>
40 #if __BYTE_ORDER == __LITTLE_ENDIAN
41 #undef CORE_BIG_ENDIAN
42 #define CORE_LITTLE_ENDIAN
43 #else /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
44 #if __BYTE_ORDER == __BIG_ENDIAN
45 #define CORE_BIG_ENDIAN
46 #undef CORE_LITTLE_ENDIAN
47 #endif /* __BYTE_ORDER == __BIG_ENDIAN */
48 #endif /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
49 #endif /* Def __linux || Def *BSD */
51 #ifdef __sun
52 #ifdef __sparc
53 #define CORE_BIG_ENDIAN
54 #undef CORE_LITTLE_ENDIAN
55 #else /* Ndef __sparc */
56 #undef CORE_BIG_ENDIAN
57 #define CORE_LITTLE_ENDIAN
58 #endif /* Ndef __sparc */
59 #endif /* Def __sun */
61 #include <assert.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <errno.h>
67 #include <fcntl.h>
68 #include <string.h>
69 #include <ctype.h>
71 #ifdef __windows
72 #include <io.h>
73 #else
74 #include <unistd.h>
75 #endif
77 /* modes */
78 #ifdef __windows
79 #define FILE_O_RDONLY _O_RDONLY
80 #define FILE_O_BINARY _O_BINARY
81 #define PATHNCMP _strnicmp /* MSVC converts paths to lower-case sometimes? */
82 #define inline __inline
83 #define ssize_t long
84 #define S_ISREG(mode) (((mode) & _S_IFMT) == (_S_IFREG)) /* MSVC does not have this macro */
85 #else /* not windaube */
86 #define FILE_O_RDONLY O_RDONLY
87 #define FILE_O_BINARY 0
88 #define PATHNCMP strncmp
89 #endif /* not windaube */
91 #ifndef TRUE
92 #define TRUE 1
93 #endif
94 #ifndef FALSE
95 #define FALSE 0
96 #endif
98 int internal_boost = 0;
99 static char* base_dir;
100 static char* work_dir;
101 size_t work_dir_len;
103 #ifdef __GNUC__
104 #define clz __builtin_clz
105 #else
106 static inline int clz(unsigned int value)
108 int result = 32;
110 while(value)
112 value >>= 1;
113 result -= 1;
115 return result;
117 #endif
119 static inline unsigned int get_unaligned_uint(const unsigned char* cursor)
121 unsigned int result;
123 memcpy(&result, cursor, sizeof(unsigned int));
124 return result;
127 /* ===============================================
128 * memory pool for fast fix-size allocation (non-tread-safe)
129 * ===============================================
131 struct pool
133 void* head_free; /**< head of a linked list of freed element */
134 char* fresh; /**< top of a memory block to dig new element */
135 char* tail; /**< to detect end of extent... when fresh pass tail */
136 void* extent; /**< pointer to the primary extent block */
137 int size_elem; /**< size of an element. */
138 int primary; /**< primary allocation in bytes */
139 int secondary; /**< secondary allocation in bytes */
141 #define POOL_ALIGN_INCREMENT 8 /**< Alignement, must be a power of 2 and of size > to sizeof(void*) */
144 static void* pool_take_extent(struct pool* pool, int allocate)
146 unsigned int size = 0;
147 void* extent;
148 void* data = NULL;
150 if(pool->extent)
152 /* we already have an extent, so this is a secondary */
153 if(pool->secondary)
155 size = pool->secondary;
158 else
160 assert(pool->primary);
161 size = pool->primary;
163 if(size)
165 extent = malloc(size);
166 if(extent)
168 *(void**)extent = pool->extent;
169 pool->extent = extent;
170 if(allocate)
172 data = ((char*)extent) + POOL_ALIGN_INCREMENT;
173 pool->fresh = ((char*)data) + pool->size_elem;
174 pool->tail = pool->fresh + (size - pool->size_elem);
176 else
178 pool->fresh = ((char*)extent) + POOL_ALIGN_INCREMENT;
179 pool->tail = pool->fresh + (size - pool->size_elem);
183 return data;
186 /* Create a memory pool for fix size objects
187 * this is a simplified implementation that
188 * is _not_ thread safe.
190 struct pool* pool_create(int size_elem, int primary, int secondary)
192 struct pool* pool;
194 assert(primary > 0);
195 assert(secondary >= 0);
196 assert(size_elem > 0);
198 pool = (struct pool*)calloc(1, sizeof(struct pool));
199 if(!pool) return NULL;
200 /* Adjust the element size so that it be aligned, and so that an element could
201 * at least contain a void*
203 pool->size_elem = size_elem = (size_elem + POOL_ALIGN_INCREMENT - 1) & ~(POOL_ALIGN_INCREMENT - 1);
205 pool->primary = (size_elem * primary) + POOL_ALIGN_INCREMENT;
206 pool->secondary = secondary > 0 ? (size_elem * secondary) + POOL_ALIGN_INCREMENT : 0;
207 pool_take_extent(pool, FALSE);
209 return pool;
213 void pool_destroy(struct pool* pool)
215 void* extent;
216 void* next;
218 if(pool != NULL)
220 extent = pool->extent;
221 while(extent)
223 next = *(void**)extent;
224 free(extent);
225 extent = next;
227 free(pool);
231 static inline void* pool_alloc(struct pool* pool)
233 void* data;
235 data = pool->head_free;
236 if(data == NULL)
238 /* we have no old-freed elem */
239 if(pool->fresh <= pool->tail)
241 /* pick a slice of the current extent */
242 data = (void*)pool->fresh;
243 pool->fresh += pool->size_elem;
245 else
247 /* allocate a new extent */
248 data = pool_take_extent(pool, TRUE);
251 else
253 /* re-used old freed element by chopipng the head of the free list */
254 pool->head_free = *(void**)data;
257 return data;
261 /* ===============================================
262 * Hash implementation custumized to be just tracking
263 * a unique list of string (i.e no data associated
264 * with the key, no need for retrieval, etc..
266 * This is tuned for the particular use-case we have here
267 * measures in tail_build showed that
268 * we can get north of 4000 distinct values stored in a hash
269 * the collision rate is at worse around 2%
270 * the collision needing an expensive memcmp to resolve
271 * have a rate typically at 1 per 1000
272 * for tail_build we register 37229 unique key
273 * with a total of 377 extra memcmp needed
274 * which is completely negligible compared to the
275 * number of memcmp required to eliminate duplicate
276 * entry (north of 2.5 millions for tail_build)
277 * ===============================================
280 struct hash_elem
282 struct hash_elem* next;
283 const char* key;
284 int key_len;
287 struct hash
289 struct hash_elem** array;
290 struct pool* elems_pool;
291 int flags;
292 unsigned int used;
293 unsigned int size;
294 unsigned int load_limit;
295 #ifdef HASH_STAT
296 int stored;
297 int collisions;
298 int cost;
299 int memcmp;
300 #endif
302 #define HASH_F_NO_RESIZE (1<<0)
304 /* The following hash_compute function was adapted from :
305 * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
307 * The changes from the original are mostly cosmetic
309 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
312 #if defined CORE_BIG_ENDIAN
313 #define MASK_C1 0xFFFFFF00
314 #define MASK_C2 0xFFFF0000
315 #define MASK_C3 0xFF000000
316 #elif defined CORE_LITTLE_ENDIAN
317 #define MASK_C1 0xFFFFFF
318 #define MASK_C2 0xFFFF
319 #define MASK_C3 0xFF
320 #else
321 #error "Missing Endianness definition"
322 #endif
325 #define mix(a,b,c) \
327 a -= c; a ^= rot(c, 4); c += b; \
328 b -= a; b ^= rot(a, 6); a += c; \
329 c -= b; c ^= rot(b, 8); b += a; \
330 a -= c; a ^= rot(c,16); c += b; \
331 b -= a; b ^= rot(a,19); a += c; \
332 c -= b; c ^= rot(b, 4); b += a; \
334 #define final(a,b,c) \
336 c ^= b; c -= rot(b,14); \
337 a ^= c; a -= rot(c,11); \
338 b ^= a; b -= rot(a,25); \
339 c ^= b; c -= rot(b,16); \
340 a ^= c; a -= rot(c,4); \
341 b ^= a; b -= rot(a,14); \
342 c ^= b; c -= rot(b,24); \
345 static unsigned int hash_compute( struct hash* hash, const char* key, int length)
347 unsigned int a;
348 unsigned int b;
349 unsigned int c; /* internal state */
350 const unsigned char* uk = (const unsigned char*)key;
352 /* Set up the internal state */
353 a = b = c = 0xdeadbeef + (length << 2);
355 /* we use this to 'hash' full path with mostly a common root
356 * let's now waste too much cycles hashing mostly constant stuff
358 if(length > 36)
360 uk += length - 36;
361 length = 36;
363 /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
364 while (length > 12)
366 a += get_unaligned_uint(uk);
367 b += get_unaligned_uint(uk+4);
368 c += get_unaligned_uint(uk+8);
369 mix(a,b,c);
370 length -= 12;
371 uk += 12;
374 /*----------------------------- handle the last (probably partial) block */
375 /* Note: we possibly over-read, which would trigger complaint from VALGRIND
376 * but we mask the undefined stuff if any, so we are still good, thanks
377 * to alignment of memory allocation and tail-memory management overhead
378 * we always can read 3 bytes past the official end without triggering
379 * a segfault -- if you find a platform/compiler couple for which that postulat
380 * is false, then you just need to over-allocate by 2 more bytes in file_load()
381 * file_load already over-allocate by 1 to sitck a \0 at the end of the buffer.
383 switch(length)
385 case 12: c+=get_unaligned_uint(uk+8); b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
386 case 11: c+=get_unaligned_uint(uk+8) & MASK_C1; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
387 case 10: c+=get_unaligned_uint(uk+8) & MASK_C2; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
388 case 9 : c+=get_unaligned_uint(uk+8) & MASK_C3; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
389 case 8 : b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
390 case 7 : b+=get_unaligned_uint(uk+4) & MASK_C1; a+=get_unaligned_uint(uk); break;
391 case 6 : b+=get_unaligned_uint(uk+4) & MASK_C2; a+=get_unaligned_uint(uk); break;
392 case 5 : b+=get_unaligned_uint(uk+4) & MASK_C3; a+=get_unaligned_uint(uk); break;
393 case 4 : a+=get_unaligned_uint(uk); break;
394 case 3 : a+=get_unaligned_uint(uk) & MASK_C1; break;
395 case 2 : a+=get_unaligned_uint(uk) & MASK_C2; break;
396 case 1 : a+=get_unaligned_uint(uk) & MASK_C3; break;
397 case 0 : return c & hash->size; /* zero length strings require no mixing */
400 final(a,b,c);
401 return c & hash->size;
404 static void hash_destroy(struct hash* hash)
406 if(hash)
408 if(hash->array)
410 free(hash->array);
412 if(hash->elems_pool)
414 pool_destroy(hash->elems_pool);
416 free(hash);
420 static struct hash* hash_create(unsigned int size)
422 struct hash* hash;
424 assert(size > 0);
425 hash = calloc(1, sizeof(struct hash));
426 if(hash)
428 size += (size >> 2) + 1; /* ~ 75% load factor */
429 if(size >= 15)
431 hash->size = (((unsigned int)0xFFFFFFFF) >> clz((unsigned int)size));
433 else
435 hash->size = size = 15;
437 hash->load_limit = hash->size - (hash->size >> 2);
438 hash->used = 0;
439 hash->array = (struct hash_elem**)calloc(hash->size + 1, sizeof(struct hash_elem*));
440 if(hash->array == NULL)
442 hash_destroy(hash);
443 hash = NULL;
446 if(hash)
448 hash->elems_pool = pool_create(sizeof(struct hash_elem),
449 size, size << 1);
450 if(!hash->elems_pool)
452 hash_destroy(hash);
453 hash = NULL;
456 return hash;
459 static void hash_resize(struct hash* hash)
461 unsigned int old_size = hash->size;
462 unsigned int hashed;
463 struct hash_elem* hash_elem;
464 struct hash_elem* next;
465 struct hash_elem** array;
466 unsigned int i;
468 hash->size = (old_size << 1) + 1;
469 /* we really should avoid to get there... so print a message to alert of the condition */
470 fprintf(stderr, "resize hash %d -> %d\n", old_size, hash->size);
471 if(hash->size == old_size)
473 hash->flags |= HASH_F_NO_RESIZE;
474 return;
476 array = calloc(hash->size + 1, sizeof(struct hash_elem*));
477 if(array)
479 hash->load_limit = hash->size - (hash->size >> 2);
480 for(i=0; i <= old_size; i++)
482 hash_elem = (struct hash_elem*)hash->array[i];
483 while(hash_elem)
485 next = hash_elem->next;
487 hashed = hash_compute(hash, hash_elem->key, hash_elem->key_len);
488 hash_elem->next = array[hashed];
489 array[hashed] = hash_elem;
490 hash_elem = next;
493 free(hash->array);
494 hash->array = (struct hash_elem**)array;
496 else
498 hash->size = old_size;
499 hash->flags |= HASH_F_NO_RESIZE;
503 #ifdef HASH_STAT
504 static inline int compare_key(struct hash* hash, const char* a, const char* b, int len, int* cost)
506 *cost += 1;
507 hash->memcmp += 1;
508 return memcmp(a,b, len);
510 #else
511 #define compare_key(h,a,b,l,c) memcmp(a,b,l)
512 #endif
514 /* a customized hash_store function that just store the key and return
515 * TRUE if the key was effectively stored, or FALSE if the key was already there
517 static int hash_store(struct hash* hash, const char* key, int key_len)
519 unsigned int hashed;
520 struct hash_elem* hash_elem;
521 int cost = 0;
523 (void) cost;
524 hashed = hash_compute(hash, key, key_len);
525 #ifdef HASH_STAT
526 hash->stored += 1;
527 #endif
528 hash_elem = (struct hash_elem*)hash->array[hashed];
529 while(hash_elem && (hash_elem->key_len != key_len || compare_key(hash, hash_elem->key, key, key_len, &cost)))
531 hash_elem = hash_elem->next;
534 if(!hash_elem)
536 hash_elem = pool_alloc(hash->elems_pool);
537 if(hash_elem)
539 hash_elem->key = key;
540 hash_elem->key_len = key_len;
541 hash_elem->next = hash->array[hashed];
543 #ifdef HASH_STAT
544 if(hash_elem->next)
546 hash->collisions += 1;
547 hash->cost += cost;
549 #endif
550 hash->array[hashed] = hash_elem;
551 hash->used += 1;
552 if(hash->used > hash->load_limit)
554 hash_resize(hash);
557 return TRUE;
559 return FALSE;
562 static int file_stat(const char* name, struct stat* buffer_stat, int* rc)
564 int rc_local = 0;
566 rc_local = stat(name, buffer_stat);
567 if (rc_local < 0)
569 *rc = errno;
571 return rc_local;
574 static off_t file_get_size(const char* name, int* rc)
576 struct stat buffer_stat;
577 off_t size = -1;
579 if (!file_stat(name, &buffer_stat, rc))
581 if(S_ISREG(buffer_stat.st_mode))
583 size = buffer_stat.st_size;
585 else
587 *rc = EINVAL;
590 return size;
593 static char* file_load(const char* name, off_t* size, int* return_rc)
595 off_t local_size = 0;
596 int rc = 0;
597 char* buffer = NULL;
598 int fd;
600 assert(name != NULL);
602 if(!size)
604 size = &local_size;
606 *size = file_get_size(name, &rc);
607 if (!rc)
609 fd = open(name, FILE_O_RDONLY | FILE_O_BINARY);
610 if (!(fd == -1))
612 buffer = malloc((size_t)(*size + 1));
613 if (buffer == NULL)
615 rc = ENOMEM;
617 else
619 ssize_t i;
621 REDO:
622 i = read(fd, buffer, (size_t)(*size));
623 if(i == -1)
625 if(errno == EINTR)
627 goto REDO;
629 else
631 rc = errno;
634 else
636 if (i != *size)
638 rc = EIO;
641 buffer[*size] = 0;
643 close(fd);
647 if(rc && buffer)
649 free(buffer);
650 buffer = NULL;
652 if(return_rc)
654 *return_rc = rc;
656 return buffer;
659 static void _cancel_relative(char* base, char** ref_cursor, char** ref_cursor_out, char* end)
661 char* cursor = *ref_cursor;
662 char* cursor_out = *ref_cursor_out;
666 cursor += 3;
667 while(cursor_out > base && cursor_out[-1] == '/')
668 cursor_out--;
669 while(cursor_out > base && *--cursor_out != '/');
671 while(cursor + 3 < end && !memcmp(cursor, "/../", 4));
672 *ref_cursor = cursor;
673 *ref_cursor_out = cursor_out;
676 static inline void eat_space(char ** token)
678 while ((' ' == **token) || ('\t' == **token)) {
679 ++(*token);
684 * Prune LibreOffice specific duplicate dependencies to improve
685 * gnumake startup time, and shrink the disk-space footprint.
687 static inline int
688 elide_dependency(const char* key, int key_len, const char **unpacked_end)
690 #if 0
692 int i;
693 fprintf (stderr, "elide?%d!: '", internal_boost);
694 for (i = 0; i < key_len; i++) {
695 fprintf (stderr, "%c", key[i]);
697 fprintf (stderr, "'\n");
699 #endif
701 /* boost brings a plague of header files */
702 int i;
703 int unpacked = 0;
704 /* walk down path elements */
705 for (i = 0; i < key_len - 1; i++)
707 if (key[i] == '/')
709 if (0 == unpacked)
711 if (!PATHNCMP(key + i + 1, "workdir/", 8))
713 unpacked = 1;
714 continue;
717 else
719 if (!PATHNCMP(key + i + 1, "UnpackedTarball/", 16))
721 if (unpacked_end)
722 *unpacked_end = strchr(key + i + 17, '/');
723 return 1;
729 return 0;
733 * We collapse tens of internal boost headers to the unpacked target, such
734 * that you can re-compile / install boost and all is well.
736 static void emit_single_boost_header(void)
738 #define BOOST_TARGET "/UnpackedTarball/boost.done"
739 fprintf(stdout, "%s" BOOST_TARGET " ", work_dir);
742 static void emit_unpacked_target(const char* token, const char* end)
744 fwrite(token, 1, end-token, stdout);
745 fputs(".done ", stdout);
748 /* prefix paths to absolute */
749 static inline void print_fullpaths(char* line)
751 char* token;
752 char* end;
753 int boost_count = 0;
754 int token_len;
755 const char * unpacked_end = 0; /* end of UnpackedTarget match (if any) */
756 /* for UnpackedTarget the target is GenC{,xx}Object, dont mangle! */
757 int target_seen = 0;
759 token = line;
760 eat_space(&token);
761 while (*token)
763 end = token;
764 /* hard to believe that in this day and age drive letters still exist */
765 if (*end && (':' == *(end+1)) &&
766 (('\\' == *(end+2)) || ('/' == *(end+2))) && isalpha(*end))
768 end = end + 3; /* only one cross, err drive letter per filename */
770 while (*end && (' ' != *end) && ('\t' != *end) && (':' != *end)) {
771 ++end;
773 token_len = end - token;
774 if (target_seen &&
775 elide_dependency(token, token_len, &unpacked_end))
777 if (unpacked_end)
779 if (internal_boost && !PATHNCMP(unpacked_end - 5, "boost", 5))
781 ++boost_count;
782 if (boost_count == 1)
783 emit_single_boost_header();
784 else
786 /* don't output, and swallow trailing \\\n if any */
787 token = end;
788 eat_space(&token);
789 if (token[0] == '\\' && token[1] == '\n')
790 end = token + 2;
793 else
795 emit_unpacked_target(token, unpacked_end);
797 unpacked_end = 0;
800 else
802 if (fwrite(token, token_len, 1, stdout) != 1)
803 abort();
804 fputc(' ', stdout);
806 token = end;
807 eat_space(&token);
808 if (!target_seen)
810 if (':' == *token)
812 target_seen = 1;
813 fputc(':', stdout);
814 ++token;
815 eat_space(&token);
821 static inline char * eat_space_at_end(char * end)
823 char * real_end;
824 assert('\0' == *end);
825 real_end = end - 1;
826 while (' ' == *real_end || '\t' == *real_end || '\n' == *real_end
827 || ':' == *real_end)
828 { /* eat colon and whitespace at end */
829 --real_end;
831 return real_end;
834 static char* phony_content_buffer;
835 static inline char* generate_phony_line(char* phony_target, char* extension)
837 char* src;
838 char* dest;
839 char* last_dot = NULL;
840 //fprintf(stderr, "generate_phony_line called with phony_target %s and extension %s\n", phony_target, extension);
841 for(dest = phony_content_buffer+work_dir_len, src = phony_target; *src != 0; ++src, ++dest)
843 *dest = *src;
844 if(*dest == '.')
846 last_dot = dest;
849 //fprintf(stderr, "generate_phony_line after phony_target copy: %s\n", phony_content_buffer);
850 for(dest = last_dot+1, src = extension; *src != 0; ++src, ++dest)
852 *dest = *src;
854 //fprintf(stderr, "generate_phony_line after extension add: %s\n", phony_content_buffer);
855 strcpy(dest, ": $(gb_Helper_PHONY)\n");
856 //fprintf(stderr, "generate_phony_line after phony add: %s\n", phony_content_buffer);
857 return phony_content_buffer;
860 static inline int generate_phony_file(char* fn, char* content)
862 FILE* depfile;
863 depfile = fopen(fn, "w");
864 if(!depfile)
866 fprintf(stderr, "Could not open '%s' for writing: %s\n", fn, strerror(errno));
868 else
870 fputs(content, depfile);
871 fclose(depfile);
873 return !depfile;
876 static int _process(struct hash* dep_hash, char* fn)
878 int rc;
879 char* buffer;
880 char* end;
881 char* cursor;
882 char* cursor_out;
883 char* base;
884 char* created_line = NULL;
885 char* src_relative;
886 int continuation = 0;
887 char last_ns = 0;
888 off_t size;
890 buffer = file_load(fn, &size, &rc);
891 /* Note: yes we are going to leak 'buffer'
892 * this is on purpose, to avoid cloning the 'key' out of it
893 * and our special 'hash' just store the pointer to the key
894 * inside of buffer, hence it need to remain allocated
896 if(!rc)
898 base = cursor_out = cursor = end = buffer;
899 end += size;
901 /* first eat unneeded space at the beginning of file
903 while(cursor < end && (*cursor == ' ' || *cursor == '\\'))
904 ++cursor;
906 while(cursor < end)
908 if(*cursor == '\\')
910 continuation = 1;
911 *cursor_out++ = *cursor++;
913 else if(*cursor == '/')
915 if(cursor + 3 < end)
917 if(!memcmp(cursor, "/../", 4))
919 _cancel_relative(base, &cursor, &cursor_out, end);
922 *cursor_out++ = *cursor++;
924 else if(*cursor == '\n')
926 if(!continuation)
928 *cursor_out = 0;
929 if(base < cursor)
931 /* here we have a complete rule */
932 if(last_ns == ':')
934 /* if the rule ended in ':' that is a no-dep rule
935 * these are the one for which we want to filter
936 * duplicate out
938 int key_len = eat_space_at_end(cursor_out) - base;
939 if (!elide_dependency(base,key_len + 1, NULL)
940 && hash_store(dep_hash, base, key_len))
942 /* DO NOT modify base after it has been added
943 as key by hash_store */
944 print_fullpaths(base);
945 putc('\n', stdout);
948 else
950 /* rule with dep, just write it */
951 print_fullpaths(base);
952 putc('\n', stdout);
954 last_ns = ' '; // cannot hurt to reset it
956 cursor += 1;
957 base = cursor_out = cursor;
959 else
961 /* here we have a '\' followed by \n this is a continuation
962 * i.e not a complete rule yet
964 *cursor_out++ = *cursor++;
965 continuation = 0; // cancel current one (empty lines!)
968 else
970 continuation = 0;
971 /* not using isspace() here save 25% of I refs and 75% of D refs based on cachegrind */
972 if(*cursor != ' ' && *cursor != '\n' && *cursor != '\t' )
974 last_ns = *cursor;
976 *cursor_out++ = *cursor++;
979 /* just in case the file did not end with a \n, there may be a pending rule */
980 if(base < cursor_out)
982 if(last_ns == ':')
984 int key_len = eat_space_at_end(cursor_out) - base;
985 if (!elide_dependency(base,key_len + 1, NULL) &&
986 hash_store(dep_hash, base, key_len))
988 puts(base);
989 putc('\n', stdout);
992 else
994 puts(base);
995 putc('\n', stdout);
999 else
1001 if(strncmp(fn, work_dir, work_dir_len) == 0)
1003 if(strncmp(fn+work_dir_len, "/Dep/", 5) == 0)
1005 src_relative = fn+work_dir_len+5;
1006 // cases ordered by frequency
1007 if(strncmp(src_relative, "CxxObject/", 10) == 0)
1009 created_line = generate_phony_line(src_relative+10, "o");
1010 rc = generate_phony_file(fn, created_line);
1012 else if(strncmp(fn+work_dir_len+5, "SrsPartTarget/", 14) == 0)
1014 created_line = generate_phony_line(src_relative+14, "");
1015 rc = generate_phony_file(fn, created_line);
1017 else if(strncmp(src_relative, "GenCxxObject/", 13) == 0)
1019 created_line = generate_phony_line(src_relative+13, "o");
1020 rc = generate_phony_file(fn, created_line);
1022 else if(strncmp(src_relative, "CObject/", 8) == 0)
1024 created_line = generate_phony_line(src_relative+8, "o");
1025 rc = generate_phony_file(fn, created_line);
1027 else if(strncmp(src_relative, "GenCObject/", 11) == 0)
1029 created_line = generate_phony_line(src_relative+11, "o");
1030 rc = generate_phony_file(fn, created_line);
1032 else if(strncmp(src_relative, "SdiObject/", 10) == 0)
1034 created_line = generate_phony_line(src_relative+10, "o");
1035 rc = generate_phony_file(fn, created_line);
1037 else if(strncmp(src_relative, "AsmObject/", 10) == 0)
1039 created_line = generate_phony_line(src_relative+10, "o");
1040 rc = generate_phony_file(fn, created_line);
1042 else if(strncmp(src_relative, "ObjCxxObject/", 13) == 0)
1044 created_line = generate_phony_line(src_relative+13, "o");
1045 rc = generate_phony_file(fn, created_line);
1047 else if(strncmp(src_relative, "ObjCObject/", 11) == 0)
1049 created_line = generate_phony_line(src_relative+11, "o");
1050 rc = generate_phony_file(fn, created_line);
1052 else
1054 fprintf(stderr, "no magic for %s(%s) in %s\n", fn, src_relative, work_dir);
1057 if(!rc)
1059 puts(created_line);
1063 return rc;
1066 static void _usage(void)
1068 fputs("Usage: concat-deps <file that contains dep_files>\n", stderr);
1071 #define kDEFAULT_HASH_SIZE 4096
1072 #define PHONY_TARGET_BUFFER 4096
1074 static int get_var(char **var, const char *name)
1076 *var = (char *)getenv(name);
1077 if(!*var)
1079 fprintf(stderr,"Error: %s is missing in the environement\n", name);
1080 return 1;
1082 return 0;
1085 int main(int argc, char** argv)
1087 int rc = 0;
1088 off_t in_list_size = 0;
1089 char* in_list;
1090 char* in_list_cursor;
1091 char* in_list_base;
1092 struct hash* dep_hash;
1093 const char *env_str;
1095 if(argc < 2)
1097 _usage();
1098 return 1;
1100 if(get_var(&base_dir, "SRCDIR") || get_var(&work_dir, "WORKDIR"))
1101 return 1;
1102 work_dir_len = strlen(work_dir);
1103 phony_content_buffer = malloc(PHONY_TARGET_BUFFER);
1104 strcpy(phony_content_buffer, work_dir);
1105 phony_content_buffer[work_dir_len] = '/';
1107 env_str = getenv("SYSTEM_BOOST");
1108 internal_boost = !env_str || strcmp(env_str,"TRUE");
1110 in_list = file_load(argv[1], &in_list_size, &rc);
1111 if(!rc)
1113 dep_hash = hash_create( kDEFAULT_HASH_SIZE);
1114 in_list_base = in_list_cursor = in_list;
1116 /* extract filename of dep file from a 'space' separated list */
1117 while(*in_list_cursor)
1119 if(*in_list_cursor == ' ' || *in_list_cursor == '\n')
1121 *in_list_cursor = 0;
1122 if(in_list_base < in_list_cursor)
1124 rc = _process(dep_hash, in_list_base);
1125 if(rc)
1127 break;
1130 in_list_cursor += 1;
1131 in_list_base = in_list_cursor;
1133 else
1135 in_list_cursor += 1;
1138 if(!rc)
1140 /* catch the last entry in case the input did not terminate with a 'space' */
1141 if(in_list_base < in_list_cursor)
1143 rc = _process(dep_hash, in_list_base);
1146 #ifdef HASH_STAT
1147 fprintf(stderr, "stats: u:%d s:%d l:%d t:%d c:%d m:%d $:%d\n",
1148 dep_hash->used, dep_hash->size, dep_hash->load_limit, dep_hash->stored,
1149 dep_hash->collisions, dep_hash->memcmp, dep_hash->cost);
1150 #endif
1152 return rc;
1155 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */