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 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 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 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 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 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 * 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 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 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 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