VirtualBox

source: kBuild/trunk/src/lib/kDep.c@ 2886

Last change on this file since 2886 was 2886, checked in by bird, 9 years ago

kmk: Added $(dircache-ctl cmd,...) function for controlling the directory content cache on windows. Some other optimizations.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* $Id: kDep.c 2886 2016-09-06 14:31:46Z bird $ */
2/** @file
3 * kDep - Common Dependency Managemnt Code.
4 */
5
6/*
7 * Copyright (c) 2004-2013 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#ifdef KMK /* For when it gets compiled and linked into kmk. */
36# include "make.h"
37#endif
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <errno.h>
42#include <ctype.h>
43#include <limits.h>
44#include <sys/stat.h>
45#include "k/kDefs.h"
46#include "k/kTypes.h"
47#if K_OS == K_OS_WINDOWS
48# define USE_WIN_MMAP
49# include <io.h>
50# include <Windows.h>
51# include "nt_fullpath.h"
52# include "nt/ntstat.h"
53#else
54# include <dirent.h>
55# include <unistd.h>
56# include <stdint.h>
57#endif
58
59#include "kDep.h"
60
61
62/*******************************************************************************
63* Global Variables *
64*******************************************************************************/
65/** List of dependencies. */
66static PDEP g_pDeps = NULL;
67
68
69/**
70 * Corrects all slashes to unix slashes.
71 *
72 * @returns pszFilename.
73 * @param pszFilename The filename to correct.
74 */
75static char *fixslash(char *pszFilename)
76{
77 char *psz = pszFilename;
78 while ((psz = strchr(psz, '\\')) != NULL)
79 *psz++ = '/';
80 return pszFilename;
81}
82
83
84#if K_OS == K_OS_OS2
85
86/**
87 * Corrects the case of a path.
88 *
89 * @param pszPath Pointer to the path, both input and output.
90 * The buffer must be able to hold one more byte than the string length.
91 */
92static void fixcase(char *pszFilename)
93{
94 return;
95}
96
97#elif K_OS != K_OS_WINDOWS
98
99/**
100 * Corrects the case of a path.
101 *
102 * @param pszPath Pointer to the path, both input and output.
103 */
104static void fixcase(char *pszFilename)
105{
106 char *psz;
107
108 /*
109 * Skip the root.
110 */
111 psz = pszFilename;
112 while (*psz == '/')
113 psz++;
114
115 /*
116 * Iterate all the components.
117 */
118 while (*psz)
119 {
120 char chSlash;
121 struct stat s;
122 char *pszStart = psz;
123
124 /*
125 * Find the next slash (or end of string) and terminate the string there.
126 */
127 while (*psz != '/' && *psz)
128 *psz++;
129 chSlash = *psz;
130 *psz = '\0';
131
132 /*
133 * Does this part exist?
134 * If not we'll enumerate the directory and search for an case-insensitive match.
135 */
136 if (stat(pszFilename, &s))
137 {
138 struct dirent *pEntry;
139 DIR *pDir;
140 if (pszStart == pszFilename)
141 pDir = opendir(*pszFilename ? pszFilename : ".");
142 else
143 {
144 pszStart[-1] = '\0';
145 pDir = opendir(pszFilename);
146 pszStart[-1] = '/';
147 }
148 if (!pDir)
149 {
150 *psz = chSlash;
151 break; /* giving up, if we fail to open the directory. */
152 }
153
154 while ((pEntry = readdir(pDir)) != NULL)
155 {
156 if (!strcasecmp(pEntry->d_name, pszStart))
157 {
158 strcpy(pszStart, pEntry->d_name);
159 break;
160 }
161 }
162 closedir(pDir);
163 if (!pEntry)
164 {
165 *psz = chSlash;
166 break; /* giving up if not found. */
167 }
168 }
169
170 /* restore the slash and press on. */
171 *psz = chSlash;
172 while (*psz == '/')
173 psz++;
174 }
175
176 return;
177}
178
179#endif /* !OS/2 && !Windows */
180
181
182/**
183 * 'Optimizes' and corrects the dependencies.
184 */
185void depOptimize(int fFixCase, int fQuiet)
186{
187 /*
188 * Walk the list correct the names and re-insert them.
189 */
190 PDEP pDepOrg = g_pDeps;
191 PDEP pDep = g_pDeps;
192 g_pDeps = NULL;
193 for (; pDep; pDep = pDep->pNext)
194 {
195#ifndef PATH_MAX
196 char szFilename[_MAX_PATH + 1];
197#else
198 char szFilename[PATH_MAX + 1];
199#endif
200 char *pszFilename;
201#ifndef KMK
202 struct stat s;
203#endif
204
205 /*
206 * Skip some fictive names like <built-in> and <command line>.
207 */
208 if ( pDep->szFilename[0] == '<'
209 && pDep->szFilename[pDep->cchFilename - 1] == '>')
210 continue;
211 pszFilename = pDep->szFilename;
212
213#if K_OS != K_OS_OS2 && K_OS != K_OS_WINDOWS
214 /*
215 * Skip any drive letters from compilers running in wine.
216 */
217 if (pszFilename[1] == ':')
218 pszFilename += 2;
219#endif
220
221 /*
222 * The microsoft compilers are notoriously screwing up the casing.
223 * This will screw up kmk (/ GNU Make).
224 */
225 if (fFixCase)
226 {
227#if K_OS == K_OS_WINDOWS
228 nt_fullpath_cached(pszFilename, szFilename, sizeof(szFilename));
229 fixslash(szFilename);
230#else
231 strcpy(szFilename, pszFilename);
232 fixslash(szFilename);
233 fixcase(szFilename);
234#endif
235 pszFilename = szFilename;
236 }
237
238 /*
239 * Check that the file exists before we start depending on it.
240 */
241#ifdef KMK
242 if (!file_exists_p(pszFilename))
243#elif K_OS == K_OS_WINDOWS
244 if (birdStatModTimeOnly(pszFilename, &s.st_mtim, 1 /*fFollowLink*/) != 0)
245#else
246 if (stat(pszFilename, &s) != 0)
247#endif
248 {
249 if ( !fQuiet
250 || errno != ENOENT
251 || ( pszFilename[0] != '/'
252 && pszFilename[0] != '\\'
253 && ( !isalpha(pszFilename[0])
254 || pszFilename[1] != ':'
255 || ( pszFilename[2] != '/'
256 && pszFilename[2] != '\\')))
257 )
258 fprintf(stderr, "kDep: Skipping '%s' - %s!\n", pszFilename, strerror(errno));
259 continue;
260 }
261
262 /*
263 * Insert the corrected dependency.
264 */
265 depAdd(pszFilename, strlen(pszFilename));
266 }
267
268 /*
269 * Free the old ones.
270 */
271 while (pDepOrg)
272 {
273 pDep = pDepOrg;
274 pDepOrg = pDepOrg->pNext;
275 free(pDep);
276 }
277}
278
279
280/**
281 * Prints the dependency chain.
282 *
283 * @returns Pointer to the allocated dependency.
284 * @param pOutput Output stream.
285 */
286void depPrint(FILE *pOutput)
287{
288 PDEP pDep;
289 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
290 fprintf(pOutput, " \\\n\t%s", pDep->szFilename);
291 fprintf(pOutput, "\n\n");
292}
293
294
295/**
296 * Prints empty dependency stubs for all dependencies.
297 */
298void depPrintStubs(FILE *pOutput)
299{
300 PDEP pDep;
301 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
302 fprintf(pOutput, "%s:\n\n", pDep->szFilename);
303}
304
305
306/* sdbm:
307 This algorithm was created for sdbm (a public-domain reimplementation of
308 ndbm) database library. it was found to do well in scrambling bits,
309 causing better distribution of the keys and fewer splits. it also happens
310 to be a good general hashing function with good distribution. the actual
311 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
312 is the faster version used in gawk. [there is even a faster, duff-device
313 version] the magic constant 65599 was picked out of thin air while
314 experimenting with different constants, and turns out to be a prime.
315 this is one of the algorithms used in berkeley db (see sleepycat) and
316 elsewhere. */
317static unsigned sdbm(const char *str, size_t size)
318{
319 unsigned hash = 0;
320 int c;
321
322 while (size-- > 0 && (c = *(unsigned const char *)str++))
323 hash = c + (hash << 6) + (hash << 16) - hash;
324
325 return hash;
326}
327
328
329/**
330 * Adds a dependency.
331 *
332 * @returns Pointer to the allocated dependency.
333 * @param pszFilename The filename. Does not need to be terminated.
334 * @param cchFilename The length of the filename.
335 */
336PDEP depAdd(const char *pszFilename, size_t cchFilename)
337{
338 unsigned uHash = sdbm(pszFilename, cchFilename);
339 PDEP pDep;
340 PDEP pDepPrev;
341
342 /*
343 * Check if we've already got this one.
344 */
345 pDepPrev = NULL;
346 for (pDep = g_pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
347 if ( pDep->uHash == uHash
348 && pDep->cchFilename == cchFilename
349 && !memcmp(pDep->szFilename, pszFilename, cchFilename))
350 return pDep;
351
352 /*
353 * Add it.
354 */
355 pDep = (PDEP)malloc(sizeof(*pDep) + cchFilename);
356 if (!pDep)
357 {
358 fprintf(stderr, "\nOut of memory! (requested %lx bytes)\n\n",
359 (unsigned long)(sizeof(*pDep) + cchFilename));
360 exit(1);
361 }
362
363 pDep->cchFilename = cchFilename;
364 memcpy(pDep->szFilename, pszFilename, cchFilename);
365 pDep->szFilename[cchFilename] = '\0';
366 pDep->uHash = uHash;
367
368 if (pDepPrev)
369 {
370 pDep->pNext = pDepPrev->pNext;
371 pDepPrev->pNext = pDep;
372 }
373 else
374 {
375 pDep->pNext = g_pDeps;
376 g_pDeps = pDep;
377 }
378 return pDep;
379}
380
381
382/**
383 * Frees the current dependency chain.
384 */
385void depCleanup(void)
386{
387 PDEP pDep = g_pDeps;
388 g_pDeps = NULL;
389 while (pDep)
390 {
391 PDEP pFree = pDep;
392 pDep = pDep->pNext;
393 free(pFree);
394 }
395}
396
397
398/**
399 * Performs a hexdump.
400 */
401void depHexDump(const KU8 *pb, size_t cb, size_t offBase)
402{
403 const unsigned cchWidth = 16;
404 size_t off = 0;
405 while (off < cb)
406 {
407 unsigned i;
408 printf("%s%0*lx %04lx:", off ? "\n" : "", (int)sizeof(pb) * 2,
409 (unsigned long)offBase + (unsigned long)off, (unsigned long)off);
410 for (i = 0; i < cchWidth && off + i < cb ; i++)
411 printf(off + i < cb ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pb[i]);
412
413 while (i++ < cchWidth)
414 printf(" ");
415 printf(" ");
416
417 for (i = 0; i < cchWidth && off + i < cb; i++)
418 {
419 const KU8 u8 = pb[i];
420 printf("%c", u8 < 127 && u8 >= 32 ? u8 : '.');
421 }
422 off += cchWidth;
423 pb += cchWidth;
424 }
425 printf("\n");
426}
427
428
429/**
430 * Reads the file specified by the pInput file stream into memory.
431 *
432 * @returns The address of the memory mapping on success. This must be
433 * freed by calling depFreeFileMemory.
434 *
435 * @param pInput The file stream to load or map into memory.
436 * @param pcbFile Where to return the mapping (file) size.
437 * @param ppvOpaque Opaque data when mapping, otherwise NULL.
438 */
439void *depReadFileIntoMemory(FILE *pInput, size_t *pcbFile, void **ppvOpaque)
440{
441 void *pvFile;
442 long cbFile;
443
444 /*
445 * Figure out file size.
446 */
447#if defined(_MSC_VER)
448 cbFile = _filelength(fileno(pInput));
449 if (cbFile < 0)
450#else
451 if ( fseek(pInput, 0, SEEK_END) < 0
452 || (cbFile = ftell(pInput)) < 0
453 || fseek(pInput, 0, SEEK_SET))
454#endif
455 {
456 fprintf(stderr, "kDep: error: Failed to determin file size.\n");
457 return NULL;
458 }
459 if (pcbFile)
460 *pcbFile = cbFile;
461
462 /*
463 * Try mmap first.
464 */
465#ifdef USE_WIN_MMAP
466 {
467 HANDLE hMapObj = CreateFileMapping((HANDLE)_get_osfhandle(fileno(pInput)),
468 NULL, PAGE_READONLY, 0, cbFile, NULL);
469 if (hMapObj != NULL)
470 {
471 pvFile = MapViewOfFile(hMapObj, FILE_MAP_READ, 0, 0, cbFile);
472 if (pvFile)
473 {
474 *ppvOpaque = hMapObj;
475 return pvFile;
476 }
477 fprintf(stderr, "kDep: warning: MapViewOfFile failed, %d.\n", GetLastError());
478 CloseHandle(hMapObj);
479 }
480 else
481 fprintf(stderr, "kDep: warning: CreateFileMapping failed, %d.\n", GetLastError());
482 }
483
484#endif
485
486 /*
487 * Allocate memory and read the file.
488 */
489 pvFile = malloc(cbFile + 1);
490 if (pvFile)
491 {
492 if (fread(pvFile, cbFile, 1, pInput))
493 {
494 ((KU8 *)pvFile)[cbFile] = '\0';
495 *ppvOpaque = NULL;
496 return pvFile;
497 }
498 fprintf(stderr, "kDep: error: Failed to read %ld bytes.\n", cbFile);
499 free(pvFile);
500 }
501 else
502 fprintf(stderr, "kDep: error: Failed to allocate %ld bytes (file mapping).\n", cbFile);
503 return NULL;
504}
505
506
507/**
508 * Free resources allocated by depReadFileIntoMemory.
509 *
510 * @param pvFile The address of the memory mapping.
511 * @param pvOpaque The opaque value returned together with the mapping.
512 */
513void depFreeFileMemory(void *pvFile, void *pvOpaque)
514{
515#if defined(USE_WIN_MMAP)
516 if (pvOpaque)
517 {
518 UnmapViewOfFile(pvFile);
519 CloseHandle(pvOpaque);
520 return;
521 }
522#endif
523 free(pvFile);
524}
525
Note: See TracBrowser for help on using the repository browser.

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