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