VirtualBox

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

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

kWorker/kDep: save a few header stat calls while optimizing dependencies.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: kDep.c 2948 2016-09-20 15:36: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)
190{
191 /*
192 * Walk the list correct the names and re-insert them.
193 */
194 PDEP pDepOrg = g_pDeps;
195 PDEP pDep = g_pDeps;
196 g_pDeps = NULL;
197 for (; pDep; pDep = pDep->pNext)
198 {
199#ifndef PATH_MAX
200 char szFilename[_MAX_PATH + 1];
201#else
202 char szFilename[PATH_MAX + 1];
203#endif
204 char *pszFilename;
205#ifndef KMK
206 struct stat s;
207#endif
208
209 /*
210 * Skip some fictive names like <built-in> and <command line>.
211 */
212 if ( pDep->szFilename[0] == '<'
213 && pDep->szFilename[pDep->cchFilename - 1] == '>')
214 continue;
215 pszFilename = pDep->szFilename;
216
217#if K_OS != K_OS_OS2 && K_OS != K_OS_WINDOWS
218 /*
219 * Skip any drive letters from compilers running in wine.
220 */
221 if (pszFilename[1] == ':')
222 pszFilename += 2;
223#endif
224
225 /*
226 * The microsoft compilers are notoriously screwing up the casing.
227 * This will screw up kmk (/ GNU Make).
228 */
229 if (fFixCase)
230 {
231#if K_OS == K_OS_WINDOWS
232 nt_fullpath_cached(pszFilename, szFilename, sizeof(szFilename));
233 fixslash(szFilename);
234#else
235 strcpy(szFilename, pszFilename);
236 fixslash(szFilename);
237 fixcase(szFilename);
238#endif
239 pszFilename = szFilename;
240 }
241
242 /*
243 * Check that the file exists before we start depending on it.
244 */
245#ifdef KWORKER
246 if (kwFsPathExists(pszFilename))
247#elif defined(KMK)
248 if (!file_exists_p(pszFilename))
249#elif K_OS == K_OS_WINDOWS
250 if (birdStatModTimeOnly(pszFilename, &s.st_mtim, 1 /*fFollowLink*/) != 0)
251#else
252 if (stat(pszFilename, &s) != 0)
253#endif
254 {
255 if ( !fQuiet
256 || errno != ENOENT
257 || ( pszFilename[0] != '/'
258 && pszFilename[0] != '\\'
259 && ( !isalpha(pszFilename[0])
260 || pszFilename[1] != ':'
261 || ( pszFilename[2] != '/'
262 && pszFilename[2] != '\\')))
263 )
264 fprintf(stderr, "kDep: Skipping '%s' - %s!\n", pszFilename, strerror(errno));
265 continue;
266 }
267
268 /*
269 * Insert the corrected dependency.
270 */
271 depAdd(pszFilename, strlen(pszFilename));
272 }
273
274 /*
275 * Free the old ones.
276 */
277 while (pDepOrg)
278 {
279 pDep = pDepOrg;
280 pDepOrg = pDepOrg->pNext;
281 free(pDep);
282 }
283}
284
285
286/**
287 * Prints the dependency chain.
288 *
289 * @returns Pointer to the allocated dependency.
290 * @param pOutput Output stream.
291 */
292void depPrint(FILE *pOutput)
293{
294 PDEP pDep;
295 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
296 fprintf(pOutput, " \\\n\t%s", pDep->szFilename);
297 fprintf(pOutput, "\n\n");
298}
299
300
301/**
302 * Prints empty dependency stubs for all dependencies.
303 */
304void depPrintStubs(FILE *pOutput)
305{
306 PDEP pDep;
307 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
308 fprintf(pOutput, "%s:\n\n", pDep->szFilename);
309}
310
311
312/* sdbm:
313 This algorithm was created for sdbm (a public-domain reimplementation of
314 ndbm) database library. it was found to do well in scrambling bits,
315 causing better distribution of the keys and fewer splits. it also happens
316 to be a good general hashing function with good distribution. the actual
317 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
318 is the faster version used in gawk. [there is even a faster, duff-device
319 version] the magic constant 65599 was picked out of thin air while
320 experimenting with different constants, and turns out to be a prime.
321 this is one of the algorithms used in berkeley db (see sleepycat) and
322 elsewhere. */
323static unsigned sdbm(const char *str, size_t size)
324{
325 unsigned hash = 0;
326 int c;
327
328 while (size-- > 0 && (c = *(unsigned const char *)str++))
329 hash = c + (hash << 6) + (hash << 16) - hash;
330
331 return hash;
332}
333
334
335/**
336 * Adds a dependency.
337 *
338 * @returns Pointer to the allocated dependency.
339 * @param pszFilename The filename. Does not need to be terminated.
340 * @param cchFilename The length of the filename.
341 */
342PDEP depAdd(const char *pszFilename, size_t cchFilename)
343{
344 unsigned uHash = sdbm(pszFilename, cchFilename);
345 PDEP pDep;
346 PDEP pDepPrev;
347
348 /*
349 * Check if we've already got this one.
350 */
351 pDepPrev = NULL;
352 for (pDep = g_pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
353 if ( pDep->uHash == uHash
354 && pDep->cchFilename == cchFilename
355 && !memcmp(pDep->szFilename, pszFilename, cchFilename))
356 return pDep;
357
358 /*
359 * Add it.
360 */
361 pDep = (PDEP)malloc(sizeof(*pDep) + cchFilename);
362 if (!pDep)
363 {
364 fprintf(stderr, "\nOut of memory! (requested %lx bytes)\n\n",
365 (unsigned long)(sizeof(*pDep) + cchFilename));
366 exit(1);
367 }
368
369 pDep->cchFilename = cchFilename;
370 memcpy(pDep->szFilename, pszFilename, cchFilename);
371 pDep->szFilename[cchFilename] = '\0';
372 pDep->uHash = uHash;
373
374 if (pDepPrev)
375 {
376 pDep->pNext = pDepPrev->pNext;
377 pDepPrev->pNext = pDep;
378 }
379 else
380 {
381 pDep->pNext = g_pDeps;
382 g_pDeps = pDep;
383 }
384 return pDep;
385}
386
387
388/**
389 * Frees the current dependency chain.
390 */
391void depCleanup(void)
392{
393 PDEP pDep = g_pDeps;
394 g_pDeps = NULL;
395 while (pDep)
396 {
397 PDEP pFree = pDep;
398 pDep = pDep->pNext;
399 free(pFree);
400 }
401}
402
403
404/**
405 * Performs a hexdump.
406 */
407void depHexDump(const KU8 *pb, size_t cb, size_t offBase)
408{
409 const unsigned cchWidth = 16;
410 size_t off = 0;
411 while (off < cb)
412 {
413 unsigned i;
414 printf("%s%0*lx %04lx:", off ? "\n" : "", (int)sizeof(pb) * 2,
415 (unsigned long)offBase + (unsigned long)off, (unsigned long)off);
416 for (i = 0; i < cchWidth && off + i < cb ; i++)
417 printf(off + i < cb ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pb[i]);
418
419 while (i++ < cchWidth)
420 printf(" ");
421 printf(" ");
422
423 for (i = 0; i < cchWidth && off + i < cb; i++)
424 {
425 const KU8 u8 = pb[i];
426 printf("%c", u8 < 127 && u8 >= 32 ? u8 : '.');
427 }
428 off += cchWidth;
429 pb += cchWidth;
430 }
431 printf("\n");
432}
433
434
435/**
436 * Reads the file specified by the pInput file stream into memory.
437 *
438 * @returns The address of the memory mapping on success. This must be
439 * freed by calling depFreeFileMemory.
440 *
441 * @param pInput The file stream to load or map into memory.
442 * @param pcbFile Where to return the mapping (file) size.
443 * @param ppvOpaque Opaque data when mapping, otherwise NULL.
444 */
445void *depReadFileIntoMemory(FILE *pInput, size_t *pcbFile, void **ppvOpaque)
446{
447 void *pvFile;
448 long cbFile;
449
450 /*
451 * Figure out file size.
452 */
453#if defined(_MSC_VER)
454 cbFile = _filelength(fileno(pInput));
455 if (cbFile < 0)
456#else
457 if ( fseek(pInput, 0, SEEK_END) < 0
458 || (cbFile = ftell(pInput)) < 0
459 || fseek(pInput, 0, SEEK_SET))
460#endif
461 {
462 fprintf(stderr, "kDep: error: Failed to determin file size.\n");
463 return NULL;
464 }
465 if (pcbFile)
466 *pcbFile = cbFile;
467
468 /*
469 * Try mmap first.
470 */
471#ifdef USE_WIN_MMAP
472 {
473 HANDLE hMapObj = CreateFileMapping((HANDLE)_get_osfhandle(fileno(pInput)),
474 NULL, PAGE_READONLY, 0, cbFile, NULL);
475 if (hMapObj != NULL)
476 {
477 pvFile = MapViewOfFile(hMapObj, FILE_MAP_READ, 0, 0, cbFile);
478 if (pvFile)
479 {
480 *ppvOpaque = hMapObj;
481 return pvFile;
482 }
483 fprintf(stderr, "kDep: warning: MapViewOfFile failed, %d.\n", GetLastError());
484 CloseHandle(hMapObj);
485 }
486 else
487 fprintf(stderr, "kDep: warning: CreateFileMapping failed, %d.\n", GetLastError());
488 }
489
490#endif
491
492 /*
493 * Allocate memory and read the file.
494 */
495 pvFile = malloc(cbFile + 1);
496 if (pvFile)
497 {
498 if (fread(pvFile, cbFile, 1, pInput))
499 {
500 ((KU8 *)pvFile)[cbFile] = '\0';
501 *ppvOpaque = NULL;
502 return pvFile;
503 }
504 fprintf(stderr, "kDep: error: Failed to read %ld bytes.\n", cbFile);
505 free(pvFile);
506 }
507 else
508 fprintf(stderr, "kDep: error: Failed to allocate %ld bytes (file mapping).\n", cbFile);
509 return NULL;
510}
511
512
513/**
514 * Free resources allocated by depReadFileIntoMemory.
515 *
516 * @param pvFile The address of the memory mapping.
517 * @param pvOpaque The opaque value returned together with the mapping.
518 */
519void depFreeFileMemory(void *pvFile, void *pvOpaque)
520{
521#if defined(USE_WIN_MMAP)
522 if (pvOpaque)
523 {
524 UnmapViewOfFile(pvFile);
525 CloseHandle(pvOpaque);
526 return;
527 }
528#endif
529 free(pvFile);
530}
531
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