e4206251e6b10ecc631e2ede080c0342e9cd772a
[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, TargetFileName);
656 ReplaceSymbols (Str, sizeof (Str));
657 fprintf (mGlobals.OutFptr, "%s : ", Str);
658 strcpy (Str, DependentFile);
659 ReplaceSymbols (Str, sizeof (Str));
660 fprintf (mGlobals.OutFptr, "%s\n", Str);
661 }
662
663 static
664 void
665 ReplaceSymbols (
666 INT8 *Str,
667 UINT32 StrSize
668 )
669 {
670 SYMBOL *Sym;
671 INT8 StrCopy[MAX_LINE_LEN];
672 INT8 *From;
673 INT8 *To;
674 BOOLEAN Replaced;
675
676 //
677 // Go through the entire string to look for replacement strings at
678 // every position.
679 //
680 From = Str;
681 To = StrCopy;
682 while (*From) {
683 //
684 // Copy the character
685 //
686 *To = *From;
687 Replaced = FALSE;
688 //
689 // Go through each symbol and try to find a string substitution
690 //
691 Sym = mGlobals.SymbolTable;
692 while (Sym != NULL) {
693 if (strnicmp (From, Sym->Value, strlen (Sym->Value)) == 0) {
694 //
695 // Replace the string, then advance the pointers past the
696 // replaced strings
697 //
698 strcpy (To, Sym->Name);
699 To += strlen (Sym->Name);
700 From += strlen (Sym->Value);
701 Replaced = TRUE;
702 //
703 // Break from the while()
704 //
705 break;
706 } else {
707 Sym = Sym->Next;
708 }
709 }
710
711 if (!Replaced) {
712 From++;
713 To++;
714 }
715 }
716 //
717 // Null terminate, and return it
718 //
719 *To = 0;
720 if (strlen (StrCopy) < StrSize) {
721 strcpy (Str, StrCopy);
722 }
723 }
724 //
725 // Given a filename, try to find it along the include paths.
726 //
727 static
728 FILE *
729 FindFile (
730 INT8 *FileName,
731 UINT32 FileNameLen
732 )
733 {
734 FILE *Fptr;
735 STRING_LIST *List;
736 STRING_LIST *SubDir;
737 INT8 FullFileName[MAX_PATH * 2];
738
739 //
740 // Traverse the list of paths and try to find the file
741 //
742 List = mGlobals.IncludePaths;
743 while (List != NULL) {
744 //
745 // Put the path and filename together
746 //
747 if (strlen (List->Str) + strlen (FileName) + 1 > sizeof (FullFileName)) {
748 Error (
749 __FILE__,
750 __LINE__,
751 0,
752 "application error",
753 "cannot concatenate '%s' + '%s'",
754 List->Str,
755 FileName
756 );
757 return NULL;
758 }
759 //
760 // Append the filename to this include path and try to open the file.
761 //
762 strcpy (FullFileName, List->Str);
763 strcat (FullFileName, FileName);
764 if ((Fptr = fopen (FullFileName, "r")) != NULL) {
765 //
766 // Return the file name
767 //
768 if (FileNameLen <= strlen (FullFileName)) {
769 Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");
770 //
771 // fprintf (stdout, "File length > %d: %s\n", FileNameLen, FullFileName);
772 //
773 return NULL;
774 }
775
776 strcpy (FileName, FullFileName);
777 return Fptr;
778 }
779 //
780 // Didn't find it there. Now try this directory with every subdirectory
781 // the user specified on the command line
782 //
783 for (SubDir = mGlobals.SubDirs; SubDir != NULL; SubDir = SubDir->Next) {
784 strcpy (FullFileName, List->Str);
785 strcat (FullFileName, SubDir->Str);
786 strcat (FullFileName, FileName);
787 if ((Fptr = fopen (FullFileName, "r")) != NULL) {
788 //
789 // Return the file name
790 //
791 if (FileNameLen <= strlen (FullFileName)) {
792 Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");
793 return NULL;
794 }
795
796 strcpy (FileName, FullFileName);
797 return Fptr;
798 }
799 }
800
801 List = List->Next;
802 }
803 //
804 // Not found
805 //
806 return NULL;
807 }
808 //
809 // Process the command-line arguments
810 //
811 static
812 STATUS
813 ProcessArgs (
814 int Argc,
815 char *Argv[]
816 )
817 {
818 STRING_LIST *NewList;
819 STRING_LIST *LastIncludePath;
820 STRING_LIST *LastSourceFile;
821 SYMBOL *Symbol;
822 int Index;
823 //
824 // Clear our globals
825 //
826 memset ((char *) &mGlobals, 0, sizeof (mGlobals));
827 mGlobals.NoDupes = TRUE;
828 //
829 // Skip program name
830 //
831 Argc--;
832 Argv++;
833 //
834 // Initialize locals
835 //
836 LastIncludePath = NULL;
837 LastSourceFile = NULL;
838 //
839 // Process until no more args
840 //
841 while (Argc) {
842 //
843 // -i path add include search path
844 //
845 if (stricmp (Argv[0], "-i") == 0) {
846 //
847 // check for one more arg
848 //
849 if (Argc > 1) {
850 //
851 // Allocate memory for a new list element, fill it in, and
852 // add it to our list of include paths. Always make sure it
853 // has a "\" on the end of it.
854 //
855 NewList = malloc (sizeof (STRING_LIST));
856 if (NewList == NULL) {
857 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
858 return STATUS_ERROR;
859 }
860
861 NewList->Next = NULL;
862 NewList->Str = malloc (strlen (Argv[1]) + 2);
863 if (NewList->Str == NULL) {
864 free (NewList);
865 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
866 return STATUS_ERROR;
867 }
868
869 strcpy (NewList->Str, Argv[1]);
870 if (NewList->Str[strlen (NewList->Str) - 1] != '\\' && NewList->Str[strlen (NewList->Str) - 1] != '/') {
871 strcat (NewList->Str, "/");
872 }
873 //
874 // Add it to the end of the our list of include paths
875 //
876 if (mGlobals.IncludePaths == NULL) {
877 mGlobals.IncludePaths = NewList;
878 } else {
879 LastIncludePath->Next = NewList;
880 }
881
882 LastIncludePath = NewList;
883 //
884 // fprintf (stdout, "Added path: %s\n", NewList->Str);
885 //
886 } else {
887 Error (NULL, 0, 0, Argv[0], "option requires an include path");
888 Usage ();
889 return STATUS_ERROR;
890 }
891
892 Argc--;
893 Argv++;
894 } else if (stricmp (Argv[0], "-f") == 0) {
895 //
896 // Check for one more arg
897 //
898 if (Argc > 1) {
899 //
900 // Allocate memory for a new list element, fill it in, and
901 // add it to our list of source files.
902 //
903 NewList = malloc (sizeof (STRING_LIST));
904 if (NewList == NULL) {
905 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
906 return STATUS_ERROR;
907 }
908
909 NewList->Next = NULL;
910 //
911 // Allocate space to replace ".c" with ".obj", plus null termination
912 //
913 NewList->Str = malloc (strlen (Argv[1]) + 5);
914 if (NewList->Str == NULL) {
915 free (NewList);
916 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
917 return STATUS_ERROR;
918 }
919
920 strcpy (NewList->Str, Argv[1]);
921 if (mGlobals.SourceFiles == NULL) {
922 mGlobals.SourceFiles = NewList;
923 } else {
924 LastSourceFile->Next = NewList;
925 }
926
927 LastSourceFile = NewList;
928 } else {
929 Error (NULL, 0, 0, Argv[0], "option requires a file name");
930 Usage ();
931 return STATUS_ERROR;
932 }
933 //
934 // The C compiler first looks for #include files in the directory where
935 // the source file came from. Add the file's source directory to the
936 // list of include paths.
937 //
938 NewList = malloc (sizeof (STRING_LIST));
939 if (NewList == NULL) {
940 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
941 return STATUS_ERROR;
942 }
943
944 NewList->Next = NULL;
945 NewList->Str = malloc (strlen (Argv[1]) + 3);
946 if (NewList->Str == NULL) {
947 free (NewList);
948 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
949 return STATUS_ERROR;
950 }
951
952 strcpy (NewList->Str, Argv[1]);
953 //
954 // Back up in the source file name to the last backslash and terminate after it.
955 //
956 for (Index = strlen (NewList->Str) - 1; (Index > 0) && (NewList->Str[Index] != '\\' && NewList->Str[Index] != '/'); Index--)
957 ;
958 if (Index < 0) {
959 strcpy (NewList->Str, "./");
960 } else {
961 NewList->Str[Index + 1] = 0;
962 }
963 //
964 // Add it to the end of the our list of include paths
965 //
966 if (mGlobals.IncludePaths == NULL) {
967 mGlobals.IncludePaths = NewList;
968 } else {
969 LastIncludePath->Next = NewList;
970 }
971
972 if (mGlobals.Verbose) {
973 fprintf (stdout, "Adding include path: %s\n", NewList->Str);
974 }
975
976 LastIncludePath = NewList;
977 Argc--;
978 Argv++;
979 } else if (stricmp (Argv[0], "-s") == 0) {
980 //
981 // -s subdir add subdirectory subdir to list of subdirecties to scan.
982 // Check for one more arg first.
983 //
984 if (Argc > 1) {
985 //
986 // Allocate memory for a new list element, fill it in, and
987 // add it to our list of subdirectory include paths. Always
988 // make sure it has a "\" on the end of it.
989 //
990 NewList = malloc (sizeof (STRING_LIST));
991 if (NewList == NULL) {
992 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
993 return STATUS_ERROR;
994 }
995
996 NewList->Str = malloc (strlen (Argv[1]) + 2);
997 if (NewList->Str == NULL) {
998 free (NewList);
999 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1000 return STATUS_ERROR;
1001 }
1002
1003 strcpy (NewList->Str, Argv[1]);
1004 if (NewList->Str[strlen (NewList->Str) - 1] != '\\' && NewList->Str[strlen (NewList->Str) - 1] != '/') {
1005 strcat (NewList->Str, "/");
1006 }
1007
1008 NewList->Next = mGlobals.SubDirs;
1009 mGlobals.SubDirs = NewList;
1010 } else {
1011 Error (NULL, 0, 0, Argv[0], "option requires a subdirectory name");
1012 Usage ();
1013 return STATUS_ERROR;
1014 }
1015
1016 Argc--;
1017 Argv++;
1018 } else if (stricmp (Argv[0], "-sub") == 0) {
1019 //
1020 // -sub symname symvalue to do string substitution in the output
1021 //
1022 if (Argc > 2) {
1023 //
1024 // Allocate memory for the symbol object
1025 //
1026 Symbol = malloc (sizeof (SYMBOL));
1027 if (Symbol == NULL) {
1028 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1029 return STATUS_ERROR;
1030 }
1031 //
1032 // Allocate memory for the symbol name and value, then save copies
1033 //
1034 Symbol->Name = malloc (strlen (Argv[1]) + 1);
1035 if (Symbol->Name == NULL) {
1036 free (Symbol);
1037 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1038 return STATUS_ERROR;
1039 }
1040
1041 strcpy (Symbol->Name, Argv[1]);
1042 Symbol->Value = malloc (strlen (Argv[2]) + 1);
1043 if (Symbol->Value == NULL) {
1044 free (Symbol->Name);
1045 free (Symbol);
1046 Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
1047 return STATUS_ERROR;
1048 }
1049
1050 strcpy (Symbol->Value, Argv[2]);
1051 //
1052 // Add it to the list
1053 //
1054 Symbol->Next = mGlobals.SymbolTable;
1055 mGlobals.SymbolTable = Symbol;
1056 } else {
1057 Error (NULL, 0, 0, Argv[0], "option requires a symbol name and value");
1058 Usage ();
1059 return STATUS_ERROR;
1060 }
1061 //
1062 // Skip over args
1063 //
1064 Argc -= 2;
1065 Argv += 2;
1066 } else if (stricmp (Argv[0], "-nosystem") == 0) {
1067 mGlobals.NoSystem = TRUE;
1068 } else if (stricmp (Argv[0], "-nodupes") == 0) {
1069 mGlobals.NoDupes = TRUE;
1070 } else if (stricmp (Argv[0], "-nodups") == 0) {
1071 mGlobals.NoDupes = TRUE;
1072 } else if (stricmp (Argv[0], "-target") == 0) {
1073 //
1074 // -target TargetFileName - Target object file (only one allowed right
1075 // now) is TargetFileName rather than SourceFile.obj
1076 //
1077 if (Argc > 1) {
1078 strcpy (mGlobals.TargetFileName, Argv[1]);
1079 } else {
1080 Error (NULL, 0, 0, Argv[0], "option requires a target file name");
1081 Usage ();
1082 return STATUS_ERROR;
1083 }
1084
1085 Argc--;
1086 Argv++;
1087 } else if (stricmp (Argv[0], "-usesumdeps") == 0) {
1088 //
1089 // -usesumdeps Path - if we find an included file xxx.h, and file
1090 // Path/xxx.dep exists, list Path/xxx.dep as a dependency rather than
1091 // xxx.h and don't parse xxx.h. This allows you to create a dependency
1092 // file for a commonly included file, and have its dependency file updated
1093 // only if its included files are updated. Then anyone else including this
1094 // common include file can simply have a dependency on that file's .dep file
1095 // rather than on all the files included by it. Confusing enough?
1096 //
1097 mGlobals.UseSumDeps = 1;
1098 if (Argc > 1) {
1099 strcpy (mGlobals.SumDepsPath, Argv[1]);
1100 //
1101 // Add slash on end if not there
1102 //
1103 if (mGlobals.SumDepsPath[strlen (mGlobals.SumDepsPath) - 1] != '\\' && mGlobals.SumDepsPath[strlen (mGlobals.SumDepsPath) - 1] != '/') {
1104 strcat (mGlobals.SumDepsPath, "/");
1105 }
1106 } else {
1107 Error (NULL, 0, 0, Argv[0], "option requires path to summary dependency files");
1108 Usage ();
1109 return STATUS_ERROR;
1110 }
1111
1112 Argc--;
1113 Argv++;
1114
1115 } else if (stricmp (Argv[0], "-o") == 0) {
1116 //
1117 // -o OutputFileName - specify an output filename for dependency list
1118 // check for one more arg
1119 //
1120 if (Argc > 1) {
1121 //
1122 // Try to open the file
1123 //
1124 if ((mGlobals.OutFptr = fopen (Argv[1], "w")) == NULL) {
1125 Error (NULL, 0, 0, Argv[1], "could not open file for writing");
1126 return STATUS_ERROR;
1127 }
1128
1129 mGlobals.OutFileName = Argv[1];
1130 } else {
1131 Error (NULL, 0, 0, Argv[0], "option requires output file name");
1132 Usage ();
1133 return STATUS_ERROR;
1134 }
1135
1136 Argc--;
1137 Argv++;
1138 } else if (stricmp (Argv[0], "-v") == 0) {
1139 mGlobals.Verbose = TRUE;
1140 } else if (stricmp (Argv[0], "-neverfail") == 0) {
1141 mGlobals.NeverFail = TRUE;
1142 } else if (stricmp (Argv[0], "-q") == 0) {
1143 mGlobals.QuietMode = TRUE;
1144 } else if (stricmp (Argv[0], "-ignorenotfound") == 0) {
1145 mGlobals.IgnoreNotFound = TRUE;
1146 } else if ((stricmp (Argv[0], "-h") == 0) || (strcmp (Argv[0], "-?") == 0)) {
1147 Usage ();
1148 return STATUS_ERROR;
1149 } else {
1150 Error (NULL, 0, 0, Argv[0], "unrecognized option");
1151 Usage ();
1152 return STATUS_ERROR;
1153 }
1154
1155 Argc--;
1156 Argv++;
1157 }
1158 //
1159 // Had to specify at least one source file
1160 //
1161 if (mGlobals.SourceFiles == NULL) {
1162 Error (NULL, 0, 0, "must specify one source file name", NULL);
1163 Usage ();
1164 return STATUS_ERROR;
1165 }
1166 //
1167 // Assume output to stdout if not specified
1168 //
1169 if (mGlobals.OutFptr == NULL) {
1170 mGlobals.OutFptr = stdout;
1171 }
1172
1173 return STATUS_SUCCESS;
1174 }
1175 //
1176 // Free the global string lists we allocated memory for
1177 //
1178 static
1179 void
1180 FreeLists (
1181 VOID
1182 )
1183 {
1184 STRING_LIST *Temp;
1185 SYMBOL *NextSym;
1186
1187 //
1188 // printf ("Free lists.....");
1189 //
1190 // Traverse the include paths, freeing each
1191 // printf ("freeing include paths\n");
1192 //
1193 while (mGlobals.IncludePaths != NULL) {
1194 Temp = mGlobals.IncludePaths->Next;
1195 //
1196 // printf ("Freeing include path string '%s' at 0x%X\n",
1197 // mGlobals.IncludePaths->Str, (int)(mGlobals.IncludePaths->Str));
1198 //
1199 free (mGlobals.IncludePaths->Str);
1200 //
1201 // printf ("Freeing include path object at 0x%X\n", (int)(mGlobals.IncludePaths));
1202 //
1203 free (mGlobals.IncludePaths);
1204 mGlobals.IncludePaths = Temp;
1205 }
1206 //
1207 // Traverse the source files, freeing each
1208 //
1209 while (mGlobals.SourceFiles != NULL) {
1210 Temp = mGlobals.SourceFiles->Next;
1211 free (mGlobals.SourceFiles->Str);
1212 free (mGlobals.SourceFiles);
1213 mGlobals.SourceFiles = Temp;
1214 }
1215 //
1216 // Traverse the subdirectory list, freeing each
1217 //
1218 while (mGlobals.SubDirs != NULL) {
1219 Temp = mGlobals.SubDirs->Next;
1220 free (mGlobals.SubDirs->Str);
1221 free (mGlobals.SubDirs);
1222 mGlobals.SubDirs = Temp;
1223 }
1224 //
1225 // Free the symbol table
1226 //
1227 while (mGlobals.SymbolTable != NULL) {
1228 NextSym = mGlobals.SymbolTable->Next;
1229 free (mGlobals.SymbolTable->Name);
1230 free (mGlobals.SymbolTable->Value);
1231 mGlobals.SymbolTable = NextSym;
1232 }
1233 //
1234 // printf ("done\n");
1235 //
1236 }
1237
1238 static
1239 void
1240 Usage (
1241 VOID
1242 )
1243 /*++
1244
1245 Routine Description:
1246
1247 Print usage information for this utility.
1248
1249 Arguments:
1250
1251 None.
1252
1253 Returns:
1254
1255 Nothing.
1256
1257 --*/
1258 {
1259 int Index;
1260 static const char *Str[] = {
1261 UTILITY_NAME " -- make dependencies",
1262 " Usage: MakeDeps [options]",
1263 " Options include:",
1264 " -h or -? for this help information",
1265 " -f SourceFile add SourceFile to list of files to scan",
1266 " -i IncludePath add IncludePath to list of search paths",
1267 " -o OutputFile write output dependencies to OutputFile",
1268 " -s SubDir for each IncludePath, also search IncludePath\\SubDir",
1269 " -v for verbose output",
1270 " -ignorenotfound don't warn for files not found",
1271 " -target Target for single SourceFile, target is Target, not SourceFile.obj",
1272 " -q quiet mode to not report files not found if ignored",
1273 " -sub sym str replace all occurrances of 'str' with 'sym' in the output",
1274 " -nosystem not process system <include> files",
1275 " -neverfail always return a success return code",
1276 //
1277 // " -nodupes keep track of include files, don't rescan duplicates",
1278 //
1279 " -usesumdeps path use summary dependency files in 'path' directory.",
1280 "",
1281 NULL
1282 };
1283 for (Index = 0; Str[Index] != NULL; Index++) {
1284 fprintf (stdout, "%s\n", Str[Index]);
1285 }
1286 }