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