]> git.proxmox.com Git - mirror_edk2.git/blob - EdkCompatibilityPkg/Sample/Tools/Source/MakeDeps/MakeDeps.c
Add in the 1st version of ECP.
[mirror_edk2.git] / EdkCompatibilityPkg / Sample / Tools / Source / MakeDeps / MakeDeps.c
1 /*++
2
3 Copyright (c) 2004 - 2006, 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 "Tiano.h"
29 #include "EfiUtilityMsgs.h"
30
31 //
32 // Structure to maintain a linked list of strings
33 //
34 typedef struct _STRING_LIST {
35 struct _STRING_LIST *Next;
36 char *Str;
37 } STRING_LIST;
38
39 #define UTILITY_NAME "MakeDeps"
40
41 #define MAX_LINE_LEN 2048
42 #define MAX_PATH 2048
43 #define START_NEST_DEPTH 1
44 #define MAX_NEST_DEPTH 1000 // just in case we get in an endless loop.
45 //
46 // Define the relative paths used by the special #include macros
47 //
48 #define PROTOCOL_DIR_PATH "Protocol\\"
49 #define GUID_DIR_PATH "Guid\\"
50 #define ARCH_PROTOCOL_DIR_PATH "ArchProtocol\\"
51 #define PPI_PROTOCOL_DIR_PATH "Ppi\\"
52
53 //
54 // Use this structure to keep track of all the special #include forms
55 //
56 typedef struct {
57 INT8 *IncludeMacroName;
58 INT8 *PathName;
59 } INCLUDE_MACRO_CONVERSION;
60
61 //
62 // This data is used to convert #include macros like:
63 // #include EFI_PROTOCOL_DEFINITION(xxx)
64 // into
65 // #include Protocol/xxx/xxx.h
66 //
67 static const INCLUDE_MACRO_CONVERSION mMacroConversion[] = {
68 "EFI_PROTOCOL_DEFINITION",
69 PROTOCOL_DIR_PATH,
70 "EFI_GUID_DEFINITION",
71 GUID_DIR_PATH,
72 "EFI_ARCH_PROTOCOL_DEFINITION",
73 ARCH_PROTOCOL_DIR_PATH,
74 "EFI_PROTOCOL_PRODUCER",
75 PROTOCOL_DIR_PATH,
76 "EFI_PROTOCOL_CONSUMER",
77 PROTOCOL_DIR_PATH,
78 "EFI_PROTOCOL_DEPENDENCY",
79 PROTOCOL_DIR_PATH,
80 "EFI_ARCH_PROTOCOL_PRODUCER",
81 ARCH_PROTOCOL_DIR_PATH,
82 "EFI_ARCH_PROTOCOL_CONSUMER",
83 ARCH_PROTOCOL_DIR_PATH,
84 "EFI_ARCH_PROTOCOL_DEPENDENCY",
85 ARCH_PROTOCOL_DIR_PATH,
86 "EFI_PPI_DEFINITION",
87 PPI_PROTOCOL_DIR_PATH,
88 "EFI_PPI_PRODUCER",
89 PPI_PROTOCOL_DIR_PATH,
90 "EFI_PPI_CONSUMER",
91 PPI_PROTOCOL_DIR_PATH,
92 "EFI_PPI_DEPENDENCY",
93 PPI_PROTOCOL_DIR_PATH,
94 NULL,
95 NULL
96 };
97
98 typedef struct _SYMBOL {
99 struct _SYMBOL *Next;
100 INT8 *Name;
101 INT8 *Value;
102 } SYMBOL;
103
104 //
105 // Here's all our globals. We need a linked list of include paths, a linked
106 // list of source files, a linked list of subdirectories (appended to each
107 // include path when searching), and flags to keep track of command-line options.
108 //
109 static struct {
110 STRING_LIST *IncludePaths; // all include paths to search
111 STRING_LIST *SourceFiles; // all source files to parse
112 STRING_LIST *SubDirs; // appended to each include path when searching
113 SYMBOL *SymbolTable; // for replacement strings
114 FILE *OutFptr; // output dependencies to this file
115 BOOLEAN Verbose; // for more detailed output
116 BOOLEAN IgnoreNotFound; // no warnings if files not found
117 BOOLEAN QuietMode; // -q - don't print missing file warnings
118 BOOLEAN NoSystem; // don't process #include <system> files
119 BOOLEAN NeverFail; // always return success
120 BOOLEAN NoDupes; // to not list duplicate dependency files (for timing purposes)
121 BOOLEAN UseSumDeps; // use summary dependency files if found
122 BOOLEAN IsAsm; // The SourceFiles are assembler files
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 fclose (Fptr);
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, there is no # for asm
481 //
482 if ((*Cptr == '#') || (mGlobals.IsAsm)) {
483 if (*Cptr == '#') {
484 Cptr++;
485 }
486
487 //
488 // Check for "include", case insensitive for asm
489 //
490 while (*Cptr && (isspace (*Cptr))) {
491 Cptr++;
492 }
493 if (((!mGlobals.IsAsm) && (strncmp (Cptr, "include", 7) == 0)) ||
494 (mGlobals.IsAsm && (_strnicmp (Cptr, "include", 7) == 0))) {
495 //
496 // Skip over "include" and move on to filename as "file" or <file> or file for asm
497 //
498 Cptr += 7;
499 while (*Cptr && (isspace (*Cptr))) {
500 Cptr++;
501 }
502
503 if (*Cptr == '<') {
504 EndChar = '>';
505 } else if (*Cptr == '"') {
506 EndChar = '"';
507 } else if (mGlobals.IsAsm) {
508 //
509 // Handle include file for asm
510 // Set EndChar to null so we fall through on processing below.
511 //
512 EndChar = 0;
513
514 //
515 // Look for the end of include file name
516 //
517 EndPtr = Cptr;
518 while (*EndPtr && (!isspace (*EndPtr))) {
519 EndPtr++;
520 }
521
522 //
523 // Null terminate the filename and try to process it.
524 //
525 *EndPtr = 0;
526 Status = ProcessFile (TargetFileName, Cptr, NestDepth + 1, ProcessedFiles);
527 } else {
528 //
529 // Handle special #include MACRO_NAME(file)
530 // Set EndChar to null so we fall through on processing below.
531 //
532 EndChar = 0;
533 //
534 // Look for all the special include macros and convert accordingly.
535 //
536 for (Index = 0; mMacroConversion[Index].IncludeMacroName != NULL; Index++) {
537 //
538 // Save the start of the string in case some macros are substrings
539 // of others.
540 //
541 SaveCptr = Cptr;
542 if (strncmp (
543 Cptr,
544 mMacroConversion[Index].IncludeMacroName,
545 strlen (mMacroConversion[Index].IncludeMacroName)
546 ) == 0) {
547 //
548 // Skip over the macro name
549 //
550 Cptr += strlen (mMacroConversion[Index].IncludeMacroName);
551 //
552 // Skip over open parenthesis, blank spaces, then find closing
553 // parenthesis or blank space
554 //
555 while (*Cptr && (isspace (*Cptr))) {
556 Cptr++;
557 }
558
559 if (*Cptr == '(') {
560 Cptr++;
561 while (*Cptr && (isspace (*Cptr))) {
562 Cptr++;
563 }
564
565 EndPtr = Cptr;
566 while (*EndPtr && !isspace (*EndPtr) && (*EndPtr != ')')) {
567 EndPtr++;
568 }
569
570 *EndPtr = 0;
571 //
572 // Create the path
573 //
574 strcpy (MacroIncludeFileName, mMacroConversion[Index].PathName);
575 strcat (MacroIncludeFileName, Cptr);
576 strcat (MacroIncludeFileName, "\\");
577 strcat (MacroIncludeFileName, Cptr);
578 strcat (MacroIncludeFileName, ".h");
579 //
580 // Process immediately, then break out of the outside FOR loop.
581 //
582 Status = ProcessFile (TargetFileName, MacroIncludeFileName, NestDepth + 1, ProcessedFiles);
583 break;
584 }
585 }
586 //
587 // Restore the start
588 //
589 Cptr = SaveCptr;
590 }
591 //
592 // Don't recognize the include line? Ignore it. We assume that the
593 // file compiles anyway.
594 //
595 if (mMacroConversion[Index].IncludeMacroName == NULL) {
596 //
597 // Warning (FileNameCopy, LineNum, 0, "could not parse line", NULL);
598 // Status = STATUS_WARNING;
599 //
600 }
601 }
602 //
603 // Process "normal" includes. If the endchar is 0, then the
604 // file has already been processed. Otherwise look for the
605 // endchar > or ", and process the include file.
606 //
607 if (EndChar != 0) {
608 Cptr++;
609 EndPtr = Cptr;
610 while (*EndPtr && (*EndPtr != EndChar)) {
611 EndPtr++;
612 }
613
614 if (*EndPtr == EndChar) {
615 //
616 // If we're processing it, do it
617 //
618 if ((EndChar != '>') || (!mGlobals.NoSystem)) {
619 //
620 // Null terminate the filename and try to process it.
621 //
622 *EndPtr = 0;
623 Status = ProcessFile (TargetFileName, Cptr, NestDepth + 1, ProcessedFiles);
624 }
625 } else {
626 Warning (FileNameCopy, LineNum, 0, "malformed include", "missing closing %c", EndChar);
627 Status = STATUS_WARNING;
628 goto Finish;
629 }
630 }
631 }
632 }
633 }
634
635 Finish:
636 //
637 // Close open files and return status
638 //
639 if (Fptr != NULL) {
640 fclose (Fptr);
641 }
642
643 return Status;
644 }
645
646 static
647 void
648 PrintDependency (
649 INT8 *TargetFileName,
650 INT8 *DependentFile
651 )
652 /*++
653
654 Routine Description:
655
656 Given a target (.obj) file name, and a dependent file name, do any string
657 substitutions (per the command line options) on the file names, then
658 print the dependency line of form:
659
660 TargetFileName : DependentFile
661
662 Arguments:
663
664 TargetFileName - build target file name
665 DependentFile - file on which TargetFileName depends
666
667 Returns:
668
669 None
670
671 --*/
672 {
673 INT8 Str[MAX_PATH];
674
675 //
676 // Go through the symbols and do replacements
677 //
678 strcpy (Str, TargetFileName);
679 ReplaceSymbols (Str, sizeof (Str));
680 fprintf (mGlobals.OutFptr, "%s : ", Str);
681 strcpy (Str, DependentFile);
682 ReplaceSymbols (Str, sizeof (Str));
683 fprintf (mGlobals.OutFptr, "%s\n", Str);
684 //
685 // Add pseudo target to avoid incremental build failure when the file is deleted
686 //
687 fprintf (mGlobals.OutFptr, "%s : \n", Str);
688 }
689
690 static
691 void
692 ReplaceSymbols (
693 INT8 *Str,
694 UINT32 StrSize
695 )
696 {
697 SYMBOL *Sym;
698 INT8 StrCopy[MAX_LINE_LEN];
699 INT8 *From;
700 INT8 *To;
701 BOOLEAN Replaced;
702
703 //
704 // Go through the entire string to look for replacement strings at
705 // every position.
706 //
707 From = Str;
708 To = StrCopy;
709 while (*From) {
710 //
711 // Copy the character
712 //
713 *To = *From;
714 Replaced = FALSE;
715 //
716 // Go through each symbol and try to find a string substitution
717 //
718 Sym = mGlobals.SymbolTable;
719 while (Sym != NULL) {
720 if (_strnicmp (From, Sym->Value, strlen (Sym->Value)) == 0) {
721 //
722 // Replace the string, then advance the pointers past the
723 // replaced strings
724 //
725 strcpy (To, Sym->Name);
726 To += strlen (Sym->Name);
727 From += strlen (Sym->Value);
728 Replaced = TRUE;
729 //
730 // Break from the while()
731 //
732 break;
733 } else {
734 Sym = Sym->Next;
735 }
736 }
737
738 if (!Replaced) {
739 From++;
740 To++;
741 }
742 }
743 //
744 // Null terminate, and return it
745 //
746 *To = 0;
747 if (strlen (StrCopy) < StrSize) {
748 strcpy (Str, StrCopy);
749 }
750 }
751 //
752 // Given a filename, try to find it along the include paths.
753 //
754 static
755 FILE *
756 FindFile (
757 INT8 *FileName,
758 UINT32 FileNameLen
759 )
760 {
761 FILE *Fptr;
762 STRING_LIST *List;
763 STRING_LIST *SubDir;
764 INT8 FullFileName[MAX_PATH * 2];
765
766 //
767 // Traverse the list of paths and try to find the file
768 //
769 List = mGlobals.IncludePaths;
770 while (List != NULL) {
771 //
772 // Put the path and filename together
773 //
774 if (strlen (List->Str) + strlen (FileName) + 1 > sizeof (FullFileName)) {
775 Error (
776 __FILE__,
777 __LINE__,
778 0,
779 "application error",
780 "cannot concatenate '%s' + '%s'",
781 List->Str,
782 FileName
783 );
784 return NULL;
785 }
786 //
787 // Append the filename to this include path and try to open the file.
788 //
789 strcpy (FullFileName, List->Str);
790 strcat (FullFileName, FileName);
791 if ((Fptr = fopen (FullFileName, "r")) != NULL) {
792 //
793 // Return the file name
794 //
795 if (FileNameLen <= strlen (FullFileName)) {
796 Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");
797 //
798 // fprintf (stdout, "File length > %d: %s\n", FileNameLen, FullFileName);
799 //
800 return NULL;
801 }
802
803 strcpy (FileName, FullFileName);
804 return Fptr;
805 }
806 //
807 // Didn't find it there. Now try this directory with every subdirectory
808 // the user specified on the command line
809 //
810 for (SubDir = mGlobals.SubDirs; SubDir != NULL; SubDir = SubDir->Next) {
811 strcpy (FullFileName, List->Str);
812 strcat (FullFileName, SubDir->Str);
813 strcat (FullFileName, FileName);
814 if ((Fptr = fopen (FullFileName, "r")) != NULL) {
815 //
816 // Return the file name
817 //
818 if (FileNameLen <= strlen (FullFileName)) {
819 Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");
820 return NULL;
821 }
822
823 strcpy (FileName, FullFileName);
824 return Fptr;
825 }
826 }
827
828 List = List->Next;
829 }
830 //
831 // Not found
832 //
833 return NULL;
834 }
835 //
836 // Process the command-line arguments
837 //
838 static
839 STATUS
840 ProcessArgs (
841 int Argc,
842 char *Argv[]
843 )
844 {
845 STRING_LIST *NewList;
846 STRING_LIST *LastIncludePath;
847 STRING_LIST *LastSourceFile;
848 SYMBOL *Symbol;
849 int Index;
850 //
851 // Clear our globals
852 //
853 memset ((char *) &mGlobals, 0, sizeof (mGlobals));
854 mGlobals.NoDupes = TRUE;
855 //
856 // Skip program name
857 //
858 Argc--;
859 Argv++;
860 //
861 // Initialize locals
862 //
863 LastIncludePath = NULL;
864 LastSourceFile = NULL;
865 //
866 // Process until no more args
867 //
868 while (Argc) {
869 //
870 // -i path add include search path
871 //
872 if (_stricmp (Argv[0], "-i") == 0) {
873 //
874 // check for one more arg
875 //
876 if (Argc > 1) {
877 //
878 // Allocate memory for a new list element, fill it in, and
879 // add it to our list of include paths. Always make sure it
880 // has a "\" on the end of it.
881 //
882 NewList = malloc (sizeof (STRING_LIST));
883 if (NewList == NULL) {
884 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
885 return STATUS_ERROR;
886 }
887
888 NewList->Next = NULL;
889 NewList->Str = malloc (strlen (Argv[1]) + 2);
890 if (NewList->Str == NULL) {
891 free (NewList);
892 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
893 return STATUS_ERROR;
894 }
895
896 strcpy (NewList->Str, Argv[1]);
897 if (NewList->Str[strlen (NewList->Str) - 1] != '\\') {
898 strcat (NewList->Str, "\\");
899 }
900 //
901 // Add it to the end of the our list of include paths
902 //
903 if (mGlobals.IncludePaths == NULL) {
904 mGlobals.IncludePaths = NewList;
905 } else {
906 LastIncludePath->Next = NewList;
907 }
908
909 LastIncludePath = NewList;
910 //
911 // fprintf (stdout, "Added path: %s\n", NewList->Str);
912 //
913 } else {
914 Error (NULL, 0, 0, Argv[0], "option requires an include path");
915 Usage ();
916 return STATUS_ERROR;
917 }
918
919 Argc--;
920 Argv++;
921 } else if (_stricmp (Argv[0], "-f") == 0) {
922 //
923 // Check for one more arg
924 //
925 if (Argc > 1) {
926 //
927 // Allocate memory for a new list element, fill it in, and
928 // add it to our list of source files.
929 //
930 NewList = malloc (sizeof (STRING_LIST));
931 if (NewList == NULL) {
932 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
933 return STATUS_ERROR;
934 }
935
936 NewList->Next = NULL;
937 //
938 // Allocate space to replace ".c" with ".obj", plus null termination
939 //
940 NewList->Str = malloc (strlen (Argv[1]) + 5);
941 if (NewList->Str == NULL) {
942 free (NewList);
943 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
944 return STATUS_ERROR;
945 }
946
947 strcpy (NewList->Str, Argv[1]);
948 if (mGlobals.SourceFiles == NULL) {
949 mGlobals.SourceFiles = NewList;
950 } else {
951 LastSourceFile->Next = NewList;
952 }
953
954 LastSourceFile = NewList;
955 } else {
956 Error (NULL, 0, 0, Argv[0], "option requires a file name");
957 Usage ();
958 return STATUS_ERROR;
959 }
960 //
961 // The C compiler first looks for #include files in the directory where
962 // the source file came from. Add the file's source directory to the
963 // list of include paths.
964 //
965 NewList = malloc (sizeof (STRING_LIST));
966 if (NewList == NULL) {
967 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
968 return STATUS_ERROR;
969 }
970
971 NewList->Next = NULL;
972 NewList->Str = malloc (strlen (Argv[1]) + 3);
973 if (NewList->Str == NULL) {
974 free (NewList);
975 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
976 return STATUS_ERROR;
977 }
978
979 strcpy (NewList->Str, Argv[1]);
980 //
981 // Back up in the source file name to the last backslash and terminate after it.
982 //
983 for (Index = strlen (NewList->Str) - 1; (Index > 0) && (NewList->Str[Index] != '\\'); Index--)
984 ;
985 if (Index < 0) {
986 strcpy (NewList->Str, ".\\");
987 } else {
988 NewList->Str[Index + 1] = 0;
989 }
990 //
991 // Add it to the end of the our list of include paths
992 //
993 if (mGlobals.IncludePaths == NULL) {
994 mGlobals.IncludePaths = NewList;
995 } else {
996 LastIncludePath->Next = NewList;
997 }
998
999 if (mGlobals.Verbose) {
1000 fprintf (stdout, "Adding include path: %s\n", NewList->Str);
1001 }
1002
1003 LastIncludePath = NewList;
1004 Argc--;
1005 Argv++;
1006 } else if (_stricmp (Argv[0], "-s") == 0) {
1007 //
1008 // -s subdir add subdirectory subdir to list of subdirecties to scan.
1009 // Check for one more arg first.
1010 //
1011 if (Argc > 1) {
1012 //
1013 // Allocate memory for a new list element, fill it in, and
1014 // add it to our list of subdirectory include paths. Always
1015 // make sure it has a "\" on the end of it.
1016 //
1017 NewList = malloc (sizeof (STRING_LIST));
1018 if (NewList == NULL) {
1019 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1020 return STATUS_ERROR;
1021 }
1022
1023 NewList->Str = malloc (strlen (Argv[1]) + 2);
1024 if (NewList->Str == NULL) {
1025 free (NewList);
1026 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1027 return STATUS_ERROR;
1028 }
1029
1030 strcpy (NewList->Str, Argv[1]);
1031 if (NewList->Str[strlen (NewList->Str) - 1] != '\\') {
1032 strcat (NewList->Str, "\\");
1033 }
1034
1035 NewList->Next = mGlobals.SubDirs;
1036 mGlobals.SubDirs = NewList;
1037 } else {
1038 Error (NULL, 0, 0, Argv[0], "option requires a subdirectory name");
1039 Usage ();
1040 return STATUS_ERROR;
1041 }
1042
1043 Argc--;
1044 Argv++;
1045 } else if (_stricmp (Argv[0], "-sub") == 0) {
1046 //
1047 // -sub symname symvalue to do string substitution in the output
1048 //
1049 if (Argc > 2) {
1050 //
1051 // Allocate memory for the symbol object
1052 //
1053 Symbol = malloc (sizeof (SYMBOL));
1054 if (Symbol == NULL) {
1055 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1056 return STATUS_ERROR;
1057 }
1058 //
1059 // Allocate memory for the symbol name and value, then save copies
1060 //
1061 Symbol->Name = malloc (strlen (Argv[1]) + 1);
1062 if (Symbol->Name == NULL) {
1063 free (Symbol);
1064 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1065 return STATUS_ERROR;
1066 }
1067
1068 strcpy (Symbol->Name, Argv[1]);
1069 Symbol->Value = malloc (strlen (Argv[2]) + 1);
1070 if (Symbol->Value == NULL) {
1071 free (Symbol->Name);
1072 free (Symbol);
1073 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1074 return STATUS_ERROR;
1075 }
1076
1077 strcpy (Symbol->Value, Argv[2]);
1078 //
1079 // Add it to the list
1080 //
1081 Symbol->Next = mGlobals.SymbolTable;
1082 mGlobals.SymbolTable = Symbol;
1083 } else {
1084 Error (NULL, 0, 0, Argv[0], "option requires a symbol name and value");
1085 Usage ();
1086 return STATUS_ERROR;
1087 }
1088 //
1089 // Skip over args
1090 //
1091 Argc -= 2;
1092 Argv += 2;
1093 } else if (_stricmp (Argv[0], "-nosystem") == 0) {
1094 mGlobals.NoSystem = TRUE;
1095 } else if (_stricmp (Argv[0], "-nodupes") == 0) {
1096 mGlobals.NoDupes = TRUE;
1097 } else if (_stricmp (Argv[0], "-nodups") == 0) {
1098 mGlobals.NoDupes = TRUE;
1099 } else if (_stricmp (Argv[0], "-target") == 0) {
1100 //
1101 // -target TargetFileName - Target object file (only one allowed right
1102 // now) is TargetFileName rather than SourceFile.obj
1103 //
1104 if (Argc > 1) {
1105 strcpy (mGlobals.TargetFileName, Argv[1]);
1106 } else {
1107 Error (NULL, 0, 0, Argv[0], "option requires a target file name");
1108 Usage ();
1109 return STATUS_ERROR;
1110 }
1111
1112 Argc--;
1113 Argv++;
1114 } else if (_stricmp (Argv[0], "-usesumdeps") == 0) {
1115 //
1116 // -usesumdeps Path - if we find an included file xxx.h, and file
1117 // Path/xxx.dep exists, list Path/xxx.dep as a dependency rather than
1118 // xxx.h and don't parse xxx.h. This allows you to create a dependency
1119 // file for a commonly included file, and have its dependency file updated
1120 // only if its included files are updated. Then anyone else including this
1121 // common include file can simply have a dependency on that file's .dep file
1122 // rather than on all the files included by it. Confusing enough?
1123 //
1124 mGlobals.UseSumDeps = 1;
1125 if (Argc > 1) {
1126 strcpy (mGlobals.SumDepsPath, Argv[1]);
1127 //
1128 // Add slash on end if not there
1129 //
1130 if (mGlobals.SumDepsPath[strlen (mGlobals.SumDepsPath) - 1] != '\\') {
1131 strcat (mGlobals.SumDepsPath, "\\");
1132 }
1133 } else {
1134 Error (NULL, 0, 0, Argv[0], "option requires path to summary dependency files");
1135 Usage ();
1136 return STATUS_ERROR;
1137 }
1138
1139 Argc--;
1140 Argv++;
1141
1142 } else if (_stricmp (Argv[0], "-o") == 0) {
1143 //
1144 // -o OutputFileName - specify an output filename for dependency list
1145 // check for one more arg
1146 //
1147 if (Argc > 1) {
1148 //
1149 // Try to open the file
1150 //
1151 if ((mGlobals.OutFptr = fopen (Argv[1], "w")) == NULL) {
1152 Error (NULL, 0, 0, Argv[1], "could not open file for writing");
1153 return STATUS_ERROR;
1154 }
1155
1156 mGlobals.OutFileName = Argv[1];
1157 } else {
1158 Error (NULL, 0, 0, Argv[0], "option requires output file name");
1159 Usage ();
1160 return STATUS_ERROR;
1161 }
1162
1163 Argc--;
1164 Argv++;
1165 } else if (_stricmp (Argv[0], "-v") == 0) {
1166 mGlobals.Verbose = TRUE;
1167 } else if (_stricmp (Argv[0], "-neverfail") == 0) {
1168 mGlobals.NeverFail = TRUE;
1169 } else if (_stricmp (Argv[0], "-q") == 0) {
1170 mGlobals.QuietMode = TRUE;
1171 } else if (_stricmp (Argv[0], "-ignorenotfound") == 0) {
1172 mGlobals.IgnoreNotFound = TRUE;
1173 } else if (_stricmp (Argv[0], "-asm") == 0) {
1174 mGlobals.IsAsm = TRUE;
1175 } else if ((_stricmp (Argv[0], "-h") == 0) || (strcmp (Argv[0], "-?") == 0)) {
1176 Usage ();
1177 return STATUS_ERROR;
1178 } else {
1179 Error (NULL, 0, 0, Argv[0], "unrecognized option");
1180 Usage ();
1181 return STATUS_ERROR;
1182 }
1183
1184 Argc--;
1185 Argv++;
1186 }
1187 //
1188 // Had to specify at least one source file
1189 //
1190 if (mGlobals.SourceFiles == NULL) {
1191 Error (NULL, 0, 0, "must specify one source file name", NULL);
1192 Usage ();
1193 return STATUS_ERROR;
1194 }
1195 //
1196 // Assume output to stdout if not specified
1197 //
1198 if (mGlobals.OutFptr == NULL) {
1199 mGlobals.OutFptr = stdout;
1200 }
1201
1202 return STATUS_SUCCESS;
1203 }
1204 //
1205 // Free the global string lists we allocated memory for
1206 //
1207 static
1208 void
1209 FreeLists (
1210 VOID
1211 )
1212 {
1213 STRING_LIST *Temp;
1214 SYMBOL *NextSym;
1215
1216 //
1217 // printf ("Free lists.....");
1218 //
1219 // Traverse the include paths, freeing each
1220 // printf ("freeing include paths\n");
1221 //
1222 while (mGlobals.IncludePaths != NULL) {
1223 Temp = mGlobals.IncludePaths->Next;
1224 //
1225 // printf ("Freeing include path string '%s' at 0x%X\n",
1226 // mGlobals.IncludePaths->Str, (int)(mGlobals.IncludePaths->Str));
1227 //
1228 free (mGlobals.IncludePaths->Str);
1229 //
1230 // printf ("Freeing include path object at 0x%X\n", (int)(mGlobals.IncludePaths));
1231 //
1232 free (mGlobals.IncludePaths);
1233 mGlobals.IncludePaths = Temp;
1234 }
1235 //
1236 // Traverse the source files, freeing each
1237 //
1238 while (mGlobals.SourceFiles != NULL) {
1239 Temp = mGlobals.SourceFiles->Next;
1240 free (mGlobals.SourceFiles->Str);
1241 free (mGlobals.SourceFiles);
1242 mGlobals.SourceFiles = Temp;
1243 }
1244 //
1245 // Traverse the subdirectory list, freeing each
1246 //
1247 while (mGlobals.SubDirs != NULL) {
1248 Temp = mGlobals.SubDirs->Next;
1249 free (mGlobals.SubDirs->Str);
1250 free (mGlobals.SubDirs);
1251 mGlobals.SubDirs = Temp;
1252 }
1253 //
1254 // Free the symbol table
1255 //
1256 while (mGlobals.SymbolTable != NULL) {
1257 NextSym = mGlobals.SymbolTable->Next;
1258 free (mGlobals.SymbolTable->Name);
1259 free (mGlobals.SymbolTable->Value);
1260 mGlobals.SymbolTable = NextSym;
1261 }
1262 //
1263 // printf ("done\n");
1264 //
1265 }
1266
1267 static
1268 void
1269 Usage (
1270 VOID
1271 )
1272 /*++
1273
1274 Routine Description:
1275
1276 Print usage information for this utility.
1277
1278 Arguments:
1279
1280 None.
1281
1282 Returns:
1283
1284 Nothing.
1285
1286 --*/
1287 {
1288 int Index;
1289 static const char *Str[] = {
1290 UTILITY_NAME " -- make dependencies",
1291 " Usage: MakeDeps [options]",
1292 " Options include:",
1293 " -h or -? for this help information",
1294 " -f SourceFile add SourceFile to list of files to scan",
1295 " -i IncludePath add IncludePath to list of search paths",
1296 " -o OutputFile write output dependencies to OutputFile",
1297 " -s SubDir for each IncludePath, also search IncludePath\\SubDir",
1298 " -v for verbose output",
1299 " -ignorenotfound don't warn for files not found",
1300 " -target Target for single SourceFile, target is Target, not SourceFile.obj",
1301 " -q quiet mode to not report files not found if ignored",
1302 " -sub sym str replace all occurrances of 'str' with 'sym' in the output",
1303 " -nosystem not process system <include> files",
1304 " -neverfail always return a success return code",
1305 //
1306 // " -nodupes keep track of include files, don't rescan duplicates",
1307 //
1308 " -usesumdeps path use summary dependency files in 'path' directory.",
1309 " -asm The SourceFile is assembler file",
1310 "",
1311 NULL
1312 };
1313 for (Index = 0; Str[Index] != NULL; Index++) {
1314 fprintf (stdout, "%s\n", Str[Index]);
1315 }
1316 }