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