Fixed EDKT118
[mirror_edk2.git] / Tools / Source / TianoTools / MakeDeps / MakeDeps.c
CommitLineData
b09da165 1/*++\r
2\r
3Copyright (c) 2004, Intel Corporation \r
4All rights reserved. This program and the accompanying materials \r
5are licensed and made available under the terms and conditions of the BSD License \r
6which accompanies this distribution. The full text of the license may be found at \r
7http://opensource.org/licenses/bsd-license.php \r
8 \r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
11\r
12Module Name:\r
13\r
14 MakeDeps.c \r
15\r
16Abstract:\r
17\r
18 Recursively scan source files to find include files and emit them to \r
19 create dependency lists.\r
20\r
21--*/\r
22\r
23#include <stdio.h>\r
24#include <string.h>\r
25#include <stdlib.h>\r
26#include <ctype.h>\r
27\r
ce53a8c3 28#include <Common/UefiBaseTypes.h>\r
29\r
b09da165 30#include "EfiUtilityMsgs.h"\r
ce53a8c3 31#include "CommonLib.h"\r
b09da165 32\r
33//\r
34// Structure to maintain a linked list of strings\r
35//\r
36typedef struct _STRING_LIST {\r
37 struct _STRING_LIST *Next;\r
38 char *Str;\r
39} STRING_LIST;\r
40\r
41#define UTILITY_NAME "MakeDeps"\r
42\r
43#define MAX_LINE_LEN 2048\r
44#define MAX_PATH 2048\r
45#define START_NEST_DEPTH 1\r
46#define MAX_NEST_DEPTH 1000 // just in case we get in an endless loop.\r
47//\r
48// Define the relative paths used by the special #include macros\r
49//\r
52cbbdbc 50#define PROTOCOL_DIR_PATH "Protocol/"\r
51#define GUID_DIR_PATH "Guid/"\r
52#define ARCH_PROTOCOL_DIR_PATH "ArchProtocol/"\r
53#define PPI_PROTOCOL_DIR_PATH "Ppi/"\r
b09da165 54\r
55//\r
56// Use this structure to keep track of all the special #include forms\r
57//\r
58typedef struct {\r
59 INT8 *IncludeMacroName;\r
60 INT8 *PathName;\r
61} INCLUDE_MACRO_CONVERSION;\r
62\r
63//\r
64// This data is used to convert #include macros like:\r
65// #include EFI_PROTOCOL_DEFINITION(xxx)\r
66// into\r
67// #include Protocol/xxx/xxx.h\r
68//\r
69static const INCLUDE_MACRO_CONVERSION mMacroConversion[] = {\r
70 "EFI_PROTOCOL_DEFINITION",\r
71 PROTOCOL_DIR_PATH,\r
72 "EFI_GUID_DEFINITION",\r
73 GUID_DIR_PATH,\r
74 "EFI_ARCH_PROTOCOL_DEFINITION",\r
75 ARCH_PROTOCOL_DIR_PATH,\r
76 "EFI_PROTOCOL_PRODUCER",\r
77 PROTOCOL_DIR_PATH,\r
78 "EFI_PROTOCOL_CONSUMER",\r
79 PROTOCOL_DIR_PATH,\r
80 "EFI_PROTOCOL_DEPENDENCY",\r
81 PROTOCOL_DIR_PATH,\r
82 "EFI_ARCH_PROTOCOL_PRODUCER",\r
83 ARCH_PROTOCOL_DIR_PATH,\r
84 "EFI_ARCH_PROTOCOL_CONSUMER",\r
85 ARCH_PROTOCOL_DIR_PATH,\r
86 "EFI_ARCH_PROTOCOL_DEPENDENCY",\r
87 ARCH_PROTOCOL_DIR_PATH,\r
88 "EFI_PPI_DEFINITION",\r
89 PPI_PROTOCOL_DIR_PATH,\r
90 "EFI_PPI_PRODUCER",\r
91 PPI_PROTOCOL_DIR_PATH,\r
92 "EFI_PPI_CONSUMER",\r
93 PPI_PROTOCOL_DIR_PATH,\r
94 "EFI_PPI_DEPENDENCY",\r
95 PPI_PROTOCOL_DIR_PATH,\r
96 NULL,\r
97 NULL\r
98};\r
99\r
100typedef struct _SYMBOL {\r
101 struct _SYMBOL *Next;\r
102 INT8 *Name;\r
103 INT8 *Value;\r
104} SYMBOL;\r
105\r
106//\r
107// Here's all our globals. We need a linked list of include paths, a linked\r
108// list of source files, a linked list of subdirectories (appended to each\r
109// include path when searching), and flags to keep track of command-line options.\r
110//\r
111static struct {\r
112 STRING_LIST *IncludePaths; // all include paths to search\r
113 STRING_LIST *SourceFiles; // all source files to parse\r
114 STRING_LIST *SubDirs; // appended to each include path when searching\r
115 SYMBOL *SymbolTable; // for replacement strings\r
116 FILE *OutFptr; // output dependencies to this file\r
117 BOOLEAN Verbose; // for more detailed output\r
118 BOOLEAN IgnoreNotFound; // no warnings if files not found\r
119 BOOLEAN QuietMode; // -q - don't print missing file warnings\r
120 BOOLEAN NoSystem; // don't process #include <system> files\r
121 BOOLEAN NeverFail; // always return success\r
122 BOOLEAN NoDupes; // to not list duplicate dependency files (for timing purposes)\r
123 BOOLEAN UseSumDeps; // use summary dependency files if found\r
124 INT8 TargetFileName[MAX_PATH]; // target object filename\r
125 INT8 SumDepsPath[MAX_PATH]; // path to summary files\r
126 INT8 *OutFileName; // -o option\r
127} mGlobals;\r
128\r
129static\r
130STATUS\r
131ProcessFile (\r
132 INT8 *TargetFileName,\r
133 INT8 *FileName,\r
134 UINT32 NestDepth,\r
135 STRING_LIST *ProcessedFiles\r
136 );\r
137\r
138static\r
139FILE *\r
140FindFile (\r
141 INT8 *FileName,\r
142 UINT32 FileNameLen\r
143 );\r
144\r
145static\r
146void\r
147PrintDependency (\r
148 INT8 *Target,\r
149 INT8 *DependentFile\r
150 );\r
151\r
152static\r
153void\r
154ReplaceSymbols (\r
155 INT8 *Str,\r
156 UINT32 StrSize\r
157 );\r
158\r
159static\r
160STATUS\r
161ProcessArgs (\r
162 int Argc,\r
163 char *Argv[]\r
164 );\r
165\r
166static\r
167void\r
168Usage (\r
169 VOID\r
170 );\r
171\r
172static\r
173void\r
174FreeLists (\r
175 VOID\r
176 );\r
177\r
178int\r
179main (\r
180 int Argc,\r
181 char *Argv[]\r
182 )\r
183/*++\r
184\r
185Routine Description:\r
186\r
187 Call the routine to parse the command-line options, then process each file\r
188 to build dependencies.\r
189 \r
190Arguments:\r
191\r
192 Argc - Standard C main() argc.\r
193 Argv - Standard C main() argv.\r
194\r
195Returns:\r
196\r
197 0 if successful\r
198 nonzero otherwise\r
199 \r
200--*/\r
201{\r
202 STRING_LIST *File;\r
203 STRING_LIST ProcessedFiles;\r
204 STRING_LIST *TempList;\r
205 STATUS Status;\r
206 INT8 *Cptr;\r
207 INT8 TargetFileName[MAX_PATH];\r
208\r
209 SetUtilityName (UTILITY_NAME);\r
210 //\r
211 // Process the command-line arguments\r
212 //\r
213 Status = ProcessArgs (Argc, Argv);\r
214 if (Status != STATUS_SUCCESS) {\r
215 return STATUS_ERROR;\r
216 }\r
217 //\r
218 // Go through the list of source files and process each.\r
219 //\r
220 memset (&ProcessedFiles, 0, sizeof (STRING_LIST));\r
221 File = mGlobals.SourceFiles;\r
222 while (File != NULL) {\r
223 //\r
224 // Clear out our list of processed files\r
225 //\r
226 TempList = ProcessedFiles.Next;\r
227 while (ProcessedFiles.Next != NULL) {\r
228 TempList = ProcessedFiles.Next->Next;\r
229 free (ProcessedFiles.Next->Str);\r
230 free (ProcessedFiles.Next);\r
231 ProcessedFiles.Next = TempList;\r
232 }\r
233 //\r
234 // Replace filename extension with ".obj" if they did not\r
235 // specifically specify the target file\r
236 //\r
237 if (mGlobals.TargetFileName[0] == 0) {\r
238 strcpy (TargetFileName, File->Str);\r
239 //\r
240 // Find the .extension\r
241 //\r
242 for (Cptr = TargetFileName + strlen (TargetFileName) - 1;\r
52cbbdbc 243 (*Cptr != '\\' && *Cptr != '/') && (Cptr > TargetFileName) && (*Cptr != '.');\r
b09da165 244 Cptr--\r
245 )\r
246 ;\r
247 if (Cptr == TargetFileName) {\r
248 Error (NULL, 0, 0, File->Str, "could not locate extension in filename");\r
249 goto Finish;\r
250 }\r
251 //\r
252 // Tack on the ".obj"\r
253 //\r
254 strcpy (Cptr, ".obj");\r
255 } else {\r
256 //\r
257 // Copy the target filename they specified\r
258 //\r
259 strcpy (TargetFileName, mGlobals.TargetFileName);\r
260 }\r
261\r
262 Status = ProcessFile (TargetFileName, File->Str, START_NEST_DEPTH, &ProcessedFiles);\r
263 if (Status != STATUS_SUCCESS) {\r
264 goto Finish;\r
265 }\r
266\r
267 File = File->Next;\r
268 }\r
269\r
270Finish:\r
271 //\r
272 // Free up memory\r
273 //\r
274 FreeLists ();\r
275 //\r
276 // Free up our processed files list\r
277 //\r
278 TempList = ProcessedFiles.Next;\r
279 while (ProcessedFiles.Next != NULL) {\r
280 TempList = ProcessedFiles.Next->Next;\r
281 free (ProcessedFiles.Next->Str);\r
282 free (ProcessedFiles.Next);\r
283 ProcessedFiles.Next = TempList;\r
284 }\r
285 //\r
286 // Close our output file\r
287 //\r
288 if ((mGlobals.OutFptr != stdout) && (mGlobals.OutFptr != NULL)) {\r
289 fclose (mGlobals.OutFptr);\r
290 }\r
291\r
292 if (mGlobals.NeverFail) {\r
293 return STATUS_SUCCESS;\r
294 }\r
295 //\r
296 // If any errors, then delete our output so that it will get created\r
297 // again on a rebuild.\r
298 //\r
299 if ((GetUtilityStatus () == STATUS_ERROR) && (mGlobals.OutFileName != NULL)) {\r
300 remove (mGlobals.OutFileName);\r
301 }\r
302\r
303 return GetUtilityStatus ();\r
304}\r
305\r
306static\r
307STATUS\r
308ProcessFile (\r
309 INT8 *TargetFileName,\r
310 INT8 *FileName,\r
311 UINT32 NestDepth,\r
312 STRING_LIST *ProcessedFiles\r
313 )\r
314/*++\r
315\r
316Routine Description:\r
317\r
318 Given a source file name, open the file and parse all #include lines.\r
319 \r
320Arguments:\r
321\r
322 TargetFileName - name of the usually .obj target\r
323 FileName - name of the file to process\r
324 NestDepth - how deep we're nested in includes\r
325 ProcessedFiles - list of processed files.\r
326\r
327Returns:\r
328\r
329 standard status.\r
330 \r
331--*/\r
332{\r
333 FILE *Fptr;\r
334 INT8 Line[MAX_LINE_LEN];\r
335 INT8 *Cptr;\r
336 INT8 *EndPtr;\r
337 INT8 *SaveCptr;\r
338 INT8 EndChar;\r
339 INT8 FileNameCopy[MAX_PATH];\r
340 INT8 MacroIncludeFileName[MAX_LINE_LEN];\r
341 INT8 SumDepsFile[MAX_PATH];\r
342 STATUS Status;\r
343 UINT32 Index;\r
344 UINT32 LineNum;\r
345 STRING_LIST *ListPtr;\r
346\r
347 Status = STATUS_SUCCESS;\r
348 Fptr = NULL;\r
349 //\r
350 // Print the file being processed. Indent so you can tell the include nesting\r
351 // depth.\r
352 //\r
353 if (mGlobals.Verbose) {\r
354 fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', FileName);\r
355 }\r
356 //\r
357 // If we're using summary dependency files, and a matching .dep file is\r
358 // found for this file, then just emit the summary dependency file as\r
359 // a dependency and return.\r
360 //\r
361 if (mGlobals.UseSumDeps) {\r
362 strcpy (SumDepsFile, mGlobals.SumDepsPath);\r
363 strcat (SumDepsFile, FileName);\r
364 for (Cptr = SumDepsFile + strlen (SumDepsFile) - 1;\r
52cbbdbc 365 (*Cptr != '\\' && *Cptr != '/') && (Cptr > SumDepsFile) && (*Cptr != '.');\r
b09da165 366 Cptr--\r
367 )\r
368 ;\r
369 if (*Cptr == '.') {\r
370 strcpy (Cptr, ".dep");\r
371 } else {\r
372 strcat (SumDepsFile, ".dep");\r
373 }\r
374 //\r
375 // See if the summary dep file exists. Could use _stat() function, but\r
376 // it's less portable.\r
377 //\r
378 if ((Fptr = fopen (SumDepsFile, "r")) != NULL) {\r
379 PrintDependency (TargetFileName, SumDepsFile);\r
380 return STATUS_SUCCESS;\r
381 }\r
382 }\r
383 //\r
384 // If we're not doing duplicates, and we've already seen this filename,\r
385 // then return\r
386 //\r
387 if (mGlobals.NoDupes) {\r
388 for (ListPtr = ProcessedFiles->Next; ListPtr != NULL; ListPtr = ListPtr->Next) {\r
389 if (stricmp (FileName, ListPtr->Str) == 0) {\r
390 break;\r
391 }\r
392 }\r
393 //\r
394 // If we found a match, we're done. If we didn't, create a new element\r
395 // and add it to the list.\r
396 //\r
397 if (ListPtr != NULL) {\r
398 //\r
399 // Print a message if verbose mode\r
400 //\r
401 if (mGlobals.Verbose) {\r
402 DebugMsg (NULL, 0, 0, FileName, "duplicate include -- not processed again");\r
403 }\r
404\r
405 return STATUS_SUCCESS;\r
406 }\r
407\r
408 ListPtr = malloc (sizeof (STRING_LIST));\r
409 ListPtr->Str = malloc (strlen (FileName) + 1);\r
410 strcpy (ListPtr->Str, FileName);\r
411 ListPtr->Next = ProcessedFiles->Next;\r
412 ProcessedFiles->Next = ListPtr;\r
413 }\r
414\r
415 //\r
416 // Make sure we didn't exceed our maximum nesting depth\r
417 //\r
418 if (NestDepth > MAX_NEST_DEPTH) {\r
419 Error (NULL, 0, 0, FileName, "max nesting depth exceeded on file");\r
420 goto Finish;\r
421 }\r
422 //\r
423 // Make a local copy of the filename. Then we can manipulate it\r
424 // if we have to.\r
425 //\r
426 strcpy (FileNameCopy, FileName);\r
427 //\r
428 // Try to open the file locally\r
429 //\r
430 if ((Fptr = fopen (FileNameCopy, "r")) == NULL) {\r
431 //\r
432 // Try to find it among the paths.\r
433 //\r
434 Fptr = FindFile (FileNameCopy, sizeof (FileNameCopy));\r
435 if (Fptr == NULL) {\r
436 //\r
437 // If this is not the top-level file, and the command-line argument\r
438 // said to ignore missing files, then return ok\r
439 //\r
440 if (NestDepth != START_NEST_DEPTH) {\r
441 if (mGlobals.IgnoreNotFound) {\r
442 if (!mGlobals.QuietMode) {\r
443 DebugMsg (NULL, 0, 0, FileNameCopy, "could not find file");\r
444 }\r
445\r
446 return STATUS_SUCCESS;\r
447 } else {\r
448 Error (NULL, 0, 0, FileNameCopy, "could not find file");\r
449 return STATUS_ERROR;\r
450 }\r
451 } else {\r
452 //\r
453 // Top-level (first) file. Emit an error.\r
454 //\r
455 Error (NULL, 0, 0, FileNameCopy, "could not find file");\r
456 return STATUS_ERROR;\r
457 }\r
458 }\r
459 }\r
460 //\r
461 // Print the dependency, with string substitution\r
462 //\r
463 PrintDependency (TargetFileName, FileNameCopy);\r
464\r
465 //\r
466 // Now read in lines and find all #include lines. Allow them to indent, and\r
467 // to put spaces between the # and include.\r
468 //\r
469 LineNum = 0;\r
470 while ((fgets (Line, sizeof (Line), Fptr) != NULL) && (Status == STATUS_SUCCESS)) {\r
471 LineNum++;\r
472 Cptr = Line;\r
473 //\r
474 // Skip preceeding spaces on the line\r
475 //\r
476 while (*Cptr && (isspace (*Cptr))) {\r
477 Cptr++;\r
478 }\r
479 //\r
480 // Check for # character\r
481 //\r
482 if (*Cptr == '#') {\r
483 Cptr++;\r
484 //\r
485 // Check for "include"\r
486 //\r
487 while (*Cptr && (isspace (*Cptr))) {\r
488 Cptr++;\r
489 }\r
490\r
491 if (strncmp (Cptr, "include", 7) == 0) {\r
492 //\r
493 // Skip over "include" and move on to filename as "file" or <file>\r
494 //\r
495 Cptr += 7;\r
496 while (*Cptr && (isspace (*Cptr))) {\r
497 Cptr++;\r
498 }\r
499\r
500 if (*Cptr == '<') {\r
501 EndChar = '>';\r
502 } else if (*Cptr == '"') {\r
503 EndChar = '"';\r
504 } else {\r
505 //\r
506 // Handle special #include MACRO_NAME(file)\r
507 // Set EndChar to null so we fall through on processing below.\r
508 //\r
509 EndChar = 0;\r
510 //\r
511 // Look for all the special include macros and convert accordingly.\r
512 //\r
513 for (Index = 0; mMacroConversion[Index].IncludeMacroName != NULL; Index++) {\r
514 //\r
515 // Save the start of the string in case some macros are substrings\r
516 // of others.\r
517 //\r
518 SaveCptr = Cptr;\r
519 if (strncmp (\r
520 Cptr,\r
521 mMacroConversion[Index].IncludeMacroName,\r
522 strlen (mMacroConversion[Index].IncludeMacroName)\r
523 ) == 0) {\r
524 //\r
525 // Skip over the macro name\r
526 //\r
527 Cptr += strlen (mMacroConversion[Index].IncludeMacroName);\r
528 //\r
529 // Skip over open parenthesis, blank spaces, then find closing\r
530 // parenthesis or blank space\r
531 //\r
532 while (*Cptr && (isspace (*Cptr))) {\r
533 Cptr++;\r
534 }\r
535\r
536 if (*Cptr == '(') {\r
537 Cptr++;\r
538 while (*Cptr && (isspace (*Cptr))) {\r
539 Cptr++;\r
540 }\r
541\r
542 EndPtr = Cptr;\r
543 while (*EndPtr && !isspace (*EndPtr) && (*EndPtr != ')')) {\r
544 EndPtr++;\r
545 }\r
546\r
547 *EndPtr = 0;\r
548 //\r
549 // Create the path\r
550 //\r
551 strcpy (MacroIncludeFileName, mMacroConversion[Index].PathName);\r
552 strcat (MacroIncludeFileName, Cptr);\r
52cbbdbc 553 strcat (MacroIncludeFileName, "/");\r
b09da165 554 strcat (MacroIncludeFileName, Cptr);\r
555 strcat (MacroIncludeFileName, ".h");\r
556 //\r
557 // Process immediately, then break out of the outside FOR loop.\r
558 //\r
559 Status = ProcessFile (TargetFileName, MacroIncludeFileName, NestDepth + 1, ProcessedFiles);\r
560 break;\r
561 }\r
562 }\r
563 //\r
564 // Restore the start\r
565 //\r
566 Cptr = SaveCptr;\r
567 }\r
568 //\r
569 // Don't recognize the include line? Ignore it. We assume that the\r
570 // file compiles anyway.\r
571 //\r
572 if (mMacroConversion[Index].IncludeMacroName == NULL) {\r
573 //\r
574 // Warning (FileNameCopy, LineNum, 0, "could not parse line", NULL);\r
575 // Status = STATUS_WARNING;\r
576 //\r
577 }\r
578 }\r
579 //\r
580 // Process "normal" includes. If the endchar is 0, then the\r
581 // file has already been processed. Otherwise look for the\r
582 // endchar > or ", and process the include file.\r
583 //\r
584 if (EndChar != 0) {\r
585 Cptr++;\r
586 EndPtr = Cptr;\r
587 while (*EndPtr && (*EndPtr != EndChar)) {\r
588 EndPtr++;\r
589 }\r
590\r
591 if (*EndPtr == EndChar) {\r
592 //\r
593 // If we're processing it, do it\r
594 //\r
595 if ((EndChar != '>') || (!mGlobals.NoSystem)) {\r
596 //\r
597 // Null terminate the filename and try to process it.\r
598 //\r
599 *EndPtr = 0;\r
600 Status = ProcessFile (TargetFileName, Cptr, NestDepth + 1, ProcessedFiles);\r
601 }\r
602 } else {\r
603 Warning (FileNameCopy, LineNum, 0, "malformed include", "missing closing %c", EndChar);\r
604 Status = STATUS_WARNING;\r
605 goto Finish;\r
606 }\r
607 }\r
608 }\r
609 }\r
610 }\r
611\r
612Finish:\r
613 //\r
614 // Close open files and return status\r
615 //\r
616 if (Fptr != NULL) {\r
617 fclose (Fptr);\r
618 }\r
619\r
620 return Status;\r
621}\r
622\r
623static\r
624void\r
625PrintDependency (\r
626 INT8 *TargetFileName,\r
627 INT8 *DependentFile\r
628 )\r
629/*++\r
630\r
631Routine Description:\r
632\r
633 Given a target (.obj) file name, and a dependent file name, do any string\r
634 substitutions (per the command line options) on the file names, then\r
635 print the dependency line of form:\r
636 \r
637 TargetFileName : DependentFile\r
638 \r
639Arguments:\r
640\r
641 TargetFileName - build target file name\r
642 DependentFile - file on which TargetFileName depends\r
643\r
644Returns:\r
645\r
646 None\r
647 \r
648--*/\r
649{\r
650 INT8 Str[MAX_PATH];\r
651\r
652 //\r
653 // Go through the symbols and do replacements\r
654 //\r
655 strcpy (Str, TargetFileName);\r
656 ReplaceSymbols (Str, sizeof (Str));\r
657 fprintf (mGlobals.OutFptr, "%s : ", Str);\r
658 strcpy (Str, DependentFile);\r
659 ReplaceSymbols (Str, sizeof (Str));\r
660 fprintf (mGlobals.OutFptr, "%s\n", Str);\r
661}\r
662\r
663static\r
664void\r
665ReplaceSymbols (\r
666 INT8 *Str,\r
667 UINT32 StrSize\r
668 )\r
669{\r
670 SYMBOL *Sym;\r
671 INT8 StrCopy[MAX_LINE_LEN];\r
672 INT8 *From;\r
673 INT8 *To;\r
674 BOOLEAN Replaced;\r
675\r
676 //\r
677 // Go through the entire string to look for replacement strings at\r
678 // every position.\r
679 //\r
680 From = Str;\r
681 To = StrCopy;\r
682 while (*From) {\r
683 //\r
684 // Copy the character\r
685 //\r
686 *To = *From;\r
687 Replaced = FALSE;\r
688 //\r
689 // Go through each symbol and try to find a string substitution\r
690 //\r
691 Sym = mGlobals.SymbolTable;\r
692 while (Sym != NULL) {\r
693 if (strnicmp (From, Sym->Value, strlen (Sym->Value)) == 0) {\r
694 //\r
695 // Replace the string, then advance the pointers past the\r
696 // replaced strings\r
697 //\r
698 strcpy (To, Sym->Name);\r
699 To += strlen (Sym->Name);\r
700 From += strlen (Sym->Value);\r
701 Replaced = TRUE;\r
702 //\r
703 // Break from the while()\r
704 //\r
705 break;\r
706 } else {\r
707 Sym = Sym->Next;\r
708 }\r
709 }\r
710\r
711 if (!Replaced) {\r
712 From++;\r
713 To++;\r
714 }\r
715 }\r
716 //\r
717 // Null terminate, and return it\r
718 //\r
719 *To = 0;\r
720 if (strlen (StrCopy) < StrSize) {\r
721 strcpy (Str, StrCopy);\r
722 }\r
723}\r
724//\r
725// Given a filename, try to find it along the include paths.\r
726//\r
727static\r
728FILE *\r
729FindFile (\r
730 INT8 *FileName,\r
731 UINT32 FileNameLen\r
732 )\r
733{\r
734 FILE *Fptr;\r
735 STRING_LIST *List;\r
736 STRING_LIST *SubDir;\r
737 INT8 FullFileName[MAX_PATH * 2];\r
738\r
739 //\r
740 // Traverse the list of paths and try to find the file\r
741 //\r
742 List = mGlobals.IncludePaths;\r
743 while (List != NULL) {\r
744 //\r
745 // Put the path and filename together\r
746 //\r
747 if (strlen (List->Str) + strlen (FileName) + 1 > sizeof (FullFileName)) {\r
748 Error (\r
749 __FILE__,\r
750 __LINE__,\r
751 0,\r
752 "application error",\r
753 "cannot concatenate '%s' + '%s'",\r
754 List->Str,\r
755 FileName\r
756 );\r
757 return NULL;\r
758 }\r
759 //\r
760 // Append the filename to this include path and try to open the file.\r
761 //\r
762 strcpy (FullFileName, List->Str);\r
763 strcat (FullFileName, FileName);\r
764 if ((Fptr = fopen (FullFileName, "r")) != NULL) {\r
765 //\r
766 // Return the file name\r
767 //\r
768 if (FileNameLen <= strlen (FullFileName)) {\r
769 Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");\r
770 //\r
771 // fprintf (stdout, "File length > %d: %s\n", FileNameLen, FullFileName);\r
772 //\r
773 return NULL;\r
774 }\r
775\r
776 strcpy (FileName, FullFileName);\r
777 return Fptr;\r
778 }\r
779 //\r
780 // Didn't find it there. Now try this directory with every subdirectory\r
781 // the user specified on the command line\r
782 //\r
783 for (SubDir = mGlobals.SubDirs; SubDir != NULL; SubDir = SubDir->Next) {\r
784 strcpy (FullFileName, List->Str);\r
785 strcat (FullFileName, SubDir->Str);\r
786 strcat (FullFileName, FileName);\r
787 if ((Fptr = fopen (FullFileName, "r")) != NULL) {\r
788 //\r
789 // Return the file name\r
790 //\r
791 if (FileNameLen <= strlen (FullFileName)) {\r
792 Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");\r
793 return NULL;\r
794 }\r
795\r
796 strcpy (FileName, FullFileName);\r
797 return Fptr;\r
798 }\r
799 }\r
800\r
801 List = List->Next;\r
802 }\r
803 //\r
804 // Not found\r
805 //\r
806 return NULL;\r
807}\r
808//\r
809// Process the command-line arguments\r
810//\r
811static\r
812STATUS\r
813ProcessArgs (\r
814 int Argc,\r
815 char *Argv[]\r
816 )\r
817{\r
818 STRING_LIST *NewList;\r
819 STRING_LIST *LastIncludePath;\r
820 STRING_LIST *LastSourceFile;\r
821 SYMBOL *Symbol;\r
822 int Index;\r
823 //\r
824 // Clear our globals\r
825 //\r
826 memset ((char *) &mGlobals, 0, sizeof (mGlobals));\r
827 mGlobals.NoDupes = TRUE;\r
828 //\r
829 // Skip program name\r
830 //\r
831 Argc--;\r
832 Argv++;\r
833 //\r
834 // Initialize locals\r
835 //\r
836 LastIncludePath = NULL;\r
837 LastSourceFile = NULL;\r
838 //\r
839 // Process until no more args\r
840 //\r
841 while (Argc) {\r
842 //\r
843 // -i path add include search path\r
844 //\r
845 if (stricmp (Argv[0], "-i") == 0) {\r
846 //\r
847 // check for one more arg\r
848 //\r
849 if (Argc > 1) {\r
850 //\r
851 // Allocate memory for a new list element, fill it in, and\r
852 // add it to our list of include paths. Always make sure it\r
853 // has a "\" on the end of it.\r
854 //\r
855 NewList = malloc (sizeof (STRING_LIST));\r
856 if (NewList == NULL) {\r
857 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
858 return STATUS_ERROR;\r
859 }\r
860\r
861 NewList->Next = NULL;\r
862 NewList->Str = malloc (strlen (Argv[1]) + 2);\r
863 if (NewList->Str == NULL) {\r
864 free (NewList);\r
865 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
866 return STATUS_ERROR;\r
867 }\r
868\r
869 strcpy (NewList->Str, Argv[1]);\r
52cbbdbc 870 if (NewList->Str[strlen (NewList->Str) - 1] != '\\' && NewList->Str[strlen (NewList->Str) - 1] != '/') {\r
871 strcat (NewList->Str, "/");\r
b09da165 872 }\r
873 //\r
874 // Add it to the end of the our list of include paths\r
875 //\r
876 if (mGlobals.IncludePaths == NULL) {\r
877 mGlobals.IncludePaths = NewList;\r
878 } else {\r
879 LastIncludePath->Next = NewList;\r
880 }\r
881\r
882 LastIncludePath = NewList;\r
883 //\r
884 // fprintf (stdout, "Added path: %s\n", NewList->Str);\r
885 //\r
886 } else {\r
887 Error (NULL, 0, 0, Argv[0], "option requires an include path");\r
888 Usage ();\r
889 return STATUS_ERROR;\r
890 }\r
891\r
892 Argc--;\r
893 Argv++;\r
894 } else if (stricmp (Argv[0], "-f") == 0) {\r
895 //\r
896 // Check for one more arg\r
897 //\r
898 if (Argc > 1) {\r
899 //\r
900 // Allocate memory for a new list element, fill it in, and\r
901 // add it to our list of source files.\r
902 //\r
903 NewList = malloc (sizeof (STRING_LIST));\r
904 if (NewList == NULL) {\r
905 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
906 return STATUS_ERROR;\r
907 }\r
908\r
909 NewList->Next = NULL;\r
910 //\r
911 // Allocate space to replace ".c" with ".obj", plus null termination\r
912 //\r
913 NewList->Str = malloc (strlen (Argv[1]) + 5);\r
914 if (NewList->Str == NULL) {\r
915 free (NewList);\r
916 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
917 return STATUS_ERROR;\r
918 }\r
919\r
920 strcpy (NewList->Str, Argv[1]);\r
921 if (mGlobals.SourceFiles == NULL) {\r
922 mGlobals.SourceFiles = NewList;\r
923 } else {\r
924 LastSourceFile->Next = NewList;\r
925 }\r
926\r
927 LastSourceFile = NewList;\r
928 } else {\r
929 Error (NULL, 0, 0, Argv[0], "option requires a file name");\r
930 Usage ();\r
931 return STATUS_ERROR;\r
932 }\r
933 //\r
934 // The C compiler first looks for #include files in the directory where\r
935 // the source file came from. Add the file's source directory to the\r
936 // list of include paths.\r
937 //\r
938 NewList = malloc (sizeof (STRING_LIST));\r
939 if (NewList == NULL) {\r
940 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
941 return STATUS_ERROR;\r
942 }\r
943\r
944 NewList->Next = NULL;\r
945 NewList->Str = malloc (strlen (Argv[1]) + 3);\r
946 if (NewList->Str == NULL) {\r
947 free (NewList);\r
948 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
949 return STATUS_ERROR;\r
950 }\r
951\r
952 strcpy (NewList->Str, Argv[1]);\r
953 //\r
954 // Back up in the source file name to the last backslash and terminate after it.\r
955 //\r
52cbbdbc 956 for (Index = strlen (NewList->Str) - 1; (Index > 0) && (NewList->Str[Index] != '\\' && NewList->Str[Index] != '/'); Index--)\r
b09da165 957 ;\r
958 if (Index < 0) {\r
52cbbdbc 959 strcpy (NewList->Str, "./");\r
b09da165 960 } else {\r
961 NewList->Str[Index + 1] = 0;\r
962 }\r
963 //\r
964 // Add it to the end of the our list of include paths\r
965 //\r
966 if (mGlobals.IncludePaths == NULL) {\r
967 mGlobals.IncludePaths = NewList;\r
968 } else {\r
969 LastIncludePath->Next = NewList;\r
970 }\r
971\r
972 if (mGlobals.Verbose) {\r
973 fprintf (stdout, "Adding include path: %s\n", NewList->Str);\r
974 }\r
975\r
976 LastIncludePath = NewList;\r
977 Argc--;\r
978 Argv++;\r
979 } else if (stricmp (Argv[0], "-s") == 0) {\r
980 //\r
981 // -s subdir add subdirectory subdir to list of subdirecties to scan.\r
982 // Check for one more arg first.\r
983 //\r
984 if (Argc > 1) {\r
985 //\r
986 // Allocate memory for a new list element, fill it in, and\r
987 // add it to our list of subdirectory include paths. Always\r
988 // make sure it has a "\" on the end of it.\r
989 //\r
990 NewList = malloc (sizeof (STRING_LIST));\r
991 if (NewList == NULL) {\r
992 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
993 return STATUS_ERROR;\r
994 }\r
995\r
996 NewList->Str = malloc (strlen (Argv[1]) + 2);\r
997 if (NewList->Str == NULL) {\r
998 free (NewList);\r
999 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
1000 return STATUS_ERROR;\r
1001 }\r
1002\r
1003 strcpy (NewList->Str, Argv[1]);\r
52cbbdbc 1004 if (NewList->Str[strlen (NewList->Str) - 1] != '\\' && NewList->Str[strlen (NewList->Str) - 1] != '/') {\r
1005 strcat (NewList->Str, "/");\r
b09da165 1006 }\r
1007\r
1008 NewList->Next = mGlobals.SubDirs;\r
1009 mGlobals.SubDirs = NewList;\r
1010 } else {\r
1011 Error (NULL, 0, 0, Argv[0], "option requires a subdirectory name");\r
1012 Usage ();\r
1013 return STATUS_ERROR;\r
1014 }\r
1015\r
1016 Argc--;\r
1017 Argv++;\r
1018 } else if (stricmp (Argv[0], "-sub") == 0) {\r
1019 //\r
1020 // -sub symname symvalue to do string substitution in the output\r
1021 //\r
1022 if (Argc > 2) {\r
1023 //\r
1024 // Allocate memory for the symbol object\r
1025 //\r
1026 Symbol = malloc (sizeof (SYMBOL));\r
1027 if (Symbol == NULL) {\r
1028 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
1029 return STATUS_ERROR;\r
1030 }\r
1031 //\r
1032 // Allocate memory for the symbol name and value, then save copies\r
1033 //\r
1034 Symbol->Name = malloc (strlen (Argv[1]) + 1);\r
1035 if (Symbol->Name == NULL) {\r
1036 free (Symbol);\r
1037 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
1038 return STATUS_ERROR;\r
1039 }\r
1040\r
1041 strcpy (Symbol->Name, Argv[1]);\r
1042 Symbol->Value = malloc (strlen (Argv[2]) + 1);\r
1043 if (Symbol->Value == NULL) {\r
1044 free (Symbol->Name);\r
1045 free (Symbol);\r
1046 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);\r
1047 return STATUS_ERROR;\r
1048 }\r
1049\r
1050 strcpy (Symbol->Value, Argv[2]);\r
1051 //\r
1052 // Add it to the list\r
1053 //\r
1054 Symbol->Next = mGlobals.SymbolTable;\r
1055 mGlobals.SymbolTable = Symbol;\r
1056 } else {\r
1057 Error (NULL, 0, 0, Argv[0], "option requires a symbol name and value");\r
1058 Usage ();\r
1059 return STATUS_ERROR;\r
1060 }\r
1061 //\r
1062 // Skip over args\r
1063 //\r
1064 Argc -= 2;\r
1065 Argv += 2;\r
1066 } else if (stricmp (Argv[0], "-nosystem") == 0) {\r
1067 mGlobals.NoSystem = TRUE;\r
1068 } else if (stricmp (Argv[0], "-nodupes") == 0) {\r
1069 mGlobals.NoDupes = TRUE;\r
1070 } else if (stricmp (Argv[0], "-nodups") == 0) {\r
1071 mGlobals.NoDupes = TRUE;\r
1072 } else if (stricmp (Argv[0], "-target") == 0) {\r
1073 //\r
1074 // -target TargetFileName - Target object file (only one allowed right\r
1075 // now) is TargetFileName rather than SourceFile.obj\r
1076 //\r
1077 if (Argc > 1) {\r
1078 strcpy (mGlobals.TargetFileName, Argv[1]);\r
1079 } else {\r
1080 Error (NULL, 0, 0, Argv[0], "option requires a target file name");\r
1081 Usage ();\r
1082 return STATUS_ERROR;\r
1083 }\r
1084\r
1085 Argc--;\r
1086 Argv++;\r
1087 } else if (stricmp (Argv[0], "-usesumdeps") == 0) {\r
1088 //\r
1089 // -usesumdeps Path - if we find an included file xxx.h, and file\r
1090 // Path/xxx.dep exists, list Path/xxx.dep as a dependency rather than\r
1091 // xxx.h and don't parse xxx.h. This allows you to create a dependency\r
1092 // file for a commonly included file, and have its dependency file updated\r
1093 // only if its included files are updated. Then anyone else including this\r
1094 // common include file can simply have a dependency on that file's .dep file\r
1095 // rather than on all the files included by it. Confusing enough?\r
1096 //\r
1097 mGlobals.UseSumDeps = 1;\r
1098 if (Argc > 1) {\r
1099 strcpy (mGlobals.SumDepsPath, Argv[1]);\r
1100 //\r
1101 // Add slash on end if not there\r
1102 //\r
52cbbdbc 1103 if (mGlobals.SumDepsPath[strlen (mGlobals.SumDepsPath) - 1] != '\\' && mGlobals.SumDepsPath[strlen (mGlobals.SumDepsPath) - 1] != '/') {\r
1104 strcat (mGlobals.SumDepsPath, "/");\r
b09da165 1105 }\r
1106 } else {\r
1107 Error (NULL, 0, 0, Argv[0], "option requires path to summary dependency files");\r
1108 Usage ();\r
1109 return STATUS_ERROR;\r
1110 }\r
1111\r
1112 Argc--;\r
1113 Argv++;\r
1114\r
1115 } else if (stricmp (Argv[0], "-o") == 0) {\r
1116 //\r
1117 // -o OutputFileName - specify an output filename for dependency list\r
1118 // check for one more arg\r
1119 //\r
1120 if (Argc > 1) {\r
1121 //\r
1122 // Try to open the file\r
1123 //\r
1124 if ((mGlobals.OutFptr = fopen (Argv[1], "w")) == NULL) {\r
1125 Error (NULL, 0, 0, Argv[1], "could not open file for writing");\r
1126 return STATUS_ERROR;\r
1127 }\r
1128\r
1129 mGlobals.OutFileName = Argv[1];\r
1130 } else {\r
1131 Error (NULL, 0, 0, Argv[0], "option requires output file name");\r
1132 Usage ();\r
1133 return STATUS_ERROR;\r
1134 }\r
1135\r
1136 Argc--;\r
1137 Argv++;\r
1138 } else if (stricmp (Argv[0], "-v") == 0) {\r
1139 mGlobals.Verbose = TRUE;\r
1140 } else if (stricmp (Argv[0], "-neverfail") == 0) {\r
1141 mGlobals.NeverFail = TRUE;\r
1142 } else if (stricmp (Argv[0], "-q") == 0) {\r
1143 mGlobals.QuietMode = TRUE;\r
1144 } else if (stricmp (Argv[0], "-ignorenotfound") == 0) {\r
1145 mGlobals.IgnoreNotFound = TRUE;\r
1146 } else if ((stricmp (Argv[0], "-h") == 0) || (strcmp (Argv[0], "-?") == 0)) {\r
1147 Usage ();\r
1148 return STATUS_ERROR;\r
1149 } else {\r
1150 Error (NULL, 0, 0, Argv[0], "unrecognized option");\r
1151 Usage ();\r
1152 return STATUS_ERROR;\r
1153 }\r
1154\r
1155 Argc--;\r
1156 Argv++;\r
1157 }\r
1158 //\r
1159 // Had to specify at least one source file\r
1160 //\r
1161 if (mGlobals.SourceFiles == NULL) {\r
1162 Error (NULL, 0, 0, "must specify one source file name", NULL);\r
1163 Usage ();\r
1164 return STATUS_ERROR;\r
1165 }\r
1166 //\r
1167 // Assume output to stdout if not specified\r
1168 //\r
1169 if (mGlobals.OutFptr == NULL) {\r
1170 mGlobals.OutFptr = stdout;\r
1171 }\r
1172\r
1173 return STATUS_SUCCESS;\r
1174}\r
1175//\r
1176// Free the global string lists we allocated memory for\r
1177//\r
1178static\r
1179void\r
1180FreeLists (\r
1181 VOID\r
1182 )\r
1183{\r
1184 STRING_LIST *Temp;\r
1185 SYMBOL *NextSym;\r
1186\r
1187 //\r
1188 // printf ("Free lists.....");\r
1189 //\r
1190 // Traverse the include paths, freeing each\r
1191 // printf ("freeing include paths\n");\r
1192 //\r
1193 while (mGlobals.IncludePaths != NULL) {\r
1194 Temp = mGlobals.IncludePaths->Next;\r
1195 //\r
1196 // printf ("Freeing include path string '%s' at 0x%X\n",\r
1197 // mGlobals.IncludePaths->Str, (int)(mGlobals.IncludePaths->Str));\r
1198 //\r
1199 free (mGlobals.IncludePaths->Str);\r
1200 //\r
1201 // printf ("Freeing include path object at 0x%X\n", (int)(mGlobals.IncludePaths));\r
1202 //\r
1203 free (mGlobals.IncludePaths);\r
1204 mGlobals.IncludePaths = Temp;\r
1205 }\r
1206 //\r
1207 // Traverse the source files, freeing each\r
1208 //\r
1209 while (mGlobals.SourceFiles != NULL) {\r
1210 Temp = mGlobals.SourceFiles->Next;\r
1211 free (mGlobals.SourceFiles->Str);\r
1212 free (mGlobals.SourceFiles);\r
1213 mGlobals.SourceFiles = Temp;\r
1214 }\r
1215 //\r
1216 // Traverse the subdirectory list, freeing each\r
1217 //\r
1218 while (mGlobals.SubDirs != NULL) {\r
1219 Temp = mGlobals.SubDirs->Next;\r
1220 free (mGlobals.SubDirs->Str);\r
1221 free (mGlobals.SubDirs);\r
1222 mGlobals.SubDirs = Temp;\r
1223 }\r
1224 //\r
1225 // Free the symbol table\r
1226 //\r
1227 while (mGlobals.SymbolTable != NULL) {\r
1228 NextSym = mGlobals.SymbolTable->Next;\r
1229 free (mGlobals.SymbolTable->Name);\r
1230 free (mGlobals.SymbolTable->Value);\r
1231 mGlobals.SymbolTable = NextSym;\r
1232 }\r
1233 //\r
1234 // printf ("done\n");\r
1235 //\r
1236}\r
1237\r
1238static\r
1239void\r
1240Usage (\r
1241 VOID\r
1242 )\r
1243/*++\r
1244\r
1245Routine Description:\r
1246\r
1247 Print usage information for this utility.\r
1248 \r
1249Arguments:\r
1250\r
1251 None.\r
1252\r
1253Returns:\r
1254\r
1255 Nothing.\r
1256 \r
1257--*/\r
1258{\r
1259 int Index;\r
1260 static const char *Str[] = {\r
1261 UTILITY_NAME " -- make dependencies",\r
1262 " Usage: MakeDeps [options]",\r
1263 " Options include:",\r
1264 " -h or -? for this help information",\r
1265 " -f SourceFile add SourceFile to list of files to scan",\r
1266 " -i IncludePath add IncludePath to list of search paths",\r
1267 " -o OutputFile write output dependencies to OutputFile",\r
1268 " -s SubDir for each IncludePath, also search IncludePath\\SubDir",\r
1269 " -v for verbose output",\r
1270 " -ignorenotfound don't warn for files not found",\r
1271 " -target Target for single SourceFile, target is Target, not SourceFile.obj",\r
1272 " -q quiet mode to not report files not found if ignored",\r
1273 " -sub sym str replace all occurrances of 'str' with 'sym' in the output",\r
1274 " -nosystem not process system <include> files",\r
1275 " -neverfail always return a success return code",\r
1276 //\r
1277 // " -nodupes keep track of include files, don't rescan duplicates",\r
1278 //\r
1279 " -usesumdeps path use summary dependency files in 'path' directory.",\r
1280 "",\r
1281 NULL\r
1282 };\r
1283 for (Index = 0; Str[Index] != NULL; Index++) {\r
1284 fprintf (stdout, "%s\n", Str[Index]);\r
1285 }\r
1286}\r