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