VirtualBox

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

Last change on this file since 3223 was 3174, checked in by bird, 7 years ago

kmkbultin: environment fixes and stats.

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