VirtualBox

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

Last change on this file since 3063 was 3063, checked in by bird, 8 years ago

warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: kDep.c 3063 2017-09-30 11:34:07Z 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#ifdef KWORKER
62extern int kwFsPathExists(const char *pszPath);
63#endif
64
65
66/*******************************************************************************
67* Global Variables *
68*******************************************************************************/
69/** List of dependencies. */
70static PDEP g_pDeps = NULL;
71
72
73/**
74 * Corrects all slashes to unix slashes.
75 *
76 * @returns pszFilename.
77 * @param pszFilename The filename to correct.
78 */
79static char *fixslash(char *pszFilename)
80{
81 char *psz = pszFilename;
82 while ((psz = strchr(psz, '\\')) != NULL)
83 *psz++ = '/';
84 return pszFilename;
85}
86
87
88#if K_OS == K_OS_OS2
89
90/**
91 * Corrects the case of a path.
92 *
93 * @param pszPath Pointer to the path, both input and output.
94 * The buffer must be able to hold one more byte than the string length.
95 */
96static void fixcase(char *pszFilename)
97{
98 return;
99}
100
101#elif K_OS != K_OS_WINDOWS
102
103/**
104 * Corrects the case of a path.
105 *
106 * @param pszPath Pointer to the path, both input and output.
107 */
108static void fixcase(char *pszFilename)
109{
110 char *psz;
111
112 /*
113 * Skip the root.
114 */
115 psz = pszFilename;
116 while (*psz == '/')
117 psz++;
118
119 /*
120 * Iterate all the components.
121 */
122 while (*psz)
123 {
124 char chSlash;
125 struct stat s;
126 char *pszStart = psz;
127
128 /*
129 * Find the next slash (or end of string) and terminate the string there.
130 */
131 while (*psz != '/' && *psz)
132 psz++;
133 chSlash = *psz;
134 *psz = '\0';
135
136 /*
137 * Does this part exist?
138 * If not we'll enumerate the directory and search for an case-insensitive match.
139 */
140 if (stat(pszFilename, &s))
141 {
142 struct dirent *pEntry;
143 DIR *pDir;
144 if (pszStart == pszFilename)
145 pDir = opendir(*pszFilename ? pszFilename : ".");
146 else
147 {
148 pszStart[-1] = '\0';
149 pDir = opendir(pszFilename);
150 pszStart[-1] = '/';
151 }
152 if (!pDir)
153 {
154 *psz = chSlash;
155 break; /* giving up, if we fail to open the directory. */
156 }
157
158 while ((pEntry = readdir(pDir)) != NULL)
159 {
160 if (!strcasecmp(pEntry->d_name, pszStart))
161 {
162 strcpy(pszStart, pEntry->d_name);
163 break;
164 }
165 }
166 closedir(pDir);
167 if (!pEntry)
168 {
169 *psz = chSlash;
170 break; /* giving up if not found. */
171 }
172 }
173
174 /* restore the slash and press on. */
175 *psz = chSlash;
176 while (*psz == '/')
177 psz++;
178 }
179
180 return;
181}
182
183#endif /* !OS/2 && !Windows */
184
185
186/**
187 * 'Optimizes' and corrects the dependencies.
188 */
189void depOptimize(int fFixCase, int fQuiet, const char *pszIgnoredExt)
190{
191 /*
192 * Walk the list correct the names and re-insert them.
193 */
194 size_t cchIgnoredExt = pszIgnoredExt ? strlen(pszIgnoredExt) : 0;
195 PDEP pDepOrg = g_pDeps;
196 PDEP pDep = g_pDeps;
197 g_pDeps = NULL;
198 for (; pDep; pDep = pDep->pNext)
199 {
200#ifndef PATH_MAX
201 char szFilename[_MAX_PATH + 1];
202#else
203 char szFilename[PATH_MAX + 1];
204#endif
205 char *pszFilename;
206#if !defined(KWORKER) && !defined(KMK)
207 struct stat s;
208#endif
209
210 /*
211 * Skip some fictive names like <built-in> and <command line>.
212 */
213 if ( pDep->szFilename[0] == '<'
214 && pDep->szFilename[pDep->cchFilename - 1] == '>')
215 continue;
216 pszFilename = pDep->szFilename;
217
218 /*
219 * Skip pszIgnoredExt if given.
220 */
221 if ( pszIgnoredExt
222 && pDep->cchFilename > cchIgnoredExt
223 && memcmp(&pDep->szFilename[pDep->cchFilename - cchIgnoredExt], pszIgnoredExt, cchIgnoredExt) == 0)
224 continue;
225
226#if K_OS != K_OS_OS2 && K_OS != K_OS_WINDOWS
227 /*
228 * Skip any drive letters from compilers running in wine.
229 */
230 if (pszFilename[1] == ':')
231 pszFilename += 2;
232#endif
233
234 /*
235 * The microsoft compilers are notoriously screwing up the casing.
236 * This will screw up kmk (/ GNU Make).
237 */
238 if (fFixCase)
239 {
240#if K_OS == K_OS_WINDOWS
241 nt_fullpath_cached(pszFilename, szFilename, sizeof(szFilename));
242 fixslash(szFilename);
243#else
244 strcpy(szFilename, pszFilename);
245 fixslash(szFilename);
246 fixcase(szFilename);
247#endif
248 pszFilename = szFilename;
249 }
250
251 /*
252 * Check that the file exists before we start depending on it.
253 */
254#ifdef KWORKER
255 if (!kwFsPathExists(pszFilename))
256#elif defined(KMK)
257 if (!file_exists_p(pszFilename))
258#elif K_OS == K_OS_WINDOWS
259 if (birdStatModTimeOnly(pszFilename, &s.st_mtim, 1 /*fFollowLink*/) != 0)
260#else
261 if (stat(pszFilename, &s) != 0)
262#endif
263 {
264 if ( !fQuiet
265 || errno != ENOENT
266 || ( pszFilename[0] != '/'
267 && pszFilename[0] != '\\'
268 && ( !isalpha(pszFilename[0])
269 || pszFilename[1] != ':'
270 || ( pszFilename[2] != '/'
271 && pszFilename[2] != '\\')))
272 )
273 fprintf(stderr, "kDep: Skipping '%s' - %s!\n", pszFilename, strerror(errno));
274 continue;
275 }
276
277 /*
278 * Insert the corrected dependency.
279 */
280 depAdd(pszFilename, strlen(pszFilename));
281 }
282
283 /*
284 * Free the old ones.
285 */
286 while (pDepOrg)
287 {
288 pDep = pDepOrg;
289 pDepOrg = pDepOrg->pNext;
290 free(pDep);
291 }
292}
293
294
295/**
296 * Prints the dependency chain.
297 *
298 * @returns Pointer to the allocated dependency.
299 * @param pOutput Output stream.
300 */
301void depPrint(FILE *pOutput)
302{
303 PDEP pDep;
304 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
305 fprintf(pOutput, " \\\n\t%s", pDep->szFilename);
306 fprintf(pOutput, "\n\n");
307}
308
309
310/**
311 * Prints empty dependency stubs for all dependencies.
312 */
313void depPrintStubs(FILE *pOutput)
314{
315 PDEP pDep;
316 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
317 fprintf(pOutput, "%s:\n\n", pDep->szFilename);
318}
319
320
321/* sdbm:
322 This algorithm was created for sdbm (a public-domain reimplementation of
323 ndbm) database library. it was found to do well in scrambling bits,
324 causing better distribution of the keys and fewer splits. it also happens
325 to be a good general hashing function with good distribution. the actual
326 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
327 is the faster version used in gawk. [there is even a faster, duff-device
328 version] the magic constant 65599 was picked out of thin air while
329 experimenting with different constants, and turns out to be a prime.
330 this is one of the algorithms used in berkeley db (see sleepycat) and
331 elsewhere. */
332static unsigned sdbm(const char *str, size_t size)
333{
334 unsigned hash = 0;
335 int c;
336
337 while (size-- > 0 && (c = *(unsigned const char *)str++))
338 hash = c + (hash << 6) + (hash << 16) - hash;
339
340 return hash;
341}
342
343
344/**
345 * Adds a dependency.
346 *
347 * @returns Pointer to the allocated dependency.
348 * @param pszFilename The filename. Does not need to be terminated.
349 * @param cchFilename The length of the filename.
350 */
351PDEP depAdd(const char *pszFilename, size_t cchFilename)
352{
353 unsigned uHash = sdbm(pszFilename, cchFilename);
354 PDEP pDep;
355 PDEP pDepPrev;
356
357 /*
358 * Check if we've already got this one.
359 */
360 pDepPrev = NULL;
361 for (pDep = g_pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
362 if ( pDep->uHash == uHash
363 && pDep->cchFilename == cchFilename
364 && !memcmp(pDep->szFilename, pszFilename, cchFilename))
365 return pDep;
366
367 /*
368 * Add it.
369 */
370 pDep = (PDEP)malloc(sizeof(*pDep) + cchFilename);
371 if (!pDep)
372 {
373 fprintf(stderr, "\nOut of memory! (requested %lx bytes)\n\n",
374 (unsigned long)(sizeof(*pDep) + cchFilename));
375 exit(1);
376 }
377
378 pDep->cchFilename = cchFilename;
379 memcpy(pDep->szFilename, pszFilename, cchFilename);
380 pDep->szFilename[cchFilename] = '\0';
381 pDep->uHash = uHash;
382
383 if (pDepPrev)
384 {
385 pDep->pNext = pDepPrev->pNext;
386 pDepPrev->pNext = pDep;
387 }
388 else
389 {
390 pDep->pNext = g_pDeps;
391 g_pDeps = pDep;
392 }
393 return pDep;
394}
395
396
397/**
398 * Frees the current dependency chain.
399 */
400void depCleanup(void)
401{
402 PDEP pDep = g_pDeps;
403 g_pDeps = NULL;
404 while (pDep)
405 {
406 PDEP pFree = pDep;
407 pDep = pDep->pNext;
408 free(pFree);
409 }
410}
411
412
413/**
414 * Performs a hexdump.
415 */
416void depHexDump(const KU8 *pb, size_t cb, size_t offBase)
417{
418 const unsigned cchWidth = 16;
419 size_t off = 0;
420 while (off < cb)
421 {
422 unsigned i;
423 printf("%s%0*lx %04lx:", off ? "\n" : "", (int)sizeof(pb) * 2,
424 (unsigned long)offBase + (unsigned long)off, (unsigned long)off);
425 for (i = 0; i < cchWidth && off + i < cb ; i++)
426 printf(off + i < cb ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pb[i]);
427
428 while (i++ < cchWidth)
429 printf(" ");
430 printf(" ");
431
432 for (i = 0; i < cchWidth && off + i < cb; i++)
433 {
434 const KU8 u8 = pb[i];
435 printf("%c", u8 < 127 && u8 >= 32 ? u8 : '.');
436 }
437 off += cchWidth;
438 pb += cchWidth;
439 }
440 printf("\n");
441}
442
443
444/**
445 * Reads the file specified by the pInput file stream into memory.
446 *
447 * @returns The address of the memory mapping on success. This must be
448 * freed by calling depFreeFileMemory.
449 *
450 * @param pInput The file stream to load or map into memory.
451 * @param pcbFile Where to return the mapping (file) size.
452 * @param ppvOpaque Opaque data when mapping, otherwise NULL.
453 */
454void *depReadFileIntoMemory(FILE *pInput, size_t *pcbFile, void **ppvOpaque)
455{
456 void *pvFile;
457 long cbFile;
458
459 /*
460 * Figure out file size.
461 */
462#if defined(_MSC_VER)
463 cbFile = _filelength(fileno(pInput));
464 if (cbFile < 0)
465#else
466 if ( fseek(pInput, 0, SEEK_END) < 0
467 || (cbFile = ftell(pInput)) < 0
468 || fseek(pInput, 0, SEEK_SET))
469#endif
470 {
471 fprintf(stderr, "kDep: error: Failed to determin file size.\n");
472 return NULL;
473 }
474 if (pcbFile)
475 *pcbFile = cbFile;
476
477 /*
478 * Try mmap first.
479 */
480#ifdef USE_WIN_MMAP
481 {
482 HANDLE hMapObj = CreateFileMapping((HANDLE)_get_osfhandle(fileno(pInput)),
483 NULL, PAGE_READONLY, 0, cbFile, NULL);
484 if (hMapObj != NULL)
485 {
486 pvFile = MapViewOfFile(hMapObj, FILE_MAP_READ, 0, 0, cbFile);
487 if (pvFile)
488 {
489 *ppvOpaque = hMapObj;
490 return pvFile;
491 }
492 fprintf(stderr, "kDep: warning: MapViewOfFile failed, %d.\n", GetLastError());
493 CloseHandle(hMapObj);
494 }
495 else
496 fprintf(stderr, "kDep: warning: CreateFileMapping failed, %d.\n", GetLastError());
497 }
498
499#endif
500
501 /*
502 * Allocate memory and read the file.
503 */
504 pvFile = malloc(cbFile + 1);
505 if (pvFile)
506 {
507 if (fread(pvFile, cbFile, 1, pInput))
508 {
509 ((KU8 *)pvFile)[cbFile] = '\0';
510 *ppvOpaque = NULL;
511 return pvFile;
512 }
513 fprintf(stderr, "kDep: error: Failed to read %ld bytes.\n", cbFile);
514 free(pvFile);
515 }
516 else
517 fprintf(stderr, "kDep: error: Failed to allocate %ld bytes (file mapping).\n", cbFile);
518 return NULL;
519}
520
521
522/**
523 * Free resources allocated by depReadFileIntoMemory.
524 *
525 * @param pvFile The address of the memory mapping.
526 * @param pvOpaque The opaque value returned together with the mapping.
527 */
528void depFreeFileMemory(void *pvFile, void *pvOpaque)
529{
530#if defined(USE_WIN_MMAP)
531 if (pvOpaque)
532 {
533 UnmapViewOfFile(pvFile);
534 CloseHandle(pvOpaque);
535 return;
536 }
537#endif
538 free(pvFile);
539}
540
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