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