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